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.