PageSourceSearch

https://www.sports-fest.com/public/js/form-renderer.js

js sports-fest.com collected 2026-10-06 17:00:42 UTC 104,108 bytes, 2,437 lines download raw bytes

1$(function () {
2	// Store uploaded files in a global variable
3	const renderedOnLink = $("body").attr("data-form-link");
4	let isPreview = false;
5	let isFormPreview = false;
6	let formCompleteObject = {};
7	let isFrameLoad = false;
8	const urlParams = new URLSearchParams(window.location.search);
9	const recordTokenId = urlParams.get("recordToken");
10
11	window.addEventListener("message", function (event) {
12		if (event.data.type === "EditRecord") {
13			window.currentRecordId = event.data.recordId;
14		}
15	});
16
17	let autoSaveToDB = false;
18	const draftToken = urlParams.get("draftToken");
19	const renderMode = urlParams.get("renderMode");
20
21	// Utility functions for draft management
22	const getAutoSaveKey = (formId) => `formAutoSave_${formId}`;
23
24	const saveToLocalStorage = (formId, data, currentPage = 0, metadata) => {
25		// if (autoSaveToDB) return;
26		try {
27			localStorage.setItem(
28				getAutoSaveKey(formId),
29				JSON.stringify({
30					data: data,
31					currentPage: currentPage,
32					metadata: metadata,
33					savedAt: new Date().toISOString(),
34				})
35			);
36		} catch (e) {
37			console.error("Error saving draft to localStorage", e);
38		}
39	};
40
41	const loadDraftFromLocalStorage = (formId) => {
42		try {
43			const draft = localStorage.getItem(getAutoSaveKey(formId));
44			return draft ? JSON.parse(draft) : null;
45		} catch (e) {
46			console.error("Error loading draft from localStorage", e);
47			return null;
48		}
49	};
50
51	// Email validation function
52	const validateEmailInput = (formId) => {
53		const emailInput = document.getElementById(`email-input-${formId}`);
54		const errorElement = document.getElementById(`email-error-${formId}`);
55		const emailValue = emailInput.value.trim();
56
57		// Clear previous errors
58		errorElement.style.display = "none";
59
60		// Check if empty
61		if (!emailValue) {
62			errorElement.textContent = "Please enter an email address";
63			errorElement.style.display = "block";
64			return false;
65		}
66
67		// Check if valid format
68		const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
69		if (!emailRegex.test(emailValue)) {
70			errorElement.textContent = "Please enter a valid email address";
71			errorElement.style.display = "block";
72			return false;
73		}
74
75		return true;
76	};
77
78	const getMultiLookup = (question, value) => {
79		const ids = Array.from(new Set(value || [])); // unique IDs
80		const key = `${question?.lookupObject}_${question?.lookupFieldName}`;
81		// If key not found, return ids as-is
82		if (!window.searchItems?.hasOwnProperty(key)) {
83			return ids;
84		}
85		let searchList = window.searchItems[key] || [];
86		// 🔹 Make searchList unique by _id
87		searchList = Object.values(
88			searchList.reduce((acc, item) => {
89				acc[item._id] = item; // overwrite duplicates with same _id
90				return acc;
91			}, {})
92		);
93		// Optionally update window.searchItems[key] with cleaned list
94		window.searchItems[key] = searchList;
95		// 🔹 Filter by selected ids
96		return searchList.filter((item) => ids.includes(item._id));
97	};
98
99	const getLookup = (question, value) => {
100		const key = `${question?.lookupObject}_${question?.lookupFieldName}`;
101		// Check if window.searchItems has the key
102		if (!window.searchItems?.hasOwnProperty(key)) {
103			return value; // Return the value as-is
104		}
105		return window.searchItems[key]?.find((f) => f._id === value);
106	};
107
108	const getAddress = (question, value) => {
109		if (!value) return value;
110		const cityField = window.searchItems["Contact_City"]?.find((f) => f._id === value?.cityDropdown);
111		return { ...value, city: cityField?.searchname };
112	};
113
114	const getDropdown = (question, value) => {
115		if(value == "other" && question.showOtherItem) return value;
116		if (!question?.choicesByUrl?.url?.includes("/getlistitems")) return value;
117		return question?.choicesFromUrl?.find((f) => f.value === value);
118	};
119
120	const getTagbox = (question, value) => {
121		if (!question?.choicesByUrl?.url?.includes("/getlistitems")) return value;
122		const ids = value || [];
123		const searchList = question?.choicesFromUrl || [];
124		if(ids.includes("other")) {
125			let otherItem = new Survey.ItemValue("other", "other");
126			searchList.push(otherItem)
127		}
128		return searchList.filter((item) => ids.includes(item.value));
129	};
130
131	// Email sending handler
132	const handleEmailSend = async (resumeLink, formId, emailInputId, successWrapperId,survey, dfToken) => {
133		// Validate first
134		if (!validateEmailInput(formId)) {
135			return;
136		}
137
138		try {
139			const emailInput = document.getElementById(emailInputId);
140			const email = emailInput.value.trim();
141			const fields = loadDraftFromLocalStorage(formId);
142			let surveyData = {...fields.data}
143			survey.getAllQuestions().forEach((question) => {
144				const val = question.value;
145
146				switch (question.getType()) {
147					case "multilookupfield":
148						surveyData[question.valueName] = getMultiLookup(question, val);
149						break;
150
151					case "lookupfield":
152						surveyData[question.valueName] = getLookup(question, val);
153						break;
154
155					case "address":
156						surveyData[question.valueName] = val?.cityDropdown ? getAddress(question, val) : val;
157						break;
158
159					case "dropdown":
160						surveyData[question.valueName] = getDropdown(question, val);
161						break;
162
163					case "tagbox":
164						surveyData[question.valueName] = getTagbox(question, val);
165						break;
166
167					case "paneldynamic":
168						surveyData[question.valueName] = (question.panels || []).map((panel) => {
169							let rowData = {};
170							(panel.questions || []).forEach((el) => {
171								const innerVal = el.value;
172
173								switch (el.getType()) {
174									case "multilookupfield":
175										rowData[el.valueName] = getMultiLookup(el, innerVal);
176										break;
177
178									case "lookupfield":
179										rowData[el.valueName] = getLookup(el, innerVal);
180										break;
181
182									case "address":
183										rowData[el.valueName] = innerVal?.cityDropdown ? getAddress(el, innerVal) : innerVal;
184										break;
185
186									case "dropdown":
187										rowData[el.valueName] = getDropdown(el, innerVal);
188										break;
189
190									case "tagbox":
191										rowData[el.valueName] = getTagbox(el, innerVal);
192										break;
193
194									case "file":
195										rowData[el.valueName] = innerVal ? innerVal : [];
196										break;
197
198									default:
199										rowData[el.valueName] = innerVal;
200								}
201							});
202							return rowData;
203						});
204						break;
205
206					default:
207						surveyData[question.valueName] = val;
208				}
209			});
210			const emailData = {
211				email: email,
212				resumeLink: resumeLink,
213				formId: formId,
214				fields: surveyData,
215				draftToken: dfToken
216			};
217
218			const response = await $.ajax({
219				url: `/api/form/drafts/send-link-via-email`,
220				method: "POST",
221				contentType: "application/json",
222				data: JSON.stringify(emailData),
223				dataType: "json",
224			});
225
226			if (response.message !== "success") {
227				return swal({
228					title: "Error",
229					text: response?.message,
230					icon: "error",
231				});
232			}
233
234			// Show success message
235			document.getElementById(successWrapperId).style.display = "block";
236			emailInput.value = "";
237			// localStorage.removeItem(getAutoSaveKey(formId));
238			return true;
239			// survey.clear();
240		} catch (error) {
241			swal({
242				title: "Error",
243				text: "An unexpected error occurred. Try again later or contact support.",
244				icon: "error",
245			});
246			console.error("Email send error:", error);
247		}
248	};
249
250	const triggerDraftSavedPopup = (resumeLink, data, survey, dfToken) => {
251		const now = new Date();
252		const day = String(now.getDate()).padStart(2, "0");
253		const month = String(now.getMonth() + 1).padStart(2, "0");
254		const year = now.getFullYear();
255		const hours = String(now.getHours()).padStart(2, "0");
256		const minutes = String(now.getMinutes()).padStart(2, "0");
257		const seconds = String(now.getSeconds()).padStart(2, "0");
258		const currentDate = `${day}-${month}-${year} ${hours}:${minutes}:${seconds}`;
259
260		const showEmailOption = data?.Settings?.Email?.DraftNotification?.SaveEmailProgressEnabled;
261		const formId = data?.FormID;
262
263		const modal = `
264		<div class="save-draft-feature">
265			<div class="popup-bx">
266				<!-- Centered Larger Green Circle Icon -->
267				<div>
268					<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
269						<rect width="90" height="90" rx="45" fill="#DCF4EC" />
270						<rect x="12" y="12" width="66" height="66" rx="33" fill="#15C082" />
271						<path
272							d="M60.0728 39.313L43.3048 56.0798C42.0632 57.3217 40.0489 57.3217 38.8061 56.0798L30.1504 47.4234C28.9082 46.1815 28.9082 44.167 30.1504 42.9249C31.3928 41.6825 33.4068 41.6825 34.6487 42.9244L41.0561 49.332L55.5738 34.8143C56.8162 33.5719 58.8304 33.5728 60.0723 34.8143C61.3142 36.0564 61.3142 38.0701 60.0728 39.313Z"
273							fill="white" />
274					</svg>
275		
276				</div>
277		
278				<h2 class="large">Progress Saved.</h2>
279				<p class="font-weight-600">${currentDate}</p>
280		
281					<p class="text-left large">You can return to this form later using the link below:</p>
282		
283				${
284					showEmailOption
285						? `
286				<!-- Toggle Options -->
287				<div class="radio-toggle">
288							<label class="custom-radio-button">
289						<input type="radio" name="share-method" checked onclick="
290																	document.getElementById('copy-container').style.display = 'block';
291																	document.getElementById('email-container').style.display = 'none';
292																	document.getElementById('email-success-wrapper-${formId}').style.display = 'none';
293																">
294								<span class="custom-radio-mark"></span>
295						<span class="ml-1">Copy Link</span>
296					</label>
297		
298							<label class="custom-radio-button">
299						<input type="radio" name="share-method" onclick="
300																	document.getElementById('copy-container').style.display = 'none';
301																	document.getElementById('email-container').style.display = 'block';
302																	document.getElementById('copy-success-wrapper').style.display = 'none';
303																">
304								<span class="custom-radio-mark"></span>
305						<span class="ml-1">Email Link</span>
306					</label>
307				</div>
308				`
309						: ""
310				}
311		
312				<!-- Copy Section -->
313				<div id="copy-container">
314						<div class="input-wrapper">
315							<input readonly value="${resumeLink}"/>
316						<button onclick="
317										navigator.clipboard.writeText('${resumeLink}').then(() => {
318											const msgWrapper = document.getElementById('copy-success-wrapper');
319											if (msgWrapper) msgWrapper.style.display = 'block';
320										})
321									" class="copy-link-btn">
322							<svg width="12" height="15" viewBox="0 0 12 15" fill="none" xmlns="http://www.w3.org/2000/svg">
323								<path
324									d="M7.80815 2.47656H1.61552C1.03445 2.47656 0.5625 2.97882 0.5625 3.5972V13.053C0.5625 13.6714 1.03445 14.1736 1.61552 14.1736H7.80815C8.38922 14.1736 8.86117 13.6714 8.86117 13.053V3.5972C8.85844 2.97882 8.38649 2.47656 7.80815 2.47656ZM8.12187 13.0501C8.12187 13.2359 7.98001 13.3868 7.80542 13.3868H1.61279C1.4382 13.3868 1.29634 13.2359 1.29634 13.0501V3.5972C1.29634 3.4114 1.4382 3.26043 1.61279 3.26043H7.80542C7.98001 3.26043 8.12187 3.4114 8.12187 3.5972V13.0501Z"
325									fill="white" />
326								<path
327									d="M10.1831 0H3.99052C3.40945 0 2.9375 0.502256 2.9375 1.12064C2.9375 1.33838 3.10118 1.51257 3.30578 1.51257C3.51039 1.51257 3.67407 1.33838 3.67407 1.12064C3.67407 0.934835 3.81593 0.783868 3.99052 0.783868H10.1831C10.3577 0.783868 10.4996 0.934835 10.4996 1.12064V10.5764C10.4996 10.7622 10.3577 10.9132 10.1831 10.9132C9.97854 10.9132 9.81486 11.0874 9.81486 11.3051C9.81486 11.5229 9.97854 11.697 10.1831 11.697C10.7642 11.697 11.2362 11.1948 11.2362 10.5764V1.12064C11.2362 0.502256 10.7642 0 10.1831 0Z"
328									fill="white" />
329							</svg>
330		
331							Copy Link
332						</button>
333					</div>
334				</div>
335		
336				<!-- Copy Confirmation -->
337				<div id="copy-success-wrapper">
338					<svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg">
339						<path
340							d="M8.17401 15H2.41299C1.08243 15 0 13.9486 0 12.6562V4.7168C0 3.42442 1.08243 2.37305 2.41299 2.37305H8.17401C9.50458 2.37305 10.587 3.42442 10.587 4.7168V12.6562C10.587 13.9486 9.50458 15 8.17401 15ZM2.41299 3.54492C1.74777 3.54492 1.2065 4.07066 1.2065 4.7168V12.6562C1.2065 13.3024 1.74777 13.8281 2.41299 13.8281H8.17401C8.83924 13.8281 9.38051 13.3024 9.38051 12.6562V4.7168C9.38051 4.07066 8.83924 3.54492 8.17401 3.54492H2.41299ZM13 11.1914V2.34375C13 1.05137 11.9176 0 10.587 0H3.89095C3.55775 0 3.2877 0.262299 3.2877 0.585938C3.2877 0.909576 3.55775 1.17188 3.89095 1.17188H10.587C11.2522 1.17188 11.7935 1.69762 11.7935 2.34375V11.1914C11.7935 11.515 12.0636 11.7773 12.3968 11.7773C12.73 11.7773 13 11.515 13 11.1914Z"
341							fill="#0F6848" />
342					</svg>
343		
344					<strong class="url-txt">URL Copied!</strong>
345					<div class="small ml-lg-4">
346						You can bookmark this link or save it somewhere safe to continue your form later.
347					</div>
348				</div>
349		
350				<!-- Email Section -->
351				<div id="email-container">
352						<div class="input-wrapper">
353							<input type="email" id="email-input-${formId}" placeholder="Enter Email Address"
354								/>
355							<button id="send-email-btn-${formId}" 
356								class="copy-link-btn">
357							<svg width="17" height="15" viewBox="0 0 17 15" fill="none" xmlns="http://www.w3.org/2000/svg">
358								<path
359									d="M15.7301 0.319198C15.5782 0.175857 15.3865 0.0752849 15.1768 0.0288894C14.967 -0.0175061 14.7476 -0.00786795 14.5434 0.0567106L1.39306 4.16619C1.1593 4.23917 0.956423 4.38008 0.81385 4.56846C0.671277 4.75684 0.596423 4.9829 0.600146 5.21386C0.603868 5.44481 0.685974 5.66865 0.834554 5.85291C0.983133 6.03716 1.19046 6.17226 1.42646 6.23859L7.27344 7.88321C7.36534 7.90889 7.44897 7.95562 7.51678 8.01919C7.58459 8.08276 7.63444 8.16116 7.66183 8.24732L9.41577 13.7286C9.48586 13.9502 9.62974 14.1449 9.82634 14.2843C10.0229 14.4236 10.262 14.5004 10.5084 14.5033L10.5266 14.5035C10.77 14.5041 11.0074 14.4325 11.2048 14.299C11.4023 14.1655 11.5497 13.9769 11.626 13.7602L16.0098 1.43169C16.0787 1.24028 16.089 1.03459 16.0396 0.837962C15.9901 0.641328 15.8829 0.461614 15.7301 0.319198ZM10.5266 13.4165L8.77261 7.93497C8.74347 7.85744 8.70804 7.78211 8.66663 7.70966L12.075 4.51415C12.1288 4.46368 12.1715 4.40377 12.2006 4.33784C12.2298 4.2719 12.2448 4.20124 12.2448 4.12987C12.2448 4.0585 12.2298 3.98783 12.2006 3.9219C12.1715 3.85596 12.1288 3.79606 12.075 3.74559C12.0212 3.69513 11.9573 3.6551 11.8869 3.62779C11.8166 3.60047 11.7412 3.58642 11.6651 3.58642C11.589 3.58642 11.5136 3.60047 11.4433 3.62779C11.3729 3.6551 11.309 3.69513 11.2552 3.74559L7.84689 6.9411C7.76961 6.90231 7.68928 6.86913 7.60659 6.84184L1.75965 5.19752L14.9103 1.08775L10.5266 13.4165ZM1.05989 11.5798C0.951176 11.6817 0.803732 11.7389 0.649991 11.7389C0.496251 11.7389 0.348808 11.6817 0.240097 11.5798C0.131386 11.4779 0.0703125 11.3396 0.0703125 11.1955C0.0703125 11.0514 0.131386 10.9131 0.240097 10.8112L3.13881 8.09367C3.24752 7.99175 3.39496 7.93449 3.5487 7.93449C3.70244 7.93449 3.84989 7.99175 3.9586 8.09367C4.06731 8.19558 4.12838 8.33381 4.12838 8.47794C4.12838 8.62208 4.06731 8.76031 3.9586 8.86222L1.05989 11.5798ZM7.43705 11.3547C7.4909 11.4052 7.53361 11.4651 7.56275 11.531C7.5919 11.597 7.6069 11.6676 7.6069 11.739C7.6069 11.8104 7.5919 11.881 7.56275 11.947C7.53361 12.0129 7.4909 12.0728 7.43705 12.1233L4.53834 14.8408C4.48451 14.8913 4.42061 14.9313 4.35028 14.9586C4.27995 14.9859 4.20457 15 4.12844 15C4.05232 15 3.97694 14.9859 3.90661 14.9586C3.83628 14.9313 3.77238 14.8913 3.71855 14.8408C3.66472 14.7904 3.62202 14.7305 3.59289 14.6645C3.56376 14.5986 3.54876 14.5279 3.54876 14.4565C3.54876 14.3852 3.56376 14.3145 3.59289 14.2486C3.62202 14.1826 3.66472 14.1227 3.71855 14.0723L6.61726 11.3547C6.67108 11.3042 6.73498 11.2642 6.80531 11.2369C6.87564 11.2096 6.95102 11.1955 7.02715 11.1955C7.10328 11.1955 7.17867 11.2096 7.249 11.2369C7.31933 11.2642 7.38323 11.3042 7.43705 11.3547ZM4.52135 11.5585L1.64246 14.2947C1.589 14.3455 1.52539 14.3859 1.45526 14.4137C1.38513 14.4415 1.30985 14.456 1.23373 14.4565C1.1576 14.457 1.08212 14.4434 1.01159 14.4166C0.941057 14.3897 0.876859 14.3501 0.822659 14.3C0.768459 14.2498 0.72532 14.1902 0.695703 14.1245C0.666086 14.0587 0.650573 13.9881 0.650048 13.9168C0.649524 13.8454 0.663998 13.7746 0.692645 13.7085C0.721293 13.6424 0.763552 13.5822 0.817009 13.5314L3.6959 10.7953C3.80398 10.6932 3.95083 10.6354 4.10427 10.6346C4.2577 10.6339 4.4052 10.6902 4.51444 10.7912C4.62368 10.8922 4.68575 11.0297 4.68705 11.1735C4.68834 11.3173 4.62876 11.4558 4.52135 11.5585Z"
360									fill="white" />
361							</svg>
362		
363							Send
364						</button>
365					</div>
366					<div id="email-error-${formId}"
367						style="color: #dc3545; font-size: 12px; margin-top: 4px; display: none; text-align: left; padding-left: 4px;">
368						Please enter a valid email address
369					</div>
370				</div>
371		
372				<!-- Email Confirmation -->
373				<div id="email-success-wrapper-${formId}" style="
374								background: #d4edda;
375								border: 1px solid #c3e6cb;
376								padding: 10px 12px;
377								border-radius: 4px;
378								display: none;
379								margin-bottom: 12px;
380								text-align: left;
381								margin-top: 20px;
382							">
383					<strong class="url-txt">
384						<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
385							<path
386								d="M15.6817 1.21221C15.5382 1.0678 15.3572 0.966474 15.1591 0.919731C14.9609 0.872988 14.7537 0.882698 14.5608 0.94776L2.13999 5.08802C1.9192 5.16155 1.72758 5.30351 1.59291 5.4933C1.45825 5.6831 1.38755 5.91085 1.39107 6.14354C1.39458 6.37623 1.47213 6.60174 1.61247 6.78738C1.75281 6.97302 1.94863 7.10912 2.17154 7.17595L7.69416 8.83289C7.78096 8.85876 7.85996 8.90584 7.924 8.96989C7.98805 9.03393 8.03513 9.11293 8.061 9.19973L9.71765 14.7221C9.78385 14.9453 9.91974 15.1415 10.1054 15.2819C10.2911 15.4223 10.5169 15.4996 10.7497 15.5026L10.7668 15.5028C10.9967 15.5033 11.221 15.4312 11.4075 15.2967C11.594 15.1622 11.7332 14.9722 11.8053 14.7539L15.9458 2.33304C16.0109 2.1402 16.0207 1.93297 15.974 1.73486C15.9273 1.53676 15.826 1.3557 15.6817 1.21221ZM10.7668 14.4076L9.11016 8.88504C9.08264 8.80692 9.04917 8.73104 9.01006 8.65803L12.2293 5.43859C12.2802 5.38775 12.3205 5.32739 12.348 5.26096C12.3755 5.19453 12.3897 5.12333 12.3897 5.05143C12.3897 4.97953 12.3755 4.90833 12.348 4.8419C12.3205 4.77548 12.2802 4.71512 12.2293 4.66428C12.1785 4.61343 12.1181 4.5731 12.0517 4.54559C11.9853 4.51807 11.9141 4.50391 11.8422 4.50391C11.7703 4.50391 11.6991 4.51807 11.6327 4.54559C11.5662 4.5731 11.5059 4.61343 11.455 4.66428L8.2358 7.88372C8.16281 7.84464 8.08693 7.81121 8.00883 7.78372L2.48625 6.12708L14.9074 1.98652L10.7668 14.4076ZM1.8253 12.5571C1.72262 12.6598 1.58336 12.7175 1.43815 12.7175C1.29293 12.7175 1.15367 12.6598 1.05099 12.5571C0.94831 12.4545 0.890625 12.3152 0.890625 12.17C0.890625 12.0248 0.94831 11.8855 1.05099 11.7828L3.7889 9.04492C3.89158 8.94224 4.03084 8.88456 4.17605 8.88456C4.32126 8.88456 4.46053 8.94224 4.56321 9.04492C4.66589 9.1476 4.72357 9.28687 4.72357 9.43208C4.72357 9.57729 4.66589 9.71656 4.56321 9.81924L1.8253 12.5571ZM7.84869 12.3304C7.89955 12.3812 7.9399 12.4416 7.96743 12.508C7.99495 12.5745 8.00912 12.6457 8.00912 12.7176C8.00912 12.7895 7.99495 12.8607 7.96743 12.9271C7.9399 12.9935 7.89955 13.0539 7.84869 13.1047L5.11079 15.8426C5.05995 15.8935 4.99959 15.9338 4.93316 15.9613C4.86673 15.9888 4.79553 16.003 4.72363 16.003C4.65173 16.003 4.58053 15.9888 4.51411 15.9613C4.44768 15.9338 4.38732 15.8935 4.33648 15.8426C4.28563 15.7918 4.2453 15.7314 4.21779 15.665C4.19027 15.5986 4.17611 15.5274 4.17611 15.4555C4.17611 15.3836 4.19027 15.3124 4.21779 15.2459C4.2453 15.1795 4.28563 15.1192 4.33648 15.0683L7.07438 12.3304C7.12521 12.2796 7.18557 12.2392 7.252 12.2117C7.31843 12.1842 7.38963 12.17 7.46154 12.17C7.53344 12.17 7.60465 12.1842 7.67108 12.2117C7.73751 12.2392 7.79786 12.2796 7.84869 12.3304ZM5.09475 12.5358L2.37556 15.2924C2.32506 15.3436 2.26498 15.3843 2.19874 15.4123C2.1325 15.4403 2.0614 15.4549 1.9895 15.4554C1.9176 15.4559 1.8463 15.4422 1.77968 15.4152C1.71307 15.3881 1.65243 15.3482 1.60124 15.2977C1.55004 15.2472 1.5093 15.1871 1.48132 15.1209C1.45335 15.0547 1.4387 14.9836 1.4382 14.9117C1.4377 14.8398 1.45138 14.7685 1.47843 14.7018C1.50549 14.6352 1.54541 14.5746 1.5959 14.5234L4.31509 11.7668C4.41717 11.6639 4.55587 11.6057 4.7008 11.6049C4.84572 11.604
3861 4.98503 11.6609 5.08821 11.7626C5.1914 11.8644 5.25002 12.0029 5.25125 12.1478C5.25247 12.2927 5.19619 12.4323 5.09475 12.5358Z"
387								fill="#0F6848" />
388						</svg>
389						Email sent</strong>
390					<div class="small ml-lg-4">
391						You can use the link provided in the email to continue your form later.
392					</div>
393				</div>
394		
395				<!-- Close Button -->
396				<div>
397						<button id="save-draft-feature-closeBtn" onclick="swal.close();">
398						<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
399							<path
400								d="M6.49674 8.5L0.415039 2.41829C-0.138346 1.86491 -0.138346 0.968424 0.415039 0.415039C0.968424 -0.138346 1.86491 -0.138346 2.41829 0.415039L8.5 6.49674L14.5817 0.415039C15.1351 -0.138346 16.0316 -0.138346 16.585 0.415039C17.1383 0.968424 17.1383 1.86491 16.585 2.41829L10.5033 8.5L16.585 14.5817C17.1383 15.1351 17.1383 16.0316 16.585 16.585C16.3083 16.8617 15.9458 17 15.5833 17C15.2209 17 14.8584 16.8617 14.5817 16.585L8.5 10.5033L2.41829 16.585C2.1416 16.8617 1.77913 17 1.41667 17C1.0542 17 0.691732 16.8617 0.415039 16.585C-0.138346 16.0316 -0.138346 15.1351 0.415039 14.5817L6.49674 8.5Z"
401								fill="#CCCCCC" />
402						</svg>
403		
404					</button>
405				</div>
406			</div>
407		</div>
408		
409		`;
410		swal({
411			buttons: false,
412			closeOnClickOutside: false,
413			closeOnEsc: false,
414			content: {
415				element: "div",
416				attributes: {
417					innerHTML: modal,
418				},
419			},
420			onOpen: () => {
421				if (showEmailOption) {
422					document.getElementById("copy-container").style.display = "block";
423					document.getElementById("email-container").style.display = "none";
424				}
425
426				$(document)
427					.off("click", `#send-email-btn-${formId}`)
428					.on("click", `#send-email-btn-${formId}`, async function () {
429						await handleEmailSend(resumeLink, formId, `email-input-${formId}`, `email-success-wrapper-${formId}`,survey, dfToken);
430					});
431
432				$(document).on("input", `#email-input-${formId}`, function () {
433					document.getElementById(`email-error-${formId}`).style.display = "none";
434				});
435			},
436		});
437
438		setTimeout(() => {
439			// Add class to the .swal-content wrapper
440			const swalModal = document.querySelector(".swal-modal");
441			if (swalModal) {
442				swalModal.classList.add("my-swal-wrapper");
443			}
444		});
445
446		$(document).on("click", `#save-draft-feature-closeBtn`, async function () {
447			// survey.clear();
448			// localStorage.removeItem(getAutoSaveKey(formId));
449		});
450
451		$(document).on("click", `#send-email-btn-${formId}`, async function () {
452			await handleEmailSend(resumeLink, formId, `email-input-${formId}`, `email-success-wrapper-${formId}`,survey, dfToken);
453			//  survey.clear();
454		});
455	};
456
457	const resolveBaseEndpoint = (formId) => {
458		let suffix = "";
459		const paths = window.location.pathname.split("/");
460		const search = window.location.search;
461		const query = new URLSearchParams(search);
462		if (paths.length && paths[2] && paths[2] === "preview") {
463			isPreview = true;
464			suffix = `/${paths[1]}/`;
465		}
466		if (query.get("renderMode") == "Preview") {
467			isFormPreview = true;
468			return `${suffix}/api/form/metadata/${formId}?type=${renderedOnLink ? "link" : "embed"}&renderMode=Preview`;
469		}
470		return `${suffix}/api/form/metadata/${formId}?type=${renderedOnLink ? "link" : "embed"}`;
471	};
472
473	const Makeid = (length) => {
474		let result = "";
475		let characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
476		let charactersLength = characters.length;
477		for (let i = 0; i < length; i++) {
478			result += characters.charAt(Math.floor(Math.random() * charactersLength));
479		}
480		return result;
481	};
482
483	const verifyAndGetGoogleCaptchaToken = () => {
484		let siteKey = document.querySelector('meta[name="g-captcha-site-key"]') ? document.querySelector('meta[name="g-captcha-site-key"]').content : "";
485		return new Promise((res, rej) => {
486			grecaptcha.ready(() => {
487				try {
488					grecaptcha
489						.execute(siteKey)
490						.then((token) => {
491							return res(token);
492						})
493						.catch(() => {
494							swal({ text: "Google catcha key is not valid", icon: "error" });
495						});
496				} catch (err) {
497					swal({ text: "Google catcha key is not valid", icon: "error" });
498				}
499			});
500		});
501	};
502
503	function htmlToText(html) {
504		var temp = document.createElement("div");
505		temp.innerHTML = html;
506
507		// Select all elements with the class 'secondary-label'
508		temp.querySelectorAll(".secondary-label").forEach((element) => {
509			element.remove();
510		});
511		// Return text content (or use innerText for visible text only)
512		return temp.textContent.trim(); // Or use temp.innerText if needed.
513	}
514	// Replace 'my-class' with the class you want to remove
515
516	Survey.FunctionFactory.Instance.register("randomId", (prefix) => {
517		const newPrefix = prefix[0] ? prefix[0] : "";
518		const caseType = prefix[1] ? prefix[1] : "upper";
519		const uid = Math.random().toString(36).substring(2, 8) + Math.random().toString(36).substring(2, 8);
520		const finalUID = caseType === "upper" ? uid.toUpperCase() : uid.toLowerCase();
521		return `${newPrefix}${finalUID}`;
522	});
523
524	window.React = { createElement: SurveyUI.createElement, Component: SurveyUI.Component, useEffect: SurveyUI.useEffect, useState: SurveyUI.useState };
525	window.searchItems = {};
526	SurveyUI.ReactElementFactory.Instance.registerElement("MyComponent", (props) => {
527		const getSearchName = () => {
528			if (props.question?.lookupObject) {
529				console.log(window.searchItems[props.question.lookupObject + "_" + props.question.lookupFieldName]);
530				return window.searchItems[props.question.lookupObject + "_" + props.question.lookupFieldName]?.find((i) => i._id == props.item.value)?.searchname || props.item.value || "";
531			}
532			return "";
533			//return window.searchItems.
534		};
535		return React.createElement("div", {
536			className: "dynamic-content",
537			dangerouslySetInnerHTML: { __html: props.item.jsonObj.label ? props.item.jsonObj.label : getSearchName() },
538		});
539	});
540
541	$("[data-form-id]").each(async function (i, el) {
542		let uploadedFiles = {};
543		const formId = $(this).attr("data-form-id");
544
545		const mapping = {
546			Prefix: "prefixDropdown",
547			"First Name": "firstName",
548			"Middle Name": "middleName",
549			"Last Name": "lastName",
550			"St. Address 1": "streetAddressLine1",
551			"St. Address 2": "streetAddressLine2",
552			City: "cityDropdown",
553			State: "state",
554			Country: "country",
555			"Postal Code": "postalCode",
556		};
557
558		const data = await $.ajax({
559			url: resolveBaseEndpoint(formId),
560			method: "GET",
561			contentType: "application/json",
562		});
563		if (!data) return;
564		let surveyData = {};
565		window.tpSurveyFormData = data;
566
567		// Enable Captcha
568		const survey = new Survey.Model(data.Metadata.Content);
569
570		const showDraftStatusPopup = (type, message, canContinue = false) => {
571			let iconBgColor, iconTextColor, iconChar, buttonBg, buttonText;
572
573			switch (type) {
574				case "new-question":
575					iconBgColor = "#075da8";
576					iconTextColor = "white";
577					iconChar = `<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
578							<rect width="90" height="90" rx="45" fill="#075DA8" fill-opacity="0.1"/>
579							<rect x="12" y="12" width="66" height="66" rx="33" fill="#075DA8"/>
580							<g clip-path="url(#clip0_2465_2559)">
581							<path d="M45 32C48.4478 32 51.7544 33.3696 54.1924 35.8076C56.6304 38.2456 58 41.5522 58 45C58 48.4478 56.6304 51.7544 54.1924 54.1924C51.7544 56.6304 48.4478 58 45 58C41.5522 58 38.2456 56.6304 35.8076 54.1924C33.3696 51.7544 32 48.4478 32 45C32 41.5522 33.3696 38.2456 35.8076 35.8076C38.2456 33.3696 41.5522 32 45 32ZM45 61C49.2435 61 53.3131 59.3143 56.3137 56.3137C59.3143 53.3131 61 49.2435 61 45C61 40.7565 59.3143 36.6869 56.3137 33.6863C53.3131 30.6857 49.2435 29 45 29C40.7565 29 36.6869 30.6857 33.6863 33.6863C30.6857 36.6869 29 40.7565 29 45C29 49.2435 30.6857 53.3131 33.6863 56.3137C36.6869 59.3143 40.7565 61 45 61ZM42.5 50C41.6687 50 41 50.6688 41 51.5C41 52.3312 41.6687 53 42.5 53H47.5C48.3312 53 49 52.3312 49 51.5C49 50.6688 48.3312 50 47.5 50H47V44.5C47 43.6687 46.3312 43 45.5 43H42.5C41.6687 43 41 43.6687 41 44.5C41 45.3312 41.6687 46 42.5 46H44V50H42.5ZM45 41C45.5304 41 46.0391 40.7893 46.4142 40.4142C46.7893 40.0391 47 39.5304 47 39C47 38.4696 46.7893 37.9609 46.4142 37.5858C46.0391 37.2107 45.5304 37 45 37C44.4696 37 43.9609 37.2107 43.5858 37.5858C43.2107 37.9609 43 38.4696 43 39C43 39.5304 43.2107 40.0391 43.5858 40.4142C43.9609 40.7893 44.4696 41 45 41Z" fill="white"/>
582							</g>
583							<defs>
584							<clipPath id="clip0_2465_2559">
585							<rect width="32" height="32" fill="white" transform="translate(29 29)"/>
586							</clipPath>
587							</defs>
588							</svg>
589							`;
590					buttonBg = "#075da8";
591					buttonText = `<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
592								<path d="M8.61834 11.3978L7.48216 10.2616C7.34849 10.1279 7.14798 10.1279 7.01431 10.2616C6.88065 10.3953 6.88065 10.5958 7.01431 10.7294L8.38442 12.0995C8.45126 12.1664 8.55151 12.1998 8.61834 12.1998C8.7186 12.1998 8.78543 12.1664 8.85226 12.0995L11.6259 9.32591C11.7596 9.19224 11.7596 8.99173 11.6259 8.85806C11.4922 8.7244 11.2917 8.7244 11.1581 8.85806L8.61834 11.3978Z" fill="white"/>
593								<path d="M15.5402 14.3101L13.2678 12.0377C13.4349 11.6367 13.5686 11.2023 13.602 10.7678C13.7022 9.63164 13.3681 8.49545 12.6329 7.6266C12.2319 7.12534 11.7306 6.75775 11.1625 6.49041V4.75272C11.1625 3.9507 10.4942 3.28236 9.65874 3.28236H8.75647V2.81451C8.75647 2.31325 8.35547 1.91225 7.82079 1.91225H6.81827C6.65119 1.37757 6.14993 0.976562 5.58183 0.976562C4.98032 0.976562 4.51248 1.37757 4.34539 1.87883H3.34287C2.8082 1.91225 2.40719 2.31325 2.40719 2.81451V3.28236H1.50492C0.669492 3.28236 0.00114639 3.9507 0.00114639 4.75272V15.5465C-0.0322709 16.3485 0.669492 17.0169 1.50492 17.0169H9.65874C10.4942 17.0169 11.1625 16.3485 11.1625 15.5131V14.4437L13.2678 16.549C13.8693 17.1505 14.8718 17.1839 15.5068 16.549C16.1417 15.9141 16.1751 14.9116 15.5402 14.3101ZM12.1316 8.02761C12.7331 8.76279 13.0339 9.73189 12.9336 10.6676C12.8 12.0377 11.9311 13.1739 10.7281 13.6751C10.7281 13.6751 10.7281 13.6751 10.6947 13.6751C10.16 13.909 9.59191 13.9759 8.95698 13.9425C6.95194 13.742 5.515 11.9708 5.7155 9.96581C5.81575 9.03013 6.25018 8.19469 6.91852 7.59318C6.91852 7.59318 6.91852 7.59318 6.95194 7.55977C7.68712 6.92484 8.68964 6.59067 9.69216 6.69092C10.0597 6.72434 10.3939 6.82459 10.7281 6.95826C11.2628 7.22559 11.7306 7.55977 12.1316 8.02761ZM3.07554 2.81451C3.07554 2.68084 3.20921 2.58059 3.34287 2.58059H4.64615C4.84665 2.58059 4.98032 2.44692 4.98032 2.24642C4.98032 1.91225 5.24766 1.67833 5.58183 1.67833C5.91601 1.67833 6.21676 1.94566 6.21676 2.24642C6.21676 2.44692 6.35043 2.58059 6.55093 2.58059H7.85421C7.98788 2.58059 8.12155 2.68084 8.12155 2.81451V4.41854C8.12155 4.55221 7.98788 4.65246 7.85421 4.65246H3.34287C3.20921 4.65246 3.07554 4.55221 3.07554 4.41854V2.81451ZM9.65874 16.3485H1.50492C1.03708 16.3485 0.636075 15.9809 0.636075 15.5465V4.75272C0.636075 4.31829 1.00366 3.9507 1.47151 3.9507H2.37377V4.41854C2.37377 4.9198 2.77478 5.32081 3.30946 5.32081H7.82079C8.32205 5.32081 8.75647 4.9198 8.75647 4.41854V3.9507H9.65874C10.1266 3.9507 10.4942 4.31829 10.4942 4.7
5935272V6.22308C10.2603 6.15624 9.99291 6.08941 9.72558 6.08941C8.52255 5.95574 7.41978 6.35675 6.55093 7.05851H2.54086C2.34036 7.05851 2.20669 7.19218 2.20669 7.39268C2.20669 7.59318 2.34036 7.72685 2.54086 7.72685H5.91601C5.68208 8.02761 5.515 8.32836 5.34791 8.69595H2.57428C2.37377 8.69595 2.2401 8.82962 2.2401 9.03013C2.2401 9.23063 2.37377 9.3643 2.57428 9.3643H5.18083C5.14741 9.5648 5.08057 9.76531 5.08057 9.96581V10.3334H2.57428C2.37377 10.3334 2.2401 10.4671 2.2401 10.6676C2.2401 10.8681 2.37377 11.0017 2.57428 11.0017H5.08057C5.11399 11.3359 5.21424 11.6367 5.31449 11.9708H2.57428C2.37377 11.9708 2.2401 12.1045 2.2401 12.305C2.2401 12.5055 2.37377 12.6392 2.57428 12.6392H5.64867C5.84917 13.0068 6.11651 13.3075 6.41726 13.5749H2.57428C2.37377 13.5749 2.2401 13.7085 2.2401 13.9091C2.2401 14.1096 2.37377 14.2432 2.57428 14.2432H7.41978C7.88762 14.4771 8.35547 14.6108 8.89014 14.6776C9.42482 14.7445 9.9595 14.6776 10.4942 14.544V15.5799C10.5276 15.9475 10.1266 16.3485 9.65874 16.3485ZM15.0723 16.0812C14.7048 16.4488 14.1367 16.4488 13.7691 16.0812L11.6304 13.9759C12.165 13.6417 12.5995 13.2073 12.9336 12.6726L15.0389 14.7779C15.4399 15.1455 15.4399 15.7136 15.0723 16.0812Z" fill="white"/>
594								</svg>
595								Review Form`;
596					break;
597
598				case "removed-question":
599					iconBgColor = "#FFC107";
600					iconTextColor = "white";
601					iconChar = `<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
602								<rect width="90" height="90" rx="45" fill="#FABC2F" fill-opacity="0.1"/>
603								<rect x="12" y="12" width="66" height="66" rx="33" fill="#FABC2F"/>
604								<rect x="41" y="29.4453" width="7" height="20.2222" rx="3.5" fill="white"/>
605								<rect x="41" y="53.5547" width="7" height="7" rx="3.5" fill="white"/>
606								</svg>
607								`;
608					buttonBg = "#FFC107";
609					buttonText = `
610								<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="vertical-align: middle; margin-right: 6px;">
611									<path d="M9.79375 0.0293723C10.6062 0.191858 11.1344 0.982412 10.9719 1.79484L10.9 2.15106C10.7344 2.98536 10.4281 3.77904 10 4.50085H14.5C15.3281 4.50085 16 5.17266 16 6.00071C16 6.57879 15.6719 7.08187 15.1906 7.33185C15.5312 7.60682 15.75 8.02866 15.75 8.50049C15.75 9.23168 15.225 9.841 14.5344 9.97223C14.6719 10.2003 14.75 10.4659 14.75 10.7503C14.75 11.4159 14.3156 11.9814 13.7156 12.1752C13.7375 12.2783 13.75 12.3876 13.75 12.5001C13.75 13.3282 13.0781 14 12.25 14H9.20312C8.60938 14 8.03125 13.825 7.5375 13.4969L6.33437 12.6939C5.5 12.1377 5 11.2002 5 10.1972V9.00045V7.50058V6.72252C5 5.81011 5.41563 4.95081 6.125 4.37898L6.35625 4.19462C7.18437 3.53218 7.75 2.60102 7.95625 1.56361L8.02812 1.20739C8.19063 0.394965 8.98125 -0.133113 9.79375 0.0293723ZM1 5.0008H3C3.55312 5.0008 4 5.44764 4 6.00071V13.0001C4 13.5532 3.55312 14 3 14H1C0.446875 14 0 13.5532 0 13.0001V6.00071C0 5.44764 0.446875 5.0008 1 5.0008Z" fill="white"/>
612								</svg>
613								Acknowledge
614								`;
615					break;
616
617				case "error":
618					iconBgColor = "#DC3545";
619					iconTextColor = "white";
620					iconChar = `<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
621							<rect width="90" height="90" rx="45" fill="#E73047" fill-opacity="0.1"/>
622							<rect x="12" y="12" width="66" height="66" rx="33" fill="#E74A3B"/>
623							<path fill-rule="evenodd" clip-rule="evenodd" d="M44.7644 52.3334C44.2442 52.3334 43.7991 51.8895 43.7088 51.2789L41.0479 33.3322C40.7134 31.072 42.1735 29 44.1005 29H45.4271C47.3541 29 48.8142 31.072 48.4797 33.3322L45.8189 51.2789C45.7298 51.8895 45.2847 52.3334 44.7644 52.3334Z" fill="white"/>
624							<path fill-rule="evenodd" clip-rule="evenodd" d="M47.197 59.0829C47.197 57.7423 46.1032 56.6484 44.7626 56.6484C43.422 56.6484 42.3281 57.7423 42.3281 59.0829C42.3281 60.4235 43.422 61.5173 44.7626 61.5173C46.1032 61.5173 47.197 60.4248 47.197 59.0829Z" fill="white"/>
625							</svg>
626							`;
627					buttonBg = "#DC3545";
628					buttonText = `<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
629							<path d="M14.0913 8.55746C13.601 8.48285 13.1656 8.81891 13.0953 9.29802C12.9187 10.5014 12.3633 11.5993 11.4891 12.4736C9.26405 14.6986 5.64634 14.6968 3.42218 12.4736C1.19846 10.2494 1.19846 6.63038 3.42218 4.40666C4.25868 3.5706 5.31468 3.02549 6.49653 2.82705C7.00478 2.7306 7.55121 2.71392 8.11818 2.77817C8.89666 2.86778 9.62126 3.12607 10.2825 3.50531L9.13986 3.70041C8.6625 3.78229 8.34102 4.23569 8.42334 4.71349C8.49619 5.14117 8.86735 5.44326 9.28731 5.44326C9.33619 5.44326 9.38587 5.4394 9.43642 5.4308L12.4996 4.9071C12.7284 4.86769 12.9333 4.73912 13.0679 4.54884C13.2025 4.359 13.2556 4.12326 13.2161 3.89358L12.6916 0.83082C12.611 0.353462 12.1636 0.0337311 11.6785 0.113861C11.2011 0.195747 10.8796 0.649584 10.962 1.12738L11.1 1.93351C10.2505 1.45694 9.31346 1.14862 8.31785 1.034C7.57219 0.949128 6.84882 0.972737 6.18672 1.09956C4.6624 1.35496 3.27737 2.06937 2.18109 3.16557C-0.72703 6.07368 -0.72703 10.8057 2.18109 13.7146C3.6351 15.169 5.54559 15.8959 7.45564 15.8959C9.36612 15.8959 11.2756 15.1691 12.7302 13.7146C13.8727 12.572 14.5995 11.133 14.8317 9.55342C14.9021 9.07343 14.5704 8.62767 14.0913 8.55746Z" fill="white"/>
630							</svg>
631							Try Again`;
632					break;
633
634				case "resume":
635					iconBgColor = "#075da8";
636					iconTextColor = "white";
637					iconChar = `<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
638					<rect width="90" height="90" rx="45" fill="#075da8" fill-opacity="0.1"/>
639					<rect x="12" y="12" width="66" height="66" rx="33" fill="#075da8"/>
640					<circle cx="45" cy="45" r="20" stroke="white" stroke-width="3"/>
641					<path d="M45 30V45H60" stroke="white" stroke-width="3" stroke-linecap="round"/>
642					<path d="M60 45L51 36" stroke="white" stroke-width="3" stroke-linecap="round"/>
643					<path d="M60 45L51 54" stroke="white" stroke-width="3" stroke-linecap="round"/>
644					</svg>`;
645					buttonBg = "#075da8";
646					buttonText = "Continue";
647					break;
648
649				default:
650					return;
651			}
652
653			const modalContent = `
654						<div style="
655							text-align: center;
656						">
657							<div class="mb-3">
658								${iconChar}
659							</div>
660				
661							<p class="text-dark mb-4 pb-1">${message}</p>
662				
663							<button onclick="swal.close(); ${canContinue ? "" : "location.reload();"}" class="Button-modal" style="background-color: ${buttonBg};">
664								${buttonText}
665							</button>
666							<button id="save-draft-feature-closeBtn" onclick="swal.close();">
667								<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
668		<path d="M6.49674 8.5L0.415039 2.41829C-0.138346 1.86491 -0.138346 0.968424 0.415039 0.415039C0.968424 -0.138346 1.86491 -0.138346 2.41829 0.415039L8.5 6.49674L14.5817 0.415039C15.1351 -0.138346 16.0316 -0.138346 16.585 0.415039C17.1383 0.968424 17.1383 1.86491 16.585 2.41829L10.5033 8.5L16.585 14.5817C17.1383 15.1351 17.1383 16.0316 16.585 16.585C16.3083 16.8617 15.9458 17 15.5833 17C15.2209 17 14.8584 16.8617 14.5817 16.585L8.5 10.5033L2.41829 16.585C2.1416 16.8617 1.77913 17 1.41667 17C1.0542 17 0.691732 16.8617 0.415039 16.585C-0.138346 16.0316 -0.138346 15.1351 0.415039 14.5817L6.49674 8.5Z" fill="#CCCCCC"/>
669		</svg>
670		
671							</button>
672						</div>
673					`;
674
675			swal({
676				buttons: false,
677				closeOnClickOutside: false,
678				closeOnEsc: false,
679				content: {
680					element: "div",
681					attributes: {
682						innerHTML: modalContent,
683					},
684				},
685			});
686
687			$(document).on("click", `#save-draft-feature-closeBtn`, async function () {
688				if (type === "error") {
689					const currentUrl = window.location.href;
690					const cleanUrl = currentUrl.split("?draftToken")[0];
691					window.location.href = cleanUrl;
692				}
693			});
694
695			setTimeout(() => {
696				// Add class to the .swal-content wrapper
697				const swalModal = document.querySelector(".swal-modal");
698				if (swalModal) {
699					swalModal.classList.add("my-swal-wrapper");
700				}
701			});
702		};
703
704		const compareMetadata = (currentMetadata, draftMetadata) => {
705			if (!currentMetadata || !draftMetadata) {
706				return { changed: true, lastOperation: "unknown" };
707			}
708
709			const currentQuestions = {};
710			const draftQuestions = {};
711			const changeHistory = [];
712
713			currentMetadata.Content.pages.forEach((page) => {
714				page.elements?.forEach((el) => {
715					const key = el.valueName || el.name;
716					currentQuestions[key] = true;
717				});
718			});
719
720			draftMetadata.Content.pages.forEach((page) => {
721				page.elements?.forEach((el) => {
722					const key = el.valueName || el.name;
723					draftQuestions[key] = true;
724				});
725			});
726
727			const addedQuestions = [];
728			const removedQuestions = [];
729
730			for (const key in currentQuestions) {
731				if (!draftQuestions[key]) {
732					addedQuestions.push(key);
733					changeHistory.push({ key, operation: "added" });
734				}
735			}
736
737			for (const key in draftQuestions) {
738				if (!currentQuestions[key]) {
739					removedQuestions.push(key);
740					changeHistory.push({ key, operation: "removed" });
741				}
742			}
743
744			const lastChange = changeHistory.length > 0 ? changeHistory[changeHistory.length - 1].operation : "none";
745
746			return {
747				changed: addedQuestions.length > 0 || removedQuestions.length > 0,
748				addedQuestions,
749				removedQuestions,
750				hasAddedQuestions: addedQuestions.length > 0,
751				hasRemovedQuestions: removedQuestions.length > 0,
752				lastOperation: lastChange,
753				removedPagesMap: removedQuestions.map((q) => draftQuestions[q]?.page?.name),
754			};
755		};
756
757		// Function to compare metadata and check for changes
758		// const compareMetadata = (currentMetadata, draftMetadata) => {
759		// 	if (!currentMetadata || !draftMetadata) return { changed: true };
760
761		// 	const currentQuestions = {};
762		// 	const draftQuestions = {};
763
764		// 	// Extract all questions from current form
765		// 	currentMetadata.Content.pages.forEach(page => {
766		// 		page.elements?.forEach(el => {
767		// 			currentQuestions[el.valueName || el.name] = { ...el, page };
768		// 		});
769		// 	});
770		// 	draftMetadata.Content.pages.forEach(page => {
771		// 		page.elements?.forEach(el => {
772		// 			draftQuestions[el.valueName || el.name] = { ...el, page };
773		// 		});
774		// 	});
775
776		// 	// Check for added questions
777		// 	const addedQuestions = Object.keys(currentQuestions).filter(
778		// 		key => !draftQuestions[key]
779		// 	);
780
781		// 	// Check for removed questions
782		// 	const removedQuestions = Object.keys(draftQuestions).filter(
783		// 		key => !currentQuestions[key]
784		// 	);
785
786		// 	return {
787		// 		changed: addedQuestions.length > 0 || removedQuestions.length > 0,
788		// 		addedQuestions,
789		// 		removedQuestions,
790		// 		 // Add these flags for easier checking
791		// 		hasAddedQuestions: addedQuestions.length > 0,
792		// 		hasRemovedQuestions: removedQuestions.length > 0,
793		// 		removedPagesMap: removedQuestions.map(q => draftQuestions[q]?.page?.name)
794		// 	};
795		// };
796
797		// Scroll helper
798		const scrollToQuestion = (questionName) => {
799			setTimeout(() => {
800				let el = document.querySelector(`[name="${questionName}"]`) || document.querySelector(`[data-name="${questionName}"]`) || Array.from(document.querySelectorAll("*")).find((e) => e.name === questionName || e.dataset?.name === questionName);
801
802				if (el) {
803					console.log("✅ Scrolling to:", questionName);
804					el.scrollIntoView({ behavior: "smooth", block: "center" });
805				} else {
806					console.warn("⚠️ Could not find element to scroll to:", questionName);
807				}
808			}, 300);
809		};
810
811		// Get page index of question
812		const getPageIndexOfQuestion = (survey, questionName) => {
813			for (let i = 0; i < survey.pages.length; i++) {
814				if (survey.pages[i].elements.some((q) => q.name === questionName)) {
815					return i;
816				}
817			}
818			return 0;
819		};
820
821		// Get last question on a given page
822		const getLastQuestionOnPage = (page) => {
823			if (!page || !page.elements?.length) return null;
824			return page.elements[page.elements.length - 1].name;
825		};
826
827		// Find fallback question when a removed question exists
828		const findFallbackQuestionFromRemoved = (survey, removedPagesMap) => {
829			for (let pageName of removedPagesMap) {
830				const page = survey.pages.find((p) => p.name === pageName);
831				if (page && page.elements.length > 0) {
832					return getLastQuestionOnPage(page);
833				}
834			}
835
836			// fallback to last question of previous page
837			for (let i = survey.pages.length - 1; i >= 0; i--) {
838				if (survey.pages[i].elements.length > 0) {
839					return getLastQuestionOnPage(survey.pages[i]);
840				}
841			}
842			return null;
843		};
844
845		// Load draft
846		if (draftToken) {
847			try {
848				// Call API to get draft data
849				const draftResponse = await $.ajax({
850					url: `/api/form/${formId}/draft/${draftToken}`,
851					method: "GET",
852					contentType: "application/json",
853					dataType: "json",
854				});
855
856				if (draftResponse.status === 200) {
857					const draftData = draftResponse.data;
858
859					// Compare metadata
860					const comparison = compareMetadata(data.Metadata, draftData.Metadata);
861
862					// Load form data
863					survey.data = draftData.FormData;
864					if (draftData.Files) {
865						Object.keys(draftData.Files).forEach((key) => {
866							uploadedFiles[key] = draftData.Files[key].map((fileInfo) => ({
867								...fileInfo,
868								// Add file property if needed for immediate use
869								name: fileInfo.FileName,
870							}));
871						});
872					}
873
874					// Create question lookup
875					const currentQuestionMap = {};
876					survey.pages.forEach((p) => {
877						p.elements.forEach((el) => {
878							currentQuestionMap[el.name] = el;
879						});
880					});
881
882					// Ordered list
883					const orderedQuestionNames = [];
884					survey.pages.forEach((p) => {
885						p.elements.forEach((el) => orderedQuestionNames.push(el.name));
886					});
887
888					// Get last answered question
889					const answeredKeys = Object.keys(draftData.FormData || {});
890					let lastAnsweredQuestion = null;
891					for (let i = answeredKeys.length - 1; i >= 0; i--) {
892						if (currentQuestionMap[answeredKeys[i]]) {
893							lastAnsweredQuestion = answeredKeys[i];
894							break;
895						}
896					}
897
898					let targetQuestion = lastAnsweredQuestion;
899
900					// If removed, find closest fallback
901					if (comparison.hasRemovedQuestions) {
902						targetQuestion = findFallbackQuestionFromRemoved(survey, comparison.removedPagesMap);
903						console.warn("📌 Scrolling to fallback due to removal:", targetQuestion);
904					} else if (!targetQuestion || comparison.removedQuestions.includes(targetQuestion)) {
905						targetQuestion = orderedQuestionNames.at(-1); // last fallback
906					}
907
908					// Decide page to load
909					let initialPageIndex = 0;
910					if (comparison.hasAddedQuestions) {
911						initialPageIndex = 0;
912					} else if (comparison.hasRemovedQuestions && targetQuestion) {
913						initialPageIndex = getPageIndexOfQuestion(survey, targetQuestion);
914					} else if (!comparison.changed && typeof draftData.CurrentPage === "number") {
915						initialPageIndex = draftData.CurrentPage;
916					} else {
917						initialPageIndex = getPageIndexOfQuestion(survey, targetQuestion);
918					}
919					survey.currentPageNo = initialPageIndex;
920
921					let scrolled = false;
922
923					survey.onAfterRenderPage.add(() => {
924						if (scrolled) return;
925						if (comparison.lastOperation === "added") {
926							showDraftStatusPopup("new-question", "A new question has been added to the form since your last visit. Kindly review the form again.", true);
927							survey.currentPageNo = 0;
928						} else if (comparison.lastOperation === "removed") {
929							showDraftStatusPopup("removed-question", "A question you previously answered has been removed from the form. Your response to that question will not be included in your submission.", true);
930							scrollToQuestion(targetQuestion);
931						}
932						// if (comparison.hasAddedQuestions) {
933						// 	showDraftStatusPopup(
934						// 		"new-question",
935						// 		"A new question has been added to the form since your last visit. Kindly review the form again.",
936						// 		true
937						// 	);
938						// 	survey.currentPageNo = 0;
939						// } else if (comparison.hasRemovedQuestions) {
940						// 	showDraftStatusPopup(
941						// 		"removed-question",
942						// 		"A question you previously answered has been removed from the form. Your response to that question will not be included in your submission.",
943						// 		true
944						// 	);
945						// 	scrollToQuestion(targetQuestion);
946						// }
947						else {
948							showDraftStatusPopup("resume", "Let's pick up right where you left off!", true);
949							setTimeout(() => scrollToQuestion(targetQuestion), 500);
950						}
951						scrolled = true;
952					});
953
954					survey.onCurrentPageChanged.add(() => {
955						if (!scrolled && !(comparison.hasAddedQuestions && comparison.hasRemovedQuestions)) {
956							scrollToQuestion(targetQuestion);
957							scrolled = true;
958						}
959					});
960				}
961			} catch (error) {
962				if (error.status === 404) {
963					showDraftStatusPopup("error", "The draft form link you provided is invalid. This may be because the link is incorrect or the form has already been submitted. Please check the link and try again.", false);
964				} else {
965					showDraftStatusPopup("error", "An unexpected error occurred while saving your progress. Please try again later or contact support if the issue persists.", false);
966				}
967			}
968		}
969		// AUTO-RECOVERY FUNCTIONALITY
970		const loadDraftData = () => {
971			// Only load from localStorage if we don't have a draft token
972			if (!draftToken) {
973				const draft = loadDraftFromLocalStorage(formId);
974				if (draft && draft.data) {
975					survey.data = draft.data;
976					if (draft.currentPage >= 0 && draft.currentPage < survey.visiblePages.length) {
977						survey.currentPageNo = draft.currentPage;
978					}
979				}
980			}
981		};
982		loadDraftData();
983
984		const saveDraftToDatabase = async () => {
985			if (urlParams.has("renderMode") || autoSaveToDB) return;
986			try {
987				if (!navigator.onLine) {
988					return showDraftStatusPopup("error", "We’ve encountered network error, reconnect to the internet and try saving again.", false);
989				}
990				// Get the current auto-saved data
991				const autoSavedData = loadDraftFromLocalStorage(formId);
992				if (!autoSavedData || !autoSavedData.data) {
993					return swal({ text: "No draft data to save", icon: "info" });
994				}
995
996				const formData = new FormData();
997				// Append files to FormData
998				Object.keys(uploadedFiles).forEach((key) => {
999					uploadedFiles[key].forEach((fileObj) => {
1000						formData.append(key, fileObj.file, fileObj.file.name);
1001					});
1002					delete surveyData[key];
1003				});
1004				// Prepare the draft data
1005				const draftData = {
1006					formData: survey.data,
1007					// formData: autoSaveToDB ? survey.data : autoSavedData.data,
1008					currentPage: survey.currentPageNo,
1009					metadata: autoSavedData.metadata,
1010					form_id: data._id,
1011					draftToken: draftToken ? draftToken : null,
1012					formId: formId,
1013				};
1014
1015				formData.append("draftData", JSON.stringify(draftData));
1016				// Call the API
1017				const response = await $.ajax({
1018					url: `/api/form/${formId}/draft`,
1019					method: "POST",
1020					data: formData,
1021					contentType: false,
1022					processData: false,
1023				});
1024
1025				if (response.status === "success") {
1026					// Store the token locally
1027					// if(!autoSaveToDB){
1028
1029					let resumeLink = "";
1030					const currentUrl = new URL(window.location.href);
1031					if (currentUrl.searchParams.has("draftToken")) {
1032						// Replace existing draftToken
1033						currentUrl.searchParams.set("draftToken", response.data.draftToken);
1034						resumeLink = currentUrl.toString();
1035					} else {
1036						resumeLink = `${window.location.href}?draftToken=${response.data.draftToken}`;
1037					}
1038					let dfToken = response.data.draftToken;
1039					triggerDraftSavedPopup(resumeLink, data, survey, dfToken);
1040					// }
1041					autoSaveToDB = true;
1042					return response.data;
1043				} else {
1044					throw new Error(response.message || "Failed to save draft");
1045				}
1046			} catch (error) {
1047				console.error("Draft save error:", error);
1048				swal({
1049					title: "Error",
1050					text: "Failed to save draft. Please try again.",
1051					icon: "error",
1052				});
1053				throw error;
1054			}
1055		};
1056
1057		survey.onValueChanged.add((sender, options) => {
1058			if (options.question.getType() === "phonenumber" && options.question.itl) {
1059				const fullNumber = options.question.itl.getNumber();
1060				options.question.value = fullNumber; // update SurveyJS value
1061				sender.data[options.question.valueName] = fullNumber; // update sender.data (important for localStorage)
1062			}
1063			if (!recordTokenId && data.SaveProgressEnabled && (renderMode === null || renderMode !== "Preview")) {
1064				const filteredData = {...sender.data};
1065				sender.getAllQuestions().forEach(question => {
1066					if (question.getType() === "paneldynamic" && filteredData[question.valueName]) {
1067						const panelData = filteredData[question.valueName];
1068				
1069						if (Array.isArray(panelData)) {
1070							// Process each panel instance
1071							filteredData[question.valueName] = panelData.map(panelInstance => {
1072								const filteredInstance = { ...panelInstance };
1073								// Find file questions within this panel
1074								question.templateElements.forEach(templateQuestion => {
1075									if (templateQuestion.getType() === "file") {
1076										const fileValueName = templateQuestion.valueName;
1077										if (filteredInstance[fileValueName]) {
1078											// Remove file data
1079											delete filteredInstance[fileValueName];
1080										}
1081									}
1082								});
1083								return filteredInstance;
1084							});
1085						}
1086					} else if (question.getType() === "file") {
1087						// Remove regular file questions
1088						delete filteredData[question.valueName];
1089					}
1090				});
1091				saveToLocalStorage(formId, filteredData, sender.currentPageNo, data.Metadata);
1092				// saveToLocalStorage(formId, sender.data, sender.currentPageNo, data.Metadata);
1093				// autoSaveToDB ? saveDraftToDatabase() : saveToLocalStorage(formId, sender.data, sender.currentPageNo, data.Metadata);
1094			}
1095		});
1096
1097		survey.onCurrentPageChanged.add((sender) => {
1098			if (!recordTokenId && data.SaveProgressEnabled && (renderMode === null || renderMode !== "Preview")) {
1099				saveToLocalStorage(formId, sender.data, sender.currentPageNo, data.Metadata);
1100				// autoSaveToDB ? saveDraftToDatabase() : saveToLocalStorage(formId, sender.data, sender.currentPageNo, data.Metadata);
1101			}
1102		});
1103
1104		// Clear storage on complete
1105		// survey.onComplete.add(() => {
1106		// 	localStorage.removeItem(getAutoSaveKey(formId));
1107		//     localStorage.removeItem(`formDraft_${formId}`);
1108		// });
1109
1110		const resetForm = () => {
1111			if (Object.keys(survey.data).length === 0) {
1112				return swal({
1113					icon: "warning",
1114					title: "Nothing to Reset",
1115					text: "There is no saved data to reset in this form.",
1116					buttons: {
1117						ok: {
1118							text: "OK",
1119							className: "swal-button--confirm",
1120						},
1121					},
1122				});
1123			}
1124
1125			const modalContent = `
1126			<div style="text-align: center; padding: 10px 20px;">
1127				<div class="mb-3">
1128					<svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
1129						<rect width="90" height="90" rx="45" fill="#FFC107" fill-opacity="0.1"/>
1130						<rect x="12" y="12" width="66" height="66" rx="33" fill="#FFC107"/>
1131						<path fill-rule="evenodd" clip-rule="evenodd" d="M44.7644 52.3334C44.2442 52.3334 43.7991 51.8895 43.7088 51.2789L41.0479 33.3322C40.7134 31.072 42.1735 29 44.1005 29H45.4271C47.3541 29 48.8142 31.072 48.4797 33.3322L45.8189 51.2789C45.7298 51.8895 45.2847 52.3334 44.7644 52.3334Z" fill="white"/>
1132						<path fill-rule="evenodd" clip-rule="evenodd" d="M47.197 59.0829C47.197 57.7423 46.1032 56.6484 44.7626 56.6484C43.422 56.6484 42.3281 57.7423 42.3281 59.0829C42.3281 60.4235 43.422 61.5173 44.7626 61.5173C46.1032 61.5173 47.197 60.4248 47.197 59.0829Z" fill="white"/>
1133					</svg>
1134				</div>
1135				<p class="text-dark mb-4 pb-1" style="font-weight: 500;">You’re about to reset the previously filled data, all form data which has not been submitted will be lost</p>
1136				<div style="display: flex; justify-content: center; gap: 10px;">
1137					<button id="noBtn" class="Button-modal" style="background-color: #6c757d; color: white;">Cancel</button>
1138					<button id="yesBtn" class="Button-modal" style="background-color: #FFC107; color: white;">Proceed</button>
1139				</div>
1140				<button id="save-draft-feature-closeBtn" style="margin-top: 15px; background: transparent; border: none;">
1141					<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
1142						<path d="M6.49674 8.5L0.415039 2.41829C-0.138346 1.86491 -0.138346 0.968424 0.415039 0.415039C0.968424 -0.138346 1.86491 -0.138346 2.41829 0.415039L8.5 6.49674L14.5817 0.415039C15.1351 -0.138346 16.0316 -0.138346 16.585 0.415039C17.1383 0.968424 17.1383 1.86491 16.585 2.41829L10.5033 8.5L16.585 14.5817C17.1383 15.1351 17.1383 16.0316 16.585 16.585C16.3083 16.8617 15.9458 17 15.5833 17C15.2209 17 14.8584 16.8617 14.5817 16.585L8.5 10.5033L2.41829 16.585C2.1416 16.8617 1.77913 17 1.41667 17C1.0542 17 0.691732 16.8617 0.415039 16.585C-0.138346 16.0316 -0.138346 15.1351 0.415039 14.5817L6.49674 8.5Z" fill="#CCCCCC"/>
1143					</svg>
1144				</button>
1145			</div>
1146			`;
1147
1148			swal({
1149				buttons: false,
1150				closeOnClickOutside: false,
1151				closeOnEsc: false,
1152				content: {
1153					element: "div",
1154					attributes: {
1155						innerHTML: modalContent,
1156					},
1157				},
1158			});
1159
1160			$(document)
1161				.off("click", "#yesBtn")
1162				.on("click", "#yesBtn", function () {
1163					localStorage.removeItem(getAutoSaveKey(formId));
1164					survey.clear();
1165					uploadedFiles = {};
1166					survey
1167						.getAllQuestions()
1168						.filter((q) => q.getType() === "datepicker")
1169						.forEach((q) => {
1170							if (q.datepicker) {
1171								q.datepicker.setDate(null); // picker
1172								q.datepicker.element.value = ""; // <input>
1173							}
1174						});
1175					swal.close();
1176					document.querySelectorAll(".iti input").forEach((input) => {
1177						input.value = "";
1178					});
1179				});
1180
1181			$(document)
1182				.off("click", "#noBtn")
1183				.on("click", "#noBtn", function () {
1184					swal.close();
1185				});
1186
1187			$(document)
1188				.off("click", "#save-draft-feature-closeBtn")
1189				.on("click", "#save-draft-feature-closeBtn", function () {
1190					swal.close();
1191				});
1192		};
1193
1194		if ((data.SaveProgressEnabled || data?.SaveProgressEnabled === undefined) && !recordTokenId) {
1195			survey.addNavigationItem({
1196				id: "sv-nav-save-draft",
1197				title: "Save Progress",
1198				action: saveDraftToDatabase,
1199				css: "sd-btn--secondary",
1200				innerCss: "sd-btn sd-nav-save-draft",
1201				visibleIndex: 2,
1202			});
1203			// Clear Fields button as hyperlink style
1204			survey.addNavigationItem({
1205				id: "sv-nav-clear-fields",
1206				title: "Reset",
1207				// action: function() {
1208				// 	localStorage.removeItem(getAutoSaveKey(formId));
1209				// 	survey.clear();
1210				// },
1211				action: resetForm,
1212				css: "sd-btn--link", // This makes it look like a link
1213				innerCss: "sd-nav-clear-fields sd-clear-btn-link", // Custom class for styling
1214				// visibleIndex: 1,
1215			});
1216
1217			const resetButton = survey.navigationBar.actions.find((action) => action.id === "sv-nav-clear-fields");
1218
1219			// if(completeButton) completeButton.visibleIndex = 2;
1220			// if(saveDraftButton) saveDraftButton.visibleIndex = 1;
1221			if (resetButton) resetButton.visibleIndex = 3;
1222		}
1223
1224		survey.applyTheme(data.Metadata.Theme);
1225
1226		if (recordTokenId) {
1227			try {
1228				const recordData = await $.ajax({
1229					url: `/api/form/${formId}/${recordTokenId}`,
1230					method: "GET",
1231					contentType: "application/json",
1232				});
1233				if (recordData?.status === "error") {
1234					return swal({ text: "Invalid Record Token", icon: "error" });
1235				}
1236				const { _id, UserAgent, UpdatedBy, UpdatedDate, CreatedAt, recordToken, tokenExpiry, ...cleanedData } = recordData?.data || {};
1237				if (recordData?.data && cleanedData) {
1238					isFrameLoad = true;
1239					survey.data = cleanedData;
1240					window.currentRecordId = _id;
1241
1242					// 🔹 Only show file names (no upload binding)
1243					survey.getAllQuestions().forEach((q) => {
1244						if (q.getType() === "file" && cleanedData[q.valueName]) {
1245							let savedFiles = cleanedData[q.valueName];
1246							if (!Array.isArray(savedFiles)) savedFiles = [savedFiles];
1247							q.value = savedFiles.map((file) => ({ name: file.FileName || file.name || "Unknown file" }));
1248						}
1249						if (q.getType() === "paneldynamic" && cleanedData[q.valueName]) {
1250							let panelData = cleanedData[q.valueName];
1251							if (Array.isArray(panelData)) {
1252								panelData.forEach((panelItem, panelIndex) => {
1253									// Loop over each inner question in template
1254									q.templateElements.forEach((innerQ) => {
1255										if (innerQ.getType() === "file" && panelItem[innerQ.valueName]) {
1256											let savedFiles = panelItem[innerQ.valueName];
1257											if (!Array.isArray(savedFiles)) savedFiles = [savedFiles];
1258											// set file value in the correct panel row
1259											q.panels[panelIndex].getQuestionByName(innerQ.name).value = savedFiles.map((file) => ({
1260												name: file.FileName || file.name || "Unknown file",
1261											}));
1262										}
1263									});
1264								});
1265							}
1266						}
1267					});
1268				}
1269			} catch (err) {
1270				console.error("Error fetching record by token:", err);
1271			}
1272		}
1273
1274		function renderCaptcha() {
1275			const captchaQuestion = {
1276				name: "Captcha",
1277				isRequired: true,
1278				requiredErrorText: "Please complete the captcha",
1279				type: "system-captcha",
1280			};
1281			const pageCount = survey.pages.length;
1282			if (pageCount > 0) {
1283				const lastPage = survey.pages[pageCount - 1];
1284				const captchaQuest = Survey.Serializer.createClass(captchaQuestion.type);
1285				captchaQuest.fromJSON(captchaQuestion);
1286				lastPage.addQuestion(captchaQuest);
1287			}
1288		}
1289
1290		if (data.Captcha == 1) renderCaptcha();
1291
1292		// Custom Error Handling
1293		survey.onSettingQuestionErrors.add((sender, options) => {
1294			const question = options.question;
1295			if (question.value && question.itl && question.getType() === "phonenumber") {
1296				question.clearErrors();
1297				let isValidNo = true;
1298				let validKeyChars = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 32, 40, 41, 43, 45];
1299				for (const c of question.value) {
1300					if (!validKeyChars.includes(c.charCodeAt(0))) {
1301						options.errors.push(new Survey.CustomError(`Invalid number.`));
1302						isValidNo = false;
1303						break;
1304					}
1305				}
1306
1307				if (isValidNo && !question.itl.isValidNumber()) {
1308					const errorMap = ["Invalid number", "Invalid country code", "Number too short", "Number too long"];
1309					const errorCode = question.itl.getValidationError();
1310					const errorMessage = errorMap[errorCode] || "Invalid number";
1311					!options.errors.length && options.errors.push(new Survey.CustomError(errorMessage));
1312				}
1313
1314				if (isValidNo) question.value = question.itl.getNumber();
1315			}
1316			if (question.getType() === "termsconditions" && question.value === false) {
1317				options.errors.push(new Survey.CustomError("Response required."));
1318			}
1319
1320			if (question.getType() === "emailfield" && question.value) {
1321				const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})$/;
1322				if (!emailRegex.test(question.value)) {
1323					options.errors.push(new Survey.CustomError(`Invalid email.`));
1324				}
1325			}
1326
1327			if (question.value && question.inputType === "tel") {
1328				question.clearErrors();
1329				let validKeyChars = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 32, 40, 41, 43, 45];
1330				for (const c of question.value) {
1331					if (!validKeyChars.includes(c.charCodeAt(0))) {
1332						options.errors.push(new Survey.CustomError(`Invalid number.`));
1333						break;
1334					}
1335				}
1336			}
1337		});
1338
1339		survey.onServerValidateQuestions.add(async (_, options) => {
1340			try {
1341				if (formCompleteObject.navigateToUrl) _.navigateToUrl = formCompleteObject.navigateToUrl;
1342				if (formCompleteObject.navigateToUrlOnCondition) _.navigateToUrlOnCondition = formCompleteObject.navigateToUrlOnCondition;
1343				surveyData = {};
1344				const cleanUploadedFiles = {};
1345				Object.assign(surveyData, _.data);
1346
1347				const formLastPage = _.isLastPage;
1348
1349				if (!isPreview && _.isLastPage && !isFormPreview) {
1350					const formData = new FormData();
1351					// const surveyData = { ...options.data };
1352					// options.allowComplete = false;
1353					// Append files to FormData
1354					Object.keys(uploadedFiles).forEach((key) => {
1355						const files = uploadedFiles[key].map((file) => file.file);
1356						delete surveyData[key];
1357						files.forEach((file) => file && formData.append(key, file, file.name));
1358					});
1359
1360					// const isAllFileMetaValid = Object.values(uploadedFiles).every((fileArray) =>
1361					// 	fileArray.every(
1362					// 		(fileObj) =>
1363					// 			fileObj?.FileName &&
1364					// 			fileObj?.Path &&
1365					// 			fileObj?.fieldname &&
1366					// 			fileObj?.Ext
1367					// 	)
1368					// );
1369
1370					if (draftToken) {
1371						// Object.keys(uploadedFiles).forEach((key) => {
1372						// 	cleanUploadedFiles[key] = uploadedFiles[key].map(({ file, ...rest }) => rest);
1373						// });
1374						Object.keys(uploadedFiles).forEach((key) => {
1375							const fileArray = uploadedFiles[key];
1376
1377							if (Array.isArray(fileArray)) {
1378								// Separate valid and invalid files
1379								const draftFiles = fileArray.filter((fileObj) => fileObj?.FileName && fileObj?.Path);
1380								const formFiles = fileArray.filter((fileObj) => !(fileObj?.FileName && fileObj?.Path));
1381								// If there are valid files, store them in cleanUploadedFiles
1382								if (draftFiles.length > 0) {
1383									cleanUploadedFiles[key] = draftFiles.map(({ file, ...rest }) => rest);
1384								}
1385
1386								// Replace uploadedFiles[key] with only the invalid files
1387								if (formFiles.length > 0) {
1388									uploadedFiles[key] = formFiles;
1389								} else {
1390									// If no invalid files, remove the key entirely
1391									delete uploadedFiles[key];
1392								}
1393							}
1394						});
1395					}
1396
1397					// If captcha
1398					if (data.Captcha != 0) {
1399						if (data.Captcha == 1) {
1400							const key = $(el).find("[data-system-captcha-token]").attr("data-system-captcha-token");
1401							surveyData["Captcha"] = `${surveyData["Captcha"]}:${key}`;
1402						} else {
1403							surveyData.Captcha = await verifyAndGetGoogleCaptchaToken();
1404						}
1405					}
1406					survey.getAllQuestions().forEach((question) => {
1407						const val = question.value;
1408
1409						switch (question.getType()) {
1410							case "multilookupfield":
1411								surveyData[question.valueName] = getMultiLookup(question, val);
1412								break;
1413
1414							case "lookupfield":
1415								surveyData[question.valueName] = getLookup(question, val);
1416								break;
1417
1418							case "address":
1419								surveyData[question.valueName] = val?.cityDropdown ? getAddress(question, val) : val;
1420								break;
1421
1422							case "dropdown":
1423								surveyData[question.valueName] = getDropdown(question, val);
1424								break;
1425
1426							case "tagbox":
1427								surveyData[question.valueName] = getTagbox(question, val);
1428								break;
1429
1430							case "file":
1431								if (val && val.length > 0) surveyData[question.valueName] = val;
1432								break;
1433
1434							case "paneldynamic":
1435								surveyData[question.valueName] = (question.panels || []).map((panel) => {
1436									let rowData = {};
1437									(panel.questions || []).forEach((el) => {
1438										const innerVal = el.value;
1439
1440										switch (el.getType()) {
1441											case "multilookupfield":
1442												rowData[el.valueName] = getMultiLookup(el, innerVal);
1443												break;
1444
1445											case "lookupfield":
1446												rowData[el.valueName] = getLookup(el, innerVal);
1447												break;
1448
1449											case "address":
1450												rowData[el.valueName] = innerVal?.cityDropdown ? getAddress(el, innerVal) : innerVal;
1451												break;
1452
1453											case "dropdown":
1454												if(innerVal == "other") {
1455													rowData[el.valueName + "-Comment"] = el.comment
1456												}
1457												rowData[el.valueName] = getDropdown(el, innerVal);
1458												break;
1459											
1460											case "radiogroup": 
1461												if(innerVal == "other") {
1462													rowData[el.valueName + "-Comment"] = el.comment
1463												}
1464												rowData[el.valueName] = innerVal;
1465												break;
1466
1467											case "checkbox": 
1468												if(innerVal.includes("other")){
1469													rowData[el.valueName + "-Comment"] = el.comment
1470												}
1471												rowData[el.valueName] = innerVal;
1472												break;
1473
1474											case "tagbox":
1475												if(innerVal.includes("other")){
1476													rowData[el.valueName + "-Comment"] = el.comment
1477												}
1478												rowData[el.valueName] = getTagbox(el, innerVal);
1479												break;
1480
1481											case "file":
1482												rowData[el.valueName] = innerVal ? innerVal : [];
1483												break;
1484
1485											default:
1486												rowData[el.valueName] = innerVal;
1487										}
1488									});
1489									return rowData;
1490								});
1491								break;
1492
1493							default:
1494								surveyData[question.valueName] = val;
1495						}
1496					});
1497
1498					if (isFrameLoad && window.currentRecordId) {
1499						formData.append("recordId", window.currentRecordId);
1500					}
1501
1502					if (draftToken) formData.append("files", JSON.stringify(cleanUploadedFiles));
1503					formData.append("fields", JSON.stringify(surveyData));
1504					formData.append("draftToken", draftToken ? draftToken : "");
1505					$("#tp--form-loader").show();
1506					$("body").addClass("overflow-hidden");
1507					// AJAX request to send FormData
1508					const response = await $.ajax({
1509						url: `/api/form/${formId}`,
1510						method: "post",
1511						data: formData,
1512						cache: false,
1513						contentType: false,
1514						processData: false,
1515					});
1516					if (response.status === "success") {
1517						// ✅ Close parent popup if iframe mode
1518						if (isFrameLoad && window.parent) {
1519							window.parent.postMessage({ type: "CloseIframe" }, "*");
1520						}
1521						if (recordTokenId) {
1522							$("body").removeClass("overflow-hidden");
1523							$("#tp--form-loader").hide();
1524							return swal({ text: "Record(s) updated", icon: "success", buttons: fal
1524se, timer: 2000 });
1525						}
1526					}
1527
1528					$("body").removeClass("overflow-hidden");
1529					$("#tp--form-loader").hide();
1530					Object.assign(surveyData, {});
1531					if (["error", "conflict", "captcha_required", "captcha_mismatch", "limitReached"].includes(response.status)) {
1532						options.complete();
1533						_.clear(false, false);
1534						await survey.onScrollingElementToTop.add((sender, options) => {
1535							options.cancel = true;
1536						});
1537					}
1538
1539					switch (response.status) {
1540						case "error":
1541							return swal({ text: response.message ? response.message : "Something went wrong!.", icon: "error" });
1542						case "conflict":
1543							return swal({
1544								// text: "This form has been submitted. It cannot be submitted more than once.",
1545								text: response?.message || "This form has been submitted. It cannot be submitted more than once.",
1546								buttons: {
1547									ok: {
1548										text: "OK",
1549										className: "confirm-btn",
1550									},
1551								},
1552							});
1553						case "captcha_required":
1554							return swal({ text: "You must complete the security question." });
1555						case "limitReached":
1556							return swal({ text: response.message });
1557						case "captcha_mismatch":
1558							if (data.Captcha == 1) {
1559								options.errors["Captcha"] = "Invalid captcha value";
1560								return swal({ text: "Invalid captcha value.", icon: "error" });
1561							} else {
1562								return swal({ text: "We have detected unusual activity from your system. This form will not be submitted." });
1563							}
1564							break;
1565					}
1566				}
1567				options.complete();
1568
1569				if (formLastPage) {
1570					uploadedFiles = {};
1571					let SubmissionMessage = _.locCompletedHtml.renderedText || _.locCompletedHtml.renderedHtml;
1572					_.clear(true, false);
1573					$(".survey-captcha input.txtCaptch").val("");
1574					setTimeout(() => {
1575						_.clear(true, false);
1576					}, 100);
1577					if (isFormPreview) {
1578						generateFormResults(surveyData);
1579					}
1580					await survey.onScrollingElementToTop.add((sender, options) => {
1581						options.cancel = true;
1582					});
1583					SubmissionMessage = SubmissionMessage.replace(/<[^>]+>/g, "");
1584					if (!_.navigateToUrl && !(_.navigateToUrlOnCondition && _.navigateToUrlOnCondition.length) && !isFormPreview) {
1585						localStorage.removeItem(getAutoSaveKey(formId));
1586						localStorage.removeItem(`formDraft_${formId}`);
1587						return swal({ text: SubmissionMessage, icon: "success" });
1588					}
1589				}
1590			} catch (error) {
1591				$("body").removeClass("overflow-hidden");
1592				$("#tp--form-loader").hide();
1593				console.error(error.responseJSON, error);
1594
1595				if (_.navigateToUrl) formCompleteObject["navigateToUrl"] = _.navigateToUrl;
1596				if (_.navigateToUrlOnCondition) formCompleteObject["navigateToUrlOnCondition"] = _.navigateToUrlOnCondition;
1597				_.navigateToUrl = "";
1598				_.navigateToUrlOnCondition = [];
1599
1600				if (error.responseJSON && error.responseJSON.status == "error") {
1601					options.complete();
1602					_.clear(false, false);
1603					await survey.onScrollingElementToTop.add((sender, options) => {
1604						options.cancel = true;
1605					});
1606					return swal({ text: error.responseJSON.data ? error.responseJSON.data[0] : "Something went wrong!.", icon: "error" });
1607				} else {
1608					if (error.toString().includes("ReferenceError: grecaptcha is not defined")) {
1609						options.complete();
1610						_.clear(false, false);
1611						await survey.onScrollingElementToTop.add((sender, options) => {
1612							options.cancel = true;
1613						});
1614						return swal({ text: "Google captcha is invalid.", icon: "error" });
1615					} else {
1616						if (error.responseJSON && error.responseJSON.message === "Draft not found") {
1617							return showDraftStatusPopup("error", "The draft form link you provided is invalid. This may be because the link is incorrect or the form has already been submitted. Please check the link and try again.", false);
1618						}
1619						return swal({ text: "Something went wrong!.", icon: "error" });
1620					}
1621				}
1622			}
1623		});
1624
1625		// survey.onComplete.add(async (sender, options) => {
1626		//     let SubmissionMessage = sender.locCompletedHtml.renderedText;
1627		//     sender.clear(true, false);
1628		//     await survey.onScrollingElementToTop.add((sender, options) => {
1629		//         options.cancel = true;
1630		//     })
1631		//     SubmissionMessage = SubmissionMessage.replace(/<[^>]+>/g, '');
1632		//     return swal({ text: SubmissionMessage, icon: 'success' })
1633		// })
1634
1635		function formatFileSize(bytes) {
1636			const sizes = ["Bytes", "KB", "MB", "GB"];
1637			let i = 0;
1638			while (bytes >= 1024 && i < sizes.length - 1) {
1639				bytes /= 1024;
1640				i++;
1641			}
1642			return `${bytes.toFixed(i > 1 ? 2 : 0)} ${sizes[i]}`;
1643		}
1644
1645		survey.onUploadFiles.add((_, options) => {
1646			const isPanelField = options.question.parent.getType() === "panel";
1647
1648	let uniqueKey = options.question.valueName;
1649	let panelInfo = null;
1650
1651	if (isPanelField) {
1652		let panel = options.question.parent; // panel row instance
1653		let parentPanelDynamic = panel?.parentQuestion || panel?.parentPanel; // real dynamic panel question
1654		let panelIndex = null;
1655		let panelName = null;
1656	
1657		// ✅ Get correct index safely
1658		if (parentPanelDynamic?.panels && Array.isArray(parentPanelDynamic.panels)) {
1659			panelIndex = parentPanelDynamic.panels.indexOf(panel);
1660		}
1661	
1662		// ✅ Use actual dynamic panel question name
1663		panelName = parentPanelDynamic?.name || panel?.name || "PanelDynamic";
1664	
1665		// Fallback if index still not found
1666		if (panelIndex === -1 || panelIndex === null || panelIndex === undefined) {
1667			panelIndex = parentPanelDynamic?.currentIndex ?? 0;
1668		}
1669	
1670		panelInfo = { panelName, panelIndex, valueName: options.question.valueName };
1671		uniqueKey = `PANELFILE_${panelName}_${panelIndex}_${options.question.valueName}`;
1672	}
1673	
1674	
1675	const files = options.files.map((file) => ({
1676		file,
1677		...(isPanelField && {
1678			isPanelField: true,
1679			panelName: panelInfo.panelName,
1680			panelIndex: panelInfo.panelIndex,
1681			originalFilename: file.name,
1682			name: `${Makeid(6)}-${file.name.replace(/[^a-zA-Z0-9-_.]/g, "-")}`,
1683		}),
1684	}));
1685
1686	if (isPanelField) {
1687		uploadedFiles[uniqueKey] = [...(uploadedFiles[uniqueKey] || []), ...files];
1688	} else {
1689		uploadedFiles[options.question.valueName] = [
1690			...(uploadedFiles[options.question.valueName] || []),
1691			...files,
1692		];
1693	}
1694			options.callback(files);
1695
1696			for (const file of files) {
1697				let fileType = file.file.name.split(".").pop().toLowerCase();
1698				let fileMime = file.file.type;
1699				let isAccepted = !options.question.acceptedTypes;
1700				let acceptedFormats = [];
1701				if (!options.question.acceptedTypes?.includes(",") && options.question.acceptedTypes?.split("*").length - 1 > 1) {
1702					// for 'audio/*video/*' case
1703					acceptedFormats = [];
1704				} else {
1705					acceptedFormats = options.question.acceptedTypes
1706						?.split(",")
1707						?.map((i) => i.trim())
1708						?.map((j) => (j.startsWith(".") ? j.substring(1).toLowerCase() : j.toLowerCase()));
1709				}
1710
1711				if (acceptedFormats?.length) {
1712					isAccepted =
1713						acceptedFormats.includes(fileType) ||
1714						acceptedFormats.some(
1715							(acc) =>
1716								acc.endsWith("/*") && // check for audio/* etc.
1717								fileMime &&
1718								fileMime.toLowerCase().startsWith(acc.split("/")[0] + "/")
1719						);
1720				}
1721
1722				if (!isAccepted) {
1723					options.question.removeFile(file.file.name);
1724					options.question.clearErrors();
1725					options.question.addError(new Survey.CustomError(`Invalid file type. Please upload ${options.question.acceptedTypes} file type`));
1726				} else if (options.question.maximumfilesize && file.file.size > options.question.maximumfilesize) {
1727					options.question.removeFile(file.file.name);
1728					options.question.clearErrors();
1729					options.question.addError(new Survey.CustomError(`The file size should not exceed ${formatFileSize(options.question.maximumfilesize)}.`));
1730				}
1731				// })
1732			}
1733
1734			setTimeout(() => {
1735				document.querySelectorAll(".sd-file__preview-item .sd-file__sign a[download]").forEach((link) => {
1736					link.removeAttribute("href"); // Remove download link
1737					link.style.pointerEvents = "none"; // Disable clicking
1738					link.style.cursor = "pointer"; // Change cursor
1739				});
1740			});
1741		});
1742
1743		function stripRandomPrefix(fileName) {
1744			if (!fileName) return "";
1745			const parts = fileName.split("-");
1746			parts.shift(); // Remove the random prefix (first 6-char segment)
1747			return parts.join("-");
1748		}
1749
1750		function normalizeFileNameForComparison(fileName) {
1751			return fileName.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
1752		}
1753
1754		survey.onClearFiles.add((_, options) => {
1755			const files = uploadedFiles[options.question.valueName];
1756			const question = options.question;
1757			const isPanelField = question.parent.getType() === "panel";
1758		
1759			if (isPanelField) {
1760				// Identify the panel structure
1761				const panel = question.parent;
1762				const parentQuestion = panel.parentQuestion;
1763				const panelName = parentQuestion?.name || "PanelDynamic";
1764				const panelIndex = parentQuestion.panels.indexOf(panel);
1765				// Use question.name instead of valueName for consistent key
1766				const baseFieldName = options.question.valueName;
1767				const fieldKey = `PANELFILE_${panelName}_${panelIndex}_${baseFieldName}`;
1768				const files = uploadedFiles[fieldKey]
1769				if (Array.isArray(files)) {
1770					if (!options.fileName) {
1771						// uploadedFiles[fieldKey] = files.filter((f) => options?.value?.findIndex((v) => v.name !== f?.file?.name) !== -1);
1772						uploadedFiles[fieldKey] = [];
1773					} else {
1774						uploadedFiles[fieldKey] = files.filter((f) => options?.fileName !== f?.file?.name);
1775					}
1776					uploadedFiles[fieldKey] = uploadedFiles[fieldKey].filter((f) => {
1777						if (options.fileName && f?.FileName) {
1778							const stripped = stripRandomPrefix(f?.FileName || "");
1779							const normalizedStored = normalizeFileNameForComparison(stripped);
1780							const normalizedTarget = normalizeFileNameForComparison(options.fileName);
1781							return normalizedStored !== normalizedTarget;
1782						} else {
1783							return !(f?.FileName && f?.Path);
1784						}
1785					});
1786				} else {
1787					uploadedFiles[fieldKey] = [];
1788				}
1789			} else {
1790				if (Array.isArray(files)) {
1791					if (!options.fileName) {
1792						// uploadedFiles[options.question.valueName] = files.filter((f) => options?.value?.findIndex((v) => v.name !== f?.file?.name) !== -1);
1793						uploadedFiles[options.question.valueName] = [];
1794					} else {
1795						uploadedFiles[options.question.valueName] = files.filter((f) => options?.fileName !== f?.file?.name);
1796					}
1797					uploadedFiles[options.question.valueName] = uploadedFiles[options.question.valueName].filter((f) => {
1798						if (options.fileName && f?.FileName) {
1799							const stripped = stripRandomPrefix(f?.FileName || "");
1800							const normalizedStored = normalizeFileNameForComparison(stripped);
1801							const normalizedTarget = normalizeFileNameForComparison(options.fileName);
1802							return normalizedStored !== normalizedTarget;
1803						} else {
1804							return !(f?.FileName && f?.Path);
1805						}
1806					});
1807				} else {
1808					uploadedFiles[options.question.valueName] = [];
1809				}
1810			}
1811			console.log(uploadedFiles);
1812			return options.callback("success");
1813		});
1814		survey.onChoicesLazyLoad.add((_, options) => {
1815			//console.log(options.question.name)
1816			if (options.question.getType() === "dropdown" && options.question.name === "cityDropdown") {
1817				const url = `/api/getlookuplist`;
1818				let params = {
1819					objectname: "Contact",
1820					fieldname: "City",
1821					skip: options.skip,
1822					searchStr: options.filter,
1823				};
1824				sendRequest(url, params, (data, total) => {
1825					options.setItems(data, data.length);
1826					if (!window.searchItems["Contact_City"]) window.searchItems["Contact_City"] = [];
1827					data.forEach((d) => {
1828						window.searchItems["Contact_City"].push({ _id: d.value, searchname: d.text });
1829					});
1830				});
1831			}
1832			if ((options.question.getType() === "dropdown" && options.question.name === "lookupfield") || (options.question.getType() === "tagbox" && options.question.name === "multilookupfield")) {
1833				const lookupFieldName = options.question.lookupFieldName;
1834				const lookupObject = options.question.lookupObject;
1835				const url = `/api/getlookuplist`;
1836				let params = {
1837					objectname: lookupObject,
1838					fieldname: lookupFieldName,
1839					skip: options.skip,
1840					searchStr: options.filter,
1841				};
1842				if (options.filter) {
1843					if (!$(".sv-list__container").find(".sv-loader").length) $(".sv-list__container").prepend("<div class='sv-loader'></div>");
1844					//$(".sv-list__container").prepend("<div class='sv-loader'></div>");
1845					$(".sv-list__container").find(".sv-list").addClass("hide-sv-list");
1846					$(".sv-list__empty-container").removeClass("sv-dropdown-popup2");
1847					sendRequest(url, params, (data, total) => {
1848						options.setItems(data, data.length);
1849						if (!window.searchItems[lookupObject + "_" + lookupFieldName]) window.searchItems[lookupObject + "_" + lookupFieldName] = [];
1850						data.forEach((d) => {
1851							window.searchItems[lookupObject + "_" + lookupFieldName].push({ _id: d.value, searchname: d.text });
1852						});
1853
1854						$(".sv-list__container").find(".sv-loader").remove();
1855						$(".sv-list__container").find(".sv-list").removeClass("hide-sv-list");
1856
1857						if (!data.length) $(".sv-list__empty-container").addClass("sv-dropdown-popup2");
1858						else $(".sv-list__empty-container").removeClass("sv-dropdown-popup2");
1859					});
1860				} //else {
1861				//options.setItems([], 0);
1862				// $(".sv-list__container").find(".sv-loader").remove();
1863				// $(".sv-list__container").find(".sv-list").removeClass("hide-sv-list");
1864				//$(".sv-list__empty-container").removeClass("sv-dropdown-popup2");
1865				//options.setItems([],0);
1866				//}
1867			}
1868		});
1869
1870		survey.onGetChoiceDisplayValue.add(async (_, options) => {
1871			try {
1872				if (options.question.getType() === "dropdown" && options.question.name === "cityDropdown") {
1873					// const url = `/api/getlookuplist`;
1874					const url = `/api/getmultiplelookuplist`;
1875					let params = {
1876						objectname: "Contact",
1877						fieldname: "City",
1878						skip: options.skip || 0,
1879						searchStr: options.filter || "",
1880						recordIds: options.values,
1881					};
1882					const data = await sendRequest2(url, params);
1883					const name = data.find((i) => i._id == options.question.value)?.searchname;
1884					const key = "Contact" + "_" + "City";
1885					window.searchItems[key] = window.searchItems[key] || [];
1886					data.forEach((d) => {
1887						window.searchItems[key].push({ _id: d._id, searchname: d.searchname });
1888					});
1889					options.setItems([name]);
1890				}
1891
1892				if ((options.question.getType() === "dropdown" && options.question.name === "lookupfield") || (options.question.getType() === "tagbox" && options.question.name === "multilookupfield")) {
1893					const lookupObject = options.question.lookupObject;
1894					const lookupFieldName = options.question.lookupFieldName;
1895					const url = `/api/getmultiplelookuplist`;
1896					let params = {
1897						objectname: lookupObject,
1898						fieldname: lookupFieldName,
1899						recordIds: options.values,
1900					};
1901
1902					// if (window.searchItems[lookupObject + "_" + lookupFieldName] && options.values && options.values.length) {
1903					// 	let items = window.searchItems[lookupObject + "_" + lookupFieldName]?.filter((i) => options.values.includes(i._id)).map((d) => d.searchname) || [];
1904					// 	//return window.searchItems.
1905					// 	options.setItems(items);
1906					// 	return;
1907					// }
1908
1909					let data = await sendRequest2(url, params);
1910					// if((options.question.getType() === "dropdown" && options.question.name === "lookupfield")){
1911					const key = lookupObject + "_" + lookupFieldName;
1912					window.searchItems[key] = window.searchItems[key] || [];
1913					data?.forEach((d) => {
1914						window.searchItems[key].push({ _id: d._id, searchname: d.searchname });
1915					});
1916					// }
1917					data = data.map((d) => d.searchname);
1918					options.setItems(data);
1919				}
1920			} catch (error) {
1921				console.error("Error fetching choice display values:", error);
1922				options.setItems([]);
1923			}
1924		});
1925
1926		const sendRequest2 = async (url, params) => {
1927			try {
1928				const response = await fetch(url, {
1929					method: "POST",
1930					headers: {
1931						"Content-Type": "application/json",
1932					},
1933					body: JSON.stringify(params),
1934				});
1935
1936				if (!response || response.ok != true) {
1937					return [];
1938				}
1939				const data = await response.json();
1940
1941				return data.data;
1942			} catch (error) {
1943				console.error("Error in sendRequest:", error);
1944				return []; // Return an empty array on failure to maintain application flow
1945			}
1946		};
1947
1948		function sendRequest(url, params, onloadSuccessCallback) {
1949			const xhr = new XMLHttpRequest();
1950			xhr.open("POST", url);
1951			//xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
1952			xhr.setRequestHeader("Content-Type", "application/json");
1953			xhr.onload = () => {
1954				if (xhr.status === 200) {
1955					let data = JSON.parse(xhr.response);
1956					Survey.Serializer.findProperty("address", "cityExtraData").value = JSON.stringify(data.data);
1957					data = data.data.map((d) => {
1958						return { value: d._id, text: htmlToText(d.name), label: d.name };
1959						//return { value: d._id, text:  d.searchname, label: d.name };
1960					});
1961					onloadSuccessCallback(data);
1962					return;
1963				}
1964				onloadSuccessCallback([]);
1965			};
1966			// const bodyParams = JSON.stringify(params);
1967			xhr.send(JSON.stringify(params));
1968		}
1969		// let removeAfterPrefilled = false;
1970		// let updateArray,
1971		//     fieldNames = [],
1972		//     searchObj = {},
1973		//     surveyFields = [],
1974		//     sectionObject = [],
1975		//     sectionIdsMap = {};
1976
1977		// if (data?.Settings?.Integration?.Optimiser?.update) {
1978		//     updateArray = data.Settings.Integration.Optimiser.update;
1979		//     fieldNames = updateArray.flatMap((field) => field.UniqueFields.map((j) => j.FormFieldName.replace(/^.*\((.*?)\)$/, '$1')));
1980		//     fieldNames = fieldNames.map((m) => mapping[m] || m)
1981		// }
1982
1983		// function checkUniqueFields(surveyFields, lastAddedField) {
1984		//     // objects where all unique fields are present in surveyFields
1985		//     const completedObjects = updateArray.filter(object =>
1986		//         object.UniqueFields.every(field => {
1987		//             let processedName = field.FormFieldName.replace(/^.*\((.*?)\)$/, '$1').trim() // $1 extract bracket () string and replace
1988		//             return surveyFields.includes(mapping[processedName] || processedName)
1989		//     } )
1990		//     );
1991
1992		//     if (completedObjects.length === 0) return null;
1993
1994		//     // Function to find a related object by "record _id"
1995		//     const findRecordIdObject = (obj) =>
1996		//         updateArray.filter(object =>
1997		//             object.UniqueFields.some(field =>
1998		//                 field.FormFieldName.endsWith("record _id") &&
1999		//                 field.FormFieldName.split(" ")[0] === obj.ObjectName
2000		//             )
2001		//         );
2002
2003		//     let selectedObject;
2004
2005		//     if (completedObjects.length === 1) {
2006		//         selectedObject = completedObjects[0];
2007		//     } else {
2008		//         // If multiple objects are complete, prioritize the one completed due to lastAddedField
2009		//         selectedObject = completedObjects.find(object =>
2010		//             object.UniqueFields.some(field => {
2011		//                 let processedName = field.FormFieldName.trim().replace(/^.*\((.*?)\)$/, '$1').trim()
2012		//                 return (mapping[processedName] || processedName) === lastAddedField
2013		//             }
2014		//             )
2015		//         ) || null;
2016		//     }
2017
2018		//     if (selectedObject) {
2019		//         sectionObject = [selectedObject];
2020		//         for (const obj of sectionObject) {
2021		//             const recordIdObject = findRecordIdObject(obj);
2022		//             if (recordIdObject) {
2023		//                 sectionObject.push(...recordIdObject)
2024		//             }
2025		//         }
2026		//         return true;
2027		//     }
2028
2029		//     return null;
2030		// }
2031
2032		// survey.onValueChanged.add(async (sender, options) => {
2033		//     setTimeout(async () => {
2034		//         const quest = options.question;
2035		//         const questName = ["address", "fullname"].includes(quest.getType()) ? quest.currentElement : quest.name;
2036		//         if (data.Settings.Integration.Optimiser.status === "Disable" || !updateArray) return;
2037		//         if (!fieldNames.includes(questName)) return;
2038
2039		//         surveyFields.push(questName);
2040		//         if(quest.getType() == "phonenumber" && (options.value != quest?.itl?.getNumber()) ){
2041		//             searchObj[options.name] = await quest?.itl && quest.itl.getNumber();
2042		//         }
2043		//         else searchObj[options.name] = options.value;
2044
2045		//         if (!checkUniqueFields(surveyFields, questName)) return;
2046
2047		//         const formData = new FormData();
2048		//         formData.append("fields", JSON.stringify(searchObj));
2049		//         formData.append("updateArray", JSON.stringify(updateArray));
2050		//         formData.append("sectionObject", JSON.stringify(sectionObject));
2051		//         formData.append("sectionIdsMap", JSON.stringify(sectionIdsMap));
2052
2053		//         const response = await $.ajax({
2054		//             url: `/api/form/checkformdataexist/${formId}`,
2055		//             method: "post",
2056		//             data: formData,
2057		//             cache: false,
2058		//             contentType: false,
2059		//             processData: false,
2060		//         });
2061		//         if(response.data){
2062		//             removeAfterPrefilled = true;
2063		//             let mergedData = {...survey.data};
2064		//             Object.keys(response.data).forEach((f) => {
2065		//                 if(mergedData[f] && typeof response.data[f] == "object" && !Array.isArray(response.data[f])) { // for multi-fields like address
2066		//                     mergedData[f] = {...mergedData[f], ...response.data[f]}
2067		//                 } else {
2068		//                     mergedData[f] = response.data[f]
2069		//                 }
2070		//             })
2071		//             survey.data = mergedData;
2072		//         }
2073		//         if(response.allFormFieldsName.length && removeAfterPrefilled) {
2074		//             response.allFormFieldsName.forEach(field => {
2075		//                 const match = field.match(/^(.*?)\s*\((.*?)\)\s*$/);
2076
2077		//                 if (match) {  // for multi-input question like address
2078		//                   const outerFieldName = match[1].trim();
2079		//                   const innerFieldName = match[2].trim();
2080
2081		//                   const innerQuestion = survey.getQuestionByName(outerFieldName)
2082		//                                          ?.contentPanel
2083		//                                          ?.getQuestionByName(innerFieldName);
2084
2085		//                   if (innerQuestion && innerQuestion.value) innerQuestion.value = null;
2086
2087		//                 } else {
2088		//                   const question = survey.getQuestionByName(field);
2089		//                   if (question && question.value) question.value = null;
2090		//                 }
2091		//             });
2092		//         }
2093		//     }, 100)
2094		// });
2095
2096		survey.getAllQuestions().forEach((quest) => {
2097			if (quest.getType() == "datepicker") {
2098				if (quest.currentDate && !quest.visible) {
2099					setTimeout(() => {
2100						const currentDate = moment(new Date(), "YYYY/MM/DD").format(quest.dateFormat || "DD/MM/YYYY");
2101						quest.value = currentDate;
2102					}, 1000);
2103				}
2104			}
2105			if (quest.getType() == "file") {
2106				const maximumfilesize = quest.jsonObj.maximumfilesize;
2107				Survey.Serializer.addProperty("file", {
2108					visibleIndex: 16,
2109					name: "maximumfilesize",
2110					type: "number",
2111					category: "general",
2112					displayName: "Maximum file size (in bytes)",
2113					default: 0,
2114				});
2115				let maximumSize = maximumfilesize ? maximumfilesize : quest.maxSize;
2116				// surveyElement.setPropertyValue("maximumfilesize", maximumSize);
2117				quest.maximumfilesize = maximumSize;
2118
2119				Survey.Serializer.findProperty("file", "maxSize").visible = false;
2120				quest.setPropertyValue("maxSize", 0);
2121			}
2122		});
2123
2124		survey.onAfterRenderQuestion.add((sender, options) => {
2125			if (options.question.getType() == "text") {
2126				let input = options.htmlElement.querySelector("input");
2127				input.disabled = options.question.readOnly ? true : false;
2128				if (input.type === "date" && options.question.value && recordTokenId) {
2129					const formatted = moment(options.question.value).format("YYYY-MM-DD");
2130					console.log("Formatted date:", formatted);
2131					options.question.value = formatted; // update SurveyJS value
2132					input.value = formatted; // sync UI
2133				}
2134
2135				if (input.type === "datetime-local" && options.question.value && recordTokenId) {
2136					const formatted = moment(options.question.value).format("YYYY-MM-DDTHH:mm");
2137					console.log("Formatted datetime:", formatted);
2138					options.question.value = formatted; // update SurveyJS value
2139					input.value = formatted; // sync UI
2140				}
2141			}
2142			const draft = localStorage.getItem(getAutoSaveKey(formId));
2143			if (options.question.getType() === "phonenumber" && options.question.value && (draftToken || draft !== null)) {
2144				setTimeout(() => {
2145					const input = options.htmlElement.querySelector("input");
2146					if (input && window.intlTelInput) {
2147						options.question.itl = window.intlTelInput(input, {
2148							initialCountry: "auto",
2149							geoIpLookup: function (success, failure) {
2150								success("in"); // or dynamic detection
2151							},
2152							utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js",
2153						});
2154						// ✅ IMPORTANT: sync saved number (sets flag correctly)
2155						if (options.question.value) {
2156							options.question.itl.setNumber(options.question.value);
2157						}
2158					}
2159				}, 100);
2160			}
2161			if (options.question.getType() === "datepicker") {
2162				if (recordTokenId) {
2163					setTimeout(() => {
2164						if (options.question.value) {
2165							const value = options.question.value;
2166							// Detect if it's an ISO string (contains 'T' and '-')
2167							const isISODate = typeof value === "string" && value.includes("T") && value.includes("-");
2168
2169							let parsedDate;
2170
2171							if (isISODate) {
2172								parsedDate = moment(value, moment.ISO_8601, true);
2173							} else {
2174								parsedDate = moment(value, ["DD/MM/YYYY", "MM/DD/YYYY"], true);
2175							}
2176
2177							// If still invalid, try last fallback
2178							if (!parsedDate.isValid()) {
2179								parsedDate = moment(value); // loose parse as last resort
2180							}
2181
2182							const formattedDate = parsedDate.isValid() ? parsedDate.format(options.question.dateFormat || "DD/MM/YYYY") : value; // fallback to original if invalid
2183
2184							options.question.datepicker.setDate(formattedDate);
2185						}
2186					}, 500);
2187				}
2188				if (options.question.value && (draftToken || draft != null)) {
2189					setTimeout(() => {
2190						options.question.datepicker.setDate(options.question.value);
2191					}, 1000);
2192				} else if (options.question.currentDate) {
2193					setTimeout(() => {
2194						const currentDate = moment(new Date(), "YYYY/MM/DD").format(options.question.dateFormat || "DD/MM/YYYY");
2195						options.question.datepicker.setDate(currentDate);
2196					}, 1000);
2197				}
2198			}
2199			if (options.question.getType() === "address" && recordTokenId && options.question.value) {
2200				const countryValue = options.question.value.country; // e.g. "India"
2201				const contentPanel = options.question.contentPanel;
2202				const countryQuestion = contentPanel?.getQuestionByName("country");
2203				if (countryQuestion && countryValue) {
2204					const waitForChoices = () => {
2205						console.log("one");
2206						if (countryQuestion.choicesFromUrl && countryQuestion.choicesFromUrl.length > 0) {
2207							const matched = countryQuestion.choicesFromUrl.find((choice) => choice.text?.toLowerCase() === countryValue?.toLowerCase() || choice.value?.toLowerCase() === countryValue?.toLowerCase());
2208							console.log(matched, "matched");
2209							if (matched) {
2210								countryQuestion.value = matched.value;
2211							}
2212						} else {
2213							setTimeout(waitForChoices, 100);
2214						}
2215					};
2216					waitForChoices();
2217				}
2218			}
2219			if (options.question.getType() === "file" && recordTokenId) {
2220				options.question.readOnly = true;
2221			
2222				// Make the file input completely non-clickable
2223				const fileWrapper = options.htmlElement.querySelector(".sd-file");
2224				if (fileWrapper) {
2225					fileWrapper.style.pointerEvents = "none";
2226					fileWrapper.style.opacity = "0.6";
2227				}
2228			}
2229			
2230
2231			switch (options.question.name) {
2232				case "cityDropdown":
2233					if (data.Settings && data.Settings.Integration && data.Settings.Integration.Optimiser) {
2234						options.question.visible = true;
2235					} else {
2236						options.question.visible = false;
2237					}
2238					break;
2239				case "city":
2240					if (data.Settings && data.Settings.Integration && data.Settings.Integration.Optimiser) {
2241						options.question.visible = false;
2242					} else {
2243						options.question.visible = true;
2244					}
2245					break;
2246				case "state":
2247				case "country":
2248					if (data.Settings && data.Settings.Integration && data.Settings.Integration.Optimiser) {
2249						options.question.readOnly = true;
2250					} else {
2251						options.question.readOnly = false;
2252					}
2253					break;
2254				default:
2255					break;
2256			}
2257		});
2258
2259		if (recordTokenId) {
2260			survey.completeText = "Save"; // Change the complete button text
2261		}
2262
2263		$(`[data-form-id="${data.FormID}"]`).Survey({ model: survey });
2264
2265		const $deviceToolbar = $(".device-toolbar");
2266		const $deviceSimulator = $(".device-simulator");
2267		const $previewWrapper = $(".preview-wrapper");
2268		const $formResults = $("#form-results");
2269		const $formName = $("toolbar-form-name");
2270		const $formResultsHeaderButtons = $formResults.find(".header button");
2271		const $resTable = $formResults.find(".content #res_table");
2272		const $resJson = $formResults.find(".content #res_json");
2273
2274		const dims = {
2275			iPhoneSE: { height: 375, width: 667, parentH: 407, parentW: 795 },
2276			iPhone15: { height: 393, width: 852, parentH: 425, parentW: 980 },
2277			iPhone15Plus: { height: 430, width: 932, parentH: 462, parentW: 1060 },
2278			iPadMini: { height: 614.4, width: 819.2, parentH: 646.4, parentW: 947.2 },
2279			iPadAir: { height: 820, width: 1180, parentH: 852, parentW: 1308 },
2280			AndroidPhone: { height: 360, width: 682.667, parentH: 392, parentW: 810.667 },
2281			AndroidTablet: { height: 553.333, width: 853.333, parentH: 594.333, parentW: 1010.333 },
2282			MSSurface: { height: 768, width: 1366, parentH: 800, parentW: 1494 },
2283			desktop: null, // special case
2284		};
2285
2286		let currentDevice = "desktop";
2287		let currentOrientation = "landscape";
2288
2289		function applyDimension(device, orientation) {
2290			// Reset any inline sizing first
2291			$deviceSimulator.add($deviceSimulator.find("> div")).css({ width: "", height: "" });
2292
2293			if (device === "desktop") {
2294				$deviceSimulator.removeClass("simulator");
2295				$previewWrapper.removeClass("simulator");
2296				$("#btn-toggle-orientation").prop("disabled", true);
2297				return;
2298			}
2299			// non-desktop
2300			$deviceSimulator.addClass("simulator");
2301			$previewWrapper.addClass("simulator");
2302			$("#btn-toggle-orientation").prop("disabled", false);
2303
2304			const cfg = dims[device];
2305			if (!cfg) return;
2306
2307			let w = cfg.width,
2308				h = cfg.height;
2309			let parentW = cfg.parentW,
2310				parentH = cfg.parentH;
2311			if (orientation === "portrait") {
2312				[w, h] = [h, w];
2313				[parentW, parentH] = [parentH, parentW];
2314			}
2315			$deviceSimulator.find("> div").css({ width: `${w}px`, height: `${h}px` });
2316			$deviceSimulator.css({ width: `${parentW}px`, height: `${parentH}px` });
2317		}
2318
2319		if (typeof isFormPreview !== "undefined" && isFormPreview) {
2320			$deviceToolbar.addClass("simulator");
2321			$deviceSimulator.addClass("simulator");
2322			$deviceToolbar.find(".toolbar-form-name").text(data.Name);
2323			applyDimension(currentDevice, currentOrientation);
2324		}
2325
2326		// Device selection handler
2327		$deviceToolbar.on("click", ".select-device", function () {
2328			$deviceToolbar.find(".select-device").removeClass("active");
2329			$(this).addClass("active");
2330			currentDevice = $(this).data("device");
2331
2332			let dropdownIconBtn = $deviceToolbar.find("#dropdownIconBtn > img");
2333			if (currentDevice == "desktop") {
2334				dropdownIconBtn.attr("src", `../public/images/desktop.svg`);
2335			} else if (["iPhoneSE", "iPhone15", "iPhone15Plus", "AndroidPhone"].includes(currentDevice)) {
2336				dropdownIconBtn.attr("src", `../public/images/phone.svg`);
2337			} else {
2338				dropdownIconBtn.attr("src", `../public/images/tablet.svg`);
2339			}
2340			applyDimension(currentDevice, currentOrientation);
2341		});
2342
2343		// Orientation toggle
2344		$deviceToolbar.on("click", "#btn-toggle-orientation", function () {
2345			currentOrientation = currentOrientation === "landscape" ? "portrait" : "landscape";
2346			applyDimension(currentDevice, currentOrientation);
2347		});
2348
2349		// Show invisible elements toggle
2350		$deviceToolbar.on("click", "#show-invisible-elements", function () {
2351			survey.showInvisibleElements = !survey.showInvisibleElements;
2352			$(this).toggleClass("active");
2353		});
2354
2355		// Close button
2356		$deviceToolbar.on("click", "#close-btn", function () {
2357			window.close();
2358		});
2359
2360		$previewWrapper.on("click", ".preview-form-again button", function () {
2361			$(".preview-wrapper .sd-body").show();
2362			$(".preview-form-again").hide();
2363			$deviceToolbar.find("#show-invisible-elements").show();
2364			$formResults.hide();
2365		});
2366
2367		function generateFormResults(dataObj) {
2368			$(".preview-wrapper .sd-body").hide();
2369			$(".preview-wrapper .sv-components-row").prepend(`
2370				<div class="preview-form-again" style="padding: 96px; width: 100%; display: none">
2371					<div class="d-flex flex-column justify-content-center align-items-center">
2372						<p>Thank you for completing the form</p>
2373						<button class="btn font-weight-bold bg-light py-3 px-5">Preview Form Again</button>
2374					</div>
2375				</div>`);
2376
2377			// Build form results rows safely
2378			const rows = Object.entries(dataObj)
2379				.map(([key, value]) => {
2380					const question = survey.getQuestionByValueName(key);
2381					const title = question ? question.title || question.name : key;
2382					return `
2383				<tr>
2384				  <td style="padding-left: 44px; font-weight: 600">${title}</td>
2385				  <td>${typeof value == "object" ? JSON.stringify(value) : value}</td>
2386				</tr>`;
2387				})
2388				.join("");
2389
2390			const tableHtml = `
2391			  <table class="table">
2392				<thead style="background: #f3f3f3; font-size: 12px">
2393				  <tr>
2394					<td class="px-4">Question title</td>
2395					<td>Display Value</td>
2396				  </tr>
2397				</thead>
2398				<tbody style="font-size: 13px">
2399				  ${rows}
2400				</tbody>
2401			  </table>`;
2402
2403			$(`.preview-form-again`).show();
2404			$formResults.show();
2405			$resTable.html(tableHtml);
2406			$resJson.html(`<pre>${JSON.stringify(dataObj, null, 2)}</pre>`).hide();
2407
2408			$formResultsHeaderButtons.removeClass("selected-btn");
2409			$formResultsHeaderButtons.filter("#table").addClass("selected-btn");
2410			$deviceToolbar.find("#show-invisible-elements").hide();
2411			$("html, body").animate({ scrollTop: 0 }, "fast");
2412		}
2413
2414		$formResultsHeaderButtons.click(function () {
2415			$formResultsHeaderButtons.removeClass("selected-btn");
2416			$(this).addClass("selected-btn");
2417			if (this.id === "table") {
2418				$resJson.hide();
2419				$resTable.show();
2420			} else {
2421				$resTable.hide();
2422				$resJson.show();
2423			}
2424		});
2425		if (data.IsResponseLimitFilled) {
2426			$("form .sd-body").hide(); // hide form body
2427			$("form .sd-container-modern").css({ display: "flex", "flex-direction": "column", height: "100vh" });
2428			$("form .sv-components-row").css({ flex: 1 });
2429			$("form .sv-components-row").prepend(`
2430				<div class="limit_response" style="padding: 96px; width: 100%">
2431					<div class="d-flex flex-column justify-content-center align-items-center">
2432						<h5 class="sd-title">${data.ResponseLimitMessage}</h5>
2433					</div>
2434				</div>`);
2435		}
2436	});
2437});

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.