@prosopo/client-bundle-example 2.10.24 → 2.11.0
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 +5 -4
- package/.turbo/turbo-build$colon$tsc.log +11 -11
- package/.turbo/turbo-build.log +6 -5
- package/CHANGELOG.md +16 -0
- package/dist/cjs/index.cjs +13 -41
- package/dist/cjs/plugins/pages.cjs +113 -0
- package/dist/index.js +2 -42
- package/dist/plugins/pages.js +103 -0
- package/package.json +2 -2
- package/src/frictionless-explicit-web3.html +10 -23
- package/src/frictionless-explicit.html +10 -22
- package/src/frictionless-implicit.html +60 -86
- package/src/frictionless-manual-start.html +13 -33
- package/src/image-explicit-web3.html +8 -24
- package/src/image-explicit.html +8 -23
- package/src/image-implicit.html +192 -0
- package/src/index.html +11 -26
- package/src/index.ts +1 -62
- package/src/invisible-frictionless-explicit.html +8 -17
- package/src/invisible-frictionless-implicit.html +8 -25
- package/src/invisible-image-explicit.html +8 -23
- package/src/invisible-image-implicit.html +8 -24
- package/src/invisible-pow-explicit.html +8 -23
- package/src/invisible-pow-implicit.html +9 -21
- package/src/invisible-puzzle-explicit.html +8 -23
- package/src/invisible-puzzle-implicit.html +9 -21
- package/src/plugins/code-snippet-injector.ts +179 -0
- package/src/plugins/form-filler-injector.ts +36 -121
- package/src/plugins/layout-injector.ts +238 -0
- package/src/plugins/logo.ts +15 -0
- package/src/plugins/pages.ts +219 -0
- package/src/plugins/placement-injector.ts +25 -64
- package/src/plugins/slots.ts +32 -0
- package/src/plugins/status-log-injector.ts +35 -114
- package/src/pow-explicit-web3.html +8 -24
- package/src/pow-explicit.html +8 -23
- package/src/pow-implicit-sessionid.html +10 -24
- package/src/pow-implicit.html +9 -24
- package/src/puzzle-bind-explicit.html +9 -24
- package/src/puzzle-explicit.html +8 -23
- package/src/puzzle-implicit.html +9 -24
- package/src/styles/demo.css +803 -0
- package/src/tests/plugins.unit.test.ts +285 -118
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.config.ts +5 -5
- package/src/plugins/explanation-injector.ts +0 -294
- package/src/plugins/navigation-injector.ts +0 -606
- package/src/styles/captcha.css +0 -78
- package/src/styles/field.css +0 -12
- package/src/tests/floatLabel.unit.test.ts +0 -195
- package/src/tests/floatLabelReady.unit.test.ts +0 -36
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
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
4
|
|
|
7
|
-
<
|
|
8
|
-
<title>Procaptcha Invisible Puzzle Mode - Implicit Rendering</title>
|
|
9
|
-
<link href="styles/captcha.css" rel="stylesheet" type="text/css"/>
|
|
5
|
+
<title>Procaptcha demo: Puzzle, invisible, implicit rendering</title>
|
|
10
6
|
<script type="module">
|
|
11
|
-
// Function to update CAPTCHA status display is now provided by status-log-injector
|
|
12
7
|
|
|
13
8
|
document.addEventListener('DOMContentLoaded', function() {
|
|
14
9
|
updateCaptchaStatus('Page loaded - Initializing CAPTCHA system', 'info');
|
|
@@ -67,27 +62,24 @@
|
|
|
67
62
|
<script type="module" src="%VITE_BUNDLE_URL%" async defer></script>
|
|
68
63
|
</head>
|
|
69
64
|
<body>
|
|
70
|
-
<div class="
|
|
71
|
-
<h1>Invisible Puzzle CAPTCHA - Implicit Rendering</h1>
|
|
72
|
-
<p>This example demonstrates how to use Procaptcha in invisible Puzzle mode with implicit rendering directly on a button. The puzzle overlay appears when the button is clicked.</p>
|
|
65
|
+
<div class="demo-card">
|
|
73
66
|
|
|
74
|
-
<!--
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
<div class="mui-textfield mui-textfield--float-label">
|
|
67
|
+
<!-- nosemgrep: python.django.security.django-no-csrf-token.django-no-csrf-token -->
|
|
68
|
+
<form id="demo-form" action="javascript:void(0);" method="POST" class="demo-form">
|
|
69
|
+
<h2>Create an account</h2>
|
|
70
|
+
<div class="demo-field">
|
|
79
71
|
<label for="name">Name</label>
|
|
80
72
|
<input type="text" id="name" name="name" required />
|
|
81
73
|
</div>
|
|
82
74
|
|
|
83
|
-
<div class="
|
|
75
|
+
<div class="demo-field">
|
|
84
76
|
<label for="email">Email</label>
|
|
85
77
|
<input type="email" id="email" name="email" required />
|
|
86
78
|
</div>
|
|
87
79
|
|
|
88
80
|
<!-- Submit button with Procaptcha attributes -->
|
|
89
81
|
<button
|
|
90
|
-
class="
|
|
82
|
+
class="demo-button procaptcha"
|
|
91
83
|
data-sitekey="%PROSOPO_SITE_KEY_PUZZLE%"
|
|
92
84
|
data-callback="onSubmit"
|
|
93
85
|
data-size="invisible">
|
|
@@ -95,12 +87,8 @@
|
|
|
95
87
|
</button>
|
|
96
88
|
</form>
|
|
97
89
|
|
|
98
|
-
<div id="result" class="
|
|
99
|
-
|
|
100
|
-
<!-- Console output display area -->
|
|
101
|
-
<div id="console-output" class="console-output" style="display: none;"></div>
|
|
90
|
+
<div id="result" class="demo-result" style="display: none;"></div>
|
|
102
91
|
|
|
103
|
-
<!-- Explanation will be injected by the explanation-injector plugin -->
|
|
104
92
|
</div>
|
|
105
93
|
</body>
|
|
106
94
|
</html>
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
|
|
15
|
+
// Shows the code a site would add to get the setup on the current demo page.
|
|
16
|
+
import type { IndexHtmlTransformContext, Plugin } from "vite";
|
|
17
|
+
import {
|
|
18
|
+
type DemoPage,
|
|
19
|
+
findPageByPath,
|
|
20
|
+
pagePathFromFilename,
|
|
21
|
+
} from "./pages.js";
|
|
22
|
+
import { fillSlot } from "./slots.js";
|
|
23
|
+
|
|
24
|
+
const BUNDLE_URL = "https://js.prosopo.io/js/procaptcha.bundle.js";
|
|
25
|
+
|
|
26
|
+
const implicitSnippet = `<script src="${BUNDLE_URL}" async defer></script>
|
|
27
|
+
|
|
28
|
+
<form action="/signup" method="POST">
|
|
29
|
+
<!-- your fields -->
|
|
30
|
+
<div class="procaptcha" data-sitekey="YOUR_SITE_KEY"></div>
|
|
31
|
+
<button type="submit">Submit</button>
|
|
32
|
+
</form>`;
|
|
33
|
+
|
|
34
|
+
const explicitSnippet = `<div id="procaptcha-container"></div>
|
|
35
|
+
|
|
36
|
+
<script type="module">
|
|
37
|
+
import { render } from "${BUNDLE_URL}";
|
|
38
|
+
|
|
39
|
+
render(document.getElementById("procaptcha-container"), {
|
|
40
|
+
siteKey: "YOUR_SITE_KEY",
|
|
41
|
+
callback: (token) => {
|
|
42
|
+
// send the token to your server with the form
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
</script>`;
|
|
46
|
+
|
|
47
|
+
const manualSnippet = `<script src="${BUNDLE_URL}" async defer></script>
|
|
48
|
+
|
|
49
|
+
<div class="procaptcha" data-sitekey="YOUR_SITE_KEY"
|
|
50
|
+
data-start-mode="manual"></div>
|
|
51
|
+
|
|
52
|
+
<script>
|
|
53
|
+
// nothing runs until this is called or the box is ticked
|
|
54
|
+
window.procaptcha.start();
|
|
55
|
+
</script>`;
|
|
56
|
+
|
|
57
|
+
const boundSnippet = `<div id="procaptcha-container"></div>
|
|
58
|
+
<button id="submit-button" type="submit">Submit</button>
|
|
59
|
+
|
|
60
|
+
<script type="module">
|
|
61
|
+
import { render } from "${BUNDLE_URL}";
|
|
62
|
+
|
|
63
|
+
render(document.getElementById("procaptcha-container"), {
|
|
64
|
+
siteKey: "YOUR_SITE_KEY",
|
|
65
|
+
bind: "#submit-button",
|
|
66
|
+
callback: (token) => {
|
|
67
|
+
// send the token to your server with the form
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
</script>`;
|
|
71
|
+
|
|
72
|
+
const invisibleImplicitSnippet = `<script src="${BUNDLE_URL}" async defer></script>
|
|
73
|
+
|
|
74
|
+
<button class="procaptcha" data-sitekey="YOUR_SITE_KEY"
|
|
75
|
+
data-size="invisible" data-callback="onSubmit">Submit</button>
|
|
76
|
+
|
|
77
|
+
<script>
|
|
78
|
+
function onSubmit(token) {
|
|
79
|
+
// send the token to your server with the form
|
|
80
|
+
}
|
|
81
|
+
</script>`;
|
|
82
|
+
|
|
83
|
+
const invisibleExplicitSnippet = `<div id="procaptcha-container"></div>
|
|
84
|
+
|
|
85
|
+
<script type="module">
|
|
86
|
+
import { render, execute } from "${BUNDLE_URL}";
|
|
87
|
+
|
|
88
|
+
render(document.getElementById("procaptcha-container"), {
|
|
89
|
+
siteKey: "YOUR_SITE_KEY",
|
|
90
|
+
size: "invisible",
|
|
91
|
+
callback: (token) => {
|
|
92
|
+
// send the token to your server with the form
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
document.querySelector("form").addEventListener("submit", (event) => {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
execute();
|
|
99
|
+
});
|
|
100
|
+
</script>`;
|
|
101
|
+
|
|
102
|
+
export const snippetFor = (page: DemoPage): string => {
|
|
103
|
+
if (page.mode === "invisible") {
|
|
104
|
+
return page.rendering === "explicit"
|
|
105
|
+
? invisibleExplicitSnippet
|
|
106
|
+
: invisibleImplicitSnippet;
|
|
107
|
+
}
|
|
108
|
+
switch (page.rendering) {
|
|
109
|
+
case "explicit":
|
|
110
|
+
return explicitSnippet;
|
|
111
|
+
case "manual":
|
|
112
|
+
return manualSnippet;
|
|
113
|
+
case "bound":
|
|
114
|
+
return boundSnippet;
|
|
115
|
+
default:
|
|
116
|
+
return implicitSnippet;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const escapeHtml = (text: string): string =>
|
|
121
|
+
text
|
|
122
|
+
.replace(/&/g, "&")
|
|
123
|
+
.replace(/</g, "<")
|
|
124
|
+
.replace(/>/g, ">")
|
|
125
|
+
.replace(/"/g, """)
|
|
126
|
+
.replace(/'/g, "'");
|
|
127
|
+
|
|
128
|
+
const copyScript = `
|
|
129
|
+
<script>
|
|
130
|
+
document.addEventListener("click", function (event) {
|
|
131
|
+
var button = event.target.closest && event.target.closest("[data-demo-copy]");
|
|
132
|
+
if (!button) return;
|
|
133
|
+
var code = button.closest(".demo-code-block").querySelector("code");
|
|
134
|
+
navigator.clipboard.writeText(code.textContent).then(function () {
|
|
135
|
+
button.textContent = "Copied";
|
|
136
|
+
setTimeout(function () {
|
|
137
|
+
button.textContent = "Copy";
|
|
138
|
+
}, 1500);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
</script>`;
|
|
142
|
+
|
|
143
|
+
// A plain button, not type="button": several specs probe
|
|
144
|
+
// button[type='button']:nth-of-type(2) and must not find this one.
|
|
145
|
+
const snippetMarkup = (page: DemoPage): string => `
|
|
146
|
+
<div class="demo-code-block">
|
|
147
|
+
<div class="demo-code-bar"><button class="demo-copy" data-demo-copy>Copy</button></div>
|
|
148
|
+
<pre class="demo-code"><code>${escapeHtml(snippetFor(page))}</code></pre>
|
|
149
|
+
</div>
|
|
150
|
+
<p class="demo-note">Pick the captcha type for your site key in the <a href="https://portal.prosopo.io/">portal</a>.</p>
|
|
151
|
+
${copyScript}`;
|
|
152
|
+
|
|
153
|
+
export default function codeSnippetInjector(): Plugin {
|
|
154
|
+
return {
|
|
155
|
+
name: "code-snippet-injector",
|
|
156
|
+
transformIndexHtml: {
|
|
157
|
+
order: "post",
|
|
158
|
+
handler(html: string, ctx: IndexHtmlTransformContext): string {
|
|
159
|
+
if (!html.includes("<body") || !html.includes("</body>")) {
|
|
160
|
+
return html;
|
|
161
|
+
}
|
|
162
|
+
if (html.includes("data-demo-copy")) {
|
|
163
|
+
return html;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const page = findPageByPath(pagePathFromFilename(ctx.filename));
|
|
167
|
+
if (!page) {
|
|
168
|
+
return html;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const markup = snippetMarkup(page);
|
|
172
|
+
return (
|
|
173
|
+
fillSlot(html, "code", markup) ??
|
|
174
|
+
html.replace("</body>", () => `${markup}</body>`)
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -11,183 +11,105 @@
|
|
|
11
11
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
// Vite plugin to inject a button that fills the demo form with sample values.
|
|
15
16
|
import type { IndexHtmlTransformContext, Plugin } from "vite";
|
|
17
|
+
import { fillSlot } from "./slots.js";
|
|
16
18
|
|
|
17
19
|
export default function formFillerInjector(): Plugin {
|
|
18
|
-
//
|
|
19
|
-
const buttonStyle = `
|
|
20
|
-
<style>
|
|
21
|
-
.form-filler-button {
|
|
22
|
-
position: fixed;
|
|
23
|
-
bottom: 20px;
|
|
24
|
-
right: 20px;
|
|
25
|
-
background-color: #2196F3;
|
|
26
|
-
color: white;
|
|
27
|
-
border: none;
|
|
28
|
-
border-radius: 30px;
|
|
29
|
-
padding: 12px 20px;
|
|
30
|
-
font-size: 14px;
|
|
31
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
32
|
-
cursor: pointer;
|
|
33
|
-
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
|
34
|
-
z-index: 9999;
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
gap: 8px;
|
|
38
|
-
transition: all 0.2s ease;
|
|
39
|
-
outline: none;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.form-filler-button:hover {
|
|
43
|
-
background-color: #1976D2;
|
|
44
|
-
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
|
|
45
|
-
transform: translateY(-2px);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.form-filler-button:active {
|
|
49
|
-
transform: translateY(1px);
|
|
50
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.form-filler-button svg {
|
|
54
|
-
width: 18px;
|
|
55
|
-
height: 18px;
|
|
56
|
-
fill: white;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.form-filler-button span {
|
|
60
|
-
font-weight: 500;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@media (max-width: 768px) {
|
|
64
|
-
.form-filler-button {
|
|
65
|
-
bottom: 15px;
|
|
66
|
-
right: 15px;
|
|
67
|
-
padding: 10px 15px;
|
|
68
|
-
font-size: 12px;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.form-filler-button svg {
|
|
72
|
-
width: 14px;
|
|
73
|
-
height: 14px;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
</style>
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
// HTML for the form filler button
|
|
20
|
+
// No type attribute: specs probe button[type='button']:nth-of-type(2).
|
|
80
21
|
const buttonHtml = `
|
|
81
|
-
<button id="form-filler-button" class="form-filler-button" title="Fill
|
|
82
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
22
|
+
<button id="form-filler-button" class="form-filler-button" title="Fill the form with sample values">
|
|
23
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true">
|
|
83
24
|
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
|
|
84
25
|
</svg>
|
|
85
|
-
<span>Fill
|
|
26
|
+
<span>Fill form</span>
|
|
86
27
|
</button>
|
|
87
28
|
`;
|
|
88
29
|
|
|
89
|
-
// JavaScript for form filling functionality
|
|
90
30
|
const fillerScript = `
|
|
91
31
|
<script>
|
|
92
32
|
document.addEventListener('DOMContentLoaded', function() {
|
|
93
|
-
// Create a function to fill the form with sensible default values
|
|
94
33
|
function fillFormWithDefaults() {
|
|
95
|
-
// Get all input elements
|
|
96
34
|
const inputs = document.querySelectorAll('input, select, textarea');
|
|
97
|
-
|
|
98
|
-
// Record which elements were filled for status feedback
|
|
99
35
|
const filledElements = [];
|
|
100
|
-
|
|
101
|
-
// Process each input
|
|
36
|
+
|
|
102
37
|
inputs.forEach(input => {
|
|
103
38
|
let filled = true;
|
|
104
|
-
|
|
105
|
-
// Skip hidden inputs and those with existing values
|
|
39
|
+
|
|
106
40
|
if (input.type === 'hidden' || (input.value && input.value.trim() !== '')) {
|
|
107
41
|
filled = false;
|
|
108
42
|
}
|
|
109
|
-
// Handle different input types
|
|
110
43
|
else if (input.type === 'text') {
|
|
111
44
|
if (input.id.toLowerCase().includes('name') || input.name.toLowerCase().includes('name')) {
|
|
112
45
|
input.value = 'John Doe';
|
|
113
46
|
} else {
|
|
114
47
|
input.value = 'Default Text';
|
|
115
48
|
}
|
|
116
|
-
}
|
|
49
|
+
}
|
|
117
50
|
else if (input.type === 'email') {
|
|
118
51
|
input.value = 'test@example.com';
|
|
119
|
-
}
|
|
52
|
+
}
|
|
120
53
|
else if (input.type === 'password') {
|
|
121
54
|
input.value = 'Password123!';
|
|
122
|
-
}
|
|
55
|
+
}
|
|
123
56
|
else if (input.type === 'tel') {
|
|
124
57
|
input.value = '+1234567890';
|
|
125
|
-
}
|
|
58
|
+
}
|
|
126
59
|
else if (input.type === 'number') {
|
|
127
60
|
input.value = '42';
|
|
128
|
-
}
|
|
61
|
+
}
|
|
129
62
|
else if (input.type === 'url') {
|
|
130
63
|
input.value = 'https://example.com';
|
|
131
|
-
}
|
|
64
|
+
}
|
|
132
65
|
else if (input.type === 'date') {
|
|
133
66
|
const today = new Date();
|
|
134
67
|
const yyyy = today.getFullYear();
|
|
135
68
|
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
|
136
69
|
const dd = String(today.getDate()).padStart(2, '0');
|
|
137
70
|
input.value = \`\${yyyy}-\${mm}-\${dd}\`;
|
|
138
|
-
}
|
|
71
|
+
}
|
|
139
72
|
else if (input.type === 'time') {
|
|
140
73
|
input.value = '12:00';
|
|
141
|
-
}
|
|
74
|
+
}
|
|
142
75
|
else if (input.type === 'checkbox' || input.type === 'radio') {
|
|
143
76
|
input.checked = true;
|
|
144
|
-
}
|
|
77
|
+
}
|
|
145
78
|
else if (input.tagName === 'SELECT') {
|
|
146
79
|
if (input.options.length > 0) {
|
|
147
|
-
input.selectedIndex = 1 >= input.options.length ? 0 : 1;
|
|
80
|
+
input.selectedIndex = 1 >= input.options.length ? 0 : 1;
|
|
148
81
|
}
|
|
149
|
-
}
|
|
82
|
+
}
|
|
150
83
|
else if (input.tagName === 'TEXTAREA') {
|
|
151
84
|
input.value = 'This is a sample text for the textarea field.';
|
|
152
|
-
}
|
|
85
|
+
}
|
|
153
86
|
else {
|
|
154
|
-
// For any other input types
|
|
155
87
|
input.value = 'Default Value';
|
|
156
88
|
}
|
|
157
|
-
|
|
158
|
-
// Add to filled elements if we actually filled it
|
|
89
|
+
|
|
159
90
|
if (filled) {
|
|
160
91
|
filledElements.push(input);
|
|
161
|
-
|
|
162
|
-
// Dispatch input event to trigger any listeners
|
|
163
|
-
const event = new Event('input', { bubbles: true });
|
|
164
|
-
input.dispatchEvent(event);
|
|
165
|
-
|
|
166
|
-
// For select elements, also dispatch change event
|
|
92
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
167
93
|
if (input.tagName === 'SELECT') {
|
|
168
|
-
|
|
169
|
-
input.dispatchEvent(changeEvent);
|
|
94
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
170
95
|
}
|
|
171
96
|
}
|
|
172
97
|
});
|
|
173
|
-
|
|
174
|
-
// Show a notification about what was filled
|
|
98
|
+
|
|
175
99
|
if (filledElements.length > 0) {
|
|
176
|
-
// If an updateCaptchaStatus function exists (from our other demos), use it
|
|
177
100
|
if (typeof window.updateCaptchaStatus === 'function') {
|
|
178
101
|
window.updateCaptchaStatus(\`Filled \${filledElements.length} form fields with default values\`, 'info');
|
|
179
102
|
} else {
|
|
180
103
|
console.log(\`Filled \${filledElements.length} form fields with default values\`);
|
|
181
104
|
}
|
|
182
|
-
|
|
183
|
-
// Highlight the filled fields briefly
|
|
105
|
+
|
|
184
106
|
filledElements.forEach(el => {
|
|
185
107
|
const originalBackground = el.style.backgroundColor;
|
|
186
108
|
const originalTransition = el.style.transition;
|
|
187
|
-
|
|
109
|
+
|
|
188
110
|
el.style.transition = 'background-color 1.5s ease';
|
|
189
|
-
el.style.backgroundColor = '#
|
|
190
|
-
|
|
111
|
+
el.style.backgroundColor = '#EDECF5';
|
|
112
|
+
|
|
191
113
|
setTimeout(() => {
|
|
192
114
|
el.style.backgroundColor = originalBackground;
|
|
193
115
|
setTimeout(() => {
|
|
@@ -197,8 +119,7 @@ export default function formFillerInjector(): Plugin {
|
|
|
197
119
|
});
|
|
198
120
|
}
|
|
199
121
|
}
|
|
200
|
-
|
|
201
|
-
// Add the click event listener to the form filler button
|
|
122
|
+
|
|
202
123
|
const formFillerButton = document.getElementById('form-filler-button');
|
|
203
124
|
if (formFillerButton) {
|
|
204
125
|
formFillerButton.addEventListener('click', fillFormWithDefaults);
|
|
@@ -210,23 +131,17 @@ export default function formFillerInjector(): Plugin {
|
|
|
210
131
|
return {
|
|
211
132
|
name: "form-filler-injector",
|
|
212
133
|
transformIndexHtml: {
|
|
213
|
-
order: "post",
|
|
214
|
-
handler(html: string,
|
|
215
|
-
// Skip if no HTML body is found
|
|
134
|
+
order: "post",
|
|
135
|
+
handler(html: string, _ctx: IndexHtmlTransformContext): string {
|
|
216
136
|
if (!html.includes("<body") || !html.includes("</body>")) {
|
|
217
137
|
return html;
|
|
218
138
|
}
|
|
219
139
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// Inject button and script before closing body tag
|
|
224
|
-
updatedHtml = updatedHtml.replace(
|
|
225
|
-
"</body>",
|
|
226
|
-
`${buttonHtml}${fillerScript}</body>`,
|
|
227
|
-
);
|
|
140
|
+
const withButton =
|
|
141
|
+
fillSlot(html, "toolbar", buttonHtml) ??
|
|
142
|
+
html.replace("</body>", () => `${buttonHtml}</body>`);
|
|
228
143
|
|
|
229
|
-
return
|
|
144
|
+
return withButton.replace("</body>", () => `${fillerScript}</body>`);
|
|
230
145
|
},
|
|
231
146
|
},
|
|
232
147
|
};
|