1<!DOCTYPE html> 2<html lang="ar" dir="rtl"> 3 4<head> 5 <meta charset="utf-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> 7 8 9 <title>Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© - ØªÙØ§ØµÙ Ù Ø¹ÙØ§</title> 10 <meta name="title" content="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© - ØªÙØ§ØµÙ Ù Ø¹ÙØ§" /> 11 <meta name="description" content=" ÙÙØ¯Ù اÙÙ Ø¹Ø§ÙØ¬Ø§Øª Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© ÙØ§ÙتØÙÙÙØ§Øª اÙÙ Ø¹Ù ÙØ© اÙ٠رتبطة Ø¨Ø§ÙØ´Ø£Ù اÙÙÙØ³Ø·ÙÙÙ ÙØªØ£Ø«Ùرات٠إÙÙÙÙ ÙØ§ ÙØ¯ÙÙÙØ§ عبر ÙÙØ§Ùب ØªØ±ÙØ² عÙ٠اÙÙ ÙÙÙØ©" /> 12 <meta name="author" content="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©" /> 13 <meta name="keywords" content="Ø´Ø¨ÙØ© ÙØ¯Ø³,ÙÙØ§ÙØ© ÙØ¯Ø³,صØÙÙØ© اÙÙØ¯Ø³,jerusalem,أخبار اÙÙØ¯Ø³,اÙÙØ¯Ø³ عاص٠ة,أخبار,ÙÙØ³Ø·ÙÙ,غزة,اÙÙØ¯Ø³ Ø§ÙØ´Ø±ÙÙØ©,اÙÙØ¯Ø³ Ø§ÙØºØ±Ø¨ÙØ©,ÙØ¯Ø³Ùا,Ø§ÙØ¶ÙØ©,ÙÙØ³Ø¨ÙÙ ÙØ¯Ø³,ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©,خبر,خبر برس,برس,ØµÙØØ© ÙÙØ³Ø¨ÙÙ,أخبار ÙÙØ³Ø¨ÙÙ,Ø£ÙØ¨Ø§Ø¡ ÙØ¯Ø³,Ø£ÙØ±Ø´Ø§ÙÙÙ ,Ø¬ÙØ±ÙزاÙÙÙ ,Ø¥ÙØªØØ§Ù ,Ø¥ÙÙØ¬Ø§Ø±,ÙØµÙ,إختطاÙ,Ø¥Ø¹ØªÙØ§Ù,٠عتÙÙ,Ø£Ø³ÙØ±,أسرÙ,Ø§ÙØ¥ØØªÙاÙ,ÙØ¯Ø³ ÙØª,Ø§ÙØªØ®Ø§Ø¨Ø§Øª,Ø§ÙØªØ®Ø§Ø¨Ø§Øª ÙÙØ³Ø·ÙÙ,ØÙ اس,ÙØªØ,Ø§ÙØ¬Ùاد,Ø³Ø±Ø§ÙØ§,ÙØ³Ø§Ù ,اÙÙØ¯Ø³" /> 14 <meta name="theme-color" content="#2b74bd"> 15 16 17 <meta property="og:type" content="website" /> 18 <meta property="og:site_name" content="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©" /> 19 <meta property="og:title" content="ØªÙØ§ØµÙ Ù Ø¹ÙØ§" /> 20 <meta property="og:description" content="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©Ø Ø¹Ø§Ø¬Ù ÙØªØÙÙÙØ§Øª Ø§ÙØ£Ø®Ø¨Ø§Ø± ٠٠اÙÙØ¯Ø³ ÙÙÙØ³Ø·Ù٠إÙÙ Ø§ÙØ¹Ø§ÙÙ Ø§ÙØ¢Ù | ÙÙØ§ÙØ©Ø ØµØÙÙØ© Ø¥ÙÙØªØ±ÙÙÙØ©Ø Ù ÙÙØ¹Ø ÙÙØ³Ø¨ÙÙØ ÙØª Quds News Network" /> 21 <meta property="og:locale" content="ar_AR" /> 22 <meta property="og:image" content="https://qudsn.co/og/02dbfcf970eda8d246bd275c321840b0407d1f5b.jpg" /> 23 <meta property="og:image:secure_url" content="https://qudsn.co/og/02dbfcf970eda8d246bd275c321840b0407d1f5b.jpg" /> 24 <meta property="og:image:alt" content="ØªÙØ§ØµÙ Ù Ø¹ÙØ§" /> 25 <meta property="og:url" content="https://qudsn.co/contact_us" /> 26 27 28 <meta name="twitter:card" content="summary_large_image" /> 29 <meta name="twitter:title" content="ØªÙØ§ØµÙ Ù Ø¹ÙØ§" /> 30 <meta name="twitter:description" content="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©Ø Ø¹Ø§Ø¬Ù ÙØªØÙÙÙØ§Øª Ø§ÙØ£Ø®Ø¨Ø§Ø± ٠٠اÙÙØ¯Ø³ ÙÙÙØ³Ø·Ù٠إÙÙ Ø§ÙØ¹Ø§ÙÙ Ø§ÙØ¢Ù | ÙÙØ§ÙØ©Ø ØµØÙÙØ© Ø¥ÙÙØªØ±ÙÙÙØ©Ø Ù ÙÙØ¹Ø ÙÙØ³Ø¨ÙÙØ ÙØª Quds News Network" /> 31 <meta name="twitter:image" content="https://qudsn.co/og/02dbfcf970eda8d246bd275c321840b0407d1f5b.jpg" /> 32 <meta name="twitter:image:alt" content="ØªÙØ§ØµÙ Ù Ø¹ÙØ§" /> 33 <meta name="twitter:site" content="@QudsN" /> 34 <meta name="twitter:creator" content="@QudsN" /> 35 36 37 <link rel="canonical" href="https://qudsn.co/contact_us" /> 38 <meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /> 39 40 41 <link rel="alternate" type="application/rss+xml" title="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© RSS" 42 href="https://qudsn.co/rss/category/all" /> 43 44 45
45<script type="application/ld+json"> 46 {"@context":"https://schema.org","@type":"NewsMediaOrganization","name":"Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©","url":"https://qudsn.co","logo":"https://fsn1.your-objectstorage.com/qudsn/uploads/2026/08/file_6a6f0b281cbc8.png"} 47 </script>
47 48
48<script type="application/ld+json"> 49 {"@context":"https://schema.org","@type":"WebSite","name":"Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©","url":"https://qudsn.co","inLanguage":"ar","potentialAction":{"@type":"SearchAction","target":"https://qudsn.co/search_page?search={search_term_string}","query-input":"required name=search_term_string"}} 50 </script>
50 51 52 <link rel="shortcut icon" type="image/png" 53 href="https://fsn1.your-objectstorage.com/qudsn/uploads/2026/08/file_6a6f0b463c00e.ico" /> 54 55 56 57 58 <link rel="preload" href="/assets/main/quds_ar/fonts/ibm-plex-sans-arabic/plex-arabic-400-core.woff2" as="font" type="font/woff2" crossorigin> 59 <link rel="preload" href="/assets/main/quds_ar/fonts/ibm-plex-sans-arabic/plex-arabic-600-core.woff2" as="font" type="font/woff2" crossorigin> 60 <link rel="preload" href="/assets/main/quds_ar/fonts/ibm-plex-sans-arabic/plex-arabic-700-core.woff2" as="font" type="font/woff2" crossorigin> 61 <link rel="preload" href="/assets/main/quds_ar/vendor/fontawesome/webfonts/fa-solid-900-subset.woff2" as="font" type="font/woff2" crossorigin> 62 <link rel="preload" href="/assets/main/quds_ar/vendor/fontawesome/webfonts/fa-regular-400-subset.woff2" as="font" type="font/woff2" crossorigin> 63 64 65 <link rel="stylesheet" data-fa-full data-href="/assets/main/quds_ar/vendor/fontawesome/css/all.min.css?v=1789735121"> 66 <noscript><link rel="stylesheet" href="/assets/main/quds_ar/vendor/fontawesome/css/all.min.css?v=1789735121"></noscript> 67 <link rel="stylesheet" href="/assets/main/quds_ar/vendor/fontawesome/css/fa.subset.css?v=1789735121"> 68 <link rel="stylesheet" href="/assets/main/quds_ar/vendor/swiper/swiper.subset.css?v=1789735121"> 69 <link rel="stylesheet" href="/assets/main/quds_ar/vendor/aos/aos.subset.css?v=1789735121"> 70 71 72 <link rel="stylesheet" href="/assets/main/quds_ar/css/style.css?v=1789735121"> 73 <link rel="stylesheet" href="/assets/main/quds_ar/css/pages.css?v=1787234408"> 74 75 76
76<script> 77 (function () { 78 var root = document.documentElement, t; 79 try { t = localStorage.getItem('qd_theme'); } catch (e) {} 80 if (t !== 'dark' && t !== 'light') { 81 t = (window.matchMedia && matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; 82 } 83 root.setAttribute('data-qtheme', t); 84 })(); 85 </script>
85 86 <link rel="stylesheet" href="/assets/main/quds_ar/css/modes.css?v=1787676073"> 87 88 89 <style> 90 .clamp-2, .clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; } 91 .clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; } 92 .clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; } 93 94 /* live search suggestions (autocomplete dropdown) */ 95 .search-suggest { margin-top: 16px; display: flex; flex-direction: column; gap: 4px; max-height: 360px; overflow-y: auto; } 96 .search-suggest__item { display: flex; flex-direction: column; gap: 3px; padding: 11px 14px; border-radius: 10px; border: 1px solid transparent; transition: .15s; cursor: pointer; } 97 .search-suggest__item:hover, .search-suggest__item.is-active { background: var(--bg-blue); border-color: var(--line); } 98 .search-suggest__cat { font-size: 11.5px; font-weight: 700; color: var(--accent); } 99 .search-suggest__cat i { margin-inline-end: 5px; } 100 .search-suggest__item--podcast .search-suggest__cat { color: #7c3aed; } 101 .search-suggest__item--video .search-suggest__cat { color: #dc2626; } 102 .search-suggest__item--tag .search-suggest__cat { color: var(--muted); } 103 .search-suggest__title { font-size: 14.5px; font-weight: 600; color: var(--ink); line-height: 1.55; } 104 .search-suggest__state { padding: 16px 6px; text-align: center; color: var(--muted); font-size: 14px; } 105 .search-suggest__state i { color: var(--accent); margin-inline-end: 6px; } 106 107 /* newsletter inline error */ 108 .news-form__error { margin-top: 10px; color: #fff; background: rgba(220,53,69,.92); padding: 8px 14px; border-radius: 10px; font-size: 13.5px; font-weight: 600; } 109 110 /* most-read tabs (24h / week / all-time) */ 111 [x-cloak] { display: none !important; } 112 .mr-tabs { display: flex; flex-wrap: wrap; gap: 8px; } 113 .mr-tab { font-size: 13px; font-weight: 700; color: var(--muted); background: var(--bg); border: 1px solid var(--line); padding: 7px 15px; border-radius: 30px; cursor: pointer; transition: .15s; font-family: inherit; } 114 .mr-tab:hover { color: var(--ink); border-color: var(--accent); } 115 .mr-tab.is-active { background: var(--accent); color: #fff; border-color: var(--accent); } 116 .mr-item { display: flex; align-items: center; gap: 12px; } 117 118 /* read-aloud (TTS) bar */ 119 .tts-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 16px 0 6px; } 120 .tts-bar__play { display: inline-flex; align-items: center; gap: 9px; background: var(--accent); color: #fff; border: none; font-family: inherit; font-weight: 700; font-size: 14.5px; padding: 10px 20px; border-radius: 30px; cursor: pointer; transition: .15s; } 121 .tts-bar__play:hover { filter: brightness(1.07); } 122 .tts-bar__play.is-active { background: var(--accent-dark, #1f5fa8); } 123 .tts-bar__controls { display: inline-flex; align-items: center; gap: 6px; } 124 .tts-bar__btn { width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; } 125 .tts-bar__btn:hover { border-color: var(--accent); color: var(--accent); } 126 .tts-bar__sep { width: 1px; height: 22px; background: var(--line); margin: 0 4px; } 127 .tts-bar__rate { border: 1px solid var(--line); background: var(--bg); color: var(--muted); font-family: inherit; font-weight: 700; font-size: 12.5px; padding: 6px 11px; border-radius: 20px; cursor: pointer; } 128 .tts-bar__rate.is-active { background: var(--accent); color: #fff; border-color: var(--accent); } 129 130 /* ===== audio news digest Â«Ù ÙØ¬Ø² Ø§ÙØ£Ø®Ø¨Ø§Ø±Â» ===== */ 131 .newsbrief { display: grid; grid-template-columns: 1fr; border-radius: 20px; overflow: hidden; box-shadow: var(--shadow-card, 0 18px 44px rgba(15, 35, 60, .14)); border: 1px solid var(--line); background: var(--bg); } 132 @media (min-width: 900px) { .newsbrief { grid-template-columns: minmax(330px, 38%) 1fr; } } 133 .newsbrief__player { position: relative; overflow: hidden; padding: 30px 28px; color: #fff; background: linear-gradient(145deg, #0f2a48 0%, var(--accent-dark, #1c4a70) 55%, var(--accent, #2f7fcf) 150%); display: flex; flex-direction: column; } 134 .newsbrief__blob { position: absolute; width: 240px; height: 240px; border-radius: 50%; background: radial-gradient(circle, rgba(255,255,255,.16), transparent 65%); inset-block-start: -90px;
134 inset-inline-start: -70px; pointer-events: none; } 135 .newsbrief__badge { position: relative; align-self: flex-start; display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 800; letter-spacing: .3px; background: rgba(255,255,255,.16); border: 1px solid rgba(255,255,255,.28); padding: 6px 14px; border-radius: 30px; } 136 .newsbrief__title { position: relative; font-size: 28px; font-weight: 800; margin: 16px 0 6px; } 137 .newsbrief__sub { position: relative; font-size: 13.5px; color: #d6e6f6; margin-bottom: 22px; } 138 .newsbrief__controls { position: relative; margin-top: auto; display: flex; align-items: center; gap: 14px; } 139 .newsbrief__play { flex: none; width: 58px; height: 58px; border-radius: 50%; border: none; cursor: pointer; background: var(--bg); color: var(--accent-dark, #1f5fa8); font-size: 20px; box-shadow: 0 8px 22px rgba(0,0,0,.25); transition: transform .15s; display: inline-flex; align-items: center; justify-content: center; } 140 .newsbrief__play:hover { transform: scale(1.06); } 141 .newsbrief__eq { display: inline-flex; align-items: flex-end; gap: 3px; height: 24px; } 142 .newsbrief__eq span { width: 3px; height: 7px; background: rgba(255,255,255,.55); border-radius: 2px; } 143 .newsbrief__player.is-playing .newsbrief__eq span { animation: nbEq .9s ease-in-out infinite; } 144 .newsbrief__player.is-playing .newsbrief__eq span:nth-child(2) { animation-delay: .12s; } 145 .newsbrief__player.is-playing .newsbrief__eq span:nth-child(3) { animation-delay: .24s; } 146 .newsbrief__player.is-playing .newsbrief__eq span:nth-child(4) { animation-delay: .36s; } 147 .newsbrief__player.is-playing .newsbrief__eq span:nth-child(5) { animation-delay: .48s; } 148 @keyframes nbEq { 0%, 100% { height: 7px; } 50% { height: 22px; } } 149 .newsbrief__track { flex: 1; height: 6px; border-radius: 6px; background: rgba(255,255,255,.22); overflow: hidden; } 150 .newsbrief__progress { width: 0; height: 100%; background: var(--bg); border-radius: 6px; transition: width .25s linear; } 151 .newsbrief__time { font-size: 12.5px; font-weight: 700; color: #d6e6f6; min-width: 38px; } 152 .newsbrief__list { margin: 0; padding: 12px 10px; list-style: none; display: flex; flex-direction: column; } 153 .newsbrief__list li + li { border-top: 1px solid var(--line); } 154 .newsbrief__list a { display: flex; align-items: center; gap: 14px; padding: 13px 14px; border-radius: 12px; transition: background .15s; } 155 .newsbrief__list a:hover { background: var(--bg-blue, #f3f7fb); } 156 .newsbrief__num { flex: none; font-size: 13px; font-weight: 800; color: var(--accent); width: 26px; } 157 .newsbrief__headline { flex: 1; font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.55; } 158 .newsbrief__go { color: var(--muted-3, #a4a9b2); font-size: 14px; } 159 .newsbrief__list a:hover .newsbrief__go { color: var(--accent); } 160 </style> 161 162 163 <!-- Global site tag (gtag.js) - Google Analytics -->
vendor: 96 bytes, lines 163-164
163 164<script type="text/devlo-deferred" async data-src="https://www.googletagmanager.com/gtag/js?id=
164UA-38391677-1
vendor: 12 bytes, line 164
164"></script>
165<script> 166
vendor: 134 bytes, lines 166-170
166window.dataLayer = window.dataLayer || []; 167 function gtag(){dataLayer.push(arguments);} 168 gtag('js', new Date()); 169 170 gtag('config', '
170UA-38391677-1
vendor: 4 bytes, line 170
170');
171</script>
171 172
173<script type="text/devlo-deferred" async data-src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8563461161168276" 174 crossorigin="anonymous"></script>
174 175
176<script>function coronavirus(){var TotalCases=JSON.parse(this.responseText);document.querySelector("#TotalCases").innerHTML=TotalCases.data.TotalCases;var TotalActiveCases=JSON.parse(this.responseText);document.querySelector("#TotalActiveCases").innerHTML=TotalActiveCases.data.TotalActiveCases;var TotalRecovery=JSON.parse(this.responseText);document.querySelector("#TotalRecovery").innerHTML=TotalRecovery.data.TotalRecovery;var TotalCriticalCases=JSON.parse(this.responseText);document.querySelector("#TotalCriticalCases").innerHTML=TotalCriticalCases.data.TotalCriticalCases;var TotalDeath=JSON.parse(this.responseText);document.querySelector("#TotalDeath").innerHTML=TotalDeath.data.TotalDeath;} var coronavirusupdate=new XMLHttpRequest();coronavirusupdate.addEventListener("load",coronavirus);coronavirusupdate.open("GET","https://corona.ps/API/summary");coronavirusupdate.send();</script>
176 177 178<meta name="google-adsense-account" content="ca-pub-8563461161168276">
179<script> 180/* Replays the third-party
180<script src> tags above once the page has loaded. 181 Order is preserved; async/defer/crossorigin and every data-* attribute other 182 than data-src are carried over untouched. */ 183(function () { 184 if (window.__devloDeferredCode) { return; } 185 window.__devloDeferredCode = true; 186 187 function idle(fn, timeout) { 188 if (window.requestIdleCallback) requestIdleCallback(fn, { timeout: timeout || 2000 }); 189 else setTimeout(fn, 0); 190 } 191 192 /* 193 * ONE AT A TIME, each in its own idle slot. 194 * 195 * Releasing them together only moves the pile-up: analytics and the ad 196 * stack then boot in the same burst and hold the main thread through it, 197 * which is exactly the stall this defer exists to avoid â just later in the 198 * page's life, where it lands on a reader who has started scrolling. 199 * Waiting for each to finish before asking for the next slot leaves gaps 200 * the browser can spend on input instead. 201 * 202 * Errors and blocked requests (an ad blocker, an offline visitor) resolve 203 * the same way a load does, so one refused script can never strand the 204 * ones behind it. 205 */ 206 function next(tags, i) { 207 if (i >= tags.length) { return; } 208 209 var old = tags[i]; 210 var s = document.createElement('script'); 211 for (var a = 0; a < old.attributes.length; a++) { 212 var at = old.attributes[a]; 213 if (at.name === 'type' || at.name === 'data-src') { continue; } 214 s.setAttribute(at.name, at.value); 215 } 216 217 var moved = false; 218 function advance() { 219 if (moved) { return; } 220 moved = true; 221 idle(function () { next(tags, i + 1); }, 1000); 222 } 223 s.addEventListener('load', advance, { once: true }); 224 s.addEventListener('error', advance, { once: true }); 225 // A script that never fires either event must not strand the queue. 226 setTimeout(advance, 4000); 227 228 s.src = old.getAttribute('data-src'); 229 old.parentNode.insertBefore(s, old); 230 old.parentNode.removeChild(old); 231 } 232 233 function run() { 234 next(document.querySelectorAll('script[type="text/devlo-deferred"]'), 0); 235 } 236 237 /* 238 * "After load" stopped being late enough. 239 * 240 * Once the page got lighter the load event moved from ~1.2s to ~0.45s, so 241 * releasing on load put the ad stack back on the main thread WHILE the 242 * article was still painting â the exact contention this defer exists to 243 * avoid, just reached sooner. Wait for the largest contentful paint to 244 * settle instead: that is the frame the reader came for, and it is the 245 * honest definition of "the page is ready for someone else's code". 246 * 247 * The observer reports a new candidate each time a bigger element paints, 248 * so the signal is not the first entry but a quiet gap after one. A 6s cap 249 * from load runs them regardless â on a browser with no LCP support, a page 250 * that never produces a candidate, or a reader who just sits still. 251 */ 252 function whenContentSettled(cb) { 253 var fired = false; 254 function go() { 255 if (fired) { return; } 256 fired = true; 257 idle(cb, 2000); 258 } 259 260 setTimeout(go, 6000); 261 262 try { 263 var quiet = null; 264 var po = new PerformanceObserver(function () { 265 clearTimeout(quiet); 266 quiet = setTimeout(function () { 267 try { po.disconnect(); } catch (e) { /* already gone */ } 268 go(); 269 }, 700); 270 }); 271 po.observe({ type: 'largest-contentful-paint', buffered: true }); 272 } catch (e) { 273 // No LCP observer here: fall back to a short settle after load. 274 setTimeout(go, 1500); 275 } 276 } 277 278 if (document.readyState === 'complete') { whenContentSettled(run); } 279 else { window.addEventListener('load', function () { whenContentSettled(run); }, { once: true }); } 280})(); 281</script>
281 282 <style> 283 /* page-scoped helpers for the "ØªÙØ§ØµÙ Ù Ø¹ÙØ§" page only */ 284 .contact-social{margin-top:4px;padding-top:18px;border-top:1px solid var(--line)} 285 .contact-social__label{display:block;font-size:13px;font-weight:700;color:var(--muted);margin-bottom:12px} 286 .contact-social__row{display:flex;flex-wrap:wrap;gap:8px} 287 </style> 288
288<script> 289 (function () { 290 // Relative on purpose: this HTML gets written to disk and replayed, so a 291 // baked absolute URL would carry whatever scheme/host the seeding render 292 // computed â an http:// APP_URL behind Cloudflare TLS turns the refresh 293 // into blocked mixed content on every visitor. 294 var ENDPOINT = '\/__csrf'; 295 296 /* 297 * Livewire's getCsrfToken() tries meta[name="csrf-token"], then 298 * [data-csrf], then window.livewireScriptConfig.csrf, and takes the 299 * first that exists. Only four themes emit the meta tag; on the rest the 300 * live value is the data-csrf attribute Livewire puts on its own script 301 * tag, so patching the meta alone would fix nothing there. Read and 302 * write all of them, in that same order. 303 */ 304 function currentToken() { 305 var tag = document.querySelector('meta[name="csrf-token"]'); 306 if (tag) { 307 return tag.getAttribute('content'); 308 } 309 var holder = document.querySelector('[data-csrf]'); 310 if (holder) { 311 return holder.getAttribute('data-csrf'); 312 } 313 return (window.livewireScriptConfig && window.livewireScriptConfig.csrf) || null; 314 } 315 316 function apply(token) { 317 var i; 318 var metas = document.querySelectorAll('meta[name="csrf-token"]'); 319 for (i = 0; i < metas.length; i++) { 320 metas[i].setAttribute('content', token); 321 } 322 var holders = document.querySelectorAll('[data-csrf]'); 323 for (i = 0; i < holders.length; i++) { 324 holders[i].setAttribute('data-csrf', token); 325 } 326 // Plain form POSTs (the newsletter) carry their own baked copy. 327 var fields = document.querySelectorAll('input[name="_token"]'); 328 for (i = 0; i < fields.length; i++) { 329 fields[i].value = token; 330 } 331 if (window.livewireScriptConfig) { 332 window.livewireScriptConfig.csrf = token; 333 } 334 } 335 336 // The token the server last confirmed is live for this visitor's session. 337 // Survives wire:navigate: this
337<script> is byte-identical on every page, 338 // and Livewire's mergeNewHead skips head assets it already has, so this 339 // closure is never re-created and no listener is ever bound twice. 340 var known = null; 341 var pending = null; 342 343 /** 344 * Resolves with the live token, or null. One request at a time: a 345 * caller that arrives while one is in the air waits on the same one. 346 */ 347 function refresh() { 348 if (pending) { 349 return pending; 350 } 351 352 try { 353 pending = fetch(ENDPOINT, { 354 headers: { 'Accept': 'application/json' }, 355 credentials: 'same-origin', 356 // This runs while the <head> is still parsing, so without a 357 // hint the browser schedules it alongside the stylesheets 358 // and the LCP image. Nothing waits on the token â it only 359 // has to land before the first interaction â so let the 360 // render-critical requests go first. Ignored where Priority 361 // Hints are unsupported, which just restores the old timing. 362 priority: 'low', 363 }) 364 .then(function (r) { return r.ok ? r.json() : null; }) 365 .then(function (data) { 366 pending = null; 367 if (!data || !data.token) { 368 return null; 369 } 370 known = data.token; 371 apply(known); 372 // This runs from <head>, and both the _token fields and 373 // Livewire's data-csrf script tag live further down the 374 // page â they may not exist yet when the fetch lands. 375 if (document.readyState === 'loading') { 376 document.addEventListener('DOMContentLoaded', function () { 377 apply(known); 378 }, { once: true }); 379 } 380 381 return known; 382 }) 383 .catch(function () { pending = null; return null; }); 384 } catch (e) { 385 // No fetch (ancient browser) â leave the baked token alone. 386 pending = null; 387 388 return Promise.resolve(null); 389 } 390 391 return pending; 392 } 393 394 function hasXsrfCookie() { 395 return /(^|;\s*)XSRF-TOKEN=/.test(document.cookie); 396 } 397 398 /* 399 * Two signals now, because the cookie alone was wrong for the visitor 400 * who matters most here. 401 * 402 * No cookie still means «this HTML came off disk», and it is known 403 * while the <head> is still parsing â so that fetch goes out as early 404 * as it always did. 405 * 406 * The second signal is the page's own token, and it is the exact one: 407 * a cached copy carries NO token at all, since PageCsrfToken::strip 408 * empties every holder before the file is written. A reader who 409 * already held an XSRF-TOKEN from an earlier live page used to be 410 * taken for a live render and left with the token the file was seeded 411 * with, so their next click answered 419 â «This page has expired», a 412 * maktoob reader on the archive's pagination, 2026-09-22. Read after 413 * DOMContentLoaded on purpose: on the themes that emit no meta tag the 414 * live value is Livewire's data-csrf attribute, and that script tag is 415 * at the end of <body>, nowhere near this one. 416 */ 417 if (!hasXsrfCookie()) { 418 refresh(); 419 } else if (document.readyState === 'loading') { 420 document.addEventListener('DOMContentLoaded', adopt, { once: true }); 421 } else { 422 adopt(); 423 } 424 425 /** This page's own token, when it has one: it IS the session's. */ 426 function adopt() { 427 var baked = currentToken(); 428 429 if (baked) { 430 // Recording it costs nothing and gives the navigation check 431 // below a baseline. 432 known = baked; 433 } else {
434 watchForIntent(); 435 } 436 } 437 438 /* 439 * A cached page in the hands of a visitor who already has a session. 440 * Asking for the token on load would put one request â and one row in 441 * a `sessions` table that reached 8.6M on maktoob â behind every view 442 * of the most-visited pages on the site, which is the cost these 443 * caches exist to remove. So the token is fetched the moment the 444 * visitor reaches for something that will need it, and a reader who 445 * only reads never asks at all. A click that lands before the answer 446 * does is what the 419 net below is for. 447 */ 448 var watching = false; 449 450 function watchForIntent() { 451 if (watching) { 452 return; 453 } 454 watching = true; 455 456 ['pointerdown', 'focusin', 'keydown'].forEach(function (name) { 457 document.addEventListener(name, onIntent, true); 458 }); 459 } 460 461 function onIntent(event) { 462 if (!event.target || !needsToken(event.target)) { 463 return; 464 } 465 466 ['pointerdown', 'focusin', 'keydown'].forEach(function (name) { 467 document.removeEventListener(name, onIntent, true); 468 }); 469 watching = false; 470 471 refresh(); 472 } 473 474 /** A `wire:` control or a form â anything else needs no token. */ 475 function needsToken(target) { 476 for (var el = target; el && el.nodeType === 1; el = el.parentElement) { 477 if (el.tagName === 'FORM') { 478 return true; 479 } 480 481 for (var i = 0; i < el.attributes.length; i++) { 482 if (el.attributes[i].name.indexOf('wire:') === 0) { 483 return true; 484 } 485 } 486 } 487 488 return false; 489 } 490 491 /* 492 * wire:navigate does not preserve the fix. mergeNewHead() treats only 493 *
493<script>/<style>/stylesheet <link> as assets and deletes every other 494 * head child, so a patched meta is dropped for the incoming page's own â 495 * and the body, data-csrf script tag included, is swapped wholesale. 496 * Whenever that incoming page is itself a cache HIT the token is stale 497 * again. But the session's token does not change between navigations, so 498 * a token we already fetched can simply be re-applied â instant, and one 499 * request for the whole visit instead of one per cached page. Only a 500 * visitor who has never held a live token asks the server. 501 */ 502 document.addEventListener('livewire:navigated', function () { 503 var now = currentToken(); 504 505 if (!known) { 506 // Nothing held yet: the incoming page's own token, if it has 507 // one, is the session's; a cached one has none and must ask. 508 if (now) { 509 known = now; 510 } else if (hasXsrfCookie()) { 511 watchForIntent(); 512 } else { 513 refresh(); 514 } 515 516 return; 517 } 518 519 if (now !== known) { 520 apply(known); 521 } 522 }); 523 524 /* 525 * The net under all of it. 526 * 527 * Whatever is repaired above, a token can still be wrong when the 528 * request goes out: a tab left open until `sessions:prune` removed its 529 * row, a cached file written before this shipped, a click that landed 530 * while the fetch was still in the air. Livewire answers a 419 with a 531 * native confirm() â «This page has expired. Would you like to refresh 532 * the page?» â which blocks the JS thread, so whatever the reader 533 * pressed also stays frozen behind the dialog. 534 * 535 * So the dialog is suppressed, a fresh token fetched, and the action 536 * that failed sent once more. The 419 stopped the request at the CSRF 537 * middleware, before the component ran, so nothing happened twice. 538 */ 539 var recoveries = 0; 540 var hooked = false; 541 542 function hookLivewire() { 543 if (hooked || !window.Livewire || !window.Livewire.hook) { 544 return; 545 } 546 hooked = true; 547 548 var sent = []; 549 550 window.Livewire.hook('commit', function (context) { 551 var entry = { component: context.component, commit: context.commit, done: false }; 552 553 sent.push(entry); 554 if (sent.length > 10) { 555 sent.shift(); 556 } 557 558 context.succeed(function () { entry.done = true; }); 559 }); 560 561 window.Livewire.hook('request', function (context) { 562 context.fail(function (failure) { 563 if (failure.status !== 419) { 564 return; 565 } 566 567 // No confirm(); the request's finish hooks then run as 568 // usual, so wire:loading states release. 569 failure.preventDefault(); 570 571 // Twice, so a token that is somehow wrong again stops 572 // instead of looping. 573 if (recoveries >= 2) { 574 return; 575 } 576 recoveries++; 577 578 var again = sent.filter(function (entry) { return !entry.done; }); 579 sent = []; 580 581 known = null; 582 583 refresh().then(function (token) { 584 if (token) { 585 again.forEach(replay); 586 } 587 }); 588 }); 589 }); 590 } 591 592 /** Send a failed commit again, now that the page holds a live token. */ 593 function replay(entry) { 594 var wire = entry.component && entry.component.$wire; 595 if (!wire) { 596 return; 597 } 598 599 var updates = entry.commit.updates || {}; 600 var calls = (entry.commit.calls || []).filter(function (call) { 601 // `$set` IS the updates below â replaying it as a call would 602 // send the magic method with no arguments. 603 return call.method && call.method !== '$set'; 604 }); 605 606 // Everything fired in this tick coalesces into one request.
607 Object.keys(updates).forEach(function (name) { 608 try { wire.set(name, updates[name]); } catch (e) {} 609 }); 610 611 calls.forEach(function (call) { 612 try { wire.call.apply(wire, [call.method].concat(call.params || [])); } catch (e) {} 613 }); 614 } 615 616 document.addEventListener('livewire:init', hookLivewire); 617 hookLivewire(); 618 })(); 619</script>
619 620 621 622
622<script> 623 (function () { 624 var URL = '\/__view'; 625 var ARMED = []; 626 var PAGE = JSON.parse('{\u0022k\u0022:\u0022page\u0022,\u0022p\u0022:\u0022\\\/contact_us\u0022,\u0022s\u0022:\u00224600845e3762d1edd3b2c4410d27aa2c\u0022}'); 627 var sent = {}; 628 var pending = {}; 629 var pageTimers = []; 630 631 function enc(v) { return encodeURIComponent(v); } 632 633 function send(body) { 634 try { 635 if (navigator.sendBeacon && navigator.sendBeacon(URL, new Blob([body], {type: 'application/x-www-form-urlencoded'}))) return; 636 } catch (e) {} 637 try { 638 fetch(URL, { 639 method: 'POST', 640 body: body, 641 headers: {'Content-Type': 'application/x-www-form-urlencoded'}, 642 credentials: 'same-origin', 643 keepalive: true 644 }).catch(function () {}); 645 } catch (e) {} 646 } 647 648 // `phase`: 'o' = opened it, 'r' = stayed on it. 649 function itemBody(p, phase) { 650 return 't=' + enc(p.t) + '&i=' + enc(p.i) + '&s=' + enc(p.s) + '&p=' + phase; 651 } 652 function pageBody() { 653 if (!PAGE) return ''; 654 return 'k=' + enc(PAGE.k) + '&pp=' + enc(PAGE.p) + '&ps=' + enc(PAGE.s) 655 + '&tt=' + enc((document.title || '').slice(0, 180)); 656 } 657 658 function post(p, phase) { 659 var key = phase + ':' + p.t + ':' + p.i; 660 if (sent[key]) return; 661 sent[key] = 1; 662 send(itemBody(p, phase)); 663 } 664 665 // Visible-time timer: accumulates only while `wanted` AND the tab is 666 // visible; calls done() once `need` ms have been reached. need = 0 is 667 // "as soon as the page is really on screen", which is what an open is. 668 function timer(need, done) { 669 var acc = 0, since = null, handle = null, dead = false, wanted = true; 670 function elapsed() { return acc + (since === null ? 0 : Date.now() - since); } 671 function check() { 672 if (dead) return; 673 if (elapsed() >= need) { dead = true; clearTimeout(handle); done(); } 674 else schedule(); 675 } 676 function schedule() { clearTimeout(handle); handle = setTimeout(check, Math.max(50, need - elapsed())); } 677 function sync() { 678 if (dead) return; 679 var run = wanted && !document.hidden; 680 if (run && since === null) { since = Date.now(); schedule(); } 681 else if (!run && since !== null) { acc += Date.now() - since; since = null; clearTimeout(handle); } 682 } 683 document.addEventListener('visibilitychange', sync); 684 sync(); 685 return { 686 pause: function () { wanted = false; sync(); }, 687 resume: function () { wanted = true; sync(); }, 688 cancel: function () { dead = true; clearTimeout(handle); } 689 }; 690 } 691 692 // ââ The load report: the page, plus the open of what it shows. One 693 // request when both exist â an article page must not cost more 694 // round-trips than it did before the page stat existed. 695 pageTimers.push(timer(0, function () { 696 var page = pageBody(); 697 var first = ARMED.length ? ARMED[0] : null; 698 699 if (first) { 700 sent['o:' + first.t + ':' + first.i] = 1; 701 send(itemBody(first, 'o') + (page ? '&' + page : '')); 702 } else if (page) { 703 send(page); 704 } 705 706 for (var i = 1; i < ARMED.length; i++) post(ARMED[i], 'o'); 707 })); 708 709 // ââ The read report: one per armed item, after its dwell. 710 ARMED.forEach(function (p) { 711 pageTimers.push(timer((p.d || 0) * 1000, function () { post(p, 'r'); })); 712 }); 713 714 function hitEl(target) { 715 return target && target.closest ? target.closest('[data-view-hit]') : null; 716 } 717 function payloadOf(el) { 718 try { return JSON.parse(el.getAttribute('data-view-hit')); } catch (e) { return null; } 719 } 720 function arm(el, media) { 721 var p = payloadOf(el); 722 if (!p) return; 723 var key = p.t + ':' + p.i; 724 if (sent['r:' + key]) return; 725 // Touching it IS opening it â the dwell below decides the read. 726 post(p, 'o'); 727 if (pending[key]) { pending[key].resume(); return; } 728 var need = (p.d || 0) * 1000; 729 // A clip shorter than the dwell counts at 80% of its own length. 730 if (media && isFinite(media.duration) && media.duration > 0) {
731 need = Math.min(need, media.duration * 800); 732 } 733 pending[key] = timer(need, function () { delete pending[key]; post(p, 'r'); }); 734 } 735 736 // Capture phase: a theme handler that stops propagation (a deck or a 737 // modal opener) must not be able to hide the click from the counter. 738 document.addEventListener('click', function (e) { 739 var el = hitEl(e.target); 740 if (el && (el.getAttribute('data-view-on') || 'click') === 'click') arm(el, null); 741 }, true); 742 // A click INSIDE an embedded player (YouTube / Instagram iframe) never 743 // reaches this document; the focus moving into the iframe does. 744 window.addEventListener('blur', function () { 745 setTimeout(function () { 746 var a = document.activeElement; 747 if (!a || a.tagName !== 'IFRAME') return; 748 var el = hitEl(a); 749 if (el && (el.getAttribute('data-view-on') || 'click') === 'click') arm(el, null); 750 }, 0); 751 }); 752 // Media events do not bubble â capture them. 753 document.addEventListener('play', function (e) { 754 var el = hitEl(e.target); 755 if (el && el.getAttribute('data-view-on') === 'play') arm(el, e.target); 756 }, true); 757 document.addEventListener('pause', function (e) { 758 var el = hitEl(e.target), p = el && payloadOf(el); 759 if (p && pending[p.t + ':' + p.i]) pending[p.t + ':' + p.i].pause(); 760 }, true); 761 762 document.addEventListener('livewire:navigating', function () { 763 pageTimers.forEach(function (t) { t.cancel(); }); 764 for (var k in pending) pending[k].cancel(); 765 pending = {}; 766 }, {once: true}); 767 })(); 768</script>
768 769 770 771<style> 772 /* !important so the clamp wins even when a theme rule (e.g. a compound 773 selector like `.card--featured .excerpt{display:block}`) has higher 774 specificity than this single-class utility. Only ever applied to text 775 containers (titles/excerpts), so forcing the display is safe. */ 776 .line-clamp-1,.line-clamp-2,.line-clamp-3,.line-clamp-4{display:-webkit-box!important;-webkit-box-orient:vertical!important;overflow:hidden!important} 777 .line-clamp-1{-webkit-line-clamp:1!important} 778 .line-clamp-2{-webkit-line-clamp:2!important} 779 .line-clamp-3{-webkit-line-clamp:3!important} 780 .line-clamp-4{-webkit-line-clamp:4!important} 781</style> 782<meta name="generator" content="Devlo CMS"/> 783<!-- Livewire Styles --><style >[wire\:loading][wire\:loading], [wire\:loading\.delay][wire\:loading\.delay], [wire\:loading\.list-item][wire\:loading\.list-item], [wire\:loading\.inline-block][wire\:loading\.inline-block], [wire\:loading\.inline][wire\:loading\.inline], [wire\:loading\.block][wire\:loading\.block], [wire\:loading\.flex][wire\:loading\.flex], [wire\:loading\.table][wire\:loading\.table], [wire\:loading\.grid][wire\:loading\.grid], [wire\:loading\.inline-flex][wire\:loading\.inline-flex] {display: none;}[wire\:loading\.delay\.none][wire\:loading\.delay\.none], [wire\:loading\.delay\.shortest][wire\:loading\.delay\.shortest], [wire\:loading\.delay\.shorter][wire\:loading\.delay\.shorter], [wire\:loading\.delay\.short][wire\:loading\.delay\.short], [wire\:loading\.delay\.default][wire\:loading\.delay\.default], [wire\:loading\.delay\.long][wire\:loading\.delay\.long], [wire\:loading\.delay\.longer][wire\:loading\.delay\.longer], [wire\:loading\.delay\.longest][wire\:loading\.delay\.longest] {display: none;}[wire\:offline][wire\:offline] {display: none;}[wire\:dirty]:not(textarea):not(input):not(select) {display: none;}:root {--livewire-progress-bar-color: #2299dd;}[x-cloak] {display: none !important;}[wire\:cloak] {display: none !important;}dialog#livewire-error::backdrop {background-color: rgba(0, 0, 0, .6);}</style> 784</head> 785 786<body data-page=""> 787 788<div class="topbar"> 789 <div class="container topbar__inner"> 790 <div class="topbar__meta"> 791 <span><i class="fa-regular fa-calendar" aria-hidden="true"></i> 792 Ø§ÙØ¬Ù Ø¹Ø©Ø 25 سبت٠بر 2026</span> 793 </div> 794 <div class="topbar__social"> 795 <a href="https://tiktok.com/@qudsn" target="_blank" rel="noopener" aria-label="Tiktok" class="social-link"><i class="fab fa-tiktok" aria-hidden="true"></i></a> 796 <a href="https://t.me/QudsN" target="_blank" rel="noopener" aria-label="Facebook" class="social-link"><i class="fab fa-facebook-f" aria-hidden="true"></i></a> 797 <a href="https://www.instagram.com/qudsn" target="_blank" rel="noopener" aria-label="Instagram" class="social-link"><i class="fab fa-instagram" aria-hidden="true"></i></a> 798 <a href="https://www.youtube.com/user/QudsNetwork" target="_blank" rel="noopener" aria-label="Youtube-play" class="social-link"><i class="fab fa-youtube" aria-hidden="true"></i></a> 799 <a href="https://t.me/QudsN" target="_blank" rel="noopener" aria-label="ÙÙØ§Ø© تÙÙÙØºØ±Ø§Ù " class="social-link"><i class="fab fa-telegram" aria-hidden="true"></i></a> 800 <a href="https://whatsapp.com/channel/0029VbCpgXo8fewqkgDTLM2z" target="_blank" rel="noopener" aria-label="ÙÙØ§Ø© ÙØ§ØªØ³Ø§Ø¨" class="social-link"><i class="fab fa-whatsapp" aria-hidden="true"></i></a> 801 <a href="https://twitter.com/QudsN" target="_blank" rel="noopener" aria-label="Twitter" class="social-link"><i class="fab fa-x-twitter" aria-hidden="true"></i></a> 802 <a href="https://t.me/Qudsn_hebrew" target="_blank" rel="noopener" aria-label="Telegram " class="social-link"><i class="fab fa-telegram" aria-hidden="true"></i></a> 803 <span class="topbar__sep" aria-hidden="true"></span> 804 <a href="https://qudsnen.com" class="topbar__lang" target="_blank" rel="noopener">English</a> 805 </div> 806 </div> 807</div> 808 809 810<header class="site-header"> 811 <div class="container header__inner"> 812 <a href="https://qudsn.co" class="brand" 813 aria-label="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© â Ø§ÙØµÙØØ© Ø§ÙØ±Ø¦ÙØ³ÙØ©"> 814 815 <img src="https://fsn1.your-objectstorage.com/qudsn/uploads/2026/08/file_6a6f0b281cbc8.png" alt="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©" class="brand__logo" width="90" height="38" 816 decoding="async" > 817 </a> 818 819 820 <nav class="cat-nav" id="catNav" aria-label="Ø£ÙØ³Ø§Ù اÙÙ ÙÙØ¹"> 821 <div class="cat-nav__inner"> 822 <a href="/" 823 class="cat-link">Ø§ÙØ±Ø¦ÙØ³ÙØ©</a> 824 <a href="/category_news/أخبار" 825 class="cat-link">أخبار</a> 826 <a href="/category_news/٠تابعات ÙØ¯Ø³" 827 class="cat-link">٠تابعات</a> 828 <a href="/category_news/ØªÙØ§Ø±Ùر ÙØ¯Ø³" 829 class="cat-link">ØªÙØ§Ø±Ùر</a> 830 <a href="/category_news/٠دÙÙÙØ§Øª" 831 class="cat-link">٠دÙÙÙØ§Øª</a> 832 <a href="/category_news/ترج٠ات Ø¹Ø¨Ø±ÙØ©" 833 class="cat-link">ترج٠ات</a> 834 <a href="/all_podcast" 835 class="cat-link">Ø¨ÙØ¯Ùاست ÙØ¯Ø³</a> 836 <a href="/contact_us" 837 class="cat-link">Ø§ØªØµÙ Ø¨ÙØ§</a> 838 <a href="/special_page/3" 839 class="cat-link">Ù Ù ÙØÙ</a> 840 841 </div> 842 843 844 <div class="cat-more" id="catMore" hidden> 845 <button type="button" class="cat-link cat-more__btn" aria-haspopup="true" aria-expanded="false"> 846 اÙÙ Ø²ÙØ¯ <i class="fa-solid fa-chevron-down" aria-hidden="true"></i> 847 </button> 848 <div class="cat-more__menu" role="menu" hidden></div> 849 </div> 850 </nav> 851 852 <div class="header__actions"> 853 854 <button type="button" class="icon-btn qd-theme-toggle" id="qdThemeToggle" 855 aria-label="تبدÙ٠اÙÙØ¶Ø¹ اÙÙÙÙÙ ÙØ§ÙÙÙØ§Ø±Ù" aria-pressed="false"> 856
857 <span class="theme-icon" aria-hidden="true"> 858 <svg class="theme-icon__moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" 859 stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"> 860 <path d="M20.4 13.2A8.2 8.2 0 1 1 10.8 3.6a6.6 6.6 0 0 0 9.6 9.6z"/> 861 </svg> 862 <svg class="theme-icon__sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" 863 stroke-width="1.9" stroke-linecap="round"> 864 <circle cx="12" cy="12" r="4"/> 865 <path d="M12 2.6v2.1M12 19.3v2.1M2.6 12h2.1M19.3 12h2.1M5.4 5.4l1.5 1.5M17.1 17.1l1.5 1.5M18.6 5.4l-1.5 1.5M6.9 17.1l-1.5 1.5"/> 866 </svg> 867 </span> 868 </button> 869 <button type="button" class="icon-btn" id="searchOpen" aria-label="ÙØªØ Ø§ÙØ¨ØØ«" aria-haspopup="dialog"> 870 <i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i> 871 </button> 872 873 <a href="https://t.me/QudsN" class="btn btn--primary" target="_blank" rel="noopener" aria-label="اشتر٠Ù٠اÙÙÙØ§Ø©"> 874 <i class="fa-regular fa-paper-plane" aria-hidden="true"></i><span>اشتر٠Ù٠اÙÙÙØ§Ø©</span> 875 </a> 876 <button type="button" class="icon-btn nav-toggle" id="navToggle" aria-label="ÙØªØ اÙÙØ§Ø¦Ù Ø©" 877 aria-expanded="false" aria-controls="catNav"> 878 <i class="fa-solid fa-bars" aria-hidden="true"></i> 879 </button> 880 </div> 881 </div> 882 883</header> 884 885
886<script> 887/* ========================================================================== 888 NAV OVERFLOW â move whatever will not fit into the «اÙÙ Ø²ÙØ¯Â» dropdown 889 The menu is dashboard-driven, so no CSS breakpoint can know when it stops 890 fitting: the row is measured instead. Links keep their DOM order; only 891 their parent changes, so a link never disappears â worst case it is one 892 click deeper. 893 ========================================================================== */ 894(function () { 895 'use strict'; 896 var inner = document.querySelector('.header__inner .cat-nav__inner'); 897 var more = document.getElementById('catMore'); 898 if (!inner || !more) return; 899 900 var btn = more.querySelector('.cat-more__btn'); 901 var menu = more.querySelector('.cat-more__menu'); 902 // Snapshot the original order once: after the first pass the links live in 903 // two parents, and re-reading the row would lose the ones already moved. 904 var links = [].slice.call(inner.querySelectorAll(':scope > .cat-link')); 905 if (!links.length) return; 906 907 function close() { 908 if (menu.hidden) return; 909 menu.hidden = true; 910 btn.setAttribute('aria-expanded', 'false'); 911 } 912 913 var bucketed = false; // true once a pass has moved links into the menu 914 915 // Start from "everything fits" so the row can grow back when the window 916 // widens or a link is removed from the dashboard. Only when a previous pass 917 // actually moved something: re-appending every link on the first pass (all of 918 // them still in the row) dirtied the header's layout, and the measurement 919 // right after it then forced a synchronous layout of the whole page. 920 function restore() { 921 if (!bucketed) return; 922 links.forEach(function (l) { inner.appendChild(l); }); 923 more.hidden = true; 924 bucketed = false; 925 } 926 927 function layout() { 928 // Below the burger breakpoint the drawer lists everything; putting links 929 // in the dropdown too would show them twice. 930 if (window.matchMedia('(max-width: 768px)').matches) { 931 restore(); 932 close(); 933 return; 934 } 935 936 close(); 937 restore(); 938 939 var fits = function () { return inner.scrollWidth <= inner.clientWidth + 1; }; 940 if (fits()) return; // it all fits â no bucket needed 941 942 // The button is a flex sibling of the row, so revealing it SHRINKS the 943 // row. Reveal it first and re-measure every pass â a width cached before 944 // this point is too generous and leaves one link half-cut. 945 more.hidden = false; 946 947 // Push links off the end until the row fits, prepending each one so the 948 // dropdown reads in the same order as the bar. 949 var moved = 0; 950 for (var i = links.length - 1; i >= 0 && !fits(); i--) { 951 menu.insertBefore(links[i], menu.firstChild); 952 moved++; 953 } 954 955 bucketed = true; 956 if (!moved) { // nothing moved: the bucket is dead weight 957 more.hidden = true; 958 } 959 } 960 961 btn.addEventListener('click', function (e) { 962 e.stopPropagation(); 963 menu.hidden = !menu.hidden; 964 btn.setAttribute('aria-expanded', menu.hidden ? 'false' : 'true'); 965 }); 966 document.addEventListener('click', function (e) { 967 if (!more.contains(e.target)) close(); 968 }); 969 document.addEventListener('keydown', function (e) { 970 if (e.key === 'Escape') close(); 971 }); 972 973 layout(); 974 // Fonts land after first paint and change every link's width, so a single 975 // pass at DOMContentLoaded measures the wrong row. 976 if (document.fonts && document.fonts.ready) { 977 document.fonts.ready.then(layout).catch(function () {}); 978 } 979 var t; 980 window.addEventListener('resize', function () { 981 clearTimeout(t); 982 t = setTimeout(layout, 150); 983 }); 984})(); 985</script>
985 986 987 988 989<main id="main"> 990 <div wire:snapshot="{"data":{"state":[{"full_name":"","email":"","subject":"","message":""},{"s":"arr"}]},"memo":{"id":"RCTLk9ZOF5hYcn4kgdxh","name":"main.quds-ar.send-news","path":"contact_us","method":"GET","release":"a-a-a","children":[],"scripts":[],"assets":[],"errors":[],"locale":"ar","islands":[]},"checksum":"3c5432552dc73913508bc0d367d3709634130ceeeb9071d0c2bf1aa0d351c4aa"}" wire:effects="[]" wire:id="RCTLk9ZOF5hYcn4kgdxh" wire:name="main.quds-ar.send-news"> 991 992 <!-- breadcrumb --> 993 <nav class="breadcrumb" aria-label="٠سار Ø§ÙØªØµÙÙØ"> 994 <div class="container"> 995 <ol class="breadcrumb__list"> 996 <li><a href="https://qudsn.co">Ø§ÙØ±Ø¦ÙØ³ÙØ©</a></li> 997 <li><i class="fa-solid fa-angle-left breadcrumb__sep" aria-hidden="true"></i></li> 998 <li class="breadcrumb__current">ØªÙØ§ØµÙ Ù Ø¹ÙØ§</li> 999 </ol> 1000 </div> 1001 </nav> 1002 1003 <!-- ===== PAGE HERO ===== --> 1004 <section class="page-hero"> 1005 <span class="page-hero__blob page-hero__blob--1" aria-hidden="true"></span> 1006 <span class="page-hero__blob page-hero__blob--2" aria-hidden="true"></span> 1007 <div class="container"> 1008 <span class="page-hero__eyebrow"><i class="fa-regular fa-envelope" aria-hidden="true"></i>ØªÙØ§ØµÙ Ù Ø¹ÙØ§</span> 1009 <h1 class="page-hero__title">ØªÙØ§ØµÙ Ù Ø¹ÙØ§</h1> 1010 <p class="page-hero__sub">ÙØ³Ø¹Ø¯ Ø¨ØªÙØ§ØµÙÙÙ ÙØ§Ø³ØªÙØ¨Ø§Ù Ù ÙØ§ØØ¸Ø§ØªÙÙ ÙØ§Ø³ØªÙساراتÙ٠عÙ٠٠دار Ø§ÙØ³Ø§Ø¹Ø©.</p> 1011 </div> 1012 </section> 1013 1014 <!-- ===== CONTACT ===== --> 1015 <section class="section"> 1016 <div class="container"> 1017 1018 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> <div class="contact-layout"> 1019 1020 <!-- INFO COLUMN --> 1021 <div class="contact-info" data-aos="fade-up"> 1022 <div class="contact-card"> 1023 <span class="contact-card__icon"><i class="fa-solid fa-envelope" aria-hidden="true"></i></span> 1024 <div> 1025 <div class="contact-card__label">Ø§ÙØ¨Ø±Ùد Ø§ÙØ¥ÙÙØªØ±ÙÙÙ</div> 1026 <a class="contact-card__value" href="/cdn-cgi/l/email-protection#3950575f5679484c5d4a5717494a"><span class="__cf_email__" data-cfemail="99f0f7fff6d9e8ecfdeaf7b7e9ea">[email protected]</span></a> 1027 </div> 1028 </div> 1029 1030 <div class="contact-card"> 1031 <span class="contact-card__icon"><i class="fa-solid fa-phone" aria-hidden="true"></i></span> 1032 <div> 1033 <div class="contact-card__label">اÙÙØ§ØªÙ</div> 1034 <a class="contact-card__value" href="tel:+970568644929">+970568644929</a> 1035 </div> 1036 </div> 1037 1038 <div class="contact-card contact-card--value-rtl"> 1039 <span class="contact-card__icon"><i class="fa-solid fa-location-dot" aria-hidden="true"></i></span> 1040 <div> 1041 <div class="contact-card__label">اÙÙ ÙØ± Ø§ÙØ±Ø¦ÙسÙ</div> 1042 <div class="contact-card__value">را٠اÙÙÙ</div> 1043 </div> 1044 </div> 1045 1046 <div class="contact-social"> 1047 <span class="contact-social__label">تابعÙÙØ§ عÙÙ Ù ÙØµÙØ§ØªÙØ§</span> 1048 <div class="contact-social__row"> 1049 <a href="https://twitter.com/QudsN" aria-label="Ø¥ÙØ³" class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-x-twitter" aria-hidden="true"></i></a> 1050 <a href="https://www.instagram.com/qudsn" aria-label="Ø¥ÙØ³ØªØºØ±Ø§Ù " class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-instagram" aria-hidden="true"></i></a> 1051 <a href="https://t.me/QudsN" aria-label="تÙÙÙØºØ±Ø§Ù " class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-telegram" aria-hidden="true"></i></a> 1052 <a href="https://whatsapp.com/channel/0029Va54z83IiRp163Clyw3P" aria-label="ÙØ§ØªØ³Ø§Ø¨" class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-whatsapp" aria-hidden="true"></i></a> 1053 <a href="https://www.youtube.com/user/QudsNetwork" aria-label="ÙÙØªÙÙØ¨" class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-youtube" aria-hidden="true"></i></a> 1054 <a href="https://tiktok.com/@qudsn" aria-label="تÙ٠تÙÙ" class="footer__social-link" target="_blank" rel="noopener"><i class="fa-brands fa-tiktok" aria-hidden="true"></i></a> 1055 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> </div> 1056 </div> 1057 </div> 1058 1059 <!-- FORM COLUMN --> 1060 <div data-aos="fade-up" data-aos-delay="100"> 1061 <form class="form" id="contactForm" wire:submit.prevent="createSendNews" novalidate> 1062 <div class="form__success" id="contactSuccess" wire:ignore.self 1063 x-data="{ shown: false }" 1064 x-on:hide_contact_form.window="shown = true; setTimeout(() => shown = false, 6000)" 1065 x-show="shown" x-cloak :hidden="!shown"> 1066 <i class="fa-solid fa-circle-check" aria-hidden="true"></i>
1067 <span>ØªÙ Ø¥Ø±Ø³Ø§Ù Ø±Ø³Ø§ÙØªÙ Ø¨ÙØ¬Ø§ØØ Ø³ÙØ±Ø¯Ù عÙÙÙ ÙØ±ÙØ¨ÙØ§.</span> 1068 </div> 1069 1070 <div class="form__row"> 1071 <div class="field"> 1072 <label for="contactName">Ø§ÙØ§Ø³Ù </label> 1073 <input wire:model="state.full_name" id="contactName" name="name" type="text" autocomplete="name" placeholder="Ø§ÙØªØ¨ اس٠٠اÙÙØ§Ù Ù" required> 1074 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> </div> 1075 <div class="field"> 1076 <label for="contactEmail">Ø§ÙØ¨Ø±Ùد Ø§ÙØ¥ÙÙØªØ±ÙÙÙ</label> 1077 <input wire:model="state.email" id="contactEmail" name="email" type="email" inputmode="email" autocomplete="email" placeholder="[email protected]" required> 1078 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> </div> 1079 </div> 1080 1081 <div class="field"> 1082 <label for="contactSubject">اÙÙ ÙØ¶Ùع</label> 1083 <input wire:model="state.subject" id="contactSubject" name="subject" type="text" placeholder="Ù ÙØ¶Ùع Ø§ÙØ±Ø³Ø§ÙØ©"> 1084 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> </div> 1085 1086 <div class="field"> 1087 <label for="contactMessage">Ø§ÙØ±Ø³Ø§ÙØ©</label> 1088 <textarea wire:model="state.message" id="contactMessage" name="message" placeholder="Ø§ÙØªØ¨ Ø±Ø³Ø§ÙØªÙ ÙÙØ§..." required></textarea> 1089 <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]--> </div> 1090 1091 <button class="btn btn--primary" type="submit"><i class="fa-regular fa-paper-plane" aria-hidden="true"></i><span>Ø¥Ø±Ø³Ø§Ù Ø§ÙØ±Ø³Ø§ÙØ©</span></button> 1092 </form> 1093 </div> 1094 1095 </div> 1096 </div> 1097 </section> 1098 1099</div> 1100</main> 1101 1102<footer class="site-footer"> 1103 <div class="container footer__grid"> 1104 <div class="footer__col"> 1105 <a href="https://qudsn.co" class="footer__brand" aria-label="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©"> 1106 <img src="https://fsn1.your-objectstorage.com/qudsn/uploads/2026/08/file_6a6f0b28738ed.png" alt="Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©" loading="lazy" decoding="async" width="90" height="38" 1107 > 1108 </a> 1109 <p class="footer__about"> 1110 ÙÙØ¯Ù اÙÙ Ø¹Ø§ÙØ¬Ø§Øª Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© ÙØ§ÙتØÙÙÙØ§Øª اÙÙ Ø¹Ù ÙØ© اÙ٠رتبطة Ø¨Ø§ÙØ´Ø£Ù اÙÙÙØ³Ø·ÙÙÙ ÙØªØ£Ø«Ùرات٠إÙÙÙÙ ÙØ§ ÙØ¯ÙÙÙØ§ عبر ÙÙØ§Ùب ØªØ±ÙØ² عÙ٠اÙÙ ÙÙÙØ© 1111 </p> 1112 <div class="footer__social"> 1113 <a href="https://tiktok.com/@qudsn" target="_blank" rel="noopener" aria-label="Tiktok" class="footer__social-link"><i class="fab fa-tiktok" aria-hidden="true"></i></a> 1114 <a href="https://t.me/QudsN" target="_blank" rel="noopener" aria-label="Facebook" class="footer__social-link"><i class="fab fa-facebook-f" aria-hidden="true"></i></a> 1115 <a href="https://www.instagram.com/qudsn" target="_blank" rel="noopener" aria-label="Instagram" class="footer__social-link"><i class="fab fa-instagram" aria-hidden="true"></i></a> 1116 <a href="https://www.youtube.com/user/QudsNetwork" target="_blank" rel="noopener" aria-label="Youtube-play" class="footer__social-link"><i class="fab fa-youtube" aria-hidden="true"></i></a> 1117 <a href="https://t.me/QudsN" target="_blank" rel="noopener" aria-label="ÙÙØ§Ø© تÙÙÙØºØ±Ø§Ù " class="footer__social-link"><i class="fab fa-telegram" aria-hidden="true"></i></a> 1118 <a href="https://whatsapp.com/channel/0029VbCpgXo8fewqkgDTLM2z" target="_blank" rel="noopener" aria-label="ÙÙØ§Ø© ÙØ§ØªØ³Ø§Ø¨" class="footer__social-link"><i class="fab fa-whatsapp" aria-hidden="true"></i></a> 1119 <a href="https://twitter.com/QudsN" target="_blank" rel="noopener" aria-label="Twitter" class="footer__social-link"><i class="fab fa-x-twitter" aria-hidden="true"></i></a> 1120 <a href="https://t.me/Qudsn_hebrew" target="_blank" rel="noopener" aria-label="Telegram " class="footer__social-link"><i class="fab fa-telegram" aria-hidden="true"></i></a> 1121 </div> 1122 </div> 1123
1124 <nav class="footer__col" aria-label="Ø§ÙØ£Ùسا٠"> 1125 <h3 class="footer__title">Ø§ÙØ£Ùسا٠</h3> 1126 <ul class="footer__links"> 1127 <li><a 1128 href="https://qudsn.co/category_news/%D8%A3%D8%AE%D8%A8%D8%A7%D8%B1">أخبار</a> 1129 </li> 1130 <li><a 1131 href="https://qudsn.co/category_news/%D9%85%D8%AF%D9%88%D9%91%D9%86%D8%A7%D8%AA">٠دÙÙÙØ§Øª</a> 1132 </li> 1133 <li><a 1134 href="https://qudsn.co/category_news/%D8%AA%D8%B1%D8%AC%D9%85%D8%A7%D8%AA%20%D8%B9%D8%A8%D8%B1%D9%8A%D8%A9">ترج٠ات Ø¹Ø¨Ø±ÙØ©</a> 1135 </li> 1136 <li><a 1137 href="https://qudsn.co/category_news/%D9%85%D9%82%D8%A7%D9%84%D8%A7%D8%AA">Ù ÙØ§Ùات</a> 1138 </li> 1139 <li><a 1140 href="https://qudsn.co/category_news/%D8%AA%D9%82%D8%A7%D8%B1%D9%8A%D8%B1%20%D9%82%D8%AF%D8%B3">ØªÙØ§Ø±Ùر ÙØ¯Ø³</a> 1141 </li> 1142 <li><a 1143 href="https://qudsn.co/category_news/%D9%85%D8%AA%D8%A7%D8%A8%D8%B9%D8%A7%D8%AA%20%D9%82%D8%AF%D8%B3">٠تابعات ÙØ¯Ø³</a> 1144 </li> 1145 <li><a href="https://qudsn.co/all_podcast">Ø¨ÙØ¯Ùاست ÙØ¯Ø³</a></li> 1146 </ul> 1147 </nav> 1148 1149 <nav class="footer__col" aria-label="Ø±ÙØ§Ø¨Ø·"> 1150 <h3 class="footer__title">Ø±ÙØ§Ø¨Ø·</h3> 1151 <ul class="footer__links"> 1152 <li><a href="https://qudsn.co/about_us">Ù Ù ÙØÙ</a></li> 1153 <li><a href="https://qudsn.co/contact_us">ØªÙØ§ØµÙ Ù Ø¹ÙØ§</a></li> 1154 <li><a href="https://qudsn.co/all_posts">Ù٠اÙÙ ÙØ§Ùات</a></li> 1155 <li><a href="https://qudsn.co/all_videos">ÙØ¯Ø³ ÙÙØ¯ÙÙ</a></li> 1156 <li><a href="https://qudsnen.com" target="_blank" rel="noopener">English</a></li> 1157 </ul> 1158 </nav> 1159 1160 1161 <div class="footer__col"> 1162 <h3 class="footer__title">تابعÙÙØ§ ٠باشرة</h3> 1163 1164 <a href="https://t.me/QudsN" class="footer-channel" 1165 style="--ch:#26a5e4" target="_blank" rel="noopener"> 1166 <i class="fab fa-telegram" aria-hidden="true"></i> 1167 <span><span class="footer-channel__name">ÙÙØ§Ø© تÙÙÙØºØ±Ø§Ù </span> 1168 <span class="footer-channel__sub">آخر Ø§ÙØ£Ø®Ø¨Ø§Ø± Ø§ÙØ¹Ø§Ø¬ÙØ©</span> 1169 </span> 1170 </a> 1171 1172 <a href="https://whatsapp.com/channel/0029VbCpgXo8fewqkgDTLM2z" class="footer-channel" 1173 style="--ch:#25d366" target="_blank" rel="noopener"> 1174 <i class="fab fa-whatsapp" aria-hidden="true"></i> 1175 <span><span class="footer-channel__name">ÙÙØ§Ø© ÙØ§ØªØ³Ø§Ø¨</span> 1176 <span class="footer-channel__sub">Ø§ÙØ¶Ù Ø§ÙØ¢Ù</span> 1177 </span> 1178 </a> 1179 </div> 1180 </div> 1181 1182 <div class="container footer__bottom"> 1183 <p>Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ© Ø¬Ù ÙØ¹ ØÙÙ٠اÙÙØ´Ø± Ù ØÙÙØ¸Ø© © 2026</p> 1184 <p>تص٠ÙÙ ÙØªØ·ÙÙØ± Ø¨Ø¹ÙØ§ÙØ© ÙØªØ¬Ø±Ø¨Ø© ÙØ±Ø§Ø¡Ø© Ø£ÙØ¶Ù</p> 1185 </div> 1186</footer> 1187 1188 1189 <div class="breaking-float" id="qdBreakingFloat" hidden 1190 data-endpoint="/breaking_latest" 1191 data-interval="90" role="status" aria-live="polite"> 1192 <div class="breaking-float__inner">
1193 <span class="breaking-float__label"><span class="breaking-float__pulse" aria-hidden="true"></span>عاجÙ</span> 1194 <a class="breaking-float__title" href="#"></a> 1195 <button type="button" class="breaking-float__close" aria-label="Ø¥ØºÙØ§Ù Ø´Ø±ÙØ· Ø§ÙØ¹Ø§Ø¬Ù"> 1196 <i class="fa-solid fa-xmark" aria-hidden="true"></i> 1197 </button> 1198 </div> 1199 </div> 1200 1201 1202<div class="search-overlay" id="searchOverlay" role="dialog" aria-modal="true" aria-label="Ø§ÙØ¨ØØ« ÙÙ Ø´Ø¨ÙØ© ÙØ¯Ø³" hidden> 1203 <div class="search-box" role="document"> 1204 <div class="search-box__head"> 1205 <h3 class="search-box__title">Ø§Ø¨ØØ« ÙÙ Ø´Ø¨ÙØ© ÙØ¯Ø³ Ø§ÙØ¥Ø®Ø¨Ø§Ø±ÙØ©</h3> 1206 <button type="button" class="icon-btn" id="searchClose" aria-label="Ø¥ØºÙØ§Ù Ø§ÙØ¨ØØ«"><i 1207 class="fa-solid fa-xmark" aria-hidden="true"></i></button> 1208 </div> 1209 <form class="search-box__field" role="search" action="https://qudsn.co/search_page" method="get"> 1210 <i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i> 1211 <label class="sr-only" for="searchInput">ÙÙÙ Ø© Ø§ÙØ¨ØØ«</label> 1212 <input id="searchInput" name="search" type="search" autocomplete="off" 1213 role="combobox" aria-expanded="false" aria-controls="searchSuggest" 1214 placeholder="Ø§Ø¨ØØ« ÙÙ Ø§ÙØ£Ø®Ø¨Ø§Ø± ÙØ§ÙØªÙØ§Ø±Ùر ÙØ§ÙÙ ÙØ§Ùات..."> 1215 </form> 1216 1217 1218 <div class="search-suggest" id="searchSuggest" role="listbox" aria-label="Ø§ÙØªØ±Ø§ØØ§Øª Ø§ÙØ¨ØØ«" hidden></div> 1219 1220 1221 <div class="search-box__trending" id="searchTrending"> 1222 <span class="search-box__trending-label">Ø§ÙØ£Ùثر Ø¨ØØ«Ùا</span> 1223 <div class="search-box__tags"> 1224 <a href="https://qudsn.co/search_page?search=%D8%BA%D8%B2%D8%A9" class="tag">غزة</a> 1225 <a href="https://qudsn.co/search_page?search=%D8%A7%D9%84%D8%B6%D9%81%D8%A9+%D8%A7%D9%84%D8%BA%D8%B1%D8%A8%D9%8A%D8%A9" class="tag">Ø§ÙØ¶ÙØ© Ø§ÙØºØ±Ø¨ÙØ©</a> 1226 <a href="https://qudsn.co/search_page?search=%D8%A7%D9%84%D9%82%D8%AF%D8%B3" class="tag">اÙÙØ¯Ø³</a> 1227 <a href="https://qudsn.co/search_page?search=%D8%A7%D9%84%D8%A3%D8%B3%D8%B1%D9%89" class="tag">Ø§ÙØ£Ø³Ø±Ù</a> 1228 <a href="https://qudsn.co/search_page?search=%D9%88%D9%82%D9%81+%D8%A5%D8%B7%D9%84%D8%A7%D9%82+%D8%A7%D9%84%D9%86%D8%A7%D8%B1" class="tag">ÙÙÙ Ø¥Ø·ÙØ§Ù اÙÙØ§Ø±</a> 1229 <a href="https://qudsn.co/search_page?search=%D8%AA%D8%B1%D8%AC%D9%85%D8%A7%D8%AA+%D8%B9%D8%A8%D8%B1%D9%8A%D8%A9" class="tag">ترج٠ات Ø¹Ø¨Ø±ÙØ©</a> 1230 </div> 1231 </div> 1232 </div> 1233</div>
vendor: 111 bytes, lines 1233-1236
1233 1234 1235 1236<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1236<script src="/assets/main/quds_ar/js/main.js?v=1789735121" data-swiper-src="/assets/main/quds_ar/vendor/swiper/swiper-bundle.min.js?v=1789735121" defer></script>
1236 1237 1238
1239<script> 1240(function () { 1241 var input = document.getElementById('searchInput'); 1242 var box = document.getElementById('searchSuggest'); 1243 var trending = document.getElementById('searchTrending'); 1244 if (!input || !box) return; 1245 1246 1247 var endpoint = "\/search_suggest"; 1248 var timer = null, controller = null, activeIdx = -1; 1249 1250 function esc(s) { 1251 return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { 1252 return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; 1253 }); 1254 } 1255 function expanded(v) { input.setAttribute('aria-expanded', v ? 'true' : 'false'); } 1256 function showTrending() { 1257 box.hidden = true; box.innerHTML = ''; activeIdx = -1; expanded(false); 1258 if (trending) trending.hidden = false; 1259 } 1260 function state(html) { 1261 if (trending) trending.hidden = true; 1262 box.hidden = false; expanded(true); 1263 box.innerHTML = '<div class="search-suggest__state">' + html + '</div>'; 1264 } 1265 function setActive(i) { 1266 var nodes = box.querySelectorAll('.search-suggest__item'); 1267 nodes.forEach(function (n) { n.classList.remove('is-active'); }); 1268 if (i >= 0 && i < nodes.length) { activeIdx = i; nodes[i].classList.add('is-active'); nodes[i].scrollIntoView({ block: 'nearest' }); } 1269 else { activeIdx = -1; } 1270 } 1271 function render(items) { 1272 if (trending) trending.hidden = true; 1273 box.hidden = false; expanded(true); activeIdx = -1; 1274 if (!items || !items.length) { 1275 box.innerHTML = '<div class="search-suggest__state">ÙØ§ ØªÙØ¬Ø¯ ÙØªØ§Ø¦Ø¬ Ù Ø·Ø§Ø¨ÙØ© â Ø§Ø¶ØºØ· Enter ÙÙØ¨ØØ« اÙÙØ§Ù Ù</div>'; 1276 return; 1277 } 1278 var ICONS = { article: 'fa-newspaper', podcast: 'fa-microphone', video: 'fa-play', tag: 'fa-hashtag' }; 1279 box.innerHTML = items.map(function (it) { 1280 var ic = ICONS[it.type] || 'fa-newspaper'; 1281 return '<a class="search-suggest__item search-suggest__item--' + (it.type || 'article') + '" role="option" href="' + esc(it.url) + '">' + 1282 '<span class="search-suggest__cat"><i class="fa-solid ' + ic + '" aria-hidden="true"></i>' + esc(it.category) + '</span>' + 1283 '<span class="search-suggest__title">' + esc(it.title) + '</span></a>'; 1284 }).join(''); 1285 } 1286 function fetchSuggest(q) { 1287 if (controller) controller.abort(); 1288 controller = new AbortController(); 1289 state('<i class="fa-solid fa-spinner fa-spin" aria-hidden="true"></i> Ø¬Ø§Ø±Ù Ø§ÙØ¨ØØ«â¦'); 1290 fetch(endpoint + '?q=' + encodeURIComponent(q), { signal: controller.signal, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) 1291 .then(function (r) { return r.json(); }) 1292 .then(function (d) { render(d.items || []); }) 1293 .catch(function (e) { if (e.name !== 'AbortError') showTrending(); }); 1294 } 1295 1296 input.addEventListener('input', function () { 1297 var q = input.value.trim(); 1298 clearTimeout(timer); 1299 if (q.length < 2) { showTrending(); return; } 1300 timer = setTimeout(function () { fetchSuggest(q); }, 220); 1301 }); 1302 input.addEventListener('keydown', function (e) { 1303 var nodes = box.querySelectorAll('.search-suggest__item'); 1304 if (box.hidden || !nodes.length) return; 1305 if (e.key === 'ArrowDown') { e.preventDefault(); setActive(Math.min(activeIdx + 1, nodes.length - 1)); } 1306 else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(Math.max(activeIdx - 1, 0)); } 1307 else if (e.key === 'Enter' && activeIdx >= 0) { e.preventDefault(); window.location.href = nodes[activeIdx].getAttribute('href'); } 1308 }); 1309})(); 1310</script>
1310 1311 1312
1313<script> 1314(function () { 1315 var forms = document.querySelectorAll('.js-news-form'); 1316 if (!forms.length) return; 1317 // Path only, not route()'s absolute URL. That URL is built from APP_URL, so 1318 // an http:// value behind an https:// site makes the browser block the 1319 // request as mixed content and the form reports Â«ØªØ¹Ø°ÙØ± Ø§ÙØ§ØªØµØ§Ù Ø¨Ø§ÙØ®Ø§Ø¯Ù » 1320 // even though the endpoint is fine. A relative path always matches the 1321 // scheme and host the visitor is actually on. 1322 var endpoint = "\/newsletter_subscribe"; 1323 1324 forms.forEach(function (form) { 1325 form.addEventListener('submit', function (e) { 1326 e.preventDefault(); 1327 var wrap = form.closest('.newsletter__form-wrap') || form.parentNode; 1328 var input = form.querySelector('input[type="email"], input[name="email"]'); 1329 var success = wrap.querySelector('.news-form__success'); 1330 var error = wrap.querySelector('.news-form__error'); 1331 var btn = form.querySelector('button[type="submit"]'); 1332 if (error) { error.hidden = true; error.textContent = ''; } 1333 if (input && !input.checkValidity()) { input.reportValidity(); return; } 1334 1335 var email = input ? input.value.trim() : ''; 1336 if (btn) btn.disabled = true; 1337 1338 fetch(endpoint, { 1339 method: 'POST', 1340 headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, 1341 body: JSON.stringify({ email: email }) 1342 }) 1343 .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, body: d }; }); }) 1344 .then(function (res) { 1345 if (res.ok && res.body && res.body.ok) { 1346 form.hidden = true; 1347 if (success) success.hidden = false; 1348 } else { 1349 if (error) { error.textContent = (res.body && res.body.message) || 'ØªØ¹Ø°ÙØ± Ø§ÙØ§Ø´ØªØ±Ø§ÙØ ØØ§Ù٠٠رة أخرÙ.'; error.hidden = false; } 1350 if (btn) btn.disabled = false; 1351 } 1352 }) 1353 .catch(function () { 1354 if (error) { error.textContent = 'ØªØ¹Ø°ÙØ± Ø§ÙØ§ØªØµØ§Ù Ø¨Ø§ÙØ®Ø§Ø¯Ù Ø ØØ§Ù٠٠رة أخرÙ.'; error.hidden = false; } 1355 if (btn) btn.disabled = false; 1356 }); 1357 }); 1358 }); 1359})(); 1360</script>
1360 1361 1362 1363 1364 <style> 1365 /* ---------- Carousel ---------- */ 1366 .post-gallery { margin: 1.5em auto; max-width: 100%; } 1367 .pg-carousel { position: relative; border-radius: 12px; overflow: hidden; background: #0b0f1a; } 1368 .pg-viewport { overflow: hidden; } 1369 .pg-track { display: flex; transition: transform .4s cubic-bezier(.22,.61,.36,1); will-change: transform; } 1370 .pg-slide { flex: 0 0 100%; min-width: 100%; } 1371 .pg-slide img { 1372 display: block; width: 100%; height: 100%; 1373 aspect-ratio: 16 / 9; object-fit: cover; cursor: zoom-in; margin: 0; 1374 } 1375 /* Grid layout â media side by side instead of one at a time. */ 1376 .pg-grid { 1377 display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); 1378 gap: 10px; margin: 28px auto; max-width: 100%; 1379 } 1380 /* Wider grid, opt-in via data-pg-layout="grid-3". */ 1381 .pg-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } 1382 /* Mosaic: the lead photo carries the block, the rest support it â the 1383 shape newsrooms actually use for a photo set. */ 1384 .pg-mosaic { grid-template-columns: repeat(4, minmax(0, 1fr)); } 1385 .pg-mosaic .pg-cell:first-child { grid-column: span 2; grid-row: span 2; } 1386 .pg-grid .pg-cell { position: relative; min-width: 0; border-radius: 10px; overflow: hidden; background: #0b1220; } 1387 .pg-grid .pg-cell img, 1388 .pg-grid .pg-cell video { 1389 display: block; width: 100%; height: 100%; 1390 aspect-ratio: 16 / 9; object-fit: cover; margin: 0; 1391 } 1392 .pg-grid .pg-cell video { object-fit: contain; background: #000; } 1393 .pg-grid .pg-cell img { cursor: zoom-in; } 1394 .pg-grid .pg-cell--doc { display: grid; place-items: center; aspect-ratio: 16 / 9; padding: 14px; } 1395 /* A lone item should not sit in a half-width column. */ 1396 .pg-grid .pg-cell:only-child { grid-column: 1 / -1; } 1397 @media (max-width: 640px) { 1398 .pg-grid, .pg-grid--3 { grid-template-columns: 1fr; } 1399 /* Mosaic keeps two columns on a phone â one column would erase it. */ 1400 .pg-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); } 1401 } 1402 1403 /* ---------- Aspect ratio (data-pg-ratio) ---------- 1404 Absent = 16/9 cover, exactly as every gallery authored before this. */ 1405 [data-pg-ratio="4-3"] .pg-slide img, [data-pg-ratio="4-3"] .pg-cell img, 1406 [data-pg-ratio="4-3"] .pg-slide video, [data-pg-ratio="4-3"] .pg-cell video { aspect-ratio: 4 / 3; } 1407 [data-pg-ratio="1-1"] .pg-slide img, [data-pg-ratio="1-1"] .pg-cell img, 1408 [data-pg-ratio="1-1"] .pg-slide video, [data-pg-ratio="1-1"] .pg-cell video { aspect-ratio: 1 / 1; } 1409 /* "auto" = never crop. Portrait photography survives; the cap keeps a tall 1410 shot from pushing the article off the screen. */ 1411 [data-pg-ratio="auto"] .pg-slide img, [data-pg-ratio="auto"] .pg-cell img { 1412 aspect-ratio: auto; height: auto; max-height: 72vh; object-fit: contain; background: #0b0f1a; 1413 } 1414 [data-pg-ratio="auto"].pg-grid .pg-cell { background: transparent; } 1415 1416 /* ---------- Captions ---------- 1417 Carousel: ONE strip under the viewport that follows the active slide, so 1418 slide heights stay uniform (a per-slide caption made the track jump). 1419 Grid: a caption under each cell. */ 1420 .pg-cap { 1421 font-size: 13px; line-height: 1.65; color: #475569; 1422 padding: 8px 12px 0; text-align: center; 1423 } 1424 .pg-cap:empty { display: none; } 1425 .pg-grid .pg-cap { text-align: start; padding: 6px 2px 0; font-size: 12.5px; } 1426 /* The cell is the caption's parent, so it must not clip it. */ 1427 .pg-grid .pg-cell--captioned { overflow: visible; background: transparent; } 1428 .pg-grid .pg-cell--captioned > img, 1429 .pg-grid .pg-cell--captioned > video { border-radius: 10px; } 1430 1431 /* ---------- "+N more" overflow badge ---------- */ 1432 .pg-more { 1433 position: absolute; inset: 0; display: grid; place-items: center; 1434 background: rgba(6, 9, 18, .62); color: #fff; 1435 font-size: 22px; font-weight: 800; letter-spacing: .02em; 1436 cursor: zoom-in; direction: ltr; border: 0; width: 100%; padding: 0; 1437 } 1438 .pg-more:hover { background: rgba(6, 9, 18, .74); } 1439 1440 /* Video slide: same 16/9 frame as an image slide, native controls. */ 1441 .pg-slide video { 1442 display: block; width: 100%; height: 100%; 1443 aspect-ratio: 16 / 9; object-fit: contain; background: #000; margin: 0; 1444 } 1445 /* Audio slide: a centred player with the track name above it. */ 1446 .pg-slide--audio, .pg-grid .pg-cell--audio { 1447 display: grid; place-items: center; align-content: center; gap: 10px; 1448 aspect-ratio: 16 / 9; padding: 18px; 1449 } 1450 .pg-slide--audio audio, .pg-grid .pg-cell--audio audio { 1451 width: min(100%, 420px); height: 40px; display: block; margin: 0; 1452 } 1453 /* Track name comes from data-pg-name; ::before keeps the markup a lone <audio>. */ 1454 .pg-slide--audio::before, .pg-grid .pg-cell--audio::before { 1455 content: attr(data-pg-label); 1456 font-size: 13px; font-weight: 700; color: #e2e8f0; text-align: center; 1457 max-width: 90%; line-height: 1.6; word-break: break-word; 1458 } 1459 .pg-slide--audio:not([data-pg-label])::before, 1460 .pg-grid .pg-cell--audio:not([data-pg-label])::before { content: none; } 1461 1462 /* Document slide (PDF / Word / anything else): a card, not a preview. */ 1463 .pg-slide--doc { display: grid; place-items: center; aspect-ratio: 16 / 9; padding: 18px; } 1464 .pg-doc { 1465 display: flex; flex-direction: column; align-items: center; gap: 6px; 1466 max-width: 90%; padding: 18px 22px; border-radius: 12px; text-decoration: none; 1467 background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.16); 1468 color: #f1f5f9; text-align: center; transition: background .15s ease, transform .15s ease; 1469 } 1470 .pg-doc:hover { background: rgba(255,255,255,.12); transform: translateY(-1px); } 1471 /* The glyph is drawn in CSS so no icon font is required by any theme. */ 1472 .pg-doc::before { 1473 content: ''; width: 34px; height: 42px; margin-bottom: 4px; 1474 background: #e2e8f0; border-radius: 3px; 1475 clip-path: polygon(0 0, 68% 0, 100% 26%, 100% 100%, 0 100%); 1476 } 1477 .pg-doc__name { font-size: 14px; font-weight: 700; word-break: break-word; line-height: 1.5; } 1478 .pg-doc__meta { font-size: 11.5px; opacity: .75; direction: ltr; } 1479 1480 .pg-arrow { 1481 position: absolute; top: 50%; transform: translateY(-50%); 1482 width: 42px; height: 42px; border: none; border-radius: 50%; 1483 background: rgba(0,0,0,.5); color: #fff; font-size: 22px; line-height: 1; 1484 cursor: pointer; display: grid; place-items: center; z-index: 2; padding: 0; 1485 transition: background .15s ease, opacity .15s ease; 1486 } 1487 .pg-arrow:hover { background: rgba(0,0,0,.78); } 1488 .pg-prev { inset-inline-start: 10px; } 1489 .pg-next { inset-inline-end: 10px; } 1490 .pg-counter { 1491 position: absolute; top: 10px; inset-inline-end: 12px; z-index: 2; 1492 background: rgba(0,0,0,.55); color: #fff; font-size: 12px; font-weight: 600; 1493 padding: 3px 9px; border-radius: 999px; direction: ltr; 1494 } 1495 .pg-dots { display: flex; gap: 2px; justify-content: center; padding: 10px; flex-wrap: wrap; } 1496 /* a11y target-size: same 8px visual dot, but a 24x24 hit area (the dot is 1497 drawn as a centered radial gradient inside the bigger button) */ 1498 .pg-dot { 1499 width: 24px; height: 24px; border-radius: 50%; border: none; padding: 0; cursor: pointer; 1500 background: radial-gradient(circle 4px at center, rgba(255,255,255,.35) 100%, transparent 0); 1501 transition: background .15s ease, transform .15s ease; 1502 } 1503 .pg-dot.is-active { background: radial-gradient(circle 5px at center, #fff 100%, transparent 0); } 1504 .pg-carousel--single .pg-arrow, .pg-carousel--single .pg-dots, .pg-carousel--single .pg-counter { display: none; } 1505 1506 /* ---------- Lightbox ---------- */ 1507 .pg-lightbox { 1508 position: fixed; inset: 0; z-index: 2147483000; 1509 background: rgba(6,8,15,.94); display: none; 1510 align-items: center; justify-content: center; 1511 opacity: 0; transition: opacity .2s ease; 1512 touch-action: none; 1513 } 1514 .pg-lightbox.is-open { display: flex; opacity: 1; } 1515 .pg-lb-stage { position: relative; width: 100%; height: 100%; display: grid; place-items: center; padding: 56px 16px; } 1516 .pg-lb-img { max-width: 94vw; max-height: 86vh; object-fit: contain; border-radius: 6px; box-shadow: 0 10px 40px rgba(0,0,0,.5); user-select: none; } 1517 .pg-lb-btn { 1518 position: absolute; border: none; background: rgba(255,255,255,.12); color: #fff; 1519 cursor: pointer; display: grid; place-items: center; border-radius: 50%; 1520 padding: 0; line-height: 1; transition: background .15s ease; 1521 /* The â® / ⯠ornaments are Bidi_Mirrored=Yes â inside an RTL page they get 1522 flipped by the text engine. Force LTR so the glyph renders as authored. 1523 (Buttons are positioned with physical left/right, so this is safe.) */ 1524 direction: ltr; 1525 } 1526 .pg-lb-btn:hover { background: rgba(255,255,255,.26); } 1527 .pg-lb-close { top: 16px; left: 50%; transform: translateX(-50%); width: 44px; height: 44px; font-size: 26px; } 1528 .pg-lb-nav { top: 50%; transform: translateY(-50%); width: 52px; height: 52px; font-size: 30px; } 1529 /* Physical left/right (NOT logical) so the arrows always sit & point outward, 1530 immune to however a theme sets direction on the page or its buttons. */
1531 .pg-lb-prev { left: 16px; } 1532 .pg-lb-next { right: 16px; } 1533 .pg-lb-counter { position: absolute; top: 24px; inset-inline-start: 18px; color: #e5e7eb; font-size: 13px; font-weight: 600; direction: ltr; } 1534 /* Zoom: the image scales inside the stage, which then pans by drag. */ 1535 .pg-lb-img { transition: transform .18s ease; transform-origin: center center; } 1536 .pg-lb-img.is-zoomed { cursor: grab; max-width: none; max-height: none; } 1537 .pg-lb-tools { 1538 position: absolute; top: 16px; inset-inline-end: 18px; 1539 display: flex; align-items: center; gap: 6px; direction: ltr; 1540 } 1541 .pg-lb-tool { 1542 width: 34px; height: 34px; border: 0; border-radius: 8px; cursor: pointer; 1543 background: rgba(255,255,255,.12); color: #fff; font-size: 16px; line-height: 1; 1544 display: grid; place-items: center; text-decoration: none; padding: 0; 1545 transition: background .15s ease; 1546 } 1547 .pg-lb-tool:hover { background: rgba(255,255,255,.26); } 1548 .pg-lb-zoomlabel { color: #cbd5e1; font-size: 12px; font-weight: 700; min-width: 40px; text-align: center; } 1549 .pg-lb-caption { 1550 position: absolute; bottom: 14px; inset-inline: 0; text-align: center; 1551 color: #e5e7eb; font-size: 14px; padding: 0 60px; line-height: 1.5; 1552 text-shadow: 0 1px 3px rgba(0,0,0,.8); 1553 } 1554 @media (max-width: 576px) { 1555 .pg-slide img { aspect-ratio: 4 / 3; } 1556 .pg-lb-nav { width: 42px; height: 42px; font-size: 24px; } 1557 .pg-lb-caption { font-size: 12.5px; padding: 0 16px; bottom: 8px; } 1558 } 1559</style>
1560<script> 1561(function () { 1562 if (window.__postGalleryReady) return; 1563 window.__postGalleryReady = true; 1564 1565 // Control labels come from the SITE's lang files. They used to be baked 1566 // into this script â an Arabic dot label ("Ø§ÙØ§ÙØªÙØ§Ù Ø¥ÙÙ Ø§ÙØµÙرة") shipped 1567 // to English sites, and English Previous/Next/Close shipped to Arabic 1568 // ones. Defaults keep the partial safe if a key is ever missing. 1569 var T = Object.assign({ 1570 prev: 'Previous', next: 'Next', close: 'Close', 1571 goTo: 'Go to image', zoomIn: 'Zoom in', zoomOut: 'Zoom out', 1572 openOriginal: 'Open the original', 1573 }, {"prev":"\u0627\u0644\u0633\u0627\u0628\u0642","next":"\u0627\u0644\u062a\u0627\u0644\u064a","close":"\u0625\u063a\u0644\u0627\u0642","goTo":"\u0627\u0644\u0627\u0646\u062a\u0642\u0627\u0644 \u0625\u0644\u0649 \u0627\u0644\u0635\u0648\u0631\u0629","zoomIn":"\u062a\u0643\u0628\u064a\u0631","zoomOut":"\u062a\u0635\u063a\u064a\u0631","openOriginal":"\u0641\u062a\u062d \u0627\u0644\u0635\u0648\u0631\u0629 \u0627\u0644\u0623\u0635\u0644\u064a\u0629"} || {}); 1574 1575 // Direction is read from each element's OWN computed `direction` â never from 1576 // <html>. Some themes keep dir="rtl" on <html> but render the body/overlay LTR 1577 // via CSS; reading the element keeps the arrow GLYPH, its logical POSITION and 1578 // the swipe/key LOGIC all derived from one source, so they can never disagree. 1579 function elRTL(el) { try { return getComputedStyle(el).direction === 'rtl'; } catch (e) { return false; } } 1580 // prev sits on the inline-start edge, next on inline-end, so the glyph that 1581 // "points outward" flips with direction. LTR: prev â® / next â¯. RTL: mirrored. 1582 function prevGlyph(rtl) { return rtl ? '❯' : '❮'; } 1583 function nextGlyph(rtl) { return rtl ? '❮' : '❯'; } 1584 // Images eligible for the lightbox: gallery slides, editor figures, and the 1585 // usual content wrappers. Header/footer/logo images are excluded by scope. 1586 var IMG_SELECTOR = [ 1587 '[data-post-gallery] img', 1588 'figure.post-image-figure img', 1589 '.post-content img', '.article-content img', '.entry-content img', 1590 '.single-post img', '[data-post-body] img' 1591 ].join(','); 1592 1593 // ...but those wrappers are PAGE-level: `.article-content` on several 1594 // themes wraps the header too, so the byline avatar and the share 1595 // control's icon sat inside it and the lightbox happily opened them as 1596 // photographs. An <img> that lives inside an interactive control or a 1597 // byline is chrome, never a picture the reader asked to enlarge. 1598 var CHROME_SELECTOR = [ 1599 'button', '[role="button"]', 1600 '.share-wrap', '.share-menu', '.social-share__link', 1601 '.post-meta', '.article-meta', '.author-face', 1602 '[data-pg-skip]' 1603 ].join(','); 1604 1605 /** 1606 * Is this <img> something the lightbox should open? 1607 * Explicit gallery markup always wins; otherwise the image must sit in a 1608 * content wrapper, outside chrome, and be big enough to be a photograph. 1609 */ 1610 function isLightboxImage(img) { 1611 if (!img || !img.matches || !img.matches(IMG_SELECTOR)) return false; 1612 if (img.closest('[data-post-gallery]')) return true; 1613 if (img.closest(CHROME_SELECTOR)) return false; 1614 // An icon is not a photograph. Measured, not guessed â and only when 1615 // the image actually has a box, so a not-yet-laid-out image is kept. 1616 var r = img.getBoundingClientRect(); 1617 if (r.width && r.height && r.width < 40 && r.height < 40) return false; 1618 return true; 1619 } 1620 1621 /* ----------------------------- Carousel ----------------------------- */ 1622 // A slide can be an image, a video, or a document card. Images keep being 1623 // plain <img> children (so galleries authored before this stayed valid and 1624 // the lightbox still finds them); the other kinds are opt-in markup emitted 1625 // by the editor. Direct children only â never reaches into a card's insides. 1626 var MEDIA_SELECTOR = ':scope > img, :scope > video, :scope > audio, :scope > [data-pg-doc]'; 1627 function galleryMedia(gal) { 1628 try { 1629 return Array.prototype.slice.call(gal.querySelectorAll(MEDIA_SELECTOR)); 1630 } catch (e) { 1631 // :scope is unsupported on very old engines â fall back to filtering. 1632 return Array.prototype.filter.call(gal.children, function (el) { 1633 return el.matches && el.matches('img, video, audio, [data-pg-doc]'); 1634 }); 1635 } 1636 } 1637 1638 /** Slide modifier for a media node â images get none (the default look). */ 1639 function slideKindClass(node) {
1640 if (node.tagName === 'VIDEO') return 'pg-slide--video'; 1641 if (node.tagName === 'AUDIO') return 'pg-slide--audio'; 1642 if (node.hasAttribute && node.hasAttribute('data-pg-doc')) return 'pg-slide--doc'; 1643 return ''; 1644 } 1645 1646 /** Audio slides label themselves from the node's data-pg-name (CSS ::before). */ 1647 function applySlideLabel(wrapper, node) { 1648 if (node.tagName !== 'AUDIO') return; 1649 var name = node.getAttribute('data-pg-name'); 1650 if (name) wrapper.setAttribute('data-pg-label', name); 1651 } 1652 1653 /** A media node's caption, authored on the node itself as data-pg-caption. */ 1654 function mediaCaption(node) { 1655 return (node.getAttribute && node.getAttribute('data-pg-caption') || '').trim(); 1656 } 1657 1658 /** 1659 * Below-the-fold gallery images should not block the article's own render. 1660 * The editor now stamps loading/decoding at insert time, but EVERY gallery 1661 * authored before that is still out there â mark them here. The first item 1662 * stays eager: it is often the one in view. 1663 */ 1664 function applyLoadingHints(media) { 1665 media.forEach(function (node, i) { 1666 if (node.tagName !== 'IMG' || node.hasAttribute('loading')) return; 1667 if (i > 0) node.setAttribute('loading', 'lazy'); 1668 node.setAttribute('decoding', 'async'); 1669 }); 1670 } 1671 1672 function enhanceGalleries(root) { 1673 var scope = root || document; 1674 var galleries = scope.querySelectorAll('[data-post-gallery]:not([data-pg-ready])'); 1675 galleries.forEach(function (gal) { 1676 var media = galleryMedia(gal); 1677 if (!media.length) { gal.setAttribute('data-pg-ready', '1'); return; } 1678 1679 applyLoadingHints(media); 1680 1681 // Grid family: everything side by side, no arrows/dots/counter. 1682 // Opt-in via data-pg-layout â a block WITHOUT the attribute stays a 1683 // carousel, which is every gallery authored before this existed, 1684 // and "grid" keeps meaning exactly the two-column layout it always 1685 // did. New values are additive only. 1686 var layout = gal.getAttribute('data-pg-layout') || ''; 1687 var GRIDS = { 'grid': '', 'grid-3': 'pg-grid--3', 'mosaic': 'pg-mosaic' }; 1688 if (layout in GRIDS) { 1689 gal.classList.add('pg-grid'); 1690 if (GRIDS[layout]) gal.classList.add(GRIDS[layout]); 1691 gal.setAttribute('data-pg-ready', '1'); 1692 1693 // data-pg-max caps how many cells are drawn; the last visible one 1694 // wears a "+N" badge that opens the lightbox at the first hidden 1695 // item, so nothing becomes unreachable. 1696 var max = parseInt(gal.getAttribute('data-pg-max') || '0', 10); 1697 var shown = (max > 0 && max < media.length) ? max : media.length; 1698 var hidden = media.length - shown; 1699 1700 var cells = document.createDocumentFragment(); 1701 media.forEach(function (node, i) { 1702 var cap = mediaCaption(node); 1703 if (i >= shown) { 1704 // Kept in the DOM (hidden) so the lightbox list still 1705 // contains it â only its cell is not drawn. 1706 node.style.display = 'none'; 1707 cells.appendChild(node); 1708 return; 1709 } 1710 var cell = document.createElement('div'); cell.className = 'pg-cell'; 1711 var cellCls = slideKindClass(node).replace('pg-slide--', 'pg-cell--'); 1712 if (cellCls) cell.classList.add(cellCls); 1713 if (cap) cell.classList.add('pg-cell--captioned'); 1714 applySlideLabel(cell, node); 1715 cell.appendChild(node); 1716 if (cap) { 1717 var c = document.createElement('div'); c.className = 'pg-cap'; c.textContent = cap; 1718 cell.appendChild(c); 1719 } 1720 if (hidden > 0 && i === shown - 1) { 1721 var more = document.createElement('button'); 1722 more.type = 'button'; more.className = 'pg-more'; 1723 more.setAttribute('data-pg-more', String(shown)); 1724 more.textContent = '+' + hidden; 1725 cell.appendChild(more); 1726 } 1727 cells.appendChild(cell); 1728 }); 1729 gal.innerHTML = ''; 1730 gal.appendChild(cells); 1731 // Images keep their [data-post-gallery] img ancestry, so the 1732 // lightbox picks them up here exactly as in a carousel. 1733 return; 1734 } 1735 1736 gal.classList.add('pg-carousel'); 1737 gal.setAttribute('data-pg-ready', '1'); 1738 if (media.length === 1) gal.classList.add('pg-carousel--single'); 1739 1740 var viewport = document.createElement('div'); viewport.className = 'pg-viewport'; 1741 var track = document.createElement('div'); track.className = 'pg-track';
1742 media.forEach(function (node) { 1743 var slide = document.createElement('div'); slide.className = 'pg-slide'; 1744 var kindCls = slideKindClass(node); 1745 if (kindCls) slide.classList.add(kindCls); 1746 applySlideLabel(slide, node); 1747 slide.appendChild(node); 1748 track.appendChild(slide); 1749 }); 1750 viewport.appendChild(track); 1751 gal.innerHTML = ''; 1752 gal.appendChild(viewport); 1753 1754 // ONE caption strip under the viewport, refreshed per slide â a 1755 // per-slide caption made every slide a different height and the 1756 // track jumped on each move. 1757 if (media.some(function (n) { return mediaCaption(n); })) { 1758 var capEl = document.createElement('div'); 1759 capEl.className = 'pg-cap'; 1760 gal.appendChild(capEl); 1761 } 1762 1763 gal.setAttribute('data-pg-index', '0'); 1764 var galRTL = elRTL(gal); 1765 var counter = document.createElement('div'); counter.className = 'pg-counter'; 1766 // <bdo dir="ltr"> stops the Bidi_Mirrored â®/⯠from being flipped by an 1767 // RTL page, while the button keeps its logical (direction-aware) position. 1768 var prev = document.createElement('button'); prev.type = 'button'; prev.className = 'pg-arrow pg-prev'; prev.setAttribute('data-pg-go', 'prev'); prev.setAttribute('aria-label', T.prev); prev.innerHTML = '<bdo dir="ltr">' + prevGlyph(galRTL) + '</bdo>'; 1769 var next = document.createElement('button'); next.type = 'button'; next.className = 'pg-arrow pg-next'; next.setAttribute('data-pg-go', 'next'); next.setAttribute('aria-label', T.next); next.innerHTML = '<bdo dir="ltr">' + nextGlyph(galRTL) + '</bdo>'; 1770 var dots = document.createElement('div'); dots.className = 'pg-dots'; 1771 media.forEach(function (_, i) { 1772 var d = document.createElement('button'); d.type = 'button'; d.className = 'pg-dot' + (i === 0 ? ' is-active' : ''); d.setAttribute('data-pg-dot', i); 1773 d.setAttribute('aria-label', T.goTo + ' ' + (i + 1)); 1774 dots.appendChild(d); 1775 }); 1776 gal.appendChild(counter); gal.appendChild(prev); gal.appendChild(next); gal.appendChild(dots); 1777 applyCarousel(gal); 1778 }); 1779 } 1780 1781 // Controls are handled by the ONE delegated listener below â not per-button 1782 // listeners â so navigation keeps working even when Livewire/theme scripts 1783 // re-render the post body and drop per-node listeners (that's exactly why the 1784 // lightbox survived but the arrows didn't). State lives on the DOM element. 1785 function applyCarousel(gal) { 1786 var track = gal.querySelector('.pg-track'); 1787 if (!track) return; 1788 var count = track.children.length || 1; 1789 var index = parseInt(gal.getAttribute('data-pg-index') || '0', 10); 1790 index = ((index % count) + count) % count; 1791 gal.setAttribute('data-pg-index', index); 1792 track.style.transform = 'translateX(' + ((elRTL(gal) ? 1 : -1) * index * 100) + '%)'; 1793 var counter = gal.querySelector('.pg-counter'); if (counter) counter.textContent = (index + 1) + ' / ' + count; 1794 gal.querySelectorAll('.pg-dot').forEach(function (d, i) { d.classList.toggle('is-active', i === index); }); 1795 var capEl = gal.querySelector(':scope > .pg-cap'); 1796 if (capEl) { 1797 var active = track.children[index]; 1798 var node = active && active.firstElementChild; 1799 capEl.textContent = node ? mediaCaption(node) : ''; 1800 } 1801 pauseHiddenVideos(gal); 1802 } 1803 function moveCarousel(gal, delta) { 1804 gal.setAttribute('data-pg-index', parseInt(gal.getAttribute('data-pg-index') || '0', 10) + delta); 1805 applyCarousel(gal); 1806 } 1807 1808 // Leaving a slide stops its video or audio, so navigating never leaves sound 1809 // playing behind the reader. No-op on galleries that hold only images. 1810 function pauseHiddenVideos(gal) { 1811 var index = parseInt(gal.getAttribute('data-pg-index') || '0', 10);
1812 gal.querySelectorAll('.pg-slide').forEach(function (slide, i) { 1813 if (i === index) return; 1814 slide.querySelectorAll('video, audio').forEach(function (v) { 1815 try { if (!v.paused) v.pause(); } catch (e) {} 1816 }); 1817 }); 1818 } 1819 1820 /* ----------------------------- Lightbox ----------------------------- */ 1821 var lb, lbImg, lbCaption, lbCounter, lbList = [], lbIndex = 0, lbTouchX = 0; 1822 function buildLightbox() { 1823 if (lb) return; 1824 lb = document.createElement('div'); lb.className = 'pg-lightbox'; lb.setAttribute('role', 'dialog'); 1825 document.body.appendChild(lb); 1826 // Fullscreen viewer = universal physical layout: left button sits on the 1827 // left and points left (= previous), right sits right and points right 1828 // (= next), in every theme/locale. No direction logic â can't be reversed. 1829 lb.innerHTML = 1830 '<div class="pg-lb-stage">' + 1831 '<button type="button" class="pg-lb-btn pg-lb-close" aria-label="' + T.close + '">×</button>' + 1832 '<button type="button" class="pg-lb-btn pg-lb-nav pg-lb-prev" aria-label="' + T.prev + '">❮</button>' + 1833 '<img class="pg-lb-img" alt="">' + 1834 '<button type="button" class="pg-lb-btn pg-lb-nav pg-lb-next" aria-label="' + T.next + '">❯</button>' + 1835 '<div class="pg-lb-counter"></div>' + 1836 '<div class="pg-lb-tools">' + 1837 '<button type="button" class="pg-lb-tool" data-pg-zoom="-1" aria-label="' + T.zoomOut + '" title="' + T.zoomOut + '">−</button>' + 1838 '<span class="pg-lb-zoomlabel">100%</span>' + 1839 '<button type="button" class="pg-lb-tool" data-pg-zoom="1" aria-label="' + T.zoomIn + '" title="' + T.zoomIn + '">+</button>' + 1840 '<a class="pg-lb-tool pg-lb-original" target="_blank" rel="noopener" aria-label="' + T.openOriginal + '" title="' + T.openOriginal + '">↗</a>' + 1841 '</div>' + 1842 '<div class="pg-lb-caption"></div>' + 1843 '</div>'; 1844 lbImg = lb.querySelector('.pg-lb-img'); 1845 lbCaption = lb.querySelector('.pg-lb-caption'); 1846 lbCounter = lb.querySelector('.pg-lb-counter'); 1847 lb.querySelector('.pg-lb-close').addEventListener('click', close); 1848 lb.querySelector('.pg-lb-prev').addEventListener('click', function (e) { e.stopPropagation(); step(-1); }); 1849 lb.querySelector('.pg-lb-next').addEventListener('click', function (e) { e.stopPropagation(); step(1); }); 1850 lb.addEventListener('click', function (e) { if (e.target === lb || e.target.classList.contains('pg-lb-stage')) close(); }); 1851 lb.addEventListener('touchstart', function (e) { lbTouchX = e.changedTouches[0].clientX; }, { passive: true }); 1852 lb.addEventListener('touchend', function (e) { 1853 // A zoomed image is being panned, not swiped between photos. 1854 if (lbZoom > 1) return; 1855 var d = e.changedTouches[0].clientX - lbTouchX; 1856 if (Math.abs(d) > 45) step(d < 0 ? 1 : -1); 1857 }); 1858 document.addEventListener('keydown', function (e) { 1859 if (!lb.classList.contains('is-open')) return; 1860 if (e.key === 'Escape') close(); 1861 else if (e.key === 'ArrowRight') step(1); 1862 else if (e.key === 'ArrowLeft') step(-1); 1863 else if (e.key === '+' || e.key === '=') setZoom(lbZoom + .25); 1864 else if (e.key === '-') setZoom(lbZoom - .25); 1865 else if (e.key === '0') setZoom(1); 1866 }); 1867 1868 // Zoom controls, the wheel, and double-click-to-toggle. 1869 lb.addEventListener('click', function (e) { 1870 var z = e.target.closest && e.target.closest('[data-pg-zoom]'); 1871 if (!z) return; 1872 e.stopPropagation(); 1873 setZoom(lbZoom + (parseFloat(z.getAttribute('data-pg-zoom')) * .25)); 1874 }); 1875 lb.addEventListener('wheel', function (e) { 1876 if (!lb.classList.contains('is-open')) return; 1877 e.preventDefault(); 1878 setZoom(lbZoom + (e.deltaY < 0 ? .2 : -.2)); 1879 }, { passive: false }); 1880 lbImg.addEventListener('dblclick', function (e) { 1881 e.stopPropagation(); 1882 setZoom(lbZoom > 1 ? 1 : 2); 1883 }); 1884 1885 // Pan a zoomed image by dragging it. 1886 var panning = false, panX = 0, panY = 0; 1887 lbImg.addEventListener('pointerdown', function (e) { 1888 if (lbZoom <= 1) return; 1889 panning = true; panX = e.clientX - lbPanX; panY = e.clientY - lbPanY; 1890 try { lbImg.setPointerCapture(e.pointerId); } catch (err) {} 1891 }); 1892 lbImg.addEventListener('pointermove', function (e) { 1893 if (!panning) return; 1894 lbPanX = e.clientX - panX; lbPanY = e.clientY - panY; 1895 applyZoom(); 1896 }); 1897 ['pointerup', 'pointercancel'].forEach(function (evt) { 1898 lbImg.addEventListener(evt, function () { panning = false; }); 1899 }); 1900 } 1901 1902 /* Zoom state lives beside the viewer, reset on every image change so a 1903 reader never lands on the next photo already scaled and off-centre. */ 1904 var lbZoom = 1, lbPanX = 0, lbPanY = 0; 1905 function applyZoom() { 1906 if (!lbImg) return; 1907 lbImg.style.transform = 'translate(' + lbPanX + 'px,' + lbPanY + 'px) scale(' + lbZoom + ')'; 1908 lbImg.classList.toggle('is-zoomed', lbZoom > 1); 1909 var label = lb && lb.querySelector('.pg-lb-zoomlabel'); 1910 if (label) label.textContent = Math.round(lbZoom * 100) + '%'; 1911 } 1912 function setZoom(z) { 1913 lbZoom = Math.min(4, Math.max(1, Math.round(z * 100) / 100)); 1914 if (lbZoom === 1) { lbPanX = 0; lbPanY = 0; } 1915 applyZoom(); 1916 } 1917 function showAt(i) { 1918 lbIndex = (i + lbList.length) % lbList.length; 1919 var src = lbList[lbIndex]; 1920 // Reset zoom per image: landing on the next photo already scaled and 1921 // panned off-centre is disorienting. 1922 setZoom(1); 1923 lbImg.src = src.full; 1924 lbImg.alt = src.alt || ''; 1925 var orig = lb.querySelector('.pg-lb-original'); 1926 if (orig) orig.setAttribute('href', src.full); 1927 lbCaption.textContent = src.caption || ''; 1928 lbCaption.style.display = src.caption ? '' : 'none'; 1929 lbCounter.textContent = lbList.length > 1 ? (lbIndex + 1) + ' / ' + lbList.length : ''; 1930 lb.querySelector('.pg-lb-prev').style.display = lbList.length > 1 ? '' : 'none'; 1931 lb.querySelector('.pg-lb-next').style.display = lbList.length > 1 ? '' : 'none'; 1932 } 1933 function step(d) { showAt(lbIndex + d); } 1934 function open(list, i) { 1935 buildLightbox(); 1936 lbList = list; showAt(i); 1937 lb.classList.add('is-open'); 1938 document.documentElement.style.overflow = 'hidden'; 1939 } 1940 function close() { 1941 if (!lb) return; 1942 lb.classList.remove('is-open'); 1943 document.documentElement.style.overflow = ''; 1944 lbImg.src = ''; 1945 } 1946 1947 // Build the eligible-image list (deduped, DOM order) and open at the clicked one. 1948 function openFromImage(target) { 1949 var nodes = Array.prototype.slice.call(document.querySelectorAll(IMG_SELECTOR)).filter(isLightboxImage); 1950 var seen = [], list = [], startIndex = 0, found = false;
1951 nodes.forEach(function (img) { 1952 if (!img.src || seen.indexOf(img) !== -1) return; 1953 seen.push(img); 1954 // Caption sources, in order: the gallery caption authored on the 1955 // node itself, then a sibling <figcaption> for ordinary article 1956 // figures. 1957 var cap = mediaCaption(img); 1958 if (!cap) { 1959 var fig = img.closest('figure'); 1960 if (fig) { var fc = fig.querySelector('figcaption'); if (fc && !fc.hasAttribute('data-caption-placeholder')) cap = fc.textContent.trim(); } 1961 } 1962 list.push({ full: img.getAttribute('src'), alt: img.alt || '', caption: cap }); 1963 if (img === target) { startIndex = list.length - 1; found = true; } 1964 }); 1965 if (list.length) open(list, found ? startIndex : 0); 1966 } 1967 1968 // One delegated listener handles carousel controls AND lightbox-eligible 1969 // images (now and future) â survives any later DOM re-render. 1970 document.addEventListener('click', function (e) { 1971 var t = e.target; 1972 if (!t || !t.closest) return; 1973 var goBtn = t.closest('[data-pg-go]'); 1974 if (goBtn) { 1975 var g = goBtn.closest('[data-post-gallery]'); 1976 if (g) { e.preventDefault(); moveCarousel(g, goBtn.getAttribute('data-pg-go') === 'next' ? 1 : -1); } 1977 return; 1978 } 1979 // "+N" badge: open the viewer at the first item the grid did not draw. 1980 var moreBtn = t.closest('[data-pg-more]'); 1981 if (moreBtn) { 1982 e.preventDefault(); 1983 var gm = moreBtn.closest('[data-post-gallery]'); 1984 var imgs = gm ? gm.querySelectorAll('img') : []; 1985 var at = parseInt(moreBtn.getAttribute('data-pg-more'), 10) || 0; 1986 if (imgs[at]) openFromImage(imgs[at]); 1987 return; 1988 } 1989 var dotBtn = t.closest('[data-pg-dot]'); 1990 if (dotBtn) { 1991 var g2 = dotBtn.closest('[data-post-gallery]'); 1992 if (g2) { e.preventDefault(); g2.setAttribute('data-pg-index', dotBtn.getAttribute('data-pg-dot')); applyCarousel(g2); } 1993 return; 1994 } 1995 var img = t.closest('img'); 1996 if (isLightboxImage(img)) { e.preventDefault(); openFromImage(img); } 1997 }); 1998 1999 // Swipe â also delegated, for the same resilience. 2000 var swGal = null, swX = 0, swDX = 0; 2001 document.addEventListener('touchstart', function (e) { 2002 var v = e.target.closest ? e.target.closest('.pg-viewport') : null; 2003 swGal = v ? v.closest('[data-post-gallery]') : null; 2004 if (swGal) { swX = e.touches[0].clientX; swDX = 0; } 2005 }, { passive: true }); 2006 document.addEventListener('touchmove', function (e) { if (swGal) swDX = e.touches[0].clientX - swX; }, { passive: true }); 2007 document.addEventListener('touchend', function () { 2008 if (swGal && Math.abs(swDX) > 40) moveCarousel(swGal, (swDX < 0) !== elRTL(swGal) ? 1 : -1); 2009 swGal = null; 2010 }); 2011 2012 /* ----------------------------- Boot ----------------------------- */ 2013 function boot() { try { enhanceGalleries(document); } catch (e) {} } 2014 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot); 2015 else boot(); 2016 // Re-enhance content rendered later (Livewire navigations, lazy sections). 2017 if (window.MutationObserver) { 2018 var mo = new MutationObserver(function (muts) { 2019 for (var i = 0; i < muts.length; i++) { 2020 if (muts[i].addedNodes && muts[i].addedNodes.length) { enhanceGalleries(document); break; } 2021 } 2022 }); 2023 try { mo.observe(document.body, { childList: true, subtree: true }); } catch (e) {} 2024 } 2025 document.addEventListener('livewire:navigated', boot); 2026})(); 2027</script>
2027 2028 2029 2030<div class="qd-modal" id="qdMediaModal" hidden> 2031 <div class="qd-modal__backdrop" data-media-close></div> 2032 2033 <div class="qd-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="qdMediaTitle"> 2034 2035 <div class="qd-modal__bar"> 2036 <button type="button" class="qd-modal__close" data-media-close aria-label="Ø¥ØºÙØ§Ù"> 2037 <i class="fa-solid fa-xmark" aria-hidden="true"></i> 2038 </button> 2039 2040 2041 <div class="qd-modal__nav" id="qdMediaNav" hidden> 2042 <button type="button" class="qd-modal__step" data-media-prev aria-label="Ø§ÙØ³Ø§Ø¨Ù"> 2043 <i class="fa-solid fa-chevron-right" aria-hidden="true"></i> 2044 </button> 2045 <span class="qd-modal__count" id="qdMediaCount"></span> 2046 <button type="button" class="qd-modal__step" data-media-next aria-label="Ø§ÙØªØ§ÙÙ"> 2047 <i class="fa-solid fa-chevron-left" aria-hidden="true"></i> 2048 </button> 2049 </div> 2050 </div> 2051 2052 <div class="qd-modal__stage"> 2053 <div class="qd-modal__player" id="qdMediaPlayer"></div> 2054 2055 2056 <span class="qd-modal__rail qd-modal__rail--start" data-media-swipe aria-hidden="true"></span> 2057 <span class="qd-modal__rail qd-modal__rail--end" data-media-swipe aria-hidden="true"></span> 2058 </div> 2059 2060 2061 <div class="qd-modal__updown" id="qdMediaUpDown" hidden> 2062 <button type="button" class="qd-modal__step" data-media-prev aria-label="Ø§ÙØ³Ø§Ø¨Ù"> 2063 <i class="fa-solid fa-chevron-up" aria-hidden="true"></i> 2064 </button> 2065 <span class="qd-modal__count" id="qdMediaCountV"></span> 2066 <button type="button" class="qd-modal__step" data-media-next aria-label="Ø§ÙØªØ§ÙÙ"> 2067 <i class="fa-solid fa-chevron-down" aria-hidden="true"></i> 2068 </button> 2069 </div> 2070 2071 <div class="qd-modal__body"> 2072 <h2 class="qd-modal__title" id="qdMediaTitle"></h2> 2073 <p class="qd-modal__desc" id="qdMediaDesc"></p> 2074 2075 <button type="button" class="qd-modal__more" id="qdMediaMore" hidden> 2076 <span>عرض اÙÙ Ø²ÙØ¯</span><i class="fa-solid fa-chevron-down" aria-hidden="true"></i> 2077 </button> 2078 </div> 2079 </div> 2080</div> 2081 2082<style> 2083 .qd-modal { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 20px; } 2084 .qd-modal[hidden] { display: none; } 2085 .qd-modal__backdrop { position: absolute; inset: 0; background: rgba(8, 14, 24, .72); backdrop-filter: blur(3px); } 2086 .qd-modal__dialog { position: relative; width: min(880px, 100%); max-height: 90vh; overflow-y: auto; 2087 background: var(--bg, #fff); border-radius: var(--r-lg, 16px); box-shadow: 0 30px 80px rgba(0, 0, 0, .35); } 2088 /* Its own strip, so nothing of ours sits on the player's controls. */ 2089 .qd-modal__bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; 2090 padding: 8px 10px; background: #0d1117; border-radius: var(--r-lg, 16px) var(--r-lg, 16px) 0 0; } 2091 .qd-modal__close, .qd-modal__step { width: 36px; height: 36px; border: 0; border-radius: 50%; cursor: pointer; 2092 background: rgba(255, 255, 255, .12); color: #fff; font-size: 16px; display: grid; place-items: center; 2093 transition: background .2s; } 2094 .qd-modal__close:hover, .qd-modal__step:hover:not(:disabled) { background: rgba(255, 255, 255, .25); } 2095 .qd-modal__step:disabled { opacity: .3; cursor: default; } 2096 .qd-modal__nav { display: flex; align-items: center; gap: 8px; } 2097 .qd-modal__count { color: rgba(255, 255, 255, .65); font-size: 12.5px; font-weight: 700; 2098 font-variant-numeric: tabular-nums; min-width: 52px; text-align: center; } 2099 .qd-modal__player { background: #000; overflow: hidden; } 2100 .qd-modal__player iframe, .qd-modal__player video { display: block; width: 100%; border: 0; } 2101 /* Video keeps 16:9; an audio player is short and must not be stretched. */ 2102 .qd-modal__player[data-kind="youtube"] iframe, 2103 .qd-modal__player[data-kind="iframe"] iframe { aspect-ratio: 16 / 9; height: auto; } 2104 .qd-modal__stage { position: relative; } 2105 .qd-modal__rail { display: none; } 2106 .qd-modal__updown { display: none; } 2107 2108 /* ââ REEL VIEWER ââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2109 A reel is the whole thing: no card around it, no copy under it. The 2110 dialog becomes the video's own 9:16 frame, sized to the viewport so it 2111 never scrolls, with the controls floating clear of it. */ 2112 .qd-modal__dialog[data-ratio="vertical"] { 2113 width: auto; max-height: none; overflow: visible; 2114 background: transparent; box-shadow: none; 2115 display: flex; align-items: center; gap: 14px; 2116 } 2117 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__bar { 2118 position: absolute; inset-block-start: -52px; inset-inline-end: 0; 2119 background: transparent; padding: 0; z-index: 3; 2120 } 2121 /* The horizontal pager belongs to the wide player; a reel gets the 2122 up/down column beside it instead. */ 2123 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__nav, 2124 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__body { display: none; } 2125 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__updown { 2126 display: flex; flex-direction: column; align-items: center; gap: 12px; 2127 } 2128 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__stage { 2129 /* 9:16, bounded by the shorter of the two axes so the frame always fits. */ 2130 height: min(86vh, calc((100vw - 140px) * 16 / 9)); 2131 aspect-ratio: 9 / 16; 2132 border-radius: 18px; overflow: hidden; background: #000; 2133 box-shadow: 0 30px 80px rgba(0, 0, 0, .5); 2134 } 2135 /* The iframe is exactly the frame â no scaling, no clipping. An earlier 2136 version built it 16:9 at full height and let it overflow sideways, to 2137 trade YouTube's letterboxing for a fuller picture; it magnified the video
2138 instead and cut the captions burnt into it off both edges. Whatever 2139 YouTube pads around a Short is better than showing part of it. */ 2140 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__player { height: 100%; } 2141 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__player[data-kind="youtube"] iframe, 2142 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__player[data-kind="iframe"] iframe { 2143 width: 100%; height: 100%; aspect-ratio: auto; 2144 } 2145 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__rail { 2146 display: block; position: absolute; inset-block: 0; width: 56px; z-index: 2; 2147 touch-action: pan-y; 2148 } 2149 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__rail--start { inset-inline-start: 0; } 2150 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__rail--end { inset-inline-end: 0; } 2151 /* «5 / 12», not «12 / 5»: the slash makes RTL reorder the two numbers. */ 2152 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__count { 2153 min-width: 0; font-size: 12px; direction: ltr; 2154 } 2155 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__step { 2156 width: 44px; height: 44px; font-size: 17px; 2157 background: rgba(255, 255, 255, .14); backdrop-filter: blur(6px); 2158 } 2159 2160 /* On a phone the reel takes the screen and everything of ours floats on top 2161 of it: there is no room for a bar above or a column beside. */ 2162 @media (max-width: 560px) { 2163 .qd-modal { padding: 0; } 2164 .qd-modal__dialog[data-ratio="vertical"] { gap: 0; width: 100%; } 2165 /* The height decides the width here â aspect-ratio derives it â so the 2166 modal's own 12px padding has to come out of the height, or the frame 2167 ends up 24px wider than the space it sits in and hangs off the left. */ 2168 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__stage { 2169 width: auto; 2170 height: min(calc(100dvh - 24px), calc((100vw - 24px) * 16 / 9)); 2171 border-radius: 12px; 2172 } 2173 /* Above the frame there is only the browser's own chrome, so the close 2174 button came to rest off-screen. It sits inside the frame now. */ 2175 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__bar { 2176 inset-block-start: 10px; inset-inline-end: 10px; z-index: 4; 2177 } 2178 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__close { 2179 background: rgba(0, 0, 0, .55); backdrop-filter: blur(6px); 2180 } 2181 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__updown { 2182 position: absolute; inset-inline-start: 10px; inset-block-start: 50%; 2183 transform: translateY(-50%); z-index: 3; gap: 10px; 2184 } 2185 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__step { width: 40px; height: 40px; } 2186 /* Narrower rails: on a phone the arrows already occupy one side, and a 2187 56px strip over a 375px screen would swallow a third of the video. */ 2188 .qd-modal__dialog[data-ratio="vertical"] .qd-modal__rail { width: 40px; } 2189 } 2190 .qd-modal__player[data-kind="soundcloud"] { background: transparent; } 2191 .qd-modal__player[data-kind="soundcloud"] iframe { height: 166px; } 2192 .qd-modal__player[data-kind="file"] { background: transparent; padding: 18px 20px 0; } 2193 /* An uploaded clip plays in place, letterboxed like the embeds above it. */ 2194 .qd-modal__player[data-kind="video"] video { display: block; width: 100%; max-height: 78vh; background: #000; } 2195 .qd-modal__body { padding: 20px 24px 26px; } 2196 .qd-modal__title { margin: 0 0 10px; font-size: clamp(17px, 2vw, 21px); font-weight: 800; line-height: 1.6; color: var(--ink, #15181d); } 2197 .qd-modal__desc { margin: 0; color: var(--muted, #5b626e); line-height: 1.95; font-size: 15px; white-space: pre-line; } 2198 .qd-modal__desc:empty { display: none; } 2199 .qd-modal__more { display: none; } 2200 body.qd-modal-open { overflow: hidden; } 2201 2202 /* On a phone the episode blurb ran longer than the video itself, pushing the 2203 player off-screen the moment the modal opened. Collapsed to a few lines 2204 with a toggle; the class only ever goes on when the text really overflows, 2205 so short descriptions get no button. */ 2206 @media (max-width: 640px) { 2207 .qd-modal { padding: 12px; } 2208 .qd-modal__body { padding: 16px 18px 20px; } 2209 .qd-modal__desc.is-clamped { 2210 display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; 2211 } 2212 .qd-modal__more { 2213 display: inline-flex; align-items: center; gap: 6px; margin-top: 10px; 2214 padding: 7px 14px; border: 1px solid var(--line, #e3e6ea); border-radius: 999px; 2215 background: transparent; color: var(--accent, #2f7fcf); 2216 font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; 2217 } 2218 .qd-modal__more i { font-size: 11px; transition: transform .2s; } 2219 .qd-modal__more[aria-expanded="true"] i { transform: rotate(180deg); } 2220 } 2221</style> 2222
2223<script> 2224 (function () { 2225 var modal = document.getElementById('qdMediaModal'); 2226 if (!modal) return; 2227 2228 var player = document.getElementById('qdMediaPlayer'); 2229 var titleEl = document.getElementById('qdMediaTitle'); 2230 var descEl = document.getElementById('qdMediaDesc'); 2231 var moreBtn = document.getElementById('qdMediaMore'); 2232 var dialog = modal.querySelector('.qd-modal__dialog'); 2233 var navEl = document.getElementById('qdMediaNav'); 2234 var upDownEl = document.getElementById('qdMediaUpDown'); 2235 var countEls = [document.getElementById('qdMediaCount'), document.getElementById('qdMediaCountV')]; 2236 var prevBtns = [].slice.call(modal.querySelectorAll('[data-media-prev]')); 2237 var nextBtns = [].slice.call(modal.querySelectorAll('[data-media-next]')); 2238 var narrow = window.matchMedia('(max-width: 640px)'); 2239 var lastFocus = null; 2240 2241 // The open trigger's siblings, so Â«Ø§ÙØªØ§ÙÙ» walks the grid the reader 2242 // was looking at rather than every player on the page. 2243 var siblings = []; 2244 var index = -1; 2245 2246 /** 2247 * Collect the playable cards around a trigger. 2248 * 2249 * Scoped to the nearest list container â a swiper wrapper, a grid â so a 2250 * reel opened from the video rail steps through that rail. Swiper clones 2251 * its slides for looping, and a clone carries the same attributes as the 2252 * original, so they are filtered out or the reader would meet the same 2253 * video twice. 2254 */ 2255 function collectSiblings(trigger) { 2256 var scope = trigger.closest('.swiper-wrapper, .cards-grid, .video-grid, .section, .container') || document; 2257 var all = [].slice.call(scope.querySelectorAll('[data-media-open][data-media-src]')); 2258 2259 siblings = all.filter(function (el) { 2260 return !el.closest('.swiper-slide-duplicate'); 2261 }); 2262 2263 index = siblings.indexOf(trigger); 2264 if (index === -1 && siblings.length) { 2265 // A duplicate slide was clicked: match the original by source. 2266 var src = trigger.getAttribute('data-media-src'); 2267 index = siblings.findIndex(function (el) { 2268 return el.getAttribute('data-media-src') === src; 2269 }); 2270 } 2271 2272 var many = siblings.length > 1 && index > -1; 2273 navEl.hidden = !many; 2274 upDownEl.hidden = !many; 2275 if (many) syncNav(); 2276 } 2277 2278 /** Reflect the current position on both control sets. */ 2279 function syncNav() { 2280 prevBtns.forEach(function (b) { b.disabled = index <= 0; }); 2281 nextBtns.forEach(function (b) { b.disabled = index >= siblings.length - 1; }); 2282 countEls.forEach(function (el) { 2283 if (el) el.textContent = (index + 1) + ' / ' + siblings.length; 2284 }); 2285 } 2286 2287 function step(delta) { 2288 var target = siblings[index + delta]; 2289 if (!target) return; 2290 open(target, true); 2291 } 2292 2293 prevBtns.forEach(function (b) { b.addEventListener('click', function () { step(-1); }); }); 2294 nextBtns.forEach(function (b) { b.addEventListener('click', function () { step(1); }); }); 2295 2296 /* ââ Gestures, for the reel viewer ââââââââââââââââââââââââââââââââ 2297 A swipe up means Â«Ø§ÙØªØ§ÙÙ», the way every reel feed behaves. The 2298 listeners live on the side rails because the player's iframe keeps 2299 every touch that lands on it; the wheel is read off the dialog, 2300 which does see it. */ 2301 var touchY = null; 2302 2303 modal.addEventListener('touchstart', function (e) { 2304 if (!e.target.closest('[data-media-swipe]')) return; 2305 touchY = e.changedTouches[0].clientY; 2306 }, { passive: true }); 2307 2308 modal.addEventListener('touchend', function (e) { 2309 if (touchY === null) return; 2310 var dy = e.changedTouches[0].clientY - touchY; 2311 touchY = null; 2312 if (Math.abs(dy) < 45) return; // a tap, or a hesitation 2313 step(dy < 0 ? 1 : -1); // swipe up â next 2314 }, { passive: true }); 2315 2316 // One notch of a trackpad can fire dozens of events; ignore the tail. 2317 var wheelLock = false;
2318 modal.addEventListener('wheel', function (e) { 2319 if (dialog.getAttribute('data-ratio') !== 'vertical') return; 2320 if (Math.abs(e.deltaY) < 20 || wheelLock) return; 2321 wheelLock = true; 2322 setTimeout(function () { wheelLock = false; }, 600); 2323 step(e.deltaY > 0 ? 1 : -1); 2324 }, { passive: true }); 2325 2326 /** 2327 * Collapse the description if it is long enough to be worth collapsing. 2328 * Measured after the modal is visible â a hidden element reports zero 2329 * height, which would clamp everything or nothing. 2330 */ 2331 function setupClamp() { 2332 descEl.classList.remove('is-clamped'); 2333 moreBtn.hidden = true; 2334 moreBtn.setAttribute('aria-expanded', 'false'); 2335 moreBtn.querySelector('span').textContent = 'عرض اÙÙ Ø²ÙØ¯'; 2336 2337 if (!narrow.matches || !descEl.textContent.trim()) return; 2338 2339 descEl.classList.add('is-clamped'); 2340 if (descEl.scrollHeight - descEl.clientHeight > 4) { 2341 moreBtn.hidden = false; 2342 } else { 2343 descEl.classList.remove('is-clamped'); 2344 } 2345 } 2346 2347 moreBtn.addEventListener('click', function () { 2348 var expanded = descEl.classList.toggle('is-clamped') === false; 2349 moreBtn.setAttribute('aria-expanded', String(expanded)); 2350 moreBtn.querySelector('span').textContent = expanded ? 'عرض Ø£ÙÙ' : 'عرض اÙÙ Ø²ÙØ¯'; 2351 }); 2352 2353 function close() { 2354 modal.hidden = true; 2355 document.body.classList.remove('qd-modal-open'); 2356 // Emptying the container stops playback â a paused iframe would keep 2357 // running audio behind the closed modal. 2358 player.innerHTML = ''; 2359 player.removeAttribute('data-kind'); 2360 if (lastFocus && lastFocus.focus) lastFocus.focus(); 2361 } 2362 2363 /** 2364 * Start playing as soon as the player appears. 2365 * 2366 * The modal only ever opens from a click, so this is the reader asking 2367 * to watch â they should not have to ask twice. `playsinline` keeps a 2368 * phone from taking the video fullscreen on its own, and `rel=0` stops 2369 * YouTube ending on a wall of other channels' videos. 2370 * 2371 * A browser that refuses unmuted autoplay simply shows YouTube's own 2372 * play button, which is exactly where this started â no regression. 2373 */ 2374 function withAutoplay(src, kind) { 2375 if (kind !== 'youtube' && kind !== 'iframe') return src; 2376 if (src.indexOf('autoplay=') !== -1) return src; 2377 2378 return src + (src.indexOf('?') === -1 ? '?' : '&') + 'autoplay=1&playsinline=1&rel=0'; 2379 } 2380 2381 function open(trigger, stepping) { 2382 var kind = trigger.getAttribute('data-media-kind') || 'iframe'; 2383 var src = trigger.getAttribute('data-media-src') || ''; 2384 if (!src) return false; 2385 2386 if (stepping) { 2387 index = siblings.indexOf(trigger); 2388 syncNav(); 2389 } else { 2390 lastFocus = trigger; 2391 collectSiblings(trigger); 2392 } 2393 2394 // A reel is vertical; the dialog follows the video's shape. 2395 dialog.setAttribute('data-ratio', 2396 trigger.getAttribute('data-media-ratio') === 'vertical' ? 'vertical' : 'wide'); 2397 2398 player.setAttribute('data-kind', kind); 2399 2400 if (kind === 'file') { 2401 var audio = document.createElement('audio'); 2402 audio.src = src; 2403 audio.controls = true; 2404 audio.style.width = '100%'; 2405 player.innerHTML = ''; 2406 player.appendChild(audio); 2407 } else if (kind === 'video') { 2408 // An uploaded clip â a podcast episode filmed rather than 2409 // recorded, or a local video material. An <iframe> would show 2410 // the browser's bare file viewer instead of our player. 2411 var video = document.createElement('video'); 2412 video.src = src; 2413 video.controls = true; 2414 video.autoplay = true;
2415 video.playsInline = true; 2416 video.preload = 'metadata'; 2417 player.innerHTML = ''; 2418 player.appendChild(video); 2419 } else { 2420 var frame = document.createElement('iframe'); 2421 frame.src = withAutoplay(src, kind); 2422 frame.setAttribute('allow', 'autoplay; accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'); 2423 frame.setAttribute('allowfullscreen', ''); 2424 // Not lazy: this frame is created because the reader just asked 2425 // for it, and a deferred load is a beat of nothing on screen. 2426 frame.setAttribute('loading', 'eager'); 2427 frame.setAttribute('title', trigger.getAttribute('data-media-title') || ''); 2428 if (kind === 'soundcloud') frame.setAttribute('scrolling', 'no'); 2429 player.innerHTML = ''; 2430 player.appendChild(frame); 2431 } 2432 2433 titleEl.textContent = trigger.getAttribute('data-media-title') || ''; 2434 descEl.textContent = trigger.getAttribute('data-media-desc') || ''; 2435 2436 modal.hidden = false; 2437 document.body.classList.add('qd-modal-open'); 2438 setupClamp(); 2439 modal.querySelector('.qd-modal__close').focus(); 2440 return true; 2441 } 2442 2443 document.addEventListener('click', function (e) { 2444 if (e.target.closest('[data-media-close]')) { close(); return; } 2445 2446 var trigger = e.target.closest('[data-media-open]'); 2447 if (!trigger) return; 2448 2449 // Only swallow the navigation if a player actually opened, so a card 2450 // with nothing playable still follows its link. 2451 if (open(trigger)) e.preventDefault(); 2452 }); 2453 2454 document.addEventListener('keydown', function (e) { 2455 if (modal.hidden) return; 2456 if (e.key === 'Escape') { close(); return; } 2457 2458 // A reel is a vertical feed, so it answers to Up/Down; the wide 2459 // player pages sideways. RTL puts Â«Ø§ÙØªØ§ÙÙ» on the left. 2460 if (dialog.getAttribute('data-ratio') === 'vertical') { 2461 if (e.key === 'ArrowDown') { e.preventDefault(); step(1); } 2462 if (e.key === 'ArrowUp') { e.preventDefault(); step(-1); } 2463 return; 2464 } 2465 if (e.key === 'ArrowLeft') { step(1); } 2466 if (e.key === 'ArrowRight') { step(-1); } 2467 }); 2468 2469 // Leaving the page via SPA navigation must not leave a player running. 2470 document.addEventListener('livewire:navigating', close); 2471 })(); 2472</script>
2472 2473 2474
2475<script> 2476 (function () { 2477 if (!document.querySelector('blockquote.twitter-tweet')) return; 2478 if (document.getElementById('twitter-wjs')) return; 2479 var s = document.createElement('script'); 2480 s.id = 'twitter-wjs'; 2481 s.async = true; 2482 s.charset = 'utf-8'; 2483 s.src = 'https://platform.twitter.com/widgets.js'; 2484 document.body.appendChild(s); 2485 })(); 2486</script>
vendor: 1 bytes, line 2486
2486
2487<script src="https://qudsn.co/vendor/livewire/livewire.min.js?id=f4724cc4" data-csrf="Y0gik7cwXceZDdqSc3S7sjrWhvqOy61dNGTi6Jk4" data-module-url="https://qudsn.co/livewire-dcd59c78" data-update-uri="https://qudsn.co/livewire-dcd59c78/update" data-navigate-once="true"></script>
vendor: 1 bytes, line 2487
2487
2488<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"afc00504e7c949c9b1d808ce2fc6fc19","r":1,"spa":2}' crossorigin="anonymous"></script>
2488 2489</body> 2490 2491</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.