PageSourceSearch

https://manasra-carpet.com/wp-content/plugins/app-addon-new/assets/js/added-to-cart-modal.js?ver=1790261233

js manasra-carpet.com collected 2026-09-24 14:49:05 UTC 14,003 bytes, 397 lines download raw bytes

1(function($){
2	'use strict';
3
4	var autoCloseTimer = null;
5	var progressTimer = null;
6	var AUTO_CLOSE_MS = 10000;
7var startedAt = 0;
8var elapsedBase = 0; // accumulated elapsed time across pauses
9var cartBadgeObserver = null;
10var cartBadgeAttachObserver = null;
11var lastBadgeCountSent = null;
12var badgeDebounceTimer = null;
13
14	function openModal(){
15		var $modal = $('#treesal-added-to-cart-modal');
16		$modal.show().attr('aria-hidden','false');
17		$('body').addClass('treesal-added-to-cart-open');
18	}
19
20	function closeModal(){
21		var $modal = $('#treesal-added-to-cart-modal');
22		$modal.hide().attr('aria-hidden','true');
23		$modal.find('.treesal-added-to-cart-modal__body').empty();
24		$('body').removeClass('treesal-added-to-cart-open');
25		if (autoCloseTimer){ clearTimeout(autoCloseTimer); autoCloseTimer = null; }
26		if (progressTimer){ clearInterval(progressTimer); progressTimer = null; }
27	$(document).off('.treesalTimer');
28	$('#treesal-added-to-cart-modal .treesal-added-to-cart-modal__dialog').off('.treesalTimer');
29	}
30
31function setRingProgress(ratio){
32	ratio = Math.min(1, Math.max(0, ratio));
33	var circumference = 113;
34	var offset = circumference * ratio;
35	var $ring = $('#treesal-added-to-cart-modal').find('.ring-fg');
36	if ($ring.length){ $ring.css('stroke-dashoffset', offset); }
37}
38
39function startAutoClose(){
40	if (autoCloseTimer){ clearTimeout(autoCloseTimer); }
41	if (progressTimer){ clearInterval(progressTimer); }
42	startedAt = Date.now();
43	var remaining = AUTO_CLOSE_MS - elapsedBase;
44	progressTimer = setInterval(function(){
45		var now = Date.now();
46		var elapsedTotal = elapsedBase + (now - startedAt);
47		var ratio = elapsedTotal / AUTO_CLOSE_MS;
48		setRingProgress(ratio);
49		if (ratio >= 1){
50			clearInterval(progressTimer); progressTimer = null;
51		}
52	}, 50);
53	autoCloseTimer = setTimeout(function(){ closeModal(); }, remaining);
54}
55
56function pauseAutoClose(){
57	if (!startedAt){ return; }
58	if (autoCloseTimer){ clearTimeout(autoCloseTimer); autoCloseTimer = null; }
59	if (progressTimer){ clearInterval(progressTimer); progressTimer = null; }
60	elapsedBase += Math.max(0, Date.now() - startedAt);
61}
62
63function resumeAutoClose(){
64	if (elapsedBase >= AUTO_CLOSE_MS){ closeModal(); return; }
65	startAutoClose();
66}
67
68	function fetchAndShow(productId){
69		var data = {
70			action: 'treesal_added_to_cart_modal',
71			product_id: productId || 0,
72			nonce: (window.TreesalCartModal && TreesalCartModal.nonce) || ''
73		};
74		$.post((window.TreesalCartModal && TreesalCartModal.ajax_url) || window.ajaxurl, data)
75			.done(function(resp){
76				if (resp && resp.success && resp.data && resp.data.html){
77					var $modal = $('#treesal-added-to-cart-modal');
78					var $container = $modal.find('.treesal-added-to-cart-modal__body');
79					var $loading = $container.find('.treesal-loading');
80					var $temp = $('<div class="treesal-loaded treesal-fade is-pre"></div>').html(resp.data.html);
81					if ($loading.length){
82						$loading.after($temp);
83						requestAnimationFrame(function(){
84							$temp.removeClass('is-pre');
85							$loading.addClass('is-fading');
86							setTimeout(function(){ $loading.remove(); }, 250);
87						});
88					} else {
89						$container.html(resp.data.html);
90					}
91					openModal();
92					// reset progress state and start
93					elapsedBase = 0;
94					setRingProgress(0);
95					startAutoClose();
96					initSwiper();
97					bindPauseResume();
98				}
99			});
100	}
101
102	function initSwiper(){
103		// Use parent theme's MinimogSwiper if available so styling and breakpoints match
104		var $tmSwiper = $('#treesal-added-to-cart-modal .tm-swiper');
105		if ($tmSwiper.length && $.fn.MinimogSwiper){
106			$tmSwiper.MinimogSwiper();
107		}
108	}
109
110function bindPauseResume(){
111	var $dialog = $('#treesal-added-to-cart-modal .treesal-added-to-cart-modal__dialog');
112	$dialog.off('.treesalTimer');
113	$(document).off('.treesalTimer');
114	$dialog.on('mouseenter.treesalTimer pointerenter.treesalTimer focusin.treesalTimer', function(){
115		pauseAutoClose();
116	});
117	$dialog.on('mouseleave.treesalTimer pointerleave.treesalTimer', function(){
118		resumeAutoClose();
119	});
120	$dialog.on('touchstart.treesalTimer', function(){
121		pauseAutoClose();
122	});
123	// Resume when touch ends outside the dialog.
124	$(document).on('touchend.treesalTimer', function(e){
125		var $d = $('#treesal-added-to-cart-modal .treesal-added-to-cart-modal__dialog');
126		if (!$d.length){ return; }
127		var rect = $d.get(0).getBoundingClientRect();
128		var t = e.changedTouches && e.changedTouches[0];
129		if (!t){ return; }
130		var x = t.clientX, y = t.clientY;
131		if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom){
132			resumeAutoClose();
133		}
134	});
135}
136
137function showLoadingState(){
138	var $modal = $('#treesal-added-to-cart-modal');
139	var $body = $modal.find('.treesal-added-to-cart-modal__body');
140	$body.html(
141		'<div class="treesal-added-head">'
142		+ '<div class="treesal-added-head__icon" aria-hidden="true">'
143		+   '<div class="treesal-added-head__ring">'
144		+     '<svg class="ring" width="40" height="40" viewBox="0 0 40 40">'
145		+       '<circle class="ring-bg" cx="20" cy="20" r="18" stroke-width="4" fill="none"></circle>'
146		+       '<circle class="ring-fg" cx="20" cy="20" r="18" stroke-width="4" fill="none" stroke-linecap="round"></circle>'
147		+     '</svg>'
148		+   '</div>'
149		+   '<svg class="check" width="28" height="28" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle class="check-bg" cx="12" cy="12" r="11" fill="#e5e5e5"/><path d="M17.5 8.5l-6 6-3-3" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
150		+ '</div>'
151		+ '<div class="treesal-added-head__text">'
152		+   '<div class="treesal-added-head__title">... جاري الإضافة</div>'
153		+   '<div class="treesal-added-head__meta">'
154		+     '<span class="treesal-added-head__total skeleton-line"></span>'
155		+     '<span class="treesal-added-head__sep"></span>'
156		+     '<span class="treesal-added-head__count skeleton-line" ></span>'
157		+   '</div>'
158		+ '</div>'
159		+ '</div>'
160		+ '<div class="treesal-added-suggest treesal-loading">'
161		+   '<div class="treesal-added-suggest__title">قد يعجبك أيضًا</div>'
162		+   '<div class="treesal-added-skeleton">'
163		+     skeletonCard() + skeletonCard() + skeletonCard() + skeletonCard()
164		+   '</div>'
165		+ '</div>'
166	);
167	setRingProgress(0);
168	openModal();
169	bindPauseResume();
170}
171
172function skeletonCard(){
173	return (
174		'<div class="skeleton-card">'
175		+ '<div class="skeleton skeleton-thumb"></div>'
176		+ '<div class="skeleton skeleton-line" style="width:80%"></div>'
177		+ '<div class="skeleton skeleton-line" style="width:50%"></div>'
178		+ '</div>'
179	);
180}
181
182// Send cart badge count to React Native WebView
183function postCartBadgeCount(count, action){
184	try {
185		var payload = JSON.stringify({ type: 'CART_BADGE_COUNT', action: action || 'badge_count_on_add', count: count || 0 });
186		if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
187			window.ReactNativeWebView.postMessage(payload);
188		} else if (
189			window.webkit &&
190			window.webkit.messageHandlers &&
191			window.webkit.messageHandlers.ReactNativeWebView &&
192			typeof window.webkit.messageHandlers.ReactNativeWebView.postMessage === 'function'
193		) {
194			window.webkit.messageHandlers.ReactNativeWebView.postMessage(payload);
195		}
196	} catch (_e) {}
197}
198
199// Read badge count from DOM
200function readBadgeCount(){
201	var el = document.querySelector('.icon-badge.mini-cart-badge');
202	if (!el){ return null; }
203	var attrVal = el.getAttribute('data-count');
204	var fromAttr = parseInt(attrVal || '', 10);
205	var count = !isNaN(fromAttr) ? fromAttr : (parseInt((el.textContent || '').trim(), 10) || 0);
206	return count;
207}
208
209// Send only when changed
210function sendBadgeCountIfChanged(action){
211	var count = readBadgeCount();
212	if (count === null){ return; }
213	if (lastBadgeCountSent === count){ return; }
214	lastBadgeCountSent = count;
215	postCartBadgeCount(count, action || 'badge_observer');
216}
217
218// Observe badge element for changes
219function attachBadgeObserver(){
220	try { if (cartBadgeObserver){ cartBadgeObserver.disconnect(); } } catch(_){ }
221	cartBadgeObserver = null;
222	var el = document.querySelector('.icon-badge.mini-cart-badge');
223	if (!el){ return false; }
224	cartBadgeObserver = new MutationObserver(function(){
225		if (badgeDebounceTimer){ clearTimeout(badgeDebounceTimer); }
226		badgeDebounceTimer = setTimeout(function(){ sendBadgeCountIfChanged('badge_observer'); }, 120);
227	});
228	cartBadgeObserver.observe(el, { attributes: true, attributeFilter: ['data-count','class','aria-label'], childList: true, characterData: true, subtree: true });
229	return true;
230}
231
232// Ensure an observer exists, also re-attach if the badge node is replaced
233function ensureBadgeObserver(){
234	if (attachBadgeObserver()){ return; }
235	if (cartBadgeAttachObserver){ return; }
236	var root = document.body || document.documentElement;
237	cartBadgeAttachObserver = new MutationObserver(function(mutations){
238		for (var i = 0; i < mutations.length; i++){
239			var m = mutations[i];
240			if (m.type === 'childList'){
241				if (attachBadgeObserver()){
242					try { cartBadgeAttachObserver.disconnect(); } catch(_){ }
243					cartBadgeAttachObserver = null;
244					// Do not send immediately to avoid premature zero; observer will handle changes
245					break;
246				}
247			}
248		}
249	});
250	cartBadgeAttachObserver.observe(root, { childList: true, subtree: true });
251}
252
253function updateHeaderFromFragments(fragments){
254	try{
255		var $tmp = $('<div></div>');
256		// Prefer cart-data-js for a clean JSON with count
257		if (fragments['.cart-data-js']){ $tmp.append(fragments['.cart-data-js']); }
258		if (fragments['.mini-cart-total']){ $tmp.append(fragments['.mini-cart-total']); }
259		if (fragments['.cart-totals-table']){ $tmp.append(fragments['.cart-totals-table']); }
260
261		var count = 0;
262		var $info = $tmp.find('.cart-data-info');
263		if ($info.length){
264			var raw = $info.attr('data-value') || '';
265			if (raw){
266				try{ var obj = JSON.parse(raw); if (obj && typeof obj.count !== 'undefined'){ count = parseInt(obj.count, 10) || 0; } }catch(_e){}
267			}
268		}
269		if (!count && fragments['.mini-cart-badge']){
270			var $badge = $(fragments['.mini-cart-badge']);
271			var dc = parseInt($badge.attr('data-count'), 10);
272			if (!isNaN(dc)){ count = dc; }
273			else { count = parseInt(($badge.text()||'').trim(), 10) || 0; }
274		}
275
276		var totalHtml = '';
277		if (fragments['.mini-cart-total']){
278			totalHtml = $(fragments['.mini-cart-total']).html() || '';
279		}
280		if (!totalHtml && fragments['.cart-totals-table']){
281			var $table = $(fragments['.cart-totals-table']);
282			var $orderTotal = $table.find('.order-total .amount').first();
283			totalHtml = $orderTotal.length ? $orderTotal.prop('outerHTML') : '';
284		}
285
286		var $modal = $('#treesal-added-to-cart-modal');
287		$modal.find('.treesal-added-head__title').text('تمت إضافة المنتج لسلة التسوق');
288		$modal.find('.treesal-added-head__total').removeClass('skeleton-line').html('قيمة السلة ' + (totalHtml || ''));
289		$modal.find('.treesal-added-head__sep').text(':');
290		$modal.find('.treesal-added-head__count').removeClass('skeleton-line').text('(' + (count || 0) + ' منتجات)');
291		$modal.find('.check-bg').attr('fill', '#27ae60');
292		return count;
293	}catch(e){ return 0; }
294}
295
296	$(document).on('click', '.treesal-added-to-cart-modal__close, .treesal-added-to-cart-modal__backdrop', function(){
297		closeModal();
298	});
299
300	// Close on "Continue" button inside modal actions
301	$(document).on('click', '.treesal-added-actions__btn--secondary[data-close="1"]', function(e){
302		e.preventDefault();
303		closeModal();
304	});
305
306	$(document).on('keydown', function(e){
307		if (e.key === 'Escape'){
308			closeModal();
309		}
310	});
311
312	// Hook WooCommerce event
313	$(document.body).on('added_to_cart', function(evt, fragments, cart_hash, $button){
314		var productId = 0;
315		if ($button && $button.length){
316			var id = $button.data('product_id') || $button.data('product') || $button.val();
317			productId = parseInt(id, 10) || 0;
318		}
319	var cartCount = updateHeaderFromFragments(fragments || {}) || 0;
320	postCartBadgeCount(cartCount, 'badge_count_on_add');
321	fetchAndShow(productId);
322	});
323
324// Open immediately on click, then fill content once added_to_cart fires
325$(document.body).on('adding_to_cart', function(evt, $button){
326	var productId = 0;
327	if ($button && $button.length){
328		var id = $button.data('product_id') || $button.data('product') || $button.val();
329		productId = parseInt(id, 10) || 0;
330	}
331	elapsedBase = 0; setRingProgress(0);
332	showLoadingState();
333});
334
335// Wait for Woo fragments or a non-zero badge before sending initial count
336function waitAndSendInitialBadgeCount(){
337	var start = Date.now();
338	var maxWait = 4000; // ms
339	var iv = null;
340	function trySend(action){
341		var count = readBadgeCount();
342		if (count === null){ return false; }
343		if (count > 0){
344			// force re-check against lastBadgeCountSent
345			if (lastBadgeCountSent !== count){
346				postCartBadgeCount(count, action || 'badge_count_on_load_ready');
347				lastBadgeCountSent = count;
348			}
349			return true;
350		}
351		if ((Date.now() - start) >= maxWait){
352			// timeout: send whatever is available (0 or otherwise)
353			sendBadgeCountIfChanged(action || 'badge_count_on_load_timeout');
354			return true;
355		}
356		return false;
357	}
358	if (!trySend()){
359		iv = setInterval(function(){
360			if (trySend('badge_count_on_load_poll')){
361				clearInterval(iv);
362			}
363		}, 150);
364	}
365	// Also react to WooCommerce fragments events
366	try {
367		jQuery(document.body).one('wc_fragments_loaded wc_fragments_refreshed', function(e, fragments){
368			try {
369				if (fragments && typeof fragments === 'object'){
370					var c = updateHeaderFromFragments(fragments) || 0;
371					if (c > 0){
372						if (iv){ clearInterval(iv); }
373						postCartBadgeCount(c, 'badge_count_on_fragments');
374						lastBadgeCountSent = c;
375						return;
376					}
377				}
378			} catch(_e) {}
379			// Fallback attempt after fragments event
380			trySend('badge_count_on_fragments');
381		});
382	} catch(_e) {}
383}
384
385// On page load, read the mini cart badge count and send it to React Native
386document.addEventListener('DOMContentLoaded', function(){
387	try {
388		ensureBadgeObserver();
389		waitAndSendInitialBadgeCount();
390	} catch (_e) {}
391});
392
393})(jQuery);
394
395
396
397

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.