UNPKG

@trustcomponent/trustcaptcha-frontend

Version:

TrustCaptcha – Privacy-first CAPTCHA solution. GDPR-compliant, bot protection made in Europe.

761 lines (760 loc) 32.8 kB
import { h, Host } from "@stencil/core"; import { CaptchaBox } from "./src/captcha-box"; import { Status } from "./src/status"; import { getTranslation } from "./locale"; import { License } from "./src/license"; import { ErrorCode } from "./src/api/error/error-code"; import { ErrorModel } from "./src/api/error/error-model"; import { CheckIcon, ErrorIcon, LogoIcon, PrivacyText, SpinnerIcon, TrustcaptchaText } from "./icons"; import { Config } from "./config"; export class TrustcaptchaComponent { constructor() { this.design = { rounding: { box: '6px', checkbox: '6px', invisibleHint: '6px', }, theme: { light: { boxDefaultBackground: '#f9fafb', // gray-50 boxDefaultText: '#374151', // gray-700 boxDefaultBorder: '#d1d5db', // gray-300 boxCheckboxBackground: '#ffffff', // white boxCheckboxBorder: '#d1d5db', // gray-300 boxSuccessBackground: '#f9fafb', // gray-50 boxSuccessBorder: '#86efac', // green-300 invisibleHintBackground: '#ffffff', // white }, dark: { boxDefaultBackground: '#1f2937', // gray-800 boxDefaultText: '#e5e7eb', // gray-200 boxDefaultBorder: '#374151', // gray-700 boxCheckboxBackground: '#030712', // gray-900 boxCheckboxBorder: '#374151', // gray-700 boxSuccessBackground: '#1f2937', // gray-800 boxSuccessBorder: '#15803d', // green-700 invisibleHintBackground: '#1f2937', // gray-800 } } }; this.trustcaptchaUrl = 'https://www.trustcomponent.com/en/products/captcha'; this.trustcaptchaApi = 'https://api.trustcomponent.com'; this.framework = null; this.language = 'auto'; this.customTranslations = ''; this.theme = 'light'; this.customDesign = ''; this.autostart = true; this.tokenFieldName = 'tc-verification-token'; this.bypassToken = ''; this.mode = 'standard'; this.width = 'fixed'; this.license = null; this.invisible = false; this.invisibleHint = 'right-border'; this.hideBranding = false; this.captchaStatus = Status.START; this.currentTheme = 'light'; this.failureText = ''; this.mergedDesign = this.design; this.dummyState = 0; } getConfig() { return new Config(this.sitekey, this.privacyUrl, this.trustcaptchaUrl, this.trustcaptchaApi, this.framework, this.language, this.theme, this.currentTheme, this.autostart, this.tokenFieldName, this.bypassToken, this.mode, this.translation, this.hideBranding, this.invisible, this.licenseObject); } mergeDeep(target, source) { for (const key in source) { if (source[key] instanceof Object && !(source[key] instanceof Array)) { target[key] = this.mergeDeep(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target; } async componentWillLoad() { var _a, _b, _c, _d; this.translation = getTranslation(this.language, this.customTranslations); this.watchThemeUpdate(); this.watchHtmlLangUpdate(); this.captchaBox = new CaptchaBox(this.getConfig(), this.hostElement); this.captchaBox.statusChangedEvent.on('statusChanged', value => { this.captchaStatus = value; if (value == Status.RUNNING) { this.captchaStarted.emit(); } }); this.captchaBox.captchaFinishEvent.on("captchaSolved", () => { this.captchaSolved.emit(this.captchaBox.verificationToken); }); this.captchaBox.captchaFinishEvent.on('captchaFailed', reason => { this.failureText = reason.message; this.captchaFailed.emit(reason); }); this.captchaBox.captchaFinishEvent.on('captchaReset', () => { this.captchaReset.emit(); }); if (!this.sitekey) { this.captchaBox.setErrorStatus(new ErrorModel(ErrorCode.NO_SITE_KEY_SPECIFIED, 'No site key specified. Please provide a site key.')); } if (this.license) { this.licenseObject = await License.create(this.license, this.sitekey, errorModel => { this.captchaBox.setErrorStatus(errorModel); }); } if (this.licenseObject) { if (this.hideBranding && !((_a = this.licenseObject) === null || _a === void 0 ? void 0 : _a.customizabilityOption)) { this.captchaBox.setErrorStatus(new ErrorModel(ErrorCode.OPTION_NOT_AVAILABLE, "Hide branding option not included in your current license. Please check the given license or contact the support.")); } if (this.privacyUrl && !((_b = this.licenseObject) === null || _b === void 0 ? void 0 : _b.customizabilityOption)) { this.captchaBox.setErrorStatus(new ErrorModel(ErrorCode.OPTION_NOT_AVAILABLE, "Privacy url option not included in your current license. Please check the given license or contact the support.")); } if (this.invisible && !((_c = this.licenseObject) === null || _c === void 0 ? void 0 : _c.invisibilityOption)) { this.captchaBox.setErrorStatus(new ErrorModel(ErrorCode.OPTION_NOT_AVAILABLE, "Invisible option not included in your current license. Please check the given license or contact the support.")); } } let customizedDesign = {}; if (this.customDesign && this.licenseObject && ((_d = this.licenseObject) === null || _d === void 0 ? void 0 : _d.customizabilityOption)) { try { customizedDesign = JSON.parse(this.customDesign); this.mergedDesign = this.mergeDeep(structuredClone(this.design), customizedDesign); } catch (e) { console.error("Failed to parse custom design information"); } } this.captchaBox.setup(); } watchLanguageUpdate() { this.translation = getTranslation(this.language, this.customTranslations); this.setNewConfig(); } watchThemeUpdate() { switch (this.theme) { case "media": if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { this.currentTheme = 'dark'; } else { this.currentTheme = 'light'; } break; case "dark": this.currentTheme = 'dark'; break; case "light": default: this.currentTheme = 'light'; break; } this.setNewConfig(); } watchCustomTranslationUpdate() { this.dummyState++; } watchCustomDesignUpdate() { this.dummyState++; } setNewConfig() { const newConfig = this.getConfig(); if (this.captchaBox) { this.captchaBox.setNewConfig(newConfig); } } watchHtmlLangUpdate() { const observer = new MutationObserver(mutations => { for (let mutation of mutations) { if (mutation.type === 'attributes' && mutation.attributeName === 'lang' && this.language == 'auto') { this.translation = getTranslation('auto', this.customTranslations); this.setNewConfig(); } } }); const config = { attributes: true, attributeFilter: ['lang'] }; observer.observe(document.documentElement, config); } async startVerification() { this.captchaBox.startVerification(); } async reset() { this.captchaBox.reset(); } render() { return (h(Host, { key: '7aa8994af99df390988351e4b02e8894cba0b307', style: { '--round-box': this.mergedDesign.rounding.box, '--round-cb': this.mergedDesign.rounding.checkbox, '--round-ih': this.mergedDesign.rounding.invisibleHint, '--b-default-bg-l': this.mergedDesign.theme.light.boxDefaultBackground, '--b-default-text-l': this.mergedDesign.theme.light.boxDefaultText, '--b-default-border-l': this.mergedDesign.theme.light.boxDefaultBorder, '--b-default-bg-d': this.mergedDesign.theme.dark.boxDefaultBackground, '--b-default-border-d': this.mergedDesign.theme.dark.boxDefaultBorder, '--b-default-text-d': this.mergedDesign.theme.dark.boxDefaultText, '--b-checkbox-bg-l': this.mergedDesign.theme.light.boxCheckboxBackground, '--b-checkbox-border-l': this.mergedDesign.theme.light.boxCheckboxBorder, '--b-checkbox-bg-d': this.mergedDesign.theme.dark.boxCheckboxBackground, '--b-checkbox-border-d': this.mergedDesign.theme.dark.boxCheckboxBorder, '--b-success-bg-l': this.mergedDesign.theme.light.boxSuccessBackground, '--b-success-border-l': this.mergedDesign.theme.light.boxSuccessBorder, '--b-success-bg-d': this.mergedDesign.theme.dark.boxSuccessBackground, '--b-success-border-d': this.mergedDesign.theme.dark.boxSuccessBorder, '--ih-bg-l': this.mergedDesign.theme.light.invisibleHintBackground, '--ih-bg-d': this.mergedDesign.theme.dark.invisibleHintBackground, } }, this.renderCheckboxDesign(), this.invisible && this.invisibleHint !== 'hidden' && (h("div", { key: 'fb781a7534170cb4b7e809b53093cc2b92c4d34d' }, this.invisibleHint === 'right-bottom' && (h("div", { key: 'a27263db2c8982e5f7de5670e398a13ede0b7cbe', class: "absolute bottom-5 right-5" }, this.renderInvisibleHint())), this.invisibleHint === 'right-border' && (h("div", { key: '48fcf6b23ea6a107fa699143b306a920a6390b8f', class: "absolute right-5 transform -translate-y-full" }, this.renderInvisibleHint())), this.invisibleHint === 'inline' && (h("div", { key: '03f91e390d7bc1b6a5b17d7f5ff9701fdc82bc7a', class: "relative" }, h("div", { key: '76208ba08862818d7f9bac6bc08bdb4399ff6621', class: "absolute -right-2 transform translate-x-full -translate-y-full" }, this.renderInvisibleHint()))))))); } renderCheckboxDesign() { var _a, _b, _c; return (h("div", { class: { 'p-2 border': true, 'hidden': this.invisible && ((_a = this.licenseObject) === null || _a === void 0 ? void 0 : _a.invisibilityOption), 'max-w-96': this.width === 'fixed', 'max-w-full': this.width === 'full', 'cursor-pointer': this.captchaBox.status === Status.START, 'bg-red-50 border-red-300': this.currentTheme == 'light' && this.captchaBox.status === Status.FAILED, 'bg-red-800 border-red-700': this.currentTheme == 'dark' && this.captchaBox.status === Status.FAILED, }, style: { borderRadius: 'var(--round-box)', backgroundColor: this.captchaBox.status === Status.START || this.captchaBox.status === Status.RUNNING ? this.currentTheme === 'light' ? 'var(--b-default-bg-l)' : 'var(--b-default-bg-d)' : this.captchaBox.status === Status.DONE ? this.currentTheme === 'light' ? 'var(--b-success-bg-l)' : 'var(--b-success-bg-d)' : '', borderColor: this.captchaBox.status === Status.START || this.captchaBox.status === Status.RUNNING ? this.currentTheme === 'light' ? 'var(--b-default-border-l)' : 'var(--b-default-border-d)' : this.captchaBox.status === Status.DONE ? this.currentTheme === 'light' ? 'var(--b-success-border-l)' : 'var(--b-success-border-d)' : '', }, onClick: () => this.startVerification(), onKeyDown: (e) => { if (e.key === " " || e.key === "Enter") { e.preventDefault(); this.startVerification(); } } }, h("div", { class: 'flex space-x-2', style: { color: this.currentTheme === 'light' ? 'var(--b-default-text-l)' : 'var(--b-default-text-d)', } }, h("div", { class: "flex justify-center items-center p-1" }, h("div", { class: 'w-8 h-8 items-center content-center' }, this.captchaStatus == Status.START && this.renderCheckBoxIcon(), this.captchaStatus == Status.RUNNING && h(SpinnerIcon, { size: 32, stroke: 4 }), this.captchaStatus == Status.DONE && h(CheckIcon, { size: 32 }), this.captchaStatus == Status.FAILED && h(ErrorIcon, { size: 32 }))), h("div", { class: 'grow content-center text-sm font-medium', tabindex: this.captchaStatus === Status.START ? "0" : "-1", role: this.captchaStatus === Status.START ? "button" : undefined, "aria-label": this.captchaStatus === Status.START ? this.translation.ariaLabelStart : this.captchaStatus === Status.RUNNING ? this.translation.ariaLabelRunning : this.captchaStatus === Status.DONE ? this.translation.ariaLabelDone : this.captchaStatus === Status.FAILED ? this.translation.srFailed : "" }, this.captchaStatus == Status.START && this.translation.boxStart, this.captchaStatus == Status.RUNNING && this.translation.boxInProgress, this.captchaStatus == Status.DONE && this.translation.boxCompleted, this.captchaStatus == Status.FAILED && h("div", { class: 'leading-3' }, h("span", { class: "text-red-500 text-xs" }, this.failureText))), h("div", { class: "sr-only", "aria-live": "polite", "aria-atomic": "true" }, this.captchaStatus === Status.RUNNING && this.translation.srRunning, this.captchaStatus === Status.DONE && this.translation.srDone, this.captchaStatus === Status.FAILED && this.translation.srFailed), h("div", { class: 'content-center' }, h("div", { class: 'flex flex-col justify-end space-x-2 text-xs' }, !(this.hideBranding && ((_b = this.licenseObject) === null || _b === void 0 ? void 0 : _b.customizabilityOption)) && h(TrustcaptchaText, { currentTheme: this.currentTheme, trustcaptchaUrl: this.trustcaptchaUrl, translation: this.translation }), (this.privacyUrl && ((_c = this.licenseObject) === null || _c === void 0 ? void 0 : _c.customizabilityOption)) && h(PrivacyText, { privacyUrl: this.privacyUrl, translation: this.translation })))))); } renderCheckBoxIcon() { return (h("div", { class: 'h-8 w-8 p-2 border', style: { borderRadius: 'var(--round-cb)', backgroundColor: this.currentTheme === 'light' ? 'var(--b-checkbox-bg-l)' : 'var(--b-checkbox-bg-d)', borderColor: this.currentTheme === 'light' ? 'var(--b-checkbox-border-l)' : 'var(--b-checkbox-border-d)', } })); } renderInvisibleHint() { var _a; return (h("div", { class: { 'p-3 rounded-md z-[1000] shadow-lg main-container flex gap-4': true, 'hidden': this.captchaBox.status === Status.START, 'bg-red-50 border-red-300': this.currentTheme == 'light' && this.captchaBox.status === Status.FAILED, 'bg-red-800 border-red-700': this.currentTheme == 'dark' && this.captchaBox.status === Status.FAILED }, style: { borderRadius: 'var(--round-ih)', color: this.currentTheme === 'light' ? 'var(--b-default-text-l)' : 'var(--b-default-text-d)', backgroundColor: this.currentTheme === 'light' ? 'var(--ih-bg-l)' : 'var(--ih-bg-d)' } }, h("div", { class: "flex justify-center items-center" }, h("div", { class: { 'iconLight': this.currentTheme == 'light', 'iconDark': this.currentTheme == 'dark', } }, h(LogoIcon, { size: 32 })), this.captchaStatus == Status.RUNNING && h(SpinnerIcon, { size: 20, stroke: 4 }), this.captchaStatus == Status.DONE && h(CheckIcon, { size: 20 }), this.captchaStatus == Status.FAILED && h(ErrorIcon, { size: 20 })), h("div", { class: 'hidden-container content-center' }, h("div", { class: 'flex flex-col justify-end space-x-2 text-xs' }, !(this.hideBranding && ((_a = this.licenseObject) === null || _a === void 0 ? void 0 : _a.customizabilityOption)) && h(TrustcaptchaText, { currentTheme: this.currentTheme, trustcaptchaUrl: this.trustcaptchaUrl, translation: this.translation }), this.privacyUrl && h(PrivacyText, { privacyUrl: this.privacyUrl, translation: this.translation }))))); } static get is() { return "trustcaptcha-component"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["trustcaptcha-component.css"] }; } static get styleUrls() { return { "$": ["trustcaptcha-component.css"] }; } static get properties() { return { "sitekey": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "sitekey", "reflect": false }, "privacyUrl": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "privacy-url", "reflect": false }, "trustcaptchaApi": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "trustcaptcha-api", "reflect": false, "defaultValue": "'https://api.trustcomponent.com'" }, "framework": { "type": "string", "mutable": false, "complexType": { "original": "string | null", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "framework", "reflect": false, "defaultValue": "null" }, "language": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "language", "reflect": false, "defaultValue": "'auto'" }, "customTranslations": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "custom-translations", "reflect": false, "defaultValue": "''" }, "theme": { "type": "string", "mutable": false, "complexType": { "original": "'light' | 'dark' | 'media'", "resolved": "\"dark\" | \"light\" | \"media\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "theme", "reflect": false, "defaultValue": "'light'" }, "customDesign": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "custom-design", "reflect": false, "defaultValue": "''" }, "autostart": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "autostart", "reflect": true, "defaultValue": "true" }, "tokenFieldName": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "token-field-name", "reflect": false, "defaultValue": "'tc-verification-token'" }, "bypassToken": { "type": "string", "mutable": false, "complexType": { "original": "string | null", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "bypass-token", "reflect": false, "defaultValue": "''" }, "mode": { "type": "string", "mutable": false, "complexType": { "original": "'standard' | 'minimal'", "resolved": "\"minimal\" | \"standard\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "mode", "reflect": false, "defaultValue": "'standard'" }, "width": { "type": "string", "mutable": false, "complexType": { "original": "'fixed' | 'full'", "resolved": "\"fixed\" | \"full\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "width", "reflect": false, "defaultValue": "'fixed'" }, "license": { "type": "string", "mutable": false, "complexType": { "original": "string | null", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "license", "reflect": false, "defaultValue": "null" }, "invisible": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "invisible", "reflect": true, "defaultValue": "false" }, "invisibleHint": { "type": "string", "mutable": false, "complexType": { "original": "'hidden' | 'inline' | 'right-border' | 'right-bottom'", "resolved": "\"hidden\" | \"inline\" | \"right-border\" | \"right-bottom\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "invisible-hint", "reflect": false, "defaultValue": "'right-border'" }, "hideBranding": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "getter": false, "setter": false, "attribute": "hide-branding", "reflect": true, "defaultValue": "false" } }; } static get states() { return { "captchaStatus": {}, "licenseObject": {}, "translation": {}, "currentTheme": {}, "dummyState": {} }; } static get events() { return [{ "method": "captchaStarted", "name": "captchaStarted", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "captchaSolved", "name": "captchaSolved", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }, { "method": "captchaFailed", "name": "captchaFailed", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }, { "method": "captchaReset", "name": "captchaReset", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "startVerification": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } }, "reset": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } } }; } static get elementRef() { return "hostElement"; } static get watchers() { return [{ "propName": "language", "methodName": "watchLanguageUpdate" }, { "propName": "theme", "methodName": "watchThemeUpdate" }, { "propName": "customTranslations", "methodName": "watchCustomTranslationUpdate" }, { "propName": "customDesign", "methodName": "watchCustomDesignUpdate" }]; } } //# sourceMappingURL=trustcaptcha-component.js.map