@prosopo/procaptcha-audio 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +52 -0
- package/.turbo/turbo-build$colon$tsc.log +53 -0
- package/.turbo/turbo-build.log +54 -0
- package/CHANGELOG.md +35 -0
- package/LICENSE +201 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/cjs/components/audioPlayer.cjs +275 -0
- package/dist/cjs/components/procaptchaAudio.cjs +7 -0
- package/dist/cjs/components/procaptchaWidget.cjs +195 -0
- package/dist/cjs/index.cjs +8 -0
- package/dist/cjs/services/Manager.cjs +218 -0
- package/dist/components/audioPlayer.d.ts +18 -0
- package/dist/components/audioPlayer.d.ts.map +1 -0
- package/dist/components/audioPlayer.js +275 -0
- package/dist/components/audioPlayer.js.map +1 -0
- package/dist/components/procaptchaAudio.d.ts +7 -0
- package/dist/components/procaptchaAudio.d.ts.map +1 -0
- package/dist/components/procaptchaAudio.js +6 -0
- package/dist/components/procaptchaAudio.js.map +1 -0
- package/dist/components/procaptchaWidget.d.ts +7 -0
- package/dist/components/procaptchaWidget.d.ts.map +1 -0
- package/dist/components/procaptchaWidget.js +195 -0
- package/dist/components/procaptchaWidget.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/services/Manager.d.ts +10 -0
- package/dist/services/Manager.d.ts.map +1 -0
- package/dist/services/Manager.js +217 -0
- package/dist/services/Manager.js.map +1 -0
- package/dist/tests/audioPlayer.unit.test.d.ts +2 -0
- package/dist/tests/audioPlayer.unit.test.d.ts.map +1 -0
- package/dist/tests/audioPlayer.unit.test.js +341 -0
- package/dist/tests/audioPlayer.unit.test.js.map +1 -0
- package/dist/tests/domHarness.d.ts +20 -0
- package/dist/tests/domHarness.d.ts.map +1 -0
- package/dist/tests/domHarness.js +56 -0
- package/dist/tests/domHarness.js.map +1 -0
- package/dist/tests/manager.unit.test.d.ts +2 -0
- package/dist/tests/manager.unit.test.d.ts.map +1 -0
- package/dist/tests/manager.unit.test.js +627 -0
- package/dist/tests/manager.unit.test.js.map +1 -0
- package/dist/tests/managerHarness.d.ts +21 -0
- package/dist/tests/managerHarness.d.ts.map +1 -0
- package/dist/tests/managerHarness.js +67 -0
- package/dist/tests/managerHarness.js.map +1 -0
- package/dist/tests/procaptchaAudio.test-d.d.ts +2 -0
- package/dist/tests/procaptchaAudio.test-d.d.ts.map +1 -0
- package/dist/tests/procaptchaAudio.test-d.js +103 -0
- package/dist/tests/procaptchaAudio.test-d.js.map +1 -0
- package/dist/tests/procaptchaAudio.unit.test.d.ts +2 -0
- package/dist/tests/procaptchaAudio.unit.test.d.ts.map +1 -0
- package/dist/tests/procaptchaAudio.unit.test.js +93 -0
- package/dist/tests/procaptchaAudio.unit.test.js.map +1 -0
- package/dist/tests/procaptchaWidget.unit.test.d.ts +2 -0
- package/dist/tests/procaptchaWidget.unit.test.d.ts.map +1 -0
- package/dist/tests/procaptchaWidget.unit.test.js +597 -0
- package/dist/tests/procaptchaWidget.unit.test.js.map +1 -0
- package/dist/tests/setup.d.ts +2 -0
- package/dist/tests/setup.d.ts.map +1 -0
- package/dist/tests/setup.js +12 -0
- package/dist/tests/setup.js.map +1 -0
- package/package.json +70 -3
- package/vite.cjs.config.ts +22 -0
- package/vite.esm.config.ts +20 -0
- package/vite.test.config.ts +26 -0
- package/README.md +0 -3
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
let _prosopo_procaptcha_common = require("@prosopo/procaptcha-common");
|
|
2
|
+
//#region src/components/audioPlayer.ts
|
|
3
|
+
var CONTAINER_WIDTH = 320;
|
|
4
|
+
var SHAKE_MS = 500;
|
|
5
|
+
var SHAKE_KEYFRAMES = `
|
|
6
|
+
@keyframes prosopo-audio-shake {
|
|
7
|
+
0%, 100% { transform: translateX(0); }
|
|
8
|
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
|
|
9
|
+
20%, 40%, 60%, 80% { transform: translateX(4px); }
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
var VISUALLY_HIDDEN = {
|
|
13
|
+
position: "absolute",
|
|
14
|
+
width: "1px",
|
|
15
|
+
height: "1px",
|
|
16
|
+
overflow: "hidden",
|
|
17
|
+
clip: "rect(0 0 0 0)",
|
|
18
|
+
clipPath: "inset(50%)",
|
|
19
|
+
whiteSpace: "nowrap"
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The audio challenge. Accessibility is its purpose, so:
|
|
23
|
+
* - every control is a native `<button>` or `<input>`, not a role on a div;
|
|
24
|
+
* - the `<audio>` element is hidden and driven by our buttons, because several
|
|
25
|
+
* browsers' native controls offer the clip as a download;
|
|
26
|
+
* - status changes go to a polite live region, since a screen-reader user
|
|
27
|
+
* cannot see the shake that marks a wrong answer;
|
|
28
|
+
* - nothing autoplays: browsers block audio without a gesture, and a blocked
|
|
29
|
+
* play() looks like a broken widget.
|
|
30
|
+
*/
|
|
31
|
+
var mountAudioPlayer = (initialProps) => {
|
|
32
|
+
const teardown = new _prosopo_procaptcha_common.Teardown();
|
|
33
|
+
let props = initialProps;
|
|
34
|
+
let answer = "";
|
|
35
|
+
let playing = false;
|
|
36
|
+
let visible = false;
|
|
37
|
+
let shaking = false;
|
|
38
|
+
let shakeTimer;
|
|
39
|
+
let replays = 0;
|
|
40
|
+
let hasPlayed = false;
|
|
41
|
+
let events = [];
|
|
42
|
+
let startedAt = Date.now();
|
|
43
|
+
const t = (key, options) => props.translator.t(key, options);
|
|
44
|
+
const record = (kind) => {
|
|
45
|
+
events.push({
|
|
46
|
+
kind,
|
|
47
|
+
t: Date.now() - startedAt
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const style = (0, _prosopo_procaptcha_common.createElement)("style", { text: SHAKE_KEYFRAMES });
|
|
51
|
+
const instruction = (0, _prosopo_procaptcha_common.createElement)("p", { style: {
|
|
52
|
+
margin: 0,
|
|
53
|
+
fontSize: "14px"
|
|
54
|
+
} });
|
|
55
|
+
const audio = (0, _prosopo_procaptcha_common.createElement)("audio", {
|
|
56
|
+
attributes: { preload: "auto" },
|
|
57
|
+
style: { display: "none" }
|
|
58
|
+
});
|
|
59
|
+
const playButton = (0, _prosopo_procaptcha_common.createElement)("button", { attributes: { type: "button" } });
|
|
60
|
+
const playRow = (0, _prosopo_procaptcha_common.createElement)("div", {
|
|
61
|
+
style: {
|
|
62
|
+
display: "flex",
|
|
63
|
+
gap: "8px"
|
|
64
|
+
},
|
|
65
|
+
children: [playButton]
|
|
66
|
+
});
|
|
67
|
+
const input = (0, _prosopo_procaptcha_common.createElement)("input", { attributes: {
|
|
68
|
+
type: "text",
|
|
69
|
+
inputmode: "numeric",
|
|
70
|
+
autocomplete: "off",
|
|
71
|
+
autocorrect: "off",
|
|
72
|
+
autocapitalize: "off",
|
|
73
|
+
spellcheck: "false"
|
|
74
|
+
} });
|
|
75
|
+
const submitButton = (0, _prosopo_procaptcha_common.createElement)("button", { attributes: { type: "button" } });
|
|
76
|
+
const announcer = (0, _prosopo_procaptcha_common.createElement)("div", {
|
|
77
|
+
attributes: {
|
|
78
|
+
"aria-live": "polite",
|
|
79
|
+
"aria-atomic": "true"
|
|
80
|
+
},
|
|
81
|
+
style: VISUALLY_HIDDEN
|
|
82
|
+
});
|
|
83
|
+
if ("production" !== process.env.NODE_ENV) {
|
|
84
|
+
(0, _prosopo_procaptcha_common.applyAttributes)(audio, { "data-cy": "prosopo-audio-clip" });
|
|
85
|
+
(0, _prosopo_procaptcha_common.applyAttributes)(playButton, { "data-cy": "prosopo-audio-play" });
|
|
86
|
+
(0, _prosopo_procaptcha_common.applyAttributes)(input, { "data-cy": "prosopo-audio-answer" });
|
|
87
|
+
(0, _prosopo_procaptcha_common.applyAttributes)(submitButton, { "data-cy": "prosopo-audio-submit" });
|
|
88
|
+
}
|
|
89
|
+
const panel = (0, _prosopo_procaptcha_common.createElement)("div", {
|
|
90
|
+
style: {
|
|
91
|
+
position: "relative",
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
gap: "14px",
|
|
95
|
+
width: `${CONTAINER_WIDTH}px`,
|
|
96
|
+
boxSizing: "border-box",
|
|
97
|
+
padding: "20px",
|
|
98
|
+
borderRadius: "20px",
|
|
99
|
+
transition: "opacity 0.3s ease, transform 0.3s ease"
|
|
100
|
+
},
|
|
101
|
+
children: [
|
|
102
|
+
instruction,
|
|
103
|
+
audio,
|
|
104
|
+
playRow,
|
|
105
|
+
input,
|
|
106
|
+
submitButton,
|
|
107
|
+
announcer
|
|
108
|
+
]
|
|
109
|
+
});
|
|
110
|
+
const surfaceProps = () => ({
|
|
111
|
+
show: true,
|
|
112
|
+
placement: props.placement,
|
|
113
|
+
anchor: props.anchor,
|
|
114
|
+
onDismiss: props.onDismiss,
|
|
115
|
+
scrim: visible ? "dim" : "none",
|
|
116
|
+
dialogLabel: t("WIDGET.AUDIO_CHALLENGE_LABEL")
|
|
117
|
+
});
|
|
118
|
+
const surface = (0, _prosopo_procaptcha_common.mountChallengeSurface)(surfaceProps());
|
|
119
|
+
surface.content.append(style, panel);
|
|
120
|
+
const announce = (message) => {
|
|
121
|
+
announcer.textContent = message;
|
|
122
|
+
};
|
|
123
|
+
const playLabel = () => {
|
|
124
|
+
if (playing) return t("WIDGET.AUDIO_PLAYING");
|
|
125
|
+
return hasPlayed || answer.length > 0 ? t("WIDGET.AUDIO_REPLAY") : t("WIDGET.AUDIO_PLAY");
|
|
126
|
+
};
|
|
127
|
+
const render = () => {
|
|
128
|
+
const { theme, showRetry, submitting, characterCount } = props;
|
|
129
|
+
const accent = showRetry ? theme.palette.error.main : theme.palette.primary.main;
|
|
130
|
+
const blocked = submitting || 0 === answer.length;
|
|
131
|
+
const buttonStyle = {
|
|
132
|
+
fontFamily: theme.font.fontFamily,
|
|
133
|
+
fontSize: "14px",
|
|
134
|
+
padding: "10px 16px",
|
|
135
|
+
borderRadius: "8px",
|
|
136
|
+
border: `1px solid ${accent}`,
|
|
137
|
+
backgroundColor: "transparent",
|
|
138
|
+
color: accent,
|
|
139
|
+
cursor: "pointer"
|
|
140
|
+
};
|
|
141
|
+
(0, _prosopo_procaptcha_common.applyStyles)(panel, {
|
|
142
|
+
backgroundColor: theme.palette.surface,
|
|
143
|
+
color: theme.palette.onSurface,
|
|
144
|
+
fontFamily: theme.font.fontFamily,
|
|
145
|
+
opacity: visible ? 1 : 0,
|
|
146
|
+
transform: visible ? "scale(1)" : "scale(0.9)",
|
|
147
|
+
animation: shaking ? `prosopo-audio-shake ${SHAKE_MS}ms ease` : "none"
|
|
148
|
+
});
|
|
149
|
+
instruction.textContent = showRetry ? t("WIDGET.AUDIO_INCORRECT") : t("WIDGET.AUDIO_INSTRUCTIONS", { count: characterCount });
|
|
150
|
+
(0, _prosopo_procaptcha_common.applyStyles)(instruction, { color: showRetry ? theme.palette.error.main : theme.palette.onSurface });
|
|
151
|
+
if (audio.getAttribute("src") !== props.clip) audio.setAttribute("src", props.clip);
|
|
152
|
+
playButton.textContent = playLabel();
|
|
153
|
+
(0, _prosopo_procaptcha_common.applyStyles)(playButton, {
|
|
154
|
+
...buttonStyle,
|
|
155
|
+
flex: 1
|
|
156
|
+
});
|
|
157
|
+
input.maxLength = characterCount * 2;
|
|
158
|
+
input.disabled = submitting;
|
|
159
|
+
if (input.value !== answer) input.value = answer;
|
|
160
|
+
(0, _prosopo_procaptcha_common.applyAttributes)(input, { "aria-label": t("WIDGET.AUDIO_INPUT_LABEL", { count: characterCount }) });
|
|
161
|
+
(0, _prosopo_procaptcha_common.applyStyles)(input, {
|
|
162
|
+
fontFamily: theme.font.fontFamily,
|
|
163
|
+
fontSize: "18px",
|
|
164
|
+
letterSpacing: "0.3em",
|
|
165
|
+
textAlign: "center",
|
|
166
|
+
padding: "10px",
|
|
167
|
+
borderRadius: "8px",
|
|
168
|
+
border: `1px solid ${accent}`,
|
|
169
|
+
backgroundColor: "transparent",
|
|
170
|
+
color: theme.palette.onSurface
|
|
171
|
+
});
|
|
172
|
+
submitButton.textContent = submitting ? t("WIDGET.AUDIO_CHECKING") : t("WIDGET.AUDIO_SUBMIT");
|
|
173
|
+
submitButton.disabled = blocked;
|
|
174
|
+
(0, _prosopo_procaptcha_common.applyStyles)(submitButton, {
|
|
175
|
+
...buttonStyle,
|
|
176
|
+
backgroundColor: theme.palette.primary.main,
|
|
177
|
+
borderColor: theme.palette.primary.main,
|
|
178
|
+
color: theme.palette.primary.contrastText,
|
|
179
|
+
opacity: blocked ? .5 : 1,
|
|
180
|
+
cursor: blocked ? "not-allowed" : "pointer"
|
|
181
|
+
});
|
|
182
|
+
surface.update(surfaceProps());
|
|
183
|
+
};
|
|
184
|
+
const play = () => {
|
|
185
|
+
const isReplay = hasPlayed;
|
|
186
|
+
audio.currentTime = 0;
|
|
187
|
+
record(isReplay ? "replay" : "play");
|
|
188
|
+
if (isReplay) replays += 1;
|
|
189
|
+
hasPlayed = true;
|
|
190
|
+
const clipAtPlay = props.clip;
|
|
191
|
+
audio.play().then(() => {
|
|
192
|
+
if (clipAtPlay !== props.clip) return;
|
|
193
|
+
playing = true;
|
|
194
|
+
announce(t("WIDGET.AUDIO_PLAYING"));
|
|
195
|
+
render();
|
|
196
|
+
}, () => {
|
|
197
|
+
playing = false;
|
|
198
|
+
announce(t("WIDGET.AUDIO_PLAYBACK_FAILED"));
|
|
199
|
+
render();
|
|
200
|
+
});
|
|
201
|
+
render();
|
|
202
|
+
};
|
|
203
|
+
const submit = () => {
|
|
204
|
+
if (props.submitting || 0 === answer.length) return;
|
|
205
|
+
props.onComplete(answer, replays, [...events]);
|
|
206
|
+
};
|
|
207
|
+
teardown.addEventListener(playButton, "click", play);
|
|
208
|
+
teardown.addEventListener(submitButton, "click", submit);
|
|
209
|
+
teardown.addEventListener(input, "input", () => {
|
|
210
|
+
record("key");
|
|
211
|
+
answer = input.value;
|
|
212
|
+
render();
|
|
213
|
+
});
|
|
214
|
+
teardown.addEventListener(input, "keydown", (event) => {
|
|
215
|
+
if (!(event instanceof KeyboardEvent) || "Enter" !== event.key) return;
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
submit();
|
|
218
|
+
});
|
|
219
|
+
teardown.addEventListener(audio, "ended", () => {
|
|
220
|
+
playing = false;
|
|
221
|
+
announce(t("WIDGET.AUDIO_FINISHED"));
|
|
222
|
+
render();
|
|
223
|
+
});
|
|
224
|
+
teardown.addEventListener(audio, "pause", () => {
|
|
225
|
+
playing = false;
|
|
226
|
+
render();
|
|
227
|
+
});
|
|
228
|
+
const resetForNewClip = () => {
|
|
229
|
+
audio.pause();
|
|
230
|
+
audio.currentTime = 0;
|
|
231
|
+
answer = "";
|
|
232
|
+
playing = false;
|
|
233
|
+
replays = 0;
|
|
234
|
+
hasPlayed = false;
|
|
235
|
+
events = [];
|
|
236
|
+
startedAt = Date.now();
|
|
237
|
+
};
|
|
238
|
+
const startShake = () => {
|
|
239
|
+
shaking = true;
|
|
240
|
+
announce(t("WIDGET.AUDIO_INCORRECT"));
|
|
241
|
+
if (void 0 !== shakeTimer) clearTimeout(shakeTimer);
|
|
242
|
+
shakeTimer = setTimeout(() => {
|
|
243
|
+
shakeTimer = void 0;
|
|
244
|
+
shaking = false;
|
|
245
|
+
render();
|
|
246
|
+
}, SHAKE_MS);
|
|
247
|
+
};
|
|
248
|
+
teardown.add(() => {
|
|
249
|
+
if (void 0 !== shakeTimer) clearTimeout(shakeTimer);
|
|
250
|
+
});
|
|
251
|
+
if (props.showRetry) startShake();
|
|
252
|
+
render();
|
|
253
|
+
input.focus();
|
|
254
|
+
const frameRequest = requestAnimationFrame(() => {
|
|
255
|
+
visible = true;
|
|
256
|
+
render();
|
|
257
|
+
});
|
|
258
|
+
teardown.add(() => cancelAnimationFrame(frameRequest));
|
|
259
|
+
return {
|
|
260
|
+
update: (nextProps) => {
|
|
261
|
+
const previous = props;
|
|
262
|
+
props = nextProps;
|
|
263
|
+
if (nextProps.clip !== previous.clip) resetForNewClip();
|
|
264
|
+
if (nextProps.showRetry && !previous.showRetry) startShake();
|
|
265
|
+
render();
|
|
266
|
+
},
|
|
267
|
+
destroy: () => {
|
|
268
|
+
teardown.run();
|
|
269
|
+
audio.pause();
|
|
270
|
+
surface.destroy();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
//#endregion
|
|
275
|
+
exports.mountAudioPlayer = mountAudioPlayer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
let _prosopo_procaptcha_common = require("@prosopo/procaptcha-common");
|
|
2
|
+
//#region src/components/procaptchaAudio.ts
|
|
3
|
+
var loadProcaptchaAudio = async () => (await Promise.resolve().then(() => require("./procaptchaWidget.cjs"))).mountProcaptchaAudioWidget;
|
|
4
|
+
var mountProcaptchaAudio = (0, _prosopo_procaptcha_common.lazyMount)(loadProcaptchaAudio);
|
|
5
|
+
//#endregion
|
|
6
|
+
exports.loadProcaptchaAudio = loadProcaptchaAudio;
|
|
7
|
+
exports.mountProcaptchaAudio = mountProcaptchaAudio;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const require_Manager = require("../services/Manager.cjs");
|
|
2
|
+
const require_audioPlayer = require("./audioPlayer.cjs");
|
|
3
|
+
let _prosopo_locale = require("@prosopo/locale");
|
|
4
|
+
let _prosopo_procaptcha_common = require("@prosopo/procaptcha-common");
|
|
5
|
+
let _prosopo_types = require("@prosopo/types");
|
|
6
|
+
let _prosopo_widget_skeleton = require("@prosopo/widget-skeleton");
|
|
7
|
+
//#region src/components/procaptchaWidget.ts
|
|
8
|
+
var mountProcaptchaAudioWidget = (container, props) => {
|
|
9
|
+
const teardown = new _prosopo_procaptcha_common.Teardown();
|
|
10
|
+
const config = props.config;
|
|
11
|
+
const i18n = props.i18n;
|
|
12
|
+
const frictionlessState = props.frictionlessState;
|
|
13
|
+
const callbacks = props.callbacks || {};
|
|
14
|
+
const translator = (0, _prosopo_locale.createTranslator)(i18n);
|
|
15
|
+
const isInvisible = _prosopo_types.ModeEnum.invisible === config.mode;
|
|
16
|
+
const theme = "light" === config.theme ? _prosopo_widget_skeleton.lightTheme : _prosopo_widget_skeleton.darkTheme;
|
|
17
|
+
const store = (0, _prosopo_procaptcha_common.createProcaptchaState)();
|
|
18
|
+
let loading = false;
|
|
19
|
+
let audioPhase = "checkbox";
|
|
20
|
+
let challengeData = null;
|
|
21
|
+
let showRetry = true === props.startShowRetry;
|
|
22
|
+
let lastError = store.state.error;
|
|
23
|
+
let lastCoords = null;
|
|
24
|
+
let sessionInvalidatedFired = false;
|
|
25
|
+
let honeypot;
|
|
26
|
+
let checkbox;
|
|
27
|
+
let player;
|
|
28
|
+
const onReload = props.onReload;
|
|
29
|
+
const updateState = (0, _prosopo_procaptcha_common.buildUpdateState)(store.state, store.update);
|
|
30
|
+
const manager = require_Manager.Manager(config, store.state, updateState, callbacks, frictionlessState, () => honeypot?.getValue(), onReload ? (x, y) => onReload(x, y, { showRetry: true }) : void 0);
|
|
31
|
+
const root = (0, _prosopo_procaptcha_common.createElement)("div");
|
|
32
|
+
container.appendChild(root);
|
|
33
|
+
if (frictionlessState?.hp) honeypot = (0, _prosopo_procaptcha_common.mountHoneypot)(root, { encodedQuestion: frictionlessState.hp });
|
|
34
|
+
const reportError = (error) => {
|
|
35
|
+
callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
36
|
+
};
|
|
37
|
+
const returnToCheckbox = () => {
|
|
38
|
+
audioPhase = "checkbox";
|
|
39
|
+
challengeData = null;
|
|
40
|
+
showRetry = false;
|
|
41
|
+
};
|
|
42
|
+
const showChallenge = (challenge) => {
|
|
43
|
+
if (challenge) {
|
|
44
|
+
challengeData = challenge;
|
|
45
|
+
audioPhase = "answering";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const handleAudioComplete = async (answer, replays, audioEvents) => {
|
|
49
|
+
audioPhase = "submitting";
|
|
50
|
+
scheduler.schedule();
|
|
51
|
+
let verified = false;
|
|
52
|
+
try {
|
|
53
|
+
verified = await manager.submitSolution(answer, replays, audioEvents);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
reportError(error);
|
|
56
|
+
}
|
|
57
|
+
if (verified) {
|
|
58
|
+
returnToCheckbox();
|
|
59
|
+
loading = false;
|
|
60
|
+
scheduler.schedule();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
showRetry = true;
|
|
64
|
+
if (onReload) {
|
|
65
|
+
scheduler.schedule();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
audioPhase = "answering";
|
|
69
|
+
scheduler.schedule();
|
|
70
|
+
try {
|
|
71
|
+
const newChallenge = await manager.start();
|
|
72
|
+
if (newChallenge) challengeData = newChallenge;
|
|
73
|
+
else returnToCheckbox();
|
|
74
|
+
} catch {
|
|
75
|
+
returnToCheckbox();
|
|
76
|
+
}
|
|
77
|
+
loading = false;
|
|
78
|
+
scheduler.schedule();
|
|
79
|
+
};
|
|
80
|
+
const handleDismiss = () => {
|
|
81
|
+
returnToCheckbox();
|
|
82
|
+
loading = false;
|
|
83
|
+
scheduler.schedule();
|
|
84
|
+
};
|
|
85
|
+
const playerProps = (challenge) => ({
|
|
86
|
+
clip: challenge.clip,
|
|
87
|
+
characterCount: challenge.characterCount,
|
|
88
|
+
onComplete: (answer, replays, events) => {
|
|
89
|
+
handleAudioComplete(answer, replays, events);
|
|
90
|
+
},
|
|
91
|
+
showRetry,
|
|
92
|
+
submitting: "submitting" === audioPhase,
|
|
93
|
+
theme,
|
|
94
|
+
translator,
|
|
95
|
+
placement: config.placement,
|
|
96
|
+
anchor: props.container,
|
|
97
|
+
onDismiss: handleDismiss
|
|
98
|
+
});
|
|
99
|
+
const runErrorEffect = () => {
|
|
100
|
+
if (store.state.error === lastError) return;
|
|
101
|
+
lastError = store.state.error;
|
|
102
|
+
if (!store.state.error) return;
|
|
103
|
+
loading = false;
|
|
104
|
+
returnToCheckbox();
|
|
105
|
+
if ("CAPTCHA.NO_SESSION_FOUND" !== store.state.error.key) return;
|
|
106
|
+
if (props.onSessionInvalidated && !sessionInvalidatedFired || void 0 !== frictionlessState) {
|
|
107
|
+
loading = true;
|
|
108
|
+
store.update({ error: void 0 });
|
|
109
|
+
}
|
|
110
|
+
if (props.onSessionInvalidated && !sessionInvalidatedFired) {
|
|
111
|
+
sessionInvalidatedFired = true;
|
|
112
|
+
props.onSessionInvalidated(lastCoords?.x, lastCoords?.y);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (frictionlessState) {
|
|
116
|
+
const timer = setTimeout(() => {
|
|
117
|
+
frictionlessState.restart();
|
|
118
|
+
}, 100);
|
|
119
|
+
teardown.add(() => clearTimeout(timer));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const render = () => {
|
|
123
|
+
runErrorEffect();
|
|
124
|
+
if (("answering" === audioPhase || "submitting" === audioPhase) && null !== challengeData) if (void 0 === player) player = require_audioPlayer.mountAudioPlayer(playerProps(challengeData));
|
|
125
|
+
else player.update(playerProps(challengeData));
|
|
126
|
+
else {
|
|
127
|
+
player?.destroy();
|
|
128
|
+
player = void 0;
|
|
129
|
+
}
|
|
130
|
+
checkbox?.update(checkboxProps());
|
|
131
|
+
};
|
|
132
|
+
const scheduler = (0, _prosopo_procaptcha_common.createRenderScheduler)(render);
|
|
133
|
+
const beginChallenge = async (coords) => {
|
|
134
|
+
if (loading) return;
|
|
135
|
+
loading = true;
|
|
136
|
+
showRetry = false;
|
|
137
|
+
scheduler.schedule();
|
|
138
|
+
if (coords) lastCoords = coords;
|
|
139
|
+
try {
|
|
140
|
+
showChallenge(await manager.start(coords?.x, coords?.y));
|
|
141
|
+
} catch (error) {
|
|
142
|
+
reportError(error);
|
|
143
|
+
} finally {
|
|
144
|
+
loading = false;
|
|
145
|
+
scheduler.schedule();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const checkboxProps = () => ({
|
|
149
|
+
checked: store.state.isHuman,
|
|
150
|
+
theme,
|
|
151
|
+
labelText: translator.isReady() ? translator.t("WIDGET.I_AM_HUMAN") : "",
|
|
152
|
+
error: store.state.error ? (0, _prosopo_locale.localiseErrorMessage)(translator.i18n, store.state.error) : void 0,
|
|
153
|
+
loadingText: translator.t("WIDGET.CHECKING", { defaultValue: "Checking that you are human" }),
|
|
154
|
+
loading: loading || "submitting" === audioPhase,
|
|
155
|
+
onChange: (event) => beginChallenge((0, _prosopo_procaptcha_common.trustedClickCoords)(event))
|
|
156
|
+
});
|
|
157
|
+
if (!isInvisible) checkbox = (0, _prosopo_procaptcha_common.mountCheckbox)(root, checkboxProps());
|
|
158
|
+
teardown.add(scheduler.cancel);
|
|
159
|
+
teardown.add(store.subscribe(scheduler.schedule));
|
|
160
|
+
teardown.add(translator.subscribe(scheduler.schedule));
|
|
161
|
+
const handleExecute = () => {
|
|
162
|
+
beginChallenge();
|
|
163
|
+
};
|
|
164
|
+
if (props.container) teardown.addEventListener(props.container, _prosopo_procaptcha_common.PROCAPTCHA_EXECUTE_EVENT, handleExecute);
|
|
165
|
+
if (isInvisible) teardown.addEventListener(document, _prosopo_procaptcha_common.PROCAPTCHA_EXECUTE_EVENT, handleExecute);
|
|
166
|
+
if (config.language) if (i18n) {
|
|
167
|
+
if (i18n.language !== config.language) i18n.changeLanguage(config.language);
|
|
168
|
+
} else (0, _prosopo_locale.loadI18next)(false, config.language);
|
|
169
|
+
if (props.autoStart) {
|
|
170
|
+
loading = true;
|
|
171
|
+
const coords = props.startCoords;
|
|
172
|
+
lastCoords = coords ?? null;
|
|
173
|
+
scheduler.schedule();
|
|
174
|
+
manager.start(coords?.x ?? 0, coords?.y ?? 0).then((challenge) => {
|
|
175
|
+
showChallenge(challenge);
|
|
176
|
+
loading = false;
|
|
177
|
+
scheduler.schedule();
|
|
178
|
+
}, () => {
|
|
179
|
+
loading = false;
|
|
180
|
+
scheduler.schedule();
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
render();
|
|
184
|
+
return { destroy: () => {
|
|
185
|
+
manager.dispose();
|
|
186
|
+
teardown.run();
|
|
187
|
+
player?.destroy();
|
|
188
|
+
checkbox?.destroy();
|
|
189
|
+
honeypot?.destroy();
|
|
190
|
+
root.parentNode?.removeChild(root);
|
|
191
|
+
} };
|
|
192
|
+
};
|
|
193
|
+
//#endregion
|
|
194
|
+
exports.default = mountProcaptchaAudioWidget;
|
|
195
|
+
exports.mountProcaptchaAudioWidget = mountProcaptchaAudioWidget;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_audioPlayer = require("./components/audioPlayer.cjs");
|
|
3
|
+
const require_procaptchaWidget = require("./components/procaptchaWidget.cjs");
|
|
4
|
+
const require_procaptchaAudio = require("./components/procaptchaAudio.cjs");
|
|
5
|
+
exports.loadProcaptchaAudio = require_procaptchaAudio.loadProcaptchaAudio;
|
|
6
|
+
exports.mountAudioPlayer = require_audioPlayer.mountAudioPlayer;
|
|
7
|
+
exports.mountProcaptchaAudio = require_procaptchaAudio.mountProcaptchaAudio;
|
|
8
|
+
exports.mountProcaptchaAudioWidget = require_procaptchaWidget.default;
|