@trustcomponent/trustcaptcha-frontend
Version:
TrustCaptcha – Privacy-first CAPTCHA solution. GDPR-compliant, bot protection made in Europe.
761 lines (760 loc) • 32.8 kB
JavaScript
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