{"version":3,"file":"core.js","names":["window","broadcasts","static","toString","version","eventBroadcaster","windowBroadcasts","constructor","this","Map","broadcast","detail","isWwindowMessage","message","console","log","list","subscribers","get","i","exceptions","length","push","subscriber","handleBroadcast","error","exception","isDebugMode","stack","unsubscribe","forEach","hasSubscribers","subscribe","map","startsWith","has","messageArray","set","Error","unsubscriber","splice","delete","messages","TIMEOUT_3_MINUTES","TIMEOUT_6_MINUTES","RETRY_05_SECONDS","RETRY_15_SECONDS","RETRY_50_SECONDS","actions","parseMessage","action","actionType","nextAction","reloadChart","params","iFrame","application","getIframeWindow","setChartData","chartData","chartId","reloadPage","url","location","href","indexOf","alert","showToast","type","toLowerCase","core","notify","setupSignalR","lockResolver","navigator","locks","request","promise","Promise","res","mode","connection","signalR","HubConnectionBuilder","withUrl","skipNegotiation","transport","HttpTransportType","WebSockets","configureLogging","LogLevel","Critical","withAutomaticReconnect","build","keepAliveIntervalInMilliseconds","serverTimeoutInMilliseconds","onreconnecting","onreconnected","connectionId","onclose","start","shift","messageParams","applicationhub","APPLICATION_READY","baseUrl","on","validator","bubble","bubbleTimer","field","hideTimeout","parent","validateOnBlur","validatorParent","closest","querySelector","setAttribute","addEventListener","e","preventDefault","capture","hasAttribute","validateHandler","validateFocusHandler","isFormValidation","hideError","validate","event","hasBubble","group","hideFieldError","items","document","querySelectorAll","name","split","item","setInvalid","classList","add","showError","innerHTML","remove","createElement","append","clearTimeout","setTimeout","once","replace","required","isValid","value","forceValidate","ownerWindow","ownerDocument","defaultView","setCustomValidity","groupItems","invalidItemCount","filter","validation","validateGroup","element","validity","valid","invalidProperties","getInvalidProperties","children","validateAdditional","validateReadOnly","validationMethod","dataset","customvalidator","errorMessage","getMessage","afterValidate","aftervalidate","targetWindow","validityState","key","parentNode","removeChild","removeAttribute","removeEventListener","invalidElement","invalidContentElement","toast","TopLeft","TopCenter","TopRight","CenterLeft","CenterCenter","CenterRight","BottomLeft","BottomCenter","BottomRight","toastPositionIndex","toastPosition","svgs","success","warn","info","defaultOptions","callback","undefined","closable","duration","focusable","offsetBlock","offsetInline","position","purify","title","appendContainer","hide","toastCard","style","setProperty","options","yPos","offsetHeight","show","toastContainer","className","ri","row","ci","col","appendChild","documentElement","autoHide","isFocus","setCardBindEvents","setCardContent","textGroupDiv","setCardIntroAnimation","toastType","getElementsByClassName","toastMessage","DOMPurify","sanitize","USE_PROFILES","html","ADD_ATTR","closeElem","close","insetBlockStart","getBoundingClientRect","top","insetInlineStart","left","confirmToast","super","handleResponse","trueLabel","resources","base","confirm","falseLabel","cancel","confirmButtonContainer","trueButton","falseButton","onclick","dating","expressions","formattingTokens","MINYEAR","MAXYEAR","match2","match1to2","matchWord","addInput","property","input","meridiemMatch","isLowerCase","zoneExpressions","zone","offset","offsetFromString","A","afternoon","a","S","milliseconds","SS","SSS","s","ss","m","mm","H","h","HH","hh","d","dd","M","MM","y","yy","year","parseTwoDigitYear","yyyy","Z","ZZ","formatDate","date","format","isNaN","Date","parse","dateObject","formatString","padZone","instance","negMinutes","getTimezoneOffset","minutes","Math","abs","hourOffset","floor","minuteOffset","String","padStart","meridiemFunc","hour","minute","isLowercase","meridiem","getFullYear","month","getMonth","day","getDate","hours","getHours","getMinutes","seconds","getSeconds","getMilliseconds","matches","match","slice","$1","formatDateTime","dateString","parsedDate","join","getDateFromString","myDate","initialTimeSeparatorPosition","firstDashPosition","secondDashPosition","spacePosition","hasTime","strDay","substring","strMonth","strYear","timeString","timeSeparatorPosition","strHour","strMinute","parseInt","setFullYear","setHours","isDateStr","trim","validationMessages","invalidDate","isDateOnly","isTimeOnly","isBoth","dateAndTime","hasBoth","hasDate","time","invalidTime","dateParts","dayString","monthString","yearString","Number","daysInMonth","daysInFebruary","isWithin","invalidDay","invalidMonth","invalidYear","timeParts","hoursString","minutesString","parseDateString","datestring","makeParser","parser","now","parsedDateString","correctHours","datetime","number","min","max","array","token","parseTo","regex","part","exec","call","parts","emStyle","rem","BREAKPOINT","CALLBACK_TIMEOUT","CLEANUP_URL_TIMEOUT","getEmPixels","addValidators","container","form","ADD_VALIDATORS","currentWidth","innerWidth","controllerReadyPromise","resolve","resolveWithRegistration","serviceWorker","getRegistration","then","registration","controller","DOMContentLoaded","fn","readyState","downloadFile","data","isJson","xhr","XMLHttpRequest","open","responseType","onload","status","blob","response","filename","disposition","getResponseHeader","msSaveBlob","URL","webkitURL","downloadUrl","createObjectURL","download","body","click","revokeObjectURL","setRequestHeader","send","downloadFileBlob","leanformsNext","isIosPWA","fileUrl","target","getAttribute","textContent","fetch","catch","markNetworkError","blobUrl","tempLink","display","config","Object","create","cache","credentials","method","redirect","referrer","headers","localeCompare","sensitivity","antiforgeryToken","antiforgeryHeader","getEmbedUrl","pageUrlParameter","decodeURIComponent","getQueryParameter","testElement","cssText","clientWidth","getFormDataWithDisabled","includeDisabled","formData","FormData","checked","option","selected","parameter","reference","Proxy","parseUrl","searchParams","prop","getPageQueryParameter","hideNotification","notification","htmlDecode","DOMParser","parseFromString","htmlEncode","htmlstring","el","isElementInView","rect","bottom","clientHeight","right","isVisible","offsetWidth","getClientRects","getComputedStyle","visibility","parseDimensionValue","parsed","parseFloat","readCssVar","varName","getPropertyValue","removeQueryParameter","setQueryParameter","sanitizeHtml","allowedTags","sanitized","ALLOWED_TAGS","urlObject","path","urlSearchParams","outputParams","entries","encodeURIComponent","setEmbedUrl","pushState","parentUrl","historyFunction","history","supportsOffline","every","feature","toggleDisplayState","xmlDecode","str","xmlEncode","getPage","viewIndex","lastIndexOf","URLSearchParams","index","isEmptyComplexurl","hideTimeOut","minYear","maxYear","validateTypes","onFormSubmitHandler","bind","onFormResetHandler","elements","formelement","addElementValidation","clear","validateForm","onInvalidHandler","force","elementType","getElementType","test","validatorparent","firstOnly","first","getFirst","createMessage","validationProperty","validationproperty","isNumeric","numericText","positiveIntOnly","resetValidity","all","nodeName","parentElement","scrollIntoView","scrollOffset","scrollTo","scrollTop","behavior","setErrorMessage","validationmessage","msg","validatorParentElement","validateFormat","validateDateTime","dateformat","files","validateAccept","validateFileSize","acceptedArray","commaSplit","accept","file","extension","substr","includes","invalidaccept","invalidAccept","validateCompare","compareTo","compareto","compareToElement","comparetomessage","invaliddatemessage","allowedSize","size","validateAll","customValidation","validateElement","validateDate","validateEmail","validateNumber","validateMinMax","itemsChecked","minimumchecked","valuemissingmessage","selectOne","selectMinimum","invalidnumbermessage","invalidNumber","decimalsRequired","decimalValue","invaliddecimalsmessage","invalidDecimals","minRange","maxRange","validateRangeState","val","minOk","maxOk","invalidrangemessage","invalidRange","invalidrangemaxmessage","invalidRangeMax","invalidrangeminmessage","invalidRangeMin","minRangeOk","maxRangeOk","rangeNotOk","rangeOk","Boolean","toggle","validateRequired","valuemissing","validateValueCompare","leftValue","operator","rightValue","parsedLeftValue","parsedRightValue","isDateValue","string","validationMessage","typeId","invalidemailmessage","invalidEmail","minChars","maxChars","characterLength","invalidmincharsmessage","invalidMinChars","invalidmaxcharsmessage","invalidMaxChars"],"sources":["../../../scripts/leanforms/broadcasts.js","../../../scripts/leanforms/event-broadcaster.js","../../../scripts/messaging/messages.js","../../../scripts/messaging/applicationHub.js","../../../scripts/leanforms/components/validator.js","../../../scripts/leanforms/components/toast.js","../../../scripts/leanforms/components/confirmToast.js","../../../scripts/leanforms/dating.js","../../../scripts/leanforms/core.js","../../../scripts/leanforms/validation.js"],"sourcesContent":["// #region: Class definition *********************************************************************/\r\n/**\r\n * Global broadcast messages @see {eventBroadcaster}.\r\n */\r\nwindow.broadcasts = class {\r\n\r\n // #region: Static Data **********************************************************************/\r\n // Custom events.\r\n static ADD_VALIDATORS = \"addValidators\";\r\n static APPLICATION_READY = \"applicationReady\";\r\n static LOCK_SCREEN = \"lockScreen\";\r\n static RIGHTS_LOADED = \"rightsLoaded\";\r\n\r\n // Plugin events.\r\n static FORM_SAVED_DATA_LOADED = \"formSavedDataLoaded\";\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {String} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[broadcasts]\";\r\n }\r\n\r\n static toString() {\r\n return \"[broadcasts]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {String} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n};\r\n// #endregion ************************************************************************************/\r\n","// #region: Class definition *********************************************************************/\r\n/**\r\n * Broadcast predefined events for subscribers, @see {broadcasts}.\r\n */\r\nwindow.eventBroadcaster = new class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #broadcasts;\r\n #windowBroadcasts;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n /** \r\n * Stores the broadcasts and its corresponding listeners/subscribers.\r\n * @type {Map}\r\n */\r\n this.#broadcasts = new Map();\r\n\r\n /**\r\n * Stores the window broadcasts and its corresponding listeners/subscribers.\r\n * We use a separate map as window events typically only fire once.\r\n * @type {Map}\r\n */\r\n this.#windowBroadcasts = new Map();\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {String} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[eventBroadcaster]\";\r\n }\r\n\r\n static toString() {\r\n return \"[eventBroadcaster]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {String} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Broadcast message to subscribers.\r\n * @param {Object} [detail=null] Optional and defaulting to null, of type any, that is an event-dependent value associated with the event.\r\n * @param {String} message The (predefined) message to broadcast @see {broadcasts}.\r\n * @param {Boolean} isWwindowMessage Optional and defaulting to false, of type boolean, that indicates if it is a window event.\r\n */\r\n broadcast({detail = null, isWwindowMessage = false, message}) {\r\n if(message == null) {\r\n console.log(\"Undefined broadcast.\");\r\n }\r\n else {\r\n const list = [];\r\n const subscribers = isWwindowMessage ? this.#windowBroadcasts.get(message) : this.#broadcasts.get(message);\r\n let i = 0;\r\n\r\n if(subscribers != null) {\r\n // This will store possible failed subscribers.\r\n const exceptions = [];\r\n\r\n // First collect in a temp list. This will prevent sudden unsubscribers from modifying the length of the list while we iterate.\r\n while(i < subscribers.length) {\r\n list.push(subscribers[i++]);\r\n }\r\n\r\n i = 0;\r\n while(i < list.length) {\r\n const subscriber = list[i];\r\n try {\r\n if (subscriber.handleBroadcast) {\r\n subscriber.handleBroadcast({ detail, message });\r\n }\r\n else if (typeof (subscriber) === \"function\") {\r\n subscriber({ detail, message });\r\n }\r\n else {\r\n console.error(`${subscriber.toString()} has no method 'handleBroadcast' or is not a Function.\\nBroadcast cannot be evaluated.`);\r\n }\r\n\r\n }\r\n catch (exception) {\r\n exceptions.push(subscriber);\r\n if(isDebugMode) {\r\n debugger;\r\n }\r\n\r\n console.error(`Exception in ${ subscriber.toString() } on broadcast ${ message }.\\n${ exception.message ?? exception }\\nStack:\\n${ exception.stack ?? \"\" }`);\r\n }\r\n\r\n // Window events should fire only once.\r\n if(isWwindowMessage) {\r\n eventBroadcaster.unsubscribe({message, subscriber});\r\n }\r\n\r\n i += 1;\r\n }\r\n\r\n // Brutally exclude subscribers that raised exceptions.\r\n exceptions.forEach((subscriber) => {\r\n eventBroadcaster.unsubscribe({message, subscriber});\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Message has subscribers?\r\n * @param {String} message The (predefined) message (@see {broadcasts}) to verify if it has a subscriber.\r\n * @returns {Boolean} True if any subscribers exist, false if not.\r\n */\r\n hasSubscribers({message}) {\r\n return this.#broadcasts.get(message)?.length > 0;\r\n }\r\n\r\n /**\r\n * Add subscription. Should implement method handleBroadcast.\r\n * @param {String} message The (predefined) message @see {broadcasts}.\r\n * @param {Element} subscriber The subscribing element/class/document/window.\r\n */\r\n subscribe({message, subscriber}) {\r\n if(message == null) {\r\n console.log(`Exception in '${ subscriber.toString() }' on broadcast '${ message }'`);\r\n\r\n return;\r\n }\r\n\r\n // Normal or window message?\r\n const map = message.startsWith(\"window\") ? this.#windowBroadcasts : this.#broadcasts;\r\n if(map.has(message)) {\r\n const messageArray = map.get(message);\r\n messageArray.push(subscriber);\r\n map.set(message, messageArray);\r\n }\r\n else {\r\n map.set(message, [subscriber]);\r\n }\r\n }\r\n\r\n /**\r\n * Remove subscription.\r\n * @param {String} message The (predefined) message @see {broadcasts}.\r\n * @param {Element} subscriber The element/class/document/window that should be removed from the listeners list.\r\n */\r\n unsubscribe({message, subscriber}) {\r\n if(message == null) {\r\n if (isDebugMode) {\r\n debugger;\r\n }\r\n\r\n throw new Error(`Undefined broadcast: ${ unsubscriber }`);\r\n }\r\n\r\n // Normal or window message?\r\n const isWindowMessage = message.startsWith(\"window\");\r\n const map = isWindowMessage ? this.#windowBroadcasts : this.#broadcasts;\r\n const subscribers = map.get(message);\r\n let i = 0;\r\n\r\n if(subscribers) {\r\n while(i < subscribers.length) {\r\n const unsubscriber = subscribers[i];\r\n if(subscriber === unsubscriber) {\r\n subscribers.splice(i, 1);\r\n break;\r\n }\r\n\r\n i++;\r\n }\r\n }\r\n\r\n // No more subscribers, then remove the item completely.\r\n if(subscribers == null || subscribers?.length === 0) {\r\n map.delete(message);\r\n }\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}();\r\n// #endregion ************************************************************************************/\r\n","// Region: Class definition *********************************************************************/\r\n/**\r\n * Parses messages sent from the server, through signalR.\r\n */\r\nwindow.messages = new class {\r\n\r\n // Region: Constructor **********************************************************************/\r\n constructor() {\r\n\r\n // Timeout/retry constants:\r\n this.TIMEOUT_3_MINUTES = 380000;\r\n this.TIMEOUT_6_MINUTES = 360000;\r\n\r\n this.RETRY_05_SECONDS = 5000;\r\n this.RETRY_15_SECONDS = 15000;\r\n this.RETRY_50_SECONDS = 50000;\r\n\r\n /**\r\n * List of actions waiting to be executed.\r\n * @type {Array}\r\n */\r\n this.actions = [];\r\n }\r\n // endregion ********************************************************************************/\r\n\r\n // region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[messages]\";\r\n }\r\n\r\n static toString() {\r\n return \"[messages]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // endregion ********************************************************************************/\r\n\r\n // region: Public Methods *******************************************************************/\r\n /**\r\n * Parses and translates the received message from the server to local 'actions', queues the message if needed.\r\n * @param {Object} message The message sent (from the server).\r\n */\r\n parseMessage(action) {\r\n if(action) {\r\n this.actions.push(action);\r\n if (isDebugMode) {\r\n // eslint-disable-next-line no-console -- This is only used when debug mode is enabled.\r\n console.log(`Action \"${ action.actionType }\" called.`);\r\n }\r\n\r\n this.nextAction();\r\n }\r\n }\r\n\r\n /**\r\n * Reloads a given chart (if still open).\r\n * @param {Object} params All data sent from the server.\r\n */\r\n reloadChart(params) {\r\n var iFrame = application.getIframeWindow();\r\n var hasChart = typeof iFrame.setChartData === 'function';\r\n\r\n if (!hasChart) {\r\n return;\r\n }\r\n\r\n const chartData = params.chartData;\r\n const chartId = params.chartId;\r\n\r\n iFrame.setChartData(chartData, chartId, 'signalr');\r\n }\r\n\r\n /**\r\n * Reloads a given page (if still open).\r\n * @param {Object} params All data sent from the server.\r\n */\r\n reloadPage(params) {\r\n const url = params.url;\r\n const activePage = application.getIframeWindow().location.href;\r\n if (activePage.indexOf(url) > -1) {\r\n // Refresh page...\r\n alert(`Refresh ${url}.`);\r\n }\r\n }\r\n\r\n /**\r\n * Shows a toast message.\r\n * @param {Object} params All data sent from the server.\r\n */\r\n showToast(params) {\r\n const type = params.type.toLowerCase();\r\n const message = params.message;\r\n\r\n core.notify(type, message, params);\r\n }\r\n\r\n /**\r\n * Initialize & setup SignalR.\r\n */\r\n // eslint-disable-next-line class-methods-use-this -- Cannot use/access static methods within an anonymous class expression.\r\n async setupSignalR(url) {\r\n\r\n // Keep curent tab awake and avoid an unexpected connection closure.\r\n let lockResolver;\r\n if (window.navigator?.locks?.request) {\r\n const promise = new Promise((res) => {\r\n lockResolver = res;\r\n });\r\n\r\n window.navigator.locks.request(\"LeanFormsSleepLock\", { mode: \"shared\" }, () => {\r\n return promise;\r\n });\r\n }\r\n\r\n // Create the signalR connection.\r\n const connection = new signalR.HubConnectionBuilder().\r\n withUrl(url, {\r\n skipNegotiation: true,\r\n transport: signalR.HttpTransportType.WebSockets\r\n })\r\n\r\n // SignalR.LogLevel. Trace is really chatty.\r\n .configureLogging(isDebugMode ? signalR.LogLevel.Error : signalR.LogLevel.Critical)\r\n\r\n // Re-tries after 5s, 15s and 50s. The last null parameter tells SignalR to stop re-trying.\r\n .withAutomaticReconnect([this.RETRY_05_SECONDS, this.RETRY_15_SECONDS, this.RETRY_50_SECONDS, null])\r\n .build();\r\n\r\n // Time-outs (match server side).\r\n connection.keepAliveIntervalInMilliseconds = this.TIMEOUT_3_MINUTES;\r\n connection.serverTimeoutInMilliseconds = this.TIMEOUT_6_MINUTES;\r\n\r\n // Connection (status) events.\r\n connection.onreconnecting(error => {\r\n if (isDebugMode) {\r\n // eslint-disable-next-line no-console -- This is only used when debug mode is enabled.\r\n if (error) {\r\n console.log(`Connection lost due to error: ${error}. Reconnecting.`);\r\n }\r\n else {\r\n console.log(`Connection lost. Reconnecting.`);\r\n }\r\n }\r\n });\r\n\r\n // The connectionId parameter is undefined if the HubConnection is configured to skip negotiation (that we set in the HubConnectionBuilder above).\r\n connection.onreconnected(connectionId => {\r\n if (isDebugMode) {\r\n // eslint-disable-next-line no-console -- This is only used when debug mode is enabled.\r\n console.log(`Connection reestablished. Connected with connectionId \"${connectionId}\".`);\r\n }\r\n });\r\n\r\n connection.onclose(error => {\r\n if (isDebugMode) {\r\n // eslint-disable-next-line no-console -- This is only used when debug mode is enabled.\r\n if (error) {\r\n console.log(`Connection closed due to error: ${error}.`);\r\n }\r\n else {\r\n console.log(`Connection closed.`);\r\n }\r\n }\r\n\r\n // Free lock.\r\n lockResolver();\r\n });\r\n\r\n // Start it up.\r\n await connection.start();\r\n\r\n // We're running.\r\n return connection;\r\n }\r\n // endregion ********************************************************************************/\r\n\r\n // region: Private Methods ******************************************************************/\r\n /**\r\n * Evaluate the next action in the queue.\r\n */\r\n nextAction() {\r\n if(isDebugMode) {\r\n console.log(`Current actions queue length = ${ this.actions.length }.`);\r\n }\r\n \r\n if(this.actions.length > 0) {\r\n const action = this.actions.shift();\r\n \r\n // Any parameters?\r\n const params = action.messageParams;\r\n\r\n // Parse action.\r\n switch (action.actionType.toLowerCase()) {\r\n\r\n case \"refreshpage\":\r\n this.reloadPage(params);\r\n break;\r\n\r\n case \"toast\":\r\n this.showToast(params);\r\n break;\r\n\r\n case \"refreshchart\":\r\n this.reloadChart(params);\r\n break;\r\n\r\n default:\r\n break;\r\n \r\n }\r\n }\r\n }\r\n // endregion ********************************************************************************/\r\n\r\n}();\r\n// endregion ************************************************************************************/\r\n","// #region: Class definition *********************************************************************/\r\n/**\r\n * Setup and handle events for the SignalR connection to the ApplicationHub.\r\n */\r\nwindow.applicationhub = new class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #connection;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n constructor() {\r\n /**\r\n * The signalR connection.\r\n */\r\n this.#connection = null;\r\n\r\n // Listen.\r\n eventBroadcaster.subscribe({message: broadcasts.APPLICATION_READY, subscriber: this});\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[applicationhub]\";\r\n }\r\n\r\n static toString() {\r\n return \"[applicationhub]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Events ***************************************************************************/\r\n /**\r\n * Handle EventBroadcaster transmissions.\r\n * @param {String} message The broadcast message, @see {broadcasts}.\r\n * @returns {Void}\r\n */\r\n handleBroadcast({message}) {\r\n switch (message) {\r\n\r\n case broadcasts.APPLICATION_READY:\r\n this.#setupSignalR();\r\n break;\r\n\r\n default:\r\n break;\r\n \r\n }\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Initialize & setup SignalR.\r\n */\r\n async #setupSignalR() {\r\n // Create the signalR connection.\r\n this.#connection = await messages.setupSignalR(`${ baseUrl }/ApplicationHub`);\r\n\r\n // Add listeners.\r\n this.#connection.on(\"MessageAsync\", (message) => {\r\n messages.parseMessage(message);\r\n });\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}();\r\n// #endregion ************************************************************************************/\r\n","// #region: Class definition *********************************************************************/\r\n/**\r\n * Replaces the browsers native rendering of form validation errors.\r\n * Fields will display validation errors only when the field is invalid and loses focus,\r\n * or when the field is invalid and it's parent form is submitted. \r\n * When the field regains focus, the validation message will update as the user updates the field, finally removing the error\r\n * altogether once the validation constraints have been met.\r\n * \r\n * Depends on the validation class.\r\n */\r\nwindow.validator = class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #bubble;\r\n #bubbleTimer;\r\n #field;\r\n #hideTimeout;\r\n #parent;\r\n #validateOnBlur;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n * @param {HTMLElement} field A form element that this validator will be attached to.\r\n * @param {String} validatorParent An element selector (related to the field) that the bubble will be attached to.\r\n * @param {Boolean} validateOnBlur Validate on blur, i.e. user leaves the field (defaults to false).\r\n * @param {Integer} hideTimeout When set to another value than '0' (default) the bubble will hide after the amount set (milliseconds).\r\n */\r\n constructor({ field, validatorParent = null, validateOnBlur = false, hideTimeout = 0 } = {}) {\r\n // No use when we do not have a field.\r\n if (!field) {\r\n return false;\r\n }\r\n\r\n /**\r\n * The error bubble to show.\r\n * @type {HTMLElement}\r\n */\r\n this.#bubble = null;\r\n\r\n /**\r\n * The field that is to be validated.\r\n * @type {HTMLElement}\r\n */\r\n this.#field = field;\r\n\r\n /**\r\n * Validated on losing focus...thus blur.\r\n * @type {Boolean}\r\n */\r\n this.#validateOnBlur = validateOnBlur;\r\n\r\n /**\r\n * The amount of milliseconds after the error bubble is hidden, 0 means no hiding.\r\n * @type {Integer}\r\n */\r\n this.#hideTimeout = hideTimeout;\r\n\r\n /**\r\n * The field element that the bubble will be attached to.\r\n * First checks the tree upwards (closests), then tries to find the 'parent' by the selector provided in the current form.\r\n * Defaults to it's closests parent div.\r\n * @type {HTMLElement}\r\n */\r\n this.#parent = validatorParent ?\r\n (\r\n field.closest(validatorParent)\r\n ?? field.closest(\"form\")?.querySelector(validatorParent)\r\n ?? field.closest(\"div\")\r\n ) : field.closest(\"div\");\r\n\r\n // Hide default title value if the field supports the validation API.\r\n this.#field.setAttribute(\"title\", \"\");\r\n\r\n // Capture invalid event, as we want to override the display of the message.\r\n this.#field.addEventListener(\"invalid\", e => e.preventDefault(), { capture: true });\r\n\r\n // We always validate on change, but not when explicitly disabled.\r\n if (!this.#field.hasAttribute(\"data-novalidate-onchange\")) {\r\n this.#field.addEventListener(\"change\", this.validateHandler, { capture: false });\r\n this.#field.addEventListener(\"validateonchange\", this.validateHandler, { capture: false });\r\n }\r\n\r\n // When an element is getting focus.\r\n this.#field.addEventListener(\"focus\", this.validateFocusHandler, { capture: false });\r\n\r\n // Do we want to validate on blur?\r\n if (this.#validateOnBlur) {\r\n this.#field.addEventListener(\"blur\", this.validateHandler, { capture: false });\r\n }\r\n\r\n // Do we want to validate on input?\r\n if (this.#field.hasAttribute(\"data-validate-oninput\")) {\r\n this.#field.addEventListener(\"input\", this.validateHandler, { capture: false });\r\n\r\n // When validating on input, we also validate on blur so any error message does not disappear.\r\n this.#field.addEventListener(\"blur\", this.validateHandler, { capture: false });\r\n }\r\n\r\n // A validator can validate through events, direct call and when a form is validated.\r\n // In the latter case we always want to show bubbles, though individual fields might prefer not showing them initially.\r\n this.isFormValidation = false;\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {String} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[validator]\";\r\n }\r\n\r\n static toString() {\r\n return \"[validator]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {String} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.1\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Events ***************************************************************************/\r\n /**\r\n * Handles events on the element, removes any error messages present and re-evaluates.\r\n * @param {Event} e The Event data passed when an event fires.\r\n */\r\n validateFocusHandler = (e) => {\r\n if (this.#field.hasAttribute(\"readonly\")) {\r\n this.hideError();\r\n }\r\n else {\r\n this.validate({ event = e });\r\n }\r\n }\r\n\r\n /**\r\n * Handles events on the element, removes any error messages present and re-evaluates.\r\n * @param {Event} e The Event data passed when an event fires.\r\n */\r\n validateHandler = (e) => {\r\n this.validate({ event = e });\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Checks if a buuble exists.\r\n * @returns {Boolean} True when a bubble exists, false if not.\r\n */\r\n hasBubble() {\r\n return !!this.#bubble;\r\n }\r\n\r\n /**\r\n * Hide any error indicators.\r\n * @param {Boolean} group The value is true if this method is called from within a radio/checkbox group 'hideError'\r\n * to prevent recursion and therefor a stack overflow exception.\r\n */\r\n hideError(group = false) {\r\n this.#hideFieldError();\r\n\r\n // If the field is a checkbox or a radio button it might be part of a group.\r\n // Clear any messages on any item of the group.\r\n if (!group && (this.#field.type === \"radio\" || this.#field.type === \"checkbox\")) {\r\n // Do we have group items that are invalid.\r\n const items = document.querySelectorAll(`input[name^=\"${this.#field.name.split(\"#\")[0]}\"]`);\r\n if (items?.length > 1) {\r\n [...items].forEach((item) => {\r\n item.validator?.hideError(true);\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Give the validator (related) elements invalid indicators.\r\n */\r\n setInvalid() {\r\n this.#field.setAttribute(\"aria-invalid\", \"true\");\r\n this.#parent?.classList.add(\"has-invalid-content\");\r\n }\r\n\r\n /**\r\n * Shows/updates the error bubble when not present.\r\n * @param {string} message The message to display.\r\n * @param {HTMLElement} parent Where to display the bubble (optional).\r\n * @param {Integer} hideTimeout Hide the bubble after some time, defaults to null.\r\n * Not auto hiding or using the value set from the constructor (optional).\r\n */\r\n showError({ message = null, parent = null, hideTimeout = null }) {\r\n if (message) {\r\n const ignoreBubble = !this.isFormValidation && this.#field.hasAttribute(\"data-ignorebubble\");\r\n if (!ignoreBubble) {\r\n this.#bubble = this.#field.closest(\".bubble\") ?? this.#parent.querySelector(\".bubble\");\r\n if (this.#bubble) {\r\n this.#bubble.innerHTML = `${message}`;\r\n this.#bubble.classList.remove(\"hidden\");\r\n }\r\n else {\r\n // Create bubble.\r\n this.#bubble = document.createElement(\"div\");\r\n this.#bubble.classList.add(\"bubble\", \"hidden\");\r\n this.#bubble.innerHTML = `${message}`;\r\n this.#bubble.addEventListener(\"click\", () => {\r\n this.hideError();\r\n }, { capture: false });\r\n\r\n (parent ?? this.#parent)?.append(this.#bubble);\r\n this.#bubble.classList.remove(\"hidden\");\r\n }\r\n\r\n // Is a custom timeout provided? Update the instance setting.\r\n if (hideTimeout) {\r\n this.#hideTimeout = hideTimeout;\r\n }\r\n\r\n this.setInvalid();\r\n\r\n // Hide after some time?\r\n if (this.#hideTimeout > 0) {\r\n clearTimeout(this.#bubbleTimer);\r\n this.#bubbleTimer = setTimeout(() => {\r\n return this.hideError();\r\n }, this.#hideTimeout);\r\n }\r\n\r\n // Validation is done, reset the isFormValidation to false again.\r\n this.isFormValidation = false;\r\n }\r\n else {\r\n this.#field.setAttribute(\"title\", message);\r\n }\r\n\r\n // When a error is displayed, add the oninput handler so message will updated when the user enters content.\r\n this.#field.addEventListener(\"input\", this.validateHandler, { capture: false, once: true });\r\n }\r\n else {\r\n console.error(\"Parameter 'message' is 'null' on element '{Field}'\".replace(\"{Field}\", this.#field.name));\r\n }\r\n }\r\n\r\n /**\r\n * Actually validate the attached field.\r\n * Shows the error message when invalid.\r\n * @param {Boolean} required Whether to validate required, typically only when called from validation.validateForm.\r\n * @param {Event} event The generic Event data passed when a change event fires.\r\n * @returns {Boolean} True when the control validates, otherwise false.\r\n */\r\n validate({ required = false, event = null }) {\r\n let isValid = true;\r\n\r\n if (event?.type === \"focus\" && !this.#field.value) {\r\n return true;\r\n }\r\n\r\n // Do not validate disabled fields, no way the user can fix it.\r\n if (this.#field && !this.#field.hasAttribute(\"disabled\")) {\r\n const forceValidate = this.#field.hasAttribute(\"data-force-validate\");\r\n\r\n let ownerWindow = this.#field.ownerDocument.defaultView;\r\n\r\n // Element implements the validation API.\r\n if (typeof this.#field.setCustomValidity === \"function\") {\r\n\r\n // A radiobutton or checkbox might be part of a group and when invalid do not parse validity on every member.\r\n if (this.#field.type === \"radio\" || this.#field.type === \"checkbox\") {\r\n // A '#' is used to identify checkboxes in a group name.\r\n const groupItems = document.querySelectorAll(`input[type=\"${this.#field.type}\"][name^=\"${this.#field.name.split(\"#\")[0]}\"]`);\r\n const invalidItemCount = [...groupItems].filter(item => item.hasAttribute('aria-invalid')).length;\r\n if (groupItems.length > 1 && invalidItemCount > 1 && invalidItemCount !== groupItems.length) {\r\n // We have already validated this element and only want one message per group, so we do not validate any further.\r\n return true;\r\n }\r\n\r\n if (this.#field.hasAttribute(\"required\") && required) {\r\n isValid = validation.validateGroup({ element: this.#field, items: [...groupItems] });\r\n }\r\n }\r\n else {\r\n // Default contraint API check.\r\n isValid = this.#field.validity.valid;\r\n\r\n // When state is invalid AND we do not want to validate required we need to check if the 'valuemissing' property is\r\n // the only one property that is invalid. If so reset isValid property.\r\n if (!isValid && !required) {\r\n const invalidProperties = this.#getInvalidProperties(this.#field.validity);\r\n\r\n // When length is '1', only one is invalid.\r\n // Check if that's 'valueMissing'.\r\n if (invalidProperties?.length === 1 && invalidProperties[0] === \"valueMissing\") {\r\n isValid = true;\r\n }\r\n }\r\n\r\n // If it's a file (upload) control, its value(s) are set in a separate 'attachments_' container.\r\n // If data was (previously) loaded, check if any preview containers are created.\r\n if (this.#field.type === \"file\" && !isValid) {\r\n isValid = document.querySelector(\"#attachments_\" + this.#field.name).children.length > 0;\r\n }\r\n }\r\n\r\n // Check additional validation when defaults are good (or we want to validate everything).\r\n if (isValid || forceValidate) {\r\n const validateAdditional = validation.validateAdditional({ event: event, element: this.#field });\r\n\r\n // Do not reset isValid when false.\r\n if (isValid) {\r\n isValid = validateAdditional;\r\n }\r\n }\r\n\r\n // The HTML5 spec says: “If the readonly attribute is specified on an input element, the element is barred from constraint validation.”\r\n // So we need to do required validation for readonly fields ourselves.\r\n if ((isValid || forceValidate) && this.#field.hasAttribute(\"readonly\")) {\r\n const validateReadOnly = validation.validateReadOnly({ event: event, element: this.#field });\r\n\r\n // Do not reset isValid when false.\r\n if (isValid) {\r\n isValid = validateReadOnly;\r\n }\r\n }\r\n \r\n // Custom validators.\r\n if ((isValid || forceValidate) && this.#field.hasAttribute(\"data-customvalidator\")) {\r\n const validationMethod = this.#field.dataset.customvalidator;\r\n const targetWindow = typeof ownerWindow[validationMethod] === \"function\" ? ownerWindow : window;\r\n\r\n // Only call when function exists.\r\n if (typeof ownerWindow[validationMethod] === \"function\") {\r\n isValid = ownerWindow[validationMethod]({ event: event, element: this.#field });\r\n }\r\n }\r\n }\r\n\r\n if (isValid) {\r\n this.hideError();\r\n }\r\n else {\r\n const errorMessage = validation.getMessage(this.#field, true);\r\n this.showError({ message: errorMessage });\r\n\r\n // Do we want to do something when done? This is set on a control in html so check if the method exists before calling.\r\n const afterValidate = this.#field.dataset.aftervalidate;\r\n const targetWindow = typeof ownerWindow[afterValidate] === \"function\" ? ownerWindow : window;\r\n if (typeof targetWindow[afterValidate] === \"function\") {\r\n targetWindow[afterValidate]();\r\n }\r\n }\r\n }\r\n\r\n return isValid;\r\n }\r\n // #region: Public Methods *******************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Check which ValidityState Instance properties are true, i.e. are invalid.\r\n * @param {Object} validityState The state object.\r\n * @returns {Object} The collection of invalid properties.\r\n */\r\n #getInvalidProperties(validityState) {\r\n const invalidProperties = [];\r\n for (var key in validityState) {\r\n if (validityState[key]) {\r\n invalidProperties.push(key);\r\n }\r\n }\r\n\r\n return invalidProperties;\r\n }\r\n\r\n /**\r\n * Actually hides the error indicators (bubble, invalid and parent-has-invalid-content).\r\n */\r\n #hideFieldError() {\r\n const bubble = this.#bubble ?? this.#field.closest(\".bubble\") ?? this.#parent.querySelector(\".bubble\");\r\n if (bubble) {\r\n bubble.parentNode?.removeChild(bubble);\r\n }\r\n\r\n this.#field.removeAttribute(\"aria-invalid\");\r\n this.#field.setAttribute(\"title\", \"\");\r\n if (this.#field.setCustomValidity) {\r\n this.#field.setCustomValidity(\"\");\r\n }\r\n\r\n // Remove 'input' event (when not set as an attribute on the field itself).\r\n if (!(this.#field.hasAttribute(\"data-validate-oninput\"))) {\r\n this.#field.removeEventListener(\"input\", this.validateHandler, { capture: false });\r\n }\r\n \r\n // Remove 'invalid' class (and on any parents).\r\n this.#field.classList.remove(\"invalid\");\r\n let invalidElement = this.#field.closest(\".invalid\");\r\n do {\r\n invalidElement?.classList.remove(\"invalid\");\r\n invalidElement = invalidElement?.closest(\".invalid\");\r\n }\r\n while (invalidElement);\r\n\r\n // Remove 'has-invalid-content' class on any parents.\r\n let invalidContentElement = this.#field.closest(\".has-invalid-content\");\r\n do {\r\n invalidContentElement?.classList.remove(\"has-invalid-content\");\r\n invalidContentElement = invalidContentElement?.closest(\".has-invalid-content\");\r\n }\r\n while (invalidContentElement);\r\n\r\n // Reset bubble;\r\n this.#bubble = null;\r\n }\r\n // #region: Private Methods ******************************************************************/\r\n\r\n};\r\n// #endregion ************************************************************************************/","// #region: Class definition *********************************************************************/\r\n/**\r\n * A small, nonblocking notification pop-up.\r\n * A toast is shown to users with readable message content at a specific target and disappears automatically after a time-out.\r\n * The toast also allows to be manually closed when its set to be 'closable'.\r\n * \r\n * Depends on the DOMPurify library. \r\n */\r\nclass toast {\r\n\r\n // #region: Static Data **********************************************************************/\r\n /** Where do we position the toast message. */\r\n static toastPosition = {\r\n TopLeft: \"top-left\",\r\n TopCenter: \"top-center\",\r\n TopRight: \"top-right\",\r\n CenterLeft: \"center-left\",\r\n CenterCenter: \"center-center\",\r\n CenterRight: \"center-right\",\r\n BottomLeft: \"bottom-left\",\r\n BottomCenter: \"bottom-center\",\r\n BottomRight: \"bottom-right\"\r\n };\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n\r\n /** Used to set the allowed classes on the positioning grid (the location of the actual toast). */\r\n this.toastPositionIndex = [\r\n [toast.toastPosition.TopLeft, toast.toastPosition.TopCenter, toast.toastPosition.TopRight],\r\n [toast.toastPosition.CenterLeft, toast.toastPosition.CenterCenter, toast.toastPosition.CenterRight],\r\n [toast.toastPosition.BottomLeft, toast.toastPosition.BottomCenter, toast.toastPosition.BottomRight]\r\n ];\r\n\r\n /** The inline svg icons on the respective toast types. */\r\n this.svgs = {\r\n success: '',\r\n warn: '',\r\n info: '',\r\n error: ''\r\n };\r\n\r\n /** The default options, that can be overridden by the caller. */\r\n this.defaultOptions = {\r\n callback: undefined,\r\n closable: true,\r\n duration: 4000,\r\n focusable: true,\r\n offsetBlock: 0,\r\n offsetInline: 0,\r\n position: toast.toastPosition.TopCenter,\r\n purify: true,\r\n title: undefined\r\n };\r\n\r\n this.appendContainer();\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[toast]\";\r\n }\r\n\r\n static toString() {\r\n return \"[toast]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Hide/delete the toast.\r\n */\r\n hide(toastCard) {\r\n toastCard.style.setProperty(`margin-${toastCard.options.yPos}`, `-${toastCard.offsetHeight}px`);\r\n toastCard.style.setProperty(\"opacity\", \"0\");\r\n\r\n setTimeout(() => {\r\n toastCard.remove()\r\n\r\n if (typeof toastCard.options.callback === \"function\") {\r\n toastCard.options.callback();\r\n }\r\n }, 500);\r\n }\r\n\r\n /**\r\n * Show an error message.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this toast.\r\n */\r\n error(message, options) {\r\n return this.show(message, options, \"error\");\r\n }\r\n\r\n /**\r\n * Show an informational message.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this toast.\r\n */\r\n info(message, options) {\r\n return this.show(message, options, \"info\");\r\n }\r\n\r\n /**\r\n * Show a success message.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this toast.\r\n */\r\n success(message, options) {\r\n return this.show(message, options, \"success\");\r\n }\r\n\r\n /**\r\n * Show a warning message.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this toast.\r\n */\r\n warn(message, options) {\r\n return this.show(message, options, \"warn\");\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Appends the toast container when it does not exist.\r\n */\r\n appendContainer() {\r\n const container = document.querySelector(\".toast-container\");\r\n if (!container) {\r\n // Render and append the container.\r\n const toastContainer = document.createElement(\"div\");\r\n toastContainer.className = \"toast-container\";\r\n\r\n // Create the positioning grid.\r\n for (const ri of [0, 1, 2]) {\r\n const row = document.createElement(\"div\");\r\n row.className = \"toast-row\";\r\n\r\n for (const ci of [0, 1, 2]) {\r\n const col = document.createElement(\"div\");\r\n col.className = `toast-col ${this.toastPositionIndex[ri][ci]}`;\r\n row.appendChild(col);\r\n }\r\n\r\n toastContainer.appendChild(row);\r\n }\r\n\r\n document.documentElement.appendChild(toastContainer);\r\n }\r\n }\r\n\r\n /**\r\n * Hides the toast message after the duration set in the options.\r\n * If duration is '0' the toast will not auto hide.\r\n * @param {HTMLDivElement} toastCard The toast message.\r\n */\r\n autoHide(toastCard) {\r\n if (toastCard?.options?.duration > 0) {\r\n setTimeout(() => {\r\n if (!toastCard.options.isFocus) {\r\n this.hide(toastCard);\r\n }\r\n }, toastCard.options.duration);\r\n }\r\n }\r\n\r\n /**\r\n * Set events on the toast.\r\n * @param {HTMLDivElement} toastCard The toast message.\r\n */\r\n setCardBindEvents(toastCard) {\r\n if (toastCard.options.closable) {\r\n toastCard.addEventListener(\"click\", () => {\r\n this.hide(toastCard);\r\n });\r\n }\r\n\r\n toastCard.addEventListener(\"mouseover\", () => {\r\n toastCard.options.isFocus = toastCard.options.focusable;\r\n });\r\n\r\n toastCard.addEventListener(\"mouseout\", () => {\r\n toastCard.options.isFocus = false;\r\n this.autoHide(toastCard);\r\n });\r\n }\r\n\r\n /**\r\n * Append the content to the toast container.\r\n * @param {HTMLDivElement} toastCard The toast message.\r\n */\r\n setCardContent(toastCard) {\r\n const textGroupDiv = document.createElement(\"div\");\r\n textGroupDiv.className = \"text-group\";\r\n\r\n if (toastCard.options.title) {\r\n textGroupDiv.innerHTML = `

${toastCard.options.title}

`\r\n }\r\n\r\n textGroupDiv.innerHTML += `

${toastCard.options.message}

`;\r\n toastCard.appendChild(textGroupDiv);\r\n }\r\n\r\n /**\r\n * Add the animation style(s) on the toast.\r\n * @param {HTMLDivElement} toastCard The toast message.\r\n */\r\n setCardIntroAnimation(toastCard) {\r\n toastCard.style.setProperty(`margin-${toastCard.options.yPos}`, \"-15px\");\r\n toastCard.style.setProperty(\"opacity\", \"0\");\r\n\r\n setTimeout(() => {\r\n toastCard.style.setProperty(`margin-${toastCard.options.yPos}`, \"15px\")\r\n toastCard.style.setProperty(\"opacity\", \"1\")\r\n }, 50);\r\n }\r\n\r\n /**\r\n * Actually shows the given message on the 'type' toast configured by the options.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this toast.\r\n * @param {String} type The type of message to display, default to \"warn\".\r\n * @returns {HTMLDivElement} The generated toast so the caller can modify html/style/events. \r\n */\r\n show(message = \"\", options, type) {\r\n options = { ...this.defaultOptions, ...options };\r\n\r\n // Make sure the container exists.\r\n this.appendContainer();\r\n\r\n const toastType = type || \"warn\";\r\n const col = document.getElementsByClassName(options.position)[0];\r\n const toastCard = document.createElement(\"div\");\r\n const toastMessage = options.purify ? DOMPurify.sanitize(message, { USE_PROFILES: { html: true }, ADD_ATTR: [\"target\"] }) : message;\r\n\r\n toastCard.className = `toast-card ${toastType}`;\r\n toastCard.innerHTML += this.svgs[toastType];\r\n toastCard.options = {\r\n ...options,\r\n ...{\r\n message: toastMessage,\r\n type: toastType,\r\n yPos: options.position.indexOf(\"top\") > -1 ? \"top\" : \"bottom\",\r\n isFocus: false\r\n }\r\n };\r\n\r\n // Create close element.\r\n if (toastCard.options.closable) {\r\n const closeElem = document.createElement(\"span\");\r\n closeElem.classList.add(\"toast-card-close\");\r\n toastCard.appendChild(closeElem);\r\n }\r\n\r\n this.setCardContent(toastCard);\r\n this.setCardIntroAnimation(toastCard);\r\n this.setCardBindEvents(toastCard);\r\n this.autoHide(toastCard);\r\n\r\n toastCard.close = () => {\r\n this.hide(toastCard);\r\n }\r\n\r\n // Offset.\r\n toastCard.style.insetBlockStart = toastCard.getBoundingClientRect().top + options.offsetBlock + \"px\";\r\n toastCard.style.insetInlineStart = toastCard.getBoundingClientRect().left + options.offsetInline + \"px\";\r\n\r\n col.appendChild(toastCard);\r\n\r\n return toastCard;\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n};\r\n// #endregion ************************************************************************************/","// #region: Class definition *********************************************************************/\r\n/**\r\n * A small, blocking (modal) confirmation pop-up.\r\n * A toast is shown to users with readable message content at a specific target and only disappears when a button is clicked.\r\n * \r\n * Depends on the toast class. \r\n */\r\nclass confirmToast extends toast {\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n super();\r\n\r\n /** The default options, that can be overridden by the caller. */\r\n this.defaultOptions = {\r\n handleResponse: undefined,\r\n position: toast.toastPosition.TopCenter,\r\n purify: true,\r\n title: undefined,\r\n trueLabel: resources?.base.confirm ?? \"OK\",\r\n falseLabel: resources?.base.cancel ?? \"Cancel\"\r\n };\r\n\r\n // Append confirm svg.\r\n this.svgs.confirm = ''\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[confirmToast]\";\r\n }\r\n\r\n static toString() {\r\n return \"[confirmToast]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Actually shows the given message on the confirm.\r\n * Returns true or false depending on the button clicked.\r\n * @param {String} message The message to display; can contain html.\r\n * @param {Object} options The options for this confirm.\r\n */\r\n show(message = \"\", options) {\r\n options = { ...this.defaultOptions, ...options };\r\n\r\n // Make sure the container exists.\r\n this.appendContainer();\r\n\r\n const toastType = \"confirm\";\r\n const col = document.getElementsByClassName(options.position)[0];\r\n const toastCard = document.createElement(\"div\");\r\n const toastMessage = options.purify ? DOMPurify.sanitize(message, { USE_PROFILES: { html: true }, ADD_ATTR: [\"target\"] }) : message;\r\n\r\n toastCard.className = `toast-card ${toastType}`;\r\n toastCard.innerHTML += this.svgs[toastType];\r\n toastCard.options = {\r\n ...options,\r\n ...{\r\n duration: 0,\r\n message: toastMessage,\r\n type: toastType,\r\n yPos: options.position.indexOf(\"top\") > -1 ? \"top\" : \"bottom\",\r\n isFocus: false\r\n }\r\n };\r\n\r\n this.setCardContent(toastCard);\r\n this.setCardIntroAnimation(toastCard);\r\n this.setCardBindEvents(toastCard);\r\n this.autoHide(toastCard);\r\n\r\n col.appendChild(toastCard);\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Append the content to the toast container.\r\n * @param {HTMLDivElement} toastCard The toast message.\r\n */\r\n setCardContent(toastCard) {\r\n const textGroupDiv = document.createElement(\"div\");\r\n textGroupDiv.className = \"text-group\";\r\n\r\n if (toastCard.options.title) {\r\n textGroupDiv.innerHTML = `

${toastCard.options.title}

`\r\n }\r\n\r\n textGroupDiv.innerHTML += `

${toastCard.options.message}

`;\r\n toastCard.appendChild(textGroupDiv);\r\n\r\n // Create confirm buttons.\r\n const confirmButtonContainer = document.createElement(\"div\");\r\n confirmButtonContainer.classList.add(\"confirm-toast-buttons\");\r\n\r\n const trueButton = document.createElement(\"button\");\r\n trueButton.innerHTML = toastCard.options.trueLabel;\r\n trueButton.classList.add(\"solid-light-button\");\r\n confirmButtonContainer.appendChild(trueButton);\r\n\r\n const falseButton = document.createElement(\"button\");\r\n falseButton.innerHTML = toastCard.options.falseLabel;\r\n falseButton.classList.add(\"solid-light-button\");\r\n confirmButtonContainer.appendChild(falseButton);\r\n\r\n // Add events.\r\n trueButton.onclick = () => {\r\n this.hide(toastCard);\r\n toastCard.options.handleResponse(true);\r\n };\r\n\r\n falseButton.onclick = () => {\r\n this.hide(toastCard);\r\n toastCard.options.handleResponse(false);\r\n };\r\n\r\n textGroupDiv.appendChild(confirmButtonContainer);\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}\r\n// #endregion ************************************************************************************/","// #region: Class definition *********************************************************************/\r\n/**\r\n * The dating class contains all date/time related code.\r\n */\r\nwindow.dating = new class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #expressions;\r\n #formattingTokens;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n // Magic numbers.\r\n this.MINYEAR = 1800;\r\n this.MAXYEAR = 2200;\r\n\r\n // Local regex expressions for format matching.\r\n const match1 = /\\d/ // 0 - 9\r\n const match2 = /\\d\\d/ // 00 - 99\r\n const match3 = /\\d{3}/ // 000 - 999\r\n const match4 = /\\d{4}/ // 0000 - 9999\r\n const match1to2 = /\\d\\d?/ // 0 - 99\r\n const matchSigned = /[+-]?\\d+/ // -inf - inf\r\n const matchOffset = /[+-]\\d\\d:?(\\d\\d)?|Z/ // +00:00 -00:00 +0000 or -0000 +00 or Z\r\n const matchWord = /\\d*[^-_:/,()\\s\\d]+/ // Word\r\n\r\n // Local function to create/calculate property values in the datetime object (array).\r\n const addInput = function (property) {\r\n return function (input) {\r\n this[property] = +input;\r\n }\r\n };\r\n\r\n // Local function to check for a present meridiem indicator, being in the afternoon.\r\n const meridiemMatch = (input, isLowerCase) => {\r\n return input === (isLowerCase ? \"pm\" : \"PM\");\r\n };\r\n\r\n // Local function to fix two digit year display.\r\n const parseTwoDigitYear = (input) => {\r\n input = +input;\r\n return input + (input > 68 ? 1900 : 2000);\r\n };\r\n\r\n // Local function to parse time zones.\r\n const zoneExpressions = [matchOffset, function (input) {\r\n const zone = this.zone || (this.zone = {});\r\n zone.offset = offsetFromString(input);\r\n }];\r\n\r\n /**\r\n * All the date/time format tokens.\r\n * @type {Regex}\r\n */\r\n this.#formattingTokens = /(\\[[^[]*\\])|([-_:\\/.,()\\s]+)|(A|a|yyyy|yy?|MM?|dd?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g;\r\n this.#expressions = {\r\n A: [matchWord, function (input) {\r\n this.afternoon = meridiemMatch(input, false);\r\n }],\r\n a: [matchWord, function (input) {\r\n this.afternoon = meridiemMatch(input, true);\r\n }],\r\n S: [match1, function (input) {\r\n this.milliseconds = +input * 100;\r\n }],\r\n SS: [match2, function (input) {\r\n this.milliseconds = +input * 10;\r\n }],\r\n SSS: [match3, function (input) {\r\n this.milliseconds = +input;\r\n }],\r\n s: [match1to2, addInput(\"seconds\")],\r\n ss: [match1to2, addInput(\"seconds\")],\r\n m: [match1to2, addInput(\"minutes\")],\r\n mm: [match1to2, addInput(\"minutes\")],\r\n H: [match1to2, addInput(\"hours\")],\r\n h: [match1to2, addInput(\"hours\")],\r\n HH: [match1to2, addInput(\"hours\")],\r\n hh: [match1to2, addInput(\"hours\")],\r\n d: [match1to2, addInput(\"day\")],\r\n dd: [match2, addInput(\"day\")],\r\n M: [match1to2, addInput(\"month\")],\r\n MM: [match2, addInput(\"month\")],\r\n y: [matchSigned, addInput(\"year\")],\r\n yy: [match2, function (input) {\r\n this.year = parseTwoDigitYear(input);\r\n }],\r\n yyyy: [match4, addInput(\"year\")],\r\n Z: zoneExpressions,\r\n ZZ: zoneExpressions\r\n };\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[dating]\";\r\n }\r\n\r\n static toString() {\r\n return \"[dating]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Format an (ISO) date to the format provided.\r\n * @param {String} date The (ISO) date string to parse.\r\n * @param {String} format The format to use.\r\n * @returns {String} The parsed date, or the date provided when not a valid date.\r\n */\r\n formatDate(date, format = null) {\r\n if (!isNaN(Date.parse(date))) {\r\n const dateObject = new Date(date);\r\n const formatString = format ?? \"d-M-yyyy\";\r\n\r\n // Local function to parse a time zone.\r\n const padZone = (instance) => {\r\n const negMinutes = -instance.getTimezoneOffset();\r\n const minutes = Math.abs(negMinutes);\r\n const hourOffset = Math.floor(minutes / 60);\r\n const minuteOffset = minutes % 60;\r\n return `${negMinutes <= 0 ? \"+\" : \"-\"}${String(hourOffset).padStart(2, \"0\")}:${String(minuteOffset).padStart(2, \"0\")}`;\r\n };\r\n\r\n // Local function to parse AM/PM display.\r\n const meridiemFunc = (hour, minute, isLowercase) => {\r\n const meridiem = (hour < 12 ? \"AM\" : \"PM\");\r\n return isLowercase ? meridiem.toLowerCase() : meridiem;\r\n };\r\n\r\n // Create the separate parts based on the format.\r\n const year = dateObject.getFullYear();\r\n const month = dateObject.getMonth();\r\n const day = dateObject.getDate();\r\n const hours = dateObject.getHours();\r\n const minutes = dateObject.getMinutes();\r\n const seconds = dateObject.getSeconds();\r\n const milliseconds = dateObject.getMilliseconds();\r\n const zone = padZone(dateObject);\r\n\r\n const matches = (match) => {\r\n switch (match) {\r\n case \"yy\":\r\n case \"YY\":\r\n return String(year).slice(-2);\r\n\r\n case \"yyyy\":\r\n case \"YYYY\":\r\n return String(year).padStart(4, \"0\");\r\n\r\n case \"M\":\r\n return (month + 1);\r\n\r\n case \"MM\":\r\n return String(month + 1).padStart(2, \"0\");\r\n\r\n case \"d\":\r\n return day;\r\n\r\n case \"dd\":\r\n return String(day).padStart(2, \"0\");\r\n\r\n case \"H\":\r\n return String(hours);\r\n\r\n case \"HH\":\r\n return String(hours).padStart(2, \"0\");\r\n\r\n case \"h\":\r\n return String(hours).padStart(1, \"0\");\r\n\r\n case \"hh\":\r\n return String(hours).padStart(2, \"0\");\r\n\r\n case \"a\":\r\n return meridiemFunc(hours, minutes, true);\r\n\r\n case \"A\":\r\n return meridiemFunc(hours, minutes, false);\r\n\r\n case \"m\":\r\n return String(minutes);\r\n\r\n case \"mm\":\r\n return String(minutes).padStart(2, \"0\");\r\n\r\n case \"s\":\r\n return String(seconds);\r\n\r\n case \"ss\":\r\n return String(seconds).padStart(2, \"0\");\r\n\r\n case \"SSS\":\r\n return String(milliseconds).padStart(3, \"0\");\r\n\r\n case \"z\":\r\n case \"Z\":\r\n return \"GMT\" + zone.replace(\":\", \"\");\r\n\r\n default:\r\n break\r\n }\r\n\r\n return null;\r\n }\r\n\r\n return formatString.replace(this.#formattingTokens, (match, $1) => $1 || matches(match) || match);\r\n }\r\n\r\n // Not a valid date.\r\n return \"\";\r\n }\r\n\r\n /**\r\n * Formats a string to display as a human readable date time (losing the seconds, using dashes).\r\n * @@param {String} dateString The date string to format.\r\n */\r\n formatDateTime(dateString) {\r\n const parsedDate = new Date(dateString);\r\n const date = [parsedDate.getDate(), parsedDate.getMonth() + 1, parsedDate.getFullYear()].join(\"-\");\r\n const minutes = parsedDate.getMinutes().toString();\r\n const time = `${parsedDate.getHours()}:${minutes.length < 2 ? '0' : ''}${minutes}`;\r\n\r\n return `${date} ${time}`;\r\n }\r\n\r\n /**\r\n * Tries to convert a given string to a javascript date object.\r\n * @param {String} dateString The string to parse to a date object.\r\n * @returns {Date} The parsed date when success, min date when failed.\r\n */\r\n getDateFromString(dateString) {\r\n let myDate = new Date();\r\n\r\n try {\r\n const initialTimeSeparatorPosition = dateString.indexOf(\":\");\r\n\r\n if (initialTimeSeparatorPosition > -1 && initialTimeSeparatorPosition <= 3) {\r\n // Is time only. Add date to help parsing.\r\n dateString = \"01-01-1900 \" + dateString;\r\n }\r\n\r\n const firstDashPosition = dateString.indexOf(\"-\");\r\n const secondDashPosition = dateString.indexOf(\"-\", firstDashPosition + 1);\r\n const spacePosition = dateString.indexOf(\" \");\r\n const hasTime = spacePosition > -1;\r\n\r\n const strDay = dateString.substring(0, firstDashPosition);\r\n const strMonth = dateString.substring(firstDashPosition + 1, secondDashPosition);\r\n let strYear;\r\n\r\n if (hasTime) {\r\n strYear = dateString.substring(secondDashPosition + 1, spacePosition);\r\n }\r\n else {\r\n strYear = dateString.substring(secondDashPosition + 1);\r\n }\r\n\r\n const timeString = dateString.substring(spacePosition);\r\n const timeSeparatorPosition = timeString.indexOf(\":\");\r\n const strHour = timeString.substring(0, timeSeparatorPosition);\r\n const strMinute = timeString.substring(timeSeparatorPosition + 1);\r\n\r\n const day = parseInt(strDay, 10);\r\n const month = parseInt(strMonth, 10);\r\n const year = parseInt(strYear, 10);\r\n const hour = hasTime ? parseInt(strHour, 10) : 0;\r\n const minute = hasTime ? parseInt(strMinute, 10) : 0;\r\n\r\n myDate.setFullYear(year, month - 1, day);\r\n myDate.setHours(hour, minute, 0, 0);\r\n }\r\n catch (e) {\r\n myDate = new Date(1900, 1, 1);\r\n }\r\n\r\n return myDate;\r\n }\r\n\r\n /**\r\n * Check is a given string can be evaluated to a date, time or a combination of the two.\r\n * @param {String} dateString The string to validate.\r\n * @param {String} format The format to use: date, time, both.\r\n * @returns {String} An empty string when input is a valid date, an error message if not.\r\n */\r\n isDateStr(dateString, format) {\r\n dateString = dateString.trim();\r\n\r\n // If no content is given to validate, return 'Invalid date'.\r\n if (!dateString) {\r\n return resources.validationMessages.invalidDate;\r\n }\r\n\r\n let isDateOnly = format === \"date\";\r\n let isTimeOnly = format === \"time\";\r\n let isBoth = format === \"both\";\r\n\r\n let dateAndTime = dateString.split(\" \"),\r\n hasBoth = dateAndTime.length === 2,\r\n hasDate = dateString.indexOf(\"-\") > -1,\r\n hasTime = dateString.indexOf(\":\") > -1,\r\n date = hasBoth ? dateAndTime[0] : dateString,\r\n time = hasBoth ? dateAndTime[1] : dateString;\r\n\r\n if (dateAndTime.length > 2 || ((isDateOnly || isTimeOnly) && hasBoth) || (!hasDate && !hasTime)) {\r\n return isTimeOnly ? resources.validationMessages.invalidTime : resources.validationMessages.invalidDate;\r\n }\r\n\r\n if (isBoth || isDateOnly || hasBoth || hasDate) {\r\n let dateParts = date.split(\"-\");\r\n\r\n if (dateParts.length !== 3) {\r\n return resources.validationMessages.invalidDate;\r\n }\r\n\r\n let dayString = dateParts[0];\r\n let monthString = dateParts[1];\r\n let yearString = dateParts[2];\r\n let day = Number(dayString);\r\n let month = Number(monthString);\r\n let year = Number(yearString);\r\n const daysInMonths = [31, this.#daysInFebruary(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\r\n const daysInMonth = daysInMonths[month - 1];\r\n\r\n if (!this.#isWithin(dayString.length, 1, 2) || !this.#isWithin(day, 1, daysInMonth)) {\r\n return resources.validationMessages.invalidDay;\r\n }\r\n\r\n if (!this.#isWithin(monthString.length, 1, 2) || !this.#isWithin(month, 1, 12)) {\r\n return resources.validationMessages.invalidMonth;\r\n }\r\n\r\n if (yearString.length !== 4 || !this.#isWithin(year, this.MINYEAR, this.MAXYEAR)) {\r\n return resources.validationMessages.invalidYear.replace(\"{minyear}\", this.MINYEAR).replace(\"{maxyear}\", this.MAXYEAR);\r\n }\r\n }\r\n\r\n if (isBoth || isTimeOnly || hasBoth || hasTime) {\r\n let timeParts = time.split(\":\");\r\n\r\n if (timeParts.length !== 2) {\r\n return resources.validationMessages.invalidTime;\r\n }\r\n\r\n let hoursString = timeParts[0],\r\n minutesString = timeParts[1],\r\n hours = Number(hoursString);\r\n let minutes = Number(minutesString);\r\n\r\n if (!this.#isWithin(hoursString.length, 1, 2) || !this.#isWithin(hours, 0, 23)) {\r\n return resources.validationMessages.invalidTime;\r\n }\r\n\r\n if (minutesString.length !== 2 || !this.#isWithin(minutes, 0, 59)) {\r\n return resources.validationMessages.invalidTime;\r\n }\r\n }\r\n\r\n return \"\";\r\n }\r\n\r\n /**\r\n * Parses a provided string to to a valid datetime (string representation).\r\n * @param {String} datestring The date string to check.\r\n * @param {String} format The string that sets the format that it's suppose to be.\r\n * @returns {String} A standard ISO 8601 string.\r\n */\r\n parseDateString(datestring, format = null) {\r\n try {\r\n // The parser will return a function that can parse the provided string.\r\n const parser = this.#makeParser(format ?? \"d-M-yyyy\");\r\n const parsedDate = parser(datestring);\r\n\r\n // Create the separate parts based on the format.\r\n let { year, month, day, hours, minutes, seconds, milliseconds, zone } = parsedDate;\r\n const now = new Date();\r\n\r\n // Day default to 1.\r\n day ??= ((!year && !month) ? now.getDate() : 1);\r\n\r\n // Month in javascript is 1 off (if we need to get it from the date object).\r\n month ??= now.getMonth() + 1;\r\n year ??= now.getFullYear();\r\n hours ??= 0;\r\n minutes ??= 0;\r\n seconds ??= 0;\r\n milliseconds ??= 0;\r\n\r\n // Check if we can create a valid date from this data.\r\n // Create a standard ISO 8601 string so we can do a Date.parse for a valid date.\r\n // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse;\r\n // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart\r\n let parsedDateString = `${year}-${month.toString().padStart(2, \"0\")}-${day.toString().padStart(2, \"0\")}T${hours.toString().padStart(2, \"0\")}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}.${milliseconds.toString().padStart(3, \"0\")}`;\r\n if (zone) {\r\n parsedDateString += `+${(zone.offset * 60 * 1000)}`;\r\n }\r\n\r\n return parsedDateString;\r\n }\r\n catch {\r\n // Not possible to parse the string, return original string.\r\n return datestring;\r\n }\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Fix hour display for 12 hour (AM/PM) display.\r\n * @param {Object} datetime The object to parse and optionally correct for meridiem setting.\r\n */\r\n #correctHours(datetime) {\r\n const { afternoon } = datetime;\r\n if (afternoon !== undefined) {\r\n const { hours } = datetime;\r\n if (afternoon) {\r\n if (hours < 12) {\r\n time.hours += 12;\r\n }\r\n }\r\n else if (hours === 12) {\r\n time.hours = 0;\r\n }\r\n\r\n delete time.afternoon;\r\n }\r\n }\r\n\r\n /**\r\n * Check if a given year is a leap year (February has 29 days).\r\n * @param {Integer} year The year to check.\r\n * @returns {Integer} The number of days in February.\r\n */\r\n #daysInFebruary(year) {\r\n // February has 29 days in any year evenly divisible by four, except for centurial years which are not also divisible by 400.\r\n return (((year % 4 === 0) && ((!(year % 100 === 0)) || (year % 400 === 0))) ? 29 : 28);\r\n }\r\n\r\n /**\r\n * Checks if a give number is within a given range.\r\n * @param { Integer } number The number to validate.\r\n * @param { Integer } min Range start(included).\r\n * @param { Integer } max Range end(included).\r\n * @returns { Boolean } True when within the range, false if not.\r\n */\r\n #isWithin(number, min, max) {\r\n return number >= min && number <= max;\r\n }\r\n\r\n /**\r\n * Provides a parser function that can be used to evaluate a date/time based on a give format.\r\n * @param {String} format The format to use.\r\n */\r\n #makeParser(format) {\r\n const array = format.match(this.#formattingTokens);\r\n const { length } = array;\r\n for (let i = 0; i < length; i += 1) {\r\n const token = array[i];\r\n const parseTo = this.#expressions[token];\r\n const regex = parseTo && parseTo[0];\r\n const parser = parseTo && parseTo[1];\r\n if (parser) {\r\n array[i] = { regex, parser };\r\n }\r\n else {\r\n array[i] = token.replace(/^\\[|\\]$/g, \"\");\r\n }\r\n }\r\n\r\n return (input) => {\r\n const datetime = {};\r\n for (let i = 0, start = 0; i < length; i += 1) {\r\n const token = array[i]\r\n if (typeof token === \"string\") {\r\n start += token.length;\r\n }\r\n else {\r\n const { regex, parser } = token;\r\n const part = input.slice(start);\r\n const match = regex.exec(part);\r\n const value = match[0];\r\n parser.call(datetime, value);\r\n input = input.replace(value, \"\");\r\n }\r\n }\r\n\r\n this.#correctHours(datetime);\r\n return datetime\r\n }\r\n }\r\n\r\n /**\r\n * Get any time zone offset defined in the given string.\r\n * @param {String} dateString The string to check for a time zone offset.\r\n */\r\n #offsetFromString(dateString) {\r\n if (!dateString) {\r\n return 0;\r\n }\r\n\r\n if (dateString === \"Z\") {\r\n return 0;\r\n }\r\n\r\n const parts = dateString.match(/([+-]|\\d\\d)/g);\r\n const minutes = +(parts[1] * 60) + (+parts[2] || 0);\r\n\r\n // eslint-disable-next-line no-nested-ternary -- We like this single line expression.\r\n return minutes === 0 ? 0 : parts[0] === \"+\" ? -minutes : minutes;\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}();\r\n// #endregion ************************************************************************************/\r\n","// #region: Class definition *********************************************************************/\r\n/**\r\n * Manages core javascript content, needed everywhere.\r\n * The manifest for core.js includes/needs:\r\n * broadcasts.js\r\n * event-broadcaster.js\r\n * messages.js\r\n * applicationHub.js\r\n * toast.js\r\n * toastConfirm.js\r\n * core.js (this file)\r\n * validator.js\r\n * validation.js\r\n * \r\n * Depends on the 'toast' component (load before core.js).\r\n * \r\n * Date/time formatting inspired on day.js, see: https://day.js.org/\r\n */\r\nwindow.core = new class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #confirmToast;\r\n #emStyle;\r\n #rem;\r\n #toast;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n this.#toast = new toast();\r\n this.#confirmToast = new confirmToast();\r\n\r\n // Magic numbers.\r\n this.BREAKPOINT = 992;\r\n this.CALLBACK_TIMEOUT = 2500;\r\n this.CLEANUP_URL_TIMEOUT = 100;\r\n\r\n /**\r\n * Style string for measuring em pixels.\r\n * @type {String}\r\n */\r\n this.#emStyle = \"position:absolute !important;visibility:hidden !important; width:1em !important; font-size:1em !important; padding:0 !important;\";\r\n\r\n /** \r\n * Get the base pixel value so we can calculate sizes with em values in javascript.\r\n * @type {Number}\r\n */\r\n this.#rem = this.getEmPixels();\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {String} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[core]\";\r\n }\r\n\r\n static toString() {\r\n return \"[core]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {String} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Add validators to any form element present. And 'tag' labels of required elements with an asterisk.\r\n * @param {HTMLDocument} container The document to scan for forms that could/should have validators applied to its children.\r\n * @param {HTMLFormElement} form The form to add the validators to, or null to add them to all forms found.\r\n */\r\n addValidators(container, form) {\r\n // When no container is provided, we assume the whole document.\r\n container ??= document;\r\n\r\n if (form) {\r\n eventBroadcaster.broadcast(\r\n {\r\n detail: { form: form, validationMessages: resources.validationMessages },\r\n message: broadcasts.ADD_VALIDATORS\r\n });\r\n }\r\n else {\r\n // Add validators to any form present within the container.\r\n const forms = container.querySelectorAll(\"form\");\r\n [...forms].forEach((form) => {\r\n eventBroadcaster.broadcast(\r\n {\r\n detail: { form: form, validationMessages: resources.validationMessages },\r\n message: broadcasts.ADD_VALIDATORS\r\n });\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Displays a confirm (toast) notification.\r\n * @param {String} message The message text to display.\r\n * @param {Object} options [Optional] Any additional settings to pass on to the toast instance.\r\n */\r\n confirm(message, options) {\r\n options = options || {};\r\n\r\n if (!options.position) {\r\n const currentWidth = top.window.innerWidth;\r\n options.position = currentWidth > this.BREAKPOINT ? toast.toastPosition.TopCenter : toast.toastPosition.BottomCenter;\r\n }\r\n\r\n this.#confirmToast.show(message, options);\r\n }\r\n\r\n /**\r\n * Provides an equivalent to window.navigator.serviceWorker.ready that waits for the page to be controlled,\r\n * as opposed to waiting for the active service worker.\r\n * See https://github.com/slightlyoff/ServiceWorker/issues/799\r\n */\r\n controllerReadyPromise = new Promise(function (resolve) {\r\n // Resolve with the registration, to match the .ready promise's behavior.\r\n var resolveWithRegistration = function () {\r\n window.navigator.serviceWorker.getRegistration().then(function (registration) {\r\n resolve(registration);\r\n });\r\n };\r\n\r\n if (window.navigator.serviceWorker.controller) {\r\n resolveWithRegistration();\r\n }\r\n else {\r\n window.navigator.serviceWorker.addEventListener(\"controllerchange\", resolveWithRegistration);\r\n }\r\n });\r\n\r\n /**\r\n * Implementation of jQuery's ready() function, so init functions are called even when the 'DOMContentLoaded'' event already fired.\r\n * @param {Function} fn The function to call.\r\n */\r\n DOMContentLoaded(fn) {\r\n if (document.readyState !== \"loading\") {\r\n fn();\r\n }\r\n else {\r\n document.addEventListener(\"DOMContentLoaded\", fn);\r\n }\r\n }\r\n\r\n /**\r\n * Download a file through Ajax.\r\n * @param {String} url The url to get the file from.\r\n * @param {Object} data Any form data to send along.\r\n * @param {Function} callback Anything to call when done.\r\n * @param {Boolean} isJson Wether the data provided is Json (true) or Form data (false).\r\n */\r\n downloadFile(url, data = null, callback = null, isJson = false) {\r\n var xhr = new XMLHttpRequest();\r\n xhr.open(\"POST\", url, true);\r\n xhr.responseType = \"blob\";\r\n xhr.onload = function () {\r\n if (this.status === 200) {\r\n var blob = this.response;\r\n var filename = \"\";\r\n var disposition = xhr.getResponseHeader(\"Content-Disposition\");\r\n if (disposition && disposition.indexOf(\"attachment\") !== -1) {\r\n var filenameRegex = /filename[^;=\\n]*=(([\"\"]).*?\\2|[^;\\n]*)/;\r\n var matches = filenameRegex.exec(disposition);\r\n if (matches != null && matches[1]) filename = matches[1].replace(/[\"\"]/g, \"\");\r\n }\r\n\r\n if (typeof window.navigator.msSaveBlob !== \"undefined\") {\r\n // IE workaround for \"HTML7007: One or more blob URLs were revoked by closing the blob for which they were created.\r\n // These URLs will no longer resolve as the data backing the URL has been freed.\r\n window.navigator.msSaveBlob(blob, filename);\r\n }\r\n else {\r\n var URL = window.URL || window.webkitURL;\r\n var downloadUrl = URL.createObjectURL(blob);\r\n\r\n if (filename) {\r\n // Use HTML5 a[download] attribute to specify filename.\r\n var a = document.createElement(\"a\");\r\n\r\n // Safari doesn't support this yet.\r\n if (typeof a.download === \"undefined\") {\r\n window.location.href = downloadUrl;\r\n }\r\n else {\r\n a.href = downloadUrl;\r\n a.download = filename;\r\n document.body.appendChild(a);\r\n a.click();\r\n }\r\n }\r\n else {\r\n window.location.href = downloadUrl;\r\n }\r\n\r\n // Cleanup.\r\n setTimeout(function () {\r\n URL.revokeObjectURL(downloadUrl);\r\n }, this.CLEANUP_URL_TIMEOUT);\r\n\r\n if (callback) {\r\n setTimeout(function () {\r\n callback();\r\n }, this.CALLBACK_TIMEOUT);\r\n }\r\n }\r\n }\r\n };\r\n\r\n if (isJson) {\r\n xhr.setRequestHeader(\"Content-type\", \"application/json\");\r\n }\r\n else {\r\n xhr.setRequestHeader(\"Content-type\", \"application/x-www-form-urlencoded\");\r\n }\r\n\r\n xhr.send(data);\r\n }\r\n\r\n /**\r\n * Handles the click event on the attachment link. Using blob for iOS pwa, default behavior for al the rest (ignored).\r\n * @param {MouseEvent} event The event data for the click.\r\n */\r\n async downloadFileBlob(event) {\r\n // Only apply on iOS PWA environments.\r\n if (!leanformsNext.isIosPWA) {\r\n return;\r\n }\r\n\r\n event.preventDefault();\r\n\r\n const fileUrl = event.target.getAttribute('href');\r\n const filename = event.target.textContent.trim();\r\n\r\n await core.fetch({ url: fileUrl})\r\n .catch(leanformsNext.markNetworkError)\r\n .then(response => response.blob())\r\n .then(blob => {\r\n // Create a data URL from the blob.\r\n const blobUrl = window.URL.createObjectURL(blob);\r\n \r\n // Create a temporary link and trigger download.\r\n const tempLink = document.createElement('a');\r\n tempLink.style.display = 'none';\r\n tempLink.href = blobUrl;\r\n tempLink.setAttribute('download', filename);\r\n tempLink.setAttribute('target', '_blank');\r\n \r\n // Add to DOM, click and remove.\r\n document.body.appendChild(tempLink);\r\n tempLink.click();\r\n \r\n // Clean up.\r\n setTimeout(() => {\r\n window.URL.revokeObjectURL(blobUrl);\r\n document.body.removeChild(tempLink);\r\n }, 100);\r\n })\r\n .catch(error => {\r\n alert('Download failed:\\n' + error);\r\n \r\n // Fallback -> direct navigation.\r\n window.open(url, '_blank');\r\n });\r\n }\r\n\r\n /**\r\n * Generic fetch.\r\n * @param {Object} config Any configuration required, to be merged with the default.\r\n * @param {String} url The url of the API to call.\r\n * @returns {Promise} A promise with the returned result.\r\n */\r\n async fetch({ config = Object.create(null), url = \"\" }) {\r\n // Default options are marked with *.\r\n const defaults = {\r\n // *Default, no-cache, reload, force-cache, only-if-cached.\r\n cache: \"no-cache\",\r\n\r\n // Include, *same-origin, omit.\r\n credentials: \"same-origin\",\r\n\r\n // *GET, POST, PUT, DELETE, etc.\r\n method: \"GET\",\r\n\r\n // No-cors, cors, *same-origin\r\n mode: \"cors\",\r\n\r\n // Manual, *follow, error.\r\n redirect: \"follow\",\r\n\r\n // No-referrer, *client.\r\n referrer: \"no-referrer\"\r\n };\r\n\r\n // Combine with defaults.\r\n const options = { ...defaults, ...config };\r\n\r\n // We need headers - at least for the antiforgeryHeader.\r\n if (!options.headers) {\r\n options.headers = {};\r\n }\r\n\r\n // When posting FormData do not set the content type.\r\n if (config.body?.constructor.name.localeCompare(\"FormData\", undefined, { sensitivity: 'accent' }) !== 0) {\r\n // Check if a content-type header is provided in the config.\r\n if (!options.headers[\"Content-Type\"]) {\r\n options.headers[\"Content-Type\"] = \"application/json; charset=utf-8\";\r\n }\r\n }\r\n\r\n // We defined the verification token header to be 'X-XSRF-TOKEN' in the startup configuration.\r\n // See ConfigureSecurity in Web.Framework.Infrastructure.Extensions.\r\n const antiforgeryToken = document.querySelector(\"[name=__RequestVerificationToken]\").value;\r\n options.headers[antiforgeryHeader] = antiforgeryToken;\r\n\r\n const response = await fetch(url, options);\r\n\r\n // Return an empty string when 'no-content' (204) is returned.\r\n if (response.status === 204) {\r\n return \"\";\r\n }\r\n\r\n // Return the response for the implementer to deal with.\r\n return response;\r\n }\r\n\r\n /**\r\n * Gets the URL of the embedded page, from the parent URL.\r\n * @returns {URL} The URL object for the embedded page.\r\n */\r\n getEmbedUrl() {\r\n const pageUrlParameter = decodeURIComponent(this.getQueryParameter(\"url\"));\r\n\r\n return new URL(pageUrlParameter);\r\n }\r\n\r\n /**\r\n * Allows you to accurately obtain an element's em value in pixels. This makes dealing with the cascading inheritance of ems simple.\r\n * It can also be used to find the root em (rem) value of a page.\r\n * See: http://www.matanich.com/2013/06/24/em-values-javascript/\r\n * \r\n * @returns {Int} The pixel value to use within javascript.\r\n */\r\n getEmPixels() {\r\n // Create and style a test element.\r\n const testElement = document.createElement(\"i\");\r\n testElement.style.cssText = this.#emStyle;\r\n document.documentElement.appendChild(testElement);\r\n const value = testElement.clientWidth;\r\n document.documentElement.removeChild(testElement);\r\n\r\n // Return the em value in pixels.\r\n return value;\r\n }\r\n\r\n /**\r\n * Create from data, with the disbabled elements that are excluded by default.\r\n * @param {HTMLFormElement} form The form to get the form data for\r\n * @returns {FormData} The complete formdata object for the given form\r\n */\r\n getFormDataWithDisabled(form, includeDisabled = false) {\r\n const formData = new FormData(form);\r\n const disabledElements = form.querySelectorAll(\":disabled\");\r\n [...disabledElements].forEach(element => {\r\n if (element.type === \"checkbox\" || element.type === \"radio\") {\r\n if (element.checked) {\r\n formData.append(element.name, element.value);\r\n }\r\n }\r\n else if (element.type === \"select-multiple\") {\r\n // Handle multi-select options.\r\n [...element.options].forEach(option => {\r\n if (option.selected) {\r\n formData.append(element.name, option.value);\r\n }\r\n });\r\n }\r\n else {\r\n // Append other disabled fields.\r\n formData.append(element.name, element.value);\r\n }\r\n });\r\n\r\n return formData;\r\n }\r\n\r\n /**\r\n * Gets the value of a provided parameter from a querystring, handles complex urls too.\r\n * @param {String} parameter The querystring parameter to look for.\r\n * @param {Window} reference The window to get the location from.\r\n * @returns {String} The value for the requested param.\r\n */\r\n getQueryParameter(parameter, reference = top.window) {\r\n const params = new Proxy(this.#parseUrl(reference.location.href).params, {\r\n get: (searchParams, prop) => searchParams.get(prop),\r\n });\r\n\r\n return params[parameter];\r\n }\r\n\r\n /**\r\n * Gets the 'page' parameter in a given URL.\r\n * @param {Window} reference The window to get the location from.\r\n * @returns {String} The value for the requested param.\r\n */\r\n getPageQueryParameter(reference = top.window) {\r\n const params = new Proxy(this.#parseUrl(reference.location.href, true).params, {\r\n get: (searchParams, prop) => searchParams.get(prop),\r\n });\r\n\r\n return params[\"page\"];\r\n }\r\n\r\n /**\r\n * Hides a toast notification.\r\n * @param {Object} notification The notification.\r\n */\r\n hideNotification(notification) {\r\n this.#toast.hide(notification);\r\n }\r\n\r\n /**\r\n * Decode a HTML encoded string back to Html (safe - no script is evaluated).\r\n * @param {String} input The content to decode.\r\n */\r\n htmlDecode(input) {\r\n var doc = new DOMParser().parseFromString(input, \"text/html\");\r\n return doc.documentElement.textContent;\r\n }\r\n\r\n /**\r\n * Use this function to encode text for use as HTML element content (not attribute or elsewhere)\r\n * \r\n * Using .textContent or $.text() directly is preferred.\r\n * If moving away from HTML generation with strings is not reasonable, use this function.\r\n * \r\n * @param {string} htmlstring Text that should be HTML encoded\r\n */\r\n htmlEncode(htmlstring) {\r\n let el = document.createElement(\"p\");\r\n el.textContent = htmlstring;\r\n\r\n return el.innerHTML;\r\n }\r\n\r\n /**\r\n * Checks if a given element is visible within the viewport.\r\n * @param {HTMLELement} element The element to validate against.\r\n * @param {Integer} offset How many pixels to use as a buffer for the position check (take into account a fixed header par example).\r\n * @param {HTMLELement} container The container that contains the element, defaults to document.documentElement.\r\n * @returns True when the element is visible, false if not.\r\n */\r\n isElementInView(element, offset = 0, container = document.documentElement) {\r\n const rect = element.getBoundingClientRect();\r\n\r\n return rect.top >= offset &&\r\n rect.left >= offset &&\r\n rect.bottom <= container.clientHeight - offset &&\r\n rect.right <= container.clientWidth - offset;\r\n }\r\n\r\n /**\r\n * Checks if a given element is visble.\r\n * @param {HTMLELement} element The element to validate against.\r\n * @returns True when the element is visible, false if not.\r\n */\r\n isVisible(element) {\r\n return !!(element?.offsetWidth || element?.offsetHeight || element?.getClientRects?.().length)\r\n && window.getComputedStyle(element).visibility !== \"hidden\";\r\n }\r\n\r\n /**\r\n * Displays a toast notification.\r\n * @param {String} type The message type; info, success, warn, error. Defaults to 'warn'.\r\n * @param {String} message The message text to display.\r\n * @param {Object} options [Optional] Any additional settings to pass on to the toast instance.\r\n */\r\n notify(type, message, options) {\r\n options = options || {};\r\n\r\n if (!options.position) {\r\n const currentWidth = top.window.innerWidth;\r\n options.position = currentWidth > this.BREAKPOINT ? toast.toastPosition.TopCenter : toast.toastPosition.BottomCenter;\r\n }\r\n\r\n // Set default to call.\r\n type = typeof this.#toast[type] === \"function\" ? type : \"warn\";\r\n return this.#toast[type](message ?? resources.validationMessages[\"default\"], options);\r\n }\r\n\r\n /**\r\n * Parses a dimension value , independent of value type.\r\n * @param {String} value The value to parse.\r\n */\r\n parseDimensionValue(value) {\r\n const parsed = parseFloat(value);\r\n if (isNaN(parsed) || !value) {\r\n return 0;\r\n }\r\n\r\n if (value.toString().indexOf(\"em\") !== -1) {\r\n return parsed * this.#rem;\r\n }\r\n\r\n return parsed;\r\n }\r\n\r\n /**\r\n * Retruns a CSS custom property value applied at the element.\r\n * Like: core.readCssVar(\"--color\", document.querySelector(\".box\"));\r\n * @param {String} varName The name of the variable to get the value of.\r\n * @param {DOMElement} element [Optional] The reference element. Defaults to document.documentElement.\r\n */\r\n // eslint-disable-next-line class-methods-use-this -- Cannot use/access static methods within an anonymous class expression.\r\n readCssVar(varName, element = document.documentElement) {\r\n const elementStyles = window.getComputedStyle(element);\r\n const value = elementStyles.getPropertyValue(`${varName}`).trim();\r\n\r\n return value.startsWith(\"\\\"\") ? value.substring(1, value.length - 1) : value;\r\n }\r\n\r\n /**\r\n * Removes the given parameter.\r\n * @param {String} url The url that the query string parameter should be removed.\r\n * @param {String} parameter The parameter to remove. \r\n */\r\n removeQueryParameter(url, parameter) {\r\n return this.setQueryParameter(url, parameter, null);\r\n }\r\n\r\n /**\r\n * Rendering potentially untrusted HTML in a safe manner.\r\n * @param {string} html The string HTML to sanitize.\r\n */\r\n sanitizeHtml(html, allowedTags = []) {\r\n let sanitized = html;\r\n if (allowedTags.length > 0) {\r\n sanitized = DOMPurify.sanitize(html,\r\n {\r\n ADD_ATTR: [\"target\"],\r\n ALLOWED_TAGS: allowedTags\r\n });\r\n }\r\n else {\r\n sanitized = DOMPurify.sanitize(html,\r\n {\r\n USE_PROFILES:\r\n {\r\n html: true\r\n },\r\n ADD_ATTR: [\"target\"]\r\n });\r\n }\r\n \r\n return sanitized;\r\n }\r\n\r\n /**\r\n * Replaces or adds the given parameter with its corresponding value.\r\n * The method only looks at the last '?' for indicating the search params, so this will also work in complex urls.\r\n * @param {String} url The url that needs it query string parameters modified.\r\n * @param {String} parameter The parameter to replace or add. \r\n * @param {Object} value The value for the parameter.\r\n */\r\n setQueryParameter(url, parameter, value) {\r\n if (url === \"about:blank\" || url === \"\") {\r\n return url;\r\n }\r\n\r\n // Get url parts.\r\n const urlObject = this.#parseUrl(url);\r\n const path = urlObject.path;\r\n const urlSearchParams = urlObject.params;\r\n\r\n // Remove parameter when value is null.\r\n if (value === null) {\r\n urlSearchParams.delete(parameter);\r\n }\r\n else {\r\n urlSearchParams.set(parameter, value);\r\n }\r\n\r\n const outputParams = [];\r\n\r\n for (let [key, value] of urlSearchParams.entries()) {\r\n if (key !== \"page\") {\r\n value = encodeURIComponent(value);\r\n }\r\n\r\n outputParams.push(`${key}=${value}`);\r\n }\r\n\r\n // Join again.\r\n return `${path}${outputParams.length ? \"?\" + outputParams.join(\"&\") : \"\"}`;\r\n }\r\n \r\n /**\r\n * Sets the URL of the embedded page to the parent URL.\r\n * @param {URL} url The embedded url should be set to the parent.\r\n * @param {Boolean} url True to use pushState, false to use replaceState.\r\n */\r\n setEmbedUrl(url, pushState = false) {\r\n const parentUrl = new URL(top.window.location);\r\n parentUrl.searchParams.set(\"url\", url);\r\n \r\n const historyFunction = pushState ? \"pushState\" : \"replaceState\";\r\n top.window.history[historyFunction](null, \"\", parentUrl);\r\n }\r\n\r\n /**\r\n * Returns true when the current browser supports offline behavior.\r\n */\r\n supportsOffline() {\r\n // Not every feature is tested, only those that are easy to check.\r\n const supportServiceWorker = \"serviceWorker\" in navigator;\r\n const supportsFetch = \"fetch\" in window;\r\n const supportsPromise = \"Promise\" in window;\r\n const supportsSearchParams = \"URLSearchParams\" in window;\r\n\r\n return [\r\n supportServiceWorker,\r\n supportsFetch,\r\n supportsPromise,\r\n supportsSearchParams\r\n ].every((feature) => {\r\n return feature === true;\r\n });\r\n }\r\n\r\n /**\r\n * Toggles the visibility of the element.\r\n * Ignore when no element is given.\r\n * @param {HTMLElement} el The element to toggle its display state for.\r\n */\r\n toggleDisplayState(el) {\r\n if (el) {\r\n if (el.style.display === \"none\") {\r\n el.style.display = \"\";\r\n }\r\n else {\r\n el.style.display = \"none\";\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Decode s aprovided string (back) to valid xml.\r\n * @param {String} str The string to decode to proper xml.\r\n */\r\n xmlDecode(str) {\r\n if (!str) {\r\n return \"\";\r\n }\r\n\r\n return str.replace(/'/g, \"'\").\r\n replace(/"/g, \"\\\"\").\r\n replace(/>/g, \">\").\r\n replace(/</g, \"<\").\r\n replace(/&/g, \"&\");\r\n }\r\n\r\n /**\r\n * Encode a string of xml to something that we can safely send over https.\r\n * @param {String} str The xml to encode.\r\n */\r\n xmlEncode(str) {\r\n if (!str) {\r\n return \"\";\r\n }\r\n\r\n return str.replace(/&/g, \"&\").\r\n replace(//g, \">\").\r\n replace(/\"/g, \""\").\r\n replace(/'/g, \"'\");\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Parses the url parameters, including complex urls.\r\n * @param {String} url The url string to parse the urls parameters for.\r\n * @param {Boolean} getPage When we want the full page querystring parameter, as ReturnUrl par example. Defaults to false.\r\n * @returns {Object} Contains the path and URLSearchParams.\r\n */\r\n #parseUrl(url, getPage = false) {\r\n const viewIndex = url.lastIndexOf(\"?page\");\r\n\r\n if (getPage) {\r\n\r\n return {\r\n path: url.slice(0, viewIndex),\r\n params: new URLSearchParams(url.slice(viewIndex))\r\n }\r\n }\r\n \r\n const index = url.lastIndexOf(\"?\");\r\n var isEmptyComplexurl = false;\r\n if (viewIndex === index) {\r\n // We have a complex url without a queryParameter for the page url.\r\n isEmptyComplexurl = true;\r\n }\r\n\r\n const path = isEmptyComplexurl ? url : url.slice(0, index);\r\n const searchParams = isEmptyComplexurl ? \"\" : url.slice(index);\r\n\r\n return {\r\n path: path,\r\n params: new URLSearchParams(searchParams)\r\n }\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}();\r\n// #endregion ************************************************************************************/\r\n","// #region: Class definition *********************************************************************/\r\n/**\r\n * Handles validation on data elements and forms.\r\n * \r\n * Depends on the core and dating classes and the validator component (class).\r\n */\r\nwindow.validation = new class {\r\n\r\n // #region: Private Fields *******************************************************************/\r\n #hideTimeOut;\r\n #minYear;\r\n #maxYear;\r\n #validateTypes;\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Constructor **********************************************************************/\r\n /**\r\n * The constructor does work that needs to be executed exactly once.\r\n */\r\n constructor() {\r\n\r\n // Magic numbers.\r\n this.#hideTimeOut = 0;\r\n\r\n /**\r\n * All the types we want to validate.\r\n * @type {Regex}\r\n */\r\n this.#validateTypes = /^(?:checkbox|color|date|datetime|datetime-local|email|file|month|number|password|radio|range|search|select-multiple|select-one|tel|text|textarea|time|url|week)$/ui;\r\n\r\n /**\r\n * The resources for custom messages.\r\n * @type {Object}\r\n */\r\n this.validationMessages = resources.validationMessages;\r\n\r\n // Listen.\r\n eventBroadcaster.subscribe({ message: broadcasts.ADD_VALIDATORS, subscriber: this });\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Build ****************************************************************************/\r\n /**\r\n * Identifies class.\r\n * @returns {string} The string representation of this class.\r\n */\r\n toString() {\r\n return \"[validation]\";\r\n }\r\n\r\n static toString() {\r\n return \"[validation]\";\r\n }\r\n\r\n /** \r\n * Identifies the version.\r\n * @returns {string} The version number of this class.\r\n */\r\n static version() {\r\n return \"1.0.0\";\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Events ***************************************************************************/\r\n /**\r\n * Handle top level DOM events.\r\n * @param {Event} e The event data.\r\n */\r\n handleBroadcast({ detail, message }) {\r\n switch (message) {\r\n\r\n case broadcasts.ADD_VALIDATORS: {\r\n // Only set custom messages when provided.\r\n if (detail?.validationMessages) {\r\n this.validationMessages = detail.validationMessages;\r\n }\r\n\r\n const form = detail?.form;\r\n if (form) {\r\n form.addEventListener(\"submit\", this.onFormSubmitHandler.bind(this), { capture: true });\r\n form.addEventListener(\"reset\", this.onFormResetHandler.bind(this), { capture: true });\r\n\r\n // All form elements.\r\n [...form.elements].forEach((formelement) => {\r\n this.addElementValidation(formelement);\r\n });\r\n\r\n // Any elements we want to force a validator on.\r\n [...document.querySelectorAll(\"[data-validator]\")].forEach((element) => {\r\n this.addElementValidation(element, true);\r\n });\r\n }\r\n\r\n break;\r\n }\r\n\r\n default:\r\n break;\r\n\r\n }\r\n }\r\n\r\n /**\r\n * Handles form reset.\r\n * @param {Event} e The fired reset event.\r\n */\r\n onFormResetHandler(e) {\r\n this.clear(e.target);\r\n [...e.target.elements].forEach((element) => {\r\n element.validator?.hideError();\r\n });\r\n }\r\n\r\n /**\r\n * Handles/captures the form submit event, so we can do our own validation.\r\n * @param {Event} e The fired submit event.\r\n */\r\n onFormSubmitHandler(e) {\r\n // It's an actual intended submit, check if we should validate in the first place.\r\n const bypassSubmit = !!(this.hasAttribute && this.hasAttribute(\"formnovalidate\"));\r\n\r\n // Should we bypass validation or are we not valid (last one in the checks).\r\n if (!bypassSubmit && !this.validateForm({ container: document, form: e.target })) {\r\n e.preventDefault();\r\n }\r\n\r\n // If we are here the submit will normally execute.\r\n }\r\n\r\n /**\r\n * Handles the HTML5forms constraint invalid event on the element.\r\n * @param {Event} e The event data contains a message in the details when we fired a custom event ourselves.\r\n */\r\n onInvalidHandler(e) {\r\n e.preventDefault();\r\n e.target.validator?.showError({ message: e.detail?.message ?? this.getMessage(e.target) });\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Public Methods *******************************************************************/\r\n /**\r\n * Attach validation events to the provided (input) element.\r\n * When a validator is present, it will be overwritten, i.e. only one validator allowed per element.\r\n * @param {HTMLElement} element The (input) element to add validation on.\r\n * @param {Boolean} force When set to true allow the validator on a none '#validateTypes' element.\r\n */\r\n addElementValidation(element, force = false) {\r\n // Only attach validators to the elements we're interested in.\r\n const elementType = this.#getElementType(element);\r\n if (this.#validateTypes.test(elementType) || force) {\r\n const validatorParent = element.dataset?.validatorparent;\r\n const validateOnBlur = element.hasAttribute(\"data-validate-onblur\");\r\n\r\n element.addEventListener(\"invalid\", this.onInvalidHandler.bind(this), { capture: true });\r\n element.validator = new validator({\r\n field: element,\r\n validatorParent: validatorParent,\r\n validateOnBlur: validateOnBlur,\r\n hideTimeout: this.#hideTimeOut\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Gets a custom message for default validity messages.\r\n * These messages default to the browser language settings, which might not be what you want.\r\n * This also gives you the opportunity to nuance certain message types.\r\n * @param {HTMLElement} element The (input/form) element to clear the message(s) for.\r\n * @param {Boolean} firstOnly True to return only the first message, false to concatenate results. Defaults to true.\r\n */\r\n getMessage(element, firstOnly = true) {\r\n\r\n // Local function to get first 'true' property.\r\n const getFirst = (validity) => {\r\n for (const key in validity) {\r\n if (key !== \"valid\" && validity[key]) {\r\n return key;\r\n }\r\n };\r\n return undefined;\r\n };\r\n\r\n if (firstOnly) {\r\n const first = getFirst(element.validity);\r\n return this.#createMessage(element, first);\r\n }\r\n else {\r\n let errorMessage = \"\";\r\n\r\n // eslint-disable-next-line guard-for-in -- Validity is a system defined object that has no additional properties that we need to check for.\r\n for (const validationProperty in element.validity) {\r\n if (element.validity[validationproperty]) {\r\n const message = this.#createMessage(element, validationProperty)\r\n if (message) {\r\n errorMessage += `${message}\\n`;\r\n }\r\n }\r\n }\r\n\r\n return errorMessage.trim();\r\n }\r\n }\r\n\r\n /**\r\n * Hides an error marker when valid.\r\n * @param {Object} element The element the error should be removed from.\r\n */\r\n hideError(element) {\r\n element?.validator?.hideError();\r\n }\r\n\r\n /**\r\n * Validates if a given string can be evaluated to a number (int or float).\r\n * @param {String} numericText The string to validate.\r\n * @param {Boolean} positiveIntOnly True to check exclusively if the text is a positive integer, false to allow 0, negative and decimal point.\r\n * @returns {Boolean} True when a valid number, false if not.\r\n */\r\n isNumeric(numericText, positiveIntOnly) {\r\n if (positiveIntOnly) {\r\n // Match only digits, no leading zeros.\r\n const positiveNumberRegex = /^([1-9][0-9]*)$/;\r\n\r\n return positiveNumberRegex.test(numericText);\r\n }\r\n\r\n const numberRegex = /^(-)?(\\d*)(\\.?)(\\d*)$/;\r\n\r\n return numberRegex.test(numericText);\r\n }\r\n\r\n /**\r\n * Clear any error messages on the element.\r\n * @param {HTMLElement} element The (input/form) element to clear the message(s) for.\r\n * @param {Boolean} all True to reset every element child.\r\n */\r\n resetValidity(element, all = false) {\r\n if (all) {\r\n element = element.querySelector(\"form\");\r\n }\r\n\r\n if (element.nodeName === \"FORM\") {\r\n [...element.elements].forEach((formelement) => {\r\n this.hideError(formelement);\r\n });\r\n\r\n // Any bubbles still left?\r\n [...element.querySelectorAll(\".bubble\")].forEach((bubble) => {\r\n bubble.parentElement.removeChild(bubble);\r\n });\r\n }\r\n else {\r\n this.hideError(element);\r\n }\r\n }\r\n\r\n /**\r\n * Make sure the control is visible within the viewport without giving it focus (that would hide the message).\r\n * @param {HTMLELement} element The element that should be visible.\r\n * @param {Integer} scrollOffset How many pixels to use as a buffer for the position check.\r\n * Take into account a fixed header par example, defaults to 0.\r\n */\r\n scrollIntoView(element, scrollOffset) {\r\n if (element && !core.isElementInView(element, scrollOffset)) {\r\n document.documentElement.scrollTo({\r\n top: element.getBoundingClientRect().top + document.documentElement.scrollTop - scrollOffset,\r\n left: 0,\r\n behavior: \"smooth\"\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Sets the error message.\r\n * @param {HTMLELement} element The element to set the error message on.\r\n * @param {String} message The message to use.\r\n * @return {Boolean} Always false as we're setting an error message.\r\n */\r\n setErrorMessage(element, message) {\r\n element.dataset.validationmessage = message;\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Displays an error marker, attached to the element.\r\n * @param {Object} element The element the error should be attached to.\r\n * @param {String} msg The message to display.\r\n */\r\n showError(element, msg) {\r\n // When an element is validated, but invisible, the error is not shown (nothing to attach to).\r\n // If the validatorparent is set and visible we can display an error.\r\n const validatorParent = element.dataset?.validatorparent;\r\n const validatorParentElement = element.closest(\"form\").querySelector(validatorParent) ?? element.parentElement;\r\n if (core.isVisible(element) || core.isVisible(validatorParentElement)) {\r\n // If no message is provided, try to get it from the element.\r\n msg ??= this.getMessage(element);\r\n if (!element.validator) {\r\n // Other libraries might have messed up the reference.\r\n element.validator = new validator({\r\n field: element,\r\n hideTimeout: this.#hideTimeOut,\r\n validatorParent: validatorParent\r\n });\r\n }\r\n\r\n element.validator.showError({ message: msg });\r\n }\r\n\r\n // We're showing an error, always return false.\r\n return false;\r\n }\r\n\r\n /**\r\n * Validate the element.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element (input) to check.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateAdditional({ event = null, element = null }) {\r\n let isValid = true;\r\n\r\n const forceValidate = element?.hasAttribute(\"data-force-validate\");\r\n\r\n if (isValid || forceValidate) {\r\n isValid = this.validateFormat({ event: event, element: element });\r\n }\r\n\r\n if ((isValid || forceValidate) && element?.hasAttribute(\"data-dateformat\")) {\r\n isValid = this.validateDateTime({ event: event, element: element, format: element.dataset.dateformat });\r\n }\r\n\r\n if ((isValid || forceValidate) && element?.hasAttribute(\"data-accept\") && element?.hasAttribute(\"data-files\") && element.files.length) {\r\n isValid = this.validateAccept({ event: event, element: element });\r\n }\r\n\r\n if ((isValid || forceValidate) && element?.hasAttribute(\"data-filesize\")) {\r\n isValid = this.validateFileSize({ event: event, element: element });\r\n }\r\n\r\n return isValid;\r\n }\r\n\r\n /**\r\n * Validate the accept parameter.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element (input) to check.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateAccept({ event = null, element = null }) {\r\n const files = element?.files;\r\n const acceptedArray = this.#commaSplit(element?.accept);\r\n if (files?.length > 0 && acceptedArray?.length > 0) {\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n const extension = file.name.substr(file.name.lastIndexOf(\".\"));\r\n if (acceptedArray.includes(extension)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return this.setErrorMessage(element,\r\n (element.dataset.invalidaccept ?? this.validationMessages.invalidAccept).replace(\"{Extensions}\", acceptedArray));\r\n }\r\n\r\n /**\r\n * Validates if two elements are equal.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element (input) to check.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateCompare({ event = null, element = null }) {\r\n const compareTo = element.dataset.compareto;\r\n const compareToElement = element.form.querySelector(\"#\" + compareTo);\r\n if (compareToElement) {\r\n if (element.value === compareToElement.value) {\r\n return true;\r\n }\r\n }\r\n\r\n return this.setErrorMessage(element, element.dataset.comparetomessage);\r\n }\r\n\r\n /**\r\n * Validate if the provided string can be parsed to a valid datetime for the format given.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element (input) to check.\r\n * @param {String} format The string that sets the format that it's suppose to be.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateDateTime({ event = null, element = null, format = null }) {\r\n try {\r\n // Check first if we have a valid date in the first place.\r\n const isDateString = dating.isDateStr(element.value);\r\n if (isDateString === \"\") {\r\n // Get a standard ISO 8601 string.\r\n const parsedDate = dating.parseDateString(element.value, format);\r\n if (!isNaN(Date.parse(parsedDate))) {\r\n return true;\r\n }\r\n }\r\n\r\n return this.setErrorMessage(element, element.dataset.invaliddatemessage ?? this.validationMessages.invalidDate);\r\n }\r\n catch {\r\n return this.setErrorMessage(element, element.dataset.invaliddatemessage ?? this.validationMessages.invalidDate);\r\n }\r\n }\r\n\r\n /**\r\n * Validate the allowed file size.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element (input) to check.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateFileSize({ event = null, element = null }) {\r\n const files = element?.files;\r\n\r\n if (files?.length > 0) {\r\n const allowedSize = parseInt(element.getAttribute(\"filesize\") ?? \"0.01\");\r\n if (files[0].size < allowedSize) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Validate all elements in the form, call this before submitting data.\r\n * @param {HTMLDocument} container The document to scan for forms that could/should have validators applied to its children.\r\n * @param {HTMLFormElement} form The form to validate.\r\n * @param {Integer} scrollOffset How many pixels to use as a buffer for the position check.\r\n * Take into account a fixed header par example, defaults to 0.\r\n * @param {Boolean} validateAll True to validate all (default), false to stop validation on first invalid result.\r\n * @param {Function} customValidation Function to also run as part of the validation.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateForm({ container, form, scrollOffset = 0, validateAll = true, customValidation = null }) {\r\n // When no form is provided, we assume we're validating the given container or the whole document when container is null.\r\n container ??= document;\r\n form ??= container.querySelector(\"form\");\r\n\r\n // Local function to validate the individual element.\r\n const validateElement = (element) => {\r\n // When the parent element is hidden always return true.\r\n // When we have the 'data-force-validate' attribute something else is at play.\r\n const forceValidate = element?.hasAttribute(\"data-force-validate\");\r\n if ((!forceValidate && !core.isVisible(element)) || !core.isVisible(element.parentElement)) {\r\n return true;\r\n }\r\n\r\n // Validate the element and return immediately when requested.\r\n if (element.validator) {\r\n element.validator.isFormValidation = true;\r\n\r\n // When doing form validation we also validate the required elements.\r\n return element.validator.validate({ required: true });\r\n }\r\n\r\n // When validation isn't possible (no validator found), return true.\r\n return true;\r\n }\r\n\r\n // We assume valid.\r\n let isValid = true;\r\n\r\n // Check if we should validate in the first place.\r\n const ignoreValidation = !!form?.hasAttribute(\"formnovalidate\");\r\n if (!ignoreValidation) {\r\n const elements = [...form.elements];\r\n elements.push(...[...document.querySelectorAll(\"[data-validator]\")]);\r\n\r\n // 'Every' returns false immediately when a 'falsy' value is returned from the inner callback.\r\n if (!validateAll) {\r\n isValid = elements.every((element) => {\r\n return validateElement(element);\r\n });\r\n }\r\n else {\r\n elements.forEach((element) => {\r\n if (!validateElement(element)) {\r\n isValid = false;\r\n }\r\n });\r\n }\r\n\r\n // Do we need to run some more? The customValidation function should return true when valid and false when not.\r\n if (customValidation) {\r\n const validationResult = customValidation();\r\n\r\n // Only modify isValid variable when custom validation fails, do not revert previous invalid results.\r\n if (!validationResult) {\r\n isValid = false;\r\n }\r\n }\r\n }\r\n\r\n // When isValid is false, we have error bubbles.\r\n if (!isValid) {\r\n this.scrollIntoView(container.querySelector(\".bubble\"), scrollOffset);\r\n }\r\n\r\n return isValid;\r\n }\r\n\r\n /**\r\n * Check the element format (custom) property.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element to get the type for.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateFormat({ event = null, element = null }) {\r\n // But when we have the 'data-forcevalidate' attribute something else is at play.\r\n const forceValidate = element?.hasAttribute(\"data-force-validate\");\r\n if (!forceValidate && (!core.isVisible(element) || element.hasAttribute(\"disabled\"))) {\r\n return true;\r\n }\r\n\r\n const format = element.getAttribute(\"format\");\r\n switch (format) {\r\n case \"date\":\r\n return this.#validateDate({ event, element });\r\n\r\n case \"email\":\r\n return this.#validateEmail({ event, element });\r\n\r\n case \"number\":\r\n return this.validateNumber({ event, element });\r\n \r\n case \"text\":\r\n return this.#validateMinMax({ event, element });\r\n\r\n default:\r\n return true;\r\n }\r\n }\r\n\r\n /**\r\n * Validate the checkbox/radio group.\r\n * When called we assume at least one should be checked.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLElement} element The element to check.\r\n * @param {Array} items All the items in the group. Can be just one.\r\n * @param {HTMLElement} container Where to look for the checkbox elements, defaults to 'document'.\r\n * @returns {Boolean} True when the group is valid, false when invalid.\r\n */\r\n validateGroup({ event = null, element = null, items = null, container = document }) {\r\n let itemsChecked = +element?.dataset?.minimumchecked;\r\n if (isNaN(itemsChecked)) {\r\n itemsChecked = 1;\r\n }\r\n\r\n if (items.filter((item) => item.checked).length < itemsChecked) {\r\n if (itemsChecked === 1) {\r\n // When the element is a radio group or a single checkbox, use the default required message.\r\n this.setErrorMessage(element,\r\n element.dataset.valuemissingmessage ??\r\n (element.type === \"radio\" || items.length === 1\r\n ? this.validationMessages.required\r\n : this.validationMessages.selectOne));\r\n }\r\n else {\r\n this.setErrorMessage(element,\r\n (element.dataset.valuemissingmessage ?? this.validationMessages.selectMinimum)\r\n .replace(\"{itemschecked}\", itemsChecked));\r\n }\r\n\r\n // Indicate all members of the group as invalid.\r\n [...items].forEach((item) => {\r\n item.validator?.setInvalid();\r\n });\r\n\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Validates if a given input adheres to its rules (being a number).\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateNumber({ event = null, element = null }) {\r\n // Empty is validated by the 'required' property.\r\n if (element == null || element.value.trim() === \"\") {\r\n return true;\r\n }\r\n\r\n if (!this.isNumeric(element.value, element.hasAttribute(\"data-whole-positive-only\"))) {\r\n // Not valid.\r\n this.setErrorMessage(element, element.dataset.invalidnumbermessage ?? this.validationMessages.invalidNumber);\r\n\r\n return false;\r\n }\r\n\r\n let isValid = true;\r\n const forceValidate = element.hasAttribute(\"data-force-validate\");\r\n\r\n const decimalsRequired = parseInt(element.getAttribute(\"decimals\"));\r\n const decimalValue = element.value?.split(\".\") ?? \"\";\r\n\r\n if (decimalValue.length > 1 && decimalValue[1].length > decimalsRequired) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invaliddecimalsmessage ?? this.validationMessages.invalidDecimals).replace(\"{decimals}\", decimalsRequired));\r\n\r\n isValid = false;\r\n if (!forceValidate) {\r\n return isValid;\r\n }\r\n }\r\n\r\n // Boolean checks on these variables work correctly with 0 because type is string.\r\n let min = element.getAttribute(\"Min\"),\r\n max = element.getAttribute(\"Max\"),\r\n minRange = element.getAttribute(\"MinRange\"),\r\n maxRange = element.getAttribute(\"MaxRange\");\r\n\r\n // None of the properties are set.\r\n if (!min && !max && !minRange && !maxRange) {\r\n return true;\r\n }\r\n\r\n // Set range state.\r\n this.validateRangeState({ element });\r\n\r\n // Validate value (min/max).\r\n let val = parseFloat(element.value),\r\n minOk = true,\r\n maxOk = true;\r\n\r\n if (min) {\r\n minOk = val >= parseFloat(min);\r\n }\r\n\r\n if (max) {\r\n maxOk = val <= parseFloat(max);\r\n }\r\n\r\n if (!minOk || !maxOk) {\r\n if (min && max) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invalidrangemessage ?? this.validationMessages.invalidRange)\r\n .replace(\"{rangestart}\", min).replace(\"{rangeend}\", max));\r\n\r\n isValid = false;\r\n if (!forceValidate) {\r\n return isValid;\r\n }\r\n }\r\n else if (!minOk) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invalidrangeminmessage ?? this.validationMessages.invalidRangeMin)\r\n .replace(\"{rangestart}\", min));\r\n\r\n isValid = false;\r\n if (!forceValidate) {\r\n return isValid;\r\n }\r\n }\r\n else if (!maxOk) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invalidrangemaxmessage ?? this.validationMessages.invalidRangeMax).replace(\"{rangeend}\", max));\r\n\r\n isValid = false;\r\n if (!forceValidate) {\r\n return isValid;\r\n }\r\n }\r\n }\r\n\r\n return isValid;\r\n }\r\n\r\n /** \r\n * Check the input whether a value is within a given 'check range' - setting the background color to indicate validation state (without error).\r\n * @param {HTMLInputElement} element The input to validate.\r\n */\r\n validateRangeState({ element = null }) {\r\n if (element == null) {\r\n return;\r\n }\r\n\r\n // When a value an empty string or the value is not numeric.\r\n if (element.value.trim() === \"\" || !this.isNumeric(element.value, element.hasAttribute(\"data-whole-positive-only\"))) {\r\n // Remove any color indicators.\r\n element.classList.remove(\"range-not-ok\");\r\n element.classList.remove(\"range-ok\");\r\n\r\n return;\r\n }\r\n\r\n // Ranges are an indicator about expected values, indicating the user about the validity of the content without forcing it.\r\n let val = parseFloat(element.value),\r\n minRange = element.getAttribute(\"MinRange\"),\r\n maxRange = element.getAttribute(\"MaxRange\"),\r\n minRangeOk = true,\r\n maxRangeOk = true;\r\n\r\n // When neither value is set, return.\r\n if (!minRange && !maxRange) {\r\n return;\r\n }\r\n\r\n if (minRange) {\r\n minRangeOk = val >= parseFloat(minRange);\r\n }\r\n\r\n if (maxRange) {\r\n maxRangeOk = val <= parseFloat(maxRange);\r\n }\r\n\r\n let rangeNotOk = !minRangeOk || !maxRangeOk,\r\n rangeOk = !rangeNotOk && Boolean(minRange || maxRange);\r\n\r\n element.classList.toggle(\"range-not-ok\", rangeNotOk);\r\n element.classList.toggle(\"range-ok\", rangeOk);\r\n }\r\n\r\n /**\r\n * Validates if a given readonly input adheres to any rules (required only for now).\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateReadOnly({ event = null, element = null }) {\r\n if (element == null || !element.hasAttribute(\"readonly\")) {\r\n // Attibute is missing, thus return true.\r\n return true;\r\n }\r\n\r\n return this.validateRequired({ event: event, element: element });\r\n }\r\n\r\n /**\r\n * Validates if a given readonly input adheres to required rules.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n validateRequired({ event = null, element = null }) {\r\n if (element && element.hasAttribute(\"required\") && element.value?.trim() === \"\") {\r\n return this.setErrorMessage(element, element.dataset.valuemissingmessage ?? this.validationMessages.valuemissing);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Compares two string values for a given comparer.\r\n * @param {Event} event The event data passed.\r\n * @param {String} leftValue The first value to check.\r\n * @param {String} operator The operator to use.\r\n * @param {String} rightValue The (second) value to compare against.\r\n * @returns {Boolean} How the comparison evaluated.\r\n */\r\n validateValueCompare({ event = null, leftValue = null, operator = null, rightValue = null }) {\r\n let isValid = false,\r\n parsedLeftValue,\r\n parsedRightValue;\r\n\r\n if (operator !== \"contains\") {\r\n if (this.#isDateValue(leftValue) && this.#isDateValue(rightValue)) {\r\n parsedLeftValue = dating.getDateFromString(leftValue);\r\n parsedRightValue = dating.getDateFromString(rightValue);\r\n }\r\n else if (!isNaN(leftValue) && !isNaN(rightValue)) {\r\n parsedLeftValue = parseFloat(leftValue);\r\n parsedRightValue = parseFloat(rightValue);\r\n }\r\n else {\r\n parsedLeftValue = leftValue;\r\n parsedRightValue = rightValue;\r\n }\r\n }\r\n else {\r\n parsedLeftValue = leftValue?.toLowerCase() ?? \"\";\r\n parsedRightValue = rightValue?.toLowerCase().trim() ?? \"\";\r\n }\r\n \r\n if (operator === \"=\" && parsedLeftValue == parsedRightValue) { isValid = true; }\r\n if (operator === \"<>\" && parsedLeftValue != parsedRightValue) { isValid = true; }\r\n if (operator === \">\" && parsedLeftValue > parsedRightValue) { isValid = true; }\r\n if (operator === \"<\" && parsedLeftValue < parsedRightValue) { isValid = true; }\r\n if (operator === \">=\" && parsedLeftValue >= parsedRightValue) { isValid = true; }\r\n if (operator === \"<=\" && parsedLeftValue <= parsedRightValue) { isValid = true; }\r\n if (operator === \"contains\" && parsedLeftValue.length > 0 && parsedLeftValue.indexOf(parsedRightValue) > -1) { isValid = true; }\r\n\r\n return isValid;\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n // #region: Private Methods ******************************************************************/\r\n /**\r\n * Split & trim the item in a comma delimited string.\r\n * @param {String} string The string to split on any comma present.\r\n */\r\n #commaSplit(string) {\r\n return string.split(\",\").map((item) => {\r\n return item.trim();\r\n })\r\n .filter((item) => {\r\n return item;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a validation message for the given element and the validation property to get the message for.\r\n * Prefers any custom message from a dataset property.\r\n * @param {HTMLElement} element The element to create the message for.\r\n * @param {String} validationProperty The property to create the message for.\r\n * @returns {String} The message creted.\r\n */\r\n #createMessage(element, validationProperty) {\r\n return element.dataset[`${validationProperty?.toLowerCase()}message`]\r\n ?? element.dataset.validationmessage\r\n ?? this.validationMessages[`${element.type}${validationProperty?.toLowerCase()}`]\r\n ?? this.validationMessages[validationProperty?.toLowerCase()]\r\n ?? (element.validationMessage !== \"\" ? element.validationMessage : null)\r\n ?? this.validationMessages[\"default\"]\r\n ?? \"An error has occurred.\";\r\n }\r\n\r\n /**\r\n * What type of element are we validating.\r\n * @param {HTMLElement} element The element to get the type for.\r\n */\r\n #getElementType(element) {\r\n if (element.nodeName === \"TEXTAREA\") {\r\n return \"textarea\";\r\n }\r\n else if (element.nodeName === \"SELECT\") {\r\n return element.hasAttribute(\"multiple\") ? \"select-multiple\" : \"select-one\";\r\n }\r\n else if (element.nodeName === \"INPUT\") {\r\n return element.getAttribute(\"type\") || element.type || \"text\";\r\n }\r\n\r\n return \"\";\r\n }\r\n\r\n /**\r\n * Checks if a string can represent a date, time or a combination of the two.\r\n * @param {String} dateString The string to validate.\r\n * @returns {Boolean} True when the string represents a valid date, false if not.\r\n */\r\n #isDateValue(dateString) {\r\n if (!dateString || !dateString.trim()) {\r\n return false;\r\n }\r\n\r\n var message = dating.isDateStr(dateString);\r\n if (message !== \"\") {\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Validates if a given input adheres to its rules (being a date/time).\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n #validateDate({ event = null, element = null }) {\r\n let dateString = element?.value;\r\n\r\n // Empty means true. Empty is validated by the 'required' property.\r\n if (!dateString?.trim()) {\r\n return true;\r\n }\r\n\r\n let typeIdMap = { \"0\": \"date\", \"1\": \"time\", \"2\": \"both\" },\r\n typeId = element.getAttribute(\"datetype\"),\r\n message = dating.isDateStr(dateString, typeIdMap[typeId]);\r\n\r\n if (message) {\r\n // Not valid.\r\n return this.setErrorMessage(element, element.dataset.invaliddatemessage ?? message);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Validates if a given input adheres to its rules (being an email address).\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n #validateEmail({ event = null, element = null }) {\r\n // Can't start or end with a period, can't contain whitespace, can only contain a single @.\r\n const filter = /^[^\\.@\\s][^@\\s]*@[^@\\s]+\\.[^@\\s\\.]+$/;\r\n\r\n // Empty is validated by the 'required' property.\r\n if (!element?.value || filter.test(element.value)) {\r\n return true;\r\n }\r\n\r\n // Not valid.\r\n return this.setErrorMessage(element, element.dataset.invalidemailmessage ?? this.validationMessages.invalidEmail);\r\n }\r\n\r\n /**\r\n * Validates if a given input adheres to its rules (min chars, max chars).\r\n * Empty is valid, emptiness should be validated by 'required'.\r\n * @param {Event} event The event data passed.\r\n * @param {HTMLInputElement} element The input to validate.\r\n * @returns {Boolean} True when the input is valid, false when invalid.\r\n */\r\n #validateMinMax({ event = null, element = null }) {\r\n if (!element?.value.trim()) {\r\n return true;\r\n }\r\n\r\n let isValid = true;\r\n const forceValidate = element?.hasAttribute(\"data-force-validate\");\r\n\r\n const minChars = element.getAttribute(\"MinChars\");\r\n const maxChars = element.getAttribute(\"MaxChars\");\r\n const characterLength = element.value.length;\r\n const isInputEvent = event?.type === \"input\";\r\n\r\n if ((!isInputEvent || element.validator?.hasBubble()) && minChars && characterLength < minChars) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invalidmincharsmessage ?? this.validationMessages.invalidMinChars)\r\n .replace(\"{typed}\", characterLength)\r\n .replace(\"{characters}\", minChars));\r\n \r\n isValid = false;\r\n if (!forceValidate) {\r\n return isValid;\r\n }\r\n }\r\n\r\n if (maxChars && characterLength > maxChars) {\r\n // Not valid.\r\n this.setErrorMessage(element,\r\n (element.dataset.invalidmaxcharsmessage ?? this.validationMessages.invalidMaxChars)\r\n .replace(\"{typed}\", characterLength)\r\n .replace(\"{characters}\", maxChars));\r\n\r\n isValid = false;\r\n }\r\n\r\n return isValid;\r\n }\r\n // #endregion ********************************************************************************/\r\n\r\n}();\r\n// #endregion ************************************************************************************/"],"mappings":"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","ignoreList":[]}