https://www.pumaslove.com/seo/resource/id/c0da1be46ddba6eaaf24d701675f643d.js?ver=1640949085

Last Checked: Jun 16, 2023, 22:21 EDT

IP Address: 69.192.139.233
ASN #: AS20940 AKAMAI-ASN1, NL
Location: Unknown, Unknown, Unknown
URL Reputation:
  • Unknown This URL is not identified as malicious in the PhishTank Database.
  • Unknown PhishCheck thinks this URL is likely not a phish.
  • Unknown OpenPhish: URL not in feed.

Other submissions on 69.192.139.233:

  • https://www.flirtysparks.com/aff.php?dynamicpage=all_wlp_5st_vid_a_d_sound&utm_bo=1&a_bid=4fb1bd2a&utm_source=facebook&utm_campaign=crrsale&utm_content=21090&tds_reason=freeVipapprovednokey=2Fadv&tds_reason=direct&tds_campaign=b0450kly&tds_id=b0450kly_lp_a_1608557363838_nd&tds_oid=46121&dci=435c5ed38e8f1761ff90ec8af1fa8f77c754241b&data2=ah3VMF0i6h&utm_term=4st&s1=adv&utm_funnel=tds&tds_host=look4loves.com&utm_ex=a&utm_content=Desktop&dynamicpage=all_wlp_passion_versus_t&p_tds_cid=&utm_source=intc&utm_campaign=d3b0d71b

  • https://www.withu4ever.com/aff.php?%20dynamicpage=all_wlp_5st_mod_a_d&utm_bo=1&a_bid=4fb1bd2%C2%A0in_passion_c&utm_source=brand&utm_medium=web&utm_campaign=search&utm_term=us&text=brand&ppc_cp=1000

  • https://www.pumaslove.com/aff.php?dynamicpage=all_wlp_5st_vid_a_d_sound&utm_bo=1&a_bid=4fb1bd2a&utm_source=facebook&utm_campaign=crrsale&utm_content=21090&tds_reason=freeVipapprovednokey=2Fadv&tds_reason=direct&tds_campaign=b0450kly&tds_id=b0450kly_lp_a_1608557363838_nd&tds_oid=46121&dci=435c5ed38e8f1761ff90ec8af1fa8f77c754241b&data2=ah3VMF0i6h&utm_term=4st&s1=adv&utm_funnel=tds&tds_host=look4loves.com&utm_ex=a&utm_content=Desktop&dynamicpage=all_wlp_passion_versus_t&p_tds_cid=&utm_source=intc&utm_campaign=d3b0d71b/nov22

  • https://www.pumaslove.com/user/view/id/fdd17b1729b06c1b980dde92a689e6bc

  • https://www.pumaslove.com/search/country:AUS&tab:new_members&sortType:lastvisit&gender:1&ageFrom:18&ageTo:35&country:AUS&location:MacKay%2C%204740&distance:50&searchbar_wphoto:wphoto&wphoto%5B%5D:0&photoLevel%5B%5D:0&photoLevel%5B%5D:1&photoLevel%5B%5D:2&photoLevel%5B%5D:3&photoLevel%5B%5D:4&sexual_orientation%5B%5D:hetero

  • https://www.pumaslove.com/aff.php?dynamicpage=nav_wlp_5st_vid_a&utm_bo=1&a_bid=4fb1bd2a&utm_source=facebook&utm_campaign=crrsale&utm_content=91090&tds_reason=direct&utm_term=%7Butm_term%7D&_disAL=true&tds_bo_origin=lpaHR0cHM6Ly9iYW5nLXNleHkuY29tL3Rkcy9jcGEvcy80ODI3NzY4MGUzYTQ2M2QwYTU3ZDhlMjEwNmE2ZDhjMz9fX3Q9MTU2MDQzMjQyMzgyNSZfX2w9MzYwMA%3D%3D&b=1&tds_bo=1&utm_bo=1

  • https://www.flirtysparks.com/ppc.php?dynamicpage=all_wlp_5st_mod_a_d&utm_bo=1&a_bid=4fb1bd2a%C2%A0in_passion_c&utm_source=brand&utm_medium=web&utm_campaign=search&utm_term=us&text=brand&utm_sub=opnfnlconf&ppc_cp=1000000001&clkid=f133a822c23a56657c1f4a5612cc7842

  • https://m.flirtysparks.com/ppc.php?dynamicpage=all_wlp_5st_snapyellow_a&utm_bo=1&a_bid=4ct3ce8r&utm_mediums=web&utm_campaign=53F23927482

  • https://www.flirtysparks.com/aff.php?s1=int&dynamicpage=all_wlp_4st_product4_a&s3=1320827&utm_ex=b&tdsId=b2375koz_lp_b_1637072464101_mw&tds%20_campaign=b2375koz&dci=5805a2e8e31c0b9fdae3b516e32c102017e9f2bb&tds_host=privatesinglesmeet.com&utm_term=10&data3=%7Bdata3%7D&tds_path=%2Ftds%2Fint&tds_%20oid=46782&tds_ac_id=s6782yal&tds_cid=1aa641f9327e01eb1023b5a063209af9d5f10d2f&tds_id=b2375koz_lp_b_1637072464101_mw&h=1&_cbUrl=aHR0cHM6Ly9wcml2YXRlc2luZ2%20xlc21lZXQuY29tL3Rkcy9pbnQ%2FdXRtX2NvbnRlbnQ9MTAyNjEyJnRkc19ob3N0PXByaXZhdGVzaW5nbGVzbWVldC5jb20mcF90ZHNfY2lkPTQ4Y2I4YmVmMDY3OWYwOTI5YzdmOWRlMjRhZTZlZDI5ZDc1ZG%20QzNjkmdXRtX2NhbXBhaWduPWNmMjkzMjJiJnRkc19wX2NhbXBhaWduPWIxODE1eWFsJnV0bV90ZXJtPTEwJnRkc19yZWFzb249cmVzZXJ2ZWQmczM9MTMyMDgyNyZ0ZHNfY2FtcGFpZ249YjIzNzVrb3omZGNpPTU4MDVhMmU4ZTMxYzBiOWZkYW%20UzYjUxNmUzMmMxMDIwMTdlOWYyYmImdGRzX29pZD1tdyZ0ZHNJZD1iMjM3NWtvel90ZHNfc2l0ZV9ncm91cF9iXzE2MzcwNzI0NjQxMDEmdGRzX2FjX2lkPXM2NzgyeWFsJnRkc19wYXRoPSUyRnRkcyUyRmludCZ1dG1fc3ViPW9wbmZ%20ubGNvbmYmdGRzX2NpZD0xYWE2NDFmOTMyN2UwMWViMTAyM2I1YTA2MzIwOWFmOWQ1ZjEwZDJmJnRkc19pZD1iMjM3NWtvel90ZHNfc2l0ZV9ncm91cF9iXzE2MzcwNzI0NjQxMDEmdXRtX3NvdXJjZT1pbnQmczE9aW50JmRhdGEyPWVy%20cXRhNjFmM2JjYTQwMDA3ZTEzNSZkYXRhMz0lN0JkYXRhMyU3RCZ0ZHNUcmFmZmljPWJhY2tUcmFmZmljJnRkc1NvbHV0aW9uPW13&utm_content=102612&data2=erqta61f3bca40007e135&_disAL=true&utm_sub=opnfnlco%20nf&p_tds_cid=48cb8bef0679f0929c7f9de24ae6ed29d75dd369&tds_ao=1&tds_p_campaign=b1815yal&tds_reason=reserved&utm_campaign=cf29322b&utm_funnel=tds&utm_source=facebook

  • https://www.pumaslove.com/aff.php?dynamicpage=all_wlp_5st_tiktok_vid_a&utm_bo=1&a_bid=4fb1bd2a%C2%A0in_passion_c&utm_source=brand&utm_medium=web&utm_campaign=b7979yal&utm_term=GB&text=chat&ppc_cp=5859665984&clkid=f133a822c23a56657c1f4a5612cc78421

Other submissions on pumaslove.com:

  • https://www.pumaslove.com/aff.php?dynamicpage=all_wlp_5st_purple_a&utm_bo=1&a_bid=4ctce8r%20in_reason=VIPupgraded_c&utm_source=facebook&utm_medium=web&utm_disAL_campaign=search&utm_term=us&text=facebook&ppc_cp=1000000001&clkid=f133a822c23a5

  • http://pumaslove.com/

  • https://www.pumaslove.com/aff.php?tds_ao=1&dynamicpage=all_wlp_5st_halfphoto_v2_a&tds_cid=f96c2b612cbd12d86226faf679da2f8b57735360&btUrl=aHR0cHM6Ly9jbG9zZW1lZXR1cHMuY29tL3Rkcy9hZS9jYi9zLzdlMzZmNTgwZjA4OTJhNjIyZjkxYjUzZDkwZWFjZWMyP19fdD0xNjc3MjYzNDMwMTMzJl9fbD0zNjAw&tds_ac_id=s0792tok&p_tds_cid=&utm_content=MkRufiq&utm_ex=a&dci=e8b8298fd6cac1e6a21b8bfe57f99794d72dd085&tdsId=b5382yas_lp_a_1567437013893_bn&s3=%7Bsubid2%7D&tds_path=%2Ftds%2Fae&s1=ps&tds_oid=4375327&utm_source=intcc&utm_sub=opnfnl&tds_host=closemeetups.com&tds_reason=direct&tds_ps=a&_disAL=true&utm_funnel=tds&tds_id=b5382yas_lp_a_1567437013893_bn&utm_campaign=17f36e01&tds_campaign=b5382yas&data2=%7Bclickid%7D

  • http://pumaslove.com/

  • http://pumaslove.com/

  • https://pumaslove.com/MERON-NA-TAYO-MGA-TROPA-UNLIMITED-MESSAGE/CREATE/SETUP-ACCOUNT-FOR-FLIRTYSPARKS-PUMASLOVE-FOR-USA-CANADA-AUSTRALIA-UNITEDKINGDOM-FOR-SALE-MESSAGE-ME-SAGIMANA99@GMAIL.COM

  • http://pumaslove.com/buying-TEXTNOW-1000PESOS-PER-ACCOUNT-I-NEED-3ACCOUNT-.com/qqq

  • http://pumaslove.com/buying-TEXTNOW-1000PESOS-PER-ACCOUNT-I-NEED-3ACCOUNT///PMMEONHANGOUTSIHAVETEXTNOW

  • http://pumaslove.com/buying-TEXTNOW-1000PESOS-PER-ACCOUNT-I-NEED-3ACCOUNT--OKAYNA-NAKABILI---NA--SALMAT--SHAVEEEE

  • http://pumaslove.com/user/loginFromCompletePage/withoutConfirm/verified/2?mobToWeb

Previous checks:

                               Domain Name: pumaslove.com
Registry Domain ID: 2146616059_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.rrpproxy.net
Registrar URL: 
Updated Date: 2021-10-25T16:40:54Z
Creation Date: 2017-07-25T15:18:10Z
Registrar Registration Expiration Date: 2030-07-25T15:18:10Z
Registrar: Key-Systems GmbH
Registrar IANA ID: 269
Registrar Abuse Contact Email: abusereport@key-systems.net
Registrar Abuse Contact Phone: +49.68949396850
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Registry Registrant ID: Not Available From Registry
Registrant Name: On behalf of pumaslove.com OWNER 
Registrant Organization: c/o whoisproxy.com
Registrant Street: 604 Cameron Street
Registrant City: Alexandria
Registrant State/Province: VA
Registrant Postal Code: 22314
Registrant Country: US
Registrant Phone: +64.48319528
Registrant Phone Ext: 
Registrant Fax: 
Registrant Fax Ext: 
Registrant Email: 12960c50c1c5afdd3b112aa92644b176d74baa293f76af5888b93b1333af0ce9@pumaslove.com.whoisproxy.org
Registry Admin ID: Not Available From Registry
Admin Name: On behalf of pumaslove.com ADMIN 
Admin Organization: c/o whoisproxy.com
Admin Street: 604 Cameron Street
Admin City: Alexandria
Admin State/Province: VA
Admin Postal Code: 22314
Admin Country: US
Admin Phone: +64.48319528
Admin Phone Ext: 
Admin Fax: 
Admin Fax Ext: 
Admin Email: 12960c50c1c5afdd3b112aa92644b176d74baa293f76af5888b93b1333af0ce9@pumaslove.com.whoisproxy.org
Registry Tech ID: Not Available From Registry
Tech Name: On behalf of pumaslove.com TECH 
Tech Organization: c/o whoisproxy.com
Tech Street: 604 Cameron Street
Tech City: Alexandria
Tech State/Province: VA
Tech Postal Code: 22314
Tech Country: US
Tech Phone: +64.48319528
Tech Phone Ext: 
Tech Fax: 
Tech Fax Ext: 
Tech Email: 12960c50c1c5afdd3b112aa92644b176d74baa293f76af5888b93b1333af0ce9@pumaslove.com.whoisproxy.org
Registry Billing ID: Not Available From Registry
Billing Name: On behalf of pumaslove.com BILLING 
Billing Organization: c/o whoisproxy.com
Billing Street: 604 Cameron Street
Billing City: Alexandria
Billing State/Province: VA
Billing Postal Code: 22314
Billing Country: US
Billing Phone: +64.48319528
Billing Phone Ext: 
Billing Fax: 
Billing Fax Ext: 
Billing Email: 12960c50c1c5afdd3b112aa92644b176d74baa293f76af5888b93b1333af0ce9@pumaslove.com.whoisproxy.org
Name Server: ns5.dnsmadeeasy.com 
Name Server: ns6.dnsmadeeasy.com 
Name Server: ns7.dnsmadeeasy.com 
DNSSEC: unsigned
Whoisprivacy: 1
URL of the ICANN WHOIS Data Problem Reporting System: https://wdprs.internic.net/
>>> Last update of WHOIS database: 2023-06-17T02:21:05Z <<<

For more information on Whois status codes, please visit https://www.icann.org/epp

To contact the registered registrant please proceed to:
https://www.domain-contact.org


This data is provided by 
for information purposes, and to assist persons obtaining information
about or related to domain name registration records.
 does not guarantee its accuracy.
By submitting a WHOIS query, you agree that you will use this data
only for lawful purposes and that, under no circumstances, you will
use this data to
1) allow, enable, or otherwise support the transmission of mass
   unsolicited, commercial advertising or solicitations via E-mail
   (spam) or
2) enable high volume, automated, electronic processes that apply
   to this WHOIS server.
These terms may be changed without prior notice.
By submitting this query, you agree to abide by this policy.

                             
  • GET
    0 Timed out waiting for a response.

    https://www.pumaslove.com/assets/aa6ebe3d/pumaslove_favicon.ico

  • https://www.pumaslove.com/favicon.ico https://www.pumaslove.com/assets/aa6ebe3d/pumaslove_favicon.ico
<html><head><link rel="stylesheet" href="resource://content-accessible/plaintext.css"></head><body><pre>"use strict";

// Utils
class Utils {
	isTouchDevice() {
		return "ontouchstart" in window || navigator.MaxTouchPoints &gt; 0 || navigator.msMaxTouchPoints &gt; 0 ? true : false;
	}

	isObject(value) {
		return value &amp;&amp; typeof value === "object" &amp;&amp; value.constructor === Object;
	}

	mergeDeep(target, ...sources) {
		if (!sources.length) return target;
		const source = sources.shift();

		if (this.isObject(target) &amp;&amp; this.isObject(source)) {
			for (const key in source) {
				if (this.isObject(source[key])) {
					if (!target[key]) Object.assign(target, { [key]: {} });
					this.mergeDeep(target[key], source[key]);
				} else {
					Object.assign(target, { [key]: source[key] });
				}
			}
		}

		return this.mergeDeep(target, ...sources);
	}

	debounce(f, ms) {
		let isReady = false;

		let wrapper = function () {
			if (isReady) return;

			f.apply(this, arguments);
			isReady = true;

			setTimeout(function () {
				isReady = false;
			}, ms);
		};

		return wrapper;
	}

	throttle(func, ms) {
		let isThrottled = false,
			savedArgs,
			savedThis;

		function wrapper() {
			if (isThrottled) {
				savedArgs = arguments;
				savedThis = this;
				return;
			}

			func.apply(this, arguments);

			isThrottled = true;

			setTimeout(function () {
				isThrottled = false;
				if (savedArgs) {
					wrapper.apply(savedThis, savedArgs);
					savedArgs = savedThis = null;
				}
			}, ms);
		}

		return wrapper;
	}
}

// Validator
class Validator {
	constructor({ props, formContainer, fields }) {
		this.formContainer = formContainer;
		this.props = props;
		this.fields = fields;
		this.storage = {};
		this.errorList = new Set();
		this.lastErrorName = "";
		this.isSubmitted = false;

		this.setEvents();
	}

	setEvents() {
		this.formContainer.addEventListener("field-validate", (e) =&gt; {
			this.highlight(e.detail.response);
		});
	}

	prepareFormData(fields) {
		const formDataList = fields.reduce((acc, item) =&gt; {
			// cancel a repeated request with the same value
			if (this.storage[item.name] === item.element.value) {
				return acc;
			}

			// new formData for new request
			const formData = new FormData();
			formData.append("ajax", "register-form");
			formData.append("scenario", item.validateRule.scenario);
			formData.append(item.fieldName, item.element.value);
			acc.push(formData);

			this.storage[item.name] = item.element.value;
			this.lastErrorName = item.errorName;

			return acc;
		}, []);

		return formDataList;
	}

	prepareRequests(dataList) {
		return dataList.map((request) =&gt; {
			const data = {
				method: "POST",
				body: request,
			};

			return fetch(this.props.actionURL, data);
		});
	}

	validate(fields) {
		// Avoid requests if empty field
		const notEmptyFields = this.props.hasValidationEmptyFields ? this.checkEmptyFields(fields) : fields;

		// Send requests
		const fieldsData = this.prepareFormData(notEmptyFields);
		const promises = this.prepareRequests(fieldsData);

		return Promise.all(promises)
			.then((responses) =&gt; {
				return Promise.all(responses.map((res) =&gt; res.json()));
			})
			.then((responses) =&gt; {
				responses.forEach((response) =&gt; {
					if (Array.isArray(response) &amp;&amp; !response.length) {
						this.unhighlight(this.lastErrorName);
					} else {
						const eventFieldValidate = new CustomEvent("field-validate", {
							detail: { response },
						});
						this.formContainer.dispatchEvent(eventFieldValidate);
					}
				});

				return this.errorList;
			})
			.catch((err) =&gt; {
				console.error(err);
			});
	}

	checkEmptyFields(fields) {
		const emptyFields = fields.filter((field) =&gt; field.element.value.length === 0).map((field) =&gt; field.errorName);
		emptyFields.forEach((name) =&gt; this.renderEmptyFieldError(name));

		return fields.filter((field) =&gt; field.element.value.length !== 0);
	}

	renderEmptyFieldError(name) {
		if (!window.jqueryValidationMessages) return; // Errors object from backend

		const fieldName = this.fields.filter((field) =&gt; field.errorName === name).map((field) =&gt; field.fieldName);
		const errorName = window.jqueryValidationMessages[fieldName];
		const errorMessage = errorName &amp;&amp; errorName.required;

		this.renderErrorMessage(name, errorMessage);
	}

	getErrorElements(name) {
		const { fieldContainer, errorContainer } = this.props;

		const errorField = this.fields.find((field) =&gt; field.errorName === name);
		if (!errorField) return;

		const parent = errorField.element.closest(fieldContainer);
		const container = parent.querySelector(errorContainer);

		return {
			errorField,
			parent,
			container,
		};
	}

	resetErrors(name) {
		const { errorClass } = this.props;
		const { parent, container } = this.getErrorElements(name);

		container.innerHTML = "";
		parent.classList.remove(errorClass);

		this.errorList.delete(name);
	}

	renderErrorMessage(name, message) {
		const { errorClass } = this.props;
		const { parent, container } = this.getErrorElements(name);

		container.dataset.errorName = name;
		container.innerHTML = `&lt;p&gt;${message}&lt;/p&gt;`;
		parent.classList.add(errorClass);

		this.errorList.add(name);
	}

	unhighlight(name) {
		this.resetErrors(name);
	}

	highlight(data) {
		// data example = { UserForm_age: ["Enter your age"] }

		const [errorName] = Object.keys(data);
		const [errorMessage] = Object.values(data);

		this.renderErrorMessage(errorName, errorMessage[0]);
	}
}

// User Recovery
class UserRecovery {
	constructor({ formContainer, props: { userRecoveryProps } }) {
		this.formContainer = formContainer;
		this.userRecoveryProps = userRecoveryProps;

		this.init();
	}

	init() {
		this.emailElement = this.formContainer.querySelector(this.userRecoveryProps.emailElement);

		const tokenElement = this.formContainer.querySelector(this.userRecoveryProps.tokenElement);
		this.token = tokenElement ? tokenElement.value : "";

		this.setEvents();
	}

	setEvents() {
		this.formContainer.querySelector(this.userRecoveryProps.errorContainer).addEventListener("click", (e) =&gt; {
			e.preventDefault();

			if (e.target.matches(this.userRecoveryProps.passwordElement)) {
				this.recoveryPassword(e.target.href);
			}

			if (e.target.matches(this.userRecoveryProps.confirmElement)) {
				this.recoveryConfirm(e.target.href);
			}

			if (e.target.matches(this.userRecoveryProps.cancelElement)) {
				this.recoveryCancel(e.target.href);
			}
		});
	}

	recoveryRequest(url, formData) {
		const headers = new Headers({
			"x-requested-with": "XMLHttpRequest",
		});

		const options = {
			method: "POST",
			headers: headers,
			body: formData,
		};

		fetch(url, options)
			.then((res) =&gt; res.json())
			.then(({ status, data, meta }) =&gt; {
				// redirect to captcha
				if (status === "error" &amp;&amp; meta.code === 302 &amp;&amp; meta.redirect) {
					this.redirectToCaptcha(meta.redirect);
					return;
				}
				const message = status === "success" ? data.message : meta.description.description;
				this.showErrorMessage(message);
			})
			.catch((err) =&gt; {
				console.error(err);
			});
	}

	recoveryPassword(url) {
		const formData = new FormData();

		formData.append("RecoveryForm[email]", this.emailElement.value);

		this.recoveryRequest(url, formData);
	}

	recoveryConfirm(url) {
		const recoveryId = url.substr(-32);
		url = url.replace("/id/" + recoveryId, "");

		const formData = new FormData();
		formData.append("id", recoveryId);
		formData.append("YII_CSRF_TOKEN", this.token);

		this.recoveryRequest(url, formData);
	}

	recoveryCancel(url) {
		this.recoveryRequest(url);
	}

	showErrorMessage(msg) {
		this.errorElement = this.formContainer.querySelector(this.userRecoveryProps.errorElement);
		this.errorElement &amp;&amp; (this.errorElement.innerHTML = `&lt;p&gt;${msg}&lt;/p&gt;`);
	}

	redirectToCaptcha(url) {
		const exp = new RegExp(/\/\/(www|m)/);

		if (!exp.test(url)) {
			url = location.protocol + "//" + location.host + (url[0] === "/" ? url : "/" + url);
		}

		window.location.href = url;
	}
}

//Login
class LoginForm extends Utils {
	constructor(formContainer, options = {}) {
		super();
		this.formContainer = formContainer;
		this.props = this.initProps(options);
		this.state = this.initState(this.formContainer);
		this.errorList = [];
		this.lastErrorContainer = null;
		this.isSubmitted = false;
		this.setEvents();
	}
	initProps(options) {
		const defaults = {
			login: {
				formNamespace: "LoginForm",
				actionURL: "/site/checkLogin",
				formElement: ".login-form",
				emailElement: ".login-email-field",
				emailError: '[data-error-name="email"]',
				msisdnError: '[data-error-name="msisdn"]',
				passwordElement: ".login-password-field",
				passwordError: '[data-error-name="password"]',
				submitElement: ".login-form-submit",
			},
			recovery: {
				formNamespace: "RecoveryForm",
				actionURL: "/account/remindPassword",
				formElement: ".recovery-form",
				emailElement: ".recovery-email-field",
				emailError: '[data-error-name="email"]',
				emailSuccess: '[data-success-name="email"]',
				submitElement: ".recovery-form-submit",
			},
			fieldContainer: ".form-item",
			fieldElement: ".form-input input",
			errorClass: "error-field",
			validClass: "valid-field",
		};
		return this.mergeDeep(defaults, options);
	}
	initState(formContainer) {
		const loginForm = formContainer.querySelector(this.props.login.formElement);
		const recoveryForm = formContainer.querySelector(this.props.recovery.formElement);
		const fields = this.initFields(loginForm, recoveryForm);
		return {
			loginForm,
			recoveryForm,
			fields,
		};
	}
	initFields(loginForm, recoveryForm) {
		const { login, recovery } = this.props;
		const loginEmailField = loginForm.querySelector(login.emailElement);
		const loginEmailError = loginForm.querySelector(login.emailError);
		const loginMsisdnError = loginForm.querySelector(login.msisdnError);
		const loginPasswordField = loginForm.querySelector(login.passwordElement);
		const loginPasswordError = loginForm.querySelector(login.passwordError);
		const recoveryEmailField = recoveryForm.querySelector(recovery.emailElement);
		const recoveryEmailError = recoveryForm.querySelector(recovery.emailError);
		const recoveryEmailSuccess = recoveryForm.querySelector(recovery.emailSuccess);
		return {
			[loginEmailField.name]: {
				element: loginEmailField,
				value: null,
				error: loginEmailError,
			},
			[`${login.formNamespace}[msisdn]`]: {
				element: loginEmailField,
				value: null,
				error: loginMsisdnError,
			},
			[loginPasswordField.name]: {
				element: loginPasswordField,
				value: null,
				error: loginPasswordError,
			},
			[recoveryEmailField.name]: {
				element: recoveryEmailField,
				value: null,
				error: recoveryEmailError,
				success: recoveryEmailSuccess,
			},
		};
	}
	setEvents() {
		const { login, recovery } = this.props;
		const { loginForm, recoveryForm } = this.state;
		const loginSubmitBtn = loginForm.querySelector(login.submitElement);
		loginSubmitBtn.addEventListener("click", (e) =&gt; {
			e.preventDefault();
			if (this.hasSameValues(loginForm)) return;
			this.submit(login.actionURL, loginForm);
		});
		const recoverySubmitBtn = recoveryForm.querySelector(recovery.submitElement);
		recoverySubmitBtn.addEventListener("click", (e) =&gt; {
			e.preventDefault();
			this.validate(recovery.actionURL, recoveryForm);
		});
		const forgotPassword = this.formContainer.querySelector(".recovery-password-btn");
		forgotPassword.addEventListener("click", () =&gt; {
			this.switchToForm("login", "recovery");
		});
		const backToLogin = this.formContainer.querySelector(".login-switch-btn");
		backToLogin.addEventListener("click", () =&gt; {
			this.switchToForm("recovery", "login");
		});
	}
	hasSameValues(form) {
		const sameValues = [];
		form.querySelectorAll(this.props.fieldElement).forEach(({ name, value }) =&gt; {
			sameValues.push(this.state.fields[name].value === value);
		});
		return sameValues.every(Boolean);
	}
	switchToForm(from, to) {
		const fromForm = this.formContainer.querySelector(this.props[from].formElement);
		const toForm = this.formContainer.querySelector(this.props[to].formElement);
		fromForm.classList.remove("visible");
		fromForm.classList.add("hidden");
		toForm.classList.remove("hidden");
		toForm.classList.add("visible");
		fromForm.querySelector(this.props.fieldContainer).classList.remove(this.props.errorClass);
		const { fields } = this.state;
		if (fields[`${this.props[from].formNamespace}[email]`].element.value !== "") {
			fields[`${this.props[to].formNamespace}[email]`].element.closest(".form-item").classList.add("is-focused");
			fields[`${this.props[from].formNamespace}[email]`].element.closest(".form-item").classList.remove("is-focused");
		}

		fields[`${this.props[to].formNamespace}[email]`].element.value = fields[`${this.props[from].formNamespace}[email]`].element.value;
		toForm.querySelector(this.props[to].submitElement).click();
	}
	redirect(url) {
		const exp = new RegExp(/\/\/(www|m)/);
		if (!exp.test(url)) {
			url = location.protocol + "//" + location.host + (url[0] === "/" ? url : "/" + url);
		}
		window.location.href = url;
	}
	prepareFetchOptions(form) {
		const body = new FormData(form);
		const headers = new Headers({
			"x-requested-with": "XMLHttpRequest",
		});
		return {
			method: "POST",
			headers,
			body,
		};
	}
	validate(url, form) {
		// this.updateState(form);
		const options = this.prepareFetchOptions(form);
		return fetch(url, options)
			.then((res) =&gt; res.json())
			.then(({ data, status, meta }) =&gt; {
				if (status === "error") {
					if (meta.code === 302 &amp;&amp; meta.redirect) {
						this.redirect(meta.redirect);
						return;
					}
					this.renderErrors(meta.description);
				}

				if (status === "success") {
					if (data.valid) {
						this.renderErrors(data);
						return;
					}
					this.errorList = [];
				}
				return this.errorList;
			})
			.catch((err) =&gt; {
				throw err;
			});
	}
	renderErrors(data) {
		const { login, recovery, errorClass, validClass } = this.props;
		const { fields } = this.state;
		if (data.type) {
			const errorName = `${login.formNamespace}[${data.type}]`;
			this.renderMessage(fields[errorName].error, data.message, errorClass);
			this.errorList.push(errorName);
		}
		if (data.type === "email") {
			fields["LoginForm[password]"].error.closest(this.props.fieldContainer).classList.add(errorClass);
		}
		if (data.type === "password") {
			fields["LoginForm[email]"].error.closest(this.props.fieldContainer).classList.add(errorClass);
		}
		const recoveryName = `${recovery.formNamespace}[email]`;
		if (data.email) {
			this.renderMessage(fields[recoveryName].error, data.email[0], errorClass, validClass);
		}
		if (data.valid) {
			this.renderMessage(fields[recoveryName].success, data.message, validClass, errorClass);
		}
	}
	renderMessage(container, message, errorClass, validClass) {
		this.lastErrorContainer &amp;&amp; (this.lastErrorContainer.innerHTML = "");
		this.lastErrorContainer = container;
		container.innerHTML = `&lt;p&gt;${message}&lt;/p&gt;`;

		container.closest(this.props.fieldContainer).classList.add(errorClass);
		container.closest(this.props.fieldContainer).classList.remove(validClass);
	}
	async submit(url, form) {
		if (!this.isSubmitted) {
			const errors = await this.validate(url, form);
			if (!errors.length &amp;&amp; !this.isSubmitted) {
				this.isSubmitted = true;
				form.submit();
			}
		}
	}
}

/* Fields Blur &amp; focus events  */
class FieldEvents {
	constructor(root, options = {}) {
		this.props = this.initProps(options);
		this.fields = this.setFields(root);

		this.setEvents();
	}

	initProps(options) {
		const defaults = {
			fieldContainer: ".form-item",
			activeClass: "is-visible",
			focusClass: "is-focused",
			select: {
				selectContainer: ".form-select",
				selectedValue: "select-value",
				selectDropdown: "select-dropdown",
				selectDropdownItem: "select-item",
			},
		};

		return { ...defaults, ...options };
	}

	setFields(root) {
		const fieldsArray = Array.from(root.querySelectorAll(this.props.fieldContainer));
		const fields = fieldsArray.reduce((acc, formItem) =&gt; {
			formItem.querySelectorAll("input, select").forEach((el) =&gt; acc.push(el));

			return acc;
		}, []);

		return fields;
	}

	setEvents() {
		const {
			activeClass,
			select: { selectContainer, selectedValue, selectDropdownItem },
		} = this.props;

		const inputs = this.fields.filter((input) =&gt; input.tagName === "INPUT");
		const selects = this.fields.filter((select) =&gt; select.tagName === "SELECT");

		// Input change
		inputs.forEach((input) =&gt; {
			input.value ? this.addFocus(input) : this.removeFocus(input);

			input.addEventListener("focus", (e) =&gt; this.addFocus(e.currentTarget));
			input.addEventListener("blur", ({ currentTarget }) =&gt; {
				currentTarget.value ? this.addFocus(currentTarget) : this.removeFocus(currentTarget);
			});
		});

		selects &amp;&amp; this.renderCustomSelects(selects);
		// Selects change
		let openedSelectedValue;
		selects &amp;&amp;
			selects.forEach((select) =&gt; {
				const parent = select.closest(selectContainer);
				const selected = parent.querySelector(`.${selectedValue}`);

				select.addEventListener("change", ({ currentTarget }) =&gt; {
					selected.textContent = currentTarget[currentTarget.selectedIndex].innerHTML;
				});
				// show/hide dropdown
				selected.addEventListener("click", (e) =&gt; {
					const openedSelect = document.querySelector(`${selectContainer}.${this.props.activeClass}`);
					openedSelectedValue = openedSelect &amp;&amp; openedSelect.querySelector(`.${selectedValue}`);
					if (openedSelect &amp;&amp; openedSelectedValue !== e.currentTarget) {
						openedSelect.classList.remove(this.props.activeClass);
					}

					parent.classList.toggle(activeClass);

					e.stopPropagation();
				});

				// select-item click
				parent.querySelectorAll(`.${selectDropdownItem}`).forEach((item) =&gt; {
					item.addEventListener("click", ({ currentTarget }) =&gt; {
						const gender = currentTarget.dataset.genderValue;
						const value = currentTarget.getAttribute("value");
						selected.textContent = currentTarget.textContent;
						select.value = value;

						if (gender) {
							select.dataset.genderSelected = gender;
							select.querySelector(`option[value="${value}"][data-gender-value="${gender}"]`).selected = true;
						}

						parent.classList.remove(activeClass);
						this.addFocus(currentTarget);

						select.dispatchEvent(new Event("change"));
					});
				});
			});

		// Close opened selects
		document.addEventListener("click", (e) =&gt; {
			if (e.target !== openedSelectedValue) {
				const openedSelect = document.querySelector(`${selectContainer}.${this.props.activeClass}`);
				if (openedSelect) {
					openedSelect.classList.remove(this.props.activeClass);
				}
			}
		});
	}

	addFocus(el) {
		const { fieldContainer, focusClass } = this.props;

		el.closest(fieldContainer).classList.add(focusClass);
	}

	removeFocus(el) {
		const { fieldContainer, focusClass } = this.props;

		el.closest(fieldContainer).classList.remove(focusClass);
	}

	renderCustomSelects(selects) {
		const { selectContainer, selectedValue, selectDropdown, selectDropdownItem } = this.props.select;

		selects.forEach((select) =&gt; {
			const dropdown = document.createElement("div");
			dropdown.className = selectDropdown;
			const selected = document.createElement("div");
			selected.className = selectedValue;

			select.querySelectorAll("option").forEach((option) =&gt; {
				if (option.selected) {
					selected.textContent = option.textContent;
					this.addFocus(select);
				}

				let clone = `&lt;div class="${selectDropdownItem}" value="${option.value}"&gt;${option.textContent}&lt;/div&gt;`;
				if (option.dataset.genderValue) {
					clone = `&lt;div class="${selectDropdownItem}" value="${option.value}" data-gender-value="${option.dataset.genderValue}"&gt;${option.textContent}&lt;/div&gt;`;
				}

				dropdown.insertAdjacentHTML("beforeend", clone);
			});

			select.closest(selectContainer).appendChild(selected);
			select.closest(selectContainer).appendChild(dropdown);
		});
	}
}

// Multi Step
class MultiStep {
	constructor(validator, { formContainer, fields, props }) {
		this.validator = validator;
		this.formContainer = formContainer;
		this.fields = fields;
		this.props = props;

		this.init();
	}

	init() {
		const { stepContainer, formControls, pagination, activeClass } = this.props;

		this.stepsList = this.formContainer.querySelectorAll(stepContainer);
		this.maxStep = this.stepsList.length;
		this.currentStep = Array.from(this.stepsList).find((step) =&gt; step.classList.contains(activeClass));
		this.currentStepIndex = !this.currentStep ? 0 : Array.from(this.stepsList).findIndex((step) =&gt; step.classList.contains(activeClass));
		this.setCurrentStepData(this.currentStepIndex);

		this.nextBtn = this.formContainer.querySelector(formControls.nextElement);
		this.prevBtn = this.formContainer.querySelector(formControls.prevElement);

		if (pagination.exist) {
			this.initPagination();
		}

		this.setEvents();
	}

	setEvents() {
		const onNextClick = (e) =&gt; {
			e.preventDefault();
			e.target.focus();
			if (!event.detail || event.detail == 1) {
				this.next();
			}
		};
		this.nextBtn &amp;&amp; this.nextBtn.addEventListener("click", onNextClick);

		const onPrevClick = (e) =&gt; {
			e.preventDefault();

			this.prev();
		};
		this.prevBtn &amp;&amp; this.prevBtn.addEventListener("click", onPrevClick);
	}

	setCurrentStepData(index) {
		this.stepsList[index].classList.add(this.props.activeClass);
		this.currentStep = this.stepsList[index];
		this.formContainer.dataset.currentStepIndex = index + 1;
		document.body.dataset.currentStepName = this.currentStep.dataset.stepName;
	}

	getStepNodes(index) {
		const step = this.stepsList[index];
		if (!step) {
			return;
		}

		return Array.from(step.querySelectorAll(`[name^="${this.props.formNamespace}"]`)).map((node) =&gt; node.name);
	}

	getCurrentStepFields(index) {
		const currentStepNodes = this.getStepNodes(index);

		return this.fields.filter((field) =&gt; currentStepNodes.includes(field.fieldName));
	}

	getCurrentStepRequiredFields(index) {
		const currentStepRequiredNodes = this.getStepNodes(index);

		return this.fields.filter((field) =&gt; {
			return field.validateRule &amp;&amp; field.validateRule.required &amp;&amp; currentStepRequiredNodes.includes(field.fieldName);
		});
	}

	getPrevStepFields(index) {
		const prevStepNodes = this.getStepNodes(index - 1);
		if (!prevStepNodes) return null;

		return this.fields.filter((field) =&gt; prevStepNodes.includes(field.fieldName));
	}

	getNextStepFields(index) {
		const nextStepNodes = this.getStepNodes(index + 1);
		if (!nextStepNodes) return null;

		return this.fields.filter((field) =&gt; nextStepNodes.includes(field.fieldName));
	}

	showNextStep() {
		if (this.currentStepIndex &lt; this.stepsList.length - 1) {
			this.currentStepIndex++;
			this.stepBy(this.currentStepIndex, "step-next");
			this.dispatchMultistepEvent("step-next");
			this.dispatchMultistepEvent("step-change");
		}
	}

	next() {
		const currentStepFields = this.getCurrentStepRequiredFields(this.currentStepIndex);

		if (currentStepFields.length) {
			this.validator.isNext = true;
			this.validator
				.validate(currentStepFields)
				.then((errorList) =&gt; {
					const currentStepErrorNames = currentStepFields.map((field) =&gt; field.errorName).filter((field) =&gt; errorList.has(field));

					if (!errorList.size || !currentStepErrorNames.length) {
						// Show next step
						this.showNextStep();
					}
				})
				.catch((err) =&gt; {
					console.error(err);
					return err;
				});
		} else {
			// Show next step if missing required fields
			this.showNextStep();
		}
	}

	prev() {
		if (this.currentStepIndex &gt; 0) {
			this.currentStepIndex--;
			this.stepBy(this.currentStepIndex);
			this.dispatchMultistepEvent("step-prev");
			this.dispatchMultistepEvent("step-change");
		}
	}

	stepBy(index) {
		this.currentStep.classList.remove(this.props.activeClass);
		this.setCurrentStepData(index);
	}

	dispatchMultistepEvent(name = "step-change") {
		const event = new CustomEvent(name, {
			detail: {
				maxStep: this.maxStep,
				currentStep: this.currentStep,
				currentStepIndex: this.currentStepIndex,
				currentStepFields: this.getCurrentStepFields(this.currentStepIndex),
				prevStepFields: this.getPrevStepFields(this.currentStepIndex),
				nextStepFields: this.getNextStepFields(this.currentStepIndex),
			},
		});
		this.formContainer.dispatchEvent(event);
	}

	initPagination() {
		const { root, container, item } = this.props.pagination;

		let items = `&lt;div class="${container}"&gt;`;
		for (let i = 1; i &lt;= this.maxStep; i++) {
			items += `&lt;div class="${item}" data-pagination-index="${i}"&gt;&lt;span&gt;${i}&lt;/span&gt;&lt;/div&gt;`;
		}
		items += "&lt;/div&gt;";

		root.insertAdjacentHTML("beforeend", items);
		this.paginationContainer = this.formContainer.querySelector(`.${container}`);
		this.paginations = [...this.paginationContainer.children];
		this.updatePagination(0);

		this.formContainer.addEventListener("step-next", (e) =&gt; this.updatePagination(e.detail.currentStepIndex));
		this.formContainer.addEventListener("step-prev", (e) =&gt; this.updatePagination(e.detail.currentStepIndex));
	}
	updatePagination(i) {
		const { root, currentClass, visitedClass } = this.props.pagination;

		this.paginations.forEach((el) =&gt; {
			el.classList.remove(currentClass);
			el.classList.remove(visitedClass);
		});
		this.paginations
			.filter((el, index) =&gt; index &lt;= i)
			.forEach((el) =&gt; {
				el.classList.add(visitedClass);
			});

		this.paginationContainer.children[i].classList.remove(visitedClass);
		this.paginationContainer.children[i].classList.add(currentClass);
	}
}

// scenario: loginOnly
// scenario: ageOnly
// scenario: email
// scenario: passwordOnly

class Regform extends Utils {
	constructor(formContainer, options = {}) {
		super();
		this.formContainer = formContainer;

		this.props = this.initProps(options);
		this.state = this.initState(this.formContainer, this.props);

		this.init();
	}

	init() {
		const { formElement, gender, orientation } = this.props;

		this.form = this.formContainer.querySelector(formElement);
		this.validator = new Validator(this.state);
		this.userRecovery = new UserRecovery(this.state);

		if (this.props.hasMultiSteps) {
			this.multiStep = new MultiStep(this.validator, this.state);
		}

		this.setValidatorEvents();

		// Set default genderSelect or genderButtons
		this.props.genderBtns.exist ? this.initGenderBtns() : this.initGenderSelect();

		// Set default gender &amp; orientation
		this.setOrientation(gender, orientation);

		// Set FieldEvents
		new FieldEvents(this.formContainer);

		if (this.getFieldsElement("password")) {
			// Visible/hide password
			this.initPasswordIcon(this.getFieldsElement("password"));
		}
	}

	initProps(options) {
		const defaults = {
			formNamespace: "UserForm",
			formElement: ".register-hidden-form",
			actionURL: "/user/register",
			stepContainer: ".form-step-item",
			fieldContainer: ".form-item",
			errorContainer: ".form-error-block",
			errorClass: "error-field",
			validClass: "valid-field",
			activeClass: "is-active",
			passwordIcon: ".password-icon",
			gender: "male",
			orientation: "hetero",
			hasMultiSteps: false,
			formControls: {
				submitElement: ".submit-btn",
				nextElement: ".next-btn",
				prevElement: ".prev-btn",
			},
			genderBtns: {
				exist: false,
				activeClass: "is-active",
				genderAttr: "[data-gender]",
				partnerGenderAttr: "[data-partner-gender]",
			},
			pagination: {
				exist: false,
				root: this.formContainer,
				container: "pagination-block",
				item: "pagination-item",
				currentClass: "is-current",
				visitedClass: "is-visited",
			},
			hasValidationEmptyFields: false,
			fields: {
				gender: {
					name: "gender",
					validateRule: {
						required: false,
					},
				},
				partnerGender: {
					name: "partnerGender",
					validateRule: {
						required: false,
					},
				},
				sexual_orientation: {
					name: "sexual_orientation",
					validateRule: {
						required: false,
					},
				},
				age: {
					name: "age",
					validateRule: {
						required: true,
						scenario: "ageOnly",
					},
				},
				screenname: {
					name: "login",
					validateRule: {
						required: true,
						scenario: "loginOnly",
					},
				},
				location: {
					name: "location",
					validateRule: {
						required: true,
						scenario: "location",
					},
				},
				email: {
					name: "email",
					validateRule: {
						required: true,
						scenario: "email",
					},
				},
				password: {
					name: "password",
					validateRule: {
						required: true,
						scenario: "passwordOnly",
					},
				},
			},
			userRecoveryProps: {
				formNamespace: "UserForm",
				errorContainer: `[data-step-name="email"] .form-error-block`,
				errorElement: `div[data-error-name="UserForm_email"]`,
				cancelElement: "#recoveryCancelUser",
				confirmElement: "#recoveryConfirm",
				passwordElement: "#recoveryPassword",
				tokenElement: 'input[name="YII_CSRF_TOKEN"]',
				emailElement: `input[name="UserForm[email]"]`,
			},
		};

		return this.mergeDeep(defaults, options);
	}

	initState(formContainer, props) {
		const fields = this.setFields();

		return {
			props,
			formContainer,
			fields,
		};
	}

	setFields() {
		return Object.values(this.props.fields).reduce((acc, field) =&gt; {
			const element = this.formContainer.querySelector(`[name='${this.props.formNamespace}[${field.name}]']:not([type='hidden'])`);

			if (element) {
				const fieldName = element.name;
				const extendedField = {
					element,
					fieldName,
					errorName: `${this.props.formNamespace}_${field.name}`,
				};

				acc.push(Object.assign(field, extendedField));
			}

			return acc;
		}, []);
	}

	getFieldsElement(name) {
		const elem = this.state.fields.find((field) =&gt; field.name === name);

		return elem &amp;&amp; elem.element;
	}

	setFieldValue(name, value) {
		const element = this.state.fields.find((field) =&gt; field.name === name).element;

		element.value = value;
		element.dispatchEvent(new Event("blur"));
		element.dispatchEvent(new Event("change"));
	}

	updateFieldValue(fields) {
		const nodes = fields.map((field) =&gt; field.element);

		nodes.forEach(({ name, value }) =&gt; {
			const input = this.form.querySelector(`[name='${name}']`);
			input &amp;&amp; (input.value = value);
		});
	}

	setValidatorEvents() {
		const validateRequiredFields = this.state.fields.filter(({ validateRule }) =&gt; validateRule &amp;&amp; validateRule.required);
		const inputs = validateRequiredFields.filter((input) =&gt; input.element.tagName === "INPUT");
		const selects = validateRequiredFields.filter((select) =&gt; select.element.tagName === "SELECT");

		// Event listener for Submit FORM
		this.submitBtn = this.formContainer.querySelector(this.props.formControls.submitElement);
		this.submitBtn.addEventListener("click", (e) =&gt; {
			e.preventDefault();
			if (e.detail &gt; 1) return; // prevent multiple clicks on submit button
			e.target.focus(); // safari fix relatedTarget

			this.submit(validateRequiredFields);
		});

		// Event listeners for BLUR INPUTS
		const eventInputBlur = new Event("field-blur");
		const handleInputBlur = (e) =&gt; {
			// do not trigger BLUR event if the button is clicked
			if (
				(this.submitBtn &amp;&amp; this.submitBtn === e.relatedTarget) ||
				(this.multiStep &amp;&amp; this.multiStep.nextBtn &amp;&amp; this.multiStep.nextBtn === e.relatedTarget)
			) {
				return;
			}

			e.target.dispatchEvent(eventInputBlur);
		};
		inputs.forEach((input) =&gt; {
			input.element.addEventListener("field-blur", () =&gt; {
				// Updating the value of hidden inputs
				this.updateFieldValue([input]);

				this.validator.validate([input]);
			});

			input.element.addEventListener("blur", handleInputBlur);
		});

		// Event listeners for CHANGE SELECTS
		const eventSelectChange = new Event("field-change");
		selects.forEach((select) =&gt; {
			select.element.addEventListener("field-change", () =&gt; {
				// Updating the value of hidden inputs
				this.updateFieldValue([select]);

				this.validator.validate([select]);
			});
			select.element.addEventListener("change", (e) =&gt; {
				e.target.dispatchEvent(eventSelectChange);
			});
		});

		// update to actual state values on validate
		this.formContainer.addEventListener("field-validate", () =&gt; {
			this.updateFieldValue(validateRequiredFields);
		});
	}

	initPasswordIcon(passwordElement) {
		this.formContainer.querySelector(this.props.passwordIcon).addEventListener("click", (e) =&gt; {
			e.target.classList.toggle("active");
			passwordElement.type == "password" ? passwordElement.setAttribute("type", "text") : passwordElement.setAttribute("type", "password");
		});
	}

	initGenderBtns() {
		const { activeClass, genderAttr, partnerGenderAttr } = this.props.genderBtns;
		const { gender, formNamespace, orientation } = this.props;
		const genderBtns = this.formContainer.querySelectorAll(genderAttr);
		const partnerGenderBtns = this.formContainer.querySelectorAll(partnerGenderAttr);

		const triggerActiveClass = (currentTarget, btns) =&gt; {
			this.formContainer.querySelectorAll(btns).forEach((btn) =&gt; btn.classList.remove(activeClass));
			currentTarget.classList.add(activeClass);
		};

		const triggerPartnerGenderBtn = (gender) =&gt; {
			partnerGenderBtns.forEach((btn) =&gt; {
				if (orientation === "hetero") {
					btn.dataset.partnerGender === gender ? btn.classList.remove(activeClass) : btn.classList.add(activeClass);
				} else {
					btn.dataset.partnerGender === gender ? btn.classList.add(activeClass) : btn.classList.remove(activeClass);
				}
			});
		};

		// Set default active Gender btn
		const defaultGender = Array.from(genderBtns).find((btn) =&gt; btn.dataset.gender === gender);
		defaultGender.classList.add(activeClass);

		// Set default active PartnerGender btn
		triggerPartnerGenderBtn(gender);

		genderBtns.forEach((btn) =&gt; {
			btn.addEventListener("click", ({ currentTarget }) =&gt; {
				triggerActiveClass(currentTarget, genderAttr);
				this.setOrientation(currentTarget.dataset.gender, orientation);

				triggerPartnerGenderBtn(currentTarget.dataset.gender);
			});
		});

		partnerGenderBtns.forEach((btn) =&gt; {
			btn.addEventListener("click", ({ currentTarget }) =&gt; {
				triggerActiveClass(currentTarget, partnerGenderAttr);

				const currentGender = this.form.querySelector(`[name='${formNamespace}[gender]']`).value;
				this.changeOrientation(currentGender, currentTarget.dataset.partnerGender);
			});
		});
	}

	initGenderSelect() {
		const { gender, orientation } = this.props;
		const notRequiredFields = this.state.fields.filter(({ validateRule }) =&gt; validateRule &amp;&amp; !validateRule.required);

		// change orientation
		const genderField = this.getFieldsElement("gender");
		const partnerGenderField = this.getFieldsElement("partnerGender");

		notRequiredFields.forEach((field) =&gt; {
			field.element.addEventListener("change", () =&gt; {
				this.updateFieldValue(notRequiredFields);
			});

			const eventOrientationChange = new Event("orientation-change");

			switch (field.name) {
				case "gender":
					// Set default value
					genderField.querySelector(`option[value="${gender}"]`).selected = true;

					// Event listener for Gender
					genderField.addEventListener("change", ({ target }) =&gt; {
						this.changeOrientation(target.value, partnerGenderField.value);
					});
					break;

				case "partnerGender":
					// Set default value
					if (orientation === "homo") {
						partnerGenderField.querySelector(`option[value="${gender}"]`).selected = true;
					} else {
						const partnerValue = Array.from(partnerGenderField.querySelectorAll("option")).find((el) =&gt; el.value !== gender);

						partnerGenderField.querySelector(`option[value="${partnerValue.value}"]`).selected = true;
					}

					// Event listener for Gender
					partnerGenderField.addEventListener("change", ({ target }) =&gt; {
						this.changeOrientation(genderField.value, target.value);
					});
					break;

				case "sexual_orientation":
					// Set default value
					const orientationField = this.getFieldsElement("sexual_orientation");
					orientationField.querySelector(`option[value="${orientation}"][data-gender-value="${gender}"]`).selected = true;

					// Event listener for Orientation
					orientationField.addEventListener("orientation-change", ({ target }) =&gt; {
						target.dataset.genderSelected = target.selectedOptions[0].dataset.genderValue;
						this.setOrientation(target.dataset.genderSelected, target.value);
					});
					orientationField.addEventListener("change", ({ target }) =&gt; {
						target.dispatchEvent(eventOrientationChange);
					});
			}
		});
	}

	changeOrientation(gender, partnerGender) {
		if (`${gender}-${partnerGender}` === "male-female") this.setOrientation("male", "hetero");
		if (`${gender}-${partnerGender}` === "male-male") this.setOrientation("male", "homo");
		if (`${gender}-${partnerGender}` === "female-male") this.setOrientation("female", "hetero");
		if (`${gender}-${partnerGender}` === "female-female") this.setOrientation("female", "homo");
	}

	setOrientation(gender, orientation) {
		this.form.querySelector(`[name='${this.state.props.formNamespace}[gender]']`).value = gender;
		this.form.querySelector(`[name='${this.state.props.formNamespace}[sexual_orientation]']`).value = orientation;

		document.body.dataset.orientation = `${orientation}-${gender}`;
	}

	async submit(fields) {
		// Prevent double submit form
		if (!this.isSubmitted) {
			// Updating the value of hidden inputs
			this.updateFieldValue(fields);

			const errors = await this.validator.validate(fields);
			if (!errors.size &amp;&amp; !this.isSubmitted) {
				const eventFormSubmit = new Event("form-submit");
				this.formContainer.dispatchEvent(eventFormSubmit);

				this.isSubmitted = true;
				this.form.submit();
			}
		}
	}
}
</pre></body></html>

                             

Screenshot: