@humanmark/sdk-js
Version:
Browser-native JavaScript SDK for Humanmark human verification challenges
758 lines (757 loc) • 25 kB
JavaScript
import { QRCodeGenerator } from "./QRCodeGenerator-997MoXR0.js";
import { C as CSS_CLASSES, A as ANIMATION_TIMINGS, F as FOCUSABLE_SELECTORS, K as KEYBOARD_KEYS, c as createSVGElement, S as SVG_DIMENSIONS, M as MESSAGES, a as createElement, b as appendChildren, d as ARIA, E as ELEMENT_IDS, U as URLS, q as querySelector, e as addClass, r as removeAllChildren, f as removeClass, g as focusElement, i as isInDOM, h as COLORS, D as DeepLinkHandler } from "./DeepLinkHandler-DqDFWmlg.js";
import { g as getTokenExpiration } from "./index-AC1SXkK1.js";
function detectDevice() {
const userAgent = navigator.userAgent || "";
const uaIsMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
userAgent
);
const hasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0 || window.matchMedia && window.matchMedia("(pointer: coarse)").matches;
const hasSmallViewport = window.innerWidth <= 768 && window.innerHeight <= 1024;
const isMobile = uaIsMobile || hasTouchSupport && hasSmallViewport;
const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !("MSStream" in window);
const isAndroid = /Android/.test(userAgent);
return {
isMobile,
isIOS,
isAndroid,
userAgent
};
}
function shouldShowQRCode() {
const device = detectDevice();
return !device.isMobile;
}
function isMobileDevice() {
const device = detectDevice();
return device.isMobile;
}
const _AccessibilityManager = class _AccessibilityManager {
/**
* Announces a message to screen readers
* @param message - The message to announce
* @param priority - 'polite' waits for current speech, 'assertive' interrupts
*/
static announce(message, priority = "polite") {
if (!this.announcementElement) {
this.announcementElement = document.createElement("div");
this.announcementElement.setAttribute("aria-live", priority);
this.announcementElement.setAttribute("aria-atomic", "true");
this.announcementElement.className = CSS_CLASSES.ACCESSIBILITY.SCREEN_READER_ONLY;
document.body.appendChild(this.announcementElement);
}
this.announcementElement.setAttribute("aria-live", priority);
this.announcementElement.textContent = message;
setTimeout(() => {
if (this.announcementElement) {
this.announcementElement.textContent = "";
}
}, ANIMATION_TIMINGS.ANNOUNCEMENT_CLEAR);
}
/**
* Creates a focus trap within an element
* @param container - The container element to trap focus within
* @returns Cleanup function to remove the trap
*/
static createFocusTrap(container) {
const focusableElements = container.querySelectorAll(
FOCUSABLE_SELECTORS.join(",")
);
if (focusableElements.length === 0) return () => {
};
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
const previouslyFocused = document.activeElement;
const handleKeyDown = (e) => {
if (e.key !== KEYBOARD_KEYS.TAB) return;
if (e.shiftKey) {
if (document.activeElement === firstFocusable && lastFocusable) {
e.preventDefault();
lastFocusable.focus();
}
} else {
if (document.activeElement === lastFocusable && firstFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
};
container.addEventListener("keydown", handleKeyDown);
return () => {
container.removeEventListener("keydown", handleKeyDown);
previouslyFocused?.focus();
};
}
/**
* Adds ARIA attributes for modal dialogs
* @param modal - The modal element
* @param labelId - ID of the element that labels the modal
* @param descriptionId - ID of the element that describes the modal
*/
static setupModalAria(modal, labelId, descriptionId) {
modal.setAttribute("role", "dialog");
modal.setAttribute("aria-modal", "true");
modal.setAttribute("aria-labelledby", labelId);
if (descriptionId) {
modal.setAttribute("aria-describedby", descriptionId);
}
}
/**
* Creates a visually hidden but screen reader accessible element
* @param content - The content for screen readers
* @returns The created element
*/
static createScreenReaderOnly(content) {
const span = document.createElement("span");
span.textContent = content;
span.className = CSS_CLASSES.ACCESSIBILITY.SCREEN_READER_ONLY;
span.setAttribute("aria-hidden", "false");
return span;
}
/**
* Cleanup function to remove announcement element
*/
static cleanup() {
if (this.announcementElement?.parentNode) {
this.announcementElement.parentNode.removeChild(this.announcementElement);
this.announcementElement = null;
}
}
};
_AccessibilityManager.announcementElement = null;
let AccessibilityManager = _AccessibilityManager;
function createSuccessCheckmarkSVG() {
const svg = createSVGElement("svg", {
viewBox: SVG_DIMENSIONS.VIEWBOX
});
const circle = createSVGElement("circle", {
class: "checkmark-circle",
cx: SVG_DIMENSIONS.CIRCLE.CX,
cy: SVG_DIMENSIONS.CIRCLE.CY,
r: SVG_DIMENSIONS.CIRCLE.R,
fill: "none"
});
const path = createSVGElement("path", {
class: "checkmark-check",
d: SVG_DIMENSIONS.CHECK_PATH,
fill: "none"
});
svg.appendChild(circle);
svg.appendChild(path);
return svg;
}
function createCloseButtonSVG(size = 20) {
const svg = createSVGElement("svg", {
width: size.toString(),
height: size.toString(),
viewBox: "0 0 24 24",
class: "humanmark-close-icon",
fill: "none"
});
const path = createSVGElement("path", {
d: "M18 6L6 18M6 6l12 12",
stroke: "currentColor",
// Inherits color from parent button
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round"
});
svg.appendChild(path);
return svg;
}
function createHumanmarkIcon() {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "24");
svg.setAttribute("height", "24");
svg.setAttribute("viewBox", "0 0 1024 1024");
svg.setAttribute("class", "humanmark-icon");
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
path1.setAttribute("fill", "#9580ff");
path1.setAttribute(
"d",
"M 511.367523 495.643921 C 623.741394 495.643921 714.83429 404.54895 714.83429 292.177124 C 714.83429 179.805298 623.741394 88.710327 511.367523 88.710327 C 398.995697 88.710327 307.900726 179.805298 307.900726 292.177124 C 307.900726 404.54895 398.995697 495.643921 511.367523 495.643921 Z"
);
const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
path2.setAttribute("fill", "#9580ff");
path2.setAttribute(
"d",
"M 782.453125 563.264282 C 710.045105 635.674805 613.772278 675.553711 511.365479 675.553711 C 408.964752 675.553711 312.689941 635.674805 240.279861 563.264282 L 37.999184 765.540405 C 119.708984 847.251709 218.834152 905.197937 326.988434 935.999878 C 359.412354 866.653259 429.761475 818.586548 511.365479 818.586548 C 592.975586 818.586548 663.322693 866.653259 695.744568 935.999878 C 803.900879 905.19696 903.028076 847.251709 984.73584 765.540405 L 782.453125 563.264282 Z"
);
g.appendChild(path1);
g.appendChild(path2);
svg.appendChild(g);
return svg;
}
function createProgressBar() {
const container = createElement("div", {
className: CSS_CLASSES.PROGRESS.CONTAINER
});
const progressBar = createElement("div", {
className: CSS_CLASSES.PROGRESS.BAR,
attributes: {
role: "progressbar",
"aria-valuemin": ARIA.PROGRESS.VALUE_MIN,
"aria-valuemax": ARIA.PROGRESS.VALUE_MAX,
"aria-valuenow": ARIA.PROGRESS.VALUE_NOW,
"aria-label": MESSAGES.ACCESSIBILITY.PROGRESS_LABEL
}
});
container.appendChild(progressBar);
return container;
}
function createBrandedTitle(text = MESSAGES.VERIFICATION.TITLE) {
const title = createElement("h2", {
className: CSS_CLASSES.TITLE.CONTAINER,
attributes: {
id: ELEMENT_IDS.TITLE
}
});
const icon = createHumanmarkIcon();
title.appendChild(icon);
const brandSpan = createElement("span", {
className: CSS_CLASSES.TITLE.BRAND,
textContent: text
});
title.appendChild(brandSpan);
return title;
}
function createSubtitle(text) {
return createElement("p", {
className: CSS_CLASSES.SUBTITLE,
textContent: text,
attributes: {
id: ELEMENT_IDS.DESCRIPTION
}
});
}
function createSuccessContent() {
const container = createElement("div", {
className: CSS_CLASSES.SUCCESS.CONTAINER
});
const checkmarkDiv = createElement("div", {
className: CSS_CLASSES.SUCCESS.CHECKMARK
});
checkmarkDiv.appendChild(createSuccessCheckmarkSVG());
const message = createElement("div", {
className: CSS_CLASSES.SUCCESS.MESSAGE,
textContent: MESSAGES.SUCCESS.TITLE
});
const submessage = createElement("div", {
className: CSS_CLASSES.SUCCESS.SUBMESSAGE,
textContent: MESSAGES.SUCCESS.SUBTITLE
});
appendChildren(container, checkmarkDiv, message, submessage);
return container;
}
function createQRCodeContainer() {
const container = createElement("div", {
className: CSS_CLASSES.QR_CODE.CONTAINER
});
const qrWrapper = createElement("div", {
className: CSS_CLASSES.QR_CODE.WRAPPER
});
const instructions = createElement("p", {
className: CSS_CLASSES.QR_CODE.INSTRUCTIONS,
textContent: MESSAGES.VERIFICATION.QR_INSTRUCTIONS
});
container.appendChild(qrWrapper);
container.appendChild(instructions);
return container;
}
function createMobileContainer() {
return createElement("div", {
className: CSS_CLASSES.MOBILE.CONTAINER
});
}
function createModalBody() {
return createElement("div", {
className: CSS_CLASSES.MODAL.BODY
});
}
function createScreenReaderAnnouncement(message) {
return createElement("div", {
className: CSS_CLASSES.ACCESSIBILITY.SCREEN_READER_ONLY,
textContent: message,
attributes: {
role: "status",
"aria-live": "polite"
}
});
}
function createWhatIsThisLink() {
return createElement("a", {
className: CSS_CLASSES.LINKS.WHAT_IS_THIS,
textContent: MESSAGES.VERIFICATION.WHAT_IS_THIS,
attributes: {
href: URLS.WHAT_IS_THIS,
target: "_blank",
rel: "noopener noreferrer"
}
});
}
let cachedScrollbarWidth = null;
function getScrollbarWidth() {
if (cachedScrollbarWidth !== null) {
return cachedScrollbarWidth;
}
const outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.overflow = "scroll";
outer.style.width = "100px";
outer.style.position = "absolute";
outer.style.top = "-9999px";
document.body.appendChild(outer);
const inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
document.body.removeChild(outer);
cachedScrollbarWidth = scrollbarWidth;
return scrollbarWidth;
}
function hasVisibleScrollbar() {
return document.documentElement.scrollHeight > window.innerHeight;
}
function lockBodyScroll() {
if (!isMobileDevice() && hasVisibleScrollbar()) {
const scrollbarWidth = getScrollbarWidth();
if (scrollbarWidth > 0) {
const originalPaddingRight = window.getComputedStyle(
document.body
).paddingRight;
document.body.setAttribute(
"data-humanmark-original-padding",
originalPaddingRight
);
const currentPadding = parseFloat(originalPaddingRight) || 0;
document.body.style.paddingRight = `${currentPadding + scrollbarWidth}px`;
}
}
document.body.classList.add("humanmark-modal-open");
}
function unlockBodyScroll() {
document.body.classList.remove("humanmark-modal-open");
const originalPadding = document.body.getAttribute(
"data-humanmark-original-padding"
);
if (originalPadding !== null) {
document.body.style.paddingRight = originalPadding;
document.body.removeAttribute("data-humanmark-original-padding");
}
}
const _UIManager = class _UIManager {
constructor() {
this.modal = null;
this.focusTrapCleanup = null;
this.modalClosedCallback = null;
this.successCallback = null;
this.progressInterval = null;
this.tokenExpiration = 0;
this._currentState = "initializing";
this.modalOptions = {};
this.closeButtonHandler = () => {
this.setState("cancelled");
this.hideModal();
};
this.backdropClickHandler = (e) => {
if (e.target === this.modal) {
this.setState("cancelled");
this.hideModal();
}
};
this.handleKeyDown = (event) => {
if (event.key === KEYBOARD_KEYS.ESCAPE && this.modalOptions.closeOnEsc !== false) {
this.setState("cancelled");
this.hideModal();
}
};
this.stopPropagation = (event) => {
event.stopPropagation();
};
}
get currentState() {
return this._currentState;
}
set currentState(state) {
this._currentState = state;
}
onModalClosed(callback) {
this.modalClosedCallback = callback;
}
onSuccess(callback) {
this.successCallback = callback;
}
showSuccess() {
if (!this.modal) return;
this.setState("completed");
const modalContent = querySelector(
this.modal,
`.${CSS_CLASSES.MODAL.CONTENT}`
);
if (!modalContent) return;
const modalBody = querySelector(
modalContent,
`.${CSS_CLASSES.MODAL.BODY}`
);
if (!modalBody) return;
addClass(modalBody, CSS_CLASSES.ANIMATIONS.FADE_OUT);
setTimeout(() => {
removeAllChildren(modalBody);
const successContainer = createSuccessContent();
modalBody.appendChild(successContainer);
removeClass(modalBody, CSS_CLASSES.ANIMATIONS.FADE_OUT);
requestAnimationFrame(() => {
addClass(successContainer, CSS_CLASSES.SUCCESS.VISIBLE);
});
AccessibilityManager.announce(
MESSAGES.ACCESSIBILITY.VERIFICATION_SUCCESS,
"assertive"
);
const progressContainer = querySelector(
modalContent,
`.${CSS_CLASSES.PROGRESS.CONTAINER}`
);
if (progressContainer) {
addClass(progressContainer, CSS_CLASSES.PROGRESS.HIDDEN);
}
setTimeout(() => {
if (this.successCallback) {
this.successCallback();
this.successCallback = null;
}
}, ANIMATION_TIMINGS.SUCCESS_DISPLAY);
}, ANIMATION_TIMINGS.FADE_OUT);
}
showModal(options) {
this.modalOptions = options ?? {};
this.setState("initializing");
return Promise.resolve();
}
updateContent(content) {
if (!this.modal) return;
const modalBody = querySelector(
this.modal,
`.${CSS_CLASSES.MODAL.BODY}`
);
if (modalBody) {
while (modalBody.firstChild) {
modalBody.removeChild(modalBody.firstChild);
}
const contentWrapper = createElement("div");
contentWrapper.textContent = content;
modalBody.appendChild(contentWrapper);
}
}
setState(state) {
const previousState = this.currentState;
if (!this.isValidStateTransition(previousState, state)) {
return;
}
this.currentState = state;
const event = new CustomEvent("stateChanged", {
detail: { state, previousState }
});
this.modal?.dispatchEvent(event);
}
isValidStateTransition(from, to) {
const validTransitions = {
initializing: [
"showing-qr",
"waiting-for-verification",
"cancelled",
"error"
],
"showing-qr": ["waiting-for-verification", "cancelled", "error"],
"waiting-for-verification": ["completed", "cancelled", "error"],
completed: [],
// Terminal state
cancelled: [],
// Terminal state
error: []
// Terminal state
};
return validTransitions[from]?.includes(to) ?? false;
}
cleanup() {
this.hideModal(true);
this.modalClosedCallback = null;
this.successCallback = null;
this.stopProgressAnimation();
AccessibilityManager.cleanup();
}
async showVerificationModal(token, options) {
if (this.modal && this.currentState !== "cancelled") {
return;
}
this.modalOptions = options ?? {};
this.hideModal(true);
this._currentState = "initializing";
try {
this.tokenExpiration = getTokenExpiration(token);
} catch {
this.tokenExpiration = Date.now() + 12e4;
}
this.modal = this.createModal();
if (shouldShowQRCode()) {
this.setState("showing-qr");
await this.addQRCodeContent(this.modal, token);
} else {
this.addMobileContent(this.modal, token);
}
this.setState("waiting-for-verification");
document.body.appendChild(this.modal);
lockBodyScroll();
AccessibilityManager.setupModalAria(
this.modal,
_UIManager.TITLE_ID,
_UIManager.DESC_ID
);
this.focusTrapCleanup = AccessibilityManager.createFocusTrap(this.modal);
this.startProgressAnimation();
requestAnimationFrame(() => {
if (!this.modal) return;
if (shouldShowQRCode()) {
const title = querySelector(
this.modal,
`#${_UIManager.TITLE_ID}`
);
focusElement(title, true);
} else {
const verifyButton = querySelector(
this.modal,
`.${CSS_CLASSES.BUTTONS.VERIFY}`
);
focusElement(verifyButton);
}
});
AccessibilityManager.announce(
MESSAGES.ACCESSIBILITY.MODAL_OPENED,
"assertive"
);
this.addKeyboardListeners();
}
hideModal(immediate = false) {
const wasVisible = !!this.modal;
this.stopProgressAnimation();
if (this.modal) {
if (immediate) {
this.cleanupModal();
} else {
addClass(this.modal, CSS_CLASSES.MODAL.CLOSING);
setTimeout(() => {
this.cleanupModal();
}, ANIMATION_TIMINGS.MODAL_CLOSE);
}
} else {
const existingModal = document.getElementById(_UIManager.MODAL_ID);
if (existingModal && document.body.contains(existingModal)) {
document.body.removeChild(existingModal);
}
}
if (wasVisible && this.modalClosedCallback) {
this.modalClosedCallback();
this.modalClosedCallback = null;
}
}
cleanupModal() {
this.stopProgressAnimation();
if (this.modal?.parentNode) {
const modalContent = querySelector(
this.modal,
`.${CSS_CLASSES.MODAL.CONTENT}`
);
if (modalContent) {
modalContent.removeEventListener("click", this.stopPropagation);
const closeButton = querySelector(
modalContent,
`.${CSS_CLASSES.BUTTONS.CLOSE}`
);
if (closeButton) {
closeButton.removeEventListener("click", this.closeButtonHandler);
}
}
this.modal.removeEventListener("click", this.backdropClickHandler);
if (isInDOM(this.modal)) {
document.body.removeChild(this.modal);
}
this.modal = null;
}
unlockBodyScroll();
if (this.focusTrapCleanup) {
this.focusTrapCleanup();
this.focusTrapCleanup = null;
}
AccessibilityManager.announce(
MESSAGES.ACCESSIBILITY.MODAL_CLOSED,
"polite"
);
this.removeKeyboardListeners();
}
createModal() {
const zIndex = this.modalOptions.zIndex?.toString() ?? "";
const modal = createElement("div", {
className: CSS_CLASSES.MODAL.OVERLAY,
attributes: {
id: _UIManager.MODAL_ID,
...zIndex && { style: `z-index: ${zIndex}` }
}
});
const modalContent = createElement("div", {
className: CSS_CLASSES.MODAL.CONTENT
});
const closeButton = createElement("button", {
className: CSS_CLASSES.BUTTONS.CLOSE,
attributes: {
"aria-label": MESSAGES.ACCESSIBILITY.CLOSE_BUTTON_LABEL,
type: "button"
}
});
const closeIcon = createCloseButtonSVG();
closeButton.appendChild(closeIcon);
closeButton.addEventListener("click", this.closeButtonHandler);
modalContent.appendChild(closeButton);
const progressContainer = createProgressBar();
modalContent.appendChild(progressContainer);
const progressBar = querySelector(
progressContainer,
`.${CSS_CLASSES.PROGRESS.BAR}`
);
if (progressBar && this.tokenExpiration) {
const totalSeconds = Math.ceil(
(this.tokenExpiration - Date.now()) / 1e3
);
progressBar.setAttribute("aria-valuemax", totalSeconds.toString());
progressBar.setAttribute("aria-valuenow", totalSeconds.toString());
}
modal.appendChild(modalContent);
modalContent.addEventListener("click", this.stopPropagation);
if (this.modalOptions.closeOnBackdropClick !== false) {
modal.addEventListener("click", this.backdropClickHandler);
}
return modal;
}
async addQRCodeContent(modal, token) {
const content = querySelector(
modal,
`.${CSS_CLASSES.MODAL.CONTENT}`
);
if (!content) return;
const modalBody = createModalBody();
const title = createBrandedTitle();
modalBody.appendChild(title);
const subtitle = createSubtitle(MESSAGES.VERIFICATION.DESKTOP_SUBTITLE);
modalBody.appendChild(subtitle);
const qrCodeContainer = createQRCodeContainer();
const qrWrapper = querySelector(
qrCodeContainer,
`.${CSS_CLASSES.QR_CODE.WRAPPER}`
);
if (qrWrapper) {
try {
const qrCodeImg = await QRCodeGenerator.generateQRCodeElement(token, {
color: {
dark: COLORS.QR_CODE.PRIMARY,
light: COLORS.QR_CODE.LIGHT
}
});
qrWrapper.appendChild(qrCodeImg);
} catch {
const errorMsg = createElement("p", {
className: "humanmark-error",
textContent: MESSAGES.ERRORS.QR_GENERATION_FAILED
});
qrWrapper.appendChild(errorMsg);
}
}
const timeNotice = createScreenReaderAnnouncement(
MESSAGES.ACCESSIBILITY.TIME_NOTICE
);
qrCodeContainer.appendChild(timeNotice);
modalBody.appendChild(qrCodeContainer);
const whatIsThisLink = createWhatIsThisLink();
modalBody.appendChild(whatIsThisLink);
content.appendChild(modalBody);
}
addMobileContent(modal, token) {
const content = querySelector(
modal,
`.${CSS_CLASSES.MODAL.CONTENT}`
);
if (!content) return;
const modalBody = createModalBody();
const title = createBrandedTitle();
modalBody.appendChild(title);
const subtitle = createSubtitle(MESSAGES.VERIFICATION.MOBILE_SUBTITLE);
modalBody.appendChild(subtitle);
const mobileContainer = createMobileContainer();
const verifyButton = DeepLinkHandler.createVerifyButton(token);
addClass(verifyButton, CSS_CLASSES.BUTTONS.VERIFY);
mobileContainer.appendChild(verifyButton);
modalBody.appendChild(mobileContainer);
const whatIsThisLink = createWhatIsThisLink();
modalBody.appendChild(whatIsThisLink);
content.appendChild(modalBody);
}
addKeyboardListeners() {
document.addEventListener("keydown", this.handleKeyDown);
}
removeKeyboardListeners() {
document.removeEventListener("keydown", this.handleKeyDown);
}
startProgressAnimation() {
if (!this.modal) return;
const progressBar = querySelector(
this.modal,
`.${CSS_CLASSES.PROGRESS.BAR}`
);
if (!progressBar) return;
this.stopProgressAnimation();
const totalDuration = this.tokenExpiration - Date.now();
const startTime = Date.now();
this.updateProgress(progressBar, startTime, totalDuration);
this.progressInterval = setInterval(() => {
this.updateProgress(progressBar, startTime, totalDuration);
}, 100);
}
stopProgressAnimation() {
if (this.progressInterval) {
clearInterval(this.progressInterval);
this.progressInterval = null;
}
}
updateProgress(progressBar, startTime, totalDuration) {
if (!this.modal || !isInDOM(this.modal)) {
this.stopProgressAnimation();
return;
}
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, totalDuration - elapsed);
const progress = Math.max(
0,
Math.min(100, elapsed / totalDuration * 100)
);
if (!isInDOM(progressBar)) {
this.stopProgressAnimation();
return;
}
progressBar.style.width = `${100 - progress}%`;
const remainingSeconds = Math.ceil(remaining / 1e3);
progressBar.setAttribute("aria-valuenow", remainingSeconds.toString());
progressBar.setAttribute(
"aria-label",
`${remainingSeconds} seconds remaining for verification`
);
if (remaining <= 0) {
this.stopProgressAnimation();
}
}
};
_UIManager.MODAL_ID = ELEMENT_IDS.MODAL;
_UIManager.TITLE_ID = ELEMENT_IDS.TITLE;
_UIManager.DESC_ID = ELEMENT_IDS.DESCRIPTION;
let UIManager = _UIManager;
export {
UIManager
};
//# sourceMappingURL=UIManager-zGtGiLS0.js.map