@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,204 @@
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: Invisible Frictionless - 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
+ // Define callbacks directly on window object so they're globally accessible
14
+ window.onCaptchaFailed = function () {
15
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
16
+ }
17
+
18
+ window.onCaptchaVerified = function(output) {
19
+ updateCaptchaStatus('Challenge passed successfully!', 'success');
20
+ updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
21
+ }
22
+
23
+ window.onActionHandler = function(token) {
24
+ updateCaptchaStatus('CAPTCHA callback executed with token', 'success');
25
+
26
+ if (!token) {
27
+ updateCaptchaStatus('Error: No token received from CAPTCHA', 'error');
28
+ alert("Must complete captcha");
29
+ return;
30
+ }
31
+
32
+ updateCaptchaStatus('Form submission initiated with valid CAPTCHA token', 'info');
33
+ const payload = {
34
+ email: document.getElementById('email').value,
35
+ name: document.getElementById('name').value,
36
+ password: document.getElementById('password').value,
37
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_FRICTIONLESS,
38
+ "procaptcha-response": token,
39
+ };
40
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
41
+ console.log("Posting to", url, "with payload", payload);
42
+ fetch(url, {
43
+ method: "POST",
44
+ headers: {
45
+ "Content-Type": "application/json",
46
+ },
47
+ body: JSON.stringify(payload),
48
+ contentType: "application/json",
49
+ }).then((response) => {
50
+ return new Promise((resolve) => response.json()
51
+ .then((json) => resolve({
52
+ status: response.status,
53
+ ok: response.ok,
54
+ json,
55
+ })))
56
+ })
57
+ .then(async ({status, json, ok}) => {
58
+ console.log("Response status:", status, "message:", json.message, "ok:", ok);
59
+ console.log("Full response:", json);
60
+ try {
61
+ if (status !== 200) {
62
+ window.setIsError(true);
63
+ window.setMessage(json.message);
64
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
65
+ } else {
66
+ window.setIsError(false);
67
+ window.setMessage(json.message);
68
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
69
+ }
70
+ } catch (err) {
71
+ console.log("Error processing response:", err);
72
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
73
+ }
74
+ })
75
+ .catch((err) => {
76
+ console.log("Fetch error:", err);
77
+ window.setIsError(true);
78
+ window.setMessage("Error: " + err);
79
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
80
+ });
81
+ };
82
+
83
+ window.addEventListener('load', () => {
84
+
85
+
86
+ window.setIsError = (isError) => {
87
+ const messageContainer = document.getElementById('messageContainer');
88
+ messageContainer.style.color = isError ? 'red' : 'black';
89
+ messageContainer.style.display = 'block';
90
+ };
91
+
92
+ window.setMessage = (message) => {
93
+ document.getElementById('message').innerText = message;
94
+ };
95
+
96
+ window.onLoggedIn = (token) => {
97
+ const url = new URL("/private", config.serverUrl).href;
98
+ console.log("getting private resource with token ", token, "at", url);
99
+ fetch(url, {
100
+ method: "GET",
101
+ headers: {
102
+ Origin: "http://localhost:9232", // TODO: change this to env var
103
+ "Content-Type": "application/json",
104
+ Authorization: `Bearer ${token}`,
105
+ },
106
+ })
107
+ .then(async (res) => {
108
+ try {
109
+ const jsonRes = await res.json();
110
+ if (res.status === 200) {
111
+ setMessage(jsonRes.message);
112
+ }
113
+ } catch (err) {
114
+ console.log(err);
115
+ }
116
+ })
117
+ .catch((err) => {
118
+ console.log(err);
119
+ });
120
+ };
121
+
122
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
123
+
124
+ // Monitor DOM for CAPTCHA initialization
125
+ const observer = new MutationObserver((mutations) => {
126
+ mutations.forEach((mutation) => {
127
+ if (mutation.addedNodes.length) {
128
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
129
+ const node = mutation.addedNodes[i];
130
+ if (node.classList && (node.classList.contains('procaptcha') ||
131
+ node.querySelector && node.querySelector('.procaptcha'))) {
132
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
133
+ observer.disconnect();
134
+ break;
135
+ }
136
+ }
137
+ }
138
+ });
139
+ });
140
+
141
+ observer.observe(document.body, { childList: true, subtree: true });
142
+ });
143
+
144
+ // Create a method to show CAPTCHA execution events
145
+ document.addEventListener('procaptcha:execute', function(e) {
146
+ updateCaptchaStatus(`CAPTCHA execution started: type=frictionless, mode=invisible`, 'info');
147
+ updateCaptchaStatus(`Container: ${e.detail.containerId}, timestamp: ${new Date(e.detail.timestamp).toLocaleTimeString()}`, 'info');
148
+ });
149
+
150
+ // Add custom event listener for CAPTCHA verification stages
151
+ document.addEventListener('DOMContentLoaded', function() {
152
+ setTimeout(() => {
153
+ updateCaptchaStatus('CAPTCHA script loaded and initialized', 'info');
154
+ updateCaptchaStatus('Waiting for user interaction...', 'info');
155
+ }, 500);
156
+ });
157
+ </script>
158
+ <script id="procaptchaScript" type="module" src="%VITE_BUNDLE_URL%" async defer></script>
159
+ </head>
160
+ <body>
161
+
162
+ <div class="mui-container">
163
+ <h1>Invisible Frictionless CAPTCHA - Implicit Rendering</h1>
164
+ <p>This example demonstrates how to use Procaptcha in invisible frictionless mode with implicit rendering directly on a button.</p>
165
+
166
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
167
+
168
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form">
169
+ <h2>Example Login Form</h2>
170
+ <div class="mui-textfield mui-textfield--float-label">
171
+ <label for="name">Name</label>
172
+ <input id="name" type="text" name="name" required/>
173
+ </div>
174
+ <div class="mui-textfield mui-textfield--float-label">
175
+ <label for="email">Email Address</label>
176
+ <input id="email" type="email" name="email" required/>
177
+ </div>
178
+ <div class="mui-textfield mui-textfield--float-label">
179
+ <label for="password">Password</label>
180
+ <input id="password" type="password" name="password" required/>
181
+ </div>
182
+ <div class="mui-textfield mui-textfield--float-label">
183
+ <!-- The div here was empty, we don't need it for invisible mode -->
184
+ </div>
185
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
186
+ <button
187
+ type="button"
188
+ class="mui-btn mui-btn--raised procaptcha"
189
+ data-sitekey="%PROSOPO_SITE_KEY_FRICTIONLESS%"
190
+ data-callback="onActionHandler"
191
+ data-failed-callback="onCaptchaFailed"
192
+ data-size="invisible"
193
+ data-cy="submit-button">
194
+ Submit
195
+ </button>
196
+ </form>
197
+
198
+ <!-- Console output display area -->
199
+ <div id="console-output" class="console-output" style="display: none;"></div>
200
+
201
+ <!-- Explanation will be injected by the explanation-injector plugin -->
202
+ </div>
203
+ </body>
204
+ </html>
@@ -0,0 +1,161 @@
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
+
15
+ updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
16
+
17
+ // Monitor DOM for CAPTCHA initialization
18
+ const observer = new MutationObserver((mutations) => {
19
+ mutations.forEach((mutation) => {
20
+ if (mutation.addedNodes.length) {
21
+ for (let i = 0; i < mutation.addedNodes.length; i++) {
22
+ const node = mutation.addedNodes[i];
23
+ if (node.classList && (node.classList.contains('procaptcha') ||
24
+ node.querySelector && node.querySelector('.procaptcha'))) {
25
+ updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
26
+ observer.disconnect();
27
+ break;
28
+ }
29
+ }
30
+ }
31
+ });
32
+ });
33
+
34
+ observer.observe(document.body, { childList: true, subtree: true });
35
+ });
36
+
37
+ // updateCaptchaStatus is now provided by status-log-injector
38
+ </script>
39
+ </head>
40
+ <body>
41
+ <div class="mui-container">
42
+ <h1>Procaptcha Image Mode - Explicit Rendering</h1>
43
+ <p>This example demonstrates how to use Procaptcha in image mode with explicit rendering.</p>
44
+
45
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
46
+
47
+ <form id="demo-form" class="mui-form">
48
+ <h2>Example Form</h2>
49
+
50
+ <div class="mui-textfield mui-textfield--float-label">
51
+ <label for="name">Name</label>
52
+ <input type="text" id="name" name="name" required />
53
+ </div>
54
+
55
+ <div class="mui-textfield mui-textfield--float-label">
56
+ <label for="email">Email</label>
57
+ <input type="email" id="email" name="email" required />
58
+ </div>
59
+
60
+ <!-- The container for the CAPTCHA -->
61
+ <div id="procaptcha-container"></div>
62
+
63
+ <button type="submit" class="mui-btn mui-btn--raised" data-cy="submit-button">Submit</button>
64
+
65
+ <div id="error"></div>
66
+ </form>
67
+
68
+ <div id="result" class="info-box" style="display: none;"></div>
69
+
70
+ <!-- Console output display area -->
71
+ <div id="console-output" class="console-output" style="display: none;"></div>
72
+
73
+ <!-- Explanation will be injected by the explanation-injector plugin -->
74
+ </div>
75
+
76
+ <script type="module">
77
+ import { render, execute } from "%VITE_BUNDLE_URL%"
78
+
79
+ // Form validation function
80
+ function validateForm() {
81
+ const nameInput = document.getElementById('name');
82
+ const emailInput = document.getElementById('email');
83
+ const errorDisplay = document.getElementById('error');
84
+
85
+ if (!nameInput.value || !emailInput.value) {
86
+ errorDisplay.textContent = 'Please fill out all fields';
87
+ errorDisplay.style.display = 'block';
88
+ window.updateCaptchaStatus('Form validation failed: Empty fields', 'error');
89
+ return false;
90
+ }
91
+
92
+ // Basic email validation
93
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
94
+ if (!emailPattern.test(emailInput.value)) {
95
+ errorDisplay.textContent = 'Please enter a valid email address';
96
+ errorDisplay.style.display = 'block';
97
+ window.updateCaptchaStatus('Form validation failed: Invalid email format', 'error');
98
+ return false;
99
+ }
100
+
101
+ errorDisplay.style.display = 'none';
102
+ window.updateCaptchaStatus('Form validation passed', 'info');
103
+ return true;
104
+ }
105
+
106
+ // Callback for successful CAPTCHA verification
107
+ function handleCaptchaResponse(token) {
108
+ console.log('Procaptcha verified, token:', token);
109
+ window.updateCaptchaStatus('Challenge passed successfully!', 'success');
110
+ window.updateCaptchaStatus(`Token generated: ${token.substring(0, 15)}...`, 'success');
111
+
112
+ const name = document.getElementById('name').value;
113
+ const email = document.getElementById('email').value;
114
+
115
+ // Display result
116
+ const resultElement = document.getElementById('result');
117
+ resultElement.innerHTML = `<strong>Form submitted with:</strong><br>
118
+ - Name: ${name}<br>
119
+ - Email: ${email}<br>
120
+ - Procaptcha verified: Yes`;
121
+ resultElement.style.display = 'block';
122
+
123
+ window.updateCaptchaStatus('Form submission completed', 'success');
124
+ }
125
+
126
+ // Callback for failed CAPTCHA verification
127
+ function handleCaptchaFailed() {
128
+ console.log('Captcha verification failed');
129
+ window.updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
130
+ document.getElementById('error').textContent = 'CAPTCHA verification failed. Please try again.';
131
+ document.getElementById('error').style.display = 'block';
132
+ }
133
+
134
+ // Wait for DOM content to be loaded
135
+ document.addEventListener('DOMContentLoaded', function() {
136
+ window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
137
+
138
+ // Render the CAPTCHA
139
+ const widgetId = render(document.getElementById('procaptcha-container'), {
140
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_IMAGE,
141
+ callback: handleCaptchaResponse,
142
+ "failed-callback": handleCaptchaFailed,
143
+ size: "invisible"
144
+ });
145
+
146
+ window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
147
+
148
+ // Add form submit handler
149
+ document.getElementById('demo-form').addEventListener('submit', function(e) {
150
+ e.preventDefault();
151
+ window.updateCaptchaStatus('Form submission attempted', 'info');
152
+ if (validateForm()) {
153
+ // Form is valid, CAPTCHA verification will be handled by the callback
154
+ window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
155
+ execute();
156
+ }
157
+ });
158
+ });
159
+ </script>
160
+ </body>
161
+ </html>
@@ -0,0 +1,203 @@
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: Invisible image - 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
+ // Define callbacks directly on window object so they're globally accessible
14
+ window.onCaptchaFailed = function () {
15
+ updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
16
+ }
17
+
18
+ window.onCaptchaVerified = function(output) {
19
+ updateCaptchaStatus('Challenge passed successfully!', 'success');
20
+ updateCaptchaStatus(`Token generated: ${output.substring(0, 15)}...`, 'success');
21
+ }
22
+
23
+ window.onActionHandler = function(token) {
24
+ updateCaptchaStatus('CAPTCHA callback executed with token', 'success');
25
+
26
+ if (!token) {
27
+ updateCaptchaStatus('Error: No token received from CAPTCHA', 'error');
28
+ alert("Must complete captcha");
29
+ return;
30
+ }
31
+
32
+ updateCaptchaStatus('Form submission initiated with valid CAPTCHA token', 'info');
33
+ const payload = {
34
+ email: document.getElementById('email').value,
35
+ name: document.getElementById('name').value,
36
+ password: document.getElementById('password').value,
37
+ siteKey: import.meta.env.PROSOPO_SITE_KEY_IMAGE,
38
+ "procaptcha-response": token,
39
+ };
40
+ const url = new URL('signup', import.meta.env.PROSOPO_SERVER_URL).href;
41
+ console.log("Posting to", url, "with payload", payload);
42
+ fetch(url, {
43
+ method: "POST",
44
+ headers: {
45
+ "Content-Type": "application/json",
46
+ },
47
+ body: JSON.stringify(payload),
48
+ contentType: "application/json",
49
+ }).then((response) => {
50
+ return new Promise((resolve) => response.json()
51
+ .then((json) => resolve({
52
+ status: response.status,
53
+ ok: response.ok,
54
+ json,
55
+ })))
56
+ })
57
+ .then(async ({status, json, ok}) => {
58
+ console.log("Response status:", status, "message:", json.message, "ok:", ok);
59
+ console.log("Full response:", json);
60
+ try {
61
+ if (status !== 200) {
62
+ window.setIsError(true);
63
+ window.setMessage(json.message);
64
+ updateCaptchaStatus(`API Error: ${json.message}`, 'error');
65
+ } else {
66
+ window.setIsError(false);
67
+ window.setMessage(json.message);
68
+ updateCaptchaStatus(`API Success: ${json.message}`, 'success');
69
+ }
70
+ } catch (err) {
71
+ console.log("Error processing response:", err);
72
+ updateCaptchaStatus(`Error processing response: ${err}`, 'error');
73
+ }
74
+ })
75
+ .catch((err) => {
76
+ console.log("Fetch error:", err);
77
+ window.setIsError(true);
78
+ window.setMessage("Error: " + err);
79
+ updateCaptchaStatus(`Fetch error: ${err}`, 'error');
80
+ });
81
+ };
82
+
83
+ window.addEventListener('load', () => {
84
+
85
+ window.setIsError = (isError) => {
86
+ const messageContainer = document.getElementById('messageContainer');
87
+ messageContainer.style.color = isError ? 'red' : 'black';
88
+ messageContainer.style.display = 'block';
89
+ };
90
+
91
+ window.setMessage = (message) => {
92
+ document.getElementById('message').innerText = message;
93
+ };
94
+
95
+ window.onLoggedIn = (token) => {
96
+ const url = new URL("/private", config.serverUrl).href;
97
+ console.log("getting private resource with token ", token, "at", url);
98
+ fetch(url, {
99
+ method: "GET",
100
+ headers: {
101
+ Origin: "http://localhost:9232", // TODO: change this to env var
102
+ "Content-Type": "application/json",
103
+ Authorization: `Bearer ${token}`,
104
+ },
105
+ })
106
+ .then(async (res) => {
107
+ try {
108
+ const jsonRes = await res.json();
109
+ if (res.status === 200) {
110
+ setMessage(jsonRes.message);
111
+ }
112
+ } catch (err) {
113
+ console.log(err);
114
+ }
115
+ })
116
+ .catch((err) => {
117
+ console.log(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
+ // Create a method to show CAPTCHA execution events
144
+ document.addEventListener('procaptcha:execute', function(e) {
145
+ updateCaptchaStatus(`CAPTCHA execution started: type=image, mode=invisible`, 'info');
146
+ updateCaptchaStatus(`Container: ${e.detail.containerId}, timestamp: ${new Date(e.detail.timestamp).toLocaleTimeString()}`, 'info');
147
+ });
148
+
149
+ // Add custom event listener for CAPTCHA verification stages
150
+ document.addEventListener('DOMContentLoaded', function() {
151
+ setTimeout(() => {
152
+ updateCaptchaStatus('CAPTCHA script loaded and initialized', 'info');
153
+ updateCaptchaStatus('Waiting for user interaction...', 'info');
154
+ }, 500);
155
+ });
156
+ </script>
157
+ <script id="procaptchaScript" type="module" src="%VITE_BUNDLE_URL%" async defer></script>
158
+ </head>
159
+ <body>
160
+
161
+ <div class="mui-container">
162
+ <h1>Invisible image CAPTCHA - Implicit Rendering</h1>
163
+ <p>This example demonstrates how to use Procaptcha in invisible image mode with implicit rendering directly on a button.</p>
164
+
165
+ <!-- CAPTCHA Status Display will be injected by the status-log-injector plugin -->
166
+
167
+ <form action="%PROSOPO_SERVER_URL%/signup" method="POST" class="mui-form">
168
+ <h2>Example Login Form</h2>
169
+ <div class="mui-textfield mui-textfield--float-label">
170
+ <label for="name">Name</label>
171
+ <input id="name" type="text" name="name" required/>
172
+ </div>
173
+ <div class="mui-textfield mui-textfield--float-label">
174
+ <label for="email">Email Address</label>
175
+ <input id="email" type="email" name="email" required/>
176
+ </div>
177
+ <div class="mui-textfield mui-textfield--float-label">
178
+ <label for="password">Password</label>
179
+ <input id="password" type="password" name="password" required/>
180
+ </div>
181
+ <div class="mui-textfield mui-textfield--float-label">
182
+ <!-- The div here was empty, we don't need it for invisible mode -->
183
+ </div>
184
+ <div id="messageContainer" style="display: none; color: black;"><span id="message"></span></div>
185
+ <button
186
+ type="button"
187
+ class="mui-btn mui-btn--raised procaptcha"
188
+ data-sitekey="%PROSOPO_SITE_KEY_IMAGE%"
189
+ data-callback="onActionHandler"
190
+ data-failed-callback="onCaptchaFailed"
191
+ data-size="invisible"
192
+ data-cy="submit-button">
193
+ Submit
194
+ </button>
195
+ </form>
196
+
197
+ <!-- Console output display area -->
198
+ <div id="console-output" class="console-output" style="display: none;"></div>
199
+
200
+ <!-- Explanation will be injected by the explanation-injector plugin -->
201
+ </div>
202
+ </body>
203
+ </html>