@prosopo/client-bundle-example 2.10.12 → 2.10.18

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.
Files changed (42) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +6 -6
  2. package/.turbo/turbo-build$colon$tsc.log +14 -14
  3. package/.turbo/turbo-build.log +7 -7
  4. package/CHANGELOG.md +46 -0
  5. package/README.md +3 -1
  6. package/env.development +7 -0
  7. package/env.production +6 -0
  8. package/env.staging +2 -2
  9. package/package.json +10 -5
  10. package/src/assets/dummy.txt +0 -0
  11. package/src/frictionless-explicit-web3.html +249 -0
  12. package/src/frictionless-explicit.html +208 -0
  13. package/src/frictionless-implicit.html +218 -0
  14. package/src/image-explicit-web3.html +198 -0
  15. package/src/image-explicit.html +158 -0
  16. package/src/index.html +207 -0
  17. package/src/index.ts +76 -0
  18. package/src/invisible-frictionless-explicit.html +132 -0
  19. package/src/invisible-frictionless-implicit.html +204 -0
  20. package/src/invisible-image-explicit.html +161 -0
  21. package/src/invisible-image-implicit.html +203 -0
  22. package/src/invisible-pow-explicit.html +161 -0
  23. package/src/invisible-pow-implicit.html +106 -0
  24. package/src/invisible-puzzle-explicit.html +160 -0
  25. package/src/invisible-puzzle-implicit.html +106 -0
  26. package/src/plugins/explanation-injector.ts +294 -0
  27. package/src/plugins/form-filler-injector.ts +233 -0
  28. package/src/plugins/navigation-injector.ts +602 -0
  29. package/src/plugins/status-log-injector.ts +199 -0
  30. package/src/pow-explicit-web3.html +195 -0
  31. package/src/pow-explicit.html +158 -0
  32. package/src/pow-implicit.html +207 -0
  33. package/src/puzzle-explicit.html +158 -0
  34. package/src/puzzle-implicit.html +207 -0
  35. package/src/styles/captcha.css +78 -0
  36. package/src/styles/field.css +12 -0
  37. package/tsconfig.cjs.json +25 -0
  38. package/tsconfig.json +31 -0
  39. package/tsconfig.tsbuildinfo +1 -0
  40. package/tsconfig.types.json +9 -0
  41. package/vite.cjs.config.ts +1 -1
  42. package/vite.config.ts +37 -4
@@ -0,0 +1,158 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <title>Procaptcha Image Mode - Explicit Rendering</title>
9
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
10
+ <script type="module">
11
+ // Function to update CAPTCHA status display is now provided by status-log-injector
12
+
13
+ document.addEventListener('DOMContentLoaded', function() {
14
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
15
+
16
+ // Monitor DOM for CAPTCHA initialization
17
+ const observer = new MutationObserver((mutations) => {
18
+ mutations.forEach((mutation) => {
19
+ if (mutation.addedNodes.length) {
20
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
21
+ const node = mutation.addedNodes[i];
22
+ if (node.classList && (node.classList.contains('procaptcha') ||
23
+ node.querySelector && node.querySelector('.procaptcha'))) {
24
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
25
+ observer.disconnect();
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ });
31
+ });
32
+
33
+ observer.observe(document.body, { childList: true, subtree: true });
34
+ });
35
+
36
+ // updateCaptchaStatus is now provided by status-log-injector
37
+ </script>
38
+ </head>
39
+ <body>
40
+ <div class="mui-container">
41
+ <h1>Procaptcha Image Mode - Explicit Rendering</h1>
42
+ <p>This example demonstrates how to use Procaptcha in image mode with explicit rendering.</p>
43
+
44
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
45
+
46
+ <form id="demo-form" class="mui-form">
47
+ <h2>Example Form</h2>
48
+
49
+ <div class="mui-textfield mui-textfield--float-label">
50
+ <label for="name">Name</label>
51
+ <input type="text" id="name" name="name" required />
52
+ </div>
53
+
54
+ <div class="mui-textfield mui-textfield--float-label">
55
+ <label for="email">Email</label>
56
+ <input type="email" id="email" name="email" required />
57
+ </div>
58
+
59
+ <!-- The container for the CAPTCHA -->
60
+ <div id="procaptcha-container"></div>
61
+
62
+ <button type="submit" class="mui-btn mui-btn--raised">Submit</button>
63
+
64
+ <div id="error"></div>
65
+ </form>
66
+
67
+ <div id="result" class="info-box" style="display: none;"></div>
68
+
69
+ <!-- Console output display area -->
70
+ <div id="console-output" class="console-output" style="display: none;"></div>
71
+
72
+ <!-- Explanation will be injected by the explanation-injector plugin -->
73
+ </div>
74
+
75
+ <script type="module">
76
+ import { render } from "%VITE_BUNDLE_URL%"
77
+
78
+ // Form validation function
79
+ function validateForm() {
80
+ const nameInput = document.getElementById('name');
81
+ const emailInput = document.getElementById('email');
82
+ const errorDisplay = document.getElementById('error');
83
+
84
+ if (!nameInput.value || !emailInput.value) {
85
+ errorDisplay.textContent = 'Please fill out all fields';
86
+ errorDisplay.style.display = 'block';
87
+ window.updateCaptchaStatus('Form validation failed: Empty fields', 'error');
88
+ return false;
89
+ }
90
+
91
+ // Basic email validation
92
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
93
+ if (!emailPattern.test(emailInput.value)) {
94
+ errorDisplay.textContent = 'Please enter a valid email address';
95
+ errorDisplay.style.display = 'block';
96
+ window.updateCaptchaStatus('Form validation failed: Invalid email format', 'error');
97
+ return false;
98
+ }
99
+
100
+ errorDisplay.style.display = 'none';
101
+ window.updateCaptchaStatus('Form validation passed', 'info');
102
+ return true;
103
+ }
104
+
105
+ // Callback for successful CAPTCHA verification
106
+ function handleCaptchaResponse(token) {
107
+ console.log('Procaptcha verified, token:', token);
108
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
109
+ window.updateCaptchaStatus(`Token generated: ${token.substring(0, 15)}...`, 'success');
110
+
111
+ const name = document.getElementById('name').value;
112
+ const email = document.getElementById('email').value;
113
+
114
+ // Display result
115
+ const resultElement = document.getElementById('result');
116
+ resultElement.innerHTML = `<strong>Form submitted with:</strong><br>
117
+ - Name: ${name}<br>
118
+ - Email: ${email}<br>
119
+ - Procaptcha verified: Yes`;
120
+ resultElement.style.display = 'block';
121
+
122
+ window.updateCaptchaStatus('Form submission completed', 'success');
123
+ }
124
+
125
+ // Callback for failed CAPTCHA verification
126
+ function handleCaptchaFailed() {
127
+ console.log('Captcha verification failed');
128
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
129
+ document.getElementById('error').textContent = 'CAPTCHA verification failed. Please try again.';
130
+ document.getElementById('error').style.display = 'block';
131
+ }
132
+
133
+ // Wait for DOM content to be loaded
134
+ document.addEventListener('DOMContentLoaded', function() {
135
+ window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
136
+
137
+ // Render the CAPTCHA
138
+ const widgetId = render(document.getElementById('procaptcha-container'), {
139
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_IMAGE,
140
+ callback: handleCaptchaResponse,
141
+ "failed-callback": handleCaptchaFailed
142
+ });
143
+
144
+ window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
145
+
146
+ // Add form submit handler
147
+ document.getElementById('demo-form').addEventListener('submit', function(e) {
148
+ e.preventDefault();
149
+ window.updateCaptchaStatus('Form submission attempted', 'info');
150
+ if (validateForm()) {
151
+ // Form is valid, CAPTCHA verification will be handled by the callback
152
+ window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
153
+ }
154
+ });
155
+ });
156
+ </script>
157
+ </body>
158
+ </html>
package/src/index.html ADDED
@@ -0,0 +1,207 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <title>Procaptcha demo: PoW - Implicit Rendering</title>
9
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
10
+ <script type="module">
11
+ // Function to update CAPTCHA status display is now provided by status-log-injector
12
+
13
+ window.addEventListener('load', () => {
14
+
15
+ window.setIsError = (isError) => {
16
+ const messageContainer = document.getElementById('messageContainer');
17
+ messageContainer.style.color = isError ? 'red' : 'black';
18
+ messageContainer.style.display = 'block';
19
+ };
20
+
21
+ window.setMessage = (message) => {
22
+ document.getElementById('message').innerText = message;
23
+ };
24
+
25
+ window.onLoggedIn = (token) => {
26
+ updateCaptchaStatus('Logged in, fetching private resource', 'info');
27
+ const url = new URL("/private", config.serverUrl).href;
28
+ console.log("getting private resource with token ", token, "at", url);
29
+ fetch(url, {
30
+ method: "GET",
31
+ headers: {
32
+ Origin: "http://localhost:9232", // TODO: change this to env var
33
+ "Content-Type": "application/json",
34
+ Authorization: `Bearer ${token}`,
35
+ },
36
+ })
37
+ .then(async (res) => {
38
+ try {
39
+ const jsonRes = await res.json();
40
+ if (res.status === 200) {
41
+ updateCaptchaStatus(`Successfully accessed private resource`, 'success');
42
+ setMessage(jsonRes.message);
43
+ } else {
44
+ updateCaptchaStatus(`Failed to access private resource: ${res.status}`, 'error');
45
+ }
46
+ } catch (err) {
47
+ console.log(err);
48
+ updateCaptchaStatus(`Error parsing response: ${err}`, 'error');
49
+ }
50
+ })
51
+ .catch((err) => {
52
+ console.log(err);
53
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
54
+ });
55
+ };
56
+
57
+ window.onActionHandler = () => {
58
+ updateCaptchaStatus('Form submission initiated', 'info');
59
+
60
+ const procaptchaElements = document.getElementsByName('procaptcha-response');
61
+
62
+ if (!procaptchaElements.length) {
63
+ updateCaptchaStatus('Error: No CAPTCHA response found', 'error');
64
+ alert("Must complete captcha");
65
+ return
66
+ }
67
+
68
+ const procaptchaToken = procaptchaElements[0].value;
69
+ updateCaptchaStatus(`Token received: ${procaptchaToken.substring(0, 15)}...`, 'success');
70
+
71
+ const payload = {
72
+ email: document.getElementById('email').value,
73
+ name: document.getElementById('name').value,
74
+ password: document.getElementById('password').value,
75
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_IMAGE,
76
+ "procaptcha-response": procaptchaToken,
77
+ };
78
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
79
+ console.log("posting to", url, "with payload", payload);
80
+ fetch(url, {
81
+ method: "POST",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ },
85
+ body: JSON.stringify(payload),
86
+ contentType: "application/json",
87
+ }).then((response) => {
88
+ return new Promise((resolve) => response.json()
89
+ .then((json) => resolve({
90
+ status: response.status,
91
+ ok: response.ok,
92
+ json,
93
+ })))
94
+ })
95
+ .then(async ({status, json, ok}) => {
96
+ console.log("status", status, "json", json.message, "ok", ok);
97
+ console.log("json", json)
98
+ try {
99
+ if (status !== 200) {
100
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
101
+ setIsError(true);
102
+ setMessage(json.message);
103
+ } else {
104
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
105
+ setIsError(false);
106
+ setMessage(json.message);
107
+ }
108
+ } catch (err) {
109
+ console.log(err);
110
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
111
+ }
112
+ })
113
+ .catch((err) => {
114
+ console.log(err);
115
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
116
+ setIsError(true);
117
+ setMessage("Error: " + err);
118
+ });
119
+ };
120
+
121
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
122
+
123
+ // Monitor DOM for CAPTCHA initialization
124
+ const observer = new MutationObserver((mutations) => {
125
+ mutations.forEach((mutation) => {
126
+ if (mutation.addedNodes.length) {
127
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
128
+ const node = mutation.addedNodes[i];
129
+ if (node.classList && (node.classList.contains('procaptcha') ||
130
+ node.querySelector && node.querySelector('.procaptcha'))) {
131
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
132
+ observer.disconnect();
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ });
138
+ });
139
+
140
+ observer.observe(document.body, { childList: true, subtree: true });
141
+ });
142
+
143
+
144
+ window.onCaptchaFailed = function () {
145
+ console.log('Challenge failed');
146
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
147
+ }
148
+
149
+ window.onCaptchaVerified = (output) => {
150
+ console.log('Challenge passed');
151
+ updateCaptchaStatus('Challenge passed successfully!', 'success');
152
+ updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
153
+ }
154
+
155
+ // Add custom event listener for CAPTCHA verification stages
156
+ document.addEventListener('DOMContentLoaded', function() {
157
+ setTimeout(() => {
158
+ updateCaptchaStatus('CAPTCHA script loaded and initialized', 'info');
159
+ updateCaptchaStatus('Waiting for user interaction...', 'info');
160
+ }, 500);
161
+ });
162
+ </script>
163
+ <script id="procaptchaScript" type="module" src="%VITE_BUNDLE_URL%" async defer></script>
164
+ </head>
165
+ <body>
166
+
167
+ <div class="mui-container">
168
+ <h1>Image CAPTCHA - Implicit Rendering</h1>
169
+ <p>This example demonstrates how to use Procaptcha in Proof of Work mode with implicit rendering.</p>
170
+
171
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
172
+
173
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form">
174
+ <h2>Example Login Form</h2>
175
+ <div class="mui-textfield mui-textfield--float-label">
176
+ <label for="name">Name</label>
177
+ <input id="name" type="text" name="name" required/>
178
+ </div>
179
+ <div class="mui-textfield mui-textfield--float-label">
180
+ <label for="email">Email Address</label>
181
+ <input id="email" type="email" name="email" required/>
182
+ </div>
183
+ <div class="mui-textfield mui-textfield--float-label">
184
+ <label for="password">Password</label>
185
+ <input id="password" type="password" name="password" required/>
186
+ </div>
187
+ <div class="mui-textfield mui-textfield--float-label">
188
+ <!-- Dev sitekey -->
189
+ <div
190
+ class="procaptcha"
191
+ data-theme="light"
192
+ data-sitekey="%PROSOPO_SITE_KEY_IMAGE%"
193
+ data-failed-callback="onCaptchaFailed"
194
+ data-callback="onCaptchaVerified"
195
+ ></div>
196
+ </div>
197
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
198
+ <button type="button" class="mui-btn mui-btn--raised" onclick="onActionHandler()" data-cy="submit-button">Submit</button>
199
+ </form>
200
+
201
+ <!-- Console output display area -->
202
+ <div id="console-output" class="console-output" style="display: none;"></div>
203
+
204
+ <!-- Explanation will be injected by the explanation-injector plugin -->
205
+ </div>
206
+ </body>
207
+ </html>
package/src/index.ts ADDED
@@ -0,0 +1,76 @@
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //@ts-nocheck
15
+ (() => {
16
+ function whenDOMReady(fn: () => void) {
17
+ if (document.readyState !== "loading") fn();
18
+ else document.addEventListener("DOMContentLoaded", fn);
19
+ }
20
+
21
+ whenDOMReady(() => {
22
+ const wrappers = Array.from(
23
+ document.querySelectorAll(".mui-textfield--float-label"),
24
+ );
25
+
26
+ for (const wrapper of wrappers) {
27
+ const input = wrapper.querySelector("input, textarea, select");
28
+ const label = wrapper.querySelector("label");
29
+ if (!input || !label) return;
30
+
31
+ // ensure screen-reader accessibility if label is hidden
32
+ if (!input.getAttribute("aria-label") && label.textContent) {
33
+ input.setAttribute("aria-label", label.textContent.trim());
34
+ }
35
+
36
+ const update = () => {
37
+ const isChecked = input.type === "checkbox" || input.type === "radio";
38
+ const hasValue = isChecked
39
+ ? input.checked
40
+ : input.value && input.value.trim() !== "";
41
+ if (document.activeElement === input || hasValue) {
42
+ wrapper.classList.add("label-hidden");
43
+ } else {
44
+ wrapper.classList.remove("label-hidden");
45
+ }
46
+ };
47
+
48
+ // respond to common input events
49
+ for (const evt of [
50
+ "focus",
51
+ "input",
52
+ "blur",
53
+ "change",
54
+ "keyup",
55
+ "paste",
56
+ ]) {
57
+ input.addEventListener(evt, update, { passive: true });
58
+ }
59
+
60
+ // initial run (handles pre-filled values)
61
+ update();
62
+ }
63
+
64
+ // short polling window to detect autofill (stops after ~3s)
65
+ const maxChecks = 12;
66
+ let checks = 0;
67
+ const iv = setInterval(() => {
68
+ checks++;
69
+ for (const w of wrappers) {
70
+ const input = w.querySelector("input,textarea,select");
71
+ if (input) input.dispatchEvent(new Event("input", { bubbles: true }));
72
+ }
73
+ if (checks >= maxChecks) clearInterval(iv);
74
+ }, 250);
75
+ });
76
+ })();
@@ -0,0 +1,132 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet" type="text/css"/>
5
+ <link href="styles/field.css" rel="stylesheet" type="text/css"/>
6
+
7
+ <script src="index.js"></script>
8
+ <title>Procaptcha Frictionless Mode - Explicit Rendering</title>
9
+ <link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
10
+ <!-- Status log will be injected by the status-log-injector plugin -->
11
+ </head>
12
+ <body>
13
+ <div class="mui-container">
14
+ <h1>Procaptcha Frictionless Mode - Explicit Rendering</h1>
15
+ <p>This example demonstrates how to use Procaptcha in frictionless mode with explicit rendering.</p>
16
+
17
+ <!-- Status log will be injected by the status-log-injector plugin -->
18
+
19
+ <form id="demo-form" class="mui-form">
20
+ <h2>Example Form</h2>
21
+
22
+ <div class="mui-textfield mui-textfield--float-label">
23
+ <label for="name">Name</label>
24
+ <input type="text" id="name" name="name" required />
25
+ </div>
26
+
27
+ <div class="mui-textfield mui-textfield--float-label">
28
+ <label for="email">Email</label>
29
+ <input type="email" id="email" name="email" required />
30
+ </div>
31
+
32
+ <!-- The container for the CAPTCHA -->
33
+ <div id="procaptcha-container"></div>
34
+
35
+ <button type="submit" class="mui-btn mui-btn--raised" data-cy="submit-button">Submit</button>
36
+
37
+ <div id="error"></div>
38
+ </form>
39
+
40
+ <div id="result" class="info-box" style="display: none;"></div>
41
+
42
+
43
+ <!-- Explanation will be injected by the explanation-injector plugin -->
44
+ </div>
45
+
46
+ <script type="module">
47
+ import { render, execute } from "%VITE_BUNDLE_URL%"
48
+
49
+ // Form validation function
50
+ function validateForm() {
51
+ const nameInput = document.getElementById('name');
52
+ const emailInput = document.getElementById('email');
53
+ const errorDisplay = document.getElementById('error');
54
+
55
+ if (!nameInput.value || !emailInput.value) {
56
+ errorDisplay.textContent = 'Please fill out all fields';
57
+ errorDisplay.style.display = 'block';
58
+ window.updateCaptchaStatus('Form validation failed: Empty fields', 'error');
59
+ return false;
60
+ }
61
+
62
+ // Basic email validation
63
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
64
+ if (!emailPattern.test(emailInput.value)) {
65
+ errorDisplay.textContent = 'Please enter a valid email address';
66
+ errorDisplay.style.display = 'block';
67
+ window.updateCaptchaStatus('Form validation failed: Invalid email format', 'error');
68
+ return false;
69
+ }
70
+
71
+ errorDisplay.style.display = 'none';
72
+ window.updateCaptchaStatus('Form validation passed', 'info');
73
+ return true;
74
+ }
75
+
76
+ // Callback for successful CAPTCHA verification
77
+ function handleCaptchaResponse(token) {
78
+ console.log('Procaptcha verified, token:', token);
79
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
80
+ window.updateCaptchaStatus(`Token generated: ${token.substring(0, 15)}...`, 'success');
81
+
82
+ const name = document.getElementById('name').value;
83
+ const email = document.getElementById('email').value;
84
+
85
+ // Display result
86
+ const resultElement = document.getElementById('result');
87
+ resultElement.innerHTML = `<strong>Form submitted with:</strong><br>
88
+ - Name: ${name}<br>
89
+ - Email: ${email}<br>
90
+ - Procaptcha verified: Yes`;
91
+ resultElement.style.display = 'block';
92
+
93
+ window.updateCaptchaStatus('Form submission completed', 'success');
94
+ }
95
+
96
+ // Callback for failed CAPTCHA verification
97
+ function handleCaptchaFailed() {
98
+ console.log('Captcha verification failed');
99
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
100
+ document.getElementById('error').textContent = 'CAPTCHA verification failed. Please try again.';
101
+ document.getElementById('error').style.display = 'block';
102
+ }
103
+
104
+ // Wait for DOM content to be loaded
105
+ document.addEventListener('DOMContentLoaded', function() {
106
+ window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
107
+
108
+ // Render the CAPTCHA
109
+ const widgetId = render(document.getElementById('procaptcha-container'), {
110
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
111
+ callback: handleCaptchaResponse,
112
+ "failed-callback": handleCaptchaFailed,
113
+ size: "invisible"
114
+ });
115
+
116
+ window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
117
+
118
+ // Add form submit handler
119
+ document.getElementById('demo-form').addEventListener('submit', function(e) {
120
+ e.preventDefault();
121
+ window.updateCaptchaStatus('Form submission attempted', 'info');
122
+
123
+ if (validateForm()) {
124
+ // Form is valid, CAPTCHA verification will be handled by the callback
125
+ window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
126
+ execute();
127
+ }
128
+ });
129
+ });
130
+ </script>
131
+ </body>
132
+ </html>