1document.addEventListener('DOMContentLoaded', function () { 2 3 document.querySelectorAll('.shop-filter-price').forEach(function (filter) { 4 5 var rangeBox = filter.querySelector('.lx_input_range'); 6 7 var minRange = 8 filter.querySelector('.lx_filter_price_min_range'); 9 10 var maxRange = 11 filter.querySelector('.lx_filter_price_max_range'); 12 13 var minInput = 14 filter.querySelector('.lx_filter_price_min'); 15 16 var maxInput = 17 filter.querySelector('.lx_filter_price_max'); 18 19 if ( 20 !rangeBox || 21 !minRange || 22 !maxRange || 23 !minInput || 24 !maxInput 25 ) { 26 return; 27 } 28 29 var step = parseFloat(minRange.step) || 1; 30 31 32 /* 33 * عدد را از input استخراج Ù ÛâÚ©ÙØ¯ 34 */ 35 function getNumber(value) { 36 37 value = String(value || ''); 38 39 value = value.replace(/,/g, ''); 40 41 value = value.replace(/[^\d.]/g, ''); 42 43 var number = parseFloat(value); 44 45 if (!isFinite(number)) { 46 return 0; 47 } 48 49 return number; 50 } 51 52 53 /* 54 * ÙØ±Ù ت ÙÛ٠ت Ø¨Ø±Ø§Û ÙÙ Ø§ÛØ´ 55 */ 56 function formatPrice(value) { 57 58 value = Math.round(value); 59 60 return value.toLocaleString('en-US'); 61 } 62 63 64 /* 65 * جÙÙÚ¯ÛØ±Û از Ø¹Ø¨ÙØ± min Ù max از ÛکدÛگر 66 */ 67 function normalize() { 68 69 var min = 70 parseFloat(minRange.value) || 0; 71 72 var max = 73 parseFloat(maxRange.value) || 0; 74 75 if (min > max) { 76 77 if ( 78 document.activeElement === minRange 79 ) { 80 81 min = max; 82 83 minRange.value = min; 84 85 } else { 86 87 max = min; 88 89 maxRange.value = max; 90 } 91 } 92 93 minInput.value = 94 formatPrice(min); 95 96 maxInput.value = 97 formatPrice(max); 98 99 updateTrack(min, max); 100 } 101 102 103 /* 104 * ÙÙØ§Ø± رÙÚ¯Û Ø¨Û٠د٠دست٠105 */ 106 function updateTrack(min, max) { 107 108 var minLimit = 109 parseFloat(minRange.min) || 0; 110 111 var maxLimit = 112 parseFloat(minRange.max) || 0; 113 114 if (maxLimit <= minLimit) { 115 return; 116 } 117 118 var start = 119 ((min - minLimit) / 120 (maxLimit - minLimit)) * 100; 121 122 var end = 123 ((max - minLimit) / 124 (maxLimit - minLimit)) * 100; 125 126 start = 127 Math.max( 128 0, 129 Math.min(100, start) 130 ); 131 132 end = 133 Math.max( 134 0, 135 Math.min(100, end) 136 ); 137 138 rangeBox.style.setProperty( 139 '--price-start', 140 start + '%' 141 ); 142 143 rangeBox.style.setProperty( 144 '--price-end', 145 end + '%' 146 ); 147 } 148 149 150 /* 151 * تغÛÛØ± minimum 152 */ 153 minRange.addEventListener('input', function () { 154 155 var min = 156 parseFloat(this.value) || 0; 157 158 var max = 159 parseFloat(maxRange.value) || 0; 160 161 if (min > max) { 162 this.value = max; 163 } 164 165 normalize(); 166 }); 167 168 169 /* 170 * تغÛÛØ± maximum 171 */ 172 maxRange.addEventListener('input', function () { 173 174 var min = 175 parseFloat(minRange.value) || 0; 176 177 var max = 178 parseFloat(this.value) || 0; 179 180 if (max < min) { 181 this.value = min; 182 } 183 184 normalize(); 185 }); 186 187 188 /* 189 * تغÛÛØ± Ø¯Ø³ØªÛ minimum 190 */ 191 minInput.addEventListener('input', function () { 192 193 var value = 194 getNumber(this.value); 195 196 var minLimit = 197 parseFloat(minRange.min) || 0; 198 199 var maxLimit = 200 parseFloat(minRange.max) || 0; 201 202 value = 203 Math.max( 204 minLimit, 205 Math.min(maxLimit, value) 206 ); 207 208 minRange.value = value; 209 210 var max = 211 parseFloat(maxRange.value) || 0; 212 213 if (value > max) { 214 maxRange.value = value; 215 } 216 217 updateTrack( 218 parseFloat(minRange.value) || 0, 219 parseFloat(maxRange.value) || 0 220 ); 221 }); 222 223 224 /* 225 * تغÛÛØ± Ø¯Ø³ØªÛ maximum 226 */ 227 maxInput.addEventListener('input', function () { 228 229 var value = 230 getNumber(this.value); 231 232 var minLimit = 233 parseFloat(maxRange.min) || 0; 234 235 var maxLimit = 236 parseFloat(maxRange.max) || 0; 237 238 value = 239 Math.max( 240 minLimit, 241 Math.min(maxLimit, value) 242 ); 243 244 maxRange.value = value; 245 246 var min = 247 parseFloat(minRange.value) || 0; 248 249 if (value < min) { 250 minRange.value = value; 251 } 252 253 updateTrack( 254 parseFloat(minRange.value) || 0, 255 parseFloat(maxRange.value) || 0 256 ); 257 }); 258 259 260 /*
261 * ÙÙÚ¯Ø§Ù Ø®Ø±ÙØ¬ از inputØ Ù ÙØ¯Ø§Ø± را ٠رتب Ú©Ù 262 */ 263 minInput.addEventListener('blur', function () { 264 normalize(); 265 }); 266 267 maxInput.addEventListener('blur', function () { 268 normalize(); 269 }); 270 271 272 /* 273 * ÙØ¨Ù از Ø§Ø±Ø³Ø§Ù ÙØ±Ù : 274 * Ú©Ø§Ù Ø§ÙØ§Û ÙÛ٠ت را ØØ°Ù Ù ÛâÚ©ÙÛÙ 275 * 276 * ÙÙ Ø§ÛØ´: 277 * 310,000 278 * 279 * ارساÙ: 280 * 310000 281 */ 282 var form = 283 filter.closest('.lx-shop-filter-form'); 284 285 if (form) { 286 287 form.addEventListener('submit', function () { 288 289 minInput.value = 290 getNumber(minInput.value); 291 292 maxInput.value = 293 getNumber(maxInput.value); 294 295 }); 296 } 297 298 299 /* 300 * Ù ÙØ¯Ø§Ø± اÙÙÛÙ 301 */ 302 normalize(); 303 304 }); 305 306}); 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324document.addEventListener('DOMContentLoaded', function () { 325 326 var form = document.getElementById('lxReviewSubmitForm'); 327 328 if (!form) { 329 return; 330 } 331 332 333 /* ========================================================= 334 Elements 335 ========================================================= */ 336 337 var ratingInput = document.getElementById( 338 'lxReviewRating' 339 ); 340 341 var ratingContainer = form.querySelector( 342 '.lx-rating-input' 343 ); 344 345 var ratingButtons = ratingContainer 346 ? ratingContainer.querySelectorAll( 347 'button[data-rating]' 348 ) 349 : []; 350 351 var ratingCaption = document.getElementById( 352 'lxRatingCaption' 353 ); 354 355 var imageInput = document.getElementById( 356 'lxReviewImages' 357 ); 358 359 var imagePreview = document.getElementById( 360 'lxReviewImagePreview' 361 ); 362 363 var message = document.getElementById( 364 'lxReviewMessage' 365 ); 366 367 var submitButton = document.getElementById( 368 'lxReviewSubmit' 369 ); 370 371 var openButton = document.getElementById( 372 'lxReviewOpenForm' 373 ); 374 375 var closeButton = document.getElementById( 376 'lxReviewCloseForm' 377 ); 378 379 var cancelButton = document.getElementById( 380 'lxReviewCancel' 381 ); 382 383 var reviewFormWrap = document.getElementById( 384 'lxReviewForm' 385 ); 386 387 388 /* ========================================================= 389 Rating texts 390 ========================================================= */ 391 392 var ratingTexts = { 393 1: 'Ø®ÛÙÛ Ø¶Ø¹ÛÙ', 394 2: 'ضعÛÙ', 395 3: 'Ù ØªÙØ³Ø·', 396 4: 'Ø®ÙØ¨', 397 5: 'عاÙÛ' 398 }; 399 400 401 /* ========================================================= 402 Open Form 403 ========================================================= */ 404 405 if (openButton && reviewFormWrap) { 406 407 openButton.addEventListener( 408 'click', 409 function () { 410 411 reviewFormWrap.classList.add( 412 'is-open' 413 ); 414 415 try { 416 417 reviewFormWrap.scrollIntoView({ 418 behavior: 'smooth', 419 block: 'start' 420 }); 421 422 } catch (e) { 423 424 reviewFormWrap.scrollIntoView(); 425 } 426 } 427 ); 428 } 429 430 431 /* ========================================================= 432 Close Form 433 ========================================================= */ 434 435 function closeReviewForm() { 436 437 if (!reviewFormWrap) { 438 return; 439 } 440 441 reviewFormWrap.classList.remove( 442 'is-open' 443 ); 444 } 445 446 447 if (closeButton) { 448 449 closeButton.addEventListener( 450 'click', 451 closeReviewForm 452 ); 453 } 454 455 456 if (cancelButton) { 457 458 cancelButton.addEventListener( 459 'click', 460 closeReviewForm 461 ); 462 } 463 464 465 /* ========================================================= 466 Rating 467 ========================================================= */ 468 469 function setRating(rating) { 470 471 rating = parseInt( 472 rating, 473 10 474 ); 475 476 if ( 477 isNaN(rating) || 478 rating < 0 || 479 rating > 5 480 ) { 481 rating = 0; 482 } 483 484 485 /* 486 * Hidden input 487 */ 488 if (ratingInput) { 489 490 ratingInput.value = rating; 491 } 492 493 494 /* 495 * Container state 496 */ 497 if (ratingContainer) { 498 499 ratingContainer.setAttribute( 500 'data-rating', 501 rating 502 ); 503 } 504 505 506 /* 507 * Stars 508 */ 509 ratingButtons.forEach( 510 function (button) { 511 512 var buttonRating = parseInt( 513 button.getAttribute( 514 'data-rating' 515 ), 516 10 517 ); 518 519 button.classList.remove( 520 'is-hover' 521 ); 522 523 if ( 524 buttonRating <= rating 525 ) { 526 527 button.classList.add( 528 'is-active' 529 ); 530 531 } else { 532 533 button.classList.remove( 534 'is-active' 535 ); 536 } 537 } 538 ); 539 540 541 /* 542 * Caption 543 */ 544 if (ratingCaption) { 545 546 if ( 547 rating > 0 && 548 ratingTexts[rating] 549 ) { 550 551 ratingCaption.textContent = 552 ratingTexts[rating]; 553 554 ratingCaption.classList.add( 555 'is-selected' 556 ); 557 558 } else { 559 560 ratingCaption.textContent = 561 'Ø§Ù ØªÛØ§Ø² Ø®ÙØ¯ را Ø§ÙØªØ®Ø§Ø¨ Ú©ÙÛØ¯'; 562 563 ratingCaption.classList.remove( 564 'is-selected' 565 ); 566 } 567 } 568 } 569 570 571 /* 572 * Hover preview 573 */ 574 function showRatingHover(rating) { 575 576 rating = parseInt( 577 rating, 578 10 579 ); 580 581 if ( 582 isNaN(rating) || 583 rating < 1 || 584 rating > 5 585 ) { 586 return; 587 } 588 589 590 if (ratingContainer) { 591 592 ratingContainer.classList.add( 593 'is-hovering' 594 ); 595 } 596 597 598 ratingButtons.forEach( 599 function (button) { 600 601 var buttonRating = parseInt( 602 button.getAttribute( 603 'data-rating' 604 ), 605 10 606 ); 607 608 button.classList.remove( 609 'is-hover' 610 ); 611 612 613 if ( 614 buttonRating <= rating 615 ) { 616 617 button.classList.add( 618 'is-hover' 619 ); 620 } 621 } 622 ); 623 624 625 /* 626 * Hover caption 627 */ 628 if ( 629 ratingCaption && 630 ratingTexts[rating] 631 ) { 632 633 ratingCaption.textContent = 634 ratingTexts[rating]; 635 } 636 } 637 638 639 /* 640 * Remove hover and restore selected rating 641 */ 642 function restoreSelectedRating() { 643 644 if (ratingContainer) { 645 646 ratingContainer.classList.remove( 647 'is-hovering' 648 ); 649 } 650 651 652 ratingButtons.forEach( 653 function (button) { 654 655 button.classList.remove( 656 'is-hover' 657 ); 658 } 659 ); 660 661 662 var selectedRating = ratingInput 663 ? parseInt( 664 ratingInput.value, 665 10 666 ) 667 : 0; 668 669 670 if (isNaN(selectedRating)) { 671 selectedRating = 0; 672 } 673 674 675 /* 676 * Restore stars 677 */ 678 ratingButtons.forEach( 679 function (button) { 680 681 var buttonRating = parseInt( 682 button.getAttribute( 683 'data-rating' 684 ), 685 10 686 ); 687 688 689 if ( 690 buttonRating <= selectedRating 691 ) { 692 693 button.classList.add( 694 'is-active' 695 ); 696 697 } else { 698 699 button.classList.remove( 700 'is-active' 701 ); 702 } 703 } 704 ); 705 706 707 /* 708 * Restore caption 709 */ 710 if (ratingCaption) { 711 712 if ( 713 selectedRating > 0 && 714 ratingTexts[selectedRating] 715 ) { 716 717 ratingCaption.textContent = 718 ratingTexts[selectedRating]; 719 720 ratingCaption.classList.add( 721 'is-selected' 722 ); 723 724 } else { 725 726 ratingCaption.textContent = 727 'Ø§Ù ØªÛØ§Ø² Ø®ÙØ¯ را Ø§ÙØªØ®Ø§Ø¨ Ú©ÙÛØ¯'; 728 729 ratingCaption.classList.remove( 730 'is-selected' 731 ); 732 } 733 } 734 } 735 736 737 /* 738 * Attach rating events 739 */ 740 if ( 741 ratingContainer && 742 ratingButtons.length 743 ) { 744 745 ratingButtons.forEach( 746 function (button) { 747 748 /* 749 * Mouse enters star 750 */ 751 button.addEventListener( 752 'mouseenter', 753 function () { 754 755 var hoverRating = 756 parseInt( 757 button.getAttribute( 758 'data-rating' 759 ), 760 10 761 ); 762 763 showRatingHover( 764 hoverRating 765 ); 766 } 767 ); 768 769 770 /* 771 * Click star 772 */ 773 button.addEventListener( 774 'click', 775 function () { 776 777 var rating = 778 parseInt( 779 button.getAttribute( 780 'data-rating' 781 ), 782 10 783 ); 784 785 if ( 786 isNaN(rating) || 787 rating < 1 || 788 rating > 5 789 ) { 790 return; 791 } 792 793 setRating( 794 rating 795 ); 796 } 797 ); 798 } 799 ); 800 801 802 /* 803 * Mouse leaves rating area 804 */ 805 ratingContainer.addEventListener( 806 'mouseleave', 807 function () { 808 809 restoreSelectedRating(); 810 } 811 ); 812 } 813 814 815 /* ========================================================= 816 Image Preview 817 ========================================================= */ 818 819 if ( 820 imageInput && 821 imagePreview 822 ) { 823 824 imageInput.addEventListener( 825 'change', 826 function () { 827 828 imagePreview.innerHTML = ''; 829 830 831 var files = 832 imageInput.files; 833 834 835 if ( 836 !files || 837 !files.length 838 ) { 839 840 return; 841 } 842 843 844 /* 845 * Maximum 5 images 846 */ 847 if (files.length > 5) { 848 849 showMessage( 850 'error', 851 'ØØ¯Ø§Ú©Ø«Ø± Ûµ تصÙÛØ± Ù ÛâØªÙØ§ÙÛØ¯ Ø§ÙØªØ®Ø§Ø¨ Ú©ÙÛØ¯.' 852 ); 853 854 imageInput.value = ''; 855 856 return; 857 } 858 859 860 /* 861 * Check files 862 */ 863 for ( 864 var i = 0;
865 i < files.length; 866 i++ 867 ) { 868 869 var file = 870 files[i]; 871 872 873 /* 874 * Maximum 5MB 875 */ 876 if ( 877 file.size > 878 5 * 1024 * 1024 879 ) { 880 881 showMessage( 882 'error', 883 'ØØ¬Ù ÙØ± تصÙÛØ± ØØ¯Ø§Ú©Ø«Ø± Ûµ Ù Ú¯Ø§Ø¨Ø§ÛØª است.' 884 ); 885 886 imageInput.value = ''; 887 888 imagePreview.innerHTML = 889 ''; 890 891 return; 892 } 893 894 895 /* 896 * Check image type 897 */ 898 if ( 899 !file.type || 900 file.type.indexOf( 901 'image/' 902 ) !== 0 903 ) { 904 905 showMessage( 906 'error', 907 'ÙÙØ· ÙØ§ÛÙâÙØ§Û تصÙÛØ±Û ÙØ§Ø¨Ù Ø§ÙØªØ®Ø§Ø¨ ÙØ³ØªÙد.' 908 ); 909 910 imageInput.value = ''; 911 912 imagePreview.innerHTML = 913 ''; 914 915 return; 916 } 917 918 919 /* 920 * Preview 921 */ 922 var reader = 923 new FileReader(); 924 925 926 reader.onload = 927 (function (file) { 928 929 return function (event) { 930 931 var item = 932 document.createElement( 933 'div' 934 ); 935 936 item.className = 937 'lx-review-image-preview-item'; 938 939 940 var img = 941 document.createElement( 942 'img' 943 ); 944 945 img.src = 946 event.target.result; 947 948 img.alt = ''; 949 950 951 item.appendChild( 952 img 953 ); 954 955 956 imagePreview.appendChild( 957 item 958 ); 959 }; 960 961 })(file); 962 963 964 reader.readAsDataURL( 965 file 966 ); 967 } 968 } 969 ); 970 } 971 972 973 /* ========================================================= 974 Form Submit 975 ========================================================= */ 976 977 form.addEventListener( 978 'submit', 979 function (event) { 980 981 event.preventDefault(); 982 983 984 hideMessage(); 985 986 987 /* 988 * Rating 989 */ 990 var rating = 991 ratingInput 992 ? parseInt( 993 ratingInput.value, 994 10 995 ) 996 : 0; 997 998 999 if ( 1000 !rating || 1001 rating < 1 || 1002 rating > 5 1003 ) { 1004 1005 showMessage( 1006 'error', 1007 'ÙØ·ÙØ§Ù Ø§Ù ØªÛØ§Ø² Ø®ÙØ¯ را Ø§ÙØªØ®Ø§Ø¨ Ú©ÙÛØ¯.' 1008 ); 1009 1010 return; 1011 } 1012 1013 1014 /* 1015 * Body 1016 */ 1017 var bodyInput = 1018 form.querySelector( 1019 'textarea[name="body"]' 1020 ); 1021 1022 1023 if ( 1024 !bodyInput || 1025 !bodyInput.value.trim() 1026 ) { 1027 1028 showMessage( 1029 'error', 1030 'ÙØ·ÙØ§Ù Ù ØªÙ ÙØ¸Ø± Ø®ÙØ¯ را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.' 1031 ); 1032 1033 1034 if (bodyInput) { 1035 1036 bodyInput.focus(); 1037 } 1038 1039 1040 return; 1041 } 1042 1043 1044 /* 1045 * Disable submit 1046 */ 1047 if (submitButton) { 1048 1049 submitButton.disabled = 1050 true; 1051 1052 submitButton.classList.add( 1053 'is-loading' 1054 ); 1055 } 1056 1057 1058 /* 1059 * FormData 1060 */ 1061 var formData = 1062 new FormData(form); 1063 1064 1065 /* 1066 * Make sure action exists 1067 */ 1068 if ( 1069 !formData.get('action') 1070 ) { 1071 1072 formData.append( 1073 'action', 1074 'review_submit' 1075 ); 1076 } 1077 1078 1079 /* 1080 * Make sure product_id exists 1081 * 1082 * در ØØ§Ùت Ø¹Ø§Ø¯Û Ø§Ø² hidden input ÙØ±Ù 1083 * ارسا٠٠ÛâØ´ÙØ¯. 1084 */ 1085 if ( 1086 !formData.get('product_id') 1087 ) { 1088 1089 var productIdInput = 1090 form.querySelector( 1091 'input[name="product_id"]' 1092 ); 1093 1094 1095 if ( 1096 productIdInput && 1097 productIdInput.value 1098 ) { 1099 1100 formData.set( 1101 'product_id', 1102 productIdInput.value 1103 ); 1104 } 1105 } 1106 1107 1108 /* 1109 * AJAX 1110 */ 1111 fetch( 1112 '/coreshop/ajax.php', 1113 { 1114 method: 'POST', 1115 body: formData, 1116 credentials: 'same-origin' 1117 } 1118 ) 1119 .then( 1120 function (response) { 1121 1122 if (!response.ok) { 1123 1124 throw new Error( 1125 'HTTP ' + 1126 response.status 1127 ); 1128 } 1129 1130 1131 return response.json(); 1132 } 1133 ) 1134 .then( 1135 function (result) { 1136 1137 if ( 1138 result && 1139 result.success 1140 ) { 1141 1142 showMessage( 1143 'success', 1144 result.message || 1145 'ÙØ¸Ø± ش٠ا با Ù ÙÙÙÛØª ثبت شد.' 1146 ); 1147 1148 1149 /* 1150 * Reset form 1151 * 1152 * ØªÙØ¬Ù: 1153 * hidden product_id ÙÙ reset Ù ÛâØ´ÙØ¯ 1154 * ٠٠٠ک٠است خاÙÛ Ø´ÙØ¯. 1155 * 1156 * Ø¨ÙØ§Ø¨Ø±Ø§Û٠بعد از reset Ø¯ÙØ¨Ø§Ø±Ù 1157 * product_id را از Ù ÙØ¯Ø§Ø± ÙØ¨ÙÛ Ø¨Ø±Ù ÛâگرداÙÛÙ . 1158 */ 1159 var productIdValue = 1160 form.querySelector( 1161 'input[name="product_id"]' 1162 ); 1163 1164 1165 var savedProductId = 1166 productIdValue 1167 ? productIdValue.value 1168 : ''; 1169 1170 1171 form.reset(); 1172 1173 1174 /* 1175 * Restore product ID 1176 */ 1177 if ( 1178 productIdValue && 1179 savedProductId 1180 ) { 1181 1182 productIdValue.value = 1183 savedProductId; 1184 } 1185 1186 1187 /* 1188 * Reset rating 1189 */ 1190 setRating(0); 1191 1192 1193 /* 1194 * Clear hover state 1195 */ 1196 if (ratingContainer) { 1197 1198 ratingContainer.classList.remove( 1199 'is-hovering' 1200 ); 1201 } 1202 1203 1204 ratingButtons.forEach( 1205 function (button) { 1206 1207 button.classList.remove( 1208 'is-active' 1209 ); 1210 1211 button.classList.remove( 1212 'is-hover' 1213 ); 1214 } 1215 ); 1216 1217 1218 /* 1219 * Clear image preview 1220 */ 1221 if (imagePreview) { 1222 1223 imagePreview.innerHTML = 1224 ''; 1225 } 1226 1227 1228 /* 1229 * Store review ID 1230 */ 1231 if ( 1232 result.review_id 1233 ) { 1234 1235 form.setAttribute( 1236 'data-review-id', 1237 result.review_id 1238 ); 1239 } 1240 1241 } else { 1242 1243 showMessage( 1244 'error', 1245 ( 1246 result && 1247 result.message 1248 ) || 1249 'ثبت ÙØ¸Ø± Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 1250 ); 1251 } 1252 } 1253 ) 1254 .catch( 1255 function (error) { 1256 1257 console.error( 1258 'Review submit error:', 1259 error 1260 ); 1261 1262 1263 showMessage( 1264 'error', 1265 'خطاÛÛ Ø¯Ø± ارتباط با Ø³Ø±ÙØ± رخ داد. ÙØ·ÙØ§Ù Ø¯ÙØ¨Ø§Ø±Ù ØªÙØ§Ø´ Ú©ÙÛØ¯.' 1266 ); 1267 } 1268 ) 1269 .finally( 1270 function () { 1271 1272 if (submitButton) { 1273 1274 submitButton.classList.remove( 1275 'is-loading' 1276 ); 1277 1278 submitButton.disabled = 1279 false; 1280 } 1281 } 1282 ); 1283 } 1284 ); 1285 1286 1287 /* ========================================================= 1288 Show Message 1289 ========================================================= */ 1290 1291 function showMessage( 1292 type, 1293 text 1294 ) { 1295 1296 if (!message) { 1297 return; 1298 } 1299 1300 1301 message.className = 1302 'lx-review-message'; 1303 1304 1305 if ( 1306 type === 'success' 1307 ) { 1308 1309 message.classList.add( 1310 'is-success' 1311 ); 1312 1313 } else { 1314 1315 message.classList.add( 1316 'is-error'
1317 ); 1318 } 1319 1320 1321 message.textContent = 1322 text; 1323 1324 1325 message.style.display = 1326 'block'; 1327 1328 1329 try { 1330 1331 message.scrollIntoView({ 1332 behavior: 'smooth', 1333 block: 'nearest' 1334 }); 1335 1336 } catch (e) { 1337 1338 /* Ignore */ 1339 } 1340 } 1341 1342 1343 /* ========================================================= 1344 Hide Message 1345 ========================================================= */ 1346 1347 function hideMessage() { 1348 1349 if (!message) { 1350 return; 1351 } 1352 1353 1354 message.className = 1355 'lx-review-message'; 1356 1357 1358 message.textContent = 1359 ''; 1360 1361 1362 message.style.display = 1363 'none'; 1364 } 1365 1366}); 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389(function ($) { 1390 1391 'use strict'; 1392 1393 1394 /* 1395 * ============================================================ 1396 * LoxShop Product Gallery 1397 * ============================================================ 1398 */ 1399 1400 var mainSwiper = null; 1401 1402 var thumbsSwiper = null; 1403 1404 var pendingVariantImage = null; 1405 1406 /* 1407 * ============================================================ 1408 * Normalize Image URL 1409 * ============================================================ 1410 */ 1411 1412 function normalizeImageUrl(url) { 1413 1414 if (!url) { 1415 return ''; 1416 } 1417 1418 url = String(url).trim(); 1419 1420 try { 1421 1422 var parsed = new URL( 1423 url, 1424 window.location.href 1425 ); 1426 1427 /* 1428 * Ø¨Ø±Ø§Û Ù ÙØ§ÛØ³ÙØ query string را ØØ°Ù Ù ÛâÚ©ÙÛÙ . 1429 * 1430 * ٠ثاÙ: 1431 * 1432 * image.jpg 1433 * 1434 * image.jpg?w=800 1435 * 1436 * ÙØ± د٠ÛÚ©Û Ø¯Ø± ÙØ¸Ø± Ú¯Ø±ÙØªÙ Ù ÛâØ´ÙÙØ¯. 1437 */ 1438 1439 parsed.search = ''; 1440 1441 return parsed.href; 1442 1443 } catch (e) { 1444 1445 return url.split('?')[0]; 1446 1447 } 1448 1449 } 1450 1451 1452 /* 1453 * ============================================================ 1454 * Find Main Slide 1455 * ============================================================ 1456 */ 1457 1458 function findSlideIndex(imageUrl) { 1459 1460 var target = 1461 normalizeImageUrl(imageUrl); 1462 1463 var index = -1; 1464 1465 1466 $('.lx-gallery-main .swiper-slide').each( 1467 function (i) { 1468 1469 var slideUrl = 1470 $(this).attr( 1471 'data-product-image' 1472 ); 1473 1474 1475 if ( 1476 normalizeImageUrl(slideUrl) === 1477 target 1478 ) { 1479 1480 index = i; 1481 1482 return false; 1483 1484 } 1485 1486 } 1487 ); 1488 1489 1490 return index; 1491 1492 } 1493 1494 1495 /* 1496 * ============================================================ 1497 * Create Main Slide 1498 * ============================================================ 1499 */ 1500 1501 function createMainSlide(imageUrl) { 1502 1503 var safeUrl = escapeHtmlAttribute(imageUrl); 1504 1505 return ( 1506 '<div class="swiper-slide"' +
1507 ' data-product-image="' + safeUrl + '">' + 1508 1509 '<a class="lx-gallery-pswp-link"' + 1510 ' href="' + safeUrl + '"' + 1511 ' data-pswp-src="' + safeUrl + '">' + 1512 1513 '<div class="swiper-zoom-container">' + 1514 1515 '<img' + 1516 ' src="' + safeUrl + '"' + 1517 ' alt=""' + 1518 ' class="lx-gallery-main-image">' + 1519 1520 '</div>' + 1521 1522 '</a>' + 1523 1524 '</div>' 1525 ); 1526 } 1527 1528 /* 1529 * ============================================================ 1530 * Create Thumbnail 1531 * ============================================================ 1532 */ 1533 1534 function createThumbnail(imageUrl) { 1535 1536 return ( 1537 '<div class="swiper-slide">' + 1538 1539 '<button' + 1540 ' type="button"' + 1541 ' class="lx-gallery-thumb"' + 1542 '>' + 1543 1544 '<img' + 1545 ' src="' + 1546 escapeHtmlAttribute(imageUrl) + 1547 '?w=180"' + 1548 ' alt=""' + 1549 '>' + 1550 1551 '</button>' + 1552 1553 '</div>' 1554 ); 1555 1556 } 1557 1558 1559 /* 1560 * ============================================================ 1561 * Create PhotoSwipe Item 1562 * ============================================================ 1563 */ 1564 1565 function createPhotoSwipeItem(imageUrl) { 1566 1567 var safeUrl = 1568 escapeHtmlAttribute(imageUrl); 1569 1570 return ( 1571 '<a' + 1572 ' href="' + safeUrl + '"' + 1573 ' data-pswp-src="' + safeUrl + '"' + 1574 '>' + 1575 1576 '<img' + 1577 ' src="' + safeUrl + '?w=150"' + 1578 ' alt=""' + 1579 '>' + 1580 1581 '</a>' 1582 ); 1583 } 1584 1585 1586 /* 1587 * ============================================================ 1588 * Escape HTML Attribute 1589 * ============================================================ 1590 */ 1591 1592 function escapeHtmlAttribute(value) { 1593 1594 return String(value) 1595 .replace(/&/g, '&') 1596 .replace(/"/g, '"') 1597 .replace(/</g, '<') 1598 .replace(/>/g, '>') 1599 .replace(/'/g, '''); 1600 1601 } 1602 1603 1604 /* 1605 * ============================================================ 1606 * Add Image To Gallery 1607 * ============================================================ 1608 */ 1609 1610 function addImageToGallery(imageUrl) { 1611 1612 if (!mainSwiper) { 1613 return -1; 1614 } 1615 1616 1617 /* 1618 * اÙÙ Ø¨Ø±Ø±Ø³Û Ù ÛâÚ©ÙÛ٠ک٠تصÙÛØ± ÙØ¬Ùد دارد ÛØ§ ÙÙ. 1619 */ 1620 1621 var existingIndex = 1622 findSlideIndex(imageUrl); 1623 1624 1625 if (existingIndex >= 0) { 1626 1627 return existingIndex; 1628 1629 } 1630 1631 1632 /* 1633 * ======================================================== 1634 * MAIN SLIDE 1635 * ======================================================== 1636 */ 1637 1638 mainSwiper.appendSlide( 1639 createMainSlide(imageUrl) 1640 ); 1641 1642 1643 /* 1644 * ======================================================== 1645 * THUMBNAIL 1646 * ======================================================== 1647 */ 1648 1649 if (thumbsSwiper) { 1650 1651 thumbsSwiper.appendSlide( 1652 createThumbnail(imageUrl) 1653 ); 1654 1655 } 1656 1657 1658 1659 1660 /* 1661 * ======================================================== 1662 * UPDATE SWIPER 1663 * ======================================================== 1664 */ 1665 1666 mainSwiper.update(); 1667 1668 1669 if (thumbsSwiper) { 1670 1671 thumbsSwiper.update(); 1672 1673 } 1674 1675 1676 /* 1677 * index تصÙÛØ± Ø¬Ø¯ÛØ¯ 1678 */ 1679 1680 return mainSwiper.slides.length - 1; 1681 1682 } 1683 1684 1685 /* 1686 * ============================================================ 1687 * Show Image 1688 * ============================================================ 1689 */ 1690 1691 function showImage(imageUrl) { 1692 1693 if (!imageUrl) { 1694 return; 1695 } 1696 1697 if (!mainSwiper) { 1698 1699 pendingVariantImage = imageUrl; 1700 1701 return; 1702 } 1703 1704 /* 1705 * تصÙÛØ± ÙØ¨Ùا٠در Ú¯Ø§ÙØ±Û ÙØ¬Ùد Ø¯Ø§Ø±Ø¯Ø 1706 */ 1707 1708 var index = 1709 findSlideIndex(imageUrl); 1710 1711 1712 /* 1713 * اگر ÙØ¬Ùد ÙØ¯Ø§Ø±Ø¯Ø اضاÙÙâØ§Ø´ Ù ÛâÚ©ÙÛÙ . 1714 */ 1715 1716 if (index < 0) { 1717 1718 index = 1719 addImageToGallery(imageUrl); 1720 1721 } 1722 1723 1724 if (index < 0) { 1725 return; 1726 } 1727 1728 1729 /* 1730 * Ø±ÙØªÙ ب٠تصÙÛØ± Variation 1731 */ 1732 1733 mainSwiper.slideTo( 1734 index, 1735 500 1736 ); 1737 1738 1739 /* 1740 * اط٠ÛÙØ§Ù از ØØ±Ú©Øª thumbnail 1741 */ 1742 1743 if (thumbsSwiper) { 1744 1745 thumbsSwiper.slideTo( 1746 index, 1747 500 1748 ); 1749 1750 } 1751 1752 1753 /* 1754 * Counter 1755 */ 1756 1757 updateCounter(); 1758 1759 } 1760 1761 1762 /* 1763 * ============================================================ 1764 * Counter 1765 * ============================================================ 1766 */ 1767 1768 function updateCounter() { 1769 1770 if (!mainSwiper) { 1771 return; 1772 } 1773 1774 1775 var current = 1776 mainSwiper.activeIndex + 1; 1777 1778 1779 var total = 1780 mainSwiper.slides.length; 1781 1782 1783 $('.lx-gallery-current') 1784 .text(current); 1785 1786 1787 $('.lx-gallery-total') 1788 .text(total); 1789 1790 } 1791 1792 1793 /* 1794 * ============================================================ 1795 * Init 1796 * ============================================================ 1797 */ 1798 1799 1800 /* 1801 * ============================================================ 1802 * PhotoSwipe Lightbox 1803 * ============================================================ 1804 */ 1805 1806 function initPhotoSwipe() { 1807 1808 if ( 1809 typeof PhotoSwipeLightbox === 'undefined' || 1810 typeof PhotoSwipe === 'undefined' 1811 ) { 1812 return; 1813 } 1814 1815 1816 var lightbox = new PhotoSwipeLightbox({ 1817 1818 /* 1819 * Ú¯Ø§ÙØ±ÛâÙØ§Û PhotoSwipe 1820 * 1821 * ÙØ± کدا٠از اÛÙ containerÙØ§ 1822 * ÛÚ© Ú¯Ø§ÙØ±Û ٠ستÙÙ Ù ØØ³Ùب Ù ÛâØ´ÙÙØ¯. 1823 */ 1824 gallery: 1825 '.lx-gallery-main, .lx-review-images', 1826 1827 /* 1828 * ÙÛÙÚ©âÙØ§Û ÙØ§Ø¨Ù Ú©ÙÛÚ© Ø¯Ø§Ø®Ù Ú¯Ø§ÙØ±Û 1829 */ 1830 children: 1831 '.lx-gallery-pswp-link, .lx-review-photo', 1832 1833 pswpModule: PhotoSwipe, 1834 1835 bgOpacity: 0.96, 1836 1837 showHideAnimationType: 'zoom', 1838 1839 wheelToZoom: true, 1840 1841 pinchToClose: true, 1842 1843 closeOnVerticalDrag: true, 1844 1845 paddingFn: function () { 1846 1847 return { 1848 1849 top: 30, 1850 1851 bottom: 30, 1852 1853 left: 30, 1854 1855 right: 30 1856 }; 1857 } 1858 }); 1859 1860 1861 /* 1862 * ============================================================ 1863 * Ø¯Ø±ÛØ§Ùت ابعاد ÙØ§ÙØ¹Û ØªØµÙÛØ± 1864 * ============================================================ 1865 * 1866 * ÙØ§Ø²Ù ÙÛØ³Øª width Ù height را در Ø¯ÛØªØ§Ø¨Ûس Ø°Ø®ÛØ±Ù Ú©ÙÛÙ . 1867 * 1868 * PhotoSwipe ÙØ¨Ù از ÙÙ Ø§ÛØ´Ø ابعاد تصÙÛØ± Ù ÙØ¬Ùد 1869 * در Ù٠ا٠<img> را Ø¯Ø±ÛØ§Ùت Ù ÛâÚ©ÙØ¯. 1870 */ 1871 lightbox.addFilter( 1872 'itemData', 1873 function (itemData, index) { 1874 1875 /* 1876 * اگر ابعاد از ÙØ¨Ù ÙØ¬Ùد Ø¯Ø§Ø±Ø¯Ø 1877 * ÙÛØ§Ø²Û ب٠ادا٠٠ÙÛØ³Øª. 1878 */ 1879 if ( 1880 itemData.width && 1881 itemData.height 1882 ) { 1883 1884 return itemData; 1885 } 1886 1887 1888 /* 1889 * URL تصÙÛØ± 1890 */ 1891 var src = 1892 itemData.src || 1893 ( 1894 itemData.element 1895 ? itemData.element.getAttribute('href') 1896 : '' 1897 ); 1898 1899 1900 if (!src) { 1901 1902 return itemData; 1903 } 1904 1905 1906 /* 1907 * اگر ÙØ¨Ùا٠ابعاد اÛ٠تصÙÛØ± را 1908 * رÙÛ element Ø°Ø®ÛØ±Ù کردÙâØ§ÛÙ Ø 1909 * Ø¯ÙØ¨Ø§Ø±Ù Ù ØØ§Ø³Ø¨Ù ÙÚ©Ù. 1910 */ 1911 var cached = 1912 itemData.element && 1913 itemData.element.dataset.pswpWidth; 1914 1915 1916 if (cached) { 1917 1918 itemData.width = 1919 parseInt( 1920 itemData.element.dataset.pswpWidth, 1921 10 1922 ); 1923 1924 itemData.height = 1925 parseInt( 1926 itemData.element.dataset.pswpHeight, 1927 10 1928 ); 1929 1930 return itemData; 1931 } 1932 1933 1934 /* 1935 * تصÙÛØ± داخ٠ÙÛÙÚ© 1936 * 1937 * ÙÙ Ø¨Ø±Ø§Û ØªØµØ§ÙÛØ± Ù ØØµÙ٠کار Ù ÛâÚ©ÙØ¯ 1938 * ÙÙ Ø¨Ø±Ø§Û ØªØµØ§ÙÛØ± Ú©Ø§Ù ÙØª. 1939 */ 1940 var img = 1941 itemData.element 1942 ? itemData.element.querySelector('img') 1943 : null; 1944 1945 1946 if ( 1947 img &&
1948 img.naturalWidth > 0 && 1949 img.naturalHeight > 0 1950 ) { 1951 1952 itemData.width = 1953 img.naturalWidth; 1954 1955 itemData.height = 1956 img.naturalHeight; 1957 1958 1959 /* 1960 * Cache 1961 */ 1962 if (itemData.element) { 1963 1964 itemData.element.dataset.pswpWidth = 1965 img.naturalWidth; 1966 1967 itemData.element.dataset.pswpHeight = 1968 img.naturalHeight; 1969 } 1970 } 1971 1972 1973 return itemData; 1974 } 1975 ); 1976 1977 1978 /* 1979 * Ø´Ø±ÙØ¹ PhotoSwipe 1980 */ 1981 lightbox.init(); 1982 1983 1984 /* 1985 * Ø¯Ø³ØªØ±Ø³Û Ø¹Ù ÙÙ Û 1986 */ 1987 window.lxProductPhotoSwipe = 1988 lightbox; 1989 } 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 function init() { 2003 2004 /* 2005 * اگر Ú¯Ø§ÙØ±Û ÙØ¬Ùد ÙØ¯Ø§Ø±Ø¯Ø Ú©Ø§Ø±Û ÙÚ©Ù. 2006 */ 2007 2008 if ( 2009 !document.querySelector( 2010 '.lx-gallery-main' 2011 ) 2012 ) { 2013 2014 return; 2015 2016 } 2017 2018 2019 /* 2020 * ======================================================== 2021 * THUMBNAILS SWIPER 2022 * ======================================================== 2023 */ 2024 2025 thumbsSwiper = new Swiper( 2026 '.lx-gallery-thumbs', 2027 { 2028 2029 slidesPerView: 5, 2030 2031 spaceBetween: 10, 2032 2033 watchSlidesProgress: true, 2034 2035 navigation: { 2036 2037 nextEl: 2038 '.lx-thumb-next', 2039 2040 prevEl: 2041 '.lx-thumb-prev' 2042 2043 }, 2044 2045 breakpoints: { 2046 2047 0: { 2048 2049 slidesPerView: 4 2050 2051 }, 2052 2053 576: { 2054 2055 slidesPerView: 5 2056 2057 }, 2058 2059 768: { 2060 2061 slidesPerView: 6 2062 2063 } 2064 2065 } 2066 2067 } 2068 ); 2069 2070 2071 /* 2072 * ======================================================== 2073 * MAIN SWIPER 2074 * ======================================================== 2075 */ 2076 2077 mainSwiper = new Swiper( 2078 '.lx-gallery-main', 2079 { 2080 2081 slidesPerView: 1, 2082 2083 spaceBetween: 0, 2084 2085 speed: 500, 2086 2087 direction: 'horizontal', 2088 2089 navigation: { 2090 2091 nextEl: 2092 '.lx-gallery-button-next', 2093 2094 prevEl: 2095 '.lx-gallery-button-prev' 2096 2097 }, 2098 2099 zoom: { 2100 2101 maxRatio: 3 2102 2103 }, 2104 2105 thumbs: { 2106 2107 swiper: 2108 thumbsSwiper 2109 2110 }, 2111 2112 on: { 2113 2114 init: function () { 2115 2116 updateCounter(); 2117 2118 }, 2119 2120 slideChange: function () { 2121 2122 updateCounter(); 2123 2124 } 2125 2126 } 2127 2128 } 2129 ); 2130 2131 initPhotoSwipe(); 2132 2133 /* 2134 * ======================================================== 2135 * PUBLIC API 2136 * ======================================================== 2137 */ 2138 2139 window.lxProductGallery = { 2140 2141 /* 2142 * ÙÙ Ø§ÛØ´ تصÙÛØ± 2143 */ 2144 2145 showImage: 2146 showImage, 2147 2148 2149 /* 2150 * اضاÙ٠کرد٠تصÙÛØ± 2151 */ 2152 2153 addImage: 2154 addImageToGallery, 2155 2156 2157 /* 2158 * Ú¯Ø±ÙØªÙ Main Swiper 2159 */ 2160 2161 getMainSwiper: 2162 function () { 2163 2164 return mainSwiper; 2165 2166 }, 2167 2168 2169 /* 2170 * Ú¯Ø±ÙØªÙ Thumbnail Swiper 2171 */ 2172 2173 getThumbsSwiper: 2174 function () { 2175 2176 return thumbsSwiper; 2177 2178 } 2179 2180 }; 2181 2182 2183 2184 if (pendingVariantImage) { 2185 2186 var imageUrl = 2187 pendingVariantImage; 2188 2189 pendingVariantImage = null; 2190 2191 showImage(imageUrl); 2192 2193 } 2194 2195 2196 } 2197 2198 2199 /* 2200 * ============================================================ 2201 * Start 2202 * ============================================================ 2203 */ 2204 2205 $(function () { 2206 2207 init(); 2208 2209 });
2210 2211 2212})(jQuery); 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245(function (window, document) { 2246 2247 'use strict'; 2248 2249 /* 2250 |-------------------------------------------------------------------------- 2251 |-------------------------------------------------------------------------- 2252 | LOXBLOG SHOP CART 2253 |-------------------------------------------------------------------------- 2254 |-------------------------------------------------------------------------- 2255 */ 2256 2257 const ShopCart = { 2258 2259 endpoint: '/shoporder/checkorder.php', 2260 2261 2262 /* 2263 |-------------------------------------------------------------------------- 2264 | AJAX REQUEST 2265 |-------------------------------------------------------------------------- 2266 */ 2267 2268 request: async function (data) { 2269 2270 let response; 2271 2272 try { 2273 2274 response = await fetch( 2275 this.endpoint, 2276 { 2277 method: 'POST', 2278 headers: { 2279 'Content-Type': 'application/json', 2280 'X-Requested-With': 'XMLHttpRequest' 2281 }, 2282 credentials: 'same-origin', 2283 body: JSON.stringify(data) 2284 } 2285 ); 2286 2287 } catch (error) { 2288 2289 throw { 2290 success: false, 2291 error: 'network_error', 2292 message: 'ارتباط با Ø³Ø±ÙØ± Ø¨Ø±ÙØ±Ø§Ø± ÙØ´Ø¯.' 2293 }; 2294 } 2295 2296 2297 let result; 2298 2299 try { 2300 2301 result = await response.json(); 2302 2303 } catch (error) { 2304 2305 throw { 2306 success: false, 2307 error: 'invalid_response', 2308 message: 'پاسخ Ø³Ø±ÙØ± ٠عتبر ÙÛØ³Øª.' 2309 }; 2310 } 2311 2312 2313 if ( 2314 !response.ok || 2315 !result || 2316 result.success !== true 2317 ) { 2318 2319 throw result || { 2320 success: false, 2321 error: 'server_error', 2322 message: 'خطاÛÛ Ø¯Ø± Ø³Ø±ÙØ± رخ داد٠است.' 2323 }; 2324 } 2325 2326 return result; 2327 }, 2328 2329 2330 /* 2331 |-------------------------------------------------------------------------- 2332 | GET CHECKOUT SESSION 2333 |-------------------------------------------------------------------------- 2334 | 2335 | Ø§Ø·ÙØ§Ø¹Ø§Øª Checkout Session از Ø®ÙØ¯ DOM Ø®ÙØ§Ùد٠٠ÛâØ´ÙØ¯. 2336 | 2337 */ 2338 2339 getCheckoutSession: function () { 2340 2341 const box = 2342 document.getElementById( 2343 'LoxShopCoupon' 2344 ); 2345 2346 if (!box) { 2347 return null; 2348 } 2349 2350 2351 const checkoutSessionId = 2352 parseInt( 2353 box.getAttribute( 2354 'data-checkout-session-id' 2355 ) || 0, 2356 10 2357 ); 2358 2359 2360 const checkoutSessionToken = 2361 ( 2362 box.getAttribute( 2363 'data-checkout-session-token' 2364 ) || '' 2365 ).trim(); 2366 2367 2368 if ( 2369 checkoutSessionId <= 0 || 2370 checkoutSessionToken === '' 2371 ) { 2372 return null; 2373 } 2374 2375 2376 return { 2377 id: checkoutSessionId, 2378 token: checkoutSessionToken 2379 }; 2380 }, 2381 2382 2383 /* 2384 |-------------------------------------------------------------------------- 2385 | GET COUPON CODE 2386 |-------------------------------------------------------------------------- 2387 */ 2388 2389 getCouponCode: function () { 2390 2391 const input = 2392 document.getElementById( 2393 'shop_coupon_code' 2394 ); 2395 2396 if (!input) { 2397 return ''; 2398 } 2399 2400 return ( 2401 input.value || '' 2402 ).trim(); 2403 }, 2404 2405 2406 /* 2407 |-------------------------------------------------------------------------- 2408 | SET LOADING 2409 |-------------------------------------------------------------------------- 2410 */ 2411 2412 setCouponLoading: function ( 2413 loading 2414 ) { 2415 2416 const applyButton = 2417 document.getElementById( 2418 'shop_coupon_apply' 2419 ); 2420 2421 const removeButton = 2422 document.getElementById( 2423 'shop_coupon_remove' 2424 ); 2425 2426
2427 if (applyButton) { 2428 2429 if (loading) { 2430 2431 if ( 2432 applyButton.dataset.couponLoading === '1' 2433 ) { 2434 return false; 2435 } 2436 2437 applyButton.dataset.couponLoading = '1'; 2438 applyButton.disabled = true; 2439 2440 } else { 2441 2442 applyButton.dataset.couponLoading = '0'; 2443 applyButton.disabled = false; 2444 } 2445 } 2446 2447 2448 if (removeButton) { 2449 2450 removeButton.disabled = loading; 2451 } 2452 2453 return true; 2454 }, 2455 2456 2457 /* 2458 |-------------------------------------------------------------------------- 2459 | COUPON MESSAGE 2460 |-------------------------------------------------------------------------- 2461 */ 2462 2463 showCouponMessage: function ( 2464 message, 2465 type 2466 ) { 2467 2468 const element = 2469 document.getElementById( 2470 'shop_coupon_message' 2471 ); 2472 2473 if (!element) { 2474 return; 2475 } 2476 2477 2478 element.className = 2479 'mT_10 ' + 2480 ( 2481 type === 'success' 2482 ? 'msuccess' 2483 : 'merror' 2484 ); 2485 2486 2487 element.textContent = 2488 message || ''; 2489 2490 2491 element.hidden = 2492 !message; 2493 }, 2494 2495 2496 /* 2497 |-------------------------------------------------------------------------- 2498 | CLEAR COUPON MESSAGE 2499 |-------------------------------------------------------------------------- 2500 */ 2501 2502 clearCouponMessage: function () { 2503 2504 const element = 2505 document.getElementById( 2506 'shop_coupon_message' 2507 ); 2508 2509 if (!element) { 2510 return; 2511 } 2512 2513 element.textContent = ''; 2514 element.hidden = true; 2515 }, 2516 2517 2518 /* 2519 |-------------------------------------------------------------------------- 2520 | UPDATE CHECKOUT TOTALS 2521 |-------------------------------------------------------------------------- 2522 | 2523 | ÙØªÛج٠coupon_apply / coupon_remove 2524 | Ù Ø¨ÙØº ÙÙØ§ÛÛ Checkout را بر٠ÛâÚ¯Ø±Ø¯Ø§ÙØ¯. 2525 | 2526 */ 2527 2528 handleCouponCheckout: function ( 2529 result 2530 ) { 2531 2532 if (!result) { 2533 return; 2534 } 2535 2536 2537 /* 2538 |-------------------------------------------------------------------------- 2539 | FINAL DISCOUNT 2540 |-------------------------------------------------------------------------- 2541 */ 2542 2543 document 2544 .querySelectorAll( 2545 '[data-checkout-discount]' 2546 ) 2547 .forEach(function (element) { 2548 2549 element.textContent = 2550 this.formatMoney( 2551 result.discount_amount || 0 2552 ); 2553 2554 }, this); 2555 2556 2557 /* 2558 |-------------------------------------------------------------------------- 2559 | CART DISCOUNT 2560 |-------------------------------------------------------------------------- 2561 */ 2562 2563 document 2564 .querySelectorAll( 2565 '[data-checkout-cart-discount]' 2566 ) 2567 .forEach(function (element) { 2568 2569 element.textContent = 2570 this.formatMoney( 2571 result.cart_discount || 0 2572 ); 2573 2574 }, this); 2575 2576 2577 /* 2578 |-------------------------------------------------------------------------- 2579 | COUPON DISCOUNT 2580 |-------------------------------------------------------------------------- 2581 */ 2582 2583 document 2584 .querySelectorAll( 2585 '[data-checkout-coupon-discount]' 2586 ) 2587 .forEach(function (element) { 2588 2589 element.textContent = 2590 this.formatMoney( 2591 result.coupon_discount_amount || 0 2592 ); 2593 2594 }, this); 2595 2596 2597 /* 2598 |-------------------------------------------------------------------------- 2599 | SHIPPING 2600 |-------------------------------------------------------------------------- 2601 */ 2602 2603 document 2604 .querySelectorAll( 2605 '[data-checkout-shipping]' 2606 ) 2607 .forEach(function (element) { 2608 2609 element.textContent = 2610 this.formatMoney( 2611 result.shipping_amount || 0 2612 ); 2613 2614 }, this); 2615 2616 2617 /* 2618 |-------------------------------------------------------------------------- 2619 | TAX 2620 |-------------------------------------------------------------------------- 2621 */ 2622 2623 document 2624 .querySelectorAll( 2625 '[data-checkout-tax]' 2626 ) 2627 .forEach(function (element) { 2628 2629 element.textContent = 2630 this.formatMoney( 2631 result.tax_amount || 0 2632 ); 2633 2634 }, this); 2635 2636 2637 /* 2638 |-------------------------------------------------------------------------- 2639 | FINAL TOTAL 2640 |-------------------------------------------------------------------------- 2641 */ 2642 2643 document 2644 .querySelectorAll( 2645 '[data-checkout-total]' 2646 ) 2647 .forEach(function (element) { 2648 2649 element.textContent = 2650 this.formatMoney( 2651 result.total_amount || 0 2652 ); 2653 2654 }, this); 2655 2656 2657 /* 2658 |--------------------------------------------------------------------------
2659 | GENERIC TOTAL 2660 |-------------------------------------------------------------------------- 2661 | 2662 | اگر Checkout ÙØ¹ÙÛ Ø§Ø² Ù٠ا٠selector ÙØ¨ÙÛ 2663 | Ø§Ø³ØªÙØ§Ø¯Ù Ù ÛâÚ©ÙØ¯. 2664 | 2665 */ 2666 2667 document 2668 .querySelectorAll( 2669 '[data-cart-total]' 2670 ) 2671 .forEach(function (element) { 2672 2673 element.textContent = 2674 this.formatMoney( 2675 result.total_amount || 0 2676 ); 2677 2678 }, this); 2679 }, 2680 2681 2682 /* 2683 |-------------------------------------------------------------------------- 2684 | SHOW APPLIED COUPON 2685 |-------------------------------------------------------------------------- 2686 */ 2687 2688 showAppliedCoupon: function ( 2689 coupon 2690 ) { 2691 2692 const box = 2693 document.getElementById( 2694 'shop_coupon_applied' 2695 ); 2696 2697 const title = 2698 document.getElementById( 2699 'shop_coupon_title' 2700 ); 2701 2702 const input = 2703 document.getElementById( 2704 'shop_coupon_code' 2705 ); 2706 2707 const applyButton = 2708 document.getElementById( 2709 'shop_coupon_apply' 2710 ); 2711 2712 2713 if (title) { 2714 2715 title.textContent = 2716 coupon && 2717 coupon.title 2718 ? coupon.title 2719 : ( 2720 coupon && 2721 coupon.code 2722 ? coupon.code 2723 : 'کد تخÙÛ٠اع٠ا٠شد.' 2724 ); 2725 } 2726 2727 2728 if (input) { 2729 input.value = 2730 coupon && 2731 coupon.code 2732 ? coupon.code 2733 : ''; 2734 input.disabled = true; 2735 } 2736 2737 2738 if (applyButton) { 2739 applyButton.hidden = true; 2740 } 2741 2742 2743 if (box) { 2744 box.hidden = false; 2745 } 2746 }, 2747 2748 2749 /* 2750 |-------------------------------------------------------------------------- 2751 | HIDE APPLIED COUPON 2752 |-------------------------------------------------------------------------- 2753 */ 2754 2755 hideAppliedCoupon: function () { 2756 2757 const box = 2758 document.getElementById( 2759 'shop_coupon_applied' 2760 ); 2761 2762 const input = 2763 document.getElementById( 2764 'shop_coupon_code' 2765 ); 2766 2767 const applyButton = 2768 document.getElementById( 2769 'shop_coupon_apply' 2770 ); 2771 2772 2773 if (input) { 2774 2775 input.value = ''; 2776 input.disabled = false; 2777 } 2778 2779 2780 if (applyButton) { 2781 applyButton.hidden = false; 2782 } 2783 2784 2785 if (box) { 2786 box.hidden = true; 2787 } 2788 }, 2789 2790 2791 /* 2792 |-------------------------------------------------------------------------- 2793 | APPLY COUPON 2794 |-------------------------------------------------------------------------- 2795 */ 2796 2797 applyCoupon: async function () { 2798 2799 const session = 2800 this.getCheckoutSession(); 2801 2802 if (!session) { 2803 2804 this.showCouponMessage( 2805 'Ø§Ø·ÙØ§Ø¹Ø§Øª Checkout Ù¾ÛØ¯Ø§ ÙØ´Ø¯. ÙØ·ÙØ§Ù ØµÙØÙ Ø±Ø§ Ø¯ÙØ¨Ø§Ø±Ù Ø¨Ø§Ø±Ú¯Ø°Ø§Ø±Û Ú©ÙÛØ¯.', 2806 'error' 2807 ); 2808 2809 return null; 2810 } 2811 2812 2813 const couponCode = 2814 this.getCouponCode(); 2815 2816 2817 if (!couponCode) { 2818 2819 this.showCouponMessage( 2820 'کد تخÙÛ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.', 2821 'error' 2822 ); 2823 2824 const input = 2825 document.getElementById( 2826 'shop_coupon_code' 2827 ); 2828 2829 if (input) { 2830 input.focus(); 2831 } 2832 2833 return null; 2834 } 2835 2836 2837 if ( 2838 this.setCouponLoading(true) === false 2839 ) { 2840 return null; 2841 } 2842 2843 2844 this.clearCouponMessage(); 2845 2846 2847 try { 2848 2849 const result = 2850 await this.requestToCheckout( 2851 { 2852 action: 'coupon_apply', 2853 2854 checkout_session_id: 2855 session.id, 2856 2857 checkout_session_token: 2858 session.token, 2859 2860 coupon_code: 2861 couponCode 2862 } 2863 ); 2864 2865 2866 /* 2867 |-------------------------------------------------------------------------- 2868 | UPDATE TOTALS 2869 |-------------------------------------------------------------------------- 2870 */ 2871 2872 this.handleCouponCheckout( 2873 result 2874 ); 2875 2876 2877 /* 2878 |-------------------------------------------------------------------------- 2879 | SHOW COUPON 2880 |-------------------------------------------------------------------------- 2881 */ 2882 2883 this.showAppliedCoupon( 2884 result.coupon || { 2885 code: couponCode 2886 } 2887 ); 2888 2889 2890 /* 2891 |-------------------------------------------------------------------------- 2892 | MESSAGE 2893 |-------------------------------------------------------------------------- 2894 */ 2895 2896 this.showCouponMessage( 2897 result.message || 2898 'کد تخÙÛ٠با Ù ÙÙÙÛØª اع٠ا٠شد.', 2899 'success' 2900 ); 2901 2902 2903 /* 2904 |-------------------------------------------------------------------------- 2905 | EVENT 2906 |-------------------------------------------------------------------------- 2907 */ 2908 2909 this.dispatchCartEvent( 2910 'loxshop:coupon-applied', 2911 result 2912 ); 2913 2914 2915 return result; 2916 2917 2918 } catch (error) { 2919 2920 console.error( 2921 'LOX SHOP COUPON APPLY ERROR:', 2922 error 2923 ); 2924 2925 2926 this.showCouponMessage( 2927 error.message || 2928 'اع٠ا٠کد تخÙÛÙ Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.', 2929 'error' 2930 ); 2931 2932 2933 return null; 2934 2935 2936 } finally { 2937 2938 this.setCouponLoading(false); 2939 } 2940 }, 2941 2942 2943 /* 2944 |-------------------------------------------------------------------------- 2945 | REMOVE COUPON 2946 |-------------------------------------------------------------------------- 2947 */ 2948 2949 removeCoupon: async function () { 2950 2951 const session = 2952 this.getCheckoutSession(); 2953 2954 2955 if (!session) { 2956 2957 this.showCouponMessage( 2958 'Ø§Ø·ÙØ§Ø¹Ø§Øª Checkout Ù¾ÛØ¯Ø§ ÙØ´Ø¯. ÙØ·ÙØ§Ù ØµÙØÙ Ø±Ø§ Ø¯ÙØ¨Ø§Ø±Ù Ø¨Ø§Ø±Ú¯Ø°Ø§Ø±Û Ú©ÙÛØ¯.', 2959 'error' 2960 ); 2961 2962 return null; 2963 } 2964 2965 2966 if ( 2967 this.setCouponLoading(true) === false 2968 ) { 2969 return null; 2970 } 2971 2972 2973 this.clearCouponMessage(); 2974 2975 2976 try { 2977 2978 const result = 2979 await this.requestToCheckout( 2980 { 2981 action: 'coupon_remove', 2982 2983 checkout_session_id: 2984 session.id, 2985 2986 checkout_session_token: 2987 session.token 2988 } 2989 ); 2990 2991 2992 /* 2993 |-------------------------------------------------------------------------- 2994 | UPDATE TOTALS 2995 |-------------------------------------------------------------------------- 2996 */ 2997 2998 this.handleCouponCheckout( 2999 result 3000 ); 3001 3002 3003 /* 3004 |-------------------------------------------------------------------------- 3005 | HIDE COUPON 3006 |-------------------------------------------------------------------------- 3007 */ 3008 3009 this.hideAppliedCoupon(); 3010 3011 3012 /* 3013 |-------------------------------------------------------------------------- 3014 | MESSAGE 3015 |-------------------------------------------------------------------------- 3016 */ 3017 3018 this.showCouponMessage( 3019 result.message || 3020 'کد تخÙÛÙ ØØ°Ù شد.', 3021 'success' 3022 ); 3023 3024 3025 /* 3026 |-------------------------------------------------------------------------- 3027 | EVENT 3028 |-------------------------------------------------------------------------- 3029 */ 3030 3031 this.dispatchCartEvent( 3032 'loxshop:coupon-removed', 3033 result 3034 ); 3035 3036 3037 return result; 3038 3039 3040 } catch (error) { 3041 3042 console.error( 3043 'LOX SHOP COUPON REMOVE ERROR:', 3044 error 3045 ); 3046 3047 3048 this.showCouponMessage( 3049 error.message || 3050 'ØØ°Ù کد تخÙÛÙ Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.', 3051 'error' 3052 ); 3053 3054 3055 return null; 3056 3057 3058 } finally { 3059 3060 this.setCouponLoading(false); 3061 } 3062 }, 3063 3064 3065 /* 3066 |-------------------------------------------------------------------------- 3067 | CHECKOUT REQUEST 3068 |-------------------------------------------------------------------------- 3069 | 3070 | coupon_apply / coupon_remove 3071 | ب٠/coreshop/ajax.php Ù ÛâØ±ÙÙØ¯. 3072 | 3073 */ 3074 3075 requestToCheckout: async function ( 3076 data 3077 ) { 3078 3079 let response; 3080 3081 3082 try { 3083 3084 response = 3085 await fetch( 3086 '/coreshop/ajax.php', 3087 { 3088 method: 'POST', 3089 3090 headers: { 3091 'Content-Type': 3092 'application/json', 3093 3094 'X-Requested-With': 3095 'XMLHttpRequest'
3096 }, 3097 3098 credentials: 3099 'same-origin', 3100 3101 body: 3102 JSON.stringify(data) 3103 } 3104 ); 3105 3106 3107 } catch (error) { 3108 3109 throw { 3110 success: false, 3111 error: 'network_error', 3112 message: 'ارتباط با Ø³Ø±ÙØ± Ø¨Ø±ÙØ±Ø§Ø± ÙØ´Ø¯.' 3113 }; 3114 } 3115 3116 3117 let result; 3118 3119 3120 try { 3121 3122 result = 3123 await response.json(); 3124 3125 } catch (error) { 3126 3127 throw { 3128 success: false, 3129 error: 'invalid_response', 3130 message: 'پاسخ Ø³Ø±ÙØ± ٠عتبر ÙÛØ³Øª.' 3131 }; 3132 } 3133 3134 3135 if ( 3136 !response.ok || 3137 !result || 3138 result.success !== true 3139 ) { 3140 3141 throw result || { 3142 success: false, 3143 error: 'server_error', 3144 message: 'خطاÛÛ Ø¯Ø± Ø³Ø±ÙØ± رخ داد٠است.' 3145 }; 3146 } 3147 3148 3149 return result; 3150 }, 3151 3152 3153 /* 3154 |-------------------------------------------------------------------------- 3155 | FORMAT MONEY 3156 |-------------------------------------------------------------------------- 3157 */ 3158 3159 formatMoney: function ( 3160 value 3161 ) { 3162 3163 value = 3164 Number( 3165 value || 0 3166 ); 3167 3168 3169 if (!isFinite(value)) { 3170 value = 0; 3171 } 3172 3173 3174 return value.toLocaleString( 3175 'en-US' 3176 ); 3177 }, 3178 3179 3180 /* 3181 |-------------------------------------------------------------------------- 3182 | DISPATCH EVENT 3183 |-------------------------------------------------------------------------- 3184 */ 3185 3186 dispatchCartEvent: function ( 3187 name, 3188 detail 3189 ) { 3190 3191 try { 3192 3193 document.dispatchEvent( 3194 new CustomEvent( 3195 name, 3196 { 3197 detail: detail 3198 } 3199 ) 3200 ); 3201 3202 } catch (error) { 3203 3204 /* 3205 |-------------------------------------------------------------------------- 3206 | Browser fallback 3207 |-------------------------------------------------------------------------- 3208 */ 3209 } 3210 } 3211 3212 }; 3213 3214 3215 /* 3216 |-------------------------------------------------------------------------- 3217 | APPLY COUPON BUTTON 3218 |-------------------------------------------------------------------------- 3219 */ 3220 3221 document.addEventListener( 3222 'click', 3223 function (event) { 3224 3225 const button = 3226 event.target.closest( 3227 '#shop_coupon_apply' 3228 ); 3229 3230 3231 if (!button) { 3232 return; 3233 } 3234 3235 3236 event.preventDefault(); 3237 3238 3239 ShopCart.applyCoupon(); 3240 } 3241 ); 3242 3243 3244 /* 3245 |-------------------------------------------------------------------------- 3246 | REMOVE COUPON BUTTON 3247 |-------------------------------------------------------------------------- 3248 */ 3249 3250 document.addEventListener( 3251 'click', 3252 function (event) { 3253 3254 const button = 3255 event.target.closest( 3256 '#shop_coupon_remove' 3257 ); 3258 3259 3260 if (!button) { 3261 return; 3262 } 3263 3264 3265 event.preventDefault(); 3266 3267 3268 ShopCart.removeCoupon(); 3269 } 3270 ); 3271 3272 3273 /* 3274 |--------------------------------------------------------------------------
3275 | ENTER KEY 3276 |-------------------------------------------------------------------------- 3277 | 3278 | کاربر Ù ÛâØªÙØ§Ùد کد را با Enter ÙÛØ² Ø§Ø¹Ù Ø§Ù Ú©ÙØ¯. 3279 | 3280 */ 3281 3282 document.addEventListener( 3283 'keydown', 3284 function (event) { 3285 3286 if ( 3287 event.key !== 'Enter' 3288 ) { 3289 return; 3290 } 3291 3292 3293 const input = 3294 event.target.closest( 3295 '#shop_coupon_code' 3296 ); 3297 3298 3299 if (!input) { 3300 return; 3301 } 3302 3303 3304 event.preventDefault(); 3305 3306 3307 ShopCart.applyCoupon(); 3308 } 3309 ); 3310 3311 3312 /* 3313 |-------------------------------------------------------------------------- 3314 | GLOBAL EXPORT 3315 |-------------------------------------------------------------------------- 3316 */ 3317 3318 window.LoxShopCart = 3319 ShopCart; 3320 3321 3322})(window, document); 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369(function () { 3370 3371 'use strict'; 3372 3373 3374 /* 3375 |-------------------------------------------------------------------------- 3376 | CONFIG 3377 |-------------------------------------------------------------------------- 3378 */ 3379 3380 var OTP_SECONDS = 900; 3381 3382 var AJAX_URL = 3383 '/shoporder/customer_auth.php'; 3384 3385 3386 /* 3387 |-------------------------------------------------------------------------- 3388 | ROOT 3389 |-------------------------------------------------------------------------- 3390 */ 3391 3392 var authWrapper = 3393 document.getElementById( 3394 'shopAuth' 3395 ); 3396 3397 if (!authWrapper) { 3398 return; 3399 } 3400 3401 3402 var WID = 3403 parseInt( 3404 authWrapper.getAttribute('data-wid'), 3405 10 3406 ) || 0; 3407 3408 3409 /* 3410 |-------------------------------------------------------------------------- 3411 | ELEMENTS 3412 |-------------------------------------------------------------------------- 3413 */ 3414 3415 var mobileStep = 3416 document.getElementById( 3417 'shopAuthMobileStep' 3418 ); 3419 3420 var otpStep = 3421 document.getElementById( 3422 'shopAuthOtpStep' 3423 ); 3424 3425 var mobileForm = 3426 document.getElementById( 3427 'shopAuthMobileForm' 3428 ); 3429 3430 var otpForm = 3431 document.getElementById( 3432 'shopAuthOtpForm' 3433 ); 3434 3435 var mobileInput = 3436 document.getElementById( 3437 'shopAuthMobile' 3438 ); 3439 3440 var otpInput = 3441 document.getElementById( 3442 'shopAuthOtp' 3443 ); 3444 3445 var mobilePreview = 3446 document.getElementById( 3447 'shopAuthMobilePreview' 3448 ); 3449 3450 var sendButton = 3451 document.getElementById( 3452 'shopAuthSendButton' 3453 ); 3454 3455 var verifyButton = 3456 document.getElementById( 3457 'shopAuthVerifyButton' 3458 ); 3459 3460 var resendButton = 3461 document.getElementById( 3462 'shopAuthResendButton' 3463 ); 3464 3465 var changeMobileButton = 3466 document.getElementById( 3467 'shopAuthChangeMobile' 3468 ); 3469 3470 var mobileError = 3471 document.getElementById( 3472 'shopAuthMobileError' 3473 ); 3474 3475 var otpError = 3476 document.getElementById( 3477 'shopAuthOtpError' 3478 ); 3479 3480 var timer = 3481 document.getElementById( 3482 'shopAuthTimer' 3483 ); 3484 3485 var timerWrapper = 3486 document.getElementById( 3487 'shopAuthTimerWrapper' 3488 ); 3489 3490 var sendText = 3491 document.querySelector( 3492 '.shop-auth-send-text' 3493 ); 3494 3495 var sendLoading = 3496 document.querySelector( 3497 '.shop-auth-send-loading' 3498 ); 3499 3500 var verifyText = 3501 document.querySelector( 3502 '.shop-auth-verify-text' 3503 ); 3504 3505 var verifyLoading = 3506 document.querySelector( 3507 '.shop-auth-verify-loading' 3508 ); 3509 3510 3511 /* 3512 |-------------------------------------------------------------------------- 3513 | STATE 3514 |-------------------------------------------------------------------------- 3515 */ 3516 3517 var currentMobile = ''; 3518 3519 var timerInterval = null; 3520 3521 var remainingSeconds = 0; 3522 3523 3524 /* 3525 |-------------------------------------------------------------------------- 3526 | NORMALIZE MOBILE 3527 |-------------------------------------------------------------------------- 3528 */ 3529 3530 function normalizeMobile(value) { 3531
3532 value = String(value || ''); 3533 3534 3535 value = value.replace( 3536 /[۰-۹]/g, 3537 function (char) { 3538 3539 return String( 3540 '۰۱۲۳۴۵۶۷۸۹'.indexOf(char) 3541 ); 3542 3543 } 3544 ); 3545 3546 3547 value = value.replace( 3548 /[٠-٩]/g, 3549 function (char) { 3550 3551 return String( 3552 '٠١٢٣٤٥٦٧٨٩'.indexOf(char) 3553 ); 3554 3555 } 3556 ); 3557 3558 3559 value = value.replace( 3560 /[^0-9+]/g, 3561 '' 3562 ); 3563 3564 3565 if ( 3566 value.indexOf('+98') === 0 3567 ) { 3568 3569 value = 3570 '0' + 3571 value.substring(3); 3572 3573 } 3574 else if ( 3575 value.indexOf('98') === 0 3576 ) { 3577 3578 value = 3579 '0' + 3580 value.substring(2); 3581 3582 } 3583 else if ( 3584 value.length === 10 3585 && 3586 value.charAt(0) === '9' 3587 ) { 3588 3589 value = 3590 '0' + 3591 value; 3592 3593 } 3594 3595 3596 return value; 3597 3598 } 3599 3600 3601 /* 3602 |-------------------------------------------------------------------------- 3603 | VALIDATE MOBILE 3604 |-------------------------------------------------------------------------- 3605 */ 3606 3607 function isValidMobile(mobile) { 3608 3609 return /^09[0-9]{9}$/.test( 3610 mobile 3611 ); 3612 3613 } 3614 3615 3616 /* 3617 |-------------------------------------------------------------------------- 3618 | ERROR 3619 |-------------------------------------------------------------------------- 3620 */ 3621 3622 function showError( 3623 element, 3624 message 3625 ) { 3626 3627 element.textContent = 3628 message || ''; 3629 3630 element.classList.remove( 3631 'd-none' 3632 ); 3633 3634 } 3635 3636 3637 function hideError(element) { 3638 3639 element.textContent = ''; 3640 3641 element.classList.add( 3642 'd-none' 3643 ); 3644 3645 } 3646 3647 3648 /* 3649 |-------------------------------------------------------------------------- 3650 | SEND LOADING 3651 |-------------------------------------------------------------------------- 3652 */ 3653 3654 function setSendLoading(loading) { 3655 3656 sendButton.disabled = 3657 loading; 3658 3659 if (loading) { 3660 3661 sendText.classList.add( 3662 'd-none' 3663 ); 3664 3665 sendLoading.classList.remove( 3666 'd-none' 3667 ); 3668 3669 } 3670 else { 3671 3672 sendText.classList.remove( 3673 'd-none' 3674 ); 3675 3676 sendLoading.classList.add( 3677 'd-none' 3678 ); 3679 3680 } 3681 3682 } 3683 3684 3685 /* 3686 |-------------------------------------------------------------------------- 3687 | VERIFY LOADING 3688 |-------------------------------------------------------------------------- 3689 */ 3690 3691 function setVerifyLoading(loading) { 3692 3693 verifyButton.disabled = 3694 loading; 3695 3696 if (loading) { 3697 3698 verifyText.classList.add( 3699 'd-none' 3700 ); 3701 3702 verifyLoading.classList.remove( 3703 'd-none' 3704 ); 3705 3706 } 3707 else { 3708 3709 verifyText.classList.remove( 3710 'd-none' 3711 ); 3712 3713 verifyLoading.classList.add( 3714 'd-none' 3715 ); 3716 3717 } 3718 3719 } 3720 3721 3722 /* 3723 |-------------------------------------------------------------------------- 3724 | TIMER 3725 |-------------------------------------------------------------------------- 3726 */ 3727 3728 function formatTime(seconds) { 3729 3730 var minutes = 3731 Math.floor( 3732 seconds / 60 3733 ); 3734 3735 var secs = 3736 seconds % 60; 3737 3738 3739 return ( 3740 String(minutes).padStart(2, '0') 3741 + 3742 ':' 3743 + 3744 String(secs).padStart(2, '0') 3745 ); 3746 3747 } 3748 3749 3750 function stopTimer() { 3751 3752 if ( 3753 timerInterval !== null 3754 ) { 3755 3756 clearInterval( 3757 timerInterval 3758 ); 3759 3760 timerInterval = null; 3761 3762 } 3763 3764 } 3765 3766 3767 function startTimer(seconds) { 3768 3769 stopTimer(); 3770 3771 3772 remainingSeconds = 3773 parseInt( 3774 seconds, 3775 10 3776 ) || OTP_SECONDS; 3777 3778 3779 timer.textContent = 3780 formatTime( 3781 remainingSeconds 3782 ); 3783 3784 3785 timerWrapper.classList.remove( 3786 'd-none' 3787 ); 3788 3789 resendButton.classList.add( 3790 'd-none' 3791 ); 3792 3793 3794 timerInterval = 3795 setInterval( 3796 function () { 3797 3798 remainingSeconds--; 3799 3800 3801 if ( 3802 remainingSeconds <= 0 3803 ) { 3804 3805 remainingSeconds = 0; 3806 3807 timer.textContent = 3808 '00:00'; 3809 3810 stopTimer(); 3811 3812 3813 timerWrapper.classList.add( 3814 'd-none' 3815 ); 3816 3817 resendButton.classList.remove( 3818 'd-none' 3819 ); 3820 3821 return; 3822 3823 } 3824 3825 3826 timer.textContent = 3827 formatTime( 3828 remainingSeconds 3829 ); 3830 3831 }, 3832 1000 3833 ); 3834 3835 } 3836 3837 3838 /* 3839 |-------------------------------------------------------------------------- 3840 | SHOW OTP 3841 |-------------------------------------------------------------------------- 3842 */ 3843 3844 function showOtpStep() { 3845 3846 mobileStep.classList.add( 3847 'd-none' 3848 ); 3849 3850 otpStep.classList.remove( 3851 'd-none' 3852 ); 3853 3854 3855 mobilePreview.textContent = 3856 currentMobile; 3857 3858 3859 otpInput.value = ''; 3860 3861 3862 setTimeout( 3863 function () { 3864 3865 otpInput.focus(); 3866 3867 }, 3868 100 3869 ); 3870 3871 } 3872 3873 3874 /* 3875 |-------------------------------------------------------------------------- 3876 | SHOW MOBILE 3877 |-------------------------------------------------------------------------- 3878 */ 3879 3880 function showMobileStep() { 3881 3882 stopTimer(); 3883 3884 3885 otpStep.classList.add( 3886 'd-none' 3887 ); 3888 3889 mobileStep.classList.remove( 3890 'd-none' 3891 ); 3892 3893 3894 otpInput.value = ''; 3895 3896 hideError( 3897 otpError 3898 ); 3899 3900 mobileInput.focus(); 3901 3902 } 3903 3904 3905 /* 3906 |-------------------------------------------------------------------------- 3907 | SEND OTP
3908 |-------------------------------------------------------------------------- 3909 */ 3910 3911 function sendOtp() { 3912 3913 var mobile = 3914 normalizeMobile( 3915 mobileInput.value 3916 ); 3917 3918 3919 mobileInput.value = 3920 mobile; 3921 3922 3923 hideError( 3924 mobileError 3925 ); 3926 3927 3928 3929 3930 3931 if ( 3932 !isValidMobile(mobile) 3933 ) { 3934 3935 showError( 3936 mobileError, 3937 'ÙØ·ÙØ§Ù Ø´Ù Ø§Ø±Ù Ù ÙØ¨Ø§Û٠٠عتبر ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.' 3938 ); 3939 3940 mobileInput.focus(); 3941 3942 return; 3943 3944 } 3945 3946 3947 currentMobile = 3948 mobile; 3949 3950 3951 setSendLoading(true); 3952 3953 3954 var formData = 3955 new FormData(); 3956 3957 3958 /* 3959 * Ù ÙÙ : 3960 * PHP ÙÙ action را Ù ÛâØ®ÙØ§Ùد. 3961 */ 3962 3963 formData.append( 3964 'action', 3965 'send_code' 3966 ); 3967 3968 3969 formData.append( 3970 'wid', 3971 String(WID) 3972 ); 3973 3974 3975 formData.append( 3976 'mobile', 3977 mobile 3978 ); 3979 3980 3981 fetch( 3982 AJAX_URL, 3983 { 3984 method: 'POST', 3985 body: formData, 3986 credentials: 'same-origin' 3987 } 3988 ) 3989 .then( 3990 function (response) { 3991 3992 if (!response.ok) { 3993 3994 throw new Error( 3995 'HTTP ' + 3996 response.status 3997 ); 3998 3999 } 4000 4001 return response.json(); 4002 4003 } 4004 ) 4005 .then( 4006 function (result) { 4007 4008 setSendLoading(false); 4009 4010 4011 /* 4012 * ÙÙØ· ok Ù already_sent 4013 * Ø§Ø¬Ø§Ø²Ù ÙØ±Ùد ب٠٠رØÙÙ OTP Ø¯Ø§Ø±ÙØ¯. 4014 */ 4015 4016 if ( 4017 !result 4018 || 4019 ( 4020 result.status !== 'ok' 4021 && 4022 result.status !== 'already_sent' 4023 ) 4024 ) { 4025 4026 showError( 4027 mobileError, 4028 result 4029 && result.message 4030 ? result.message 4031 : 'ارسا٠کد تأÛÛØ¯ Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 4032 ); 4033 4034 return; 4035 4036 } 4037 4038 4039 showOtpStep(); 4040 4041 4042 startTimer( 4043 result.expires_in 4044 || OTP_SECONDS 4045 ); 4046 4047 } 4048 ) 4049 .catch( 4050 function (error) { 4051 4052 console.error( 4053 'SHOP AUTH SEND ERROR:', 4054 error 4055 ); 4056 4057 4058 setSendLoading(false); 4059 4060 4061 showError( 4062 mobileError, 4063 'ارتباط با Ø³Ø±ÙØ± Ø¨Ø±ÙØ±Ø§Ø± ÙØ´Ø¯. Ø¯ÙØ¨Ø§Ø±Ù ØªÙØ§Ø´ Ú©ÙÛØ¯.' 4064 ); 4065 4066 } 4067 ); 4068 4069 } 4070 4071 4072 /* 4073 |-------------------------------------------------------------------------- 4074 | VERIFY OTP 4075 |-------------------------------------------------------------------------- 4076 */ 4077 4078 function verifyOtp() { 4079 4080 var code = 4081 otpInput.value 4082 .replace( 4083 /[^0-9]/g, 4084 '' 4085 ); 4086 4087 4088 otpInput.value = 4089 code; 4090 4091 4092 hideError( 4093 otpError 4094 ); 4095 4096 4097 4098 4099 4100 if ( 4101 !currentMobile 4102 ) { 4103 4104 showError( 4105 otpError, 4106 'Ø´Ù Ø§Ø±Ù Ù ÙØ¨Ø§Û٠٠شخص ÙÛØ³Øª.' 4107 ); 4108 4109 return; 4110 4111 } 4112 4113 4114 if ( 4115 code.length !== 4 4116 ) { 4117 4118 showError( 4119 otpError, 4120 'کد تأÛÛØ¯ Ø¨Ø§ÛØ¯ Û¶ رÙÙ Û Ø¨Ø§Ø´Ø¯.' 4121 ); 4122 4123 otpInput.focus(); 4124 4125 return; 4126 4127 } 4128 4129 4130 setVerifyLoading(true); 4131 4132 4133 var formData = 4134 new FormData(); 4135 4136 4137 formData.append( 4138 'action', 4139 'verify_code' 4140 ); 4141 4142 4143 formData.append( 4144 'wid', 4145 String(WID) 4146 ); 4147 4148 4149 formData.append( 4150 'mobile', 4151 currentMobile 4152 ); 4153 4154 4155 formData.append( 4156 'code', 4157 code 4158 ); 4159 4160 4161 fetch( 4162 AJAX_URL, 4163 { 4164 method: 'POST', 4165 body: formData, 4166 credentials: 'same-origin' 4167 } 4168 ) 4169 .then( 4170 function (response) { 4171 4172 if (!response.ok) { 4173 4174 throw new Error( 4175 'HTTP ' + 4176 response.status 4177 ); 4178 4179 } 4180 4181 return response.json(); 4182 4183 } 4184 ) 4185 .then( 4186 function (result) { 4187 4188 setVerifyLoading(false); 4189 4190 4191 if ( 4192 !result
4193 || 4194 result.status !== 'ok' 4195 ) { 4196 4197 showError( 4198 otpError, 4199 result 4200 && result.message 4201 ? result.message 4202 : 'کد تأÛÛØ¯ صØÛØ ÙÛØ³Øª.' 4203 ); 4204 4205 return; 4206 4207 } 4208 4209 4210 /* 4211 * Login/Register Ù ÙÙÙ 4212 */ 4213 4214 stopTimer(); 4215 4216 4217 if ( 4218 result.redirect 4219 ) { 4220 4221 window.location.href = 4222 result.redirect; 4223 4224 return; 4225 4226 } 4227 4228 4229 /* 4230 * در ØØ§Ùت Ø¹Ø§Ø¯Û ØµÙØÙ Reload Ù ÛâØ´ÙØ¯ 4231 * Ù Session Ø¬Ø¯ÛØ¯ Customer را Ù ÛâØ¨ÛÙØ¯. 4232 */ 4233 4234 window.location.reload(); 4235 4236 } 4237 ) 4238 .catch( 4239 function (error) { 4240 4241 console.error( 4242 'SHOP AUTH VERIFY ERROR:', 4243 error 4244 ); 4245 4246 4247 setVerifyLoading(false); 4248 4249 4250 showError( 4251 otpError, 4252 'ارتباط با Ø³Ø±ÙØ± Ø¨Ø±ÙØ±Ø§Ø± ÙØ´Ø¯. Ø¯ÙØ¨Ø§Ø±Ù ØªÙØ§Ø´ Ú©ÙÛØ¯.' 4253 ); 4254 4255 } 4256 ); 4257 4258 } 4259 4260 4261 /* 4262 |-------------------------------------------------------------------------- 4263 | RESEND 4264 |-------------------------------------------------------------------------- 4265 */ 4266 4267 function resendOtp() { 4268 4269 if ( 4270 !currentMobile 4271 ) { 4272 4273 showMobileStep(); 4274 4275 return; 4276 4277 } 4278 4279 4280 mobileInput.value = 4281 currentMobile; 4282 4283 4284 sendOtp(); 4285 4286 } 4287 4288 4289 /* 4290 |-------------------------------------------------------------------------- 4291 | CHANGE MOBILE 4292 |-------------------------------------------------------------------------- 4293 */ 4294 4295 changeMobileButton.addEventListener( 4296 'click', 4297 function () { 4298 4299 currentMobile = ''; 4300 4301 showMobileStep(); 4302 4303 } 4304 ); 4305 4306 4307 /* 4308 |-------------------------------------------------------------------------- 4309 | FORMS 4310 |-------------------------------------------------------------------------- 4311 */ 4312 4313 mobileForm.addEventListener( 4314 'submit', 4315 function (event) { 4316 4317 event.preventDefault(); 4318 4319 sendOtp(); 4320 4321 } 4322 ); 4323 4324 4325 otpForm.addEventListener( 4326 'submit', 4327 function (event) { 4328 4329 event.preventDefault(); 4330 4331 verifyOtp(); 4332 4333 } 4334 ); 4335 4336 4337 /* 4338 |-------------------------------------------------------------------------- 4339 | RESEND 4340 |-------------------------------------------------------------------------- 4341 */ 4342 4343 resendButton.addEventListener( 4344 'click', 4345 function () { 4346 4347 resendButton.disabled = 4348 true; 4349 4350 4351 resendOtp(); 4352 4353 4354 setTimeout( 4355 function () { 4356 4357 resendButton.disabled = 4358 false; 4359 4360 }, 4361 1000 4362 ); 4363 4364 } 4365 ); 4366 4367 4368 /* 4369 |-------------------------------------------------------------------------- 4370 | OTP INPUT 4371 |-------------------------------------------------------------------------- 4372 */ 4373 4374 otpInput.addEventListener( 4375 'input', 4376 function () { 4377 4378 this.value = 4379 this.value 4380 .replace( 4381 /[Û°-Û¹]/g, 4382 function (char) { 4383 4384 return String( 4385 'Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹' 4386 .indexOf(char) 4387 ); 4388 4389 } 4390 ) 4391 .replace( 4392 /[Ù -Ù©]/g, 4393 function (char) { 4394 4395 return String( 4396 '٠١٢٣٤٥٦٧٨٩' 4397 .indexOf(char) 4398 ); 4399 4400 } 4401 ) 4402 .replace( 4403 /[^0-9]/g, 4404 '' 4405 ) 4406 .substring( 4407 0, 4408 6 4409 ); 4410 4411 } 4412 ); 4413 4414 4415 /* 4416 |-------------------------------------------------------------------------- 4417 | MOBILE INPUT 4418 |-------------------------------------------------------------------------- 4419 */ 4420 4421 mobileInput.addEventListener( 4422 'input', 4423 function () { 4424 4425 var value = 4426 normalizeMobile( 4427 this.value 4428 ); 4429 4430 4431 this.value = 4432 value.substring( 4433 0, 4434 11 4435 ); 4436 4437 } 4438 ); 4439 4440 4441})(); 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481(function (window, document) { 4482 'use strict'; 4483 /* 4484 |-------------------------------------------------------------------------- 4485 | LOXBLOG SHOP CART 4486 |-------------------------------------------------------------------------- 4487 */ 4488 const ShopCart = { 4489 endpoint: '/shoporder/checkorder.php', 4490 /* 4491 |-------------------------------------------------------------------------- 4492 | AJAX REQUEST 4493 |-------------------------------------------------------------------------- 4494 */ 4495 request: async function (data) { 4496 let response; 4497 try { 4498 response = await fetch( 4499 this.endpoint, 4500 { 4501 method: 'POST', 4502 headers: { 4503 'Content-Type': 'application/json', 4504 'X-Requested-With': 'XMLHttpRequest'
4505 }, 4506 credentials: 'same-origin', 4507 body: JSON.stringify(data) 4508 } 4509 ); 4510 } catch (error) { 4511 throw { 4512 success: false, 4513 error: 'network_error', 4514 message: 'ارتباط با Ø³Ø±ÙØ± Ø¨Ø±ÙØ±Ø§Ø± ÙØ´Ø¯.' 4515 }; 4516 } 4517 /* 4518 |-------------------------------------------------------------------------- 4519 | READ JSON 4520 |-------------------------------------------------------------------------- 4521 */ 4522 let result; 4523 try { 4524 result = await response.json(); 4525 } catch (error) { 4526 throw { 4527 success: false, 4528 error: 'invalid_response', 4529 message: 'پاسخ Ø³Ø±ÙØ± ٠عتبر ÙÛØ³Øª.' 4530 }; 4531 } 4532 /* 4533 |-------------------------------------------------------------------------- 4534 | PHP ERROR 4535 |-------------------------------------------------------------------------- 4536 */ 4537 if ( 4538 !response.ok || 4539 !result || 4540 result.success !== true 4541 ) { 4542 throw result || { 4543 success: false, 4544 error: 'server_error', 4545 message: 'خطاÛÛ Ø¯Ø± Ø³Ø±ÙØ± رخ داد٠است.' 4546 }; 4547 } 4548 return result; 4549 }, 4550 /* 4551 |-------------------------------------------------------------------------- 4552 | GET FORM 4553 |-------------------------------------------------------------------------- 4554 */ 4555 getForm: function (element) { 4556 if (!element) { 4557 return null; 4558 } 4559 /* 4560 | Ø®ÙØ¯ ÙØ±Ù 4561 */ 4562 if ( 4563 element.tagName && 4564 element.tagName.toLowerCase() === 'form' 4565 ) { 4566 return element; 4567 } 4568 /* 4569 | دک٠٠/ input / element 4570 */ 4571 if (element.closest) { 4572 const form = element.closest( 4573 'form.product-cart-form, form[data-shop-cart-form], form' 4574 ); 4575 if (form) { 4576 return form; 4577 } 4578 } 4579 return null; 4580 }, 4581 /* 4582 |-------------------------------------------------------------------------- 4583 | GET PRODUCT ID 4584 |-------------------------------------------------------------------------- 4585 */ 4586 getProductId: function (form) { 4587 if (!form) { 4588 return 0; 4589 } 4590 /* 4591 | input[name="product_id"] 4592 */ 4593 const input = form.querySelector( 4594 'input[name="product_id"]' 4595 ); 4596 if (input) { 4597 const productId = parseInt( 4598 input.value || 0, 4599 10 4600 ); 4601 if (productId > 0) { 4602 return productId; 4603 } 4604 } 4605 /* 4606 | data-product-id 4607 */ 4608 const dataId = parseInt( 4609 form.getAttribute('data-product-id') || 0, 4610 10 4611 ); 4612 if (dataId > 0) { 4613 return dataId; 4614 } 4615 return 0; 4616 }, 4617 /* 4618 |-------------------------------------------------------------------------- 4619 | GET SELECT VALUE 4620 |-------------------------------------------------------------------------- 4621 */ 4622 getSelectValue: function (element) { 4623 if (!element) { 4624 return null; 4625 } 4626 const value = element.value; 4627 if ( 4628 value === null || 4629 value === '' || 4630 value === '0' 4631 ) { 4632 return null; 4633 } 4634 return value; 4635 }, 4636 /* 4637 |-------------------------------------------------------------------------- 4638 | COLLECT ATTRIBUTES 4639 |-------------------------------------------------------------------------- 4640 | 4641 | Ø®Ø±ÙØ¬Û: 4642 | 4643 | { 4644 | "1": "73", 4645 | "2": "91" 4646 | } 4647 | 4648 |-------------------------------------------------------------------------- 4649 */ 4650 collectAttributes: function (form) { 4651 const attributes = {}; 4652 if (!form) { 4653 return attributes; 4654 } 4655 /* 4656 |-------------------------------------------------------------------------- 4657 | SELECT 4658 |-------------------------------------------------------------------------- 4659 */ 4660 const selects = form.querySelectorAll( 4661 'select.product_var' 4662 );
4663 selects.forEach(function (select) { 4664 const key = 4665 select.getAttribute('key'); 4666 if (!key) { 4667 return; 4668 } 4669 const value = 4670 ShopCart.getSelectValue(select); 4671 if (value !== null) { 4672 attributes[key] = value; 4673 } 4674 }); 4675 /* 4676 |-------------------------------------------------------------------------- 4677 | RADIO 4678 |-------------------------------------------------------------------------- 4679 */ 4680 const radios = form.querySelectorAll( 4681 'input.product_var[type="radio"]' 4682 ); 4683 const radioKeys = {}; 4684 radios.forEach(function (radio) { 4685 const key = 4686 radio.getAttribute('key'); 4687 if (!key) { 4688 return; 4689 } 4690 if (radioKeys[key]) { 4691 return; 4692 } 4693 radioKeys[key] = true; 4694 const checked = 4695 form.querySelector( 4696 'input.product_var[type="radio"][key="' + 4697 CSS.escape(key) + 4698 '"]:checked' 4699 ); 4700 if ( 4701 checked && 4702 checked.value !== '' && 4703 checked.value !== '0' 4704 ) { 4705 attributes[key] = 4706 checked.value; 4707 } 4708 }); 4709 /* 4710 |-------------------------------------------------------------------------- 4711 | CHECKBOX 4712 |-------------------------------------------------------------------------- 4713 | 4714 | PHP ÙØ¹ÙØ§Ù Ø¨Ø±Ø§Û ÙØ± attribute ÙÙØ· ÛÚ© value 4715 | را Ù¾Ø´ØªÛØ¨Ø§ÙÛ Ù ÛâÚ©ÙØ¯. 4716 | 4717 |-------------------------------------------------------------------------- 4718 */ 4719 const checkboxes = form.querySelectorAll( 4720 'input.product_var[type="checkbox"]' 4721 ); 4722 const checkboxKeys = {}; 4723 checkboxes.forEach(function (checkbox) { 4724 const key = 4725 checkbox.getAttribute('key'); 4726 if (!key) { 4727 return; 4728 } 4729 if (checkboxKeys[key]) { 4730 return; 4731 } 4732 checkboxKeys[key] = true; 4733 const checked = 4734 form.querySelector( 4735 'input.product_var[type="checkbox"][key="' + 4736 CSS.escape(key) + 4737 '"]:checked' 4738 ); 4739 if ( 4740 checked && 4741 checked.value !== '' && 4742 checked.value !== '0' 4743 ) { 4744 attributes[key] = 4745 checked.value; 4746 } 4747 }); 4748 return attributes; 4749 }, 4750 /* 4751 |-------------------------------------------------------------------------- 4752 | GET QUANTITY 4753 |-------------------------------------------------------------------------- 4754 */ 4755 getQuantity: function (form) { 4756 if (!form) { 4757 return 1; 4758 } 4759 const input = 4760 form.querySelector( 4761 'input[name="product_number"]' 4762 ); 4763 if (!input) { 4764 return 1; 4765 } 4766 let quantity = 4767 parseFloat(input.value); 4768 if ( 4769 !isFinite(quantity) || 4770 quantity <= 0 4771 ) { 4772 quantity = 1; 4773 } 4774 /* 4775 | PHP maximum 4776 */ 4777 if (quantity > 999999) { 4778 quantity = 999999; 4779 } 4780 return quantity; 4781 }, 4782 /* 4783 |-------------------------------------------------------------------------- 4784 | VALIDATE PRODUCT 4785 |-------------------------------------------------------------------------- 4786 */ 4787 validateProduct: async function (element) { 4788 const form = 4789 this.getForm(element); 4790 if (!form) { 4791 throw { 4792 success: false,
4793 error: 'invalid_form', 4794 message: 'ÙØ±Ù Ù ØØµÙÙ Ù¾ÛØ¯Ø§ ÙØ´Ø¯.' 4795 }; 4796 } 4797 const productId = 4798 this.getProductId(form); 4799 if (!productId) { 4800 throw { 4801 success: false, 4802 error: 'invalid_product', 4803 message: 'Ø´ÙØ§Ø³Ù Ù ØØµÙ٠٠شخص ÙÛØ³Øª.' 4804 }; 4805 } 4806 const attributes = 4807 this.collectAttributes(form); 4808 return await this.request({ 4809 action: 'validate_product', 4810 product_id: productId, 4811 attributes: attributes 4812 }); 4813 }, 4814 /* 4815 |-------------------------------------------------------------------------- 4816 | ADD TO CART 4817 |-------------------------------------------------------------------------- 4818 */ 4819 addToCart: async function (element) { 4820 let form = null; 4821 let button = null; 4822 /* 4823 |-------------------------------------------------------------------------- 4824 | EVENT 4825 |-------------------------------------------------------------------------- 4826 */ 4827 if ( 4828 element && 4829 typeof element.preventDefault === 'function' 4830 ) { 4831 element.preventDefault(); 4832 element = 4833 element.currentTarget || 4834 element.target; 4835 } 4836 /* 4837 |-------------------------------------------------------------------------- 4838 | BUTTON 4839 |-------------------------------------------------------------------------- 4840 */ 4841 if ( 4842 element && 4843 element.tagName && 4844 element.tagName.toLowerCase() === 'button' 4845 ) { 4846 button = element; 4847 form = 4848 this.getForm(element); 4849 } 4850 /* 4851 |-------------------------------------------------------------------------- 4852 | FORM 4853 |-------------------------------------------------------------------------- 4854 */ 4855 else if ( 4856 element && 4857 element.tagName && 4858 element.tagName.toLowerCase() === 'form' 4859 ) { 4860 form = element; 4861 button = 4862 form.querySelector( 4863 'button[type="submit"]' 4864 ); 4865 } 4866 /* 4867 |-------------------------------------------------------------------------- 4868 | FALLBACK 4869 |-------------------------------------------------------------------------- 4870 */ 4871 if (!form) { 4872 form = 4873 document.querySelector( 4874 'form.product-cart-form, form[data-shop-cart-form]' 4875 ); 4876 } 4877 if (!form) { 4878 this.showError( 4879 'ÙØ±Ù Ù ØØµÙÙ Ù¾ÛØ¯Ø§ ÙØ´Ø¯.' 4880 ); 4881 return false; 4882 } 4883 /* 4884 |-------------------------------------------------------------------------- 4885 | BUTTON 4886 |-------------------------------------------------------------------------- 4887 */ 4888 if (!button) { 4889 button = 4890 form.querySelector( 4891 'button[type="submit"]' 4892 ); 4893 } 4894 /* 4895 |-------------------------------------------------------------------------- 4896 | PRODUCT ID 4897 |-------------------------------------------------------------------------- 4898 */ 4899 const productId = 4900 this.getProductId(form); 4901 if (!productId) { 4902 this.showError( 4903 'Ø´ÙØ§Ø³Ù Ù ØØµÙ٠٠شخص ÙÛØ³Øª.' 4904 ); 4905 return false; 4906 } 4907 /* 4908 |-------------------------------------------------------------------------- 4909 | ATTRIBUTES 4910 |-------------------------------------------------------------------------- 4911 */ 4912 const attributes = 4913 this.collectAttributes(form); 4914 /* 4915 |-------------------------------------------------------------------------- 4916 | QUANTITY 4917 |-------------------------------------------------------------------------- 4918 */ 4919 const quantity = 4920 this.getQuantity(form); 4921 /* 4922 |-------------------------------------------------------------------------- 4923 | PREVENT DOUBLE CLICK 4924 |-------------------------------------------------------------------------- 4925 */ 4926 if (button) { 4927 if ( 4928 button.dataset.cartLoading === '1' 4929 ) { 4930 return false; 4931 } 4932 button.dataset.cartLoading = '1'; 4933 button.disabled = true; 4934 } 4935 try { 4936 /* 4937 |-------------------------------------------------------------------------- 4938 | REQUEST 4939 |-------------------------------------------------------------------------- 4940 */ 4941 const result = 4942 await this.request({ 4943 action: 'add_to_cart', 4944 product_id: productId, 4945 quantity: quantity, 4946 attributes: attributes 4947 }); 4948 /* 4949 |-------------------------------------------------------------------------- 4950 | UPDATE CART UI 4951 |-------------------------------------------------------------------------- 4952 */ 4953 this.handleCartResponse(result); 4954 /* 4955 |-------------------------------------------------------------------------- 4956 | SUCCESS 4957 |-------------------------------------------------------------------------- 4958 */ 4959 4960 this.showSuccess( 4961 (result.message || 4962 'Ù ØØµÙ٠با Ù ÙÙÙÛØª ب٠سبد Ø®Ø±ÛØ¯ اضاÙ٠شد.') + 4963 ' <a href="cart/">Ù Ø´Ø§ÙØ¯Ù سبد Ø®Ø±ÛØ¯</a>' 4964 ); 4965 const add2cartMessage = 4966 document.getElementById('add2cart-message'); 4967 4968 if (add2cartMessage) { 4969 add2cartMessage.classList.add('show'); 4970 } 4971 4972 /* 4973 |-------------------------------------------------------------------------- 4974 | CALLBACK 4975 |-------------------------------------------------------------------------- 4976 */ 4977 this.dispatchCartEvent( 4978 'loxshop:cart-added', 4979 result 4980 ); 4981 return true; 4982 } catch (error) { 4983 console.error( 4984 'LOX SHOP CART ERROR:', 4985 error 4986 ); 4987 this.showError( 4988 error.message || 4989 'خطاÛÛ Ø¯Ø± Ø§ÙØ²ÙØ¯Ù Ù ØØµÙ٠ب٠سبد رخ داد.' 4990 ); 4991 return false; 4992 } finally { 4993 if (button) { 4994 button.dataset.cartLoading = '0'; 4995 button.disabled = false; 4996 } 4997 } 4998 }, 4999 /* 5000 |-------------------------------------------------------------------------- 5001 | GET CART 5002 |-------------------------------------------------------------------------- 5003 */ 5004 getCart: async function () { 5005 try { 5006 const result = 5007 await this.request({ 5008 action: 'get_cart' 5009 }); 5010 this.handleCartResponse( 5011 result 5012 ); 5013 this.dispatchCartEvent( 5014 'loxshop:cart-loaded', 5015 result 5016 ); 5017 return result; 5018 } catch (error) { 5019 console.error( 5020 'LOX SHOP GET CART ERROR:', 5021 error 5022 ); 5023 this.showError( 5024 error.message || 5025 'خطا در Ø¯Ø±ÛØ§Ùت سبد Ø®Ø±ÛØ¯.' 5026 ); 5027 return null; 5028 } 5029 }, 5030 /* 5031 |-------------------------------------------------------------------------- 5032 | UPDATE CART 5033 |-------------------------------------------------------------------------- 5034 */ 5035 updateCart: async function ( 5036 cartItemId, 5037 quantity 5038 ) { 5039 cartItemId = 5040 parseInt( 5041 cartItemId || 0, 5042 10 5043 ); 5044 quantity = 5045 parseFloat( 5046 quantity 5047 ); 5048 if (cartItemId <= 0) { 5049 this.showError( 5050 'Ø¢ÛØªÙ سبد ٠عتبر ÙÛØ³Øª.' 5051 ); 5052 return null; 5053 } 5054 if ( 5055 !isFinite(quantity) 5056 ) { 5057 quantity = 0; 5058 } 5059 if (quantity > 999999) { 5060 quantity = 999999; 5061 } 5062 try { 5063 const result = 5064 await this.request({ 5065 action: 'update_cart', 5066 cart_item_id: 5067 cartItemId, 5068 quantity: 5069 quantity 5070 }); 5071 this.handleCartResponse( 5072 result 5073 ); 5074 this.dispatchCartEvent( 5075 'loxshop:cart-updated', 5076 result 5077 ); 5078 return result; 5079 } catch (error) { 5080 console.error( 5081 'LOX SHOP UPDATE CART ERROR:', 5082 error 5083 ); 5084 this.showError( 5085 error.message || 5086 'خطا در Ø¨Ø±ÙØ²Ø±Ø³Ø§ÙÛ Ø³Ø¨Ø¯ Ø®Ø±ÛØ¯.' 5087 ); 5088 return null; 5089 } 5090 }, 5091 /* 5092 |-------------------------------------------------------------------------- 5093 | REMOVE FROM CART 5094 |-------------------------------------------------------------------------- 5095 */ 5096 removeFromCart: async function ( 5097 cartItemId 5098 ) { 5099 cartItemId = 5100 parseInt( 5101 cartItemId || 0, 5102 10 5103 ); 5104 if (cartItemId <= 0) { 5105 this.showError( 5106 'Ø¢ÛØªÙ سبد ٠عتبر ÙÛØ³Øª.' 5107 ); 5108 return null; 5109 } 5110 try { 5111 const result = 5112 await this.request({ 5113 action: 5114 'remove_from_cart', 5115 cart_item_id: 5116 cartItemId 5117 }); 5118 this.handleCartResponse( 5119 result 5120 ); 5121 this.dispatchCartEvent( 5122 'loxshop:cart-removed', 5123 result 5124 ); 5125 return result; 5126 } catch (error) { 5127 console.error( 5128 'LOX SHOP REMOVE CART ERROR:', 5129 error 5130 ); 5131 this.showError( 5132 error.message || 5133 'خطا در ØØ°Ù Ú©Ø§ÙØ§.' 5134 ); 5135 return null; 5136 } 5137 }, 5138 /* 5139 |-------------------------------------------------------------------------- 5140 | CLEAR CART 5141 |-------------------------------------------------------------------------- 5142 */ 5143 clearCart: async function () { 5144 try { 5145 const result = 5146 await this.request({ 5147 action: 5148 'clear_cart' 5149 }); 5150 this.handleCartResponse( 5151 result 5152 ); 5153 this.dispatchCartEvent( 5154 'loxshop:cart-cleared', 5155 result 5156 ); 5157 return result; 5158 } catch (error) { 5159 console.error( 5160 'LOX SHOP CLEAR CART ERROR:', 5161 error 5162 ); 5163 this.showError( 5164 error.message || 5165 'خطا در خاÙÛ Ú©Ø±Ø¯Ù Ø³Ø¨Ø¯ Ø®Ø±ÛØ¯.' 5166 ); 5167 return null; 5168 } 5169 }, 5170 /* 5171 |-------------------------------------------------------------------------- 5172 | HANDLE CART RESPONSE 5173 |-------------------------------------------------------------------------- 5174 */ 5175 handleCartResponse: function (result) { 5176 if ( 5177 !result || 5178 !result.cart 5179 ) { 5180 /* 5181 | در PHP cart ٠٠ک٠است null باشد. 5182 */ 5183 return; 5184 } 5185 const cart = 5186 result.cart; 5187 /* 5188 |-------------------------------------------------------------------------- 5189 | ITEMS COUNT 5190 |-------------------------------------------------------------------------- 5191 */ 5192 document 5193 .querySelectorAll( 5194 '[data-cart-items-count]' 5195 ) 5196 .forEach(function (element) { 5197 element.textContent = 5198 Number( 5199 result.items_count || 0 5200 ); 5201 }); 5202 /* 5203 |-------------------------------------------------------------------------- 5204 | TOTAL QUANTITY 5205 |-------------------------------------------------------------------------- 5206 */ 5207 document 5208 .querySelectorAll( 5209 '[data-cart-quantity]' 5210 ) 5211 .forEach(function (element) { 5212 element.textContent = 5213 Number( 5214 result.quantity || 0 5215 ); 5216 }); 5217 /* 5218 |-------------------------------------------------------------------------- 5219 | SUBTOTAL 5220 |-------------------------------------------------------------------------- 5221 */ 5222 document 5223 .querySelectorAll( 5224 '[data-cart-subtotal]' 5225 ) 5226 .forEach(function (element) { 5227 element.textContent = 5228 this.formatMoney( 5229 result.subtotal || 0 5230 ); 5231 }, this); 5232 /* 5233 |-------------------------------------------------------------------------- 5234 | DISCOUNT 5235 |-------------------------------------------------------------------------- 5236 */ 5237 document 5238 .querySelectorAll( 5239 '[data-cart-discount]' 5240 ) 5241 .forEach(function (element) { 5242 element.textContent = 5243 this.formatMoney( 5244
5244 result.discount || 0 5245 ); 5246 }, this); 5247 /* 5248 |-------------------------------------------------------------------------- 5249 | TOTAL 5250 |-------------------------------------------------------------------------- 5251 */ 5252 document 5253 .querySelectorAll( 5254 '[data-cart-total]' 5255 ) 5256 .forEach(function (element) { 5257 element.textContent = 5258 this.formatMoney( 5259 result.total || 0 5260 ); 5261 }, this); 5262 /* 5263 |-------------------------------------------------------------------------- 5264 | CART UUID 5265 |-------------------------------------------------------------------------- 5266 */ 5267 document 5268 .querySelectorAll( 5269 '[data-cart-uuid]' 5270 ) 5271 .forEach(function (element) { 5272 element.textContent = 5273 cart.uuid || ''; 5274 element.setAttribute( 5275 'data-cart-uuid', 5276 cart.uuid || '' 5277 ); 5278 }); 5279 /* 5280 |-------------------------------------------------------------------------- 5281 | CART ID 5282 |-------------------------------------------------------------------------- 5283 */ 5284 document 5285 .querySelectorAll( 5286 '[data-cart-id]' 5287 ) 5288 .forEach(function (element) { 5289 element.setAttribute( 5290 'data-cart-id', 5291 cart.id || '' 5292 ); 5293 }); 5294 /* 5295 |--------------------------------------------------------------------------
5296 | EMPTY CART STATE 5297 |-------------------------------------------------------------------------- 5298 */ 5299 document 5300 .querySelectorAll( 5301 '[data-cart-empty]' 5302 ) 5303 .forEach(function (element) { 5304 element.hidden = 5305 Number( 5306 result.items_count || 0 5307 ) > 0; 5308 }); 5309 /* 5310 |-------------------------------------------------------------------------- 5311 | HAS ITEMS STATE 5312 |-------------------------------------------------------------------------- 5313 */ 5314 document 5315 .querySelectorAll( 5316 '[data-cart-has-items]' 5317 ) 5318 .forEach(function (element) { 5319 element.hidden = 5320 Number( 5321 result.items_count || 0 5322 ) <= 0; 5323 }); 5324 /* 5325 |-------------------------------------------------------------------------- 5326 | CART ITEM COUNT BADGE 5327 |-------------------------------------------------------------------------- 5328 */ 5329 document 5330 .querySelectorAll( 5331 '[data-cart-badge]' 5332 ) 5333 .forEach(function (element) { 5334 const count = 5335 Number( 5336 result.items_count || 0 5337 ); 5338 element.textContent = 5339 count; 5340 element.hidden = 5341 count <= 0;
5342 }); 5343 }, 5344 /* 5345 |-------------------------------------------------------------------------- 5346 | FORMAT MONEY 5347 |-------------------------------------------------------------------------- 5348 */ 5349 formatMoney: function (value) { 5350 value = 5351 Number(value || 0); 5352 if (!isFinite(value)) { 5353 value = 0; 5354 } 5355 return value.toLocaleString( 5356 'en-US' 5357 ); 5358 }, 5359 /* 5360 |-------------------------------------------------------------------------- 5361 | VALIDATE PRODUCT RESULT 5362 |-------------------------------------------------------------------------- 5363 */ 5364 getValidationData: function (result) { 5365 if ( 5366 !result || 5367 result.success !== true 5368 ) { 5369 return null; 5370 } 5371 return { 5372 product: 5373 result.product || null, 5374 sku: 5375 result.sku || null, 5376 price: 5377 result.price || null, 5378 availability: 5379 result.availability || null 5380 }; 5381 }, 5382 /* 5383 |-------------------------------------------------------------------------- 5384 | DISPATCH EVENT 5385 |-------------------------------------------------------------------------- 5386 */ 5387 dispatchCartEvent: function ( 5388 name, 5389 detail 5390 ) { 5391 try { 5392 document.dispatchEvent( 5393 new CustomEvent( 5394 name, 5395 { 5396 detail: detail 5397 } 5398 ) 5399 ); 5400 } catch (error) { 5401 /* 5402 | Ù Ø±ÙØ±Ú¯Ø±ÙØ§Û Ø®ÛÙÛ ÙØ¯ÛÙ Û 5403 | در ØµÙØ±Øª ÙÛØ§Ø² Ù ÛâØªÙØ§Ù fallback اضاÙ٠کرد. 5404 */ 5405 } 5406 }, 5407 /* 5408 |-------------------------------------------------------------------------- 5409 | SUCCESS 5410 |-------------------------------------------------------------------------- 5411 */ 5412 5413 5414 showSuccess: function (message) { 5415 5416 const element = document.getElementById( 5417 'shopresponse' 5418 ); 5419 5420 if (!element) { 5421 return; 5422 } 5423 5424 element.className = 'messs msuccess'; 5425 5426 element.innerHTML = 5427 message || 'ع٠ÙÛØ§Øª با Ù ÙÙÙÛØª Ø§ÙØ¬Ø§Ù شد.'; 5428 5429 element.hidden = false; 5430 }, 5431 5432 5433 5434 showError: function (message) { 5435 5436 const element = document.getElementById( 5437 'shopresponse' 5438 ); 5439 5440 if (!element) { 5441 return; 5442 } 5443 5444 element.className = 'messs merror'; 5445 element.textContent = 5446 message || 'خطاÛÛ Ø±Ø® داد٠است.'; 5447 5448 element.hidden = false; 5449 } 5450 5451 5452 }; 5453 /* 5454 |-------------------------------------------------------------------------- 5455 | GLOBAL: ADD TO CART 5456 |-------------------------------------------------------------------------- 5457 | 5458 | Ù¾Ø´ØªÛØ¨Ø§ÙÛ: 5459 | 5460 | onclick="return add_to_cart(event)" 5461 | 5462 | onclick="return add_to_cart()" 5463 | 5464 |-------------------------------------------------------------------------- 5465 */ 5466 window.add_to_cart = function (event) { 5467 /* 5468 |-------------------------------------------------------------------------- 5469 | EVENT 5470 |-------------------------------------------------------------------------- 5471 */ 5472 if ( 5473 event && 5474 typeof event.preventDefault === 'function' 5475 ) { 5476 event.preventDefault(); 5477 return ShopCart.addToCart( 5478 event 5479 ); 5480 } 5481 /* 5482 |-------------------------------------------------------------------------- 5483 | ACTIVE ELEMENT 5484 |-------------------------------------------------------------------------- 5485 */ 5486 let element = 5487 document.activeElement; 5488 /* 5489 |-------------------------------------------------------------------------- 5490 | اگر activeElement Ù ÙØ§Ø³Ø¨ ÙØ¨Ùد 5491 |-------------------------------------------------------------------------- 5492 */ 5493 if ( 5494 !element || 5495 !element.closest 5496 ) { 5497 element = 5498 document.getElementById( 5499 'add_btn' 5500 ); 5501 } 5502 /* 5503 |-------------------------------------------------------------------------- 5504 | ADD 5505 |-------------------------------------------------------------------------- 5506 */ 5507 ShopCart.addToCart( 5508 element 5509 ); 5510 return false; 5511 }; 5512 /* 5513 |-------------------------------------------------------------------------- 5514 | GLOBAL: PRODUCT MINUS 5515 |-------------------------------------------------------------------------- 5516 */ 5517 window.product_minus = function ( 5518 event, 5519 element 5520 ) { 5521 if (event) { 5522 event.preventDefault(); 5523 } 5524 const form = 5525 ShopCart.getForm(element); 5526 if (!form) { 5527 return false; 5528 } 5529 const input = 5530 form.querySelector( 5531 'input[name="product_number"]' 5532 ); 5533 if (!input) { 5534 return false; 5535 } 5536 let value = 5537 parseFloat( 5538 input.value 5539 ); 5540 if ( 5541 !isFinite(value) || 5542 value < 1 5543 ) { 5544 value = 1; 5545 } 5546 value--; 5547 if (value < 1) { 5548 value = 1; 5549 } 5550 input.value = value; 5551 input.dispatchEvent( 5552 new Event( 5553 'change', 5554 { 5555 bubbles: true 5556 } 5557 ) 5558 ); 5559 return false; 5560 }; 5561 /* 5562 |-------------------------------------------------------------------------- 5563 | GLOBAL: PRODUCT PLUS 5564 |-------------------------------------------------------------------------- 5565 */ 5566 window.product_plus = function ( 5567 event, 5568 element 5569 ) { 5570 if (event) { 5571 event.preventDefault(); 5572 } 5573 const form = 5574 ShopCart.getForm(element); 5575 if (!form) { 5576 return false; 5577 } 5578 const input = 5579 form.querySelector( 5580 'input[name="product_number"]' 5581 ); 5582 if (!input) { 5583 return false; 5584 } 5585 let value = 5586 parseFloat( 5587 input.value 5588 ); 5589 if ( 5590 !isFinite(value) || 5591 value < 1 5592 ) { 5593 value = 1; 5594 } 5595 value++; 5596 if (value > 999999) { 5597 value = 999999; 5598 } 5599 input.value = value; 5600 input.dispatchEvent( 5601 new Event( 5602 'change', 5603 { 5604 bubbles: true 5605 } 5606 ) 5607 ); 5608 return false; 5609 }; 5610 /* 5611 |-------------------------------------------------------------------------- 5612 | FORM SUBMIT 5613 |-------------------------------------------------------------------------- 5614 |
5615 | ØØªÛ بدÙÙ onclick: 5616 | 5617 | <form class="product-cart-form"> 5618 | 5619 | AJAX اجرا Ù ÛâØ´ÙØ¯. 5620 |-------------------------------------------------------------------------- 5621 */ 5622 document.addEventListener( 5623 'submit', 5624 function (event) { 5625 const form = 5626 event.target.closest( 5627 'form.product-cart-form, form[data-shop-cart-form]' 5628 ); 5629 if (!form) { 5630 return; 5631 } 5632 /* 5633 | جÙÙÚ¯ÛØ±Û از submit ٠ع٠ÙÙÛ 5634 */ 5635 event.preventDefault(); 5636 /* 5637 | ADD TO CART 5638 */ 5639 ShopCart.addToCart( 5640 form 5641 ); 5642 } 5643 ); 5644 /* 5645 |-------------------------------------------------------------------------- 5646 | GLOBAL EXPORT 5647 |-------------------------------------------------------------------------- 5648 */ 5649 window.LoxShopCart = 5650 ShopCart; 5651})(window, document); 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676(function () { 5677 5678 'use strict'; 5679 5680 /* 5681 |-------------------------------------------------------------------------- 5682 | PRODUCT SKU DATA 5683 |-------------------------------------------------------------------------- 5684 */ 5685 5686 const skuDataElement = 5687 document.getElementById('product-sku-data'); 5688 5689 if (!skuDataElement) { 5690 return; 5691 } 5692 5693 let skuData = []; 5694 5695 try { 5696 skuData = 5697 JSON.parse( 5698 skuDataElement.textContent || '[]' 5699 ); 5700 } catch (e) { 5701 console.error( 5702 'LoxShop: invalid SKU JSON', 5703 e 5704 ); 5705 5706 return; 5707 } 5708 5709 5710 /* 5711 |-------------------------------------------------------------------------- 5712 | ELEMENTS 5713 |-------------------------------------------------------------------------- 5714 */ 5715 5716 const form = 5717 skuDataElement.closest('form'); 5718 5719 if (!form) { 5720 return; 5721 } 5722 5723 5724 /* 5725 |-------------------------------------------------------------------------- 5726 | HELPERS 5727 |-------------------------------------------------------------------------- 5728 */ 5729 5730 function getVariationElements() { 5731 5732 return Array.from( 5733 form.querySelectorAll( 5734 '.product_var' 5735 ) 5736 ); 5737 5738 } 5739 5740 5741 /* 5742 |-------------------------------------------------------------------------- 5743 | GET CURRENT SELECTIONS 5744 |-------------------------------------------------------------------------- 5745 | 5746 | Result: 5747 | 5748 | { 5749 | "1": "1", 5750 | "9": "143" 5751 | } 5752 | 5753 |-------------------------------------------------------------------------- 5754 */ 5755 5756function getSelections(ignoreKey = null) { 5757 5758 const selections = {}; 5759 5760 const elements = getVariationElements(); 5761 5762 elements.forEach(function (element) { 5763 5764 console.log( 5765 'VAR ELEMENT:', 5766 { 5767 tag: element.tagName, 5768 key: element.getAttribute('key'), 5769 value: element.value, 5770 name: element.name, 5771 id: element.id, 5772 className: element.className 5773 } 5774 ); 5775 5776 const key = element.getAttribute('key'); 5777 5778 if (!key) { 5779 return; 5780 } 5781 5782 if ( 5783 ignoreKey !== null && 5784 String(key) === String(ignoreKey) 5785 ) { 5786 return; 5787 } 5788 5789 if (element.tagName === 'SELECT') { 5790 5791 const value = element.value; 5792 5793 console.log( 5794 'SELECT VALUE:', 5795 key, 5796 value 5797 ); 5798 5799 if ( 5800 value && 5801 value !== '0' 5802 ) { 5803 selections[key] = String(value); 5804 } 5805 5806 return; 5807 } 5808 5809 if (element.type === 'radio') { 5810 if (element.checked) { 5811 selections[key] = String(element.value); 5812 } 5813 return; 5814 } 5815 5816 if (element.type === 'checkbox') { 5817 if (element.checked) { 5818 selections[key] = String(element.value); 5819 } 5820 return; 5821 } 5822 5823 if ( 5824 element.value !== undefined && 5825 element.value !== '' 5826 ) { 5827 selections[key] = String(element.value); 5828 } 5829 5830 });
5831 5832 console.log( 5833 'FINAL VARIATION SELECTIONS:', 5834 selections 5835 ); 5836 5837 return selections; 5838} 5839 5840 5841 /* 5842 |-------------------------------------------------------------------------- 5843 | CHECK SKU AGAINST SELECTIONS 5844 |-------------------------------------------------------------------------- 5845 */ 5846 5847 function skuMatches( 5848 sku, 5849 selections 5850 ) { 5851 5852 const attributes = 5853 sku.attributes || {}; 5854 5855 for ( 5856 const key in selections 5857 ) { 5858 5859 if ( 5860 String(attributes[key]) !== 5861 String(selections[key]) 5862 ) { 5863 5864 return false; 5865 5866 } 5867 5868 } 5869 5870 return true; 5871 5872 } 5873 5874 5875 /* 5876 |-------------------------------------------------------------------------- 5877 | FIND EXACT SKU 5878 |-------------------------------------------------------------------------- 5879 */ 5880 5881 function findSelectedSku() { 5882 5883 const selections = 5884 getSelections(); 5885 5886 const keys = 5887 Object.keys(selections); 5888 5889 /* 5890 |-------------------------------------------------------------------------- 5891 | ÙÙÙØ² ÙÙ Ù ÙÛÚÚ¯ÛâÙØ§ Ø§ÙØªØ®Ø§Ø¨ ÙØ´Ø¯ÙâØ§ÙØ¯ 5892 |-------------------------------------------------------------------------- 5893 */ 5894 5895 const variationKeys = 5896 getVariationKeys(); 5897 5898 if ( 5899 keys.length !== 5900 variationKeys.length 5901 ) { 5902 5903 return null; 5904 5905 } 5906 5907 5908 return skuData.find( 5909 function (sku) { 5910 5911 return skuMatches( 5912 sku, 5913 selections 5914 ); 5915 5916 } 5917 ) || null; 5918 5919 } 5920 5921 5922 /* 5923 |-------------------------------------------------------------------------- 5924 | GET ALL ATTRIBUTE KEYS 5925 |-------------------------------------------------------------------------- 5926 */ 5927 5928 function getVariationKeys() { 5929 5930 const keys = []; 5931 5932 skuData.forEach( 5933 function (sku) { 5934 5935 const attributes = 5936 sku.attributes || {}; 5937 5938 Object.keys(attributes) 5939 .forEach( 5940 function (key) { 5941 5942 if ( 5943 !keys.includes(key) 5944 ) { 5945 5946 keys.push(key); 5947 5948 } 5949 5950 } 5951 ); 5952 5953 } 5954 ); 5955 5956 return keys; 5957 5958 } 5959 5960 5961 /* 5962 |-------------------------------------------------------------------------- 5963 | FIND VALID VALUES FOR ATTRIBUTE 5964 |-------------------------------------------------------------------------- 5965 | 5966 | Ù ÙÙ âØªØ±ÛÙ ÙØ³Ù ت Ø³ÛØ³ØªÙ 5967 | 5968 | Ø¨Ø±Ø§Û ÙØ± attributeØ Ø§ÙØªØ®Ø§Ø¨ ÙØ¹ÙÛ Ø¢Ù attribute 5969 | را ÙØ§Ø¯Ûد٠٠ÛâÚ¯ÛØ±ÛÙ Ù Ø¨Ø±Ø±Ø³Û Ù ÛâÚ©ÙÛÙ ÚÙ valueÙØ§ÛÛ 5970 | با بÙÛÙ Ø§ÙØªØ®Ø§Ø¨âÙØ§Û کاربر سازگار ÙØ³ØªÙد. 5971 | 5972 |-------------------------------------------------------------------------- 5973 */ 5974 5975 function getValidValuesForKey( 5976 key 5977 ) { 5978 5979 const selections = 5980 getSelections(key); 5981 5982 const validValues = 5983 new Set(); 5984 5985 5986 skuData.forEach( 5987 function (sku) { 5988 5989 /* 5990 |-------------------------------------------------------------------------- 5991 | SKU Ø¨Ø§ÛØ¯ با Ø§ÙØªØ®Ø§Ø¨âÙØ§Û Ø³Ø§ÛØ± ÙÛÚÚ¯ÛâÙØ§ 5992 | سازگار باشد. 5993 |-------------------------------------------------------------------------- 5994 */ 5995 5996 if ( 5997 !skuMatches( 5998 sku, 5999 selections 6000 ) 6001 ) { 6002 6003 return; 6004 6005 } 6006 6007 6008 /* 6009 |-------------------------------------------------------------------------- 6010 | ÙÙØ· SKUÙØ§Û ÙØ§Ø¨Ù Ø®Ø±ÛØ¯ 6011 |-------------------------------------------------------------------------- 6012 */ 6013 6014 if ( 6015 sku.is_purchasable === 0 || 6016 sku.is_purchasable === false 6017 ) { 6018 6019 return; 6020 6021 } 6022 6023 6024 /* 6025 |-------------------------------------------------------------------------- 6026 | Ù ÙØ¬ÙØ¯Û 6027 |-------------------------------------------------------------------------- 6028 */ 6029 6030 if ( 6031 sku.available === false 6032 ) { 6033 6034 return; 6035 6036 } 6037 6038 6039 if ( 6040 sku.attributes && 6041 sku.attributes[key] !== undefined 6042 ) { 6043 6044 validValues.add( 6045 String( 6046 sku.attributes[key] 6047 ) 6048 ); 6049 6050 } 6051 6052 } 6053 ); 6054 6055 6056 return validValues; 6057 6058 } 6059 6060 6061 /* 6062 |-------------------------------------------------------------------------- 6063 | UPDATE SELECT OPTIONS 6064 |-------------------------------------------------------------------------- 6065 */ 6066 6067 function updateSelect(select) { 6068
6069 const key = 6070 select.getAttribute('key'); 6071 6072 if (!key) { 6073 return; 6074 } 6075 6076 const validValues = 6077 getValidValuesForKey(key); 6078 6079 Array.from( 6080 select.options 6081 ).forEach( 6082 function (option) { 6083 6084 if ( 6085 option.value === '0' || 6086 option.value === '' 6087 ) { 6088 option.disabled = false; 6089 return; 6090 } 6091 6092 const valid = 6093 validValues.has( 6094 String(option.value) 6095 ); 6096 6097 option.disabled = !valid; 6098 } 6099 ); 6100} 6101 6102 6103 /* 6104 |-------------------------------------------------------------------------- 6105 | UPDATE RADIO / CHECKBOX 6106 |-------------------------------------------------------------------------- 6107 */ 6108 6109 function updateInput( 6110 element 6111 ) { 6112 6113 const key = 6114 element.getAttribute('key'); 6115 6116 if (!key) { 6117 return; 6118 } 6119 6120 6121 const validValues = 6122 getValidValuesForKey(key); 6123 6124 6125 const valid = 6126 validValues.has( 6127 String(element.value) 6128 ); 6129 6130 6131 element.disabled = 6132 !valid; 6133 6134 6135 /* 6136 |-------------------------------------------------------------------------- 6137 | اگر Ø§ÙØªØ®Ø§Ø¨ شد٠ÙÙÛ Ø¯Ûگر ٠عتبر ÙÛØ³Øª 6138 |-------------------------------------------------------------------------- 6139 */ 6140 6141 if ( 6142 !valid && 6143 element.checked 6144 ) { 6145 6146 element.checked = false; 6147 6148 } 6149 6150 6151 /* 6152 |-------------------------------------------------------------------------- 6153 | Ø¨Ø±Ø§Û label Ù Ø±Ø¨ÙØ· ب٠input 6154 |-------------------------------------------------------------------------- 6155 */ 6156 6157 const label = 6158 element.closest('label'); 6159 6160 if (label) { 6161 6162 label.classList.toggle( 6163 'loxshop-option-disabled', 6164 !valid 6165 ); 6166 6167 } 6168 6169 } 6170 6171 6172 /* 6173 |-------------------------------------------------------------------------- 6174 | UPDATE ALL OPTIONS 6175 |-------------------------------------------------------------------------- 6176 */ 6177 6178 function updateVariationOptions() { 6179 6180 getVariationElements() 6181 .forEach( 6182 function (element) { 6183 6184 if ( 6185 element.tagName === 'SELECT' 6186 ) { 6187 6188 updateSelect( 6189 element 6190 ); 6191 6192 return; 6193 6194 } 6195 6196 6197 if ( 6198 element.type === 'radio' || 6199 element.type === 'checkbox' 6200 ) { 6201 6202 updateInput( 6203 element 6204 ); 6205 6206 } 6207 6208 } 6209 ); 6210 6211 } 6212 6213 6214 function findLowestPricedSku() { 6215 6216 6217 const availableSkus = 6218 skuData.filter(function (sku) { 6219 6220 if ( 6221 sku.is_purchasable === 0 || 6222 sku.is_purchasable === false 6223 ) { 6224 return false; 6225 } 6226 6227 if ( 6228 sku.available === false 6229 ) { 6230 return false; 6231 } 6232 6233 if ( 6234 sku.final_price === null || 6235 sku.final_price === undefined || 6236 sku.final_price === '' 6237 ) { 6238 return false; 6239 } 6240 6241 return true; 6242 6243 }); 6244 6245 6246 if (!availableSkus.length) { 6247 return null; 6248 } 6249 6250 6251 return availableSkus.reduce( 6252 function (lowest, sku) { 6253 6254 return Number(sku.final_price) < 6255 Number(lowest.final_price) 6256 ? sku 6257 : lowest; 6258 6259 } 6260 ); 6261 6262 6263 } 6264 /* 6265 |-------------------------------------------------------------------------- 6266 | UPDATE PRODUCT INFORMATION 6267 |-------------------------------------------------------------------------- 6268 */ 6269 6270 function updateProductInfo(sku) { 6271 6272 /* 6273 |-------------------------------------------------------------------------- 6274 | ÙÛÚ ØªÙÙØ¹Û ÙÙÙØ² Ø§ÙØªØ®Ø§Ø¨ ÙØ´Ø¯Ù 6275 |-------------------------------------------------------------------------- 6276 */ 6277 6278 if (!sku) { 6279 6280 /* 6281 |-------------------------------------------------------------------------- 6282 | اگر ÙÛÚ variation Ø§ÙØªØ®Ø§Ø¨ ÙØ´Ø¯ÙØ
6283 | ک٠ترÛÙ ÙÛ٠ت Ù ÙØ¬Ùد را ÙÙ Ø§ÛØ´ بدÙ. 6284 |-------------------------------------------------------------------------- 6285 */ 6286 6287 const selections = 6288 getSelections(); 6289 6290 6291 if ( 6292 Object.keys(selections).length === 0 6293 ) { 6294 6295 const lowestSku = 6296 findLowestPricedSku(); 6297 6298 6299 if (lowestSku) { 6300 6301 updateLoxShop( 6302 'Product_Price', 6303 formatPrice( 6304 lowestSku.final_price 6305 ) 6306 ); 6307 6308 6309 updateLoxShop( 6310 'Product_Discount', 6311 lowestSku.discount_percent > 0 6312 ? formatPrice(lowestSku.price) 6313 : '' 6314 ); 6315 6316 6317 updateLoxShop( 6318 'Product_Discount_Percent', 6319 lowestSku.discount_percent || 0 6320 ); 6321 6322 } 6323 6324 return; 6325 6326 } 6327 6328 6329 /* 6330 |-------------------------------------------------------------------------- 6331 | ØªØ¹Ø¯Ø§Ø¯Û variation Ø§ÙØªØ®Ø§Ø¨ شد٠ÙÙÛ ÙÙÙØ² SKU کا٠٠ÙÛØ³Øª 6332 |-------------------------------------------------------------------------- 6333 */ 6334 6335 updateLoxShop( 6336 'Product_Price', 6337 '' 6338 ); 6339 6340 updateLoxShop( 6341 'Product_Discount', 6342 '' 6343 ); 6344 6345 updateLoxShop( 6346 'Product_Discount_Percent', 6347 '0' 6348 ); 6349 6350 return; 6351 6352 } 6353 6354 6355 /* 6356 |-------------------------------------------------------------------------- 6357 | SKU Ú©Ø§Ù Ù Ø§ÙØªØ®Ø§Ø¨ شد٠6358 |-------------------------------------------------------------------------- 6359 */ 6360 6361 updateLoxShop( 6362 'Product_Price', 6363 formatPrice( 6364 sku.final_price 6365 ) 6366 ); 6367 6368 6369 updateLoxShop( 6370 'Product_Discount', 6371 sku.discount_percent > 0 6372 ? formatPrice(sku.price) 6373 : '' 6374 ); 6375 6376 6377 updateLoxShop( 6378 'Product_Discount_Percent', 6379 sku.discount_percent || 0 6380 ); 6381 6382 6383 updateStock(sku); 6384 6385 updateProductImage(sku); 6386 6387 updateCartButton(sku); 6388 6389 6390 } 6391 6392 6393 6394 /* 6395 |-------------------------------------------------------------------------- 6396 | UPDATE LOXSHOP ELEMENTS 6397 |-------------------------------------------------------------------------- 6398 */ 6399 6400 function updateLoxShop( 6401 className, 6402 value 6403 ) { 6404 6405 const elements = 6406 document.querySelectorAll( 6407 '.LoxShop_' + className 6408 ); 6409 6410 6411 elements.forEach( 6412 function (element) { 6413 6414 element.textContent = 6415 value ?? ''; 6416 6417 } 6418 ); 6419 6420 } 6421 6422 6423 /* 6424 |-------------------------------------------------------------------------- 6425 | FORMAT PRICE 6426 |-------------------------------------------------------------------------- 6427 */ 6428 6429 function formatPrice( 6430 value 6431 ) { 6432 6433 if ( 6434 value === null || 6435 value === undefined || 6436 value === '' 6437 ) { 6438 6439 return ''; 6440 6441 } 6442 6443 6444 return Number(value) 6445 .toLocaleString('en-US'); 6446 6447 } 6448 6449 6450 /* 6451 |-------------------------------------------------------------------------- 6452 | STOCK 6453 |-------------------------------------------------------------------------- 6454 */ 6455 6456 function updateStock( 6457 sku 6458 ) { 6459 6460 const elements = 6461 document.querySelectorAll( 6462 '.LoxShop_Product_Stock' 6463 ); 6464 6465
6466 elements.forEach( 6467 function (element) { 6468 6469 if ( 6470 sku.manage_stock && 6471 Number(sku.stock_quantity) <= 0 6472 ) { 6473 6474 element.textContent = 6475 'ÙØ§Ù ÙØ¬Ùد'; 6476 6477 return; 6478 6479 } 6480 6481 6482 if ( 6483 sku.manage_stock 6484 ) { 6485 6486 element.textContent = 6487 'Ù ÙØ¬ÙدÛ: ' + 6488 Number( 6489 sku.stock_quantity 6490 ).toLocaleString('en-US'); 6491 6492 return; 6493 6494 } 6495 6496 6497 element.textContent = 6498 'Ù ÙØ¬Ùد'; 6499 6500 } 6501 ); 6502 6503 } 6504 6505 6506 /* 6507 |-------------------------------------------------------------------------- 6508 | PRODUCT IMAGE 6509 |-------------------------------------------------------------------------- 6510 */ 6511 function updateProductImage(sku) { 6512 6513 if (!sku || !sku.image) { 6514 return; 6515 } 6516 6517 var imageUrl = sku.image; 6518 6519 /* 6520 * ------------------------------------------------------------ 6521 * 1. Ø±ÙØªØ§Ø± ÙØ¨ÙÛ ÙØ§Ùب را ØÙظ Ù ÛâÚ©ÙÛÙ 6522 * ------------------------------------------------------------ 6523 */ 6524 6525 const images = document.querySelectorAll( 6526 '.LoxShop_Product_Image' 6527 ); 6528 6529 images.forEach(function (image) { 6530 6531 if (image.tagName === 'IMG') { 6532 6533 image.src = imageUrl; 6534 6535 } else { 6536 6537 image.style.backgroundImage = 6538 'url("' + imageUrl + '")'; 6539 6540 } 6541 6542 }); 6543 6544 6545 /* 6546 * ------------------------------------------------------------ 6547 * 2. اگر Ú¯Ø§ÙØ±Û ØØ±ÙÙâØ§Û ÙØ¬Ùد ÙØ¯Ø§Ø±Ø¯Ø ت٠ا٠6548 * ------------------------------------------------------------ 6549 */ 6550 6551 if ( 6552 typeof window.lxProductGallery === 'undefined' || 6553 !window.lxProductGallery 6554 ) { 6555 return; 6556 } 6557 6558 6559 /* 6560 * ------------------------------------------------------------ 6561 * 3. ÙÙ Ø§ÛØ´ تصÙÛØ± SKU Ø¯Ø§Ø®Ù Ú¯Ø§ÙØ±Û 6562 * ------------------------------------------------------------ 6563 */ 6564 6565 window.lxProductGallery.showImage(imageUrl); 6566 6567 } 6568 6569 6570 /* 6571 |-------------------------------------------------------------------------- 6572 | ADD TO CART BUTTON 6573 |-------------------------------------------------------------------------- 6574 */ 6575 6576 function updateCartButton( 6577 sku 6578 ) { 6579 6580 const buttons = 6581 form.querySelectorAll( 6582 '#add_btn, [type="submit"]' 6583 ); 6584 6585 6586 buttons.forEach( 6587 function (button) { 6588 6589 const purchasable = 6590 sku && 6591 sku.is_purchasable && 6592 sku.available !== false; 6593 6594 6595 button.disabled = 6596 !purchasable; 6597 6598 6599 if (!sku) { 6600 6601 button.textContent = 6602 'Ø§ÙØªØ®Ø§Ø¨ تÙÙØ¹'; 6603 6604 return; 6605 6606 } 6607 6608 6609 if (!purchasable) { 6610 6611 button.textContent = 6612 'ÙØ§Ù ÙØ¬Ùد'; 6613 6614 return; 6615 6616 } 6617 6618 6619 button.textContent = 6620 'Ø§ÙØ²Ùد٠ب٠سبد'; 6621 6622 } 6623 ); 6624 6625 } 6626 6627 6628 /* 6629 |-------------------------------------------------------------------------- 6630 | MAIN UPDATE 6631 |-------------------------------------------------------------------------- 6632 */ 6633 6634 function updateVariations() { 6635 6636 console.log( 6637 '========== UPDATE VARIATIONS ==========' 6638 ); 6639 6640 const selections = 6641 getSelections(); 6642 6643 console.log( 6644 'SELECTIONS BEFORE OPTIONS:', 6645 selections 6646 ); 6647
6648 updateVariationOptions(); 6649 6650 const sku = 6651 findSelectedSku(); 6652 6653 console.log( 6654 'MATCHED SKU:', 6655 sku 6656 ); 6657 6658 updateProductInfo( 6659 sku 6660 ); 6661 6662 form.dataset.selectedSkuId = 6663 sku 6664 ? sku.sku_id 6665 : ''; 6666} 6667 6668 /* 6669 |-------------------------------------------------------------------------- 6670 | EVENT HANDLER 6671 |-------------------------------------------------------------------------- 6672 */ 6673 6674 form.addEventListener( 6675 'change', 6676 function (event) { 6677 6678 if ( 6679 !event.target.matches( 6680 '.product_var' 6681 ) 6682 ) { 6683 6684 return; 6685 6686 } 6687 6688 6689 updateVariations(); 6690 6691 } 6692 ); 6693 6694 6695 /* 6696 |-------------------------------------------------------------------------- 6697 | INITIALIZE 6698 |-------------------------------------------------------------------------- 6699 */ 6700 6701 updateVariations(); 6702 6703 6704 /* 6705 |-------------------------------------------------------------------------- 6706 | PUBLIC API 6707 |-------------------------------------------------------------------------- 6708 | 6709 | در ØµÙØ±Øª ÙÛØ§Ø² از Ø¨ÛØ±ÙÙ ÙÙ ÙØ§Ø¨Ù Ø§Ø³ØªÙØ§Ø¯Ù است. 6710 |-------------------------------------------------------------------------- 6711 */ 6712 6713 window.LoxShopProductVariations = { 6714 6715 getSku: function () { 6716 6717 return findSelectedSku(); 6718 6719 }, 6720 6721 getSelections: function () { 6722 6723 return getSelections(); 6724 6725 }, 6726 6727 update: function () { 6728 6729 updateVariations(); 6730 6731 } 6732 6733 }; 6734 6735})(); 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751(function () { 6752 6753 'use strict'; 6754 6755 6756 /* ============================================================ 6757 * LOXSHOP CART 6758 * 6759 * ساختار HTML: 6760 * 6761 * <tr data-cart-item-id="64"> 6762 * 6763 * <input 6764 * class="product_number" 6765 * name="product[13][3]" 6766 * value="6" 6767 * > 6768 * 6769 * ============================================================ */ 6770 6771 6772 /* ============================================================ 6773 * GET CART ROW 6774 * ============================================================ */ 6775 6776 function getCartRow(element) { 6777 6778 if (!element) { 6779 return null; 6780 } 6781 6782 return element.closest( 6783 '#LOXSHOPCART tr[data-cart-item-id]' 6784 ); 6785 } 6786 6787 6788 /* ============================================================ 6789 * GET CART ITEM ID 6790 * ============================================================ */ 6791 6792 function getCartItemId(element) { 6793 6794 const row = getCartRow(element); 6795 6796 if (!row) { 6797 return 0; 6798 } 6799 6800 return parseInt( 6801 row.getAttribute('data-cart-item-id') || '0', 6802 10 6803 ); 6804 } 6805 6806 6807 6808 6809 6810 /* ============================================================ 6811 * NORMALIZE NUMBER 6812 * ============================================================ */ 6813 6814 function normalizeNumber(value) { 6815 6816 if ( 6817 value === null || 6818 value === undefined 6819 ) { 6820 return 0; 6821 } 6822 6823 value = String(value); 6824 6825 /* ØØ°Ù جداکÙÙØ¯Ù ÙØ²Ø§Ø±Ú¯Ø§Ù */ 6826 value = value.replace(/,/g, ''); 6827 6828 /* تبدÛ٠اعداد ÙØ§Ø±Ø³Û */ 6829 value = value.replace(/[Û°-Û¹]/g, function (char) { 6830 6831 return String( 6832 'Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹'.indexOf(char) 6833 ); 6834 6835 }); 6836 6837 /* تبدÛ٠اعداد Ø¹Ø±Ø¨Û */ 6838 value = value.replace(/[Ù -Ù©]/g, function (char) { 6839 6840 return String( 6841 '٠١٢٣٤٥٦٧٨٩'.indexOf(char) 6842 ); 6843 6844 }); 6845 6846 /* ÙÙØ· عدد */ 6847 value = value.replace(/[^\d]/g, ''); 6848 6849 return parseInt( 6850 value || '0', 6851 10 6852 ); 6853 } 6854 6855 6856 /* ============================================================ 6857 * FORMAT NUMBER 6858 * ============================================================ */ 6859 6860 function formatNumber(value) { 6861 6862 value = normalizeNumber(value); 6863
6864 return value.toLocaleString('en-US'); 6865 } 6866 6867 6868 6869 6870 6871 6872 /* ============================================================ 6873 * UPDATE CART TOTAL 6874 * ============================================================ */ 6875 6876 6877 6878 window.updateCartTotal = function (cartResponse) { 6879 6880 if (!cartResponse) { 6881 return; 6882 } 6883 6884 6885 /* ======================================================== 6886 * CART DATA 6887 * ======================================================== */ 6888 6889 const cart = cartResponse.cart; 6890 6891 if (!cart) { 6892 console.warn( 6893 'Cart data not found.' 6894 ); 6895 return; 6896 } 6897 6898 6899 /* ======================================================== 6900 * TOTAL QUANTITY 6901 * ======================================================== */ 6902 6903 document 6904 .querySelectorAll( 6905 '.LoxShop_Cart_Total_Count' 6906 ) 6907 .forEach(function (element) { 6908 6909 element.textContent = 6910 formatNumber( 6911 cart.quantity || 0 6912 ) + ' Ù ØØµÙÙ'; 6913 6914 }); 6915 6916 6917 /* ======================================================== 6918 * SUBTOTAL 6919 * ======================================================== */ 6920 6921 document 6922 .querySelectorAll( 6923 '.LoxShop_Cart_Total_Price' 6924 ) 6925 .forEach(function (element) { 6926 6927 element.textContent = 6928 formatNumber( 6929 cart.subtotal || 0 6930 ) + ' تÙ٠اÙ'; 6931 6932 }); 6933 6934 6935 /* ======================================================== 6936 * DISCOUNT 6937 * ======================================================== */ 6938 6939 document 6940 .querySelectorAll( 6941 '.LoxShop_Cart_Total_Discount_Profit' 6942 ) 6943 .forEach(function (element) { 6944 6945 element.textContent = 6946 formatNumber( 6947 cart.discount || 0 6948 ) + ' تÙ٠اÙ'; 6949 6950 }); 6951 6952 6953 /* ======================================================== 6954 * FINAL TOTAL 6955 * ======================================================== */ 6956 6957 document 6958 .querySelectorAll( 6959 '.LoxShop_Cart_Total_All_Price' 6960 ) 6961 .forEach(function (element) { 6962 6963 element.textContent = 6964 formatNumber( 6965 cart.total || 0 6966 ) + ' تÙ٠اÙ'; 6967 6968 }); 6969 6970 }; 6971 6972 6973 6974 /* ============================================================ 6975 * UPDATE CART ROW 6976 * ============================================================ */ 6977 6978function updateCartRow(cartItem) { 6979 if (!cartItem) { 6980 return; 6981 } 6982 6983 /* 6984 * ============================================================ 6985 * Ù¾ÛØ¯Ø§ کرد٠ردÛÙ Ù Ø±Ø¨ÙØ· ب٠ÙÙ ÛÙ cart item 6986 * ============================================================ 6987 */ 6988 6989 const row = 6990 document.querySelector( 6991 '#LOXSHOPCART tr[data-cart-item-id="' + 6992 cartItem.cart_item_id + 6993 '"]' 6994 ); 6995 6996 if (!row) { 6997 console.warn( 6998 'Cart row not found:', 6999 cartItem.cart_item_id 7000 ); 7001 return; 7002 } 7003 7004 7005 /* 7006 * ============================================================ 7007 * QUANTITY 7008 * ============================================================ 7009 */ 7010 7011 const input = 7012 row.querySelector( 7013 'input.product_number' 7014 ); 7015 7016 if (input) { 7017 setQuantity( 7018 input, 7019 cartItem.quantity 7020 ); 7021 } 7022 7023 7024 /* 7025 * ============================================================ 7026 * LINE TOTAL 7027 * ============================================================ 7028 * 7029 * ÙÙØ· ÙÛ٠ت ÙÙ Û٠ردÛ٠را تغÛÛØ± Ù ÛâØ¯ÙÛÙ . 7030 * 7031 * ============================================================ 7032 */ 7033 7034 const totalElement = 7035 row.querySelector( 7036 '.LoxShop_Cart_Item_Total_Price' 7037 ); 7038 7039 if (totalElement) { 7040 7041 const lineTotal = 7042 normalizeNumber( 7043 cartItem.line_total || 0 7044 ); 7045 7046 totalElement.textContent = 7047 formatNumber( 7048 lineTotal 7049 ); 7050 } 7051} 7052 7053 7054 7055 7056 7057 7058 7059 /* ============================================================ 7060 * SET QUANTITY 7061 * ============================================================ */ 7062 7063 function setQuantity(input, value) { 7064 7065 if (!input) { 7066 return; 7067 } 7068 7069 value = normalizeNumber(value); 7070 7071 if (value < 1) { 7072 value = 1; 7073 } 7074 7075 input.value = formatNumber(value); 7076 } 7077 7078 7079 7080 7081 7082 7083 /* ============================================================ 7084 * CART QUANTITY AUTO UPDATE 7085 * 7086 * بعد از آخرÛ٠تغÛÛØ± ØªØ¹Ø¯Ø§Ø¯Ø 2 ثاÙÛ٠صبر Ù ÛâÚ©ÙÛÙ 7087 * ٠سپس quantity را در checkorder.php Ø°Ø®ÛØ±Ù Ù ÛâÚ©ÙÛÙ . 7088 * ============================================================ */ 7089 7090 const cartQuantityTimers = new WeakMap(); 7091 7092 function scheduleCartQuantityUpdate(input) { 7093 7094 if (!input) { 7095 return; 7096 } 7097
7098 const row = input.closest( 7099 '#LOXSHOPCART tr[data-cart-item-id]' 7100 ); 7101 7102 if (!row) { 7103 return; 7104 } 7105 7106 const cartItemId = parseInt( 7107 row.getAttribute('data-cart-item-id') || '0', 7108 10 7109 ); 7110 7111 if (!cartItemId) { 7112 console.error( 7113 'Invalid cart item ID for quantity update.' 7114 ); 7115 return; 7116 } 7117 7118 const quantity = normalizeNumber(input.value); 7119 7120 if (quantity < 1) { 7121 return; 7122 } 7123 7124 /* -------------------------------------------------------- 7125 * اگر تاÛ٠ر ÙØ¨ÙÛ ÙØ¬Ùد Ø¯Ø§Ø±Ø¯Ø ÙØºÙØ´ Ù ÛâÚ©ÙÛÙ 7126 * -------------------------------------------------------- */ 7127 7128 const oldTimer = cartQuantityTimers.get(input); 7129 7130 if (oldTimer) { 7131 clearTimeout(oldTimer); 7132 } 7133 7134 /* -------------------------------------------------------- 7135 * ÙÙ Ø§ÛØ´ ÙØ¶Ø¹Ûت در ØØ§Ù Ø°Ø®ÛØ±Ù 7136 * -------------------------------------------------------- */ 7137 7138 input.dataset.saving = '1'; 7139 7140 /* -------------------------------------------------------- 7141 * DEBOUNCE = 2 ثاÙÛÙ 7142 * -------------------------------------------------------- */ 7143 7144 const timer = setTimeout(function () { 7145 7146 updateCartQuantity( 7147 input, 7148 row, 7149 cartItemId, 7150 quantity 7151 ); 7152 7153 }, 2000); 7154 7155 cartQuantityTimers.set(input, timer); 7156 } 7157 7158 7159 /* ============================================================ 7160 * UPDATE CART QUANTITY 7161 * ============================================================ */ 7162 7163 7164 function updateCartQuantity( 7165 7166 input, 7167 7168 row, 7169 7170 cartItemId, 7171 7172 quantity 7173 7174 ) { 7175 7176 if (!input || !row) { 7177 7178 return; 7179 } 7180 7181 7182 /* -------------------------------------------------------- 7183 * جÙÙÚ¯ÛØ±Û از ارسا٠Ù٠ز٠ا٠7184 * -------------------------------------------------------- */ 7185 7186 if (input.dataset.updating === '1') { 7187 7188 return; 7189 } 7190 7191 input.dataset.updating = '1'; 7192 7193 7194 /* -------------------------------------------------------- 7195 * Ø°Ø®ÛØ±Ù Ù ÙØ¯Ø§Ø± ÙØ¨ÙÛ Ø¨Ø±Ø§Û rollback در ØµÙØ±Øª خطا 7196 * -------------------------------------------------------- */ 7197 7198 const previousQuantity = 7199 7200 input.dataset.previousQuantity !== undefined 7201 7202 ? normalizeNumber( 7203 input.dataset.previousQuantity 7204 ) 7205 7206 : quantity; 7207 7208 7209 /* -------------------------------------------------------- 7210 * REQUEST DATA 7211 * -------------------------------------------------------- */ 7212 7213 const formData = new FormData(); 7214 7215 formData.append( 7216 'action', 7217 'update_cart' 7218 ); 7219 7220 formData.append( 7221 'cart_item_id', 7222 String(cartItemId) 7223 ); 7224 7225 formData.append( 7226 'quantity', 7227 String(quantity) 7228 ); 7229 7230 7231 /* -------------------------------------------------------- 7232 * ارسا٠ب٠checkorder.php 7233 * -------------------------------------------------------- */ 7234 7235 fetch( 7236 7237 '/shoporder/checkorder.php', 7238 7239 { 7240 7241 method: 'POST', 7242 7243 body: formData, 7244 7245 credentials: 'same-origin', 7246 7247 headers: { 7248 7249 'X-Requested-With': 7250 'XMLHttpRequest', 7251 7252 'Accept': 7253 'application/json' 7254 } 7255 } 7256 ) 7257 7258 7259 /* ==================================================== 7260 * RESPONSE 7261 * ==================================================== */ 7262 7263 .then(function (response) { 7264 7265 /* 7266 * ØØªÛ در HTTP 400 ÙÛØ² JSON را Ù ÛâØ®ÙØ§ÙÛÙ . 7267 * 7268 * checkorder.php ٠٠ک٠است Ù Ø«ÙØ§Ù اÛ٠پاسخ را Ø¨Ø¯ÙØ¯: 7269 * 7270 * { 7271 * success: false, 7272 * error: "out_of_stock", 7273 * message: "Ù ÙØ¬ÙØ¯Û Ø§ÛÙ Ú©Ø§ÙØ§ کاÙÛ ÙÛØ³Øª." 7274 * } 7275 */ 7276 7277 return response.json() 7278 7279 .then(function (result) { 7280 7281 return { 7282 7283 ok: 7284 response.ok, 7285 7286 status: 7287 response.status, 7288 7289 result: 7290 result 7291 }; 7292 }) 7293 7294 .catch(function () { 7295 7296 /* 7297 * اگر پاسخ Ø³Ø±ÙØ± JSON ٠عتبر ÙØ¨Ùد 7298 */ 7299 return { 7300 7301 ok: 7302 response.ok, 7303 7304 status: 7305 response.status, 7306 7307 result: 7308 null 7309 }; 7310 }); 7311 }) 7312 7313 7314 /* ==================================================== 7315 * RESULT 7316 * ==================================================== */ 7317 7318 .then(function (responseData) { 7319 7320 var result = 7321 responseData.result; 7322 7323 7324 console.log( 7325 'Cart quantity update response:', 7326 responseData 7327 ); 7328 7329 7330 /* ------------------------------------------------ 7331 * HTTP ERROR ÛØ§ Ø®Ø·Ø§Û Ù ÙØ·ÙÛ 7332 * ------------------------------------------------ */ 7333 7334 if ( 7335 7336 !responseData.ok || 7337 7338 !result || 7339 7340 result.success !== true 7341 7342 ) { 7343 7344 /* 7345 * Ø¨Ø±Ú¯Ø±Ø¯Ø§ÙØ¯Ù Ù ÙØ¯Ø§Ø± ÙØ¨ÙÛ 7346 */ 7347 if (previousQuantity > 0) { 7348 7349 input.value = 7350 formatNumber( 7351 previousQuantity 7352 ); 7353 } 7354 7355 7356 /* 7357 * Ø°Ø®ÛØ±Ù Ø¯ÙØ¨Ø§Ø±Ù Ù ÙØ¯Ø§Ø± ÙØ¨ÙÛ 7358 */ 7359 input.dataset.previousQuantity = 7360 String( 7361 previousQuantity 7362 ); 7363 7364 7365 input.dataset.saving = 7366 '0'; 7367 7368 7369 /* 7370 * ÙÙ Ø§ÛØ´ Ù¾ÛØ§Ù ÙØ§ÙØ¹Û Ø³Ø±ÙØ± 7371 */ 7372 alert( 7373 7374 result && result.message 7375 7376 ? result.message 7377 7378 : 'تعداد Ù ØØµÙ٠بÙâØ±ÙØ²Ø±Ø³Ø§ÙÛ ÙØ´Ø¯.' 7379 ); 7380 7381 7382 return; 7383 } 7384 7385 7386 /* ------------------------------------------------ 7387 * SUCCESS 7388 * ------------------------------------------------ */ 7389 7390 input.dataset.previousQuantity = 7391 String( 7392 quantity 7393 ); 7394 7395 input.dataset.saving = 7396 '0'; 7397 7398 7399 console.log( 7400 'Cart quantity updated:', 7401 cartItemId, 7402 quantity 7403 ); 7404 7405 7406 /* ------------------------------------------------ 7407 * UPDATE CART ROW 7408 * ------------------------------------------------ */ 7409 7410 if ( 7411 7412 result.cart && 7413 7414 Array.isArray( 7415 result.cart.items 7416 ) 7417 7418 ) { 7419 7420 const updatedItem = 7421 7422 result.cart.items.find( 7423 7424 function (item) { 7425 7426 return String( 7427 item.cart_item_id 7428 ) === String( 7429 cartItemId 7430 ); 7431 } 7432 ); 7433 7434 7435 if (updatedItem) { 7436 7437 updateCartRow( 7438 updatedItem 7439 ); 7440 } 7441 } 7442 7443 7444 /* ------------------------------------------------ 7445 * UPDATE CART TOTAL 7446 * ------------------------------------------------ */ 7447 7448 updateCartTotal( 7449 result 7450 ); 7451 }) 7452 7453 7454 /* ==================================================== 7455 * ERROR 7456 * ==================================================== */ 7457 7458 .catch(function (error) { 7459 7460 console.error( 7461 'Cart quantity update error:', 7462 error 7463 ); 7464 7465 7466 input.dataset.saving = 7467 '0'; 7468 7469 7470 /* 7471 * در ØµÙØ±Øª Ø®Ø·Ø§Û ÙØ§ÙØ¹Û Ø´Ø¨Ú©ÙØ 7472 * HTTP ÛØ§ JavaScript 7473 * 7474 * Ù ÙØ¯Ø§Ø± ÙØ¨ÙÛ Ø±Ø§ بر٠ÛâگرداÙÛÙ . 7475 */ 7476 if (previousQuantity > 0) { 7477 7478 input.value = 7479 formatNumber( 7480 previousQuantity 7481 ); 7482 } 7483 7484 7485 input.dataset.previousQuantity = 7486 String( 7487 previousQuantity 7488 ); 7489 7490 7491 alert( 7492 7493 error.message || 7494 7495 'خطا در ارتباط با Ø³Ø±ÙØ±. ÙØ·ÙØ§Ù Ø¯ÙØ¨Ø§Ø±Ù ØªÙØ§Ø´ Ú©ÙÛØ¯.' 7496 ); 7497 }) 7498 7499 7500 /* ==================================================== 7501 * FINALLY 7502 * ==================================================== */ 7503 7504 .finally(function () { 7505 7506 input.dataset.updating = 7507 '0'; 7508 }); 7509 } 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 /* ============================================================ 7520 * GET QUANTITY INPUT 7521 *
7522 * Ù¾Ø´ØªÛØ¨Ø§ÙÛ Ø§Ø²: 7523 * 7524 * 1. Cart 7525 * input.product_number 7526 * 7527 * 2. Product page 7528 * input[name="product_number"] 7529 * ============================================================ */ 7530 7531 function getQuantityInput(element) { 7532 7533 if (!element) { 7534 return null; 7535 } 7536 7537 7538 /* ======================================================== 7539 * CART ROW 7540 * ======================================================== */ 7541 7542 const row = 7543 element.closest( 7544 '#LOXSHOPCART tr[data-cart-item-id]' 7545 ); 7546 7547 if (row) { 7548 7549 const cartInput = 7550 row.querySelector( 7551 'input.product_number, input[name="product_number"]' 7552 ); 7553 7554 if (cartInput) { 7555 return cartInput; 7556 } 7557 } 7558 7559 7560 /* ======================================================== 7561 * PRODUCT CART FORM 7562 * ======================================================== */ 7563 7564 const productForm = 7565 element.closest( 7566 'form.product-cart-form' 7567 ); 7568 7569 if (productForm) { 7570 7571 const productInput = 7572 productForm.querySelector( 7573 'input[name="product_number"]' 7574 ); 7575 7576 if (productInput) { 7577 return productInput; 7578 } 7579 } 7580 7581 7582 /* ======================================================== 7583 * FALLBACK 7584 * ======================================================== */ 7585 7586 const parent = 7587 element.parentElement; 7588 7589 if (parent) { 7590 7591 const input = 7592 parent.querySelector( 7593 'input.product_number, input[name="product_number"]' 7594 ); 7595 7596 if (input) { 7597 return input; 7598 } 7599 } 7600 7601 7602 return null; 7603 } 7604 7605 7606 /* ============================================================ 7607 * PLUS 7608 * ============================================================ */ 7609 7610 window.product_plus = function (event, button) { 7611 7612 if (event) { 7613 event.preventDefault(); 7614 event.stopPropagation(); 7615 } 7616 7617 const input = 7618 getQuantityInput(button); 7619 7620 if (!input) { 7621 7622 console.error( 7623 'Quantity input not found.' 7624 ); 7625 7626 return false; 7627 } 7628 7629 /* 7630 * Ù ÙØ¯Ø§Ø± ÙØ¹ÙÛ Ø±Ø§ ÙØ¨Ù از تغÛÛØ± ÙÚ¯Ù Ù ÛâØ¯Ø§Ø±ÛÙ 7631 */ 7632 input.dataset.previousQuantity = 7633 String( 7634 normalizeNumber(input.value) 7635 ); 7636 7637 let quantity = 7638 normalizeNumber(input.value); 7639 7640 quantity++; 7641 7642 setQuantity( 7643 input, 7644 quantity 7645 ); 7646 7647 /* 7648 * Ø°Ø®ÛØ±Ù Ø®ÙØ¯Ú©Ø§Ø± با debounce د٠ثاÙÛÙ 7649 */ 7650 scheduleCartQuantityUpdate(input); 7651 7652 return false; 7653 }; 7654 7655 7656 /* ============================================================ 7657 * MINUS 7658 * ============================================================ */ 7659 7660 window.product_minus = function (event, button) { 7661 7662 if (event) { 7663 event.preventDefault(); 7664 event.stopPropagation(); 7665 } 7666 7667 const input = 7668 getQuantityInput(button); 7669 7670 if (!input) { 7671 7672 console.error( 7673 'Quantity input not found.' 7674 ); 7675 7676 return false; 7677 } 7678 7679 /* 7680 * Ù ÙØ¯Ø§Ø± ÙØ¹ÙÛ Ø±Ø§ ÙØ¨Ù از تغÛÛØ± ÙÚ¯Ù Ù ÛâØ¯Ø§Ø±ÛÙ 7681 */ 7682 input.dataset.previousQuantity = 7683 String( 7684 normalizeNumber(input.value) 7685 ); 7686 7687 let quantity = 7688 normalizeNumber(input.value); 7689 7690 quantity--; 7691 7692 if (quantity < 1) { 7693 quantity = 1; 7694 } 7695 7696 setQuantity( 7697 input, 7698 quantity 7699 ); 7700 7701 /* 7702 * Ø°Ø®ÛØ±Ù Ø®ÙØ¯Ú©Ø§Ø± با debounce د٠ثاÙÛÙ 7703 */ 7704 scheduleCartQuantityUpdate(input); 7705 7706 return false; 7707 }; 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 /* ============================================================ 7718 * QUANTITY INPUT 7719 * ============================================================ */ 7720 document.addEventListener( 7721 'input', 7722 function (event) { 7723 7724 const input = 7725 event.target.closest( 7726 '#LOXSHOPCART input.product_number, ' + 7727 'form.product-cart-form input[name="product_number"]' 7728 ); 7729 7730 if (!input) { 7731 return; 7732 } 7733 7734 7735 let value = 7736 normalizeNumber( 7737 input.value 7738 ); 7739 7740 7741 if (value < 1) { 7742 value = 1; 7743 } 7744 7745 7746 input.value = 7747 formatNumber(value); 7748 7749 7750 if ( 7751 input.closest( 7752 '#LOXSHOPCART tr[data-cart-item-id]' 7753 ) 7754 ) { 7755 7756 scheduleCartQuantityUpdate(input); 7757 7758 } 7759 7760 7761 7762 7763 7764 } 7765 ); 7766 7767 7768 document.addEventListener( 7769 'blur', 7770 function (event) { 7771 7772 /* 7773 * اط٠ÛÙØ§Ù از اÛÙÚ©Ù target ÛÚ© Element ٠عتبر است 7774 * ٠٠تد closest را دارد. 7775 */ 7776 if ( 7777 !event.target || 7778 typeof event.target.closest !== 'function' 7779 ) { 7780 return; 7781 } 7782 7783 7784 /* 7785 * Ù¾ÛØ¯Ø§ کرد٠input تعداد Ù ØØµÙÙ 7786 */ 7787 const input = 7788 event.target.closest( 7789 '#LOXSHOPCART input.product_number, ' + 7790 'form.product-cart-form input[name="product_number"]' 7791 ); 7792 7793 7794 /* 7795 * اگر input Ù Ø±Ø¨ÙØ· ب٠تعداد Ù ØØµÙÙ ÙØ¨Ùد 7796 */ 7797 if (!input) { 7798 return; 7799 } 7800 7801 7802 /* 7803 * تبدÛÙ Ù ÙØ¯Ø§Ø± ب٠عدد Ø§Ø³ØªØ§ÙØ¯Ø§Ø±Ø¯ 7804 */ 7805 let value = 7806 normalizeNumber( 7807 input.value 7808 ); 7809 7810 7811 /* 7812 * ØØ¯Ø§Ù٠تعداد = 1 7813 */ 7814 if (value < 1) { 7815 value = 1; 7816 } 7817 7818 7819 /* 7820 * ÙØ±Ù ت کرد٠٠جدد Ù ÙØ¯Ø§Ø± 7821 */ 7822 input.value = 7823 formatNumber(value); 7824 7825 }, 7826 true 7827 ); 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 /* ============================================================ 7838 * REMOVE CART ITEM 7839 * 7840 * HTML: 7841 * 7842 * onclick="removeCartItem(this)" 7843 * 7844 * ID از TR Ú¯Ø±ÙØªÙ Ù ÛâØ´ÙØ¯: 7845 * 7846 * <tr data-cart-item-id="64"> 7847 * 7848 * ============================================================ */ 7849 7850 window.removeCartItem = function (button) { 7851 7852 if (!button) { 7853 return; 7854 } 7855 7856 const cartItemId = 7857 getCartItemId(button); 7858 7859 if (!cartItemId) { 7860 console.error( 7861 'Invalid cart item ID.' 7862 ); 7863 return; 7864 } 7865 7866 update_basket( 7867 cartItemId, 7868 'del', 7869 'cart' 7870 ); 7871 }; 7872 7873 7874 /* ============================================================ 7875 * PREPARE CART FORM 7876 * 7877 * ÙØ¨Ù از ارساÙ: 7878 * 7879 * 1,000 7880 * 7881 * تبدÛÙ Ù ÛâØ´ÙØ¯ بÙ: 7882 * 7883 * 1000 7884 * 7885 * ============================================================ */ 7886 7887 function prepareCartForm(form) { 7888 7889 if (!form) { 7890 return; 7891 } 7892 7893 const inputs = 7894 form.querySelectorAll( 7895 'input.product_number' 7896 ); 7897
7898 inputs.forEach(function (input) { 7899 7900 input.value = 7901 normalizeNumber( 7902 input.value 7903 ); 7904 7905 }); 7906 } 7907 7908 7909 /* ============================================================ 7910 * CART FORM SUBMIT 7911 * ============================================================ */ 7912 7913 document.addEventListener( 7914 'submit', 7915 function (event) { 7916 7917 const form = 7918 event.target.closest( 7919 '#form_cart' 7920 ); 7921 7922 if (!form) { 7923 return; 7924 } 7925 7926 prepareCartForm(form); 7927 7928 } 7929 ); 7930 7931 7932 /* ============================================================ 7933 * DEBUG HELPER 7934 * ============================================================ */ 7935 7936 window.LoxShopCart = { 7937 7938 getRow: function (element) { 7939 7940 return getCartRow(element); 7941 7942 }, 7943 7944 getItemId: function (element) { 7945 7946 return getCartItemId(element); 7947 7948 }, 7949 7950 getQuantity: function (element) { 7951 7952 const input = 7953 getQuantityInput(element); 7954 7955 if (!input) { 7956 return 0; 7957 } 7958 7959 return normalizeNumber( 7960 input.value 7961 ); 7962 }, 7963 7964 setQuantity: function ( 7965 element, 7966 value 7967 ) { 7968 7969 const input = 7970 getQuantityInput(element); 7971 7972 if (!input) { 7973 return; 7974 } 7975 7976 setQuantity( 7977 input, 7978 value 7979 ); 7980 } 7981 7982 }; 7983 7984 7985})(); 7986 7987 7988 7989 7990 7991 7992 7993function update_basket(cartItemId, action, type) { 7994 7995 'use strict'; 7996 7997 /* ============================================================ 7998 * VALIDATE CART ITEM ID 7999 * ============================================================ */ 8000 8001 cartItemId = parseInt(cartItemId, 10); 8002 8003 if (!cartItemId || cartItemId <= 0) { 8004 8005 console.error( 8006 'Invalid cart item ID:', 8007 cartItemId 8008 ); 8009 8010 return false; 8011 } 8012 8013 8014 /* ============================================================ 8015 * VALIDATE TYPE 8016 * ============================================================ */ 8017 8018 if (type !== 'cart') { 8019 8020 console.error( 8021 'Invalid basket type:', 8022 type 8023 ); 8024 8025 return false; 8026 } 8027 8028 8029 /* ============================================================ 8030 * MAP OLD ACTION TO NEW CHECKORDER ACTION 8031 * 8032 * update_basket(..., 'del', 'cart') 8033 * 8034 * => 8035 * 8036 * checkorder.php 8037 * action=remove_from_cart 8038 * ============================================================ */ 8039 8040 let requestAction = ''; 8041 8042 if (action === 'del') { 8043 8044 requestAction = 'remove_from_cart'; 8045 8046 } 8047 else if (action === 'remove') { 8048 8049 requestAction = 'remove_from_cart'; 8050 8051 } 8052 else { 8053 8054 console.error( 8055 'Invalid basket action:', 8056 action 8057 ); 8058 8059 return false; 8060 } 8061 8062 8063 /* ============================================================ 8064 * FIND CART ROW 8065 * ============================================================ */ 8066 8067 const row = document.querySelector( 8068 '#LOXSHOPCART tr[data-cart-item-id="' + 8069 cartItemId + 8070 '"]' 8071 ); 8072 8073 if (!row) { 8074 8075 console.error( 8076 'Cart row not found:', 8077 cartItemId 8078 ); 8079 8080 return false; 8081 } 8082 8083 8084 /* ============================================================ 8085 * CONFIRM 8086 * ============================================================ */ 8087 8088 if ( 8089 !confirm( 8090 'Ø¢ÛØ§ از ØØ°Ù اÛÙ Ù ØØµÙ٠از سبد Ø®Ø±ÛØ¯ Ù Ø·Ù Ø¦Ù ÙØ³ØªÛدØ' 8091 ) 8092 ) { 8093 8094 return false; 8095 } 8096 8097 8098 /* ============================================================ 8099 * PREVENT DOUBLE CLICK 8100 * ============================================================ */ 8101 8102 if ( 8103 row.dataset.removing === '1' 8104 ) { 8105 8106 return false; 8107 } 8108 8109 row.dataset.removing = '1'; 8110 8111 8112 /* ============================================================ 8113 * LOADING 8114 * ============================================================ */ 8115 8116 const oldOpacity = 8117 row.style.opacity; 8118 8119 const oldPointerEvents = 8120 row.style.pointerEvents; 8121 8122 row.style.opacity = '0.5'; 8123 row.style.pointerEvents = 'none'; 8124 8125 8126 /* ============================================================ 8127 * REQUEST DATA 8128 * ============================================================ */ 8129 8130 const formData = new FormData(); 8131 8132 formData.append( 8133 'action', 8134 requestAction 8135 ); 8136 8137 formData.append( 8138 'cart_item_id', 8139 String(cartItemId) 8140 ); 8141 8142 8143 /* ============================================================ 8144 * SEND REQUEST 8145 * 8146 * /shoporder/checkorder.php 8147 * ============================================================ */ 8148 8149 fetch( 8150 '/shoporder/checkorder.php', 8151 { 8152 method: 'POST', 8153 8154 body: formData, 8155 8156 credentials: 'same-origin', 8157 8158 headers: { 8159 'X-Requested-With': 8160 'XMLHttpRequest', 8161 8162 'Accept': 8163 'application/json' 8164 } 8165 } 8166 ) 8167 8168 /* ============================================================ 8169 * RESPONSE 8170 * ============================================================ */ 8171 8172 .then(function (response) { 8173 8174 if (!response.ok) { 8175 8176 throw new Error( 8177 'HTTP error: ' + 8178 response.status 8179 ); 8180 } 8181 8182 return response.json(); 8183 }) 8184 8185 8186 /* ============================================================ 8187 * JSON RESULT 8188 * ============================================================ */ 8189 8190 .then(function (result) { 8191 8192 console.log( 8193 'Cart remove response:', 8194 result 8195 ); 8196 8197 8198 /* ======================================================== 8199 * PHP ERROR 8200 * ======================================================== */ 8201 8202 if ( 8203 !result || 8204 result.success !== true 8205 ) { 8206 8207 throw new Error( 8208 result && 8209 result.message 8210 8211 ? result.message 8212 8213 : 'ØØ°Ù Ú©Ø§ÙØ§ Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 8214 ); 8215 } 8216 8217 8218 /* ======================================================== 8219 * REMOVE ROW FROM DOM 8220 * ======================================================== */ 8221 8222 row.remove(); 8223 8224 8225 /* ======================================================== 8226 * OPTIONAL: 8227 * UPDATE CART INFORMATION 8228 * 8229 * اگر بعدا٠در پاسخ PHP Ø§Ø·ÙØ§Ø¹Ø§ØªÛ Ù Ø«Ù: 8230 * 8231 * cart.total 8232 * cart.items_count 8233 * 8234 * داشت٠باشÛÙ Ø Ø§ÛÙØ¬Ø§ Ù ÛâØªÙØ§ÙÛÙ 8235 * بخشâÙØ§Û Ù Ø±Ø¨ÙØ· ب٠سبد را Ø¢Ù¾Ø¯ÛØª Ú©ÙÛÙ . 8236 * ======================================================== */ 8237 8238 if ( 8239 result.cart 8240 ) { 8241 8242 console.log( 8243 'Updated cart:', 8244 result.cart 8245 ); 8246 updateCartTotal(result); 8247 } 8248 8249 8250 /* ======================================================== 8251 * CART EMPTY 8252 * ======================================================== */ 8253 8254 const cartTable = 8255 document.querySelector( 8256 '#LOXSHOPCART' 8257 ); 8258 8259 if (cartTable) { 8260 8261 const remainingRows = 8262 cartTable.querySelectorAll( 8263 'tr[data-cart-item-id]' 8264 ); 8265 8266 if ( 8267 remainingRows.length === 0 8268 ) { 8269 8270 console.log( 8271 'Cart is now empty.' 8272 ); 8273 } 8274 } 8275 8276 }) 8277 8278 8279 /* ============================================================ 8280 * ERROR 8281 * ============================================================ */ 8282 8283 .catch(function (error) { 8284 8285 console.error( 8286 'Cart remove error:', 8287 error 8288 ); 8289 8290 8291 /* ======================================================== 8292 * RESTORE ROW 8293 * ======================================================== */ 8294 8295 row.dataset.removing = '0'; 8296 8297 row.style.opacity = 8298 oldOpacity; 8299 8300 row.style.pointerEvents = 8301 oldPointerEvents; 8302 8303 8304 /* ======================================================== 8305 * SHOW ERROR 8306 * ======================================================== */ 8307 8308 alert( 8309 error.message || 8310 'خطا در ØØ°Ù Ù ØØµÙ٠از سبد Ø®Ø±ÛØ¯.' 8311 ); 8312 }); 8313 8314 8315 return false; 8316} 8317 8318 8319 8320/* ============================================================ 8321 * LOXSHOP - CLEAR CART 8322 * ============================================================ */ 8323 8324(function () { 8325 8326 'use strict'; 8327 8328 8329 /* ============================================================ 8330 * CLEAR CART 8331 * ============================================================ */ 8332 8333 function clearCart() { 8334 8335 /* -------------------------------------------------------- 8336 * CONFIRM 8337 * -------------------------------------------------------- */ 8338 8339 if ( 8340 !confirm( 8341 '٠ط٠ئÙÛØ¯ Ù ÛâØ®ÙØ§ÙÛØ¯ سبد Ø®Ø±ÛØ¯ را خاÙÛ Ú©ÙÛØ¯Ø' 8342 ) 8343 ) { 8344 return false; 8345 } 8346 8347 8348 /* -------------------------------------------------------- 8349 * CART 8350 * -------------------------------------------------------- */ 8351 8352 const cart = 8353 document.querySelector( 8354 '#LOXSHOPCART' 8355 ); 8356 8357 if (!cart) { 8358 8359 console.error( 8360 'LOXSHOPCART not found.' 8361 ); 8362 8363 return false; 8364 } 8365 8366 8367 /* -------------------------------------------------------- 8368 * FORM 8369 * -------------------------------------------------------- */ 8370 8371 const form = 8372 cart.querySelector(
8373 '#form_cart' 8374 ); 8375 8376 if (!form) { 8377 8378 console.error( 8379 'Cart form not found.' 8380 ); 8381 8382 return false; 8383 } 8384 8385 8386 /* -------------------------------------------------------- 8387 * BUTTON 8388 * -------------------------------------------------------- */ 8389 8390 const button = 8391 form.querySelector( 8392 'button[name="command"][value="clear"]' 8393 ); 8394 8395 8396 /* -------------------------------------------------------- 8397 * LOADING 8398 * -------------------------------------------------------- */ 8399 8400 if (button) { 8401 8402 if ( 8403 button.dataset.loading === '1' 8404 ) { 8405 return false; 8406 } 8407 8408 button.dataset.loading = '1'; 8409 8410 button.disabled = true; 8411 8412 button.dataset.originalText = 8413 button.innerHTML; 8414 8415 button.innerHTML = 8416 'در ØØ§Ù خاÙÛ Ú©Ø±Ø¯Ù...'; 8417 } 8418 8419 8420 /* -------------------------------------------------------- 8421 * REQUEST 8422 * -------------------------------------------------------- */ 8423 8424 const formData = 8425 new FormData(); 8426 8427 8428 formData.append( 8429 'action', 8430 'clear_cart' 8431 ); 8432 8433 8434 /* -------------------------------------------------------- 8435 * SEND REQUEST 8436 * -------------------------------------------------------- */ 8437 8438 fetch( 8439 '/shoporder/checkorder.php', 8440 { 8441 method: 'POST', 8442 8443 body: formData, 8444 8445 credentials: 'same-origin', 8446 8447 headers: { 8448 'X-Requested-With': 8449 'XMLHttpRequest', 8450 8451 'Accept': 8452 'application/json' 8453 } 8454 } 8455 ) 8456 8457 /* -------------------------------------------------------- 8458 * RESPONSE 8459 * -------------------------------------------------------- */ 8460 8461 .then(function (response) { 8462 8463 if (!response.ok) { 8464 8465 throw new Error( 8466 'HTTP error: ' + 8467 response.status 8468 ); 8469 } 8470 8471 return response.json(); 8472 }) 8473 8474 8475 /* -------------------------------------------------------- 8476 * RESULT 8477 * -------------------------------------------------------- */ 8478 8479 .then(function (result) { 8480 8481 console.log( 8482 'Clear cart response:', 8483 result 8484 ); 8485 8486 8487 /* ---------------------------------------------------- 8488 * PHP ERROR 8489 * ---------------------------------------------------- */ 8490 8491 if ( 8492 !result || 8493 result.success !== true 8494 ) { 8495 8496 throw new Error( 8497 result && 8498 result.message 8499 8500 ? result.message 8501 8502 : 'خاÙÛ Ú©Ø±Ø¯Ù Ø³Ø¨Ø¯ Ø®Ø±ÛØ¯ Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 8503 ); 8504 } 8505 8506 8507 8508 8509 /* ============================================================ 8510 * UPDATE CART TOTAL 8511 * ============================================================ */ 8512 8513 if (result.cart) { 8514 updateCartTotal(result); 8515 } 8516 /* ---------------------------------------------------- 8517 * CART CONTENT 8518 * ---------------------------------------------------- */ 8519 8520 const cartElement = 8521 document.querySelector( 8522 '#LOXSHOPCART' 8523 ); 8524 8525 if (cartElement) { 8526 8527 /* 8528 * ت٠ا٠ردÛÙâÙØ§Û Ù ØØµÙ٠را ØØ°Ù Ù ÛâÚ©ÙÛÙ . 8529 * 8530 * ساختار HTML را تغÛÛØ± ÙÙ ÛâØ¯ÙÛÙ . 8531 */ 8532 8533 const rows = 8534 cartElement.querySelectorAll( 8535 'tr[data-cart-item-id]' 8536 ); 8537 8538 rows.forEach(function (row) { 8539 8540 row.remove(); 8541 8542 }); 8543 } 8544 8545 8546 /* ---------------------------------------------------- 8547 * SUCCESS 8548 * ---------------------------------------------------- */ 8549 8550 console.log( 8551 'Cart cleared successfully.' 8552 ); 8553 8554 8555 /* 8556 * اگر Ø¨Ø¹Ø¯Ø§Ù Ø®ÙØ§Ø³ØªÛ٠بعد از خاÙÛ Ø´Ø¯Ù Ø³Ø¨Ø¯Ø 8557 * Ú©Ù HTML سبد را از PHP Ø¨Ú¯ÛØ±ÛÙ Ø 8558 * اÛÙ ÙØ³Ù ت را Ù ÛâØªÙØ§ÙÛÙ ØªÙØ³Ø¹Ù بدÙÛÙ . 8559 */ 8560 8561 }) 8562 8563 8564 /* -------------------------------------------------------- 8565 * ERROR 8566 * -------------------------------------------------------- */ 8567 8568 .catch(function (error) { 8569 8570 console.error( 8571 'Clear cart error:', 8572 error 8573 ); 8574 8575 8576 alert( 8577 error.message || 8578 'خطا در خاÙÛ Ú©Ø±Ø¯Ù Ø³Ø¨Ø¯ Ø®Ø±ÛØ¯.' 8579 ); 8580 8581 }) 8582 8583 8584 /* -------------------------------------------------------- 8585 * FINALLY 8586 * -------------------------------------------------------- */ 8587 8588 .finally(function () { 8589 8590 if (button) { 8591 8592 button.dataset.loading = 8593 '0'; 8594 8595 button.disabled = 8596 false; 8597 8598 if ( 8599 button.dataset.originalText 8600 ) { 8601 8602 button.innerHTML = 8603 button.dataset.originalText; 8604 } 8605 } 8606 8607 }); 8608 8609 8610 return false; 8611 } 8612 8613 8614 /* ============================================================ 8615 * INTERCEPT CLEAR BUTTON 8616 * 8617 * HTML ÙØ¹ÙÛ Ø¯Ø³Øª ÙØ®Ùرد٠باÙÛ Ù ÛâÙ Ø§ÙØ¯. 8618 * 8619 * Ø¯Ú©Ù Ù ÙØ¹ÙÛ: 8620 * 8621 * name="command" 8622 * value="clear" 8623 * 8624 * ابتدا در CAPTURE Ú¯Ø±ÙØªÙ Ù ÛâØ´ÙØ¯ تا onclick ÙØ¯ÛÙ Û 8625 * Ú©Ù load_ajax را اجرا Ù ÛâÚ©ÙØ¯Ø Ø§ØµÙØ§Ù اجرا ÙØ´Ùد. 8626 * ============================================================ */ 8627 8628 document.addEventListener( 8629 'click', 8630 function (event) { 8631 8632 const button = 8633 event.target.closest 8634 ? event.target.closest(
8635 '#form_cart button[name="command"][value="clear"]' 8636 ) 8637 : null; 8638 8639 if (!button) { 8640 return; 8641 } 8642 8643 8644 /* ---------------------------------------------------- 8645 * STOP OLD load_ajax() 8646 * ---------------------------------------------------- */ 8647 8648 event.preventDefault(); 8649 8650 event.stopPropagation(); 8651 8652 event.stopImmediatePropagation(); 8653 8654 8655 /* ---------------------------------------------------- 8656 * CLEAR CART 8657 * ---------------------------------------------------- */ 8658 8659 clearCart(); 8660 8661 }, 8662 true 8663 ); 8664 8665 8666})(); 8667 8668 8669 8670 8671 8672document.addEventListener('click', function (e) { 8673 const toggle = e.target.closest('.lx_dropdown-toggle'); 8674 8675 if (toggle) { 8676 e.preventDefault(); 8677 e.stopPropagation(); 8678 toggle.classList.toggle('lx_show-dropdown'); 8679 } else if (!e.target.closest('.lx_dropdown-menu')) { 8680 document.querySelectorAll('.lx_show-dropdown').forEach(function (el) { 8681 el.classList.remove('lx_show-dropdown'); 8682 }); 8683 } 8684}); 8685 8686 8687 8688 8689 8690 8691 8692 8693document.addEventListener('click', function (e) { 8694 8695 var button = e.target.closest('.new_addres'); 8696 8697 if (!button) { 8698 return; 8699 } 8700 8701 var form = document.querySelector('.form_add_addres'); 8702 8703 if (!form) { 8704 return; 8705 } 8706 8707 form.style.display = 'block'; 8708 8709}); 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727(function () { 8728 8729 'use strict'; 8730 8731 8732 /* ================================================================ 8733 * GATEWAY CHANGE 8734 * ================================================================ */ 8735 8736 $(document).on( 8737 'change', 8738 '.pending-order-gateway', 8739 function () { 8740 8741 var $select = 8742 $(this); 8743 8744 var $box = 8745 $select.closest( 8746 '[data-pending-order-payment="1"]' 8747 ); 8748 8749 var gatewayId = 8750 $select.val(); 8751 8752 var $button = 8753 $box.find( 8754 '.pending-order-pay-btn' 8755 ); 8756 8757 var $cardTransfer = 8758 $box.find( 8759 '[data-order-card-transfer="1"]' 8760 ); 8761 8762 8763 /* 8764 * Reset 8765 */ 8766 8767 $button 8768 .prop('disabled', !gatewayId); 8769 8770 $cardTransfer 8771 .hide() 8772 .empty(); 8773 8774 $box 8775 .find('[data-payment-response="1"]') 8776 .hide() 8777 .empty(); 8778 } 8779 ); 8780 8781 8782 /* ================================================================ 8783 * PAY / RETRY 8784 * ================================================================ */ 8785 8786 $(document).on( 8787 'click', 8788 '.pending-order-pay-btn', 8789 function (e) { 8790 8791 e.preventDefault(); 8792 8793 8794 var $button = 8795 $(this); 8796 8797 var $box = 8798 $button.closest( 8799 '[data-pending-order-payment="1"]' 8800 ); 8801 8802 var orderId = 8803 parseInt( 8804 $button.attr( 8805 'data-order-id' 8806 ), 8807 10 8808 ) || 0; 8809 8810 8811 var gatewayId = 8812 parseInt( 8813 $box 8814 .find('.pending-order-gateway') 8815 .val(), 8816 10 8817 ) || 0; 8818 8819 8820 if (!orderId) { 8821 return; 8822 } 8823 8824 8825 if (!gatewayId) { 8826 alert( 8827 'ÙØ·Ùا٠درگا٠پرداخت را Ø§ÙØªØ®Ø§Ø¨ Ú©ÙÛØ¯.' 8828 ); 8829 8830 return; 8831 } 8832 8833 8834 $button 8835 .prop('disabled', true) 8836 .addClass('disabled'); 8837 8838 8839 $box 8840 .find('[data-payment-response="1"]') 8841 .hide() 8842 .empty(); 8843 8844 8845 $.ajax({
8846 8847 url: 8848 '/coreshop/ajax.php?action=retry_order_payment', 8849 8850 type: 'POST', 8851 8852 dataType: 'json', 8853 8854 data: { 8855 order_id: orderId, 8856 gateway_id: gatewayId 8857 }, 8858 8859 headers: { 8860 'X-Requested-With': 8861 'XMLHttpRequest' 8862 }, 8863 8864 success: function (response) { 8865 8866 if ( 8867 !response || 8868 !response.success 8869 ) { 8870 8871 showPendingPaymentError( 8872 $box, 8873 response && 8874 response.message 8875 ? response.message 8876 : 'پرداخت Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 8877 ); 8878 8879 $button 8880 .prop('disabled', false) 8881 .removeClass('disabled'); 8882 8883 return; 8884 } 8885 8886 8887 /* ---------------------------------------------------- 8888 * ONLINE 8889 * ---------------------------------------------------- */ 8890 8891 if ( 8892 response.type === 'online' 8893 ) { 8894 8895 if ( 8896 response.redirect_url 8897 ) { 8898 8899 window.location.href = 8900 response.redirect_url; 8901 8902 return; 8903 } 8904 8905 8906 showPendingPaymentError( 8907 $box, 8908 'آدرس درگا٠پرداخت Ø¯Ø±ÛØ§Ùت ÙØ´Ø¯.' 8909 ); 8910 8911 $button 8912 .prop('disabled', false) 8913 .removeClass('disabled'); 8914 8915 return; 8916 } 8917 8918 8919 /* ---------------------------------------------------- 8920 * CARD TO CARD 8921 * ---------------------------------------------------- */ 8922 8923 if ( 8924 response.type === 'card_to_card' 8925 ) { 8926 8927 renderPendingCardTransferForm( 8928 $box, 8929 response 8930 ); 8931 8932 8933 $button 8934 .prop('disabled', true) 8935 .addClass('disabled'); 8936 8937 return; 8938 } 8939 8940 8941 showPendingPaymentError( 8942 $box, 8943 'ÙÙØ¹ پرداخت ÙØ§Ù عتبر است.' 8944 ); 8945 8946 8947 $button 8948 .prop('disabled', false) 8949 .removeClass('disabled'); 8950 }, 8951 8952 error: function (xhr) { 8953 8954 var message = 8955 'خطا در Ø¨Ø±ÙØ±Ø§Ø±Û ارتباط با Ø³Ø±ÙØ±.'; 8956 8957 8958 if ( 8959 xhr.responseJSON && 8960 xhr.responseJSON.message 8961 ) { 8962 message = 8963 xhr.responseJSON.message; 8964 } 8965 8966 8967 showPendingPaymentError( 8968 $box, 8969 message 8970 ); 8971 8972 8973 $button 8974 .prop('disabled', false) 8975 .removeClass('disabled'); 8976 } 8977 }); 8978 } 8979 ); 8980 8981 8982 /* ================================================================ 8983 * CARD TO CARD FORM 8984 * ================================================================ */ 8985 8986 function renderPendingCardTransferForm( 8987 $box, 8988 response 8989 ) { 8990 8991 var $container = 8992 $box.find( 8993 '[data-order-card-transfer="1"]' 8994 ); 8995 8996 8997 var gateway = 8998 response.gateway || {}; 8999 9000 var settings = 9001 gateway.settings || {}; 9002 9003 9004 var amount = 9005 parseFloat( 9006 response.amount || 0 9007 ); 9008 9009 9010 var amountFormatted = 9011 numberFormat( 9012 amount 9013 ); 9014 9015 9016 var bankName = 9017 settings.bank_name || ''; 9018 9019 var cardNumber = 9020 settings.card_number || ''; 9021 9022 var cardHolder = 9023 settings.card_holder || ''; 9024 9025 var iban = 9026 settings.iban || ''; 9027 9028 var gatewayDescription = 9029 settings.description || ''; 9030 9031 9032 var html = ''; 9033 9034 html += 9035 '<div class="shop-order-card-transfer-form">'; 9036 9037 9038 html += 9039 '<div class="card-transfer-header">'; 9040 9041 html += 9042 '<strong>ثبت Ø§Ø·ÙØ§Ø¹Ø§Øª کارت ب٠کارت</strong>'; 9043 9044 html += 9045 '</div>'; 9046 9047 if (gatewayDescription) { 9048 9049 html += 9050 '<div class="alert alert-info mb-3">'; 9051 9052 html += 9053 escapeHtml( 9054 gatewayDescription 9055 ); 9056 9057 html += 9058 '</div>'; 9059 } 9060 if (bankName) { 9061 9062 html += 9063 '<div class="mb-2">'; 9064 9065 html += 9066 '<strong>باÙÚ©:</strong> '; 9067 9068 html += 9069 escapeHtml(bankName); 9070 9071 html += 9072 '</div>'; 9073 } 9074 9075 9076 if (cardNumber) { 9077 9078 html += 9079 '<div class="mb-2">'; 9080 9081 html += 9082 '<strong>ش٠ار٠کارت:</strong> '; 9083 9084 html += 9085 '<span dir="ltr">' 9086 + escapeHtml(cardNumber) 9087 + '</span>'; 9088 9089 html += 9090 '</div>'; 9091 } 9092 9093 9094 if (cardHolder) { 9095 9096 html += 9097 '<div class="mb-2">'; 9098 9099 html += 9100 '<strong>Ø¨Ù ÙØ§Ù :</strong> '; 9101 9102 html += 9103 escapeHtml(cardHolder); 9104 9105 html += 9106 '</div>'; 9107 } 9108 9109 9110 if (iban) { 9111 9112 html += 9113 '<div class="mb-2">'; 9114 9115 html += 9116 '<strong>شبا:</strong> '; 9117 9118 html += 9119 '<span dir="ltr">' 9120 + escapeHtml(iban) 9121 + '</span>'; 9122 9123 html += 9124 '</div>'; 9125 } 9126 9127 9128 html += 9129 '<div class="mb-3">'; 9130 9131 html += 9132 '<strong>Ù Ø¨ÙØº ÙØ§Ø¨Ù پرداخت:</strong> '; 9133 9134 html += 9135 '<span>' 9136 + amountFormatted 9137 + ' تÙ٠اÙ' 9138 + '</span>'; 9139 9140 html += 9141 '</div>'; 9142 9143 9144 9145 9146 9147 html += 9148 '<form' 9149 + ' class="pending-order-card-transfer-form"' 9150 + ' enctype="multipart/form-data"' 9151 + '>'; 9152 9153 html += 9154 '<input' 9155 + ' type="hidden"' 9156 + ' name="order_id"' 9157 + ' value="' 9158 + parseInt(response.order_id, 10) 9159 + '">'; 9160 9161 html += 9162 '<input' 9163 + ' type="hidden"' 9164 + ' name="payment_id"' 9165 + ' value="' 9166 + parseInt(response.payment_id, 10) 9167 + '">'; 9168 9169 9170 /* ------------------------------------------------------------ 9171 * DATE 9172 * ------------------------------------------------------------ */ 9173 9174 html += 9175 '<div class="mb-3">'; 9176 9177 html += 9178 '<label>ØªØ§Ø±ÛØ® پرداخت</label>'; 9179 9180 html += 9181 '<input' 9182 + ' type="text"' 9183 + ' name="paid_date"' 9184 + ' class="form-control"' 9185 + ' placeholder="1405/06/19"' 9186 + ' required>'; 9187 9188 html += 9189 '</div>'; 9190 9191 9192 /* ------------------------------------------------------------ 9193 * TIME 9194 * ------------------------------------------------------------ */ 9195 9196 html += 9197 '<div class="mb-3">'; 9198 9199 html += 9200 '<label>ساعت پرداخت</label>'; 9201 9202 html += 9203 '<input' 9204 + ' type="text"' 9205 + ' name="paid_time"' 9206 + ' class="form-control"' 9207 + ' value="12:00"' 9208 + ' required>'; 9209 9210 html += 9211 '</div>'; 9212 9213 9214 /* ------------------------------------------------------------ 9215 * REFERENCE 9216 * ------------------------------------------------------------ */ 9217 9218 html += 9219 '<div class="mb-3">'; 9220 9221 html += 9222 '<label>ش٠ار٠پÛÚ¯ÛØ±Û / کد ٠رجع</label>'; 9223 9224 html += 9225 '<input' 9226 + ' type="text"' 9227 + ' name="reference_code"' 9228 + ' class="form-control"' 9229 + ' required>'; 9230 9231 html += 9232 '</div>'; 9233 9234 9235 /* ------------------------------------------------------------ 9236 * SENDER NAME 9237 * ------------------------------------------------------------ */ 9238 9239 html += 9240 '<div class="mb-3">'; 9241 9242 html += 9243 '<label>ÙØ§Ù پرداختâÚ©ÙÙØ¯Ù</label>'; 9244 9245 html += 9246 '<input' 9247 + ' type="text"' 9248 + ' name="sender_name"' 9249 + ' class="form-control"' 9250 + ' required>'; 9251 9252 html += 9253 '</div>'; 9254 9255 9256 /* ------------------------------------------------------------ 9257 * CARD LAST 4 9258 * ------------------------------------------------------------ */ 9259 9260 html += 9261 '<div class="mb-3">'; 9262 9263 html += 9264 '<label>ÚÙØ§Ø± رÙ٠آخر کارت</label>'; 9265 9266 html += 9267 '<input' 9268 + ' type="text"' 9269 + ' name="sender_card_last4"' 9270 + ' maxlength="4"' 9271 + ' class="form-control"' 9272 + ' inputmode="numeric">'; 9273 9274 html += 9275 '</div>'; 9276 9277 9278 /* ------------------------------------------------------------ 9279 * DESCRIPTION 9280 * ------------------------------------------------------------ */ 9281 9282 html += 9283 '<div class="mb-3">'; 9284 9285 html += 9286 '<label>ØªÙØ¶ÛØØ§Øª</label>'; 9287 9288 html += 9289 '<textarea' 9290 + ' name="description"' 9291 + ' class="form-control"' 9292 + ' rows="3">' 9293 + '</textarea>'; 9294 9295 html += 9296 '</div>'; 9297 9298 9299 /* ------------------------------------------------------------ 9300 * RECEIPT 9301 * ------------------------------------------------------------ */ 9302 9303 html += 9304 '<div class="mb-3">'; 9305 9306 html += 9307 '<label>تصÙÛØ± Ø±Ø³ÛØ¯ پرداخت</label>'; 9308 9309 html += 9310 '<input' 9311 + ' type="file"' 9312 + ' name="receipt"' 9313 + ' class="form-control"' 9314 + ' accept="image/jpeg,image/png,image/webp">'; 9315 9316 html += 9317 '<small class="text-muted">' 9318 + 'ØØ¯Ø§Ú©Ø«Ø± ØØ¬Ù : Ûµ Ù Ú¯Ø§Ø¨Ø§ÛØª' 9319 + '</small>'; 9320 9321 html += 9322 '</div>'; 9323 9324 9325 /* ------------------------------------------------------------ 9326 * SUBMIT 9327 * ------------------------------------------------------------ */ 9328 9329 html += 9330 '<button' 9331 + ' type="submit"' 9332 + ' class="btn btn-success pending-order-submit-card-transfer">' 9333 + '<i class="fa fa-check"></i> ' 9334 + 'ثبت Ø§Ø·ÙØ§Ø¹Ø§Øª پرداخت' 9335 + '</button>'; 9336 9337 9338 html += 9339 '<div' 9340 + ' class="pending-order-card-transfer-response mt-3"' 9341 + ' style="display:none;">' 9342 + '</div>'; 9343 9344 9345 html += 9346 '</form>'; 9347 9348 9349 html += 9350 '</div>'; 9351 9352 9353 $container 9354 .html(html) 9355 .slideDown(); 9356 } 9357 9358 9359 /* ================================================================ 9360 * SUBMIT CARD TO CARD 9361 * ================================================================ */ 9362 9363 $(document).on( 9364 'submit', 9365 '.pending-order-card-transfer-form', 9366 function (e) { 9367 9368 e.preventDefault(); 9369 9370 9371 var $form = 9372 $(this); 9373 9374 var $box = 9375 $form.closest(
9376 '[data-pending-order-payment="1"]' 9377 ); 9378 9379 var $submit = 9380 $form.find( 9381 '.pending-order-submit-card-transfer' 9382 ); 9383 9384 var $response = 9385 $form.find( 9386 '.pending-order-card-transfer-response' 9387 ); 9388 9389 9390 var formData = 9391 new FormData( 9392 this 9393 ); 9394 9395 9396 $submit 9397 .prop('disabled', true) 9398 .addClass('disabled'); 9399 9400 9401 $response 9402 .hide() 9403 .empty(); 9404 9405 9406 $.ajax({ 9407 9408 url: 9409 '/coreshop/ajax.php?action=submit_order_payment_submission', 9410 9411 type: 'POST', 9412 9413 data: 9414 formData, 9415 9416 processData: 9417 false, 9418 9419 contentType: 9420 false, 9421 9422 dataType: 9423 'json', 9424 9425 headers: { 9426 'X-Requested-With': 9427 'XMLHttpRequest' 9428 }, 9429 9430 success: function (response) { 9431 9432 if ( 9433 response && 9434 response.success 9435 ) { 9436 9437 $response 9438 .html( 9439 '<div class="alert alert-success">' 9440 + escapeHtml( 9441 response.message || 9442 'Ø§Ø·ÙØ§Ø¹Ø§Øª پرداخت با Ù ÙÙÙÛØª ثبت شد.' 9443 ) 9444 + '</div>' 9445 ) 9446 .show(); 9447 9448 9449 $form 9450 .find('input, textarea, button, select') 9451 .prop('disabled', true); 9452 9453 9454 return; 9455 } 9456 9457 9458 $response 9459 .html( 9460 '<div class="alert alert-danger">' 9461 + escapeHtml( 9462 response && 9463 response.message 9464 ? response.message 9465 : 'ثبت Ø§Ø·ÙØ§Ø¹Ø§Øª پرداخت Ø§ÙØ¬Ø§Ù ÙØ´Ø¯.' 9466 ) 9467 + '</div>' 9468 ) 9469 .show(); 9470 9471 9472 $submit 9473 .prop('disabled', false) 9474 .removeClass('disabled'); 9475 }, 9476 9477 error: function (xhr) { 9478 9479 var message = 9480 'خطا در ثبت Ø§Ø·ÙØ§Ø¹Ø§Øª پرداخت.'; 9481 9482 9483 if ( 9484 xhr.responseJSON && 9485 xhr.responseJSON.message 9486 ) { 9487 message = 9488 xhr.responseJSON.message; 9489 } 9490 9491 9492 $response 9493 .html( 9494 '<div class="alert alert-danger">' 9495 + escapeHtml(message) 9496 + '</div>' 9497 ) 9498 .show(); 9499 9500 9501 $submit 9502 .prop('disabled', false) 9503 .removeClass('disabled'); 9504 } 9505 }); 9506 } 9507 ); 9508 9509 9510 /* ================================================================ 9511 * HELPERS 9512 * ================================================================ */ 9513 9514 function showPendingPaymentError( 9515 $box, 9516 message 9517 ) { 9518 9519 $box 9520 .find('[data-payment-response="1"]') 9521 .html( 9522 '<div class="alert alert-danger">' 9523 + escapeHtml(message) 9524 + '</div>' 9525 ) 9526 .show(); 9527 } 9528 9529 9530 function numberFormat(number) { 9531 9532 number = 9533 parseFloat(number) || 0; 9534 9535 return number
9536 .toLocaleString('en-US', { 9537 maximumFractionDigits: 0 9538 }); 9539 } 9540 9541 9542 function escapeHtml(value) { 9543 9544 return $('<div>') 9545 .text(value == null ? '' : value) 9546 .html(); 9547 } 9548 9549})(); 9550 9551 9552 9553$(document).on( 9554 'click', 9555 '.lx-color-swatch', 9556 function (e) { 9557 9558 e.preventDefault(); 9559 9560 var $swatch = $(this); 9561 9562 var value = String( 9563 $swatch.attr('data-value') || '' 9564 ); 9565 9566 var $variationValues = 9567 $swatch.closest('.variation-values'); 9568 9569 var $select = 9570 $variationValues.find( 9571 'select.product_var' 9572 ); 9573 9574 if (!$select.length) { 9575 return; 9576 } 9577 9578 /* 9579 |-------------------------------------------------------------------------- 9580 | Set SELECT value 9581 |-------------------------------------------------------------------------- 9582 */ 9583 9584 $select.val(value); 9585 9586 /* 9587 |-------------------------------------------------------------------------- 9588 | Update visual active state 9589 |-------------------------------------------------------------------------- 9590 */ 9591 9592 $variationValues 9593 .find('.lx-color-swatch') 9594 .removeClass('active'); 9595 9596 $swatch.addClass('active'); 9597 9598 /* 9599 |-------------------------------------------------------------------------- 9600 | Native change event 9601 |-------------------------------------------------------------------------- 9602 */ 9603 9604 var selectElement = 9605 $select.get(0); 9606 9607 if (selectElement) { 9608 9609 selectElement.dispatchEvent( 9610 new Event( 9611 'change', 9612 { 9613 bubbles: true 9614 } 9615 ) 9616 ); 9617 9618 } 9619 9620 } 9621); 9622 9623 9624 9625 9626if( document.getElementsByClassName('product_cat')[0] ) 9627{ 9628 var $selected; 9629 var $selected2; 9630 var $selected3; 9631 const pp = new URL(window.location); 9632 const c = new URLSearchParams(pp.search); 9633 var jsonElement = document.getElementById('list_category'); 9634 var list_cat = JSON.parse(jsonElement.textContent); 9635 if(list_cat) 9636 { 9637 var list_option = '<option value="0" selected="selected">Ø¯Ø³ØªÙ Ø¨ÙØ¯Û</option>'; 9638 Object.entries(list_cat).forEach(([key, value]) => 9639 { 9640 if( c.get("cat") == key ) $selected = 'selected';else $selected = '' 9641 list_option += '<option class="level-0" value="'+key+'" '+$selected+'>'+value['title']+'</option>'; 9642 if( value['child'] ) 9643 { 9644 Object.entries(value['child']).forEach(([key2, value2]) => 9645 { 9646 if( value2['child'] ) 9647 { 9648 if( c.get("cat") == key2 ) $selected2 = 'selected';else $selected2 = '' 9649 list_option += '<option class="level-1" value="'+key2+'" style="padding-right:15px" '+$selected2+'> '+value2['title']+'</option>'; 9650 Object.entries(value2['child']).forEach(([key3, value3]) => 9651 { 9652 if( c.get("cat") == key3 ) $selected3 = 'selected';else $selected3 = '' 9653 list_option += '<option class="level-2" value="'+key3+'" style="padding-right:25px" '+$selected3+'> '+value3['title']+'</option>'; 9654 }) 9655 } 9656 }) 9657 } 9658 }) 9659 var elems = document.getElementsByClassName('product_cat'); 9660 for (let i = 0; i < elems.length; i++) 9661 { 9662 elems[i].innerHTML = list_option; 9663 } 9664 } 9665} 9666 9667 9668 9669 9670
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.