PageSourceSearch

https://www.cdrnet.org/lib/tag/xm/inc/xm.2.0.js?ver=20260809-C

js cdrnet.org collected 2026-10-02 02:53:03 UTC 190,295 bytes, 4,855 lines download raw bytes

1var dateXM = {};
2var xmRequest = {};
3var isBrowserIE = (navigator.appName.indexOf("Microsoft") > -1);
4var dispTRTag = (isBrowserIE)?"block":"table-row";
5var xmBasePath = "/";
6var xmStyleFunctions = {};
7var xmLastFormDraft;
8var xmAllowSavedDrafts = true;
9var showLeavingIndicator = false;
10
11if (!window.hasOwnProperty("xmUserUUID")) {
12	window.xmUserUUID = "000";
13}
14
15const pageURL = new URL(window.location.href);
16const pageQueryString = new URLSearchParams(pageURL.search);
17pageQueryString.delete('xm_row_index');
18const pageParamsSorted = Array.from(pageQueryString.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => `${key.toLowerCase()}_${value.toLowerCase()}`);
19const xmPageKey = `form_${pageURL.pathname.replace(/[^a-zA-Z0-9]/g, '_')}_${window.xmUserUUID.replace(/-/g,"_")}_${pageParamsSorted.join('_')}`;
20
21var xmFormSavedDraft = {};
22var ignoredFieldsForDraft = ["btnxm","actionxm","modexm","xm_user_token","xm_modify_page_path","xm_form_request_token","ReturnAfterSaving"];
23
24const urlParams = new URLSearchParams(window.location.search);
25try {
26	if (self !== parent && parent.hasOwnProperty("IFRAMESplitMode") && !urlParams.has("fullscreen")) {
27		let url = new URL(location.href);
28		url.searchParams.set("fullscreen",true);
29		history.pushState({}, document.title, window.location.pathname+url.search);
30		location.reload();		
31	}		
32} catch (error) {
33	
34}
35
36
37var pageLoadedAfterDelay = false;//MR: 11/5/2023 - adding a flag to check for certain events we want to only run after the page has been loaded for a bit
38$(document).ready(function(e){
39
40	setTimeout(() => {
41		pageLoadedAfterDelay = true;
42
43		if (window.hasOwnProperty("isLoggedIn") && window.isLoggedIn) {
44			$("form[persist]:visible :input[name]").on("change blur",function() {
45				var $this = $(this);
46				var name = $this.attr("name");
47				var $form = $this.parent().closest("form");
48				var formName = $form.attr("name");
49				if (checkBeforeLeaving) {
50					var value = GEV(name,"string","^");
51					if (value !== "" && $.inArray(name,ignoredFieldsForDraft) == -1 && !name.startsWith("xm_") && !name.endsWith("xm") ) xmFormSavedDraft[name] = value;
52					if (!$.isEmptyObject(xmFormSavedDraft)) {
53						try {
54							localStorage[xmPageKey] = JSON.stringify({date:now(),form:xmFormSavedDraft});								
55						} catch (error) {
56						}
57					}
58				}
59			});
60		}
61		
62	}, 1000);
63	if (!navigator.onLine) {
64		window.dispatchEvent(new Event('offline'));
65	}
66
67	if (xmAllowSavedDrafts && localStorage.getItem(xmPageKey) && window.hasOwnProperty("isLoggedIn") && window.isLoggedIn) {
68		var $frm = $("form[data-draft]:visible");
69		xmLastFormDraft = JSON.parse(localStorage.getItem(xmPageKey));
70		var isDraftSameTypeOfForm = true;
71		if (
72			!xmLastFormDraft.hasOwnProperty("form")
73			||
74			(xmLastFormDraft.form.hasOwnProperty("xm_dbform_section_id") && GEV("xm_dbform_section_id") !== xmLastFormDraft.form.xm_dbform_section_id)
75			||
76			(xmLastFormDraft.form.hasOwnProperty("entry_id") && GEV("entry_id") !== xmLastFormDraft.form.entry_id)
77			||
78			(xmLastFormDraft.form.hasOwnProperty("id") && GEV("id") !== xmLastFormDraft.form.id)
79		 ) {
80			isDraftSameTypeOfForm = false;
81		}
82		if (isDraftSameTypeOfForm && isLoggedIn) {
83			var $alertPrompt = $("<div/>").attr("id","RestoreDraftForm").addClass("alert alert-info").html(`You have a saved draft of this form (${xmLastFormDraft.date}). `)
84			$("<span/>").addClass("btn btn-xs btn-primary").html("Restore Saved Draft").click(function(e){
85				//loop twice because we have to first check for dynrows or file inputs, then again for individual
86				xmFormSavedDraft = xmLastFormDraft.form;
87				for (var sf in xmLastFormDraft.form) {
88					//Is this an upload field
89					if (sf.endsWith("uploader_rows") && $(`input[name=${sf.replace("uploader_rows","")}`).attr("data-type") == "upload") {
90						var fileRows = xmLastFormDraft.form[sf];
91						var filePrefix = sf.replace("uploader_rows","");
92						for (var row of String(fileRows).split(",")) {
93							if (xmLastFormDraft.form[filePrefix+"uploader_"+row+"_tmpname"]) {
94								fetch("/modules/portal/download_tmpupload.cfm?actionxm=Download&temp_file="+xmLastFormDraft.form[filePrefix+"uploader_"+row+"_tmpname"], { mode: 'no-cors'})
95								.then(response => response.blob())
96								.then(function(buffer) {
97									addNewUploadBlob(filePrefix,buffer,xmLastFormDraft.form[filePrefix+"uploader_"+row+"_name"]);
98								})
99								.catch(err => console.error(err)); 
100							}
101						}
102
103					} else if (sf.endsWith("_rows") && $("#"+sf).attr("data-dynrow")) { //Is it a dynrow
104						SEV(sf,xmLastFormDraft.form[sf]);
105					}
106				}
107				for (var sf in xmLastFormDraft.form) {					
108					if (sf.endsWith("uploader_rows") && $(`input[name=${sf.replace("uploader_rows","")}`).attr("data-type") == "upload") {
109
110					} else if (sf.endsWith("_rows") && $("#"+sf).attr("data-dynrow")) { //Is it a dynrow
111
112					} else if (String(xmLastFormDraft.form[sf]) !== "") {
113						console.log("SETTING",sf,xmLastFormDraft.form[sf]);
114						SEV(sf,xmLastFormDraft.form[sf]);
115					}
116				}
117				$("#RestoreDraftForm").hide("slow");
118			}).appendTo($alertPrompt);
119			$("<span/>").css("margin-left","10px").addClass("btn btn-xs btn-default").html("Delete Saved Draft").click(function(e){
120				xmFormSavedDraft = {};
121				try {
122					localStorage[xmPageKey] = "{}";
123				} catch (error) {
124				}
125				$("#RestoreDraftForm").hide("slow");
126			}).appendTo($alertPrompt);
127			$alertPrompt.hide().prependTo($frm).show("slow");
128		}
129	}
130});
131
132//MR: 10/4/2023 - creating this global flag and setting it false here because a recent Chrome update is causing the browser required validation check to run and bypassing our own validation script.
133var enableBrowserRequiredFieldsCheck = false;
134
135var fieldNameReservedWords = ['ADD','FORM','URL','SESSION','CGI','LOCAL','REQUEST','CLIENT','CLIENT_ID','SITE_ID','COOKIE','VARIABLES','ADD','ALL','ALTER','AND','ANY','AS','ASC','AUTHORIZATION','BACKUP','BEGIN','BETWEEN','BREAK','BROWSE','BULK','BY','CASCADE','CASE','CHECK','CHECKPOINT','CLOSE','CLUSTERED','COALESCE','COLLATE','COLUMN','COMMIT','COMPUTE','CONSTRAINT','CONTAINS','CONTAINSTABLE','CONTINUE','CONVERT','CREATE','CROSS','CURRENT','CURRENT_DATE','CURRENT_TIME','CURRENT_TIMESTAMP','CURRENT_USER','CURSOR','DATABASE','DBCC','DEALLOCATE','DECLARE','DEFAULT','DELETE','DENY','DESC','DISK','DISTINCT','DISTRIBUTED','DOUBLE','DROP','DUMP','ELSE','END','ERRLVL','ESCAPE','EXCEPT','EXEC','EXECUTE','EXISTS','EXIT','EXTERNAL','FETCH','FILE','FILLFACTOR','FOR','FOREIGN','FREETEXT','FREETEXTTABLE','FROM','FULL','FUNCTION','GOTO','GRANT','GROUP','HAVING','HOLDLOCK','IDENTITY','IDENTITY_INSERT','IDENTITYCOL','IF','IN','INDEX','INNER','INSERT','INTERSECT','INTO','IS','JOIN','KEY','KILL','LEFT','LIKE','LINENO','LOAD','MERGE','NATIONAL','NOCHECK','NONCLUSTERED','NOT','NULL','NULLIF','OF','OFF','OFFSETS','ON','OPEN','OPENDATASOURCE','OPENQUERY','OPENROWSET','OPENXML','OPTION','OR','ORDER','OUTER','OVER','PERCENT','PIVOT','PLAN','PRECISION','PRIMARY','PRINT','PROC','PROCEDURE','PUBLIC','RAISERROR','READ','READTEXT','RECONFIGURE','REFERENCES','REPLICATION','RESTORE','RESTRICT','RETURN','REVERT','REVOKE','RIGHT','ROLLBACK','ROWCOUNT','ROWGUIDCOL','RULE','SAVE','SCHEMA','SECURITYAUDIT','SELECT','SEMANTICKEYPHRASETABLE','SEMANTICSIMILARITYDETAILSTABLE','SEMANTICSIMILARITYTABLE','SESSION_USER','SET','SETUSER','SHUTDOWN','SOME','STATISTICS','SYSTEM_USER','TABLE','TABLESAMPLE','TEXTSIZE','THEN','TO','TOP','TRAN','TRANSACTION','TRIGGER','TRUNCATE','TRY_CONVERT','TSEQUAL','UNION','UNIQUE','UNPIVOT','UPDATE','UPDATETEXT','USE','USER','VALUES','VARYING','VIEW','WAITFOR','WHEN','WHERE','WHILE','WITH','WRITETEXT'];
136
137var xmFormFieldAdditionalParams = {};
138
139function addEvent(obj, evType, fn, useCapture){
140	if (!obj) return;
141	$(obj).on(evType,fn);
142}
143
144function $xm(id) {
145	return document.getElementById(id);	
146}
147
148function GetFormObj(form,id) {
149	return document.forms[form][id];	
150}
151
152var Class = {
153  create: function() {
154	return function() { 
155	  this.initialize.apply(this, arguments);
156	}
157  }
158};
159
160Object.extend = function(destination, source) {
161  for (property in source) {
162	destination[property] = source[property];
163  }
164  return destination;
165};
166ajax = Class.create();
167ajax.prototype = {
168	initialize: function(url, options){
169		this.transport = this.getTransport();
170		this.postBody = options.postBody || '';
171		this.method = options.method || 'post';
172		this.onComplete = options.onComplete || null;
173		this.update = $xm(options.update) || null;
174		this.request(url);
175	},
176
177	request: function(url){
178		this.transport.open(this.method, url, true);
179		this.transport.onreadystatechange = this.onStateChange.bind(this);
180		if (this.method == 'post') {
181			this.transport.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
182			if (this.transport.overrideMimeType) this.transport.setRequestHeader('Connection', 'close');
183		}
184		this.transport.send(this.postBody);
185	},
186
187	onStateChange: function(){
188		if (this.transport.readyState == 4 && this.transport.status == 200) {
189			if (this.onComplete) 
190				setTimeout(function(){this.onComplete(this.transport.responseText);}.bind(this), 10);
191			if (this.update)
192				setTimeout(function(){this.update.innerHTML = this.transport.responseText;}.bind(this), 10);
193			this.transport.onreadystatechange = function(){};
194		}
195	},
196
197	getTransport: function() {
198		if (window.ActiveXObject) return new ActiveXObject('Microsoft.XMLHTTP');
199		else if (window.XMLHttpRequest) return new XMLHttpRequest();
200		else return false;
201	}
202};
203
204var remoteXMRequestCounter = 0;
205var webServices = new Array();
206var browser = navigator.appName;
207function coolTip(obj,thetext, posVar){
208	var t = $(obj).tooltip({title:thetext,placement:posVar}).tooltip('show');
209}
210
211function hidecoolTip(){
212	return;
213}
214function showTitleTip(obj,thetext, thewidth, thecolor,pos){
215	return;
216}
217function positiontip(objId){
218	return;
219}
220function hideddrivetip(){
221	return;
222}
223
224function xmMerge(template, data) {
225	var newStr = template;
226
227	// Replace {XM:...} tokens only if data[p1] is defined and not empty
228	newStr = newStr.replace(/{XM:([a-z_]+)}/gi, (match, p1) => {
229		return (p1 in data && data[p1]) ? data[p1] : match;
230	});
231
232	// Replace {FORM:...} tokens only if data[p1] is defined and not empty
233	newStr = newStr.replace(/{FORM:([a-z_]+)}/gi, (match, p1) => {
234		return (p1 in data && data[p1]) ? data[p1] : match;
235	});
236
237    newStr = newStr.replace(/{XM:([a-z_]+)}/gi, (match, paramName) => {
238        return GEV(paramName);
239    });
240
241	return newStr;
242}
243
244const formatTemplateString = function(templateString, templateVars = {}){
245	var func = new Function(...Object.keys(templateVars),  "return `"+templateString +"`;")
246	return func(...Object.values(templateVars));
247}
248
249var overObj = new Object();
250function showObj(e,el,pos,clickToClose,bringToFrontVar,scrollVar) {
251	var bringToFrontFlag = (typeof(bringToFrontVar) != "undefined")?bringToFrontVar:true;
252	var scrollOpt = (typeof(scrollVar) != "undefined")?scrollVar:true;
253	var obj = $xm(el);
254	var clickToClose = (typeof(clickToClose) != "undefined")?clickToClose:true;
255	var docWidth = document.body.offsetWidth;
256	if (obj) {
257		if (typeof overObj[el] == "undefined") overObj[el] = false;
258		var pos = (typeof(pos) != "undefined")?pos:"bottom";
259		var eProp = getObjectProperties(e);
260		obj.onmouseover = function(){overObj[el]=true;};
261		obj.onmouseout = function(){overObj[el]=false;};
262		obj.style.display = "block";
263		obj.style.left  = Math.min(eProp["x"],(docWidth-obj.offsetWidth))+"px";
264		var mainContainer = $xm("MainContainer");
265		if (mainContainer) {
266			var mainProp = getObjectProperties(mainContainer);
267			obj.style.left  = Math.min(eProp["x"],(mainProp["x"]+mainProp["w"]-obj.offsetWidth))+"px";
268		}
269
270		obj.style.top = eProp["y"]+eProp["h"]+"px";
271
272		if (pos == "top") {
273			 obj.style.top = eProp["y"]-obj.offsetHeight-15+"px";
274		} else if (pos == "right") {
275			obj.style.left  = Math.min(eProp["x"]+eProp["w"],(docWidth-obj.offsetWidth-60))+"px";
276			var pgH = document.all ? document.documentElement.scrollHeight : document.body.scrollHeight;
277			var u = document.all ? document.documentElement.scrollTop : document.body.scrollTop;
278			var h = obj.offsetHeight;
279			var posTop = eProp["y"] - (h/2);
280			obj.style.top = Math.max(u,posTop) + 'px';//eProp["y"]-10+"px";
281		} else if (pos == "menuright") {
282			obj.style.left  = Math.min(eProp["x"]+(eProp["w"]/2),(docWidth-obj.offsetWidth-60))+"px";
283			var pgH = document.all ? document.documentElement.scrollHeight : document.body.scrollHeight;
284			var u = document.all ? document.documentElement.scrollTop : document.body.scrollTop;
285			var h = obj.offsetHeight;
286			obj.style.top = eProp["y"]+(eProp["h"]/2)+"px";
287		} else if (pos == "left") {
288			obj.style.left  = Math.min(eProp["x"]-eProp["w"],(docWidth-obj.offsetWidth-60))+"px";
289			var pgH = document.all ? document.documentElement.scrollHeight : document.body.scrollHeight;
290			var u = document.all ? document.documentElement.scrollTop : document.body.scrollTop;
291			var h = obj.offsetHeight;
292			var posTop = eProp["y"] - (h/2);
293			obj.style.top = Math.max(u,posTop) + 'px';//eProp["y"]-10+"px";
294		}
295		if (obj.offsetHeight > (document.body.offsetHeight-(eProp["y"]+eProp["h"])) && scrollOpt) {
296			if (document.body.offsetHeight-(eProp["y"]+eProp["h"]) > 0)
297				obj.style.height = Math.min(200,document.body.offsetHeight-(eProp["y"]+eProp["h"]))+"px";
298			obj.style.overflow = "auto";
299		}
300		if (clickToClose) {
301			document.onmouseup = function(e) {
302				for (var m in overObj) {
303					if (!overObj[m]) {
304						this.onmouseup = null;
305						hideObj(m); 
306					}
307				}
308			}
309		}
310	}
311}
312
313function hideObj(el) {
314	var obj = $xm(el);
315	if (obj) obj.style.display = "none";
316	//sendToBack(obj);
317}
318
319function getObjectProperties(elObj) {
320	var o = elObj;
321	var elPos = getObjectPosition(o);
322	var objArr = new Array();
323	objArr["x"] = elPos["x"];
324	objArr["y"] = elPos["y"];
325	objArr["h"] = o.offsetHeight;
326	objArr["w"] = o.offsetWidth;
327	return objArr;
328};
329
330function getObjectPosition(elObj) {
331	var objRealPos = Position.get(elObj);
332	var posArr = new Array();
333	posArr["x"] = objRealPos["left"];
334	posArr["y"] = objRealPos["top"];
335	return posArr;
336};
337
338
339var Position =(function(){function resolveObject(s){if(document.getElementById && document.getElementById(s)!=null){return document.getElementById(s);}else if(document.all && document.all[s]!=null){return document.all[s];}else if(document.anchors && document.anchors.length && document.anchors.length>0 && document.anchors[0].x){for(var i=0;i<document.anchors.length;i++){if(document.anchors[i].name==s){return document.anchors[i]}}}}var pos ={};
340pos.set = function(o,left,top){if(typeof(o)=="string"){o = resolveObject(o);}if(o==null || !o.style){return false;}o.style.position = "absolute";if(typeof(left)=="object"){var pos = left;left = pos.left;top = pos.top;}o.style.left = left + "px";o.style.top = top + "px";return true;};
341pos.get = function(o){var fixBrowserQuirks = true;if(typeof(o)=="string"){o = resolveObject(o);}if(o==null){return null;}var left = 0;var top = 0;var width = 0;var height = 0;var parentNode = null;var offsetParent = null;offsetParent = o.offsetParent;var originalObject = o;var el = o;while(el.parentNode!=null){el = el.parentNode;if(el.offsetParent==null){}else{var considerScroll = true;if(fixBrowserQuirks && window.opera){if(el==originalObject.parentNode || el.nodeName=="TR"){considerScroll = false;}}if(considerScroll){if(el.scrollTop && el.scrollTop>0){top -= el.scrollTop;}if(el.scrollLeft && el.scrollLeft>0){left -= el.scrollLeft;}}}if(el == offsetParent){left += o.offsetLeft;if(el.clientLeft && el.nodeName!="TABLE"){left += el.clientLeft;}top += o.offsetTop;if(el.clientTop && el.nodeName!="TABLE"){top += el.clientTop;}o = el;if(o.offsetParent==null){if(o.offsetLeft){left += o.offsetLeft;}if(o.offsetTop){top += o.offsetTop;}}offsetParent = o.offsetParent;}}if(originalObject.offsetWidth){width = originalObject.offsetWidth;}if(originalObject.offsetHeight){height = originalObject.offsetHeight;}return{'left':left, 'top':top, 'width':width, 'height':height};};
342pos.getCenter = function(o){var c = this.get(o);if(c==null){return null;}c.left = c.left +(c.width/2);c.top = c.top +(c.height/2);return c;};return pos;})();
343
344//General Scripts
345var ie = (navigator.userAgent.indexOf('MSIE') != -1);
346var moz = !ie && document.getElementById != null && document.layers == null;
347var browserIE = ie;
348var browserMozilla = moz;
349var browserSafari = (navigator.userAgent.indexOf('Safari') != -1);
350var browserOpera = (navigator.userAgent.indexOf('Opera') != -1);
351
352var windowLoaded = false;
353var filtering = false; //Used when filtering on a list page - flag set so we don't fire the "ENTER" key event
354var BreadCrumbs = null;//Track breadcrumb path
355
356/*===================================================================
357 Author: Matt Kruse
358 View documentation, examples, and source code at:
359	 http://www.JavascriptToolbox.com/
360 ===================================================================*/
361Date.$VERSION = 1.02;
362Date.LZ = function(x){return(x<0||x>9?"":"0")+x};Date.monthNames = new Array('January','February','March','April','May','June','July','August','September','October','November','December');Date.monthAbbreviations = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');Date.dayNames = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');Date.dayAbbreviations = new Array('Sun','Mon','Tue','Wed','Thu','Fri','Sat');Date.preferAmericanFormat = true;if(!Date.prototype.getFullYear){Date.prototype.getFullYear = function(){var yy=this.getYear();return(yy<1900?yy+1900:yy);}}
363Date.parseString = function(val, format){if(typeof(format)=="undefined" || format==null || format==""){var generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d','MMM-d','d-MMM');
363var monthFirst=new Array('M/d/y','M-d-y','M.d.y','M/d','M-d');var dateFirst =new Array('d/M/y','d-M-y','d.M.y','d/M','d-M');var checkList=new Array(generalFormats,Date.preferAmericanFormat?monthFirst:dateFirst,Date.preferAmericanFormat?dateFirst:monthFirst);for(var i=0;i<checkList.length;i++){var l=checkList[i];for(var j=0;j<l.length;j++){var d=Date.parseString(val,l[j]);if(d!=null){return d;}}}return null;}
364this.isInteger = function(val){for(var i=0;i < val.length;i++){if("1234567890".indexOf(val.charAt(i))==-1){return false;}}return true;};
365this.getInt = function(str,i,minlength,maxlength){for(var x=maxlength;x>=minlength;x--){var token=str.substring(i,i+x);if(token.length < minlength){return null;}if(this.isInteger(token)){return token;}}return null;};val=val+"";format=format+"";var i_val=0;var i_format=0;var c="";var token="";var token2="";var x,y;var year=new Date().getFullYear();var month=1;var date=1;var hh=0;var mm=0;var ss=0;var ampm="";while(i_format < format.length){c=format.charAt(i_format);token="";while((format.charAt(i_format)==c) &&(i_format < format.length)){token += format.charAt(i_format++);}if(token=="yyyy" || token=="yy" || token=="y"){if(token=="yyyy"){x=4;y=4;}if(token=="yy"){x=2;y=2;}if(token=="y"){x=2;y=4;}year=this.getInt(val,i_val,x,y);if(year==null){return null;}i_val += year.length;if(year.length==2){if(year > 70){year=1900+(year-0);}else{year=2000+(year-0);}}}else if(token=="MMM" || token=="NNN"){month=0;
365var names =(token=="MMM"?(Date.monthNames.concat(Date.monthAbbreviations)):Date.monthAbbreviations);for(var i=0;i<names.length;i++){var month_name=names[i];if(val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()){month=(i%12)+1;i_val += month_name.length;break;}}if((month < 1)||(month>12)){return null;}}else if(token=="EE"||token=="E"){var names =(token=="EE"?Date.dayNames:Date.dayAbbreviations);for(var i=0;i<names.length;i++){var day_name=names[i];if(val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()){i_val += day_name.length;break;}}}else if(token=="MM"||token=="M"){month=this.getInt(val,i_val,token.length,2);if(month==null||(month<1)||(month>12)){return null;}i_val+=month.length;}else if(token=="dd"||token=="d"){date=this.getInt(val,i_val,token.length,2);if(date==null||(date<1)||(date>31)){return null;}i_val+=date.length;}else if(token=="hh"||token=="h"){hh=this.getInt(val,i_val,token.length,2);if(hh==null||(hh<1)||(hh>12)){return null;}i_val+=hh.length;}else if(token=="HH"||token=="H"){hh=this.getInt(val,i_val,token.length,2);if(hh==null||(hh<0)||(hh>23)){return null;}i_val+=hh.length;}else if(token=="KK"||token=="K"){hh=this.getInt(val,i_val,token.length,2);if(hh==null||(hh<0)||(hh>11)){return null;}i_val+=hh.length;hh++;}else if(token=="kk"||token=="k"){hh=this.getInt(val,i_val,token.length,2);if(hh==null||(hh<1)||(hh>24)){return null;}i_val+=hh.length;hh--;}else if(token=="mm"||token=="m"){mm=this.getInt(val,i_val,token.length,2);if(mm==null||(mm<0)||(mm>59)){return null;}i_val+=mm.length;}else if(token=="ss"||token=="s"){ss=this.getInt(val,i_val,token.length,2);if(ss==null||(ss<0)||(ss>59)){return null;}i_val+=ss.length;}else if(token=="a"){if(val.substring(i_val,i_val+2).toLowerCase()=="am"){ampm="AM";}else if(val.substring(i_val,i_val+2).toLowerCase()=="pm"){ampm="PM";}else{return null;}i_val+=2;}else{if(val.substring(i_val,i_val+token.length)!=token){return null;}else{i_val+=token.length;}}}if(i_val != val.length){return null;}if(month==2){if( ((year%4==0)&&(year%100 != 0) ) ||(year%400==0) ){if(date > 29){return null;}}else{if(date > 28){return null;}}}if((month==4)||(month==6)||(month==9)||(month==11)){if(date > 30){return null;}}if(hh<12 && ampm=="PM"){hh=hh-0+12;}else if(hh>11 && ampm=="AM"){hh-=12;}return new Date(year,month-1,date,hh,mm,ss);}
366Date.isValid = function(val,format){return(Date.parseString(val,format) != null);}
367Date.prototype.isBefore = function(date2){if(date2==null){return false;}return(this.getTime()<date2.getTime());}
368Date.prototype.isAfter = function(date2){if(date2==null){return false;}return(this.getTime()>date2.getTime());}
369Date.prototype.equals = function(date2){if(date2==null){return false;}return(this.getTime()==date2.getTime());}
370Date.prototype.equalsIgnoreTime = function(date2){if(date2==null){return false;}var d1 = new Date(this.getTime()).clearTime();var d2 = new Date(date2.getTime()).clearTime();return(d1.getTime()==d2.getTime());}
371Date.prototype.format = function(format){format=format+"";var result="";var i_format=0;var c="";var token="";var y=this.getYear()+"";var M=this.getMonth()+1;var d=this.getDate();var E=this.getDay();var H=this.getHours();var m=this.getMinutes();var s=this.getSeconds();var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;var value=new Object();if(y.length < 4){y=""+(+y+1900);}value["y"]=""+y;value["yyyy"]=y;value["yy"]=y.substring(2,4);value["M"]=M;value["MM"]=Date.LZ(M);value["MMM"]=Date.monthNames[M-1];value["NNN"]=Date.monthAbbreviations[M-1];value["d"]=d;value["dd"]=Date.LZ(d);value["E"]=Date.dayAbbreviations[E];value["EE"]=Date.dayNames[E];value["H"]=H;value["HH"]=Date.LZ(H);if(H==0){value["h"]=12;}else if(H>12){value["h"]=H-12;}else{value["h"]=H;}value["hh"]=Date.LZ(value["h"]);value["K"]=value["h"]-1;value["k"]=value["H"]+1;value["KK"]=Date.LZ(value["K"]);value["kk"]=Date.LZ(value["k"]);if(H > 11){value["a"]="PM";}else{value["a"]="AM";}value["m"]=m;value["mm"]=Date.LZ(m);value["s"]=s;value["ss"]=Date.LZ(s);while(i_format < format.length){c=format.charAt(i_format);token="";while((format.charAt(i_format)==c) &&(i_format < format.length)){token += format.charAt(i_format++);}if(typeof(value[token])!="undefined"){result=result + value[token];}else{result=result + token;}}return result;}
372Date.prototype.getDayName = function(){return Date.dayNames[this.getDay()];}
373Date.prototype.getDayAbbreviation = function(){return Date.dayAbbreviations[this.getDay()];}
374Date.prototype.getMonthName = function(){return Date.monthNames[this.getMonth()];}
375Date.prototype.getMonthAbbreviation = function(){return Date.monthAbbreviations[this.getMonth()];}
376Date.prototype.clearTime = function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);
377return this;}
378Date.prototype.add = function(interval, number){if(typeof(interval)=="undefined" || interval==null || typeof(number)=="undefined" || number==null){
379return this;}number = +number;if(interval=='y'){this.setFullYear(this.getFullYear()+number);}else if(interval=='M'){this.setMonth(this.getMonth()+number);}else if(interval=='d'){this.setDate(this.getDate()+number);}else if(interval=='w'){var step =(number>0)?1:-1;while(number!=0){this.add('d',step);while(this.getDay()==0 || this.getDay()==6){this.add('d',step);}number -= step;}}else if(interval=='h'){this.setHours(this.getHours() + number);}else if(interval=='m'){this.setMinutes(this.getMinutes() + number);}else if(interval=='s'){this.setSeconds(this.getSeconds() + number);}
380return this;}
381
382var CGI = {}; CGI.script_name = location.href; CGI.http_host = location.host; CGI.query_string = document.location.search; var URLOBJ = {}; var queryString = document.location.search; if (queryString.length > 1) { var nmValPairs = queryString.substring(1,queryString.length); for (var q=1;q <= ListLen(nmValPairs,"&");q++) { var nmVal = ListGetAt(nmValPairs,q,"&"); URLOBJ[ListFirst(nmVal,"=")] = decodeURIComponent(ListLast(nmVal,"="));}
383}
384function getURL(str) { return (typeof(URLOBJ[str]) != "undefined")?URLOBJ[str]:"";}
385function setCookie(name,value) {document.cookie = name+"="+value+"; path=/";}
386function getCookie(name) { if (document.cookie) { var cookies=document.cookie.split(";"); for (var i=0; i<cookies.length; i++) { var varName=(cookies[i].split("=")[0]); var varValue=(cookies[i].split("=")[1]); while (varName.charAt(0)==" ")
387varName=varName.substr(1,varName.length); if (varName.toLowerCase()==name.toLowerCase())
388return varValue;}
389}
390return "";}
391function TRIM(strValue) { var objRegExp = /^(\s*)$/; if(objRegExp.test(strValue)) { strValue = String(strValue).replace(objRegExp, ''); if( strValue.length == 0) return strValue;}
392objRegExp = /^(\s*)([\W\w]*)(\b\s*$)/; if(objRegExp.test(strValue)) { strValue = strValue.replace(objRegExp, '$2');}
393return strValue;}
394function XMLFormat(xmlStr) { var regEx = new RegExp("\r\n","ig"); xmlStr = TRIM(xmlStr); var xml = "<![CDATA["+xmlStr.replace(regEx, "<br>")+"]]>"; return xml;}
395function ListFind(lst,val,del) { if (lst == null) return 0;var lstArr = lst.split(del); var fnd = 0; for (var a=0;a<lstArr.length;a++) { if (lstArr[a] == val) { fnd = a+1; break;}
396}
397return fnd;}
398function ListFindNoCase(lst,val,del) { var lstArr = lst.split(del); var fnd = 0; for (var a=0;a<lstArr.length;a++) { if (lstArr[a].toLowerCase() == val.toLowerCase()) { fnd = a+1; break;}
399}
400return fnd;}
401function getLeadingZero(nbr) {var nr = nbr;var nrStr = ""+nbr;if (nr < 10 && nrStr.length == 1) nr = "0"+nr;return nr;}
402function ListToArray(lst,del) { return lst.split(del);}
403function ArrayToList(arr,del) { return arr.join(del);}
404function Val(nbr) { return (isNaN(nbr) || nbr == "")?parseInt(0):parseInt(nbr);}
405function ListAppend(lst,val,del) { var del=del?del:",";if (lst == "") return val; var lstArr = ListToArray(lst,del); lstArr[lstArr.length] = val; var newlst = ArrayToList(lstArr,del); return newlst;}
406function ListLen(lst,del) { var del=del?del:",";var lstArr = lst.split(del); return lstArr.length;}
407function ListDeleteAt(lst,pos,del) { var del=del?del:",";var lstArr = lst.split(del); remArr = lstArr.splice(pos-1,1); var newlst = ArrayToList(lstArr,del); return newlst;}
408function ListGetAt(lst,pos,del) { var del=del?del:",";var lstArr = lst.split(del); return lstArr[pos-1];}
409function ListLast(lst,del) { var del=del?del:",";return ListGetAt(lst,ListLen(lst,del),del);}
410function ListFirst(lst,del) {var del=del?del:","; return ListGetAt(lst,1,del);}
411function NumberFormat(num) { return FormatNumber(num,0);}
412function DecimalFormat(num) { return FormatNumber(num,2);}
413function FormatNumber(num,decimalNum,bolLeadingZero,bolParens,bolCommas) { if (isNaN(parseInt(num))) return "NaN"; var tmpNum = num; var iSign = num < 0 ? -1 : 1; tmpNum *= Math.pow(10,decimalNum); tmpNum = Math.round(Math.abs(tmpNum));tmpNum /= Math.pow(10,decimalNum); tmpNum *= iSign; var tmpNumStr = new String(tmpNum); if (!bolLeadingZero && num < 1 && num > -1 && num != 0)
414if (num > 0)
415tmpNumStr = tmpNumStr.substring(1,tmpNumStr.length); else
416tmpNumStr = "-" + tmpNumStr.substring(2,tmpNumStr.length); if (bolCommas && (num >= 1000 || num <= -1000)) { var iStart = tmpNumStr.indexOf("."); if (iStart < 0)
417iStart = tmpNumStr.length; iStart -= 3; while (iStart >= 1) { tmpNumStr = tmpNumStr.substring(0,iStart) + "," + tmpNumStr.substring(iStart,tmpNumStr.length)
418iStart -= 3;}
419}
420if (bolParens && num < 0) tmpNumStr = "(" + tmpNumStr.substring(1,tmpNumStr.length) + ")"; return tmpNumStr;}
421function isDate(dateString) {
422	// First check for the pattern
423	if(!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dateString))
424		return false;
425	// Parse the date parts to integers
426	var parts = dateString.split("/");
427	var day = parseInt(parts[1], 10);
428	var month = parseInt(parts[0], 10);
429	var year = parseInt(parts[2], 10);
430	// Check the ranges of month and year
431	if(year < 1000 || year > 3000 || month == 0 || month > 12)
432		return false;
433	var monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
434	// Adjust for leap years
435	if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
436		monthLength[1] = 29;
437	// Check the range of the day
438	return day > 0 && day <= monthLength[month - 1];
439};
440function dateAdd(p_Interval, p_Number, p_Date){ if(!isDate(p_Date)){return "invalid date: '" + p_Date + "'";}
441if(isNaN(p_Number)){return "invalid number: '" + p_Number + "'";}
442p_Number = new Number(p_Number); var dt = new Date(p_Date); switch(p_Interval.toLowerCase()){ case "yyyy": { dt.setFullYear(dt.getFullYear() + p_Number); break;}
443case "q": { dt.setMonth(dt.getMonth() + (p_Number*3)); break;}
444case "m": { dt.setMonth(dt.getMonth() + p_Number); break;}
445case "y":
446case "d":
447case "w": { dt.setDate(dt.getDate() + p_Number); break;}
448case "ww": { dt.setDate(dt.getDate() + (p_Number*7)); break;}
449case "h": { dt.setHours(dt.getHours() + p_Number); break;}
450case "n": { dt.setMinutes(dt.getMinutes() + p_Number); break;}
451case "s": { dt.setSeconds(dt.getSeconds() + p_Number); break;}
452case "ms": { dt.setMilliseconds(dt.getMilliseconds() + p_Number); break;}
453default: { return "invalid interval: '" + p_Interval + "'";}
454}
455return dt;}
456function dateDiff(p_Interval, p_Date1, p_Date2, p_firstdayofweek, p_firstweekofyear){ if(!isDate(p_Date1)){return "invalid date: '" + p_Date1 + "'";}
457if(!isDate(p_Date2)){return "invalid date: '" + p_Date2 + "'";}
458var dt1 = new Date(p_Date1); var dt2 = new Date(p_Date2); var iDiffMS = dt2.valueOf() - dt1.valueOf(); var dtDiff = new Date(iDiffMS); var nYears = dtDiff.getUTCFullYear()-1970; var nMonths = dtDiff.getUTCMonth() + (nYears!=0 ? nYears*12 : 0); var nQuarters = parseInt(nMonths/3); var nWeeks = parseInt(iDiffMS/1000/60/60/24/7); var nDays = parseInt(iDiffMS/1000/60/60/24); var nHours = parseInt(iDiffMS/1000/60/60); var nMinutes = parseInt(iDiffMS/1000/60); var nSeconds= parseInt(iDiffMS/1000); var nMilliseconds = iDiffMS; var iDiff = 0; switch(p_Interval.toLowerCase()){ case "yyyy": return nYears; case "q": return nQuarters; case "m": return nMonths; case "y":
459case "d": return nDays; case "w": return nDays;
459 case "ww":return nWeeks; case "h": return nHours; case "n": return nMinutes; case "s": return nSeconds; case "ms":return nMilliseconds; default: return "invalid interval: '" + p_Interval + "'";}
460}
461function datePart(p_Interval, p_Date, p_firstdayofweek, p_firstweekofyear){ if(!isDate(p_Date)){return "invalid date: '" + p_Date + "'";}
462var dtPart = new Date(p_Date); switch(p_Interval.toLowerCase()){ 
463case "yyyy": return dtPart.getFullYear(); 
464case "q": return parseInt(dtPart.getMonth()/3)+1; case "m": return dtPart.getMonth()+1; 
465case "y": return dateDiff("y", "1/1/" + dtPart.getFullYear(), dtPart); 
466case "d": return dtPart.getDate(); case "w": return dtPart.getDay(); case "ww":return dateDiff("ww", "1/1/" + dtPart.getFullYear(), dtPart); 
467case "h": return dtPart.getHours(); case "n": return dtPart.getMinutes(); case "s": return dtPart.getSeconds(); case "ms":return dtPart.getMilliseconds(); 
468default: return "invalid interval: '" + p_Interval + "'";}
469}
470function weekdayName(p_Date, p_abbreviate){ if(!isDate(p_Date)){return "invalid date: '" + p_Date + "'";}
471var dt = new Date(p_Date); var retVal = dt.toString().split(' ')[0]; var retVal = Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')[dt.getDay()]; if(p_abbreviate==true){retVal = retVal.substring(0, 3)}
472return retVal;}
473function monthName(p_Date, p_abbreviate){ if(!isDate(p_Date)){return "invalid date: '" + p_Date + "'";}
474var dt = new Date(p_Date); var retVal = Array('January','February','March','April','May','June','July','August','September','October','November','December')[dt.getMonth()]; if(p_abbreviate==true){retVal = retVal.substring(0, 3)}
475return retVal;}
476function IsDate(p_Expression){ return isDate(p_Expression);}
477function DateAdd(p_Interval, p_Number, p_Date){ return dateAdd(p_Interval, p_Number, p_Date);}
478function DateDiff(p_interval, p_date1, p_date2, p_firstdayofweek, p_firstweekofyear){ return dateDiff(p_interval, p_date1, p_date2, p_firstdayofweek, p_firstweekofyear);}
479function DatePart(p_Interval, p_Date, p_firstdayofweek, p_firstweekofyear){ return datePart(p_Interval, p_Date, p_firstdayofweek, p_firstweekofyear);}
480function WeekdayName(p_Date){ return weekdayName(p_Date);}
481function MonthName(p_Date){ return monthName(p_Date);}
482function DateTimeFormat(dt) {return getLeadingZero(DatePart("m",dt))+"/"+getLeadingZero(DatePart("d",dt))+"/"+DatePart("yyyy",dt)+" "+getLeadingZero(DatePart("h",dt))+":"+getLeadingZero(DatePart("n",dt));}
483
484function DateCalcAge(d) {
485	var ret = "";
486	var totalMonths = moment().diff(d, 'months');
487	var years = parseInt(totalMonths / 12);
488	if (years > 0) ret = years + " year";
489	if (years > 1) ret += "s";
490	var months = totalMonths % 12;
491	if(months !== 0){
492		if (years > 0) ret += ",";
493		ret += " " + months + " month";
494		if (months > 1) ret += "s";
495	}
496	return ret;
497}
498
499function TimeDiff(time1, time2){
500	var time1Arr = time1.split(":");
501	var time1InMinutes = parseInt(time1Arr[0])*60+parseInt(time1Arr[1]);
502	var time2Arr = time2.split(":");
503	var time2InMinutes = parseInt(time2Arr[0])*60+parseInt(time2Arr[1]);
504	var diff = time2InMinutes - time1InMinutes;
505	return Math.floor(100*diff)/100;
506}
507
508function getURLParam(p) {
509	return new URL(location.href).searchParams.get(p);
510}
511function setURLParam(p,v) {
512	let url = new URL(location.href);
513	if (v === null) {
514		url.searchParams.delete(p);
515	} else {
516		url.searchParams.set(p, v);
517	}
518	history.pushState({}, document.title, window.location.pathname+url.search);
519}
520/* Duplicating rows */
521
522var dynCounter = {};
523var xmDynRow = {};
524function createDynRow(ind,prefix,rowInit, data) {
525	if (arguments.length < 3) rowInit=0;
526	var tblObj = $xm(prefix+"_table");
527	var rowSrc = tblObj.tBodies[ind].rows[0];
528	cleanupChildrenBeforeClone(rowSrc);
529	var rowDest = rowSrc.cloneNode(true);
530	var $rowDest = $(rowDest);
531	$rowDest.addClass("XMFormRow XMDynRow");
532
533
534	var rowsObj = $("#"+prefix+"_rows");
535	var currentRows = rowsObj.val().split(",");
536	var maxRows = (rowsObj.data("max") && !isNaN(rowsObj.data("max"))) ? rowsObj.data("max") : -1;
537
538	if (typeof dynCounter[prefix] == "undefined") dynCounter[prefix] = rowInit;
539	//Update the clone to have its own identity.
540	tblObj.tBodies[ind].appendChild(rowDest);
541	var a = document.createElement("span");
542	a.innerHTML = "<span class='btn btn-mini btn-xs btnDynRowDel btn-danger'>
542<i class='fa fa-trash-alt'></i></span>";
543	a.onclick = function() {if (window.confirm(GLT('Are you sure you want to delete this item?'))) deleteDynRow(rowDest,ind,prefix);};
544	rowDest.cells[rowDest.cells.length-1].appendChild(a);
545	var cntr = ++dynCounter[prefix];
546	rowDest.id = prefix+"_Row"+cntr;
547	a.id = prefix+"_Del";
548	//get input boxes so we can rename them
549
550	renameChildObjects(rowDest,cntr, data);
551
552	//Add system fields if we need to;
553	//Hide Delete button if property exists
554	if (data && data.hasOwnProperty("xm_allow_delete") && (data.xm_allow_delete === false || data.xm_allow_delete === "0" || data.xm_allow_delete == "false")) {
555		var $a = $(a);
556		$("<input/>").attr({
557			"type":"hidden",
558			"id":"xm_allow_delete_"+cntr,
559			"name":"xm_allow_delete_"+cntr
560		}).val(data.xm_allow_delete).appendTo($a.parent());
561		$a.hide();
562	}
563	
564	if (typeof tippy != "undefined") {
565		tippy('#'+rowDest.id+' .xmTip[data-tippy-content]',{
566			allowHTML:true
567		});
568	}
569	
570	try {
571		var $form = $rowDest.closest('form');
572		if ($form.calx) $form.calx();		
573	} catch (e) {
574		;
575	}
576	/*
577	$rowDest.find("[data-formula]").each(function(i,v) {
578		var $this = $(this);
579		$form.calx('getCell', $this.data("cell")).setFormula($this.data("formula"));
580	});
581	*/
582	var tsrows = $xm(prefix+"_rows");
583	tsrows.value = ListAppend(tsrows.value,cntr,",");
584	//if (inBoxes[i].tagName == "script" || (inBoxes[i].getAttribute("data-clone") && inBoxes[i].getAttribute("data-clone") == "false")) inBoxes[i].parentNode.removeChild(inBoxes[i]);
585	var $fUplClone = $rowDest.find(".XMFileUploader");
586	if ($fUplClone.length > 0) {//Means we have a file uploader we are cloning
587		$fUplClone.each(function(i,v){
588			var $this = $(this);
589			var newId = $this.prop("id");
590			initUploader(newId.replace("XMUploader_",""),$this.data("max"));
591	
592		});
593	} 
594	//Find any time fields
595	$($rowDest.find(".XMTimeField")).each(function(index, element) {
596		var $tObj = $(this);
597		var $tp = $tObj.parent();
598		var v = $tObj.val();
599		var hrs = parseInt(v / 60);
600		var mins = (v - (hrs * 60));
601		var ampm = (v >= 720) ? "PM" : "AM";
602		if (hrs > 12) hrs = hrs - 12;
603
604		$tp.find(".tiHrs").val(hrs);
605		//console.log($tp.html());
606		$tp.find(".tiMins").val(getLeadingZero(mins));
607		$tp.find(".tiMarker").val(ampm);
608
609
610	});
611
612	$rowDest.find("script").remove();
613	$rowDest.toggle(true);
614
615	if (xmDynRow.hasOwnProperty(prefix) && xmDynRow[prefix].hasOwnProperty("fx") && typeof xmDynRow[prefix].fx === "function") {
616		xmDynRow[prefix].fx(cntr);
617	}
618	$("#"+prefix+"_table").trigger("row:add",[cntr,data]);
619	$(window).trigger("dynrow:add",[prefix,cntr,data]);
620	$(window).trigger("dynrow:change",[prefix,cntr,data]);
621
622	var currentRows = rowsObj.val().split(",");
623	if (maxRows > -1) {
624		$("#"+prefix+"_AddBtn").toggle(currentRows.length < maxRows);
625	}
626
627	if (pageLoadedAfterDelay) {
628		try {
629			setTimeout(function(){
630				$firstEmptyField = $rowDest.find(':input:visible').filter(function() {
631					return $(this).val().trim() === '';
632				}).first().focus();
633			},200);		
634		} catch (error) {
635		}
636	}
637
638	return cntr;
639}
640
641function cleanupChildrenBeforeClone(obj) {
642	var inBoxes = obj.querySelectorAll(':scope [name], :scope [id]');
643	for (var i=0;i<inBoxes.length;i++) {
644		var prvNm = (typeof inBoxes[i].getAttribute('name') != null && inBoxes[i].getAttribute('name') != null && inBoxes[i].getAttribute('name') != '')?inBoxes[i].getAttribute('name'):inBoxes[i].getAttribute('id');
645		//DatePickerFix
646		if (inBoxes[i].getAttribute("datetype") == "date") {
647			//$("#"+prvNm).datepicker("destroy");
648		}
649	}
650}
651
652function renameChildObjects(obj,suffix, data) {
653	var inBoxes = obj.querySelectorAll(':scope [name], :scope [id]');
654	var newId,newNm,prvNm,prvId,inp;
655	var $inpt;
656	var uploadFields = {};
657	var dateMultiFields = {};
658	var sigFields = {};
659
660	/*
661	$('[data-id!=""]').each(function(i,v){
662		var $this = $(this);
663		$this.data("id",$this.data("id")+"_"+suffix);
664	});
665	*/
666	for (var i=0;i<inBoxes.length;i++) {
667		inpt = inBoxes[i];
668		$inpt = $(inpt);
669
670		if ($inpt.parents('.noRename').length > 0) continue;
671		prvNm = (typeof inpt.getAttribute('name') != null && inpt.getAttribute('name') != null && inpt.getAttribute('name') != '')?inpt.getAttribute('name'):inpt.getAttribute('id');
672		prvId = inpt.getAttribute('id');
673
674		if (prvNm === null) {
675			continue;
676		}
677
678		newNm =  prvNm+"_"+suffix;
679		newId = prvId+"_"+suffix;
680
681		//if (inpt.hasAttribute("data-key")) inpt.setAttribute("data-key",inpt.getAttribute("data-key")+"_"+suffix);
682		if (prvId && (prvId.indexOf("__geocode") > -1 || prvId.indexOf("__address") > -1 || prvId.indexOf("__address_2") > -1 || prvId.indexOf("__city") > -1 || prvId.indexOf("__state") > -1 || prvId.indexOf("__zip") > -1 || prvId.indexOf("__county") > -1)) {
683			newNm =  ListFirst(prvNm,"__")+"_"+suffix + "__" + ListLast(prvNm,"__");
684			newId =  ListFirst(prvId,"__")+"_"+suffix + "__" + ListLast(prvId,"__");
685		}
686		
687		//MR: 3/4/2026 - if there is a URL variable with the new name and we're not passing in a data object, let's use that.
688		if (data && !data.hasOwnProperty(newNm) && URLOBJ.hasOwnProperty(newNm)) {
689			data[prvNm] = URLOBJ[newNm];
690		}
691
692		if ($inpt.data("id")) {
693			inpt.setAttribute("data-id",$inpt.data("id")+"_"+suffix);
694		}
695
696		if (inpt.tagName.toLowerCase() == "label") {
697			inpt.setAttribute("for",inpt.getAttribute("for")+"_"+suffix);
698		}
699
700		if (inpt.getAttribute("name")) inpt.setAttribute("name",newNm);
701		inpt.name = newNm;
702		if (inpt.getAttribute("id")) inpt.setAttribute("id",newId);
703		if (typeof inpt.getAttribute('key') != null && inpt.getAttribute('key') != null && inpt.getAttribute('key') != '') { inpt.setAttribute("key",inpt.getAttribute('key')+"_"+suffix); }
704		
705		if (inpt.type == "checkbox" || inpt.type == "radio") {
706			inpt.checked = false;
707
708			//console.log(data);
709			//console.log(prvNm);
710			if (data && data.hasOwnProperty(prvNm) && data[prvNm] != "" && !($.isArray(data[prvNm])) && isNaN(data[prvNm]) && data[prvNm].indexOf("^") > -1) data[prvNm] = data[prvNm].split("^");
711			else if (data && data.hasOwnProperty(prvNm) && data[prvNm] != "" && !($.isArray(data[prvNm])) && isNaN(data[prvNm]) && data[prvNm].indexOf(",") > -1) data[prvNm] = data[prvNm].split(",");
712			if (data && $.isArray(data[prvNm])) {
713				for (var da=0;da<data[prvNm].length;da++) {
714					$("input[name='"+newNm+"'][value='" + String(data[prvNm][da]) + "']").prop('checked', true);
715				}
716			} else if (data) {
717				$("input[name='"+newNm+"'][value='" + String(data[prvNm]) + "']").prop('checked', true);
718				// for (var p=0; p < inpt.length; p++) if (inpt[p].value == data[prvNm]) inpt[p].checked = true;
719			}
720
721			if ($inpt.parent().hasClass("xm-disable-edit") || $inpt.parent().hasClass("xmFieldDisabled")) {
722				$inpt.each(function() {
723					var $this = $(this);
724					$this.attr("tabindex",-1);
725					//if ($this.is(":checked")) $("<i class='fa fa-check-square'></i>").insertBefore($this);
726					//$this.hide();
727				});				
728			}
729
730		} else if ($inpt.data("type") == "upload") {
731			
732			try {
733
734				var $fDivWrap = $("#XMUploader_"+newNm).parent().closest(".XMFieldWrapper");
735
736				var fileObj;
737				var fileArr = [];
738				var existingFilesPrevName = prvNm+"_existing_files";
739				// JH/Claude 7/29/26: fixed operator precedence — the missing-key clause must also require
740				// the _existing_files key to exist. Without the parens this ran for the XMUploader wrapper
741				// div too (it has data-type="upload"), planting data["XMUploader_..."] = undefined, which the
742				// data-fallback SEV() below then wrote as text into the div — destroying the cloned hidden
743				// inputs, so dynrow matrix uploads silently never saved.
744				if ((!data.hasOwnProperty(prvNm) || (!$.isArray(data[prvNm]) && data[prvNm] == "")) && data.hasOwnProperty(existingFilesPrevName)) {
745					data[prvNm] = data[existingFilesPrevName];
746				}
747
748				if (data && $.isArray(data[prvNm])) {
749					for (var da=0;da<data[prvNm].length;da++) {
750						fileObj = data[prvNm][da];
751						fileArr.push(fileObj.file_id);
752						$fDivWrap.append($("<div/>").addClass("noRename").html(`<label id="lbl_delete_file_${newId}" class="lblDeleteFile" for="delete_file_${newId}"><input type="checkbox" id="delete_file_${newId}_${fileObj.file_id}" class="chkDeleteFile" name="delete_file_${newId}" value="${fileObj.file_id}"> Delete File |</label> <a id="file_link_${newId}_${fileObj.file_id}" href="/modules/portal/file_viewer.cfm?uuid=${fileObj.file_uuid}&actionxm=Download">${fileObj.title}</a>`));
753					}
754					$inpt.val(fileArr.join(","));
755					$("#"+prvNm+"_existing_files_"+suffix).val(fileArr.join(","));
756					$("#"+newNm).val(fileArr.join(","));
757					//console.log(newId+" ---- " + "#"+prvNm+"_"+"_existing_files_"+suffix);
758					if (xmUploads.hasOwnProperty(newId)) xmUploads[newId].previous_uploads = fileArr.length;
759	
760				} else if (data[prvNm]) {
761					uploadFields[newId] = data[prvNm];
762					$("#"+newNm).val(data[prvNm]);
763					$("#"+prvNm+"_existing_files_"+suffix).val(data[prvNm]);
764				}
765								
766			} catch(e) {
767				console.log("ERROR DYNROW UPLOAD",e);
768			}
769
770		} else if (inpt.type == "file") {
771			//$("#"+inpt.id).before("COOL<br/>");
772		} else if (inpt.type == "text" || inpt.type == "textarea" || inpt.type == "hidden" || inpt.type == "number" || inpt.type == "email" || inpt.type == "time") {
773			inpt.value = "";
774			if 	(data && data[prvNm]) inpt.value = data[prvNm];
775
776			if ($inpt.attr("vtype") === "signature") {
777				sigFields[newId] = {"prvNm":prvNm,"data":data ? data[prvNm] : ""};
778			} else if ($inpt.attr("vtype") === "datemulti") {
779				dateMultiFields[newId] = {"prvNm":prvNm,"data":data ? data[prvNm] : ""};
780			}
781
782			if (inpt.type == "textarea" && !$inpt.hasClass("noAutoSize")) {
783				$inpt.on('input', function(){
784					var $this = $(this);
785					$this.css('height', Math.max($this.get(0).scrollHeight,50) + 'px');
786				}).trigger("input");	
787			}
788		} else if (inpt.type && inpt.type.indexOf("select") == 0) {
789
790			if ($inpt.data("elementtype") == "select2") {
791				select2Config[newId] = $.extend(true, {}, select2Config[prvNm]);
792				$inpt.select2(select2Config[newId]);
793			}
794
795			if (data && data.hasOwnProperty(prvNm)) {
796				if (!$.isArray(data[prvNm])) {
797					data[prvNm] = String(data[prvNm]);
798					data[prvNm] = data[prvNm].indexOf("^") > -1 ? data[prvNm].split("^") : data[prvNm].split(",");
799				}				
800				$inpt.attr("data-default",data[prvNm]);
801				$.each(data[prvNm], function(i,e){
802					var escapedValue = $.escapeSelector(e);
803					$("#"+newNm).find(`option[value="${escapedValue}"]`).prop("selected", true);
804				});
805			}
806
807			if ($inpt.attr("allow_other") == "true" && $inpt.find("option[value='[OTHER]']").length <= 0) {
808				$inpt.append($('<option/>', {value:"[OTHER]", text:"Other"}))
809			}
810			if ($inpt.attr("allow_other") == "true") {
811				$inpt.change(function(){
812					var $this = $(this);
813					if ($this.val() === "[OTHER]") {
814						var oth = window.prompt($this.attr("vlabel")+": "+GLT("Enter Value"),"");
815						if (oth) {
816							$this.append($('<option/>', {value:oth, text:oth}).attr("selected","selected"));
817						}
818
819					}
820				});
821			}
822		
823		} else if ( inpt.tagName.toLowerCase() == 'img' && inpt.id.substring(0,4) != 'null_') {
824			if 	(data && data[prvNm]) inpt.src = data[prvNm];			
825		} else if (data && data.hasOwnProperty(prvNm)) {
826			$("#"+newNm).val(data[prvNm]);
827		}
828
829		//if (inpt.outerHTML)
830			//inpt.outerHTML=inpt.outerHTML.replace(/name=\S+/,"name="+newNm+"");
831		//DatePickerFix
832		//For Calx
833		if (inpt.hasAttribute("data-cell")) inpt.setAttribute("data-cell",$inpt.attr("data-cell").replace(/0/g,suffix));
834		if (inpt.hasAttribute("data-formula")) inpt.setAttribute("data-formula",$inpt.attr("data-formula").replace(/([A-Z]+)([0])+/g,"$1"+suffix));
835		
836		//if mask exists set it up
837		if (inpt.hasAttribute("mask") && inpt.getAttribute("mask") != "") $("#"+newId).mask(inpt.getAttribute("mask"));
838
839		if (inpt.getAttribute("data-inputtype") == "address") {
840
841			if (addressFieldConfig.hasOwnProperty(prvNm)) {
842				addressFieldConfig[newId] = {};
843				for (oa in addressFieldConfig[prvNm]) {
844					if ($.inArray(oa,["address_field","address_2_field","city_field","state_field","state_field","zip_field"]) > -1) {
845						addressFieldConfig[newId][oa] = (addressFieldConfig[prvNm][oa] !== "") ? addressFieldConfig[prvNm][oa]+"_"+suffix : "";
846					} else {
847						addressFieldConfig[newId][oa] = addressFieldConfig[prvNm][oa];
848					}
849				}
850			}
851
852			xmSetupAddressField(newId,addressFieldConfig[newId]);
853		} else if (inpt.getAttribute("data-inputtype") == "route") {
854
855			if (typeof routeFieldConfig !== "undefined" && routeFieldConfig.hasOwnProperty(prvNm)) {
856				var newCfg = {};
857				for (var rk in routeFieldConfig[prvNm]) {
858					if (rk === "start_field" || rk === "end_field") {
859						newCfg[rk] = (routeFieldConfig[prvNm][rk] !== "") ? routeFieldConfig[prvNm][rk] + "_" + suffix : "";
860					} else {
861						newCfg[rk] = routeFieldConfig[prvNm][rk];
862					}
863				}
864				// Update the container's data-route-for attribute to point to the new field ID
865				var $routeContainer = $(inpt).closest("tr").find("[data-route-for='"+prvNm+"']");
866				if (!$routeContainer.length) $routeContainer = $(inpt).siblings("[data-route-for='"+prvNm+"']");
867				if ($routeContainer.length) $routeContainer.attr("data-route-for", newId);
868
869				xmSetupRouteField(newId, newCfg);
870			}
871
872		} else if (inpt.getAttribute("datetype") == "date") {
873			$("#"+newId).datepicker(dateXM[prvNm]);
874			setupDatePickerKeyUp(newId);
875		} else if (inpt.getAttribute("data-elementtype") == "boolean" && booleanFieldConfig.hasOwnProperty(prvNm.replace(/xmcb_/,""))) {
876			initBooleanField(newId.replace(/xmcb_/,""),booleanFieldConfig[prvNm.replace(/xmcb_/,"")]);
877		} else if (inpt.getAttribute("datetype") == "datetime") {
878			$("#"+newId).datetimepicker(dateXM[prvNm]);
879		} else if ($inpt.data("elementtype") == "select2") {
880			var prvNmLbl = prvNm+"_label";
881			var prvNmData = prvNm+"_data";
882			if (data && data.hasOwnProperty(prvNm)) $inpt.attr("data-default",data[prvNm]);
883			if (data && data.hasOwnProperty(prvNmData) && $.isArray(data[prvNmData])) {
884				for (var d=0;d<data[prvNmData].length;d++) {
885					//console.log(arguments,data[prvNmData][d],data);
886					if ($inpt.find("option[value='"+data[prvNmData][d]+"']").length > 0) {
887						$inpt.find('option[value="' + data[prvNmData][d] + '"]').attr("selected", "selected");
888					}
889				}
890			} else if (data && data.hasOwnProperty(prvNmLbl) && $inpt.find("option[value='"+data[prvNm]+"']").length === 0) {
891				var $defOpt = $("<option selected></option>").val(data[prvNm]).text(data[prvNmLbl]);
892				$inpt.append($defOpt);
893			}
894			if (select2Config[newId].hasOwnProperty("ajax")) {
895				if (data && data.hasOwnProperty(prvNm+"_remote_path")) {
896					select2Config[newId].ajax.url = data[prvNm+"_remote_path"];
897				}
898				setupSelect2RemoteDefault(newId);
899			}
900			
901			//MR: 12/27/2023 - if this has a lookup config, implement change event.
902			if (window.hasOwnProperty("select2Lookup") && select2Lookup.hasOwnProperty(prvNm)) {
903				
904
905				$("#"+newId).change(function(e) {
906					var $this = $(this);
907					var $af, v;
908					var name = $this.attr("name");
909					var prvNm = name.substring(0, name.lastIndexOf('_'));
910					var sel2LUConfig = select2Lookup[prvNm];
911
912					if ($this.val() != "" && $this.val() != null) {
913						$.getJSON(`${sel2LUConfig.path}${$this.val()}`,function(data) {
914
915							if (sel2LUConfig.hasOwnProperty("mapping")) {
916								for (var m in sel2LUConfig.mapping) {
917									SEV(m+"_"+suffix,data[sel2LUConfig.mapping[m]]);
918								}
919							}
920
921							if (sel2LUConfig.hasOwnProperty("fire_change") && sel2LUConfig.fire_change) {
922								var $form = $this.closest('form');
923								if ($form.calx) $form.calx();
924							}
925						});									   
926					}
927				}).change();
928	
929			}
930
931
932		} else if (data && data.hasOwnProperty(prvNm) && data[prvNm] !== undefined) {
933			// JH/Claude 7/29/26: added undefined guard — SEV stringifies undefined to "undefined" and its
934			// non-input fallback text()s that into the element, wiping child nodes
935			SEV(newNm,data[prvNm]);
936		}
937
938		var $par = $inpt.parent();
939		if ($par.hasClass("xmFieldDisabled") || (data && data.hasOwnProperty("xm_disabled_"+prvNm) && data["xm_disabled_"+prvNm])) {
940			//console.log("DISABLING: "+new Date().getTime())
941
942			$("<input/>").attr({
943				"type":"hidden",
944				"name":"xm_disabled_"+prvNm,
945				"value":data["xm_disabled_"+prvNm]
946			}).appendTo($par);
947
948			$par.addClass("xm-disable-edit");
949			$inpt.attr("readonly",true);//.prop("tabindex","-1");
950		}
951
952
953	}
954
955	if (!$.isEmptyObject(uploadFields)) {
956		for (var f in uploadFields) {
957			//See if FileList exists and if so, add to it;
958			$.getJSON(`/lib/cfc/plupload.cfc?returnFormat=JSON&method=GetUploadFileTitle&uuid=${uploadFields[f]}&container=${f}`,function(filedata) {
959				for (var d of filedata) {
960					var fileUUID = d.uuid;
961					var $fl = $("#FileList"+d.container);
962
963					$fl.append(`
964					<li class="list-group-item fileItem" data-uuid="${fileUUID}">
965						<a id="file_link_${prvNm}_${d.file_id}" href="/modules/portal/file_viewer.cfm?uuid=${fileUUID}&actionxm=Download">${d.client_file}</a>
966						<span class="pull-right"><i style="cursor:pointer;
966" class="fa fa-trash-alt fileDeleter"></i></span>
967					</li>
968					`);
969
970				}
971			})
972		}
973	}
974
975	if (!$.isEmptyObject(sigFields)) {
976		for (var s in sigFields) {
977			var prvNm = sigFields[s].prvNm;
978			var sigPad = $("#signature_wrapper_"+prvNm+""+"_"+suffix).signaturePad({validateFields:false,drawOnly:true,drawBezierCurves:true, lineTop:150});
979			if (sigFields[s].data !== "") sigPad.regenerate(sigFields[s].data);		
980		}
981	}
982
983	if (!$.isEmptyObject(dateMultiFields)) {
984		for (var d in dateMultiFields) {
985			var prvNm = dateMultiFields[d].prvNm;
986			var dtConfig = dateXM[prvNm];
987			dtConfig["onSelect"] = function(date) {
988				var $this = $(this);
989				var dates = $this.multiDatesPicker('getDates');
990				$this.next("input").val(dates.join(",")).change();
991			};
992			if (dateMultiFields[d].data && dateMultiFields[d].data !== "") {
993				dtConfig.addDates = dateMultiFields[d].data.split(",");
994			}
995			$('#'+prvNm+'_widget_'+suffix).multiDatesPicker(dtConfig);
996
997		}
998	}
999
1000	
1001	if (data && !$.isEmptyObject(data)) {
1002		setTimeout(function(){
1003			$(obj).find(":input").change();
1004		},100);
1005	}
1006
1007}
1008
1009function deleteDynRow(obj,ind,prefix) {
1010	if (obj.getAttribute("fx") != null) {
1011		eval(obj.getAttribute("fx"));
1012	}
1013	var $rows = $("#"+prefix+"_rows");
1014	$rows.val(ListDeleteAt($rows.val(),ListFind($rows.val(),obj.id.replace(prefix+"_Row",""),","),","));
1015	$(obj).remove();
1016	$rows.trigger("change");
1017	$("#"+prefix+"_table").trigger("row:delete",[ind]);
1018
1019
1020	var currentRows = $rows.val().split(",");
1021	var maxRows = ($rows.data("max") && !isNaN($rows.data("max"))) ? $rows.data("max") : -1;
1022	if (maxRows > -1) {
1023		$("#"+prefix+"_AddBtn").toggle(currentRows.length < maxRows);
1024	}
1025
1026	$(window).trigger("dynrow:change",[prefix,ind]);
1027	$(window).trigger("dynrow:delete",[prefix,ind]);
1028
1029	try {
1030		var $form = $rows.closest('form');
1031		if ($form.calx) $form.calx();		
1032	} catch (e) {
1033		;
1034	}
1035}
1036
1037function deleteAllDynRows(prefix) {
1038	var $rows = $("#"+prefix+"_rows");
1039	var ra = $rows.val().length > 0 ? $rows.val().split(",") : [];
1040	for (var r=0;r<ra.length;r++) {
1041		deleteDynRow($("#"+prefix+"_Row"+ra[r])[0],r,prefix);
1042	}
1043	delete dynCounter[prefix];
1044}
1045
1046function setupDatePickerKeyUp(id) {
1047	var xmDateformat = "mm/dd/yyyy";
1048	var xmDateMatch = new RegExp(xmDateformat
1049		.replace(/(\w+)\W(\w+)\W(\w+)/, "^\\s*($1)\\W*($2)?\\W*($3)?([0-9]*).*")
1050		.replace(/m|d|y/g, "\\d"));
1051	var xmDateReplace = "$1/$2/$3$4"
1052		.replace(/\//g, xmDateformat.match(/\W/));
1053
1054	$('#'+id).keyup(function(e) {
1055		if(!e.ctrlKey && !e.metaKey && (e.keyCode == 32 || e.keyCode > 46)) {
1056			e.target.value = e.target.value
1057				.replace(/(^|\W)(?=\d\W)/g, "$10")   // padding
1058				.replace(xmDateMatch, xmDateReplace)             // fields
1059				.replace(/(\W)+/g, "$1");            // remove repeats
1060		}
1061	});
1062}
1063
1064function ConvertDateTime(dt,inFormat,outFormat) {
1065	return (dt && dt != '') ? moment.utc(dt,inFormat).local().format(outFormat) : '';
1066}
1067
1068function SetDateTime(fldNm,dt) {
1069	var y = dt.getFullYear();
1070	var m = dt.getMonth()+1;
1071	var d = dt.getDate();
1072	var hr = (dt.getHours() > 12)?(dt.getHours()-12):(dt.getHours() == 0 || dt.getHours() == 24)?12:dt.getHours();
1073	var mn = dt.getMinutes();
1074	var mr = (dt.getHours() >= 12)?"PM":"AM";
1075	$("#"+fldNm).datepicker("setDate",dt);
1076	//$xm(fldNm).value = y+'-'+m+'-'+d+' '+hr+':'+mn+' '+mr;
1077}
1078
1079function toggle(objId,flagVar) {
1080	var obj = $("#"+objId);
1081	var flag = (typeof flagVar != "undefined")?flagVar:(!obj.is(":visible"));
1082	obj.trigger("onToggle",[flag]);
1083	obj.toggle(flag);
1084}
1085
1086function toggleTableRow(objId,flag) {
1087	toggle(objId,flag);
1088}
1089
1090function FireEvent(obj,evt) {
1091	if(obj.fireEvent)
1092		obj.fireEvent("on"+evt);
1093	else if (document.createEvent) {
1094		var evtObj = document.createEvent("HTMLEvents");
1095		if (evtObj.initEvent) {
1096			evtObj.initEvent(evt, true, true);
1097		}
1098		if (obj.dispatchEvent) {
1099			obj.dispatchEvent(evtObj);
1100		} 
1101	}
1102}
1103
1104var docItems = new Array();
1105
1106function turnOffPageScroll() {
1107	document.body.style.overflow = "hidden";
1108};
1109
1110function Nifty(selector,options){
1111	return;
1112}
1113function isJSON(str) {
1114	try {
1115		JSON.parse(str);
1116	} catch (e) {
1117		return false;
1118	}
1119	return true;								
1120}
1121
1122function now() {
1123	return moment().format("M/D/YYYY h:mm A");
1124}
1125
1126function DateUpdateYearDisplay(id) {
1127	var $this = $("#"+id)
1128	var $yearsDisplay = $("#"+id+"_years_display");
1129	if ($yearsDisplay.length > 0) {
1130		var today = new Date();             
1131		var thisDate = new Date($this.val());             
1132		var thisYears = today.getFullYear() - thisDate.getFullYear();             
1133		var m = today.getMonth() - thisDate.getMonth();             
1134		if (m < 0 || (m === 0 && today.getDate() < thisDate.getDate())) {                 
1135			thisYears--;             
1136		}             
1137		if (!isNaN(thisYears)) {
1138			$yearsDisplay.html(thisYears+" year(s)");             
1139		}
1140	}
1141}
1142
1143function getRemoteContent(link, div_id){
1144	$.ajax({
1145		url: link,
1146		success: function(response){
1147			$('#'+div_id).html(response);
1148		}
1149	});
1150	return '<div id="'+ div_id +'"><i class="fa fa-loader fa-spin"></i> Loading...</div>';
1151}
1152
1153function toggleMaxScreen(){
1154	$("body").toggleClass('xmBodyNoHeaderMenu');
1155	window.dispatchEvent(new Event('resize'));
1156}
1157
1158function onScreenSizeChange() {
1159	if(parent.postMessage) {
1160		var $main = $("#main");
1161		var $body = $("body");
1162		var jsonObj = {"height":$main.height(),"width":$main.width(),"body_height":$body.height(),"body_width":$body.width(),"path":location.href};
1163		parent.postMessage(JSON.stringify(jsonObj), '*');
1164	}	
1165}
1166
1167
1168// Watch main content size changes
1169const ro = new ResizeObserver(() => onScreenSizeChange());
1170
1171var tooltip=null;
1172var pageLoaded = false;
1173var isPageFramed = false;
1174var pageRenderTimeHTML = "";
1175var serverExecutionTime = -1;
1176var clientExecutionTime = -1;
1177
1178function globalXMOnload() {
1179	if (window.self !== window.top) {
1180		document.documentElement.className += " framed";
1181		isPageFramed = true;
1182
1183		window.addEventListener('message', (evt) => {
1184		if (evt.data === 'xmpop:measure' && typeof onScreenSizeChange === 'function') {
1185			onScreenSizeChange();
1186		}
1187		});
1188		setTimeout(() => {
1189			try { parent.postMessage('xmpop:ready', '*'); } catch {}			
1190		}, 200);
1191
1192		const mainEl = document.getElementById('main') || document.body;
1193		if (mainEl) ro.observe(mainEl);
1194
1195		$(window).on("load resize",function(e){
1196			onScreenSizeChange();
1197		}).resize();
1198
1199		// Observe size changes with ResizeObserver
1200		if ('ResizeObserver' in window) {
1201			const resizeObserver = new ResizeObserver(() => {
1202				onScreenSizeChange();
1203			});
1204			// Observe the body or a specific container
1205			resizeObserver.observe(document.body);
1206		} else {
1207			// Fallback: maybe use MutationObserver or call postIframeHeight() after toggles
1208			const mutationObserver = new MutationObserver(() => {
1209				onScreenSizeChange();
1210			});
1211			mutationObserver.observe(document.body, {
1212				childList: true,
1213				subtree: true,
1214				attributes: true,
1215				characterData: true
1216			});
1217		}
1218
1219		if(parent.postMessage) {
1220			var jsonObj = {"event":"load"};
1221			parent.postMessage(JSON.stringify(jsonObj), '*');
1222		}
1223
1224
1225	}
1226	if ((xmTheme == "xm-3" || xmTheme == "xm-4")) {
1227		//$(".btn-mini").addClass("btn-xs");
1228	} else {
1229		var XMBubble = document.createElement("DIV");
1230		XMBubble.id = "XMBubble";
1231		XMBubble.className = "XMBubble";
1232		document.body.appendChild(XMBubble);		
1233	}
1234
1235	$(".screenMaxToggle").click(function(e){
1236		toggleMaxScreen();
1237	});
1238
1239	//MR: 6/13/2023
1240	$(":input.xmStickyField").each(function(i,v){
1241		var $this = $(this);
1242		var nm = $this.attr("name");
1243		$this.on("input change blur",function(e){
1244			var $this = $(this);
1245			var nm = $this.attr("name");
1246			var v = GEV(nm,"string","^");
1247			try {
1248				if (v && v !== "") localStorage.setItem(nm, v);
1249				else localStorage.removeItem(nm);
1250			} catch (error) {
1251			}
1252		});
1253		var cv = GEV(nm,"string","^");
1254		try {
1255			var cdv = localStorage.getItem(nm);
1256			if (cdv && (cv == null || cv === "")) {
1257				setElementValue(nm,cdv);
1258			}
1259		} catch (error) {
1260		}
1261
1262	});
1263
1264	$(".xmToggleDetail[data-cachekey]").on("toggle",function(e) {
1265		var $this = $(this);
1266		var isOpen = $this.attr("open") !== undefined ? true : false;
1267		
1268		var cacheType = $this.attr("data-cachektype");
1269		$.getJSON(`/lib/cfc/user.cfc?returnFormat=JSON&method=SetOption&area=${$this.data("cachearea")}&key=${$this.data("cachekey")}&value=${isOpen}`,function(data){
1270			;
1271		});
1272	});
1273	$(".xmToggleDetail").on("toggle",function(e) {
1274		var $this = $(this);
1275		var isOpen = $this.attr("open") !== undefined ? true : false;
1276		
1277		$this.children('summary').first().find('.faToggle').toggleClass("fa-plus",!isOpen).toggleClass("fa-minus",isOpen);
1278	});
1279
1280	if (window.hasOwnProperty("enableAI") && window.enableAI) {
1281
1282		$(document).on("mouseover",".TextAI",function(i,v){
1283			var $this = $(this);
1284			var $bot = $this.find(".xm-bot-text");
1285			var txt = $this.attr("data-default") || $this.data("val") || $this.html();
1286			var l = txt.length;
1287			
1288			if ($bot.length === 0 && l > 400) {
1289				$this.css("position","relative");
1290				var $ai = $(`<span/>`).addClass("xm-bot-text").html(`
1291					<a class="btn btn-primary btn-xs dropdown-toggle" data-toggle="dropdown" href="javascript:void(0);"><i class="fas fa-wand-sparkles"></i> <span class="caret"></span></a>
1292					<ul class="dropdown-menu dropdown-menu-right">
1293						<li class="xmTip" data-tippy-content="Custom Prompt"><a href="javascript:void(0)" onclick="xmEnhanceText('custom',this);"><i class="fa fa-terminal"></i> Custom Prompt</a></li>
1294						<li class="xmTip" data-tippy-content="Summarize the main points of interest"><a href="javascript:void(0)" onclick="xmEnhanceText('summarize',this);"><i class="fa fa-list"></i> Summarize</a></li>
1295						<li class="xmTip" data-tippy-content="Generate Key Themes"><a href="javascript:void(0)" onclick="xmEnhanceText('themes',this);"><i class="fa fa-key"></i> Key Themes</a></li>
1296						<li class="xmTip" data-tippy-content="Provide a sentiment analysis"><a href="javascript:void(0)" onclick="xmEnhanceText('sentiment',this);"><i class="fa fa-face-smile"></i> Sentiment Analysis</a></li>
1297						<li class="xmTip" data-tippy-content="Extract entities from text"><a href="javascript:void(0)" onclick="xmEnhanceText('entities',this);"><i class="fa fa-ufo-beam"></i> Extract Entities</a></li>
1298						<li class="xmTip" data-tippy-content="Generate keywords"><a href="javascript:void(0)" onclick="xmEnhanceText('keywords',this);"><i class="fa fa-keyboard"></i> Generate Keywords</a></li>
1299					</ul>
1300				`).css({
1301					"position":"absolute",
1302					"top":"0",
1303					"right":"0"
1304				}).appendTo($this);
1305				$ai.find(".xmTip").each(function(e,v){
1306					if (typeof tippy != "undefined") {
1307						tippy($(this)[0],{
1308							allowHTML:true
1309						});
1310					}
1311			
1312				})
1313			} else if (l > 500) {
1314				$bot.show();
1315			}
1316		}).on("click",function(e){
1317			$(".xm-bot-text").hide();
1318		});	
1319	}
1320
1321	initDom();
1322
1323	setupLazyLoading();
1324
1325	try {
1326		const entries = performance.getEntriesByType("navigation");
1327		entries.forEach((entry) => {
1328			var hasPageRenderTime = $("#PageRenderTime").length > 0;
1329			if (!hasPageRenderTime) return;
1330			serverExecutionTime = entry.hasOwnProperty("serverTiming") && entry.serverTiming.length > 0 ? entry?.serverTiming[0]?.duration * 1000 : parseFloat($("#ServerExecutionTime").html().replace("(Execution Time: ","").replace(" seconds)","")) * 1000;
1331			clientExecutionTime = entry.domContentLoadedEventEnd - entry.domContentLoadedEventStart;
1332			const domContentLoadedTime = ((clientExecutionTime % 60000) / 1000).toFixed(2);
1333			const responseLoadedTime = (((entry.responseEnd - entry.requestStart) % 60000) / 1000).toFixed(2);
1334			const serverLoadTime = ((serverExecutionTime % 60000) / 1000).toFixed(2);
1335			let loadClass = "btn-success";
1336			if (serverLoadTime > 5) loadClass = "btn-danger";
1337			else if (serverLoadTime > 2) loadClass = "btn-warning";
1338			pageRenderTimeHTML = `Server: ${serverLoadTime}s | Client: ${responseLoadedTime}s`;
1339			$("#PageRenderTime").html(`Server: ${serverLoadTime}s | Client: ${responseLoadedTime}s`).removeClass("btn-danger btn-warning btn-success").addClass(loadClass);
1340			$("#BrowserRenderTime").html(`Server: ${serverLoadTime}ms | Client: ${responseLoadedTime}s | DOM: ${domContentLoadedTime}s`);
1341		});
1342		
1343	
1344	} catch(e) {
1345
1346	}
1347
1348	$(window).on("onEmbedFormSave",function(e,data) {
1349    	eModal.close();
1350		xmPopupModal.close();
1351    });
1352    
1353    $(window).on("onEmbedFormCancel",function(e,data) {
1354    	eModal.close();
1355		xmPopupModal.close();
1356    });	
1357
1358	pageLoaded = true;
1359
1360}
1361
1362var inxmPopOverRemote;
1363function initDom() {
1364
1365	$('.xmPopOverRemote').click(function() {
1366		var $this = $(this);
1367		if (inxmPopOverRemote == $this.data('remote')) {
1368			return;
1369		}
1370		inxmPopOverRemote = $this.data('remote');
1371		$.get($this.data('remote'),function(d) {
1372			$this.popover({html:true,placement:"auto right",container: "body",trigger:"focus",content: d}).popover("show");
1373		});
1374		$(document).on('click', function (e) {
1375			var $target = $(e.target);
1376			var isInPopover = $('.popover').find($target).length > 0;
1377			if (isInPopover) {
1378				e.stopPropagation();
1379			} else {
1380				$('.popover:visible').each(function () {
1381					$(this).popover("hide");
1382				});
1383			}
1384		});
1385	});
1386
1387	if (typeof iframely !== "undefined") iframely.load();
1388
1389	initTipRemote();
1390
1391	if (typeof tippy != "undefined") {
1392		tippy('.xmTip[data-tippy-content]',{
1393			allowHTML:true
1394		});	
1395	}
1396
1397	try {
1398		$("select[data-convert-to-select2]").removeAttr("data-convert-to-select2").select2();		
1399	} catch (error) {		
1400	}
1401
1402	
1403	$('.xmModalFrame').click(function() {
1404		var $this = $(this);
1405		let remoteUrl = $this.data("remote");
1406		if (!remoteUrl.includes('fullscreen=')) {
1407			remoteUrl += (remoteUrl.includes('?') ? '&' : '?') + 'fullscreen=true';
1408		}
1409		eModal.iframe(remoteUrl,$this.data("title")); 
1410	});
1411
1412	$('.xmRemoteTip').click(function() {
1413		var $this = $(this);
1414		$this.popover({
1415			html:true,
1416			placement:"auto right",
1417			container: "body",
1418			trigger:"focus",
1419			content: function () {  
1420				var $this = $(this);
1421				return $('<iframe height="'+$this.data("height")+'" width="'+$this.data("width")+'" frameborder="0" border="0"></iframe>').attr('src', $(this).data("remote"))	
1422			},
1423		}).popover("show");
1424	});	
1425}
1426
1427let xmTipRemoteFetching = {};
1428let xmTipRemoteContent = {};
1429function initTipRemote() {
1430	if (typeof tippy == "undefined") return;
1431	tippy(document.querySelectorAll('.xmTipRemote'), {
1432		allowHTML: true,
1433		theme: 'light',
1434		arrow:true,
1435		appendTo:document.body,
1436		interactive: true,
1437		delay:[500,200],
1438		onCreate(instance) {
1439			// Setup our own custom state properties
1440			instance._isFetching = false;
1441			instance._content = null;
1442			instance._error = null;
1443		},
1444		onShow(instance) {
1445			let path = $(instance.reference).data("remote");
1446			if (xmTipRemoteFetching.hasOwnProperty(path) || instance._isFetching || instance._content || instance._error) {
1447				return;
1448			}
1449			instance.setContent(`<div class="xmLoading" style="min-width:100px;">&nbsp;&nbsp;&nbsp;&nbsp;</div>`);
1450			instance._isFetching = true;
1451			xmTipRemoteFetching[path] = true;
1452
1453			//console.log($(instance.reference));
1454			//Replace params
1455			path = path.replace("module_id=0",`module_id=${module_id}`);
1456
1457			if (xmTipRemoteContent.hasOwnProperty(path)) {
1458				instance._content = xmTipRemoteContent[path];
1459				instance._isFetching = false;
1460				instance.setContent(xmTipRemoteContent[path]);
1461				delete xmTipRemoteFetching[path];
1462				return;
1463			}
1464
1465			setTimeout(() => {
1466				fetch(path)
1467					.then((response) => response.text())
1468					.then((data) => {
1469						//console.log(data);
1470						instance._content = data;
1471						xmTipRemoteContent[path] = data;
1472						instance.setContent(data);
1473					})
1474					.catch((error) => {
1475						instance._error = error;
1476						instance.setContent(`Request failed. ${error}`);
1477					})
1478					.finally(() => {
1479						instance._isFetching = false;
1480						delete xmTipRemoteFetching[path];
1481					});
1482				
1483			}, 1000);
1484		}
1485	});	
1486}
1487
1488//SESSION TIMER
1489var xmSessionWarningVisible = false, xmCountdownInterval, xmSessionNotification;
1490
1491function xmClearCountdown() {
1492	if (xmCountdownInterval) {
1493		clearInterval(xmCountdownInterval);
1494		xmCountdownInterval = null;
1495	}
1496}
1497
1498function xmCloseSessionNotification() {
1499	if (xmSessionNotification) {
1500		try { xmSessionNotification.close(); } catch(e) {}
1501		xmSessionNotification = null;
1502	}
1503}
1504
1505function xmSessionNotificationsEnabled() {
1506	return 'Notification' in window && Notification.permission === 'granted' && localStorage.getItem('sessionNotification') === 'true';
1507}
1508
1509function xmShowSessionWarning(remainingSec) {
1510	xmSessionWarningVisible = true;
1511	var totalSec = remainingSec;
1512	var pct = 100;
1513	eModal.confirm({
1514		title:'<i class="fa-solid fa-clock" style="color:#d9534f"></i> Session Expiring',
1515		message:'<div style="text-align:center;margin-bottom:12px">'
1516			+ '<div style="font-size:28px;font-weight:700;letter-spacing:1px" id="xm-session-countdown">' + Math.floor(remainingSec/60) + ':' + ('0'+(remainingSec%60)).slice(-2) + '</div>'
1517			+ '<div style="font-size:13px;color:#777;margin-top:2px">until automatic logout</div>'
1518			+ '</div>'
1519			+ '<div style="background:#eee;border-radius:4px;height:8px;overflow:hidden">'
1520			+ '<div id="xm-session-bar" style="height:100%;width:100%;border-radius:4px;transition:width 1s linear, background-color 0.5s"></div>'
1521			+ '</div>'
1522			+ '<div style="text-align:center;margin-top:14px;font-size:13px;color:#555">Click <strong>Yes</strong> to continue working or <strong>No</strong> to logout.</div>',
1523		label:"Yes"
1524	}).then(function() {
1525		xmSessionWarningVisible = false;
1526		xmClearCountdown();
1527		xmCloseSessionNotification();
1528		try {
1529			localStorage.setItem("session-last-activity-time",moment().unix());
1530		} catch (error) {
1531		}
1532		$.post("/lib/com/session/keep-alive.cfm", { path: location.pathname + location.search })
1533		.fail(function() {
1534			navigator.sendBeacon("/lib/com/session/keep-alive.cfm?path="+encodeURIComponent(location.pathname+location.search));
1535		});
1536	}, function(){
1537		xmSessionWarningVisible = false;
1538		xmClearCountdown();
1539		xmCloseSessionNotification();
1540		checkBeforeLeaving = false;
1541		window.location.href = '/auth.cfm?actionxm=Logout&noredirect=true&sign_in=true&path='+encodeURIComponent(location.pathname+location.search)
1542	});
1543
1544	xmCountdownInterval = setInterval(function() {
1545		remainingSec--;
1546		if (remainingSec <= 0) {
1547			xmClearCountdown();
1548			return;
1549		}
1550		pct = Math.max(0, (remainingSec / totalSec) * 100);
1551		var barColor = pct > 50 ? '#5cb85c' : pct > 25 ? '#f0ad4e' : '#d9534f';
1552		var el = document.getElementById("xm-session-countdown");
1553		if (el) el.textContent = Math.floor(remainingSec/60) + ':' + ('0'+(remainingSec%60)).slice(-2);
1554		var bar = document.getElementById("xm-session-bar");
1555		if (bar) {
1556			bar.style.width = pct + '%';
1557			bar.style.backgroundColor = barColor;
1558		}
1559	}, 1000);
1560}
1561
1562function xmCheckSessionTime() {
1563	$.getJSON("/lib/com/session/xm.cfc?returnFormat=JSON&method=GetLastSessionActivity",function(data) {
1564
1565		if (location.href.indexOf("/go/api/fx/") > -1 || location.href.indexOf("/go/fx/") > -1 || location.href.indexOf("/go/frx/") > -1) {
1566			//do not redirect
1567
1568		} else if (document.hidden) {
1569			// Tab is not visible — send OS notification if user opted in via control panel
1570			if (isLoggedIn && data.hasOwnProperty("logout") && data.logout) {
1571				if (xmSessionNotificationsEnabled()) {
1572					xmSessionNotification = new Notification('Session Expired', {
1573						body: 'Your session has timed out. Click to sign back in.',
1574						icon: '/favicon.ico',
1575						tag: 'xm-session-expired',
1576						requireInteraction: true
1577					});
1578					xmSessionNotification.onclick = function() { window.focus(); };
1579				}
1580			} else if (isLoggedIn && !xmSessionWarningVisible && data.show_warning) {
1581				if (xmSessionNotificationsEnabled()) {
1582					var secsLeft = Math.max(0, Math.floor((xmSessionTimeout - data.since) / 1000));
1583					xmSessionNotification = new Notification('Session Expiring', {
1584						body: 'Your session will expire in ' + Math.floor(secsLeft/60) + ':' + ('0'+(secsLeft%60)).slice(-2) + '. Click to stay logged in.',
1585						icon: '/favicon.ico',
1586						tag: 'xm-session-warning',
1587						requireInteraction: true
1588					});
1589					xmSessionNotification.onclick = function() { window.focus(); };
1590				}
1591			}
1592
1593		} else {
1594			// Tab is visible — normal in-page handling
1595			if (isLoggedIn && data.hasOwnProperty("logout") && data.logout) {
1596				checkBeforeLeaving = false;
1597				window.location.href = '/auth.cfm?actionxm=Logout&e=to&noredirect=true&sign_in=true&path='+encodeURIComponent(location.pathname+location.search)
1598			} else if (isLoggedIn && !xmSessionWarningVisible && data.show_warning) {
1599				var remainingSec = Math.max(0, Math.floor((xmSessionTimeout - data.since) / 1000));
1600				xmShowSessionWarning(remainingSec);
1601			}
1602		}
1603
1604	}).fail(function() {
1605		// Network error — silently retry on next poll interval. Do not redirect.
1606	});
1607}
1608
1609window.addEventListener('storage', function(e) {
1610	if (e.key === "session-last-activity-time") {
1611		xmCloseSessionNotification();
1612		if (xmSessionWarningVisible) {
1613			xmSessionWarningVisible = false;
1614			xmClearCountdown();
1615			eModal.close();
1616		}
1617	}
1618});
1619
1620// Throttled user activity detection — keeps session alive for active users
1621(function() {
1622	var lastActivityPing = 0;
1623	var activityThrottle = 300000; // 5 minutes
1624	var sessionEndpoints = ['/lib/com/session/', '/modules/portal/session_ping.cfm'];
1625
1626	function isSessionEndpoint(url) {
1627		if (!url) return false;
1628		for (var i = 0; i < sessionEndpoints.length; i++) {
1629			if (url.indexOf(sessionEndpoints[i]) > -1) return true;
1630		}
1631		return false;
1632	}
1633
1634	function onUserActivity() {
1635		if (!isLoggedIn) return;
1636		var now = Date.now();
1637		if (now - lastActivityPing > activityThrottle) {
1638			lastActivityPing = now;
1639			navigator.sendBeacon("/lib/com/session/keep-alive.cfm?path=" + encodeURIComponent(location.pathname + location.search));
1640			try {
1641				localStorage.setItem("session-last-activity-time", moment().unix());
1642			} catch(e) {}
1643		}
1644	}
1645
1646	// DOM events
1647	document.addEventListener("mousemove", onUserActivity, { passive: true });
1648	document.addEventListener("keydown", onUserActivity, { passive: true });
1649	document.addEventListener("scroll", onUserActivity, { passive: true });
1650	document.addEventListener("click", onUserActivity, { passive: true });
1651
1652	// jQuery AJAX — treat any non-session remote call as activity
1653	$(document).ajaxSend(function(event, jqXHR, settings) {
1654		if (!isSessionEndpoint(settings.url)) onUserActivity();
1655	});
1656
1657	// Native fetch — wrap to detect remote calls as activity
1658	var originalFetch = window.fetch;
1659	if (originalFetch) {
1660		window.fetch = function() {
1661			var url = arguments[0];
1662			if (typeof url === 'string' && !isSessionEndpoint(url)) {
1663				onUserActivity();
1664			} else if (url && typeof url === 'object' && url.url && !isSessionEndpoint(url.url)) {
1665				onUserActivity();
1666			}
1667			return originalFetch.apply(this, arguments);
1668		};
1669	}
1670})();
1671//SESSION TIMER
1672
1673$(window).scroll(function() {
1674	if ($(this).scrollTop() > 50 ) {
1675		$('.scrolltop:hidden').stop(true, true).fadeIn();
1676	} else {
1677		$('.scrolltop').stop(true, true).fadeOut();
1678	}
1679});
1680$(function(){
1681	$(".xmToggleDetail").on("toggle",function(e){
1682		var $this = $(this);
1683		var isOpen = $this.attr("open") !== undefined;
1684		//console.log(isOpen,$this.attr("open"));
1685		$.getJSON(`/modules/portal/go.cfm?a=SetOption&t=${$this.data("t")}&r=${$this.data("r")}&o=${$this.attr("id")}&v=${isOpen}&_dt=${new Date().getTime()}`,function(str){
1686			;
1687		});
1688	});
1689
1690	$(".scroll").click(function(){
1691		$("html,body").animate({scrollTop:0},"1000");
1692		return false
1693	});
1694});
1695
1696
1697function setOption(t,r,o,v) {
1698	var key = o.replace("show_","");
1699	var img = $xm("icon_"+key);
1700	img.src = (v)?xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/minus.png":""+xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/plus.png";
1701	toggle(key,v);
1702	$.getJSON("/modules/portal/go.cfm?a=SetOption&t="+t+"&r="+r+"&o="+o+"&v="+v+"&_dt="+new Date().getTime(),function(str){
1703		;
1704	});				
1705}
1706
1707var isGettingOption = false;
1708var getOptionListArr = [];
1709function getOption(inT,inR,inO) {
1710	if (inT) {
1711		getOptionListArr.push({t:inT,r:inR,o:inO});
1712	}
1713	if (!isGettingOption && getOptionListArr.length > 0) {
1714		var key = getOptionListArr[0].o.replace("show_","");
1715		var img = $xm("icon_"+key);
1716		isGettingOption = true;
1717		
1718		$.getJSON("/modules/portal/go.cfm?a=GetOption&t="+getOptionListArr[0].t+"&r="+getOptionListArr[0].r+"&o="+getOptionListArr[0].o+"&_dt="+new Date().getTime(),{},
1719			function(obj) {
1720				var txt = ""+obj;
1721				var flag = ((txt == "" || txt == "true") && txt != "false");
1722				toggle(key,flag);
1723				img.src = (flag)?""+xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/minus.png":""+xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/plus.png";
1724				isGettingOption = false;
1725				getOptionListArr.shift();
1726				getOption();
1727	
1728			}
1729		);
1730	}
1731}
1732
1733function showModal(id) {
1734	$("#"+id).modal({backdrop: 'static'}).appendTo($('body'));
1735}
1736
1737function hideModal(id) {
1738	$("#"+id).modal('hide');
1739}
1740//START FORM CODE
1741
1742var xmCustomFormValidation = {};
1743var formsGlobalsLoaded = false;
1744var formsSetupFormsCompleted = false;
1745var checkRequiredFields = true;
1746var new_fieldname = ""
1747var form_name = "";
1748var file_fld = 0;
1749var gFieldsChanged = 0;
1750var frmSubmit = 0;
1751var file_attached = 0;
1752var error_color = "#faf4f4"
1753var off_error_color = "#FFFFFF"
1754var on_error_color = "#FFF8DC"
1755var form_color = "#FFFFFF"
1756var itemsCheckedForValidation = {};
1757var frmStr = {formBeginMessage:"The following form field(s) were incomplete or incorrect.  Fields requiring input are highlighted in gray:",formEndMessage:"Please complete or correct the form and submit again.",reqMsg:"is required"};
1758frmStr["numeric"] = {message:"must be a numeric value",regex:/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/}; 
1759frmStr["integer"] = {message:"must be a whole number (greater than 0)",regex:/(^-?\d\d*$)/};
1760frmStr["phone"] = {message:"must be a valid phone number (xxx-xxx-xxxx)",regex:/^(\d\d\d-)*\d\d\d-\d\d\d\d$/};
1761frmStr["fein"] = {message:"must be a valid federal employer identification number (xx-xxxxxxx)",regex:/^(\d\d-)*\d\d\d\d\d\d\d$/};
1762frmStr["ssn"] = {message:"must be a valid social security number (xxx-xx-xxxx)",regex:/^\d\d\d-\d\d-\d\d\d\d$/};
1763frmStr["date"] = {message:"must be a valid date",regex:/^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/};
1764frmStr["datetime"] = {message:"must be a valid date",regex:/(?=\d)^(?:(?!(?:10\D(?:0?[5-9]|1[0-4])\D(?:1582))|(?:0?9\D(?:0?[3-9]|1[0-3])\D(?:1752)))((?:0?[13578]|1[02])|(?:0?[469]|11)(?!\/31)(?!-31)(?!\.31)|(?:0?2(?=.?(?:(?:29.(?!000[04]|(?:(?:1[^0-6]|[2468][^048]|[3579][^26])00))(?:(?
1764:(?:\d\d)(?:[02468][048]|[13579][26])(?!\x20BC))|(?:00(?:42|3[0369]|2[147]|1[258]|09)\x20BC))))))|(?:0?2(?=.(?:(?:\d\D)|(?:[01]\d)|(?:2[0-8])))))([-.\/])(0?[1-9]|[12]\d|3[01])\2(?!0000)((?=(?:00(?:4[0-5]|[0-3]?\d)\x20BC)|(?:\d{4}(?!\x20BC)))\d{4}(?:\x20BC)?)(?:$|(?=\x20\d)\x20))?((?:(?:0?[1-9]|1[012])(?::[0-5]\d){0,2}(?:\x20[aApP][mM]))|(?:[01]\d|2[0-3])(?::[0-5]\d){1,2})?$/};
1765frmStr["time"] = {message:"must be a valid time",regex:/^([1-9]|1[0-2]):[0-5]\d(:[0-5]\d(\.\d{1,3})?)?$/};
1766//frmStr["currency"] = {message:"must be a valid currency",regex:/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/}; does not allow commas
1767//frmStr["currency"] = {message:"must be a valid currency",regex:/^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]{2})?$/};  does not allow single decimal ie.: 75.5
1768frmStr["currency"] = {message:"must be a valid currency",regex:/^\$?\-?([1-9]{1}[0-9]{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\-?\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\(\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))\)$/}; 
1769frmStr["email"] = {message:"must be a valid email ([email protected])",regex:/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/i};
1770frmStr["date"] = {message:"must be a date",regex:/^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/};
1771
1772function frmValidCheck(frmObj) {
1773	var x = frmObj.elements.length;
1774	itemsCheckedForValidation = {};
1775	var $form = $(frmObj);
1776	checkBeforeLeaving = false;
1777
1778	if ($form.hasClass("is-validating")) {
1779		console.log("Form is currently being validated");
1780		return false;
1781	}
1782	if (window.XMAICheck && !window.XMAICheck.canSubmit(frmObj)) return false;
1783	console.log("Starting form validation");
1784	$form.addClass("is-validating");
1785
1786	if ($(":input[type=submit][clicked=true][value^='Delete']").length > 0 || $(":input[type=submit][clicked=true][value='Save Draft']").length > 0 || $(":input[type=submit][clicked=true][value='Save Draft Entry']").length > 0 || $("input[type=hidden][name=form_web_task][value^='Delete']").length > 0) frmObj.setAttribute("validate","false");
1787
1788	var formData = $form.serializeFormJSON();
1789
1790	try {
1791		if ($form.calx) $form.calx();		
1792	} catch (e) {
1793		;
1794	}
1795
1796	try {
1797		$(window).trigger("form:beforesubmit",[true]);
1798	} catch (e) {
1799		;
1800	}
1801
1802	try {
1803
1804		var uniqueFieldNames = $form.find(':input[name]:not(:button, :submit)').map(function() {
1805			return this.name; // 'this' refers to each input element.
1806		}).get() // Get the array of names
1807		.reduce(function(acc, current) {
1808			if (acc.indexOf(current) === -1) {
1809				acc.push(current); // Only add if the name isn't already in the accumulator
1810			}
1811			return acc;
1812		}, []);
1813
1814		var uniqueFieldNamesRequired = $form.find(':input[name]:not(:button, :submit)[vrequired="true"]:visible').filter(function() {
1815			return $(this).closest('.XMFormRow').is(':visible'); // Ensure the field is in a visible .XMFormRow
1816		}).map(function() {
1817			return this.name; // 'this' refers to each input element.
1818		}).get() // Get the array of names
1819		.reduce(function(acc, current) {
1820			if (acc.indexOf(current) === -1) {
1821				acc.push(current); // Only add if the name isn't already in the accumulator
1822			}
1823			return acc;
1824		}, []);
1825		
1826		if ($form.attr("method").toLowerCase() === "post") {
1827
1828			var $formFieldList = $form.find(":input[name='xm_form_field_list']");
1829			if ($formFieldList.length == 0) {
1830				$formFieldList = $("<input/>").attr({id:"xm_form_field_list",name:"xm_form_field_list",type:"hidden"}).appendTo($form)
1831			}
1832			if ($form.attr("method").toLowerCase() === "post") {
1833				$formFieldList.val(uniqueFieldNames.join(","));
1834			}
1835
1836			var $requiredFields = $form.find(":input[name='xm_form_field_required_list']");
1837			if ($requiredFields.length == 0) {
1838				$requiredFields = $("<input/>").attr({id:"xm_form_field_required_list",name:"xm_form_field_required_list",type:"hidden"}).appendTo($form)
1839			}
1840
1841			$requiredFields.val(uniqueFieldNamesRequired.join(","));
1842			
1843			let changedFields = getChangedFields(xmFormDefaultValues[$form.attr("name")], formData);
1844			var $formFieldChangedList = $form.find(":input[name='xm_form_field_changes']");
1845			if ($formFieldChangedList.length == 0) {
1846				$formFieldChangedList = $("<input/>").attr({id:"xm_form_field_changes",name:"xm_form_field_changes",type:"hidden"}).appendTo($form)
1847			}
1848			$formFieldChangedList.val(JSON.stringify(changedFields));
1849			
1850			//MR: 7/25/2024 - Adding comments
1851			var $commentFields = $form.find(":input[name='xm_form_field_comments']");
1852			if ($commentFields.length == 0) {
1853				$commentFields = $("<input/>").attr({id:"xm_form_field_comments",name:"xm_form_field_comments",type:"hidden"}).appendTo($form)
1854			}
1855			var commentsObj = $form.find('.xmCommentBoxEdit').toArray().reduce((obj, div) => {
1856				var field = $(div).data('field');
1857				if (!obj[field]) obj[field] = [];
1858				obj[field].push($(div).html());
1859				return obj;
1860			}, {});
1861			$commentFields.val(JSON.stringify(commentsObj));
1862
1863			// Replay-system context: link this form submission back to the
1864			// single-page rrweb recording captured by replay-record.js. We
1865			// only do this when form-capture mode is active on the page
1866			// (window.__xmReplayCapture present). The GUID is a shared global
1867			// lazily created by whoever touches it first (this code or the
1868			// recorder); the `||` guarantees both agree on one value, so the
1869			// hidden field matches the S3 key the recording was uploaded to.
1870			// Server side, xm_log_info stores xm_replay_capture_guid so the
1871			// record's log can render a "View Recording" link.
1872			// Wrapped in try/catch so a missing/odd replay context can never
1873			// break form prep.
1874			try {
1875				if (window.__xmReplayCapture) {
1876					if (!window.__xmReplayCaptureGuid) {
1877						window.__xmReplayCaptureGuid =
1878							(window.crypto && crypto.randomUUID)
1879								? crypto.randomUUID()
1880								: ('xg-' + Date.now().toString(16) + '-' + Math.random().toString(16).slice(2));
1881					}
1882					var $replayGuid = $form.find(":input[name='xm_replay_capture_guid']");
1883					if ($replayGuid.length == 0) {
1884						$replayGuid = $("<input/>").attr({name:"xm_replay_capture_guid",type:"hidden"}).appendTo($form);
1885					}
1886					$replayGuid.val(window.__xmReplayCaptureGuid);
1887				}
1888			} catch (replayHiddenErr) {
1889				if (window.console && console.log) console.log("xm_replay capture guid injection skipped:", replayHiddenErr);
1890			}
1891		}
1892		
1893		
1894	} catch(e) {
1895		var $errorField = $form.find(":input[name='xm_error']");
1896		if ($errorField.length == 0) {
1897			$errorField = $("<input/>").attr({id:"xm_error",name:"xm_error",type:"hidden"}).appendTo($form)
1898		}
1899		$errorField.val(e.message);
1900		console.log(e);
1901	}
1902
1903	if (frmObj.getAttribute("validate") == "false") {
1904		$form.removeClass("is-validating");
1905		try {
1906			localStorage[xmPageKey] = "{}";
1907		} catch (error) {
1908			//err
1909		}
1910		$requiredFields.val("");
1911		return true;
1912	}
1913	var invalidFields = [];
1914	var currentMessage,statusMessage,vType,req,fldObj,vReg,vMsg,currentRegex,cvCheck;
1915
1916	try {
1917		localStorage[xmPageKey] = "{}";
1918	} catch (error) {
1919	}
1920
1921	if (checkRequiredFields) {
1922        frmObj.removeAttribute("novalidate");
1923		//if we have any hidden fields with [required] attribute, let's remove them
1924		//if ($("#DynamicForm").find(":input[required]:hidden").length > 0)
1925    } else {
1926		$requiredFields.val("");
1927        frmObj.setAttribute("novalidate","novalidate");
1928    }	
1929
1930	$form.find(".XMFieldWrapper").data("checked",false);
1931	
1932	if (typeof CKEDITOR != 'undefined') {
1933		for (instance in CKEDITOR.instances) CKEDITOR.instances[instance].updateElement();
1934	}
1935	var $fldObj, $fldWrap, $fldRow;
1936	for (var i = 0; i < x; i++) {
1937		fldObj = frmObj.elements[i];
1938		$fldObj = $(fldObj);
1939		$fldWrap = $fldObj.parent().closest(".XMFieldWrapper");
1940		$fldRow = $fldObj.parent().closest(".XMFormRow");
1941
1942		if ($fldObj.data("novalidate") === "true" || $fldObj.data("novalidate") === true || fldObj.tagName == "BUTTON" || (fldObj.tagName == "INPUT" && (fldObj.getAttribute("type") == "button" || fldObj.getAttribute("type") == "submit")) || ($fldRow.length == 1 && $fldRow.is(":hidden"))) 
1943			continue;
1944		else if ($fldObj.parents(".modal:hidden").length)
1945			continue;
1946		else if (itemsCheckedForValidation.hasOwnProperty(fldObj.name) || $fldWrap.data("checked") == true) {
1947			//console.log("Skipping: ",fldObj);
1948			continue
1949		}
1950		statusMessage = frmValidateField(frmObj,fldObj);
1951
1952		if (statusMessage != "" && fldObj && fldObj.name && fldObj.name != "") {
1953			invalidFields[invalidFields.length] = {message:statusMessage,field:fldObj.name,type:fldObj.hasAttribute("vtype") ? fldObj.getAttribute("vtype") : "input"};
1954		}
1955	}
1956
1957	if (typeof xmFormCustomValidation == 'function') {
1958		var addInvalidFields = xmForm
1958CustomValidation(frmObj);
1959		Array.prototype.push.apply(invalidFields, addInvalidFields);
1960	}
1961
1962	if (invalidFields.length > 0) {		
1963		var fldFAIcon = "wpforms";
1964		var validationMessageJS = GLT(frmStr.formBeginMessage)+"\n\n"
1965		var validationMessageHTML = "<div style='margin-bottom:5px;' class=\"invalidFieldHeader alert alert-danger\">"+GLT("We didn't correctly capture your response to the following item(s).")+" "+GLT(frmStr.formEndMessage)+"</div><ul class='list-group' style='margin-bottom:5px;'>"
1966		
1967		for (var v=0;v<invalidFields.length;v++) { 
1968			//fldIcon = $xm("ValImg"+invalidFields[v].field);
1969			//if (fldIcon) fldIcon.src = ""+xmBasePath+"lib/img/icon/famfam/error.png";;
1970			fldFAIcon = "wpforms";
1971
1972			if (!invalidFields[v].hasOwnProperty("type")) fldFAIcon = "wpforms";
1973			else if (invalidFields[v].type.indexOf("date") > -1) fldFAIcon = "calendar";
1974			else if (invalidFields[v].type.indexOf("upload") > -1) fldFAIcon = "upload";
1975			else if (invalidFields[v].type.indexOf("radio") > -1) fldFAIcon = "check-circle";
1976			else if (invalidFields[v].type.indexOf("checkbox") > -1) fldFAIcon = "check-square";
1977			else if (invalidFields[v].type.indexOf("email") > -1) fldFAIcon = "envelope";
1978			else if (invalidFields[v].type.indexOf("phone") > -1) fldFAIcon = "phone";
1979			else if (invalidFields[v].type.indexOf("signature") > -1) fldFAIcon = "pencil";
1980			
1981			validationMessageJS += unescape(invalidFields[v].message) + "\n";
1982			validationMessageHTML += "<li class='list-group-item list-group-item-warning' style=\"cursor:pointer;\" onclick=\"frmNavigate('"+invalidFields[v].field+"');\"><i class='fa fa-"+fldFAIcon+"'></i> "+invalidFields[v].message + "</li>";
1983		}
1984		validationMessageJS += "\n"+GLT(frmStr.formEndMessage);
1985		validationMessageHTML += "</ul>";
1986
1987		var vObj = $xm("ValidationMessages");
1988		var formId = frmObj.getAttribute("id");
1989		if ($xm("ValidationMessages"+formId)) vObj = $xm("ValidationMessages"+formId);
1990		//console.log(vObj,formId);
1991		if (vObj) {
1992			if ($.toast) {
1993				$.toast({
1994					text: GLT("We found some items that need addressing"),
1995					position: "mid-center",
1996					bgColor: "#d03e3e",
1997					textColor:"#fff",
1998					loader:false,
1999					icon: 'warning', 
2000					showHideTransition : 'slide',
2001					hideAfter:2500,
2002					stack: false
2003				});
2004			} else {
2005				alert(GLT("We found some items that need addressing.  \n\Click OK to review them."));
2006			}
2007			vObj.innerHTML = validationMessageHTML;
2008			vObj.style.display = "block";
2009			if (!isPageFramed) {
2010				scrollToElement(vObj);
2011				//vObj.scrollIntoView();
2012			}
2013			//$("#"+invalidFields[0].field).focus();
2014		} else {
2015			alert(validationMessageJS);
2016		}
2017		$form.data('validated', false);
2018		setTimeout(function(){
2019			$("form :input.buttonLoading").removeClass("buttonLoading");
2020		},500);
2021		//$("#wrapper").css("overflow","").css("overflow","visible");
2022		$form.removeClass("is-validating");
2023		return false;
2024	} else if (xmCustomFormValidation.hasOwnProperty($form.attr("id")) && typeof xmCustomFormValidation[$form.attr("id")] == 'function') {
2025		var pvF = xmCustomFormValidation[$form.attr("id")]();
2026		$form.data('validated', pvF);
2027		if (!pvF) {
2028			setTimeout(function(){
2029				$("form :input.buttonLoading").removeClass("buttonLoading");
2030			},500);	
2031		}
2032		//$("#wrapper").css("overflow","").css("overflow","visible");
2033
2034		$form.removeClass("is-validating");
2035		if (pvF) localStorage[xmPageKey] = "{}";
2036		return pvF;
2037	} else if (typeof postValidateForm == 'function') {
2038		var pvF = postValidateForm(frmObj);
2039		$form.data('validated', pvF);
2040		if (!pvF) {
2041			setTimeout(function(){
2042				$("form :input.buttonLoading").removeClass("buttonLoading");
2043			},500);	
2044		}
2045		//$("#wrapper").css("overflow","").css("overflow","visible");
2046
2047		$form.removeClass("is-validating");
2048		try {
2049			if (pvF) localStorage[xmPageKey] = "{}";
2050		} catch (error) {
2051		}
2052		return pvF;
2053	//} else if ($form.data('submitted') === true) {
2054	//	console.log("Form already submitted");
2055	// return false;
2056	} else if (window.hasOwnProperty("xmFormPreventDoubleClick") && window.xmFormPreventDoubleClick && $form.data('submitted') === true && $form.attr("allow_multiple_submissions") === "fal
2056se") {
2057		if ((xmTheme == "xm-3" || xmTheme == "xm-4")) {
2058			$.toast({
2059				text: "This form has already been submitted",
2060				position: "mid-center",
2061				bgColor: "#d03e3e",
2062				textColor:"#fff",
2063				loader:false,
2064				icon: 'warning', 
2065				showHideTransition : 'slide',
2066				hideAfter:2500,
2067				stack: false
2068			});
2069		}
2070		console.log("Form already submitted and allow multiple submissions not active");
2071		$form.removeClass("is-validating");
2072		setTimeout(function(){
2073			$("form :input.buttonLoading").removeClass("buttonLoading");
2074		},500);	
2075		return false;
2076	} else {
2077		$form.data('validated', true);
2078
2079		//Offline capture
2080		var offlineForms = localStorage.getItem("offlineforms") ? JSON.parse(localStorage.getItem("offlineforms")) : {};
2081		var offlineLoggedInForms = localStorage.getItem("offlineLoggedInForms") ? JSON.parse(localStorage.getItem("offlineLoggedInForms")) : {};
2082		if (!navigator.onLine) {
2083			var $btnClicked = $(":input[type=submit][clicked=true]");
2084			formData[$btnClicked.attr("name")] = $btnClicked.val();
2085			if (window.hasOwnProperty("isLoggedIn") && window.isLoggedIn) {
2086				offlineLoggedInForms[new Date().getTime()] = {"path":$form.attr("action"),"data":formData,"params":$form.serialize()};
2087				localStorage.setItem("offlineLoggedInForms",JSON.stringify(offlineLoggedInForms));	
2088			} else {
2089				offlineForms[new Date().getTime()] = {"path":$form.attr("action"),"data":formData,"params":$form.serialize()};
2090				localStorage.setItem("offlineforms",JSON.stringify(offlineForms));	
2091			}
2092			setTimeout(function(){
2093				$("form :input.buttonLoading").removeClass("buttonLoading");
2094			},500);	
2095			xmShowToastMessage({
2096				id:"offlineForms",
2097				text: "Data saved locally since your network is offline. It will retry when you are back online.",
2098				position: "bottom-left",
2099				icon: "", 
2100				heading:"",
2101				bgcolor: "#EA4335",
2102				color:"#fff",
2103				timeout:5000,
2104				stack: 5
2105			});
2106			$form.removeClass("is-validating");
2107			$form.trigger("reset");
2108			return false;
2109		}
2110
2111		if (window.hasOwnProperty("xmFormPreventDoubleClick") && window.xmFormPreventDoubleClick && $form.attr("allow_multiple_submissions") === "false") {
2112			console.log("Form can submit now");
2113			// Mark it so that the next submit can be ignored
2114			$form.data('submitted', true);
2115			var $fldWrap = $(".xmFormButtonBar");
2116			var $overlay = $("<img/>").attr("src","/lib/img/blank.png").addClass("xm-disable-edit-overlay").attr("aria-label","Inactive Field");
2117			$fldWrap.addClass("xm-disable-edit").css({"opacity":1,"position":"sticky"});
2118			$fldWrap.prepend($overlay);
2119			setTimeout(function() {
2120				$("form :input.buttonLoading").removeClass("buttonLoading");
2121				$form.data('submitted', false);
2122				$fldWrap.removeClass("xm-disable-edit");
2123				$fldWrap.find(".xm-disable-edit-overlay").remove();
2124			},5000);
2125		}
2126		//$("#wrapper").css("overflow","").css("overflow","visible");
2127
2128		$form.removeClass("is-validating");
2129		localStorage[xmPageKey] = "{}";
2130		return true;
2131	}
2132}
2133
2134var xmCustomFormFieldValidation = {};
2135
2136function frmValidateField(frmObj,fldObj) {
2137	var currentMessage,statusMessage,vType,req,fldObj,vReg,vMsg,currentRegex;
2138	req = fldObj.getAttribute("vrequired");
2139	vType = fldObj.getAttribute("vtype");
2140	vReg = fldObj.getAttribute("vReg");
2141	var $obj = $(fldObj);
2142	var form_field_type = fldObj.getAttribute('type');
2143	var object_name = fldObj.getAttribute("name");
2144	var $fldWrap = $obj.parent().closest(".XMFieldWrapper");
2145	var $label = $("#form_label_"+fldObj.name);
2146	var $row = $obj.closest(".XMFormRow");
2147	changeColor(frmObj,fldObj,0);
2148	fldIcon = $xm("ValImg"+fldObj.name);
2149	if (fldIcon) fldIcon.src = ""+xmBasePath+"lib/img/clear.gif";
2150	//try {$obj.tooltip("destroy");} catch(e) {;}
2151	if ((xmTheme == "xm-3" || xmTheme == "xm-4")) $fldWrap.find(".xm-error-hint").remove();
2152
2153	if (vType != null && vType.toLowerCase() == 'numeric') { fldObj.value = String(fldObj.value).replace(/,/g,"");	}
2154	else if (vType != null && vType.toLowerCase() == 'currency') { fldObj.value = removeCurrency(String(fldObj.value));}
2155				
2156	currentRegex = (typeof(frmStr[vType]) != "undefined")?frmStr[vType].regex:vReg;
2157	currentMessage = "";
2158	if (fldObj.getAttribute('vMsg') != null) currentMessage = fldObj.getAttribute('vMsg');
2159	else if (fldObj.getAttribute('vmsg') != null) currentMessage = fldObj.getAttribute('vmsg');
2160	else if (typeof(frmStr[vType]) != "undefined") currentMessage = frmStr[vType].message;
2161
2162	currentMessage = GLT(currentMessage);
2163	statusMessage = "";
2164	if (!checkRequiredFields) {
2165		return "";
2166	}
2167	
2168	if (checkRequiredFields && (req == "1" || req == "true" || req == true)) {
2169		if (!itemsCheckedForValidation.hasOwnProperty(object_name)) {
2170			statusMessage = frmValidRequired(frmObj,fldObj,GLT(frmStr.reqMsg));
2171		}
2172	}
2173
2174	if ((vType=="date" || vType=="datetime") && fldObj.value != "" && statusMessage == "") {
2175		statusMessage == "";
2176		if (!moment(fldObj.value).isValid()) {
2177			changeColor(frmObj,fldObj,1);
2178			statusMessage = fldObj.getAttribute("vlabel")+": must be a valid date";
2179		} else if (fldObj.getAttribute("min") != null && moment(fldObj.value).isBefore(moment(fldObj.getAttribute("min")))) {
2180			changeColor(frmObj,fldObj,1);
2181			statusMessage = fldObj.getAttribute("vlabel")+": Date cannot be before "+fldObj.getAttribute("min");			
2182		} else if (fldObj.getAttribute("max") != null && moment(fldObj.value).isAfter(moment(fldObj.getAttribute("max")))){
2183			changeColor(frmObj,fldObj,1);
2184			statusMessage = fldObj.getAttribute("vlabel")+": Date cannot be after "+fldObj.getAttribute("max");			
2185		}
2186	} else if ((vType=="numeric" || vType=="integer" || vType=="currency") && fldObj.value != "" && statusMessage == "") {
2187		statusMessage == "";
2188		//Setup for numeric - built in max
2189		if (vType=="numeric" && (fldObj.getAttribute("max") == null || fldObj.getAttribute("max") == "")) {
2190			fldObj.setAttribute("max",2147483647);
2191		}
2192
2193		if (fldObj.getAttribute("min") != null && parseFloat(fldObj.value) < parseFloat(fldObj.getAttribute("min"))) {
2194			changeColor(frmObj,fldObj,1);
2195			statusMessage = fldObj.getAttribute("vlabel")+": Value cannot be less than "+fldObj.getAttribute("min");			
2196		} else if (fldObj.getAttribute("max") != null && parseFloat(fldObj.value) > parseFloat(fldObj.getAttribute("max"))) {
2197			changeColor(frmObj,fldObj,1);
2198			statusMessage = fldObj.getAttribute("vlabel")+": Value cannot be greater than "+fldObj.getAttribute("max");			
2199		}			
2200	} else if (vType=="list" && statusMessage == "") {
2201		statusMessage == "";
2202		var valueListLen = fldObj.value !== null && fldObj.value !== "" ? fldObj.value.split(",").length : 0;
2203		if (fldObj.getAttribute("min") != null && valueListLen < parseFloat(fldObj.getAttribute("min"))) {
2204			changeColor(frmObj,fldObj,1);
2205			statusMessage = fldObj.getAttribute("vlabel")+": You cannot have less than "+fldObj.getAttribute("min")+" entry(ies)";			
2206		} else if (fldObj.getAttribute("max") != null && valueListLen > parseFloat(fldObj.getAttribute("max"))) {
2207			changeColor(frmObj,fldObj,1);
2208			statusMessage = fldObj.getAttribute("vlabel")+": You cannot have more than "+fldObj.getAttribute("max")+" entry(ies)";			
2209		}			
2210	} else if (vType == "checkbox" && statusMessage == "") {
2211		statusMessage == "";
2212		var valueListLen = GEV(object_name).length;
2213		if (fldObj.getAttribute("data-min") != null && valueListLen < parseFloat(fldObj.getAttribute("data-min"))) {
2214			changeColor(frmObj,fldObj,1);
2215			statusMessage = fldObj.getAttribute("vlabel")+": Selected options cannot be less than "+fldObj.getAttribute("data-min");			
2216		} else if (fldObj.getAttribute("data-max") != null && valueListLen > parseFloat(fldObj.getAttribute("data-max"))) {
2217			changeColor(frmObj,fldObj,1);
2218			statusMessage = fldObj.getAttribute("vlabel")+": Selected options cannot be more than "+fldObj.getAttribute("data-max");			
2219		}			
2220	} else if ($obj.data("type") == "matrix") {
2221		var $fldRowsObj = $(`#${object_name}_rows`);
2222		if ($fldRowsObj != undefined && $fldRowsObj.length > 0) {
2223			var valueListLen = $fldRowsObj[0].value !== null && $fldRowsObj[0].value !== "" ? $fldRowsObj[0].value.split(",").length : 0;
2224			if (statusMessage == "" && $fldRowsObj[0].getAttribute("min") != null && valueListLen < parseFloat($fldRowsObj[0].getAttribute("min"))) {
2225				changeColor(frmObj,fldObj,1);
2226				statusMessage = fldObj.getAttribute("vlabel")+": Rows cannot be less than "+$fldRowsObj[0].getAttribute("min");			
2227			} else if (statusMessage == "" && $fldRowsObj[0].getAttribute("max") != null && valueListLen > parseFloat($fldRowsObj[0].getAttribute("max"))) {
2228				changeColor(frmObj,fldObj,1);
2229				statusMessage = fldObj.getAttribute("vlabel")+": Rows cannot be greater than "+$fldRowsObj[0].getAttribute("max");			
2230			}			
2231		}
2232
2233	}
2234
2235	if (currentRegex != null && statusMessage == "") {
2236		statusMessage = frmValidOther(frmObj,fldObj,currentRegex,currentMessage);
2237	}
2238	if (fldObj.tagName == "TEXTAREA" && fldObj.getAttribute("maxlength") != null && !isNaN(fldObj.getAttribute("maxlength")) && parseInt(fldObj.getAttribute("maxlength")) > 0 && fldObj.value.length > parseInt(fldObj.getAttribute("maxlength"))) {	
2239		changeColor(frmObj,fldObj,1);
2240		statusMessage = "You can only enter up to "+fldObj.getAttribute("maxlength")+" characters in the field "+fldObj.getAttribute("vlabel");
2241	}
2242	//Custom validations
2243	if (xmCustomFormFieldValidation.hasOwnProperty(object_name) && typeof xmCustomFormFieldValidation[object_name] == 'function') {
2244		var pvF = xmCustomFormFieldValidation[object_name]();
2245		$obj.data("xm-valid",pvF.success);
2246		if (!pvF.success) {
2247			$obj.data("xm-error",pvF.error);
2248		}
2249	}	
2250
2251	if (typeof $obj.data("xm-valid") != "undefined" && statusMessage == "") {
2252		var xmValidField = ($obj.data("xm-valid") == "true" || $obj.data("xm-valid") == true);
2253		changeColor(frmObj,fldObj,(xmValidField && $obj.data("xm-error") !== "") ? 1 : 0);
2254		statusMessage = (!xmValidField && $obj.data("xm-error") !== "") ? fldObj.getAttribute("vlabel")+": "+$obj.data("xm-error") : "";
2255	}
2256
2257	if (statusMessage == "" && ($obj.is(":visible") || $obj.data("type") == "matrix" || $obj.data("elementtype") == "select2")) {
2258		var thisObjhasRO = $obj.attr("readonly");
2259		if (thisObjhasRO) {
2260			$obj.removeAttr("readonly");
2261		}
2262		var cvCheck = (typeof fldObj.checkValidity == 'function' && fldObj.checkValidity());
2263		if (!cvCheck && ($(frmObj)[0].getAttribute("novalidate") == null || $(frmObj)[0].getAttribute("novalidate") == '')) {
2264			statusMessage = (fldObj.getAttribute('vMsg') != null) ? fldObj.getAttribute('vMsg') : fldObj.getAttribute("vlabel")+": "+fldObj.validationMessage;
2265		}
2266		if (thisObjhasRO) {
2267			$obj.attr("readonly","readonly");
2268		}
2269
2270
2271	}
2272
2273	if ($obj.attr("data-validatealways")) {
2274		//Always validate all the fields in this block;
2275	} else {
2276		$fldWrap.data("checked",true);
2277	}
2278	let errHTML = "<div class='xm-error-hint'><div style='display:inline-block' class='label label-danger'>"+statusMessage+"</div></div>";
2279	if ((xmTheme == "xm-3" || xmTheme == "xm-4") && statusMessage != "" && $obj.length > 0) {
2280		if ($fldWrap.length > 0 && $fldWrap[0].tagName === "TR") {
2281			$fldWrap.find(".xmFieldLabel").append(errHTML);
2282		} else {
2283			$fldWrap.append(errHTML);
2284		}
2285	}
2286
2287	var $frm = $(frmObj);
2288
2289	var errorCount = $frm.find(".xm-error-hint:visible").length;
2290	var $errorBanner = $("#XMErrorBanner");
2291	if ($errorBanner.length == 0) {
2292		$errorBanner = $("<div/>").css({
2293			"position":"relative",
2294			"margin-bottom": "5px"
2295		}).attr("id","XMErrorBanner").prependTo($(".xmFormButtonBar"));
2296	}
2297	$errorBanner.html(`
2298		<span onclick="showIntroJSErrors();">
2299			<i class="fa fa-triangle-exclamation"></i> 
2300			There ${errorCount == 1 ? "is" : "are"} 
2301			<span class="badge badge-important" style="background-color:white;color:black;">${errorCount}</span> 
2302			field${errorCount == 1 ? "" : "s"} 
2303			on this form that need addressing.
2304		</span>
2305		&nbsp;
2306		<a onclick="$(this).parent().hide();" style="color:inherit;"><i class="fa fa-rectangle-xmark"></i></a>
2307	`).toggle(errorCount > 0 && pageLoadedAfterDelay);
2308
2309	//try {if (showTip && statusMessage != "" && $obj.length > 0) {$obj.tooltip({trigger:"manual",title:unescape(statusMessage),placement:"right"}).tooltip("show");} catch(e) {;}
2310	return statusMessage; 
2311}
2312
2313function showIntroJSErrors() {
2314	var errorFields = [];
2315	$("form .xm-error-hint:visible").each(function(e){
2316		var $this = $(this);
2317		var $row = $this.parent().closest(".XMFormRow");
2318		errorFields.push({
2319			element: $row[0],
2320			intro:$this.text()
2321		})
2322	});
2323	introJs().setOptions({
2324		steps: errorFields		
2325	}).start();
2326}
2327
2328function frmNavigate(fldName) { 
2329	var rowObj = $xm("form_row_"+fldName);
2330	var fldObj = $xm(fldName);
2331	var labelObj = $xm("form_label_"+fldName);
2332	var $obj = $(":input[name='"+fldName+"']");
2333	var fieldWrapperObj = $obj.parent().closest(".XMFieldWrapper");
2334	//console.log(fldName,fieldWrapperObj);
2335	//console.log(fldName+" NONE:",fieldWrapperObj.offset().top);
2336	var hdrBuff = $("#header").height();
2337	
2338	if (rowObj){ 
2339		scrollToElement(rowObj);
2340		//rowObj.scrollIntoView();
2341	} else if (fieldWrapperObj.length) {
2342		window.scroll({ top: fieldWrapperObj.offset().top - hdrBuff, behavior: "smooth" });
2343	} else if (labelObj){
2344		window.scroll({ top: labelObj.offsetTop - hdrBuff, behavior: "smooth" });
2345		//labelObj.scrollIntoView();
2346	} else if ($obj){
2347		scrollToElement($obj[0]);
2348		//$obj[0].scrollIntoView();
2349	}
2350	//if (fldObj && fldObj.focus) fldObj.focus();
2351}
2352
2353function frmValidOther(frmObj,fldObj,expChk,msg) {
2354	var msg_addition = "";
2355	var objRegExp = eval(expChk);
2356	var form_field_value = fldObj.value;
2357	if (form_field_value != "" && (!objRegExp.test(form_field_value))) {
2358		msg_addition = unescape(fldObj.getAttribute('vlabel'))+' '+msg;
2359		changeColor(frmObj,fldObj,1);
2360	}
2361	return(msg_addition);
2362}
2363
2364function scrollToElement(el) {
2365	window.scrollTo({
2366		top: parseInt($(el).offset().top)-180,
2367		left: 0,
2368		behavior: 'smooth'
2369	});
2370}
2371
2372function setFieldValidation($fldObj,flag,message="") {
2373	$fldObj.data("xm-valid",flag).data("xm-error",message);
2374}
2375
2376function setRequiredField(fldObj,flag) {
2377	var $fldObj = (fldObj instanceof jQuery) ? fldObj : $(fldObj);
2378	var isRadioOrCheckbox = ($fldObj.is(":checkbox") || $fldObj.is(":radio") || $fldObj.is(":button"));
2379	if (fldObj instanceof jQuery) {
2380		try {
2381			var docImg = $xm("ReqImg"+fldObj.attr("name"));
2382			if (docImg) docImg.src =  (flag)?""+xmBasePath+"lib/img/icon/asterisk.gif":""+xmBasePath+"lib/img/clear.gif";
2383		} catch(e) {;}
2384		try {
2385			fldObj.attr("vrequired",flag);
2386			if (!isRadioOrCheckbox && enableBrowserRequiredFieldsCheck) {
2387				if (flag && fldObj.is("visible")) {
2388					fldObj.attr("required",flag);
2389				} else {
2390					fldObj.removeAttr("required");
2391				}
2392			}
2393		} catch(e) {console.log("ERROR SETTING REQUIRED",fldObj,e);}
2394
2395	} else if (fldObj && fldObj.length && fldObj[0].name && (fldObj[0].type == "input" || fldObj[0].type == "radio" || fldObj[0].type == "checkbox")) {
2396		try {
2397			var docImg = $xm("ReqImg"+fldObj[0].name);
2398			if (docImg) docImg.src =  (flag)?""+xmBasePath+"lib/img/icon/asterisk.gif":""+xmBasePath+"lib/img/clear.gif";
2399		} catch(e) {;}
2400		try {
2401			for (var f=0;f<fldObj.length;f++) {
2402				fldObj[f].setAttribute("vrequired",flag);
2403				if (enableBrowserRequiredFieldsCheck) fldObj[f].setAttribute("aria-required",flag);
2404				if (!isRadioOrCheckbox && enableBrowserRequiredFieldsCheck) {
2405					if (flag && $(fldObj[f]).is("visible")) {
2406						fldObj[f].setAttribute("required",flag);
2407					} else {
2408						fldObj[f].removeAttribute("required");
2409					}
2410				}	
2411			}
2412		} catch(e) {console.log("ERROR SETTING REQUIRED",e);}
2413	} else if (!$fldObj.is(":button")) {
2414		try {
2415			var docImg = $xm("ReqImg"+fldObj.name);
2416			if (docImg) docImg.src =  (flag)?""+xmBasePath+"lib/img/icon/asterisk.gif":""+xmBasePath+"lib/img/clear.gif";
2417		} catch(e) {;}
2418		try {
2419			fldObj.setAttribute("vrequired",flag);
2420			if (enableBrowserRequiredFieldsCheck) fldObj.setAttribute("aria-required",flag);
2421			if (!isRadioOrCheckbox && enableBrowserRequiredFieldsCheck) {
2422				if (flag && $fldObj.is("visible")) {
2423					$fldObj.attr("required",flag);
2424				} else {
2425					$fldObj.removeAttr("required");
2426				}
2427			}
2428
2429		} catch(e) {console.log("ERROR SETTING REQUIRED",fldObj,e);}
2430	}
2431	// The shared template renders a marker even when a field starts optional.
2432	// Resolve by ID, then name for radio/checkbox groups and custom field controls.
2433	$fldObj.each(function () {
2434		var marker = document.getElementById("ReqMarker" + this.id) || document.getElementById("ReqMarker" + this.name);
2435		if (marker) marker.hidden = !(this.getAttribute("vrequired") === "true" || this.getAttribute("vrequired") === "1");
2436	});
2437}
2438
2439function setDisabledField($fldObj,flag) {
2440	var $fldWrap = $fldObj.parent().closest(".XMFieldWrapper");
2441	/*
2442	if ($fldWrap.find(".input-group").length > 0) {
2443		$fldWrap = $fldWrap.find(".input-group");
2444	}
2445	*/
2446	var $overlay = $("<img/>").attr("src","/lib/img/blank.png").addClass("xm-disable-edit-overlay").attr("aria-label","Inactive Field");
2447	if (flag) {
2448		$fldWrap.addClass("xm-disable-edit");
2449		$fldWrap.prepend($overlay);
2450		//console.log($fldWrap.html());
2451		
2452		$fldObj.attr("readonly",true).prop("tabindex","-1");
2453		//$fldObj.find('option:not(:selected)').attr('disabled',true);
2454		//if (document.all) $fldObj.hide();
2455	} else {
2456		$fldWrap.removeClass("xm-disable-edit").removeClass("xm-field-error");
2457		$fldObj.removeAttr("readonly").parent().closest(".XMFieldWrapper").removeClass("xm-disable-edit");
2458		$fldObj.removeProp("tabindex").removeAttr("tabindex");
2459		$fldObj.find('option:not(:selected)').removeAttr('disabled');
2460
2461		$fldWrap.find("img.xm-disable-edit-overlay").remove();
2462
2463	}
2464}
2465
2466function setDisabledObj($obj,flag) {	
2467	var $overlay = $("<img/>").attr("src","/lib/img/blank.png").addClass("xm-disable-edit-overlay").attr("aria-label","Inactive Field");
2468	if (flag) {
2469		$obj.addClass("xm-disable-edit").prop("readonly",true).prop("tabindex",-1).prepend($overlay);
2470		$obj.find(":input").prop("tabindex",-1);
2471	} else {
2472		$obj.removeClass("xm-disable-edit").removeProp("readonly").removeProp("tabindex").removeAttr("tabindex").find("img.xm-disable-edit-overlay").remove();
2473		$obj.find(":input").removeProp("tabindex").removeAttr("tabindex");
2474	}
2475}
2476
2477function setDisabledForm($formObj,flag) {	
2478	var $overlay = $("<img/>").attr("src","/lib/img/blank.png").addClass("xm-disable-edit-overlay").attr("aria-label","Inactive Field");
2479	if (flag) {
2480		$formObj.addClass("xm-disable-edit");
2481		$formObj.prepend($overlay);
2482		$formObj.find(":input").attr("readonly",true).attr("tabindex",-1);
2483	} else {
2484		$formObj.removeClass("xm-disable-edit").removeClass("xm-field-error");
2485		$formObj.find(":input").removeAttr("readonly").removeProp("tabindex").removeAttr("tabindex");
2486		$formObj.find('option:not(:selected)').removeAttr('disabled');
2487		$formObj.find("img.xm-disable-edit-overlay").remove();
2488	}
2489}
2490
2491function frmValidRequired(frmObj,fldObj,msgInp) {
2492	changeColor(frmObj,fldObj,0);
2493	var $this = $(fldObj);
2494	var form_field_type = fldObj.getAttribute('type');
2495	var msg_addition = "";
2496	var object_name = fldObj.getAttribute("name");
2497	var field_value = $this.val();
2498	if (form_field_type == "radio" || form_field_type == "checkbox") {
2499		//console.log("Checking: "+object_name);
2500		//console.log(itemsCheckedForValidation);
2501		if (itemsCheckedForValidation.hasOwnProperty(object_name)) {
2502			//console.log("HOW: "+object_name,field_value);
2503			field_value = itemsCheckedForValidation[object_name].value;	
2504		} else {
2505			var $rcObj = $("input[name="+object_name+"]:checked");
2506			field_value = $rcObj.length > 0 ? $rcObj.val() : "";
2507			//console.log("Checking: "+object_name,field_value);
2508			itemsCheckedForValidation[object_name] = {message:"",value:field_value};
2509		}
2510	} else if ($this.data("type") == "upload") {
2511		itemsCheckedForValidation[object_name] = {message:"",value:field_value};
2512	}
2513	if (field_value == "" && form_field_type !== "radio" && form_field_type !== "checkbox" && $this.data().hasOwnProperty("val")) field_value = $this.data("val");
2514	//console.log("Field: "+object_name+" = "+field_value);
2515	field_value = $.trim(field_value);
2516	if (field_value == "" || field_value == null){
2517		//console.log("Required Field No Value: "+object_name+" = "+field_value);
2518		msg_addition = ($this.attr("vMsg") && $this.attr("vMsg") != "") ? $this.attr("vMsg") : fldObj.getAttribute('vlabel')+' '+msgInp;
2519		changeColor(frmObj,fldObj,1);
2520	}
2521	if (form_field_type == "radio" || form_field_type == "checkbox" || $this.data("type") == "upload") {
2522		itemsCheckedForValidation[object_name].message = msg_addition;
2523	}
2524	return msg_addition;
2525}
2526
2527
2528function frmValidRange(frmObj,fldObj,msg) {
2529	changeColor(frmObj,fldObj,0);
2530	var form_field_range = fldObj.getAttribute('range');
2531	var msg_addition = "";
2532	if (form_field_range && fldObj.value.length > 0) {
2533		if (form_field_range.indexOf(',') > -1) { var rng = form_field_range.split(',');} //we're dealing with a list
2534		else if (form_field_range.indexOf('-') > -1) { var rng = form_field_range.split('-');} //we're dealing with a range
2535		var val_1 = rng[0]; var val_2 = (rng[1] == 'null')?'':rng[1];
2536
2537		if (rng[0].indexOf('.value') > -1) val_1 = eval(rng[0]);
2538		if (rng[1].indexOf('.value') > -1) val_2 = eval(rng[1]);
2539		var form_field_value = fldObj.value;
2540		if (val_1 > form_field_value || (val_2.length > 2 && val_2 < form_field_value)) {
2541			msg_addition = msg;
2542			changeColor(frmObj,fldObj,1);
2543		}  
2544	}
2545	return msg_addition;
2546}
2547 
2548function changeColor(frmObj,fldObj,tog) {
2549	//fldObj.style.borderColor = (tog==1)?"#b94a48":"";;
2550	var $fldObj = $(fldObj);
2551	var $fldWrap = $fldObj.parent().closest(".XMFieldWrapper");
2552
2553	if ($(fldObj).attr("type") == "submit") return;
2554
2555	if ($fldWrap.length > 0) {
2556		$fldWrap.toggleClass("xm-field-error",(tog==1));
2557	} else {
2558		fldObj.style.backgroundColor = (tog==1)?error_color:off_error_color;
2559		fldObj.style.borderColor = (tog==1) ? "#da223a" : "#dadada";		
2560	}
2561	//fldObj.style.borderColor = (tog==1)?"red":"";
2562}
2563
2564function highlightRequired(frmObj) {
2565	for (x=0; x < frmObj.elements.length; x++) {
2566		fldObj = frmObj.elements[x];
2567		if (fldObj.getAttribute('vrequired') == 1 || fldObj.getAttribute('vrequired') == "true" || fldObj.getAttribute('vrequired') == true)
2568			changeColor(frmObj,fldObj,1);
2569	}
2570 }	
2571 
2572function IsEmail(email) {
2573	var regex = /^[a-z0-9]([a-z0-9_\-\.]*)@([a-z0-9_\-\.]*)(\.[a-z]{2,3}(\.[a-z]{2}){0,2})$/i;
2574	return ( email.length > 0 && regex.test(email));
2575}
2576
2577function removeCurrency( strValue ) {
2578  var objRegExp = /\(/;
2579  var strMinus = '';
2580  var strValue = isNaN(strValue) ? removeCommas(strValue) : strValue;
2581  objRegExp = /\)|\(|[,]/g;
2582  strValue = (strValue && isNaN(strValue))? strValue.replace(objRegExp,''):strValue;
2583  if(String(strValue).indexOf('$') >= 0){
2584	strValue = strValue.substring(1, strValue.length);
2585  }
2586  return !isNaN(strValue) ? strValue : "";
2587}
2588
2589function removeCommas( strValue ) {
2590  return String(strValue).replace(',','');
2591}
2592
2593function trimAll( strValue ) {
2594  var objRegExp = /^(\s*)$/;
2595	if(objRegExp.test(strValue)) {
2596	   strValue = strValue.replace(objRegExp, '');
2597	   if( strValue.length == 0)
2598		  return strValue;
2599	}
2600   //check for leading & trailing spaces
2601   objRegExp = /^(\s*)([\W\w]*)(\b\s*$)/;
2602   if(objRegExp.test(strValue)) {
2603	   //remove leading and trailing whitespace characters
2604	   strValue = strValue.replace(objRegExp, '$2');
2605	}
2606  return strValue;
2607}
2608//START FORM SAVING CODE
2609function SubmitForm(frm) {
2610	var frmChk = frmValidCheck(frm);
2611	//no need to check if form is dirty (since we ARE submitting the form)
2612	checkBeforeLeaving = false;
2613	if (frmChk) frm.submit();
2614}
2615
2616var xmFormObj = {};
2617var xmFieldOptions = {};
2618function setupForms() {
2619	var frms = document.forms;
2620	var frm,fldObj;
2621
2622	$(".xm-disable-edit :input").prop("tabindex","-1");
2623	
2624	$("form").on("change blur",":input:not(:submit):not(:button)", function(){
2625		var $this = $(this);
2626		var fldObj = $this[0];
2627		var frmObj = $this.closest("form");
2628		if ($this.attr("vtype") != null || $this.attr("vrequired") == "true" || typeof $this.data('xm-valid') !== 'undefined') {
2629			setTimeout(() => {
2630				frmValidateField(frmObj,fldObj);
2631			}, 300);
2632		} 
2633	});
2634	
2635	for (var f=0;f<frms.length;f++) {
2636		frm = frms[f];
2637		if (frm.getAttribute("name") && frm.getAttribute("name") != "") {
2638			addEvent(frm,"submit",function(e){checkBeforeLeaving=false;});
2639			try {
2640				xmFormDefaultValues[frm.getAttribute("name")] = $(frm).serializeFormJSON();				
2641			} catch (error) {
2642				
2643			}			
2644		}
2645	}
2646
2647	formsSetupFormsCompleted = true;
2648	$("select").each(function(index, element) {
2649		var sObj = $(this);
2650		sObj.data("ovalue",sObj.val());
2651	});
2652	$("form :input[type=submit]").click(function() {
2653		var $this = $(this);
2654		$(":input[type=submit]", $this.parents("form")).removeAttr("clicked");
2655		$this.attr("clicked", "true");		
2656	});
2657	$(".xmFormButtonBar :submit").click(function() {
2658		var $this = $(this);
2659		var $form = $this.parent().closest("form");
2660		if ($form.attr("allow_multiple_submissions") === "false") {
2661			$this.addClass("buttonLoading");
2662		} 
2663	});
2664
2665	displayOfflineFormPostsPrompt();
2666
2667	updateProgressChartForm();
2668
2669	$(":input").on("change blur",function(){
2670		updateProgressChartForm();
2671	})
2672	$('input[type="radio"]').on('change', function() {
2673		updateProgressChartForm();
2674	});
2675
2676
2677	//Add backup HTML attributes for required fields
2678	var hasSectionCompletedField = $(":input[name='section_completed']").length > 0;
2679	if (!hasSectionCompletedField && enableBrowserRequiredFieldsCheck) {
2680		$(":input[vrequired=true]:not(':checkbox'):not(':radio'):not(':button'):visible").attr("required",true);
2681		$(":input[vrequired=false]:not(':checkbox'):not(':radio'):not(':button')").removeAttr("required");	
2682	}
2683
2684}
2685
2686var xmRequiredFormInputs = {};
2687var xmAllFormInputs = {};
2688function updateProgressChartForm() {			
2689	// Loop through all visible .XMFormRow elements
2690	$('.XMFormRow:visible').each(function() {
2691		$(this).find('input[vrequired="true"]:visible, input[data-type="upload"][vrequired="true"]').each(function() {
2692			const name = $(this).attr('name');
2693			if (name) {
2694				xmAllFormInputs[name] = {};
2695				var val = GEV(name, "string");
2696				//console.log(name,val);
2697				xmRequiredFormInputs[name] = { filled: val !== null && val !== undefined && String(val).length > 0, value: val };
2698			}
2699		});
2700	});
2701
2702	const totalRequired = Object.keys(xmRequiredFormInputs).length;
2703	const allFields = Object.keys(xmAllFormInputs).length;
2704	const filledRequired = Object.values(xmRequiredFormInputs).filter(input => input.filled).length;
2705
2706	const pct = parseInt((filledRequired / totalRequired) * 100)
2707
2708	$(window).trigger("form:progress",[{"pct":pct,"all":allFields,"total":totalRequired,"filled":filledRequired}]);
2709
2710	if(parent.postMessage) {
2711		parent.postMessage(JSON.stringify({"event":"progress","all":allFields,"pct":pct,"total":totalRequired,"filled":filledRequired}), '*');
2712	}
2713
2714}
2715
2716function scrollToNextRequiredField() {
2717	$('.XMFormRow:visible').find('input[vrequired="true"]:visible, input[data-type="upload"][vrequired="true"]').each(function() {
2718		const $this = $(this);
2719		const name = $this.attr('name');
2720		if (!xmRequiredFormInputs[name].filled) {
2721			scrollToElement($this[0]);
2722			return false;
2723		}
2724	});
2725}
2726
2727function setupCalxForm($form) {
2728	$form.find("input[type=radio][data-oldcell]").each(function(i,v) {
2729		var $this = $(this);
2730		var name = $this.attr("name");
2731		var $con = $("#xmradio_"+name);
2732		if ($con.length === 0) {
2733			$("<input/>").attr({
2734				id:"xmradio_"+name,
2735				"data-cell":$this.data("oldcell"),
2736				type:"hidden"
2737			}).appendTo($this.parent());
2738			$("input[name='"+name+"']").change(function() {
2739				var $t = $(this);
2740				var name = $t.attr("name");
2741				var v = $("input[name='"+name+"']:checked").val();
2742				$("#xmradio_"+name).val(v).change();
2743				var $form = $t.closest('form');
2744				$form.calx();
2745			});
2746		}
2747	});
2748	$(":input[data-cell]").change();
2749}
2750
2751function getElementValue(name,returnType="any",del=",") {
2752	if (name === null || name === "") return null;
2753	var $id = $(`#${name}`);
2754	var $el = $(`:input[name="${name}"]`);
2755	var ret = ($el.attr("type") === "radio" || $el.attr("type") === "checkbox") ? $.map($(`input[name="${name}"]:checked`), function(c){return c.value; }) : $el.val();
2756	if ((!$id.is(":input")) && $id.attr("data-val") !== undefined) ret = $id.attr("data-val");
2757	else if ( $id.data("type") == "upload" && $id.data("val")) ret = $id.data("val");
2758	if (returnType.toLowerCase() == "string" && $.isArray(ret)) ret = ret.join(del);
2759
2760	if (window.hasOwnProperty("xmEditor") && xmEditor.hasOwnProperty(name)) {
2761		ret = xmEditor[name].data.get();
2762	} else if (window.hasOwnProperty("CKEDITOR") && CKEDITOR.instances.hasOwnProperty(name)) {
2763		ret = CKEDITOR.instances[name].getData();
2764	}
2765
2766	// Format date values (SQL Server datetime like "2026-04-01 00:00:00.0" → "04/01/2026")
2767	if (ret && typeof ret === "string") {
2768		var $dateEl = $el.length ? $el : $id;
2769		var dtype = $dateEl.attr("datetype") || $dateEl.attr("vtype") || $dateEl.attr("data-type") || "";
2770		if (dtype === "date" || dtype === "datetime" || /^\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}/.test(ret)) {
2771			var d = new Date(ret.replace(/\.0$/, ""));
2772			if (!isNaN(d.getTime())) {
2773				var isDateOnly = (dtype === "date") || /00:00:00/.test(ret);
2774				if (isDateOnly) {
2775					ret = ("0"+(d.getMonth()+1)).slice(-2)+"/"+("0"+d.getDate()).slice(-2)+"/"+d.getFullYear();
2776				} else {
2777					var h = d.getHours(), ampm = h >= 12 ? "PM" : "AM";
2778					h = h % 12 || 12;
2779					ret = ("0"+(d.getMonth()+1)).slice(-2)+"/"+("0"+d.getDate()).slice(-2)+"/"+d.getFullYear()+" "+h+":"+("0"+d.getMinutes()).slice(-2)+" "+ampm;
2780				}
2781			}
2782		}
2783	}
2784
2785	return ret;
2786}
2787const GEV = getElementValue;
2788
2789function GST(name) {
2790	return $(`#${name} option:selected`).text();
2791}
2792
2793function setElementValue(name,value,fireChange=true,highlightField=true) {
2794	var $el = $(`:input[name="${name}"]`);
2795	var $id = $(`#${name}`);
2796	var isInput = $id.is(":input");
2797
2798	if (!$.isArray(value)) value = String(value);
2799
2800	// Highlight the field wrapper if requested
2801	if (highlightField && $el.length > 0) {
2802		var $fieldWrapper = $el.closest('.XMFieldWrapper');
2803		if ($fieldWrapper.length > 0) {
2804			$fieldWrapper.addClass('xm-ai-field-highlight');
2805			setTimeout(function() {
2806				$fieldWrapper.removeClass('xm-ai-field-highlight');
2807			}, 1500);
2808		}
2809	}
2810
2811	var isRadioCheck = ($el.attr("type") === "radio" || $el.attr("type") === "checkbox");
2812	if (isRadioCheck) {
2813		if (!$.isArray(value) && String(value).indexOf("^") > -1) {
2814			value = value.split("^");
2815		}
2816		if ($.isArray(value)) {
2817			for (var v of value) {
2818				$(`input[name="${name}"][value="${v}"]`).prop("checked",true).trigger(fireChange ? "change" : "value:changed");
2819			}
2820		} else if (value == "") {
2821			$(`input[name="${name}"]`).prop("checked",false).removeProp("checked").removeAttr("checked").trigger(fireChange ? "change" : "value:changed");
2822		} else {
2823			value = String(value);
2824			$(`input[name="${name}"][value="${String(value)}"]`).prop("checked",true).trigger(fireChange ? "change" : "value:changed");
2825			if ($(`input[name="${name}"]:checked`).length === 0) {
2826				$(`input[name="${name}"][data-label="${String(value)}"]`).prop("checked",true).trigger(fireChange ? "change" : "value:changed");
2827			}
2828
2829		}
2830	} else if ($el.attr("data-dynrow")) {
2831		//Loop over rows
2832		for (var row in String(value).split(",")) {
2833			createDynRow(0,$el.attr("data-dynrow"),row,{});
2834		}
2835	} else if ($el.attr("data-elementtype") === "duration") {
2836		// Set the hidden field value and trigger sync to visual components
2837		$el.val(value);
2838		$el.trigger('duration:sync');
2839		if (fireChange) $el.trigger('change');
2840	} else if (window.hasOwnProperty("xmEditor") && xmEditor.hasOwnProperty(name)) {
2841		xmEditor[name].data.set(value);
2842	} else if (window.hasOwnProperty("CKEDITOR") && CKEDITOR.instances.hasOwnProperty(name)) {
2843		CKEDITOR.instances[name].insertHtml(value);
2844	} else if ($el.hasClass("hasDatepicker")) {
2845		if (value !== "") {
2846			var dateValue = moment(value).format($el.attr("dateformat"));
2847			if ($el.attr("vtype") == "datetime") {
2848				dateValue = moment(value).format($el.attr("dateformat")+" h:mm a");
2849			}
2850			if (dateValue && dateValue !== "") $el.datepicker('setDate',dateValue).trigger(fireChange ? "change" : "value:changed");	
2851		} else {
2852			$el.val(dateValue).trigger(fireChange ? "change" : "value:changed");
2853		}
2854	} else if ($el.attr("type") === "hidden" && $el.attr("data-switcher") && $el.attr("data-switcher") == "true") {
2855		$("#switcher_"+name).attr("aria-checked",String(value) == "1");
2856		$el.val(value).trigger(fireChange ? "change" : "value:changed");
2857	} else if ($el.attr("vtype") === "signature") {
2858		pasteCurrentSignature(name,value);
2859	} else if (!isInput) {
2860		$id.text(value);
2861	} else if ($el.data("elementtype") == "select2") {
2862		// updateSelect2Val refuses a value that is not in the field's allowed
2863		// list (see lib/com/select2/_init.cfm) instead of fabricating an option
2864		// for it. Hand that back so callers can tell a set from a silent drop.
2865		var select2Applied = updateSelect2Val(name,value);
2866		if (select2Applied !== false) {
2867			// Deferred retry survives a select2 re-init race. A refusal is an
2868			// answered, cached verdict, so retrying it only double-logs.
2869			setTimeout(() => {
2870				updateSelect2Val(name,value);
2871			}, 20);
2872		}
2873		return select2Applied;
2874	} else if ($el.data("elementtype") == "aceeditor") {
2875		window["ACEeditor"+name].setValue(value);
2876	} else if ($el.data("elementtype") == "jsoneditor" && value !== null && String(value) !== "" && isJSON(value) && JSONEditors.hasOwnProperty(name)) {
2877		JSONEditors[name].setValue($.parseJSON(value));
2878	} else if ($el.is("select") && $el.find(`option[value="${value}"]`).length === 0) {
2879		var matchedByText = $el.find("option").filter(function() {
2880			return $(this).text().trim() === String(value).trim();
2881		}).first();
2882
2883		if (matchedByText.length > 0) {
2884			value = matchedByText.val(); // Set value to the matched option's value
2885		}
2886		$el.val(value).trigger(fireChange ? "change" : "value:changed");
2887	} else {
2888		$el.val(value).trigger(fireChange ? "change" : "value:changed");
2889	}
2890
2891}
2892const SEV = setElementValue;
2893
2894function setElementAttribute(name, attr, value) {
2895    var $el = $(`:input[name="${name}"]`);
2896    var cv = GEV(name);
2897    var cav = $el.attr(attr);
2898
2899    if (cav === value) return;
2900
2901    // Set or remove the attribute
2902    if (value == null) {
2903        $el.removeAttr(attr);
2904    } else {
2905        $el.attr(attr, value);
2906    }
2907
2908    if ($el.hasClass("hasDatepicker")) {
2909        var dpValue = (value === "" || value == null) ? null : value;
2910
2911        if (attr === "min") {
2912            $el.datepicker("option", "minDate", dpValue);
2913        } else if (attr === "max") {
2914            $el.datepicker("option", "maxDate", dpValue);
2915        }
2916
2917        // Preserve empty state
2918        if (cv === "") {
2919            SEV(name, "", false);
2920        }
2921    }
2922}
2923const SEA = setElementAttribute;
2924
2925function checkBeforeDownloading() {
2926	if (window.hasOwnProperty("OnBeforePrintDownload")) {
2927		return OnBeforePrintDownload();
2928	} else {
2929		return true;
2930	}
2931}
2932
2933function checkForms() {
2934	var form;
2935	for (var f=0;
2935f<document.forms.length;f++) {
2936		form = document.forms[f];
2937		if (checkBeforeLeaving && isFormChanged(form)) {
2938			return "You have attempted to leave this page.  If you have made any changes to the fields without clicking the Save button, your changes will be lost.  Are you sure you want to exit this page?";
2939		}
2940	}
2941}
2942
2943function getChangedFields(oldData, newData) {
2944    let changes = {};
2945    for (let key in newData) {
2946        if (newData.hasOwnProperty(key)) {
2947            let oldVal = oldData[key];
2948            let newVal = newData[key];
2949            if (String(oldVal) !== String(newVal)) {
2950                changes[key] = {
2951                    old_value: oldVal,
2952                    new_value: newVal
2953                };
2954            }
2955        }
2956    }
2957    return changes;
2958}
2959
2960var checkBeforeLeaving = true;
2961var xmFormDefaultValues = {};
2962function isFormChanged(frmObj) {
2963	if (!checkBeforeLeaving || frmObj.getAttribute("validate") == "false") return false;
2964	var $frm = $(frmObj);
2965	var rtnVal = false; 
2966	var frm = (typeof(frmObj) == "undefined")?document.forms[0]:frmObj;
2967	var formName = frm.getAttribute("name");
2968	if (formName && formName != "" && xmFormDefaultValues.hasOwnProperty(formName)) {
2969		var currentFormValues = $frm.serializeFormJSON();
2970		rtnVal = !deepEqual(xmFormDefaultValues[formName],currentFormValues);
2971	}
2972	return rtnVal;
2973}
2974
2975let _scoringInProgress = false;
2976function getFormScoreFromOptions() {
2977
2978	if (_scoringInProgress) {
2979		// we’re already running—stop the recursion
2980		return 0;
2981	}
2982	_scoringInProgress = true;
2983	
2984	let total = 0;
2985	for (const [field, options] of Object.entries(xmFieldOptions)) {
2986		const val   = GEV(field);
2987		const score = options[val]?.score;
2988		if (score != null) {
2989		total += +score;
2990		}
2991	}
2992
2993	_scoringInProgress = false;
2994	return total;
2995}
2996
2997function getFormFieldScore(fields) {
2998	return fields.reduce((sum, field) => {
2999		const opts  = xmFieldOptions[field];
3000		const sc    = opts?.[GEV(field)]?.score;
3001		return sum + (sc ? +sc : 0);
3002	}, 0);
3003}
3004
3005function highlightChangedFormElements($frm) {
3006	var formName = $frm.attr("name");
3007	$frm.find(':input[name]').each(function(){
3008		var $el = $(this);
3009		var $lbl = $el.parent().closest(".XMFormRow").find(".XMLabel");
3010		var changed = isElementChanged(formName,$el);
3011		$lbl.toggleClass("XMFieldChanged",changed);
3012	});
3013}
3014
3015function isElementChanged(formName,$el) {
3016	var ret = xmFormDefaultValues.hasOwnProperty(formName) && xmFormDefaultValues[formName].hasOwnProperty($el.attr("name")) ? xmFormDefaultValues[formName][$el.attr("name")] != GEV($el.attr("name"),"string","^") : GEV($el.attr("name"),"string","^") !== "";
3017	return ret;
3018}
3019
3020function shallowEqual(object1, object2) {
3021	const keys1 = Object.keys(object1);
3022	const keys2 = Object.keys(object2);
3023	if (keys1.length !== keys2.length) {
3024		return false;
3025	}
3026	for (let key of keys1) {
3027		if (object1[key] !== object2[key]) {
3028		return false;
3029		}
3030	}
3031	return true;
3032}
3033
3034function deepEqual(object1, object2) {
3035	const keys1 = Object.keys(object1);
3036	const keys2 = Object.keys(object2);
3037	if (keys1.length !== keys2.length) {
3038		return false;
3039	}
3040	for (const key of keys1) {
3041		const val1 = object1[key];
3042		const val2 = object2[key];
3043		const areObjects = isObject(val1) && isObject(val2);
3044		if (
3045		areObjects && !deepEqual(val1, val2) ||
3046		!areObjects && val1 !== val2
3047		) {
3048		return false;
3049		}
3050	}
3051	return true;
3052}
3053function isObject(object) {
3054	return object != null && typeof object === 'object';
3055}
3056
3057function toggleFormSection(id,flagVar) {
3058	var img = $xm("icon_"+id);
3059	var flag = (arguments.length > 1)?flagVar:img.src.indexOf('minus') == -1;
3060	
3061	toggle("SectionBody"+id,flag);
3062	img.src=!flag?""+xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/plus.png":""+xmBasePath+"lib/img/icon/icomoon/lite-gradient/small/minus.png";	
3063}
3064
3065function cbToggleOthers(name,v) {
3066	var cb;
3067	var flag = cbValExists(name,v);
3068	$.each($("input[name='"+name+"']"), function() {
3069		cb = $(this);
3070		cb.removeAttr('disabled');
3071		if (flag && cb.val() != v) cb.removeAttr('checked').attr('disabled', 'disabled');
3072	});
3073}	
3074
3075function cbValExists(name,val) {
3076	var vs = cbVals(name);
3077	var flag = false;
3078	for (var c=0;c<vs.length;c++) {
3079		if (vs[c] == val) {
3080			flag = true;
3081			break;	
3082		}
3083	}
3084	return flag;
3085}
3086
3087function cbVals(name) {
3088	var values = new Array();
3089	$.each($("input[name='"+name+"']:checked"), function() {
3090		values.push($(this).val());
3091	});
3092	return values;
3093}
3094
3095function stripHtml(html){
3096	// Create a new div element
3097	var temporalDivElement = document.createElement("div");
3098	// Set the HTML content with the providen
3099	temporalDivElement.innerHTML = html;
3100	// Retrieve the text property of the element (cross-browser support)
3101	return temporalDivElement.textContent || temporalDivElement.innerText || "";
3102}	
3103
3104function xmRowsToggle(rows,flag) {
3105	var rowArr = rows.split(",");
3106	for (var r=0;r<rowArr.length;r++) {
3107		toggle('form_row_row_'+rowArr[r],flag);	
3108	}
3109}
3110var xmPageDirty = false;
3111window.onbeforeunload = function(){
3112	var pageObj = {date:now(),path:window.location.pathname,forms:{}};
3113	$("form[persist]").each(function() {
3114		var $frm = $(this);
3115		pageObj["forms"][$frm.attr("name")] = $frm.serializeFormJSON();
3116	});
3117	try {
3118		localStorage["page"] = JSON.stringify(pageObj);		
3119	} catch (error) {		
3120	}
3121	var form;
3122	for (var f=0;
3122f<document.forms.length;f++) {
3123		form = document.forms[f];
3124		if (checkBeforeLeaving && isFormChanged(form)) {
3125			highlightChangedFormElements($(form));
3126			return "You are attempting to leave this page.  If you have made any changes to the form without saving, your changes will be lost.  Are you sure you want to exit this page?";
3127		}
3128	}
3129	if (checkBeforeLeaving && xmPageDirty) {
3130		return "You are attempting to leave this page.  You may have some unsaved changes that may be lost.  Are you sure you want to exit this page?";
3131	}
3132	if (showLeavingIndicator) $("body").addClass("xmLeaving");
3133};
3134
3135if (!formsGlobalsLoaded) {
3136	addEvent(window,"load",function(){
3137		setTimeout(function(){
3138			setupForms();
3139			formsGlobalsLoaded = true;
3140		},500);
3141	});
3142}
3143
3144function xmCreateAndSubmitForm(obj) {
3145	var $frm = $('<form></form>').attr({
3146		"method":"post",
3147		"enctype":"multipart/form-data",
3148		"action":obj.action,
3149		"target":obj.target ? obj.target : "_self"
3150	});
3151	if (obj.hasOwnProperty("inputs")) {
3152		for (var i in obj.inputs) {
3153			$("<textarea/>").attr({
3154				"name":i
3155			}).css("display","none").val(obj.inputs[i]).appendTo($frm);
3156		}
3157	}
3158	if (obj.hasOwnProperty("elements")) {
3159		for (var e of obj.elements) {
3160			$(e).appendTo($frm);
3161		}
3162	}
3163	$frm.appendTo('body').submit();
3164}
3165//END FORM SAVING CODE
3166
3167//Check if form is dirty and add an event handler to prevent loss of data.
3168
3169function switchButton(obj) {
3170	var swtch = (obj.getAttribute("switch") == "on")?"off":"on";
3171	var hidObj = $xm(obj.getAttribute("key"));
3172	var lblObj = $xm(obj.getAttribute("key")+"SwitchLbl");
3173	obj.setAttribute("switch",swtch);
3174	obj.className = "Switch"+swtch;
3175	lblObj.innerHTML = swtch;
3176	hidObj.value = (swtch == "on")?1:0;
3177}
3178
3179
3180//END FORM CODE
3181
3182function initGoogleMap() {
3183	//console.log("Google Map Inited")
3184}
3185
3186
3187var NUMBER_OF_STARS = 5;
3188
3189function initRating(id,newRate) {
3190	var obj = $xm(id);
3191	if (typeof(newRate) != "undefined") obj.setAttribute("rating",newRate);
3192	
3193	var rating = obj.getAttribute("rating");
3194	obj.innerHTML = "";
3195	var uniqueid = obj.getAttribute("uniqueid");
3196	//obj.removeChild(obj.firstChild);
3197	for (var j = 0; j < NUMBER_OF_STARS; j++) {
3198		var star = document.createElement('img');
3199		if (rating >= 1) {
3200			star.setAttribute('src', xmBasePath+'lib/tag/xm/inc/rating/rating_on.png');
3201			star.className = 'on';
3202			rating--;
3203		} else if(rating == 0.5) {
3204			star.setAttribute('src', xmBasePath+'lib/tag/xm/inc/rating/rating_half.png');
3205			star.className = 'half';
3206			rating = 0;
3207		} else {
3208			star.setAttribute('src', xmBasePath+'lib/tag/xm/inc/rating/rating_off.png');
3209			star.className = 'off';
3210		}
3211		star.setAttribute('id', 'star_'+uniqueid+'_'+j);
3212		star.onclick = function() {submitRating(this.id);};
3213		star.onmouseover = new Function("evt", "displayHover("+uniqueid+", "+j+");");
3214		star.onmouseout = new Function("evt", "displayNormal("+uniqueid+", "+j+");");
3215		obj.appendChild(star);
3216	} 
3217}
3218
3219function displayHover(ratingId, star) {
3220	for (var i = 0; i <= star; i++) {
3221		$xm('star_'+ratingId+'_'+i).setAttribute('src', xmBasePath+'lib/tag/xm/inc/rating/rating_over.png');
3222	}
3223}
3224
3225function displayNormal(ratingId, star) {
3226	for (var i = 0; i <= star; i++) {
3227		var status = $xm('star_'+ratingId+'_'+i).className;
3228		$xm('star_'+ratingId+'_'+i).setAttribute('src', xmBasePath+'lib/tag/xm/inc/rating/rating_'+status+'.png');
3229	}
3230}
3231
3232function getBrowserAndOS() {
3233	let userAgent = navigator.userAgent;
3234	let browserName;
3235	let osName = "Unknown OS";
3236
3237	// Detect browser name
3238	if (userAgent.match(/chrome|chromium|crios/i)) {
3239		browserName = "Chrome";
3240	} else if (userAgent.match(/firefox|fxios/i)) {
3241		browserName = "Firefox";
3242	} else if (userAgent.match(/safari/i)) {
3243		browserName = "Safari";
3244	} else if (userAgent.match(/opr\//i)) {
3245		browserName = "Opera";
3246	} else if (userAgent.match(/edg/i)) {
3247		browserName = "Edge";
3248	} else {
3249		browserName = "Unknown";
3250	}
3251
3252	// Detect OS
3253	if (userAgent.indexOf("Win") != -1) osName = "Windows";
3254	if (userAgent.indexOf("Mac") != -1) osName = "MacOS";
3255	if (userAgent.indexOf("X11") != -1) osName = "UNIX";
3256	if (userAgent.indexOf("Linux") != -1) osName = "Linux";
3257	if (/android/i.test(userAgent)) osName = "Android";
3258	if (/iPad|iPhone|iPod/.test(userAgent)) osName = "iOS";
3259
3260	return { browserName, osName };
3261}
3262  
3263
3264function isInViewport(element) {
3265	const rect = element.getBoundingClientRect();
3266	return (
3267		rect.top >= 0 &&
3268		rect.left >= 0 &&
3269		rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
3270		rect.right <= (window.innerWidth || document.documentElement.clientWidth)
3271	);
3272}
3273
3274var localizations = {};
3275function GLT(varName) {
3276	if (!enableTranslations) return varName;
3277	else if (varName == "") return varName;
3278	else if (localizations.hasOwnProperty(varName)) return localizations[varName];
3279	//console.log(varName);
3280	var url = "/lib/cfc/util/translate.cfc?returnformat=json&method=GLT&variable_name="+encodeURIComponent(varName);
3281	if (URLOBJ.hasOwnProperty("reset_translation")) {
3282		url = url + "&reset_translation=true";
3283	}
3284	var result = "";
3285	var response = $.ajax({
3286		type: 'GET',
3287		url: url,
3288		dataType: 'json',
3289		success: function(data) {result=data; },
3290		data: {},
3291		async: false
3292	});
3293	result = $.trim(result);
3294	localizations[varName] = result;
3295	return result;
3296}
3297/*
3298var $main;
3299var localCacheObj = {};
3300
3301$(document).ready(function(e) {
3302	$main = $("#main");
3303	setupXMPreload();
3304});
3305
3306function setupXMPreload() {
3307	$(".xmPreLoad").each(function(i,v) {
3308		var $this = $(this);
3309		$this.data("url",$this.attr("href"));
3310		$this.attr("href","javascript:void(0);");
3311		$this.click(function() {
3312			//console.log($this);
3313			xmLoadInline(document.title,$this.data("url"));
3314		})
3315	});
3316}
3317
3318async function xmLoadInline(title,path) {
3319	let url = new URL(path, document.baseURI);
3320	url.searchParams.set("xm_show_body_only", true);
3321	var relativePath = url.pathname+url.search;
3322	$main.html("<img src='/lib/img/loading-bars.svg' style='position:absolute;top:50%;left:50%;'/>");
3323	//console.log(url);
3324
3325	// const response = await fetch(url);
3326	// const pageText = await response.text();
3327	if (localCacheObj.hasOwnProperty(relativePath)) {
3328		$main.html(localCacheObj[relativePath]);
3329		setupXMPreload();
3330	} else {
3331		$.get(relativePath, function( data ) {
3332			localCacheObj[relativePath] = data;
3333			$main.html(localCacheObj[relativePath]);
3334			setupXMPreload();
3335		});
3336	}
3337	//$main.load(path);
3338	document.title = title;
3339	history.pushState({}, title, path);
3340}
3341*/
3342function htmlDecode(input){
3343	var e = document.createElement('div');
3344	e.innerHTML = input;
3345	return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
3346}
3347
3348function stripAccents(str) {
3349    return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
3350}
3351
3352function getContrastYIQ(hexcolor){
3353	if (hexcolor.length === 7) hexcolor = hexcolor.slice(-6);
3354	var r = parseInt(hexcolor.substr(0,2),16);
3355	var g = parseInt(hexcolor.substr(2,2),16);
3356	var b = parseInt(hexcolor.substr(4,2),16);
3357	var yiq = ((r*299)+(g*587)+(b*114))/1000;
3358	var brightness = Math.round(r * 0.299) + Math.round(g * 0.587) + Math.round(b * 0.114);
3359
3360	return (brightness > 186) ? "#000000" : "#FFFFFF";
3361	//return (yiq >= 128) ? 'black' : 'white';
3362}
3363
3364function getContrast (hexcolor){
3365
3366	// If a leading # is provided, remove it
3367	if (hexcolor.slice(0, 1) === '#') {
3368		hexcolor = hexcolor.slice(1);
3369	}
3370
3371	// If a three-character hexcode, make six-character
3372	if (hexcolor.length === 3) {
3373		hexcolor = hexcolor.split('').map(function (hex) {
3374			return hex + hex;
3375		}).join('');
3376	}
3377
3378	// Convert to RGB value
3379	let r = parseInt(hexcolor.substr(0,2),16);
3380	let g = parseInt(hexcolor.substr(2,2),16);
3381	let b = parseInt(hexcolor.substr(4,2),16);
3382
3383	// Get YIQ ratio
3384	let yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
3385
3386	// Check contrast
3387	return (yiq >= 128) ? 'black' : 'white';
3388
3389}
3390
3391function toggleFavorite(obj) {
3392	$.getJSON("/lib/cfc/user.cfc?returnFormat=JSON&method=Favorite&_dt="+new Date().getTime(),{
3393		tne:obj.tableNameEnc,
3394		flag:obj.flag,
3395		record_id:obj.record_id
3396	},function(data){
3397		obj.fx(data);
3398	});
3399}
3400
3401function toggleArchive(obj) {
3402	$.getJSON("/lib/cfc/data.cfc?returnFormat=JSON&method=ArchiveRecord&_dt="+new Date().getTime(),{
3403		tne:obj.tableNameEnc,
3404		record_id:obj.record_id
3405	},function(data){
3406		obj.fx(data);
3407	});
3408}
3409
3410function formatPctColor(perc) {
3411	var r, g, b = 0;
3412	if (perc === null || perc === 0) {
3413		return "";
3414	} else if (perc < 50) {
3415		r = 255;
3416		g = Math.round(5.1 * perc);
3417	} else {
3418		g = 255;
3419		r = Math.round(510 - 5.10 * perc);
3420	}
3421	var h = r * 0x10000 + g * 0x100 + b * 0x1;
3422	return '#'+('000000' + h.toString(16)).slice(-6);
3423}
3424
3425function getColorFromBGColor(color) {
3426	if (color === null || color === "") return "";
3427	var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
3428	var rgb = [
3429		parseInt(result[1], 16),
3430		parseInt(result[2], 16),
3431		parseInt(result[3], 16)
3432	];
3433	var brightness = Math.round(((parseInt(rgb[0]) * 299) +
3434						  (parseInt(rgb[1]) * 587) +
3435						  (parseInt(rgb[2]) * 114)) / 1000);
3436	return (brightness > 186) ? '#000000' : '#ffffff';	
3437}
3438
3439function getColorFromRange(value,opts) {
3440	if (isNaN(value)) {
3441		value = opts.min;
3442	}
3443	value = Math.max(opts.min, Math.min(value, opts.max));
3444	var perc = (opts.min + value) / opts.max;
3445	var colorIdx = Math.round((opts.colors.length - 1) * perc);
3446	return opts.colors[colorIdx];
3447}
3448
3449function formatWFTimeline(ui) {
3450	var ret = {text:ui.cellData};
3451	if (ui.cellData && ui.cellData != null && ui.cellData != "") {
3452		var tlArr = JSON.parse(ui.cellData);
3453		ret["text"] = "<ul class='prgLineBar'>";
3454		for (var o of tlArr) {
3455			ret["text"] += `<li style="background-color:${o.color};width:${Math.max(1,o.duration)*5}px" class="xmTip" data-tippy-content="${o.title}: ${o.date}">&nbsp;</li>`;
3456		}
3457		ret["text"] += `</ul>`;
3458	}
3459	return ret;
3460}
3461
3462function formatDurationGrid(ui) {
3463	var val = ui.cellData || '';
3464	var ret = {text:val};
3465	if (val !== null && val !== '' && !isNaN(val)) {
3466		var num = parseFloat(val);
3467		var h = Math.floor(num);
3468		var m = Math.round((num - h) * 60);
3469		ret.text = h + 'h ' + m + 'm';
3470	}
3471	return ret;	
3472}
3473
3474function xmGridFormatSubGridLinkField(ui) {
3475	var tbl = ui.dataIndx;
3476	var r = ui.rowData.recordid;
3477	if (ui.hasOwnProperty("cellData") && typeof ui.cellData !== "undefined" && ui.cellData > 0) {
3478		var srcTable = (typeof xmTableName !== "undefined") ? xmTableName : "";
3479		var txt = `<span id="GridRemoteTip${tbl}-${r}" style="margin-right:5px;" class="xmTipRemote" data-remote="/modules/portal/go.cfm?a=grid&field=${tbl}&_source_id=${r}&_source=${encodeURIComponent(srcTable)}&summary_list=true&fullscreen=true&tooltip=tippy"><i class="fa fal fa-rectangle-list"></i></span>${ui.cellData}`;
3480		return {
3481			"text":txt
3482		}
3483	}
3484}
3485
3486function formatTokens(opt) {
3487	var itemsMatched = 0;
3488	var baseColor = "#dddddd";
3489	var itemStyle = `background:${baseColor};color:${getContrastYIQ(baseColor)}`;
3490	var itemText = "";
3491	if (!opt.hasOwnProperty("value") || typeof(opt.value) == "undefined") {
3492		return "";
3493	}
3494	var ret = "<ul class='xmTokenList'>";
3495	var hasItems = false;
3496	if (opt.value === undefined && opt.hasOwnProperty("ui") && opt.ui.hasOwnProperty("rowData") && opt.ui.rowData.hasOwnProperty("children") && opt.ui.rowData.children.length > 0) {
3497		opt.value = opt.ui.rowData.children[0][opt.ui.dataIndx];
3498	}
3499	//MR: 3/11/2024 - skip if grand summary
3500	if (opt.hasOwnProperty("ui") && opt.ui.hasOwnProperty("rowData") && opt.ui.rowData.hasOwnProperty("pq_grandsummary")) {
3501		return opt.value;
3502	}
3503	var del = opt.hasOwnProperty("delimiter") ? opt.delimiter : "^";
3504	if (!opt.hasOwnProperty("delimiter") && del === "^" && opt.hasOwnProperty("value") && String(opt.value).indexOf("^") === -1 && String(opt.value).indexOf(",") > -1) {
3505		del = ",";
3506	}
3507	var valArr = (opt.hasOwnProperty("value")) ? String(opt.value).split(del) : [];	
3508	var foundItem = false;
3509	var valueFound = {};
3510	for (var v of valArr) {
3511		// An empty array element (from a trailing/leading/double delimiter, e.g.
3512		// "a^b^" or "a,,b") must never render as a blank token.
3513		if ($.trim(v) === "") continue;
3514		if (opt.hasOwnProperty("options") && opt.options.length > 0) {
3515			foundItem = false;
3516			for (var a of opt.options) {
3517				if ($.trim(a.value) === $.trim(v) && a.value !== "" && !valueFound.hasOwnProperty($.trim(a.value))) {
3518					itemText = a.label;
3519					if (a.hasOwnProperty("color")) {
3520						itemStyle = `background:${a.color};color:${getContrastYIQ(a.color)}`;
3521						ret += `<li class="xmTokenItem" style="background:${a.color};color:${getContrastYIQ(a.color)}">${a.label}</li>`;
3522					} else {
3523						ret += `<li class="xmTokenItem">${a.label}</li>`;
3524					}
3525					itemsMatched++;
3526					valueFound[$.trim(a.value)] = true;
3527					hasItems = true;
3528					foundItem = true;
3529				}	
3530			}
3531			if (!foundItem) {
3532				ret += `<li class="xmTokenItem">${v}</li>`;
3533				itemText = v;
3534				itemsMatched++;
3535				hasItems = true;
3536			}
3537		} else {
3538			ret += `<li class='xmTokenItem'>${v}</li>`;
3539			itemText = v;
3540			itemsMatched++;
3541			hasItems = true;
3542		}
3543	}
3544	ret += "</ul>";
3545	if (hasItems && opt.hasOwnProperty("ui") && (opt.ui.rowData.pq_gtitle || opt.ui.rowData.pq_gsummary) ) {
3546		var grpIcon = opt.ui.rowData.pq_close ? 'plus' : 'minus';
3547		ret = '<span class="pq-group-icon fa fa-'+grpIcon+'"></span> ' + ret;
3548		if (opt.ui.rowData.pq_gid) {
3549			ret = ret + ` <span class="pq-group-count">(${opt.ui.rowData.children.length})</span>`;
3550		}
3551	}
3552	var ret = {text:ret};
3553	if (itemsMatched === 1 && itemText !== "" && window.hasOwnProperty("tokenMatchCellStyle")) {
3554		var itemPrefix = "";
3555		var itemSuffix = "";
3556		if (hasItems && opt.hasOwnProperty("ui") && (opt.ui.rowData.pq_gtitle || opt.ui.rowData.pq_gsummary) ) {
3557			var grpIcon = opt.ui.rowData.pq_close ? 'plus' : 'minus';
3558			itemPrefix = '<span class="pq-group-icon fa fa-'+grpIcon+'"></span> ';
3559			if (opt.ui.rowData.pq_gid) {
3560				itemSuffix = ` <span class="pq-group-count">(${opt.ui.rowData.children.length})</span>`;
3561			}
3562		}
3563
3564		ret.style = itemStyle;
3565		ret.text = itemPrefix + itemText + itemSuffix;
3566	} 
3567	return ret;
3568}
3569
3570function isNumeric(str) {
3571	if (typeof str != "string") return false // we only process strings!  
3572	return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
3573			!isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail
3574}
3575
3576function addValidationCheck(obj) {
3577	if (obj.type == "match") {
3578		var $fld1 = $(":input[name='"+obj.field1+"']");
3579		var $fld2 = $(":input[name='"+obj.field2+"']");
3580		$fld1.add($fld2).on("change input blur calx.renderComputedValue",function(e){
3581			var v1 = $fld1.val();
3582			var v2 = $fld2.val();
3583			var valuesMatch = (String(v2) === String(v1) && v2 !== "" && v1 !== "");
3584			if (!valuesMatch && isNumeric(v1) && isNumeric(v2)) {
3585				valuesMatch = parseFloat(v1) === parseFloat(v2);
3586			}
3587			$fld2[0].setCustomValidity(valuesMatch ? "" : obj.message);
3588		}).blur();	
3589	}
3590}
3591
3592function closeSidePanel() {
3593	$(".btnSidePanelClose").click();
3594}
3595
3596function openInSidePanel(path) {
3597	var $clsBtn = $("<span/>").css({
3598		"position":"absolute",
3599		"top":"10px",
3600		"right":"10px",
3601		"cursor":"pointer",
3602		"z-index":99
3603	}).addClass("btn-xs btnSidePanelClose btn-danger").html("Close").click(function(e){
3604		$(".xm-panel-wrap").css("transform","translateX(100%)");
3605		xmPanelOpen = false;
3606		$(window).trigger("xmPanelFrame:closed",[true]);
3607	});
3608	if ($("#xmPanelFrame").length) {
3609		/* Keep the DOM src synchronized with the active page. Besides being
3610		   clearer than mutating frames[].location, this prevents a future DOM
3611		   reparent from falling back to the original blank placeholder. */
3612		$("#xmPanelFrame").attr("src",path+"&fullscreen=true");
3613		$(".xm-panel-wrap").css("transform","translateX(0%)");
3614	} else {
3615		$(".xm-panel-wrap").html("").append(
3616			$('<iframe>', {
3617				src: path+"&fullscreen=true",
3618				id:  'xmPanelFrame',
3619				name:  'xmPanelFrame',
3620				class:  'xm-panel-frame',
3621				frameborder: 0,
3622				width: "100%",
3623				height: "100%",
3624				scrolling: 'auto'
3625			})
3626		).append($clsBtn).css("transform","translateX(0%)");
3627	}
3628	xmPanelOpen = true;
3629}
3630
3631/* Expand the existing side-panel container into a viewport workspace. The
3632   iframe must stay under the same parent: moving an iframe can reload its
3633   original src and destroy uploads, editor history, and unsaved form state.
3634   Kept next to openInSidePanel so the same behavior is available from both
3635   Data Explorer and Workbench. */
3636var xmSidePanelWorkspaceState = null;
3637
3638function xmOpenSidePanelWorkspace() {
3639	if (xmSidePanelWorkspaceState) return true;
3640	var frame = document.getElementById("xmPanelFrame");
3641	if (!frame) return false;
3642
3643	var $frame = $(frame);
3644	var $panel = $frame.closest(".xm-panel-wrap");
3645	if (!$panel.length) return false;
3646	var $sideClose = $panel.children(".btnSidePanelClose");
3647
3648	var $overlay = $('<div id="xmPanelWorkspaceOverlay" role="dialog" aria-modal="true" aria-label="Custom collection layout workspace"></div>').css({
3649		position:"fixed", top:0, right:0, bottom:0, left:0, zIndex:2147482999, background:"rgba(15,23,42,.72)"
3650	});
3651	var $close = $('<button type="button" class="xm-panel-workspace-close" aria-label="Close layout workspace"><i class="fa fa-times"></i></button>').css({
3652		position:"absolute", top:"calc(1.5vh + 10px)", right:"calc(1.5vw + 10px)", zIndex:3,
3653		display:"grid", placeItems:"center", width:"32px", height:"32px", padding:0,
3654		border:"1px solid #e2e8f0", borderRadius:"8px", background:"#fff", color:"#64748b",
3655		fontSize:"15px", boxShadow:"0 2px 8px rgba(15,23,42,.12)", cursor:"pointer"
3656	});
3657
3658	xmSidePanelWorkspaceState = {
3659		frame:frame,
3660		$panel:$panel,
3661		$sideClose:$sideClose,
3662		$overlay:$overlay,
3663		$close:$close,
3664		panelStyle:$panel.attr("style"),
3665		sideCloseStyle:$sideClose.length ? $sideClose.attr("style") : null,
3666		bodyOverflow:document.body.style.overflow
3667	};
3668
3669	$close.on("click",function() {
3670		var childModal = null;
3671		try {
3672			var child$ = frame.contentWindow.jQuery;
3673			childModal = child$ ? child$("#DocumentRecipeModal") : null;
3674			if (childModal && childModal.length && childModal.hasClass("in")) {
3675				childModal.modal("hide");
3676				setTimeout(function() { if (xmSidePanelWorkspaceState) xmCloseSidePanelWorkspace(); },450);
3677				return;
3678			}
3679		} catch (ignore) {}
3680		xmCloseSidePanelWorkspace();
3681	});
3682
3683	$overlay.appendTo(document.body);
3684	$sideClose.hide();
3685	$panel.append($close).css({
3686		position:"fixed", top:"1.5vh", right:"1.5vw", bottom:"1.5vh", left:"1.5vw",
3687		width:"auto", height:"auto", transform:"none", transition:"none", zIndex:2147483000,
3688		border:0, borderRadius:"10px", boxShadow:"0 24px 70px rgba(0,0,0,.35)",
3689		overflow:"hidden", background:"#fff"
3690	});
3691	document.body.style.overflow = "hidden";
3692	$(window).trigger("resize");
3693	return true;
3694}
3695
3696function xmCloseSidePanelWorkspace() {
3697	var state = xmSidePanelWorkspaceState;
3698	if (!state) return;
3699	xmSidePanelWorkspaceState = null;
3700
3701	state.$close.remove();
3702	if (state.panelStyle == null) state.$panel.removeAttr("style");
3703	else state.$panel.attr("style",state.panelStyle);
3704	if (state.$sideClose.length) {
3705		if (state.sideCloseStyle == null) state.$sideClose.removeAttr("style");
3706		else state.$sideClose.attr("style",state.sideCloseStyle);
3707	}
3708	state.$overlay.remove();
3709	document.body.style.overflow = state.bodyOverflow;
3710	$(window).trigger("resize");
3711}
3712
3713
3714var lazyLoadItems = [];
3715var activeLazyLoads = 0;
3716const LAZY_LOAD_CONCURRENCY = 3;
3717var lazyLoadTo ={};
3718
3719function xmYield() {
3720	if (typeof scheduler !== "undefined" && typeof scheduler.yield === "function") {
3721		return scheduler.yield();
3722	}
3723	if (typeof window.requestIdleCallback === "function") {
3724		return new Promise(function (resolve) { window.requestIdleCallback(resolve, { timeout: 50 }); });
3725	}
3726	return new Promise(function (resolve) { setTimeout(resolve, 0); });
3727}
3728
3729function lazyLoadContent($el) {
3730	if ($el && !lazyLoadItems.includes($el)) {
3731		lazyLoadItems.push($el);
3732	}
3733	while (activeLazyLoads < LAZY_LOAD_CONCURRENCY && lazyLoadItems.length > 0) {
3734		const $target = lazyLoadItems.shift();
3735		activeLazyLoads++;
3736		startLazyLoad($target);
3737	}
3738}
3739
3740function startLazyLoad($target) {
3741	let path = $target.attr("data-path");
3742	if ($target[0].hasAttribute("data-params")) {
3743		path += formatTemplateString($target.attr("data-params"));
3744	}
3745
3746	const doLoad = () => {
3747		return new Promise((resolve, reject) => {
3748			$target.load(path, function (responseText, textStatus, jqXHR) {
3749				if (textStatus === "success" || textStatus === "notmodified") {
3750					$target.removeAttr("data-retry-count");
3751					$target.addClass("xmRevealed");
3752					$target.attr("data-created", true);
3753
3754					const $loaded = $(this);
3755					requestAnimationFrame(function () {
3756						$loaded.find(".xmTip").each(function () {
3757							const $this = $(this);
3758							$this.attr("data-tippy-content", $this.attr("title"));
3759							$this.removeAttr("title");
3760						});
3761					});
3762
3763					xmYield().then(function () {
3764						const xmReveals = [...document.querySelectorAll(".xmReveal")];
3765						xmReveals.forEach((reveal) => {
3766							xmRevealsObserver.observe(reveal);
3767						});
3768						return xmYield();
3769					}).then(function () {
3770						initDom();
3771					});
3772				} else {
3773					if (jqXHR.status === 403) {
3774						$target.html(responseText);
3775					} else {
3776						var retryCount = parseInt($target.attr("data-retry-count") || "0", 10);
3777						if (retryCount < 3) {
3778							$target.attr("data-retry-count", retryCount + 1);
3779							var delay = Math.pow(2, retryCount) * 1000;
3780							setTimeout(function() {
3781								$target.removeAttr("data-created");
3782								$target.removeClass("xmRevealed");
3783								lazyLoadContent($target);
3784							}, delay);
3785						} else {
3786							$target.html(
3787								'<div class="alert alert-warning" style="margin:10px;">' +
3788									'<i class="fa-solid fa-triangle-exclamation"></i> ' +
3789									'Unable to load content (Error ' + jqXHR.status + '). ' +
3790									'<a href="javascript:void(0)" class="xm-lazy-retry alert-link">Retry</a>' +
3791								'</div>'
3792							);
3793							$target.find('.xm-lazy-retry').on('click', function () {
3794								$target.empty();
3795								$target.removeAttr("data-created");
3796								$target.removeAttr("data-retry-count");
3797								$target.removeClass("xmRevealed");
3798								lazyLoadContent($target);
3799							});
3800						}
3801					}
3802					$target.attr("data-created", true);
3803					$target.addClass("xmRevealed");
3804					if (typeof xmJSErrors !== "undefined") {
3805						xmJSErrors.push({
3806							message: 'lazyLoad Error: HTTP ' + jqXHR.status + ' loading ' + path,
3807							filename: path,
3808							lineno: 'N/A',
3809							colno: 'N/A',
3810							timestamp: new Date().toISOString(),
3811							stack: 'startLazyLoad - Status: ' + jqXHR.status + ', textStatus: ' + textStatus
3812						});
3813					}
3814				}
3815
3816				activeLazyLoads--;
3817				setTimeout(() => lazyLoadContent(), 10);
3818				setTimeout(() => $target.trigger("dataLoaded"), 10);
3819				$(window).trigger("xmreveal:load", [$target]);
3820				resolve();
3821			});
3822		});
3823	};
3824
3825	if (document.startViewTransition) {
3826		/* LAZY_LOAD_CONCURRENCY is 3, so a second reveal routinely starts a transition
3827		   while this one is still animating - the browser then skips this one and
3828		   rejects .ready with AbortError. doLoad still runs, so the content loads
3829		   either way; without these catches the skip surfaces as an uncaught promise
3830		   rejection in the console. */
3831		const xmVT = document.startViewTransition(doLoad);
3832		xmVT.ready.catch(() => {});
3833		xmVT.finished.catch(() => {});
3834		xmVT.updateCallbackDone.catch(() => {});
3835	} else {
3836		doLoad();
3837	}
3838}
3839
3840const xmRevealsCallback = (entries, observer) => {
3841	entries.forEach(entry => {
3842		const { target } = entry;
3843		const $target = $(target);
3844
3845		if (!$target.attr("id")) {
3846			$target.attr("id","uid_"+btoa(Math.random()).slice(0, 15));
3847		}
3848
3849		var uid = $target.attr("id");
3850		
3851		if (entry.isIntersecting) {
3852			//console.log("Showing: ",target, lazyLoadTo);
3853			lazyLoadTo[uid] = setTimeout(function(){
3854
3855				target.classList.add("is-visible");
3856				//console.log("Start Showing Me: ",$target.is(":visible"), target);
3857				if (!$target.data("created") && $target.is(":visible")) {
3858					$target.trigger("inView");
3859					//console.log($target);
3860					if ($target.data("type") == "html") {
3861						lazyLoadContent($target);
3862					} else if ($target.data("type") == "iframe" || $target.data("type") == "img") {
3863						$target.attr("src",$target.attr("data-path"));
3864						$target.attr("data-created",true).css("background","none");
3865					}
3866				}
3867			},50);
3868		} else {
3869			//console.log("Hiding: ",entry.isIntersecting, target, lazyLoadTo);
3870
3871			if (lazyLoadTo.hasOwnProperty(uid)) {
3872				clearTimeout(lazyLoadTo[uid]);
3873			}
3874			target.classList.remove("is-visible");
3875		}
3876	});
3877};
3878
3879const xmRevealsObserver = new IntersectionObserver(xmRevealsCallback, {
3880	rootMargin: "800px 0px",
3881	threshold: 0.01
3882});
3883
3884function observeElement(element, property, callback, delay = 0) {
3885    let elementPrototype = Object.getPrototypeOf(element);
3886    if (elementPrototype.hasOwnProperty(property)) {
3887        let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
3888        Object.defineProperty(element, property, {
3889            get: function() {
3890                return descriptor.get.apply(this, arguments);
3891            },
3892            set: function () {
3893                let oldValue = this[property];
3894                descriptor.set.apply(this, arguments);
3895                let newValue = this[property];
3896                if (typeof callback == "function") {
3897                    setTimeout(callback.bind(this, oldValue, newValue), delay);
3898                }
3899                return newValue;
3900            }
3901        });
3902    }
3903}
3904
3905var xmWarmupStarted = false;
3906const WARMUP_MAX_ITEMS = 4;
3907const WARMUP_POLL = 500;
3908const WARMUP_MAX_WAIT = 20000;
3909
3910function xmLazyQueueIsIdle() {
3911	return activeLazyLoads === 0 && lazyLoadItems.length === 0;
3912}
3913
3914/*
3915	Warm-up candidates are ONLY the reveals the real lazy loader will never pick up on
3916	its own: collapsed <details> sections the user may expand later. Anything visible is
3917	already queued (or done) by xmRevealsCallback, so warming it just duplicates the
3918	request - GenerateRecordDataView sends Cache-Control:no-store, so the browser can
3919	never reuse a warmed response. The one thing warming buys is a hot server-side record
3920	cache, and that is not worth competing for CF request threads while the user is still
3921	waiting on the section they actually opened.
3922
3923	Uses the same :visible test as xmRevealsCallback, so the two can never disagree about
3924	which reveals the real loader owns.
3925*/
3926function xmWarmupCandidates() {
3927	const reveals = document.querySelectorAll('.xmReveal[data-path][data-type="html"]:not([data-created]):not([data-warmed])');
3928	return [...reveals].filter(function (el) { return !$(el).is(":visible"); });
3929}
3930
3931function warmOneLazyLoad(el) {
3932	let path = el.getAttribute("data-path");
3933	const params = el.getAttribute("data-params");
3934	if (params) path += formatTemplateString(params);
3935	el.setAttribute("data-warmed", "1");
3936	try {
3937		return fetch(path, {
3938			method: "GET",
3939			priority: "low",
3940			credentials: "same-origin",
3941			headers: { "X-Warmup": "1" }
3942		}).then(function () {}, function () {});
3943	} catch (e) {
3944		return Promise.resolve();
3945	}
3946}
3947
3948function warmLazyLoadCache() {
3949	if (xmWarmupStarted) return;
3950	xmWarmupStarted = true;
3951
3952	const deadline = Date.now() + WARMUP_MAX_WAIT;
3953	let idleStreak = 0;
3954
3955	function warmNext(remaining) {
3956		if (remaining <= 0 || Date.now() > deadline) return;
3957
3958		// Yield the instant a real load starts - that is the one the user is waiting on.
3959		if (!xmLazyQueueIsIdle()) {
3960			setTimeout(function () { warmNext(remaining); }, WARMUP_POLL);
3961			return;
3962		}
3963
3964		const candidates = xmWarmupCandidates();
3965		if (!candidates.length) return;
3966
3967		// One at a time. Each of these is a full ColdFusion request with its own
3968		// <xm:pageheader/> bootstrap, so a fan-out here is what stalls record opens.
3969		warmOneLazyLoad(candidates[0]).then(function () {
3970			setTimeout(function () { warmNext(remaining - 1); }, WARMUP_POLL);
3971		});
3972	}
3973
3974	/*
3975		Wait for the real lazy-load queue to drain and STAY drained. Requiring two
3976		consecutive idle polls also covers the startup window, where the queue only looks
3977		idle because xmRevealsCallback has not fired its 50ms timer yet.
3978	*/
3979	(function waitForIdle() {
3980		if (Date.now() > deadline) return;
3981		if (xmLazyQueueIsIdle()) {
3982			idleStreak++;
3983			if (idleStreak >= 2) {
3984				warmNext(WARMUP_MAX_ITEMS);
3985				return;
3986			}
3987		} else {
3988			idleStreak = 0;
3989		}
3990		setTimeout(waitForIdle, WARMUP_POLL);
3991	})();
3992}
3993
3994function setupLazyLoading(){
3995	const xmReveals = [...document.querySelectorAll(".xmReveal")];
3996	xmReveals.forEach((reveal, index) => {
3997		xmRevealsObserver.observe(reveal);
3998	});
3999
4000	if ("requestIdleCallback" in window) {
4001		requestIdleCallback(warmLazyLoadCache, { timeout: 2000 });
4002	} else {
4003		setTimeout(warmLazyLoadCache, 500);
4004	}
4005}
4006
4007function fixGrammarInput(opts) {
4008	var $el = $("#"+opts.input);
4009	var $cont = $(opts.handler);
4010
4011	xmShowToastMessage({"heading":"","text":"Checking input for spelling mistakes and grammar corrections"});
4012	$.post("/lib/com/ai/bot.cfc?returnFormat=JSON&method=FixGrammar",{input:$el.val()},function(data){
4013		xmHideToastMessage();
4014		$el.val(data.response);
4015	},"JSON");
4016}
4017
4018const base64url = {
4019	encode: function(buffer) {
4020	  const base64 = window.btoa(String.fromCharCode(...new Uint8Array(buffer)));
4021	  return base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
4022	},
4023	decode: function(base64url) {
4024	  const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
4025	  const binStr = window.atob(base64);
4026	  const bin = new Uint8Array(binStr.length);
4027	  for (let i = 0; i < binStr.length; i++) {
4028		bin[i] = binStr.charCodeAt(i);
4029	  }
4030	  return bin.buffer;
4031	}
4032}
4033
4034function arrayToHTMLTable(data,header=false) {
4035	// Create a table element
4036	try {
4037
4038		const table = document.createElement('table');
4039		table.classList.add('table', 'table-striped', 'table-bordered', 'xmGridToolTip', 'jsonTable');
4040
4041		if (isJSON(data)) data = JSON.parse(data);
4042
4043		// Create a header row
4044		if (header) {
4045			const headerRow = document.createElement('tr');
4046			for (const key in data[0]) {
4047				const headerCell = document.createElement('th');
4048
4049				headerCell.textContent = key.replace(/_/g," ");
4050				headerRow.appendChild(headerCell);
4051			}
4052			table.appendChild(headerRow);	
4053		}
4054
4055		// Create a data row for each object in the array
4056		for (const object of data) {
4057			const dataRow = document.createElement('tr');
4058			for (const key in object) {
4059				const dataCell = document.createElement('td');
4060				dataCell.textContent = object[key];
4061				dataRow.appendChild(dataCell);
4062			}
4063			table.appendChild(dataRow);
4064		}
4065		// Return the HTML table string
4066		return table.outerHTML;		
4067
4068	} catch (error) {
4069		return "";	
4070	}
4071}
4072  
4073function exportHTML2XLS(html,filename) {
4074	var downloadLink;
4075	var dataType = 'application/vnd.ms-excel';
4076	
4077	// Specify file name
4078	filename = filename?filename+'.xls':'excel_data.xls';
4079	
4080	// Create download link element
4081	downloadLink = document.createElement("a");
4082	
4083	document.body.appendChild(downloadLink);
4084	
4085	if (navigator.msSaveOrOpenBlob) {
4086		var blob = new Blob(['\ufeff', html], {
4087			type: dataType
4088		});
4089		navigator.msSaveOrOpenBlob( blob, filename);
4090	} else {
4091		// Create a link to the file
4092		downloadLink.href = 'data:' + dataType + ', ' + html;
4093	
4094		// Setting the file name
4095		downloadLink.download = filename;
4096		
4097		//triggering the function
4098		downloadLink.click();
4099	}
4100}
4101
4102var capturingSpeechInput = false;
4103function recordAudioInput(opts) {
4104	var $el = $("#"+opts.input);
4105	var $cont = $(opts.handler);
4106	if('webkitSpeechRecognition' in window) {
4107		var speechRecognizer = new webkitSpeechRecognition();
4108		speechRecognizer.continuous = true;
4109		speechRecognizer.interimResults = true;
4110		speechRecognizer.lang = 'en-US';
4111		speechRecognizer.start();
4112
4113		capturingSpeechInput = true;
4114
4115		$cont.find(".fa-microphone").css("color","#EA4335");
4116
4117		var finalTranscripts = $el.val();
4118
4119		speechRecognizer.onresult = function(event) {
4120			var interimTranscripts = '';
4121			for(var i = event.resultIndex; i < event.results.length; i++){
4122				var transcript = event.results[i][0].transcript;
4123				//transcript.replace("\n", "<br>");
4124				if(event.results[i].isFinal) {
4125					finalTranscripts += transcript;
4126				}else{
4127					interimTranscripts += transcript;
4128				}
4129			}
4130			$el.val(finalTranscripts + interimTranscripts);
4131		};
4132		speechRecognizer.onend = () => {
4133			$cont.find(".fa-microphone").css("color","");
4134		};
4135		speechRecognizer.onerror = function (event) {
4136
4137		};
4138	}	
4139}
4140
4141function xmEnhanceText(type,obj) {    
4142	var $el = $(obj).parent().closest(".xm-bot-text").parent();
4143	var $newEl = $el.clone(false);
4144	$newEl.find(".xm-bot-text").remove();
4145
4146	var user_prompt = "Can you summarize the main points of ";
4147	var prompt_title = "Summary";
4148
4149	switch(type) {
4150		case "summarize":
4151			user_prompt = "Can you summarize the main points of ";
4152			prompt_title = "Summary";
4153			break;
4154		case "themes":
4155			user_prompt = "What are the key themes or topics discussed in ";
4156			prompt_title = "Key Themes";
4157			break;
4158		case "sentiment":
4159			user_prompt = "Could you provide a sentiment analysis of ";
4160			prompt_title = "Sentiment Analysis";
4161			break;
4162
4163		case "entities":
4164			user_prompt = "What are the most frequently mentioned entities in ";
4165			prompt_title = "Entities";
4166			break;
4167
4168		case "keywords":
4169			user_prompt = "Can you generate a list of keywords related to ";
4170			prompt_title = "Keywords";
4171			break;
4172
4173		case "custom":
4174			user_prompt = window.prompt("Enter your instructions in the Prompt below");
4175			prompt_title = user_prompt;
4176			break;
4177				
4178		default:
4179			user_prompt = "Can you summarize the main points of ";
4180			prompt_title = "Summary";
4181	}
4182
4183	
4184	var msgId = "Msg"+type+$el.attr("id");
4185
4186	if (type === "custom") {
4187		msgId =  "Msg"+Date.now().toString(36) + Math.random().toString(36).substr(2);
4188	}
4189
4190	if (user_prompt === null) {
4191		return;
4192	}
4193
4194    if ($("#"+msgId).length == 0) {
4195
4196        xmShowToastMessage({"heading":"","text":"Enhancing Text..."});
4197
4198        $('<div/>').attr("id",msgId).addClass('panel panel-info').html(`<div class="panel-heading">${prompt_title}</div><div id="${msgId}Body" class="panel-body xmLoading" style="">&nbsp;</div>`).insertAfter($el);
4199		$.post("/lib/com/ai/bot.cfc?returnFormat=JSON&method=AIEnhanceText",{input:stripHtml($newEl.html()),user_prompt:user_prompt},function(data){
4200			xmHideToastMessage();
4201			var $bodyObj = $("#"+msgId+"Body");
4202			var html = (data.response && data.response.length > 0) ? marked.parse(data.response) : data.response;
4203			$bodyObj.html(html).removeClass("xmLoading").find("a:not([target])").attr("target","_blank");
4204		},"JSON");
4205    
4206    }
4207}
4208
4209function TTS(obj) {
4210	var postData = new FormData();
4211	postData.append("return_path",true);
4212	for (var x in obj) {
4213		postData.append(x,obj[x]);
4214	}
4215	fetch('/lib/com/bot/tts.cfm', {
4216		method: 'POST',
4217		body: postData
4218	})
4219	.then(response => response.json())
4220	.then(response => {
4221		var a = new Audio(response.path);
4222		a.addEventListener("canplaythrough", (event) => {
4223			/* the audio is now playable; play it if permissions allow */
4224			a.play();
4225		});
4226	})
4227}
4228
4229function stopRemoteThread(channel) {
4230	$.getJSON("/lib/cfc/util/thread.cfc?returnFormat=JSON&method=Stop&channel="+channel,function(data) {
4231		;
4232	});
4233}
4234
4235var xmToasterBot, xmToasterBots = {};
4236function xmShowToastMessage(obj) {
4237	if (!obj.hasOwnProperty("stack") || !obj.stack) xmHideToastMessage();
4238	const toastConfig = {
4239		text: obj.text || "",
4240		position: obj.position || "bottom-left",
4241		heading: obj.heading || "",
4242		bgColor: obj.bgcolor || "#4285F4",
4243		textColor:obj.color || "#fff",
4244		allowToastClose:obj.hasOwnProperty("allow_close") ? obj.allow_close : true,
4245		hideAfter:obj.hasOwnProperty("timeout") ? obj.timeout : false,
4246		stack: obj.hasOwnProperty("stack") ? obj.stack : false
4247	};
4248	let thisToasterBot;
4249	if (obj.hasOwnProperty("id")) {
4250		if (xmToasterBots.hasOwnProperty(obj.id)) {
4251			xmToasterBots[obj.id].update(toastConfig);
4252		} else {
4253			thisToasterBot = $.toast(toastConfig);
4254			xmToasterBots[obj.id] = thisToasterBot;
4255		}
4256	} else {
4257		thisToasterBot = $.toast(toastConfig);
4258		xmToasterBot = thisToasterBot;
4259	}
4260}
4261
4262function xmHideToastMessage() {
4263	console.log("Hiding toast");
4264	if (xmToasterBot) xmToasterBot.reset();
4265}
4266
4267function displayOfflineFormPostsPrompt() {
4268	
4269	//retryOfflineFormPosts()
4270}
4271
4272async function retryOfflineFormPosts() {
4273	try {
4274		var offlineForms = localStorage.getItem("offlineforms") ? JSON.parse(localStorage.getItem("offlineforms")) : {};
4275		var offlineLoggedInForms = localStorage.getItem("offlineLoggedInForms") ? JSON.parse(localStorage.getItem("offlineLoggedInForms")) : {};
4276		
4277		if (navigator.onLine) {
4278			localStorage.setItem("offlineforms",JSON.stringify({}));
4279			for (o in offlineForms) {
4280				let formData = new FormData();
4281				formData.append("xm_return","json");
4282				formData.append("xm_retry_offline",true);
4283				for (d in offlineForms[o].data) {
4284					formData.append(d, offlineForms[o].data[d]);
4285				}
4286				xmShowToastMessage({
4287					id:"offlineForms",
4288					text: "Submitting offline form data.",
4289					position: "bottom-left",
4290					icon: "", 
4291					heading:"",
4292					bgcolor: "#EA4335",
4293					color:"#fff",
4294					timeout:5000,
4295					stack: 5
4296				});
4297				await fetch(offlineForms[o].path, {
4298					method: "POST",
4299					body: formData
4300				});
4301			}
4302
4303			if (window.hasOwnProperty("isLoggedIn") && window.isLoggedIn) {
4304				localStorage.setItem("offlineLoggedInForms",JSON.stringify({}));
4305				for (o in offlineLoggedInForms) {
4306					let formData = new FormData();
4307					formData.append("xm_return","json");
4308					formData.append("xm_retry_offline",true);
4309					for (d in offlineLoggedInForms[o].data) {
4310						formData.append(d, offlineLoggedInForms[o].data[d]);
4311					}
4312					xmShowToastMessage({
4313						id:"offlineLoggedInForms",
4314						text: "Submitting offline form data.",
4315						position: "bottom-left",
4316						icon: "", 
4317						heading:"",
4318						bgcolor: "#EA4335",
4319						color:"#fff",
4320						timeout:5000,
4321						stack: 5
4322					});
4323					await fetch(offlineLoggedInForms[o].path, {
4324						method: "POST",
4325						body: formData
4326					});
4327				}
4328			}
4329
4330		}
4331	} catch (error) {		
4332	}
4333}
4334
4335var onlineToast;
4336window.addEventListener("offline", function(event){
4337	$(".status-circle").attr("title","Offline").css("background-color","#EA4335");
4338	$(".avatar").removeClass("online").addClass("offline");
4339	xmShowToastMessage({
4340		id:"offlineToast",
4341		text: "Network Offline",
4342		position: "bottom-left",
4343		icon: "", 
4344		heading:"",
4345		bgcolor: "#EA4335",
4346		color:"#fff",
4347		allow_close:false,
4348		timeout:false,
4349		stack: 5
4350	});
4351	console.log("You lost connection.");
4352});
4353window.addEventListener("online", function(event){
4354	$(".status-circle").attr("title","Online").css("background-color","#34A853");
4355	$(".avatar").removeClass("offline").addClass("online");
4356	
4357	try {
4358		if (xmToasterBots.hasOwnProperty("offlineToast") && xmToasterBots["offlineToast"]) xmToasterBots["offlineToast"].reset();
4359	} catch (error) {
4360		
4361	}
4362
4363	xmShowToastMessage({
4364		id:"onlineToast",
4365		text: "Connected to the Internet",
4366		position: "bottom-left",
4367		icon: "", 
4368		heading:"",
4369		bgcolor: "#34A853",
4370		allow_close:false,
4371		color:"#fff",
4372		timeout:3000,
4373		stack: 5
4374	});
4375	displayOfflineFormPostsPrompt();
4376});
4377
4378(function ($) {
4379	$.fn.serializeFormJSON = function (delimiter="") {
4380		var o = {};
4381		var a = this.serializeArray();
4382		$.each(a, function () {
4383			if (o.hasOwnProperty(this.name) && delimiter !== "") {
4384				o[this.name] = ListAppend(o[this.name],this.value,delimiter);
4385			} else if (o.hasOwnProperty(this.name)) {
4386				if (!o[this.name].push) {
4387					o[this.name] = [o[this.name]];
4388				}
4389				o[this.name].push(this.value || '');
4390			} else {
4391				o[this.name] = this.value || '';
4392			}
4393		});
4394		return o;
4395	};
4396})(jQuery);
4397
4398var UDF = {};
4399for (const property in window) {
4400	if (typeof window[property] === 'function') {
4401		UDF[property] = window[property];
4402	}
4403}
4404UDF.DateAdd = function(interval,num,date,format="MM/DD/YYYY") {
4405	return (date && date !== null && date !== "") ? moment(date).add(num,interval).format(format) : "";
4406};
4407UDF.DateFormat = function(value,format="MM/DD/YYYY") {
4408	return (value && value !== null && value !== "") ? moment(value).format(format) : "";
4409}
4410UDF.DateDiff = function(interval,date1,date2) {
4411	if (date1.length > 0 && date2.length > 0 && moment.isDate(new Date(date1)) && moment.isDate(new Date(date2))) {
4412		return dateDiff(interval, new Date(date1), new Date(date2));
4413	} else {
4414		return "";
4415	}	
4416}
4417
4418function xmCallFX(fx,params={}) {
4419	var path = fx.indexOf("http") > -1 || fx.substring(0,1) === "/" ? fx : "/lib/com/xm/fx.cfc?returnFormat=JSON&method=" + fx;
4420
4421	if (path.indexOf("?") === -1) path += "?xmfoo=bar";
4422	for (var p in params) {
4423		path += "&" + p + "=" + encodeURIComponent(params[p]);
4424	}
4425	return xmGetJSON(path);
4426}
4427
4428function xmGetJSON(url,method="GET",params=null) {
4429	const request = new XMLHttpRequest();
4430	request.open(method, url, false); 
4431	request.setRequestHeader("X-Webauthor-Request", location.href);
4432	if (method == "POST" && params !== null) {
4433		request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
4434	}
4435	request.send(params);
4436
4437	if (request.status === 200) {
4438		return JSON.parse(request.responseText);
4439	} else {
4440		return null;
4441	}
4442}
4443
4444function xmGetHTML(url) {
4445	const request = new XMLHttpRequest();
4446	request.open("GET", url, false); 
4447	request.setRequestHeader("X-Webauthor-Request", location.href);
4448	request.send(null);
4449	return (request.status === 200) ? request.responseText : "";
4450}
4451
4452async function xmFetch(path, payload = '') {
4453
4454    const headers = {
4455        'X-Requested-With': 'XMLHttpRequest',
4456    };
4457    if (payload && !(payload instanceof FormData)) {
4458        headers['Content-Type'] = 'application/json';
4459        payload = JSON.stringify(payload);
4460    }
4461    const res = await fetch(path, {
4462        method: 'POST',
4463        credentials: 'same-origin',
4464        headers: headers,
4465        body: payload,
4466    });
4467    if (res.status === 200) {
4468        // Server authentication succeeded
4469        return res.json();
4470    } else {
4471        // Server authentication failed
4472        const result = await res.json();
4473        throw new Error(result.error);
4474    }
4475};
4476
4477var xmTabVisible = true;
4478document.addEventListener("visibilitychange", function() {
4479	xmTabVisible = !document.hidden;
4480	if (!document.hidden && typeof isLoggedIn !== 'undefined' && isLoggedIn && typeof xmCheckSessionTime === 'function') {
4481		if (typeof xmCloseSessionNotification === 'function') xmCloseSessionNotification();
4482		xmCheckSessionTime();
4483	}
4484});
4485
4486//MR: 6/10/2025 - Add New Error Logging - we will store JS errors in form submissions now
4487// Global array to store JavaScript errors
4488var xmJSErrors = [];
4489
4490// Global error handler to capture all JavaScript errors
4491$(window).on('error', function(event) {
4492    var originalEvent = event.originalEvent || event;
4493    
4494    var errorInfo = {
4495        message: originalEvent.message || 'Unknown error',
4496        filename: originalEvent.filename || 'Unknown file',
4497        lineno: originalEvent.lineno || 'Unknown line',
4498        colno: originalEvent.colno || 'Unknown column',
4499        timestamp: new Date().toISOString(),
4500        stack: originalEvent.error && originalEvent.error.stack ? originalEvent.error.stack : 'No stack trace available'
4501    };
4502    
4503    // Add error to global array
4504    xmJSErrors.push(errorInfo);
4505});
4506
4507// Handle unhandled promise rejections (optional but recommended)
4508$(window).on('unhandledrejection', function(event) {
4509    var originalEvent = event.originalEvent || event;
4510    
4511    var errorInfo = {
4512        message: 'Unhandled Promise Rejection: ' + (originalEvent.reason || 'Unknown reason'),
4513        filename: 'Promise',
4514        lineno: 'N/A',
4515        colno: 'N/A',
4516        timestamp: new Date().toISOString(),
4517        stack: originalEvent.reason && originalEvent.reason.stack ? originalEvent.reason.stack : 'No stack trace available'
4518    };
4519    
4520    // Add error to global array
4521    xmJSErrors.push(errorInfo);
4522});
4523
4524// Add event listeners to all forms to populate error field on submit
4525$(document).ready(function() {
4526    // Use event delegation to handle dynamically added forms as well
4527    $(document).on('submit', 'form', function(event) {
4528        var $this = $(this);
4529
4530		var method = ($this.attr('method') || 'GET').toLowerCase();
4531		if (method !== 'post') {
4532			return; // do nothing for non-POST forms
4533		}
4534
4535        var $errorField = $this.find('input[name="xm_js_errors"]');
4536        
4537        if ($errorField.length > 0) {
4538            // Convert errors array to JSON string
4539            var errorsString = JSON.stringify(xmJSErrors);
4540            $errorField.val(errorsString);
4541        }
4542    });
4543});
4544
4545// Optional: jQuery-specific error handling for AJAX requests
4546$(document).ajaxError(function(event, xhr, settings, thrownError) {
4547    var errorInfo = {
4548        message: 'AJAX Error: ' + thrownError + ' (Status: ' + xhr.status + ')',
4549        filename: settings.url || 'AJAX Request',
4550        lineno: 'N/A',
4551        colno: 'N/A',
4552        timestamp: new Date().toISOString(),
4553        stack: 'AJAX error - Status: ' + xhr.status + ', Response: ' + xhr.responseText
4554    };    
4555    xmJSErrors.push(errorInfo);
4556});
4557//End New Error Loggign
4558
4559// Function to dispatch the custom event
4560function dispatchWindowLoaded() {
4561    window.dispatchEvent(new CustomEvent('window:loaded'));
4562}
4563
4564// Fire on initial DOM load
4565if (document.readyState === 'loading') {
4566    document.addEventListener('DOMContentLoaded', dispatchWindowLoaded);
4567} else {
4568    // DOM already loaded
4569    dispatchWindowLoaded();
4570}
4571
4572$(document).ready(function() {
4573	// Fire after HTMX loads content and settles
4574	document.body.addEventListener('htmx:afterSettle', function(evt) {
4575		// Wait a tick to ensure external scripts have loaded
4576		setTimeout(dispatchWindowLoaded, 0);
4577	});
4578
4579	// Optional: Also listen for when external content finishes loading
4580	document.body.addEventListener('htmx:load', function(evt) {
4581		setTimeout(dispatchWindowLoaded, 0);
4582	});
4583});
4584
4585addEvent(window,"load",function(e){globalXMOnload();});
4586
4587/**
4588 * xmStickyBelow - Generic utility to make any element sticky below other sticky elements
4589 *
4590 * Usage:
4591 *   // Basic usage - make element sticky below all .xmStickyTop elements
4592 *   xmStickyBelow('#myElement');
4593 *
4594 *   // With jQuery object
4595 *   xmStickyBelow($('#myElement'));
4596 *
4597 *   // With options
4598 *   xmStickyBelow('#myElement', {
4599 *       stickySelector: '.xmStickyTop',  // Selector for elements to stack below
4600 *       initialDelay: 2000,               // Delay before enabling (ms)
4601 *       zIndex: 1020,                     // z-index when sticky
4602 *       stuckClass: 'is-stuck',           // Class added when element is stuck
4603 *       stickyClass: 'xm-sticky-below',   // Class added when sticky is enabled
4604 *       onStuck: function($el) {},        // Callback when element becomes stuck
4605 *       onUnstuck: function($el) {},      // Callback when element becomes unstuck
4606 *       onEnabled: function($el) {}       // Callback when sticky is enabled
4607 *   });
4608 *
4609 *   // Returns controller object
4610 *   var sticky = xmStickyBelow('#myElement');
4611 *   sticky.disable();  // Disable sticky
4612 *   sticky.enable();   // Re-enable sticky
4613 *   sticky.update();   // Force recalculate offset
4614 *   sticky.destroy();  // Completely remove sticky behavior
4615 */
4616(function(window, $) {
4617    'use strict';
4618
4619    var instances = [];
4620    var instanceId = 0;
4621
4622    function xmStickyBelow(element, options) {
4623        var $el = (element instanceof $) ? element : $(element);
4624
4625        if (!$el.length) {
4626            console.warn('xmStickyBelow: Element not found:', element);
4627            return null;
4628        }
4629
4630        var settings = $.extend({
4631            stickySelector: '.xmStickyTop',
4632            initialDelay: 2000,
4633            maxRetries: 10,
4634            retryDelay: 100,
4635            zIndex: 5,
4636            stuckClass: 'is-stuck',
4637            stickyClass: 'xm-sticky-below',
4638            onStuck: null,
4639            onUnstuck: null,
4640            onEnabled: null,
4641            onDisabled: null
4642        }, options);
4643
4644        var id = ++instanceId;
4645        var namespace = '.xmStickyBelow' + id;
4646        var resizeObserver = null;
4647        var enabled = false;
4648        var isStuck = false;
4649        var retryCount = 0;
4650
4651        // Calculate total offset from all sticky elements
4652        function calculateStickyOffset() {
4653            var $stickyElements = $(settings.stickySelector).not($el);
4654            var totalOffset = 0;
4655
4656            $stickyElements.each(function() {
4657                var $stickyEl = $(this);
4658                if ($stickyEl.is(':visible')) {
4659                    var position = $stickyEl.css('position');
4660                    if (position === 'fixed' || position === 'sticky') {
4661                        var elTop = parseFloat($stickyEl.css('top')) || 0;
4662                        var elHeight = $stickyEl.outerHeight() || 0;
4663                        var elBottom = elTop + elHeight;
4664                        if (elBottom > totalOffset) {
4665                            totalOffset = elBottom;
4666                        }
4667                    }
4668                }
4669            });
4670
4671            return totalOffset;
4672        }
4673
4674        // Update the top offset
4675        function updateStickyTop() {
4676            if (!enabled) return;
4677            var offset = calculateStickyOffset();
4678            $el.css('top', offset + 'px');
4679        }
4680
4681        // Check if element is stuck
4682        function checkStuck() {
4683            if (!enabled) return;
4684
4685            var rect = $el[0].getBoundingClientRect();
4686            var topValue = parseFloat($el.css('top')) || 0;
4687            var nowStuck = rect.top <= topValue + 2;
4688
4689            if (nowStuck !== isStuck) {
4690                isStuck = nowStuck;
4691                $el.toggleClass(settings.stuckClass, isStuck);
4692
4693                if (isStuck && typeof settings.onStuck === 'function') {
4694                    settings.onStuck($el);
4695                } else if (!isStuck && typeof settings.onUnstuck === 'function') {
4696                    settings.onUnstuck($el);
4697                }
4698            }
4699        }
4700
4701        // Initialize sticky behavior
4702        function initSticky() {
4703            var $stickyElements = $(settings.stickySelector).not($el);
4704
4705            // Retry if no sticky elements found yet
4706            if (!$stickyElements.length) {
4707                retryCount++;
4708                if (retryCount < settings.maxRetries) {
4709                    setTimeout(initSticky, settings.retryDelay);
4710                    return;
4711                }
4712            }
4713
4714            // Apply sticky styles
4715            $el.addClass(settings.stickyClass);
4716            $el.css({
4717                'position': 'sticky',
4718                'z-index': settings.zIndex
4719            });
4720
4721            enabled = true;
4722
4723            // Initial setup
4724            updateStickyTop();
4725            checkStuck();
4726
4727            // Bind events
4728            $(window).on('scroll' + namespace, checkStuck);
4729            $(window).on('resize' + namespace, function() {
4730                updateStickyTop();
4731                checkStuck();
4732            });
4733
4734            // Watch for sticky elements size changes
4735            if (window.ResizeObserver && $stickyElements.length) {
4736                resizeObserver = new ResizeObserver(updateStickyTop);
4737                $stickyElements.each(function() {
4738                    resizeObserver.observe(this);
4739                });
4740            }
4741
4742            // Trigger callback
4743            if (typeof settings.onEnabled === 'function') {
4744                settings.onEnabled($el);
4745            }
4746
4747            $(document).trigger('xmStickyBelow:enabled', { element: $el, id: id });
4748        }
4749
4750        // Public methods
4751        var controller = {
4752            id: id,
4753            element: $el,
4754
4755            enable: function() {
4756                if (enabled) return this;
4757                retryCount = 0;
4758                initSticky();
4759                return this;
4760            },
4761
4762            disable: function() {
4763                if (!enabled) return this;
4764
4765                enabled = false;
4766                isStuck = false;
4767                $el.removeClass(settings.stickyClass + ' ' + settings.stuckClass);
4768                $el.css({
4769                    'position': '',
4770                    'top': '',
4771                    'z-index': ''
4772                });
4773
4774                $(window).off(namespace);
4775
4776                if (resizeObserver) {
4777                    resizeObserver.disconnect();
4778                    resizeObserver = null;
4779                }
4780
4781                if (typeof settings.onDisabled === 'function') {
4782                    settings.onDisabled($el);
4783                }
4784
4785                $(document).trigger('xmStickyBelow:disabled', { element: $el, id: id });
4786                return this;
4787            },
4788
4789            update: function() {
4790                updateStickyTop();
4791                checkStuck();
4792                return this;
4793            },
4794
4795            destroy: function() {
4796                this.disable();
4797                // Remove from instances array
4798                instances = instances.filter(function(inst) {
4799                    return inst.id !== id;
4800                });
4801                return null;
4802            },
4803
4804            isEnabled: function() {
4805                return enabled;
4806            },
4807
4808            isStuck: function() {
4809                return isStuck;
4810            }
4811        };
4812
4813        // Store instance
4814        instances.push(controller);
4815
4816        // Start initialization after delay
4817        setTimeout(initSticky, settings.initialDelay);
4818
4819        return controller;
4820    }
4821
4822    // Static methods
4823    xmStickyBelow.getInstances = function() {
4824        return instances;
4825    };
4826
4827    xmStickyBelow.disableAll = function() {
4828        instances.forEach(function(inst) {
4829            inst.disable();
4830        });
4831    };
4832
4833    xmStickyBelow.enableAll = function() {
4834        instances.forEach(function(inst) {
4835            inst.enable();
4836        });
4837    };
4838
4839    xmStickyBelow.updateAll = function() {
4840        instances.forEach(function(inst) {
4841            inst.update();
4842        });
4843    };
4844
4845    xmStickyBelow.destroyAll = function() {
4846        instances.forEach(function(inst) {
4847            inst.destroy();
4848        });
4849        instances = [];
4850    };
4851
4852    // Expose globally
4853    window.xmStickyBelow = xmStickyBelow;
4854
4855})(window, jQuery);

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.