HEX
Server: LiteSpeed
System: Linux linux31.centraldnserver.com 4.18.0-553.83.1.lve.el8.x86_64 #1 SMP Wed Nov 12 10:04:12 UTC 2025 x86_64
User: salamatk (1501)
PHP: 8.1.33
Disabled: show_source, system, shell_exec, passthru, exec, popen, proc_open
Upload Files
File: /home/salamatk/takarzan.ir/wp-content/themes/xts-luman/js/scripts/ptSubscribeForm.js
/* global xts_settings, XTSThemeModule, jQuery */
XTSThemeModule.ptSubscribeForm = function() {
	var signedProducts      = [];
	var firstSignedProducts = [];

	async function init() {
		var notifierBtn = getNotifierBtn();

		if (!notifierBtn) return;

		if ('yes' === xts_settings.pt_fragments_enable) {
			const ids  = getProductAndVariationId();
			const data = await fetchSignedProducts(ids.productId);

			if (data) {
				if (data.signed_variations && data.signed_variations.length > 0) {
					firstSignedProducts = data.signed_variations;
				} else if (data.is_signed) {
					firstSignedProducts.push(ids.productId);
					signedProducts.push(ids.productId);
				}

				notifierBtn.classList.remove('xts-disabled');
			}

			renderNotifierUI(notifierBtn);
		} else {
			var variationsForm = getVariationsForm();

			if (variationsForm) {
				firstSignedProducts = JSON.parse(notifierBtn.dataset.signedVariations || '[]');
			}

			renderNotifierUI(notifierBtn);
		}

		setupEventListeners();
	}	

	/**
	 * Updates the notifier button UI based on current variation state.
	 *
	 * @param {HTMLElement} notifierBtn
	 */
	function renderNotifierUI(notifierBtn) {
		const ids = getProductAndVariationId();

		if (ids.variationId && firstSignedProducts.includes(ids.variationId)) {
			notifierBtn.classList.remove('xts-hide');
		}
	}

	/**
	 * Sets up all event listeners for notifier button, popup, and variations form.
	 */
	function setupEventListeners() {
		var notifierBtn    = getNotifierBtn();
		var popupContent   = getPopupContent();
		var variationsForm = getVariationsForm();

		if (!notifierBtn) {
			return;
		}

		if (notifierBtn.classList.contains('xts-pt-remove')) {
			notifierBtn.addEventListener('click', handleUnsubscribe);
		}

		if (popupContent) {
			var subscribeBtn           = popupContent.querySelector('.xts-pt-add');
			var policyCheckInput       = popupContent.querySelector('[name="xts-pt-policy-check"]');
			var desiredPriceCheckInput = popupContent.querySelector('[name="xts-pt-desired-price-check"]');
			var desiredPriceInput      = popupContent.querySelector('[name="xts-pt-user-desired-price"]');
			var closePopupBtn          = popupContent.querySelector('.xts-close-popup');

			subscribeBtn.addEventListener('click', handleSubscribe);

			// Remove notice when magnificPopup closes.
			jQuery(document).one('mfpClose', function() {
				maybeClearNotices();
			});

			// Remove notice when policyCheckInput is checked.
			if (policyCheckInput) {
				const removeNoticeOnCheck = function() {
					if (policyCheckInput.checked) {
						maybeClearNotices(xts_settings.pt_policy_check_msg);
					}
				};

				policyCheckInput.addEventListener('change', removeNoticeOnCheck);
			}

			if (desiredPriceCheckInput && desiredPriceInput) {
				// Set desired price check input when desired price input is clicked.
				desiredPriceInput.addEventListener('click', function(e) {
					desiredPriceCheckInput.checked = true;
				});

				// Clear desired price input when desired price check input is unchecked.
				desiredPriceCheckInput.addEventListener('change', function() {
					if (!desiredPriceCheckInput.checked) {
						desiredPriceInput.value = '';
					} else {
						desiredPriceInput.focus();
					}
				});
			}

			// Close popup when close button is clicked.
			closePopupBtn.addEventListener('click', function(e) {
				e.preventDefault();

				jQuery.magnificPopup.close();
			});
		} else if (notifierBtn.classList.contains('xts-pt-add')) {
			notifierBtn.addEventListener('click', handleSubscribe);
		}

		if (variationsForm) {
			jQuery('.variations_form')
				.off('show_variation', handleFoundVariation)
				.on('show_variation', handleFoundVariation)
				.off('click', '.reset_variations', handleResetVariations)
				.on('click', '.reset_variations', handleResetVariations);
		}
	}

	/**
	 * Handles faund variation event.
	 *
	 * @param {Event} e
	 * @param {Object} variation
	 */
	function handleFoundVariation(e, variation) {
		var notifierBtn  = getNotifierBtn();
		var popupContent = getPopupContent();

		if (!notifierBtn) {
			return;
		}

		if (popupContent) {
			updatePopupContent(variation.variation_id);
		}

		updateNotifierBtn(variation.variation_id);

		if (variation.is_in_stock) {
			notifierBtn.classList.remove('xts-hide');
		} else {
			notifierBtn.classList.add('xts-hide');
		}
		
		maybeClearNotices();
	}

	/**
	 * Handles reset variations event.
	 *
	 * @param {Event} e
	 */
	function handleResetVariations(e) {
		var notifierBtn = getNotifierBtn();

		notifierBtn.classList.add('xts-hide');

		maybeClearNotices();
	}

	/**
	 * Handles subscribe button click event.
	 *
	 * @param {Event} e
	 */
	function handleSubscribe(e) {
		if (this.classList.contains('xts-pt-remove')) {
			return;
		}

		e.preventDefault();

		var popupContent = getPopupContent();

		if (popupContent) {
			if (! validateForm()) {
				return;
			}
		}

		var ids              = getProductAndVariationId();
		var userEmail        = getUserEmail();
		var userDesiredPrice = getUserDesiredPrice();

		sendNotifierForm({
			action       : 'xts_add_to_price_tracker',
			security     : xts_settings.pt_add_button_nonce,
			user_email   : userEmail,
			product_id   : ids.productId,
			variation_id : ids.variationId,
			desired_price : userDesiredPrice,
		});
	}

	/**
	 * Handles unsubscribe button click event.
	 *
	 * @param {Event} e
	 */
	function handleUnsubscribe(e) {
		if (!this.classList.contains('xts-pt-remove')) {
			return;
		}

		e.preventDefault();

		var ids         = getProductAndVariationId();
		var productId   = parseInt(ids.productId);
		var variationId = parseInt(ids.variationId); 

		sendNotifierForm({
			action       : 'xts_remove_from_price_tracker',
			security     : xts_settings.pt_remove_button_nonce,
			product_id   : productId,
			variation_id : variationId,
		});
	}

	/**
	 * Updates notifier button UI for a specific variation.
	 *
	 * @param {number} variationId
	 */
	function updateNotifierBtn(variationId) {
		var notifierBtn     = getNotifierBtn();
		var popupContent    = getPopupContent();
		var notifierBtnLink = notifierBtn.querySelector('a');
		var notifierBtnText = notifierBtnLink.querySelector('.xts-action-text');

		if (firstSignedProducts.includes(variationId)) {
			notifierBtnText.innerText = xts_settings.pt_button_text_stop_tracking;
			notifierBtnLink.href      = '#';
			notifierBtnLink.classList.remove('added');

			notifierBtn.classList.add('xts-pt-remove');
			notifierBtn.classList.remove('xts-pt-add');

			notifierBtn.addEventListener('click', handleUnsubscribe);

			notifierBtnLink.classList.remove('xts-open-popup');
		} else if (signedProducts.includes(variationId)) {
			notifierBtnText.innerText = xts_settings.pt_button_text_watch_tracking;
			notifierBtnLink.href      = xts_settings.pt_endpoint_url;
			notifierBtnLink.classList.add('added');

			notifierBtn.classList.remove('xts-pt-remove');
			notifierBtn.classList.remove('xts-pt-add');

			notifierBtn.removeEventListener('click', handleSubscribe);

			notifierBtnLink.classList.remove('xts-open-popup');
		} else {
			notifierBtnText.innerText = xts_settings.pt_button_text_not_tracking;

			notifierBtn.classList.remove('xts-pt-remove');
			notifierBtnLink.classList.remove('xts-open-popup');
			notifierBtnLink.classList.remove('added');

			if (popupContent) {
				notifierBtnLink.href = '#xts-popup-pt';

				notifierBtnLink.classList.add('xts-open-popup');
			} else {
				notifierBtnLink.href = '#';
				notifierBtn.classList.add('xts-pt-add');

				notifierBtn.addEventListener('click', handleSubscribe);
			}
		}
	}

	/**
	 * Updates popup content for a specific variation.
	 *
	 * @param {number} variationId
	 */
	function updatePopupContent(variationId) {
		var popupContent = getPopupContent();

		if (signedProducts.includes(variationId)) {
			popupContent.querySelector('.xts-pt-signed').classList.remove('xts-hide');
			popupContent.querySelector('.xts-pt-not-signed').classList.add('xts-hide');
		} else {
			popupContent.querySelector('.xts-pt-signed').classList.add('xts-hide');
			popupContent.querySelector('.xts-pt-not-signed').classList.remove('xts-hide');
		}
	}

	/**
	 * Updates signed products based on the current state.
	 *
	 * @param {string} state
	 * @param {number} productId
	 */
	function updateSignedProducts(state, productId) {
		if ('signed' === state) {
			if (!signedProducts.includes(productId)) {
				signedProducts.push(productId);
			}
		} else if ('not-signed' === state) {
			if (signedProducts.includes(productId)) {
				signedProducts = signedProducts.filter(function(id) {
					return id !== productId;
				});
			}

			if (firstSignedProducts.includes(productId)) {
				firstSignedProducts = firstSignedProducts.filter(function(id) {
					return id !== productId;
				});
			}
		}
	}

	/**
	 * Validates the popup form (policy checkbox).
	 *
	 * @returns {boolean}
	 */
	function validateForm() {
		var popupContent = getPopupContent();

		if (!popupContent) {
			return false;
		}

		var policyCheckInput       = popupContent.querySelector('[name="xts-pt-policy-check"]');
		var desiredPriceCheckInput = popupContent.querySelector('[name="xts-pt-desired-price-check"]');
		var desiredPriceInput      = popupContent.querySelector('[name="xts-pt-user-desired-price"]');
		var noticesAria            = getNoticeAria();

		if (policyCheckInput && ! policyCheckInput.checked && noticesAria) {
			addNotice(noticesAria, xts_settings.pt_policy_check_msg, 'error');

			return false;
		}

		if (desiredPriceCheckInput && desiredPriceInput && desiredPriceCheckInput.checked && ! parseFloat( desiredPriceInput.value ) ) {
			addNotice(noticesAria, xts_settings.pt_desired_price_check_msg, 'error');

			return false;
		}

		return true;
	}

	/**
	 * Sends AJAX request to subscribe/unsubscribe and updates UI.
	 *
	 * @param {Object} data
	 */
	function sendNotifierForm(data) {
		var popupContent    = getPopupContent();
		var noticesAria     = getNoticeAria();
		var notifierBtn     = getNotifierBtn();
		var notifierBtnLink = notifierBtn.querySelector('a');
		var ids             = getProductAndVariationId();
		var productId       = ids.variationId ? ids.variationId : ids.productId;

		maybeClearNotices();

		if (popupContent) {
			var loaderOverlay = popupContent.querySelector('.xts-loader-overlay');

			loaderOverlay.classList.add('xts-loading');
		}

		notifierBtnLink.classList.add('loading');

		jQuery.ajax({
			url     : xts_settings.ajaxurl,
			data,
			method  : 'POST',
			success : function(response) {
				if (!response || !response.hasOwnProperty('data')) {
					return;
				}

				if (response.data.notice && noticesAria) {
					var status = response.data.success ? 'message' : 'error';

					addNotice(noticesAria, response.data.notice, status);
				}

				if (response.data.state) {
					updateSignedProducts(response.data.state, productId);
				}

				if (popupContent) {
					updatePopupContent(productId);
				}

				updateNotifierBtn(productId);
			},
			error   : function() {
				console.error('ajax adding to price tracker error');
			},
			complete: function() {
				if (popupContent) {
					loaderOverlay = popupContent.querySelector('.xts-loader-overlay');

					loaderOverlay.classList.remove('xts-loading');
				}

				notifierBtnLink.classList.remove('loading');
			}
		});
	}

	/**
	 * Fetches signed variations for a product via AJAX.
	 *
	 * @param {number|string} productId
	 *
	 * @returns {Promise<Object|undefined>}
	 */
	async function fetchSignedProducts(productId) {
		try {
			const data = await jQuery.ajax({
				url     : xts_settings.ajaxurl,
				data    : {
					action    : 'xts_update_price_tracker_form',
					product_id: productId,
				},
				dataType: 'json',
				method  : 'GET',
			});

			return data;
		} catch (error) {
			console.error('Error updating form data:', error);
		}
	}

	/**
	 * Gets current product and variation IDs from DOM.
	 *
	 * @returns {{productId: string|number, variationId: number}}
	 */
	function getProductAndVariationId() {
		var productId = false;

		document.querySelector('body[class*="postid-"]').classList.forEach(function(className) {
			if ( ! className.includes('postid-') ) {
				return;
			}

			productId = className.replace('postid-', '')
		});

		var variations_form = getVariationsForm();
		var variationId     = 0;

		if (variations_form) {
			var variationIdInput = variations_form.querySelector('input.variation_id');
			variationId  = variationIdInput.value ? parseInt(variationIdInput.value) : 0;
		}

		return {productId: parseInt(productId), variationId: parseInt(variationId)};
	}

	/**
	 * Retrieves the value of the user's email from the price tracker subscription form input.
	 *
	 * @returns {string} The user's email address if the input exists, otherwise an empty string.
	 */
	function getUserEmail() {
		var userEmail      = '';
		var userEmailInput = document.querySelector('[name="xts-pt-user-subscribe-email"]');

		if (userEmailInput) {
			userEmail = userEmailInput.value;
		}

		return userEmail;
	}

	/**
	 * Retrieves the value of the user's desired price from the price tracker subscription form input.
	 *
	 * @returns {string} The user's desired price if the input exists, otherwise an empty string.
	 */
	function getUserDesiredPrice() {
		var userDesiredPrice      = '';
		var userDesiredPriceInput = document.querySelector('[name="xts-pt-user-desired-price"]');

		if (userDesiredPriceInput) {
			userDesiredPrice = userDesiredPriceInput.value;
		}

		return userDesiredPrice;
	}

	/**
	 * Adds a notice message to the popup form.
	 *
	 * @param {HTMLElement} noticesAria
	 * @param {string} message
	 * @param {string} status
	 */
	function addNotice(noticesAria, message, status) {
		if (!noticesAria) {
			return;
		}

		maybeClearNotices();

		var noticeNode = document.createElement("div");

		noticeNode.classList.add(
			`woocommerce-${status}`,
		);

		noticeNode.append(message);
		noticesAria.append(noticeNode);
	}

	/**
	 * Returns the DOM element of the price drop button.
	 *
	 * @returns {HTMLElement|null}
	 */
	function getNotifierBtn() {
		return document.querySelector('.xts-pt-btn');
	}

	/**
	 * Returns the DOM element of the price drop subscription popup.
	 *
	 * @returns {HTMLElement|null}
	 */
	function getPopupContent() {
		return document.querySelector('#xts-popup-pt');
	}

	/**
	 * Returns the DOM element of the variations form.
	 *
	 * @returns {HTMLElement|null}
	 */
	function getVariationsForm() {
		return document.querySelector('.variations_form');
	}

	/**
	 * Returns the DOM element of the notices wrapper.
	 *
	 * @returns {HTMLElement|null}
	 */
	function getNoticeAria() {
		var popupContent = getPopupContent();

		if (popupContent && popupContent.closest('.mfp-ready')) {
			return popupContent;
		} else {
			return document.querySelector('.woocommerce-notices-wrapper');
		}
	}

	/**
	 * Removes the first element with the class 'woocommerce-error' from the notice area, if it exists.
	 * Utilizes the getNoticeAria function to locate the notice area in the DOM.
	 *
	 * @param {string} message
	 */
	function maybeClearNotices(message = '') {
		var noticesAria = getNoticeAria();

		if (!noticesAria) {
			return;
		}

		var noticeNodes = noticesAria.querySelectorAll('.woocommerce-error');

		if ( 0 === noticeNodes.length) {
			return;
		}

		noticeNodes.forEach(noticeNode => {
			if (!message || (message && noticeNode.innerText.includes(message))) {
				noticeNode.remove();
			}
		});
	}

	XTSThemeModule.$document.on('click', '.xts-pt-btn .xts-open-popup', function(e) {
		e.preventDefault();

		if (jQuery.magnificPopup?.instance?.isOpen) {
			jQuery.magnificPopup.instance.st.removalDelay = 0
			jQuery.magnificPopup.close()
		}

		jQuery.magnificPopup.open({
			items: {
				src         : getPopupContent(),
			},
			type        : 'inline',
			removalDelay: 400,
			tClose      : xts_settings.magnific_close,
			tLoading    : xts_settings.magnific_loading,
			preloader   : false,
			callbacks   : {
				beforeOpen: function() {
					this.st.mainClass = 'xts-popup-effect';
				},
				open      : function() {
					XTSThemeModule.$document.trigger('xtsImagesLoaded');
					XTSThemeModule.$window.resize();
				},
				close: function() {
					maybeClearNotices();
				}
			}
		});

		XTSThemeModule.$document.trigger('xtsImagesLoaded');
	});

	init();
}

window.addEventListener('load', function() {
	XTSThemeModule.ptSubscribeForm();
});