كود ار COD·AR — منصة كلانات كول اوف ديوتي العربية
COD·AR
cod-ar.com
COD·AR
📲
أضف التطبيق لجوالكثبّته على الشاشة الرئيسية ليصير كتطبيق وتستقبل الإشعارات
🔔 إشعارات التحدياتفعّلها ليوصلك تنبيه عند أي طلب تحدي
مباشر موسم الأساطير
متصل الآن
كلانات تحديات جارية متصلون طلبات مفتوحة
كلان
لاعب
طلبات التحدي
🔴 جارية الآن
🔎 طلبات مفتوحة
✅ تحديات معتمدة

👑 أقوى الكلانات الترتيب الكامل ←

...

📜 آخر نتائج الكلانات

🎯 نجوم الأفراد (1v1)التصنيف الكامل ←

🆚 آخر التحديات الفردية

🏆 الدوري
لوحتان: المصنّف بالنقاط · وغير المصنّف بالانتصارات
🛡️ كلانات COD·AR
كل الكلانات المسجّلة — تصفّح، اعرف عنها، وأرسل طلب انضمام
?
CLAN | COD-AR

الأعضاء

📜 سجل التحديات

التحديات
اطلب تحدي · ابحث عن خصم · اقبل التحديات · ⚔️ القوانين ←

🔴 التحديات الجارية الآن

🔎 طلبات تحدٍّ متاحة

⚔️ تحدّوك مباشرة — اقبل أو ارفض

📋 تحدياتي

🔒 أرشيف الكلانات
كلانات اعتزلت — سجلّها محفوظ، وتاقها محجوز لصاحبها
🚪 كلانات تبحث عن أعضاء
فاتحة باب الانضمام الآن — اضغط «انضمام» ويوصل طلبك لرئيسها
اطلب تحدي
اختر خصمك ونمط اللعب — ويوصل طلبك لكل الكلانات
🏅 المصنّف — النقاط تزيد بالفوز وتنقص بالخسارة. الفوز 4-0 أكثر من 4-3، والفوز على كلان أقوى منك يعطيك أضعاف.
تسجيل كلان جديد
أكمل القائمة ويترسّم كلانك في نفس اللحظة — بلا انتظار موافقة

1 بيانات الكلان

CLAN ____ | COD-AR

2 قائمة اللاعبين (0/6)

🔑 آيدي السوني إجباري لكل لاعب — هو هويته بالمنصة. أي لاعب يسجّل لاحقاً بنفس الآيدي يرتبط بكلانك تلقائياً، وبه يتواصل معكم الخصم وقت التحدي. تأكّد من كتابته صح — الخطأ فيه يعطّل ارتباط العضو.
إدارة الكلان
النائب · الأعضاء · الوصف · اللوقو

الأعضاء

هوية الكلان

🎖️ الرتب والصلاحيات
عيّن منازل أعضائك — الرئيس واحد، والنائب يده معه

👥 أعضاء الكلان

👑 الرئيس — يملك كل شيء: التحديات، توثيق النتائج، الرتب، وأرشفة الكلان. واحدٌ لا غير.
🎖️ النائب — يطلق التحديات ويردّ عليها ويستقبل طلبات الانضمام، ولا يمسّ الرتب.
🎮 لاعب — يمثّل الكلان في المباريات ويظهر في كشفه.
📋 منسق كلان — يدير طلبات التحديات ويوثّق نتائجها نيابةً عن الرئيس. متاح لأصحاب باقة GOLD أو DIAMOND.
?CLANكلانك
VS
?CLANالخصم
⚔️ انطلق التحدي جهّزوا فريقكم
⚔️ قوانين تحديات الكلانات — سنايبر النسخة المعتمدة كاملةً · تُطبَّق على كل تحدٍّ مصنّف · وقرار الإدارة نافذ فيما وراءها
4أقيام للحسم
8راوندات بالقيم
0.5ثحدّ الزوم
10دحدّ المهلة
🎨 العقوبة تُقرأ من لونها: راوند للمتضرر · القيم كاملاً · التحدي كاملاً · استبعاد لاعب · قاعدة / حق
⏳ جاري التحميل...
الأسئلة الشائعة شرح مصوّر لأهم خطوات استخدام المنصة — اضغط أي سؤال ليفتح الفيديو

🛡️ إدارة الكلان

طريقة إضافة الأعضاء للكلان، منح الصلاحيات داخل الكلان، وفتح باب الانضمام.

🎯 كيف تطلب تحدي

خطوات إطلاق طلب تحدي جديد لكلان آخر.

🛡️ الإشراف والتوثيق

كيف تطلب مشرفاً، تُبلّغ عن مخالفة (زي الحرق)، وتوثّق نتيجة تحدي.

🏅 نظام التصنيف والرتب

كيف تُحسب النقاط، وكيف يترقّى كلانك عبر مستويات الرتب.

🛍️

متجر COD·AR

باقات حصرية تمنحك مزايا إضافية داخل المنصة

⏳ جاري التحميل...
🎫 التواصل مع الإدارة
شكوى · اقتراح · طلب — والردّ يصلك على نفس التذكرة

🗂️ تذاكري

🎖️ التقديم على الإشراف

🔒 التقديم على الإشراف مغلق حالياً
سيتم الإعلان عند فتح باب التقديم
🎫 تذكرة

مجتمع البث المباشر

شاهد ستريمرين مجتمعنا مباشرة — من داخل المنصة

يفتح البث…
🔇 اضغط لتسمع صوته
مباشر
مباشر الآن
جميع الستريمرين
🏆 أكثر الستريمرين نشاطاً
جارٍ التحميل…
👤 الأفراد — 1v1
Free For All — تحدّى أي لاعب وارفع تصنيفك الفردي

🔎 طلبات مواجهة متاحة

⚔️ تحدّوك مباشرة — اقبل أو ارفض

📋 مواجهاتي

🎯 صدارة الأفراد

⚡ غرفة المواجهة
تبادلوا آيديات السوني واتفقوا على الوقت
⚙️ إعدادات الحساب
الأمان · الخصوصية

الدخول والأمان

🎮 آيدي السونيهويتك في المنصة — ما يتغيّر

الخصوصية

💬 الرسائل الخاصةمن يقدر يراسلك؟
🧱 جدار ملفكمن يقدر يكتب عندك؟
👣 آخر الزوّارقائمة من زار ملفك — تظهر لك أنت في الحالتين
يُحفظ فور اختيارك — بلا زر حفظ.

التنبيهات

🔔 ماذا يصلك على جوالك؟ ما يخصّك يصلك دائماً: تحدّيك · نتيجتك · بلاغك · طلبات الانضمام لكلانك.
«ما يخصّني فقط» يوقف إعلانات المنصة العامة — كإعلان لاعبٍ يبحث عن كلان. ولا يمسّ تنبيهاتك أبداً.

🗑 حذف حسابي

يُحذف حسابك وملفّك وإحصاءاتك ورسائلك حذفاً لا رجعة فيه.
قبله: انقل رئاسة كلانك إن كنت رئيساً، وأنهِ تحدّياتك القائمة.

🚫 المحظورون

من تحظره ما يشوف رسائلك ولا تشوف رسائله، وما يقدر يراسلك خاص. الإشراف يبقى قادراً على التواصل معك.
🤝 الأصدقاء
طلبات الصداقة وقائمة أصدقائك

📥 طلبات واردة

👥 أصدقائي

👥 اللاعبون
كل لاعبي COD·AR — تصفّح · راسل · تحدَّ
🚫 القائمة السوداء
اللاعبين والكلانات المحظورين حالياً من المنصة
محادثة
📬 رسائلي
محادثاتك الخاصة مع اللاعبين
🔎 أعضاء يبحثون عن كلان
لاعبون أحرار متاحون للانضمام — تواصل معهم وضمّهم لكلانك
🎮 غرفة التحدي
تبادلوا آيديات السوني واتفقوا على الوقت
🏅 نظام التصنيف
كيف تُحسب نقاطك بالضبط — بالأرقام لا بالكلام
⏳ جاري الحساب...
🔎 أبحث عن كلان
اكتب بياناتك وينزل طلبك في صفحة «يدورون كلان» — يشوفه كل رؤساء الكلانات
🔄 سوق الانتقالات
انتقالات اللاعبين المعتمدة بين الكلانات
🔔 التنبيهات
نتائج التحديات · البلاغات · الإعلانات
💬 الدردشة العامة
🟢 0 متصل الآن · دردشة كل أعضاء COD·AR
🎮 اللوبيات
افتحْ لوبي أو انضمَّ لواحدٍ مفتوحٍ الآن — بلاك أوبس ١
🧑‍⚖️ لوحة الإدارة
صلاحياتك تظهر حسب مجموعتك (أدمن / مشرف)

COD·AR — منصة كلانات Black Ops 1 · الأساطير رجعوا 🔥
est. 2012 · cod-ar.com

'; } function check(){ if(done || !window.sb) return; sb.auth.getSession().then(function(sr){ var u=sr&&sr.data&&sr.data.session&&sr.data.session.user; if(!u) return; sb.rpc('check_my_ban').then(function(r){ var bd=(r&&r.data)||{}; if(!bd.banned) return; done=true; paint(bd); try{ sb.auth.signOut(); }catch(e){} },function(){}); },function(){}); } check(); setTimeout(check, 1500); if(window.sb && sb.auth && sb.auth.onAuthStateChange){ sb.auth.onAuthStateChange(function(ev){ if(ev==='SIGNED_IN') setTimeout(check,300); }); } })(); window._banned=false; window._chatOff=false; function _blocked(){ if(window._banned){ toast('🚫 حسابك محظور — راجع الإدارة'); return true; } return false; } var _settingsP=null; function getSettings(force){ if(force) _settingsP=null; if(!_settingsP){ var boot=(window._boot&&window._boot.settings); if(window._boot) window._boot.settings=null; var src=boot?boot:sb.from('settings').select('key,value').then(function(r){ return r.data; }); _settingsP=src.then(function(d){ var m={}; (d||[]).forEach(function(s){m[s.key]=s.value;}); window._appSet=m; return m; }, function(){ return {}; }); } return _settingsP; } /* ═══ مظهر الأعضاء ═══ مصدران: مظهر المجموعة (يمشي تلقائياً بعضويتها) وتخصيص فردي لعضو بعينه — والفردي يتغلّب. نخزّنه بخريطة واحدة مفتاحها معرّف العضو. */ window._NS=[]; window._nsMap={}; window._fxU={}; (function(){ var boot=(window._boot&&window._boot.ns); if(window._boot) window._boot.ns=null; Promise.resolve(boot||sb.from('name_styles').select('*').order('sort_order').then(function(r){return r.data;})) .then(function(d){ var a=d||[]; window._NS=a; a.forEach(function(s){ window._nsMap[s.id]=s; }); },function(){}); /* تُنادى loadUserStyles بعد تعريفها بأسطر — لا هنا (كانت تُنادى قبل تعريفها فيتوقّف السكربت كلّه وتختفي البيانات) */ })(); /* ═══ أنماط الأسماء ═══ ألوان الإداريين والمجموعات. كانت تُجلب مرّةً بلا شبكة أمان — فإن فشل الجلب (كما ليلة الانقطاع) بقيت الأسماء بلا لون حتى إعادة تحميلٍ كاملة. الآن تُعيد المحاولة مرّةً بعد إنعاش الجلسة، وتُعاد رسمة الشاشة المعنيّة عند النجاح. */ window._fxLoaded=false; window.loadUserStyles=function(retry){ return sb.from('v_user_style').select('*').then(function(r){ if(r&&r.error) throw r.error; var rows=(r&&r.data)||[]; window._fxU={}; rows.forEach(function(x){ window._fxU[x.profile_id]=x; }); window._fxLoaded=true; /* الشاشة المفتوحة تلتقط الألوان فوراً */ try{ if(window.loadChat && document.querySelector('#chat.active')) window.loadChat(); }catch(e){} try{ if(window.renderPlayers && document.querySelector('#players.active')) window.renderPlayers(); }catch(e){} return rows.length; }, function(){ if(retry) return 0; /* محاولةٌ ثانية بعد إنعاش الجلسة — كما فعلنا مع الدردشة */ return (sb.auth.refreshSession?sb.auth.refreshSession():Promise.resolve()) .then(function(){ return window.loadUserStyles(true); }, function(){ return window.loadUserStyles(true); }); }); }; window.loadUserStyles(); /* الآن بعد التعريف — يبدأ تحميل الألوان بأمان */ /* مظهر عضو — العرض في القاعدة حسم الأولوية (فردي ثم مجموعة) */ window.fxOf=function(uid){ return (uid&&window._fxU[uid])||null; }; /* ═══ إطار الرسالة ═══ القالب اسمُ كلاس، وزخرفته كلها في الـCSS — فيقدر يستعمل زوايا وطبقات وحركة، وهذا ما يعجز عنه لونٌ مخزَّن في عمود. */ window.FXB={ glow:'', neon:'', glass:'', royal:'sweep', ember:'', frost:'sweep', legend:'sweep', rainbow:'', blade:'', aura:'' }; window.fxBubble=function(fx){ var k=fx&&fx.preset; if(!k||!(k in window.FXB)) return null; return { cls:'fxb fxb-'+k+(window.FXB[k]?' '+window.FXB[k]:''), varc:'--fxc:'+(fx.color||'#38b6ff') }; }; /* ═══ تأكيد بشكل المنصة ═══ بديل confirm() الأصلية. يُرجع وعداً بـtrue/false فيُنتظر بـ.then. */ window.confirmBox=function(o){ o=o||{}; return new Promise(function(done){ var old=document.getElementById('cfBack'); if(old) old.remove(); old=document.getElementById('cfBox'); if(old) old.remove(); var tone=o.tone||'danger'; var col = tone==='danger' ? ['rgba(255,107,94,.14)','rgba(255,107,94,.4)','var(--red)','linear-gradient(90deg,#ff6b5e,#d8443a)'] : tone==='gold' ? ['rgba(232,179,75,.14)','rgba(232,179,75,.45)','var(--gold)','linear-gradient(90deg,#e8b34b,#b8862c)'] : ['rgba(56,182,255,.14)','rgba(56,182,255,.42)','var(--blue2)','linear-gradient(90deg,var(--blue),#1f8fd8)']; var bk=document.createElement('div'); bk.id='cfBack'; var bx=document.createElement('div'); bx.id='cfBox'; bx.innerHTML= '
'+(o.icon||'⚠️')+'
'+ '
'+esc(o.title||'تأكيد')+'
'+ '
'+(o.text?esc(o.text):'')+ (o.points&&o.points.length?'':'')+ '
'+ '
'+ ''+ ''+ '
'; document.body.appendChild(bk); document.body.appendChild(bx); var close=function(v){ document.removeEventListener('keydown',key); bk.remove(); bx.remove(); done(v); }; var key=function(e){ if(e.key==='Escape') close(false); if(e.key==='Enter') close(true); }; document.addEventListener('keydown',key); bk.onclick=function(){ close(false); }; bx.querySelector('#cfNo').onclick=function(){ close(false); }; bx.querySelector('#cfYes').onclick=function(){ close(true); }; try{ bx.querySelector('#cfYes').focus(); }catch(e){} }); }; /* ═══ شارة الرتبة ═══ اسم المجموعة نفسها يصير الشارة، بلونها وأيقونتها — فيُدار من اللوحة بدل ما يكون محفوراً في الكود. تأخذ لمحةً من إطارها كي تنسجم معه. */ /* ═══ أيقونةُ الشارة: حرفٌ أو صورة ═══ كانت نصّاً وحدَه، فمن أرادَ شارةً مرسومةً لم يجدْ باباً. والحقلُ نفسُه يحملُ الاثنَين: ما بدأَ بـ`/` أو `http` صورةٌ، وما سواه حرف. **بابٌ واحدٌ يقبلُ الشكلَين أهونُ من حقلٍ ثانٍ يُنسى نصفُ ملئِه.** والصورةُ تُعرَضُ بحجمِ السطرِ نفسِه فتكبرُ وتصغرُ معه بلا ضبطٍ في كلِّ موضع — فهي في الدردشةِ صغيرةٌ وفي الملفِّ أكبر، ورقمٌ ثابتٌ كان سيُفسِدُ أحدَهما. */ window.fxBadgeIcon=function(v,c){ v=String(v||''); if(!v) return ''; if(/^(https?:)?\/\//.test(v) || v.charAt(0)==='/') return ''; return esc(v)+' '; }; window.fxBadge=function(fx,sz){ if(!fx||!fx.badge_on||!fx.badge_name) return ''; var c=fx.badge_color||'#38b6ff', k=fx.badge_preset; var cl=(k&&k!=='none')?(' fxb fxb-'+k+(window.FXB&&window.FXB[k]?' '+window.FXB[k]:'')):''; return ''+window.fxBadgeIcon(fx.badge_icon,c)+esc(fx.badge_name)+''; }; /* اسم مصمَّم — نفس منطق اللوحة */ window.fxName=function(fx,name,click){ if(!fx) return esc(name||'لاعب'); var st='font-weight:800;'; if(fx.grad) st+='background:'+fx.grad+';-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;'; else st+='color:'+esc(fx.color)+';'; if(fx.glow&&!fx.grad) st+='text-shadow:'+fx.glow+';'; if(click) st+='cursor:pointer;'; return ''+ (fx.icon?esc(fx.icon)+' ':'')+esc(name||'لاعب')+''; }; /* توافقاً مع النداءات القائمة */ window.nsEl=function(sid,name,click){ return window.fxName(window.fxOf(null,sid),name,click); }; window.nsBg=function(sid){ var s=sid?window._nsMap[sid]:null; return (s&&s.bg_effect)||null; }; /* ═══ تغيّرُ إعدادٍ ليس سبباً لهدم الشاشة ═══ كانت هذه تُنادي refreshNow — إعادةَ تحميلٍ كاملة تتجاوز المهدّئ. فأي كتابةٍ في settings تُعيد بناء شاشة **كل** متصلٍ في اللحظة نفسها: عشرون متصلاً × أحد عشر استعلاماً من كتابةٍ واحدة. والإعدادات لا تغيّر بيانات الشاشة أصلاً — تغيّر حدوداً ونصوصاً تُطبَّق بلا جلبٍ جديد. فنكتفي بتطبيقها، ونُهدّئ ما تبقّى. */ var _gsfT=null; window.getSettingsForce=function(){ if(_gsfT) return; /* نداءاتٌ متلاحقة ⟵ واحدة */ _gsfT=setTimeout(function(){ _gsfT=null; getSettings(true).then(function(){ try{ applyAppSettings(!!window._isStaff); }catch(e){} try{ if(window.applyAddNote) window.applyAddNote(); }catch(e){} /* المهدَّأة لا الفورية — وإن لم تتغيّر بياناتُ الشاشة فلا شيء */ try{ if(window.refreshActive) window.refreshActive(); }catch(e){} },function(){}); }, 800); }; function applyAppSettings(staff){ getSettings().then(function(m){ /* لا يُلحَقُ اسمٌ محفورٌ بعدَ اليوم — الاسمُ من window.BRAND وحدَه، وعنوانُ الشاشةِ يُبنى في go() فلا يُزاحَمُ من هنا. */ if(m.site_name){ try{ window.BRAND = String(m.site_name).trim() || window.BRAND; }catch(e){} } if(m.discord_invite && window.setDiscordInvite) window.setDiscordInvite(m.discord_invite); var ov=document.getElementById('maintOverlay'); if(m.maintenance==='1' && !staff){ if(!ov){ ov=document.createElement('div'); ov.id='maintOverlay'; ov.style.cssText='position:fixed;inset:0;z-index:9999;background:#04070b;color:#eaf2fb;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:30px;gap:14px;font-family:inherit'; ov.innerHTML='
🔧

المنصة في الصيانة

نعمل على تحسينات — نرجع بأقرب وقت 🎮
'; document.body.appendChild(ov); } } else if(ov){ ov.remove(); } if(window.applyTicker) window.applyTicker(m); if(window.applyCountdown) window.applyCountdown(m); var off = m.chat_off==='1'; window._chatOff=off; if($('chatInput')){ $('chatInput').disabled=off; $('chatInput').placeholder= off?'الدردشة موقوفة مؤقتاً':'اكتب رسالة...'; } if(window.applyRushVis) window.applyRushVis(); },function(){}); } function refreshAuth(){ /* getUser يضرب الشبكة؛ لو تعثّرت (فتح بارد داخل التطبيق) نرجع للجلسة المخزّنة محلياً كي لا تبقى الترويسة «ضيف» وبقية الصفحات تعرف صاحبها. */ window.authUser().then(_authApply, function(){ sb.auth.getSession().then(function(r){ var u=r&&r.data&&r.data.session&&r.data.session.user; if(u) _authApply({data:{user:u}}); },function(){}); }); } function _authApply(res){ (function(){ var user = res && res.data && res.data.user; /* ═══ الصلاحيات تُقرأ بعد أن تُعرف الهوية ═══ كانت ‎applyPerms‎ تُنادى مرّةً عند الإقلاع، وقد تسبق إرفاقَ الجلسة بالطلب — فتعود بصلاحيات زائر، والزائر لا يرى الدردشة، فتُخفى عن العضو ولا شيء يعيدها إلا إعادة تحميلٍ كاملة. نُعيدها كلّما تبدّلت الهوية: من ضيفٍ إلى عضو أو بالعكس. */ var uidBefore = window._uid || null; window._uid = user?user.id:null; if(uidBefore !== window._uid){ clearTimeout(window._apT); window._apT=setTimeout(function(){ if(window.applyPerms) window.applyPerms(); }, 120); } /* ارسم الهوية فوراً من بيانات الحساب — الملف الشخصي يحدّثها بعد لحظة. بدون هذا تبقى الشريحة «ضيف» لو تأخّر استعلام profiles أو فشل. */ if(user && window.renderIdentity){ var _md=user.user_metadata||{}; window.renderIdentity({ id:user.id, role:'user', username:(_md.username||_md.full_name||_md.name||_md.user_name||_md.preferred_username||(user.email||'').split('@')[0]), avatar_url:_md.avatar_url||_md.picture||null }); } if(!user){ window._banned=false; window._authDone=true; applyAppSettings(false); if(window.renderIdentity) window.renderIdentity(null); } if($('navAdmin')) $('navAdmin').hidden=true; if($('menuAdmin')) $('menuAdmin').hidden=true; if($('moreAdmin')) $('moreAdmin').hidden=true; if($('menuArchive')) $('menuArchive').hidden=true; if($('moreArchive')) $('moreArchive').hidden=true; if($('authView')) $('authView').hidden = !!user; if($('userView')) $('userView').hidden = !user; if(user){ $('uEmail').textContent = user.email || ''; var md=user.user_metadata||{}; var fb=(md.username||md.full_name||md.name||md.user_name||md.preferred_username||(user.email||'').split('@')[0]); sb.from('profiles').select('username,psn_id,avatar_url,banned,seeking_clan,discord_id,discord_name,bio,title,role').eq('id',user.id).single().then(function(pr){ var uname=(pr.data&&pr.data.username)||fb; window._myUsername=uname; var staff=(pr.data&&(pr.data.role==='admin'||pr.data.role==='moderator')); window._banned=!!(pr.data&&pr.data.banned); window._isStaff=staff; window._authDone=true; applyAppSettings(staff); if(window.updateBellBadge) window.updateBellBadge(); if(window.linkPushToMe) window.linkPushToMe(); /* من أضافه رئيس كلان بآيديه قبل تسجيله: يرتبط بصفّه الآن. مرّةً واحدة لكل جلسة لا كلّ تحميلٍ للهوية — كان يُنفَّذ آلافاً بلا داعٍ (عضوٌ مرتبطٌ سلفاً يعود دائماً بصفر). */ if((pr.data&&pr.data.psn_id) && !window._psnClaimed){ window._psnClaimed=true; sb.rpc('claim_psn_memberships').then(function(cr){ if(cr&&cr.data>0){ toast('🛡️ ارتبط حسابك بعضويتك في الكلان'); if(window.refreshNow) window.refreshNow(); } },function(){ window._psnClaimed=false; }); } if(window.loadInvites) window.loadInvites(); if(window.fillAccountId) window.fillAccountId(); if(window.checkPushReady) window.checkPushReady(); if(window._banned) toast('🚫 حسابك محظور من النشر — راجع الإدارة'); // ربط تلقائي مع Discord لمن يسجّل دخوله عبره (يفعّل أوامر البوت فوراً) try{ var ids=(user.identities||[]).filter(function(x){return x.provider==='discord';}); var did=ids.length?String(ids[0].id):''; if(did && window.joinDiscordServer) window.joinDiscordServer(); if(did && !pr.data.discord_id){ sb.from('profiles').update({discord_id:did, discord_name:(md.full_name||md.name||md.user_name||null)}).eq('id',user.id).then(function(){ toast('🔗 تم ربط حسابك بـDiscord — أوامر البوت جاهزة'); sb.rpc('claim_discord_memberships',{p_uid:user.id,p_discord:did}).then(function(r){ if(r&&r.data>0){ toast('🛡️ استُعيدت عضويتك بالكلان'); if(window.refreshNow) window.refreshNow(); } },function(){}); },function(){}); } }catch(e){} /* ═══ لوحة الإدارة داخل التطبيق ═══ كانت تظهر لكل من رتبتُه مشرفٌ أو أدمن. صارت بمفتاحها panel_app: فمن أردتَه يعمل من التطبيق وحده تمنحه إياه بلا رتبة، ومن أردتَه في اللوحة الخارجية فقط تسحبه منه. نُظهرها أولاً بالرتبة ثم نصحّحها متى وصلت المفاتيح. */ var showAdm=function(v){ ['navAdmin','menuAdmin','moreAdmin'].forEach(function(k){ if($(k)) $(k).hidden=!v; }); }; showAdm(staff); sb.rpc('my_perms').then(function(pp){ var P=(pp&&pp.data)||{}; window._perms=P; showAdm(P.panel_app===true || P.admin_all===true || (staff && P.panel_app===undefined)); },function(){}); /* الأرشيف صار أداة إشراف — لا يظهر لعامة الأعضاء */ if($('menuArchive')) $('menuArchive').hidden=!staff; if($('moreArchive')) $('moreArchive').hidden=!staff; /* المتجر — نسخة تجريبية، أدمن فقط (مو حتى المشرفين حالياً) */ if($('dwStoreBtn')) $('dwStoreBtn').style.display=''; if($('uName')) $('uName').textContent='أهلاً '+uname; // شريحة الاسم أعلى التطبيق + رأس القائمة الجانبية if(window.renderIdentity){ var idp=pr.data||{}; idp.username=uname; idp.id=user.id; sb.from('profiles').select('points').eq('id',user.id).single() .then(function(pt){ idp.points=(pt.data&&pt.data.points)||0; window.renderIdentity(idp); }, function(){ window.renderIdentity(idp); }); } if($('pfUser')){ $('pfUser').value=(pr.data&&pr.data.username)||fb||''; $('pfUser').disabled=true; } var hasPsn=!!((pr.data&&pr.data.psn_id||'').trim()); var lockPsn = hasPsn && !staff; if($('pfPsn')){ $('pfPsn').value=(pr.data&&pr.data.psn_id)||''; $('pfPsn').disabled=lockPsn; } if($('pfPsnLock')) $('pfPsnLock').hidden=!lockPsn; /* عضو جديد بلا آيدي سوني: تنبيه بارز — هو مفتاح كل شي بالمنصة */ var pw=$('psnWarn'); if(pw){ pw.hidden=hasPsn; if(!hasPsn) pw.innerHTML= '
'+ '🔑 خطوتك الأولى — آيدي السوني'+ '
'+ 'اكتب PSN ID في الخانة تحت واضغط «حفظ الملف». بدونه:
'+ '• ما تقدر تسجّل كلاناً ولا تكون رئيساً
'+ '• ما تقدر تطلق تحدياً فردياً
'+ '• ما يقدر أحد يضيفك على السوني وقت المباراة
'+ ''+ '
'; } var av=(pr.data&&pr.data.avatar_url)||''; if(!av && (md.avatar_url||md.picture)){ av=md.avatar_url||md.picture; sb.from('profiles').update({avatar_url:av}).eq('id',user.id).then(function(){},function(){}); } if($('pfAvatarInit')) $('pfAvatarInit').textContent=(uname||'?').slice(0,1).toUpperCase(); if(av){ if($('pfAvatarImg')){ $('pfAvatarImg').src=av; $('pfAvatarImg').style.display='block'; } if($('pfAvatarInit')) $('pfAvatarInit').style.display='none'; if($('pfAvatarDel')) $('pfAvatarDel').style.display=''; } else { if($('pfAvatarImg')) $('pfAvatarImg').style.display='none'; if($('pfAvatarInit')) $('pfAvatarInit').style.display=''; if($('pfAvatarDel')) $('pfAvatarDel').style.display='none'; } window._seeking=!!(pr.data&&pr.data.seeking_clan); if($('btnSeek')) $('btnSeek').textContent = window._seeking?'🔴 إيقاف البحث عن كلان':'🔎 فعّل البحث عن كلان'; if($('pfTitle')) $('pfTitle').value=(pr.data&&pr.data.title)||''; if($('pfBio')) $('pfBio').value=(pr.data&&pr.data.bio)||''; if($('pfTitleCustom')) $('pfTitleCustom').value=(pr.data&&pr.data.title)||''; sb.from('store_purchases').select('item_id,status,expires_at,store_items(perks)') .eq('profile_id',user.id).eq('status','active').order('purchased_at',{ascending:false}).limit(1) .then(function(spr){ var row=(spr.data&&spr.data[0])||null; var active=row && (!row.expires_at || new Date(row.expires_at)>new Date()); var perks=active && row.store_items && row.store_items.perks || {}; var hasCustomTitle=!!perks.custom_rank_name; if($('pfTitleCustomBox')) $('pfTitleCustomBox').hidden=!hasCustomTitle; if($('pfTitleAdminNote')) $('pfTitleAdminNote').hidden=hasCustomTitle; }); var isLinked=!!(pr.data&&pr.data.discord_id); if($('btnLinkDiscord')) $('btnLinkDiscord').style.display = isLinked?'none':''; if($('dcLinkP')) $('dcLinkP').innerHTML = isLinked ? '✅ حسابك مربوط بـDiscord'+(pr.data.discord_name?(' — '+esc(pr.data.discord_name)):'')+'
أوامر البوت جاهزة: /تحدي · /فردي · /كلاني' : 'اربط حسابك بـDiscord لتستخدم أوامر البوت (تحديات · بلاغات · رتبة كلانك تجيك تلقائياً).'; }); /* المؤرشف محفوظ باسمه لكنه لا يُقيّده — يقدر ينضم لكلان آخر */ /* ═══ من يقودُ كلاناً؟ ═══ كان يُقرأُ من `created_by` و`vice_id` وحدَهما، فالمنسّقُ لا كلانَ له في نظرِ الواجهةِ وإن كانت القاعدةُ تعرفُه. و`my_lead_clan` تجمعُ الأربعةَ (منشئ · رئيس · نائب · منسّق) وتستثني المؤرشف. */ sb.rpc('my_lead_clan',{p_user:user.id}).then(function(lr){ var cid=(lr&&lr.data)||null; if(!cid){ window._myClanId=null; window._myClanTag=null; if(window.renderClansCta) window.renderClansCta(); if(window.applyClanContext) window.applyClanContext(); return; } return sb.from('clans').select('id,tag').eq('id',cid).limit(1).then(function(cr){ if(cr&&cr.error){ if(window.applyClanContext) window.applyClanContext(); return; } var clan=cr.data&&cr.data[0]; window._myClanId=clan?clan.id:null; window._myClanTag=clan?String(clan.tag):''; if(false && $('myClanBtn')){ if(clan){ $('myClanH').textContent='🛡️ كلانك: CLAN '+String(clan.tag); $('myClanP').textContent='أدِر أعضاءك، النائب، الوصف واللوقو.'; $('myClanBtn').textContent='⚙️ إدارة الكلان'; $('myClanBtn').setAttribute('onclick',"go('manageclan')"); } } if(window.renderClansCta) window.renderClansCta(); if(window.applyClanContext) window.applyClanContext(); }); },function(){ if(window.applyClanContext) window.applyClanContext(); }); sb.from('clan_members').select('clan_id,role,clans(tag)').eq('profile_id',user.id).limit(1).then(function(mr){ if(mr&&mr.error){ if(window.applyClanContext) window.applyClanContext(); return; } var m=mr.data&&mr.data[0]; window._myMemberClanId=m?m.clan_id:null; window._myMemberRole=m?m.role:null; window._myMemberClanTag=(m&&m.clans&&String(m.clans.tag))||''; if(window.renderClansCta) window.renderClansCta(); if(window.applyClanContext) window.applyClanContext(); },function(){ if(window.applyClanContext) window.applyClanContext(); }); } })(); } /* حارس: لو ظهر «ضيف» ونحن مسجّلون فعلاً، أعد بناء الهوية */ setInterval(function(){ if(document.hidden) return; var cn=document.getElementById('chipName'); if(window._uid && cn && cn.textContent.trim()==='ضيف') refreshAuth(); }, 20000); var authMode='signup'; function setAuthMode(m){ authMode=m; var signup=(m==='signup'); $('authH').textContent = signup?'تسجيل جديد':'تسجيل دخول'; $('authP').textContent = signup?'أنشئ حسابك للانضمام للكلانات والتحديات':'ادخل باسم المستخدم أو بريدك وكلمة المرور'; $('acUser').hidden = false; $('acEmail').hidden = !signup; /* الغلاف هو ما يُخفى — لو أخفينا الحقل وحده بقي الغلاف بارتفاع صفر وطفت شارة أزرار السوني فوق حقل كلمة المرور. */ if($('acPsnWrap')) $('acPsnWrap').hidden = !signup; if($('acPsn')) $('acPsn').hidden = !signup; if($('acPsnNote')) $('acPsnNote').hidden = !signup; $('acUser').placeholder = signup ? 'اسم المستخدم (فريد لا يتكرّر)' : 'اسم المستخدم أو البريد'; $('btnAuth').textContent = signup?'✅ تسجيل جديد':'دخول'; $('discordWrap').hidden = false; if($('forgotPw')) $('forgotPw').hidden = signup; $('authSwitch').textContent = signup?'عندك حساب؟ تسجيل دخول':'جديد؟ أنشئ حساب'; $('authMsg').textContent=''; } if($('authSwitch')) $('authSwitch').addEventListener('click', function(){ setAuthMode(authMode==='signup'?'login':'signup'); }); if($('btnAuth')) $('btnAuth').addEventListener('click', function(){ var u=($('acUser').value||'').trim(), p=$('acPass').value, e=($('acEmail').value||'').trim(); var psn=window.fixPsn(($('acPsn')||{}).value); if($('acPsn')) $('acPsn').value=psn; /* يرى ما صُحّح قبل أن يُرفض */ if(authMode==='signup'){ var msg=$('authMsg'); msg.style.color='var(--gold)'; if(!u){ msg.textContent='اكتب اسم المستخدم'; return; } if(!e){ msg.textContent='اكتب البريد (يلزم لاستعادة كلمة المرور)'; return; } /* آيدي السوني هوية ثابتة — نتحقّق منه قبل إنشاء الحساب لا بعده */ if(!psn){ msg.style.color='var(--red)'; msg.textContent='🔑 آيدي السوني إجباري — هو هويتك بالمنصة'; if($('acPsn')) $('acPsn').focus(); return; } if(psn.length<3||psn.length>24){ msg.style.color='var(--red)'; msg.textContent='⚠️ آيدي السوني من 3 إلى 24 حرفاً'; return; } if(!/^[A-Za-z0-9_\-]+$/.test(psn)){ msg.style.color='var(--red)'; msg.textContent='⚠️ آيدي السوني: حروف وأرقام و _ - فقط'; return; } if(p.length<6){ msg.textContent='كلمة المرور 6 أحرف على الأقل'; return; } window.psnConfirm(psn, doSignup); return; function doSignup(){ msg.textContent='...التحقق من الاسم والآيدي'; /* اسم المستخدم هوية فريدة — نفحصه قبل إنشاء الحساب */ sb.rpc('username_available',{p_name:u}).then(function(ua){ if(ua&&ua.error){ msg.style.color='var(--red)'; msg.textContent=errAr(ua.error.message); return; } if(ua&&ua.data===false){ msg.style.color='var(--red)'; msg.textContent='⚠️ اسم المستخدم «'+u+'» محجوز — اختر غيره'; if($('acUser')) $('acUser').focus(); return; } return sb.rpc('psn_available',{p_psn:psn}).then(function(av){ if(av&&av.error){ msg.style.color='var(--red)'; msg.textContent=errAr(av.error.message); return; } if(av&&av.data===false){ msg.style.color='var(--red)'; msg.textContent='⚠️ آيدي السوني «'+psn+'» مسجّل لعضو آخر — لو هو حقك راجع الإدارة'; return; } msg.style.color='var(--gold)'; msg.textContent='...جاري التسجيل'; sb.auth.signUp({email:e,password:p,options:{data:{username:u, psn_id:psn}}}).then(function(s){ if(s.error){ msg.style.color='var(--red)'; if(/already registered|already exists/i.test(s.error.message||'')){ msg.textContent='البريد مسجّل مسبقاً — سجّل دخول.'; setAuthMode('login'); } else msg.textContent=errAr(s.error.message); return; } /* نثبّت الآيدي فور إنشاء الحساب (الدالة ترفض تغييره لاحقاً) */ sb.rpc('set_my_psn',{p_psn:psn}).then(function(){ msg.style.color='var(--green,#3ddc84)'; msg.textContent='✅ تم إنشاء حسابك!'; refreshAuth(); },function(){ msg.textContent='✅ تم إنشاء حسابك — احفظ آيدي السوني من «حسابي»'; refreshAuth(); }); }); }); },function(){ msg.style.color='var(--red)'; msg.textContent='تعذّر الاتصال'; }); } } else { if(!u||!p){ $('authMsg').textContent='اكتب اسم المستخدم أو البريد وكلمة المرور'; return; } var am=$('authMsg'); window._authBusy=true; /* لا تُعاد الصفحة ودخولُه جارٍ */ am.style.color='var(--gold)'; am.textContent='...جاري الدخول'; /* ═══ لا انتظارَ بلا نهاية ═══ كان المسار بلا مهلة: إن تعثّرت الشبكة أو تأخّرت الخدمة، بقيت «جاري الدخول» أبداً — ولا يعرف صاحبها أعطلٌ هو أم بطء، ولا يُقال له أعِد المحاولة. كل خطوةٍ الآن لها سقفٌ زمني ورسالةٌ تقول ما حدث، والزرّ يعود ليُضغط من جديد. */ var cap=function(pr, ms, what){ return Promise.race([ Promise.resolve(pr), new Promise(function(_,rej){ setTimeout(function(){ rej(new Error('TIMEOUT:'+what)); }, ms); }) ]); }; var fail=function(t){ window._authBusy=false; am.style.color='var(--red)'; am.textContent=t; }; var slow=setTimeout(function(){ if(/جاري الدخول/.test(am.textContent)) am.textContent='...الخدمة بطيئة الآن — ننتظر قليلاً'; }, 4000); cap(sb.rpc('email_for_login',{p_who:u}), 15000, 'name').then(function(r){ if(r&&r.error) throw new Error(r.error.message); var email=r&&r.data; if(!email){ clearTimeout(slow); fail('ما لقينا هذا الحساب — تأكّد من الاسم أو البريد'); return null; } return cap(sb.auth.signInWithPassword({email:email,password:p}), 20000, 'auth'); }).then(function(r2){ clearTimeout(slow); if(!r2) return; if(r2.error){ fail(/invalid login/i.test(r2.error.message||'') ? 'اسم المستخدم أو كلمة المرور خطأ' : errAr(r2.error.message)); return; } window._authBusy=false; am.style.color=''; am.textContent=''; refreshAuth(); }).catch(function(e){ clearTimeout(slow); var m=String((e&&e.message)||e); if(/^TIMEOUT:/.test(m)){ fail('⏱️ الخدمة لم تردّ خلال المهلة — اضغط «دخول» مرّةً أخرى.' + (/:auth$/.test(m) ? ' (تعثّر التحقّق من كلمة المرور)' : ' (تعثّر التعرّف على الاسم)')); } else if(/Failed to fetch|NetworkError|Load failed/i.test(m)){ fail('📶 انقطع الاتصال — تأكّد من الشبكة وأعد المحاولة.'); } else { fail(errAr(m)); } }); } }); /* ═══ تأكيد آيدي السوني ═══ نافذة وقورة بدل confirm المتصفح — الآيدي هوية ثابتة، فنعرضه كبيراً ليتفحّصه حرفاً حرفاً قبل الالتزام. */ window.psnConfirm=function(psn, onOk){ var old=document.getElementById('psnBack'); if(old) old.remove(); var b=document.createElement('div'); b.id='psnBack'; b.style.cssText='position:fixed;inset:0;z-index:96;background:rgba(2,6,12,.72);backdrop-filter:blur(3px);display:grid;place-items:center;padding:22px'; b.innerHTML= '
'+ '
'+ '
تأكيد آيدي السوني
'+ '
'+esc(psn)+'
'+ '
تأكد من PSN ID أنه صحيح
لأنه غير قابل للتعديل
'+ '
'+ ''+ ''+ '
'+ '
'+ ''; document.body.appendChild(b); function close(){ b.remove(); } b.addEventListener('click',function(e){ if(e.target===b) close(); }); document.getElementById('psnEdit').onclick=function(){ close(); if($('acPsn')) $('acPsn').focus(); }; document.getElementById('psnOk').onclick=function(){ close(); if(onOk) onOk(); }; }; /* ═══ الدخولُ بآبل — شرطُ المتجرِ 4.8 ═══ لا حيلةَ فيه ولا تحويلاتٍ يدويّة: سوبابيس تتولّى الجولةَ كلَّها. وما دامَ المزوّدُ غيرَ مفعَّلٍ في لوحةِ سوبابيس يردُّ خطأً معروفاً، فيُترجَمُ إلى عربيٍّ مفهومٍ بدلَ أن يُعرَضَ خاماً. */ window.appleAuth=function(msgEl){ var setMsg=function(t){ var e=$(msgEl); if(e) e.textContent=t; }; /* ═══ داخلَ تطبيقِ iOS: الورقةُ الأصليّةُ لا جولةُ متصفّح ═══ آبل ترفضُ إتمامَ الدخولِ في WebView مضمَّن، والقشرةُ تعملُ على capacitor://localhost فلا عنوانَ رجوعٍ لها أصلاً. فالمسارُ الأصليُّ هو الوحيدُ الذي يعمل: الإضافةُ تُعيدُ identityToken، وسوبابيس يتحقّقُ منه بمفاتيحِ آبلَ العامّة. والحارسُ يفحصُ وجودَ الإضافةِ نفسِها لا المنصّةَ وحدَها — قشرةٌ بُنيت قبلَ تركيبِها تسقطُ إلى مسارِ الويبِ بدلَ أن ترميَ استثناءً في وجهِ العضو. */ var _cap = window.Capacitor; var _swa = _cap && _cap.isNativePlatform && _cap.isNativePlatform() && _cap.Plugins && _cap.Plugins.SignInWithApple; if(_swa){ setMsg('...فتح Apple'); _swa.authorize({ clientId:'com.codar.app', redirectURI:'https://cod-ar.com/', scopes:'email name' }) .then(function(res){ var tok = res && res.response && res.response.identityToken; if(!tok){ setMsg('لم تصل هويّة Apple — أعد المحاولة'); return; } return sb.auth.signInWithIdToken({provider:'apple', token:tok}) .then(function(r){ if(r && r.error){ setMsg(errAr(r.error.message)); return; } setMsg(''); if(window.refreshAuth) refreshAuth(); }); }) .catch(function(e){ var m = String((e && (e.message||e.errorMessage)) || e); /* 1001 = ألغى العضوُ الورقةَ بنفسِه — لا رسالةَ خطأٍ على إلغاءٍ مقصود */ setMsg(/cancel|1001/i.test(m) ? '' : 'تعذّر الدخول بـApple — أعد المحاولة'); }); return; } setMsg('...التحويل إلى Apple'); sb.auth.signInWithOAuth({provider:'apple', options:{redirectTo: location.origin+'/'}}) .then(function(r){ if(r&&r.error){ setMsg(/not enabled|unsupported|provider/i.test(r.error.message||'') ? 'دخول Apple قيد التفعيل — استخدم البريد أو Discord الآن' : errAr(r.error.message)); } }, function(){ setMsg('تعذّر فتح صفحة Apple — تحقّق من اتصالك'); }); }; /* الدخول بديسكورد — يفتح تطبيق Discord بالجوال لو منزّل بدل صفحة الويب */ window.discordAuth=function(msgEl){ var setMsg=function(t){ var e=$(msgEl); if(e) e.textContent=t; }; setMsg('...التحويل إلى Discord'); var opts={provider:'discord', options:{ redirectTo: location.origin+'/', scopes:'identify email guilds.join', skipBrowserRedirect:true }}; sb.auth.signInWithOAuth(opts).then(function(r){ if(r&&r.error){ setMsg(/not enabled|unsupported|provider/i.test(r.error.message||'') ? 'دخول Discord قيد التفعيل — استخدم البريد الآن' : (errAr(r.error.message))); return; } var url=r&&r.data&&r.data.url; if(!url){ setMsg('تعذّر إنشاء رابط الدخول'); return; } try{ localStorage.setItem('cx_dcPending', String(Date.now())); }catch(e){} setMsg('📱 جاري فتح ديسكورد...'); var ua=navigator.userAgent||''; var mobile=/iphone|ipad|ipod|android/i.test(ua); var iOS=/iphone|ipad|ipod/i.test(ua); var inApp=(window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone===true; /* ═══ أين يُفتح تطبيق ديسكورد وأين لا يُفتح؟ ═══ بعد الموافقة، ديسكورد يفتح رابط العودة في المتصفح الافتراضي لا في نافذتنا. فالسؤال: هل يرى تطبيقنا الجلسة التي كُتبت هناك؟ · أندرويد: التطبيق المثبّت يعمل داخل كروم ويتشارك تخزينه — فيراها ✅ · آيفون مثبّت: تخزين التطبيق معزول عن سفاري تماماً — تضيع الجلسة ❌ فنُبقيه بنفس النافذة، ويرجع إلينا الدخول سليماً. · أي متصفح جوال: نفس النافذة ونفس التخزين ✅ */ var appOK = mobile && !(inApp && iOS); if(!appOK){ location.href=url; return; } var t=setTimeout(function(){ location.href=url; }, 4000); fetch('/api/oauth-url?u='+encodeURIComponent(url)) .then(function(x){ return x.json(); }) .then(function(d){ clearTimeout(t); if(!d||!d.url){ location.href=url; return; } window.showDiscordChoice(msgEl, d.deep||d.url, d.url); }) .catch(function(){ clearTimeout(t); location.href=url; }); }); }; /* بطاقة الاختيار — الروابط هنا حقيقية عشان iOS يفتح التطبيق */ window.showDiscordChoice=function(msgEl, deep, web){ var el=$(msgEl); if(!el){ location.href=deep; return; } var esc2=function(s){ return String(s).replace(/"/g,'"'); }; el.innerHTML= '
'+ '
📱 عندك تطبيق ديسكورد؟
اضغط الأزرق — يفتح التطبيق وتوافق بضغطة، بلا ما تكتب إيميلك ولا كلمة مرورك.
'+ '📱 افتح بتطبيق Discord'+ '🌐 أكمل بالمتصفح بدل التطبيق'+ '
'; var a=document.getElementById('dcApp'); if(a) a.addEventListener('click',function(){ // لو التطبيق مو منزّل ما ينفتح شي — نرجع للمتصفح تلقائياً var back=setTimeout(function(){ if(!document.hidden) location.href=web; }, 2200); document.addEventListener('visibilitychange',function(){ if(document.hidden) clearTimeout(back); },{once:true}); }); }; /* استئناف الدخول لو رجع المستخدم من تطبيق ديسكورد بلا جلسة */ (function(){ var tries=0; function resume(){ var pend=0; try{ pend=parseInt(localStorage.getItem('cx_dcPending')||'0',10); }catch(e){} if(!pend || (Date.now()-pend) > 10*60*1000) return; // انتهت المهلة sb.auth.getSession().then(function(r){ var s=r&&r.data&&r.data.session; if(s){ try{ localStorage.removeItem('cx_dcPending'); }catch(e){} if(window.refreshNow) window.refreshNow(); return; } if(++tries<4){ setTimeout(resume, 1500); return; } // ما وصلت الجلسة — نعرض زر استئناف واضح بدل ما يحتار var el=document.getElementById('authMsg'); if(el && !document.getElementById('dcResume')){ el.innerHTML='
'+ '⏳ ما اكتمل الدخول؟
لو وافقت بتطبيق ديسكورد ورجعت هنا بدون تسجيل، اضغط:'+ ''+ '
'; } }); } document.addEventListener('visibilitychange', function(){ if(!document.hidden){ tries=0; resume(); } }); window.addEventListener('pageshow', function(){ tries=0; resume(); }); setTimeout(resume, 2500); })(); if($('btnApple')) $('btnApple').addEventListener('click', function(){ window.appleAuth('authMsg'); }); if($('btnDiscord')) $('btnDiscord').addEventListener('click', function(){ window.discordAuth('authMsg'); }); if($('forgotPw')) $('forgotPw').addEventListener('click', function(){ var e=prompt('اكتب بريدك المسجّل لإرسال رابط استعادة كلمة المرور:'); if(!e){ return; } $('authMsg').textContent='...إرسال رابط الاستعادة'; sb.auth.resetPasswordForEmail(e.trim(), {redirectTo: location.origin+'/'}).then(function(r){ $('authMsg').textContent = (r&&r.error)?(errAr(r.error.message)):'📧 أرسلنا رابط إعادة التعيين على بريدك — افتحه وحدّث كلمتك.'; }); }); /* الجلسة تُستعاد من التخزين بعد أول فحص، فتبقى الترويسة «ضيف» بينما بقية الصفحات تعرف صاحبها. نعيد بناء الهوية مع كل تغيّر. */ /* ═══ بصمةُ الجهاز — الحسابُ يتبدّلُ والجهازُ لا يتبدّل ═══ الآيبي يُغيَّرُ ببروكسي في ثانية، **والفزعةُ تفعلُها.** لكنَّ رسمَ اللوحةِ ورقاقةَ الرسومِ والشاشةَ والمنطقةَ الزمنيّةَ لا تتبدّلُ بضغطةِ زرّ — فتجمعُ بصمةً تربطُ حسابَينِ على جهازٍ واحد. **وهي دليلُ اشتباهٍ لا دليلُ إدانة:** أخوانِ في بيتٍ واحدٍ يعطيانِ البصمةَ نفسَها. تُعرَضُ على الطاقمِ ليبتَّ، ولا يُعاقَبُ بها آليّاً أبداً. وثلاثةُ قيودٍ في تنفيذِها: **لا تُسجَّلُ في وضعِ الانتحال** — وإلّا نُسِبَ جهازُ المالكِ لكلِّ من دخلَ بحسابِه فامتلأَ الرادارُ بمطابقاتٍ كاذبةٍ من صنعِنا. **مرّةً واحدةً في الجلسة** — لا نداءَ مع كلِّ تجديدِ رمز. **والكتابةُ بدالّةٍ تكتبُ باسمِ الجالسِ لا بما يُرسِلُه** — فلا يُحقَنُ باسمِ غيرِه. */ window.codarTouchDevice = function(){ try{ if(window._fpDone) return; try{ if(sessionStorage.getItem('codar_imp_on')==='1' || sessionStorage.getItem('codar_imp_mode')) return; }catch(e){} if(!window.crypto || !crypto.subtle || !window.sb) return; window._fpDone = true; var p = []; try{ var c = document.createElement('canvas'); c.width=200; c.height=42; var g = c.getContext('2d'); g.textBaseline='top'; g.font='15px Arial'; g.fillStyle='#f60'; g.fillRect(0,0,72,18); g.fillStyle='#069'; g.fillText('COD-AR 2026', 2, 2); g.fillStyle='rgba(102,204,0,.72)'; g.fillText('COD-AR 2026', 4, 9); p.push(c.toDataURL().slice(-180)); }catch(e){ p.push('nc'); } try{ var gc = document.createElement('canvas').getContext('webgl'); var dx = gc && gc.getExtension('WEBGL_debug_renderer_info'); p.push(dx ? String(gc.getParameter(dx.UNMASKED_RENDERER_WEBGL)) : 'ng'); }catch(e){ p.push('ng'); } p.push(navigator.hardwareConcurrency||0, navigator.deviceMemory||0, screen.width+'x'+screen.height+'x'+(screen.colorDepth||0), window.devicePixelRatio||0, (Intl.DateTimeFormat().resolvedOptions().timeZone)||'', navigator.language||'', navigator.platform||'', navigator.maxTouchPoints||0); crypto.subtle.digest('SHA-256', new TextEncoder().encode(p.join('|'))) .then(function(h){ var fp = Array.prototype.map.call(new Uint8Array(h), function(b){ return ('0'+b.toString(16)).slice(-2); }).join('').slice(0,32); window.sb.rpc('touch_device', { p_fp: fp, p_ua: String(navigator.userAgent||'').slice(0,300) }); }, function(){}); }catch(e){} }; /* ═══ من جاءَ من صفحةٍ يرجعُ إليها ═══ أدواتُنا خارجَ التطبيقِ (ـ/zoom مثلاً) تطلبُ تسجيلَ الدخول، فيدخلُ صاحبُها **فيجدُ نفسَه في الرئيسةِ** ولا يعرفُ كيف يعود. فتحملُ وصلةُ الدخولِ `ret=` باسمِ الوجهة، ونردُّه إليها أوّلَ ما تثبتُ جلستُه. والوجهةُ من **قائمةِ أسماءٍ مغلقة** لا من النصِّ كما جاء: قبولُ مسارٍ حرٍّ من عنوانِ الصفحةِ بابُ تحويلٍ مفتوحٍ يُساقُ به الناسُ إلى مواقعَ أخرى بوصلةٍ تبدو وصلتَنا. والمفتاحُ يُمحى **قبلَ** التحويلِ لا بعدَه: لو بقيَ لدارت الصفحةُ على نفسِها كلَّما ثبتت الجلسةُ من جديد. ويعيشُ في `sessionStorage` لأنَّ الدخولَ بديسكوردَ يخرجُ من موقعِنا ويعودُ — فيضيعُ ما في العنوانِ ويبقى ما في المخزنِ ما دامَ التبويبُ مفتوحاً. */ (function(){ var DEST = { zoom: '/zoom' }; /* أسماءٌ مسموحةٌ لا مساراتٌ حرّة */ var KEY = 'codar_ret'; try { var q = new URLSearchParams(location.search).get('ret'); if (q && DEST[q]) sessionStorage.setItem(KEY, DEST[q]); } catch(e){} function goBack(){ var to; try { to = sessionStorage.getItem(KEY); } catch(e){ return; } if (!to) return; try { sessionStorage.removeItem(KEY); } catch(e){} if (location.pathname === to) return; /* نحن فيها أصلاً */ location.replace(to); } try { sb.auth.onAuthStateChange(function(ev, sess){ if (sess && sess.user && (ev==='SIGNED_IN' || ev==='INITIAL_SESSION')) setTimeout(goBack, 260); }); sb.auth.getSession().then(function(r){ if (r && r.data && r.data.session) setTimeout(goBack, 260); }, function(){}); } catch(e){} })(); sb.auth.onAuthStateChange(function(ev, sess){ if(window.codarBootStep && !window._bootIdent && !(sess && sess.user)) window.codarBootStep('auth'); if(ev==='SIGNED_IN' || ev==='TOKEN_REFRESHED' || ev==='INITIAL_SESSION' || ev==='USER_UPDATED'){ var uid=sess&&sess.user&&sess.user.id; if(uid && uid!==window._uid) refreshAuth(); else if(uid && !document.getElementById('chipName')) refreshAuth(); else if(uid){ // نفس المستخدم — نتأكد أن الشريحة ما زالت تعرض «ضيف» var cn=document.getElementById('chipName'); if(cn && cn.textContent.trim()==='ضيف') refreshAuth(); } } /* شبكةُ أمانٍ ثانية: أوّل جلسةٍ تصل بعد الإقلاع قد تكون بعد قراءة الصلاحيات — فنُعيد قراءتها معها ولو لم تتبدّل الهوية. */ if(ev==='INITIAL_SESSION' || ev==='SIGNED_IN'){ if(sess&&sess.user){ if(window.codarTouchDevice) window.codarTouchDevice(); clearTimeout(window._apT2); window._apT2=setTimeout(function(){ if(window.applyPerms) window.applyPerms(); }, 700); } } if(ev==='SIGNED_OUT'){ window._uid=null; if(window.renderIdentity) window.renderIdentity(null); } if(ev==='PASSWORD_RECOVERY'){ var np=prompt('اكتب كلمة المرور الجديدة (6 أحرف على الأقل):'); if(np && np.length>=6){ sb.auth.updateUser({password:np}).then(function(r){ alert((r&&r.error)?(errAr(r.error.message)):'✅ تم تغيير كلمة المرور — استخدمها بالدخول.'); refreshAuth(); }); } else if(np!==null){ alert('كلمة المرور قصيرة (6 أحرف على الأقل)'); } } }); setAuthMode('signup'); if($('btnLogout')) $('btnLogout').addEventListener('click', function(){ sb.auth.signOut().then(refreshAuth); }); window.applyClantThemeColor=function(){ var hex=document.getElementById('mgColorHex'); var pick=document.getElementById('mgColorPick'); var color=(hex&&hex.value.trim())||(pick&&pick.value)||'#FFD700'; if(!color.match(/^#[0-9a-fA-F]{3,6}$/)){ alert('لون غير صحيح — استخدم صيغة مثل #FFD700'); return; } if(!window._myClanId){ alert('ما عندك كلان'); return; } sb.rpc('clan_set_theme_color',{p_clan_id:window._myClanId, p_color:color}).then(function(r){ if(r.error||!r.data||!r.data.ok){ alert('تعذّر: '+(r.error?r.error.message:(r.data&&r.data.why)||'خطأ')); return; } toast('✅ تم تغيير لون الكلان'); }); }; document.getElementById('mgColorPick')&&document.getElementById('mgColorPick').addEventListener('input',function(){ var hex=document.getElementById('mgColorHex'); if(hex) hex.value=this.value; }); /* ═══ إيصال الدفع ═══ */ window.openReceipt=function(pid){ if(!pid){ toast('لا توجد عملية'); return; } sb.rpc('get_receipt',{p_purchase_id:pid}).then(function(r){ var d=(r&&r.data)||{}; if(r.error||!d.ok){ toast(d.why==='unpaid'?'لا إيصال لعملية غير مكتملة' : d.why==='not_yours'?'هذا الإيصال ليس لك' : 'تعذّر جلب الإيصال'); return; } var f=function(x){ return x?new Date(x).toLocaleDateString('ar-SA'):'—'; }; var ft=function(x){ return x?new Date(x).toLocaleString('ar-SA'):'—'; }; var row=function(k,v){ return ''+k+''+ ''+v+''; }; var html=''+ ''+ 'إيصال '+d.no+'
'+ '
COD·AR
'+ '
إيصال دفع
'+ '
'+d.no+'
'+ ''+ row('المشترك', esc(d.buyer))+ (d.psn?row('آيدي السوني',''+esc(d.psn)+''):'')+ (d.clan?row('الكلان',''+esc(d.clan)+''):'')+ row('الباقة', esc(d.item))+ row('المدة', d.days+' يوم')+ row('تاريخ الدفع', ft(d.paid_at))+ row('ينتهي في', f(d.expires_at))+ row('مرجع الدفع',''+esc(d.ref)+'')+ '
'+ '
المبلغ المدفوع'+ ''+d.amount+' ر.س
'+ '
شكراً لدعمك منصة COD·AR
'+ 'لأي استفسار عن هذا الإيصال، افتح تذكرة من «التواصل مع الإدارة»
'+ ''+ '
'; var w=window.open('','_blank'); if(!w){ toast('اسمح بالنوافذ المنبثقة لعرض الإيصال'); return; } w.document.write(html); w.document.close(); }); }; window.askRefund=function(){ var pid=window._mySubPurchaseId; if(!pid){ toast('لا يوجد اشتراك قابل للاسترداد'); return; } var why=prompt('اكتب سبب طلب الاسترداد:\n\nالاسترداد متاح خلال 3 أيام من الشراء، وتراجعه الإدارة عبر تذكرة.',''); if(why===null) return; why=String(why).trim(); if(why.length<10){ toast('اكتب سبباً واضحاً (10 أحرف على الأقل)'); return; } sb.rpc('request_refund',{p_purchase_id:pid, p_reason:why}).then(function(r){ var d=r&&r.data; if(r.error||!d||!d.ok){ var w=d&&d.why; if(w==='too_late') toast('⏰ انتهت مهلة الاسترداد (3 أيام)'); else if(w==='already_requested') toast('عندك طلب استرداد قيد المراجعة'); else if(w==='not_refundable') toast('هذا الاشتراك غير قابل للاسترداد'); else toast('تعذّر إرسال الطلب'); return; } toast('✅ أُرسل طلب الاسترداد — تابعه من «التواصل مع الإدارة»'); }); }; window.loadMySubscription=function(){ if(!window._uid) return; var box=document.getElementById('mySubBox'); var none=document.getElementById('noSubBox'); if(!box||!none) return; sb.rpc('get_my_subscriptions').then(function(r){ var L=(r&&r.data)||[]; if(!L.length){ box.hidden=true; none.hidden=false; return; } none.hidden=true; box.hidden=false; window._mySubs=L; window._mySubPurchaseId=L[0].purchase_id; box.innerHTML=L.map(function(x){ var dia=(x.item_id||'').indexOf('diamond')>-1; var tc=dia?'#b9f2ff':'#FFD700'; var bc=dia?'rgba(185,242,255,.35)':'rgba(255,215,0,.35)'; var bg=dia?'rgba(185,242,255,.08)':'rgba(255,215,0,.08)'; var lbl=dia?'DIAMOND':'GOLD'; var exp=''; if(x.expires_at){ var dt=new Date(x.expires_at); var dl=Math.ceil((dt-Date.now())/86400000); exp='ينتهي '+dt.toLocaleDateString('ar-SA')+' ('+(dl>0?dl+' يوم':'اليوم')+')'; } else exp='لا ينتهي'; var canRefund=false; if(x.purchased_at) canRefund=((Date.now()-new Date(x.purchased_at).getTime())/86400000)<=3; return '
'+ '
'+ ''+esc(x.item_name||x.item_id)+''+ ''+lbl+''+ '
'+ (x.clan_tag?'
🛡️ '+esc(x.clan_tag)+'
':'')+ '
'+exp+'
'+ '
'+ ''+ (x.has_receipt?'':'')+ (canRefund?'':'')+ '
'+ '
'; }).join(''); }); }; window.askRefundFor=function(pid){ if(!pid) return; var why=prompt('اكتب سبب طلب الاسترداد:\n\nالاسترداد متاح خلال 3 أيام من الشراء، وتراجعه الإدارة عبر تذكرة.',''); if(why===null) return; why=String(why).trim(); if(why.length<10){ toast('اكتب سبباً واضحاً (10 أحرف على الأقل)'); return; } sb.rpc('request_refund',{p_purchase_id:pid, p_reason:why}).then(function(r){ var d=(r&&r.data)||{}; if(r.error||!d.ok){ var w=d.why; toast(w==='too_late'?'⏰ انتهت مهلة الاسترداد (3 أيام)' : w==='already_requested'?'عندك طلب استرداد قيد المراجعة' : w==='not_refundable'?'هذا الاشتراك غير قابل للاسترداد' : 'تعذّر إرسال الطلب'); return; } toast('✅ أُرسل طلب الاسترداد — تابعه من «التواصل مع الإدارة»'); }); }; if($('btnSaveTitle')) $('btnSaveTitle').addEventListener('click', function(){ var val=($('pfTitleCustom')&&$('pfTitleCustom').value||'').trim(); var msg=$('pfTitleMsg'); if(msg){ msg.style.color='var(--tx3)'; msg.textContent='...جارٍ الحفظ'; } if(!val){ if(msg){ msg.style.color='var(--red)'; msg.textContent='اكتب لقباً أولاً'; } return; } sb.rpc('set_my_custom_title',{p_title:val}).then(function(r){ if(!msg) return; if(r.error || !r.data || !r.data.ok){ var why=(r.data&&r.data.why)||''; var m=why==='no_subscription'?'اشتراكك ما يشمل هذي الميزة': why==='perk_not_included'?'باقتك الحالية ما تدعم اللقب المخصص': why==='too_long'?'اللقب طويل — بحد أقصى 24 حرف': 'تعذّر الحفظ'; msg.style.color='var(--red)'; msg.textContent=m; return; } msg.style.color='#3ddc84'; msg.textContent='✅ تم حفظ اللقب'; }); }); if($('btnSaveProfile')) $('btnSaveProfile').addEventListener('click', function(){ window.authUser().then(function(res){ var user=res&&res.data&&res.data.user; if(!user) return; var patch={}; var newPsn = ($('pfPsn') && !$('pfPsn').disabled) ? window.fixPsn($('pfPsn').value) : ''; if(newPsn && $('pfPsn')) $('pfPsn').value=newPsn; if(newPsn){ sb.rpc('set_my_psn',{p_psn:newPsn}).then(function(pr2){ var d=pr2&&pr2.data; if(pr2&&pr2.error){ $('pfMsg').textContent=errAr(pr2.error.message); return; } if(d&&d.ok){ if($('pfPsn')) $('pfPsn').disabled=true; if($('pfPsnLock')) $('pfPsnLock').hidden=false; $('pfMsg').textContent='✅ ثُبّت آيدي السوني'; if(window.reloadProfile) window.reloadProfile(); if(window.loadInvites) window.loadInvites(); return; } var w=(d&&d.why)||''; $('pfMsg').textContent = w==='taken' ? '⚠️ هذا الآيدي مسجّل لعضو آخر' : w==='locked' ? ('🔒 آيديك مثبّت مسبقاً ('+(d.psn||'')+') — التعديل عبر الإدارة') : w==='chars' ? '⚠️ حروف وأرقام و _ - فقط' : w==='length' ? '⚠️ من 3 إلى 24 حرفاً' : 'تعذّر الحفظ'; },function(){ $('pfMsg').textContent='تعذّر الاتصال'; }); } /* اللقب لا يُرسَل — القاعدة ترفضه من غير الطاقم، وإرساله يُفشل الحفظ كلَّه */ if($('pfBio')) patch.bio=(($('pfBio').value||'').trim().slice(0,160))||null; /* الآيدي يُحفظ بدالته أعلاه — هنا بقية الحقول فقط */ if(Object.keys(patch).length===0){ if(!newPsn) $('pfMsg').textContent='لا يوجد تغيير — اسم المستخدم ثابت'; return; } $('pfMsg').textContent='...حفظ'; sb.from('profiles').update(patch).eq('id',user.id).then(function(r){ if(r.error){ $('pfMsg').textContent=errAr(r.error.message); return; } if(!newPsn) $('pfMsg').textContent='✅ تم الحفظ'; }); }); }); /* ═══ دعوات الانضمام ═══ لا يُضاف أحد لكلان بآيدي سونيه إلا بموافقته. */ window.loadInvites=function(){ var box=$('clInvites'); if(!box || !window._uid){ if(box) box.hidden=true; return; } sb.from('clan_invites').select('id,clan_id,psn,created_at,clans(tag,name,type)') .eq('profile_id',window._uid).eq('status','pending') .order('created_at',{ascending:false}).then(function(r){ var rows=r.data||[]; if(!rows.length){ box.hidden=true; box.innerHTML=''; return; } box.hidden=false; box.innerHTML=rows.map(function(v){ var tg=String((v.clans&&v.clans.tag)||'؟'); return '
'+ '📨 دعوة انضمام — CLAN '+esc(tg)+''+ '
'+ 'رئيس الكلان ضمّ آيديك '+esc(v.psn||'')+'. ما تنضم إلا بموافقتك.'+ '
'+ '
'+ ''+ ''+ '
'; }).join(''); },function(){ box.hidden=true; }); }; document.addEventListener('click', function(e){ var t=e.target.closest?e.target.closest('[data-inv-ok],[data-inv-no]'):null; if(!t) return; var ok=t.getAttribute('data-inv-ok'), no=t.getAttribute('data-inv-no'); var id=ok||no; if(!id) return; t.disabled=true; sb.rpc('respond_clan_invite',{p_invite:id, p_accept:!!ok}).then(function(r){ var d=r&&r.data; if(r&&r.error){ /* لا نسرّب نص قاعدة البيانات للاعب */ var em=String(r.error.message||''); toast(/duplicate|unique|name_uniq/i.test(em) ? '⚠️ آيدي السوني حقك مسجّل باسم عضو آخر — راجع الإدارة' : ('تعذّرت العملية: '+em.slice(0,60))); t.disabled=false; return; } if(d&&d.ok){ toast(d.act==='joined' ? ('🎉 انضممت إلى CLAN '+String(d.clan||'').toUpperCase()) : 'رفضت الدعوة'); window.loadInvites(); if(window.reloadProfile) window.reloadProfile(); if(window.refreshNow) window.refreshNow(); return; } t.disabled=false; var w=(d&&d.why)||''; toast(w==='in_clan' ? '⚠️ أنت عضو في كلان — اخرج منه أولاً' : w==='full' ? '⚠️ الكلان مكتمل' : w==='done' ? 'الدعوة انتهت' : w==='no_psn' ? '🔑 احفظ آيدي السوني في حسابك أولاً' : w==='psn_taken' ? '⚠️ آيديك مسجّل في كلان آخر — لازم يُشال منه أولاً' : w==='name_taken' ? ('⚠️ الاسم «'+(d.name||'')+'» محجوز لعضو آخر — راجع الإدارة') : w==='clan_busy' ? '⚔️ الكلان في تحدٍّ جارٍ — ما تقدر تنضم أثناء المباراة، جرّب بعد ما تخلص' : 'تعذّرت العملية'); },function(){ t.disabled=false; toast('تعذّر الاتصال'); }); }); /* خروج العضو من كلانه بإرادته */ window.leaveClan=function(){ if(!confirm('تبي تخرج من كلانك؟\n\nتنشال من قائمة الأعضاء، وتقدر تنضم لكلان ثاني بعدها.')) return; sb.rpc('leave_clan').then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(d&&d.ok){ var _lcCid=window._myMemberClanId, _lcName=window._myUsername||''; if(_lcCid&&_lcName) sb.from('transfers').insert({player_name:_lcName,from_clan:_lcCid,to_clan:null,status:'pending',requested_by:window._uid}).then(function(){},function(){}); toast('خرجت من CLAN '+String(d.clan||'').toUpperCase()); if(window.reloadProfile) window.reloadProfile(); if(window.refreshNow) window.refreshNow(); return; } var w=(d&&d.why)||''; toast(w==='is_leader' ? '👑 أنت رئيس الكلان — سلّم الرئاسة لعضو آخر أولاً' : w==='not_member'? 'ما أنت عضو في كلان' : 'تعذّرت العملية'); },function(){ toast('تعذّر الاتصال'); }); }; /* الخانة داخل صفٍّ مطويّ: نفتحه ثم ننزل إليها ونضيئها */ window.focusPsn=function(){ if(window.openEditProfile) window.openEditProfile(); setTimeout(function(){ var e=document.getElementById('pfPsn'); if(!e) return; e.scrollIntoView({behavior:'smooth',block:'center'}); try{ e.focus(); }catch(_){} e.style.transition='box-shadow .25s'; e.style.boxShadow='0 0 0 3px rgba(255,204,0,.35)'; setTimeout(function(){ e.style.boxShadow=''; }, 1600); }, 260); }; /* الضغط على صورتك واسمك يفتح محرّر ملفك — لا صفحتك العامة */ /* طلب التحدي: للرئيس ونائبه — ومن سواهما يعرف السبب فوراً */ /* ═══ حاجزُ الموافقة على شرط عدم إشراك لاعب غير مسجّل ═══ يظهرُ مرّةً لرئيس/نائبِ الكلان، وتُحفظُ الموافقةُ فلا تتكرّر. القرارُ يُقَرُّ به بمربّعٍ وزرّ لا يُقرأُ عابراً. */ (function(){ var K='cx_unregRuleOk', pending=null; function fin(){ try{ localStorage.setItem(K,'1'); }catch(e){} var m=document.getElementById('unregModal'); if(m) m.hidden=true; var f=pending; pending=null; if(f) try{ f(); }catch(e){} } window.unregGate=function(next){ var ok=false; try{ ok=localStorage.getItem(K)==='1'; }catch(e){} if(ok){ if(next) next(); return; } pending=next||null; var m=document.getElementById('unregModal'); if(!m){ if(next) next(); return; } var c=document.getElementById('unregChk'), b=document.getElementById('unregOk'); if(c) c.checked=false; if(b){ b.disabled=true; b.style.opacity='.5'; } m.hidden=false; }; document.addEventListener('change',function(e){ if(e.target && e.target.id==='unregChk'){ var b=document.getElementById('unregOk'); if(b){ b.disabled=!e.target.checked; b.style.opacity=e.target.checked?'':'.5'; } } }); document.addEventListener('click',function(e){ if(e.target && e.target.id==='unregOk' && !e.target.disabled) fin(); }); })(); window.goNewChallenge=function(){ if(window._perms && window._perms.act_challenge!==true){ toast('🔒 طلب التحدي لرئيس الكلان ونائبه والمنسق — تقدر تتحدّى 1v1 من «الأفراد»'); return; } window.unregGate(function(){ go('newch'); }); }; /* الزر العائم للأفراد يفتح صفّ التحدي */ /* نموذج الطلب يُطوى ويُفتح تحت الزرّ نفسه */ window.openDuelForm=function(){ if(!window._uid){ go('account'); return; } var b=document.getElementById('soDuelBody'); if(!b) return; b.hidden=!b.hidden; if(b.hidden) return; setTimeout(function(){ b.scrollIntoView({behavior:'smooth',block:'center'}); var i=document.getElementById('duelOpp'); if(i) try{ i.focus(); }catch(_){} }, 200); }; window.openEditProfile=function(){ var r=document.querySelector('.lrow[data-open="acProfileBody"]'); var b=document.getElementById('acProfileBody'); if(!r||!b) return; if(b.hidden) r.click(); setTimeout(function(){ r.scrollIntoView({behavior:'smooth',block:'center'}); }, 90); }; /* ═══ بطاقة هوية «حسابي» ═══ تُملأ من ملفك مرة واحدة */ window.fillAccountId=function(){ if(!window._uid) return; sb.from('profiles').select('username,psn_id,avatar_url,points,wins').eq('id',window._uid).single() .then(function(r){ var p=(r&&r.data)||{}; var nm=$('acIdName'), ps=$('acIdPsn'), av=$('acIdAv'), ini=$('acIdInit'); if(nm) nm.textContent=p.username||'—'; if(ps) ps.textContent=p.psn_id?('🎮 '+p.psn_id):''; if(av&&ini){ if(p.avatar_url){ av.src=p.avatar_url; av.style.display=''; ini.style.display='none'; } else { av.style.display='none'; ini.style.display=''; ini.textContent=String(p.username||'?').slice(0,1).toUpperCase(); } } var pt=$('acStPts'), w=$('acStWin'); if(pt) pt.textContent=(p.points||0).toLocaleString('en'); if(w) w.textContent=(p.wins||0); /* حالة ديسكورد وحالة البحث عن كلان تظهران في صفّيهما */ var ds=$('acDcState'); if(ds) sb.from('profiles').select('discord_id').eq('id',window._uid).single().then(function(d){ var ok=!!(d&&d.data&&d.data.discord_id); ds.textContent=ok?'مربوط ✓':'غير مربوط'; ds.className='lrow__v '+(ok?'on':'off'); },function(){}); },function(){}); }; window.reloadProfile=refreshAuth; refreshAuth(); // ===== الكلانات (حفظ وقراءة حقيقية) ===== function esc(s){return (s==null?'':(''+s)).replace(/[<>&"]/g,function(c){return {'<':'<','>':'>','&':'&','"':'"'}[c];});} function mTitle(t){ return t?'🏷️ '+esc(t)+'':''; } /* آيدي السوني هو الاسم — لا اسم المستخدم. القاعدة نفسها بالمنصة كلها */ function mName(m){ return (m.psn||'').trim() || (m.profiles&&(m.profiles.psn_id||'').trim()) || m.member_name || (m.profiles&&m.profiles.username) || 'عضو'; } function mPsn(m){ return (m.psn||(m.profiles&&m.profiles.psn_id)||'').trim(); } /* الاسم بابٌ إلى صاحبه — من يقرأ كشف الكلان يريد أن يعرف من فيه. ومن لا حساب له (صفٌّ بلا profile_id) لا باب له، فلا نُوهم بضغطة. */ function mLink(m, inner, extra){ if(!m || !m.profile_id) return inner; return ''+inner+''; } // ترتيب هرمي: القيادة (رئيس ← نائب) معزولة، وتحتها الأعضاء function hierarchyHTML(rows, acts){ acts = acts || function(){ return ''; }; var L=rows.filter(function(m){return m.role==='leader';}), V=rows.filter(function(m){return m.role==='vice';}), C=rows.filter(function(m){return m.role==='coordinator';}), M=rows.filter(function(m){return m.role!=='leader'&&m.role!=='vice'&&m.role!=='coordinator';}); var h=''; if(L.length||V.length){ h+='
'; h+='
⬥ القيادة ⬥
'; L.forEach(function(m){ var nm=mName(m); h+='
'+ mLink(m,'
👑
'+ ''+esc(nm.slice(0,2).toUpperCase())+'
')+ '
'+mLink(m,esc(nm),'text-decoration:underline;text-decoration-color:rgba(255,215,0,.35);text-underline-offset:3px')+'
'+ '
رئيس الكلان'+mTitle(m.title)+'
'+ acts(m)+'
'; }); if(L.length&&V.length) h+='
'; V.forEach(function(m){ var nm=mName(m); h+='
'+ '
🎖️
'+ mLink(m,''+esc(nm.slice(0,2).toUpperCase())+'')+ '
'+mLink(m,esc(nm),'text-decoration:underline;text-decoration-color:rgba(56,182,255,.35);text-underline-offset:3px')+'
'+ '
نائب الرئيس'+mTitle(m.title)+'
'+ acts(m)+'
'; }); h+='
'; } if(C.length){ h+='
📋 منسقو التحديات ('+C.length+')
'; var coordBadge='📋 منسق'; h+=C.map(function(m){ var nm=mName(m); var psn=mPsn(m); return '
'+mLink(m,''+esc(nm.slice(0,1).toUpperCase())+'')+ '
'+window.dot(m.profile_id)+ mLink(m,esc(nm),'text-decoration:underline;text-decoration-color:rgba(255,204,0,.3);text-underline-offset:3px')+''+coordBadge+mTitle(m.title)+(psn?''+esc(psn)+'':'')+'
'+acts(m)+'
'; }).join(''); h+='
'; } h+='
🎮 الأعضاء ('+M.length+')
'; var pBadge='🎮 لاعب'; var noPsn='بلا PSN'; h+= M.length? M.map(function(m){ var nm=mName(m); var psn=mPsn(m); return '
'+mLink(m,''+esc(nm.slice(0,1).toUpperCase())+'')+ '
'+window.dot(m.profile_id)+ mLink(m,esc(nm),'text-decoration:underline;text-decoration-color:rgba(56,182,255,.3);text-underline-offset:3px')+''+pBadge+mTitle(m.title)+(psn?''+esc(psn)+'':noPsn)+'
'+acts(m)+'
'; }).join('') : '
لا أعضاء بعد
'; return h; } window.loadClanDetail=function(clanId){ if(!clanId) return; sb.from('clans').select('*').eq('id',clanId).single().then(function(r){ var c=r.data; if(!c) return; if($('cdInit')) $('cdInit').textContent=(c.tag||'?').slice(0,4); /* ═══ الترويسةُ هويّةُ الكلانِ لا رتبتُه ═══ وضعتُ الشارةَ هنا فحجبتْ لوقو الكلانِ — والرتبةُ ظاهرةٌ في البطاقةِ تحتَها مباشرةً فلا تحتاجُ موضعَين. فرجعَتِ الهويّةُ إلى مكانِها، واللوقو أولى من الحرفَين إن رفعَه أهلُه، والرتبةُ شارةٌ صغيرةٌ في الزاويةِ تُعلِّمُ ولا تُزاحِم. */ (function(){ /* ═══ الحاويةُ تبقى ومحتواها يتبدّل ═══ كان يُستبدَلُ العنصرُ نفسُه بـ`outerHTML`، فيختفي `cdInit`؛ وفي الزيارةِ الثانيةِ لا يُوجَدُ فيخرجُ قبلَ الرسم — فيبقى لوقو الكلانِ الأوّل. فصارتْ حاويةٌ ثابتةٌ تُملأُ كلَّ مرّة. */ var hx=document.getElementById('cdFace'); if(!hx) return; var wrap='
'; var face = c.logo_url ? '' : '
'+esc((c.tag||'?').slice(0,4))+'
'; hx.innerHTML = wrap + face + /* ظلٌّ مزدوج: قريبٌ حادٌّ يفصلُها عن اللوقو، وبعيدٌ ناعمٌ يرفعُها فوقَه. بلا هذا تُقرأُ الشارةُ جزءاً من اللوقو لا شيئاً موضوعاً عليه. */ '
'; if(window.RANKS_ON && window.tierBadge){ window.rankTiers(function(){ var pin=document.getElementById('cdRankPin'); if(pin) pin.innerHTML=window.tierBadge(c.points,44,{still:true}); }); } })(); if($('cdName')) $('cdName').innerHTML='CLAN '+esc(c.tag)+' | COD-AR'; /* تطبيق لون الكلان المخصص — نُعيد الضبط أولاً */ var cdName=document.getElementById('cdName'); var cdRankPin=document.getElementById('cdRankPin'); if(cdName) cdName.style.color=''; if(cdRankPin) cdRankPin.style.filter=''; if(c.theme_color){ if(cdName) cdName.style.color=c.theme_color; if(cdRankPin) cdRankPin.style.filter='drop-shadow(0 0 6px '+c.theme_color+')'; } /* فحص هل رئيس الكلان يبث */ sb.from('clan_members').select('profile_id').eq('clan_id',clanId).in('role',['leader','vice']).then(function(mr){ var leaderIds=(mr.data||[]).map(function(m){return m.profile_id;}); if(!leaderIds.length) return; sb.from('streamers').select('id,platform,platform_username,display_name,is_live,stream_title,viewer_count').in('profile_id',leaderIds).eq('is_live',true).then(function(sr){ var live=sr.data&&sr.data[0]; var liveTag=document.getElementById('cdLiveTag'); if(!live){if(liveTag)liveTag.remove(); return;} var pn=live.platform==='kick'?'Kick':live.platform==='twitch'?'Twitch':'TikTok'; if(!liveTag){ liveTag=document.createElement('div'); liveTag.id='cdLiveTag'; liveTag.style.cssText='margin:8px 0;display:flex;align-items:center;gap:8px;flex-wrap:wrap'; var cdName=$('cdName'); if(cdName&&cdName.parentNode) cdName.parentNode.insertBefore(liveTag,cdName.nextSibling); } liveTag.innerHTML=''+ ''+ 'يبث الآن على '+pn+ (live.viewer_count?' · 👁 '+live.viewer_count:'')+ ''; }); }); var type=typeBadge(c.type); var stt=c.status==='ratified'?'✔ معتمد':'بانتظار الترسيم'; var fire=(c.streak&&c.streak>=2)?'🔥 ستريك '+c.streak+'':''; if($('cdBadges')) $('cdBadges').innerHTML=type+stt+fire; sb.from('public_clan_warnings').select('level').eq('clan_id',c.id).limit(1).then(function(wr){ var w=(wr.data&&wr.data[0])||null; if(!w || !$('cdBadges')) return; var wLabel = w.level==='first'?'⚠️ إنذار أول':(w.level==='second'?'⚠️ إنذار ثانٍ':'🚨 إنذار نهائي'); var wColor = w.level==='final' ? 'background:rgba(255,80,70,.15);border:1px solid rgba(255,80,70,.5);color:#ff6b5e' : 'background:rgba(255,140,60,.15);border:1px solid rgba(255,140,60,.5);color:#ff8c3c'; $('cdBadges').innerHTML += ''+wLabel+''; }); /* ═══ بطاقةُ الرتبةِ وكم بقيَ للقادمة ═══ الرقمُ وحدَه لا يُحرّك: «٣٧٣ نقطة» لا تقولُ شيئاً. أمّا «باقٍ ٢٧ نقطةً لمحترف — وفوزٌ واحدٌ يعطيكَ نحوَ ٥٠» فتقولُ للرئيسِ ما يفعلُه الليلة. فالبطاقةُ تجمعُ الثلاثةَ: أينَ أنت، وكم بقي، وبمَ تقطعُه. */ (function(){ var box=$('cdRank'); if(!box || !window.RANKS_ON) return; window.rankTiers(function(T){ if(!T.length){ box.innerHTML=''; return; } var p=Math.max(0,c.points||0), cur=T[0], nxt=null; for(var i=0;i=T[i].min_points) cur=T[i]; else { nxt=T[i]; break; } } var body; if(nxt){ var span=nxt.min_points-cur.min_points, done=p-cur.min_points; var pct=Math.max(0,Math.min(100,Math.round(done/Math.max(span,1)*100))); var left=nxt.min_points-p; body='
'+ 'باقٍ '+left.toLocaleString('en')+' نقطة لرتبة '+esc(nxt.name)+''+ ''+pct+'%
'+ '
'+ '
'+ '
⚔️ فوزٌ واحدٌ على كلانٍ مثلك ≈ 50 نقطة
'; } else { body='
🏆 بلغتَ القمّة — احتفظ بها
'+ '
لا رتبةَ بعدها. الخسارةُ وحدَها تُنزِلك.
'; } box.innerHTML= '
'+ '
'+ window.tierBadge(p,128)+ '
'+ '
'+esc(cur.name)+'
'+ '
'+esc(cur.headline||'')+'
'+ '
'+p.toLocaleString('en')+' نقطة
'+ '
'+ '
'+ body + ''+ '
'; window.loadClanAwards(c.id, p); window.rkCrown(c.id); }); })(); var total=(c.wins||0)+(c.losses||0), rate=total?Math.round((c.wins||0)/total*100):0; if($('cdStats')){ $('cdStats').className='stx'; $('cdStats').innerHTML= '
'+(c.points||0).toLocaleString('en')+'نقطة
'+ '
'+(c.wins||0)+'فوز
'+ '
'+(c.losses||0)+'خسارة
'+ '
'+rate+'%معدل
'; } /* وصف الكلان: يكتبه رئيسه في الإدارة — ومكانه صفحته لا القوائم وحدها */ if($('cdAbout')){ var ab=String(c.description||'').trim(); var rn=String(c.recruit_note||'').trim(); var h=''; /* ═══ الوصفُ تحتَ الاسمِ بلا عنوان ═══ كان صندوقاً معنوَناً بـ«عن الكلان» أسفلَ الإحصاءِ — **والعنوانُ زائدٌ لأنَّ الموضعَ يقولُه**: نصٌّ تحتَ اسمِ الكلانِ مباشرةً لا يُظنُّ شيئاً غيرَ تعريفِه. وصعودُه يوفّرُ صندوقاً كاملاً ويجعلُ الكلانَ يُعرَّفُ بنفسِه قبلَ أن يُعرَّفَ بأرقامِه. ويُطوى في ثلاثةِ أسطرٍ إن طال: **الرأسُ تعريفٌ لا مقال**، ومن أرادَ البقيّةَ فتحَها بضغطة. ولا يظهرُ زرُّ «المزيد» إلّا إن كان ثَمَّ ما يُطوى فعلاً — قياساً بالارتفاعِ لا تخميناً بعددِ الحروف. */ var _tg = $('cdTagline'); if(_tg){ /* ═══ اللوحُ يُغري بالنقش ═══ **الفراغُ لا يدعو أحداً إلى الكتابة** — ومساحةٌ بيضاءُ بلا حدٍّ تُقرأُ «لا شيءَ هنا» لا «اكتبْ هنا». أمّا لوحٌ محروقُ الحوافِّ يبدو أنّه ينتظرُ نقشاً فيُغري بملئِه. ولذا يظهرُ اللوحُ **فارغاً للرئيسِ وحدَه** بدعوةٍ صريحةٍ وبابٍ إلى التحرير، ويختفي عن غيرِه — الزائرُ لا يُرى له نقصُ غيرِه. */ var _mine = !!(window._uid && (c.created_by === window._uid || c.vice_id === window._uid)); _tg.innerHTML = ab ? '
' + '
' + '
' + esc(ab) + '
' + '' + '
' : (_mine ? '' : ''); if(ab) setTimeout(function(){ var tx = document.getElementById('cdTagTxt'), mb = document.getElementById('cdTagMore'); if(!tx || !mb) return; if(tx.scrollHeight > tx.clientHeight + 4){ mb.hidden = false; mb.onclick = function(){ var op = tx.classList.toggle('is-open'); mb.textContent = op ? 'أقلّ' : '… المزيد'; }; } }, 40); } if(rn && c.recruiting!==false) h+='

شرط الانضمام

'+ '
📌 '+esc(rn)+'
'; /* ═══ آخر تحرير ═══ سطرٌ على كل صفحة كلان: متى مسّها أهلُها آخرَ مرّة — يستوي فيه الوصفُ واللوقو والتاقُ ودخولُ عضوٍ وخروجه. والوقتُ بتوقيت الرياض واثنتي عشرة ساعة كما يقرأ الناسُ لا كما تخزّن القاعدة. */ var ed=c.edited_at&&window.tDT?window.tDT(c.edited_at):''; if(ed) h+='
'+ '✏️ آخر تحرير للصفحة: '+esc(ed)+''+ ' · بتوقيت السعودية
'; $('cdAbout').innerHTML=h; } var cw=(c.wins_casual||0), cl=(c.losses_casual||0); if($('cdCasual')) $('cdCasual').innerHTML = (cw+cl) ? '
'+ '🤝'+ /* صار الفوزُ غيرُ المصنَّفِ داخلاً في العدّادِ الأعلى، فلو بقيت التسميةُ سجلّاً مستقلّاً قُرِئت سجلّاً ثانياً وجُمِعت مرّتين. «منها» تقولُ إنّها بعضُ الأعلى لا شيءٌ سواه. */ 'منها غير مصنّفةتُحسب في السجل ولا تُعطي نقاطاً ولا تصنيفاً'+ ''+cw+' · '+cl+''+ '
' : ''; if($('cdActions')){ var tg2=String(c.tag||'؟'); var mine=(window._uid&&(c.created_by===window._uid||c.vice_id===window._uid)); var inClan=!!(window._myClanId||window._myMemberClanId); var open=(c.recruiting!==false); var _actHTML = mine ? '
'+ ''+ '
' : (open && !inClan && window._uid && c.status==='ratified') /* باب مفتوح وأنت حر: الدعوة واضحة بلا زحمة. وإن كنتَ أرسلتَ طلباً فالزرّ يختفي حتى تمرّ ٢٤ ساعة. */ ? (function(){ var w=window.joinWait?window.joinWait(c.id):0; var head='
'+ '🚪'+ 'هذا الكلان يستقبل أعضاء
'; var body = w>0 ? '
📨 أرسلت طلب انضمام لهذا الكلان
تقدر ترسل مرة ثانية بعد '+window.fmtWait(w)+'
' : ''+ '
يوصل طلبك لرئيس الكلان — ويرد عليك بالتنبيهات
'; return '
'+head+body+'
'; })() : (open && inClan) ? '
🚪 يستقبل أعضاء — واجب الخروج من كلانك أولاً للانضمام
' : (open && window._uid && c.status!=='ratified') ? '
⏳ الكلان بانتظار الترسيم — يفتح باب الانضمام بعد اعتماد الإدارة
' : ''; /* ═══ cdTopAct — الفعلُ في الأعلى لا في القاع ═══ زرُّ الانضمامِ كان على ارتفاعِ ١١٠٠ بكسلٍ من رأسِ الصفحة، **بعدَ الرتبةِ والأوسمةِ والإحصاءِ والتعريف** — ومن دخلَ ليطلبَ الانضمامَ لا يريدُ قراءةَ كلِّ ذلك أوّلاً. فيصعدُ إلى شريطِ الرأسِ مقابلَ زرِّ الرجوع: يُرى بلا تمرير، ويوفّرُ على الصفحةِ صندوقاً كاملاً. **وما صعدَ لا يُكرَّرُ في الأسفل** — زرّانِ للفعلِ الواحدِ يُربكان ويُطيلانِ ما أردنا تقصيرَه. أمّا حالُ المهلةِ فتبقى في الأسفلِ لأنّها شرحٌ لا فعل، والشرحُ لا يُختصَرُ في رقاقة. */ var _top = $('cdTopAct'); var _w = window.joinWait ? window.joinWait(c.id) : 0; var _canJoin = (open && !inClan && window._uid && c.status === 'ratified'); if(_top){ _top.innerHTML = mine ? '' : (_canJoin && !_w) ? '' : ''; } $('cdActions').innerHTML = (mine || (_canJoin && !_w)) ? '' : _actHTML; } }); /* profile_id لازمٌ هنا: الاسم يصير باباً إلى ملفّه، وبدونه يظنّه الحارسُ عضواً بلا حساب فيتركه نصّاً لا يستجيب للضغط. */ sb.from('clan_members').select('profile_id,role,member_name,psn,title,profiles(username,psn_id)').eq('clan_id',clanId).then(function(mr){ var order={leader:0,vice:1,member:2}; var rows=(mr.data||[]).slice().sort(function(a,b){ return (order[a.role]||2)-(order[b.role]||2); }); if($('cdMemCount')) $('cdMemCount').textContent=rows.length+' عضو'; if($('cdMembers')) $('cdMembers').innerHTML= rows.length? hierarchyHTML(rows) : '
لا أعضاء بعد
'; }); /* سجل مباريات الكلان — من السجل الدائم بالتاريخ والوقت */ sb.from('match_log').select('*').eq('kind','team') .or('clan_a.eq.'+clanId+',clan_b.eq.'+clanId) .order('verified_at',{ascending:false}) .limit(window._cdAllM===clanId?500:40).then(function(rr){ var all=rr.data||[]; var W=all.filter(function(x){return x.winner_clan===clanId;}).length, Lo=all.length-W; var rk=all.filter(function(x){return x.ranked!==false;}).length; var head = all.length ? '
'+ '\u2714 '+W+' \u0641\u0648\u0632\u00b7'+ '\u2715 '+Lo+' \u062e\u0633\u0627\u0631\u0629\u00b7'+ '\ud83c\udfc5 '+rk+' \u0645\u0635\u0646\u0651\u0641\u0629\u00b7'+ '\ud83e\udd1d '+(all.length-rk)+' غير مصنّفة
' : ''; if($('cdMatchCount')) $('cdMatchCount').textContent = all.length ? (all.length+' مباراة') : ''; if($('cdMatches')) $('cdMatches').innerHTML= all.length? head+all.map(function(x){ var isA=x.clan_a===clanId; var oTag=String((isA?x.tag_b:x.tag_a)||'\u061f'); var won=x.winner_clan===clanId; var mine=isA?x.score_a:x.score_b, their=isA?x.score_b:x.score_a; var when=String(x.played_at||x.verified_at||''); var dd=tDay(when).split('-'), tm=tHM(when); var dsh=dd.length===3?(dd[2]+'/'+dd[1]):''; /* \u2550\u2550\u2550 \u0627\u0644\u0625\u062b\u0628\u0627\u062a\u0627\u062a \u0644\u0627 \u062a\u064f\u0641\u062a\u062d \u0645\u0646 \u0635\u0641\u062d\u0629 \u0627\u0644\u0643\u0644\u0627\u0646 \u2550\u2550\u2550 \u0633\u062c\u0644\u0651 \u0627\u0644\u0643\u0644\u0627\u0646 \u0639\u0627\u0645\u0651 \u0644\u0644\u062c\u0645\u064a\u0639\u060c \u0648\u0627\u0644\u0625\u062b\u0628\u0627\u062a\u0627\u062a \u0645\u0627\u062f\u0651\u0629\u064f \u0645\u0631\u0627\u062c\u0639\u0629\u064d \u0644\u0644\u0637\u0627\u0642\u0645 \u0644\u0627 \u0644\u0639\u0631\u0636\u064d \u0639\u0627\u0645 \u2014 \u0641\u0623\u064f\u063a\u0644\u0642\u062a \u0647\u0646\u0627. \u062a\u064f\u0631\u0627\u062c\u064e\u0639 \u0645\u0646 \u0644\u0648\u062d\u0629 \u0627\u0644\u0625\u0634\u0631\u0627\u0641 \u0648\u062d\u062f\u0647\u0627. */ var pf=''; var dlt=''; if(x.ranked!==false){ var v=won?x.delta_win:x.delta_loss; /* x2log — الفوزُ المضاعَفُ يُعلَنُ سببُه بدل أن يُستغرَبَ رقمُه */ var x2=Number(x.xp_mult||0)>1 && won; if(v!=null) dlt=''+(v>0?'+':'')+v+'' +(x2?'⚡2X':''); } else dlt='غير مصنّفة'; return '
'+ ''+(won?'\u2714':'\u2715')+''+ 'CLAN '+esc(oTag)+''+ ''+(mine!=null?mine:'?')+'\u2013'+(their!=null?their:'?')+''+ dlt+ ''+dsh+(tm?(' '+tm):'')+''+ '#M'+x.id+''+pf+'
'; }).join('')+ ((all.length>=40 && window._cdAllM!==clanId) ? '' : '') : '
لا مباريات موثّقة بعد — أول نتيجة معتمدة تظهر هنا
'; },function(){}); }; /* ═══════════ المواجهات الفردية ═══════════ نفس رحلة تحديات الكلانات: طلبٌ مفتوح → يتطوّع لاعبون → صاحبه يختار. والتوثيق بالكيلات: أوّل من يبلغ الهدف يفوز، فلا نتيجة تُكتب باليد. */ window._duTab='open'; window._duFilter='active'; window._duRows={}; function duKills(){ return parseInt((window._appSet||{}).duel_kills,10)||30; } function duStat(s){ return s==='accepted' ? '● جارية' : s==='completed' ? '✓ منتهية' : s==='cancelled' ? '✕ ملغاة' : s==='pending' ? '● بانتظار قبوله' : '● مفتوحة للكل'; } function duCard(d,kind,uid){ var mine=(d.challenger_id===uid), other=mine?d.opponent_name:d.challenger_name; var vs=''+esc(d.challenger_name||'؟')+' ضد '+esc(d.opponent_name||'مفتوح للكل')+''; var h='
'+vs+'
'; h+='
'+duStat(d.status)+' · 🎯 أول من يوصل '+(d.target_kills||30)+' كيل
'; if(d.note) h+='
💬 '+esc(d.note)+'
'; if(d.status==='accepted') h+='
🎮 '+esc(d.challenger_psn||'—')+' 🆚 '+esc(d.opponent_psn||'—')+' — أضيفوا بعض والعبوا
'; var b=[]; if(!uid){ /* ضيف: يشوف ولا يتصرّف */ } else if(kind==='open' && d.challenger_id!==uid) b.push(''); else if(kind==='directed') { b.push(''); b.push(''); } else if(kind==='mine'){ if(d.status==='accepted'){ b.push(''); b.push(''); } if(d.status==='open'||d.status==='pending'||d.status==='accepted') b.push(''); } if(b.length) h+='
'+b.join('')+'
'; return h+'
'; } function duEmpty(t){ return '
'+t+'
'; } window.loadSolo=function(){ /* ═══ لوحةُ الأفراد — أرقامُها هي: قتلٌ وموتٌ وK/D ═══ */ sb.rpc('ffa_board',{p_limit:60}).then(function(r){ var box=$('soloBoard'), pod=$('soloPodium'), tot=$('ffaTotals'), sub=$('ffaSub'); if(!box) return; var d=r.data||{}, rows=d.rows||[], t=d.totals||{}; if(tot) tot.innerHTML = '
'+(t.players||0)+'لاعب مصنّف
'+ '
'+(t.duels||0)+'مواجهة موثّقة
'+ '
'+(t.kills||0)+'قتلة مسجّلة
'; if(sub) sub.textContent = rows.length? 'أعلى '+rows.length : ''; if(!rows.length){ box.innerHTML=duEmpty('لا لاعبين مصنّفين بعد — كن الأول 🎯'); if(pod) pod.innerHTML=''; return; } /* الصورةُ أو الحرفانِ الأوّلان */ var avOf=function(p){ return '
'+(p.avatar_url ? '' : '
'+esc((p.username||'?').slice(0,2).toUpperCase())+'
')+'
'; }; var nameOf=function(p){ var f=window.fxOf(p.id); return f? window.fxName(f,p.username||'لاعب') : esc(p.username||'لاعب'); }; var clanOf=function(p){ return p.clan? ''+esc(p.clan)+'' : ''; }; /* الفوزُ والخسارةُ رقمانِ في خانةٍ واحدة — فيُلزَمانِ بـltr */ var wl=function(p){ return ''+p.w+'–'+p.l+''; }; var fire=function(p){ return (p.streak>=2)? ' 🔥'+p.streak : ''; }; /* ═══ المتصدّر — بطاقةٌ عريضةٌ لا منصّة ═══ */ var c=rows[0]; var champ='
'+ '
المتصدّر
'+avOf(c)+ '
'+ '
'+nameOf(c)+clanOf(c)+fire(c)+'
'+ '
'+ '
'+wl(c)+'فوز · خسارة
'+ '
'+c.kd+'K/D
'+ '
'+c.kf+'قتلة
'+ '
'+c.points+'نقطة
'+ '
'+ '
'; /* ═══ الثاني والثالث ═══ */ var two=function(p,rank){ if(!p) return ''; return '
'+ '
'+rank+'
'+avOf(p)+ '
'+ '
'+nameOf(p)+'
'+ '
'+p.w+'–'+p.l+' · K/D '+p.kd+'
'+ '
'; }; var pair = (rows[1]||rows[2]) ? '
'+two(rows[1],2)+two(rows[2],3)+'
' : ''; if(pod) pod.innerHTML = champ+pair; /* ═══ البقيّة ═══ */ var rest=rows.slice(3); box.innerHTML = rest.length? rest.map(function(p){ return '
'+ ''+p.rk+''+avOf(p)+ '
'+nameOf(p)+fire(p)+''+ ''+(p.clan? esc(p.clan)+' · ' : '')+p.wr+'٪ نسبة الفوز
'+ '
'+ ''+wl(p)+'ف · خ'+ ''+p.kd+'K/D'+ ''+p.points+'نقطة'+ '
'; }).join('') : ''; }); sb.from('profiles').select('username').limit(500).then(function(r){ if($('userOptions')) $('userOptions').innerHTML=(r.data||[]).map(function(p){return '';}).join(''); }); var uid=window._uid; window._soloUid=uid||null; if($('duelEntrySub')) $('duelEntrySub').textContent='أول من يوصل '+duKills()+' كيل يفوز'; /* المتاحة للجميع */ sb.from('duels').select('*').is('opponent_id',null).eq('status','open') .order('id',{ascending:false}).limit(40).then(function(r){ var rows=(r.data||[]).filter(function(d){ return !uid||d.challenger_id!==uid; }); rows.forEach(function(d){ window._duRows[d.id]=d; }); if($('duelOpen')) $('duelOpen').innerHTML = rows.length ? rows.map(function(d){ return duCard(d,'open',uid); }).join('') : duEmpty('ما فيه طلبات مفتوحة — اطلب واحداً وخل الشباب يتطوّعون ⚡'); }); if(!uid){ ['duelDirected','duelMine','duelBids'].forEach(function(id){ if($(id)) $(id).innerHTML=duEmpty('سجّل دخولك'); }); if($('duBidsSec')) $('duBidsSec').hidden=true; window.applyDuTab(); return; } /* موجّهة لك */ sb.from('duels').select('*').eq('opponent_id',uid).eq('status','pending') .order('id',{ascending:false}).then(function(r){ var rows=r.data||[]; rows.forEach(function(d){ window._duRows[d.id]=d; }); if($('duelDirected')) $('duelDirected').innerHTML = rows.length ? rows.map(function(d){ return duCard(d,'directed',uid); }).join('') : duEmpty('ما أحد تحدّاك مباشرة'); if($('duNeedN')) $('duNeedN').textContent=rows.length?('('+rows.length+')'):''; }); /* مواجهاتي مع الفلتر */ sb.from('duels').select('*').or('challenger_id.eq.'+uid+',opponent_id.eq.'+uid) .order('id',{ascending:false}).limit(60).then(function(r){ window._duMine=r.data||[]; window._duMine.forEach(function(d){ window._duRows[d.id]=d; }); window.renderDuMine(); var act=window._duMine.filter(function(d){ return ['open','pending','accepted'].indexOf(d.status)!==-1; }); if($('duMineN')) $('duMineN').textContent=act.length?('('+act.length+')'):''; }); window.loadDuelBids(); window.applyDuTab(); }; window.renderDuMine=function(){ var box=$('duelMine'); if(!box) return; var f=window._duFilter, all=window._duMine||[], uid=window._uid; var rows=all.filter(function(d){ if(f==='all') return true; if(f==='active') return ['open','pending','accepted'].indexOf(d.status)!==-1; if(f==='done') return d.status==='completed'; if(f==='cancelled') return d.status==='cancelled'; return true; }); box.innerHTML = rows.length ? rows.map(function(d){ return duCard(d,'mine',uid); }).join('') : duEmpty('ما فيه مواجهات بهذا الفلتر'); }; /* المتطوّعون لطلبي + تقدّماتي المعلّقة */ window.loadDuelBids=function(){ var box=$('duelBids'), sec=$('duBidsSec'); if(!box) return; var uid=window._uid; if(!uid){ box.innerHTML=''; if(sec) sec.hidden=true; return; } sb.from('duel_bids').select('*,duels(id,challenger_id,status,target_kills)') .eq('status','pending').order('created_at',{ascending:false}).limit(60).then(function(r){ var rows=(r.data||[]).filter(function(b){ return b.duels && b.duels.status==='open'; }); var toMe=rows.filter(function(b){ return b.duels.challenger_id===uid; }); var mine=rows.filter(function(b){ return b.player_id===uid; }); if(!toMe.length && !mine.length){ box.innerHTML=''; if(sec) sec.hidden=true; return; } if(sec) sec.hidden=false; window.applyDuTab(); var ids=toMe.map(function(b){ return b.player_id; }); var draw=function(nm){ var h=toMe.map(function(b){ return '
'+ '
🙋 '+esc(nm[b.player_id]||'لاعب')+' يبي يواجهك
'+ '
'+ (b.note?esc(b.note)+'
':'')+'وافق لتبدأ المواجهة وتتبادلوا الـPSN.
'+ '
'+ ''+ ''+ ''+ '
'; }).join(''); h+=mine.map(function(b){ return '
'+ '
⏳ تطوّعت لطلب مواجهة
'+ '
ننتظر ردّه — تقدر تسحب تقدّمك.
'+ '
'+ '
'; }).join(''); box.innerHTML=h; }; if(ids.length) sb.from('profiles').select('id,username').in('id',ids).then(function(pr){ var nm={}; (pr.data||[]).forEach(function(p){ nm[p.id]=p.username; }); draw(nm); },function(){ draw({}); }); else draw({}); },function(){ box.innerHTML=''; if(sec) sec.hidden=true; }); }; window.createDuel=function(){ var msg=$('duelMsg'); if(!msg) return; if(_blocked()){ msg.style.color='var(--red)'; msg.textContent='🚫 حسابك محظور'; return; } window.authUser().then(function(res){ var u=res&&res.data&&res.data.user; if(!u){ msg.style.color='var(--red)'; msg.textContent='سجّل دخولك أولاً'; return; } sb.from('profiles').select('username,psn_id').eq('id',u.id).single().then(function(pr){ var myPsn=(pr.data&&pr.data.psn_id)||'', myName=(pr.data&&pr.data.username)||''; if(!myPsn){ msg.style.color='var(--red)'; msg.innerHTML='⚠️ احفظ PSN ID في حسابك أول عشان يقدر الخصم يضيفك'; return; } var oppName=(($('duelOpp')&&$('duelOpp').value)||'').trim(); var note=(($('duelNote')&&$('duelNote').value)||'').trim().slice(0,90); var K=duKills(); var doInsert=function(oppId,oppUser,oppPsn){ sb.from('duels').insert({challenger_id:u.id, challenger_name:myName, challenger_psn:myPsn, opponent_id:oppId||null, opponent_name:oppUser||null, opponent_psn:oppPsn||null, status:oppId?'pending':'open', target_kills:K, note:note||null}).then(function(r){ if(r.error){ msg.style.color='var(--red)'; msg.textContent=errAr(r.error.message); return; } msg.style.color='#3ddc84'; msg.textContent=oppId?'✅ وصله طلبك — ننتظر قبوله':'✅ طلب مفتوح — انتظر من يتطوّع'; if($('duelOpp')) $('duelOpp').value=''; if($('duelNote')) $('duelNote').value=''; if(oppId) sb.from('notifications').insert({user_id:oppId,title:'⚔️ تحدّاك لاعب 1v1', body:myName+' يبي يواجهك — أول من يوصل '+K+' كيل يفوز'}).then(function(){},function(){}); window.loadSolo(); }); }; if(!oppName){ doInsert(null,null,null); return; } sb.from('profiles').select('id,username,psn_id').ilike('username',oppName).limit(1).then(function(op){ var o=op.data&&op.data[0]; if(!o){ msg.style.color='var(--red)'; msg.textContent='ما لقيت لاعب باسم: '+oppName; return; } if(o.id===u.id){ msg.style.color='var(--gold)'; msg.textContent='ما تقدر تتحدى نفسك 😄'; return; } if(!o.psn_id){ msg.style.color='var(--gold)'; msg.textContent='هذا اللاعب ما حفظ PSN بعد'; return; } doInsert(o.id,o.username,o.psn_id); }); }); }); }; var duWhy={auth:'سجّل دخولك',not_found:'الطلب غير موجود',closed:'الطلب انسحب أو قُبل من غيرك', own:'هذا طلبك أنت',banned:'🚫 حسابك محظور',no_psn:'احفظ PSN في حسابك أول', already:'تقدّمت لهذا الطلب من قبل',forbidden:'ما تملك هذا الإجراء',done:'تمّ الردّ عليه', no_duel:'المواجهة غير موجودة', use_withdraw:'⚔️ المواجهة جارية — ما تُلغى، وثّق نتيجتها أو اضغط «انسحب من المواجهة» بغرفتها'}; function duRun(fn,args,okMsg){ sb.rpc(fn,args).then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(d&&d.ok===false){ toast(duWhy[d.why]||'تعذّر الإجراء'); if(d.why==='no_psn') go('account'); return; } toast(okMsg); window.loadSolo(); },function(){ toast('تعذّر الاتصال'); }); } window.bidDuel=function(id){ if(_blocked()) return; if(!window._uid){ go('account'); return; } duRun('bid_duel',{p_duel:parseInt(id,10),p_note:null},'🙋 تطوّعت — ننتظر اختياره'); }; window.acceptDuel=function(id){ if(_blocked()) return; if(!window._uid){ go('account'); return; } duRun('accept_duel',{p_did:parseInt(id,10)},'✅ بدأت المواجهة — تبادلوا الـPSN'); }; window.cancelDuel=function(id){ if(!confirm('إلغاء هذه المواجهة؟')) return; duRun('cancel_duel',{p_did:parseInt(id,10)},'أُلغيت المواجهة'); }; window.answerDuelBid=function(bid,ok){ duRun('answer_duel_bid',{p_bid:bid,p_accept:!!ok}, ok?'✅ بدأت المواجهة':'رفضت التقدّم'); }; window.pullDuelBid=function(bid){ duRun('pull_duel_bid',{p_bid:bid},'سحبت تقدّمك'); }; /* ═══════════ غرفة المواجهة ═══════════ مكانٌ خاصٌّ بالطرفين يتّفقان فيه على الموعد ويتبادلان الـPSN — بدل ما يتراسلان خارج المنصة فتضيع الاتفاقات عند الخلاف. */ window.closeDuelRoom=function(){ if(window._dqCh){ try{ sb.removeChannel(window._dqCh); }catch(e){} window._dqCh=null; } window._dqId=null; }; /* ═══ رابط الغرفة ═══ يفتحها لمن يملك حقّ دخولها — والحقّ من سياسات القاعدة لا من الرابط. فلو تسرّب لغير أهله لم يرَ شيئاً. */ window.roomLink=function(kind,id){ return location.origin+'/?s='+(kind==='dq'?'dq':'chchat')+'&focus='+encodeURIComponent(id); }; document.addEventListener('click',function(e){ var b=e.target&&e.target.closest?e.target.closest('[data-roomlink]'):null; if(!b) return; e.preventDefault(); var p=String(b.getAttribute('data-roomlink')||'').split(':'); var url=window.roomLink(p[0], p.slice(1).join(':')); var done=function(){ toast('🔗 نُسخ رابط الغرفة'); }; if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(url).then(done,function(){ prompt('انسخ الرابط:',url); }); } else prompt('انسخ الرابط:',url); }); /* ═══ الدخول بالرابط ═══ الداخل من رابطٍ لا يملك ذاكرة الشاشة، فنجلب المواجهة له. بها يدخل المشرف غرفةً لم يفتح صفحتها أصلاً. */ window.openDuelRoomById=function(id){ if(!window._uid){ go('account'); return; } var d=(window._duRows||{})[id]; if(d) return window.openDuelRoom(id); sb.from('duels').select('*').eq('id',parseInt(id,10)).single().then(function(r){ if(!r||!r.data){ toast('المواجهة غير موجودة'); return; } window._duRows=window._duRows||{}; window._duRows[r.data.id]=r.data; window.openDuelRoom(r.data.id); },function(){ toast('تعذّر فتح المواجهة'); }); }; window.openDuelRoom=function(id){ var d=(window._duRows||{})[id]; if(!d){ return window.openDuelRoomById(id); } if(!window._uid){ go('account'); return; } window._dqId=parseInt(id,10); window._dqRow=d; go('dq'); window.renderDuelRoom(); }; window.renderDuelRoom=function(){ var d=window._dqRow||{}, id=window._dqId; if(!id) return; if($('dqTitle')) $('dqTitle').innerHTML='⚡ '+esc(d.challenger_name||'؟')+' ضد '+esc(d.opponent_name||'؟'); if($('dqSub')) $('dqSub').textContent='🎯 أول من يوصل '+(d.target_kills||duKills())+' كيل يفوز'; if($('dqPsn')) $('dqPsn').innerHTML= '🎮 '+esc(d.challenger_name||'؟')+': '+esc(d.challenger_psn||'—')+'
'+ '🎮 '+esc(d.opponent_name||'؟')+': '+esc(d.opponent_psn||'—')+''; var live=(d.status==='accepted'); /* المشرف يأخذ رابط الغرفة ليعود إليها أو يمرّره لزميله */ var modLink = window._isStaff ? '' : ''; if($('dqActs')) $('dqActs').innerHTML = (live ? ''+ ''+ ''+ '' : '
المواجهة '+(d.status==='completed'?'انتهت':'أُلغيت')+' — الغرفة للقراءة فقط
') + modLink; var bar=document.querySelector('#dq [id="dqInput"]'); if(bar) bar.disabled=!live; ['dqAttach','dqMic','dqSend'].forEach(function(x){ var e=$(x); if(e) e.disabled=!live; }); window.loadDuelChat(); /* بثٌّ حيّ: رسالة الطرف الآخر تظهر بلا تحديث */ if(window._dqCh){ try{ sb.removeChannel(window._dqCh); }catch(e){} } window._dqCh=sb.channel('dq'+id) .on('postgres_changes',{event:'INSERT',schema:'public',table:'duel_chat',filter:'duel_id=eq.'+id}, function(){ window.loadDuelChat(); }).subscribe(); }; window.loadDuelChat=function(){ var box=$('dqMsgs'), id=window._dqId; if(!box||!id) return; sb.from('duel_chat').select('*').eq('duel_id',id).order('id',{ascending:true}).limit(200).then(function(r){ if(window._dqId!==id) return; /* غرفة أخرى فُتحت أثناء الانتظار */ if(r.error){ box.innerHTML='
'+ (/permission|policy/i.test(r.error.message)?'هذي الغرفة لطرفَي المواجهة فقط':'تعذّر التحميل')+'
'; return; } var rows=r.data||[]; box.innerHTML = rows.length ? rows.map(function(m){ if(m.system){ /* لون الرسالة يقول حكمها — كما في غرفة الكلانات */ var K={ report:['rgba(255,204,0,.07)','rgba(255,204,0,.4)','var(--gold)'], verdict_ok:['rgba(61,220,132,.08)','rgba(61,220,132,.45)','#3ddc84'], verdict_no:['rgba(255,107,94,.08)','rgba(255,107,94,.45)','#ff8c7a'], /* النقضُ والاسترجاعُ كهرمانيّانِ: ليسا قبولاً ولا رفضاً */ verdict_revert:['rgba(232,153,58,.09)','rgba(232,153,58,.5)','#f0a94a'], restored:['rgba(232,153,58,.07)','rgba(232,153,58,.32)','#e8993a'] }; var k=K[m.kind]||['rgba(56,182,255,.07)','rgba(56,182,255,.25)','var(--blue2)']; var b0=String(m.body||''), nl=b0.indexOf('\n'); var hd=(nl>0)?b0.slice(0,nl):b0, rest=(nl>0)?b0.slice(nl+1):''; return '
'+ '
'+esc(hd)+'
'+ (rest?'
'+esc(rest)+'
':'')+ '
'+tHM(m.created_at)+'
'; } var own=(m.user_id===window._uid); var att=''; if(m.media_url){ var mu=String(m.media_url).replace(/"/g,'"'); att = (m.kind==='audio') ? '' : window.isVid(mu) ? ''+ ''+ '' : ''; } var f=window.fxOf(m.user_id), fb=window.fxBubble(f); var nm=f?window.fxName(f,m.username||'لاعب'):esc(m.username||'لاعب'); return '
'+ '
'+ '
'+(own?'أنت':nm)+(window.rkPin?window.rkPin(m.user_id,18):'')+' · '+tHM(m.created_at)+'
'+ (fb?'
'+esc(m.body||'')+att+'
' :'
'+ esc(m.body||'')+att+'
')+ '
'; }).join('') : '
ابدأوا الاتفاق على الموعد 👋
'; box.scrollTop=box.scrollHeight; },function(){}); }; function dqInsert(row){ var id=window._dqId; if(!id) return Promise.resolve(); return sb.from('duel_chat').insert(Object.assign({duel_id:id, user_id:window._uid, username:(window._myUsername||'لاعب')}, row)).then(function(r){ if(r&&r.error){ toast(/policy|violates/i.test(r.error.message||'')?'الغرفة مقفلة — المواجهة انتهت':errAr(r.error.message)); return; } window.loadDuelChat(); }); } window.sendDuelChat=function(){ if(_blocked()) return; var i=$('dqInput'); if(!i) return; var b=(i.value||'').trim(); if(!b||!window._dqId) return; i.value=''; dqInsert({body:b.slice(0,400)}).then(null,function(){ i.value=b; }); }; /* ── مرفق: صورة أو مقطع ── */ window.dqUpload=function(){ var fi=$('dqFile'); if(!fi||!fi.files||!fi.files[0]) return; var f=fi.files[0], msg=$('dqUpMsg'); fi.value=''; if(f.size>(window.UP_MAX_MB||100)*1024*1024){ if(msg){msg.style.color='var(--red)';msg.textContent='⚠️ أكبر من '+(window.UP_MAX_MB||100)+'MB';} return; } if(msg){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري الرفع...'; } var ref=String(window._dqId||'duel').replace(/[^0-9a-zA-Z-]/g,'').slice(0,40)||'duel'; sb.auth.getSession().then(function(sr){ var tok=sr&&sr.data&&sr.data.session&&sr.data.session.access_token; if(!tok){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} return; } return fetch('/api/upload',{method:'POST', headers:{'Authorization':'Bearer '+tok,'x-codar-kind':'proof','x-codar-ref':ref}, body:f}) .then(function(r){ return r.json().then(function(j){ return {r:r,j:j}; }); }) .then(function(x){ if(!x.r.ok||!x.j||!x.j.url){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} return; } if(msg) msg.textContent=''; dqInsert({body:'', media_url:x.j.url, kind:window.isVid(x.j.url)?'video':'image'}); }); }).catch(function(){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} }); }; /* ── تسجيل صوتي ── */ window._dqRecSt=null; window.dqMicStart=function(){ if(!navigator.mediaDevices||!window.MediaRecorder){ toast('جهازك ما يدعم التسجيل'); return; } navigator.mediaDevices.getUserMedia({audio:true}).then(function(stream){ var mr, chunks=[]; try{ mr=new MediaRecorder(stream); }catch(e){ toast('تعذّر التسجيل'); return; } var t0=Date.now(), tick=setInterval(function(){ var s=Math.floor((Date.now()-t0)/1000); var el=$('dqRecT'); if(el) el.textContent=Math.floor(s/60)+':'+('0'+(s%60)).slice(-2); if(s>=120) window.dqMicStop(true); /* حدٌّ رحيم: دقيقتان */ },250); mr.ondataavailable=function(e){ if(e.data&&e.data.size) chunks.push(e.data); }; mr.onstop=function(){ clearInterval(tick); try{ stream.getTracks().forEach(function(t){t.stop();}); }catch(e){} var r=$('dqRec'); if(r) r.hidden=true; var st=window._dqRecSt; window._dqRecSt=null; if(!st||st.cancel||!chunks.length) return; var dur=Math.max(1,Math.round((Date.now()-t0)/1000)); var blob=new Blob(chunks,{type:(chunks[0]&&chunks[0].type)||'audio/webm'}); var msg=$('dqUpMsg'); if(msg){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري إرسال التسجيل...'; } var ref=String(window._dqId||'duel').replace(/[^0-9a-zA-Z-]/g,'').slice(0,40)||'duel'; sb.auth.getSession().then(function(sr){ var tok=sr&&sr.data&&sr.data.session&&sr.data.session.access_token; if(!tok){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر إرسال التسجيل';} return; } return fetch('/api/upload',{method:'POST', headers:{'Authorization':'Bearer '+tok,'x-codar-kind':'proof','x-codar-ref':ref}, body:blob}) .then(function(r){ return r.json().then(function(j){ return {r:r,j:j}; }); }) .then(function(x){ if(!x.r.ok||!x.j||!x.j.url){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر إرسال التسجيل';} return; } if(msg) msg.textContent=''; dqInsert({body:'', media_url:x.j.url, kind:'audio', dur:dur}); }); }).catch(function(){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر إرسال التسجيل';} }); }; window._dqRecSt={mr:mr,cancel:false}; mr.start(); var r=$('dqRec'); if(r) r.hidden=false; var el=$('dqRecT'); if(el) el.textContent='0:00'; },function(){ toast('لازم تسمح بالمايك'); }); }; window.dqMicStop=function(send){ var st=window._dqRecSt; if(!st) return; st.cancel=!send; try{ st.mr.stop(); }catch(e){} }; /* ── بلاغ مخالفة ── */ window.dqReportSend=function(){ var msg=$('dqRpMsg'); if(!msg) return; if(_blocked()) return; var id=window._dqId, d=window._dqRow||{}; if(!id||!window._uid){ go('account'); return; } var reason=(($('dqRpReason')||{}).value||'').trim(); var det=(($('dqRpDetails')||{}).value||'').trim().slice(0,400); var proof=(($('dqRpProof')||{}).value||'').trim(); if(!det){ msg.style.color='var(--red)'; msg.textContent='اكتب تفاصيل المخالفة'; return; } var accused=(d.challenger_id===window._uid)?(d.opponent_name||''):(d.challenger_name||''); msg.style.color='var(--gold)'; msg.textContent='...جاري الإرسال'; sb.from('reports').insert({reporter_id:window._uid, duel_id:id, accused:accused, reason:reason, details:det, evidence_url:proof||null}).then(function(r){ if(r&&r.error){ msg.style.color='var(--red)'; msg.textContent=errAr(r.error.message); return; } msg.style.color='#3ddc84'; msg.textContent='✅ وصل البلاغ للمشرفين'; if($('dqRpDetails')) $('dqRpDetails').value=''; if($('dqRpProof')) $('dqRpProof').value=''; if($('dqRpUp')) $('dqRpUp').textContent=''; setTimeout(function(){ var b=$('dqReport'); if(b) b.hidden=true; msg.textContent=''; window.loadDuelChat(); },1200); }); }; window.dqReportProof=function(){ var fi=$('dqRpFile'); if(!fi||!fi.files||!fi.files[0]) return; var f=fi.files[0], msg=$('dqRpUp'); fi.value=''; if(f.size>(window.UP_MAX_MB||100)*1024*1024){ if(msg){msg.style.color='var(--red)';msg.textContent='⚠️ أكبر من '+(window.UP_MAX_MB||100)+'MB';} return; } if(msg){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري الرفع...'; } var ref=String(window._dqId||'duel').replace(/[^0-9a-zA-Z-]/g,'').slice(0,40)||'duel'; sb.auth.getSession().then(function(sr){ var tok=sr&&sr.data&&sr.data.session&&sr.data.session.access_token; if(!tok){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} return; } return fetch('/api/upload',{method:'POST', headers:{'Authorization':'Bearer '+tok,'x-codar-kind':'proof','x-codar-ref':ref}, body:f}) .then(function(r){ return r.json().then(function(j){ return {r:r,j:j}; }); }) .then(function(x){ if(!x.r.ok||!x.j||!x.j.url){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} return; } if($('dqRpProof')) $('dqRpProof').value=x.j.url; if(msg){ msg.style.color='#3ddc84'; msg.innerHTML='✅ أُرفق الإثبات'; } }); }).catch(function(){ if(msg){msg.style.color='var(--red)';msg.textContent='تعذّر الرفع';} }); }; /* ── استدعاء مشرف ── */ window.dqCallMod=function(){ var id=window._dqId; if(!id) return; window.confirmBox({icon:'🛡️',tone:'gold',title:'تستدعي مشرفاً للغرفة؟', text:'يدخل المشرف ليقرأ ما دار بينكما ويفصل.', points:['يُرسل تنبيه لطاقم الإشراف','يُسجَّل الاستدعاء في الغرفة','لا يُستدعى إلا مرّة واحدة في الوقت نفسه'], yes:'🛡️ استدعِه', no:'تراجع' }).then(function(ok){ if(!ok) return; sb.rpc('call_mod_duel',{p_did:id,p_reason:null}).then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(d&&d.ok===false){ toast(d.why==='already'?'فيه استدعاء مفتوح أصلاً':(duWhy[d.why]||'تعذّر')); return; } toast('🛡️ وصل الاستدعاء للمشرفين'); window.loadDuelChat(); },function(){ toast('تعذّر الاتصال'); }); }); }; (function(){ var on=function(id,ev,fn){ var e=document.getElementById(id); if(e) e.addEventListener(ev,fn); }; on('dqSend','click',window.sendDuelChat); on('dqAttach','click',function(){ var f=$('dqFile'); if(f) f.click(); }); on('dqFile','change',window.dqUpload); on('dqMic','click',function(){ window._dqRecSt?window.dqMicStop(true):window.dqMicStart(); }); on('dqRecStop','click',function(){ window.dqMicStop(true); }); on('dqRecCancel','click',function(){ window.dqMicStop(false); }); on('dqRpFileBtn','click',function(){ var f=$('dqRpFile'); if(f) f.click(); }); on('dqRpFile','change',window.dqReportProof); on('dqRpSend','click',window.dqReportSend); on('dqRpCancel','click',function(){ var b=$('dqReport'); if(b) b.hidden=true; }); var sc=document.getElementById('dq'); if(sc) sc.addEventListener('click',function(e){ var t=e.target; if(!t||!t.closest) return; if(t.closest('#dqRpOpen')){ var b=$('dqReport'); if(b){ b.hidden=!b.hidden; if(!b.hidden) b.scrollIntoView({behavior:'smooth',block:'center'}); } return; } if(t.closest('#dqModCall')){ window.dqCallMod(); return; } var rs=t.closest('[data-dres]'); if(rs){ window.openDrSheet(rs.getAttribute('data-dres')); return; } var wd=t.closest('[data-dqwd]'); if(wd){ window.withdrawDuel(wd.getAttribute('data-dqwd')); return; } }); })(); window.withdrawDuel=function(did){ if(!confirm('تبي تنسحب من هذي المواجهة؟\n\nالطرف الآخر يُسجَّل فائزاً تلقائياً، ولا يمكن التراجع.')) return; sb.rpc('withdraw_duel',{p_did:parseInt(did,10)}).then(function(r){ var dd=r&&r.data; if(r.error||(dd&&dd.ok===false)){ var w=(dd&&dd.why)||''; var W={auth:'سجّل دخولك',not_found:'المواجهة غير موجودة',not_active:'المواجهة مو جارية', forbidden:'أنت لست طرفاً بهذي المواجهة',already:'وثّقت نتيجة لهذي المواجهة من قبل'}; toast('⚠️ '+(W[w]||'تعذّر الانسحاب')); return; } toast('🏳️ انسحبت من المواجهة — بانتظار اعتماد المشرف'); if(window.loadSolo) window.loadSolo(); },function(){ toast('تعذّر الاتصال'); }); }; /* ═══ توثيق النتيجة ═══ */ window.closeDrSheet=function(){ var s=$('drSheet'),b=$('drBack'); if(s)s.hidden=true; if(b)b.hidden=true; }; window.openDrSheet=function(id){ var d=(window._duRows||{})[id]; if(!d){ toast('المواجهة غير موجودة'); return; } var K=d.target_kills||duKills(), uid=window._uid; var meIsCh=(d.challenger_id===uid); var oppName=meIsCh?(d.opponent_name||'الخصم'):(d.challenger_name||'الخصم'); window._drDuel=d; if($('drDuel')) $('drDuel').value=String(id); if($('drVs')) $('drVs').innerHTML=''+esc(d.challenger_name||'؟')+' ضد '+esc(d.opponent_name||'؟')+''; if($('drRule')) $('drRule').textContent='أول من يوصل '+K+' كيل يفوز'; if($('drWinKills')) $('drWinKills').textContent=String(K); if($('drLoseKills')){ $('drLoseKills').value=''; $('drLoseKills').max=String(K-1); } if($('drScoreNote')) $('drScoreNote').innerHTML='الفائز يُسجَّل له '+K+' تلقائياً — اكتب كيلات الخاسر فقط (٠ إلى '+(K-1)+').'; if($('drProof')) $('drProof').value=''; if($('drUpMsg')) $('drUpMsg').textContent=''; if($('drMsg')) $('drMsg').textContent=''; var wg=$('drWinner'); if(wg){ var bs=wg.querySelectorAll('button'); if(bs[0]) bs[0].textContent='أنا فزت 🏆'; if(bs[1]) bs[1].textContent=oppName+' فاز'; [].forEach.call(bs,function(x,i){ x.classList.toggle('on',i===0); }); } var s=$('drSheet'),b=$('drBack'); if(s)s.hidden=false; if(b)b.hidden=false; }; window.submitDuelResult=function(){ var msg=$('drMsg'); if(!msg) return; var d=window._drDuel; if(!d){ msg.style.color='var(--red)'; msg.textContent='اختر المواجهة'; return; } var uid=window._uid; if(!uid){ msg.style.color='var(--red)'; msg.textContent='سجّل دخولك'; return; } var K=d.target_kills||duKills(); var kl=parseInt(($('drLoseKills')||{}).value,10); if(isNaN(kl)||kl<0||kl>=K){ msg.style.color='var(--red)'; msg.textContent='اكتب كيلات الخاسر (٠ إلى '+(K-1)+')'; return; } var proof=(($('drProof')&&$('drProof').value)||'').trim(); if(!proof){ msg.style.color='var(--red)'; msg.textContent='📸 ارفع إثبات النتيجة أولاً'; return; } var wb=$('drWinner').querySelector('button.on'); var who=wb?wb.getAttribute('data-v'):'me'; var winnerId = who==='me' ? uid : (d.challenger_id===uid? d.opponent_id : d.challenger_id); msg.style.color='var(--gold)'; msg.textContent='...جاري الرفع'; sb.rpc('submit_duel_result',{p_did:parseInt(d.id,10), p_winner_id:winnerId, p_kills_w:K, p_kills_l:kl, p_proof:proof}).then(function(r){ var dd=r&&r.data; if(r.error||(dd&&dd.ok===false)){ var w=(dd&&dd.why)||''; var W={auth:'سجّل دخولك',not_found:'المواجهة غير موجودة',not_active:'المواجهة مو جارية', forbidden:'أنت لست طرفاً بهذي المواجهة', already:'⚠️ فيه نتيجة مسجّلة لهذي المواجهة من قبل — راجع الغرفة', need_proof:'📸 ارفع إثبات النتيجة أولاً'}; msg.style.color='var(--red)'; msg.textContent=W[w]||(r.error&&r.error.message)||'تعذّر الرفع'; return; } msg.style.color='#3ddc84'; msg.textContent='✅ رُفعت النتيجة — بانتظار توثيق المشرف'; setTimeout(function(){ window.closeDrSheet(); window.loadSolo(); },1200); }); }; window.uploadDuelProof=function(){ var fi=$('drFile'); if(!fi||!fi.files||!fi.files[0]) return; var f=fi.files[0]; var msg=$('drUpMsg'); if(!msg) return; if(f.size>(window.UP_MAX_MB||100)*1024*1024){ msg.style.color='var(--red)'; msg.textContent='⚠️ الحجم أكبر من '+(window.UP_MAX_MB||100)+'MB — قصّ المقطع'; fi.value=''; return; } msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري رفع الإثبات...'; var d=window._drDuel||{}; var ref=String(d.id||'duel').replace(/[^0-9a-zA-Z-]/g,'').slice(0,40)||'duel'; window.upProof(f, ref).then(function(r){ if(!r||!r.ok){ msg.style.color='var(--red)'; msg.textContent='تعذّر الرفع: '+((r&&r.why)||'حاول مرة ثانية'); return; } if($('drProof')) $('drProof').value=r.url; msg.style.color='#3ddc84'; msg.innerHTML='✅ تم رفع الإثبات — معاينة'; }); }; (function(){ var ds=document.getElementById('duelSend'); if(ds) ds.addEventListener('click',window.createDuel); var drs=document.getElementById('drSubmit'); if(drs) drs.addEventListener('click',window.submitDuelResult); var dfb=document.getElementById('drFileBtn'), dfi=document.getElementById('drFile'); if(dfb&&dfi){ dfb.addEventListener('click',function(){ dfi.click(); }); dfi.addEventListener('change',window.uploadDuelProof); } /* شرائح الأقسام — تُظهر ما يخصّها وتخفي الباقي */ var chips=document.getElementById('duChips'); if(chips) chips.addEventListener('click',function(e){ var b=e.target.closest('button[data-f]'); if(!b) return; window._duTab=b.getAttribute('data-f'); [].forEach.call(chips.querySelectorAll('button'),function(x){ x.classList.toggle('on',x===b); }); window.applyDuTab(); }); var flt=document.getElementById('duFilter'); if(flt) flt.addEventListener('click',function(e){ var b=e.target.closest('button[data-s]'); if(!b) return; window._duFilter=b.getAttribute('data-s'); [].forEach.call(flt.querySelectorAll('button'),function(x){ x.classList.toggle('on',x===b); }); window.renderDuMine(); }); var ss=document.getElementById('solo'); if(ss) ss.addEventListener('click',function(e){ var t=e.target; if(!t||!t.closest) return; var g=function(a){ var el=t.closest('['+a+']'); return el&&el.getAttribute(a); }; var v; if((v=g('data-dacc'))) { window.acceptDuel(v); return; } if((v=g('data-dbid'))) { window.bidDuel(v); return; } if((v=g('data-dcan'))) { window.cancelDuel(v); return; } if((v=g('data-dres'))) { window.openDrSheet(v); return; } if((v=g('data-droom'))) { window.openDuelRoom(v); return; } if((v=g('data-dbok'))) { window.answerDuelBid(v,true); return; } if((v=g('data-dbno'))) { window.answerDuelBid(v,false); return; } if((v=g('data-dbpull'))){ window.pullDuelBid(v); return; } }); })(); /* أي قسمٍ يظهر حسب الشريحة المختارة */ window.applyDuTab=function(){ var t=window._duTab||'open'; var show=function(id,on){ var e=$(id); if(e) e.style.display=on?'':'none'; }; show('duSecLive', t==='live'); show('duLive', t==='live'); show('duSecOpen', t==='open'); show('duelOpen', t==='open'); show('duSecDirected', t==='need'); show('duelDirected', t==='need'); var hasBids=!($('duBidsSec')||{}).hidden; show('duBidsSec', t==='need'&&hasBids); show('duelBids', t==='need'); show('duSecMine', t==='mine'); show('duFilter', t==='mine'); show('duelMine', t==='mine'); if(t==='live' && window.loadLiveDuels) window.loadLiveDuels(); }; /* ═══ المواجهات الفردية الجارية الآن — لوحة عامّة لكل زوّار المنصة ═══ */ window.loadLiveDuels=function(){ var box=$('duLive'); if(!box) return; box.innerHTML='
⏳ جاري التحميل...
'; Promise.all([ sb.from('duels').select('id,challenger_id,challenger_name,challenger_psn,opponent_id,opponent_name,opponent_psn,target_kills,created_at') .eq('status','accepted').order('created_at',{ascending:false}).limit(60), sb.from('duel_results').select('duel_id').in('status',['pending','verified']) ]).then(function(rs){ var r=rs[0]||{}; var pendingIds={}; ((rs[1]&&rs[1].data)||[]).forEach(function(x){ pendingIds[x.duel_id]=true; }); var rows=(r.data||[]).filter(function(d){ return !pendingIds[d.id]; }); var n=rows.length; var b=$('duLiveN'); if(b) b.textContent = n?('· '+n):''; window._duLiveRows=rows; box.innerHTML = rows.length ? rows.map(function(d){ return '
'+ '
'+ ''+esc(d.challenger_name||'؟')+''+ 'VS'+ ''+esc(d.opponent_name||'؟')+''+ '🔴 جاري الآن'+ '
'+ '
🎯 أول من يوصل '+(d.target_kills||30)+' كيل يفوز
'+ '
'; }).join('') : '
ما فيه مواجهات جارية الآن
'; },function(){ box.innerHTML='
تعذّر التحميل
'; }); }; /* ═══ عرض مواجهة 1v1 الجارية — بطاقة عامّة، ودخول الغرفة لطرفيها والطاقم فقط ═══ */ window.openDuelLiveShowcase=function(did){ if(!did) return; var row=(window._duLiveRows||[]).filter(function(x){return x.id===did;})[0]; if(!row) return; if(window.closeLiveShowcase) window.closeLiveShowcase(); var bk=document.createElement('div'); bk.id='lsBack'; bk.style.cssText='position:fixed;inset:0;background:rgba(3,5,8,.86);backdrop-filter:blur(4px);z-index:240'; bk.onclick=window.closeLiveShowcase; var cd=document.createElement('div'); cd.id='lsCard'; cd.style.cssText='position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:241;width:min(92vw,380px);'+ 'background:linear-gradient(180deg,#0d1016,#070a0e);border:1px solid #1c2230;border-radius:20px;padding:20px 18px;'+ 'box-shadow:0 24px 60px rgba(0,0,0,.6)'; var uid=window._uid; var canEnter=window._isStaff || (uid && (uid===row.challenger_id || uid===row.opponent_id)); cd.innerHTML= '
'+ '🔴 جاري الآن'+ ''+ '
'+ '
'+ esc(row.challenger_name||'؟')+' VS '+esc(row.opponent_name||'؟')+ '
'+ '
🎯 أول من يوصل '+(row.target_kills||30)+' كيل يفوز
'+ (canEnter ? '' : '
🔒 الغرفة خاصّة بالطرفين والطاقم — وأنت تشاهد من الخارج 👀
'); document.body.appendChild(bk); document.body.appendChild(cd); document.body.style.overflow='hidden'; }; window.uploadAvatar=function(){ var fi=$('pfAvatarFile'); if(!fi||!fi.files||!fi.files[0]) return; var f=fi.files[0]; var msg=$('pfAvatarMsg'); if(f.size>10*1024*1024){ if(msg){msg.style.color='var(--red)';msg.textContent='الصورة أكبر من 10MB';} fi.value=''; return; } if(msg){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري رفع الصورة...'; } window.uploadImage(f,'avatar',null).then(function(url){ if(msg){ msg.style.color='#3ddc84'; msg.textContent='✅ تم تحديث الصورة'; } if($('pfAvatarImg')){ $('pfAvatarImg').src=url; $('pfAvatarImg').style.display='block'; } if($('pfAvatarInit')) $('pfAvatarInit').style.display='none'; if($('pfAvatarDel')) $('pfAvatarDel').style.display=''; if(window.renderIdentity) window.renderIdentity({id:window._uid,username:window._myUsername,avatar_url:url,role:window._isStaff?'moderator':'member'}); },function(e){ if(msg){ msg.style.color='var(--red)'; msg.textContent=String(e&&e.message||'تعذّر الرفع'); } }); fi.value=''; }; /* ===== رفع الصور لسيرفرنا (دائم — بلا حدود مزوّد خارجي) ===== */ window.uploadImage=function(file, kind, clanId){ var SIZE = (kind==='clan') ? 400 : 320; var shrink=function(){ return new Promise(function(done){ try{ var img=new Image(), rd=new FileReader(); rd.onload=function(){ img.onload=function(){ var c=document.createElement('canvas'); c.width=SIZE; c.height=SIZE; var s=Math.min(img.width,img.height), sx=(img.width-s)/2, sy=(img.height-s)/2; var cx=c.getContext('2d'); cx.drawImage(img,sx,sy,s,s,0,0,SIZE,SIZE); c.toBlob(function(b){ done(b||file); }, 'image/jpeg', 0.86); }; img.onerror=function(){ done(file); }; img.src=rd.result; }; rd.onerror=function(){ done(file); }; rd.readAsDataURL(file); }catch(e){ done(file); } }); }; return sb.auth.getSession().then(function(s){ var tok=s&&s.data&&s.data.session&&s.data.session.access_token; if(!tok) throw new Error('سجّل دخولك أولاً'); return shrink().then(function(blob){ var h={'Content-Type':(blob.type||'image/jpeg'),'Authorization':'Bearer '+tok,'X-Codar-Kind':kind}; if(clanId) h['X-Codar-Clan']=clanId; return fetch('/api/upload',{method:'POST',headers:h,body:blob}).then(function(r){ return r.json(); }); }); }).then(function(d){ if(!d||!d.url) throw new Error((d&&d.error)||'تعذّر الرفع'); return d.url; }); }; window.deleteAvatar=function(){ window.authUser().then(function(res){ var u=res&&res.data&&res.data.user; if(!u) return; try{ sb.storage.from('avatars').remove([u.id+'/avatar.jpg']); }catch(e){} sb.from('profiles').update({avatar_url:null}).eq('id',u.id).then(function(){ if($('pfAvatarImg')){ $('pfAvatarImg').style.display='none'; $('pfAvatarImg').src=''; } if($('pfAvatarInit')) $('pfAvatarInit').style.display=''; if($('pfAvatarDel')) $('pfAvatarDel').style.display='none'; var msg=$('pfAvatarMsg'); if(msg){ msg.style.color='var(--tx3)'; msg.textContent='تم حذف الصورة'; } if(window.renderIdentity) window.renderIdentity({id:u.id,username:window._myUsername,avatar_url:null,role:'member'}); }); }); }; (function(){ var b=document.getElementById('pfAvatarBtn'), fi=document.getElementById('pfAvatarFile'); if(b&&fi){ b.addEventListener('click',function(){ fi.click(); }); fi.addEventListener('change',window.uploadAvatar); } var d=document.getElementById('pfAvatarDel'); if(d) d.addEventListener('click',window.deleteAvatar); })(); /* شرط «آيدي سوني موثّق» يُضبط من لوحة التحكم (يُوقف أثناء اختبار الثغرات) */ window.strictPsn=function(){ var v=window._appSet&&window._appSet.require_verified_psn; if(v===undefined||v===null) return true; return ['true','1','on','yes'].indexOf(String(v).trim().toLowerCase())>=0; }; window.applyAddNote=function(){ var e=$('mgAddNote'); if(!e) return; e.innerHTML='🔑 آيدي السوني هو هوية اللاعب واسمه — ما تحتاج تكتب اسماً منفصلاً.
'+ (window.strictPsn() ? '• ما تقدر تضمّ إلا لاعباً مسجّلاً بالمنصة وآيديه محفوظ في حسابه.
'+ '• توصله دعوة ولا ينضم إلا بموافقته — الآيدي ملك صاحبه.
' : '• وضع الاختبار مفعّل: الآيدي غير المسجّل يُضاف مباشرة بلا موافقة.
'+ '• المسجّل تصله دعوة كالمعتاد.
')+ '• الترقية لنائب تصير من أزرار العضو بعد انضمامه.'; }; /* أقل عدد أعضاء لإطلاق تحدٍّ أو قبوله — يُضبط من لوحة التحكم */ window.chMinMembers=function(){ var v=window._appSet&&parseInt(window._appSet.challenge_min_members,10); return (v&&v>0)?v:6; }; /* ===== حالة الكلان: شريط التقدّم للترسيم التلقائي + باب القبول ===== */ window.renderClanState=function(clan){ /* حالة باب الانضمام تظهر في صفّها بلا ضغط */ try{ var open=(clan&&clan.recruiting!==false); var st=document.getElementById('mgDoorState'), sb2=document.getElementById('mgDoorSub'); if(st){ st.textContent=open?'مفتوح':'مقفل'; st.className='lrow__v '+(open?'on':'off'); } if(sb2) sb2.textContent=open?'يستقبل طلبات اللاعبين الآن':'ما يوصلك أي طلب'; }catch(e){} var note=$('mgRatifyNote'); if(!note) return; var min=(window._appSet&&parseInt(window._appSet.clan_min_members,10))||6; var max=clan.max_members||(window._appSet&&parseInt(window._appSet.clan_max_members,10))||20; sb.from('clan_members').select('id',{count:'exact',head:true}).eq('clan_id',clan.id).then(function(r){ var n=r.count||0, ok=(clan.status==='ratified'); if(ok){ note.innerHTML='
'+ '✅ كلانك مرسَّم رسمياً — يظهر بالترتيب ويقدر يتحدّى. ('+n+'/'+max+' عضو)
'; } else { var left=Math.max(0,min-n), pct=Math.min(100,Math.round(n*100/min)); note.innerHTML='
'+ '
🏗️ كلانك بانتظار الترسيم — ما يحتاج موافقة أحد!
'+ '
أضِف '+left+' '+(left===1?'عضواً':'أعضاء')+' بعد ويترسّم تلقائياً في نفس اللحظة.
'+ '
'+ '
'+ '
'+n+' / '+min+' أعضاء
'; } // باب القبول var card=$('mgDoorCard'); if(card) card.hidden=!ok; var b=$('mgDoorBtn'); if(b){ var open=!!clan.recruiting; b.textContent = open?'🔒 اقفل باب القبول':'🚪 افتح باب القبول'; b.className = open?'btnp':'btnp btng'; } if($('mgRecruitNote')) $('mgRecruitNote').value=clan.recruit_note||''; if($('mgDoorMsg')) $('mgDoorMsg').innerHTML = clan.recruiting ? '✅ الباب مفتوح — كلانك يظهر بلوحة الانضمام' : '🔒 الباب مقفل — ما يوصلك أي طلب'; },function(){}); }; (function(){ var b=document.getElementById('mgDoorBtn'); if(b) b.addEventListener('click',function(){ var cid=window._myClanId; if(!cid) return; var nv=!window._clanRecruiting; var m=$('mgDoorMsg'); /* بابُ القبول وشرطُه سطران على الصفحة — والصفحةُ مقفلةٌ أثناء التحدي */ if(window._clanLocked){ if(m){ m.style.color='var(--red)'; m.textContent='🔒 كلانك في تحدٍّ جارٍ — الصفحة مقفلة حتى يُوثَّق'; } return; } if(m){ m.style.color='var(--tx3)'; m.textContent='...جاري الحفظ'; } sb.from('clans').update({recruiting:nv}).eq('id',cid).then(function(r){ if(r&&r.error){ if(m){m.style.color='var(--red)';m.textContent=errAr(r.error.message);} return; } window._clanRecruiting=nv; if(window.loadManageClan) window.loadManageClan(); toast(nv?'🚪 فُتح باب القبول':'🔒 أُقفل باب القبول'); }); }); var s=document.getElementById('mgSaveNote'); if(s) s.addEventListener('click',function(){ var cid=window._myClanId; if(!cid) return; if(window._clanLocked){ var m0=$('mgDoorMsg'); if(m0){ m0.style.color='var(--red)'; m0.textContent='🔒 كلانك في تحدٍّ جارٍ — الصفحة مقفلة حتى يُوثَّق'; } return; } sb.from('clans').update({recruit_note:($('mgRecruitNote').value||'').trim()||null}).eq('id',cid).then(function(r){ var m=$('mgDoorMsg'); if(!m) return; if(r&&r.error){ m.style.color='var(--red)'; m.textContent=errAr(r.error.message); } else { m.style.color='#3ddc84'; m.textContent='✅ حُفظت الملاحظة'; } }); }); })(); /* رفع لوقو الكلان */ (function(){ var b=document.getElementById('mgLogoBtn'), fi=document.getElementById('mgLogoFile'); if(!b||!fi) return; b.addEventListener('click',function(){ fi.click(); }); fi.addEventListener('change',function(){ var f=fi.files&&fi.files[0]; if(!f) return; var cid=window._myClanId||window._myMemberClanId; var msg=document.getElementById('mgLogoMsg'); if(!cid){ if(msg){msg.style.color='var(--red)';msg.textContent='ما لقيت كلانك';} return; } if(msg){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري الرفع...'; } window.uploadImage(f,'clan',cid).then(function(url){ if(msg){ msg.style.color='#3ddc84'; msg.textContent='✅ تم رفع اللوقو'; } if($('mgLogo')) $('mgLogo').value=url; if($('mgLogoImg')){ $('mgLogoImg').src=url; $('mgLogoImg').style.display='block'; } if($('mgLogoInit')) $('mgLogoInit').style.display='none'; },function(e){ if(msg){ msg.style.color='var(--red)'; msg.textContent=String(e&&e.message||'تعذّر الرفع'); } }); fi.value=''; }); })(); window.updateBellBadge=function(){ if(!window._uid){ if($('bellBadge')) $('bellBadge').style.display='none'; return; } /* الجرس شخصي فقط — البثّ العام ما يدخله */ sb.from('notifications').select('id',{count:'exact',head:true}) .eq('user_id',window._uid).eq('read',false).neq('scope','broadcast').then(function(r){ var n=r.count||0; var b=$('bellBadge'); if(!b) return; if(n>0){ b.textContent=n>99?'99+':String(n); b.style.display=''; } else b.style.display='none'; },function(){}); }; /* وجهة التنبيه: من رابطه إن وُجد، وإلا نستنتجها من عنوانه */ window._notifMap={}; window.notifDest=function(n){ var d=null, u=String(n.url||''), t=String(n.title||''); var m=/[?&]s=([a-z]+)/.exec(u), f=/[?&]focus=([0-9a-f-]{36})/.exec(u); var flt=/[?&]f=([a-z]+)/.exec(u); if(m) d={screen:m[1], focus:f?f[1]:null, filter:flt?flt[1]:null}; else if(/دعوة انضمام/.test(t)) d={screen:'account'}; /* ما يطلب ردّاً يفتح القائمة التي فيها الردّ */ else if(/يبي يتحدّاك|يتحدّى|موجّه لكلانك/.test(t)) d={screen:'challenges', filter:'need'}; else if(/تحدي|تحدّ|قُبل|انسحب/.test(t)) d={screen:'challenges'}; else if(/رسالة|صداقة/.test(t)) d={screen:'inbox'}; /* بلاغٌ جديدٌ شأنُ مشرفٍ لا لاعب — وموضعُه اللوحةُ لا قائمةُ التحديات */ else if(/بلاغ جديد|بلاغات جديدة/.test(t)) d={screen:'report'}; else if(/بلاغ|مخالفة/.test(t)) d={screen:'challenges'}; else if(/كلان|ترسيم|انضمام/.test(t)) d={screen:'manageclan'}; else if(/نتيجة|فاز|نقطة|ترقّيت/.test(t)) d={screen:'home'}; if(d) window._notifMap[n.id]=d; return d; }; document.addEventListener('click',function(e){ var el=e.target.closest?e.target.closest('[data-ngo]'):null; if(!el) return; var d=window._notifMap[el.getAttribute('data-ngo')]; if(!d) return; if(d.focus && d.screen==='chchat' && window.openChChat) return window.openChChat(d.focus); if(d.focus && d.screen==='ticket' && window.openTicket) return window.openTicket(d.focus); /* البلاغُ يُحكَمُ فيه من اللوحة، فالتنبيهُ ينقلُك إليها مفتوحاً على بلاغِك */ if(d.screen==='report'){ location.href='panel.html?tab=reports'+(d.focus?('&rep='+encodeURIComponent(d.focus)):''); return; } /* ═══ التنبيه يوصلك للقرار ═══ من ينتظر ردّك يُفتح له «يحتاج ردّك» لا بطاقةٌ تُقرأ ثم تُغلق. */ if(d.filter==='need'){ go('challenges'); setTimeout(function(){ if(window.chFilterTo) window.chFilterTo('need'); }, 260); return; } if(d.focus && window._goChalOn) return window._goChalOn(d.focus); go(d.screen); }); /* مسحُ الإشعارات — بدالّةٍ لا بحذفٍ مباشر: الحذفُ على الجدولِ يرجعُ بصفرِ صفوفٍ بلا خطأ (منحةٌ بلا سياسة)، فيبدو نجاحاً وهو لا شيء. */ window.clearNotifs=function(){ var T=(typeof toast==='function')?toast:function(m){ alert(m); }; var G=(typeof $==='function')?$:function(i){ return document.getElementById(i); }; var b=G('btnClearNotifs'); if(b&&b.dataset.busy) return; if(!confirm('تمسح كل إشعاراتك؟ ما تقدر ترجعها.')) return; var old=b?b.textContent:''; if(b){ b.dataset.busy='1'; b.textContent='...جاري المسح'; } var done=function(){ if(b){ delete b.dataset.busy; b.textContent=old; } }; sb.rpc('clear_my_notifications',{p_mode:'all'}).then(function(r){ done(); var d=(r&&r.data)||{}; if(!d.ok){ T('تعذّر المسح — حدّث الصفحة وجرّب'); return; } T('🗑️ مُسحت '+(d.deleted||0)+' إشعاراً'); var box=G('notifList'); if(box) box.innerHTML='
ما عندك إشعارات
'; /* المستمِعُ الحيُّ يراقبُ الإدراجَ وحدَه — فالشارةُ تُحدَّثُ يدويّاً */ if(window.updateBellBadge) window.updateBellBadge(); },function(){ done(); T('تعذّر المسح'); }); }; window.loadNotifs=function(){ var box=$('notifList'); if(!box) return; if(!window._uid){ box.innerHTML='
سجّل دخولك لرؤية تنبيهاتك
'; return; } sb.from('notifications').select('id,title,body,url,read,created_at').eq('user_id',window._uid).neq('scope','broadcast').order('id',{ascending:false}).limit(50).then(function(r){ var rows=r.data||[]; box.innerHTML = rows.length? rows.map(function(n){ /* كل تنبيه يأخذك لموضعه — نستنتج الوجهة من الرابط أو من نصّه */ var t=n.title||'', dest=window.notifDest(n); return '
'+ '
'+ '
'+esc(t||'تنبيه')+'
'+ '
'+esc(n.body||'')+'
'+ '
'+tDT(n.created_at)+'
'+ (dest?'':'')+'
'; }).join('') : '
لا تنبيهات بعد 🔔
'; var unread=rows.filter(function(n){return !n.read;}).map(function(n){return n.id;}); if(unread.length){ sb.from('notifications').update({read:true}).in('id',unread).then(function(){ window.updateBellBadge&&window.updateBellBadge(); },function(){}); } },function(){ box.innerHTML='
لا تنبيهات
'; }); }; (function(){ var b=document.getElementById('btnBell'); if(b) b.addEventListener('click',function(){ go('notifs'); }); var i=document.getElementById('btnInbox'); if(i) i.addEventListener('click',function(){ if(!window._uid) return go('account'); go('inbox'); }); })(); window.toggleSeek=function(){ var msg=$('seekMsg'); if(window._myClanId||window._myMemberClanId){ if(msg){ msg.style.color='var(--gold)'; msg.textContent='🛡️ أنت ضمن كلان — ما تحتاج تبحث عن كلان.'; } if(window.applyClanContext) window.applyClanContext(); return; } window.authUser().then(function(res){ var u=res&&res.data&&res.data.user; if(!u){ go('account'); return; } var nv=!window._seeking; sb.from('profiles').update({seeking_clan:nv}).eq('id',u.id).then(function(r){ if(r&&r.error){ if(msg){msg.style.color='var(--red)';msg.textContent=errAr(r.error.message);} return; } window._seeking=nv; if($('btnSeek')) $('btnSeek').textContent = nv?'🔴 إيقاف البحث عن كلان':'🔎 فعّل البحث عن كلان'; var ss=$('acSeekState'); if(ss){ ss.textContent=nv?'مفعّل':'متوقف'; ss.className='lrow__v '+(nv?'on':'off'); } if(msg){ msg.style.color='#3ddc84'; msg.textContent = nv?'✅ صرت ظاهر في «لاعبون يدورون كلان»':'تم الإيقاف'; } }); }); }; (function(){ var b=document.getElementById('btnSeek'); if(b) b.addEventListener('click',window.toggleSeek); })(); /* رمزُ ربطٍ لمرّةٍ واحدة — الإثباتُ الذي لا يُقرَأُ إلّا من داخلِ الحساب. كان الإرشادُ هنا: «اكتبْ اسمَك وآيدي السوني في ديسكورد» — وكلاهما منشورٌ للعامّة، فمن قرأهما ادّعى الحساب. */ window.showLinkCode=function(m){ if(!m) return; m.style.color='var(--tx3)'; m.textContent='...جاري توليد الرمز'; sb.rpc('gen_link_code').then(function(r){ var d=(r&&r.data)||{}; if(!d.ok){ m.style.color='var(--red)'; m.textContent='تعذّر توليد الرمز — حدّث الصفحة وجرّب.'; return; } var c=String(d.code||'').replace(/[^A-Z0-9]/g,''); try{ m.style.color='var(--tx2)'; m.innerHTML='
'+ '\u{1F517} اربط حسابك من ديسكورد برمز مؤقّت:
'+ '\u2460 افتح سيرفرنا بديسكورد
'+ '\u2461 اكتب /اربط والصق الرمز
'+ '
'+c+'
'+ 'صالح '+(d.mins||10)+' دقائق \u00B7 لمرّة واحدة \u00B7 لا تعطه أحداً.
'; }catch(e){ m.style.color='var(--gold)'; m.textContent='رمزك: '+c+' — اكتبه بديسكورد: /اربط'; } },function(){ m.style.color='var(--red)'; m.textContent='تعذّر توليد الرمز.'; }); }; (function(){ var b=document.getElementById('btnLinkDiscord'); if(!b) return; b.addEventListener('click',function(){ var m=$('dcLinkMsg'); if(m){ m.style.color='var(--gold)'; m.textContent='...جاري التحويل لـDiscord'; } if(!sb.auth.linkIdentity){ window.showLinkCode(m); return; } sb.auth.linkIdentity({provider:'discord', options:{redirectTo: location.origin+'/', scopes:'identify email guilds.join', skipBrowserRedirect:true}}).then(function(r){ if(r&&r.error){ /* ═══ الربط المباشر مقفل من إعدادات المزوّد ═══ نرشده للطريق الذي يعمل اليوم بدل رسالة إنجليزية ما يفهمها. */ var em=String((r.error&&r.error.message)||''); var code=String((r.error&&r.error.code)||''); if(m){ if(/manual_linking_disabled/i.test(code)||/manual linking/i.test(em)){ m.style.color='var(--tx2)'; window.showLinkCode(m); } else if(/identity|already|exists|conflict/i.test(em+code)){ m.style.color='var(--gold)'; m.textContent='⚠️ حساب ديسكورد هذا مربوط بعضوية ثانية بالمنصة — راجع الإدارة.'; } else { m.style.color='var(--red)'; m.textContent='تعذّر الربط — جرّب أمر /اربط بديسكورد.'; } } return; } var url=r&&r.data&&r.data.url; if(!url){ if(m) m.textContent='تعذّر إنشاء الرابط'; return; } /* نفس قاعدة الدخول: آيفون مثبّت يبقى بنافذته كي لا يضيع الربط */ var ua2=navigator.userAgent||''; var mobile=/iphone|ipad|ipod|android/i.test(ua2); var inApp2=(window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone===true; if(!mobile || (inApp2 && /iphone|ipad|ipod/i.test(ua2))){ location.href=url; return; } var t=setTimeout(function(){ location.href=url; },4000); fetch('/api/oauth-url?u='+encodeURIComponent(url)).then(function(x){return x.json();}) .then(function(d){ clearTimeout(t); if(d&&d.url) window.showDiscordChoice('dcLinkMsg', d.deep||d.url, d.url); else location.href=url; }) .catch(function(){ clearTimeout(t); location.href=url; }); },function(e){ if(m){ m.style.color='var(--red)'; m.textContent='تعذّر الربط'; } }); }); })(); // ضمّه لسيرفر Discord تلقائياً (مرة واحدة لكل جلسة دخول) window.joinDiscordServer=function(){ try{ if(localStorage.getItem('cx_dcJoined')==='1') return; }catch(e){} sb.auth.getSession().then(function(r){ var s=r&&r.data&&r.data.session; var pt=s&&s.provider_token; if(!pt) return; // ما فيه توكن (جلسة قديمة) — يشتغل بالدخول القادم fetch('/api/discord-join',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({provider_token:pt})}) .then(function(r){ return r.json(); }) .then(function(d){ if(d&&d.ok){ try{ localStorage.setItem('cx_dcJoined','1'); }catch(e){} if(d.joined) toast('🎉 انضممت لسيرفر Discord تلقائياً!'); } }).catch(function(){}); },function(){}); }; /* ===== إعدادات الحساب ===== */ window.loadSettings=function(){ if(!window._uid){ go('account'); return; } if(window.renderBlockList) window.renderBlockList(); sb.from('profiles').select('dm_policy,wall_policy,visits_policy,notif_general').eq('id',window._uid).single().then(function(r){ var p=r.data||{}; if($('stDm')) $('stDm').value=p.dm_policy||'all'; if($('stWall')) $('stWall').value=p.wall_policy||'all'; if($('stVisits')) $('stVisits').value=p.visits_policy||'all'; if($('stGen')) $('stGen').value=(p.notif_general===false)?'0':'1'; if(window.syncPills) window.syncPills(); },function(){}); window.authUser().then(function(u){ var e=u&&u.data&&u.data.user&&u.data.user.email; if($('stEmail')) $('stEmail').placeholder=e?('الحالي: '+e):'البريد الجديد'; }); }; window.loadFriends=function(){ if(!window._uid) return; sb.from('friends').select('id,requester,addressee,status').or('requester.eq.'+window._uid+',addressee.eq.'+window._uid).then(function(r){ var rows=r.data||[]; var reqs=rows.filter(function(f){return f.status==='pending'&&f.addressee===window._uid;}); var acc=rows.filter(function(f){return f.status==='accepted';}); var ids=[]; rows.forEach(function(f){ ids.push(f.requester,f.addressee); }); ids=ids.filter(function(x){return x!==window._uid;}); window._friendIds=acc.map(function(f){return f.requester===window._uid?f.addressee:f.requester;}); if($('stFrCount')) $('stFrCount').textContent=reqs.length?('('+reqs.length+')'):''; if(!ids.length){ if($('stFriendReqs')) $('stFriendReqs').innerHTML='
لا طلبات
'; if($('stFriends')) $('stFriends').innerHTML='
لا أصدقاء بعد — أضفهم من قائمة اللاعبين
'; return; } sb.from('profiles').select('id,username,avatar_url').in('id',ids).then(function(pr){ var pm={}; (pr.data||[]).forEach(function(p){pm[p.id]=p;}); var row=function(pid,extra){ var p=pm[pid]||{}; var av=p.avatar_url?'':''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; return '
'+av+'
'+window.dot(pid)+esc(p.username||'لاعب')+'
'+extra+'
'; }; var bs='background:rgba(56,182,255,.1);border:1px solid var(--line2);color:var(--blue2);font-family:inherit;font-size:10px;font-weight:800;padding:5px 9px;border-radius:7px;cursor:pointer'; if($('stFriendReqs')) $('stFriendReqs').innerHTML = reqs.length? reqs.map(function(f){ var other=f.requester; return row(other,'
'); }).join('') : '
لا طلبات
'; if($('stFriends')) $('stFriends').innerHTML = acc.length? acc.map(function(f){ var other=f.requester===window._uid?f.addressee:f.requester; return row(other,'
'); }).join('') : '
لا أصدقاء بعد — أضفهم من قائمة اللاعبين
'; }); },function(){}); }; (function(){ /* ═══ المستمع على المستند لا على شاشةٍ بعينها ═══ كان مربوطاً بشاشة «الإعدادات» يوم كان الأصدقاء جزءاً منها. ثم انتقلوا إلى شاشة ‎#friends‎ فبقي المستمع حيث كان — فالأزرار تُرسم وتُضغط ولا يسمعها أحد. وربطُه بالمستند يُنهي هذا الصنف من الأعطال: أينما انتقلت البطاقة، الضغطة تصل. */ document.addEventListener('click',function(e){ var t=e.target&&e.target.closest?e.target.closest('[data-fracc],[data-frdel]'):null; if(!t) return; var a=t.getAttribute('data-fracc'), d=t.getAttribute('data-frdel'); t.disabled=true; /* ضغطتان لا تُنشئان طلبين */ var done=function(m){ toast(m); if(window.loadFriends) window.loadFriends(); }; var fail=function(r){ t.disabled=false; toast((r&&r.error&&r.error.message)?('تعذّر: '+r.error.message):'تعذّر — أعد المحاولة'); }; if(a){ sb.from('friends').update({status:'accepted'}).eq('id',a).select('id') .then(function(r){ if(r&&r.error) return fail(r); /* صفرُ صفوفٍ يعني أن السياسة حجبته بصمت — لا نقل «تم» كذباً */ if(!r||!r.data||!r.data.length) return fail({error:{message:'ما قدرت أقبله — حدّث الصفحة'}}); done('✅ صار صديقك'); }, fail); } else if(d){ sb.from('friends').delete().eq('id',d).select('id') .then(function(r){ if(r&&r.error) return fail(r); if(!r||!r.data||!r.data.length) return fail({error:{message:'ما قدرت أزيله — حدّث الصفحة'}}); done('تم'); }, fail); } }); var sp=document.getElementById('stSavePass'); if(sp) sp.addEventListener('click',function(){ var m=$('stPassMsg'), a=($('stPass1').value||''), b=($('stPass2').value||''); if(a.length<6){ m.style.color='var(--red)'; m.textContent='6 أحرف على الأقل'; return; } if(a!==b){ m.style.color='var(--red)'; m.textContent='الكلمتان غير متطابقتين'; return; } m.style.color='var(--gold)'; m.textContent='...جاري التحديث'; sb.auth.updateUser({password:a}).then(function(r){ if(r&&r.error){ m.style.color='var(--red)'; m.textContent=errAr(r.error.message); return; } m.style.color='#3ddc84'; m.textContent='✅ تم تحديث كلمة المرور'; $('stPass1').value=''; $('stPass2').value=''; }); }); var se=document.getElementById('stSaveEmail'); if(se) se.addEventListener('click',function(){ /* المعرّفُ في HTML هو stMailMsg — والاختلافُ عنه كان يقتلُ الضغطةَ صامتاً */ var m=$('stMailMsg')||$('stEmailMsg') ||{style:{},set textContent(v){ if(window.toast) toast(v); }}; var e=($('stEmail').value||'').trim(); if(!/^\S+@\S+\.\S+$/.test(e)){ m.style.color='var(--red)'; m.textContent='بريد غير صالح'; return; } m.style.color='var(--gold)'; m.textContent='...جاري التحديث'; /* ═══ لا نَعِد برسالةٍ قد لا تُرسل ═══ كنّا نقول دائماً «أُرسل رابط تأكيد». وتأكيدُ البريد مُطفأٌ في إعدادات المنصة، فلا رسالةَ تُرسل والتغيير يقع فوراً — فينتظر العضو بريداً لا يأتي ويظنّ الأمر معطّلاً. الردّ نفسه يقول الحقيقة: إن حمل ‎new_email‎ فالتأكيد مطلوب، وإلا فقد تمّ. */ sb.auth.updateUser({email:e}).then(function(r){ if(r&&r.error){ m.style.color='var(--red)'; m.textContent=errAr(r.error.message); return; } var u=r&&r.data&&r.data.user, pending=!!(u&&u.new_email); m.style.color='#3ddc84'; if(pending){ m.textContent='📧 أُرسل رابط تأكيد إلى «'+e+'» — افتحه لإكمال التغيير'; }else{ m.textContent='✅ صار بريدك «'+((u&&u.email)||e)+'» — استعمله بالدخول من الآن'; $('stEmail').value=''; $('stEmail').placeholder='الحالي: '+((u&&u.email)||e); } },function(){ m.style.color='var(--red)'; m.textContent='تعذّر الاتصال — أعد المحاولة'; }); }); var sv=document.getElementById('stSavePriv'); if(sv) sv.addEventListener('click',function(){ var m=$('stPrivMsg'); sb.from('profiles').update({dm_policy:$('stDm').value, wall_policy:$('stWall').value, visits_policy: (($('stVisits')||{}).value||'all'), notif_general: (($('stGen')||{}).value!=='0')}).eq('id',window._uid).then(function(r){ if(r&&r.error){ m.style.color='var(--red)'; m.textContent=errAr(r.error.message); return; } m.style.color='#3ddc84'; m.textContent='✅ حُفظت إعدادات الخصوصية'; }); }); })(); /* ===== الأصدقاء + الجدار من ملف اللاعب ===== */ window.addFriend=function(pid){ if(!window._uid){ go('account'); return; } sb.from('friends').insert({requester:window._uid,addressee:pid,status:'pending'}).then(function(r){ if(r&&r.error){ toast(/duplicate|unique/i.test(r.error.message||'')?'الطلب مُرسل مسبقاً':'تعذّر الإرسال'); return; } toast('👥 أُرسل طلب الصداقة'); window.loadPlayer&&window.loadPlayer(); }); }; window.loadWall=function(pid){ var box=$('plWall'); if(!box) return; sb.from('wall_posts').select('id,author_id,body,created_at,reply_to').eq('profile_id',pid).order('id',{ascending:false}).limit(60).then(function(r){ var rows=r.data||[]; var ids=rows.map(function(x){return x.author_id;}); if(!rows.length){ box.innerHTML='
لا تعليقات بعد — كن الأول 👋
'; return; } sb.from('profiles').select('id,username,avatar_url,style_id').in('id',ids).then(function(pr){ var pm={}; (pr.data||[]).forEach(function(p){pm[p.id]=p;}); var kids={}; rows.forEach(function(w){ if(w.reply_to){ (kids[w.reply_to]=kids[w.reply_to]||[]).push(w); } }); Object.keys(kids).forEach(function(k){ kids[k].sort(function(a,b){return a.id-b.id;}); }); var one=function(w,isReply){ var a=pm[w.author_id]||{}, sz=isReply?25:30; var av=a.avatar_url?'':''+esc(String(a.username||'?').slice(0,2).toUpperCase())+''; var can=(window._uid===w.author_id)||(window._uid===pid)||window._isStaff; var wf=window.fxOf(w.author_id,a.style_id); var wNm=wf?window.fxName(wf,a.username||'لاعب',true):(''+esc(a.username||'لاعب')+''); var wb=window.fxBubble(wf); return ''+av+ '
'+wNm+''+tDT(w.created_at)+'
'+ '
'+esc(w.body||'')+'
'+ (window._uid&&!isReply?'':'')+ '
'+ (can?'':'')+''; }; box.innerHTML=rows.filter(function(w){return !w.reply_to;}).map(function(w){ var rep=(kids[w.id]||[]).map(function(k){return one(k,true);}).join(''); return one(w,false)+(rep?'
'+rep+'
':''); }).join(''); }); },function(){}); }; window.replyWall=function(id,name){ window._wallReply=id; var bar=$('plReplyTo'), tx=$('plReplyTx'), i=$('plWallInput'); if(tx) tx.textContent='↩ ترد على '+name; if(bar) bar.hidden=false; if(i){ i.focus(); i.scrollIntoView({behavior:'smooth',block:'center'}); } }; window.cancelReply=function(){ window._wallReply=null; var b=$('plReplyTo'); if(b) b.hidden=true; }; /* عضو في كلان ⇒ ما يبحث عن كلان (منطق بديهي) */ window.applyClanContext=function(){ var cid=window._myClanId||window._myMemberClanId; var tag=window._myClanTag||window._myMemberClanTag||''; var owner=!!window._myClanId; var inClan=!!cid; /* «أبحث عن كلان» لا تُعرض إلا لمن نتيقّن أنه بلا كلان */ var sc=$('seekCard'); if(sc) sc.hidden = inClan || !window._uid; var cs=$('cdirSeek'); if(cs) cs.hidden = inClan || !window._uid; var crb=$('cdirRegBtn'); if(crb) crb.hidden = inClan; var af=$('agFindClan'); if(af) af.hidden = inClan; var sk=document.getElementById('seek'); if(inClan && sk && sk.classList.contains('active')) go('clandir'); /* ═══ مجموعة «كلاني» ═══ صفوف تُلخّص حالتك في الكلان */ var box=$('acClanRows'), grp=$('acGrpClan'); if(!box||!grp) return; var R=function(ic,t,sub2,val,cls,act){ return ''; }; if(inClan){ grp.hidden=false; var rl = window._myMemberRole==='leader' ? 'رئيس الكلان' : (window._myMemberRole==='vice' ? 'نائب الرئيس' : (window._myMemberRole==='coordinator' ? 'منسق كلان' : 'عضو')); var ric = window._myMemberRole==='leader' ? '👑' : (window._myMemberRole==='vice' ? '🎖️' : (window._myMemberRole==='coordinator' ? '📋' : '🎮')); var h2=''; /* صفٌّ واحد: صاحب الكلان يدخل الإدارة مباشرة، وغيره يفتح صفحة كلانه */ h2 += R(ric, 'كلاني', 'CLAN '+(tag||'؟')+' · أنت '+rl, '', '', canManage ? "go('manageclan')" : ("openClan('"+cid+"')")); /* الرئيس والنائب يديران الكلان، العضو العادي يغادر */ var canManage = owner || window._myMemberRole==='leader' || window._myMemberRole==='vice'; if(!canManage) h2 += ''; box.innerHTML=h2; } else { grp.hidden=false; var base = R('🚪','ابحث عن كلان','كلانات تستقبل أعضاء الآن','','', "go('hiring')"); /* عنده كلان مؤرشف؟ نوجّهه للأرشيف بدل تسجيل كلان جديد سيُرفض */ sb.from('clans').select('id,tag').eq('created_by',window._uid||'00000000-0000-0000-0000-000000000000') .eq('status','archived').is('deleted_at',null).limit(1).then(function(ar){ var a=ar&&ar.data&&ar.data[0]; box.innerHTML = (a ? R('🔒','كلانك CLAN '+String(a.tag)+' بالأرشيف','استرجعه أو احذفه لتسجّل غيره','بالأرشيف','off', "go('archive')") : R('👑','سجّل كلانك','تصير رئيسه وتضيف أعضاءك','','', "go('createclan')")) + base; },function(){ box.innerHTML = R('👑','سجّل كلانك','تصير رئيسه وتضيف أعضاءك','','', "go('createclan')") + base; }); } /* شريط الإحصاءات: تاق كلانك */ var sc=$('acStClan'); if(sc) sc.textContent = inClan ? (tag||'—') : '—'; }; window.postWall=function(pid){ if(_blocked()) return; var i=$('plWallInput'); if(!i) return; var b=(i.value||'').trim(); if(!b) return; if(b.length>300) b=b.slice(0,300); if(!window._uid){ go('account'); return; } var row={profile_id:pid,author_id:window._uid,body:b}; if(window._wallReply) row.reply_to=window._wallReply; sb.from('wall_posts').insert(row).then(function(r){ if(r&&r.error){ toast(/policy|violates/i.test(r.error.message||'')?'🔒 هذا اللاعب يقيّد الكتابة على جداره':'تعذّر النشر: '+String(r.error.message||'').slice(0,60)); return; } i.value=''; window.cancelReply(); window.loadWall(pid); /* الإشعار تُنشئه القاعدة بمحفّز ‎trg_notify_wall‎ — كان المتصفّح يحاول إنشاءه فيُردّ، لأن العضو لا يملك أن يُشعر غيره. */ }); }; (function(){ var p=document.getElementById('player'); if(!p) return; p.addEventListener('click',function(e){ var t=e.target; if(!t.getAttribute) return; var d=t.getAttribute('data-wdel'); if(d) return sb.from('wall_posts').delete().eq('id',d).then(function(){ window.loadWall(window._viewPlayer); }); var rp=t.getAttribute('data-wrep'); if(rp) window.replyWall(rp, t.getAttribute('data-wrepn')||'لاعب'); }); })(); /* ═══ تجربة اللمسات على المنصة نفسها ═══ ?fxtest=clash | win | lose — عرضٌ محليّ على جهاز الفاتح وحده، لا يمسّ أحداً ولا يكتب شيئاً. */ try{ var _fm=/[?&]fxtest=(clash|win|lose)/.exec(location.search||''); if(_fm){ setTimeout(function(){ if(_fm[1]==='clash') window.fxClash('RLX','BATL','🔭 سنايبر · أفضل من 6', null); else window.fxResult({mine:'RLX', opp:'BATL', myScore:_fm[1]==='lose'?1:4, oppScore:_fm[1]==='lose'?4:1, won:_fm[1]!=='lose', pts:18}); }, 2200); } }catch(e){} /* ═══════════ وضع الإطلاق ═══════════ الحارس في القاعدة يرفض أصلاً — هذي الواجهة تقول ذلك بلطفٍ قبل المحاولة: سجّلوا كلاناتكم، والتحديات على الأبواب. */ window.applyLaunchMode=function(){ var off=((window._appSet||{}).challenges_open==='0'); var btn=document.getElementById('btnGoNewCh'); var fab=document.getElementById('fabCh'); var note=document.getElementById('launchNote'); if(off){ if(btn) btn.style.display='none'; if(fab) fab.style.display='none'; if(!note){ var sec=document.getElementById('challenges'); var sub=sec?sec.querySelector('.ssub'):null; if(sub){ note=document.createElement('div'); note.id='launchNote'; note.style.cssText='margin:4px 14px 12px;border-radius:15px;padding:14px 15px;text-align:center;'+ 'background:linear-gradient(135deg,rgba(232,179,75,.14),rgba(232,179,75,.03));'+ 'border:1px solid rgba(232,179,75,.4);font-size:12.5px;line-height:2'; note.innerHTML='🚀 المنصة في مرحلة التسجيل
'+ 'سجّلوا كلاناتكم وجهّزوا صفوفكم — التحديات تُفتح قريباً ⚔️
'+ '🛡️ سجّل كلانك الآن'; sub.parentNode.insertBefore(note, sub.nextSibling); } } else note.hidden=false; } else { if(btn) btn.style.display=''; if(fab) fab.style.display=''; if(note) note.hidden=true; } }; /* تغيّر المفتاح من اللوحة يصل حيّاً — الإعدادات مُشترَك عليها أصلاً */ setInterval(function(){ var sc=document.getElementById('challenges'); if(sc && sc.classList.contains('active') && window.applyLaunchMode) window.applyLaunchMode(); }, 5000); /* ═══ صفحة القوانين: شريطٌ يتبع قراءتك، وأقسام تظهر بهدوء ═══ */ (function(){ var sec=document.getElementById('rules'); if(!sec) return; var nav=document.getElementById('rlNav'); if(nav) nav.addEventListener('click',function(e){ var b=e.target.closest('button[data-rl]'); if(!b) return; var t=document.getElementById(b.getAttribute('data-rl')); if(t) t.scrollIntoView({behavior:'smooth',block:'start'}); }); /* ═══ باحث القوانين ═══ يكتب «تيباق» فيجد T-Bag — البحث يتجاهل التشكيل وصور الحروف. */ function rlNorm(t){ return String(t==null?'':t) .replace(/[\u064B-\u0652\u0670\u0640]/g,'') .replace(/[\u0622\u0623\u0625\u0671]/g,'\u0627') .replace(/\u0629/g,'\u0647').replace(/\u0649/g,'\u064A') .toLowerCase(); } /* المصطلحات الأجنبية تُكتب كما تُنطق: «تيباق» تجد T-Bag */ var RL_ALIAS=[[/تي ?باق|تيباك/g,'t-bag'],[/كيل ?ستريك/g,'killstreak'], [/سبيكتيت|سبكتيت/g,'spectate'],[/باور ?دايركتور|بور ?دايركتور/g,'powerdirector'], [/كيل ?كام/g,'kill cam'],[/تايم ?اوت/g,'time out'],[/اوفلاين|أوفلاين/g,'offline'], [/كلان ?تاق|كلان ?تاغ/g,'clan tag'],[/فيرست ?بلود/g,'first blood'],[/قلتش|جلتش/g,'قلتش']]; var rlQ=document.getElementById('rlQ'); if(rlQ) rlQ.addEventListener('input',function(){ var q=rlNorm(rlQ.value.trim()); RL_ALIAS.forEach(function(a){ q=q.replace(a[0],a[1]); }); var n=0; [].forEach.call(sec.querySelectorAll('.rl__sec'),function(sc){ var any=false; [].forEach.call(sc.querySelectorAll('.rl__r, .rl__note'),function(r){ var hit=!q || rlNorm(r.textContent).indexOf(q)!==-1; r.classList.toggle('rlhide', !hit); if(hit && q) n++; if(hit) any=true; }); /* العناوين الفرعية والرسوم تظهر مع قسمها كاملاً أو تختفي مع البحث */ [].forEach.call(sc.querySelectorAll('.rl__sub, .rl__zoom, .rl__stats'),function(x){ x.classList.toggle('rlhide', !!q && !any ? true : !!q); }); sc.classList.toggle('rlhide', !!q && !any); }); var c=document.getElementById('rlQN'); if(c) c.textContent = q ? (n? ('🔎 '+n+' قاعدة مطابقة') : 'ما فيه قاعدة تطابق بحثك — جرّب كلمة أقصر') : ''; var nv=document.getElementById('rlNav'); if(nv) nv.style.display = q ? 'none' : ''; }); var io1=null; window._rlBoot=function(){ if(io1) return; try{ io1=new IntersectionObserver(function(es){ es.forEach(function(en){ if(en.isIntersecting){ en.target.classList.add('vis'); var id=en.target.id; if(nav) [].forEach.call(nav.querySelectorAll('button'),function(b){ b.classList.toggle('on', b.getAttribute('data-rl')===id); }); } }); },{threshold:.25}); [].forEach.call(sec.querySelectorAll('.rl__sec'),function(x){ io1.observe(x); }); }catch(e){ [].forEach.call(sec.querySelectorAll('.rl__sec'),function(x){ x.classList.add('vis'); }); } /* شبكة أمان: إن لم يعمل الكشف لأي سبب، تظهر الأقسام كلها — الحركة زينة، والمحتوى لا يُرهن بها */ setTimeout(function(){ [].forEach.call(sec.querySelectorAll('.rl__sec:not(.vis)'),function(x){ x.classList.add('vis'); }); }, 1200); }; })(); /* ═══════════════ التواصل مع الإدارة ═══════════════ تذكرة برقمٍ مميز #T، والردّ على التذكرة نفسها لا برسائل متناثرة — وكل ردٍّ يوصل صاحبه إشعاراً على الجرس والجوال. */ var TK_ST={ open: ['⏳ بانتظار الردّ','rgba(255,204,0,.13)','rgba(255,204,0,.4)','var(--gold)'], answered: ['✅ تم الردّ','rgba(61,220,132,.13)','rgba(61,220,132,.4)','#3ddc84'], closed: ['🔒 مغلقة','rgba(255,255,255,.05)','var(--line)','var(--tx3)'] }; function tkChip(st){ var c=TK_ST[st]||TK_ST.open; return ''+c[0]+''; } function tkLife(ts){ var ttl=window._tkTTL||30; var ms=typeof ts==='number'?ts:new Date(ts).getTime(); var rem=ms+ttl*86400000-Date.now(); if(rem<=0) return '⏱️ منتهية'; var d=Math.floor(rem/86400000), h=Math.floor((rem%86400000)/3600000); if(d>0) return '⏱️ '+d+' يوم'+(h?' و'+h+' س':'')+''; return '⏱️ '+h+' ساعة'; } /* ═══ تذاكر الزائر على جهازه ═══ المفتاح السرّي لا يغادر التخزين المحلي */ function gtkAll(){ try{ return JSON.parse(localStorage.getItem('codar_gtk')||'[]')||[]; }catch(e){ return []; } } function gtkSave(list){ try{ localStorage.setItem('codar_gtk', JSON.stringify(list.slice(-30))); }catch(e){} } function gtkFind(id){ return gtkAll().filter(function(t){ return t.id===id; })[0]||null; } /* ═══ الأنواعُ للزائرِ أيضاً ═══ الكتلةُ داخلَ `loadSupport` تقرؤها **بعدَ** خروجِ الزائر، فيبقى على قائمةٍ مكتوبةٍ في الوسمِ لا تُطابِقُ اللوحة. وهذه تُنادى قبلَ الخروج. وهي أيضاً بابُ `?s=support&kind=…` الذي تنزلُ عليه إعلاناتُ الرئيسة. */ window._supKinds=function(){ sb.from('settings').select('key,value').in('key',['ticket_kinds','ticket_off']).then(function(r){ var m={}; ((r&&r.data)||[]).forEach(function(x){ m[x.key]=x.value; }); var sel=$('supKind'); if(!sel) return; try{ var arr=JSON.parse(m.ticket_kinds||'null'); if(Array.isArray(arr) && arr.length) sel.innerHTML=arr.map(function(k){ return ''; }).join(''); }catch(e){} /* ═══ نوعٌ جاءَ من رابطِ إعلان ═══ يُطابَقُ بالاحتواءِ لا بالتساوي: الأنواعُ تُحرَّرُ من اللوحةِ فقد يتغيّرُ إيموجيها أو تُزادُ كلمة، **ورابطُ الإعلانِ منشورٌ لا يُعدَّل**. ولا يُفتَحُ النموذجُ إن كان الاستقبالُ موقوفاً — وعدٌ لا يُوفى. */ var want=String(window._tkKind||''); if(!want) return; window._tkKind=null; if(m.ticket_off==='1') return; var hit=null; for(var k=0;k=0 || want.indexOf(v)>=0){ hit=v; break; } } if(!hit) return; sel.value=hit; var fm=$('supForm'); if(fm) fm.hidden=false; setTimeout(function(){ try{ (fm||sel).scrollIntoView({block:'center',behavior:'smooth'}); }catch(e){} },320); },function(){}); }; window.loadSupport=function(){ var box=$('supList'); if(!box) return; window._supKinds(); var contact=$('supContact'); if(contact) contact.style.display=window._uid?'none':''; sb.rpc('is_apply_open').then(function(r){ var d=r&&r.data; if(!d) d={open:false,ttl:30}; var open=d.open===true; window._tkTTL=d.ttl||30; if($('applyOn')) $('applyOn').hidden=!open; if($('applyOff')) $('applyOff').hidden=open; if($('applyContact')) $('applyContact').style.display=window._uid?'none':''; var tn=$('tkTTLNote'); if(tn) tn.innerHTML='⏱️ التذكرة صالحة لمدة '+window._tkTTL+' يوم'; },function(){}); if(!window._uid){ var mine=gtkAll().slice().reverse(); var c0=$('supCount'); if(c0) c0.textContent=mine.length?(mine.length+' تذكرة على هذا الجهاز'):''; box.innerHTML = mine.length ? mine.map(function(t){ return '
'+ '#T'+t.num+''+ '
'+esc(t.subject||'تذكرة')+''+ ''+esc(t.kind||'')+'
'+tkLife(t.at)+'
'; }).join('') : '
🎫 تقدر تفتح تذكرة بلا حساب
واحفظ الرابط — تذكرتك تبقى على هذا الجهاز
'; return; } /* الأنواع ومفتاح الاستقبال بيد الأدمن — تُقرأ مع كل فتح */ sb.from('settings').select('key,value').in('key',['ticket_kinds','ticket_off']).then(function(r){ var m={}; ((r&&r.data)||[]).forEach(function(x){ m[x.key]=x.value; }); try{ var arr=JSON.parse(m.ticket_kinds||'null'); var sel=$('supKind'); if(sel && Array.isArray(arr) && arr.length) sel.innerHTML=arr.map(function(k){ return ''; }).join(''); }catch(e){} var off=(m.ticket_off==='1'); var entry=document.querySelector('#support .entry'); var form=$('supForm'); if(entry) entry.style.display=off?'none':''; if(off && form){ form.hidden=true; } var note=document.getElementById('supOffNote'); if(off && !note && entry){ note=document.createElement('div'); note.id='supOffNote'; note.style.cssText='background:rgba(255,204,0,.08);border:1px solid rgba(255,204,0,.35);border-radius:13px;padding:12px 14px;font-size:12px;line-height:1.9;color:var(--tx2)'; note.innerHTML='⏸️ استقبال التذاكر موقف مؤقتاً
تذاكرك السابقة تحت وتوصلك ردودها — والفتح يرجع قريباً.'; entry.parentNode.insertBefore(note, entry); } else if(!off && note){ note.remove(); } },function(){}); sb.from('support_tickets').select('id,num,kind,subject,status,created_at') .eq('profile_id',window._uid).order('created_at',{ascending:false}).limit(50).then(function(r){ var rows=r.data||[]; var c=$('supCount'); if(c) c.textContent=rows.length?(rows.length+' تذكرة'):''; box.innerHTML = rows.length ? rows.map(function(t){ return '
'+ '#T'+t.num+''+ '
'+esc(t.subject||t.kind||'تذكرة')+''+ ''+esc(t.kind||'')+' · '+tDT(t.created_at)+'
'+tkLife(t.created_at)+'
'+ tkChip(t.status)+'
'; }).join('') : '
ما فتحت تذاكر بعد 🎫
أي شكوى أو طلب — افتح تذكرة وتوصل الإدارة فوراً
'; }); }; (function(){ var b=$('supSend'); if(b) b.addEventListener('click',function(){ var m=$('supMsg'); var kind=($('supKind')||{}).value||'أخرى'; var subj=(($('supSubject')||{}).value||'').trim(); var body=(($('supBody')||{}).value||'').trim(); if(subj.length<4){ m.style.color='var(--red)'; m.textContent='اكتب عنواناً واضحاً (4 أحرف فأكثر)'; return; } if(body.length<10){ m.style.color='var(--red)'; m.textContent='اشرح التفاصيل — 10 أحرف على الأقل'; return; } b.disabled=true; m.style.color='var(--gold)'; m.textContent='...إرسال'; var done=function(num){ b.disabled=false; $('supSubject').value=''; $('supBody').value=''; var f=$('supForm'); if(f) f.hidden=true; toast('🎫 فُتحت تذكرتك #T'+num+(window._uid?' — الردّ يوصلك بإشعار':' — احفظها، هي على هذا الجهاز')); window.loadSupport(); }; if(window._uid){ sb.from('support_tickets').insert({kind:kind, subject:subj, details:body, status:'open', profile_id:window._uid}) .select('id,num').single().then(function(r){ if(r&&r.error){ b.disabled=false; m.style.color='var(--red)'; m.textContent=errAr(r.error.message); return; } done(r.data&&r.data.num); }); } else { /* الزائر: عبر البوّابة — يُمنح مفتاحاً سرّياً يبقى على جهازه */ sb.rpc('guest_ticket_create',{p_kind:kind, p_subject:subj, p_details:body, p_contact:(($('supContact')||{}).value||'').trim()||null}).then(function(r){ var d=r&&r.data; if(r&&r.error){ b.disabled=false; m.style.color='var(--red)'; m.textContent='تعذّر الفتح'; return; } if(!d||!d.ok){ b.disabled=false; m.style.color='var(--red)'; m.textContent = d&&d.why==='flood' ? 'ضغط مؤقت — جرّب بعد قليل' : 'راجع العنوان والتفاصيل'; return; } var list=gtkAll(); list.push({id:d.id, num:d.num, token:d.token, subject:subj, kind:kind, at:Date.now()}); gtkSave(list); done(d.num); }); } }); })(); /* ═══ تقديم على الإشراف ═══ */ (function(){ var b=$('applySend'); if(b) b.addEventListener('click',function(){ var m=$('applyMsgBox'); var name=(($('applyName')||{}).value||'').trim(); var phone=(($('applyPhone')||{}).value||'').trim(); var psn=(($('applyPsn')||{}).value||'').trim(); var clan=(($('applyClan')||{}).value||'').trim(); var msg=(($('applyMsg')||{}).value||'').trim(); if(name.length<2){ m.style.color='var(--red)'; m.textContent='اكتب اسمك'; return; } if(phone.length<7){ m.style.color='var(--red)'; m.textContent='اكتب رقم جوالك'; return; } if(psn.length<3){ m.style.color='var(--red)'; m.textContent='اكتب PSN ID'; return; } if(clan.length<2){ m.style.color='var(--red)'; m.textContent='اكتب اسم كلانك'; return; } b.disabled=true; m.style.color='var(--gold)'; m.textContent='...إرسال'; var kind='تقديم على الإشراف'; var details='الاسم: '+name+'\nالجوال: '+phone+'\nPSN ID: '+psn+'\nالكلان: '+clan+(msg?'\n───\n'+msg:''); var subj='طلب إشراف — '+name; var done=function(num){ b.disabled=false; $('applyName').value=''; $('applyPhone').value=''; $('applyPsn').value=''; $('applyClan').value=''; $('applyMsg').value=''; var f=$('applyForm'); if(f) f.hidden=true; toast('🎖️ أُرسل طلبك #T'+num+' — سيُراجع وتصلك النتيجة'); window.loadSupport(); }; if(window._uid){ sb.from('support_tickets').insert({kind:kind, subject:subj, details:details, status:'open', profile_id:window._uid}) .select('id,num').single().then(function(r){ if(r&&r.error){ b.disabled=false; m.style.color='var(--red)'; m.textContent=errAr(r.error.message); return; } done(r.data&&r.data.num); }); } else { sb.rpc('guest_ticket_create',{p_kind:kind, p_subject:subj, p_details:details, p_contact:(($('applyContact')||{}).value||'').trim()||null}).then(function(r){ var d=r&&r.data; if(r&&r.error){ b.disabled=false; m.style.color='var(--red)'; m.textContent='تعذّر الإرسال'; return; } if(!d||!d.ok){ b.disabled=false; m.style.color='var(--red)'; m.textContent=d&&d.why==='flood'?'ضغط مؤقت — جرّب بعد قليل':'راجع البيانات'; return; } var list=gtkAll(); list.push({id:d.id,num:d.num,token:d.token,subject:subj,kind:kind,at:Date.now()}); gtkSave(list); done(d.num); }); } }); })(); window._tk={}; window.openTicket=function(id){ if(!window._uid && !gtkFind(id)){ go('account'); return; } window._tk={id:id}; go('ticket'); }; window.loadTicket=function(){ var id=window._tk.id, box=$('tkMsgs'); if(!id||!box) return; box.innerHTML='
⏳ جاري الفتح...
'; var gt=window._uid?null:gtkFind(id); window._tk.token=gt?gt.token:null; var fetchP = window._uid ? Promise.all([ sb.from('support_tickets').select('*').eq('id',id).single(), sb.from('ticket_msgs').select('*').eq('ticket_id',id).order('id').limit(200) ]).then(function(r){ return {t:r[0]&&r[0].data, msgs:(r[1]&&r[1].data)||[]}; }) : sb.rpc('guest_ticket_get',{p_id:id, p_token:gt&>.token}).then(function(r){ var d=r&&r.data; if(!d||!d.ok) return {t:null,msgs:[]}; return {t:d.ticket, msgs:d.msgs||[]}; }); fetchP.then(function(rr){ if(window._tk.id!==id) return; var t=rr.t; if(!t){ box.innerHTML='
التذكرة غير موجودة — أو ليست على هذا الجهاز
'; return; } window._tk.status=t.status; if($('tkTitle')) $('tkTitle').innerHTML='🎫 #T'+t.num+' — '+esc(t.subject||'تذكرة'); if($('tkSub')) $('tkSub').innerHTML=esc(t.kind||'')+' · '+tkChip(t.status)+' · '+tkLife(t.created_at); var msgs=rr.msgs; /* ═══ ردّ الأدمن بوجهه واسمه ═══ لا «الإدارة» المجهولة */ var staffIds=msgs.filter(function(m){return m.staff&&m.author_id;}).map(function(m){return m.author_id;}); var SP=window._tkStaff||{}; var needIds=staffIds.filter(function(x){return !SP[x];}); if(needIds.length && window._uid!==undefined){ sb.from('profiles').select('id,username,avatar_url,role').in('id',needIds).then(function(pr){ (pr&&pr.data||[]).forEach(function(p){ SP[p.id]=p; }); window._tkStaff=SP; if(window._tk.id===id) window.loadTicket(); },function(){}); } var att=function(m){ if(!m.media_url) return ''; var mu=String(m.media_url).replace(/"/g,'"'); if(m.kind==='audio') return ''; if(window.isVid(mu)) return ''+ ''+ ''; return ''; }; var h='
'+ '
📨 نصّ التذكرة · '+tDT(t.created_at)+'
'+ '
'+esc(t.details||'—')+'
'; h+=msgs.map(function(m){ var mine=window._uid ? (m.author_id===window._uid) : !m.staff; var who; if(m.staff){ var sp=SP[m.author_id]; var nm=sp?sp.username:'الإدارة'; var av=sp&&sp.avatar_url ? '' : ''+esc(String(nm).slice(0,1).toUpperCase())+''; who=av+'🛡️ '+esc(nm)+' ⓘ'; } else who=(mine?'أنت':'👤 صاحب التذكرة'); return '
'+ '
'+ '
'+who+' · '+tHM(m.created_at)+'
'+ '
'+esc(m.body)+'
'+att(m)+'
'; }).join(''); box.innerHTML=h; var closed=(t.status==='closed'); if($('tkBar')) $('tkBar').style.display=closed?'none':'flex'; var cn=$('tkClosed'); if(cn){ cn.hidden=!closed; if(closed) cn.innerHTML='🔒 أُغلقت هذه التذكرة
'+ (t.resolution?esc(t.resolution)+'
':'')+'إن بقي عندك شيء افتح تذكرة جديدة.'; } window.scrollTo(0, document.body.scrollHeight); /* البثّ الحيّ: الردّ يظهر وأنت واقف */ if(window._tk.sub){ try{ sb.removeChannel(window._tk.sub); }catch(e){} } if(window._uid){ window._tk.sub=sb.channel('tk-'+id) .on('postgres_changes',{event:'INSERT',schema:'public',table:'ticket_msgs',filter:'ticket_id=eq.'+id}, function(){ if(window._tk.id===id) window.loadTicket(); }) .subscribe(function(){}); } else { /* الزائر بلا مقبس (بالتصميم) — استطلاعٌ هادئ ما دام في الصفحة */ clearInterval(window._tk.poll); window._tk.poll=setInterval(function(){ if(document.hidden) return; var sc=document.getElementById('ticket'); if(!sc||!sc.classList.contains('active')||window._tk.id!==id){ clearInterval(window._tk.poll); return; } window.loadTicket(); }, 15000); } }); }; (function(){ function sendMsg(txt, media, mkind){ if(!window._tk.id) return; if(window._uid){ var isStaff=!!(window._perms && window._perms.mod_tickets); sb.from('ticket_msgs').insert({ticket_id:window._tk.id, author_id:window._uid, staff:isStaff, body: txt||'📎 مرفق', media_url:media||null, kind:mkind||null}).then(function(r){ if(r&&r.error){ toast(/policy|permission/i.test(r.error.message)?'التذكرة مغلقة — افتح جديدة':'تعذّر الإرسال'); return; } if(!isStaff) sb.from('support_tickets').update({status:'open'}).eq('id',window._tk.id).then(function(){}); window.loadTicket(); }); } else { sb.rpc('guest_ticket_reply',{p_id:window._tk.id, p_token:window._tk.token, p_body:txt||'', p_media:media||null, p_kind:mkind||null}).then(function(r){ var d=r&&r.data; if(r&&r.error||!d||!d.ok){ toast(d&&d.why==='slow'?'⏳ رسالة كل 10 ثوانٍ':d&&d.why==='closed'?'التذكرة مغلقة':'تعذّر الإرسال'); return; } window.loadTicket(); }); } } function send(){ var i=$('tkInput'); if(!i) return; var t=(i.value||'').trim(); if(!t) return; i.value=''; sendMsg(t, null, null); } var b=$('tkSend'); if(b) b.addEventListener('click',send); var i=$('tkInput'); if(i) i.addEventListener('keydown',function(e){ if(e.key==='Enter') send(); }); /* ═══ الإرفاق ═══ عبر خدمة الوسائط بفحص البصمة والسقف */ var fb=$('tkAttach'), fi=$('tkFile'); if(fb&&fi){ fb.addEventListener('click',function(){ fi.click(); }); fi.addEventListener('change',function(){ var f=fi.files&&fi.files[0]; if(!f) return; fi.value=''; /* ═══ السقفُ يُقال قبلَ الرفعِ لا بعدَه ═══ كان الملفُّ يُرفَعُ كاملاً ثمّ يردُّه الخادمُ بـ413 — أي أنّ العضوَ يدفعُ ثمنَ الرفعِ كلَّه ليُقالَ له «كبير». صار يُقالُ له أوّلاً. */ var lim = window._uid ? (window.UP_MAX_MB||100)*1024*1024 : 12*1024*1024; if(f.size>lim){ toast('⚠️ الملف أكبر من '+Math.round(lim/1048576)+'MB'); return; } toast('⏳ جاري رفع المرفق...'); var hdrs={'x-codar-kind':'ticket','x-codar-ticket':window._tk.id, 'x-codar-media':(/^video\//.test(f.type||'')?'video':/^audio\//.test(f.type||'')?'audio':'image')}; var pre = window._uid ? sb.auth.getSession().then(function(r){ var tk=r&&r.data&&r.data.session&&r.data.session.access_token; if(tk) hdrs['Authorization']='Bearer '+tk; }) : Promise.resolve(hdrs['x-codar-token']=window._tk.token); /* الصورُ تُضغَطُ هنا كما تُضغَطُ في كلِّ باب — كانت التذكرةُ الثغرةَ الوحيدةَ التي تمرُّ منها صورةُ جوّالٍ بحجمِها الكامل. */ var seen=-1; Promise.resolve(pre).then(function(){ return window.shrinkImg(f); }).then(function(blob){ return window.upXHR(hdrs, blob, function(p){ var st=Math.floor(p*5); /* كلَّ خُمسٍ لا كلَّ بايت — التنبيهُ لا يُزاحَم */ if(st>seen && p<1){ seen=st; toast('⏳ جاري رفع المرفق… '+(st*20)+'%'); } }); }).then(function(x){ var d=x&&x.j; if(!x.ok||!d||!d.ok){ toast('تعذّر الرفع: '+((d&&d.error)||'؟')); return; } var cap=(($('tkInput')||{}).value||'').trim(); if($('tkInput')) $('tkInput').value=''; sendMsg(cap, d.url, d.kind); }).catch(function(){ toast('تعذّر الرفع'); }); }); } })(); /* ═══════════════ نصوص المنصة ═══════════════ الأدمن يحرّرها من اللوحة وينشرها، وهنا تُطبَّق على الصفحة: خريطةُ «الأصل ← البديل» تُبدَّل في نصوص العناصر وفي حقول الإدخال والعناوين. والمحتوى يُرسَم بعد الجلب، فنراقب ما يُضاف ونطبّق عليه. تكلفتها صفر لمن لم ينشر شيئاً: بلا خريطة لا مراقبة ولا مرور. */ window._txtMap=null; (function(){ var SKIP={SCRIPT:1,STYLE:1,TEXTAREA:1,NOSCRIPT:1}; var ATTRS=['placeholder','title','aria-label']; var pending=null, obs=null; function swapIn(root){ var M=window._txtMap; if(!M||!root) return; /* النصوص */ try{ var w=document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode:function(n){ if(!n.nodeValue || n.nodeValue.length<2) return NodeFilter.FILTER_REJECT; var p=n.parentNode; if(!p || SKIP[p.nodeName]) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; } }); var n, hits=[]; while((n=w.nextNode())){ var k=n.nodeValue.trim(); if(k && M[k]!==undefined && M[k]!==k) hits.push(n); } hits.forEach(function(x){ var k=x.nodeValue.trim(); x.nodeValue=x.nodeValue.replace(k, M[k]); }); }catch(e){} /* الخانات والعناوين */ try{ var els=(root.querySelectorAll?root.querySelectorAll('[placeholder],[title],[aria-label]'):[]); [].forEach.call(els, function(el){ ATTRS.forEach(function(a){ var v=el.getAttribute(a); if(!v) return; var k=v.trim(); if(M[k]!==undefined && M[k]!==k) el.setAttribute(a, M[k]); }); }); if(root.nodeType===1) ATTRS.forEach(function(a){ var v=root.getAttribute&&root.getAttribute(a); if(!v) return; var k=v.trim(); if(M[k]!==undefined && M[k]!==k) root.setAttribute(a, M[k]); }); }catch(e){} } function sweep(){ swapIn(document.body); } function schedule(){ clearTimeout(pending); pending=setTimeout(sweep, 90); } window.applyTexts=function(map){ if(!map || !Object.keys(map).length){ window._txtMap=null; return; } window._txtMap=map; sweep(); if(obs) return; obs=new MutationObserver(function(muts){ for(var i=0;i200){ keys.sort(function(a,b){return st[a]-st[b];}).slice(0,100).forEach(function(x){ delete st[x]; }); } localStorage.setItem('codar_fx', JSON.stringify(st)); }catch(e){} return true; } /* ① الاصطدام — لحظة قبول التحدي */ window.fxClash=function(mineTag, oppTag, sub, chId, plain, onGo){ if(!window.fxOn('clash')) return; var el=document.getElementById('fxClash'); if(!el) return; var i=function(t){ return String(t||'؟').slice(0,2).toUpperCase(); }; document.getElementById('fxcA').textContent=i(mineTag); document.getElementById('fxcB').textContent=i(oppTag); document.getElementById('fxcAt').textContent=plain?String(mineTag||'؟'):('CLAN '+String(mineTag||'؟').toUpperCase()); document.getElementById('fxcBt').textContent=plain?String(oppTag||'؟'):('CLAN '+String(oppTag||'؟').toUpperCase()); document.getElementById('fxcSub').textContent=sub||'جهّزوا فريقكم'; var go=document.getElementById('fxcGo'); go.onclick=function(e){ e.stopPropagation(); window.fxClose('fxClash'); if(onGo) onGo(); else if(chId&&window.openChChat) window.openChChat(chId); }; el.classList.remove('play'); void el.offsetWidth; el.classList.add('on','play'); clearTimeout(window._fxT); window._fxT=setTimeout(function(){ window.fxClose('fxClash'); }, 4200); }; /* ② إعلان النتيجة */ /* ═══ تحديث الرتبة المتحرك ═══ يظهر عند اعتماد نتيجة تغيّر نقاط كلانك */ window.fxRankProgress=function(oldPts, newPts){ if(!window.RANKS_ON || !window.fxOn || !window.fxOn('result')) return; if(newPts===oldPts) return; window.rankTiers(function(T){ if(!T || !T.length) return; var tierAt=function(p){ var cur=T[0]; for(var i=0;i=T[i].min_points) cur=T[i]; } return cur; }; var nextAfter=function(cur){ for(var i=0;icur.level) return T[i]; } return null; }; var curOld=tierAt(oldPts), curNew=tierAt(newPts), leveledUp=curNew.level>curOld.level; var nxt=nextAfter(curNew); var pctOf=function(p,cur,nx){ if(!nx) return 100; var span=nx.min_points-cur.min_points, done=p-cur.min_points; return Math.max(0,Math.min(100,Math.round(done/Math.max(span,1)*100))); }; var pctFrom=pctOf(oldPts, curOld, nextAfter(curOld)); var pctTo=pctOf(newPts, curNew, nxt); var bk=document.createElement('div'); bk.id='fxRankBack'; bk.style.cssText='position:fixed;inset:0;background:rgba(3,5,8,.86);backdrop-filter:blur(4px);z-index:245'; var cd=document.createElement('div'); cd.id='fxRankCard'; cd.style.cssText='position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:246;width:min(90vw,340px);'+ 'background:linear-gradient(180deg,#0d1016,#070a0e);border:1px solid '+esc(curOld.color||'#888')+';border-radius:20px;padding:22px 18px;text-align:center;'+ 'box-shadow:0 24px 60px rgba(0,0,0,.6);transition:border-color .6s;overflow:hidden'; var closeIt=function(){ bk.remove(); cd.remove(); }; bk.onclick=closeIt; var badgeSrc=window.rkSrc?window.rkSrc(curOld.image_url,128):''; cd.innerHTML= '
🎉 ترقّى كلانكم لرتبة جديدة!
'+ '
'+ (badgeSrc?'':'')+ '
'+ '
'+esc(curOld.name||'')+'
'+ '
'+oldPts+' نقطة
'+ '
'+ '
'+ '
'+ ''; document.body.appendChild(bk); document.body.appendChild(cd); cd.querySelector('#fxRankClose').onclick=closeIt; var setLeft=function(nx,color2){ var el=cd.querySelector('#fxRankLeft'); if(!el) return; el.innerHTML=nx?('باقٍ '+(nx.min_points-newPts)+' نقطة لرتبة '+esc(nx.name)+''):'🏆 بلغتم القمّة'; }; var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches; if(!leveledUp) setLeft(nxt, nxt?nxt.color:null); setTimeout(function(){ var bar=cd.querySelector('#fxRankBar'), ptsEl=cd.querySelector('#fxRankPts'); if(!bar||!ptsEl) return; if(reduce){ bar.style.width=pctTo+'%'; ptsEl.textContent=newPts+' نقطة'; if(leveledUp) setLeft(nxt, nxt?nxt.color:null); return; } bar.style.width=(leveledUp?100:pctTo)+'%'; var cur=oldPts, boundary=leveledUp?curNew.min_points:newPts; var step=function(){ cur++; ptsEl.textContent=cur+' نقطة'; if(cur===boundary && leveledUp){ var img=cd.querySelector('#fxRankImg'), nm=cd.querySelector('#fxRankName'), bw=cd.querySelector('#fxRankBW'), lvl=cd.querySelector('#fxRankLvl'); cd.style.borderColor=curNew.color||'#888'; if(img){ img.src=window.rkSrc?window.rkSrc(curNew.image_url,128):img.src; img.style.filter='drop-shadow(0 4px 12px '+(curNew.color||'#888')+'80)'; } if(nm){ nm.textContent=curNew.name||''; nm.style.color=curNew.color||'#888'; } bar.style.background='linear-gradient(90deg,'+(curNew.color||'#888')+',#fff)'; if(bw){ bw.style.transform='scale(1.25)'; setTimeout(function(){ bw.style.transform='scale(1)'; },450); } if(lvl){ lvl.style.opacity='1'; lvl.style.transform='scale(1)'; } bar.style.width=pctTo+'%'; setLeft(nxt, nxt?nxt.color:null); for(var i=0;i<16;i++){ (function(i){ var sp=document.createElement('div'); sp.style.cssText='position:absolute;bottom:40%;right:'+(20+Math.random()*60)+'%;width:6px;height:6px;border-radius:50%;background:'+[curNew.color||'#fff','#f0d17a','#fff'][i%3]+';opacity:0'; cd.appendChild(sp); sp.animate([{transform:'translateY(0)',opacity:0},{opacity:1,offset:.15}, {transform:'translateY(-'+(60+Math.random()*40)+'px)',opacity:0}], {duration:800+Math.random()*400, delay:i*25, easing:'ease-out'} ).onfinish=function(){ sp.remove(); }; })(i); } } if(cur'+ '
'+(won?'🏆 فزتم':'اكتملت المباراة')+'
'+ '
'+ '
CLAN '+esc(String(o.mine||'؟').toUpperCase())+'
'+ ''+(o.myScore!=null?o.myScore:'—')+'
'+ ''+ '
CLAN '+esc(String(o.opp||'؟').toUpperCase())+'
'+ ''+(o.oppScore!=null?o.oppScore:'—')+'
'+ '
'+ (won ? '
+0 نقطة
' : '
خسرتم هذه — والسجل يُبنى على المدى.
تنتظركم جولة ثانية.
')+ ''; el.classList.add('on'); clearTimeout(window._fxT2); window._fxT2=setTimeout(function(){ window.fxClose('fxRes'); }, 7000); if(!won) return; var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches; var target=Math.abs(Number(o.pts||0))||0, cur=0, p=document.getElementById('fxPts'); if(!p) return; if(reduce||!target){ p.textContent='+'+target+' نقطة'; return; } var step=function(){ cur++; p.textContent='+'+cur+' نقطة'; if(cur'; }; window.refreshDots=function(){ [].forEach.call(document.querySelectorAll('.pdot[data-pres]'), function(el){ el.classList.toggle('on', window.isOnline(el.getAttribute('data-pres'))); }); if(window.radarSync) window.radarSync(); /* قلب الرادار يتبع الحضور */ if(window.loadStaffOnline) window.loadStaffOnline(); }; window.loadPresence=function(){ var since=new Date(Date.now()-150000).toISOString(); return sb.from('profiles').select('id,last_seen_at').gt('last_seen_at',since).then(function(r){ var m={}; (r.data||[]).forEach(function(p){ m[p.id]=Date.parse(p.last_seen_at); }); window._pres=m; window._presLoaded=true; window.refreshDots(); /* شريط الدردشة يتنفس مع الحضور */ try{ var chs=document.getElementById('chat'); if(chs && chs.classList.contains('active') && window.renderChatBar) window.renderChatBar(); }catch(e){} /* القائمة معروضة الآن؟ نعيد ترتيبها بالحضور الجديد */ try{ var pl=document.getElementById('players'); if(pl && pl.classList.contains('active') && window.renderPlayers) window.renderPlayers(); }catch(e){} },function(){}); }; (function(){ /* ═══ النبضة تُرسل فعلاً ═══ طلبات سوبابيس كسولة: لا تنطلق حتى تُطلب نتيجتها بـ then — وبلا ذلك كانت الدالة تُبنى ولا تُنادى، فما وصل الخادمَ نبضٌ قطّ. */ /* يُفحَصُ عندَ أوّلِ نبضةٍ بعدَ الدخولِ وعندَ العودةِ للتبويب */ (function(){ var done=false; var go=function(){ if(done||!window._uid||!window.awCheckUnseen) return; done=true; setTimeout(window.awCheckUnseen, 1200); }; setTimeout(go, 2500); document.addEventListener('visibilitychange', function(){ if(!document.hidden && window.awCheckUnseen && window._uid) window.awCheckUnseen(); }); })(); window.pingPresence=function(){ if(!window._uid) return; try{ sb.rpc('ping_presence').then(function(){ window._pinged=true; }, function(){}); }catch(e){} }; function tick(){ if(document.hidden) return; window.pingPresence(); window.loadPresence(); } /* ═══ بصمة الاتصال ═══ المتصفح لا يعرف عنوانه — خادمنا هو من يراه. نناديه بهويةٍ موقّعة فيسجّلها، وبها تُكشف الحسابات التي يديرها شخصٌ واحد. */ window.markSeen=function(){ if(!window._uid) return; /* تبويبُ الانتحال لا يبصم عنوانَ المالك باسم العضو */ if(window.IMP && window.IMP.on) return; sb.auth.getSession().then(function(r){ var t=r&&r.data&&r.data.session&&r.data.session.access_token; if(!t) return; fetch('/api/seen',{method:'POST',headers:{Authorization:'Bearer '+t}}).catch(function(){}); },function(){}); }; /* بصمة الجهاز — مرّةً واحدة لكل جلسة، لا حاجة للتكرار كالحضور */ window._fpSent=false; window.sendFingerprint=function(){ if(!window._uid || window._fpSent) return; if(window.IMP && window.IMP.on) return; if(!window.FingerprintJS) { setTimeout(window.sendFingerprint, 1500); return; } window._fpSent=true; FingerprintJS.load().then(function(fp){ return fp.get(); }).then(function(result){ sb.auth.getSession().then(function(r){ var t=r&&r.data&&r.data.session&&r.data.session.access_token; if(!t) return; fetch('/api/fingerprint',{method:'POST',headers:{'Content-Type':'application/json',Authorization:'Bearer '+t}, body:JSON.stringify({fp:result.visitorId})}).catch(function(){}); },function(){}); }).catch(function(){}); }; /* أول نداء بمجرّد اكتمال الدخول، ثم عند كل عودةٍ للتطبيق — فيمتلئ السجلّ من أوّل زيارة بدل انتظار دورةٍ كاملة */ var _seenT=setInterval(function(){ if(window._uid){ window.markSeen(); window.sendFingerprint(); clearInterval(_seenT); } }, 1200); setTimeout(function(){ clearInterval(_seenT); }, 60000); setInterval(window.markSeen, 15*60*1000); document.addEventListener('visibilitychange',function(){ if(!document.hidden) window.markSeen(); }); /* الحضور يُقرأ فوراً لا بعد ثانيتين — الرادار يُرسم أول لحظة */ window.loadPresence(); setTimeout(tick,2500); setInterval(tick,60000); /* أول نبضة فور اكتمال الدخول — لا ننتظر دورة الدقيقة */ var t0=setInterval(function(){ if(window._uid){ window.pingPresence(); window.loadPresence(); clearInterval(t0); } }, 1500); setTimeout(function(){ clearInterval(t0); }, 45000); document.addEventListener('visibilitychange',function(){ if(!document.hidden) tick(); }); })(); /* ===== قائمة اللاعبين + البحث ===== */ window.loadPlayers=function(){ var box=$('playersList'); if(!box) return; sb.from('profiles').select('id,username,psn_id,avatar_url,title,wins,losses,points,streak,seeking_clan,role,created_at,style_id').order('points',{ascending:false}).limit(300).then(function(r){ window._allPlayers=(r.data||[]).filter(function(p){ return p.username; }); sb.from('clan_members').select('profile_id,clans(tag)').not('profile_id','is',null).then(function(cm){ var m={}; (cm.data||[]).forEach(function(x){ if(x.clans) m[x.profile_id]=String(x.clans.tag||''); }); window._playerClan=m; window.renderPlayers(); },function(){ window._playerClan={}; window.renderPlayers(); }); }); }; window.renderPlayers=function(){ var box=$('playersList'); if(!box) return; /* حين يبحثُ العضوُ نعرِضُ ما ردَّته القاعدةُ لا ما صادفَ أن حُمِّل */ var all=(window._plFound!=null)?window._plFound:(window._allPlayers||[]), cm=window._playerClan||{}; var q=(window._plSearch||'').trim().toLowerCase(), f=window._plFilter||'all'; var list=all.filter(function(p){ if(f==='online' && !(window.isOnline&&window.isOnline(p.id))) return false; if(q && window._plFound==null){ var s=((p.username||'')+' '+(p.psn_id||'')+' '+(p.title||'')).toLowerCase(); if(s.indexOf(q)===-1) return false; } return true; }); /* ═══ من هو داخل الآن يتصدّر ═══ الحاضر أولى بالظهور ممن غاب — تُرسل له رسالة أو تتحدّاه فيردّ. */ if(f==='all' && window.isOnline){ list=list.slice().sort(function(a,b){ var oa=window.isOnline(a.id)?1:0, ob=window.isOnline(b.id)?1:0; if(oa!==ob) return ob-oa; return (b.points||0)-(a.points||0); }); } /* عدّاد الحاضرين على الحبّة نفسها */ var _on=$('plOnN'); if(_on && window.isOnline){ var n=(window._allPlayers||[]).filter(function(p){ return window.isOnline(p.id); }).length; _on.textContent = n?('· '+n):''; } /* «الكل» يبقى على التصنيف بالنقاط — والباقي بتاريخ الانضمام */ if(f==='new'||f==='old'){ var dir=(f==='new')?-1:1; list=list.slice().sort(function(a,b){ return dir*String(a.created_at||'').localeCompare(String(b.created_at||'')); }); } if(!list.length){ box.innerHTML='
'+ (f==='online' ? 'ما فيه أحد متصل الآن 🌙
جرّب «الكل» — تقدر تراسلهم ويردّون لاحقاً' : 'ما فيه لاعب بهذا البحث 🔎')+'
'; return; } box.innerHTML='
'+list.length+' لاعب
'+list.map(function(p){ var av=p.avatar_url?'':''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; var ttl=p.title?' '+esc(p.title)+'':''; var cl=cm[p.id]?'🛡️ CLAN '+esc(cm[p.id])+'':(p.seeking_clan?'🔎 يبحث عن كلان':'بدون كلان'); var st=p.role==='admin'?' 🛡️':(p.role==='moderator'?' 🧑‍⚖️':''); var fire=(p.streak>=2)?' 🔥':''; return '
'+av+ '
'+window.dot(p.id)+(function(){var f=window.fxOf(p.id,p.style_id);return f?window.fxName(f,p.username):esc(p.username);})()+st+fire+ttl+'
'+ '
'+cl+(p.psn_id?' · 🎮 '+esc(p.psn_id)+'':'')+'
'+ '
'+(p.points||0)+'
'+(p.wins||0)+'ف '+(p.losses||0)+'خ
'+ (window._uid&&window._uid!==p.id?'':'')+ '
'; }).join(''); }; (function(){ /* ═══ البحثُ يسألُ القاعدةَ لا الذاكرة ═══ الشاشةُ تُحمِّلُ ٣٠٠ لاعبٍ مرتَّبينَ بالنقاط، والمنصّةُ فيها آلاف — و٦٠ فقط لهم نقطةٌ واحدةٌ فأكثر. فالباقونَ يدخلونَ الصفحةَ بالقرعةِ لا بالاستحقاق، ومن سقطَ منها لم يكنْ يُوجَدُ مهما كُتِبَ اسمُه صحيحاً. السؤالُ الآن يذهبُ إلى القاعدة. */ window._plFound=null; window.searchPlayers=function(raw){ var q=String(raw||'').trim(); /* حرفٌ واحدٌ يجرُّ نصفَ المنصّة — والحرفانِ حدٌّ معقول */ if(q.length<2){ window._plFound=null; window.renderPlayers(); return; } /* ═══ تنظيفُ ما يكسِرُ مُرشِّحَ PostgREST ═══ صيغةُ or تفصلُ بالفواصلِ وتُحيطُ بالأقواس، و% و* بدائلُ نمطيّة. فاصلةٌ واحدةٌ من العضوِ تُحوِّلُ الاستعلامَ إلى شرطٍ ثالثٍ فاسد. */ var safe=q.replace(/[,()%*\\]/g,' ').trim(); if(!safe){ window._plFound=null; window.renderPlayers(); return; } var mine=(window._plSeq=(window._plSeq||0)+1); sb.from('profiles') .select('id,username,psn_id,avatar_url,title,wins,losses,points,streak,seeking_clan,role,created_at,style_id') .or('username.ilike.*'+safe+'*,psn_id.ilike.*'+safe+'*') .order('points',{ascending:false}).limit(60) .then(function(r){ /* ردٌّ متأخّرٌ لبحثٍ قديمٍ لا يدوسُ الأحدث */ if(mine!==window._plSeq) return; var rows=(r.data||[]).filter(function(p){ return p.username; }); window._plFound=rows; /* شاراتُ الكلانِ للنتائجِ الجديدةِ — الخريطةُ لا تعرفُهم */ var need=rows.map(function(p){ return p.id; }) .filter(function(id){ return !(window._playerClan||{})[id]; }); if(!need.length){ window.renderPlayers(); return; } sb.from('clan_members').select('profile_id,clans(tag)').in('profile_id',need) .then(function(cm){ if(mine!==window._plSeq) return; window._playerClan=window._playerClan||{}; ((cm&&cm.data)||[]).forEach(function(x){ if(x.clans) window._playerClan[x.profile_id]=String(x.clans.tag||''); }); window.renderPlayers(); },function(){ window.renderPlayers(); }); },function(){ window._plFound=null; window.renderPlayers(); }); }; var s=$('plSearch'); if(s) s.addEventListener('input',function(){ window._plSearch=s.value; window.renderPlayers&&window.renderPlayers(); /* ردٌّ فوريٌّ على المحمَّل */ clearTimeout(window._plTmr); window._plTmr=setTimeout(function(){ window.searchPlayers(s.value); }, 280); }); var f=$('plFilter'); if(f) f.querySelectorAll('button').forEach(function(b){ b.addEventListener('click',function(){ f.querySelectorAll('button').forEach(function(x){x.classList.remove('on')}); b.classList.add('on'); window._plFilter=b.getAttribute('data-f'); window.renderPlayers&&window.renderPlayers(); }); }); })(); /* ===== القائمة السوداء ===== */ window._blData=null; window._blSearch=''; window._blFilter='all'; window.loadBlacklist=function(){ if(window._blData){ window.renderBlacklist(); return; } var box=$('blacklistList'); if(box) box.innerHTML='
جارٍ التحميل...
'; sb.from('public_bans').select('*').order('created_at',{ascending:false}).then(function(r){ window._blData=r.data||[]; window.renderBlacklist(); },function(){ window._blData=[]; if(box) box.innerHTML='
تعذّر تحميل القائمة
'; }); }; window.renderBlacklist=function(){ var box=$('blacklistList'); if(!box||!window._blData) return; var q=(window._blSearch||'').trim().toLowerCase(); var f=window._blFilter||'all'; var items=window._blData.filter(function(b){ if(f!=='all' && b.target_type!==f) return false; if(q && (b.banned_name||'').toLowerCase().indexOf(q)===-1) return false; return true; }); if(!items.length){ box.innerHTML='
ما فيه نتائج مطابقة
'; return; } box.innerHTML=items.map(function(b){ var isClan=b.target_type==='clan'; var badge=isClan?'كلان' :'لاعب'; var dur; if(b.days===null||b.days===undefined){ dur='دائم'; } else { var rem=b.expires_at?Math.max(0,Math.ceil((new Date(b.expires_at)-new Date())/86400000)):0; dur='باقي '+rem+' يوم
'+b.days+' يوم
'; } return '
'+ '
🚫
'+ '
'+ '
'+esc(b.banned_name||'—')+''+badge+'
'+ '
'+esc(b.reason||'')+'
'+ '
'+ '
'+dur+'
'+ '
'; }).join(''); }; (function(){ var s=$('blSearch'); if(s) s.addEventListener('input',function(){ window._blSearch=s.value; window.renderBlacklist&&window.renderBlacklist(); }); var f=$('blFilter'); if(f) f.querySelectorAll('button').forEach(function(b){ b.addEventListener('click',function(){ f.querySelectorAll('button').forEach(function(x){x.classList.remove('on')}); b.classList.add('on'); window._blFilter=b.getAttribute('data-f'); window.renderBlacklist&&window.renderBlacklist(); }); }); })(); /* ===== الرسائل المباشرة ===== */ window.openDM=function(pid,uname){ window._dmWith=pid; window._dmName=uname||''; go('dm'); window.loadDM(); }; window.loadDM=function(){ var box=$('dmMsgs'), pid=window._dmWith; if(!box||!pid||!window._uid) return; if($('dmSub')) $('dmSub').innerHTML= (window.isOnline(pid)?' متصل الآن · ':'')+ 'عرض ملفه ←'; /* الاسم والصورة: نرسم المعروف فوراً ثم نُصحّحه من ملفه */ var drawHead=function(p){ var nm=(p&&p.username)||window._dmName||'محادثة'; var f=window.fxOf(pid); if($('dmTitle')) $('dmTitle').innerHTML=(f?window.fxName(f,nm):esc(nm))+(window.rkPin?window.rkPin(pid,22):''); var av=$('dmAv'); if(!av) return; av.onclick=function(){ openPlayer(pid); }; av.innerHTML=(p&&p.avatar_url) ? '' : esc(String(nm).slice(0,2).toUpperCase()); }; drawHead(null); sb.from('profiles').select('username,avatar_url').eq('id',pid).single() .then(function(r){ if(r&&r.data){ window._dmName=r.data.username||window._dmName; drawHead(r.data); } },function(){}); sb.from('dms').select('id,from_id,to_id,body,created_at').or('and(from_id.eq.'+window._uid+',to_id.eq.'+pid+'),and(from_id.eq.'+pid+',to_id.eq.'+window._uid+')').order('id',{ascending:false}).limit(80).then(function(r){ var rows=(r.data||[]).slice().reverse(); box.innerHTML = rows.length? rows.map(function(m){ var own=m.from_id===window._uid, tm=tHM(m.created_at); var db=window.fxBubble(window.fxOf(m.from_id)); var dbub = db ? '
'+esc(m.body||'')+'
' : '
'+esc(m.body||'')+'
'; return '
'+dbub+'
'+tm+'
'; }).join('') : '
ابدأ المحادثة 👋
'; box.scrollTop=box.scrollHeight; var un=rows.filter(function(m){return m.to_id===window._uid;}).map(function(m){return m.id;}); if(un.length) sb.from('dms').update({read:true}).in('id',un).then(function(){ window.updateDMBadge&&window.updateDMBadge(); },function(){}); }); }; window.sendDM=function(){ if(_blocked()) return; var inp=$('dmInput'); if(!inp) return; var b=(inp.value||'').trim(); if(!b) return; if(b.length>500) b=b.slice(0,500); if(!window._uid||!window._dmWith) return; sb.from('dms').insert({from_id:window._uid,to_id:window._dmWith,body:b}).then(function(r){ if(r&&r.error){ toast('تعذّر الإرسال'); return; } inp.value=''; window.loadDM(); }); }; (function(){ var b=document.getElementById('dmSend'); if(b) b.addEventListener('click',window.sendDM); })(); window.updateDMBadge=function(){ if(!window._uid) return; sb.from('dms').select('id',{count:'exact',head:true}).eq('to_id',window._uid).eq('read',false).then(function(r){ window._dmUnread=r.count||0; ['inboxBadge','dwInboxBadge','hdrInboxBadge'].forEach(function(id){ var el=$(id); if(!el) return; if(window._dmUnread>0){ el.textContent=window._dmUnread>99?'99+':String(window._dmUnread); el.hidden=false; } else el.hidden=true; }); if(window.updateMenuBadge) window.updateMenuBadge(); },function(){}); }; window.loadInbox=function(){ var box=$('inboxList'); if(!box) return; if(!window._uid){ box.innerHTML='
سجّل دخولك لرؤية رسائلك
'; return; } sb.from('dms').select('id,from_id,to_id,body,read,created_at').or('from_id.eq.'+window._uid+',to_id.eq.'+window._uid).order('id',{ascending:false}).limit(200).then(function(r){ var rows=r.data||[], conv={}; rows.forEach(function(m){ var other=(m.from_id===window._uid)?m.to_id:m.from_id; if(!conv[other]) conv[other]={last:m,unread:0}; if(m.to_id===window._uid&&!m.read) conv[other].unread++; }); var ids=Object.keys(conv); if(!ids.length){ box.innerHTML='
لا رسائل بعد 📬
افتح «👥 اللاعبون» وراسل أي لاعب
'; return; } sb.from('profiles').select('id,username,avatar_url').in('id',ids).then(function(pr){ var pm={}; (pr.data||[]).forEach(function(p){pm[p.id]=p;}); box.innerHTML=ids.map(function(id){ var c=conv[id], p=pm[id]||{}, av=p.avatar_url?'':''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; var badge=c.unread?''+c.unread+'':''; return '
'+av+ '
'+window.dot(id)+esc(p.username||'لاعب')+'
'+((c.last.from_id===window._uid)?'أنت: ':'')+esc(String(c.last.body||'').slice(0,44))+'
'+badge+'
'; }).join(''); }); }); }; window.openPlayer=function(pid){ if(pid) window._viewPlayer=pid; go('player'); }; // go() تستدعي loadPlayer — لا نكرّرها /* ═══════════════════════════════════════════════════════════ آخرُ الزوّار ─────────────────────────────────────────────────────────── صفٌّ واحدٌ لكلِّ زائرٍ في القاعدة لا صفٌّ لكلِّ فتحةِ صفحة — فالقائمةُ «آخرُ من زار» لا «كم مرّةً دخل». وثلاثةٌ لا تُسجَّل: زيارةُ المرءِ ملفَّ نفسِه، والزائرُ غيرُ المسجَّل، و**وضعُ الدخولِ بحسابِ عضو** — فالدخولُ بحسابِ غيرِك لا يُخلّفُ أثراً باسمِه، وهو مبدأٌ مستقرٌّ في المنصةِ لا يُنقَض. */ window.loadVisitors=function(pid){ var box=$('plVisitors'); if(!box||!pid) return; var imp=false; try{ imp = sessionStorage.getItem('codar_imp_on')==='1'; }catch(e){} var isMe = (window._uid===pid); var draw=function(){ sb.rpc('profile_visitors',{p_profile:pid, p_limit:12}).then(function(r){ var L=(r&&r.data)||[]; if(!L.length){ box.innerHTML=''; return; } /* بلا زوّار: لا قسمَ فارغاً */ var ago=function(t){ var d=(Date.now()-new Date(t).getTime())/1000; if(d<60) return 'الآن'; if(d<3600) return Math.floor(d/60)+'د'; if(d<86400) return Math.floor(d/3600)+'س'; if(d<604800) return Math.floor(d/86400)+'ي'; return Math.floor(d/604800)+'أ'; }; var card=function(x){ var nm=x.name||'لاعب'; var av=x.avatar ? '' : ''+esc(String(nm).slice(0,2).toUpperCase())+''; var f=window.fxOf(x.id); return ''; }; box.innerHTML= '
'+ '

👣 آخر الزوّار ('+L.length+')

'+ '
'+L.map(card).join('')+'
'+ '
'; },function(){ box.innerHTML=''; }); }; /* نُسجّلُ أوّلاً ثمّ نقرأُ — كي يرى الزائرُ نفسَه في القائمةِ فوراً */ if(window._uid && !isMe && !imp){ sb.rpc('note_profile_visit',{p_profile:pid}).then(draw, draw); } else { draw(); } }; window.loadPlayer=function(){ var box=$('playerBody'), pid=window._viewPlayer; if(!box||!pid) return; box.innerHTML='
⏳ جاري التحميل...
'; sb.from('profiles').select('id,username,psn_id,avatar_url,wins,losses,points,streak,seeking_clan,role,bio,title,style_id').eq('id',pid).single().then(function(pr){ var p=pr.data; if(!p){ box.innerHTML='
اللاعب غير موجود
'; return; } sb.from('profiles').select('id',{count:'exact'}).limit(1).gt('points',p.points||0).then(function(rk){ var rank=(rk.count||0)+1, medal=rank===1?'🥇':rank===2?'🥈':rank===3?'🥉':('#'+rank); var t=(p.wins||0)+(p.losses||0), ratio=t?Math.round((p.wins||0)*100/t):0; var ranked=((p.points||0)>0||t>0); // بدون مباريات = غير مصنّف (ما نعطيه 🥇 على الفاضي) var av=p.avatar_url?'':''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; var isMe=(window._uid===p.id); /* الشارة من مجموعته إن فُعّلت، وإلا الشارة المبنيّة على الرتبة */ var _pf=window.fxOf(p.id), badge=window.fxBadge(_pf,10); if(!badge) badge=p.role==='admin'?'🛡️ إداري':(p.role==='moderator'?'🧑‍⚖️ مشرف':''); var seek=p.seeking_clan?'🔎 يبحث عن كلان':''; /* ترويسة الملف: بطاقة واحدة تجمع الصورة والاسم والشارات */ var head='
'+av+ '
'+window.dot(p.id)+(function(){var f=window.fxOf(p.id,p.style_id);return f?window.fxName(f,p.username||'لاعب'):esc(p.username||'لاعب');})()+(p.streak>=2?' 🔥'+p.streak:'')+'
'+ (p.psn_id?'
🎮 '+esc(p.psn_id)+'
':'')+ '
'+ (ranked ? ''+medal+' التصنيف الفردي' : '') +(p.title?'🏷️ '+esc(p.title)+'':'') +badge+seek+ /* شارة الاشتراك */ (function(){ var sub=window._mySubscription; if(!sub||p.id!==window._uid) return ''; var isDiamond=sub.item_id&&sub.item_id.indexOf('diamond')>-1; var tc=isDiamond?'#b9f2ff':'#FFD700'; var bc=isDiamond?'rgba(185,242,255,.35)':'rgba(255,215,0,.35)'; var bg=isDiamond?'rgba(185,242,255,.1)':'rgba(255,215,0,.1)'; var lbl=isDiamond?'💎 DIAMOND':'🥇 GOLD'; return ''+lbl+''; })()+ '
'+ /* النبذة تتوسّط لا تلتصق بحافّة: أكثرها سطرٌ أو سطران، والمحاذاة لليمين تتركها معلّقةً في طرف. و‎dir=auto‎ لأن نبذةً إنجليزية في صفحةٍ عربية تنقلب أطرافها إن فرضنا الاتجاه. و‎balance‎ يقسم السطرين نصفين متساويين بدل سطرٍ طويل وكلمةٍ يتيمة. */ (p.bio?'
'+esc(p.bio)+'
':'')+ '
'; /* شريط الإحصاءات — نفس شريط بقية الصفحات */ var cell=function(l,v,c){ return '
'+v+''+l+'
'; }; var stats='
'+ cell('نقطة',(p.points||0),'var(--gold)')+ cell('فوز',(p.wins||0),'#3ddc84')+ cell('خسارة',(p.losses||0),'var(--red)')+ cell('معدل الفوز',ratio+'%','var(--blue2)')+'
'+ '
'+ cell('مباريات',t,'var(--tx2)')+ cell('ستريك',(p.streak||0)>=2?('🔥'+p.streak):'—','var(--tx2)')+ cell('الترتيب',ranked?medal:'—','var(--blue2)')+'
'; /* الخزانةُ بعدَ الإحصاءاتِ لا في ذيلِ الصفحة: كانت تحتَ جدارِ الملفِّ فلا تُرى إلّا بعدَ تمريرٍ طويل — **وخزانةُ أوسمةٍ في القاعِ ليست خزانة.** */ box.innerHTML=head+'
'+stats+ '
'+ '
'+ '
'+ '

📜 سجل المباريات الفردية

'+ '

💌 جدار الملف

'+ (window._uid?''+ '
':'')+ '
'; /* ═══ النداءُ ينتظرُ تعريفَه ═══ التعريفُ في كتلةِ سكربتٍ تأتي بعدَ هذه، **و`try/catch` كان يبتلعُ الخطأَ صامتاً** فلم تظهرِ الخزانةُ ولا ظهرَ سببُ غيابِها. **الكتمُ الصامتُ أسوأُ من العطلِ الصريح** — العطلُ يُرى فيُصلَح. فيُنتظَرُ التعريفُ ثانيتَين ثمَّ يُشتكى في وحدةِ التحكّم. */ (function(_pid){ var _t = 0; var _awWait = setInterval(function(){ if (window.renderAwards) { clearInterval(_awWait); try { window.renderAwards(_pid, 'plAwards'); } catch(e){ console.warn('[awards] فشل الرسم:', e); } } else if (++_t > 40) { clearInterval(_awWait); console.warn('[awards] renderAwards غير معرّفة بعد ثانيتين'); } }, 50); })(p.id); /* الطيُّ يُطبَّقُ بعدَ أن تكتملَ الأقسامُ المحمَّلةُ لاحقاً */ [400, 1200, 2600].forEach(function(ms){ setTimeout(function(){ try{ window.plAccordion(box); }catch(e){} }, ms); }); window.loadWall(p.id); // كلانه // كلانه: الرؤساء والنواب مو دايم مسجّلين في clan_members — نفحص الجدولين var renderClanOf=function(cid,tag,type,points,rank){ var sk=document.getElementById('plSeekBadge'); if(sk) sk.remove(); // عنده كلان ⇒ ما يبحث عن كلان var T=String(tag||'?'); var label = rank==='leader' ? ('👑 رئيس كلان '+T) : rank==='vice' ? ('🎖️ نائب رئيس كلان '+T) : rank==='coordinator' ? ('📋 منسق كلان '+T) : ('🛡️ عضو كلان '+T); var col = rank==='leader' ? ['rgba(255,204,0,.16)','rgba(255,204,0,.45)','var(--gold)'] : rank==='vice' ? ['rgba(56,182,255,.14)','rgba(56,182,255,.42)','var(--blue2)'] : rank==='coordinator' ? ['rgba(255,204,0,.12)','rgba(255,204,0,.4)','var(--gold)'] : ['rgba(125,142,160,.14)','rgba(125,142,160,.35)','var(--tx2)']; var chips=document.getElementById('plChips'); var old=document.getElementById('plClanChip'); if(old) old.remove(); // ما نكرّر الشارة if(chips) chips.insertAdjacentHTML('beforeend', ''+esc(label)+''); var rl = rank==='leader'?'👑 رئيس':(rank==='vice'?'🎖️ نائب':(rank==='coordinator'?'📋 منسق':'🎮 لاعب')); if($('plClan')) $('plClan').innerHTML= '

كلانه

'+ ''+ '
'; }; /* ═══════════ أصدقاؤه ═══════════ الصداقة علاقةٌ متبادلة: صفٌّ واحدٌ يخصّ الطرفين، والطرف الآخر مرّةً في requester ومرّةً في addressee. فنجمعهما ثم نطرح صاحب الملف. ولا نجلبها إلا للمقبولة — المعلّقة شأنُ صاحبها وحده. */ window.loadVisitors(p.id); (function(){ var fbox=$('plFriends'); if(!fbox) return; sb.from('friends').select('requester,addressee') .eq('status','accepted') .or('requester.eq.'+p.id+',addressee.eq.'+p.id) .then(function(fr){ var rows=(fr&&fr.data)||[]; var ids=rows.map(function(f){ return f.requester===p.id?f.addressee:f.requester; }) .filter(function(x,i,a){ return x && a.indexOf(x)===i; }); if(!ids.length){ fbox.innerHTML=''; return; } /* بلا أصدقاء: لا قسمَ فارغاً */ sb.from('profiles').select('id,username,avatar_url,points,style_id') .in('id',ids.slice(0,60)).then(function(pr2){ var L=(pr2&&pr2.data)||[]; /* الأعلى نقاطاً أوّلاً — الوجه الأول يقول شيئاً */ L.sort(function(a,b){ return (b.points||0)-(a.points||0); }); var card=function(x){ var nm=x.username||'لاعب'; var av=x.avatar_url ? '' : ''+esc(String(nm).slice(0,2).toUpperCase())+''; var f=window.fxOf(x.id,x.style_id); return ''; }; fbox.innerHTML= '
'+ '

🤝 أصدقاؤه ('+ids.length+')

'+ '
'+L.map(card).join('')+'
'+ '
'; },function(){ fbox.innerHTML=''; }); },function(){ fbox.innerHTML=''; }); })(); /* ═══ أيُّ كلانٍ يُنسب إليه؟ ═══ من يملك كلاناً وينوب في آخر كان يظهر بأيّهما اتفق، لأن الاختيار كان «أوّل ما تجده». الترتيب هنا قاطع: ما تملكه ثم ما ترأسه ثم ما تنوب فيه. */ sb.from('clans').select('id,tag,type,points,president_id,vice_id,created_by') .or('created_by.eq.'+p.id+',president_id.eq.'+p.id+',vice_id.eq.'+p.id) .neq('status','archived').then(function(cr){ var rows=cr.data||[]; /* ═══ الرئاسةُ لا تبقى بعدَ الخروج ═══ من تركَ كلانَه وانضمَّ لغيرِه كان يظهرُ رئيساً للأوّل أبداً، لأنَّ `president_id` يبقى مكتوباً فيه. فلا تُقرأُ الرئاسةُ إلّا لمن هو في كشفِ الكلانِ فعلاً. */ sb.from('clan_members').select('role,clan_id,clans(tag,type,points)') .eq('profile_id',p.id).limit(1).then(function(cm){ var m=cm.data&&cm.data[0]; var myClans={}; if(m) myClans[m.clan_id]=true; var owned=rows.filter(function(x){ return myClans[x.id]; }); var rank=function(x){ return x.created_by===p.id?1:(x.president_id===p.id?2:3); }; owned.sort(function(a,b){ return rank(a)-rank(b); }); var c=owned[0]; if(c){ renderClanOf(c.id,c.tag,c.type,c.points, (c.created_by===p.id||c.president_id===p.id)?'leader':'vice'); return; } if(m&&m.clans) renderClanOf(m.clan_id,m.clans.tag,m.clans.type,m.clans.points,m.role); },function(){}); },function(){}); // سجل الفردي sb.from('duel_results').select('*, duels(challenger_id,opponent_id,challenger_name,opponent_name)').eq('status','verified').order('id',{ascending:false}).limit(40).then(function(dr){ var all=(dr.data||[]).filter(function(x){ var d=x.duels||{}; return d.challenger_id===p.id||d.opponent_id===p.id; }).slice(0,10); if(!$('plMatches')) return; $('plMatches').innerHTML= all.length? all.map(function(x){ var d=x.duels||{}, won=x.winner_id===p.id; var opp=(d.challenger_id===p.id)?d.opponent_name:d.challenger_name; var dt=tDay(x.created_at).split('-'); var dsh=dt.length===3?(dt[2]+'/'+dt[1]):''; return '
'+ ''+(won?'✔ فوز':'✕ خسارة')+''+ ''+esc(opp||'؟')+''+ ''+esc(x.score||'—')+''+ ''+dsh+'
'; }).join('') : '
لا مباريات موثّقة بعد
'; },function(){}); // إجراءات /* فعلٌ رئيسي واحد بارز، وما دونه صفوف هادئة — كبقية صفحات المنصة */ var fr=(window._friendIds||[]).indexOf(p.id)!==-1; var actsHTML = isMe ? '' : (window._uid ? ''+ '
'+ ''+ (fr ? '
👥'+ 'صديقكفي قائمة أصدقائك'+ '
' : '')+ /* ═══ الحظرُ حقُّ العضوِ لا منّةُ الإدارة ═══ البلاغُ يمرُّ بالطاقمِ ويأخذُ وقتَه؛ والحظرُ فوريٌّ بيدِ صاحبِ الشكوى. وكلاهما مطلوب. */ (window.isBlocked(p.id) ? '' : '')+ '
' : ''); // الأزرار بأعلى الصفحة فقط — كانت تتكرر أسفلها بلا داعٍ if($('plActsTop')) $('plActsTop').innerHTML=actsHTML; if(isMe){ sb.rpc('get_active_subscription',{p_uid:pid}).then(function(r){ if(!r.data) return; var sub=r.data; var isDiamond=sub.item_id&&sub.item_id.indexOf('diamond')>-1; var tc=isDiamond?'#b9f2ff':'#FFD700'; var bc=isDiamond?'rgba(185,242,255,.35)':'rgba(255,215,0,.35)'; var bg=isDiamond?'rgba(185,242,255,.1)':'rgba(255,215,0,.1)'; var lbl=(isDiamond?'💎 DIAMOND':'🥇 GOLD'); var chips=document.getElementById('plChips'); if(chips&&!chips.querySelector('.sub-badge')){ var span=document.createElement('span'); span.className='sub-badge'; span.style.cssText='background:'+bg+';border:1px solid '+bc+';color:'+tc+';font-size:10px;font-weight:900;padding:2px 9px;border-radius:20px'; span.textContent=lbl; chips.appendChild(span); } }); } if($('plActs')){ $('plActs').innerHTML=''; $('plActs').hidden=true; } }); },function(){ box.innerHTML='
تعذّر التحميل
'; }); }; window.challengePlayer=function(uname){ go('solo'); setTimeout(function(){ var i=$('duelOpp'); if(i){ i.value=uname; i.scrollIntoView({behavior:'smooth',block:'center'}); i.focus(); toast('اضغط «إطلاق التحدي الفردي» ⚡'); } },600); }; window.loadAgents=function(){ var box=$('agentsList'); if(!box) return; sb.from('free_agents').select('*').order('points',{ascending:false}).limit(100).then(function(r){ var rows=r.data||[]; var ST={sniper:'🔭 سنايبر',rush:'🔫 رشاش',both:'🎮 الاثنين'}; /* بطاقتي أنا — تظهر فوق بشكل مميّز مع أزرار التعديل والسحب */ var mine=window._uid?rows.filter(function(p){ return p.id===window._uid; })[0]:null; var mbox=$('agMine'), btn=$('agFindClan'); if(mbox){ if(mine){ mbox.hidden=false; mbox.innerHTML='
'+ '
'+ 'طلبك منشور'+ ''+esc(tDay(mine.fa_at))+'
'+ '
🎮 '+esc(mine.psn_id||'—')+''+ ' · '+esc(ST[mine.fa_style]||'—')+(mine.fa_hours?(' · 🕐 '+esc(mine.fa_hours)):'')+'
'+ '
يشوفه كل رؤساء الكلانات هنا وفي الديسكورد.
'+ '
'+ ''+ ''+ '
'; if(btn) btn.hidden=true; } else { mbox.hidden=true; mbox.innerHTML=''; if(btn) btn.hidden=false; } } var canOffer = !!(window._myClanId && window._myClanTag); box.innerHTML = rows.length? rows.map(function(p){ var av = p.avatar_url ? '' : ''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; var t=(p.wins||0)+(p.losses||0); var tag=function(txt,col){ return ''+txt+''; }; return '
'+ '
'+av+ '
'+ '
'+esc(p.username||'لاعب')+ (p.on_discord?' Discord':'')+'
'+ '
🎮 '+esc(p.psn_id||'—')+'
'+ '
'+(p.points||0)+' نقطة · '+(p.wins||0)+'ف '+(p.losses||0)+'خ'+(t?(' · '+t+' مباراة'):'')+'
'+ '
'+ 'متاح'+ '
'+ '
'+ (p.fa_style?tag(ST[p.fa_style]||p.fa_style,'var(--blue2)'):'')+ (p.fa_exp?tag('🏅 '+esc(p.fa_exp),'var(--gold)'):'')+ (p.fa_hours?tag('🕐 '+esc(p.fa_hours),'var(--tx2)'):'')+ '
'+ (p.fa_note?'
'+esc(p.fa_note)+'
':'')+ (canOffer?(''):'')+ '
'; }).join('') : '
ما فيه لاعبين يدورون كلان حالياً 🔎
لو ماعندك كلان اضغط «أبحث عن كلان» فوق وانشر طلبك
'; /* إبراز بطاقتي بعد النشر مباشرة */ if(window._agHighlight){ var hid=window._agHighlight; window._agHighlight=null; setTimeout(function(){ var el=document.getElementById('ag-'+hid); if(!el) return; el.scrollIntoView({behavior:'smooth',block:'center'}); el.style.transition='box-shadow .3s'; el.style.boxShadow='0 0 0 2px #3ddc84, 0 0 24px rgba(61,220,132,.45)'; setTimeout(function(){ el.style.boxShadow=''; },3500); },350); } },function(){ box.innerHTML='
لا لاعبين حالياً
'; }); }; /* ===== صلاحيات المجموعات ===== تخفي ما لا يُسمح برؤيته. الحماية الحقيقية تبقى في سياسات القاعدة (RLS) — هذي طبقة عرض تمنع الزحمة وتوجّه الزائر للتسجيل. */ window.can=function(k){ var p=window._perms; if(!p) return true; // قبل التحميل لا نخفي شيئاً return p[k]===true; }; /* ═══ الصلاحيات تُخزَّن مؤقتاً ═══ كانت my_perms تُنادى عند كل تحميلٍ للهوية وكل تغيّر حالة، مرّتين أحياناً في ثانية — آلاف النداءات بلا داعٍ. نُخزّن نتيجتها ٢٥ ثانية: فالنداءات المتلاحقة تُعيد رسم الواجهة من المخزَّن بلا ضرب القاعدة. ومن يحتاج طزاجةً (تغيّرت صلاحيةٌ فعلاً) ينادي applyPerms(true). */ window._permsAt=0; window.applyPerms=function(force){ if(!force && window._perms && (Date.now()-window._permsAt < 25000)){ try{ window._applyPermsUI(window._perms); }catch(e){} return; } sb.rpc('my_perms').then(function(r){ var p=(r&&r.data)||null; if(!p||!Object.keys(p).length) return; window._perms=p; window._permsAt=Date.now(); window._applyPermsUI(p); },function(){}); }; /* رسمُ الواجهة من الصلاحيات — مفصولٌ كي يُستعمل مع المخزَّن أيضاً */ window._applyPermsUI=function(p){ { var SCREENS={home:'view_home',clans:'view_clans',history:'view_history',challenges:'view_challenges',newch:'view_challenges',hiring:'view_clans', players:'view_players',agents:'view_agents',transfers:'view_transfers',solo:'view_solo',chat:'view_chat'}; // نجمع كل عنصر ينقل لشاشة، مهما كان شكل الـ onclick var links=[]; [].forEach.call(document.querySelectorAll('[onclick],[data-go]'),function(el){ var g=el.getAttribute('data-go'); if(!g){ var oc=el.getAttribute('onclick')||''; // يشمل go / dwGo / goMore وأي دالة تنقّل أخرى var m=oc.match(/go[a-z]*\(\s*['"]([a-z]+)['"]/i); if(m) g=m[1]; } if(g) links.push({el:el,go:g}); }); Object.keys(SCREENS).forEach(function(id){ var allowed=p[SCREENS[id]]===true; links.forEach(function(L){ if(L.go!==id) return; /* نحفظ تخطيطه الأصلي مرة واحدة — إعادته فارغاً تمحو flex وتفكّك الصف */ if(L.el.dataset.d0===undefined) L.el.dataset.d0 = L.el.style.display || ''; L.el.style.display = allowed ? L.el.dataset.d0 : 'none'; }); if(!allowed){ var sc=document.getElementById(id); if(sc && sc.classList.contains('active')){ sc.classList.remove('active'); var h=document.getElementById('home'); if(h) h.classList.add('active'); } } }); // ولو حاول يفتحها برابط مباشر، نرجّعه للرئيسية var origGo=window.go; if(origGo && !window._goGuarded){ window._goGuarded=true; window.go=function(id){ var need=SCREENS[id]; if(need && window._perms && window._perms[need]!==true){ toast('سجّل دخولك للوصول لهذي الصفحة'); return origGo('account'); } return origGo.apply(null,arguments); }; } document.body.classList.toggle('no-chat-write', p.act_chat_write!==true); document.body.classList.toggle('guest-mode', p.act_create_clan!==true && !window._uid); // طلب تحدي الكلانات للرئيس والنائب فقط — نوضّحه بدل ما يفشل الطلب بصمت var rq=$('btnReqCh'); if(rq){ var okCh=p.act_challenge===true; rq.disabled=!okCh; rq.style.opacity=okCh?'':'.5'; rq.style.cursor=okCh?'':'not-allowed'; rq.textContent=okCh?'طلب تحدي ⚡':'🔒 لرئيس الكلان ونائبه فقط'; /* يعرف قبل الدخول: الطلب لرئيس الكلان ونائبه */ var ent=$('btnGoNewCh'); if(ent){ var sub=ent.querySelector('.entry__t span'); if(sub) sub.textContent = okCh ? 'اختر خصمك ونمط اللعب وأطلق الطلب' : '🔒 لرئيس الكلان ونائبه فقط'; } var note=$('chLeadNote'); if(!okCh && !note && rq.parentElement){ note=document.createElement('div'); note.id='chLeadNote'; note.style.cssText='font-size:11.5px;color:var(--tx3);line-height:1.85;margin-top:8px;background:rgba(255,204,0,.06);border:1px solid rgba(255,204,0,.28);border-radius:10px;padding:10px'; note.innerHTML='🛡️ تحديات الكلانات يطلقها رئيس الكلان أو نائبه.
'+ '🎯 تقدر تتحدّى أي لاعب 1v1 من صفحة الأفراد — مفتوحة لك.'; rq.parentElement.appendChild(note); } else if(okCh && note) note.remove(); } } }; /* ===== عناوين ونصوص الصفحات — تُحرَّر من لوحة الإدارة ===== المفتاح مشتقّ من معرّف الشاشة: ui__title · ui__sub والتذييل العام: ui_foot */ window.applyUiTexts=function(force){ var run=function(map){ [].forEach.call(document.querySelectorAll('section.screen'),function(sec){ var id=sec.id; if(!id) return; var t=sec.querySelector('.stitle'), s=sec.querySelector('.ssub'); if(t){ if(!t.dataset.orig) t.dataset.orig=t.innerHTML; var vt=map['ui_'+id+'_title']; t.innerHTML = (vt!==undefined && String(vt).trim()!=='') ? vt : t.dataset.orig; } if(s){ if(!s.dataset.orig) s.dataset.orig=s.innerHTML; var vs=map['ui_'+id+'_sub']; s.innerHTML = (vs!==undefined && String(vs).trim()!=='') ? vs : s.dataset.orig; } }); var f=document.querySelector('p.foot'); if(f){ if(!f.dataset.orig) f.dataset.orig=f.innerHTML; /* ═══ اسمُ المنصّة ═══ كان محفوراً في ثلاثةِ مواضعَ فتغييرُه يحتاجُ نشراً. صار يُقرأُ من النصوصِ المنشورةِ كبقيّتِها، ويُحدَّثُ عنوانُ التبويبِ فوراً لا في التنقّلِ التالي — وإلا بقيَ الاسمُ القديمُ أمامَ من غيّرَه. */ (function(){ var vb = map.ui_brand; window.BRAND = (vb!==undefined && String(vb).trim()!=='') ? String(vb).trim() : 'COD·AR — منصة كلانات كول اوف ديوتي العربية'; try{ var cur=document.querySelector('.screen.active'); var t=(window.SCR_TITLES_LAST||''); document.title = t ? (t+' · '+window.BRAND) : window.BRAND; void cur; }catch(_){} })(); var vf=map.ui_foot; f.innerHTML = (vf!==undefined && String(vf).trim()!=='') ? vf : f.dataset.orig; } /* عناوين القائمة الجانبية وورقة «المزيد» — نبدّل النص فقط ونُبقي الأيقونة والسهم وعدّاد الإشعارات كما هي */ [].forEach.call(document.querySelectorAll('[data-nav]'),function(el){ var key=el.getAttribute('data-nav'); var v=map['ui_nav_'+key]; // عناوين الأقسام نص خالص if(el.classList.contains('dw__sec')){ if(!el.dataset.orig) el.dataset.orig=el.textContent; el.textContent=(v!==undefined && String(v).trim()!=='')?v:el.dataset.orig; return; } // نلقى العقدة النصية اللي بين الأيقونة والسهم var node=null; for(var i=0;iاحفظه من حسابي عشان تقدر تنشر طلبك'; } if($('skExp')) $('skExp').value=p.fa_exp||''; if($('skHours')) $('skHours').value=p.fa_hours||''; if($('skNote')) $('skNote').value=p.fa_note||''; segSet('skStyle', p.fa_style||'sniper'); if($('skSend')) $('skSend').textContent = p.seeking_clan ? '💾 حدّث طلبي' : '📣 انشر طلبي'; if($('skRemove')) $('skRemove').hidden = !p.seeking_clan; },function(){}); }); // نصفّر اختيار الكلان عند كل دخول if($('skClan')) $('skClan').value=''; if($('skClanQ')) $('skClanQ').value=''; if($('skClanPick')) { $('skClanPick').hidden=true; $('skClanPick').innerHTML=''; } skHideSug(); }; /* ===== بحث استنتاجي عن الكلانات ===== «pw» تطلع pwrz و pwr و pwoer — مطابقة متتابعة لا حرفية */ function skHideSug(){ var s=$('skClanSug'); if(s){ s.hidden=true; s.innerHTML=''; } window._skSug=null; window._skIdx=-1; } function skMark(txt,q){ var out='', qi=0, t=String(txt||''), lq=q.toLowerCase(); for(var i=0;i'; qi++; } else out+=esc(ch); } return out; } function skRank(c,q){ var tag=String(c.tag||'').toLowerCase(), nm=String(c.name||'').toLowerCase(), lq=q.toLowerCase(); if(tag===lq) return 0; if(tag.indexOf(lq)===0) return 1; if(tag.indexOf(lq)!==-1) return 2; if(nm.indexOf(lq)!==-1) return 3; return 4; } window.skSearchClans=function(q){ q=String(q||'').trim(); if(q.length<1){ skHideSug(); return; } // نظّف: نمنع فواصل PostgREST من كسر الاستعلام var clean=q.replace(/[^\p{L}\p{N} _-]/gu,''); if(!clean){ skHideSug(); return; } // نمط متتابع: p%w%r% → يطابق pwrz و pwoer var sub='%'+clean.split('').join('%')+'%'; /* الكلانات بانتظار الترسيم تظهر أيضاً — هي الأحوج لأعضاء ليكتمل عددها */ sb.from('clans').select('id,tag,name,logo_url,status,recruiting,points') .or('tag.ilike.'+sub+',name.ilike.'+sub) .limit(25).then(function(r){ var rows=(r.data||[]).filter(function(c){ return c.recruiting!==false; }); rows.sort(function(a,b){ var d=skRank(a,clean)-skRank(b,clean); return d||((b.points||0)-(a.points||0)); }); rows=rows.slice(0,8); var box=$('skClanSug'); if(!box) return; if(!rows.length){ box.hidden=false; box.innerHTML='
ما فيه كلان فاتح باب القبول يطابق «'+esc(q)+'»
'; window._skSug=[]; window._skIdx=-1; return; } window._skSug=rows; window._skIdx=-1; box.hidden=false; box.innerHTML=rows.map(function(c,i){ var tg=String(c.tag||''); var lg=c.logo_url ? '' : ''+esc(tg.slice(0,2))+''; var rat=c.status==='ratified'; return '
'+lg+ '
CLAN '+skMark(tg,clean)+''+ ''+(c.name?esc(c.name)+' · ':'')+(c.points||0)+' نقطة
'+ ''+(rat?'معتمد':'بانتظار الترسيم')+'
'; }).join(''); },function(){ skHideSug(); }); }; /* ═══ تنبّؤ خصم التحدي ═══ مطابقة متتابعة: «pw» تلقّط PWRZ، و«mo» تلقّط MOT — مثل بحث الكلانات. (datalist لا يعمل على سفاري بالآيفون، فبنيناها بأنفسنا) */ function chHideSug(){ var b=$('chOppSug'); if(b){ b.hidden=true; b.innerHTML=''; } window._chSug=null; window._chIdx=-1; } window.chSearchOpp=function(q){ q=String(q||'').trim(); if(q.length<1){ chHideSug(); return; } var clean=q.replace(/[^\p{L}\p{N} _-]/gu,''); if(!clean){ chHideSug(); return; } var sub='%'+clean.split('').join('%')+'%'; var mine=window._myClanId||window._myMemberClanId||null; sb.from('clans').select('id,tag,name,logo_url,status,points,wins,losses') .or('tag.ilike.'+sub+',name.ilike.'+sub).limit(25).then(function(r){ var rows=(r.data||[]).filter(function(c){ return c.id!==mine; }); rows.sort(function(a,b){ var d=skRank(a,clean)-skRank(b,clean); return d||((b.points||0)-(a.points||0)); }); rows=rows.slice(0,8); var box=$('chOppSug'); if(!box) return; if(!rows.length){ box.hidden=false; box.innerHTML='
ما فيه كلان يطابق «'+esc(q)+'»
اترك الخانة فاضية = تحدٍّ مفتوح لأي كلان
'; window._chSug=[]; window._chIdx=-1; return; } window._chSug=rows; window._chIdx=-1; box.hidden=false; box.innerHTML=rows.map(function(c,i){ var tg=String(c.tag||''); var lg=c.logo_url ? '' : ''+esc(tg.slice(0,2))+''; var rat=c.status==='ratified'; return '
'+lg+ '
CLAN '+skMark(tg,clean)+''+ ''+(c.name?esc(c.name)+' · ':'')+(c.points||0)+' نقطة · '+(c.wins||0)+'ف/'+(c.losses||0)+'خ
'+ ''+(rat?'معتمد':'بانتظار الترسيم')+'
'; }).join(''); },function(){ chHideSug(); }); }; window.chPickOpp=function(i){ var c=(window._chSug||[])[i]; if(!c) return; var tg=String(c.tag||''); if($('chOpp')) $('chOpp').value=tg; var pk=$('chOppPick'); if(pk){ pk.hidden=false; pk.innerHTML='
'+ '🎯
CLAN '+esc(tg)+''+ 'بيوصل طلبك لقيادته'+(c.status==='ratified'?'':' · الكلان بانتظار الترسيم')+'
'+ '
'; } chHideSug(); }; window.chClearOpp=function(){ if($('chOpp')){ $('chOpp').value=''; $('chOpp').focus(); } var pk=$('chOppPick'); if(pk){ pk.hidden=true; pk.innerHTML=''; } chHideSug(); }; (function(){ var q=$('chOpp'); if(!q) return; var t=null; q.addEventListener('input',function(){ var pk=$('chOppPick'); if(pk&&!pk.hidden){ pk.hidden=true; pk.innerHTML=''; } clearTimeout(t); var v=q.value; t=setTimeout(function(){ window.chSearchOpp(v); }, 170); }); q.addEventListener('focus',function(){ if((q.value||'').trim()) window.chSearchOpp(q.value); }); q.addEventListener('keydown',function(e){ var list=window._chSug||[]; if(!list.length) return; if(e.key==='ArrowDown'||e.key==='ArrowUp'){ e.preventDefault(); window._chIdx=(window._chIdx===undefined?-1:window._chIdx)+(e.key==='ArrowDown'?1:-1); if(window._chIdx<0) window._chIdx=list.length-1; if(window._chIdx>=list.length) window._chIdx=0; [].forEach.call(document.querySelectorAll('#chOppSug .sg'),function(el,i){ el.classList.toggle('on', i===window._chIdx); }); } else if(e.key==='Enter'){ e.preventDefault(); window.chPickOpp(window._chIdx>=0?window._chIdx:0); } else if(e.key==='Escape') chHideSug(); }); var box=$('chOppSug'); if(box) box.addEventListener('click',function(e){ var d=e.target.closest?e.target.closest('.sg'):null; if(!d) return; window.chPickOpp(parseInt(d.getAttribute('data-i'),10)||0); }); document.addEventListener('click',function(e){ if(!e.target.closest) return; if(!e.target.closest('#chOppSug') && e.target!==q) chHideSug(); }); })(); window.skPickClan=function(i){ var c=(window._skSug||[])[i]; if(!c) return; if($('skClan')) $('skClan').value=c.id; if($('skClanQ')) $('skClanQ').value=String(c.tag||''); var pk=$('skClanPick'); if(pk){ pk.hidden=false; var tg=String(c.tag||''); pk.innerHTML='
'+ '
CLAN '+esc(tg)+''+ 'بيوصل رئيسه طلبك'+(c.status==='ratified'?'':' · الكلان بانتظار الترسيم')+'
'+ '
'; } skHideSug(); }; window.skClearClan=function(){ if($('skClan')) $('skClan').value=''; if($('skClanQ')) { $('skClanQ').value=''; $('skClanQ').focus(); } var pk=$('skClanPick'); if(pk){ pk.hidden=true; pk.innerHTML=''; } skHideSug(); }; (function(){ var b=$('agFindClan'); if(b) b.addEventListener('click',function(){ go('seek'); }); var k=$('skKind'); if(k) k.addEventListener('click',function(e){ var t=e.target.closest('button'); if(!t) return; [].forEach.call(k.querySelectorAll('button'),function(x){x.classList.remove('on');}); t.classList.add('on'); var one=t.getAttribute('data-v')==='one'; var box=$('skClanBox'); if(box) box.hidden=!one; if(one){ setTimeout(function(){ var q=$('skClanQ'); if(q) q.focus(); },80); } else window.skClearClan(); }); /* البحث الاستنتاجي */ var cq=$('skClanQ'); if(cq){ var ctm; cq.addEventListener('input',function(){ if($('skClan')) $('skClan').value=''; var pk=$('skClanPick'); if(pk){ pk.hidden=true; pk.innerHTML=''; } clearTimeout(ctm); var v=cq.value; ctm=setTimeout(function(){ window.skSearchClans(v); },200); }); cq.addEventListener('focus',function(){ if(cq.value.trim() && !$('skClan').value) window.skSearchClans(cq.value); }); cq.addEventListener('keydown',function(e){ var list=window._skSug||[]; if(!list.length) return; if(e.key==='ArrowDown'||e.key==='ArrowUp'){ e.preventDefault(); window._skIdx=(window._skIdx===undefined?-1:window._skIdx)+(e.key==='ArrowDown'?1:-1); if(window._skIdx<0) window._skIdx=list.length-1; if(window._skIdx>=list.length) window._skIdx=0; [].forEach.call(document.querySelectorAll('#skClanSug .sg'),function(el,i){ el.classList.toggle('on', i===window._skIdx); }); } else if(e.key==='Enter'){ e.preventDefault(); window.skPickClan(window._skIdx>=0?window._skIdx:0); } else if(e.key==='Escape') skHideSug(); }); } var sug=$('skClanSug'); if(sug) sug.addEventListener('click',function(e){ var t=e.target.closest('.sg'); if(!t) return; window.skPickClan(parseInt(t.getAttribute('data-i'),10)); }); document.addEventListener('click',function(e){ if(!e.target.closest || e.target.closest('#skClanBox')) return; skHideSug(); }); var st=$('skStyle'); if(st) st.addEventListener('click',function(e){ var t=e.target.closest('button'); if(!t) return; [].forEach.call(st.querySelectorAll('button'),function(x){x.classList.remove('on');}); t.classList.add('on'); }); var rm=$('skRemove'); if(rm) rm.addEventListener('click',function(){ if(!window._uid) return; if(!confirm('تسحب طلبك؟ بيختفي من «يدورون كلان» ومن الديسكورد.')) return; sb.from('profiles').update({seeking_clan:false}).eq('id',window._uid).then(function(r){ if(r&&r.error){ toast(errAr(r.error.message)); return; } toast('🗑️ سُحب طلبك'); go('agents'); }); }); var sd=$('skSend'); if(sd) sd.addEventListener('click',function(){ var msg=$('skMsg'); if(!window._uid){ go('account'); return; } var psn=(($('skPsn')||{}).value||'').trim(); if(psn.length<3||psn.length>24){ msg.style.color='var(--red)'; msg.textContent='🎮 آيدي السوني إجباري — من 3 إلى 24 حرفاً'; return; } var kind=segVal('skKind','any'), clanId=(($('skClan')||{}).value||''); if(kind==='one' && !clanId){ msg.style.color='var(--red)'; msg.textContent='اكتب تاق الكلان واختره من القائمة، أو رجّع الخيار لـ «أي كلان»'; var cqq=$('skClanQ'); if(cqq) cqq.focus(); return; } msg.style.color='var(--gold)'; msg.textContent='...جاري النشر'; var patch={ seeking_clan:true, fa_style:segVal('skStyle','sniper'), fa_exp:(($('skExp')||{}).value||'').trim()||null, fa_hours:(($('skHours')||{}).value||'').trim()||null, fa_note:(($('skNote')||{}).value||'').trim()||null, fa_at:new Date().toISOString() }; sb.from('profiles').update(patch).eq('id',window._uid).then(function(r){ if(r&&r.error){ msg.style.color='var(--red)'; msg.textContent=errAr(r.error.message); return; } var done=function(extra){ msg.style.color='#3ddc84'; msg.textContent='✅ نزل طلبك'+(extra||''); setTimeout(function(){ go('agents'); window._agHighlight=window._uid; },700); }; if(kind==='one'){ sb.from('profiles').select('username').eq('id',window._uid).single().then(function(pr){ sb.from('join_requests').insert({clan_id:clanId, user_id:window._uid, username:(pr.data&&pr.data.username)||'لاعب', psn_id:psn, message:(($('skNote')||{}).value||'').trim()}).then(function(ir){ if(ir.error && /duplicate|unique/i.test(ir.error.message||'')) return done(' — وعندك طلب سابق لنفس الكلان'); if(ir.error) return done(' — لكن تعذّر إرسال الطلب للكلان المحدّد'); done(' ووصل رئيس الكلان طلبك'); }); },function(){ done(''); }); } else done(''); }); }); })(); /* ===== عروض الانضمام: قائد يعرض · لاعب يرد ===== */ window.offerPlayer=function(pid,name){ var cid=window._myClanId; if(!cid){ toast('سجّل كلانك أولاً'); return; } var msg=prompt('رسالتك إلى '+name+' (اختياري):\nمثال: نلعب يومياً 10م ونبي سنايبر',''); if(msg===null) return; sb.rpc('send_offer',{p_clan:cid,p_player:pid,p_msg:msg||null}).then(function(r){ if(r&&r.error){ var m=String(r.error.message||''); toast(/OFFER_EXISTS/.test(m)?'عندك عرض معلّق لهذا اللاعب' :/ALREADY_IN_CLAN/.test(m)?'اللاعب صار بكلان' :/CLAN_FULL/.test(m)?'كلانك مكتمل' :/CLAN_NOT_RATIFIED/.test(m)?'كلانك بانتظار الترسيم' :/LEADER_ONLY/.test(m)?'العروض للرئيس أو نائبه' :(window.errAr?window.errAr(m):'تعذّر الإرسال')); return; } toast('📩 أُرسل عرضك إلى '+name); }); }; window.loadOffers=function(){ var box=$('myOffers'); if(!box||!window._uid) return; sb.from('clan_offers').select('id,clan_id,message,offered_name,created_at') .eq('player_id',window._uid).eq('status','pending').order('id',{ascending:false}).then(function(r){ var rows=r.data||[]; var card=$('offersCard'); if(card) card.hidden=!rows.length; if(!rows.length){ box.innerHTML=''; return; } sb.from('clans').select('id,tag,type,points,logo_url').in('id',rows.map(function(o){return o.clan_id;})).then(function(cr){ var cm={}; (cr.data||[]).forEach(function(c){cm[c.id]=c;}); box.innerHTML=rows.map(function(o){ var c=cm[o.clan_id]||{}; var lg=c.logo_url?'':''+esc(String(c.tag||'?').slice(0,2).toUpperCase())+''; return '
'+ '
'+lg+ '
CLAN '+esc(String(c.tag||'؟'))+''+ '
'+(c.points||0)+' نقطة · '+(c.type==='rush'?'RASH':'SNIP')+' · من '+esc(o.offered_name||'القائد')+'
'+ (o.message?'
'+esc(o.message)+'
':'')+ '
'+ ''+ ''+ '
'; }).join(''); }); },function(){}); }; window.answerOffer=function(id,ok){ if(!ok){ sb.from('clan_offers').update({status:'declined',responded_at:new Date().toISOString()}).eq('id',id) .then(function(){ toast('رُفض العرض'); window.loadOffers(); }); return; } sb.from('clan_offers').select('clan_id').eq('id',id).single().then(function(o){ var cid=o.data&&o.data.clan_id; sb.rpc('accept_offer',{p_id:id}).then(function(r){ if(r&&r.error){ toast(window.errAr?window.errAr(r.error.message):'تعذّر القبول'); return; } if(r.data==='already'){ toast('تم الرد عليه مسبقاً'); window.loadOffers(); return; } toast('🎉 انضممت إلى '+r.data); window.loadOffers(); if(window.refreshNow) window.refreshNow(); if(cid) window.showClanContact(cid, r.data); else setTimeout(function(){ go('account'); },900); }); },function(){ toast('تعذّر القبول'); }); }; /* بعد الانضمام: يسلّم اللاعبَ آيدياتِ القيادة ليضيفهم على السوني */ window.showClanContact=function(clanId, clanLabel){ var sh=$('chSheet'), bk=$('chBack'), body=$('chsBody'); if(!sh||!body){ setTimeout(function(){ go('account'); },900); return; } bk.hidden=false; sh.hidden=false; $('chsTitle').textContent='🎉 انضممت إلى '+(clanLabel||'كلانك'); $('chsSub').textContent='الخطوة الأخيرة — تواصل مع القيادة'; body.innerHTML='
⏳ جاري جلب بيانات القيادة...
'; sb.from('clan_members').select('role,member_name,psn,profiles(username,psn_id)') .eq('clan_id',clanId).in('role',['leader','vice']).then(function(r){ var rows=r.data||[]; var h='
'+ '🎮 أضف قيادة كلانك على السوني
'+ 'التنسيق يصير بينكم مباشرة — أضفهم بآيدي السوني وابدأوا اللعب.
'; h+= rows.length ? rows.map(function(m){ var nm=(m.psn||'').trim()||(m.profiles&&(m.profiles.psn_id||'').trim())||m.member_name||(m.profiles&&m.profiles.username)||'القيادة'; var psn=(m.psn||(m.profiles&&m.profiles.psn_id)||'').trim(); var isL=m.role==='leader'; return '
'+ ''+esc(nm.slice(0,2).toUpperCase())+''+ '
'+esc(nm)+''+ '
'+(isL?'👑 رئيس الكلان':'🎖️ نائب الرئيس')+'
'+ '
'+(psn?'🎮 '+esc(psn)+'':'— بلا PSN —')+'
'+ (psn?'':'')+ '
'; }).join('') : '
ما فيه رئيس أو نائب مسجّل بهذا الكلان بعد.
راجع إدارة المنصة أو انتظر القيادة تتواصل معك.
'; h+='
'+ '
'; body.innerHTML=h; },function(){ body.innerHTML='
تعذّر جلب بيانات القيادة
'; }); }; window.copyTxt=function(v){ try{ navigator.clipboard.writeText(v); toast('📋 نُسخ: '+v); } catch(e){ toast(v); } }; window.loadTransfers=function(){ var box=$('transfersList'); if(!box) return; sb.from('clans').select('id,tag').then(function(cm){ var CM={}; (cm.data||[]).forEach(function(c){CM[c.id]=c.tag;}); sb.from('transfers').select('player_name,from_clan,to_clan,status,approved_at,created_at').in('status',['approved','pending']).order('id',{ascending:false}).limit(60).then(function(r){ var rows=r.data||[]; box.innerHTML = rows.length? rows.map(function(t){ var dt=tDT((t.approved_at||t.created_at)); var av=''+esc(String(t.player_name||'?').slice(0,2).toUpperCase())+''; var pname=esc(t.player_name||'لاعب'); // انضم لكلان (قبول طلب انضمام) if(t.status==='approved' && !t.from_clan && t.to_clan){ return '
'+ '
'+av+'
'+pname+'
✅ انضم لكلان · 📅 '+esc(dt)+'
🆕
'+ '
— — —CLAN '+esc(String(CM[t.to_clan]||'؟').toUpperCase())+'
'+ '
'; } // انتقال بين كلانَين if(t.status==='approved' && t.from_clan && t.to_clan){ return '
'+ '
'+av+'
'+pname+'
🔄 انتقال مكتمل · 📅 '+esc(dt)+'
🔄
'+ '
CLAN '+esc(String(CM[t.from_clan]||'؟').toUpperCase())+'CLAN '+esc(String(CM[t.to_clan]||'؟').toUpperCase())+'
'+ '
'; } // خروج (طوعي أو طرد) return '
'+ '
'+av+'
'+pname+'
🚪 خرج من الكلان · 📅 '+esc(dt)+'
🚪
'+ '
طلع من CLAN '+esc(String(CM[t.from_clan]||'؟').toUpperCase())+'لم ينضم لكلان جديد بعد
'+ '
'; }).join('') : '
لا حركات انتقال بعد 🔄
تظهر هنا عند خروج أو انتقال أي لاعب بين الكلانات
'; },function(){ box.innerHTML='
لا انتقالات بعد
'; }); }); }; window.toggleMore=function(){ if(window.closeDrawer) window.closeDrawer(); if(window.toggleUser) window.toggleUser(true); var s=document.getElementById('moreSheet'), b=document.getElementById('moreBackdrop'); var show = s && s.hidden; if(s) s.hidden=!show; if(b) b.hidden=!show; }; window.goMore=function(id){ window.toggleMore(); window.navTo(id); }; window.renderClansCta=function(){ var box=document.getElementById('clansCta'); if(!box) return; if(!window._uid){ box.innerHTML=''; return; } if(window._myClanId){ box.innerHTML=''; return; } if(window._myMemberClanId){ box.innerHTML='
✅ أنت عضو في CLAN '+esc((window._myMemberClanTag||'').toUpperCase())+''+(window._myMemberRole==='vice'?' 🎖️ نائب':'')+ (window._myMemberRole==='leader'?'':'')+ '
'; return; } box.innerHTML=''+ ''; }; window.openJoin=function(){ var card=$('joinCard'); if(!card) return; // البطاقة صارت بصفحة دليل الكلانات — ننتقل لها أولاً var sec=document.getElementById('clandir'); if(sec && !sec.classList.contains('active')) go('clandir'); sb.from('clans').select('id,tag').order('tag').then(function(r){ if($('joinClan')) $('joinClan').innerHTML=''+(r.data||[]).map(function(c){return '';}).join(''); }); card.hidden=false; setTimeout(function(){ card.scrollIntoView({behavior:'smooth',block:'center'}); },60); }; window.submitJoin=function(){ var res=$('joinResult'); if(!res) return; if(_blocked()){ res.style.color='var(--red)'; res.textContent='🚫 حسابك محظور'; return; } window.authUser().then(function(r){ var u=r&&r.data&&r.data.user; if(!u){ res.style.color='var(--red)'; res.textContent='سجّل دخولك أولاً'; return; } var clanId=$('joinClan')?$('joinClan').value:''; if(!clanId){ res.style.color='var(--gold)'; res.textContent='اختر الكلان'; return; } sb.from('profiles').select('username,psn_id').eq('id',u.id).single().then(function(pr){ var psn=(pr.data&&pr.data.psn_id)||'', uname=(pr.data&&pr.data.username)||''; if(!psn){ res.style.color='var(--red)'; res.innerHTML='⚠️ لازم تحفظ PSN ID في حسابك أول — روح لحسابي'; return; } res.style.color='var(--gold)'; res.textContent='...جاري الإرسال'; sb.from('join_requests').insert({clan_id:clanId, user_id:u.id, username:uname, psn_id:psn, message:(($('joinMsg')&&$('joinMsg').value)||'').trim()}).then(function(ir){ if(ir.error){ res.style.color='var(--red)'; res.textContent=/duplicate|unique/i.test(ir.error.message||'')?'عندك طلب سابق لهذا الكلان ✋':(errAr(ir.error.message)); return; } res.style.color='#3ddc84'; res.textContent='✅ أُرسل الطلب — بيوصل رئيس الكلان إشعار'; if($('joinMsg')) $('joinMsg').value=''; }); }); }); }; window.loadJoinReqs=function(clanId){ var card=$('mgJoinCard'), box=$('mgJoinReqs'); if(!box) return; sb.from('join_requests').select('id,user_id,username,psn_id,message').eq('clan_id',clanId).eq('status','pending').order('id',{ascending:false}).then(function(r){ /* ملخّص في مجموعة «يحتاج ردّك» أعلى الصفحة */ try{ var n=(r.data||[]).length, at=document.getElementById('mgAttention'), ar=document.getElementById('mgAttentionRows'); if(at&&ar){ at.hidden=!n; ar.innerHTML = n ? ('') : ''; } }catch(e){} var rows=r.data||[]; if($('mgJoinCount')) $('mgJoinCount').textContent=rows.length?('('+rows.length+')'):''; if(card) card.hidden = rows.length===0; box.innerHTML = rows.map(function(j){ return '
'+esc((j.username||'?').slice(0,1).toUpperCase())+'
'+esc(j.username||'لاعب')+'🎮 PSN: '+esc(j.psn_id||'—')+(j.message?' · '+esc(j.message):'')+'
'+ '
'+ '
'; }).join(''); }); }; (function(){ var b=document.getElementById('joinSend'); if(b) b.addEventListener('click',window.submitJoin); })(); window.loadClans = function(){ if(window.renderClansCta) window.renderClansCta(); var box = $('realClanList'); if(!box) return; sb.from('clans').select('id,tag,type,status,points,wins,losses,streak,created_by,wins_casual,losses_casual') .neq('status','archived').order('points',{ascending:false}).limit(200).then(function(r){ if(r.error){ box.innerHTML=''; return; } window._allClans = r.data||[]; window.renderClanRanking(); }); }; window.renderClanRanking=function(){ var box=$('realClanList'); if(!box) return; /* ═══ بطولتان لا واحدة ═══ الرشاش لعبةٌ أخرى — محاسبته بسلّم القنّاصة ظلم. كل نمطٍ سلّمُه، وكلانٌ بلا نمطٍ محدد يظهر في الاثنين. */ var style=window._clanStyle||'sniper'; if((window._appSet||{}).hide_rush==='1') style='sniper'; /* المالك أخفى بطولة الرشاش */ var all=(window._allClans||[]).slice().filter(function(c){ if(c.status==='archived') return false; if(c.type==='rush') return style==='rush'; if(c.type==='sniper') return style==='sniper'; return true; }); var mode=window._clanBoard||'ranked'; /* لوحتان مستقلتان: المصنّف بالنقاط · غير المصنّف بعدد الانتصارات */ if(mode==='casual'){ all=all.filter(function(c){ return (c.wins_casual||0)+(c.losses_casual||0)>0; }); all.sort(function(a,b){ var d=(b.wins_casual||0)-(a.wins_casual||0); if(d) return d; return (a.losses_casual||0)-(b.losses_casual||0); }); } else { all.sort(function(a,b){ return (b.points||0)-(a.points||0); }); } var nb=$('boardNote'); var styleNm = style==='rush' ? '🔫 الرشاش' : '🔭 السنايبر'; if(nb) nb.innerHTML = mode==='casual' ? '🤝 غير مصنّف '+styleNm+' — بعدد الانتصارات، لا علاقة له بالنقاط.' : '🏅 دوري '+styleNm+' الرسمي — بالنقاط من التحديات المصنّفة. لكل نمطٍ سلّمه المستقل.'; if(!all.length){ box.innerHTML='
'+ (mode==='casual'?'ما فيه مباريات غير مصنّفة موثّقة بعد 🤝':('لا كلانات '+styleNm+' مصنّفة بعد — كن أول من يعتلي هذا السلّم! 👑'))+'
'; return; } var topN=window._clanTopN||0, q=(window._clanSearch||'').trim().toUpperCase(); var list=all.map(function(c,i){ return {c:c,rank:i+1}; }); if(topN>0) list=list.slice(0,topN); if(q) list=list.filter(function(x){ return String(x.c.tag||'').indexOf(q)!==-1; }); if(!list.length){ box.innerHTML='
ما فيه كلان بهذا البحث 🔎
'; return; } box.innerHTML = list.map(function(x){ var c=x.c, rank=x.rank; var w=(mode==='casual')?(c.wins_casual||0):(c.wins||0); var l=(mode==='casual')?(c.losses_casual||0):(c.losses||0); var tot=w+l, ratio=tot?Math.round(w*100/tot):0; var bigVal=(mode==='casual')?w:(c.points||0); var bigLbl=(mode==='casual')?'فوز غير مصنّف':'نقطة'; var t=typeBadge(c.type); var st=c.status==='ratified'?'':' · بانتظار الترسيم'; var ini=String(c.tag||'?').slice(0,2).toUpperCase(); var fire=(c.streak&&c.streak>=2)?' 🔥'+c.streak:''; var mineB=(window._uid&&c.created_by===window._uid)?' كلاني':''; if(rank===1){ return '
'+ '
👑
'+esc(ini)+''+ '
CLAN '+esc(c.tag)+' '+t+(fire?' '+fire+'':'')+'
🏆 المتصدّر'+mineB+'
'+ '
'+bigVal+'
'+bigLbl+'
'+ '
✅ '+w+' فوز❌ '+l+' خسارة📊 '+ratio+'% معدل الفوز
'+ '
'; } var medal=rank===2?'🥈':rank===3?'🥉':('#'+rank); /* شارةُ الرتبةِ محلَّ السداسيّةِ الزرقاءِ الصمّاء — الترتيبُ هو الموضعُ الذي يُقارِنُ فيه الناسُ أنفسَهم، فالشارةُ فيه تُرى يوميّاً. */ return '
'+medal+''+(window.tierBadge?window.tierBadge(c.points,48):'
'+esc(ini)+'
')+ '
CLAN '+esc(c.tag)+'
'+ '
'+t+(fire?' '+fire+'':'')+mineB+' · '+bigVal+' '+bigLbl+' · '+w+'ف '+l+'خ · 📊 '+ratio+'%'+st+'
'; }).join(''); }; /* ═══ سلوك المنسدلتين ═══ الضغط على الكلمة يفتح، والاختيار يُغلق ويكتب الكلمة الجديدة — ومنطق التبديل نفسه لم يُمسّ: الأزرار ذاتها صارت عناصر القائمة. */ (function(){ function closeAll(){ [].forEach.call(document.querySelectorAll('.dd.open'),function(d){ d.classList.remove('open'); var m=d.querySelector('.dd__menu'); if(m) m.hidden=true; }); } [].forEach.call(document.querySelectorAll('#ddStyle,#ddBoard'),function(d){ var pill=d.querySelector('.dd__pill'), menu=d.querySelector('.dd__menu'); if(!pill||!menu) return; pill.addEventListener('click',function(e){ e.stopPropagation(); var was=d.classList.contains('open'); closeAll(); if(!was){ d.classList.add('open'); menu.hidden=false; } }); menu.addEventListener('click',function(e){ var b=e.target.closest('button'); if(!b) return; var tx=d.querySelector('.dd__tx'); if(tx) tx.textContent=b.textContent.replace('✓','').trim(); setTimeout(closeAll, 40); }); }); document.addEventListener('click',function(e){ if(!e.target.closest || !e.target.closest('.dd')) closeAll(); }); document.addEventListener('keydown',function(e){ if(e.key==='Escape') closeAll(); }); /* السحبة تُحدّث الكلمة أيضاً */ window.ddSyncLabels=function(){ [].forEach.call(document.querySelectorAll('#ddStyle,#ddBoard'),function(d){ var on=d.querySelector('.dd__menu button.on'), tx=d.querySelector('.dd__tx'); if(on&&tx) tx.textContent=on.textContent.replace('✓','').trim(); }); }; /* ═══ إخفاء بطولة الرشاش ═══ مفتاح hide_rush من اللوحة يطوي منتقي النمط ويعيد الجميع لسلّم السنايبر — ويسري فوراً عبر بثّ الإعدادات. */ window.applyRushVis=function(){ var off=((window._appSet||{}).hide_rush==='1'); var d=$('ddStyle'); if(d) d.style.display=off?'none':''; var h=$('homeStyle'); if(h) h.style.display=off?'none':''; /* ═══ يخفي الرشاش من كل منتقٍ في المنصة ═══ تسجيل الكلان · طلب التحدي · أسلوب الباحث عن كلان · فلاتر الدليل. من كان مختاراً الرشاش يُعاد للسنايبر بنقرة حقيقية كي تتزامن حالته. */ [].forEach.call(document.querySelectorAll( '#clType [data-v="rush"], #chPs [data-v="rush"], '+ '#skStyle [data-v="rush"], #skStyle [data-v="both"], '+ '#cdirChips [data-v="rush"], #clansChips [data-v="rush"]' ),function(b){ b.style.display = off ? 'none' : ''; if(off && b.classList.contains('on')){ var sn=b.parentNode.querySelector('[data-v="sniper"]'); if(sn){ try{ sn.click(); }catch(e){ b.classList.remove('on'); sn.classList.add('on'); } } } }); /* منتقي النمط في طلب التحدي يصير بلا معنى بزرٍّ واحد */ var cp=$('chPs'); if(cp) cp.style.display=off?'none':''; var ct=$('clType'); if(ct) ct.style.display=off?'none':''; if(!off) return; if(window._clanStyle==='rush'){ window._clanStyle='sniper'; var stg=$('clanStyle'); if(stg) [].forEach.call(stg.querySelectorAll('button'),function(x){ x.classList.toggle('on', x.getAttribute('data-v')==='sniper'); }); if(window.ddSyncLabels) window.ddSyncLabels(); if(window.renderClanRanking) window.renderClanRanking(); } if(window._homeStyle==='rush'){ window._homeStyle='sniper'; try{ cSet('homePodium',''); }catch(e){} if(window.loadHome) window.loadHome(); } }; })(); /* الرسم ثم الحركة — لا انتظار قبل المحتوى */ window.swapBoard=function(cls){ var b=$('realClanList'); if(window.renderClanRanking) window.renderClanRanking(); if(!b) return; b.classList.remove('lin-l','lin-r','lin-f'); void b.offsetWidth; b.classList.add(cls); }; (function(){ var st=$('clanStyle'); if(st) st.addEventListener('click',function(e){ var b=e.target.closest('button'); if(!b) return; var nv=b.getAttribute('data-v'); if(nv===window._clanStyle) return; [].forEach.call(st.querySelectorAll('button'),function(x){x.classList.remove('on')}); b.classList.add('on'); window._clanStyle=nv; /* الرشاش على اليسار فيدخل محتواه من اليسار — والعكس */ window.swapBoard(nv==='rush'?'lin-l':'lin-r'); }); var s=$('clanSearch'); if(s) s.addEventListener('input',function(){ window._clanSearch=s.value; window.renderClanRanking&&window.renderClanRanking(); }); var tf=$('clanTop'); if(tf) tf.querySelectorAll('button').forEach(function(b){ b.addEventListener('click',function(){ tf.querySelectorAll('button').forEach(function(x){x.classList.remove('on')}); b.classList.add('on'); window._clanTopN=parseInt(b.getAttribute('data-top'),10)||0; window.renderClanRanking&&window.renderClanRanking(); }); }); })(); var clTag=$('clTag'); if(clTag) clTag.addEventListener('input', function(){ $('clPrev').textContent='CLAN '+((clTag.value||'____').toUpperCase())+' | COD-AR'; }); var clType=$('clType'); if(clType) clType.querySelectorAll('button').forEach(function(b){ b.addEventListener('click', function(){ clType.querySelectorAll('button').forEach(function(x){x.classList.remove('on')}); b.classList.add('on'); });}); /* ===== قائمة لاعبي الكلان عند التسجيل ===== */ window._clMin=6; window.loadCreateClan=function(){ /* بوابة: ما فيه تسجيل كلان قبل توثيق آيدي السوني بالحساب */ var gate=$('clPsnGate'), body=$('clBody'); sb.from('profiles').select('psn_id').eq('id',window._uid).single().then(function(pr){ var myPsn=((pr.data&&pr.data.psn_id)||'').trim(); if(!myPsn){ if(gate){ gate.hidden=false; gate.innerHTML= '
'+ '
🔑
'+ 'وثّق آيدي السوني أولاً'+ '
'+ 'آيدي السوني هو هويتك بالمنصة — به يضيفك الخصم وقت التحدي، وبه يرتبط أعضاء كلانك بك.
'+ 'ما تقدر تسجّل كلاناً قبل حفظه.
'+ ''+ '
'; } if(body) body.hidden=true; return; } if(gate){ gate.hidden=true; gate.innerHTML=''; } if(body) body.hidden=false; window._myPsn=myPsn; clBuild(myPsn); },function(){ if(body) body.hidden=false; clBuild(''); }); }; window.clBuild=function(myPsn){ getSettings(true).then(function(m){ var mn=parseInt(m.clan_min_members||'1',10); if(!mn||mn<1) mn=1; window._clMin=mn; var box=$('clNeedBox'); if(box){ if(mn<=1){ box.style.background='rgba(61,220,132,.06)'; box.style.borderColor='rgba(61,220,132,.3)'; box.innerHTML='✅ سجّل كلانك الآن
'+ 'ما فيه حد أدنى للأعضاء حالياً — سجّل ويترسّم كلانك فوراً، وأضف لاعبيك متى ما جهزوا من صفحة إدارة الكلان.'; } else { box.style.background='rgba(255,204,0,.06)'; box.style.borderColor='rgba(255,204,0,.3)'; var rest=mn-2; box.innerHTML='⚠️ شرط الترسيم
'+ 'لازم '+mn+' '+(mn===2?'لاعبَين':'لاعبين')+' على الأقل — أنت (الرئيس)'+ (mn>=2?' والنائب':'')+(rest>0?(' و'+rest+' '+(rest===1?'عضو':'أعضاء')):'')+'.'+ ' ما تقدر تكمّل التسجيل قبل اكتمال العدد.'; } } if(!$('clRoster') || $('clRoster').children.length) return clRefresh(); clAddRow('leader', myPsn||'', myPsn||''); if(mn>=2) clAddRow('vice','',''); for(var i=2;i'+ (lead?'👑 رئيس الكلان (أنت)':vice?'🎖️ نائب الرئيس':'🎮 لاعب '+(i-1))+''+ ''+ (lead?'':'')+ ''+ ''+ ''; box.appendChild(d); } function clRows(){ /* آيدي السوني هو الهوية والاسم معاً — خانة واحدة */ return [].slice.call(document.querySelectorAll('#clRoster .clrow')).map(function(d){ var f=d.querySelector('.clpsn'); /* ترقيم آيفون «الذكي» يصل هنا شرطةً طويلة — يُردّ لأصله قبل الفحص */ var psn=window.fixPsn(f.value); if(psn!==f.value) f.value=psn; return { role:d.getAttribute('data-role'), name:psn, psn:psn, el:d }; }); } function clRefresh(){ var rows=clRows(), need=window._clMin; var ok=rows.filter(function(r){ return r.psn.length>=3 && r.psn.length<=24; }); // تكرار PSN var seen={}, dup=false; ok.forEach(function(r){ var k=r.psn.toLowerCase(); seen[k]=(seen[k]||0)+1; if(seen[k]>1) dup=true; }); rows.forEach(function(r){ var el=r.el.querySelector('.clpsn'); var bad = r.psn && (r.psn.length<3 || r.psn.length>24 || seen[r.psn.toLowerCase()]>1); el.style.borderColor = bad ? 'rgba(255,107,94,.6)' : ''; var h=r.el.querySelector('.clname'); if(h) h.value=r.psn; }); var c=$('clCount'); if(c){ c.textContent='('+ok.length+'/'+need+')'; c.style.color=ok.length>=need?'#3ddc84':'var(--tx3)'; } var p=$('clProgress'); if(p){ var pct=Math.min(100,Math.round(ok.length*100/need)); var left=Math.max(0,need-ok.length); var bar = need>1 ? '
'+ '
' : ''; p.innerHTML=bar+ '
'+ (dup?'⚠️ فيه آيدي سوني مكرر — كل لاعب بآيدي مختلف' : ok.length>=need?(need>1?'✅ القائمة مكتملة — جاهز للترسيم':'✅ جاهز — سجّل كلانك') : (need===1?'اكتب اسمك وآيدي السوني حقك' : ('باقي '+left+' '+(left===1?'لاعب واحد باسمه وآيديه':'لاعبين بأسمائهم وآيدياتهم'))))+'
'; } var btn=$('btnCreateClan'); if(btn){ var good=ok.length>=need && !dup; btn.disabled=!good; btn.style.opacity=good?'':'.45'; btn.style.cursor=good?'':'not-allowed'; } } (function(){ var box=$('clRoster'); if(box){ box.addEventListener('input',clRefresh); box.addEventListener('click',function(e){ var b=e.target.closest('.clrm'); if(!b) return; var row=b.closest('.clrow'); if(row) row.remove(); clRefresh(); }); } var add=$('clAddRow'); if(add) add.addEventListener('click',function(){ clAddRow('member','',''); clRefresh(); }); })(); function clErrAr(m){ m=String(m||''); if(/TAG_TAKEN/.test(m)) return '⚠️ التاق مستخدم من كلان آخر — اختر غيره'; if(/TAG_INVALID/.test(m)) return '⚠️ التاق حروف إنجليزية/أرقام فقط — من 1 إلى 4'; if(/ALREADY_IN_CLAN/.test(m)) return '⚠️ أنت عضو في كلان — اخرج منه أولاً'; if(/ALREADY_OWNER/.test(m)) return '⚠️ عندك كلان مسجّل بالفعل'; if(/PSN_PROFILE_REQUIRED/.test(m)) return '🔑 وثّق آيدي السوني في حسابك أولاً — بدونه ما تقدر تسجّل كلاناً'; if(/ROSTER_SHORT:(\d+)/.test(m)) return '⚠️ لازم '+m.match(/ROSTER_SHORT:(\d+)/)[1]+' لاعبين على الأقل'; if(/NAME_MISSING:(\d+)/.test(m)) return '⚠️ اسم اللاعب رقم '+m.match(/NAME_MISSING:(\d+)/)[1]+' فاضي'; if(/PSN_INVALID:(\d+)/.test(m)) return '⚠️ آيدي السوني للاعب رقم '+m.match(/PSN_INVALID:(\d+)/)[1]+' غير صالح (3–24 حرفاً)'; if(/PSN_DUPLICATE:(.+)/.test(m)) return '⚠️ آيدي مكرر بالقائمة: '+m.match(/PSN_DUPLICATE:(.+)/)[1]; if(/PSN_IN_CLAN:(.+)/.test(m)) return '⚠️ الآيدي '+m.match(/PSN_IN_CLAN:(.+)/)[1]+' مسجّل في كلان آخر — لا يُضاف لكلانين'; if(/NAME_IN_CLAN:(.+)/.test(m)) return '⚠️ الآيدي '+m.match(/NAME_IN_CLAN:(.+)/)[1]+' مستخدم باسم لاعب في كلان آخر'; if(/MEMBER_TAKEN:(.+)/.test(m)) return '⚠️ الآيدي '+m.match(/MEMBER_TAKEN:(.+)/)[1]+' مأخوذ في كلان آخر — غيّره أو اطلب من صاحبه يخرج'; if(/HAS_ARCHIVED_CLAN/.test(m)) return '🔒 عندك كلان بالأرشيف — استرجعه أو احذفه قبل ما تسجّل غيره'; if(/CLAN_REG_CLOSED/.test(m)) return '🔒 تسجيل الكلانات مقفل حالياً من الإدارة'; return window.errAr?window.errAr(m):m; } if($('btnCreateClan')) $('btnCreateClan').addEventListener('click', function(){ var msg=$('clMsg'); window.authUser().then(function(res){ var user = res && res.data && res.data.user; if(!user){ msg.textContent='سجّل دخولك أولاً (تبويب حسابي)'; go('account'); return; } var tag=(($('clTag').value||'').trim()).toUpperCase(); if(!/^[A-Z0-9]{1,4}$/.test(tag)){ msg.style.color='var(--red)'; msg.textContent='⚠️ اكتب تاق الكلان — حروف إنجليزية/أرقام، من 1 إلى 4'; $('clTag').focus(); return; } var rows=clRows().filter(function(r){ return r.psn.length>=3; }); if(rows.length بـ '+(d.members||rows.length)+' لاعبين — معتمد رسمياً وجاهز للتحديات.'; /* الصلاحيات مخزّنة من قبل تسجيل الكلان — نُحدّثها فوراً كي يجد زر «طلب تحدي» فعّالاً بلا إعادة تحميل الصفحة */ if(window.refreshNow) window.refreshNow(); if(window.applyPerms) window.applyPerms(); setTimeout(function(){ if(window.applyPerms) window.applyPerms(); }, 1200); setTimeout(function(){ go('manageclan'); }, 1400); },function(){ $('btnCreateClan').disabled=false; msg.style.color='var(--red)'; msg.textContent='تعذّر التسجيل — حاول مرة ثانية'; }); }); }); // ===== التحديات (حقيقية + إشعارات حيّة) ===== var CH_STATUS={open:'مفتوح',pending:'بانتظار القبول',accepted:'مقبول',live:'جارٍ',completed:'مكتمل',cancelled:'ملغى',expired:'انتهت المدة',postponed:'مؤجَّل',postpone_requested:'طُلب تأجيله',resume_requested:'طُلب استئنافه'}; var CH_COLOR={open:'#38b6ff',pending:'#38b6ff',accepted:'#3ddc84',live:'#3ddc84',completed:'#7d8ea0',expired:'#7d8ea0',cancelled:'#ff6b5e',postponed:'#ffaa00',postpone_requested:'#ffaa00',resume_requested:'#38b6ff'}; // شارة الحالة: كلمة نظيفة بلون CSS — بدون إيموجي var CH_EN={open:'OPEN',pending:'PENDING',accepted:'LIVE',live:'LIVE',completed:'ENDED',expired:'EXPIRED',cancelled:'CANCELLED',postponed:'POSTPONED',postpone_requested:'PAUSED',resume_requested:'RESUMING'}; function chStatusChip(s){ var c=CH_COLOR[s]||'#7d8ea0', txt=CH_STATUS[s]||s, en=CH_EN[s]||''; var pulse=(s==='accepted'||s==='live')?'':''; return ''+pulse+txt+ (en?''+en+'':'')+''; } function typeBadge(type){ return type==='rush' ? 'RASH' : 'SNIP'; } /* العنوان اسم الكلانين فقط — شارة النمط مكانها سطر المعلومات تحت */ function chClan(id){ var c=window._clanMap&&window._clanMap[id]; if(!c) return 'أي كلان'; return 'CLAN '+esc(String(c.tag||''))+''; } /* شارة مصنّف / غير مصنّف */ function rankBadge(ranked){ var on = ranked!==false; return ''+(on?'🏅 مصنّف':'🤝 غير مصنّف')+''; } window.rankBadge=rankBadge; function chContact(cid){ var cc=window._clanContacts||{}, x=cc[cid]||{}, out=[]; /* الاسم في الكشف هو آيدي السوني غالباً — فلا نكرّره مرتين */ var line=function(ic,dflt,p){ var nm=String((p&&p.name)||'').trim(), ps=String((p&&p.psn)||'').trim(); if(ps && nm.toLowerCase()===ps.toLowerCase()) return ic+' '+esc(ps)+''; if(!ps) return ic+' '+esc(nm||dflt); return ic+' '+esc(nm||dflt)+' — '+esc(ps)+''; }; if(x.leader&&(x.leader.psn||x.leader.name)) out.push(line('👑','الرئيس',x.leader)); if(x.vice&&(x.vice.psn||x.vice.name)) out.push(line('🎖️','النائب',x.vice)); return out.length?out.join('
'):'لا رئيس/نائب مسجّل'; } /* عدّاد المهلة: ثوانٍ تحت الدقيقة، وبعدها د:ثث */ function fmtLeft(sec){ sec=Math.max(0,sec|0); if(sec<60) return sec+'ث'; return Math.floor(sec/60)+':'+('0'+(sec%60)).slice(-2)+' د'; } function renderCh(box,rows,kind){ if(!box) return; if(!rows.length){ box.innerHTML='
'+(kind==='mine'?'ما عندك تحدي قائم — أطلق واحد من فوق ⚡':'لا يوجد حالياً')+'
'; return; } var _h3690=rows.map(function(c){ var vs=kind==='open'?'يطلب':'VS'; var meta=chStatusChip(c.status)+' '+typeBadge(c.playstyle==='rush'?'rush':'sniper')+' '+rankBadge(c.ranked); var btns=''; /* الطلب المفتوح لا يُحسم من طرف واحد: تتقدّم، وصاحبه يختار خصمه */ if(kind==='open') btns=' '; if(kind==='directed') btns=' '; if(kind==='mine'){ /* التحدي القائم: الخروج منه «انسحاب» بعواقبه، لا مجرد إلغاء طلب */ var live=(c.status==='accepted'||c.status==='live'||c.status==='postponed'||c.status==='postpone_requested'||c.status==='resume_requested'); btns=''; /* البلاغُ يظهرُ حيثُ توجدُ غرفة — والقاعدةُ وحدَها تقرّرُ متى يُقبَل، فالزرُّ يسألُ قبلَ أن يرسل. */ if(live||c.status==='postponed') btns+=' '; } var note=''; var _hasRoom=(c.status==='accepted'||c.status==='live'||c.status==='postponed'||c.status==='postpone_requested'||c.status==='resume_requested'); if(_hasRoom && window._myIds){ var mineId=(window._myIds.indexOf(c.challenger_clan)!==-1)?c.challenger_clan:c.opponent_clan; var oppId2=(mineId===c.challenger_clan)?c.opponent_clan:c.challenger_clan; var mm=window._clanMap||{}; var tm=tDT(c.created_at); note=''; var roomLabel=c.status==='postponed'?'⏸️ الغرفة (مؤجّل)':'📂 افتح التحدي'; btns = ''+btns; } var left=''; if(c.status==='open'||c.status==='pending'){ var rem=Math.max(0, window.chTTL(c) - Math.floor((Date.now()-new Date(c.created_at).getTime())/1000)); left=' · ⏳ '+fmtLeft(rem)+''; } var bc=CH_COLOR[c.status]||'#7d8ea0'; /* العنوان نفسه مدخل التحدي — قابل يُضغط */ var openFn = _hasRoom ? ('window.openChChat(\''+c.id+'\')') : ('window.goChallenge(\''+c.id+'\')'); return '
'+ '
'+chClan(c.challenger_clan)+vs+chClan(c.opponent_clan)+ '
'+ '
'+meta+left+'
'+note+btns+'
'; }).join(''); if(window.setHTML) window.setHTML(box,_h3690); else box.innerHTML=_h3690; } /* ================= السجل العام للمباريات ================= */ window._hist={rows:null,limit:25,q:'',f:'all'}; window.loadHistory=function(force){ var box=$('hList'); if(!box) return; if(window._hist.rows && !force){ return window.renderHistory(); } box.innerHTML='
⏳ جاري تحميل السجل...
'; /* الجدول فيه score_a و score_b — لا عمود score */ /* من السجل الدائم — يبقى حتى لو انحذف الكلان أو التحدي */ Promise.all([ sb.from('match_log').select('*').order('verified_at',{ascending:false}).limit(400), sb.from('clans').select('id,tag,name,logo_url,status,points,wins,losses,recruiting') ]).then(function(rs){ if(rs[0].error){ box.innerHTML='
تعذّر تحميل السجل
'; return; } var cm={}; (rs[1].data||[]).forEach(function(c){ cm[c.id]=c; }); window._histClans=cm; var out=(rs[0].data||[]).map(function(m){ return window.matchNorm(m, cm); }); window._hist.rows=out; window._hist.limit=25; window.renderHistory(); },function(){ box.innerHTML='
تعذّر تحميل السجل
'; }); }; /* ═══ البحث يجد الصفحات لا المباريات فقط ═══ من يبحث عن كلان يريد صفحته، لا سطر مباراة واحدة خاضها. */ window._histPeople=null; function histPeople(){ if(window._histPeople) return Promise.resolve(window._histPeople); if(window._allPlayers && window._allPlayers.length){ window._histPeople=window._allPlayers; return Promise.resolve(window._histPeople); } return sb.from('profiles').select('id,username,psn_id,avatar_url,points,wins,losses,seeking_clan') .order('points',{ascending:false}).limit(300).then(function(r){ window._histPeople=(r.data||[]).filter(function(p){ return p.username; }); return window._histPeople; },function(){ window._histPeople=[]; return []; }); } window.renderHistEntities=function(){ var box=$('hEntities'); if(!box) return; var q=(window._hist.q||'').trim().toLowerCase(); var fld=window._hist.field||'all', f=window._hist.f||'all'; if(!q || fld==='date' || fld==='score'){ box.innerHTML=''; return; } var wantClans =(fld==='all'||fld==='clan') && (f==='all'||f==='team'); var wantPeople=(fld==='all'||fld==='player') && (f==='all'||f==='duel'); var cm=window._histClans||{}; var clans=!wantClans?[]:Object.keys(cm).map(function(k){return cm[k];}).filter(function(c){ return ((String(c.tag||'')+' '+String(c.name||'')).toLowerCase()).indexOf(q)!==-1; }).slice(0,6); var paint=function(people){ var cards=''; clans.forEach(function(c){ var tg=String(c.tag||'؟'); var lg=c.logo_url ? '' : ''+esc(tg.slice(0,4))+''; var t=(c.wins||0)+(c.losses||0); cards+='
'+lg+ '
'+ '
🛡️ CLAN '+esc(tg)+ (c.status==='ratified'?' ':'')+'
'+ '
'+(c.points||0)+' نقطة · '+(c.wins||0)+'ف '+(c.losses||0)+'خ'+(t?(' · '+t+' مباراة'):'')+ (c.recruiting!==false?' · 🚪 يستقبل':'')+'
'+ '
'; }); (people||[]).forEach(function(p){ var av=p.avatar_url ? '' : ''+esc(String(p.username||'?').slice(0,2).toUpperCase())+''; cards+='
'+av+ '
'+ '
👤 '+esc(p.username||'لاعب')+'
'+ '
🎮 '+esc(p.psn_id||'—')+' · '+(p.points||0)+' نقطة'+ (p.seeking_clan?' · يدور كلان':'')+'
'+ '
'; }); box.innerHTML = cards ? '
'+ '📂 صفحات مطابقة'+ '
'+cards+ '
'+ '🎮 مبارياتها في السجل'+ '
' : ''; }; if(!wantPeople) return paint([]); histPeople().then(function(ppl){ paint((ppl||[]).filter(function(p){ return ((String(p.username||'')+' '+String(p.psn_id||'')).toLowerCase()).indexOf(q)!==-1; }).slice(0,6)); }); }; /* ═══════════ بطاقة النتيجة ═══════════ بطاقةٌ واحدة يستعملها السجل العام والرئيسية، فلا يفترق شكلُهما أبداً. ورقمُ كل فريق يُرسم في جهته: الصفّ عربيّ، فأول عنصرٍ فيه إلى اليمين — والنتيجة كانت تُكتب بـltr داخله فينقلب رقمُ كلٍّ إلى جهة خصمه. */ window.matchCard=function(r){ var tm=tHM(r.t); var side=function(name,win,logo,id,kind){ var ini=String(name).replace(/^CLAN /,'').slice(0,2).toUpperCase(); var av=logo?'' :''+esc(ini)+''; var click=id?(kind==='team'?('openClan(\''+id+'\')'):('openPlayer(\''+id+'\')')):''; return '
'+av+ ''+esc(name)+'
'; }; var mm=String(r.score||'').match(/^\s*(\d+)\s*-\s*(\d+)\s*$/); var num=function(v,win){ return ''+v+''; }; var score = mm ? '
'+ num(mm[1],r.winA)+''+num(mm[2],r.winB)+'
' : '
'+esc(r.score||'—')+'
'; var badge = r.kind==='team' ? typeBadge(r.style==='rush'?'rush':'sniper') : '1v1'; return '
'+ '
'+ side(r.aName,r.winA,r.aLogo,r.aId,r.kind)+score+side(r.bName,r.winB,r.bLogo,r.bId,r.kind)+ '
'+ '
'+ badge+''+tm+''+ ''+ ''+ (r.winA?('🏆 '+esc(r.aName)):r.winB?('🏆 '+esc(r.bName)):'تعادل')+''+ '
'; }; /* ═══════════ بكرةُ مواجهاتِ الأفراد ═══════════ */ /* ═══════════ بطاقةُ لوحةِ الإعلان ═══════════ **دالّةٌ واحدةٌ تُحقَنُ في الرئيسةِ واللوحةِ معاً**: معاينةُ المشرفِ يجبُ أن تكونَ الشيفرةَ نفسَها لا تقريباً لها، وإلّا نشرَ ما لم يره. وستّةُ أنماطٍ يختارُ المشرفُ منها لكلِّ لوحة — لأنَّ نمطاً واحداً لا يخدمُ كلَّ إعلان: side — نصٌّ فوقَ يمينِ الصورة (الافتراضيّ) bottom — شريطُ نصٍّ أسفلَ الصورةِ والصورةُ تبقى ظاهرة center — نصٌّ في الوسطِ على حجابٍ كامل split — نصفانِ: صورةٌ ولوحٌ ملوَّن — **الصورةُ لا تُعتَّمُ إطلاقاً** plain — صورةٌ وحدَها بلا شيء strip — شريطٌ رفيعٌ: صورةٌ مربّعةٌ صغيرةٌ ونصٌّ بجانبِها */ window.PROMO_COLOR = { x:'#e9edf2', tiktok:'#25f4ee', discord:'#5865f2', youtube:'#ff0033', instagram:'#e1306c', snapchat:'#f5ee3f', whatsapp:'#25d366', telegram:'#2aabee', web:'#38b6ff', other:'#8fa3b5' }; window.PROMO_ICON = { x:'𝕏', tiktok:'🎵', discord:'💬', youtube:'▶', instagram:'📸', snapchat:'👻', whatsapp:'🟢', telegram:'✈', web:'🌐', other:'🔗' }; window.PROMO_STYLES = [ ['side','▮ نص على اليمين'], ['bottom','▁ شريط سفلي'], ['center','◉ نص في الوسط'], ['split','◧ نصفان (الصورة كاملة)'], ['plain','□ صورة فقط'], ['strip','▬ شريط رفيع'] ]; window.promoCardHTML = function(p, preview){ var pf = String(p.platform||'other'); var st = String(p.style||'side'); if (['side','bottom','center','split','plain','strip'].indexOf(st) < 0) st='side'; /* المنصّةُ تُورِثُ، والمحرِّرُ يَعزِل: ما مُلئَ يغلبُ الموروث */ var col = String(p.cta_color||'').trim() || window.PROMO_COLOR[pf] || window.PROMO_COLOR.other; /* شرطةٌ وحدَها = «بلا إيموجي» صراحةً؛ والفارغُ يرثُ من المنصّة. (خانةٌ فارغةٌ لا تكفي للتمييزِ بين «لم أختر» و«لا أريدُ شيئاً».) */ var _ci = String(p.cta_icon||'').trim(); var ico = _ci ? (/^[-–—_]+$/.test(_ci) ? '' : _ci) : (window.PROMO_ICON[pf] || window.PROMO_ICON.other); var side = (String(p.cta_side||'right')==='left') ? 'left' : 'right'; var img = String(p.image_url||''); var ttl = String(p.title||'').trim(); var sub = String(p.subtitle||'').trim(); var cta = String(p.cta||'').trim(); var hasTxt = !!(ttl || sub || cta); /* بلا نصٍّ لا تُرسَمُ طبقةٌ ولا حجاب — مهما كان النمط. فالتصميمُ المكتوبُ داخلَ الصورةِ لا يُغطَّى بغيرِه. */ if (!hasTxt) st = (st==='strip') ? 'strip' : 'plain'; /* ═══ الإعلانُ قد يكونُ مقطعاً ═══ مقطعٌ مصمَّمٌ يقولُ في خمسَ عشرةَ ثانيةً ما لا تقولُه لوحةٌ ساكنة. وهو **مكتومٌ ودائرٌ** فلا يفاجئُ أحداً بصوت، و`playsinline` شرطُ ألّا يفتحَ iOS مشغّلاً يملأُ الشاشةَ فوقَ الصفحة. والغلافُ إطارُه الأوّلُ باسمِه نفسِه — يُرى قبلَ نزولِ بايتٍ من الفيديو. */ var isVid = /\.(mp4|webm|mov|m4v)(\?|$)/i.test(img); var _mOK = !(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches); var pster = isVid ? img.replace(/\.[A-Za-z0-9]+(\?|$)/, '.jpg$1') : ''; var pic; if (isVid) { pic = ''; } else if (img) { /* لا كسلَ هنا: ارتفاعُ «صورة فقط» يأتي من الصورةِ نفسِها، والكسولُ لا يُحمَّلُ حتّى يقتربَ من الشاشةِ — وما ارتفاعُه صفرٌ لا يقترب. */ pic = ''+esc(ttl||'إعلان')+''; } else { pic = '
لا صورة
'; } var head = (ttl ? ''+esc(ttl)+'' : '') + (sub ? ''+esc(sub)+'' : ''); /* أيقونةٌ فارغةٌ لا تترك فراغاً — من محا الإيموجي أراد زرّاً بلا إيموجي */ var chip = cta ? '' + (ico ? ''+esc(ico)+'' : '') + esc(cta)+'' : ''; var txt = head + chip; /* ═══ «صورة فقط» = وسيطٌ كاملٌ وشريطٌ تحتَه ═══ الصورةُ المصمَّمةُ لا تُقَصُّ ولا يجلسُ زرٌّ فوقَها. ومن أرادَ الزرَّ فوقَ الصورةِ فأنماطُ النصِّ الأربعةُ باقيةٌ كما هي. */ if (!isVid && st === 'plain') st = 'free'; var body; if (isVid || st === 'free') { /* لا حجابَ فوقَ صورةٍ متحرّكة — النصُّ شريطٌ **تحتَها**. والنمطُ لا يُسأَلُ هنا: الأنماطُ الستّةُ كلُّها تعتيمٌ على الصورةِ إلّا الخاليَين، وتعتيمُ مقطعٍ يُذهِبُ سببَ رفعِه. */ if (isVid) st = 'vid'; body = pic + (hasTxt ? '
'+head+'
'+chip+'
' : ''); } else if (st === 'split') { /* الصورةُ في نصفٍ واللوحُ في نصف — لا حجابَ فوقَ الصورةِ أبداً */ body = '
'+pic+'
' + '
' + '
'+txt+'
'; } else if (st === 'strip') { body = '
'+pic+'
' + '
'+txt+'
'; } else { body = pic + '
'+txt+'
'; } var cls = 'pmo-c pmo--'+st; if (preview) return '
'+body+'
'; /* ═══ الداخليُّ لا يُفتَحُ في تبويبٍ جديد ═══ من ضغطَ إعلاناً يقودُه إلى المنصّةِ نفسِها يريدُ الانتقالَ لا نافذةً ثانيةً تتركُ خلفَها نسخةً معلَّقة. وفي التطبيقِ المثبَّتِ أسوأُ: يخرجُ من التطبيقِ إلى المتصفّح. و`rel="noopener"` واجبٌ مع `_blank`: بدونه تُمسِكُ الصفحةُ المفتوحةُ بـ`window.opener` وتستطيعُ تحويلَ صفحتِنا إلى غيرِها. */ var href = String(p.link_url||'#'); var out = /^https?:\/\//i.test(href) && href.indexOf(location.origin + '/') !== 0 && href !== location.origin; return ''+body+''; }; /* ═══════════ بكرةُ الإعلاناتِ على الرئيسة ═══════════ */ window._pmo = window._pmo || {t:null, i:0, W:0}; window.loadPromos = function(){ var box=$('promoHome'); if(!box) return; sb.rpc('promos_public').then(function(r){ var list = r.data || []; if(!list.length){ box.hidden=true; box.innerHTML=''; return; } box.hidden=false; var cards = list.map(function(p){ return window.promoCardHTML(p,false); }).join('') + (list.length>1 ? window.promoCardHTML(list[0],false) : ''); box.innerHTML = '
' + cards + '
' + (list.length>1 ? '
' + list.map(function(_,k){return '';}).join('') + '
' : ''); window.pmoStart(); }).catch(function(){ box.hidden=true; }); }; window.pmoStart = function(){ var M=window._pmo; if(M.t){ clearInterval(M.t); M.t=null; } var track=$('pmoTrack'), win=$('pmoWin'), dots=$('pmoDots'); if(!track||!win||!track.firstElementChild) return; var n = dots ? dots.children.length : 1; M.i=0; /* ═══ ما يُقاسُ قبلَ نزولِه يُقاسُ صفراً ═══ «صورة فقط» تتبعُ نسبةَ ما رُفِع، فارتفاعُها مجهولٌ حتّى تنزل. وقياسٌ واحدٌ عندَ البناءِ كان يُثبِّتُ بكسلَينِ ثمَّ يقصُّ `overflow:hidden` الصورةَ كلَّها. فيُعادُ القياسُ عندَ أوّلِ خبرٍ عن أبعادِ كلِّ وسيط. */ var _pmoWire=function(fn){ [].forEach.call(track.querySelectorAll('img.pmo-img'), function(im){ if(!(im.complete && im.naturalWidth)) im.addEventListener('load', fn, {once:true}); }); [].forEach.call(track.querySelectorAll('video.pmo-img'), function(vd){ if(!vd.videoWidth) vd.addEventListener('loadedmetadata', fn, {once:true}); }); }; if(n<2){ /* والحارسُ يمنعُ تثبيتَ ارتفاعٍ كاذبٍ لو نُودِيَت قبلَ الأوان */ var fit1=function(){ var c0=track.firstElementChild; if(!c0) return; var h0=Math.round(c0.getBoundingClientRect().height); if(h0>20) win.style.height=h0+'px'; }; fit1(); _pmoWire(fit1); return; } /* لوحةٌ واحدةٌ لا تدور */ /* في RTL يمتدُّ المسارُ يساراً فالتقدّمُ موجب — يُحسَبُ لا يُفترَض */ var sign = getComputedStyle(document.documentElement).direction==='rtl' ? 1 : -1; /* النافذةُ تأخذُ ارتفاعَ البطاقةِ الظاهرةِ وحدَها — وإلّا بقيَ فراغٌ تحتَ الشريطِ الرفيعِ حينَ يُعرَضُ بجانبِ لوحاتٍ بنسبةِ ٨:٣ */ var pmoFit=function(){ var c=track.children[M.i % (n+1)] || track.firstElementChild; if(c){ var h=Math.round(c.getBoundingClientRect().height); if(h>20) win.style.height=h+'px'; } }; var measure=function(){ M.W = win.getBoundingClientRect().width; if(M.W) track.style.transform='translateX('+(sign*M.i*M.W)+'px)'; pmoFit(); }; var paint=function(){ var real=M.i%n; if(dots) [].forEach.call(dots.children,function(d,k){ d.className=(k===real)?'on':''; }); pmoFit(); }; var step=function(){ if(!M.W) return; M.i++; track.classList.remove('pmo-jump'); track.style.transform='translateX('+(sign*M.i*M.W)+'px)'; if(M.i===n) setTimeout(function(){ track.classList.add('pmo-jump'); M.i=0; track.style.transform='translateX(0)'; },580); paint(); }; var motionOK = !(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches); measure(); paint(); _pmoWire(measure); if(motionOK && !document.hidden) M.t=setInterval(step,6000); if(!M.wired){ M.wired=true; addEventListener('resize',function(){ if($('pmoTrack')) measure(); }); document.addEventListener('visibilitychange',function(){ if(document.hidden){ if(M.t){clearInterval(M.t); M.t=null;} } else if($('pmoTrack')) window.pmoStart(); }); } }; /* عدُّ الضغط — `sendBeacon` لأنّ الصفحةَ تُغادَرُ في اللحظةِ نفسِها فيُقطَعُ `fetch` قبلَ أن يصل. والفتحُ لا يُؤخَّرُ انتظاراً للعدّ. */ document.addEventListener('click', function(e){ var a = e.target && e.target.closest ? e.target.closest('[data-promo]') : null; if(!a) return; var id = +a.getAttribute('data-promo'); if(!id) return; try{ var url = (window.CODAR_SUPABASE_URL||'') + '/rest/v1/rpc/promo_click'; var body = new Blob([JSON.stringify({p_id:id})], {type:'application/json'}); if(navigator.sendBeacon && window.CODAR_SUPABASE_ANON){ navigator.sendBeacon(url + '?apikey=' + encodeURIComponent(window.CODAR_SUPABASE_ANON), body); } else { sb.rpc('promo_click',{p_id:id}); } }catch(_){ } }, true); window._dzl = window._dzl || {t:null, i:0, W:0, paused:false}; window.dzlCard = function(r){ var na = String(r.pname_a||'لاعب'), nb = String(r.pname_b||'لاعب'); var ka = (r.score_a!=null?r.score_a:0), kb = (r.score_b!=null?r.score_b:0); var aw = (r.winner_player && r.winner_player===r.player_a); var bw = (r.winner_player && r.winner_player===r.player_b); var tot = (ka+kb) || 1; var pa = Math.round(ka*100/tot), pb = 100-pa; var np = r.proof_url ? String(r.proof_url).split(',').filter(function(x){return x.trim();}).length : 0; var gap = Math.abs(ka-kb); var av = function(u,n){ return u ? ''+esc(n)+'' : '' + esc(n.replace(/[^\p{L}\p{N}]/gu,'').slice(0,2).toUpperCase()||'؟')+''; }; var side = function(n,k,win,u,id,right){ var clk = id ? ' onclick="event.stopPropagation();openPlayer(\''+id+'\')"' : ''; return '' + av(u,n) + ''+esc(n) + (win?'🏆':'')+'' + ''+k+''; }; return '
' + '
' + side(na,ka,aw,r.av_a,r.player_a,false) + side(nb,kb,bw,r.av_b,r.player_b,true) + '
' /* الشريطُ يبدأُ صفراً ويُملأُ بعدَ الرسمِ لتُرى الحركة */ + '
' /* يُولَدُ بعرضِه الصحيح: الحركةُ تحسينٌ لا شرطُ ظهور */ + '' + '
' + '
1v1' + 'فارق '+gap+'' + (np?'📷 '+np+'':'') + ''+esc(window.tHM(r.verified_at)||'')+'
' + '
'; }; window.dzlRender = function(rows){ var box=$('homeDuels'); if(!box) return; rows = (rows||[]).slice(0,10); if(!rows.length){ box.innerHTML='
ما فيه مواجهات فردية موثّقة بعد 🆚
'; return; } var cards = rows.map(window.dzlCard).join('') + window.dzlCard(rows[0]); box.innerHTML = '
' + '
' + 'آخر المواجهات الفردية · 1v1' + '1 / '+rows.length+'
' + '
'+cards+'
' + '
' + rows.map(function(_,k){return '';}).join('') + 'كل المواجهات ←
' + '
'; window.dzlStart(); }; window.dzlStart = function(){ var D=window._dzl; if(D.t){ clearInterval(D.t); D.t=null; } var track=$('dzlTrack'), win=$('dzlWin'); if(!track||!win||!track.firstElementChild) return; var cnt=$('dzlCnt'), dots=$('dzlDots'); var n = dots ? dots.children.length : 1; D.i=0; D.paused=false; /* **الاتّجاهُ مقيسٌ لا مفترَض**: في RTL يمتدُّ المسارُ يساراً فالموجبُ يقدّم. ولو انعكسَ المستندُ يوماً صحَّ الحسابُ من نفسِه. */ var rtl = getComputedStyle(document.documentElement).direction === 'rtl'; var sign = rtl ? 1 : -1; var measure=function(){ D.W = win.getBoundingClientRect().width; if(!D.W) return; track.style.transform = 'translateX('+(sign*D.i*D.W)+'px)'; }; var motionOK = !(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches); var fill=function(){ var card = track.children[D.i % (n+1)]; if(!card) return; /* الحركةُ تحسينٌ لا شرط: التصفيرُ يُرى إن كانت الصفحةُ ترسم، وإلّا وُضِعَ العرضُ النهائيُّ رأساً. وفي الحالتَينِ ينتهي الشريطُ صحيحاً — ولا يتجمّدُ في منتصفِ انتقالٍ لا يتقدّم. */ var live = !document.hidden && motionOK; [].forEach.call(card.querySelectorAll('.dzl-bar i'),function(b){ var w = b.getAttribute('data-w')+'%'; if(live){ b.style.width='0'; void b.offsetWidth; } b.style.width = w; }); }; var paint=function(){ var real = D.i % n; if(cnt) cnt.textContent = (real+1)+' / '+n; if(dots) [].forEach.call(dots.children,function(d,k){ d.className=(k===real)?'on':''; }); fill(); }; var step=function(){ if(D.paused || !D.W) return; D.i++; track.classList.remove('dzl-jump'); track.style.transform='translateX('+(sign*D.i*D.W)+'px)'; if(D.i===n) setTimeout(function(){ track.classList.add('dzl-jump'); D.i=0; track.style.transform='translateX(0)'; },620); paint(); }; ['mouseenter','touchstart'].forEach(function(e){ win.addEventListener(e,function(){ D.paused=true; },{passive:true}); }); ['mouseleave','touchend'].forEach(function(e){ win.addEventListener(e,function(){ D.paused=false; },{passive:true}); }); measure(); paint(); /* أبطأُ من الكلاناتِ كي لا تتزامنَ البكرتان — ولا تبدأُ والصفحةُ مخفيّة */ if(motionOK && !document.hidden) D.t=setInterval(step,5200); [].forEach.call(track.querySelectorAll('img.dzl-av'),function(im){ if(!im.complete) im.addEventListener('load',measure,{once:true}); }); if(!D.wired){ D.wired=true; addEventListener('resize',function(){ if($('dzlTrack')) measure(); }); document.addEventListener('visibilitychange',function(){ if(document.hidden){ if(D.t){ clearInterval(D.t); D.t=null; } } else if($('dzlTrack')) window.dzlStart(); }); } }; /* ═══════════ بكرةُ نتائجِ الكلانات ═══════════ بطاقةٌ واحدةٌ تنزلقُ لأعلى عبرَ آخرِ عشرِ نتائج، برأسٍ وذيل. */ window._rzl = window._rzl || {t:null, i:0, H:0, paused:false}; window.rzlCard = function(r){ var tag = function(n){ return String(n||'؟').replace(/^CLAN\s+/i,''); }; var ta = tag(r.aName), tb = tag(r.bName); var mm = String(r.score||'').match(/^\s*(\d+)\s*-\s*(\d+)\s*$/); var sa = mm?mm[1]:'—', sb = mm?mm[2]:'—'; var aw = !!r.winA, bw = !!r.winB; var np = r.proof ? String(r.proof).split(',').filter(function(x){return x.trim();}).length : 0; var x2 = Number(r.xp_mult||0) > 1; var side = function(t, sc, win, logo, id){ var av = logo ? ''
            +esc(t)+'' : ''+esc(t.slice(0,2).toUpperCase())+''; var clk = id ? ' onclick="event.stopPropagation();openClan(\''+id+'\')"' : ''; return '' + ''+av+'' + ''+(win?'🏆':'') + ''+esc(t)+'' + ''+sc+'' + (r.dw!=null&&r.dl!=null ? '' + (win?'+'+r.dw:r.dl) + (win&&x2?'⚡2X':'') + '' : '·') + ''; }; return '
' + '' + '
' + side(ta, sa, aw, r.aLogo, r.aId) + '' + 'VS' + side(tb, sb, bw, r.bLogo, r.bId) + '
' + '
' + ''+(r.style==='rush'?'⚡ RUSH':'🎯 SNIPER')+' · BO1' + (np?'📷 '+np+'':'') + ''+esc(window.tHM(r.t)||'')+'' + '
'; }; window.rzlRender = function(rows, cm){ var box=$('homeChallenges'); if(!box) return; rows = (rows||[]).slice(0,10); if(!rows.length){ box.innerHTML='
ما فيه نتائج موثّقة بعد 📭
'; return; } var list = rows.map(function(m){ return window.matchNorm(m, cm||{}); }); /* نسخةٌ زائدةٌ من الأولى تُغلِقُ الدورةَ بلا قفزةٍ مرئيّةٍ للخلف */ var cards = list.map(window.rzlCard).join('') + window.rzlCard(list[0]); box.innerHTML = '
' + '
' + 'آخر نتائج الكلانات · مصنّفة' + (list.some(function(z){return Number(z.xp_mult||0)>1;}) ? ' ⚡ دبل النقاط' : '') + '' + '1 / '+list.length+'
' + '
' + '' + '
'+cards+'
' + '
' + '
' + list.map(function(_,k){return '';}).join('') + 'كل النتائج ←
' + '
'; window.rzlStart(); }; /* تُستدعى بعدَ كلِّ إدراجٍ للشيفرة — من البناءِ أو من ذاكرةِ الجهاز */ window.rzlStart = function(){ var R=window._rzl; if(R.t){ clearInterval(R.t); R.t=null; } var reel=$('rzlReel'), track=$('rzlTrack'), win=$('rzlWin'); if(!reel||!track||!win||!track.firstElementChild) return; var cnt=$('rzlCnt'), dots=$('rzlDots'), bar=$('rzlBar'); var n = dots ? dots.children.length : 1; R.i=0; R.paused=false; var measure=function(){ R.H = track.firstElementChild.getBoundingClientRect().height; if(!R.H) return; win.style.height = R.H+'px'; track.style.transform = 'translateY(-'+(R.i*R.H)+'px)'; }; var paint=function(){ var real = R.i % n; if(cnt) cnt.textContent = (real+1)+' / '+n; if(dots) [].forEach.call(dots.children,function(d,k){ d.className = (k===real)?'on':''; }); if(bar){ bar.classList.remove('run'); void bar.offsetWidth; bar.classList.add('run'); } }; var step=function(){ if(R.paused || !R.H) return; R.i++; track.classList.remove('rzl-jump'); track.style.transform = 'translateY(-'+(R.i*R.H)+'px)'; if(R.i===n) setTimeout(function(){ track.classList.add('rzl-jump'); R.i=0; track.style.transform='translateY(0)'; },640); paint(); }; var motionOK = !(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches); /* الحارسُ يُمسِكُ التبدّلَ لا الحالةَ الأولى — فتُفحَصُ هنا صراحةً */ var run=function(){ if(R.t) clearInterval(R.t); if(motionOK && !document.hidden) R.t=setInterval(step,4600); }; ['mouseenter','touchstart'].forEach(function(e){ win.addEventListener(e,function(){ R.paused=true; if(bar) bar.style.animationPlayState='paused'; },{passive:true}); }); ['mouseleave','touchend'].forEach(function(e){ win.addEventListener(e,function(){ R.paused=false; if(bar) bar.style.animationPlayState='running'; },{passive:true}); }); measure(); paint(); run(); /* الصورةُ المتأخّرةُ تُغيّرُ الارتفاعَ فيختلُّ الانزلاقُ — نُعيدُ القياس */ [].forEach.call(track.querySelectorAll('img.rzl-lg'),function(im){ if(!im.complete) im.addEventListener('load',measure,{once:true}); }); if(!R.wired){ R.wired=true; addEventListener('resize',function(){ if($('rzlTrack')) measure(); }); /* المخفيّةُ لا تُنتِجُ إطاراتٍ فلا ينزلقُ شيءٌ بينما يعملُ المؤقّت */ document.addEventListener('visibilitychange',function(){ if(document.hidden){ if(R.t){ clearInterval(R.t); R.t=null; } } else if($('rzlTrack')) window.rzlStart(); }); } }; /* تطبيع صفّ من السجل الدائم إلى ما تفهمه البطاقة */ window.matchNorm=function(m, cm){ cm=cm||{}; var team=m.kind!=='duel'; return { kind: team?'team':'duel', t: m.verified_at, played: m.played_at, score: (m.score_a!=null&&m.score_b!=null)?(m.score_a+' - '+m.score_b):'', ranked: m.ranked!==false, aName: team?('CLAN '+String(m.tag_a||'؟')):(m.pname_a||'لاعب'), bName: team?('CLAN '+String(m.tag_b||'؟')):(m.pname_b||'لاعب'), aId: team?m.clan_a:m.player_a, bId: team?m.clan_b:m.player_b, aLogo:(cm[m.clan_a]||{}).logo_url, bLogo:(cm[m.clan_b]||{}).logo_url, winA: team?(m.winner_clan===m.clan_a):(m.winner_player===m.player_a), winB: team?(m.winner_clan===m.clan_b):(m.winner_player===m.player_b), style: m.playstyle, dw:m.delta_win, dl:m.delta_loss, proof:m.proof_url, xp_mult: m.xp_mult }; }; window.renderHistory=function(){ var box=$('hList'); if(!box) return; if(window.renderHistEntities) window.renderHistEntities(); var all=window._hist.rows||[], q=(window._hist.q||'').trim().toLowerCase(), f=window._hist.f; var fld=window._hist.field||'all'; var list=all.filter(function(r){ if(f!=='all' && r.kind!==f) return false; if(!q) return true; var names=((r.aName||'')+' '+(r.bName||'')).toLowerCase(); if(fld==='clan') return r.kind==='team' && names.indexOf(q)!==-1; if(fld==='player') return r.kind==='duel' && names.indexOf(q)!==-1; if(fld==='date') return tDay(r.played||r.t).indexOf(q)!==-1; if(fld==='score') return String(r.score||'').replace(/\s/g,'').indexOf(q.replace(/\s/g,''))!==-1; return (names+' '+String(r.score||'')+' '+tDay(r.played||r.t)).indexOf(q)!==-1; }); // إحصاءات var st=$('hStats'); if(st){ var nt=all.filter(function(r){return r.kind==='team';}).length, nd=all.length-nt; var cell=function(v,l,c){ return '
'+v+'
'+l+'
'; }; st.innerHTML=cell(all.length,'مباراة موثّقة','var(--gold)')+cell(nt,'كلانات','var(--blue2)')+cell(nd,'أفراد','#3ddc84'); } if(!list.length){ box.innerHTML='
'+ (q ? (($('hEntities')&&$('hEntities').innerHTML) ? 'ما خاض مباريات موثّقة بعد 📭
لكن صفحته فوق ↑' : ('ما لقيت نتيجة لـ «'+esc(window._hist.q)+'» 🔎
جرّب تاق الكلان أو اسم اللاعب')) : (all.length ? 'ما فيه مباريات بهذا الفلتر 📭' : 'ما فيه مباريات موثّقة بعد 📭
أول نتيجة توثَّق بتظهر هنا فوراً'))+'
'; if($('hMore')) $('hMore').hidden=true; return; } var shown=list.slice(0, window._hist.limit); // تجميع حسب اليوم var html='', lastDay=''; var AR=['الأحد','الإثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت']; shown.forEach(function(r){ var d=new Date(r.t||Date.now()); var day=tDay(r.t); if(day!==lastDay){ lastDay=day; var lbl = day===tDay(new Date()) ? 'اليوم' : day===tDay(new Date(Date.now()-86400000)) ? 'أمس' : AR[tWD(day)]+' · '+day.split('-').reverse().join('/'); html+='
'+lbl+'
'; } html+=window.matchCard(r); }); box.innerHTML=html; if($('hMore')) $('hMore').hidden = (shown.length>=list.length); }; (function(){ var s=$('hSearch'); if(s) s.addEventListener('input',function(){ window._hist.q=s.value; window._hist.limit=25; window.renderHistory(); }); var fd=$('hField'); if(fd) fd.addEventListener('change',function(){ window._hist.field=fd.value; window._hist.limit=25; var PH={all:'🔎 اكتب ما تبحث عنه...',clan:'🔎 مثال: PWRZ',player:'🔎 اسم اللاعب', date:'🔎 مثال: 2026-08-01',score:'🔎 مثال: 4 - 2'}; if(s) s.placeholder=PH[fd.value]||PH.all; window.renderHistory(); }); var f=$('hFilter'); if(f) f.addEventListener('click',function(e){ var b=e.target.closest('button'); if(!b) return; [].forEach.call(f.querySelectorAll('button'),function(x){x.classList.remove('on');}); b.classList.add('on'); window._hist.f=b.getAttribute('data-v'); window._hist.limit=25; window.renderHistory(); }); var m=$('hMore'); if(m) m.addEventListener('click',function(){ window._hist.limit+=25; window.renderHistory(); }); })(); function loadChLists(user){ sb.from('clans').select('id,tag,type,created_by').then(function(cr){ var clans=cr.data||[]; var map={}; clans.forEach(function(c){map[c.id]=c;}); window._clanMap=map; sb.from('clan_members').select('clan_id,role,member_name,psn,profiles(username,psn_id)').in('role',['leader','vice']).then(function(mr){ var cc={}; (mr.data||[]).forEach(function(m){ var ps=(m.psn||(m.profiles&&m.profiles.psn_id)||'').trim(); cc[m.clan_id]=cc[m.clan_id]||{}; cc[m.clan_id][m.role]={name:(ps||m.member_name||(m.profiles&&m.profiles.username)||''), psn:ps}; }); window._clanContacts=cc; if(!window._contactsReadyOnce){ window._contactsReadyOnce=true; loadChLists(user); } },function(){}); var myIds=user?clans.filter(function(c){return c.created_by===user.id;}).map(function(c){return c.id;}):[]; /* كلان النائب/الرئيس بالعضوية يُحسب «كلاني» أيضاً — فيرى تحدياته وخاناته ويوثّق نتائجه، لا أن يظهر له الكشف فارغاً */ if(window._myMemberClanId && (window._myMemberRole==='leader'||window._myMemberRole==='vice'||window._myMemberRole==='coordinator') && myIds.indexOf(window._myMemberClanId)===-1) myIds.push(window._myMemberClanId); window._myIds=myIds; if($('rsChallenge')){ if(myIds.length){ sb.from('challenges').select('id,challenger_clan,opponent_clan').in('status',['accepted','live']).then(function(r){ var rows=(r.data||[]).filter(function(c){return myIds.indexOf(c.challenger_clan)!==-1||myIds.indexOf(c.opponent_clan)!==-1;}); window._myChallenges={}; var opts=''; rows.forEach(function(c){ window._myChallenges[c.id]=c; var a=(map[c.challenger_clan]||{}).tag||'؟', b=(map[c.opponent_clan]||{}).tag||'؟'; opts+=''; }); $('rsChallenge').innerHTML=opts; /* المربع ما يظهر إلا لمن عنده تحدٍّ مقبول فعلاً */ if($('rsCard')) $('rsCard').hidden = !rows.length; }); } else { $('rsChallenge').innerHTML=''; if($('rsCard')) $('rsCard').hidden=true; } } if($('rpChallenge') && myIds.length){ sb.from('challenges').select('id,challenger_clan,opponent_clan').or('challenger_clan.in.('+myIds.join(',')+'),opponent_clan.in.('+myIds.join(',')+')').order('created_at',{ascending:false}).limit(20).then(function(r){ var opts=''; window._allChMap={}; (r.data||[]).forEach(function(c){ window._allChMap[c.id]=c; var a=(map[c.challenger_clan]||{}).tag||'؟', b=(map[c.opponent_clan]||{}).tag||'أي كلان'; opts+=''; }); if($('rpChallenge')) $('rpChallenge').innerHTML=opts; },function(){}); } sb.from('challenges').select('*').is('opponent_clan',null).eq('status','open').order('created_at',{ascending:false}).limit(40).then(function(r){ renderCh($('chOpen'),(r.data||[]).filter(function(c){return myIds.indexOf(c.challenger_clan)===-1;}),'open'); }); if(myIds.length){ sb.from('challenges').select('*').in('opponent_clan',myIds).eq('status','pending').order('created_at',{ascending:false}).then(function(r){ renderCh($('chDirected'),r.data||[],'directed'); }); var f=window._chFilter||'active'; var sts = f==='active'?['open','pending','accepted','live','postponed','postpone_requested','resume_requested'] : f==='done'?['completed','expired'] : f==='cancelled'?['cancelled'] : ['open','pending','accepted','live','postponed','postpone_requested','resume_requested','completed','expired','cancelled']; sb.from('challenges').select('*').or('challenger_clan.in.('+myIds.join(',')+'),opponent_clan.in.('+myIds.join(',')+')').in('status',sts).order('created_at',{ascending:false}).limit(40).then(function(r){ /* الطلب الموجَّه لكلانك مكانه «📥 طلبات موجّهة لك» حتى تقبله — بعد القبول ينتقل إلى «تحدياتي». لا يظهر بالقائمتين معاً. */ var rows=(r.data||[]).filter(function(c){ var iAmOpp = myIds.indexOf(c.opponent_clan)!==-1; var iAmChl = myIds.indexOf(c.challenger_clan)!==-1; return !(c.status==='pending' && iAmOpp && !iAmChl); }); renderCh($('chMine'),rows,'mine'); }); } else { renderCh($('chDirected'),[],'directed'); renderCh($('chMine'),[],'mine'); } if(window.loadBids) window.loadBids(myIds); if(window.renderSlots) window.renderSlots(myIds[0]); /* الشارات تتجدّد مع كل إنعاش — فيظهر الجديد بلا فتح القسم */ if(window.chCountLive) window.chCountLive(); if(window.chCountOpen) window.chCountOpen(); /* لوحة الجارية معروضة الآن؟ نُحدّثها كي تختفي المنتهية وتظهر الجديدة */ var lv=document.getElementById('secLive'); if(lv && !lv.classList.contains('fhide') && window.loadLiveChallenges) window.loadLiveChallenges(); }); } window.submitReport=function(){ var msg=$('rpMsg'); if(!msg) return; if(_blocked()){ msg.style.color='var(--red)'; msg.textContent='🚫 حسابك محظور'; return; } window.authUser().then(function(res){ var u=res&&res.data&&res.data.user; if(!u){ msg.style.color='var(--red)'; msg.textContent='سجّل دخولك'; go('account'); return; } var chId=$('rpChallenge')?$('rpChallenge').value:''; if(!chId){ msg.style.color='var(--gold)'; msg.textContent='اختر التحدي'; return; } var reason=($('rpReason')&&$('rpReason').value)||'أخرى'; var details=(($('rpDetails')&&$('rpDetails').value)||'').trim(); /* ═══ البلاغُ يصلُ كاملاً أو لا يصل ═══ كان يُرسَلُ بلا تفاصيلَ وبلا اسمِ المشكوِّ منه، فتصلُ المشرفَ بطاقةٌ فيها سببٌ واحدٌ لا يُحكَمُ به: «حرق» — من؟ في أيِّ راوند؟ فيسألُ ويُطيلُ، أو يردُّ البلاغَ ظلماً. فالتفاصيلُ شرطٌ الآن، والمشكوُّ منه يُستخرَجُ من التحدي نفسِه لا من كتابةِ المُبلِّغ. */ if(details.length<10){ msg.style.color='var(--gold)'; msg.textContent='اكتب تفاصيل المخالفة — وضّح وش صار وفي أي راوند'; if($('rpDetails')) $('rpDetails').focus(); return; } var accused=''; try{ var _c=(window._allChMap||{})[chId] || ((window._cc&&window._cc.id===chId)?(window._cc.ch||{}):{}) || {}; var _mm=window._clanMap||{}, _mine=window._myIds||[]; var _other=(_mine.indexOf(_c.challenger_clan)!==-1)?_c.opponent_clan:_c.challenger_clan; var _tag=String((_mm[_other]||{}).tag||'').toUpperCase(); if(_tag) accused='CLAN '+_tag; }catch(e){} var evidence=(window._rp||[]).join(','); msg.style.color='var(--gold)'; msg.textContent='...إرسال'; sb.from('reports').insert({reporter_id:u.id, challenge_id:chId, accused:accused||null, reason:reason, details:details, evidence_url:evidence, status:'open'}).then(function(r){ if(r.error){ msg.style.color='var(--red)'; msg.textContent=errAr(r.error.message); return; } msg.style.color='#3ddc84'; msg.textContent='✅ أُرسل البلاغ — طاقم المشرفين بيرد عليك بأسرع وقت 🛡️'; if($('rpDetails'))$('rpDetails').value=''; window._rp=[]; window.paintRpList(); if($('rpUpMsg'))$('rpUpMsg').textContent=''; }); }); }; /* ═══ أدلة البلاغ ═══ البلاغ الواحد يحمل حتى ٨ أدلة: صور ومقاطع معاً — لأن المخالفة الواحدة قد تحتاج لقطة تُظهر السلاح ومقطعاً يُظهر إطالة الزوم. */ window._rp=[]; window.paintRpList=function(){ var l=$('rpList'), b=$('rpFileBtn'); if(b) b.textContent='📎 أرفق أدلة — صور ومقاطع ('+window._rp.length+'/8)'; if(!l) return; l.innerHTML=window._rp.map(function(u,i){ var v=window.isVid(u); return ''+ ''+ (v ? ''+ '' : '')+ ''+ ''; }).join(''); }; window.uploadReportProof=function(){ var fi=$('rpFile'), msg=$('rpUpMsg'); if(!fi||!fi.files||!fi.files.length||!msg) return; /* ═══ بالتوازي لا واحداً بعدَ واحد ═══ كانت الأدلّةُ ترتفعُ بالتتابع: ثمانيةُ ملفّاتٍ = ثمانيةُ أضعافِ الانتظار، ووصلةُ الجوّالِ عاطلةٌ بينَ الواحدِ والذي يليه. ثلاثةٌ معاً تملأُ الوصلةَ ولا تُغرِقُ خادماً بنواتَين. */ var files=[].slice.call(fi.files).slice(0, Math.max(0, 8-window._rp.length)); var over=fi.files.length-files.length; var rpc=$('rpChallenge')?$('rpChallenge').value:''; var okN=0, failed=[], total=files.length, prog={}, LANES=3; if(over>0) failed.push('الحد ٨ أدلة — تُرك '+over); if(!total){ fi.value=''; msg.style.color='var(--gold)'; msg.textContent='⚠️ '+failed.join(' · '); return; } var paint=function(){ var pc=0; for(var k in prog) pc+=prog[k]; msg.style.color='var(--tx3)'; msg.textContent='⏳ نرفع '+total+' — '+Math.round(pc/total*100)+'%'; }; var finish=function(){ fi.value=''; window.paintRpList(); if(failed.length){ msg.style.color='var(--gold)'; msg.textContent='⚠️ '+failed.join(' · ')+(okN?(' — أُضيف '+okN):''); } else { msg.style.color='#3ddc84'; msg.textContent='✅ أُضيفت الأدلة ('+window._rp.length+'/8)'; } }; var next=0, live=0, doneN=0; var pump=function(){ while(live(window.UP_MAX_MB||100)*1024*1024){ failed.push((f.name||'ملف')+': أكبر من 15MB'); prog[i]=1; live--; doneN++; if(doneN===total) finish(); else pump(); return; } window.upProof(f, rpc||'report', function(p){ prog[i]=p; paint(); }) .then(function(d){ if(d&&d.ok){ window._rp.push(d.url); okN++; window.paintRpList(); } else failed.push((f.name||'ملف')+': '+((d&&d.why)||'تعذّر')); },function(){ failed.push((f.name||'ملف')+': تعذّر الرفع'); }) .then(function(){ prog[i]=1; live--; doneN++; if(doneN===total) finish(); else pump(); }); })(files[next], next); next++; } paint(); }; pump(); }; document.addEventListener('click',function(e){ var t=e.target&&e.target.closest?e.target.closest('[data-rpdel]'):null; if(!t) return; e.preventDefault(); e.stopPropagation(); window._rp.splice(parseInt(t.getAttribute('data-rpdel'),10),1); window.paintRpList(); }); window.goChallenges=function(f){ window._chFilter='active'; /* «جارية الآن» من الرئيسية/التنقّل تفتح لوحة الجارية العامّة مباشرةً */ window._chOpenTab=(f==='active')?'live':(f==='open'?'open':null); go('challenges'); }; /* ================= بطاقة التحدي — تُفتح من أي مكان ================= */ /* ═══ البطاقةُ تُفتَحُ فوقَ شاشتِها لا فوقَ أيِّ شاشة ═══ كانت تُفتَحُ حيثُ أنت، فمن ضغطَ إشعارَ «تحدٍّ مفتوح» وهو في الرئيسيّة رآها معلَّقةً فوقَها — وإذا أغلقَها رجعَ إلى الرئيسيّةِ لا إلى قائمةِ التحدّيات، فبدا وكأنَّ شيئاً ظهرَ من تلقاءِ نفسِه. الآن ننتقلُ أوّلاً ثمَّ نفتح، فيجدُ خلفَها ما يتوقّعُه ويجدُه أمامَه حينَ يُغلِق. */ window._goChalOn=function(id){ if(!id) return; var cs=document.getElementById('challenges'); if(cs && cs.classList.contains('active')){ return window.goChallenge(id); } try{ go('challenges'); }catch(e){} setTimeout(function(){ window.goChallenge(id); }, 140); }; window.closeChSheet=function(){ var s=$('chSheet'), b=$('chBack'); if(s) s.hidden=true; if(b) b.hidden=true; if(window._chsTimer){clearInterval(window._chsTimer); window._chsTimer=null;} }; var CH_TXT={open:'مفتوح — من يقبل؟',pending:'موجّه لكلان',accepted:'مقبول — جهّزوا',live:'جارٍ الآن',completed:'منتهٍ',expired:'انتهت مدته',cancelled:'ملغى'}; /* يفتح تحدياً بعينه ببطاقة مستقلة فيها كل التفاصيل وأزرار التنفيذ */ window.goChallenge=function(id){ if(!id) return go('challenges'); var sh=$('chSheet'), bk=$('chBack'), body=$('chsBody'); if(!sh||!body) return go('challenges'); bk.hidden=false; sh.hidden=false; $('chsTitle').textContent='تحدٍّ'; $('chsSub').textContent='...جاري التحميل'; body.innerHTML='
⏳ جاري تحميل التحدي...
'; window._chsId=id; sb.from('challenges').select('*').eq('id',id).single().then(function(r){ if(!r.data){ body.innerHTML='
ما لقيت هذا التحدي — يمكن انحذف.
'; $('chsSub').textContent=''; return; } var c=r.data; var ids=[c.challenger_clan, c.opponent_clan].filter(Boolean); sb.from('clans').select('id,tag,name,type,logo_url,president_id').in('id',ids).then(function(cr){ var map={}; (cr.data||[]).forEach(function(x){ map[x.id]=x; }); window._clanMap=window._clanMap||{}; (cr.data||[]).forEach(function(x){ window._clanMap[x.id]=x; }); window.authUser().then(function(ur){ var user=ur&&ur.data&&ur.data.user; if(!user) return renderChSheet(c,map,[],null); sb.from('clans').select('id').eq('created_by',user.id).neq('status','archived').then(function(mr){ var owned=(mr.data||[]).map(function(x){return x.id;}); sb.from('clan_members').select('clan_id').eq('profile_id',user.id).then(function(mm){ var mine=owned.concat((mm.data||[]).map(function(x){return x.clan_id;})); renderChSheet(c,map,mine,owned[0]||null); },function(){ renderChSheet(c,map,owned,owned[0]||null); }); },function(){ renderChSheet(c,map,[],null); }); }); }); },function(){ body.innerHTML='
تعذّر تحميل التحدي
'; }); }; function chsClanCard(cl,label,color){ if(!cl) return '
'+ '
🎯
أي كلان'+ ''+label+'
'; var lg=cl.logo_url?'':'
'+esc(String(cl.tag||'؟'))+'
'; return '
'+ '
'+lg+'
'+ 'CLAN '+esc(String(cl.tag||''))+''+ ''+label+' · اضغط للصفحة ›
'; } function renderChSheet(c,map,mineIds,ownedClan){ var body=$('chsBody'); if(!body) return; var A=map[c.challenger_clan], B=map[c.opponent_clan]; $('chsTitle').innerHTML='CLAN '+esc(String((A||{}).tag||'؟'))+' ضد '+(B?'CLAN '+esc(String(B.tag||'')):'أي كلان'); $('chsSub').textContent=(CH_TXT[c.status]||c.status)+' · '+tDT(c.created_at); var iAmChallenger = mineIds.indexOf(c.challenger_clan)!==-1; var iAmOpponent = c.opponent_clan && mineIds.indexOf(c.opponent_clan)!==-1; var isOpen = !c.opponent_clan && c.status==='open'; var h=''; h+='
'+ chsClanCard(A,'صاحب الطلب','#38b6ff')+ '
'+(isOpen?'يطلب':'VS')+'
'+ chsClanCard(B,'الخصم','#ffcc00')+'
'; h+='
'+ chStatusChip(c.status)+' '+typeBadge(c.playstyle==='rush'?'rush':'sniper')+' '+rankBadge(c.ranked)+ '
'; if(c.ranked===false) h+='
🤝 تحدٍّ غير مصنّف — النتيجة تنحسب بالسجل بلا أي تأثير على النقاط أو التصنيف.
'; if(c.status==='accepted'||c.status==='live'){ var oppId = iAmChallenger ? c.opponent_clan : c.challenger_clan; var myId = iAmChallenger ? c.challenger_clan : c.opponent_clan; h+='
'; if(iAmChallenger||iAmOpponent){ h+='
🛡️ كلانك — CLAN '+esc(String((map[myId]||{}).tag||''))+'
'+chContact(myId)+ '
🎯 الخصم — CLAN '+esc(String((map[oppId]||{}).tag||''))+'
'+chContact(oppId)+ '
🎮 أضيفوا بعض على السوني والعبوا — بعد المباراة ارفعوا النتيجة (حتى 8 صور)
'; } else { h+='
المباراة محجوزة بين الكلانين — بيانات التواصل تظهر لأعضاء الكلانين فقط.
'; } h+='
'; } var btns=[]; if(c.status==='open' && !c.opponent_clan){ /* ═══ الطلبُ المفتوحُ لا يُحسم من طرفٍ واحد ═══ كان هنا زرُّ «قبول التحدي ⚡» يمضي إلى core_accept_challenge فيصيرُ التحدي واقعاً بضغطةٍ واحدة، وصاحبُ الطلبِ يجدُ نفسَه في تحدٍّ لم يخترْ خصمَه فيه. البطاقةُ في القائمة كانت تُحسنُ الأدبَ وتَعرِضُ التقدّم، والورقةُ تخالفُها — فصارتا واحدة. */ if(iAmChallenger && ['open','pending'].indexOf(c.status)>=0) btns.push(''); else if(ownedClan){ h+='
🙋 هذا طلبٌ مفتوح — تتقدّم له، ويصلُ صاحبَ الطلبِ إشعارٌ يختارُ به خصمَه. لا يبدأ التحدي إلا بموافقته.
'; btns.push(''); } else btns.push(''); } else if(c.status==='pending'){ if(iAmOpponent){ btns.push(''); btns.push(''); } else if(iAmChallenger) btns.push(''); } else if(c.status==='accepted'||c.status==='live'){ if(iAmChallenger||iAmOpponent){ btns.push(''); btns.push(''); btns.push(''); } } if(!btns.length) btns.push(''); h+='
'+btns.join('')+'
'; body.innerHTML=h; // العدّاد الحي لطلبات المهلة if(window._chsTimer){clearInterval(window._chsTimer); window._chsTimer=null;} if(c.status==='open'||c.status==='pending'){ var ttl=window.chTTL(c); var tick=function(){ var el=$('chsLeft'); if(!el){ clearInterval(window._chsTimer); return; } var rem=Math.max(0, ttl - Math.floor((Date.now()-new Date(c.created_at).getTime())/1000)); el.innerHTML='· ⏳ '+fmtLeft(rem)+''; if(rem<=0){ clearInterval(window._chsTimer); window._chsTimer=null; el.innerHTML='· انتهت المهلة'; } }; tick(); window._chsTimer=setInterval(tick,1000); } } window.chsAct=function(kind){ var id=window._chsId; if(!id) return; if(kind==='bid'){ closeChSheet(); window.bidChallenge(id); return; } if(kind==='acc'){ acceptCh(id); setTimeout(function(){ if(!$('chSheet').hidden) window.goChallenge(id); },1200); return; } if(kind==='rej'){ updateCh(id,{status:'cancelled'},'رفضت التحدي'); closeChSheet(); return; } if(kind==='wd'){ closeChSheet(); go('challenges'); setTimeout(function(){ var b=document.querySelector('[data-wd="'+id+'"]'); if(b) b.click(); else toast('افتح «تحدياتي» للسحب'); },900); } }; window.focusChallenge=function(id,tries){ tries=tries||0; setTimeout(function(){ var el=document.getElementById('ch-'+id); if(!el){ if(tries<8) return window.focusChallenge(id,tries+1); // مو ضمن الفلتر الحالي — نجرّب «الكل» if(window._chFilter!=='all'){ window._chFilter='all'; var fb=document.querySelector('#chFilter button[data-s="all"]'); if(fb){ document.querySelectorAll('#chFilter button').forEach(function(x){x.classList.remove('on')}); fb.classList.add('on'); } if(window.loadChallengesScreen) window.loadChallengesScreen(); return window.focusChallenge(id,0); } toast('ما لقيت هذا التحدي — قد يكون انتهت مدته'); return; } el.scrollIntoView({behavior:'smooth',block:'center'}); el.style.transition='box-shadow .3s'; el.style.boxShadow='0 0 0 2px var(--gold), 0 0 26px rgba(255,204,0,.5)'; setTimeout(function(){ el.style.boxShadow=''; }, 4000); }, tries===0?500:400); }; /* ===== دليل الكلانات — تصفّح وعرض (بلا تصنيف) ===== */ window._cdir={sort:'random',filter:'all',q:'',rows:null,seed:Math.random()}; window.loadClanDir=function(force){ var box=$('cdirList'); if(!box) return; if(window._cdir.rows && !force) return window.renderClanDir(); box.innerHTML='
⏳ جاري التحميل...
'; sb.from('clans').select('id,tag,name,type,status,logo_url,description,recruiting,recruit_note,created_at,activity_at,president_id') .neq('status','archived').limit(300).then(function(r){ if(r.error){ box.innerHTML='
تعذّر التحميل
'; return; } var rows=r.data||[]; if(!rows.length){ window._cdir.rows=[]; return window.renderClanDir(); } // عدد الأعضاء لكل كلان sb.from('clan_members').select('clan_id').then(function(mr){ var cnt={}; (mr.data||[]).forEach(function(m){ cnt[m.clan_id]=(cnt[m.clan_id]||0)+1; }); rows.forEach(function(c){ c._n=cnt[c.id]||0; }); window._cdir.rows=rows; window.renderClanDir(); },function(){ window._cdir.rows=rows; window.renderClanDir(); }); },function(){ box.innerHTML='
تعذّر التحميل
'; }); }; /* خلط ثابت ما دام المستخدم بالصفحة — ما يتغيّر مع كل إعادة رسم */ function seededShuffle(arr,seed){ var a=arr.slice(), s=seed; for(var i=a.length-1;i>0;i--){ s=(s*9301+49297)%233280; var j=Math.floor((s/233280)*(i+1)); var t=a[i]; a[i]=a[j]; a[j]=t; } return a; } function agoAr(iso){ if(!iso) return '—'; var d=Math.floor((Date.now()-new Date(iso).getTime())/1000); if(d<60) return 'الآن'; if(d<3600) return 'قبل '+Math.floor(d/60)+' دقيقة'; if(d<86400) return 'قبل '+Math.floor(d/3600)+' ساعة'; if(d<2592000) return 'قبل '+Math.floor(d/86400)+' يوم'; return tDay(iso); } window.renderClanDir=function(){ var box=$('cdirList'); if(!box) return; var C=window._cdir, rows=(C.rows||[]).slice(); var q=(C.q||'').trim().toUpperCase(); if(q) rows=rows.filter(function(c){ return String(c.tag||'').indexOf(q)!==-1 || String(c.name||'').toUpperCase().indexOf(q)!==-1; }); if(C.filter==='open') rows=rows.filter(function(c){ return c.recruiting!==false; }); if(C.filter==='ratified') rows=rows.filter(function(c){ return c.status==='ratified'; }); if(C.filter==='forming') rows=rows.filter(function(c){ return c.status!=='ratified'; }); if(C.sort==='random') rows=seededShuffle(rows, Math.floor(C.seed*233280)); else if(C.sort==='active') rows.sort(function(a,b){ return String(b.activity_at||'').localeCompare(String(a.activity_at||'')); }); else if(C.sort==='new') rows.sort(function(a,b){ return String(b.created_at||'').localeCompare(String(a.created_at||'')); }); else if(C.sort==='old') rows.sort(function(a,b){ return String(a.created_at||'').localeCompare(String(b.created_at||'')); }); var cc=$('cdirCount'); if(cc) cc.textContent = rows.length ? (rows.length+' كلان'+(q?(' · بحث «'+window._cdir.q+'»'):'')) : ''; if(!rows.length){ box.innerHTML='
'+ (q?'ما لقيت كلاناً بهذا البحث 🔎':'ما فيه كلانات بهذا الفلتر')+'
'; return; } box.innerHTML=rows.map(function(c){ var tg=String(c.tag||'؟'); var lg=c.logo_url ? '' : ''+esc(tg.slice(0,2))+''; /* ═══ سطر الحالة ═══ «معتمد» كان على كل كلان تقريباً فما عاد يميّز أحداً — شِيل. والذي يهمّ الزائر: هل بابهم مفتوح للتقديم أم لا. */ var rat=c.status==='ratified'; var stChip=(c.recruiting!==false) ? 'التقديم: مفتوح' : 'التقديم: مقفل'; var door=rat?'':' · ⏳ قيد الترسيم'; return '
'+lg+ '
'+ '
CLAN '+esc(tg)+' | COD-AR '+typeBadge(c.type)+'
'+ '
'+stChip+door+' · 👥 '+(c._n||0)+' · ⚡ '+agoAr(c.activity_at)+'
'+ '
'+ ''+ '
'; }).join(''); }; (function(){ var s=$('cdirSearch'); if(s){ var tm; s.addEventListener('input',function(){ window._cdir.q=s.value; clearTimeout(tm); tm=setTimeout(window.renderClanDir,200); }); } var so=$('cdirSort'); if(so) so.addEventListener('change',function(){ window._cdir.sort=so.value; if(so.value==='random') window._cdir.seed=Math.random(); window.renderClanDir(); }); var fi=$('cdirFilter'); if(fi) fi.addEventListener('change',function(){ window._cdir.filter=fi.value; window.renderClanDir(); }); })(); window.goOpenClans=function(){ go('hiring'); }; /* ═══ طلب الانضمام ═══ زرٌّ واحد من أي مكان: يتحقّق من أهليّتك ثم يوصل طلبك لرئيس الكلان. */ /* ═══ طلبات انضمامي المعلّقة ═══ نحفظ متى أرسلتُ طلباً لكل كلان، فنُخفي زرّ الانضمام ونمنع التكرار حتى تمرّ ٢٤ ساعة. الخريطة: معرّف الكلان ← وقت آخر طلب (مِلّي). */ window._myJoinReqs={}; window.JOIN_CD_MS=24*60*60*1000; window.loadMyJoinReqs=function(){ if(!window._uid){ window._myJoinReqs={}; return Promise.resolve(); } var since=new Date(Date.now()-window.JOIN_CD_MS).toISOString(); return sb.from('join_requests').select('clan_id,created_at,status') .eq('user_id',window._uid).in('status',['pending','rejected']).gte('created_at',since) .then(function(r){ var m={}; (r&&r.data||[]).forEach(function(x){ var t=Date.parse(x.created_at); if(!isNaN(t)) m[x.clan_id]=t; }); window._myJoinReqs=m; },function(){}); }; /* هل عندي طلبٌ ساري المهلة لهذا الكلان؟ يُرجع دقائق الانتظار أو 0 */ window.joinWait=function(cid){ var t=window._myJoinReqs&&window._myJoinReqs[cid]; if(!t) return 0; var left=t+window.JOIN_CD_MS-Date.now(); return left>0 ? Math.ceil(left/60000) : 0; }; window.fmtWait=function(min){ if(min>=60){ var h=Math.floor(min/60), m=min%60; return h+' س'+(m?(' و'+m+' د'):''); } return min+' د'; }; window.joinClanNow=function(cid, tag){ tag=String(tag||''); if(!window._uid){ toast('سجّل دخولك أولاً'); return go('account'); } if(window._banned){ toast('🚫 حسابك محظور — راجع الإدارة'); return; } if(window._myClanId||window._myMemberClanId){ toast('🛡️ أنت في كلان — اخرج منه أولاً'); return; } var w=window.joinWait(cid); if(w>0){ toast('⏳ أرسلت طلباً لـCLAN '+tag+' — تقدر تعيد بعد '+window.fmtWait(w)); return; } var note=prompt('رسالتك لرئيس CLAN '+tag+' (اختياري):\nمثال: سنايبر، أونلاين يومياً 10م',''); if(note===null) return; sb.rpc('request_join',{p_clan:cid, p_msg:String(note||'').trim()}).then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(d&&d.ok){ window._myJoinReqs[cid]=Date.now(); /* يختفي الزرّ فوراً */ toast('📨 وصل طلبك لرئيس CLAN '+tag+' — بيردّ عليك بالتنبيهات'); if(window.loadHiring) window.loadHiring(true); if(window._viewClan===cid && window.loadClanDetail) window.loadClanDetail(cid); return; } var why=(d&&d.why)||''; toast( why==='cooldown' ? ('⏳ أرسلت طلباً — تقدر تعيد بعد '+window.fmtWait((d&&d.wait_min)||1)) : why==='in_clan' ? '🛡️ أنت في كلان — اخرج منه أولاً' : why==='no_psn' ? '🎮 أضف PSN ID في حسابك أولاً' : why==='banned' ? '🚫 حسابك محظور — راجع الإدارة' : why==='closed' ? '🔒 هذا الكلان أغلق باب الانضمام' : why==='not_ratified' ? '⏳ الكلان ما يستقبل طلبات حتى يُرسَّم' : why==='no_clan' ? 'الكلان غير موجود' : 'تعذّر إرسال الطلب'); if(why==='no_psn') go('settings'); if(why==='cooldown'){ window._myJoinReqs[cid]=Date.now()-(window.JOIN_CD_MS-((d&&d.wait_min)||1)*60000); } },function(){ toast('تعذّر إرسال الطلب'); }); }; /* ═══ الكلانات المستقبِلة ═══ */ window._hiring=null; window.loadHiring=function(force){ var box=$('hiringList'); if(!box) return; var note=$('hiringNote'); if(note){ note.innerHTML = (window._myClanId||window._myMemberClanId) ? '
🛡️ أنت ضمن كلان — تقدر تتصفّح، لكن الانضمام يحتاج خروجك من كلانك أولاً.
' : ''; } if(window._hiring && !force) return window.renderHiring(); box.innerHTML='
⏳ جاري التحميل...
'; sb.from('clans').select('id,tag,name,type,status,logo_url,recruiting,recruit_note,description,points,wins,losses,activity_at') .neq('status','archived').limit(300).then(function(r){ if(r.error){ box.innerHTML='
تعذّر التحميل
'; return; } var rows=(r.data||[]).filter(function(c){ return c.recruiting!==false; }); sb.from('clan_members').select('clan_id').then(function(mr){ var cnt={}; (mr.data||[]).forEach(function(m){ cnt[m.clan_id]=(cnt[m.clan_id]||0)+1; }); rows.forEach(function(c){ c._n=cnt[c.id]||0; }); window._hiring=rows; window.renderHiring(); },function(){ window._hiring=rows; window.renderHiring(); }); },function(){ box.innerHTML='
تعذّر التحميل
'; }); }; window.renderHiring=function(){ var box=$('hiringList'); if(!box) return; var rows=(window._hiring||[]).slice().sort(function(a,b){ /* المعتمد أولاً، ثم الأنشط */ var ra=(a.status==='ratified')?0:1, rb=(b.status==='ratified')?0:1; if(ra!==rb) return ra-rb; return String(b.activity_at||'').localeCompare(String(a.activity_at||'')); }); if(!rows.length){ box.innerHTML='
🚪 ما فيه كلان فاتح باب الانضمام حالياً.
انشر طلبك في «أعضاء يبحثون عن كلان» ويشوفك الرؤساء.
'; return; } var free=!(window._myClanId||window._myMemberClanId); box.innerHTML=rows.map(function(c){ var tg=String(c.tag||'؟'); var lg=c.logo_url ? '' : ''+esc(tg.slice(0,4))+''; var rat=c.status==='ratified'; var chip=function(txt,bg,bd,cl){ return ''+txt+''; }; var st=rat?chip('✔ معتمد','rgba(61,220,132,.14)','rgba(61,220,132,.4)','#3ddc84') :chip('⏳ بانتظار الترسيم','rgba(255,204,0,.13)','rgba(255,204,0,.4)','var(--gold)'); var note=c.recruit_note||c.description||''; return '
'+ '
'+lg+ '
'+ '
CLAN '+esc(tg)+' '+typeBadge(c.type)+'
'+ (c.name?'
'+esc(c.name)+'
':'')+ '
'+st+ chip('👥 '+(c._n||0)+' عضو','rgba(255,255,255,.04)','var(--line)','var(--tx3)')+ chip('🏅 '+(c.points||0)+' نقطة','rgba(255,255,255,.04)','var(--line)','var(--tx3)')+ '
'+ '
'+ ''+ '
'+ (note?'
'+esc(String(note).slice(0,120))+'
':'')+ /* الكلان غير المرسَّم ما يستقبل طلبات — نُعلمه قبل الضغط لا بعده */ (function(){ var waitM=(free&&rat&&window.joinWait)?window.joinWait(c.id):0; if(waitM>0){ /* أرسل طلباً — لا زرّ، بل حالةٌ تقول متى يعيد */ return '
'+ '📨 أرسلت طلباً
أعد بعد '+window.fmtWait(waitM)+'
'; } var can=free&&rat; var act = !free ? 'toast(\'🛡️ اخرج من كلانك أولاً\')' : !rat ? 'toast(\'⏳ ما يستقبل طلبات حتى يُرسَّم من الإدارة\')' : 'window.joinClanNow(\''+c.id+'\',\''+esc(tg)+'\')'; var lbl = !free ? '🔒 أنت في كلان' : (!rat ? '⏳ بانتظار الترسيم' : '📨 طلب انضمام'); return ''; })()+ '
'; }).join(''); }; /* من هذي القائمة نرجع لها لا للكلانات */ window.openClanFromHiring=function(cid){ window._backTo={screen:'hiring'}; openClan(cid); }; /* ═══════════ أرشيف الكلانات ═══════════ الأرشفة ليست حذفاً: الكلان يخمد ويُحرَّر أعضاؤه، ويبقى سجلّه وتاقه محجوزين لصاحبه حتى يعود. */ window.loadArchive=function(force){ var box=$('arcList'); if(!box) return; box.innerHTML='
⏳ جاري التحميل...
'; sb.from('clans').select('id,tag,name,type,logo_url,points,wins,losses,wins_casual,losses_casual,archived_at,deleted_at,created_by,created_at') .eq('status','archived').order('archived_at',{ascending:false}).limit(200).then(function(r){ if(r.error){ box.innerHTML='
تعذّر التحميل
'; return; } var rows=r.data||[]; /* كلاناتك أولاً — هي التي تهمّك */ if(window._uid) rows.sort(function(a,b){ var ma=(a.created_by===window._uid)?0:1, mb=(b.created_by===window._uid)?0:1; return ma-mb; }); var mineN=window._uid?rows.filter(function(c){return c.created_by===window._uid;}).length:0; var note=$('arcNote'); if(note && mineN){ note.innerHTML='
'+ '🔓 عندك '+mineN+' كلان في الأرشيف — سجلّه وتاقه محفوظان لك، واسترجاعه بضغطة.'+ '
والأرشفة ما تمنعك من الانضمام لكلان آخر.
'; } else if(note) note.innerHTML=rows.length ? '
🔒 هذي الكلانات خامدة: ما تتحدّى ولا تُتحدّى ولا تستقبل أعضاء. ونتائجها تبقى في السجل العام.
' : ''; if(!rows.length){ box.innerHTML='
🗂️ الأرشيف فاضي
ما اعتزل أي كلان بعد
'; return; } box.innerHTML=rows.map(function(c){ var tg=String(c.tag||'؟'); var deld=!!c.deleted_at; /* حذفته الإدارة — لا مجرد أرشفة */ var lg=c.logo_url ? '' : ''+esc(tg.slice(0,4))+''; var t=(c.wins||0)+(c.losses||0); var mine=(window._uid && c.created_by===window._uid) || window._isStaff; return '
'+ '
'+lg+ '
'+ '
'+(deld?'🗑️':'🔒')+' CLAN '+esc(tg)+ (deld?' محذوف':'')+'
'+ (c.name?'
'+esc(c.name)+'
':'')+ '
'+(c.points||0)+' نقطة · '+(c.wins||0)+'ف '+(c.losses||0)+'خ'+(t?(' · '+t+' مباراة'):'')+'
'+ '
'+ ''+ '
'+ '
'+ (deld?'🗑️ حذفته الإدارة: '+(c.deleted_at?tDay(c.deleted_at):'—')+'' :'🗂️ أُرشِف: '+(c.archived_at?tDay(c.archived_at):'—')+'')+ '📅 سُجّل: '+(c.created_at?tDay(c.created_at):'—')+''+ '
'+ (deld ? (window._isStaff ? '
' : '
🗑️ حذفته الإدارة — سجلّه محفوظ هنا، واسترجاعه قرار إداري.
') : (mine? '
'+ ''+ ''+ '
' : ''))+ '
'; }).join(''); },function(){ box.innerHTML='
تعذّر التحميل
'; }); }; window.openClanFromArchive=function(cid){ window._backTo={screen:'archive'}; openClan(cid); }; /* ═══ الأرشفة ═══ قرار يمسّ كل عضو، فنطلب كتابة التاق لا مجرد «موافق» */ window.archiveClan=function(cid, tag, memberCount){ tag=String(tag||''); var n=memberCount||0; var msg='⚠️ أرشفة CLAN '+tag+'\n\n'+ '• يخرج من التصنيف والتصفّح\n'+ (n? ('• يُحرَّر أعضاؤه ('+n+') ويصيرون بلا كلان\n') : '')+ '• تُلغى طلبات التحدي المفتوحة\n'+ '• نتائجه تبقى في السجل العام\n'+ '• تقدر تسترجعه متى شئت — وتاقه محجوز لك\n\n'+ 'اكتب تاق كلانك للتأكيد:'; var typed=prompt(msg,''); if(typed===null) return; if(String(typed).trim().toLowerCase()!==tag.trim().toLowerCase()){ toast('التاق ما طابق — أُلغيت الأرشفة'); return; } toast('...جاري الأرشفة'); sb.rpc('archive_clan',{p_cid:cid}).then(function(r){ if(r&&r.error){ toast(errAr(r.error.message)); return; } var d=r&&r.data||{}; window._myClanId=null; window._myClanTag=null; if(window.refreshNow) window.refreshNow(); /* لا نتركه حائراً: نقول له أين ذهب كلانه وكيف يرجعه */ alert('🔒 أُرشِف CLAN '+(d.tag||tag)+'\n\n'+ (d.freed?('• حُرّر '+d.freed+' عضواً — صاروا بلا كلان\n'):'')+ '• كلانك الآن في «🔒 أرشيف الكلانات»\n'+ '• سجلّه ونقاطه وتاقه محفوظة لك\n'+ '• تقدر تسترجعه من هناك متى شئت\n'+ '• وتقدر تنضم لكلان آخر الآن — الأرشيف ما يقيّدك\n\n'+ 'ناخذك للأرشيف الحين.'); setTimeout(function(){ go('archive'); }, 300); }); }; /* الحذف النهائي — يُفرِج عن التاق ويمحو الكلان من المنصة */ window.deleteArchivedClan=function(cid, tag){ tag=String(tag||''); var typed=prompt('🗑️ حذف CLAN '+tag+' نهائياً\n\n'+ '• يُمحى الكلان من المنصة ولا يُسترجع أبداً\n'+ '• يُفرَج عن التاق فيقدر أي أحد يأخذه\n'+ '• نتائجه تبقى في السجل العام باسمه\n'+ '• وبعدها تقدر تسجّل كلاناً جديداً\n\n'+ 'اكتب تاق كلانك للتأكيد:',''); if(typed===null) return; if(String(typed).trim().toLowerCase()!==tag.trim().toLowerCase()){ toast('التاق ما طابق — أُلغي الحذف'); return; } toast('...جاري الحذف'); sb.rpc('delete_archived_clan',{p_cid:cid}).then(function(r){ if(r&&r.error){ toast(errAr(r.error.message)); return; } toast('🗑️ حُذف CLAN '+tag+' — التاق صار متاحاً'); if(window.loadArchive) window.loadArchive(true); if(window.refreshNow) window.refreshNow(); }); }; window.restoreClan=function(cid, tag){ if(!confirm('🔓 استرجاع CLAN '+tag+'؟\n\nيرجع بسجلّه ونقاطه وتاقه — وبلا أعضاء، تضيفهم من جديد.')) return; toast('...جاري الاسترجاع'); sb.rpc('restore_clan',{p_cid:cid}).then(function(r){ if(r&&r.error){ toast(errAr(r.error.message)); return; } toast('🔓 رجع CLAN '+tag+' — أضف أعضاءك ليترسّم'); if(window.refreshNow) window.refreshNow(); setTimeout(function(){ go('manageclan'); }, 900); }); }; /* ===== تبديل لوحتَي الترتيب ===== */ (function(){ var g=$('clanBoard'); if(!g) return; g.addEventListener('click',function(e){ var b=e.target.closest('button'); if(!b) return; [].forEach.call(g.querySelectorAll('button'),function(x){x.classList.remove('on');}); b.classList.add('on'); var nv=b.getAttribute('data-v'); if(nv===window._clanBoard) return; window._clanBoard=nv; window.swapBoard('lin-f'); }); /* ═══ سحبة الإصبع تبدّل النمط ═══ كما تقلّب صفحات القصص */ var list=$('realClanList'); if(list){ var x0=null, y0=null; list.addEventListener('touchstart',function(e){ var t=e.touches&&e.touches[0]; if(!t) return; x0=t.clientX; y0=t.clientY; },{passive:true}); list.addEventListener('touchend',function(e){ if(x0===null) return; var t=e.changedTouches&&e.changedTouches[0]; if(!t){ x0=null; return; } var dx=t.clientX-x0, dy=t.clientY-y0; x0=null; if(Math.abs(dx)<64 || Math.abs(dy)>Math.abs(dx)*0.7) return; /* تمريرٌ عمودي لا سحبة */ var stg=$('clanStyle'); if(!stg) return; var target = dx<0 ? 'rush' : 'sniper'; /* RTL: سحبة يساراً ⇒ الرشاش */ var btn=stg.querySelector('[data-v="'+target+'"]'); if(btn && !btn.classList.contains('on')){ btn.click(); if(window.ddSyncLabels) window.ddSyncLabels(); } },{passive:true}); } })(); /* ===== غرفة دردشة التحدي ===== */ window._cc={id:null,ch:null,sub:null,myClan:null}; /* ═══ الكتمُ من داخلِ الغرفة ═══ المدّةُ والسببُ معاً في نافذةٍ واحدة — **وسببٌ إلزاميّ** لأنَّ الغرفةَ تُعلِنُ القرارَ للكلانَينِ ومعه سببُه، والصمتُ بلا سببٍ يُقرَأُ انحيازاً. والقرارُ يُكتَبُ في سجلِّ الطاقمِ باسمِ صاحبِه لا باسمِ «الإدارة». */ window.ccMute = function(uid, name){ if(!uid || !window._cc || !window._cc.id) return; var mins = prompt('كتمُ ' + (name||'اللاعب') + ' في هذه الغرفة — كم دقيقة؟', '60'); if(mins === null) return; mins = parseInt(mins, 10); if(!(mins > 0)) return alert('أدخِل عدد دقائق صحيحاً.'); var why = prompt('سببُ الكتم (إلزاميّ — يظهر للكلانَين):', ''); if(why === null) return; if(!String(why).trim()) return alert('السببُ إلزاميّ.'); sb.rpc('mod_mute_room', { p_challenge: window._cc.id, p_uid: uid, p_minutes: mins, p_reason: String(why).trim() }) .then(function(r){ if(r && r.error) return alert('تعذّر: ' + r.error.message); var d = r && r.data; if(!d || !d.ok){ var W = { forbidden:'لا تملكُ صلاحيّةَ الكتم.', reason_required:'السببُ إلزاميّ.', not_found:'اللاعبُ غيرُ موجود.', self:'لا تكتمُ نفسَك.', owner_immune:'المالكُ لا يُكتَم.', staff_immune:'المشرفُ لا يكتمُ مشرفاً.', no_challenge:'التحدّي غيرُ موجود.' }; return alert(W[(d&&d.why)] || 'تعذّرَ الكتم.'); } var R = window.loadChChat || window.ccLoad || window.loadChallengeChat; if(typeof R === 'function') R(); }); };; window.openChChat=function(chId){ try{ if(window._perms && window._perms.act_challenge===true && window.unregGate) window.unregGate(); }catch(e){} if(!chId) return; if(!window._uid){ toast('سجّل دخولك أولاً'); return go('account'); } if(window._postponeTimer){ clearInterval(window._postponeTimer); window._postponeTimer=null; } window._cc.id=chId; /* هوية كلاني تخصّ هذه الغرفة وحدها — بقاؤها من غرفةٍ سابقة يقلب الطرفين */ window._cc.myClan=null; window._cc.repaint=null; /* ولا يبقى من الغرفة السابقة شيء: رسائلها ولا بلاغاتها ولا تفاصيلها */ window._cc.reps=null; window._cc._repBusy=false; window._cc._repTried=false; window._cc.ch=null; try{ window.putHTML($('ccMsgs'),''); window.putHTML($('ccVerdicts'),''); window.putHTML($('ccDetails'),''); }catch(e){} go('chchat'); var msgs=$('ccMsgs'); window.putHTML(msgs,'
⏳ جاري الفتح...
'); /* ═══ ccWatch — المراقبُ يقولُ السببَ بدلَ أن يصمت ═══ الغرفةُ تعلقُ على «جاري الفتح» ولا يُعرَفُ عندَ أيِّ خطوةٍ وقفت. **والكتمُ الصامتُ أسوأُ من العطلِ الصريح** — من رأى العطلَ أصلحَه. فيُسجَّلُ آخرُ ما بلغَته الخطواتُ في `_cc.step`، وبعدَ اثنتَي عشرةَ ثانيةً — إن بقيَ الدوّارُ — يُعرَضُ موضعُ الوقوفِ وزرُّ إعادة. **وهو لا يُصلحُ العطلَ بل يكشفُه**، ويمنعُ انتظاراً بلا نهاية. */ window._cc.step = 'الحارس'; (function(_id, _box){ if(window._cc._wd) clearTimeout(window._cc._wd); window._cc._wd = setTimeout(function(){ if(window._cc.id !== _id || !_box) return; if(_box.innerHTML.indexOf('جاري الفتح') < 0) return; window.putHTML(_box, '
' + '
تعذّر فتح الغرفة
' + '
' + 'وقفت عند: ' + (window._cc.step || '؟') + '' + (window._cc.err ? '
' + window._cc.err + '
' : '') + '
' + '' + '
'); }, 12000); })(chId, msgs); sb.rpc('can_enter_chchat',{p_ch:chId,p_user:window._uid}).then(function(gr){ if(gr.error || gr.data!==true){ window.putHTML(msgs,'
🔒 غرفة التحدي مقصورة على رئيس الكلان ونائبه فقط
'); return; } window._cc.step='تفاصيل التحدّي'; sb.from('challenges').select('*').eq('id',chId).single().then(function(r){ if(window._cc.id !== chId) return; /* سبقتها غرفةٌ أخرى */ var c=r.data; if(!c){ window.putHTML(msgs,'
التحدي غير موجود
'); return; } window._cc.ch=c; sb.from('clans').select('id,tag,logo_url').in('id',[c.challenger_clan,c.opponent_clan].filter(Boolean)).then(function(cr){ var m={}; (cr.data||[]).forEach(function(x){ m[x.id]=x; }); /* ═══ التاق يجب أن يُعرف أينما دخلت ═══ خريطة الكلانات كانت تُملأ من شاشة التحديات وحدها، فمن فتح الغرفة من إشعار أو رابط مباشر رأى «؟» مكان التاق. نملؤها هنا. */ window._clanMap=window._clanMap||{}; for(var _k in m){ if(m.hasOwnProperty(_k)) window._clanMap[_k]=m[_k]; } var A=m[c.challenger_clan]||{}, B=m[c.opponent_clan]||{}; if($('ccTitle')) $('ccTitle').innerHTML='CLAN '+esc(String(A.tag||'؟'))+' ضد CLAN '+esc(String(B.tag||'؟')); if($('ccSub')) $('ccSub').textContent='تبادلوا آيديات السوني، أضيفوا بعض، واتفقوا على الوقت'; /* التفاصيل الكاملة داخل الصفحة — كانت متفرّقة بالبطاقة */ /* «كلانك» من القاعدة — _myIds قد تكون فارغة عند الدخول المباشر للرابط */ /* ═══ من أنت في هذه المباراة؟ ═══ لا نُخمّن. إن لم نعرف بعدُ (القاعدة تُجيب بعد لحظة) نعرض الطرفين بأسمائهما بلا «كلانك/الخصم» — والتخمين الخاطئ أسوأ من التأخّر لحظة. */ var mineId=window._cc.myClan || ((window._myIds&&window._myIds.indexOf(c.challenger_clan)!==-1)?c.challenger_clan:null) || ((window._myIds&&window._myIds.indexOf(c.opponent_clan)!==-1)?c.opponent_clan:null) || null; var oppId=mineId?((mineId===c.challenger_clan)?c.opponent_clan:c.challenger_clan):null; var tm=tDT(c.created_at); var mm=window._clanMap||{}; var det=$('ccDetails'); /* بيانات القيادة تأتي من شاشة التحديات عادةً — نحمّلها هنا لمن يفتح صفحة التحدي مباشرةً من إشعار أو رابط */ if(!window._clanContacts || !window._clanContacts[c.challenger_clan] || !window._clanContacts[c.opponent_clan]){ sb.from('clan_members').select('clan_id,role,member_name,psn,profiles(username,psn_id)') .in('clan_id',[c.challenger_clan,c.opponent_clan].filter(Boolean)) .in('role',['leader','vice']).then(function(mr){ var cc=window._clanContacts||{}; (mr.data||[]).forEach(function(m){ cc[m.clan_id]=cc[m.clan_id]||{}; var ps=(m.psn||(m.profiles&&m.profiles.psn_id)||'').trim(); cc[m.clan_id][m.role]={ name:(ps||m.member_name||(m.profiles&&m.profiles.username)||''), psn:ps }; }); window._clanContacts=cc; if(window._cc.id===chId && window._cc.repaint) window._cc.repaint(); },function(){}); } window._cc.repaint=function(){ var mid=window._cc.myClan||mineId; var oid=(mid===c.challenger_clan)?c.opponent_clan:c.challenger_clan; mineId=mid; oppId=oid; paintDet(); }; var paintDet=function(){ if(!det) return; /* ═══ المواجهة ═══ صاحب الطلب يميناً وخصمه يساراً — الجهة تحدّدها المباراة لا المشاهد، فلا تنقلب على أحد. و«كلانك» شارةٌ تحت جهتك. */ var side=function(id, sd){ var fb=(id===c.challenger_clan)?A:B; var t=String((mm[id]||fb||{}).tag||'؟'); var lg=(mm[id]||fb||{}).logo_url; var me=(mineId&&id===mineId); return '
'+ (lg?'' :''+esc(t.slice(0,2).toUpperCase())+'')+ 'CLAN '+esc(t)+''+ (me?'كلانك':'')+ '
'+chContact(id)+'
'+ '
'; }; det.innerHTML= '
'+ '
⏰ وقت التحدي: '+esc(tm)+'
'+ /* ═══ أنت البطل ═══ كلانك يميناً بالأزرق وخصمك يساراً بالأحمر. ومن يشاهد من خارج الكلانين (مشرف أو زائر) يرى صاحب الطلب يميناً. */ '
'+ side(mineId||c.challenger_clan,'a')+ '
VS
'+ side(mineId?oppId:c.opponent_clan,'b')+'
'+ '
🎮 أضيفوا بعض على السوني والعبوا — وبعد المباراة وثّقوا النتيجة بالإثبات
'+ /* ═══ قاعدة المهلة تُقال في مكانها ═══ الطرفان يقرآنها هنا قبل أن تقع، لا بعد أن يُلغى التحدي فيسألا لماذا. والساعات تُقرأ من الإعدادات لا تُكتب رقماً ثابتاً — فإن غيّرتَها من اللوحة تغيّر النصّ معها. */ ((c.status==='accepted'||c.status==='live') ? '
'+ '⏳ مهلة التوثيق '+esc(String((window._appSet||{}).result_confirm_hours||'24'))+' ساعة
'+ 'وثّق أحدُكما وسكت الآخر ← تُعتمد رواية من وثّق.
'+ 'ولم يوثّق أحدٌ منكما ← يُلغى التحدي رسمياً وتتحرّر خانتاكما المصنّفة.'+ '
' : '')+ /* ═══ شرط اعتماد المصنّف ═══ المصنّف يمسّ التصنيف، فدليله أعلى: صور الانتصار في كل قيمٍ لا لقطةٌ واحدة. يُقال للطرفين هنا قبل اللعب لا بعد الرفض. */ (c.ranked ? '
'+ '🏅 تحدٍّ مصنّف — شرط الاعتماد
'+ 'لا تُعتمد النتيجة إلا برفع صور الانتصار في القيم كاملةً — وبغير ذلك لا يتم الاعتماد.'+ '
' : '')+ (c.status==='postpone_requested' ? '
'+ '
⏸️ طلب تأجيل معلّق
'+ '
'+ ((mineId&&mineId===c.postpone_requested_by)?'⏳ بعثت الطلب — بانتظار رد الكلان الآخر':'كلان '+esc(String((mm[c.postpone_requested_by]||{}).tag||'؟'))+' يطلب تأجيل التحدي')+ '
'+ (c.resume_at ? '
'+ '
موعد استكمال التحدي المطلوب
'+ '
📅 '+window.rydDate(c.resume_at)+' · '+window.rydTime(c.resume_at)+'
'+ '
بتوقيت السعودية · بعد '+window.durAr(new Date(c.resume_at)-Date.now())+'
'+ ((mineId&&mineId!==c.postpone_requested_by) ? '
'+ 'بقبولك يصير هذا الوقت مُلزِماً للطرفين — من لا يثبت حضوره خلال '+window.apptGrace()+ ' دقيقة يُعتبَر منسحباً 4-0، وإن غبتم جميعاً أُلغي التحدي. وإن ما ناسبك الوقت ارفض الطلب.'+ '
' : '')+ '
' : '')+ '
' : (c.status==='postponed'||c.status==='resume_requested')&&c.postpone_until ? '
'+ (c.status==='resume_requested' ?'
⏸️ مؤجَّل — طُلب استئنافه
' :'
⏸️ التحدي مؤجَّل
')+ (c.resume_at ? '
'+ '
موعد الاستكمال المُلزِم للطرفين
'+ '
📅 '+window.rydDate(c.resume_at)+' · '+window.rydTime(c.resume_at)+'
'+ '
بتوقيت السعودية
'+ '
'+ '
' : '
⏰ تنتهي المهلة: ...
')+ (c.postpone_after_maps?'
⚙️ اتفاق التأجيل: بعد '+esc(String(c.postpone_after_maps))+' قيم
':'')+ (c.status==='resume_requested' ?'
'+ ((mineId&&mineId===c.resume_requested_by)?'⏳ بعثت طلب الاستئناف — بانتظار رد الكلان الآخر':'كلان '+esc(String((mm[c.resume_requested_by]||{}).tag||'؟'))+' يطلب الاستئناف')+ '
' :'
اضغط استئناف لإرسال طلب للكلان الآخر — يعود بموافقة الطرفين
')+ '
' : '')+ '
'; var _cq2=(c.status==='accepted'||c.status==='live'); /* ═══ التوثيقُ والانسحابُ لا يقفانِ عندَ التأجيل ═══ الانسحابُ يقعُ في التأجيلِ كما يقعُ في اللعب، والنتيجةُ قد تُتَّفَقُ بعدَ أن لعبا ثمَّ أجّلا. ومنعُ التوثيقِ لا يمنعُ الواقعةَ بل يمنعُ تسجيلَها — فيبقى التحدي معلّقاً يشغلُ خانةً مصنّفةً ويحتاجُ مشرفاً في كلِّ مرّة. والخادمُ يقبلُها (`forfeit_challenge` و`core_submit_result`) — فالمنعُ كان في الواجهةِ وحدَها. */ var _canAct=(_cq2||c.status==='postponed'||c.status==='resume_requested'); var _pstPrq=(c.status==='postpone_requested'); var _pst=(c.status==='postponed'); var _rsmPrq=(c.status==='resume_requested'); var _am2=mineId; var _acts2=$('ccActs'); if(_acts2) _acts2.innerHTML= (_canAct?'':'')+ (_cq2&&c.allow_postpone?'':'')+ (_pstPrq&&_am2&&_am2!==c.postpone_requested_by?'':'')+ (_pst?(function(){ /* موعدٌ مُلزِمٌ قائم؟ الزرُّ يتبعُ النافذةَ لا رغبةَ اللاعب */ var _ra=c.resume_at?new Date(c.resume_at).getTime():0; var _gr=window.apptGrace()*60000, _nw=Date.now(); if(_ra && _nw>=_ra && _nw<_ra+_gr) return ''; if(_ra && _nw<_ra) return ''; return ''; })():'')+ (_rsmPrq&&_am2&&_am2!==c.resume_requested_by?'':'')+ ''+ ''+ (window._isStaff?'':''); if($('ccInfo')) $('ccInfo').innerHTML= '
'+chStatusChip(c.status)+' '+ typeBadge(c.playstyle==='rush'?'rush':'sniper')+' '+rankBadge(c.ranked)+ ''+ ''+ ((_cq2||_pstPrq||_pst||_rsmPrq)?'':'')+ (_canAct?'':'')+ '
'; }; paintDet(); if((c.status==='postponed'||c.status==='resume_requested')&&(c.resume_at||c.postpone_until)){ /* ═══ عبورُ حدِّ النافذةِ يغيّرُ الأزرارَ لا النصَّ وحدَه ═══ فلو اكتفَينا بتحديثِ العدّادِ بقيَ زرُّ «بانتظار الموعد» معروضاً بعدَ فتحِ النافذة، فيظنُّ الرئيسُ أنّه لم يحضرْ بعد. ولهذا نُعيدُ الرسمَ **مرّةً واحدةً عندَ العبور** لا كلَّ نبضة. */ var _wasIn=null; var _updTmr=function(){ if(window._cc.id!==chId){ clearInterval(window._postponeTimer); return; } if(c.resume_at){ var el=document.getElementById('ccApptTmr'); var ra=new Date(c.resume_at).getTime(), gr=window.apptGrace()*60000, nw=Date.now(); var inW=(nw>=ra && nw'; else el.innerHTML='انقضت نافذة الحضور'; } if(_wasIn!==null && _wasIn!==inW) paintDet(); _wasIn=inW; return; } var el2=document.getElementById('ccPostTmr'); if(!el2){ clearInterval(window._postponeTimer); return; } var rem=Math.max(0,new Date(c.postpone_until)-Date.now()); if(rem===0){ el2.textContent='انتهت المهلة'; clearInterval(window._postponeTimer); return; } el2.textContent=Math.floor(rem/3600000)+'س '+Math.floor((rem%3600000)/60000)+'د'; }; _updTmr(); window._postponeTimer=setInterval(_updTmr,15000); } if(window.renderModReq) window.renderModReq(c.id); if(window.loadVerdicts) window.loadVerdicts(c.id); if(window.ccRoomState) window.ccRoomState(c.status); }); sb.rpc('my_clan_in',{p_ch:chId,p_user:window._uid}).then(function(q){ window._cc.myClan=q&&q.data||null; /* وصلت هوية كلاني متأخّرة — نعيد رسم التفاصيل بالترتيب الصحيح */ if(window._cc.myClan && window._cc.repaint) window._cc.repaint(); }); window.loadChChat(); if(window.checkVoiceAccess) window.checkVoiceAccess(); if(window._cc.sub){ try{ sb.removeChannel(window._cc.sub); }catch(e){} } window._cc.sub=sb.channel('cc-'+chId) .on('postgres_changes',{event:'INSERT',schema:'public',table:'challenge_chat',filter:'challenge_id=eq.'+chId}, function(){ window.loadChChat(true); }) .on('postgres_changes',{event:'UPDATE',schema:'public',table:'challenges',filter:'id=eq.'+chId}, function(){ setTimeout(function(){ if(window._cc.id===chId) window.openChChat(chId); },250); }) .subscribe(function(){}); },function(){ window.putHTML(msgs,'
تعذّر الفتح
'); }); }); }; /* ═══ توقيتُ السعودية ═══ الرياضُ +٣ ثابتاً بلا توقيتٍ صيفيٍّ ولا شتويّ، فالحسابُ اليدويُّ يقينيّ: لا يتبعُ منطقةَ جهازِ اللاعبِ ولا يقلبُه تقويمٌ هجريٌّ في `toLocaleString`. **والوقتُ المتَّفَقُ عليه واحدٌ للطرفَين، فلا يجوزُ أن يراه كلٌّ بساعةِ بلدِه.** وكلُّ رقمٍ في ``: رقمانِ في سطرٍ عربيٍّ واحدٍ ينقلبانِ بصريّاً. */ var RYD_OFF=3*3600000; var AR_DAY=['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت']; var AR_MON=['يناير','فبراير','مارس','أبريل','مايو','يونيو','يوليو','أغسطس','سبتمبر','أكتوبر','نوفمبر','ديسمبر']; function _p2(n){ return (n<10?'0':'')+n; } function _ryd(iso){ return new Date(new Date(iso).getTime()+RYD_OFF); } /* ═══ أيُّ يومٍ هو؟ ═══ «٢:٢٢» وحدَها غامضةٌ والمهلةُ يومان: أهي ليلةَ اليومِ أم ليلةَ الغد؟ فتُذكَرُ نسبةُ اليومِ قبلَ اسمِه في **كلِّ** موضعٍ يُعرَضُ فيه الوقت. والقياسُ بيومِ الرياضِ لا بيومِ الجهاز. */ function _rydKey(x){ var t=_ryd(x); return Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()); } window.rydRel=function(iso){ var n=Math.round((_rydKey(iso)-_rydKey(Date.now()))/86400000); return n===0?'اليوم':n===1?'بكرة':n===2?'بعد بكرة':(n===-1?'أمس':''); }; window.rydDate=function(iso){ var t=_ryd(iso), r=window.rydRel(iso); return (r?r+' · ':'')+AR_DAY[t.getUTCDay()]+' '+t.getUTCDate()+' '+AR_MON[t.getUTCMonth()]; }; window.rydTime=function(iso){ var t=_ryd(iso), h=t.getUTCHours(); return ''+(h%12||12)+':'+_p2(t.getUTCMinutes())+' '+(h<12?'صباحاً':'مساءً'); }; window.rydPlain=function(iso){ var t=_ryd(iso), h=t.getUTCHours(), r=window.rydRel(iso); return (r?r+' · ':'')+AR_DAY[t.getUTCDay()]+' '+t.getUTCDate()+' '+AR_MON[t.getUTCMonth()]+ ' الساعة '+(h%12||12)+':'+_p2(t.getUTCMinutes())+' '+(h<12?'صباحاً':'مساءً'); }; /* ═══ العددُ العربيُّ يُصرِّفُ معدودَه ═══ «٩ ساعة» خطأٌ يُقرأُ ركيكاً في وجهِ لاعبٍ ينتظرُ موعدَ مباراتِه. والقاعدة: واحدٌ مفردٌ، واثنانِ مثنّى، ومن ثلاثةٍ إلى عشرةٍ جمعٌ، ومن أحدَ عشرَ فصاعداً مفردٌ منصوب. */ window.arCount=function(n,one,two,few,many){ if(n===0) return 'أقل من '+one; if(n===1) return one; if(n===2) return two; if(n<=10) return n+' '+few; return n+' '+many; }; window.durAr=function(ms){ if(!(ms>0)) ms=0; var mn=Math.round(ms/60000), h=Math.floor(mn/60), d=Math.floor(h/24); var D=function(n){ return window.arCount(n,'يوم','يومين','أيام','يوماً'); }; var H=function(n){ return window.arCount(n,'ساعة','ساعتين','ساعات','ساعة'); }; var M=function(n){ return window.arCount(n,'دقيقة','دقيقتين','دقائق','دقيقة'); }; if(d>0) return ''+D(d)+''+((h%24)?' و'+H(h%24)+'':''); if(h>0) return ''+H(h)+''+((mn%60)?' و'+M(mn%60)+'':''); return ''+M(mn)+''; }; window.apptGrace=function(){ return parseInt((window._appSet||{}).appt_grace_min,10)||20; }; /* ═══ إدخالُ موعدِ الاستكمال ═══ الاتّفاقُ يقعُ في اللعبةِ بينَ الرئيسَين — والمنصّةُ **تُوثّقُه وتُنفّذُه** لا تُفاوضُ عليه. فهذا حقلُ إدخالٍ لما اتُّفِقَ عليه، لا بابُ مساومةٍ جديد. */ window.askResumeAt=function(maxH){ maxH=maxH||48; return new Promise(function(done){ ['cfBack','cfBox'].forEach(function(i){ var o=document.getElementById(i); if(o) o.remove(); }); var bk=document.createElement('div'); bk.id='cfBack'; var bx=document.createElement('div'); bx.id='cfBox'; var dmin=new Date(Date.now()+RYD_OFF).toISOString().slice(0,10); var dmax=new Date(Date.now()+RYD_OFF+maxH*3600000).toISOString().slice(0,10); var i, hop='', mop='', dchip=''; for(i=1;i<=12;i++) hop+=''; for(i=0;i<60;i++) mop+=''; /* ═══ ثلاثةُ أيّامٍ لا أكثر ═══ سقفُ التأجيلِ يومان، فأبعدُ يومٍ ممكنٍ هو «بعد بكرة». وحقلُ التاريخِ الرقميُّ يُخفي هذه البداهةَ خلفَ تقويمٍ كامل. */ var DCH='flex:1;min-width:0;padding:8px 4px;border-radius:9px;cursor:pointer;'+ 'font-family:inherit;text-align:center;line-height:1.5;'+ 'background:rgba(0,0,0,.3);border:1px solid rgba(255,255,255,.16);color:var(--tx2)'; var RELN=['اليوم','بكرة','بعد بكرة']; for(i=0;i<3;i++){ var dd=new Date(Date.now()+RYD_OFF+i*86400000); dchip+=''; } var IST='width:100%;padding:9px 10px;border-radius:9px;background:rgba(0,0,0,.3);'+ 'border:1px solid rgba(255,255,255,.16);color:var(--tx);font-size:14px;'+ 'font-family:inherit;text-align:center'; bx.innerHTML= '
📅
'+ '
موعد استكمال التحدي
'+ '
اكتب اليوم والساعة التي اتفقت عليها مع رئيس الكلان الآخر داخل اللعبة. '+ 'الكلان الآخر يراها في طلب التأجيل، وبقبوله تصير مُلزِمة للطرفين.
'+ '
'+ '
'+ '
اليوم
'+ '
'+dchip+'
'+ ''+ '
'+ '
'+ '
الساعة والدقيقة
'+ '
'+ ''+ ':'+ ''+ ''+ '
'+ '
'+ '
'+ '
'+ '
'+ ''+ ''+ '
'; document.body.appendChild(bk); document.body.appendChild(bx); var D=bx.querySelector('#raD'), H=bx.querySelector('#raH'), M=bx.querySelector('#raM'), Pm=bx.querySelector('#raP'), PV=bx.querySelector('#raPrev'), OK=bx.querySelector('#cfYes'); /* الافتراضُ: بعدَ ساعتَينِ من الآنَ بتوقيتِ الرياض */ var DAYS=[].slice.call(bx.querySelectorAll('.raDay')); var setDay=function(v){ D.value=v; DAYS.forEach(function(b){ var on=(b.getAttribute('data-d')===v); b.style.background = on?'linear-gradient(180deg,rgba(232,179,75,.28),rgba(232,179,75,.14))':'rgba(0,0,0,.3)'; b.style.borderColor = on?'rgba(232,179,75,.75)':'rgba(255,255,255,.16)'; b.style.color = on?'var(--gold)':'var(--tx2)'; }); paint(); }; DAYS.forEach(function(b){ b.onclick=function(){ setDay(b.getAttribute('data-d')); }; }); var d0=new Date(Date.now()+RYD_OFF+2*3600000); var h0=d0.getUTCHours(); H.value=String(h0%12||12); M.value=String(d0.getUTCMinutes()); Pm.value=(h0<12?'ص':'م'); var calc=function(){ if(!D.value) return null; var h=(parseInt(H.value,10)%12)+(Pm.value==='م'?12:0); /* الوقتُ مكتوبٌ بساعةِ الرياض: نبنيه UTC ثمَّ نطرحُ الفارقَ فيصيرُ لحظةً واحدةً للعالم */ var t=Date.parse(D.value+'T'+_p2(h)+':'+_p2(parseInt(M.value,10))+':00Z'); return isNaN(t)?null:(t-RYD_OFF); }; var paint=function(){ var t=calc(), nw=Date.now(); if(t===null){ PV.innerHTML='حدّد اليوم'; OK.disabled=true; OK.style.opacity=.5; return; } var dl=t-nw; if(dl < 15*60000){ PV.innerHTML='⚠️ الموعد قريب جداً — اختر وقتاً بعد ربع ساعة على الأقل'; OK.disabled=true; OK.style.opacity=.5; return; } if(dl > maxH*3600000){ PV.innerHTML='⚠️ أقصى تأجيل '+maxH+' ساعة — قرّب الموعد'; OK.disabled=true; OK.style.opacity=.5; return; } OK.disabled=false; OK.style.opacity=1; PV.innerHTML='
📅 '+window.rydDate(t)+'
'+ '
🕐 '+window.rydTime(t)+'
'+ '
بتوقيت السعودية · بعد '+window.durAr(dl)+'
'+ '
من لا يحضر خلال '+ window.apptGrace()+' دقيقة من الموعد يُعتبَر منسحباً
'; }; [H,M,Pm].forEach(function(el){ el.onchange=paint; el.oninput=paint; }); setDay(d0.toISOString().slice(0,10)); var close=function(v){ document.removeEventListener('keydown',key); bk.remove(); bx.remove(); done(v); }; var key=function(e){ if(e.key==='Escape') close(null); }; document.addEventListener('keydown',key); bk.onclick=function(){ close(null); }; bx.querySelector('#cfNo').onclick=function(){ close(null); }; OK.onclick=function(){ if(OK.disabled) return; close(calc()); }; }); }; var POSTPONE_ERR={ NOT_LEADER:'التأجيل لرئيس الكلان أو نائبه فقط', NOT_FOUND:'التحدي غير موجود', NOT_ALLOWED:'التأجيل غير متاح لهذا التحدي', WRONG_STATUS:'حالة التحدي لا تسمح بالتأجيل الآن', ALREADY_POSTPONED_OTHER:'⏸️ عندكم تحدٍّ آخر مؤجَّل بالفعل — لازم تستأنفوه أو تُنهوه قبل تأجيل تحدٍّ ثانٍ', MAX_REACHED:'وصلتم الحد الأقصى لطلبات التأجيل لهذا التحدي', CONCURRENT:'تعذّر الطلب — حاول مرة أخرى', SAME_CLAN:'أنت صاحب الطلب — الرد يخصّ الكلان الآخر', RESUME_REQUIRED:'لازم تحدّد موعد استكمال التحدي — حدّث الصفحة إن لم يظهر لك الحقل', RESUME_TOO_SOON:'الموعد قريب جداً — اختر وقتاً بعد ربع ساعة على الأقل', RESUME_TOO_FAR:'الموعد أبعد من الحد المسموح للتأجيل', NO_RESUME_AT:'الطلب أُرسل بلا موعد استكمال — اطلب من الكلان الآخر إعادة الطلب', RESUME_PASSED:'مضى موعد الاستكمال قبل ردّك — الطلب لم يعد صالحاً' }; var READY_ERR={ auth:'سجّل دخولك أولاً', not_found:'التحدي غير موجود', not_leader:'إثبات الحضور لرئيس الكلان أو نائبه فقط', no_window:'ما فيه نافذة حضور مفتوحة الآن — انتظر موعد الاستكمال', expired:'انقضت نافذة الحضور' }; /* ═══ إثباتُ الحضورِ عندَ الموعد ═══ «كنتُ موجوداً» دعوى، وهذه الضغطةُ أثرٌ مسجَّلٌ بوقتِه — وبها وحدَها يُفصَلُ بينَ كلانَينِ كلٌّ منهما يقولُ إنّه حضر. */ window.doReadyConfirm=function(chId){ sb.rpc('ready_confirm',{p_ch:chId}).then(function(r){ var d=r&&r.data; if(r.error||!d||d.ok!==true){ toast('⚠️ '+(READY_ERR[(d&&d.why)||'']||(r.error&&r.error.message)||'تعذّر إثبات الحضور')); window.openChChat(chId); return; } if(d.both) toast('✅ حضر الطرفان — استُؤنف التحدي'); else toast('✋ سُجّل حضورك — بانتظار الكلان الآخر'); window.openChChat(chId); }); }; window.doRequestPostpone=function(chId){ var _mx=parseInt((window._appSet||{}).postpone_max_hours,10)||48; window.askResumeAt(_mx).then(function(at){ if(!at) return; sb.rpc('request_postpone',{p_ch_id:chId,p_resume_at:new Date(at).toISOString()}).then(function(r){ var d=r&&r.data; if(r.error||(d&&d.error)){ var why=(d&&d.error)||''; toast('⚠️ '+(POSTPONE_ERR[why]||(r.error&&r.error.message)||'تعذّر الطلب')); return; } toast('⏸️ أُرسل طلب التأجيل ومعه موعد الاستكمال — بانتظار رد الكلان الآخر'); window.openChChat(chId); }); }); }; window.doRespondPostpone=function(chId,accept){ if(accept){ var _c=(window._cc&&window._cc.ch)||{}, _gr=window.apptGrace(); window.confirmBox({icon:'📅',tone:'gold',title:'قبول التأجيل والموعد؟', text:(_c.resume_at?'موعد الاستكمال: '+window.rydPlain(_c.resume_at)+' بتوقيت السعودية. ':'')+ 'بقبولك يصير الموعد مُلزِماً للطرفين: من لا يثبت حضوره خلال '+_gr+ ' دقيقة من الموعد يُعتبَر منسحباً 4-0، وإن غاب الطرفان أُلغي التحدي.', yes:'نعم، أقبل الموعد',no:'إلغاء'}).then(function(ok){ if(!ok) return; sb.rpc('respond_postpone',{p_ch_id:chId,p_accept:true}).then(function(r){ var d=r&&r.data; if(r.error||(d&&d.error)){ toast('⚠️ '+((d&&d.error)||(r.error&&r.error.message)||'تعذّر القبول')); return; } toast(d&&d.legacy?'⏸️ قُبل التأجيل — التحدي معلَّق الآن' :'📅 قُبل الموعد — صار مُلزِماً للطرفين'); window.openChChat(chId); }); }); } else { sb.rpc('respond_postpone',{p_ch_id:chId,p_accept:false}).then(function(r){ var d=r&&r.data; if(r.error||(d&&d.error)){ toast('⚠️ '+((d&&d.error)||(r.error&&r.error.message)||'تعذّر الرفض')); return; } toast('❌ رُفض التأجيل — التحدي مستمر'); window.openChChat(chId); }); } }; window.doRequestResume=function(chId){ window.confirmBox({icon:'▶️',tone:'gold',title:'طلب الاستئناف؟', text:'سيُرسَل طلب للكلان الآخر. التحدي يعود للعب بعد موافقة الطرفين.', yes:'نعم، أرسل الطلب',no:'إلغاء'}).then(function(ok){ if(!ok) return; sb.rpc('request_resume',{p_ch_id:chId}).then(function(r){ var d=r&&r.data; if(r.error||(d&&d.error)){ toast('⚠️ '+((d&&d.error)||(r.error&&r.error.message)||'تعذّر الطلب')); return; } toast('▶️ أُرسل طلب الاستئناف — بانتظار رد الكلان الآخر'); window.openChChat(chId); }); }); }; window.doRespondResume=function(chId,accept){ sb.rpc('respond_resume',{p_ch_id:chId,p_accept:accept}).then(function(r){ var d=r&&r.data; if(r.error||(d&&d.error)){ toast('⚠️ '+((d&&d.error)||(r.error&&r.error.message)||'تعذّر الاستئناف')); return; } if(accept){ toast('▶️ استُؤنف التحدي — عودوا للعب!'); } else { toast('❌ رُفض الاستئناف — التأجيل لا يزال قائماً'); } window.openChChat(chId); }); }; /* من صفحة التحدي إلى نموذج توثيق النتيجة — ونختار التحدي تلقائياً */ /* ═══ قرارات الإشراف على هذا التحدي ═══ الحكم يخصّ المباراة، فيراه الطرفان في صفحتها لا في تنبيه عابر. */ /* ═══════════ بطاقة المشرف ═══════════ الكلانان يريدان معرفة من يحكم بينهما — اسمٌ عابر لا يكفي. */ window.closeMod=function(){ var c=document.getElementById('modCard'), b=document.getElementById('modBack'); /* البطاقة مشتركة بين المشرف واللاعب — فنُنظّف مفوَّض الأفعال معها */ if(c){ c.hidden=true; c.innerHTML=''; c.onclick=null; } if(b) b.hidden=true; }; window.openMod=function(uid, fallbackName){ var c=document.getElementById('modCard'), b=document.getElementById('modBack'); if(!c||!b) return; var draw=function(p){ p=p||{}; var nm=p.username||fallbackName||'مشرف'; var av=p.avatar_url ? '' : '
'+esc(String(nm).slice(0,1).toUpperCase())+'
'; /* الإداريُّ ليس مشرفاً — ولا يُنسَبُ لغيرِ رتبتِه. و«support» مُهيّأةٌ من الآن: متى مُنِحَ الدورُ ظهرَ اسمُه بلا نشرٍ جديد. */ var R={admin:['🛡️ الإدارة','rgba(255,107,94,.16)','rgba(255,107,94,.45)','#ff8c7a'], moderator:['🧑‍⚖️ مشرف','rgba(255,204,0,.14)','rgba(255,204,0,.45)','var(--gold)'], support:['🛠️ الدعم الفني','rgba(61,220,132,.14)','rgba(61,220,132,.45)','#3ddc84']}; var r=R[p.role]||['🛡️ الإدارة','rgba(56,182,255,.14)','rgba(56,182,255,.45)','var(--blue2)']; var since=p.created_at?('عضو منذ '+String(p.created_at).slice(0,10)):''; c.innerHTML=av+ '
'+(p.id?window.dot(p.id):'')+esc(nm)+'
'+ ''+r[0]+''+ (p.title?'
🏷️ '+esc(p.title)+'
':'')+ '
'+(p.bio?esc(String(p.bio).slice(0,110))+'
':'')+ (since?''+esc(since)+'':'')+'
'+ (p.id?'':'')+ ''; c.hidden=false; b.hidden=false; }; if(!uid){ draw(null); return; } sb.from('profiles').select('id,username,avatar_url,role,title,bio,created_at').eq('id',uid).single() .then(function(r){ draw(r&&r.data); }, function(){ draw(null); }); }; document.addEventListener('click',function(e){ var t=e.target&&e.target.closest?e.target.closest('.modlink'):null; if(t) window.openMod(t.getAttribute('data-mod')||'', t.getAttribute('data-modname')||''); }); document.addEventListener('keydown',function(e){ if(e.key==='Escape') window.closeMod(); }); /* ═══════════════ بطاقة البلاغ ═══════════════ بطاقةٌ واحدة تخدم موضعين: المعلّقة فوق حين تكون قيد الفحص، والنازلة في المحادثة حين يُرفع البلاغ أو يصدر الحكم — بهيئتها كاملةً: النوع والتفاصيل والأدلّة، واسم المشرف مع قراره وعلامته. */ window.reportCard=function(p){ if(!p) return ''; var done=(p.status==='resolved'); var held=(!done && !!p.claimed_by_name); var ok=(p.outcome!=='rejected'); var col = done ? (ok?'#3ddc84':'#ff8c7a') : held ? 'var(--blue2)' : 'var(--gold)'; var bg = done ? (ok?'rgba(61,220,132,.07)':'rgba(255,107,94,.07)') : held ? 'rgba(56,182,255,.07)' : 'rgba(255,204,0,.07)'; var bd = done ? (ok?'rgba(61,220,132,.4)':'rgba(255,107,94,.4)') : held ? 'rgba(56,182,255,.4)' : 'rgba(255,204,0,.4)'; var mid=(done?(p.resolved_by||p.claimed_by):p.claimed_by)||''; var mnm=p.claimed_by_name||''; var mlink=function(txt){ return ''+esc(txt)+' ⓘ'; }; var ev=String(p.evidence_url||'').split(',').filter(Boolean); var evh = ev.length ? '
'+ ev.slice(0,6).map(function(u){ var vid=window.isVid(u); return ''+ (vid ? ''+ ''+ 'مقطع' : '')+ ''; }).join('')+ '
اضغط الدليل ليفتح بملء الشاشة
' : ''; var foot = done ? '
'+ '
⚖️ قرار المشرف'+(mnm?' — '+mlink(mnm):'')+'
'+ '
'+esc(p.resolution||'—')+'
' : held ? '
'+ '
🛡️ قيد الفحص — '+mlink(mnm||'المشرف')+'
'+ '
يوصلكم الحكم هنا وبالتنبيهات
' : '
'+ '
⏳ بانتظار مشرف يستلمها
'+ '
يوصلكم الحكم هنا وبالتنبيهات
'; return '
'+ '
'+ '🚩 البلاغ'+ ''+ ''+tDT(p.created_at)+''+ '
'+ '
'+ 'النوع: '+esc(p.reason||'مخالفة')+ (p.details?'
'+esc(p.details)+'':'')+ '
'+ foot + evh + '
'; }; /* ═══ المُبلِّغُ يرفعُ الإثباتَ المطلوبَ من مكانِه ═══ طلبُ المشرفِ كان يصلُ بلا بابٍ يُنفَّذُ منه، فيبقى البلاغُ معلّقاً حتى يسقط. الآن يظهرُ الزرُّ لصاحبِ البلاغِ وحدَه في بطاقتِه المثبّتةِ في الغرفة، والرفعُ يمرُّ بخدمةِ الوسائطِ نفسِها بفحصِ بصمتِها وسقفِها. */ window.repEvidence=function(rid){ var fi=document.getElementById('repEvFile'); if(!fi){ fi=document.createElement('input'); fi.type='file'; fi.id='repEvFile'; fi.accept='image/*,video/*'; fi.style.display='none'; document.body.appendChild(fi); } fi.onchange=function(){ var f=fi.files&&fi.files[0]; fi.value=''; if(!f) return; if(f.size>(window.UP_MAX_MB||100)*1024*1024){ toast('⚠️ الملف أكبر من '+(window.UP_MAX_MB||100)+'MB'); return; } toast('⏳ جاري رفع الإثبات…'); window.upProof(f, rid, function(p){ if(p<1) toast('⏳ رفع الإثبات… '+Math.round(p*100)+'%'); }) .then(function(u){ if(!u||!u.ok){ toast('تعذّر الرفع: '+((u&&u.why)||'؟')); return; } sb.rpc('submit_report_evidence',{p_rid:rid, p_urls:u.url}).then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(!d||!d.ok){ toast((d&&d.error)||'تعذّر الإرسال'); return; } toast('✅ وصل الإثبات — رجع البلاغ للمراجعة'); if(window._cc&&window._cc.id) window.loadPinnedReports&&window.loadPinnedReports(window._cc.id); }); }); }; fi.click(); }; /* بلاغات هذه الغرفة — تُجلب مرة وتُستعمل في المحادثة وفي المعلّق */ window.loadRepMap=function(chId, then){ sb.from('reports').select('id,reason,details,status,resolution,created_at,evidence_url,claimed_by_name,claimed_by,resolved_by,outcome') .eq('challenge_id',chId).limit(30).then(function(r){ if(window._cc.id !== chId) return; /* غرفةٌ أخرى صارت مفتوحة */ var m={}; (r.data||[]).forEach(function(x){ m[x.id]=x; }); window._cc.reps=m; if(then) then(); },function(){ window._cc.reps=window._cc.reps||{}; if(then) then(); }); }; /* ═══ الزرُّ لصاحبِ البلاغِ وحدَه وحينَ يُطلَبُ منه ═══ نلفُّ الدالّةَ الأصليّةَ ولا نُعيدُ كتابتَها — في هذا الملفِّ يدٌ أخرى تعمل، ولفُّها يُبقي عملَها سليماً مهما تغيّر. */ (function(){ var orig=window.reportCard; if(typeof orig!=='function') return; window.reportCard=function(p){ var h=orig.apply(this, arguments); try{ if(!p || p.status!=='awaiting_evidence') return h; if(!window._uid || p.reporter_id!==window._uid) return h; return h.replace(/<\/div>\s*$/, '
'+ '
📎 المشرف يطلب إثباتاً'+ (p.ev_note?'
'+esc(p.ev_note):'')+'
'+ '
'); }catch(e){ return h; } }; })(); window.loadVerdicts=function(chId){ var box=$('ccVerdicts'); if(!box) return; /* ═══ ما الذي يستحقّ التعليق فوق الرأس؟ ═══ البلاغ نفسه ينزل في المحادثة مع بقية الأحداث، والحكم كذلك. البلاغ يبقى مثبّتاً ما دام معلّقاً — استلمه مشرف أو لم يستلمه — لأنه شيءٌ ينتظر. وحين يصدر الحكم ينزل في المحادثة ولا يبقى فوق. */ sb.from('reports').select('id,reason,details,status,resolution,created_at,evidence_url,claimed_by_name,claimed_by,resolved_by,outcome,reporter_id,ev_note') .eq('challenge_id',chId).in('status',['open','reviewing','awaiting_evidence']).order('created_at',{ascending:false}).limit(5).then(function(r){ if(window._cc.id && window._cc.id !== chId) return; var rows=r.data||[]; if(!rows.length){ box.innerHTML=''; return; } box.innerHTML=rows.map(function(p){ return window.reportCard(p); }).join(''); },function(){ box.innerHTML=''; }); }; /* البلاغ داخل صفحة التحدي — والتحدي محدَّد سلفاً */ window.goReport=function(chId){ var box=$('ccReport'); if(!box) return; if(!box.hidden){ box.hidden=true; return; } box.hidden=false; var sel=$('rpChallenge'); if(sel){ var has=false; [].forEach.call(sel.options,function(o){ if(o.value===chId) has=true; }); if(!has){ var c=window._cc&&window._cc.ch, mm=window._clanMap||{}; var a=String((mm[(c||{}).challenger_clan]||{}).tag||'؟'); var b=String((mm[(c||{}).opponent_clan]||{}).tag||'؟'); var o=document.createElement('option'); o.value=chId; o.textContent=a+' ضد '+b; sel.appendChild(o); } sel.value=chId; /* التحدي محدَّد — نخفي القائمة كي لا يخطئ */ sel.style.display='none'; } box.scrollIntoView({behavior:'smooth',block:'center'}); }; /* الرجوع الذكي: من صفحة الخصم نعود لصفحة التحدي لا للرئيسية */ window.openClanFrom=function(clanId, chId){ window._backTo={screen:'chchat', ch:chId}; openClan(clanId); }; window.goBackSmart=function(){ var b=window._backTo; if(b && b.screen==='chchat' && b.ch){ window._backTo=null; return window.openChChat(b.ch); } if(b && b.screen==='hiring'){ window._backTo=null; return go('hiring'); } if(b && b.screen==='archive'){ window._backTo=null; return go('archive'); } window._backTo=null; window.goBack('clans'); }; /* التوثيق داخل صفحة التحدي — لا ينقلك لصفحة أخرى */ window._vp=[]; window._vpChId=null; window.goVerify=function(chId){ var box=$('ccVerify'); if(!box) return; /* نفس التحدي: إغلاق مؤقت — نحتفظ بالصور المرفوعة */ if(!box.hidden && window._vpChId===chId){ box.hidden=true; return; } /* تحدٍّ جديد = صفر جديد */ if(window._vpChId!==chId) window._vp=[]; window._vpChId=chId; box.innerHTML=''; var c=window._cc&&window._cc.ch; if(!c) return; /* ═══ من أنت؟ ═══ الفائز وشطرا النتيجة كانا يُشتقّان من افتراض أنك صاحب الطلب — فلو وثّق عضوٌ من الطرف الثاني قبل أن تصل هويته، انقلب الفائز والنتيجة معاً. الآن: لا افتراض، ولكل كلان زرُّه وخانتُه. */ var mine=window._cc.myClan || ((window._myIds&&window._myIds.indexOf(c.challenger_clan)!==-1)?c.challenger_clan:null) || ((window._myIds&&window._myIds.indexOf(c.opponent_clan)!==-1)?c.opponent_clan:null); var R=mine||c.challenger_clan; /* يمين */ var L=(R===c.challenger_clan)?c.opponent_clan:c.challenger_clan; /* يسار */ var opp=(R===mine)?L:null; var mm=window._clanMap||{}; var tg=function(id){ return String((mm[id]||{}).tag||'؟'); }; box.hidden=false; box.innerHTML= '
'+ '📸 توثيق نتيجة التحدي'+ '
'+ 'اختر ماذا توثّق. الفوز يحتاج إثباتاً، والخسارة تُقبل بلا صور — '+ 'وبها تصحّح نتيجة خصمك لو ضخّمها عليك.
'+ '
'+ ''+ '
'+ ''+ '
النتيجة — لكل كلان خانته
'+ '
'+ '
CLAN '+esc(tg(R))+'
'+ '
'+ ''+ '
CLAN '+esc(tg(L))+'
'+ '
'+ '
'+ ''+ '
'+ ''+ '
'+ '
'+ '
'+ ''+ '
'; var seg=$('vWin'); if(seg) seg.addEventListener('click',function(e){ var t=e.target.closest('button'); if(!t) return; [].forEach.call(seg.querySelectorAll('button'),function(x){x.classList.remove('on');}); t.classList.add('on'); }); /* الفوز يحتاج إثباتاً، والخسارة تُقبل بلا صور — فيتبدّل النموذج معهما */ window._vSide='winner'; var sideSeg=$('vSide'); if(sideSeg) sideSeg.addEventListener('click',function(e){ var t=e.target.closest('button'); if(!t) return; [].forEach.call(sideSeg.querySelectorAll('button'),function(x){x.classList.remove('on');}); t.classList.add('on'); window._vSide=t.getAttribute('data-side'); var pw=$('vProofWrap'), sv=$('vSend'), wm=$('vMsg'); var loser=(window._vSide==='loser'); if(pw) pw.hidden=loser; if(sv) sv.textContent = loser?'تأكيد الخسارة':'رفع النتيجة للمراجعة'; if(wm){ wm.style.color='var(--tx3)'; wm.textContent = loser ? 'اكتب النتيجة الصحيحة — لو خالفت ما وثّقه خصمك دخل المشرف وحسم بالإثبات.' : ''; } }); var fb=$('vFileBtn'), fi=$('vFile'), msg=$('vMsg'); var paint=function(){ var l=$('vList'); if(l) l.innerHTML=window._vp.map(function(u,i){ var isVid=/\.(mp4|mov|webm|avi)(\?|$)/i.test(u); return ''+ (isVid ? '' : '')+ ''; }).join(''); if(fb) fb.textContent='📸 أضف صورة إثبات ('+window._vp.length+'/8)'; }; if(fb&&fi){ fb.addEventListener('click',function(){ fi.click(); }); fi.addEventListener('change',function(){ var f=fi.files&&fi.files[0]; if(!f) return; if(window._vp.length>=8){ msg.style.color='var(--gold)'; msg.textContent='وصلت الحد الأقصى 8 صور'; fi.value=''; return; } msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري الضغط...'; window.upProof(f, chId, function(pct){ msg.style.color='var(--tx3)'; msg.textContent='⏳ جاري الرفع… '+Math.round(pct*100)+'%'; }).then(function(d){ fi.value=''; if(!d.ok){ msg.style.color='var(--red)'; msg.textContent='تعذّر الرفع: '+d.why; return; } window._vp.push(d.url); paint(); msg.style.color='#3ddc84'; msg.textContent='✅ أُضيفت ('+window._vp.length+'/8)'; }); }); } box.addEventListener('click',function(e){ var d=e.target&&e.target.getAttribute&&e.target.getAttribute('data-vdel'); if(d===null||d===undefined) return; window._vp.splice(parseInt(d,10),1); paint(); }); var snd=$('vSend'); if(snd) snd.addEventListener('click',function(){ var side=window._vSide||'winner'; if(side==='winner' && !window._vp.length){ msg.style.color='var(--red)'; msg.textContent='📎 توثيق الفوز يحتاج صورة إثبات واحدة على الأقل'; return; } var vr=parseInt((($('vScoreR')||{}).value||'').replace(/[^0-9]/g,''),10); var vl=parseInt((($('vScoreL')||{}).value||'').replace(/[^0-9]/g,''),10); if(isNaN(vr)||isNaN(vl)){ msg.style.color='var(--red)'; msg.textContent='اكتب نتيجة كل كلان في خانته'; return; } /* score_a لصاحب الطلب و score_b لخصمه — تُنسب بالهوية لا بالموضع */ var a=(R===c.challenger_clan)?vr:vl, b=(R===c.challenger_clan)?vl:vr; var myScore=vr, oppScore=vl; /* R هو كلاني دائماً في هذي الشاشة */ if(side==='winner' && myScore<=oppScore){ msg.style.color='var(--red)'; msg.textContent='⚠️ توثّق فوزاً ونتيجتك أقل — راجع الخانتين'; return; } if(side==='loser' && myScore>=oppScore){ msg.style.color='var(--red)'; msg.textContent='⚠️ توثّق خسارة ونتيجتك أعلى — راجع الخانتين'; return; } snd.disabled=true; msg.style.color='var(--gold)'; msg.textContent='...رفع'; sb.rpc('submit_result',{p_ch:chId, p_side:side, p_score_a:a, p_score_b:b, p_proof:window._vp.join(',')||null}).then(function(r){ snd.disabled=false; var d=r&&r.data; if(r&&r.error){ msg.style.color='var(--red)'; msg.textContent=errAr(r.error.message); return; } if(d&&d.ok===false){ var W={auth:'سجّل دخولك',not_found:'التحدي غير موجود',not_live:'التحدي غير جارٍ', not_leader:'🔒 التوثيق لرئيس الكلان ونائبه',bad_side:'اختيار غير صالح', need_proof:'📎 توثيق الفوز يحتاج إثباتاً',already:'وثّق كلانك هذي المباراة من قبل'}; msg.style.color='var(--red)'; msg.textContent=W[d.why]||'تعذّر التوثيق'; return; } var S={matched:'✅ تطابقت مع نتيجة خصمك — بانتظار اعتماد المشرف', disputed:'⚠️ اختلفت عن نتيجة خصمك — المشرف بيحسمها في الغرفة', await_other:(side==='winner' ? '✅ وُثّقت — تقدر تلعب مصنّفاً جديداً الآن، وننتظر تأكيد خصمك' : '✅ وُثّقت خسارتك — تقدر تلعب مصنّفاً جديداً الآن')}; msg.style.color=(d&&d.state==='disputed')?'var(--gold)':'#3ddc84'; msg.textContent=S[(d&&d.state)||'await_other']; window._vp=[]; paint(); setTimeout(function(){ box.hidden=true; box.innerHTML=''; }, 2600); }); }); box.scrollIntoView({behavior:'smooth',block:'center'}); }; /* عرض الصور داخل التطبيق */ window.isVid=function(u){ return /\.(mp4|mov|webm|avi|m4v)(\?|$)/i.test(String(u||'')); }; /* ═══ قراءة النتيجة ═══ اللاعب يكتبها كما اعتادها: 4-0 · 4 0 · 4VS0 · 4:0 · ٤-٠ — كلها تُقبل. ما يهمّنا الرقمان لا ما بينهما. */ window.parseScore=function(v){ var t=String(v||''); /* الأرقام العربية والهندية تُوحَّد أولاً */ t=t.replace(/[٠-٩]/g,function(d){ return String('٠١٢٣٤٥٦٧٨٩'.indexOf(d)); }) .replace(/[۰-۹]/g,function(d){ return String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)); }); var m=t.match(/(\d{1,3})\s*\D+\s*(\d{1,3})/); if(!m) return null; var a=parseInt(m[1],10), b=parseInt(m[2],10); if(isNaN(a)||isNaN(b)) return null; return [a,b]; }; /* زر تحميل الدليل — يظهر للمشرف وحده، والخادم يتحقق من الرتبة أيضاً */ window.dlBtn=function(u){ if(!window._isStaff || !/\/media\//.test(String(u||''))) return ''; return ''; }; /* تحميل الدليل لطاقم الإشراف وحده — لعدّ ثواني الزوم بدقّة. العرض يبقى للطرفين، والتنزيل محروس بالخادم لا بالواجهة فقط. */ /* ═══ حفظ الدليل ═══ على الجوال لا يصل الملف للألبوم إلا عبر ورقة المشاركة (حفظ الفيديو/الصورة). فنجلب الملف بالتوكن ثم نعرض الورقة؛ وعلى الحاسب نُنزّله مباشرة. والملف يبقى محفوظاً بالذاكرة كي تكون الضغطة الثانية فورية — لأن سفاري يشترط أن تخرج الورقة من ضغطة المستخدم لا من انتظار الشبكة. */ window._evBlob=null; window._evKey=''; function evSave(blob, name){ var f=null; try{ f=new File([blob], name, {type:blob.type||'application/octet-stream'}); }catch(e){} if(f && navigator.share && navigator.canShare && navigator.canShare({files:[f]})){ return navigator.share({files:[f], title:name}).then(function(){ return 'shared'; }, function(e){ if(e && e.name==='AbortError') return 'cancel'; evDownload(blob,name); return 'dl'; }); } evDownload(blob, name); return Promise.resolve('dl'); } function evDownload(blob, name){ var u=URL.createObjectURL(blob), a=document.createElement('a'); a.href=u; a.download=name; a.rel='noopener'; document.body.appendChild(a); a.click(); setTimeout(function(){ a.remove(); URL.revokeObjectURL(u); }, 4000); } window.dlEvidence=function(url){ var u=url||window._viewMedia; if(!u) return; if(!window._isStaff){ toast('تحميل الأدلة لطاقم الإشراف فقط'); return; } var m=/\/media\/(.+)$/.exec(String(u).split('?')[0]); if(!m){ toast('تعذّر التحميل'); return; } var rel=m[1], name='codar-'+rel.split('/').pop(); /* الملف جاهز من ضغطة سابقة — الورقة تفتح فوراً وسفاري يقبلها */ if(window._evKey===rel && window._evBlob) return evSave(window._evBlob, name); toast('⏳ نجهّز الملف...'); sb.auth.getSession().then(function(sr){ var tok=sr&&sr.data&&sr.data.session&&sr.data.session.access_token; if(!tok){ toast('سجّل دخولك'); return; } fetch('/api/dl?f='+encodeURIComponent(rel)+'&t='+encodeURIComponent(tok)).then(function(r){ if(!r.ok) return r.json().then(function(j){ throw new Error(j.error||'تعذّر التحميل'); }); return r.blob(); }).then(function(b){ window._evBlob=b; window._evKey=rel; return evSave(b, name).then(function(how){ if(how==='dl') toast('✅ نُزّل الملف'); else if(how!=='cancel') toast('✅ اختر «حفظ الفيديو» ليدخل الألبوم'); }); }).catch(function(e){ /* سفاري يرفض الورقة إذا طالت الشبكة — الملف صار بالذاكرة فالضغطة الثانية تنجح */ if(e && e.name==='NotAllowedError') toast('اضغط «حمّل الدليل» مرة ثانية — الملف جاهز'); else toast(e.message||'تعذّر التحميل'); }); }); }; window.openImg=function(u){ var b=$('imgView'), i=$('imgViewSrc'), v=$('vidViewSrc'), a=$('imgViewDl'); if(!b) return; window._viewMedia=u; /* الزر للمشرفين فقط */ if(a) a.hidden = !window._isStaff; if(window.isVid(u)){ /* المقطع بملء الشاشة — الخصم يستحق يشوف الدليل واضحاً */ if(i){ i.hidden=true; i.src=''; } if(v){ v.hidden=false; v.src=u; try{ v.play(); }catch(e){} } }else{ if(v){ v.hidden=true; try{ v.pause(); }catch(e){} v.removeAttribute('src'); v.load&&v.load(); } if(i){ i.hidden=false; i.src=u; } } b.hidden=false; document.body.style.overflow='hidden'; }; window.closeImg=function(){ var b=$('imgView'), i=$('imgViewSrc'), v=$('vidViewSrc'); if(b) b.hidden=true; if(i){ i.src=''; i.hidden=false; } if(v){ try{ v.pause(); }catch(e){} v.removeAttribute('src'); v.hidden=true; } document.body.style.overflow=''; }; document.addEventListener('click', function(e){ var t=e.target; /* closest لأن شارة ▶ تغطّي المصغّرة — الضغطة تصل الغلاف مهما كانت الطبقة */ var box=(t&&t.closest)?t.closest('.ccimg'):null; if(box){ e.preventDefault(); window.openImg(box.getAttribute('data-full')||box.src); return; } var dl=(t&&t.closest)?t.closest('[data-dl]'):null; if(dl){ e.preventDefault(); e.stopPropagation(); window.dlEvidence(dl.getAttribute('data-dl')); } }); document.addEventListener('keydown', function(e){ if(e.key==='Escape') window.closeImg(); }); /* ═══ ورقة أفعال صفحة التحدي ═══ زر + يجمعها بدل صفٍّ من الأزرار */ window.ccSheetClose=function(){ var e=document.getElementById('ccSheet'); if(e) e.hidden=true; }; (function(){ var plus=document.getElementById('ccPlus'); if(!plus) return; plus.addEventListener('click', function(){ var sh=document.getElementById('ccSheet'), box=document.getElementById('ccSheetActs'); var src=document.getElementById('ccActs'); if(!sh||!box||!src) return; var ICON={'وثّق':'📸','بلاغ':'🚩','مشرف':'🛡️','انسحاب':'🏳️','كلان':'🛡️','غرفة':'💬'}; var h=''; [].forEach.call(src.querySelectorAll('button'),function(b,i){ var t=(b.textContent||'').trim(); var ic='⚡'; Object.keys(ICON).forEach(function(k){ if(t.indexOf(k)!==-1) ic=ICON[k]; }); h+=''; }); h+=''+ ''; box.innerHTML=h; sh.hidden=false; }); document.addEventListener('click', function(e){ var sh=document.getElementById('ccSheet'); if(sh && e.target===sh){ sh.hidden=true; return; } var r=e.target&&e.target.closest?e.target.closest('[data-ccact],[data-ccx]'):null; if(!r) return; var i=r.getAttribute('data-ccact'), x=r.getAttribute('data-ccx'); if(i!==null && i!==undefined){ var b=document.querySelectorAll('#ccActs button')[parseInt(i,10)]; window.ccSheetClose(); if(b) b.click(); } else if(x==='img'){ window.ccSheetClose(); var a=document.getElementById('ccAttach'); if(a) a.click(); } else if(x==='mic'){ window.ccSheetClose(); var m=document.getElementById('ccMic'); if(m) m.click(); } }); })(); /* ═══ التحديات الجارية الآن — لوحة عامّة ═══ كل مباراةٍ قائمةٍ بين كلانين تظهر للجميع: CLAN س ضد CLAN ص، ووسمُ «جاري الآن» ينبض، وعدّادٌ يحسب منذ لحظة القبول (accepted_at) — يتجدّد كل ثانية بلا نداءٍ للقاعدة، فلا كلفة على الخادم. */ window._liveTick=null; function liveElapsed(fromISO){ var s=Math.max(0, Math.floor((Date.now()-new Date(fromISO).getTime())/1000)); var h=Math.floor(s/3600), m=Math.floor((s%3600)/60), ss=s%60; var p=function(n){ return (n<10?'0':'')+n; }; return h>0 ? (h+':'+p(m)+':'+p(ss)) : (p(m)+':'+p(ss)); } window.renderLiveCards=function(){ var box=$('chLive'); if(!box) return; var rows=window._liveRows||[]; if(!rows.length){ box.innerHTML='
ما فيه تحديات جارية الآن — تابع «🔥 المتاحة» وكن أول من يطلق ⚡
'; return; } var mm=window._clanMap||{}; box.innerHTML=rows.map(function(c){ var a=(mm[c.challenger_clan]||{}).tag||'؟', b=(mm[c.opponent_clan]||{}).tag||'؟'; var from=c.accepted_at||c.created_at; var meta=typeBadge(c.playstyle==='rush'?'rush':'sniper')+' '+rankBadge(c.ranked); return '
'+ '
'+ 'CLAN '+esc(String(a))+''+ 'VS'+ 'CLAN '+esc(String(b))+''+ '🔴 جاري الآن'+ '
'+ '
'+meta+ ' · ⏱️ منذ '+ ''+liveElapsed(from)+''+ '
'+ '
'; }).join(''); }; /* ═══ عرض التحدي الجاري العامّ ═══ الضغط على بطاقةٍ جارية كان يفتح غرفة التحدي للجميع — وهي محميّةٌ لطرفَيها والطاقم وحدهم. الآن يفتح عرضاً عامّاً: رئيسا الكلانين، وقت البداية، إحصاءاتهما، وتأثير VS متحرّك — وزرّ دخول الغرفة يظهر لأهلها فقط (طرفٌ في التحدي أو مشرف). */ window.closeLiveShowcase=function(){ ['lsBack','lsCard'].forEach(function(id){ var e=document.getElementById(id); if(e) e.remove(); }); if(window._lsTick){ clearInterval(window._lsTick); window._lsTick=null; } document.body.style.overflow=''; }; window.openLiveShowcase=function(cid){ if(!cid) return; var row=(window._liveRows||[]).filter(function(x){return x.id===cid;})[0]; if(!row) return; window.closeLiveShowcase(); var bk=document.createElement('div'); bk.id='lsBack'; bk.style.cssText='position:fixed;inset:0;background:rgba(3,5,8,.86);backdrop-filter:blur(4px);z-index:240'; bk.onclick=window.closeLiveShowcase; var cd=document.createElement('div'); cd.id='lsCard'; cd.style.cssText='position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:241;width:min(92vw,420px);'+ 'background:linear-gradient(180deg,#0d1016,#070a0e);border:1px solid #1c2230;border-radius:20px;padding:20px 18px;'+ 'box-shadow:0 24px 60px rgba(0,0,0,.6)'; cd.innerHTML='
⏳ جاري التحميل...
'; document.body.appendChild(bk); document.body.appendChild(cd); document.body.style.overflow='hidden'; var from=row.accepted_at||row.created_at; var ids=[row.challenger_clan,row.opponent_clan]; Promise.all([ sb.from('clans').select('id,tag,points,wins,losses,streak,type').in('id',ids), sb.from('clan_members').select('clan_id,member_name,psn,role,profiles(username)').in('clan_id',ids).eq('role','leader') ]).then(function(rs){ var cm={}; ((rs[0]&&rs[0].data)||[]).forEach(function(c){ cm[c.id]=c; }); var lead={}; ((rs[1]&&rs[1].data)||[]).forEach(function(m){ lead[m.clan_id]=m.member_name||(m.profiles&&m.profiles.username)||''; }); var A=cm[row.challenger_clan]||{tag:'؟'}, B=cm[row.opponent_clan]||{tag:'؟'}; var side=function(c,ldr,cls){ var wl=(c.wins||0)+'–'+(c.losses||0); var fire=(c.streak&&c.streak>=2)?' 🔥'+c.streak+'':''; return '
'+ '
'+esc(String(c.tag||'?').slice(0,4))+'
'+ 'CLAN '+esc(c.tag||'?')+''+ '
👑 '+esc(ldr||'—')+'
'+ '
'+ '
🏅 '+(c.points||0)+' نقطة
'+ '
⚔️ '+wl+''+fire+'
'+ '
'; }; var meta=typeBadge(row.playstyle==='rush'?'rush':'sniper')+' '+rankBadge(row.ranked); /* هل لهذا المستخدم حقّ دخول الغرفة؟ طرفٌ فيها أو مشرف */ var mine=window._myIds||[]; var canEnter=window._isStaff || mine.indexOf(row.challenger_clan)!==-1 || mine.indexOf(row.opponent_clan)!==-1; cd.innerHTML= '
'+ '🔴 جاري الآن'+ ''+ '
'+ '
'+ side(A,lead[row.challenger_clan],'a')+ '
VS
'+ side(B,lead[row.opponent_clan],'b')+ '
'+ '
'+meta+'
'+ '
⏱️ منذ بداية التحدي: '+ ''+liveElapsed(from)+'
'+ (canEnter ? '' : '
🔒 غرفة التحدي خاصّة بالكلانين والطاقم — وأنت تشاهد من الخارج 👀
')+ '
'; /* عدّاد حيّ داخل العرض */ window._lsTick=setInterval(function(){ var el=cd.querySelector('.liveclock'); if(el) el.textContent=liveElapsed(el.getAttribute('data-from')); },1000); },function(){ cd.innerHTML='
تعذّر التحميل
'; }); }; function liveTickAll(){ if(document.hidden) return; var sec=document.getElementById('secLive'); if(sec && sec.classList.contains('fhide')) return; /* القسم مخفيّ — لا تشغيل */ var scr=document.getElementById('challenges'); if(!scr || !scr.classList.contains('active')) return; /* لسنا في التحديات */ var els=document.querySelectorAll('#chLive .liveclock'); if(!els.length) return; [].forEach.call(els,function(el){ el.textContent=liveElapsed(el.getAttribute('data-from')); }); } window.loadLiveChallenges=function(){ var box=$('chLive'); if(!box) return; if(!window._liveRows) box.innerHTML='
⏳ جاري التحميل...
'; /* نحتاج خريطة الكلانات للأسماء — إن غابت نجلبها */ var ensureClans = (window._clanMap && Object.keys(window._clanMap).length) ? Promise.resolve() : sb.from('clans').select('id,tag,type').then(function(r){ var m={}; (r.data||[]).forEach(function(c){m[c.id]=c;}); window._clanMap=Object.assign(window._clanMap||{},m); }); ensureClans.then(function(){ sb.from('challenges').select('id,challenger_clan,opponent_clan,playstyle,ranked,status,created_at,accepted_at') .in('status',['accepted','live']).order('accepted_at',{ascending:false,nullsFirst:false}) .then(function(r){ window._liveRows=(r.data||[]).filter(function(c){ return c.challenger_clan && c.opponent_clan; }); var n=window._liveRows.length; var b=$('chLiveN'); if(b) b.textContent = n?('· '+n):''; var chip=document.querySelector('#chChips button[data-f="live"]'); if(chip){ chip.style.borderColor = n?'rgba(255,60,60,.55)':''; chip.style.color = n?'var(--red)':''; } window.renderLiveCards(); /* عدّادٌ واحد يحرّك كل البطاقات — يعمل ما دام القسم ظاهراً */ if(!window._liveTick) window._liveTick=setInterval(liveTickAll,1000); },function(){ if(!window._liveRows) box.innerHTML='
تعذّر التحميل
'; }); }); }; /* ═══ حبوب التحديات ═══ تُظهر قسماً وتُخفي البقية */ window.chFilterTo=function(f){ /* «يحتاج ردّك» يجمع ما تحدّاك مباشرةً وما تطوّع لطلبك — كلاهما ينتظر قرارك */ var M={ live:['secLive','chLive'], open:['secOpen','chOpen'], need:['secDirected','chDirected','bidsSec','chBids'], mine:['secMine','chFilter','chMine'] }; if(f==='live' && window.loadLiveChallenges) window.loadLiveChallenges(); Object.keys(M).forEach(function(k){ M[k].forEach(function(id){ var e=document.getElementById(id); if(!e) return; e.classList.toggle('fhide', k!==f); }); }); /* والحبّة تُضيء مع قائمتها — وإلا بدا الترشيح معطّلاً */ try{ [].forEach.call(document.querySelectorAll('#chChips button'),function(b){ b.classList.toggle('on', b.getAttribute('data-f')===f); }); }catch(e){} }; /* ═══ عدّاد تحدياتي القائمة ═══ التحدي النشط شأن كل أعضاء الكلان — فيظهر رقمه على الحبّة ليعرف الجميع أن لهم مباراة قائمة بلا أن يفتحوا القائمة. */ window.chCountMine=function(){ var ids=(window._myIds&&window._myIds.length)?window._myIds :(window._myClanId?[window._myClanId]:[]); var chip=document.querySelector('#chChips button[data-f="mine"]'); var b=document.getElementById('chMineN'); if(!ids.length){ if(b) b.textContent=''; if(chip){ chip.style.borderColor=''; chip.style.color=''; } return 0; } var f='('+ids.map(function(i){ return 'challenger_clan.eq.'+i+',opponent_clan.eq.'+i; }).join(',')+')'; sb.from('challenges').select('id',{count:'exact',head:true}) .or(f.slice(1,-1)) .in('status',['open','pending','accepted','live']) .then(function(r){ var n=(r&&r.count)||0; if(b) b.textContent = n ? ('· '+n) : ''; if(chip){ chip.style.borderColor = n ? 'rgba(232,179,75,.55)' : ''; chip.style.color = n ? 'var(--gold)' : ''; } },function(){}); }; /* عدّاد ما ينتظر ردّك — شارةٌ حمراء تلفت لِما يحتاج قرارك */ function setChBadge(spanId, n){ var b=document.getElementById(spanId); if(!b) return; if(n>0){ b.textContent=n>99?'99+':String(n); b.hidden=false; } else { b.textContent=''; b.hidden=true; } } window.chCountNeed=function(){ var n=0; ['chDirected','chBids'].forEach(function(id){ var box=document.getElementById(id); if(!box) return; [].forEach.call(box.children,function(c){ var t=(c.getAttribute('style')||''); if(t.indexOf('text-align:center')===-1) n++; /* رسالة الفراغ لا تُعدّ */ }); }); setChBadge('chNeedN', n); return n; }; /* ═══ عدّاد الجارية ═══ رقمٌ أحمر على «الجارية» بعدد المباريات القائمة الآن */ window.chCountLive=function(){ sb.from('challenges').select('id',{count:'exact',head:true}) .in('status',['accepted','live']) .not('challenger_clan','is',null).not('opponent_clan','is',null) .then(function(r){ setChBadge('chLiveN',(r&&r.count)||0); },function(){}); }; /* ═══ عدّاد المتاحة ═══ كإشعار الدردشة: يعدّ الطلبات الجديدة التي لم ترها بعد. «رأيتَها» = آخر مرّة فتحتَ فيها قسم «المتاحة». نحفظ الوقت محلياً. */ function openSeenTS(){ return parseInt(localStorage.getItem('chOpenSeen')||'0',10)||0; } window.chCountOpen=function(){ var seen=openSeenTS(); sb.from('challenges').select('created_at,challenger_clan') .is('opponent_clan',null).eq('status','open') .order('created_at',{ascending:false}).limit(40) .then(function(r){ var mine=window._myIds||[]; var rows=(r.data||[]).filter(function(c){ return mine.indexOf(c.challenger_clan)===-1; }); var fresh=rows.filter(function(c){ return new Date(c.created_at).getTime() > seen; }).length; setChBadge('chOpenN', fresh); },function(){}); }; /* فتحُ قسمٍ يمسح شارته — كفتح محادثة يمسح غير المقروء */ window.chMarkSeen=function(f){ if(f==='open'){ try{ localStorage.setItem('chOpenSeen', String(Date.now())); }catch(e){} setChBadge('chOpenN',0); } if(f==='need'){ setChBadge('chNeedN',0); } if(f==='live'){ setChBadge('chLiveN',0); } }; document.addEventListener('click', function(e){ var b=e.target&&e.target.closest?e.target.closest('#chChips button'):null; if(!b) return; [].forEach.call(b.parentNode.children,function(x){ x.classList.remove('on'); }); b.classList.add('on'); var f=b.getAttribute('data-f'); window.chFilterTo(f); if(window.chMarkSeen) window.chMarkSeen(f); }); /* ═══ حبوب تصفّح الكلانات ═══ تقود القوائم المخفية */ document.addEventListener('click', function(e){ var b=e.target&&e.target.closest?e.target.closest('#cdirChips button'):null; if(!b) return; var k=b.getAttribute('data-k'), v=b.getAttribute('data-v'); /* ═══ حبّة واحدة تحمل اتجاهين ═══ ضغطةٌ ثانية على «الأحدث» تقلب القائمة إلى «الأقدم» والعكس — فيرفع العضو القائمة وينزّلها بزرٍّ واحد بدل حبّتين. */ if(b.id==='cdirAge' && b.classList.contains('on')){ v = (v==='new') ? 'old' : 'new'; b.setAttribute('data-v', v); b.textContent = (v==='new') ? '🆕 الأحدث' : '🕰️ الأقدم'; } var peers=[].filter.call(b.parentNode.children,function(x){ return x.getAttribute('data-k')===k; }); peers.forEach(function(x){ x.classList.remove('on'); }); b.classList.add('on'); if(k==='filter'){ var f=document.getElementById('cdirFilter'); if(f){ f.value=v; window._cdir.filter=v; } } else { var so=document.getElementById('cdirSort'); if(so){ so.value=v; window._cdir.sort=v; if(v==='random') window._cdir.seed=Math.random(); } } if(window.renderClanDir) window.renderClanDir(); }); /* ═══ أزرار الخصوصية المقسّمة ═══ تُحفظ فور الضغط */ document.addEventListener('click', function(e){ var b=e.target&&e.target.closest?e.target.closest('.pillx button'):null; if(!b) return; var host=b.parentNode, sel=document.getElementById(host.getAttribute('data-for')); if(!sel) return; [].forEach.call(host.children,function(x){ x.classList.toggle('on', x===b); }); sel.value=b.getAttribute('data-v'); var sv=document.getElementById('stSavePriv'); if(sv) sv.click(); var m=document.getElementById('stPrivMsg'); if(m){ m.style.color='#3ddc84'; m.textContent='✅ انحفظ'; setTimeout(function(){ m.style.color=''; m.textContent='يُحفظ فور اختيارك — بلا زر حفظ.'; }, 1800); } }); /* مزامنة الحبوب مع القيم المحفوظة */ window.syncPills=function(){ [].forEach.call(document.querySelectorAll('.pillx'),function(h){ var sel=document.getElementById(h.getAttribute('data-for')); if(!sel) return; [].forEach.call(h.children,function(x){ x.classList.toggle('on', x.getAttribute('data-v')===sel.value); }); }); }; /* ═══ الصفوف القابلة للطيّ ═══ صفٌّ واحد مفتوح في المرة */ document.addEventListener('click', function(e){ var r=e.target&&e.target.closest?e.target.closest('.lrow[data-open]'):null; if(!r) return; var id=r.getAttribute('data-open'), body=document.getElementById(id); if(!body) return; var opening=body.hidden; var host=r.closest('.rows'); if(host) [].forEach.call(host.querySelectorAll('.lbody'),function(b){ b.hidden=true; var t=host.querySelector('.lrow[data-open="'+b.id+'"]'); if(t) t.classList.remove('open'); }); body.hidden=!opening; r.classList.toggle('open', opening); if(opening) setTimeout(function(){ body.scrollIntoView({behavior:'smooth',block:'nearest'}); }, 60); }); /* زر الخروج الجديد يستدعي القديم */ (function(){ var lr=document.getElementById('acLogoutRow'); if(lr) lr.addEventListener('click',function(){ var b=document.getElementById('btnLogout'); if(b) b.click(); }); })(); /* ═══ مرفقات غرفة التحدي ═══ */ function ccMsg(t, col){ var e=$('ccUpMsg'); if(e){ e.style.color=col||'var(--gold)'; e.textContent=t||''; } } /* الصور تُضغط قبل الرفع — التسجيلات تُرفع كما هي */ function ccShrink(file){ return new Promise(function(res){ if(!/^image\//.test(file.type)) return res(file); var img=new Image(), u=URL.createObjectURL(file); img.onload=function(){ var mx=1280, w=img.width, h=img.height; if(w>mx||h>mx){ if(w>h){ h=Math.round(h*mx/w); w=mx; } else { w=Math.round(w*mx/h); h=mx; } } var cv=document.createElement('canvas'); cv.width=w; cv.height=h; cv.getContext('2d').drawImage(img,0,0,w,h); cv.toBlob(function(b){ URL.revokeObjectURL(u); res(b||file); },'image/jpeg',0.82); }; img.onerror=function(){ URL.revokeObjectURL(u); res(file); }; img.src=u; }); } window.ccUpload=function(blob, kind, dur){ var chId=window._cc.id; if(!chId||!blob) return; ccMsg('⏳ جاري الرفع...'); sb.auth.getSession().then(function(sr){ var tok=sr&&sr.data&&sr.data.session&&sr.data.session.access_token; if(!tok){ ccMsg('سجّل دخولك','var(--red)'); return; } /* نُخبرُ الخادمَ بما اختارَه المستخدم: حاويةُ mp4 واحدةٌ للمقطعِ وللتسجيلِ الصوتيّ، والبصمةُ وحدَها لا تفرّق. */ return window.upXHR({'Authorization':'Bearer '+tok,'x-codar-kind':'chat','x-codar-ch':chId, 'x-codar-media':(kind==='audio'?'audio':kind==='video'?'video':'image')}, blob, function(p){ ccMsg('⏳ جاري الرفع… '+Math.round(p*100)+'%'); }) .then(function(x){ if(!x.ok||!x.j||!x.j.url){ ccMsg((x.j&&x.j.error)||'تعذّر الرفع','var(--red)'); return; } var row={challenge_id:chId, user_id:window._uid, clan_id:window._cc.myClan||null, username:(window._myUsername||'لاعب'), body:(kind==='audio'?'🎙️ تسجيل صوتي':kind==='video'?'🎬 مقطع':'📎 صورة'), kind:kind, media_url:x.j.url}; if(dur) row.dur=dur; return sb.from('challenge_chat').insert(row).then(function(ir){ if(ir&&ir.error){ ccMsg('تعذّر الإرسال: '+ir.error.message,'var(--red)'); return; } ccMsg(''); window.loadChChat(true); }); }); }).catch(function(){ ccMsg('تعذّر الاتصال','var(--red)'); }); }; (function(){ var afi=$('ccAudioFile'); if(afi) afi.addEventListener('change',function(){ var f=afi.files&&afi.files[0]; if(!f) return; if(f.size>4*1024*1024){ ccMsg('⚠️ التسجيل كبير (الحد 4MB)','var(--red)'); afi.value=''; return; } window.ccUpload(f,'audio'); afi.value=''; }); var fb=$('ccAttach'), fi=$('ccFile'); if(fb&&fi){ fb.addEventListener('click',function(){ fi.click(); }); fi.addEventListener('change',function(){ var f=fi.files&&fi.files[0]; if(!f) return; /* ═══ المقطعُ دليلٌ كالصورة ═══ كانت الغرفةُ تقبلُ الصورَ والتسجيلاتِ وحدَها، والخادمُ يقبلُ المقاطعَ منذُ البداية — فكان النزاعُ على «إطالةِ الزوم» لا يُحسَمُ في غرفتِه لأن الدليلَ نفسَه لا يدخل. صار يدخل، وسقفُه سقفُ الخادمِ نفسُه (٣٠MB) لا سقفاً أضيقَ يردُّ الناسَ بلا سبب. */ var vid=/^video\//.test(f.type||'')||/\.(mp4|mov|webm|m4v)$/i.test(f.name||''); if(vid){ if(f.size>30*1024*1024){ ccMsg('⚠️ المقطع أكبر من 30MB — قصّه أو أرسله كدليل في البلاغ','var(--red)'); fi.value=''; return; } window.ccUpload(f,'video'); fi.value=''; return; } if(f.size>8*1024*1024){ ccMsg('⚠️ الصورة كبيرة جداً','var(--red)'); fi.value=''; return; } ccShrink(f).then(function(b){ window.ccUpload(b,'image'); fi.value=''; }); }); } /* التسجيل الصوتي — MediaRecorder مدعوم بسفاري iOS 14.3+ */ var rec=null, chunks=[], t0=0, tick=null; function stopTick(){ if(tick){ clearInterval(tick); tick=null; } } function showRec(on){ var b=$('ccRec'); if(b) b.hidden=!on; var m=$('ccMic'); if(m) m.style.opacity = on?'.4':'1'; } function endRec(send){ stopTick(); if(!rec) return; rec._send=send; try{ if(rec.state!=='inactive') rec.stop(); }catch(e){} } /* بديل مضمون: مسجّل النظام — يعمل حتى لو مُنع المايك داخل التطبيق */ function micFallback(why){ var fi2=$('ccAudioFile'); if(!fi2){ ccMsg(why||'التسجيل غير متاح','var(--red)'); return; } ccMsg((why?why+' — ':'')+'اختر تسجيلاً من جهازك','var(--gold)'); fi2.click(); } var mic=$('ccMic'); if(mic) mic.addEventListener('click',function(){ if(rec){ endRec(true); return; } if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia || !window.MediaRecorder){ return micFallback('جهازك ما يدعم التسجيل المباشر'); } navigator.mediaDevices.getUserMedia({audio:true}).then(function(stream){ var opts={}; ['audio/mp4','audio/webm;codecs=opus','audio/webm','audio/ogg'].some(function(t){ if(window.MediaRecorder.isTypeSupported&&MediaRecorder.isTypeSupported(t)){ opts.mimeType=t; return true; } return false; }); chunks=[]; rec=new MediaRecorder(stream,opts); rec._send=false; rec.ondataavailable=function(e){ if(e.data&&e.data.size) chunks.push(e.data); }; rec.onstop=function(){ stream.getTracks().forEach(function(t){ t.stop(); }); showRec(false); var send=rec._send, dur=Math.round((Date.now()-t0)/1000); rec=null; if(!send){ chunks=[]; ccMsg(''); return; } if(dur<1){ chunks=[]; ccMsg('التسجيل قصير جداً','var(--red)'); return; } var blob=new Blob(chunks,{type:(opts.mimeType||'audio/webm')}); chunks=[]; window.ccUpload(blob,'audio',dur); }; t0=Date.now(); rec.start(); showRec(true); ccMsg(''); tick=setInterval(function(){ var d=Math.floor((Date.now()-t0)/1000); var e=$('ccRecT'); if(e) e.textContent=Math.floor(d/60)+':'+('0'+(d%60)).slice(-2); if(d>=120) endRec(true); // سقف دقيقتان },250); },function(err){ var n=(err&&err.name)||''; /* نسمّي السبب بدل رسالة عامة — يفرق كثيراً بالدعم */ if(n==='NotAllowedError' || n==='SecurityError') ccMsg('🎙️ رفضت إذن المايك — فعّله من إعدادات الجهاز ثم أعد المحاولة','var(--red)'); else if(n==='NotFoundError') micFallback('ما لقيت مايك'); else micFallback('تعذّر التسجيل المباشر ('+(n||'خطأ')+')'); }); }); var st=$('ccRecStop'); if(st) st.addEventListener('click',function(){ endRec(true); }); var cn=$('ccRecCancel'); if(cn) cn.addEventListener('click',function(){ endRec(false); }); })(); /* ═══ استدعاء مشرف للغرفة ═══ */ window.askChatMod=function(chId){ var why=prompt('وش المشكلة؟ (اختياري — تساعد المشرف يفهم بسرعة)'); if(why===null) return; sb.rpc('request_chat_mod',{p_ch:chId, p_reason:why||null}).then(function(r){ var d=r&&r.data; if(r&&r.error){ toast(errAr(r.error.message)); return; } if(d&&d.ok){ toast('🛡️ وصل طلبك لطاقم الإشراف'); window.loadChChat(true); if(window.renderModReq) window.renderModReq(chId); return; } var w=(d&&d.why)||''; toast(w==='already' ? '🛡️ فيه طلب مشرف قائم — انتظروا دخوله' : w==='forbidden' ? 'هذي الغرفة لأعضاء الكلانين' : w==='not_active' ? 'التحدي مو قائماً' : 'تعذّر الطلب'); },function(){ toast('تعذّر الاتصال'); }); }; /* شارة الطلب القائم + زر دخول المشرف */ window.renderModReq=function(chId){ var box=$('ccModBar'); if(!box) return; sb.from('chat_mod_requests').select('id,reason,status,created_at') .eq('challenge_id',chId).eq('status','open').limit(1).then(function(r){ var v=(r.data||[])[0]; if(!v){ box.hidden=true; box.innerHTML=''; return; } box.hidden=false; box.innerHTML='
'+ '🛡️'+ '
طلب مشرف قائم'+ (v.reason?'
'+esc(v.reason)+'':'')+'
'+ (window._isStaff?'':'')+ '
'; },function(){ box.hidden=true; }); }; window.joinAsMod=function(chId){ sb.rpc('close_chat_mod',{p_ch:chId}).then(function(r){ if(r&&r.error){ toast(errAr(r.error.message)); return; } toast('🛡️ دخلت الغرفة كمشرف'); window.loadChChat(true); window.renderModReq(chId); },function(){ toast('تعذّر الاتصال'); }); }; /* ═══ عمر الغرفة عمر المباراة ═══ تُفتح بالقبول وتُقفل بانتهائها. والقاعدة تمنع الكتابة، فيجب أن تختفي خانة الكتابة أيضاً — الخانة التي لا تعمل أسوأ من غيابها. */ window.ccRoomState=function(st){ var live = (st==='accepted' || st==='live'); var paused = (st==='postponed' || st==='postpone_requested' || st==='resume_requested'); var bar = document.getElementById('ccBar') || (document.getElementById('ccInput') ? document.getElementById('ccInput').parentNode : null); var note = document.getElementById('ccClosed'); if(!note && bar && bar.parentNode){ note=document.createElement('div'); note.id='ccClosed'; note.style.cssText='margin:0 14px 12px;background:rgba(255,255,255,.03);border:1px dashed rgba(255,255,255,.14);'+ 'border-radius:12px;padding:12px 13px;font-size:11.5px;line-height:1.9;color:var(--tx3);text-align:center'; bar.parentNode.insertBefore(note, bar); } if(bar) bar.style.display = (live || paused) ? '' : 'none'; if(note){ note.hidden = live; note.innerHTML = paused ? '⏸️ التحدي مؤجَّل — الغرفة لا تزال مفتوحة
يمكنكم التنسيق هنا · اضغط "▶️ طلب الاستئناف" للعودة' : (!live ? '🔒 أُقفلت هذه الغرفة
انتهت المباراة، والمحادثة محفوظة للإشراف.
أي تحدٍّ جديد يفتح غرفةً جديدة.' : ''); } }; /* ═══ اللقبُ يُرسَمُ مرّةً واحدة ═══ `ccRole` تُقدّمُ اللقبَ الشخصيَّ على كلِّ صفةٍ لأنَّ من مُنِحَ لقباً بعينِه قُصِدَ به أن يُرى. **و`fxBadge` تُعيدُ اللقبَ نفسَه بعدَ الاسمِ من المصدرِ نفسِه** — فظهرَ «PRO MODERATOR» مرّتَين في سطرٍ واحد. فمن رُسِمَ لقبُه قبلَ اسمِه لا يُعادُ بعدَه، **ومن لا لقبَ له يبقى ما بعدَ اسمِه كما كان** — فلا تُفقَدُ شارةٌ من أجلِ إصلاحِ تكرار. */ window._ccBadgeShown = function(uid, bdg){ try{ if(window._ccBadge && window._ccBadge[uid] && window._ccBadge[uid].badge_name) return ''; }catch(e){} return bdg || ''; }; window._incomingCallTimeout = null; window._incomingCallChallengeId = null; function playIncomingCallSound(){ try { var ctx = new (window.AudioContext||window.webkitAudioContext)(); [0, 0.35].forEach(function(delay){ var osc = ctx.createOscillator(); var gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = 880; gain.gain.setValueAtTime(0.0001, ctx.currentTime + delay); gain.gain.exponentialRampToValueAtTime(0.15, ctx.currentTime + delay + 0.02); gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + delay + 0.25); osc.start(ctx.currentTime + delay); osc.stop(ctx.currentTime + delay + 0.3); }); } catch(e){} try { if(navigator.vibrate) navigator.vibrate([200,100,200]); } catch(e){} } window.showIncomingCall = function(challengeId, body, avatarUrl){ var modal = $('incomingCallOverlay'); if(!modal) return; if(window._voice && window._voice.ws) return; window._incomingCallChallengeId = challengeId; var bodyEl = $('incomingCallBody'); if(bodyEl) bodyEl.textContent = body; var av = $('incomingCallAvatar'); if(av){ if(avatarUrl){ av.innerHTML = ''; } else { var callerName = (body||'').split(' ')[0] || '؟'; av.innerHTML = esc(callerName.slice(0,2).toUpperCase()); } } modal.hidden = false; playIncomingCallSound(); var cd = $('incomingCallCountdown'); if(cd){ cd.style.animation='none'; void cd.offsetWidth; cd.style.animation='incomingCountdown 30s linear forwards'; } var secLeft = 30; var cdText = $('incomingCallCountdownText'); if(cdText) cdText.textContent = '0:'+(secLeft<10?'0':'')+secLeft; if(window._incomingCallTickInt) clearInterval(window._incomingCallTickInt); window._incomingCallTickInt = setInterval(function(){ secLeft--; if(cdText) cdText.textContent = '0:'+(secLeft<10?'0':'')+Math.max(secLeft,0); if(secLeft <= 0) clearInterval(window._incomingCallTickInt); }, 1000); if(window._incomingCallTimeout) clearTimeout(window._incomingCallTimeout); window._incomingCallTimeout = setTimeout(function(){ window.declineIncomingCall(); }, 30000); }; window.declineIncomingCall = function(){ var modal = $('incomingCallOverlay'); if(modal) modal.hidden = true; if(window._incomingCallTimeout) clearTimeout(window._incomingCallTimeout); window._incomingCallChallengeId = null; }; window.acceptIncomingCall = function(){ var challengeId = window._incomingCallChallengeId; var modal = $('incomingCallOverlay'); if(modal) modal.hidden = true; if(window._incomingCallTimeout) clearTimeout(window._incomingCallTimeout); if(!challengeId) return; if(window.openChChat) window.openChChat(challengeId); setTimeout(function(){ if(window.startVoiceCall) window.startVoiceCall(true); }, 600); }; /* ═══ المكالمة الصوتية لقناة التحدي — نسخة Mesh (تدعم 3 أطراف) ═══ */ window._voice = { peers:{}, ws:null, localStream:null, muted:false, startedAt:null, timerInt:null, speakInt:null, audioCtx:null, localAnalyser:null, myAvatar:'', myName:'' }; window.checkVoiceAccess = async function(){ var btn = $('ccVoiceBtn'); if(!btn || !window._cc || !window._cc.id || !window._uid) { if(btn) btn.hidden = true; return; } try { var roomR = await sb.from('challenges').select('voice_room_active').eq('id',window._cc.id).single(); var roomActive = !!(roomR && roomR.data && roomR.data.voice_room_active); window._voiceRoomActive = roomActive; var sess = await sb.auth.getSession(); var token = sess && sess.data && sess.data.session && sess.data.session.access_token; if(!token) { btn.hidden = true; return; } if(roomActive){ var rJoin = await fetch('https://cod-ar.com/voice-api/turn-credentials', { method:'POST', headers:{ 'Content-Type':'application/json', 'Authorization':'Bearer '+token }, body: JSON.stringify({ challengeId: window._cc.id, intent:'check' }) }); var jJoin = await rJoin.json(); btn.hidden = !(jJoin && jJoin.ok); if(!btn.hidden){ btn.textContent = '🎙️ انضم للغرفة الصوتية'; btn.dataset.mode='join'; try { var rMembers = await fetch('https://cod-ar.com/voice-api/room-members', { method:'POST', headers:{ 'Content-Type':'application/json', 'Authorization':'Bearer '+token }, body: JSON.stringify({ challengeId: window._cc.id }) }); var jMembers = await rMembers.json(); var prevEl = $('ccVoiceMembersPreview'); if(prevEl && jMembers && jMembers.ok && jMembers.members && jMembers.members.length){ var names = jMembers.members.map(function(m){ var roleLabel = m.isStaff ? 'إداري' : (m.role==='leader'?'قائد':(m.role==='vice'?'نائب':(m.role==='coordinator'?'منسّق':''))); return m.name + (roleLabel ? ' ('+roleLabel+')' : ''); }); prevEl.textContent = '🎙️ بالغرفة الآن: ' + names.join('، '); prevEl.hidden = false; } else if(prevEl) { prevEl.hidden = true; } } catch(e){} } } else { var rStart = await fetch('https://cod-ar.com/voice-api/turn-credentials', { method:'POST', headers:{ 'Content-Type':'application/json', 'Authorization':'Bearer '+token }, body: JSON.stringify({ challengeId: window._cc.id, intent:'check-start' }) }); var jStart = await rStart.json(); btn.hidden = !(jStart && jStart.ok); if(!btn.hidden){ btn.textContent = '🎙️ فتح غرفة صوتية'; btn.dataset.mode='start'; } } } catch(e){ btn.hidden = true; } }; function voiceFormatTime(sec){ var m = Math.floor(sec/60), s = sec%60; return (m<10?'0':'')+m+':'+(s<10?'0':'')+s; } function voiceStartTimer(){ if(window._voice.startedAt) return; window._voice.startedAt = Date.now(); var el = $('voiceBarTimer'); window._voice.timerInt = setInterval(function(){ if(!el) return; var sec = Math.floor((Date.now() - window._voice.startedAt)/1000); el.textContent = voiceFormatTime(sec); }, 1000); } function voiceSetupSpeakDetection(stream){ try { if(!window._voice.audioCtx) window._voice.audioCtx = new (window.AudioContext||window.webkitAudioContext)(); var ctx = window._voice.audioCtx; var src = ctx.createMediaStreamSource(stream); var analyser = ctx.createAnalyser(); analyser.fftSize = 256; src.connect(analyser); var data = new Uint8Array(analyser.frequencyBinCount); return { read:function(){ analyser.getByteFrequencyData(data); var sum = 0; for(var i=0;i 14; var speakingNames = []; if(youSpeak) speakingNames.push('أنت'); Object.keys(window._voice.peers).forEach(function(pid){ var p = window._voice.peers[pid]; if(p.analyser && p.analyser.read() > 14) speakingNames.push(p.name || 'مشارك'); var dot = document.getElementById('voiceDot_'+pid); if(dot) dot.style.opacity = (p.analyser && p.analyser.read() > 14) ? '1' : '0.25'; }); var youDot = $('voiceYouDot'); if(youDot) youDot.style.opacity = youSpeak ? '1' : '0.25'; if(speakLabel) speakLabel.textContent = speakingNames.length ? (speakingNames.join('، ') + ' يتحدث') : 'قناة تحدٍّ صوتية'; }, 200); } async function voiceCheckQuality(pc, peerId){ try { var stats = await pc.getStats(); var quality = 'good'; stats.forEach(function(report){ if(report.type === 'candidate-pair' && report.state === 'succeeded'){ var rtt = report.currentRoundTripTime; if(rtt != null){ quality = rtt > 0.3 ? 'bad' : (rtt > 0.12 ? 'ok' : 'good'); } } if(report.type === 'inbound-rtp' && report.kind === 'audio'){ if(report.packetsLost && report.packetsReceived && report.packetsReceived > 0){ var lossRate = report.packetsLost / (report.packetsLost + report.packetsReceived); if(lossRate > 0.08) quality = 'bad'; else if(lossRate > 0.03 && quality === 'good') quality = 'ok'; } } }); if(window._voice.peers[peerId]) window._voice.peers[peerId].quality = quality; var dot = document.getElementById('voiceQuality_'+peerId); if(dot){ var qColor = quality === 'good' ? '#4cc978' : quality === 'ok' ? '#e8b34b' : '#ff6b5e'; var qTitle = quality === 'good' ? 'اتصال ممتاز' : quality === 'ok' ? 'اتصال متوسط' : 'اتصال ضعيف'; dot.style.background = qColor; dot.title = qTitle; } } catch(e){} } function voiceCreatePeerConnection(peerId, name, avatar, creds, isStaff, role, clanTag, platformRole, uid){ var pc = new RTCPeerConnection({ iceServers:[ { urls:'stun:stun.l.google.com:19302' }, { urls:creds.turnUrl, username:creds.username, credential:creds.password } ]}); window._voice.localStream.getTracks().forEach(function(t){ pc.addTrack(t, window._voice.localStream); }); pc.ontrack = function(e){ var audioEl = document.createElement('audio'); audioEl.autoplay = true; audioEl.id = 'voiceAudio_'+peerId; audioEl.srcObject = e.streams[0]; document.body.appendChild(audioEl); window._voice.peers[peerId].analyser = voiceSetupSpeakDetection(e.streams[0]); voiceRenderPeerAvatars(); voiceStartTimer(); voiceStartSpeakLoop(); voiceUpdateStatusText(); var ring = $('voiceConnectingRing'); if(ring) ring.style.display = 'none'; var qInt = setInterval(function(){ if(!window._voice.peers[peerId]){ clearInterval(qInt); return; } voiceCheckQuality(pc, peerId); }, 3000); window._voice.peers[peerId].qualityInt = qInt; }; pc.onicecandidate = function(e){ if(e.candidate && window._voice.ws){ window._voice.ws.send(JSON.stringify({ type:'candidate', to:peerId, candidate:e.candidate })); } }; window._voice.peers[peerId] = { pc:pc, name:name, avatar:avatar, analyser:null, isStaff:!!isStaff, role:role||null, clanTag:clanTag||null, platformRole:platformRole||null, uid:uid||null }; return pc; } function voiceRenderPeerAvatars(){ var list = $('voicePeerAvatars'); if(!list) return; var html = ''; Object.keys(window._voice.peers).forEach(function(pid){ var p = window._voice.peers[pid]; var inner = p.avatar ? '' : (p.name||'؟').slice(0,2); html += '
'+ '
'+inner+'
'+ ''+ '
'; }); list.innerHTML = html; } window.startVoiceCall = async function(joinOnly){ if(!window._cc || !window._cc.id) return; if(window._voice && (window._voice.ws || window._voiceConnecting)){ return; } window._voiceConnecting = true; var contentEl = $('voiceBarContent'); if(contentEl && window._voiceBarOriginalHTML && !$('voiceMyAvatar')){ contentEl.innerHTML = window._voiceBarOriginalHTML; } if(!joinOnly && !window._voiceRoomActive){ var openR = await sb.rpc('open_voice_room', { p_challenge_id: window._cc.id }); if(!openR || !openR.data || !openR.data.ok){ toast('تعذّر فتح الغرفة الصوتية'); return; } window._voiceRoomActive = true; } var bar = $('voiceBar'); if(bar) bar.hidden = false; var statusEl = $('voiceBarStatus'); if(statusEl) statusEl.textContent = 'جارٍ الاتصال...'; try { var sess = await sb.auth.getSession(); var token = sess && sess.data && sess.data.session && sess.data.session.access_token; var myName = (window._profile && window._profile.username) || window._uname || 'أنت'; var myAvatar = ''; try { var profR = await sb.from('profiles').select('avatar_url,username').eq('id',window._uid).single(); if(profR && profR.data){ myAvatar = profR.data.avatar_url || ''; if(profR.data.username) myName = profR.data.username; } } catch(e){} window._voice.myName = myName; window._voice.myAvatar = myAvatar; var myAv = $('voiceMyAvatar'); if(myAv && myAvatar) myAv.innerHTML = ''; var r = await fetch('https://cod-ar.com/voice-api/turn-credentials', { method:'POST', headers:{ 'Content-Type':'application/json', 'Authorization':'Bearer '+token }, body: JSON.stringify({ challengeId: window._cc.id, intent: joinOnly ? 'check' : 'start' }) }); var creds = await r.json(); if(!creds || !creds.ok){ var errMap = { 'بدء المكالمة متاح للمشرفين والإدارة فقط': '🔒 بدء الغرفة الصوتية متاح للمشرفين والإدارة فقط', 'لا تملك صلاحية الدخول لهذه المكالمة': '🔒 ما عندك صلاحية دخول هذي الغرفة', 'سجّل دخولك أولاً': '⚠️ سجّل دخولك أولاً للمتابعة' }; if(statusEl) statusEl.textContent = errMap[creds.error] || creds.error || 'تعذّر الاتصال بخادم الصوت'; return; } try { window._voice.localStream = await navigator.mediaDevices.getUserMedia({ audio:true, video:false }); } catch(micErr){ var micMsg = micErr.name === 'NotAllowedError' ? '🎤 رفضت إذن المايك — فعّله من إعدادات المتصفح' : micErr.name === 'NotFoundError' ? '🎤 ما فيه مايك متوفر بجهازك' : micErr.name === 'NotReadableError' ? '🎤 المايك مستخدم بتطبيق ثاني — أغلقه وحاول مرة ثانية' : '🎤 تعذّر الوصول للمايك'; if(statusEl) statusEl.textContent = micMsg; var bar2 = $('voiceBar'); if(bar2 && !joinOnly) setTimeout(function(){ bar2.hidden = true; }, 3000); return; } window._voice.localAnalyser = voiceSetupSpeakDetection(window._voice.localStream); var ws = new WebSocket('wss://cod-ar.com/voice-ws'); window._voice.ws = ws; window._voiceConnecting = false; window._voice.amStaff = (creds.platformRole === 'admin' || creds.platformRole === 'moderator'); window._voice.myPlatformRole = creds.platformRole || null; window._voice.myRole = creds.clanRole || null; window._voice.myClan = creds.clanTag || null; ws.onopen = function(){ ws.send(JSON.stringify({ type:'join', room: window._cc.id, name: myName, avatar: myAvatar, isStaff: window._voice.amStaff, platformRole: creds.platformRole, role: creds.clanRole, clanTag: creds.clanTag, uid: window._uid })); }; ws.onmessage = async function(ev){ var msg = JSON.parse(ev.data); if(msg.type === 'joined'){ voiceUpdateStatusText(); if(msg.peers && msg.peers.length){ for(var i=0;i