@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.
- package/.turbo/turbo-build$colon$cjs.log +6 -6
- package/.turbo/turbo-build$colon$tsc.log +14 -14
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +46 -0
- package/README.md +3 -1
- package/env.development +7 -0
- package/env.production +6 -0
- package/env.staging +2 -2
- package/package.json +10 -5
- package/src/assets/dummy.txt +0 -0
- package/src/frictionless-explicit-web3.html +249 -0
- package/src/frictionless-explicit.html +208 -0
- package/src/frictionless-implicit.html +218 -0
- package/src/image-explicit-web3.html +198 -0
- package/src/image-explicit.html +158 -0
- package/src/index.html +207 -0
- package/src/index.ts +76 -0
- package/src/invisible-frictionless-explicit.html +132 -0
- package/src/invisible-frictionless-implicit.html +204 -0
- package/src/invisible-image-explicit.html +161 -0
- package/src/invisible-image-implicit.html +203 -0
- package/src/invisible-pow-explicit.html +161 -0
- package/src/invisible-pow-implicit.html +106 -0
- package/src/invisible-puzzle-explicit.html +160 -0
- package/src/invisible-puzzle-implicit.html +106 -0
- package/src/plugins/explanation-injector.ts +294 -0
- package/src/plugins/form-filler-injector.ts +233 -0
- package/src/plugins/navigation-injector.ts +602 -0
- package/src/plugins/status-log-injector.ts +199 -0
- package/src/pow-explicit-web3.html +195 -0
- package/src/pow-explicit.html +158 -0
- package/src/pow-implicit.html +207 -0
- package/src/puzzle-explicit.html +158 -0
- package/src/puzzle-implicit.html +207 -0
- package/src/styles/captcha.css +78 -0
- package/src/styles/field.css +12 -0
- package/tsconfig.cjs.json +25 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.types.json +9 -0
- package/vite.cjs.config.ts +1 -1
- package/vite.config.ts +37 -4
|
@@ -0,0 +1,199 @@
|
|
|
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
|
+
// Vite plugin to inject CAPTCHA status log into HTML files
|
|
15
|
+
import type { IndexHtmlTransformContext, Plugin } from "vite";
|
|
16
|
+
|
|
17
|
+
export default function statusLogInjector(): Plugin {
|
|
18
|
+
// CSS for status log styling
|
|
19
|
+
const statusLogCss = `
|
|
20
|
+
<style>
|
|
21
|
+
.captcha-status {
|
|
22
|
+
margin-top: 20px;
|
|
23
|
+
padding: 15px;
|
|
24
|
+
background-color: #f0f8ff;
|
|
25
|
+
border: 2px solid #2196F3;
|
|
26
|
+
border-radius: 5px;
|
|
27
|
+
font-family: monospace;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
}
|
|
30
|
+
.status-item {
|
|
31
|
+
margin: 5px 0;
|
|
32
|
+
padding: 5px;
|
|
33
|
+
}
|
|
34
|
+
.status-success {
|
|
35
|
+
color: #4CAF50;
|
|
36
|
+
font-weight: bold;
|
|
37
|
+
}
|
|
38
|
+
.status-error {
|
|
39
|
+
color: #F44336;
|
|
40
|
+
font-weight: bold;
|
|
41
|
+
}
|
|
42
|
+
.status-info {
|
|
43
|
+
color: #2196F3;
|
|
44
|
+
}
|
|
45
|
+
.status-warning {
|
|
46
|
+
color: #FF9800;
|
|
47
|
+
}
|
|
48
|
+
.status-title {
|
|
49
|
+
font-weight: bold;
|
|
50
|
+
margin-bottom: 10px;
|
|
51
|
+
border-bottom: 1px solid #2196F3;
|
|
52
|
+
padding-bottom: 5px;
|
|
53
|
+
}
|
|
54
|
+
.console-output {
|
|
55
|
+
margin-top: 20px;
|
|
56
|
+
padding: 10px;
|
|
57
|
+
background-color: #f5f5f5;
|
|
58
|
+
border: 1px solid #ddd;
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
font-family: monospace;
|
|
61
|
+
white-space: pre-wrap;
|
|
62
|
+
max-height: 200px;
|
|
63
|
+
overflow-y: auto;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
// JavaScript for status log functionality
|
|
69
|
+
const statusLogJs = `
|
|
70
|
+
<script type="module">
|
|
71
|
+
// Function to update CAPTCHA status display
|
|
72
|
+
function updateCaptchaStatus(message, type = 'info') {
|
|
73
|
+
const statusContainer = document.getElementById('captcha-status');
|
|
74
|
+
if (!statusContainer) return;
|
|
75
|
+
|
|
76
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
77
|
+
const statusItem = document.createElement('div');
|
|
78
|
+
statusItem.className = \`status-item status-\${type}\`;
|
|
79
|
+
statusItem.innerHTML = \`[\${timestamp}] \${message}\`;
|
|
80
|
+
statusContainer.appendChild(statusItem);
|
|
81
|
+
|
|
82
|
+
// Also log to console
|
|
83
|
+
console.log(\`CAPTCHA Status: \${message}\`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Make updateCaptchaStatus available globally
|
|
87
|
+
window.updateCaptchaStatus = updateCaptchaStatus;
|
|
88
|
+
|
|
89
|
+
// Override the original callbacks with enhanced versions that use status logging
|
|
90
|
+
const originalOnCaptchaFailed = window.onCaptchaFailed;
|
|
91
|
+
window.onCaptchaFailed = function() {
|
|
92
|
+
updateCaptchaStatus('Challenge failed - CAPTCHA verification could not be completed', 'error');
|
|
93
|
+
if (originalOnCaptchaFailed) originalOnCaptchaFailed();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const originalOnCaptchaVerified = window.onCaptchaVerified;
|
|
97
|
+
window.onCaptchaVerified = function(output) {
|
|
98
|
+
updateCaptchaStatus('Challenge passed successfully!', 'success');
|
|
99
|
+
updateCaptchaStatus(\`Token generated: \${output.substring(0, 15)}...\`, 'success');
|
|
100
|
+
if (originalOnCaptchaVerified) originalOnCaptchaVerified(output);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const originalOnActionHandler = window.onActionHandler;
|
|
104
|
+
window.onActionHandler = function() {
|
|
105
|
+
const procaptchaElements = document.getElementsByName('procaptcha-response');
|
|
106
|
+
|
|
107
|
+
if (!procaptchaElements.length) {
|
|
108
|
+
updateCaptchaStatus('Error: No CAPTCHA response token found', 'error');
|
|
109
|
+
alert("Must complete captcha");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
updateCaptchaStatus('Form submission initiated with valid CAPTCHA token', 'info');
|
|
114
|
+
if (originalOnActionHandler) originalOnActionHandler();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
118
|
+
updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
|
|
119
|
+
|
|
120
|
+
// Monitor DOM for CAPTCHA initialization
|
|
121
|
+
const observer = new MutationObserver((mutations) => {
|
|
122
|
+
mutations.forEach((mutation) => {
|
|
123
|
+
if (mutation.addedNodes.length) {
|
|
124
|
+
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
125
|
+
const node = mutation.addedNodes[i];
|
|
126
|
+
if (node.classList && (node.classList.contains('procaptcha') ||
|
|
127
|
+
node.querySelector && node.querySelector('.procaptcha'))) {
|
|
128
|
+
updateCaptchaStatus('CAPTCHA DOM elements initialized', 'info');
|
|
129
|
+
observer.disconnect();
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Create a method to show CAPTCHA execution events
|
|
141
|
+
document.addEventListener('procaptcha:execute', function(e) {
|
|
142
|
+
// Determine captcha type from the page URL
|
|
143
|
+
const url = window.location.pathname;
|
|
144
|
+
const isFrictionless = url.includes('frictionless');
|
|
145
|
+
const isImage = url.includes('image');
|
|
146
|
+
const isPow = url.includes('pow');
|
|
147
|
+
|
|
148
|
+
let captchaType = 'unknown';
|
|
149
|
+
if (isFrictionless) captchaType = 'frictionless';
|
|
150
|
+
else if (isImage) captchaType = 'image';
|
|
151
|
+
else if (isPow) captchaType = 'pow';
|
|
152
|
+
|
|
153
|
+
updateCaptchaStatus(\`CAPTCHA execution started: type=\${captchaType}\`, 'info');
|
|
154
|
+
updateCaptchaStatus(\`Container: \${e.detail.containerId}, timestamp: \${new Date(e.detail.timestamp).toLocaleTimeString()}\`, 'info');
|
|
155
|
+
});
|
|
156
|
+
</script>
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
// HTML for status log container
|
|
160
|
+
const statusLogHtml = `
|
|
161
|
+
<!-- CAPTCHA Status Display -->
|
|
162
|
+
<div id="captcha-status" class="captcha-status">
|
|
163
|
+
<div class="status-title">CAPTCHA Status Log</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- Console output display area -->
|
|
167
|
+
<div id="console-output" class="console-output" style="display: none;"></div>
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
name: "status-log-injector",
|
|
172
|
+
transformIndexHtml: {
|
|
173
|
+
enforce: "post", // Run after other HTML transforms
|
|
174
|
+
transform(html: string, ctx: IndexHtmlTransformContext): string {
|
|
175
|
+
// Skip if no HTML body is found
|
|
176
|
+
if (!html.includes("<body") || !html.includes("</body>")) {
|
|
177
|
+
return html;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Skip if status log already exists
|
|
181
|
+
if (html.includes('id="captcha-status"')) {
|
|
182
|
+
return html;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Add CSS to head
|
|
186
|
+
let updatedHtml = html.replace("</head>", `${statusLogCss}</head>`);
|
|
187
|
+
|
|
188
|
+
// Add JavaScript to head
|
|
189
|
+
updatedHtml = updatedHtml.replace("</head>", `${statusLogJs}</head>`);
|
|
190
|
+
|
|
191
|
+
// Add status log container after the form or before the closing body if no form
|
|
192
|
+
if (updatedHtml.includes("</form>")) {
|
|
193
|
+
return updatedHtml.replace("</form>", `</form>${statusLogHtml}`);
|
|
194
|
+
}
|
|
195
|
+
return updatedHtml.replace("</body>", `${statusLogHtml}</body>`);
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
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 Proof of Work 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 Proof of Work Mode - Explicit Rendering</h1>
|
|
42
|
+
<p>This example demonstrates how to use Procaptcha in Proof of Work 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
|
+
import { web3AccountsSubscribe, web3Enable } from "@polkadot/extension-dapp";
|
|
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
|
+
// Get web3 accounts and create select element
|
|
135
|
+
async function getWeb3Accounts() {
|
|
136
|
+
return new Promise((resolve) =>{ web3AccountsSubscribe((accounts) => {
|
|
137
|
+
if (accounts.length === 0) {
|
|
138
|
+
window.updateCaptchaStatus('No accounts found. Please connect your wallet.', 'error');
|
|
139
|
+
} else {
|
|
140
|
+
window.updateCaptchaStatus(`Connected to ${accounts.length} account(s).`, 'info');
|
|
141
|
+
// render a modal and get the user to select 1 account
|
|
142
|
+
const accountSelect = document.createElement('select');
|
|
143
|
+
accountSelect.id = 'account-select';
|
|
144
|
+
accountSelect.classList.add('mui-select');
|
|
145
|
+
accountSelect.classList.add('account-select');
|
|
146
|
+
accounts.forEach((account) => {
|
|
147
|
+
const option = document.createElement('option');
|
|
148
|
+
option.value = account.address;
|
|
149
|
+
option.textContent = `${account.meta.name} (${account.address})`;
|
|
150
|
+
accountSelect.appendChild(option);
|
|
151
|
+
});
|
|
152
|
+
const accountContainer = document.createElement('div');
|
|
153
|
+
accountContainer.innerHTML = '<label for="account-select"><h2>Select Account:</h2></label>';
|
|
154
|
+
accountContainer.appendChild(accountSelect);
|
|
155
|
+
document.getElementById('demo-form').prepend(accountContainer);
|
|
156
|
+
window.updateCaptchaStatus('Accounts loaded successfully', 'info');
|
|
157
|
+
}
|
|
158
|
+
resolve(accounts);
|
|
159
|
+
})});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Wait for DOM content to be loaded
|
|
163
|
+
document.addEventListener('DOMContentLoaded', async function() {
|
|
164
|
+
window.updateCaptchaStatus('Initializing CAPTCHA with explicit render call', 'info');
|
|
165
|
+
|
|
166
|
+
await web3Enable("Procaptcha Demo");
|
|
167
|
+
|
|
168
|
+
await getWeb3Accounts();
|
|
169
|
+
|
|
170
|
+
// Render the CAPTCHA
|
|
171
|
+
const account = document.getElementById('account-select') ? document.getElementById('account-select').value : null;
|
|
172
|
+
window.updateCaptchaStatus(`Rendering the CAPTCHA for account ${account}`, 'info');
|
|
173
|
+
const widgetId = render(document.getElementById('procaptcha-container'), {
|
|
174
|
+
siteKey: import.meta.env.PROSOPO_SITE_KEY_POW,
|
|
175
|
+
callback: handleCaptchaResponse,
|
|
176
|
+
"failed-callback": handleCaptchaFailed,
|
|
177
|
+
web3:true,
|
|
178
|
+
userAccountAddress: account
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
window.updateCaptchaStatus('CAPTCHA render function called with widget ID: ' + widgetId, 'info');
|
|
182
|
+
|
|
183
|
+
// Add form submit handler
|
|
184
|
+
document.getElementById('demo-form').addEventListener('submit', function(e) {
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
window.updateCaptchaStatus('Form submission attempted', 'info');
|
|
187
|
+
if (validateForm()) {
|
|
188
|
+
// Form is valid, CAPTCHA verification will be handled by the callback
|
|
189
|
+
window.updateCaptchaStatus('Waiting for CAPTCHA verification', 'info');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
</script>
|
|
194
|
+
</body>
|
|
195
|
+
</html>
|
|
@@ -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 Proof of Work 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 Proof of Work Mode - Explicit Rendering</h1>
|
|
42
|
+
<p>This example demonstrates how to use Procaptcha in Proof of Work 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_POW,
|
|
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>
|