{"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","isWindowMessage","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","hasChart","setChartData","chartData","chartId","reloadPage","url","activePage","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","ignoreBubble","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","targetWindow","errorMessage","getMessage","afterValidate","aftervalidate","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","container","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","match1","match2","match3","match4","match1to2","matchSigned","matchOffset","matchWord","addInput","property","input","meridiemMatch","isLowerCase","parseTwoDigitYear","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","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","time","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","invalidTime","dateParts","dayString","monthString","yearString","Number","daysInMonths","daysInFebruary","daysInMonth","isWithin","invalidDay","invalidMonth","invalidYear","timeParts","hoursString","minutesString","parseDateString","datestring","parser","makeParser","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","form","ADD_VALIDATORS","forms","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","filenameRegex","msSaveBlob","URL","webkitURL","downloadUrl","createObjectURL","download","body","click","revokeObjectURL","setRequestHeader","send","fetch","config","Object","create","defaults","cache","credentials","method","redirect","referrer","headers","localeCompare","sensitivity","antiforgeryToken","antiforgeryHeader","getEmbedUrl","pageUrlParameter","decodeURIComponent","getQueryParameter","testElement","cssText","clientWidth","getFormDataWithDisabled","includeDisabled","formData","FormData","disabledElements","checked","option","selected","parameter","reference","Proxy","parseUrl","searchParams","prop","getPageQueryParameter","hideNotification","notification","htmlDecode","doc","DOMParser","parseFromString","textContent","htmlEncode","htmlstring","el","isElementInView","rect","bottom","clientHeight","right","isVisible","offsetWidth","getClientRects","getComputedStyle","visibility","parseDimensionValue","parsed","parseFloat","readCssVar","varName","elementStyles","getPropertyValue","removeQueryParameter","setQueryParameter","sanitizeHtml","allowedTags","sanitized","ALLOWED_TAGS","urlObject","path","urlSearchParams","outputParams","entries","encodeURIComponent","setEmbedUrl","pushState","parentUrl","historyFunction","history","supportsOffline","supportServiceWorker","supportsFetch","supportsPromise","supportsSearchParams","every","feature","toggleDisplayState","display","xmlDecode","str","xmlEncode","getPage","viewIndex","lastIndexOf","URLSearchParams","index","isEmptyComplexurl","hideTimeOut","minYear","maxYear","validateTypes","onFormSubmitHandler","bind","onFormResetHandler","elements","formelement","addElementValidation","clear","target","bypassSubmit","validateForm","onInvalidHandler","force","elementType","getElementType","test","validatorparent","firstOnly","getFirst","first","createMessage","validationProperty","validationproperty","isNumeric","numericText","positiveIntOnly","positiveNumberRegex","numberRegex","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","isDateString","invaliddatemessage","allowedSize","getAttribute","size","validateAll","customValidation","validateElement","ignoreValidation","validationResult","validateDate","validateEmail","validateNumber","validateMinMax","itemsChecked","minimumchecked","valuemissingmessage","selectOne","selectMinimum","invalidnumbermessage","invalidNumber","decimalsRequired","decimalValue","invaliddecimalsmessage","invalidDecimals","minRange","maxRange","validateRangeState","val","minOk","maxOk","invalidrangemessage","invalidRange","invalidrangeminmessage","invalidRangeMin","invalidrangemaxmessage","invalidRangeMax","minRangeOk","maxRangeOk","rangeNotOk","rangeOk","Boolean","toggle","validateRequired","valuemissing","validateValueCompare","leftValue","operator","rightValue","parsedLeftValue","parsedRightValue","isDateValue","string","validationMessage","typeIdMap","typeId","invalidemailmessage","invalidEmail","minChars","maxChars","characterLength","isInputEvent","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.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.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 * 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":[]}