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;"> </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 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}"> </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=""> </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.