PageSourceSearch

https://www.qurankerim.com/v5_assets/js/app.js?v=94

js qurankerim.com collected 2026-09-24 18:06:13 UTC 123,746 bytes, 4,167 lines download raw bytes

1var defArFontSize = 30;
2var maxArFontSize = 98;
3var minArFontSize = 18;
4
5if (localStorage.getItem("arFontSize") === null) {
6	localStorage.setItem('arFontSize', defArFontSize);
7}
8
9if (localStorage.getItem("autoAudioPlay") === null) {
10	localStorage.setItem('autoAudioPlay', 'no');
11}
12
13function setArFontSize(){
14	var getArFontSize = localStorage.getItem("arFontSize");
15	var arFsize = document.querySelectorAll(".ar-fsize");
16
17	for (var i = 0; i < arFsize.length; i++) {
18		if (arFsize[i].closest && arFsize[i].closest('#tepsirModal, #captureData, #qkTepsirCaptureHost, #qkTepsirExportPreview, #qkTepsirBrandStage')) {
19			continue;
20		}
21		arFsize[i].style.fontSize = getArFontSize+'px';
22	}
23}
24
25function qkModalShow(sel) {
26	var id = (typeof sel === 'string' && sel.charAt(0) !== '#') ? ('#' + sel) : sel;
27	if (typeof window.qkOpenModal === 'function') {
28		window.qkOpenModal(sel);
29	} else if (window.jQuery) {
30		window.jQuery(id).modal('show');
31	}
32}
33
34var arFontSizeIncrease = document.querySelector(".ar_increase");
35if (arFontSizeIncrease) {
36	arFontSizeIncrease.addEventListener("click", function(){
37		var getArFontSize = Number(localStorage.getItem("arFontSize"));
38		if (getArFontSize <= maxArFontSize) {
39			localStorage.setItem('arFontSize', getArFontSize+2);
40			setArFontSize();
41		}
42	});
43}
44
45var arFontSizeDecrease = document.querySelector(".ar_decrease");
46if (arFontSizeDecrease) {
47	arFontSizeDecrease.addEventListener("click", function(){
48		var getArFontSize = Number(localStorage.getItem("arFontSize"));
49		if (getArFontSize >= minArFontSize) {
50			localStorage.setItem('arFontSize', getArFontSize-2);
51			setArFontSize();
52		}
53	});
54}
55
56
57//Text Size
58var defTxFontSize = 22;
59var maxTxFontSize = 48;
60var minTxFontSize = 14;
61
62if (localStorage.getItem("txFontSize") === null) {
63	localStorage.setItem('txFontSize', defTxFontSize);
64}
65
66function setTxFontSize(){
67	var getTxFontSize = localStorage.getItem("txFontSize");
68	var txFsize = document.querySelectorAll(".tx-fsize");
69
70	for (var i = 0; i < txFsize.length; i++) {
71		txFsize[i].style.fontSize = getTxFontSize+'px';
72	}
73}
74
75var txFontSizeIncrease = document.querySelector(".tx_increase");
76if (txFontSizeIncrease) {
77	txFontSizeIncrease.addEventListener("click", function(){
78		var getTxFontSize = Number(localStorage.getItem("txFontSize"));
79		if (getTxFontSize <= maxTxFontSize) {
80			localStorage.setItem('txFontSize', getTxFontSize+2);
81			setTxFontSize();
82		}
83	});
84}
85
86var txFontSizeDecrease = document.querySelector(".tx_decrease");
87if (txFontSizeDecrease) {
88	txFontSizeDecrease.addEventListener("click", function(){
89		var getTxFontSize = Number(localStorage.getItem("txFontSize"));
90		if (getTxFontSize >= minTxFontSize) {
91			localStorage.setItem('txFontSize', getTxFontSize-2);
92			setTxFontSize();
93		}
94	});
95}
96
97
98
99if (localStorage.getItem("theme") === null) {
100    localStorage.setItem("theme", "light");
101}
102document.documentElement.className = localStorage.getItem("theme");
103
104function toggleTheme() {
105    if (localStorage.getItem('theme') == 'light') {
106        localStorage.setItem("theme", "dark");
107        document.documentElement.className = 'dark';
108    }else if (localStorage.getItem('theme') == 'dark') {
109        localStorage.setItem("theme", "light");
110        document.documentElement.className = 'light';
111    }
112}
113
114
115function selectTerjime(str, strDir) {
116	$.ajax({
117        url:"/terjimeselect",
118        type:"POST",
119        data:{
120          data0: 'terjime',
121          data1: str,
122          data2: strDir
123        },
124        success:function() {
125        	location.reload();
126       }
127      });
128}
129
130function selectQuran(str) {
131	$.ajax({
132        url:"/terjimeselect",
133        type:"POST",
134        data:{
135          data0: 'quran',
136          data1: str
137        },
138        success:function() {
139        	location.reload();
140       }
141      });
142}
143
144function reciterSelector(str) {
145	$.ajax({
146        url:"/terjimeselect",
147        type:"POST",
148        data:{
149          data0: 'reciter',
150          data1: str
151        },
152        success:function() {
153        	location.reload();
154       }
155      });
156}
157
158function tilSelector(str) {
159	$.ajax({
160        url:"/terjimeselect",
161        type:"POST",
162        data:{
163          data0: 'lang',
164          data1: str
165        },
166        success:function() {
167        	localStorage.setItem('langSelect', 'yes');
168        	location.reload();
169       }
170      });
171}
172
173function showToast(text){
174    Message.add(text, {
175      type:'success',
176      timeIn: 400, 
177      timeOut: 1000, 
178      close: false,
179      vertical:'center',
180      horizontal:'center'
181    });
182}
183
184var sureIndexShow = false;
185
186function qkIsRtlWebdir() {
187	var dir = ($('html').attr('dir') || document.documentElement.getAttribute('dir') || '').toLowerCase();
188	if (dir === 'rtl') {
189		return true;
190	}
191	if (dir === 'ltr') {
192		return false;
193	}
194	return ($('body').css('direction') || '').toLowerCase() === 'rtl';
195}
196
197function qkGetSureIndexEl() {
198	return $('#qkSureIndex, .sure-index').first();
199}
200
201function qkIsSureIndexOpen() {
202	var $idx = qkGetSureIndexEl();
203	if (!$idx.length) {
204		return false;
205	}
206	if ($idx.hasClass('qk-index-open')) {
207		return true;
208	}
209	if (sureIndexShow) {
210		return true;
211	}
212	var side = qkIsRtlWebdir() ? 'right' : 'left';
213	return $idx.css(side) === '0px';
214}
215
216function qkSyncSureIndexBackdrop(show) {
217	var $bd = $('#qkSureIndexBackdrop');
218	if (!$bd.length) {
219		return;
220	}
221	if (show) {
222		$bd.addClass('is-visible').removeAttr('hidden').attr('aria-hidden', 'false');
223	} else {
224		$bd.removeClass('is-visible').attr('hidden', 'hidden').attr('aria-hidden', 'true');
225	}
226}
227
228function qkOpenSureIndex(panel) {
229	panel = panel || 'sure';
230	var $idx = qkGetSureIndexEl();
231	if (!$idx.length) {
232		return;
233	}
234	$idx.attr('data-active-panel', panel).addClass('qk-index-open');
235	sureIndexShow = true;
236	if (qkIsRtlWebdir()) {
237		$idx.css({ right: '0px', left: '' });
238	} else {
239		$idx.css({ left: '0px', right: '' });
240	}
241	qkSyncSureIndexBackdrop(true);
242}
243
244function qkCloseSureIndex() {
245	var $idx = qkGetSureIndexEl();
246	if (!$idx.length) {
247		return;
248	}
249	$idx.removeClass('qk-index-open');
250	sureIndexShow = false;
251	if (qkIsRtlWebdir()) {
252		$idx.css({ right: '-300px' });
253	} else {
254		$idx.css({ left: '-300px' });
255	}
256	qkSyncSureIndexBackdrop(false);
257}
258
259function qkCloseFlyoutPanels() {
260	qkCloseSureIndex();
261	$('.qk-dt-dropdown.is-open').removeClass('is-open');
262	$('.qk-dt-dropdown [aria-expanded="true"]').attr('aria-expanded', 'false');
263	if (window.qkDiyanetMushaf && typeof window.qkDiyanetMushaf.setMushafSettingsDrawerOpen === 'function') {
264		var $drawer = $('#qkMushafSettingsDrawer');
265		if ($drawer.length && $drawer.hasClass('is-open')) {
266			window.qkDiyanetMushaf.setMushafSettingsDrawerOpen(false);
267		}
268	}
269}
270
271window.qkOpenSureIndex = qkOpenSureIndex;
272window.qkCloseSureIndex = qkCloseSureIndex;
273window.qkIsSureIndexOpen = qkIsSureIndexOpen;
274window.qkCloseFlyoutPanels = qkCloseFlyoutPanels;
275
276var selectedSureNo = 0;
277var selectedAyetNo = 0;
278var shareUrl       = '';
279var qkCaptureKind  = 'ayah';
280
281var qkTepsirIzahatXhr = null;
282var qkTepsirIzahatOpenKey = '';
283
284function qkGetSelectedTepsirId() {
285	var $sel = $('#tepsirSelector');
286	if ($sel.length) {
287		var storedTbl = localStorage.getItem('selectedTepsirTbl');
288		if (storedTbl) {
289			if ($sel.find('option[value="' + storedTbl.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]').length) {
290				$sel.val(storedTbl);
291			} else {
292				localStorage.removeItem('selectedTepsirTbl');
293				localStorage.removeItem('selectedTepsir');
294			}
295		}
296		var $opt = $sel.find('option:selected');
297		if (!$opt.length) {
298			$opt = $sel.find('option:first');
299		}
300		var id = $opt.attr('data-tepsir');
301		if (!id) {
302			id = $opt.data('tepsir');
303		}
304		if (id) {
305			return String(id);
306		}
307	}
308	var stored = localStorage.getItem('selectedTepsir');
309	return stored ? String(stored) : '1';
310}
311
312function qkGetSelectedTepsirTbl() {
313	var $sel = $('#tepsirSelector');
314	if ($sel.length) {
315		var val = $sel.val();
316		if (val) {
317			return val;
318		}
319	}
320	var $host = $('.qk-tefsir-commentary[data-tepsir-tbl]').first();
321	if ($host.length) {
322		return String($host.attr('data-tepsir-tbl') || '');
323	}
324	var $fn = $('a.qk-fn-link[data-tbl], a[id^="ftnt_ref"][data-tbl]').first();
325	if ($fn.length) {
326		return String($fn.attr('data-tbl') || '');
327	}
328	var stored = localStorage.getItem('selectedTepsirTbl');
329	return stored ? String(stored) : '';
330}
331
332function qkLoadTrtKuranYoluAudio() {
333	/* TRT Dinle oynatıcı şimdilik kapalı */
334}
335
336function qkToggleTrtDinleBox() {
337	$('#qkTrtDinleBox').hide();
338}
339
340function qkTepsirLoadUrl(tepsirId, sureNo, ayetNo) {
341	return '/tepsir/' + tepsirId + '/' + sureNo + '-' + ayetNo + '?v=' + Date.now();
342}
343
344function qkTepsirModalVisible() {
345	var el = document.getElementById('tepsirModal');
346	return !!(el && el.classList.contains('show'));
347}
348
349function qkCoordsFromPlaylistItem(item) {
350	if (!item) {
351		return null;
352	}
353	if (item.sureNo != null && item.ayetNo != null) {
354		return { sure: parseInt(item.sureNo, 10), ayet: parseInt(item.ayetNo, 10) };
355	}
356	if (item.sure != null && item.ayet != null) {
357		return { sure: parseInt(item.sure, 10), ayet: parseInt(item.ayet, 10) };
358	}
359	var mp3 = item.mp3 || '';
360	var m = mp3.match(/(\d{3})(\d{3})\.mp3/i);
361	if (m) {
362		return { sure: parseInt(m[1], 10), ayet: parseInt(m[2], 10) };
363	}
364	return null;
365}
366
367function qkFindPlaylistIndex(sureNo, ayetNo) {
368	var pl = window.myPlaylist;
369	if (!pl || !pl.playlist || !pl.playlist.length) {
370		return -1;
371	}
372	sureNo = parseInt(sureNo, 10);
373	ayetNo = parseInt(ayetNo, 10);
374	for (var i = 0; i < pl.playlist.length; i++) {
375		var c = qkCoordsFromPlaylistItem(pl.playlist[i]);
376		if (c && c.sure === sureNo && c.ayet === ayetNo) {
377			return i;
378		}
379	}
380	return -1;
381}
382
383function qkTepsirSyncEnabled() {
384	var $cb = $('#qkTepsirSyncAudio');
385	if ($cb.length) {
386		return $cb.is(':checked');
387	}
388	return localStorage.getItem('qkTepsirSyncAudio') !== 'no';
389}
390
391function qkTepsirUpdateNavPos(sureNo, ayetNo) {
392	var $pos = $('#qkTepsirPos');
393	if (!$pos.length) {
394		return;
395	}
396	sureNo = parseInt(sureNo, 10);
397	ayetNo = parseInt(ayetNo, 10);
398	if (!sureNo || !ayetNo) {
399		$pos.text('—');
400		return;
401	}
402	$pos.text(sureNo + ' · ' + ayetNo);
403	var $bar = $('#qkTepsirShareBar');
404	if ($bar.length) {
405		$bar.attr('data-sure', sureNo).attr('data-ayet', ayetNo).attr('data-link', 'terjime/' + sureNo + '-' + ayetNo);
406	}
407	if (typeof window.qkTepsirSyncAboutLink === 'function') {
408		window.qkTepsirSyncAboutLink();
409	}
410	var $from = $('#qkTepsirAyahFrom');
411	var $to = $('#qkTepsirAyahTo');
412	if ($from.length && $to.length) {
413		var f = parseInt($from.val(), 10);
414		var t = parseInt($to.val(), 10);
415		if (!f || !t || f === t) {
416			qkFillAyahRange('qkTepsirAyahFrom', 'qkTepsirAyahTo', sureNo, ayetNo);
417		}
418	}
419}
420
421function qkTepsirProgressHtml() {
422	return '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width:100%">...</div></div>';
423}
424
425function qkTepsirReloadContent(sureNo, ayetNo) {
426	sureNo = parseInt(sureNo, 10);
427	ayetNo = parseInt(ayetNo, 10);
428	if (!sureNo || !ayetNo || !qkTepsirModalVisible()) {
429		return Promise.resolve();
430	}
431	selectedSureNo = String(sureNo);
432	selectedAyetNo = String(ayetNo);
433	qkTepsirUpdateNavPos(sureNo, ayetNo);
434	var tepsirId = qkGetSelectedTepsirId();
435	$('#tepsirModal .izahat-box').hide();
436	qkTepsirIzahatOpenKey = '';
437	return qkTepsirLoadInto('#tepsirData', tepsirId, sureNo, ayetNo);
438}
439
440function qkTepsirLoadInto($target, tepsirId, sureNo, ayetNo) {
441	var $el = ($target && $target.jquery) ? $target : $($target);
442	if (!$el.length) {
443		return Promise.resolve();
444	}
445	$el.html(qkTepsirProgressHtml());
446	return new Promise(function (resolve) {
447		$el.load(qkTepsirLoadUrl(tepsirId, sureNo, ayetNo), function(response, status) {
448			if (status === 'error' || status === 'timeout') {
449				$el.html('<p class="tepsir-izahat text-danger uyghur">تەپسىر يۈكلەنمىدى. (HTTP ' + status + ')</p>');
450				resolve();
451				return;
452			}
453			$el.find('.ar-fsize').each(function () {
454				this.style.fontSize = '';
455			});
456			if (typeof window.qkApplyTepsirSizePreview === 'function') {
457				window.qkApplyTepsirSizePreview();
458			}
459			resolve();
460		});
461	});
462}
463
464function qkTepsirSyncPlaylistQuiet(idx) {
465	idx = parseInt(idx, 10);
466	var pl = window.myPlaylist;
467	if (!pl || !pl.playlist || !pl.playlist[idx]) {
468		return false;
469	}
470	window._qkTefsirAwaitingPlayClick = true;
471	window._qkPlaylistAutoAdvance = false;
472	try {
473		localStorage.removeItem('jumpAutoPlayPending');
474		localStorage.removeItem('_qkResumePlaylistAid');
475		localStorage.removeItem('_qkResumePlaylistIdx');
476	} catch (e) {}
477	if (typeof window.qkClearJumpAutoPlayFlags === 'function') {
478		window.qkClearJumpAutoPlayFlags();
479	}
480	pl.current = idx;
481	try {
482		var jp = $('#audioPlayer').data('jPlayer');
483		if (jp && jp.status && !jp.status.paused) {
484			$('#audioPlayer').jPlayer('pause');
485		}
486		$('#audioPlayer').jPlayer('setMedia', pl.playlist[idx]);
487	} catch (e) {}
488	if (typeof checkPlayer === 'function') {
489		checkPlayer();
490	}
491	return true;
492}
493
494function qkTepsirStep(delta) {
495	delta = parseInt(delta, 10);
496	if (!delta) {
497		return;
498	}
499	var sure = parseInt(selectedSureNo, 10);
500	var ayet = parseInt(selectedAyetNo, 10);
501	if (!sure || !ayet) {
502		return;
503	}
504	var idx = qkFindPlaylistIndex(sure, ayet);
505	var pl = window.myPlaylist;
506	if (idx >= 0 && pl && pl.playlist && pl.playlist.length) {
507		var nextIdx = idx + delta;
508		if (nextIdx >= 0 && nextIdx < pl.playlist.length) {
509			window._qkTepsirNavigating = true;
510			var c = qkCoordsFromPlaylistItem(pl.playlist[nextIdx]);
511			if (c) {
512				var mushafTefsirMode = $('body').hasClass('qk-diyanet-mushaf-active')
513					&& typeof window.qkGetContentMode === 'function'
514					&& window.qkGetContentMode() === 'tefsir';
515				if (qkTepsirModalVisible()) {
516					qkTepsirReloadContent(c.sure, c.ayet);
517				}
518				if (mushafTefsirMode && typeof window.qkMushafOpenTefsirForAyet === 'function') {
519					window.qkMushafOpenTefsirForAyet(c.sure, c.ayet);
520				} else if (!qkTepsirModalVisible()) {
521					qkTepsirReloadContent(c.sure, c.ayet);
522				}
523				if (typeof window.qkSelectPlaylistIndexQuiet === 'function') {
524					window.qkSelectPlaylistIndexQuiet(nextIdx);
525				} else {
526					qkTepsirSyncPlaylistQuiet(nextIdx);
527				}
528			}
529			window._qkTepsirNavigating = false;
530			return;
531		}
532	}
533	var nextAyet = ayet + delta;
534	if (nextAyet < 1) {
535		return;
536	}
537	window._qkTepsirNavigating = true;
538	qkTepsirReloadContent(sure, nextAyet);
539	window._qkTepsirNavigating = false;
540}
541
542function qkTepsirSyncFromPlaylistIndex(idx) {
543	if (window._qkTepsirNavigating || !qkTepsirSyncEnabled() || !qkTepsirModalVisible()) {
544		return;
545	}
546	var pl = window.myPlaylist;
547	if (!pl || !pl.playlist || !pl.playlist[idx]) {
548		return;
549	}
550	var c = qkCoordsFromPlaylistItem(pl.playlist[idx]);
551	if (!c) {
552		return;
553	}
554	if (String(c.sure) === String(selectedSureNo) && String(c.ayet) === String(selectedAyetNo)) {
555		return;
556	}
557	qkTepsirReloadContent(c.sure, c.ayet);
558}
559
560function qkOpenTepsirModal(sureNo, ayetNo) {
561	if (!sureNo || !ayetNo) {
562		return;
563	}
564	selectedSureNo = sureNo;
565	selectedAyetNo = ayetNo;
566	qkTepsirUpdateNavPos(sureNo, ayetNo);
567	var tepsirId = qkGetSelectedTepsirId();
568	qkTepsirLoadInto('#tepsirData', tepsirId, sureNo, ayetNo);
569	qkModalShow('tepsirModal');
570	qkToggleTrtDinleBox();
571	$('#tepsirModal .izahat-box').hide();
572	qkTepsirIzahatOpenKey = '';
573	var syncOn = localStorage.getItem('qkTepsirSyncAudio') !== 'no';
574	$('#qkTepsirSyncAudio').prop('checked', syncOn);
575}
576
577window.qkGetSelectedTepsirId = qkGetSelectedTepsirId;
578window.qkOpenTepsirModal = qkOpenTepsirModal;
579window.qkTepsirReloadContent = qkTepsirReloadContent;
580window.qkOnPlaylistTrackChange = function (idx) {
581	qkTepsirSyncFromPlaylistIndex(idx);
582};
583
584$(document).ready(function(){
585
586	$(document).on('click', '#qkSureIndexBackdrop', function(e) {
587		e.preventDefault();
588		qkCloseFlyoutPanels();
589	});
590
591	$(document).on('click.qkFlyoutDismiss', function(e) {
592		var $drawer = $('#qkMushafSettingsDrawer');
593		var drawerOpen = $drawer.length && $drawer.hasClass('is-open');
594		if (!qkIsSureIndexOpen() && !$('.qk-dt-dropdown.is-open').length && !drawerOpen) {
595			return;
596		}
597		var $t = $(e.target);
598		if ($t.closest([
599			'#qkSureIndex',
600			'.sure-index',
601			'.nav-sure-selector',
602			'#qkSureIndexBackdrop',
603			'.qk-dt-dropdown',
604			'.qk-dt-dropdown-menu',
605			'.qk-dt-nav-btn',
606			'#qkMshSettingsToggle',
607			'#qkMshSettingsBody',
608			'.qk-mushaf-settings-drawer'
609		].join(', ')).length) {
610			return;
611		}
612		qkCloseFlyoutPanels();
613	});
614
615	$(".nav-sure-selector").on('click', function(e) {
616		e.preventDefault();
617		e.stopPropagation();
618		if (qkIsSureIndexOpen()) {
619			qkCloseSureIndex();
620		} else {
621			qkOpenSureIndex('sure');
622		}
623	});
624
625	$(".lang-list-item").click(function(){
626		var mylang = $(this).attr("data-lang");
627		tilSelector(mylang);
628	});
629
630	$(document).on('click', '.tepsir-button', function(e){
631		e.preventDefault();
632		e.stopPropagation();
633		var sureNo = $(this).attr('data-sure');
634		var ayetNo = $(this).attr('data-ayet');
635		if ($('body').hasClass('qk-diyanet-mushaf-active') && typeof window.qkMushafOpenTefsirForAyet === 'function') {
636			window.qkMushafOpenTefsirForAyet(sureNo, ayetNo);
637			return;
638		}
639		qkOpenTepsirModal(sureNo, ayetNo);
640	});
641
642	$('#qkTepsirPrev').on('click', function(e) {
643		e.preventDefault();
644		qkTepsirStep(-1);
645	});
646	$('#qkTepsirNext').on('click', function(e) {
647		e.preventDefault();
648		qkTepsirStep(1);
649	});
650	$('#qkTepsirSyncAudio').on('change', function() {
651		localStorage.setItem('qkTepsirSyncAudio', $(this).is(':checked') ? 'yes' : 'no');
652	});
653
654	$(".setting-icon, .nav-item-icon:has(.setting-icon)").on('click touchend', function(e){
655		e.preventDefault();
656		e.stopPropagation();
657		if ($('body').hasClass('qk-diyanet-mushaf-active') && $('#qkMushafSettingsDrawer').length) {
658			if (window.qkDiyanetMushaf && typeof window.qkDiyanetMushaf.setMushafSettingsDrawerOpen === 'function') {
659				window.qkDiyanetMushaf.setMushafSettingsDrawerOpen(true);
660				return false;
661			}
662		}
663		qkModalShow('settingModal');
664	});
665
666	$(".bi-search").click(function(){
667		qkModalShow('searchModal');
668	});
669
670	function qkGetSavedFrameId() {
671		var fid = parseInt(localStorage.getItem('qkPrintFrameId') || '0', 10);
672		return isNaN(fid) ? 0 : fid;
673	}
674
675	function qkMarkActiveFrame(frameId) {
676		var $picker = $('#qkFramePicker');
677		if (!$picker.length) return;
678		$picker.find('.qk-frame-opt').removeClass('active');
679		var $match = $picker.find('.qk-frame-opt[data-frame-id="' + frameId + '"]');
680		if ($match.length) {
681			$match.addClass('active');
682		} else {
683			$picker.find('.qk-frame-opt').first().addClass('active');
684		}
685	}
686
687	function qkIsDuaCapture() {
688		return qkCaptureKind === 'dua' || $('#captureData .dprint').length > 0;
689	}
690
691	function qkCaptureSetDuaUi(isDua) {
692		qkCaptureKind = isDua ? 'dua' : 'ayah';
693		$('#qkCaptureAyahRange').toggle(!isDua);
694		$('#qkCaptureShareBar').toggle(!isDua);
695		$('#captureBtnVideo').toggle(!isDua);
696		$('#qkCaptureTefsirRow').toggle(!isDua);
697		$('#qkCaptureSourcePick').toggle(!isDua);
698	}
699
700	function qkFrameOptMeta(frameId) {
701		var $opt = (frameId > 0)
702			? $('#qkFramePicker .qk-frame-opt[data-frame-id="' + frameId + '"]')
703			: $('#qkFramePicker .qk-frame-opt.active');
704		if (!$opt.length) {
705			$opt = $('#qkFramePicker .qk-frame-opt').first();
706		}
707		if (!$opt.length) {
708			return null;
709		}
710		return {
711			id: parseInt($opt.attr('data-frame-id'), 10) || 0,
712			css: $opt.attr('data-css-key') || 'qk-frame-classic',
713			bg: $opt.attr('data-bg') || '#ffffff',
714			tip: $opt.attr('data-tip') || 'css',
715			overlay: $opt.attr('data-overlay') || '',
716			pad: parseInt($opt.attr('data-pad'), 10) || 28
717		};
718	}
719
720	function qkApplyDuaFrame(frameId) {
721		var $frame = $('#captureData .dprint').first();
722		if (!$frame.length) {
723			return;
724		}
725		var fid = (typeof frameId === 'number' && frameId > 0) ? frameId : qkGetSavedFrameId();
726		var meta = qkFrameOptMeta(fid);
727		if (!meta) {
728			return;
729		}
730		if (!$frame.find('.qk-print-inner').length) {
731			$frame.children().wrapAll('<div class="qk-print-inner"></div>');
732		}
733		$frame.addClass('qk-print-frame dprint');
734		$frame.removeClass('qk-frame-classic qk-frame-gold qk-frame-parchment qk-frame-emerald qk-frame-midnight qk-frame-img qk-frame-double qk-frame-ornate qk-frame-navy qk-frame-burgundy qk-frame-plain qk-frame-teal');
735		$frame.find('.qk-frame-overlay').remove();
736		if (meta.tip === 'image' && meta.overlay) {
737			$frame.addClass('qk-frame-img');
738			$frame.attr('style', '--qk-frame-pad:' + meta.pad + 'px;background:' + meta.bg + ';');
739			$frame.prepend($('<img class="qk-frame-overlay" alt="">').attr({ src: meta.overlay, crossorigin: 'anonymous' }));
740			$frame.find('.qk-print-inner').first().attr('style', 'background:' + meta.bg + ';');
741		} else {
742			$frame.addClass(meta.css);
743			$frame.attr('style', '--qk-frame-pad:' + meta.pad + 'px;background:' + meta.bg + ';padding:' + meta.pad + 'px;');
744			$frame.find('.qk-print-inner').first().attr('style', '');
745		}
746		$frame.attr('data-frame-id', meta.id);
747		qkMarkActiveFrame(meta.id);
748		qkApplyCaptureSizePreview();
749	}
750
751	function qkOpenDuaCapture(duaTxt, terjimeTxt) {
752		selectedSureNo = 0;
753		selectedAyetNo = 0;
754		qkCaptureSetDuaUi(true);
755		var $inner = $('<div class="qk-print-inner"></div>');
756		$inner.append($('<div class="dprint-ar ar-fsize"></div>').text(duaTxt || ''));
757		$inner.append($('<div class="dprint-txt tx-fsize"></div>').text(terjimeTxt || ''));
758		$inner.append($('<div class="web-p"></div>').text('www.qurankerim.com'));
759		var $frame = $('<div class="print-border dprint qk-print-frame"></div>').append($inner);
760		$('#captureData').empty().append($frame);
761		qkModalShow('captureModal');
762		qkApplyDuaFrame(qkGetSavedFrameId());
763	}
764	window.qkOpenDuaCapture = qkOpenDuaCapture;
765
766	function qkEnsurePrintFooterBrand($root) {
767		var $foot = $root.find('.print-social-footer').first();
768		if (!$foot.length) {
769			return;
770		}
771		if (!$foot.find('img').length) {
772			var src = $('.qk-header-logo-img, .qk-tepsir-modal-logo, .qk-tepsir-modal-brandbar-logo').first().attr('src') || '';
773			if (src) {
774				$foot.prepend($('<img class="qk-print-footer-logo" alt="Qurankerim">').attr({ src: src, crossorigin: 'anonymous' }));
775			}
776		}
777	}
778
779	function qkCaptureAyahUrl(sure, ayet, fid) {
780		var _terjimeCsv = localStorage.getItem('terjimearr') || '';
781		if (!_terjimeCsv) {
782			_terjimeCsv = 'terjime_msalih';
783		}
784		var url = '/resim/' + sure + '-' + ayet + '?terjime=' + encodeURIComponent(_terjimeCsv);
785		if (fid > 0) {
786			url += '&cerceve=' + fid;
787		}
788		return url;
789	}
790
791	function qkCaptureFetchHtml(url) {
792		return new Promise(function (resolve, reject) {
793			$.ajax({ url: url, dataType: 'html' }).done(function (html) {
794				resolve(html || '');
795			}).fail(function () {
796				reject(new Error('preview'));
797			});
798		});
799	}
800
801	function qkCaptureBuildRangeCard(htmls, from, to) {
802		var $box = $('<div></div>').append($.parseHTML(htmls[0] || '', document, false));
803		var $frame = $box.find('.print-border, .qk-print-frame').first();
804		if (!$frame.length) {
805			return $box.contents();
806		}
807		if (!htmls || htmls.length < 2) {
808			return $frame;
809		}
810		var $inner = $frame.find('.qk-print-inner').first();
811		if (!$inner.length) {
812			return $frame;
813		}
814		var $foot = $inner.find('.print-social-footer').first();
815		var $sure = $inner.find('.surename-p').first();
816		var sureRaw = ($sure.text() || '').replace(/\s+/g, ' ').trim();
817		var $stack = $('<div class="qk-capture-ayah-stack"></div>');
818		htmls.forEach(function (html, i) {
819			var $tmp = $('<div></div>').append($.parseHTML(html || '', document, false));
820			var $src = $tmp.find('.qk-print-inner').first();
821			var $piece = $('<div class="qk-capture-ayah-piece"></div>').attr('data-ayah', from + i);
822			$src.children().not('.surename-p, .print-social-footer, .web-p, .qk-frame-overlay').each(function () {
823				$piece.append($(this).clone());
824			});
825			if ($piece.children().length) {
826				$piece.attr('data-ayah-ar', qkExtractAyahArabic($piece));
827				$stack.append($piece);
828			}
829		});
830		$inner.children().not($foot).remove();
831		$inner.prepend($stack);
832		if (sureRaw) {
833			var m = sureRaw.match(/^(.*?)(\d+)(.*)$/);
834			var label = m ? (m[1] + from + '-' + to + m[3]) : (from + '-' + to);
835			var $sn = $('<div class="surename-p"></div>').text(label);
836			if ($foot.length) {
837				$foot.before($sn);
838			} else {
839				$inner.append($sn);
840			}
841		}
842		if ($foot.length) {
843			$inner.append($foot);
844		}
845		return $frame;
846	}
847
848	var qkCapturePreviewGen = 0;
849
850	function qkFinishCapturePreview(done) {
851		var go = function () {
852			qkApplyCaptureSizePreview();
853			if (typeof done === 'function') {
854				done();
855			}
856		};
857		if (typeof qkAttachCaptureTefsir === 'function' && !qkIsDuaCapture()) {
858			qkAttachCaptureTefsir().then(go, go);
859			return;
860		}
861		go();
862	}
863
864	function qkLoadCapturePreview(sure, ayet, frameId, done) {
865		var fid = (typeof frameId === 'number' && frameId > 0) ? frameId : qkGetSavedFrameId();
866		var range = qkReadAyahRange('qkCaptureAyahFrom', 'qkCaptureAyahTo', sure, ayet);
867		var gen = ++qkCapturePreviewGen;
868		var ayahs = [];
869		var a;
870		for (a = range.from; a <= range.to; a++) {
871			ayahs.push(a);
872		}
873		$('#captureData').html('<div class="text-center p-4"><div class="spinner-border text-primary"></div></div>');
874		if (ayahs.length === 1) {
875			$('#captureData').load(qkCaptureAyahUrl(sure, ayahs[0], fid), function () {
876				if (gen !== qkCapturePreviewGen) {
877					return;
878				}
879				qkEnsurePrintFooterBrand($('#captureData'));
880				qkMarkActiveFrame(fid);
881				qkFinishCapturePreview(done);
882			});
883			return;
884		}
885		Promise.all(ayahs.map(function (n) {
886			return qkCaptureFetchHtml(qkCaptureAyahUrl(sure, n, fid)).catch(function () {
887				return '';
888			});
889		})).then(function (htmls) {
890			if (gen !== qkCapturePreviewGen) {
891				return;
892			}
893			htmls = (htmls || []).filter(function (html) {
894				return html && String(html).replace(/\s+/g, '');
895			});
896			if (!htmls.length) {
897				throw new Error('preview');
898			}
899			var $card = qkCaptureBuildRangeCard(htmls, range.from, range.to);
900			$('#captureData').empty().append($card);
901			qkEnsurePrintFooterBrand($('#captureData'));
902			qkMarkActiveFrame(fid);
903			qkFinishCapturePreview(done);
904		}).catch(function () {
905			if (gen !== qkCapturePreviewGen) {
906				return;
907			}
908			$('#captureData').load(qkCaptureAyahUrl(sure, range.from, fid), function () {
909				qkEnsurePrintFooterBrand($('#captureData'));
910				qkMarkActiveFrame(fid);
911				qkFinishCapturePreview(done);
912			});
913		});
914	}
915	window.qkLoadCapturePreview = qkLoadCapturePreview;
916	window.qkGetSavedFrameId = qkGetSavedFrameId;
917
918	$(document).on('click', '.ayet-print, button:has(.ayet-print)', function(e){
919		e.preventDefault();
920		e.stopPropagation();
921		var $el = $(e.target).closest('.ayet-print');
922		if (!$el.length) {
923			$el = $(this).find('.ayet-print').first();
924		}
925		if (!$el.length && $(this).hasClass('ayet-print')) {
926			$el = $(this);
927		}
928		selectedSureNo = $el.attr('data-sure');
929		selectedAyetNo = $el.attr('data-ayet');
930		if (!selectedSureNo || !selectedAyetNo) {
931			return;
932		}
933		qkCaptureSetDuaUi(false);
934		qkModalShow('captureModal');
935		qkFillAyahRange('qkCaptureAyahFrom', 'qkCaptureAyahTo', selectedSureNo, selectedAyetNo);
936		qkSyncCaptureShareBar(selectedSureNo, selectedAyetNo);
937		qkApplyCaptureSizePreview();
938		qkLoadCapturePreview(selectedSureNo, selectedAyetNo);
939	});
940
941	$(document).on('change', '#qkCaptureAyahFrom, #qkCaptureAyahTo', function () {
942		if (qkIsDuaCapture()) {
943			return;
944		}
945		var sure = parseInt(selectedSureNo, 10) || 0;
946		var range = qkReadAyahRange('qkCaptureAyahFrom', 'qkCaptureAyahTo', sure, selectedAyetNo);
947		if (sure && range.from) {
948			qkSyncCaptureShareBar(sure, range.from);
949			qkLoadCapturePreview(sure, range.from);
950		}
951	});
952
953	$(document).on('change', 'input[name="qkCaptureSize"], input[name="qkCaptureAyahFont"], input[name="qkCaptureBodyFont"], #qkCaptureAyahColor, #qkCaptureBodyColor', function () {
954		qkApplyCaptureSizePreview();
955	});
956
957	$(document).on('change', 'input[name="qkTepsirSize"], input[name="qkTepsirFrame"], input[name="qkTepsirAyahFont"], input[name="qkTepsirBodyFont"], #qkTepsirAyahColor, #qkTepsirBodyColor', function () {
958		qkApplyTepsirSizePreview();
959		if (typeof window.qkTepsirRefreshExportPreview === 'function' && $('#qkTepsirExportPreview .qk-tepsir-capture-card').length) {
960			qkApplyExportStyle($('#qkTepsirExportPreview .qk-tepsir-capture-card'), 'tepsir');
961		}
962	});
963
964	$(document).on('change', '#qkTepsirAyahFrom, #qkTepsirAyahTo', function () {
965		if (typeof window.qkTepsirRefreshExportPreview === 'function') {
966			window.qkTepsirRefreshExportPreview();
967		}
968	});
969
970	$(document).on('click', '#qkFramePicker .qk-frame-opt, #qkTepsirFramePicker .qk-frame-opt', function(e) {
971		e.preventDefault();
972		var fid = parseInt($(this).attr('data-frame-id'), 10);
973		if (isNaN(fid) || fid < 1) return;
974		localStorage.setItem('qkPrintFrameId', String(fid));
975		$('.qk-frame-opt').removeClass('active');
976		$('.qk-frame-opt[data-frame-id="' + fid + '"]').addClass('active');
977		if (typeof qkRememberExportLock === 'function') {
978			qkRememberExportLock();
979		}
980		if ($(this).closest('#qkTepsirFramePicker').length) {
981			qkApplyTepsirSizePreview();
982			return;
983		}
984		if (qkIsDuaCapture()) {
985			qkApplyDuaFrame(fid);
986			return;
987		}
988		if (selectedSureNo) {
989			qkLoadCapturePreview(selectedSureNo, selectedAyetNo, fid);
990		}
991	});
992
993	$(document).on('click', '.dua-print', function (e) {
994		e.preventDefault();
995		e.stopPropagation();
996		qkOpenDuaCapture($(this).attr('data-dua'), $(this).attr('data-terjime'));
997	});
998
999	function qkTepsirSyncAboutLink() {
1000		var $wrap = $('#qkTepsirAboutWrap');
1001		var $link = $('#qkTepsirAboutLink');
1002		if (!$wrap.length) {
1003			return;
1004		}
1005		$wrap.attr('hidden', 'hidden');
1006		if ($link.length) {
1007			$link.attr('href', '#').text('');
1008		}
1009	}
1010	window.qkTepsirSyncAboutLink = qkTepsirSyncAboutLink;
1011
1012	var storedTepsir = localStorage.getItem('selectedTepsirTbl');
1013	if (storedTepsir) {
1014		if ($('#tepsirSelector option[value="' + storedTepsir.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]').length) {
1015			$('#tepsirSelector').val(storedTepsir);
1016		} else {
1017			localStorage.removeItem('selectedTepsirTbl');
1018			localStorage.removeItem('selectedTepsir');
1019		}
1020	}
1021	if (!$('#tepsirSelector').val() || $('#tepsirSelector').val() === 'tepsir_tehfizulquran') {
1022		var $tf = $('#tepsirSelector option[value="tepsir_tefhim"]');
1023		if ($tf.length) {
1024			$('#tepsirSelector').val('tepsir_tefhim');
1025			localStorage.setItem('selectedTepsirTbl', 'tepsir_tefhim');
1026			localStorage.setItem('selectedTepsir', String($tf.attr('data-tepsir') || $tf.data('tepsir') || ''));
1027		}
1028	}
1029	qkToggleTrtDinleBox();
1030	qkTepsirSyncAboutLink();
1031
1032	function qkSyncTepsirPicker() {
1033		var v = $('#tepsirSelector').val();
1034		var $rows = $('#qkTepsirPicker .qk-tepsir-pick');
1035		$rows.each(function () {
1036			var on = $(this).attr('data-value') === v;
1037			$(this).toggleClass('is-selected', on).attr('aria-selected', on ? 'true' : 'false');
1038		});
1039		var $on = $rows.filter('.is-selected').first();
1040		if ($on.length && $on[0].scrollIntoView) {
1041			$on[0].scrollIntoView({ block: 'nearest' });
1042		}
1043	}
1044
1045	function qkMountTepsirPicker() {
1046		var $sel = $('#tepsirSelector');
1047		if (!$sel.length) {
1048			return;
1049		}
1050		if ($('#qkTepsirPicker').length) {
1051			qkSyncTepsirPicker();
1052			return;
1053		}
1054		var $list = $('<div id="qkTepsirPicker" class="qk-tepsir-picker" role="listbox"></div>');
1055		$sel.find('option').each(function (i) {
1056			var $o = $(this);
1057			var $b = $('<button type="button" class="qk-tepsir-pick" role="option"></button>');
1058			$b.attr('data-value', $o.val());
1059			$b.attr('data-tone', String(i % 6));
1060			$b.attr('dir', 'auto');
1061			$b.text($o.text());
1062			$list.append($b);
1063		});
1064		$sel.addClass('qk-tepsir-select-native').after($list);
1065		qkSyncTepsirPicker();
1066	}
1067
1068	qkMountTepsirPicker();
1069	$(document).on('click', '.qk-tepsir-pick', function (e) {
1070		e.preventDefault();
1071		var v = $(this).attr('data-value');
1072		if ($('#tepsirSelector').val() === v) {
1073			return;
1074		}
1075		$('#tepsirSelector').val(v).trigger('change');
1076	});
1077
1078	$("#tepsirSelector").change(function(){
1079	    var $opt = $('option:selected', this);
1080	    var tepsir = $opt.attr('data-tepsir') || $opt.data('tepsir');
1081	    var tepsirTbl = $opt.val();
1082	    localStorage.setItem('selectedTepsir', tepsir);
1083	    localStorage.setItem('selectedTepsirTbl', tepsirTbl);
1084	    qkToggleTrtDinleBox();
1085	    qkTepsirSyncAboutLink();
1086	    qkSyncTepsirPicker();
1087
1088		if (selectedSureNo && selectedAyetNo) {
1089			setTimeout(function() {
1090				qkTepsirLoadInto('#tepsirData', tepsir, selectedSureNo, selectedAyetNo).then(function () {
1091					if (typeof window.qkTepsirRefreshExportPreview === 'function') {
1092						window.qkTepsirRefreshExportPreview();
1093					}
1094				});
1095			}, 300);
1096		}
1097	});
1098
1099	function qkTepsirCircledDigit(n) {
1100		n = parseInt(n, 10);
1101		return n > 0 ? String(n) : '';
1102	}
1103
1104	function qkTepsirDisplayNoteId(id) {
1105		var n = parseInt(id, 10);
1106		if (!n || isNaN(n)) {
1107			return '?';
1108		}
1109		if (n >= 1000) {
1110			var local = n % 1000;
1111			if (local > 0) {
1112				return qkTepsirCircledDigit(local);
1113			}
1114		}
1115		return qkTepsirCircledDigit(n);
1116	}
1117
1118	function qkTepsirNoteIdFromEl($el) {
1119		var base = String($el.attr('data-fn-base') || '').trim();
1120		if (/^\d+$/.test(base)) {
1121			return base;
1122		}
1123		var ids = String($el.attr('data-fn-ids') || '').trim();
1124		if (ids) {
1125			var first = ids.split(',')[0].trim();
1126			if (/^\d+$/.test(first)) {
1127				return first;
1128			}
1129		}
1130		var rawId = String($el.attr('id') || '').trim();
1131		if (!rawId) {
1132			return '';
1133		}
1134		var izahatId = rawId.replace(/^ftnt_ref/i, '');
1135		if (/^\d+$/.test(izahatId)) {
1136			return izahatId;
1137		}
1138		var partMatch = izahatId.match(/^(\d+)_([ab])$/i);
1139		return partMatch ? partMatch[1] : '';
1140	}
1141
1142	function qkTepsirPartFromEl($el) {
1143		var part = String($el.attr('data-fn-part') || '').trim().toLowerCase();
1144		if (part === 'a' || part === 'b') {
1145			return part;
1146		}
1147		var rawId = String($el.attr('id') || '').trim().replace(/^ftnt_ref/i, '');
1148		var m = rawId.match(/^\d+_([ab])$/i);
1149		return m ? m[1].toLowerCase() : '';
1150	}
1151
1152	function qkUnicodeCircledToPlain(label) {
1153		return String(label || '').replace(/[\u2460-\u2473]/g, function (ch) {
1154			return String(ch.charCodeAt(0) - 0x245f);
1155		}).replace(/\u24ea/g, '0');
1156	}
1157
1158	function qkReadingLocaleDefaults() {
1159		return window.qkReadingLocale || {
1160			dir: 'rtl',
1161			lang: 'ug',
1162			class: 'qk-reading-locale tepsir-izahat uyghur'
1163		};
1164	}
1165
1166	function qkReadingLocaleFromEl($el) {
1167		var loc = qkReadingLocaleDefaults();
1168		var $host = $el.closest('[lang][dir]').first();
1169		if ($host.length) {
1170			var cls = String($host.attr('class') || '').trim();
1171			if (cls.indexOf('qk-reading-locale') === -1) {
1172				cls = (loc.class + ' ' + cls).trim();
1173			}
1174			return {
1175				dir: $host.attr('dir') || loc.dir,
1176				lang: $host.attr('lang') || loc.lang,
1177				class: cls || loc.class
1178			};
1179		}
1180		return loc;
1181	}
1182
1183	function qkBuildFnBadgeHtml(label) {
1184		label = qkNormalizeFnBadgeLabel(label || '');
1185		if (!label) {
1186			return '';
1187		}
1188		return '<span class="qk-izahat-fn-badge" aria-hidden="true"><span class="qk-fn-circled-inner">'
1189			+ $('<div>').text(label).html() + '</span></span>';
1190	}
1191
1192	function qkNormalizeFnBadgeLabel(label) {
1193		label = String(label || '').trim();
1194		if (!label) {
1195			return '';
1196		}
1197		if (/^[\u2460-\u2473\u24ea]/.test(label)) {
1198			return qkUnicodeCircledToPlain(label);
1199		}
1200		var range = label.match(/^\[(\d+)\s*[–\-]\s*(\d+)\]$/);
1201		if (range) {
1202			return qkTepsirCircledDigit(range[1]) + '\u2013' + qkTepsirCircledDigit(range[2]);
1203		}
1204		var single = label.match(/^\[(\d+)\]$/);
1205		if (single) {
1206			return qkTepsirCircledDigit(single[1]);
1207		}
1208		if (/^\d+$/.test(label) && label.length <= 3) {
1209			return qkTepsirCircledDigit(label);
1210		}
1211		var brace = label.match(/^\{(\d+)\}$/);
1212		if (brace) {
1213			return qkTepsirCircledDigit(brace[1]);
1214		}
1215		return label;
1216	}
1217
1218	function qkResolveIzahatNoteLabel(noteLabel, izahatId) {
1219		noteLabel = qkNormalizeFnBadgeLabel(noteLabel || '');
1220		if (noteLabel) {
1221			return noteLabel;
1222		}
1223		if (izahatId) {
1224			return qkTepsirDisplayNoteId(izahatId);
1225		}
1226		return '';
1227	}
1228
1229	function qkApplyIzahatBadgeLayout(html, noteLabel, locale) {
1230		html = String(html || '').trim();
1231		locale = locale || qkReadingLocaleDefaults();
1232		noteLabel = qkNormalizeFnBadgeLabel(noteLabel || '');
1233		if (!html || qkIzahatHtmlIsEmpty(html)) {
1234			return html;
1235		}
1236		var $root = $('<div>').html(html);
1237		$root.find('script').remove();
1238		var $line = $root.find('.izahat-fn-line').first();
1239		var $badge = $root.find('.qk-izahat-fn-badge').first();
1240		if (!$line.length) {
1241			var inner = qkStripIzahatInnerHtml(html);
1242			if (!noteLabel) {
1243				return html;
1244			}
1245			var bodyClass = 'izahat-fn-body ' + (locale.class || 'qk-reading-locale tepsir-izahat uyghur');
1246			return '<div class="' + bodyClass + '" lang="' + (locale.lang || 'ug') + '" dir="' + (locale.dir || 'rtl') + '">' +
1247				'<p class="izahat-fn-line">' +
1248				qkBuildFnBadgeHtml(noteLabel) +
1249				'<span class="izahat-fn-text">' + inner + '</span>' +
1250				'</p></div>';
1251		}
1252		if ($badge.length && noteLabel) {
1253			$badge.html('<span class="qk-fn-circled-inner">' + $('<div>').text(noteLabel).html() + '</span>');
1254		} else if (!$badge.length && noteLabel) {
1255			$line.prepend($(qkBuildFnBadgeHtml(noteLabel)));
1256		}
1257		return $root.html();
1258	}
1259
1260	function qkStripIzahatInnerHtml(html) {
1261		html = String(html || '').trim();
1262		if (!html) {
1263			return '';
1264		}
1265		var $root = $('<div>').html(html);
1266		$root.find('script').remove();
1267		var $ug = $root.find('.tepsir-izahat, .qk-reading-locale').first();
1268		if ($ug.length) {
1269			$root = $('<div>').html($ug.html());
1270		}
1271		var $p = $root.find('p').first();
1272		if ($p.length) {
1273			return $p.html();
1274		}
1275		return $root.html();
1276	}
1277
1278	function qkFootnoteClickLabel($el, izahatId) {
1279		var $anchor = $el.is('a') ? $el : $el.closest('a.qk-fn-link, a[id^="ftnt_ref"]');
1280		if ($anchor.length) {
1281			$el = $anchor;
1282		}
1283		var dataLabel = String($el.attr('data-fn-label') || '').trim();
1284		if (dataLabel) {
1285			return qkNormalizeFnBadgeLabel(dataLabel);
1286		}
1287		var $inner = $el.find('.qk-fn-circled-inner').first();
1288		var text = ($inner.length ? $inner.text() : $el.text()) || '';
1289		text = String(text).replace(/\s+/g, '').trim();
1290		if (/^[\u2460-\u2473\u24ea]([\u2013-][\u2460-\u2473\u24ea])?$/.test(text)) {
1291			return qkUnicodeCircledToPlain(text);
1292		}
1293		if (/^\[\d+([–\-]\d+)?\]$/.test(text)) {
1294			var m = text.match(/\[(\d+)/);
1295			if (m) {
1296				return qkTepsirCircledDigit(m[1]);
1297			}
1298			return text;
1299		}
1300		if (/^\d+$/.test(text) && text.length <= 3) {
1301			return qkTepsirCircledDigit(text);
1302		}
1303		return qkTepsirDisplayNoteId(izahatId);
1304	}
1305
1306	function qkUseMushafIzahatPanel() {
1307		return $('body').hasClass('qk-diyanet-mushaf-active') && $('#qkMushafIzahatBox').length > 0;
1308	}
1309
1310	function qkParseIzahatPageHtml(html) {
1311		var $wrap = $('<div>').html(String(html || ''));
1312		var $inner = $wrap.find('.ayet-list-holder').first();
1313		if ($inner.length) {
1314			return $inner.html();
1315		}
1316		return String(html || '');
1317	}
1318
1319	function qkIzahatHtmlIsEmpty(html) {
1320		if (/class="text-muted"/.test(html)) {
1321			return true;
1322		}
1323		var plain = String(html || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
1324		return plain === '' || plain === '—' || plain === '-';
1325	}
1326
1327	function qkLoadTepsirIzahatHtml(html, noteLabel, locale, izahatId) {
1328		html = String(html || '').replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
1329		var isEmpty = qkIzahatHtmlIsEmpty(html);
1330		locale = locale || qkReadingLocaleDefaults();
1331		noteLabel = qkResolveIzahatNoteLabel(noteLabel, izahatId);
1332		if (!isEmpty) {
1333			html = qkApplyIzahatBadgeLayout(html, noteLabel, locale);
1334		}
1335		if (qkUseMushafIzahatPanel()) {
1336			var $box = $('#qkMushafIzahatBox .qk-mushaf-izahat-content');
1337			$box.attr('dir', locale.dir || 'rtl').attr('lang', locale.lang || 'ug');
1338			$box.html(html);
1339			$('#qkMushafIzahatBox').stop(true, true).slideDown(180);
1340			return;
1341		}
1342		var $panel = $('#tepsirModal .izahat-box');
1343		$('#tepsirModal .izahat-box-content').html(html);
1344		if ($panel.is(':visible')) {
1345			$panel.show();
1346		} else {
1347			$panel.stop(true, true).slideDown(200);
1348		}
1349	}
1350
1351	function qkTepsirIzahatUsesFileScope(tbl) {
1352		return tbl === 'tepsir_tefhim' || tbl === 'tepsir_jewherliri';
1353	}
1354
1355	function qkBuildTepsirIzahatUrl(izahatTbl, fileId, izahatId, fnPart) {
1356		if (fileId && qkTepsirIzahatUsesFileScope(izahatTbl)) {
1357			var scopedUrl = '/tpizahat/jewher/' + encodeURIComponent(fileId) + '/' + encodeURIComponent(izahatId);
1358			var qs = [];
1359			if (izahatTbl === 'tepsir_tefhim') {
1360				qs.push('tbl=tepsir_tefhim');
1361			}
1362			if (fnPart) {
1363				qs.push('part=' + encodeURIComponent(fnPart));
1364			}
1365			if (qs.length) {
1366				scopedUrl += '?' + qs.join('&');
1367			}
1368			return scopedUrl;
1369		}
1370		if (izahatTbl) {
1371			return '/tpizahat/' + encodeURIComponent(izahatTbl) + '/' + encodeURIComponent(izahatId);
1372		}
1373		return '';
1374	}
1375
1376	function qkLoadTepsirIzahat($el) {
1377		var $target = $el;
1378		if (!$target.is('a.qk-fn-link, a[id^="ftnt_ref"], a[data-fn-part], span[id]')) {
1379			$target = $el.closest('a.qk-fn-link, a[id^="ftnt_ref"], a[data-fn-part], span[id]');
1380		}
1381		if (!$target.length) {
1382			return;
1383		}
1384		var izahatId = qkTepsirNoteIdFromEl($target);
1385		if (!izahatId) {
1386			return;
1387		}
1388		var fnPart = qkTepsirPartFromEl($target);
1389		var noteLabel = qkFootnoteClickLabel($target, izahatId);
1390		var $scope = $target.closest('.myusuf');
1391		var izahatTbl = $target.attr('data-tbl') || $scope.attr('data-tepsir-tbl') || '';
1392		var fileId = $scope.attr('data-file-id') || $target.attr('data-file-id') || '';
1393		if (!izahatTbl) {
1394			izahatTbl = qkGetSelectedTepsirTbl();
1395		}
1396		if (!izahatTbl) {
1397			return;
1398		}
1399		var openKey = izahatTbl + ':' + (fileId || '') + ':' + izahatId + ':' + (fnPart || '');
1400		if (openKey === qkTepsirIzahatOpenKey && $('#tepsirModal .izahat-box').is(':visible')) {
1401			return;
1402		}
1403		qkTepsirIzahatOpenKey = openKey;
1404		var url = qkBuildTepsirIzahatUrl(izahatTbl, fileId, izahatId, fnPart);
1405		if (!url) {
1406			return;
1407		}
1408		var locale = qkReadingLocaleFromEl($target);
1409		if (qkTepsirIzahatXhr && qkTepsirIzahatXhr.abort) {
1410			qkTepsirIzahatXhr.abort();
1411		}
1412		qkTepsirIzahatXhr = $.get(url).done(function(html) {
1413			qkLoadTepsirIzahatHtml(html, noteLabel, locale, izahatId);
1414		}).fail(function() {
1415			qkLoadTepsirIzahatHtml('<p class="text-muted">—</p>', '', locale, '');
1416		}).always(function() {
1417			qkTepsirIzahatXhr = null;
1418		});
1419	}
1420
1421	$(document).on('click', '#tepsirData .tepsir-izahat span[id], #tepsirData .tepsir-izahat a[id^="ftnt_ref"], #tepsirData .tepsir-izahat a.qk-fn-link, #tepsirData .tepsir-izahat a[data-fn-part], #tepsirData .tepsir-izahat .qk-fn-circled-inner, #tepsirData .tepsiri span[id], #tepsirData .tepsiri a[id^="ftnt_ref"], #tepsirData .tepsiri a.qk-fn-link, #tepsirData .tepsiri a[data-fn-part], #tepsirData .tepsiri .qk-fn-circled-inner, #tepsirData .terjimisi span[id], #tepsirData .terjimisi a[id^="ftnt_ref"], #tepsirData .terjimisi a.qk-fn-link, #tepsirData .terjimisi a[data-fn-part], #tepsirData .terjimisi .qk-fn-circled-inner, #tepsirModal .izahat-box-content a.qk-fn-link, #tepsirModal .izahat-box-content a[id^="ftnt_ref"], #tepsirModal .izahat-box-content a[data-fn-part], #tepsirModal .izahat-box-content .qk-fn-circled-inner, .qk-tefsir-commentary a.qk-fn-link, .qk-tefsir-commentary a[id^="ftnt_ref"], .qk-tefsir-commentary a[data-fn-part], .qk-tefsir-commentary .qk-fn-circled-inner, .qk-tefsir-document a.qk-fn-link, .qk-tefsir-document a[id^="ftnt_ref"], .qk-tefsir-document a[data-fn-part], .qk-tefsir-document .qk-fn-circled-inner', function(e) {
1422		e.preventDefault();
1423		e.stopPropagation();
1424		qkLoadTepsirIzahat($(this));
1425	});
1426
1427	$(document).on('click', '.qk-tefsir-meal-box a.qk-meal-fn-link, .qk-tefsir-document a.qk-meal-fn-link', function(e) {
1428		e.preventDefault();
1429		var url = $(this).attr('href');
1430		if (!url) {
1431			return;
1432		}
1433		var $link = $(this);
1434		var noteLabel = $link.text();
1435		var fidMatch = String(url || '').match(/[?&]fid=(\d+)/i);
1436		var fid = fidMatch ? fidMatch[1] : '';
1437		if (!noteLabel && fid) {
1438			noteLabel = fid;
1439		}
1440		var locale = qkReadingLocaleFromEl($link);
1441		$.get(url).done(function(resp) {
1442			qkLoadTepsirIzahatHtml(qkParseIzahatPageHtml(resp), noteLabel, locale, fid);
1443		}).fail(function() {
1444			qkLoadTepsirIzahatHtml('<p class="text-muted">—</p>', '', locale, '');
1445		});
1446	});
1447
1448	$(document).on('click', '.close-izahat, .qk-mushaf-izahat-close', function(e) {
1449		e.preventDefault();
1450		e.stopPropagation();
1451		qkTepsirIzahatOpenKey = '';
1452		if ($('#qkMushafIzahatBox').is(':visible')) {
1453			$('#qkMushafIzahatBox').slideUp(180);
1454		}
1455		$('#tepsirModal .izahat-box').stop(true, true).slideUp(180);
1456	});
1457
1458	$("#tepsirModal").on("hidden.bs.modal", function () {
1459	    var trtAudio = document.getElementById('qkTrtDinleAudio');
1460	    if (trtAudio) {
1461	    	try { trtAudio.pause(); } catch (e) {}
1462	    }
1463	    $("#tepsirData").empty();
1464	    $('#tepsirModal .izahat-box').hide();
1465	    qkTepsirIzahatOpenKey = '';
1466	});
1467
1468	qkInitTepsirFloatingModal();
1469});
1470
1471function qkInitTepsirFloatingModal() {
1472	var $modal = $('#tepsirModal');
1473	if (!$modal.length) {
1474		return;
1475	}
1476	var $dialog = $modal.find('.modal-dialog').first();
1477	var geomKey = 'qkTepsirModalGeom';
1478	var defaults = { w: 850, h: Math.min(Math.round(window.innerHeight * 0.82), 720) };
1479
1480	function clamp(n, min, max) {
1481		return Math.max(min, Math.min(max, n));
1482	}
1483
1484	function readGeom() {
1485		try {
1486			var raw = localStorage.getItem(geomKey);
1487			if (!raw) {
1488				return null;
1489			}
1490			var g = JSON.parse(raw);
1491			if (!g || typeof g.w !== 'number' || typeof g.h !== 'number') {
1492				return null;
1493			}
1494			return g;
1495		} catch (e) {
1496			return null;
1497		}
1498	}
1499
1500	function saveGeom(g) {
1501		try {
1502			localStorage.setItem(geomKey, JSON.stringify(g));
1503		} catch (e) {}
1504	}
1505
1506	function centerDialog(w, h) {
1507		var left = Math.max(8, Math.round((window.innerWidth - w) / 2));
1508		var top = Math.max(8, Math.round((window.innerHeight - h) / 2));
1509		return { left: left, top: top, w: w, h: h };
1510	}
1511
1512	function applyGeom(g) {
1513		var w = clamp(g.w, 360, window.innerWidth - 16);
1514		var h = clamp(g.h, 280, window.innerHeight - 16);
1515		var left = clamp(g.left, 8, window.innerWidth - w - 8);
1516		var top = clamp(g.top, 8, window.innerHeight - h - 8);
1517		$dialog.css({
1518			left: left + 'px',
1519			top: top + 'px',
1520			width: w + 'px',
1521			height: h + 'px',
1522			'--qk-tepsir-w': w + 'px',
1523			'--qk-tepsir-h': h + 'px'
1524		});
1525		return { left: left, top: top, w: w, h: h };
1526	}
1527
1528	function currentGeom() {
1529		var rect = $dialog[0].getBoundingClientRect();
1530		return {
1531			left: Math.round(rect.left),
1532			top: Math.round(rect.top),
1533			w: Math.round(rect.width),
1534			h: Math.round(rect.height)
1535		};
1536	}
1537
1538	function resetSize() {
1539		var g = centerDialog(defaults.w, defaults.h);
1540		applyGeom(g);
1541		saveGeom(g);
1542	}
1543
1544	function zoomBy(delta) {
1545		var g = currentGeom();
1546		g.w = clamp(g.w + delta * 48, 360, window.innerWidth - 16);
1547		g.h = clamp(g.h + delta * 36, 280, window.innerHeight - 16);
1548		g = applyGeom(g);
1549		saveGeom(g);
1550	}
1551
1552	$modal.on('shown.bs.modal', function () {
1553		$modal.addClass('qk-tepsir-modal-float');
1554		var stored = readGeom();
1555		if (stored && typeof stored.left === 'number' && typeof stored.top === 'number') {
1556			applyGeom(stored);
1557		} else {
1558			var g = centerDialog(defaults.w, defaults.h);
1559			applyGeom(g);
1560			saveGeom(g);
1561		}
1562	});
1563
1564	$modal.find('.qk-tepsir-zoom-in').on('click', function (e) {
1565		e.preventDefault();
1566		e.stopPropagation();
1567		zoomBy(1);
1568	});
1569	$modal.find('.qk-tepsir-zoom-out').on('click', function (e) {
1570		e.preventDefault();
1571		e.stopPropagation();
1572		zoomBy(-1);
1573	});
1574	$modal.find('.qk-tepsir-reset-size').on('click', function (e) {
1575		e.preventDefault();
1576		e.stopPropagation();
1577		resetSize();
1578	});
1579	$modal.find('#qkTepsirSaveImg').on('click', function (e) {
1580		e.preventDefault();
1581		e.stopPropagation();
1582		var $panel = $('#qkTepsirExportPanel');
1583		if ($panel.attr('hidden') != null) {
1584			$panel.removeAttr('hidden');
1585			qkFillAyahRange('qkTepsirAyahFrom', 'qkTepsirAyahTo', selectedSureNo, selectedAyetNo);
1586			if (typeof window.qkTepsirRefreshExportPreview === 'function') {
1587				window.qkTepsirRefreshExportPreview();
1588			}
1589		} else {
1590			$panel.attr('hidden', 'hidden');
1591			if (typeof window.qkTepsirHideExportPreview === 'function') {
1592				window.qkTepsirHideExportPreview();
1593			}
1594		}
1595		qkApplyTepsirSizePreview();
1596	});
1597	$modal.find('#qkTepsirPlayAyah').on('click', function (e) {
1598		e.preventDefault();
1599		e.stopPropagation();
1600		qkTepsirPlayCurrentAyah();
1601	});
1602	$modal.find('#qkTepsirExportJpg').on('click', function (e) {
1603		e.preventDefault();
1604		e.stopPropagation();
1605		qkTepsirSaveAsImage(document.getElementById('qkTepsirSaveImg'), { video: false });
1606	});
1607	$modal.find('#qkTepsirExportMp4').on('click', function (e) {
1608		e.preventDefault();
1609		e.stopPropagation();
1610		qkTepsirSaveAsImage(document.getElementById('qkTepsirSaveImg'), { video: true });
1611	});
1612
1613	var drag = { active: false, startX: 0, startY: 0, startLeft: 0, startTop: 0 };
1614	var resize = { active: false, startX: 0, startY: 0, startW: 0, startH: 0, startLeft: 0, startTop: 0 };
1615
1616	function isDragExcluded(target) {
1617		return $(target).closest('button, select, input, textarea, a, label, .qk-tepsir-modal-tools, .btn-close, .qk-tepsir-export-panel, .qk-tepsir-share-bar').length > 0;
1618	}
1619
1620	$modal.on('mousedown touchstart', '.qk-tepsir-modal-header', function (e) {
1621		if (isDragExcluded(e.target)) {
1622			return;
1623		}
1624		var ev = e.type === 'touchstart' ? (e.originalEvent.touches[0] || e) : e;
1625		drag.active = true;
1626		$modal.addClass('qk-tepsir-modal-dragging');
1627		var g = currentGeom();
1628		drag.startX = ev.clientX;
1629		drag.startY = ev.clientY;
1630		drag.startLeft = g.left;
1631		drag.startTop = g.top;
1632		e.preventDefault();
1633	});
1634
1635	$modal.on('mousedown touchstart', '.qk-tepsir-resize-handle', function (e) {
1636		var ev = e.type === 'touchstart' ? (e.originalEvent.touches[0] || e) : e;
1637		resize.active = true;
1638		$modal.addClass('qk-tepsir-modal-resizing');
1639		var g = currentGeom();
1640		resize.startX = ev.clientX;
1641		resize.startY = ev.clientY;
1642		resize.startW = g.w;
1643		resize.startH = g.h;
1644		resize.startLeft = g.left;
1645		resize.startTop = g.top;
1646		e.preventDefault();
1647		e.stopPropagation();
1648	});
1649
1650	function onPointerMove(clientX, clientY) {
1651		if (drag.active) {
1652			var left = drag.startLeft + (clientX - drag.startX);
1653			var top = drag.startTop + (clientY - drag.startY);
1654			applyGeom({ left: left, top: top, w: currentGeom().w, h: currentGeom().h });
1655		} else if (resize.active) {
1656			var dx = resize.startX - clientX;
1657			var dy = clientY - resize.startY;
1658			var w = clamp(resize.startW + dx, 360, window.innerWidth - 16);
1659			var h = clamp(resize.startH + dy, 280, window.innerHeight - 16);
1660			var left = resize.startLeft - (w - resize.startW);
1661			applyGeom({ left: left, top: resize.startTop, w: w, h: h });
1662		}
1663	}
1664
1665	function endPointer() {
1666		if (drag.active || resize.active) {
1667			saveGeom(currentGeom());
1668		}
1669		drag.active = false;
1670		resize.active = false;
1671		$modal.removeClass('qk-tepsir-modal-dragging qk-tepsir-modal-resizing');
1672	}
1673
1674	$(document).on('mousemove.qkTepsirFloat touchmove.qkTepsirFloat', function (e) {
1675		if (!drag.active && !resize.active) {
1676			return;
1677		}
1678		var ev = e.type === 'touchmove' ? (e.originalEvent.touches[0] || e) : e;
1679		onPointerMove(ev.clientX, ev.clientY);
1680		e.preventDefault();
1681	});
1682
1683	$(document).on('mouseup.qkTepsirFloat touchend.qkTepsirFloat touchcancel.qkTepsirFloat', endPointer);
1684
1685	$(window).on('resize.qkTepsirFloat', function () {
1686		if (!$modal.hasClass('show')) {
1687			return;
1688		}
1689		var g = applyGeom(currentGeom());
1690		saveGeom(g);
1691	});
1692}
1693
1694function qkTepsirSureName(sureNo) {
1695	sureNo = String(parseInt(sureNo, 10) || 0);
1696	if (!sureNo) {
1697		return '';
1698	}
1699	if (window.qkSureNames && window.qkSureNames[sureNo]) {
1700		return String(window.qkSureNames[sureNo]);
1701	}
1702	var found = '';
1703	$('#qkSureIndex .sure-index-holder li a').each(function () {
1704		var n = $(this).find('span').first().text().trim();
1705		if (n === sureNo) {
1706			found = $(this).clone().children().remove().end().text().trim();
1707			return false;
1708		}
1709	});
1710	return found;
1711}
1712
1713function qkTepsirPlayCurrentAyah() {
1714	var sure = parseInt(selectedSureNo, 10) || 0;
1715	var ayet = parseInt(selectedAyetNo, 10) || 0;
1716	var idx = qkFindPlaylistIndex(sure, ayet);
1717	if (idx < 0) {
1718		return false;
1719	}
1720	if (typeof window.qkPlayByIndex === 'function') {
1721		return window.qkPlayByIndex(idx);
1722	}
1723	return false;
1724}
1725
1726var QK_AYAH_COUNTS = [0, 7, 286, 200, 176, 120, 165, 206, 75, 129, 109, 123, 111, 43, 52, 99, 128, 111, 110, 98, 135, 112, 78, 118, 64, 77, 227, 93, 88, 69, 60, 34, 30, 73, 54, 45, 83, 182, 88, 75, 85, 54, 53, 89, 59, 37, 35, 38, 29, 18, 45, 60, 49, 62, 55, 78, 96, 29, 22, 24, 13, 14, 11, 11, 18, 12, 12, 30, 52, 52, 44, 28, 28, 20, 56, 40, 31, 50, 40, 46, 42, 29, 19, 36, 25, 22, 17, 19, 26, 30, 20, 15, 21, 11, 8, 8, 19, 5, 8, 8, 11, 11, 8, 3, 9, 5, 4, 7, 3, 6, 3, 5, 4, 5, 6];
1727var QK_VIDEO_AYAH_MAX = 20;
1728
1729function qkSureAyahCount(sure) {
1730	sure = parseInt(sure, 10) || 0;
1731	return QK_AYAH_COUNTS[sure] || 286;
1732}
1733
1734function qkPad3(n) {
1735	return ('000' + (parseInt(n, 10) || 0)).slice(-3);
1736}
1737
1738function qkFillAyahRange(fromId, toId, sure, ayet) {
1739	var max = qkSureAyahCount(sure);
1740	ayet = parseInt(ayet, 10) || 1;
1741	if (ayet < 1) {
1742		ayet = 1;
1743	}
1744	if (ayet > max) {
1745		ayet = max;
1746	}
1747	$('#' + fromId + ', #' + toId).attr('max', max).attr('min', 1);
1748	$('#' + fromId).val(ayet);
1749	$('#' + toId).val(ayet);
1750}
1751
1752function qkReadAyahRange(fromId, toId, sure, fallback) {
1753	var max = qkSureAyahCount(sure);
1754	fallback = parseInt(fallback, 10) || 1;
1755	var from = parseInt($('#' + fromId).val(), 10) || fallback;
1756	var to = parseInt($('#' + toId).val(), 10) || fallback;
1757	if (from > to) {
1758		var tmp = from;
1759		from = to;
1760		to = tmp;
1761	}
1762	if (from < 1) {
1763		from = 1;
1764	}
1765	if (to > max) {
1766		to = max;
1767	}
1768	if (to - from + 1 > QK_VIDEO_AYAH_MAX) {
1769		to = from + QK_VIDEO_AYAH_MAX - 1;
1770		if (to > max) {
1771			to = max;
1772			from = Math.max(1, to - QK_VIDEO_AYAH_MAX + 1);
1773		}
1774	}
1775	return { from: from, to: to, max: max };
1776}
1777
1778function qkAyahAudioUrl(sure, ayet) {
1779	sure = parseInt(sure, 10) || 0;
1780	ayet = parseInt(ayet, 10) || 0;
1781	var idx = qkFindPlaylistIndex(sure, ayet);
1782	if (idx >= 0 && window.myPlaylist && window.myPlaylist.playlist && window.myPlaylist.playlist[idx]) {
1783		var hit = window.myPlaylist.playlist[idx].mp3 || window.myPlaylist.playlist[idx].m4a || '';
1784		if (hit) {
1785			return hit;
1786		}
1787	}
1788	var sample = '';
1789	if (window.myPlaylist && window.myPlaylist.playlist) {
1790		for (var i = 0; i < window.myPlaylist.playlist.length; i++) {
1791			sample = window.myPlaylist.playlist[i].mp3 || window.myPlaylist.playlist[i].m4a || '';
1792			if (sample) {
1793				break;
1794			}
1795		}
1796	}
1797	var file = qkPad3(sure) + qkPad3(ayet) + '.mp3';
1798	if (sample) {
1799		return sample.replace(/\d{6}\.mp3(\?.*)?$/i, file);
1800	}
1801	var rec = ($('#reciterSelector').val() || $('#qkMpReciterSelect').val() || '').replace(/[^a-z0-9_-]/gi, '');
1802	if (rec) {
1803		return qkBuildReciterAyahUrl(rec, sure, ayet);
1804	}
1805	return '';
1806}
1807
1808function qkBuildReciterAyahUrl(rec, sure, ayet) {
1809	var file = qkPad3(sure) + qkPad3(ayet) + '.mp3';
1810	var map = window.qkReciterAudio || {};
1811	var cfg = map[rec];
1812	if (!cfg) {
1813		return 'https://audio.qurancdn.com/' + rec + '/mp3/' + file;
1814	}
1815	var base = (cfg.base || '').replace(/\/$/, '');
1816	if (cfg.type === 'qurancdn') {
1817		return base + '/' + cfg.folder + '/mp3/' + file;
1818	}
1819	if (cfg.type === 'everyayah') {
1820		return base + '/' + cfg.folder + '/' + file;
1821	}
1822	if (cfg.type === 'custom') {
1823		return base + '/' + file;
1824	}
1825	return '';
1826}
1827
1828function qkCaptureSetStatus(text) {
1829	var $s = $('#qkCaptureStatus, #qkTepsirExportStatus');
1830	if (!text) {
1831		$s.attr('hidden', 'hidden').text('');
1832		return;
1833	}
1834	$s.removeAttr('hidden').text(text);
1835}
1836
1837function qkExportSizeFrom(name) {
1838	var v = ($('input[name="' + name + '"]:checked').val() || 'ig');
1839	if (v === 'fb') {
1840		return { w: 1080, h: 1350, key: 'fb' };
1841	}
1842	if (v === 'igstory') {
1843		return { w: 1080, h: 1920, key: 'igstory' };
1844	}
1845	if (v === 'reels') {
1846		return { w: 1080, h: 1920, key: 'reels' };
1847	}
1848	if (v === 'shorts') {
1849		return { w: 1080, h: 1920, key: 'shorts' };
1850	}
1851	return { w: 1080, h: 1080, key: 'ig' };
1852}
1853
1854function qkSyncCaptureShareBar(sure, ayet) {
1855	sure = parseInt(sure, 10) || 0;
1856	ayet = parseInt(ayet, 10) || 0;
1857	var $bar = $('#qkCaptureShareBar');
1858	if (!$bar.length || !sure || !ayet) {
1859		return;
1860	}
1861	$bar.attr('data-sure', sure).attr('data-ayet', ayet).attr('data-link', 'terjime/' + sure + '-' + ayet);
1862}
1863
1864function qkResetExportFit($stage) {
1865	var $fit = $stage.children('.qk-export-fit');
1866	if ($fit.length) {
1867		$fit.css({ position: '', left: '', top: '', transform: '', width: '', height: '' });
1868		$fit.contents().unwrap();
1869	}
1870}
1871
1872function qkFitExportStage($stage, cardSel) {
1873	if (!$stage || !$stage.length) {
1874		return;
1875	}
1876	var $fit = $stage.children('.qk-export-fit');
1877	if (!$fit.length) {
1878		var $card = cardSel ? $stage.find(cardSel).first() : $();
1879		if ($card.length) {
1880			$card.wrap('<div class="qk-export-fit"></div>');
1881		} else {
1882			return;
1883		}
1884		$fit = $stage.children('.qk-export-fit');
1885	}
1886	var layoutW = 520;
1887	$fit.css({
1888		transform: 'none',
1889		position: 'relative',
1890		left: 'auto',
1891		top: 'auto',
1892		width: layoutW + 'px',
1893		maxWidth: 'none',
1894		minWidth: layoutW + 'px'
1895	});
1896	var sw = $stage.innerWidth();
1897	var sh = $stage.innerHeight();
1898	var cw = $fit.outerWidth();
1899	var ch = $fit.outerHeight();
1900	if (sw < 20 || sh < 20 || cw < 40 || ch < 8) {
1901		return;
1902	}
1903	var pad = Math.min(sw, sh) * 0.05;
1904	var scale = Math.min((sw - pad * 2) / cw, (sh - pad * 2) / ch);
1905	if (!isFinite(scale) || scale <= 0) {
1906		scale = 1;
1907	}
1908	$fit.css({
1909		position: 'absolute',
1910		left: '50%',
1911		top: '50%',
1912		width: layoutW + 'px',
1913		minWidth: layoutW + 'px',
1914		transform: 'translate(-50%, -50%) scale(' + scale + ')',
1915		transformOrigin: 'center center'
1916	});
1917}
1918
1919function qkApplyCaptureSizePreview() {
1920	var v = ($('input[name="qkCaptureSize"]:checked').val() || 'ig');
1921	var $stage = $('#captureData');
1922	if (!$stage.length) {
1923		return;
1924	}
1925	qkResetExportFit($stage);
1926	$stage.addClass('qk-export-stage').removeClass('qk-size-ig qk-size-igstory qk-size-fb qk-size-reels qk-size-shorts').addClass('qk-size-' + v);
1927	qkApplyExportStyle($stage, 'capture');
1928	if (typeof qkAfterExportPreview === 'function') {
1929		qkAfterExportPreview('capture');
1930	}
1931}
1932
1933function qkApplyTepsirSizePreview() {
1934	var $data = $('#tepsirData');
1935	if ($data.length) {
1936		qkResetExportFit($data);
1937		$data.removeClass('qk-export-stage qk-size-ig qk-size-igstory qk-size-fb qk-size-reels qk-size-shorts qk-frame-teal qk-frame-thin qk-frame-dark qk-frame-gold');
1938	}
1939	var size = ($('input[name="qkTepsirSize"]:checked').val() || 'ig');
1940	var frame = ($('input[name="qkTepsirFrame"]:checked').val() || 'teal');
1941	$('#qkTepsirSizeThumb').attr('data-size', size).attr('data-frame', frame);
1942	$('#qkTepsirBrandStage').removeClass('qk-size-ig qk-size-igstory qk-size-fb qk-size-reels qk-size-shorts').addClass('qk-export-stage qk-size-' + size);
1943	qkApplyExportStyle($('#qkTepsirExportPreview .qk-tepsir-capture-card'), 'tepsir');
1944	if (typeof qkAfterExportPreview === 'function') {
1945		qkAfterExportPreview('tepsir');
1946	}
1947}
1948
1949function qkExportFontKey(name) {
1950	var v = ($('input[name="' + (name || 'qkTepsirAyahFont') + '"]:checked').val() || 'm');
1951	if (v !== 's' && v !== 'l' && v !== 'xl') {
1952		v = 'm';
1953	}
1954	return v;
1955}
1956
1957function qkExportColorVal(id, fallback) {
1958	var v = ($('#' + id).val() || '').trim();
1959	return /^#[0-9a-fA-F]{6}$/.test(v) ? v : (fallback || '#1c2a2e');
1960}
1961
1962function qkApplyExportStyle($root, kind) {
1963	if (!$root || !$root.length) {
1964		return;
1965	}
1966	var tepsir = kind !== 'capture';
1967	$root.attr('data-export-ayah-font', qkExportFontKey(tepsir ? 'qkTepsirAyahFont' : 'qkCaptureAyahFont'));
1968	$root.attr('data-export-body-font', qkExportFontKey(tepsir ? 'qkTepsirBodyFont' : 'qkCaptureBodyFont'));
1969	$root.css('--qk-export-ayah-color', qkExportColorVal(tepsir ? 'qkTepsirAyahColor' : 'qkCaptureAyahColor', '#1c2a2e'));
1970	$root.css('--qk-export-body-color', qkExportColorVal(tepsir ? 'qkTepsirBodyColor' : 'qkCaptureBodyColor', '#1c2a2e'));
1971}
1972
1973function qkApplyExportFont($root, name) {
1974	qkApplyExportStyle($root, (name || '').indexOf('Capture') >= 0 ? 'capture' : 'tepsir');
1975}
1976
1977window.qkApplyCaptureSizePreview = qkApplyCaptureSizePreview;
1978window.qkApplyTepsirSizePreview = qkApplyTepsirSizePreview;
1979
1980function qkTepsirCurrentAudioUrl() {
1981	return qkAyahAudioUrl(selectedSureNo, selectedAyetNo);
1982}
1983
1984function qkTepsirExportSize() {
1985	return qkExportSizeFrom('qkTepsirSize');
1986}
1987
1988function qkTepsirExportFrame() {
1989	return ($('input[name="qkTepsirFrame"]:checked').val() || 'teal');
1990}
1991
1992function qkTepsirFramePalette(style) {
1993	var palettes = {
1994		teal: { a: '#1FA4BE', b: '#0d7377', c: '#0D4A52' },
1995		thin: { a: '#1FA4BE', b: '#1FA4BE', c: '#0d7377' },
1996		dark: { a: '#0D4A52', b: '#0d7377', c: '#1FA4BE' },
1997		gold: { a: '#c9a227', b: '#0d7377', c: '#0D4A52' },
1998		'qk-frame-classic': { a: '#1FA4BE', b: '#ffffff', c: '#1FA4BE' },
1999		'qk-frame-gold': { a: '#c9a227', b: '#fff8ec', c: '#e8d5a3' },
2000		'qk-frame-parchment': { a: '#8b7355', b: '#faf6ee', c: '#c4b49a' },
2001		'qk-frame-emerald': { a: '#1a8a6a', b: '#e8f7f2', c: '#1a8a6a' },
2002		'qk-frame-midnight': { a: '#c9a227', b: '#2a3f4a', c: '#1e2d36' }
2003	};
2004	return palettes[style] || palettes.teal;
2005}
2006
2007function qkTepsirFrameMetrics(style, w, h) {
2008	var min = Math.min(w, h);
2009	var band = style === 'thin' ? Math.max(10, Math.round(min * 0.012)) : Math.max(22, Math.round(min * 0.026));
2010	var a = style === 'thin' ? Math.max(4, Math.round(band * 0.34)) : Math.max(8, Math.round(band * 0.38));
2011	var b = style === 'thin' ? Math.max(3, Math.round(band * 0.33)) : Math.max(7, Math.round(band * 0.34));
2012	var c = Math.max(3, band - a - b);
2013	var inset = a + b + c;
2014	return {
2015		a: a,
2016		b: b,
2017		c: c,
2018		inset: inset,
2019		inner: { x: inset, y: inset, w: w - inset * 2, h: h - inset * 2 }
2020	};
2021}
2022
2023function qkTepsirDrawEdgeBand(ctx, w, h, inset, thickness, color) {
2024	if (thickness < 1) {
2025		return;
2026	}
2027	ctx.fillStyle = color;
2028	ctx.fillRect(inset, inset, w - inset * 2, thickness);
2029	ctx.fillRect(inset, h - inset - thickness, w - inset * 2, thickness);
2030	ctx.fillRect(inset, inset, thickness, h - inset * 2);
2031	ctx.fillRect(w - inset - thickness, inset, thickness, h - inset * 2);
2032}
2033
2034function qkTepsirDrawEdgeFrame(ctx, w, h, style) {
2035	if (!style || style === 'qk-frame-img') {
2036		return;
2037	}
2038	var pal = qkTepsirFramePalette(style);
2039	var m = qkTepsirFrameMetrics(style, w, h);
2040	qkTepsirDrawEdgeBand(ctx, w, h, 0, m.a, pal.a);
2041	qkTepsirDrawEdgeBand(ctx, w, h, m.a, m.b, pal.b);
2042	qkTepsirDrawEdgeBand(ctx, w, h, m.a + m.b, m.c, pal.c);
2043}
2044
2045function qkTepsirPaintCaptureFrame(srcCanvas, style) {
2046	style = style || 'teal';
2047	var m = qkTepsirFrameMetrics(style, srcCanvas.width + 80, srcCanvas.height + 80);
2048	var out = document.createElement('canvas');
2049	out.width = srcCanvas.width + m.inset * 2;
2050	out.height = srcCanvas.height + m.inset * 2;
2051	var ctx = out.getContext('2d');
2052	ctx.fillStyle = '#fffefb';
2053	ctx.fillRect(0, 0, out.width, out.height);
2054	ctx.drawImage(srcCanvas, m.inset, m.inset);
2055	qkTepsirDrawEdgeFrame(ctx, out.width, out.height, style);
2056	return out;
2057}
2058
2059function qkTepsirComposeExportCanvas(src, size, style, bg, footer, grow) {
2060	var tw = size.w;
2061	var th = size.h;
2062	var m = qkTepsirFrameMetrics(style, tw, th);
2063	function sidePad(metrics) {
2064		return Math.max(8, Math.round(Math.min(metrics.inner.w, metrics.inner.h) * 0.012));
2065	}
2066	var pad = sidePad(m);
2067	var maxW = m.inner.w - pad * 2;
2068	function footHFor(innerH) {
2069		if (!footer || !footer.width || !footer.height) {
2070			return 0;
2071		}
2072		return Math.min(Math.round(footer.height * (maxW / footer.width)), Math.round(Math.max(innerH, 200) * 0.14));
2073	}
2074	var footH = footHFor(m.inner.h);
2075	var scale = maxW / Math.max(1, src.width);
2076	var dw = Math.max(1, Math.round(src.width * scale));
2077	var dh = Math.max(1, Math.round(src.height * scale));
2078	var gap = footH ? Math.round(pad * 0.35) : 0;
2079	var needH = pad + dh + (footH ? gap + footH : 0) + pad + m.inset * 2;
2080	if (needH > th) {
2081		th = needH;
2082		m = qkTepsirFrameMetrics(style, tw, th);
2083		pad = sidePad(m);
2084		maxW = m.inner.w - pad * 2;
2085		scale = maxW / Math.max(1, src.width);
2086		dw = Math.max(1, Math.round(src.width * scale));
2087		dh = Math.max(1, Math.round(src.height * scale));
2088		footH = footHFor(m.inner.h);
2089		gap = footH ? Math.round(pad * 0.35) : 0;
2090	}
2091	var out = document.createElement('canvas');
2092	out.width = tw;
2093	out.height = th;
2094	var ctx = out.getContext('2d');
2095	ctx.fillStyle = bg || '#fffefb';
2096	ctx.fillRect(0, 0, tw, th);
2097	var inner = m.inner;
2098	var x = inner.x + pad;
2099	var y = inner.y + pad;
2100	var availH = inner.h - pad * 2 - (footH ? gap + footH : 0);
2101	if (!grow && dh < availH - 2) {
2102		y = inner.y + pad + Math.round((availH - dh) / 2);
2103	}
2104	ctx.drawImage(src, x, y, dw, dh);
2105	if (footer && footH) {
2106		ctx.drawImage(footer, x, inner.y + inner.h - pad - footH, maxW, footH);
2107	}
2108	qkTepsirDrawEdgeFrame(ctx, tw, th, style);
2109	return out;
2110}
2111
2112function qkCanvasFromEl($el, bg, minW) {
2113	var node = $el && $el[0] ? $el[0] : $el;
2114	if (!node) {
2115		return Promise.reject(new Error('empty'));
2116	}
2117	var w = Math.max(node.scrollWidth || 0, node.offsetWidth || 0, minW || 1);
2118	var h = Math.max(node.scrollHeight || 0, node.offsetHeight || 0, 1);
2119	return window.html2canvas(node, {
2120		backgroundColor: bg || '#fffefb',
2121		scale: Math.min(window.devicePixelRatio || 1, 2),
2122		useCORS: true,
2123		allowTaint: true,
2124		logging: false,
2125		width: w,
2126		height: h
2127	});
2128}
2129
2130function qkExportBrandNode(bg, dark) {
2131	var logoSrc = $('.qk-header-logo-img, .qk-tepsir-modal-logo, .qk-tepsir-modal-brandbar-logo').first().attr('src') || '';
2132	var $brand = $('<div class="qk-tepsir-capture-brand"></div>').css({
2133		width: '720px',
2134		boxSizing: 'border-box',
2135		padding: '12px 16px 10px',
2136		background: bg || '#fffefb',
2137		borderTop: dark ? '1px solid rgba(255,255,255,0.2)' : '1px solid rgba(19, 165, 174, 0.3)'
2138	});
2139	if (logoSrc) {
2140		$brand.append($('<img alt="Qurankerim">').attr({ src: logoSrc, crossorigin: 'anonymous' }));
2141	}
2142	var $url = $('<div class="qk-tepsir-capture-url"></div>').text('www.qurankerim.com');
2143	if (dark) {
2144		$url.css('color', '#7fd4e8');
2145	}
2146	$brand.append($url);
2147	return $brand;
2148}
2149
2150function qkCaptureBrandFooter(bg, dark) {
2151	var $host = $('#qkTepsirCaptureHost');
2152	if (!$host.length) {
2153		$host = $('<div id="qkTepsirCaptureHost"></div>').appendTo(document.body);
2154	}
2155	var $brand = qkExportBrandNode(bg, dark);
2156	$host.append($brand);
2157	return qkTepsirWaitCaptureAssets($brand).then(function () {
2158		return qkCanvasFromEl($brand, bg, 720);
2159	}).then(function (canvas) {
2160		$brand.remove();
2161		return canvas;
2162	});
2163}
2164
2165function qkTepsirFitCanvas(src, tw, th, bg) {
2166	var out = document.createElement('canvas');
2167	out.width = tw;
2168	out.height = th;
2169	var ctx = out.getContext('2d');
2170	ctx.fillStyle = bg || '#fffefb';
2171	ctx.fillRect(0, 0, tw, th);
2172	var pad = Math.round(Math.min(tw, th) * 0.04);
2173	var maxW = tw - pad * 2;
2174	var maxH = th - pad * 2;
2175	var scale = Math.min(maxW / src.width, maxH / src.height);
2176	var dw = Math.max(1, Math.round(src.width * scale));
2177	var dh = Math.max(1, Math.round(src.height * scale));
2178	ctx.drawImage(src, Math.round((tw - dw) / 2), Math.round((th - dh) / 2), dw, dh);
2179	return out;
2180}
2181
2182function qkUnlockAudioForRecord() {
2183	var AudioCtx = window.AudioContext || window.webkitAudioContext;
2184	if (!AudioCtx) {
2185		return null;
2186	}
2187	if (!window._qkRecordAudioCtx || window._qkRecordAudioCtx.state === 'closed') {
2188		try {
2189			window._qkRecordAudioCtx = new AudioCtx({ sampleRate: 48000, latencyHint: 'interactive' });
2190		} catch (eNew) {
2191			window._qkRecordAudioCtx = new AudioCtx();
2192		}
2193	}
2194	var ac = window._qkRecordAudioCtx;
2195	if (ac.resume) {
2196		ac.resume();
2197	}
2198	try {
2199		var buf = ac.createBuffer(1, Math.max(1, Math.round((ac.sampleRate || 48000) * 0.03)), ac.sampleRate || 48000);
2200		var src = ac.createBufferSource();
2201		src.buffer = buf;
2202		var g = ac.createGain();
2203		g.gain.value = 0.0001;
2204		src.connect(g);
2205		g.connect(ac.destination);
2206		src.start(0);
2207	} catch (e) {}
2208	return ac;
2209}
2210
2211function qkDecodeAudioBuffer(ac, url) {
2212	return fetch(url, { mode: 'cors', credentials: 'omit', cache: 'force-cache' }).then(function (res) {
2213		if (!res.ok) {
2214			throw new Error('audio-http');
2215		}
2216		return res.arrayBuffer();
2217	}).then(function (ab) {
2218		var copy = ab.slice(0);
2219		return new Promise(function (resolve, reject) {
2220			var done = false;
2221			function ok(buf) {
2222				if (done) {
2223					return;
2224				}
2225				done = true;
2226				resolve(buf);
2227			}
2228			function fail(err) {
2229				if (done) {
2230					return;
2231				}
2232				done = true;
2233				reject(err || new Error('decode'));
2234			}
2235			try {
2236				var p = ac.decodeAudioData(copy, ok, fail);
2237				if (p && typeof p.then === 'function') {
2238					p.then(ok, fail);
2239				}
2240			} catch (eDec) {
2241				fail(eDec);
2242			}
2243		});
2244	});
2245}
2246
2247function qkStackClipCanvases(clips, bg) {
2248	var srcs = (clips || []).filter(function (c) {
2249		return c && c.canvas && c.canvas.width;
2250	});
2251	if (!srcs.length) {
2252		return null;
2253	}
2254	var w = srcs[0].canvas.width;
2255	var gap = Math.max(28, Math.round(w * 0.055));
2256	var parts = [];
2257	var totalH = 0;
2258	srcs.forEach(function (c) {
2259		var h = Math.max(1, Math.round(c.canvas.height * (w / Math.max(1, c.canvas.width))));
2260		parts.push({ canvas: c.canvas, h: h });
2261		totalH += h + gap;
2262	});
2263	totalH -= gap;
2264	var out = document.createElement('canvas');
2265	out.width = w;
2266	out.height = Math.max(1, totalH);
2267	var ctx = out.getContext('2d');
2268	ctx.fillStyle = bg || '#fffefb';
2269	ctx.fillRect(0, 0, w, out.height);
2270	var y = 0;
2271	parts.forEach(function (p) {
2272		ctx.drawImage(p.canvas, 0, y, w, p.h);
2273		y += p.h + gap;
2274	});
2275	return out;
2276}
2277
2278function qkMp4MuxerUrl() {
2279	var el = document.querySelector('script[src*="app.js"]');
2280	if (el && el.src) {
2281		return el.src.replace(/js\/app\.js(\?.*)?$/i, 'js/mp4-muxer.mjs');
2282	}
2283	return '/v5_assets/js/mp4-muxer.mjs';
2284}
2285
2286function qkLoadMp4Muxer() {
2287	if (window._qkMp4Muxer) {
2288		return Promise.resolve(window._qkMp4Muxer);
2289	}
2290	return import(qkMp4MuxerUrl()).then(function (mod) {
2291		window._qkMp4Muxer = mod;
2292		return mod;
2293	});
2294}
2295
2296function qkCanEncodeMp4WebCodecs() {
2297	return typeof VideoEncoder === 'function' && typeof AudioEncoder === 'function' && typeof VideoFrame === 'function' && typeof AudioData === 'function';
2298}
2299
2300function qkWaitEncoderDrain(encoder) {
2301	if (!encoder || encoder.encodeQueueSize < 8) {
2302		return Promise.resolve();
2303	}
2304	return new Promise(function (resolve) {
2305		function check() {
2306			if (!encoder || encoder.encodeQueueSize < 3) {
2307				resolve();
2308				return;
2309			}
2310			setTimeout(check, 8);
2311		}
2312		check();
2313	});
2314}
2315
2316function qkPickAvcConfig(w, h) {
2317	var codecs = ['avc1.42001E', 'avc1.42001f', 'avc1.4d001f', 'avc1.4d0028'];
2318	var i = 0;
2319	function next() {
2320		if (i >= codecs.length) {
2321			return Promise.reject(new Error('avc'));
2322		}
2323		var cfg = {
2324			codec: codecs[i],
2325			width: w,
2326			height: h,
2327			bitrate: 4500000,
2328			framerate: 30,
2329			avc: { format: 'avc' }
2330		};
2331		i += 1;
2332		if (typeof VideoEncoder.isConfigSupported !== 'function') {
2333			return Promise.resolve(cfg);
2334		}
2335		return VideoEncoder.isConfigSupported(cfg).then(function (r) {
2336			if (r && r.supported) {
2337				return r.config || cfg;
2338			}
2339			return next();
2340		}).catch(next);
2341	}
2342	return next();
2343}
2344
2345function qkPickAacConfig(sampleRate, channels) {
2346	var rates = [sampleRate, 48000, 44100];
2347	var seen = {};
2348	var list = [];
2349	rates.forEach(function (r) {
2350		r = parseInt(r, 10) || 0;
2351		if (r && !seen[r]) {
2352			seen[r] = 1;
2353			list.push(r);
2354		}
2355	});
2356	var i = 0;
2357	function next() {
2358		if (i >= list.length) {
2359			return Promise.reject(new Error('aac'));
2360		}
2361		var cfg = {
2362			codec: 'mp4a.40.2',
2363			sampleRate: list[i],
2364			numberOfChannels: channels,
2365			bitrate: 128000
2366		};
2367		i += 1;
2368		if (typeof AudioEncoder.isConfigSupported !== 'function') {
2369			return Promise.resolve(cfg);
2370		}
2371		return AudioEncoder.isConfigSupported(cfg).then(function (r) {
2372			if (r && r.supported) {
2373				return r.config || cfg;
2374			}
2375			return next();
2376		}).catch(next);
2377	}
2378	return next();
2379}
2380
2381function qkMergeClipAudioStereo(ac, clips) {
2382	var parts = [];
2383	(clips || []).forEach(function (c) {
2384		if (c && c.buffer && c.buffer.length) {
2385			parts.push(c.buffer);
2386		}
2387	});
2388	if (!parts.length) {
2389		return null;
2390	}
2391	var rate = parts[0].sampleRate;
2392	var total = 0;
2393	parts.forEach(function (p) {
2394		total += p.length;
2395	});
2396	var out = ac.createBuffer(2, total, rate);
2397	var off = 0;
2398	parts.forEach(function (p) {
2399		var left = p.getChannelData(0);
2400		var right = p.numberOfChannels > 1 ? p.getChannelData(1) : left;
2401		out.getChannelData(0).set(left, off);
2402		out.getChannelData(1).set(right, off);
2403		off += p.length;
2404	});
2405	return out;
2406}
2407
2408function qkEncodeAyahVideoMp4(opts) {
2409	opts = opts || {};
2410	if (!qkCanEncodeMp4WebCodecs()) {
2411		return Promise.reject(new Error('webcodecs'));
2412	}
2413	var recCanvas = opts.canvas;
2414	var paintAtProgress = opts.paintAtProgress;
2415	var clips = opts.clips || [];
2416	var ac = opts.ac;
2417	var w = opts.w - (opts.w % 2);
2418	var h = opts.h - (opts.h % 2);
2419	var fps = opts.fps || 30;
2420	var totalDur = opts.totalDur || 1;
2421	if (totalDur < 0.25) {
2422		totalDur = 0.25;
2423	}
2424	var numFrames = Math.max(8, Math.round(totalDur * fps));
2425	var mixed = qkMergeClipAudioStereo(ac, clips);
2426	if (!mixed) {
2427		return Promise.reject(new Error('audio'));
2428	}
2429	return qkLoadMp4Muxer().then(function (mod) {
2430		return qkPickAvcConfig(w, h).then(function (vCfg) {
2431			return qkPickAacConfig(mixed.sampleRate, 2).then(function (aCfg) {
2432				return { mod: mod, vCfg: vCfg, aCfg: aCfg };
2433			});
2434		});
2435	}).then(function (pack) {
2436		var Muxer = pack.mod.Muxer;
2437		var ArrayBufferTarget = pack.mod.ArrayBufferTarget;
2438		var target = new ArrayBufferTarget();
2439		var muxer = new Muxer({
2440			target: target,
2441			fastStart: 'in-memory',
2442			firstTimestampBehavior: 'offset',
2443			video: {
2444				codec: 'avc',
2445				width: w,
2446				height: h,
2447				frameRate: fps
2448			},
2449			audio: {
2450				codec: 'aac',
2451				numberOfChannels: 2,
2452				sampleRate: pack.aCfg.sampleRate
2453			}
2454		});
2455		var vErr = null;
2456		var aErr = null;
2457		var videoEncoder = new VideoEncoder({
2458			output: function (chunk, meta) {
2459				muxer.addVideoChunk(chunk, meta);
2460			},
2461			error: function (e) {
2462				vErr = e;
2463			}
2464		});
2465		var audioEncoder = new AudioEncoder({
2466			output: function (chunk, meta) {
2467				muxer.addAudioChunk(chunk, meta);
2468			},
2469			error: function (e) {
2470				aErr = e;
2471			}
2472		});
2473		videoEncoder.configure(pack.vCfg);
2474		audioEncoder.configure(pack.aCfg);
2475		if (pack.aCfg.sampleRate !== mixed.sampleRate) {
2476			throw new Error('rate');
2477		}
2478		var frameDur = Math.round(1e6 / fps);
2479		var seq = Promise.resolve();
2480		var n;
2481		for (n = 0; n < numFrames; n++) {
2482			seq = seq.then((function (idx) {
2483				return function () {
2484					if (vErr) {
2485						throw vErr;
2486					}
2487					qkCaptureSetStatus((idx + 1) + ' / ' + numFrames);
2488					paintAtProgress(idx / Math.max(numFrames - 1, 1));
2489					var vf = new VideoFrame(recCanvas, {
2490						timestamp: idx * frameDur,
2491						duration: frameDur
2492					});
2493					videoEncoder.encode(vf, { keyFrame: idx % (fps * 2) === 0 });
2494					vf.close();
2495					return qkWaitEncoderDrain(videoEncoder);
2496				};
2497			})(n));
2498		}
2499		return seq.then(function () {
2500			if (aErr) {
2501				throw aErr;
2502			}
2503			var sampleRate = mixed.sampleRate;
2504			var frameCount = 1024;
2505			var ts = 0;
2506			var i;
2507			for (i = 0; i < mixed.length; i += frameCount) {
2508				var count = Math.min(frameCount, mixed.length - i);
2509				var plane0 = mixed.getChannelData(0).subarray(i, i + count);
2510				var plane1 = mixed.getChannelData(1).subarray(i, i + count);
2511				var copy0 = new Float32Array(count);
2512				var copy1 = new Float32Array(count);
2513				copy0.set(plane0);
2514				copy1.set(plane1);
2515				var data = new Float32Array(count * 2);
2516				data.set(copy0, 0);
2517				data.set(copy1, count);
2518				var ad = new AudioData({
2519					format: 'f32-planar',
2520					sampleRate: sampleRate,
2521					numberOfFrames: count,
2522					numberOfChannels: 2,
2523					timestamp: ts,
2524					data: data
2525				});
2526				audioEncoder.encode(ad);
2527				ad.close();
2528				ts += Math.round((count / sampleRate) * 1e6);
2529			}
2530			return qkWaitEncoderDrain(audioEncoder);
2531		}).then(function () {
2532			return Promise.all([
2533				videoEncoder.state === 'configured' ? videoEncoder.flush() : Promise.resolve(),
2534				audioEncoder.state === 'configured' ? audioEncoder.flush() : Promise.resolve()
2535			]);
2536		}).then(function () {
2537			if (vErr) {
2538				throw vErr;
2539			}
2540			if (aErr) {
2541				throw aErr;
2542			}
2543			try {
2544				videoEncoder.close();
2545			} catch (eV) {}
2546			try {
2547				audioEncoder.close();
2548			} catch (eA) {}
2549			muxer.finalize();
2550			return {
2551				blob: new Blob([target.buffer], { type: 'video/mp4' }),
2552				ext: 'mp4',
2553				duration: totalDur
2554			};
2555		});
2556	});
2557}
2558
2559function qkExtractAyahArabic($root) {
2560	if (!$root || !$root.length) {
2561		return '';
2562	}
2563	var $el = $root.find('.qk-tepsir-capture-ayah-box, .qk-jumle-arabic-box, .qk-ehsen-arabic-box, .ayet.ar-fsize, .qk-modal-ayet, .ayet').first();
2564	if (!$el.length && $root.is('.ayet, .qk-modal-ayet, .qk-tepsir-capture-ayah-box')) {
2565		$el = $root;
2566	}
2567	var text = ($el.text() || '').replace(/\s+/g, ' ').trim();
2568	return text;
2569}
2570
2571function qkAyahArabicText(sure, ayet) {
2572	sure = parseInt(sure, 10) || 0;
2573	ayet = parseInt(ayet, 10) || 0;
2574	var t = '';
2575	var $tp = $('#qkTepsirExportPreview .qk-tepsir-export-ayah-piece');
2576	if ($tp.length && ayet) {
2577		var $hit = $tp.filter('[data-ayah="' + ayet + '"]').first();
2578		if (!$hit.length) {
2579			var range = qkReadAyahRange('qkTepsirAyahFrom', 'qkTepsirAyahTo', sure, ayet);
2580			$hit = $tp.eq(ayet - range.from);
2581		}
2582		t = ($hit.attr('data-ayah-ar') || '').trim() || qkExtractAyahArabic($hit);
2583	}
2584	if (!t) {
2585		var $cp = $('#captureData .qk-capture-ayah-piece');
2586		if ($cp.length && ayet) {
2587			var $ch = $cp.filter('[data-ayah="' + ayet + '"]').first();
2588			if (!$ch.length) {
2589				var cr = qkReadAyahRange('qkCaptureAyahFrom', 'qkCaptureAyahTo', sure, ayet);
2590				$ch = $cp.eq(ayet - cr.from);
2591			}
2592			t = ($ch.attr('data-ayah-ar') || '').trim() || qkExtractAyahArabic($ch);
2593		}
2594	}
2595	if (!t) {
2596		t = qkExtractAyahArabic($('#captureData'));
2597	}
2598	if (!t) {
2599		var $page = $('[data-sure="' + sure + '"][data-ayet="' + ayet + '"]').first();
2600		if ($page.length) {
2601			t = qkExtractAyahArabic($page.closest('.ayet-item, .verse, li, .row, article, div'));
2602			if (!t) {
2603				t = qkExtractAyahArabic($page);
2604			}
2605		}
2606	}
2607	if (!t) {
2608		t = qkExtractAyahArabic($('#tepsirData'));
2609	}
2610	return t;
2611}
2612
2613function qkCanvasWrapLines(ctx, text, maxW) {
2614	var words = String(text || '').split(/\s+/).filter(Boolean);
2615	var lines = [];
2616	var line = '';
2617	var i;
2618	for (i = 0; i < words.length; i++) {
2619		var test = line ? (line + ' ' + words[i]) : words[i];
2620		if (line && ctx.measureText(test).width > maxW) {
2621			lines.push(line);
2622			line = words[i];
2623		} else {
2624			line = test;
2625		}
2626	}
2627	if (line) {
2628		lines.push(line);
2629	}
2630	return lines.slice(0, 5);
2631}
2632
2633function qkMeasureAyahBoxes($root) {
2634	if (!$root || !$root[0]) {
2635		return [];
2636	}
2637	var root = $root[0];
2638	var origin = root.getBoundingClientRect();
2639	var boxes = [];
2640	var $hits = $root.find('.qk-capture-ayah-piece, .qk-tepsir-export-ayah-piece').filter(function () {
2641		var r = this.getBoundingClientRect();
2642		return r.width > 24 && r.height > 16;
2643	});
2644	if (!$hits.length) {
2645		$hits = $root.find('.qk-tepsir-capture-ayah-box, .qk-jumle-arabic-box, .qk-ehsen-arabic-box, .qk-tefsir-ayah-box, .ar-fsize').filter(function () {
2646			var r = this.getBoundingClientRect();
2647			return r.width > 24 && r.height > 16;
2648		});
2649	}
2650	if (!$hits.length) {
2651		$hits = $root.find('.ayet').filter(function () {
2652			var r = this.getBoundingClientRect();
2653			return r.width > 24 && r.height > 16;
2654		});
2655	}
2656	$hits.each(function () {
2657		var r = this.getBoundingClientRect();
2658		var box = {
2659			x: r.left - origin.left + (root.scrollLeft || 0),
2660			y: r.top - origin.top + (root.scrollTop || 0),
2661			w: r.width,
2662			h: r.height
2663		};
2664		var ar = null;
2665		if (this.querySelector) {
2666			ar = this.querySelector('.qk-jumle-arabic-box, .qk-ehsen-arabic-box, .ayet.ar-fsize, .qk-modal-ayet');
2667			if (!ar) {
2668				ar = this.querySelector('.ayet');
2669			}
2670		}
2671		if (ar) {
2672			var arR = ar.getBoundingClientRect();
2673			if (arR.width > 24 && arR.height > 12) {
2674				box.pulse = {
2675					x: arR.left - origin.left + (root.scrollLeft || 0),
2676					y: arR.top - origin.top + (root.scrollTop || 0),
2677					w: arR.width,
2678					h: arR.height
2679				};
2680			}
2681		}
2682		boxes.push(box);
2683	});
2684	return boxes;
2685}
2686
2687function qkShrinkWrapExportNode($el) {
2688	var node = $el && $el[0];
2689	if (!node) {
2690		return;
2691	}
2692	$el.css({
2693		height: 'auto',
2694		minHeight: '0',
2695		maxHeight: 'none',
2696		flex: 'none'
2697	});
2698	var h = 0;
2699	var kids = node.children;
2700	var i;
2701	for (i = 0; i < kids.length; i++) {
2702		h = Math.max(h, kids[i].offsetTop + kids[i].offsetHeight);
2703	}
2704	h = Math.max(h, node.scrollHeight || 0);
2705	if (h > 1) {
2706		$el.css('height', Math.ceil(h) + 'px');
2707	}
2708}
2709
2710function qkCropCanvasToInk(canvas, ayahBoxes) {
2711	if (!canvas || !canvas.getContext) {
2712		return canvas;
2713	}
2714	var ctx;
2715	var img;
2716	try {
2717		ctx = canvas.getContext('2d');
2718		img = ctx.getImageData(0, 0, canvas.width, canvas.height);
2719	} catch (eInk) {
2720		return canvas;
2721	}
2722	var d = img.data;
2723	var w = canvas.width;
2724	var h = canvas.height;
2725	var step = w > 1400 ? 3 : 2;
2726	function rowHasInk(y) {
2727		var row = y * w * 4;
2728		var x;
2729		for (x = 0; x < w; x += step) {
2730			var i = row + x * 4;
2731			if (d[i + 3] < 18) {
2732				continue;
2733			}
2734			if (d[i] < 248 || d[i + 1] < 247 || d[i + 2] < 244) {
2735				return true;
2736			}
2737		}
2738		return false;
2739	}
2740	var top = 0;
2741	var bot = h - 1;
2742	var y;
2743	var found = false;
2744	for (y = 0; y < h; y += 1) {
2745		if (rowHasInk(y)) {
2746			top = y;
2747			found = true;
2748			break;
2749		}
2750	}
2751	if (!found) {
2752		return canvas;
2753	}
2754	for (y = h - 1; y >= top; y -= 1) {
2755		if (rowHasInk(y)) {
2756			bot = y;
2757			break;
2758		}
2759	}
2760	var pad = Math.max(10, Math.round(w * 0.012));
2761	top = Math.max(0, top - pad);
2762	bot = Math.min(h - 1, bot + pad);
2763	if (top <= 1 && bot >= h - 2) {
2764		return canvas;
2765	}
2766	var out = document.createElement('canvas');
2767	out.width = w;
2768	out.height = bot - top + 1;
2769	out.getContext('2d').drawImage(canvas, 0, top, w, out.height, 0, 0, w, out.height);
2770	var boxes = ayahBoxes || canvas._ayahBoxes || [];
2771	out._ayahBoxes = boxes.map(function (b) {
2772		return { x: b.x, y: b.y - top, w: b.w, h: b.h };
2773	});
2774	return out;
2775}
2776
2777function qkAyahBoxesToCanvas(boxes, elW, canvas) {
2778	var sx = (canvas && canvas.width ? canvas.width : 1) / Math.max(1, elW);
2779	return (boxes || []).map(function (b) {
2780		var o = { x: b.x * sx, y: b.y * sx, w: b.w * sx, h: b.h * sx };
2781		if (b.pulse) {
2782			o.pulse = { x: b.pulse.x * sx, y: b.pulse.y * sx, w: b.pulse.w * sx, h: b.pulse.h * sx };
2783		}
2784		return o;
2785	});
2786}
2787
2788function qkPulseSrcBox(srcBox) {
2789	return (srcBox && srcBox.pulse) ? srcBox.pulse : srcBox;
2790}
2791
2792function qkMapStillRect(srcBox, still, dx, dy, dw, dh) {
2793	if (!srcBox || !still) {
2794		return null;
2795	}
2796	var sx = dw / Math.max(1, still.width);
2797	var sy = dh / Math.max(1, still.height);
2798	return {
2799		x: dx + srcBox.x * sx,
2800		y: dy + srcBox.y * sy,
2801		w: srcBox.w * sx,
2802		h: srcBox.h * sy
2803	};
2804}
2805
2806function qkPulseAyahRegion(ctx, rect, tSec) {
2807	if (!rect || rect.w < 8 || rect.h < 8) {
2808		return;
2809	}
2810	var pulse = 0.5 - 0.5 * Math.cos((Math.max(0, tSec) / 2.2) * Math.PI * 2);
2811	ctx.save();
2812	ctx.lineWidth = Math.max(2, Math.round(Math.min(rect.w, rect.h) * 0.018));
2813	ctx.strokeStyle = 'rgba(31, 164, 190,' + (0.28 + 0.72 * pulse).toFixed(3) + ')';
2814	ctx.fillStyle = 'rgba(31, 164, 190,' + (0.03 + 0.11 * pulse).toFixed(3) + ')';
2815	if (typeof ctx.roundRect === 'function') {
2816		ctx.beginPath();
2817		ctx.roundRect(rect.x, rect.y, rect.w, rect.h, Math.min(12, Math.round(rect.h * 0.12)));
2818		ctx.fill();
2819		ctx.stroke();
2820	} else {
2821		ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
2822		ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);
2823	}
2824	ctx.restore();
2825}
2826
2827function qkClipIndexAtTime(clips, tSec) {
2828	var acc = 0;
2829	var i;
2830	for (i = 0; i < clips.length; i++) {
2831		var d = clips[i] && clips[i].buffer && clips[i].buffer.duration ? clips[i].buffer.duration : 0;
2832		if (tSec < acc + d + 0.02) {
2833			return i;
2834		}
2835		acc += d;
2836	}
2837	return Math.max(0, clips.length - 1);
2838}
2839
2840function qkClipAyahBoxes(clips) {
2841	var i;
2842	for (i = 0; i < clips.length; i++) {
2843		if (clips[i] && clips[i].ayahBoxes && clips[i].ayahBoxes.length) {
2844			return clips[i].ayahBoxes;
2845		}
2846	}
2847	return [];
2848}
2849
2850function qkTepsirRecordAyahVideo(stillCanvas, audioUrl, extra) {
2851	extra = extra || {};
2852	var canvas = (stillCanvas && stillCanvas.canvas) ? stillCanvas.canvas : stillCanvas;
2853	var footer = extra.footer || (stillCanvas && stillCanvas.footer) || null;
2854	return qkRecordAyahVideoClips([{
2855		canvas: canvas,
2856		audioUrl: audioUrl,
2857		footer: footer,
2858		ayahText: extra.ayahText || qkAyahArabicText(selectedSureNo, selectedAyetNo),
2859		ayahBoxes: extra.ayahBoxes || (canvas && canvas._ayahBoxes) || (stillCanvas && stillCanvas.ayahBoxes) || []
2860	}], {
2861		scroll: true,
2862		edgeFrame: qkTepsirExportFrame(),
2863		size: qkTepsirExportSize(),
2864		footer: footer,
2865		bg: extra.bg || '#fffefb'
2866	});
2867}
2868
2869function qkRecordAyahVideoClips(clips, opts) {
2870	return new Promise(function (resolve, reject) {
2871		opts = opts || {};
2872		clips = (clips || []).filter(function (c) {
2873			return c && c.canvas && c.audioUrl;
2874		});
2875		if (!clips.length || typeof window.MediaRecorder !== 'function' || !HTMLCanvasElement.prototype.captureStream) {
2876			reject(new Error('video'));
2877			return;
2878		}
2879		var size = opts.size || { w: clips[0].canvas.width, h: clips[0].canvas.height };
2880		var w = size.w - (size.w % 2);
2881		var h = size.h - (size.h % 2);
2882		var scroll = opts.scroll !== false;
2883		var centerBlocks = opts.centerBlocks === true;
2884		var edgeFrame = opts.edgeFrame || '';
2885		var fillBg = opts.bg || '#fffefb';
2886		var overlayImg = opts.overlay || null;
2887		var frameM = edgeFrame ? qkTepsirFrameMetrics(edgeFrame, w, h) : {
2888			inner: {
2889				x: Math.round(Math.min(w, h) * 0.026),
2890				y: Math.round(Math.min(w, h) * 0.026),
2891				w: w - Math.round(Math.min(w, h) * 0.052),
2892				h: h - Math.round(Math.min(w, h) * 0.052)
2893			}
2894		};
2895		if (typeof qkInsetFrameMetrics === 'function') {
2896			frameM = qkInsetFrameMetrics(frameM, (size && size.key) || '', w, h);
2897		}
2898		var recCanvas = document.createElement('canvas');
2899		recCanvas.width = w;
2900		recCanvas.height = h;
2901		recCanvas.setAttribute('aria-hidden', 'true');
2902		recCanvas.style.cssText = 'position:fixed;left:-10000px;top:0;width:' + w + 'px;height:' + h + 'px;opacity:1;pointer-events:none;';
2903		document.body.appendChild(recCanvas);
2904		var rctx = recCanvas.getContext('2d', { alpha: false });
2905		var still = clips[0].canvas;
2906		var currentFooter = clips[0].footer || opts.footer || null;
2907		var clipStart = 0;
2908		var clipDur = 1;
2909		var videoStart = 0;
2910		var totalDur = 0;
2911		var vTrack = null;
2912		function ease(t) {
2913			if (t <= 0) {
2914				return 0;
2915			}
2916			if (t >= 1) {
2917				return 1;
2918			}
2919			return 0.5 - 0.5 * Math.cos(Math.PI * t);
2920		}
2921		function paintAtProgress(t01) {
2922			if (t01 > 1) {
2923				t01 = 1;
2924			}
2925			if (t01 < 0) {
2926				t01 = 0;
2927			}
2928			if (opts.pageReady && still) {
2929				var pageIdx = qkClipIndexAtTime(clips, t01 * (totalDur || 1));
2930				var page = (clips[pageIdx] && clips[pageIdx].canvas) || still;
2931				rctx.drawImage(page, 0, 0, w, h);
2932				if (vTrack && typeof vTrack.requestFrame === 'function') {
2933					try {
2934						vTrack.requestFrame();
2935					} catch (eRfReady) {}
2936				}
2937				return;
2938			}
2939			rctx.fillStyle = fillBg;
2940			rctx.fillRect(0, 0, w, h);
2941			if (typeof qkPaintExportBackdrop === 'function') {
2942				qkPaintExportBackdrop(rctx, w, h);
2943			}
2944			if (!still) {
2945				if (edgeFrame) {
2946					qkTepsirDrawEdgeFrame(rctx, w, h, edgeFrame);
2947				}
2948				if (overlayImg && overlayImg.naturalWidth) {
2949					rctx.drawImage(overlayImg, 0, 0, w, h);
2950				}
2951				if (typeof qkDrawExportBrand === 'function') {
2952					qkDrawExportBrand(rctx);
2953				}
2954				return;
2955			}
2956			var ts = ease(t01 < 0.92 ? (t01 / 0.92) : 1);
2957			var tSec = t01 * totalDur;
2958			var clipIdx = qkClipIndexAtTime(clips, tSec);
2959			var ayahBoxes = (still && still._ayahBoxes) || qkClipAyahBoxes(clips);
2960			var srcBox = ayahBoxes[clipIdx] || ayahBoxes[0] || null;
2961			if (scroll) {
2962				var inner = frameM.inner;
2963				var pad = Math.max(8, Math.round(Math.min(inner.w, inner.h) * 0.012));
2964				var box = { x: inner.x + pad, y: inner.y + pad, w: inner.w - pad * 2, h: inner.h - pad * 2 };
2965				var foot = currentFooter || opts.footer || null;
2966				var footH = 0;
2967				var footDrawH = 0;
2968				if (typeof qkDrawExportBrand === 'function') {
2969					foot = null;
2970				}
2971				if (foot && foot.width && foot.height) {
2972					footDrawH = Math.round(foot.height * (box.w / foot.width));
2973					footDrawH = Math.min(footDrawH, Math.round(box.h * 0.2));
2974					footH = footDrawH + Math.round(pad * 0.45);
2975				}
2976				var contentY = box.y;
2977				var viewH = Math.max(8, box.h - footH);
2978				var scale = box.w / still.width;
2979				var ch = still.height * scale;
2980				var fits = ch <= viewH + 2;
2981				var y;
2982				var sy = ch / Math.max(1, still.height);
2983				var minY = contentY + viewH - ch;
2984				var maxY = contentY;
2985				if (fits) {
2986					y = contentY + Math.round((viewH - ch) / 2);
2987				} else if (srcBox) {
2988					var pieceY = srcBox.y * sy;
2989					var pieceH = Math.max(srcBox.h * sy, 24);
2990					if (centerBlocks) {
2991						y = contentY + Math.round(viewH / 2 - (pieceY + pieceH / 2));
2992					} else {
2993						y = contentY - Math.round(pieceY);
2994						if (pieceH > viewH + 2) {
2995							y -= Math.round((pieceH - viewH) * ts);
2996						}
2997					}
2998					y = Math.max(minY, Math.min(maxY, y));
2999				} else {
3000					y = contentY - (ch - viewH) * ts;
3001				}
3002				rctx.save();
3003				rctx.beginPath();
3004				rctx.rect(box.x, contentY, box.w, viewH);
3005				rctx.clip();
3006				rctx.drawImage(still, box.x, y, box.w, ch);
3007				rctx.restore();
3008				qkPulseAyahRegion(rctx, qkMapStillRect(qkPulseSrcBox(srcBox), still, box.x, y, box.w, ch), tSec);
3009				if (foot && footDrawH) {
3010					rctx.drawImage(foot, box.x, box.y + box.h - footDrawH, box.w, footDrawH);
3011				}
3012			} else if (still.width === w && still.height === h) {
3013				rctx.drawImage(still, 0, 0);
3014				qkPulseAyahRegion(rctx, qkMapStillRect(qkPulseSrcBox(srcBox), still, 0, 0, w, h), tSec);
3015			} else {
3016				var sc = Math.min(w / still.width, h / still.height);
3017				var dw = Math.max(1, Math.round(still.width * sc));
3018				var dh = Math.max(1, Math.round(still.height * sc));
3019				var dx = Math.round((w - dw) / 2);
3020				var dy = Math.round((h - dh) / 2);
3021				rctx.drawImage(still, dx, dy, dw, dh);
3022				qkPulseAyahRegion(rctx, qkMapStillRect(qkPulseSrcBox(srcBox), still, dx, dy, dw, dh), tSec);
3023			}
3024			if (edgeFrame) {
3025				qkTepsirDrawEdgeFrame(rctx, w, h, edgeFrame);
3026			}
3027			if (overlayImg && overlayImg.naturalWidth) {
3028				rctx.drawImage(overlayImg, 0, 0, w, h);
3029			}
3030			if (typeof qkDrawExportBrand === 'function') {
3031				qkDrawExportBrand(rctx);
3032			}
3033			if (vTrack && typeof vTrack.requestFrame === 'function') {
3034				try {
3035					vTrack.requestFrame();
3036				} catch (eRf) {}
3037			}
3038		}
3039		function paintFrame() {
3040			var t01 = 0;
3041			if (videoStart && totalDur > 0.05) {
3042				t01 = (performance.now() - videoStart) / (totalDur * 1000);
3043			} else if (clipDur > 0.05 && clipStart) {
3044				t01 = (performance.now() - clipStart) / (clipDur * 1000);
3045			}
3046			paintAtProgress(t01);
3047		}
3048		paintFrame();
3049		var fps = 30;
3050		var vStream = recCanvas.captureStream(fps);
3051		try {
3052			vTrack = vStream.getVideoTracks()[0] || null;
3053			if (vTrack) {
3054				vTrack.contentHint = 'detail';
3055				vTrack.enabled = true;
3056			}
3057		} catch (eHint) {}
3058		var ac = qkUnlockAudioForRecord() || window._qkRecordAudioCtx;
3059		if (ac && ac.state === 'suspended' && ac.resume) {
3060			ac.resume();
3061		}
3062		var dest = ac ? ac.createMediaStreamDestination() : null;
3063		var gain = ac ? ac.createGain() : null;
3064		if (gain) {
3065			gain.gain.value = 0.9;
3066			gain.connect(dest);
3067			gain.connect(ac.destination);
3068		}
3069		var tracks = vStream.getVideoTracks();
3070		if (dest) {
3071			tracks = tracks.concat(dest.stream.getAudioTracks());
3072		}
3073		tracks.forEach(function (t) {
3074			try {
3075				t.enabled = true;
3076			} catch (eEn) {}
3077		});
3078		var mixed = new MediaStream(tracks);
3079		var mime = '';
3080		var types = [
3081			'video/mp4;codecs=avc1.42E01E,mp4a.40.2',
3082			'video/mp4;codecs=avc1.4d002a,mp4a.40.2',
3083			'video/mp4;codecs=avc1.42001f,mp4a.40.2',
3084			'video/mp4',
3085			'video/webm;codecs=vp8,opus',
3086			'video/webm'
3087		];
3088		for (var i = 0; i < types.length; i++) {
3089			if (MediaRecorder.isTypeSupported(types[i])) {
3090				mime = types[i];
3091				break;
3092			}
3093		}
3094		var recOpts = { audioBitsPerSecond: 192000, videoBitsPerSecond: 6000000 };
3095		if (mime) {
3096			recOpts.mimeType = mime;
3097		}
3098		var rec;
3099		try {
3100			rec = new MediaRecorder(mixed, recOpts);
3101		} catch (err) {
3102			try {
3103				rec = mime ? new MediaRecorder(mixed, { mimeType: mime }) : new MediaRecorder(mixed);
3104			} catch (err2) {
3105				reject(err2);
3106				return;
3107			}
3108		}
3109		var chunks = [];
3110		rec.ondataavailable = function (ev) {
3111			if (ev.data && ev.data.size) {
3112				chunks.push(ev.data);
3113			}
3114		};
3115		var raf = 0;
3116		var clipTimer = null;
3117		var finished = false;
3118		var settled = false;
3119		var recStarted = false;
3120		var totalMs = 0;
3121		var currentSrc = null;
3122		function stopAnim() {
3123			if (raf) {
3124				cancelAnimationFrame(raf);
3125				raf = 0;
3126			}
3127		}
3128		function dropRecCanvas() {
3129			if (recCanvas && recCanvas.parentNode) {
3130				recCanvas.parentNode.removeChild(recCanvas);
3131			}
3132		}
3133		function tick() {
3134			if (finished) {
3135				return;
3136			}
3137			paintFrame();
3138			raf = requestAnimationFrame(tick);
3139		}
3140		function finish() {
3141			if (finished) {
3142				return;
3143			}
3144			finished = true;
3145			stopAnim();
3146			if (clipTimer) {
3147				clearTimeout(clipTimer);
3148			}
3149			try {
3150				if (currentSrc) {
3151					currentSrc.stop(0);
3152				}
3153			} catch (e3) {}
3154			if (recStarted && rec.state !== 'inactive') {
3155				try {
3156					rec.stop();
3157				} catch (e2) {}
3158				return;
3159			}
3160			dropRecCanvas();
3161			if (!settled) {
3162				settled = true;
3163				reject(new Error('empty'));
3164			}
3165		}
3166		rec.onstop = function () {
3167			stopAnim();
3168			dropRecCanvas();
3169			if (settled) {
3170				return;
3171			}
3172			settled = true;
3173			var type = rec.mimeType || mime || 'video/webm';
3174			var isMp4 = /mp4/i.test(type);
3175			resolve({ blob: new Blob(chunks, { type: isMp4 ? 'video/mp4' : type }), ext: isMp4 ? 'mp4' : 'webm', duration: totalMs / 1000 });
3176		};
3177		function startRec() {
3178			if (recStarted || rec.state !== 'inactive') {
3179				return;
3180			}
3181			try {
3182				rec.start(1000);
3183				recStarted = true;
3184			} catch (eStart) {
3185				try {
3186					rec.start();
3187					recStarted = true;
3188				} catch (eStart2) {
3189					settled = true;
3190					reject(new Error('start'));
3191				}
3192			}
3193			if (ac && ac.state === 'suspended' && ac.resume) {
3194				ac.resume();
3195			}
3196		}
3197		function playClip(index) {
3198			if (finished) {
3199				return;
3200			}
3201			if (index >= clips.length) {
3202				finish();
3203				return;
3204			}
3205			var clip = clips[index];
3206			currentFooter = clip.footer || opts.footer || currentFooter || null;
3207			qkCaptureSetStatus((index + 1) + ' / ' + clips.length);
3208			var buf = clip.buffer;
3209			var dur = buf && buf.duration ? buf.duration : 0;
3210			if (!buf || !isFinite(dur) || dur < 0.2) {
3211				playClip(index + 1);
3212				return;
3213			}
3214			totalMs += dur * 1000;
3215			clipDur = dur;
3216			clipStart = performance.now();
3217			if (!videoStart) {
3218				videoStart = clipStart;
3219			}
3220			paintFrame();
3221			startRec();
3222			try {
3223				if (currentSrc) {
3224					currentSrc.stop(0);
3225				}
3226			} catch (eStop) {}
3227			var src = ac.createBufferSource();
3228			src.buffer = buf;
3229			src.connect(gain);
3230			currentSrc = src;
3231			src.onended = function () {
3232				if (finished) {
3233					return;
3234				}
3235				clipTimer = setTimeout(function () {
3236					playClip(index + 1);
3237				}, 80);
3238			};
3239			try {
3240				src.start(0);
3241			} catch (ePlay) {
3242				playClip(index + 1);
3243			}
3244		}
3245		try {
3246			$('#audioPlayer').jPlayer('pause');
3247		} catch (ePause) {}
3248		if (!ac || !dest || !gain) {
3249			reject(new Error('audio'));
3250			return;
3251		}
3252		var decodeSeq = Promise.resolve();
3253		clips.forEach(function (clip) {
3254			decodeSeq = decodeSeq.then(function () {
3255				qkCaptureSetStatus('…');
3256				return qkDecodeAudioBuffer(ac, clip.audioUrl).then(function (buf) {
3257					clip.buffer = buf;
3258				});
3259			});
3260		});
3261		decodeSeq.then(function () {
3262			if (!opts.pageReady) {
3263				var stacked = opts.stackCanvas || qkStackClipCanvases(clips, fillBg);
3264				if (stacked) {
3265					still = stacked;
3266				}
3267			}
3268			if (still && !still._ayahBoxes) {
3269				still._ayahBoxes = qkClipAyahBoxes(clips);
3270			}
3271			totalDur = 0;
3272			clips.forEach(function (c) {
3273				if (c.buffer && c.buffer.duration && isFinite(c.buffer.duration)) {
3274					totalDur += c.buffer.duration;
3275				}
3276			});
3277			if (totalDur < 0.2) {
3278				totalDur = clipDur || 1;
3279			}
3280			qkEncodeAyahVideoMp4({
3281				canvas: recCanvas,
3282				paintAtProgress: paintAtProgress,
3283				clips: clips,
3284				ac: ac,
3285				w: w,
3286				h: h,
3287				fps: fps,
3288				totalDur: totalDur
3289			}).then(function (out) {
3290				finished = true;
3291				stopAnim();
3292				dropRecCanvas();
3293				if (!settled) {
3294					settled = true;
3295					resolve(out);
3296				}
3297			}).catch(function () {
3298				raf = requestAnimationFrame(tick);
3299				playClip(0);
3300			});
3301		}).catch(function () {
3302			stopAnim();
3303			dropRecCanvas();
3304			reject(new Error('audio'));
3305		});
3306	});
3307}
3308
3309function qkSaveComposedCanvas(canvas, filename) {
3310	if (!canvas) {
3311		return;
3312	}
3313	var pages = (canvas._qkPages && canvas._qkPages.length) ? canvas._qkPages : [canvas];
3314	if (pages.length > 1) {
3315		qkCaptureSetStatus(String(pages.length));
3316	}
3317	pages.forEach(function (page, i) {
3318		var name = filename;
3319		if (pages.length > 1) {
3320			name = String(filename).replace(/\.jpg$/i, '-' + (i + 1) + '.jpg');
3321		}
3322		setTimeout(function () {
3323			qkTepsirDownloadDataUrl(page.toDataURL('image/jpeg', 0.92), name);
3324		}, i * 400);
3325	});
3326}
3327
3328function qkTepsirDownloadDataUrl(dataUrl, filename) {
3329	if (typeof window.simulateDownloadImageClick === 'function') {
3330		window.simulateDownloadImageClick(dataUrl, filename);
3331		return;
3332	}
3333	var a = document.createElement('a');
3334	a.href = dataUrl;
3335	a.download = filename;
3336	document.body.appendChild(a);
3337	a.click();
3338	document.body.removeChild(a);
3339}
3340
3341function qkTepsirWaitPaint() {
3342	return new Promise(function (resolve) {
3343		if (window.requestAnimationFrame) {
3344			window.requestAnimationFrame(function () {
3345				window.requestAnimationFrame(function () {
3346					resolve();
3347				});
3348			});
3349		} else {
3350			setTimeout(resolve, 80);
3351		}
3352	});
3353}
3354
3355function qkTepsirWaitCaptureAssets($root) {
3356	var loads = [];
3357	if (document.fonts && document.fonts.load) {
3358		loads.push(Promise.all([
3359			document.fonts.load('1em UKIJEkran'),
3360			document.fonts.load('1em UKIJTuzBB'),
3361			document.fonts.load('1em hafs'),
3362			document.fonts.load('400 1em "Scheherazade New"'),
3363			document.fonts.load('400 1em Amiri')
3364		]).then(function () {
3365			return document.fonts.ready;
3366		}).catch(function () {
3367			return document.fonts && document.fonts.ready ? document.fonts.ready : Promise.resolve();
3368		}));
3369	} else if (document.fonts && document.fonts.ready) {
3370		loads.push(document.fonts.ready);
3371	}
3372	$root.find('img').each(function () {
3373		var img = this;
3374		if (img.complete) {
3375			return;
3376		}
3377		loads.push(new Promise(function (resolve) {
3378			img.onload = img.onerror = function () {
3379				resolve();
3380			};
3381		}));
3382	});
3383	return Promise.all(loads).then(qkTepsirWaitPaint);
3384}
3385
3386function qkTepsirSanitizeExportClone($root, ayetNo, tafsirH) {
3387	if (!$root || !$root.length) {
3388		return $root;
3389	}
3390	$root.find('.qk-tepsir-about-wrap, .qk-tepsir-about-link, script, style, .progress, .izahat-box, .small-box, .accordion-button, .accordion-header').remove();
3391	$root.find('a.qk-fn-link, a[id^="ftnt_ref"]').each(function () {
3392		$(this).replaceWith(document.createTextNode($(this).text()));
3393	});
3394	var ugHeadFrom = 'ئايەتلەرنىڭ باغلىنىشى';
3395	var ugHeadTo = 'ئايەتلەردىن ئېلىنغان بەزى پايدىلار:';
3396	$root.find('.qk-muhtasar-h, h4, p, strong').each(function () {
3397		var $el = $(this);
3398		if ($el.children().length > 1) {
3399			return;
3400		}
3401		var raw = ($el.text() || '').replace(/\s+/g, ' ').trim();
3402		var stripped = raw.replace(/[::]\s*$/, '');
3403		if (stripped === ugHeadFrom || raw.indexOf(ugHeadFrom) !== -1 && stripped.length <= ugHeadFrom.length + 2) {
3404			$el.text(ugHeadTo).addClass('qk-muhtasar-h');
3405		}
3406	});
3407	$root.find('.qk-jumle-ayetler-wrap, .qk-ehsen-ayetler-wrap, .qk-jumle-arabic-box, .qk-ehsen-arabic-box').addClass('qk-tepsir-capture-ayah-box');
3408	tafsirH = ((tafsirH || $('#qkTepsirSaveImg').attr('data-tefsir-h') || 'تەپسىرى:').replace(/[\s::]+$/, '')) + ':';
3409	if (!$root.find('.qk-tepsir-capture-tefsir-h').length) {
3410		var $arBox = $root.find('.qk-tepsir-capture-ayah-box, .qk-jumle-arabic-box, .qk-ehsen-arabic-box').first();
3411		var $tefsirH = $('<div class="qk-tepsir-capture-tefsir-h"></div>').text(tafsirH);
3412		if ($arBox.length) {
3413			$arBox.after($tefsirH);
3414		} else {
3415			$root.prepend($tefsirH);
3416		}
3417	}
3418	if (ayetNo) {
3419		var $mez = $root.find('.tepsir-izahat, .qk-tepsir-mezmun').first();
3420		if ($mez.length && !$mez.find('.qk-tepsir-mezmun-no').length) {
3421			var lead = ($mez.text() || '').replace(/^\s+/, '');
3422			if (!/^\d{1,3}\s*[_\-–.]/.test(lead)) {
3423				var $mark = $('<span class="qk-tepsir-mezmun-no"></span>').text(ayetNo + ' _ ');
3424				var $p = $mez.find('p').first();
3425				if ($p.length) {
3426					$p.prepend($mark);
3427				} else {
3428					$mez.prepend($mark);
3429				}
3430			}
3431		}
3432	}
3433	return $root;
3434}
3435
3436function qkTepsirSnapshotFitted(btn, sureNo, ayetNo, onScreen, shotOpts) {
3437	shotOpts = shotOpts || {};
3438	var $btn = $(btn);
3439	var $src = $('#tepsirData .qk-tepsir-sheet, #tepsirData .tepsir').first();
3440	if (!$src.length) {
3441		$src = $('#tepsirData');
3442	}
3443	if (!$src.length || !$src.text().replace(/\s+/g, '')) {
3444		return Promise.reject(new Error('empty'));
3445	}
3446	var $host = $('#qkTepsirCaptureHost');
3447	if (!$host.length) {
3448		$host = $('<div id="qkTepsirCaptureHost"></div>').appendTo(document.body);
3449	}
3450	var $clone = $src.clone(false, false);
3451	qkTepsirSanitizeExportClone($clone, ayetNo, $btn.attr('data-tefsir-h'));
3452	var sureName = qkTepsirSureName(sureNo);
3453	var tafsirName = qkTepsirDisplayName();
3454	var tafsirTbl = ($('#tepsirSelector').val() || '').replace(/\s+/g, '');
3455	var ayahLbl = $btn.attr('data-ayah') || '';
3456	var metaBits = [];
3457	if (sureName) {
3458		metaBits.push(sureName);
3459	}
3460	if (ayetNo) {
3461		metaBits.push(ayetNo + (ayahLbl ? '-' + ayahLbl : ''));
3462	}
3463	var $card = $('<div class="qk-tepsir-capture-card"></div>');
3464	if (tafsirTbl) {
3465		$card.attr('data-tepsir-tbl', tafsirTbl);
3466	}
3467	qkApplyExportStyle($card, 'tepsir');
3468	if (!shotOpts.skipTitle) {
3469		$card.append($('<div class="qk-tepsir-capture-title"></div>').text(tafsirName));
3470	}
3471	if (shotOpts.rangeText) {
3472		$card.append($('<div class="qk-tepsir-capture-range"></div>').text(shotOpts.rangeText));
3473	}
3474	var $inner = $('<div class="qk-tepsir-capture-inner"></div>');
3475	$inner.append($('<div class="qk-tepsir-capture-body"></div>').append($clone));
3476	if (metaBits.length && !shotOpts.skipMeta) {
3477		$inner.append($('<div class="qk-tepsir-capture-meta"></div>').text(metaBits.join(' · ')));
3478	}
3479	var $brand = qkExportBrandNode('#fffefb', false);
3480	$card.append($inner);
3481	$host.empty().append($card).removeAttr('hidden');
3482	if (onScreen) {
3483		$host.addClass('is-capturing');
3484	}
3485	if (typeof qkPrepareExportClone === 'function') {
3486		qkPrepareExportClone($card, 720, 'qkTepsirSize');
3487	}
3488	var bodyCanvas = null;
3489	return qkTepsirWaitCaptureAssets($card).then(function () {
3490		var w = Math.max($card[0].scrollWidth, $card.outerWidth() || 0, 720);
3491		var h = Math.max($card[0].scrollHeight, $card.outerHeight() || 0, 1);
3492		var boxes = qkMeasureAyahBoxes($card);
3493		return window.html2canvas($card[0], {
3494			backgroundColor: '#fffefb',
3495			scale: Math.min(window.devicePixelRatio || 1, 2),
3496			useCORS: true,
3497			allowTaint: true,
3498			logging: false,
3499			width: w,
3500			height: h
3501		}).then(function (canvas) {
3502			canvas._ayahBoxes = qkAyahBoxesToCanvas(boxes, w, canvas);
3503			return canvas;
3504		});
3505	}).then(function (canvas) {
3506		bodyCanvas = canvas;
3507		$host.empty().append($brand).removeAttr('hidden');
3508		return qkTepsirWaitCaptureAssets($brand).then(function () {
3509			return qkCanvasFromEl($brand, '#fffefb', 720);
3510		});
3511	}).then(function (footer) {
3512		$host.empty().removeClass('is-capturing').attr('hidden', 'hidden');
3513		return { canvas: bodyCanvas, footer: footer, bg: '#fffefb', ayahBoxes: bodyCanvas._ayahBoxes || [] };
3514	});
3515}
3516
3517function qkTepsirDisplayName() {
3518	var $opt = $('#tepsirSelector option:selected');
3519	var name = ($opt.text() || '').replace(/\s+/g, ' ').trim();
3520	if (name) {
3521		return name;
3522	}
3523	var tbl = ($('#tepsirSelector').val() || '').replace(/^tepsir_/, '').replace(/_/g, ' ');
3524	return tbl || 'تەپسىر';
3525}
3526
3527function qkTepsirAyahRangeText(from, to, ayahWord) {
3528	from = parseInt(from, 10) || 1;
3529	to = parseInt(to, 10) || from;
3530	ayahWord = (ayahWord || '').replace(/^\s+|\s+$/g, '');
3531	if (to > from) {
3532		return from + '-' + to + (ayahWord ? '-' + ayahWord : '');
3533	}
3534	return from + (ayahWord ? '-' + ayahWord : '');
3535}
3536
3537function qkTepsirCaptureHeadCanvas(btn, sureNo, from, to) {
3538	var tafsirName = qkTepsirDisplayName();
3539	var tafsirTbl = ($('#tepsirSelector').val() || '').replace(/\s+/g, '');
3540	var sureName = qkTepsirSureName(sureNo);
3541	var ayahWord = (($(btn).attr('data-ayah') || $('#qkTepsirSaveImg').attr('data-ayah') || '') + '').trim();
3542	var $card = $('<div class="qk-tepsir-capture-card"></div>').css({
3543		width: '720px',
3544		minHeight: '120px',
3545		background: '#fffefb'
3546	});
3547	if (tafsirTbl) {
3548		$card.attr('data-tepsir-tbl', tafsirTbl);
3549	}
3550	$card.append($('<div class="qk-tepsir-capture-title"></div>').text(tafsirName));
3551	var bits = [];
3552	if (sureName) {
3553		bits.push(sureName);
3554	}
3555	bits.push(qkTepsirAyahRangeText(from, to, ayahWord));
3556	$card.append($('<div class="qk-tepsir-capture-range"></div>').text(bits.join(' • ')));
3557	var $host = $('#qkTepsirCaptureHost');
3558	if (!$host.length) {
3559		$host = $('<div id="qkTepsirCaptureHost"></div>').appendTo(document.body);
3560	}
3561	$host.empty().append($card).removeAttr('hidden').addClass('is-capturing');
3562	return qkTepsirWaitCaptureAssets($card).then(function () {
3563		return window.html2canvas($card[0], {
3564			backgroundColor: '#0d7377',
3565			scale: Math.min(window.devicePixelRatio || 1, 2),
3566			useCORS: true,
3567			allowTaint: true,
3568			logging: false,
3569			width: 720,
3570			height: Math.max($card[0].scrollHeight, $card.outerHeight() || 0, 120)
3571		});
3572	}).then(function (canvas) {
3573		$host.empty().removeClass('is-capturing').attr('hidden', 'hidden');
3574		return canvas;
3575	});
3576}
3577
3578var qkTepsirPreviewGen = 0;
3579
3580function qkTepsirExportPanelOpen() {
3581	return $('#qkTepsirExportPanel').length && $('#qkTepsirExportPanel').attr('hidden') == null;
3582}
3583
3584function qkTepsirHideExportPreview() {
3585	qkTepsirPreviewGen += 1;
3586	$('#qkTepsirExportPreview').empty().attr('hidden', 'hidden');
3587	$('#tepsirData').removeAttr('hidden');
3588}
3589
3590function qkTepsirFetchHtml(sure, ayet) {
3591	return new Promise(function (resolve) {
3592		$.ajax({
3593			url: qkTepsirLoadUrl(qkGetSelectedTepsirId(), sure, ayet),
3594			dataType: 'html'
3595		}).done(function (html) {
3596			resolve(html || '');
3597		}).fail(function () {
3598			resolve('');
3599		});
3600	});
3601}
3602
3603function qkTepsirBuildRangePreviewCard(items, sureNo, from, to) {
3604	var $card = $('<div class="qk-tepsir-capture-card"></div>');
3605	var tbl = ($('#tepsirSelector').val() || '').replace(/\s+/g, '');
3606	if (tbl) {
3607		$card.attr('data-tepsir-tbl', tbl);
3608	}
3609	qkApplyExportStyle($card, 'tepsir');
3610	$card.append($('<div class="qk-tepsir-capture-title"></div>').text(qkTepsirDisplayName()));
3611	var bits = [];
3612	var sureName = qkTepsirSureName(sureNo);
3613	if (sureName) {
3614		bits.push(sureName);
3615	}
3616	bits.push(qkTepsirAyahRangeText(from, to, ($('#qkTepsirSaveImg').attr('data-ayah') || '') + ''));
3617	$card.append($('<div class="qk-tepsir-capture-range"></div>').text(bits.join(' • ')));
3618	var $body = $('<div class="qk-tepsir-capture-body"></div>');
3619	var seenHtml = {};
3620	items.forEach(function (item) {
3621		var fp = String(item.html || '').replace(/\s+/g, ' ').trim();
3622		if (fp && seenHtml[fp]) {
3623			return;
3624		}
3625		if (fp) {
3626			seenHtml[fp] = 1;
3627		}
3628		var $piece = $('<div class="qk-tepsir-export-ayah-piece"></div>').html(item.html);
3629		qkTepsirSanitizeExportClone($piece, item.ayet);
3630		$piece.attr('data-ayah', item.ayet).attr('data-ayah-ar', qkExtractAyahArabic($piece));
3631		$body.append($piece);
3632	});
3633	$card.append($('<div class="qk-tepsir-capture-inner"></div>').append($body));
3634	var $brand = qkExportBrandNode('#fffefb', false);
3635	$brand.css({ width: '100%', maxWidth: '100%' });
3636	$card.append($brand);
3637	return $card;
3638}
3639
3640function qkTepsirRefreshExportPreview() {
3641	var $preview = $('#qkTepsirExportPreview');
3642	if (!$preview.length) {
3643		return Promise.resolve();
3644	}
3645	if (!qkTepsirExportPanelOpen()) {
3646		qkTepsirHideExportPreview();
3647		return Promise.resolve();
3648	}
3649	var sureNo = parseInt(selectedSureNo, 10) || 0;
3650	var ayetNo = parseInt(selectedAyetNo, 10) || 0;
3651	var range = qkReadAyahRange('qkTepsirAyahFrom', 'qkTepsirAyahTo', sureNo, ayetNo);
3652	if (!sureNo || range.to <= range.from) {
3653		qkTepsirHideExportPreview();
3654		return Promise.resolve();
3655	}
3656	var gen = ++qkTepsirPreviewGen;
3657	var ayahs = [];
3658	var a;
3659	for (a = range.from; a <= range.to; a++) {
3660		ayahs.push(a);
3661	}
3662	$('#tepsirData').attr('hidden', 'hidden');
3663	$preview.removeAttr('hidden').html('<div class="text-center p-4"><div class="spinner-border text-primary"></div></div>');
3664	return Promise.all(ayahs.map(function (n) {
3665		return qkTepsirFetchHtml(sureNo, n).then(function (html) {
3666			return { ayet: n, html: html };
3667		});
3668	})).then(function (items) {
3669		if (gen !== qkTepsirPreviewGen) {
3670			return;
3671		}
3672		items = (items || []).filter(function (item) {
3673			return item && item.html && String(item.html).replace(/\s+/g, '');
3674		});
3675		if (!items.length) {
3676			qkTepsirHideExportPreview();
3677			return;
3678		}
3679		$preview.empty().append(qkTepsirBuildRangePreviewCard(items, sureNo, range.from, range.to));
3680	});
3681}
3682
3683function qkTepsirSnapshotExportPreview() {
3684	var $src = $('#qkTepsirExportPreview .qk-tepsir-capture-card').first();
3685	if (!$src.length) {
3686		return Promise.reject(new Error('empty'));
3687	}
3688	var $host = $('#qkTepsirCaptureHost');
3689	if (!$host.length) {
3690		$host = $('<div id="qkTepsirCaptureHost"></div>').appendTo(document.body);
3691	}
3692	var $card = $src.clone(false, false);
3693	$card.find('.qk-tepsir-capture-brand').remove();
3694	$card.css({
3695		width: '720px',
3696		maxWidth: '720px',
3697		height: 'auto',
3698		overflow: 'visible'
3699	});
3700	$host.empty().append($card).removeAttr('hidden').addClass('is-capturing');
3701	if (typeof qkPrepareExportClone === 'function') {
3702		qkPrepareExportClone($card, 720, 'qkTepsirSize');
3703	}
3704	var bodyCanvas = null;
3705	return qkTepsirWaitCaptureAssets($card).then(function () {
3706		var w = Math.max($card[0].scrollWidth, $card.outerWidth() || 0, 720);
3707		var h = Math.max($card[0].scrollHeight, $card.outerHeight() || 0, 1);
3708		var boxes = qkMeasureAyahBoxes($card);
3709		return window.html2canvas($card[0], {
3710			backgroundColor: '#fffefb',
3711			scale: Math.min(window.devicePixelRatio || 1, 2),
3712			useCORS: true,
3713			allowTaint: true,
3714			logging: false,
3715			width: w,
3716			height: h
3717		}).then(function (canvas) {
3718			canvas._ayahBoxes = qkAyahBoxesToCanvas(boxes, w, canvas);
3719			return canvas;
3720		});
3721	}).then(function (canvas) {
3722		bodyCanvas = canvas;
3723		var $brand = qkExportBrandNode('#fffefb', false);
3724		$host.empty().append($brand).removeAttr('hidden');
3725		return qkTepsirWaitCaptureAssets($brand).then(function () {
3726			return qkCanvasFromEl($brand, '#fffefb', 720);
3727		});
3728	}).then(function (footer) {
3729		$host.empty().removeClass('is-capturing').attr('hidden', 'hidden');
3730		return { canvas: bodyCanvas, footer: footer, bg: '#fffefb', ayahBoxes: bodyCanvas._ayahBoxes || [] };
3731	});
3732}
3733
3734window.qkTepsirRefreshExportPreview = qkTepsirRefreshExportPreview;
3735window.qkTepsirHideExportPreview = qkTepsirHideExportPreview;
3736
3737function qkTepsirStackRangeClips(clips, btn, sureNo, from, to, bg) {
3738	return Promise.resolve(qkStackClipCanvases(clips || [], bg || '#fffefb'));
3739}
3740
3741function qkTepsirCollectRangeClips(btn, sureNo, from, to, collectOpts) {
3742	collectOpts = collectOpts || {};
3743	var originSure = selectedSureNo;
3744	var originAyet = selectedAyetNo;
3745	var clips = [];
3746	var seq = Promise.resolve();
3747	var a;
3748	for (a = from; a <= to; a++) {
3749		seq = seq.then((function (ayet) {
3750			return function () {
3751				qkCaptureSetStatus(ayet + ' / ' + to);
3752				return qkTepsirReloadContent(sureNo, ayet).then(function () {
3753					return qkTepsirWaitCaptureAssets($('#tepsirData'));
3754				}).then(function () {
3755					var rangeText = '';
3756					if (ayet === from && to > from) {
3757						var bits = [];
3758						var sureName = qkTepsirSureName(sureNo);
3759						if (sureName) {
3760							bits.push(sureName);
3761						}
3762						bits.push(qkTepsirAyahRangeText(from, to, ($(btn).attr('data-ayah') || $('#qkTepsirSaveImg').attr('data-ayah') || '') + ''));
3763						rangeText = bits.join(' • ');
3764					}
3765					return qkTepsirSnapshotFitted(btn, sureNo, ayet, ayet === from, {
3766						skipTitle: ayet !== from,
3767						skipMeta: true,
3768						rangeText: rangeText
3769					});
3770				}).then(function (shot) {
3771					var url = qkAyahAudioUrl(sureNo, ayet);
3772					if (collectOpts.requireAudio !== false && !url) {
3773						return;
3774					}
3775					clips.push({
3776						canvas: shot.canvas || shot,
3777						audioUrl: url || '',
3778						footer: shot.footer || null,
3779						ayahText: qkAyahArabicText(sureNo, ayet)
3780					});
3781				});
3782			};
3783		})(a));
3784	}
3785	return seq.then(function () {
3786		return qkTepsirReloadContent(originSure, originAyet).then(function () {
3787			return clips;
3788		});
3789	});
3790}
3791
3792function qkDownloadVideoOut(out, filename) {
3793	var url = URL.createObjectURL(out.blob);
3794	qkTepsirDownloadDataUrl(url, filename);
3795	setTimeout(function () {
3796		URL.revokeObjectURL(url);
3797	}, 8000);
3798}
3799
3800function qkTepsirSaveAsImage(btn, opts) {
3801	opts = opts || {};
3802	var $btn = $(btn);
3803	if ($btn.data('busy')) {
3804		return;
3805	}
3806	if (typeof window.html2canvas !== 'function') {
3807		showToast($btn.attr('data-fail') || 'Save failed');
3808		return;
3809	}
3810	var sureNo = parseInt(selectedSureNo, 10) || 0;
3811	var ayetNo = parseInt(selectedAyetNo, 10) || 0;
3812	var range = qkReadAyahRange('qkTepsirAyahFrom', 'qkTepsirAyahTo', sureNo, ayetNo);
3813	var tafsirTbl = ($('#tepsirSelector').val() || '').replace(/\s+/g, '');
3814	var slug = (tafsirTbl || 'tepsir').replace(/^tepsir_/, '');
3815	var base = slug + '-' + (sureNo || 's') + '-' + range.from + (range.to > range.from ? '-' + range.to : '');
3816	$btn.data('busy', 1).prop('disabled', true);
3817	qkCaptureSetStatus('');
3818	if (opts.video) {
3819		qkUnlockAudioForRecord();
3820	}
3821	var work;
3822	if (opts.video && typeof window.qkExportReadyPages === 'function') {
3823		var prepTv = (range.to > range.from) ? qkTepsirRefreshExportPreview() : Promise.resolve();
3824		work = Promise.resolve(prepTv).then(function () {
3825			return window.qkExportReadyPages('tepsir');
3826		}).then(function (pages) {
3827			if (!pages || !pages.length) {
3828				throw new Error('empty');
3829			}
3830			var clips = [];
3831			var a;
3832			var span = range.to - range.from + 1;
3833			for (a = range.from; a <= range.to; a++) {
3834				var url = qkAyahAudioUrl(sureNo, a);
3835				if (url) {
3836					clips.push({
3837						canvas: pages.length === span ? pages[a - range.from] : pages[0],
3838						audioUrl: url,
3839						ayahText: qkAyahArabicText(sureNo, a)
3840					});
3841				}
3842			}
3843			if (!clips.length) {
3844				throw new Error('clips');
3845			}
3846			return qkRecordAyahVideoClips(clips, {
3847				scroll: false,
3848				pageReady: true,
3849				size: qkTepsirExportSize(),
3850				bg: '#fffefb'
3851			}).then(function (out) {
3852				qkDownloadVideoOut(out, base + '.' + out.ext);
3853			});
3854		});
3855	} else if (!opts.video && typeof window.qkExportReadyPages === 'function') {
3856		var prepT = (range.to > range.from) ? qkTepsirRefreshExportPreview() : Promise.resolve();
3857		work = Promise.resolve(prepT).then(function () {
3858			return window.qkExportReadyPages('tepsir');
3859		}).then(function (pages) {
3860			if (!pages || !pages.length) {
3861				throw new Error('empty');
3862			}
3863			pages[0]._qkPages = pages;
3864			qkSaveComposedCanvas(pages[0], base + '.jpg');
3865		});
3866	} else if (range.to > range.from) {
3867		work = qkTepsirRefreshExportPreview().then(function () {
3868			if (!$('#qkTepsirExportPreview .qk-tepsir-capture-card').length) {
3869				throw new Error('empty');
3870			}
3871			qkCaptureSetStatus('');
3872			return qkTepsirSnapshotExportPreview();
3873		}).then(function (shot) {
3874			if (opts.video) {
3875				var clips = [];
3876				var a;
3877				for (a = range.from; a <= range.to; a++) {
3878					var url = qkAyahAudioUrl(sureNo, a);
3879					if (url) {
3880						clips.push({
3881							canvas: shot.canvas,
3882							audioUrl: url,
3883							footer: shot.footer,
3884							ayahText: qkAyahArabicText(sureNo, a),
3885							ayahBoxes: shot.ayahBoxes || (shot.canvas && shot.canvas._ayahBoxes) || []
3886						});
3887					}
3888				}
3889				if (!clips.length) {
3890					throw new Error('clips');
3891				}
3892				return qkRecordAyahVideoClips(clips, {
3893					scroll: true,
3894					edgeFrame: qkTepsirExportFrame(),
3895					size: qkTepsirExportSize(),
3896					footer: shot.footer,
3897					bg: '#fffefb',
3898					stackCanvas: shot.canvas
3899				}).then(function (out) {
3900					qkDownloadVideoOut(out, base + '.' + out.ext);
3901				});
3902			}
3903			var fitted = qkTepsirComposeExportCanvas(shot.canvas, qkTepsirExportSize(), qkTepsirExportFrame(), '#fffefb', shot.footer, true);
3904			qkSaveComposedCanvas(fitted, base + '.jpg');
3905		});
3906	} else {
3907		work = qkTepsirSnapshotFitted(btn, sureNo, ayetNo, true).then(function (shot) {
3908			var content = shot.canvas || shot;
3909			var footer = shot.footer || null;
3910			if (opts.video) {
3911				var audioUrl = qkAyahAudioUrl(sureNo, ayetNo);
3912				if (!audioUrl) {
3913					throw new Error('audio');
3914				}
3915				return qkTepsirRecordAyahVideo(content, audioUrl, { footer: footer, bg: shot.bg || '#fffefb', ayahBoxes: shot.ayahBoxes || (content && content._ayahBoxes) || [] }).then(function (out) {
3916					qkDownloadVideoOut(out, base + '.' + out.ext);
3917				});
3918			}
3919			var fitted = qkTepsirComposeExportCanvas(content, qkTepsirExportSize(), qkTepsirExportFrame(), shot.bg || '#fffefb', footer);
3920			qkSaveComposedCanvas(fitted, base + '.jpg');
3921		});
3922	}
3923	work.catch(function () {
3924		showToast($btn.attr('data-fail') || 'Save failed');
3925	}).then(function () {
3926		qkCaptureSetStatus('');
3927		var $host = $('#qkTepsirCaptureHost');
3928		$host.empty().removeClass('is-capturing').attr('hidden', 'hidden');
3929		$btn.data('busy', 0).prop('disabled', false);
3930	});
3931}
3932
3933function qkCaptureLoadPreviewPromise(sure, ayet) {
3934	return new Promise(function (resolve) {
3935		if (typeof window.qkLoadCapturePreview !== 'function') {
3936			resolve();
3937			return;
3938		}
3939		window.qkLoadCapturePreview(sure, ayet, 0, resolve);
3940	});
3941}
3942
3943function qkCaptureFrameKey($frame) {
3944	if (!$frame || !$frame.length) {
3945		return 'qk-frame-classic';
3946	}
3947	var m = ($frame.attr('class') || '').match(/qk-frame-(classic|gold|parchment|emerald|midnight|img|double|ornate|navy|burgundy|plain|teal)/);
3948	return m ? ('qk-frame-' + m[1]) : 'qk-frame-classic';
3949}
3950
3951function qkCaptureInnerSnapshot() {
3952	var $stage = $('#captureData');
3953	var $frame = $stage.find('.print-border, .qk-print-frame').first();
3954	var $inner = $frame.find('.qk-print-inner').first();
3955	if (!$inner.length) {
3956		$inner = $frame;
3957	}
3958	if (!$inner.length) {
3959		return Promise.reject(new Error('frame'));
3960	}
3961	var bg = $frame.css('background-color') || '#ffffff';
3962	if (!bg || bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent') {
3963		bg = '#ffffff';
3964	}
3965	var overlay = $frame.find('.qk-frame-overlay').get(0) || null;
3966	var $host = $('#qkTepsirCaptureHost');
3967	if (!$host.length) {
3968		$host = $('<div id="qkTepsirCaptureHost"></div>').appendTo(document.body);
3969	}
3970	var $clone = $inner.clone(false, false);
3971	$clone.find('script, .qk-frame-overlay, .print-social-footer, .web-p').remove();
3972	qkApplyExportStyle($clone, 'capture');
3973	var layoutW = 720;
3974	$clone.css({
3975		width: layoutW + 'px',
3976		maxWidth: layoutW + 'px',
3977		height: 'auto',
3978		overflow: 'visible',
3979		position: 'relative',
3980		background: bg,
3981		display: 'block',
3982		flex: 'none',
3983		minHeight: '0'
3984	});
3985	$clone.find('.qk-capture-ayah-stack').css({
3986		overflow: 'visible',
3987		height: 'auto',
3988		maxHeight: 'none',
3989		flex: 'none',
3990		minHeight: '0'
3991	});
3992	$clone.css({
3993		paddingTop: '0',
3994		paddingBottom: '0',
3995		justifyContent: 'flex-start'
3996	});
3997	$host.empty().append($clone).removeAttr('hidden');
3998	if (typeof qkPrepareExportClone === 'function') {
3999		qkPrepareExportClone($clone, layoutW, 'qkCaptureSize');
4000	}
4001	var bodyCanvas = null;
4002	var frameKey = qkCaptureFrameKey($frame);
4003	var dark = frameKey === 'qk-frame-midnight';
4004	return qkTepsirWaitCaptureAssets($clone).then(function () {
4005		qkShrinkWrapExportNode($clone);
4006		var w = Math.max($clone[0].scrollWidth, layoutW);
4007		var h = Math.max($clone[0].scrollHeight, 1);
4008		var boxes = qkMeasureAyahBoxes($clone);
4009		return window.html2canvas($clone[0], {
4010			backgroundColor: bg,
4011			scale: Math.min(window.devicePixelRatio || 1, 2),
4012			useCORS: true,
4013			allowTaint: true,
4014			logging: false,
4015			width: w,
4016			height: h
4017		}).then(function (canvas) {
4018			canvas._ayahBoxes = qkAyahBoxesToCanvas(boxes, w, canvas);
4019			return qkCropCanvasToInk(canvas, canvas._ayahBoxes);
4020		});
4021	}).then(function (canvas) {
4022		bodyCanvas = canvas;
4023		$host.empty();
4024		return qkCaptureBrandFooter(bg, dark);
4025	}).then(function (footer) {
4026		$host.empty().removeClass('is-capturing').attr('hidden', 'hidden');
4027		return {
4028			canvas: bodyCanvas,
4029			footer: footer,
4030			bg: bg,
4031			frameKey: frameKey,
4032			overlay: overlay,
4033			ayahBoxes: bodyCanvas._ayahBoxes || []
4034		};
4035	});
4036}
4037
4038function qkCapturePrintCanvas(sure, ayet, size) {
4039	return qkCaptureLoadPreviewPromise(sure, ayet).then(function () {
4040		qkApplyCaptureSizePreview();
4041		return qkCaptureInnerSnapshot();
4042	}).then(function (info) {
4043		var out = qkTepsirComposeExportCanvas(info.canvas, size, info.frameKey, info.bg, info.footer);
4044		return out;
4045	});
4046}
4047
4048function qkCaptureExport(opts) {
4049	opts = opts || {};
4050	var $btn = $(opts.video ? '#captureBtnVideo' : '#captureBtn');
4051	if ($btn.data('busy')) {
4052		return;
4053	}
4054	if (typeof window.html2canvas !== 'function') {
4055		showToast($('#qkTepsirSaveImg').attr('data-fail') || 'Save failed');
4056		return;
4057	}
4058	var isDua = $('#captureData .dprint').length > 0;
4059	if (isDua && opts.video) {
4060		showToast($('#qkTepsirSaveImg').attr('data-fail') || 'Save failed');
4061		return;
4062	}
4063	var sureNo = parseInt(selectedSureNo, 10) || 0;
4064	var ayetNo = parseInt(selectedAyetNo, 10) || 0;
4065	var range = qkReadAyahRange('qkCaptureAyahFrom', 'qkCaptureAyahTo', sureNo, ayetNo);
4066	var size = qkExportSizeFrom('qkCaptureSize');
4067	var base = isDua ? 'dua' : ((sureNo || 's') + '-' + range.from + (range.to > range.from ? '-' + range.to : ''));
4068	$btn.data('busy', 1).prop('disabled', true);
4069	$('#captureBtn, #captureBtnVideo').prop('disabled', true);
4070	qkCaptureSetStatus('');
4071	if (opts.video) {
4072		qkUnlockAudioForRecord();
4073	}
4074	var work;
4075	if (!opts.video && typeof window.qkExportReadyPages === 'function') {
4076		var prepCap = isDua ? Promise.resolve() : qkCaptureLoadPreviewPromise(sureNo, range.from);
4077		work = prepCap.then(function () {
4078			return window.qkExportReadyPages('capture');
4079		}).then(function (pages) {
4080			if (!pages || !pages.length) {
4081				throw new Error('empty');
4082			}
4083			pages[0]._qkPages = pages;
4084			qkSaveComposedCanvas(pages[0], base + '.jpg');
4085		});
4086	} else if (isDua) {
4087		work = Promise.resolve().then(function () {
4088			qkApplyCaptureSizePreview();
4089			return qkCaptureInnerSnapshot();
4090		}).then(function (info) {
4091			var out = qkTepsirComposeExportCanvas(info.canvas, size, info.frameKey, info.bg, info.footer, false);
4092			qkSaveComposedCanvas(out, base + '.jpg');
4093		});
4094	} else if (opts.video) {
4095		work = qkCaptureLoadPreviewPromise(sureNo, range.from).then(function () {
4096			return window.qkExportReadyPages('capture');
4097		}).then(function (pages) {
4098			if (!pages || !pages.length) {
4099				throw new Error('empty');
4100			}
4101			var clips = [];
4102			var a;
4103			var span = range.to - range.from + 1;
4104			for (a = range.from; a <= range.to; a++) {
4105				var url = qkAyahAudioUrl(sureNo, a);
4106				if (url) {
4107					var page = pages.length === span ? pages[a - range.from] : pages[0];
4108					clips.push({
4109						canvas: page,
4110						audioUrl: url,
4111						ayahText: qkAyahArabicText(sureNo, a)
4112					});
4113				}
4114			}
4115			if (!clips.length) {
4116				throw new Error('clips');
4117			}
4118			return qkRecordAyahVideoClips(clips, {
4119				scroll: false,
4120				pageReady: true,
4121				size: size,
4122				bg: '#fffefb'
4123			}).then(function (out) {
4124				qkDownloadVideoOut(out, base + '.' + out.ext);
4125			});
4126		});
4127	} else {
4128		work = qkCaptureLoadPreviewPromise(sureNo, range.from).then(function () {
4129			qkApplyCaptureSizePreview();
4130			return qkCaptureInnerSnapshot();
4131		}).then(function (info) {
4132			var out = qkTepsirComposeExportCanvas(info.canvas, size, info.frameKey, info.bg, info.footer, range.to > range.from);
4133			qkSaveComposedCanvas(out, base + '.jpg');
4134		});
4135	}
4136	work.catch(function () {
4137		showToast($('#qkTepsirSaveImg').attr('data-fail') || 'Save failed');
4138	}).then(function () {
4139		qkCaptureSetStatus('');
4140		$btn.data('busy', 0);
4141		$('#captureBtn, #captureBtnVideo').prop('disabled', false);
4142	});
4143}
4144
4145window.qkCaptureExport = qkCaptureExport;
4146
4147(function () {
4148	document.addEventListener('copy', function (e) {
4149		try {
4150			if (!e.clipboardData || !window.getSelection) {
4151				return;
4152			}
4153			var text = String(window.getSelection());
4154			if (text.length < 320) {
4155				return;
4156			}
4157			if (/qurankerim\.com/i.test(text)) {
4158				return;
4159			}
4160			e.clipboardData.setData('text/plain', text.replace(/\s+$/, '') + '\n\n— https://www.qurankerim.com');
4161			e.preventDefault();
4162		} catch (err) {}
4163	});
4164})();
4165
4166
4167 

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.