ultimate-jekyll-manager 0.0.150 → 0.0.151
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/CLAUDE.md +55 -2
- package/README.md +11 -0
- package/dist/assets/js/core/exit-popup.js +68 -7
- package/dist/assets/js/libs/form-manager.js +149 -11
- package/dist/assets/js/pages/contact/index.js +5 -7
- package/dist/assets/js/pages/test/libraries/form-manager/index.js +53 -0
- package/dist/defaults/dist/_includes/core/foot.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +2 -7
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +1 -1
- package/dist/defaults/dist/pages/test/libraries/form-manager.html +79 -0
- package/dist/defaults/dist/pages/test/translation/index.md +1 -1
- package/firebase-debug.log +84 -0
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -629,7 +629,8 @@ initializing → ready ⇄ submitting → ready (or submitted)
|
|
|
629
629
|
resetOnSuccess: false, // Clear form fields after successful submission
|
|
630
630
|
warnOnUnsavedChanges: false, // Warn user before leaving with unsaved changes
|
|
631
631
|
submittingText: 'Processing...', // Text shown on submit button during submission
|
|
632
|
-
submittedText: 'Processed!'
|
|
632
|
+
submittedText: 'Processed!', // Text shown on submit button after success (when allowResubmit: false)
|
|
633
|
+
inputGroup: null // Filter getData() by data-input-group attribute (null = all fields)
|
|
633
634
|
}
|
|
634
635
|
```
|
|
635
636
|
|
|
@@ -641,6 +642,7 @@ initializing → ready ⇄ submitting → ready (or submitted)
|
|
|
641
642
|
| `validation` | `{ data, setError }` | Custom validation before submit |
|
|
642
643
|
| `change` | `{ field, name, value, data }` | Field value changed |
|
|
643
644
|
| `statechange` | `{ state, previousState }` | State transition |
|
|
645
|
+
| `honeypot` | `{ data }` | Honeypot triggered (for spam tracking) |
|
|
644
646
|
|
|
645
647
|
**Validation System:**
|
|
646
648
|
|
|
@@ -668,8 +670,10 @@ When the form transitions to `ready` state, FormManager automatically focuses th
|
|
|
668
670
|
|--------|-------------|
|
|
669
671
|
| `on(event, callback)` | Register event listener (chainable) |
|
|
670
672
|
| `ready()` | Transition to ready state |
|
|
671
|
-
| `getData()` | Get form data as nested object (supports dot notation) |
|
|
673
|
+
| `getData()` | Get form data as nested object (supports dot notation, respects input group filter) |
|
|
672
674
|
| `setData(obj)` | Set form values from nested object |
|
|
675
|
+
| `setInputGroup(group)` | Set input group filter (string, array, or null) |
|
|
676
|
+
| `getInputGroup()` | Get current input group filter |
|
|
673
677
|
| `showSuccess(msg)` | Show success notification |
|
|
674
678
|
| `showError(msg)` | Show error notification |
|
|
675
679
|
| `reset()` | Reset form and go to ready state |
|
|
@@ -690,6 +694,55 @@ Results in:
|
|
|
690
694
|
{ user: { address: { city: 'NYC' } } }
|
|
691
695
|
```
|
|
692
696
|
|
|
697
|
+
**Input Groups:**
|
|
698
|
+
|
|
699
|
+
Filter `getData()` to only return fields matching a specific group. Fields without `data-input-group` are "global" and always included.
|
|
700
|
+
|
|
701
|
+
```html
|
|
702
|
+
<!-- Global fields (no data-input-group) - always included -->
|
|
703
|
+
<input name="settings.theme" value="dark">
|
|
704
|
+
|
|
705
|
+
<!-- Group-specific fields -->
|
|
706
|
+
<input name="options.url" data-input-group="url" value="https://example.com">
|
|
707
|
+
<input name="options.ssid" data-input-group="wifi" value="MyWiFi">
|
|
708
|
+
<input name="options.password" data-input-group="wifi" value="secret123">
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
```javascript
|
|
712
|
+
// Set group filter (accepts string or array)
|
|
713
|
+
formManager.setInputGroup('url'); // Single group
|
|
714
|
+
formManager.setInputGroup(['url', 'wifi']); // Multiple groups
|
|
715
|
+
formManager.setInputGroup(null); // Clear filter (all fields)
|
|
716
|
+
|
|
717
|
+
// Get current filter
|
|
718
|
+
formManager.getInputGroup(); // Returns ['url'] or null
|
|
719
|
+
|
|
720
|
+
// getData() respects the filter
|
|
721
|
+
formManager.setInputGroup('wifi');
|
|
722
|
+
formManager.getData();
|
|
723
|
+
// Returns: { settings: { theme: 'dark' }, options: { ssid: 'MyWiFi', password: 'secret123' } }
|
|
724
|
+
// Note: 'url' field excluded, global 'settings.theme' included
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
Can also be set via config:
|
|
728
|
+
```javascript
|
|
729
|
+
const fm = new FormManager('#form', { inputGroup: 'wifi' });
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Honeypot (Bot Detection):**
|
|
733
|
+
|
|
734
|
+
FormManager automatically rejects submissions if a honeypot field is filled. Honeypot fields are hidden from users but bots fill them automatically.
|
|
735
|
+
|
|
736
|
+
```html
|
|
737
|
+
<!-- Hidden from users via CSS -->
|
|
738
|
+
<input type="text" name="honey" autocomplete="off" tabindex="-1"
|
|
739
|
+
style="position: absolute; left: -9999px;" aria-hidden="true">
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
Fields matching `[data-honey]` or `[name="honey"]` are:
|
|
743
|
+
- Excluded from `getData()` output
|
|
744
|
+
- Checked during validation — if filled, submission is rejected with generic error
|
|
745
|
+
|
|
693
746
|
**Checkbox Handling:**
|
|
694
747
|
- **Single checkbox:** Returns `true`/`false`
|
|
695
748
|
- **Checkbox group (same name):** Returns object `{ value1: true, value2: false }`
|
package/README.md
CHANGED
|
@@ -449,6 +449,7 @@ initializing → ready ⇄ submitting → ready (or submitted)
|
|
|
449
449
|
| `validation` | `{ data, setError }` | Custom validation before submit. Use `setError(fieldName, message)` to add errors. |
|
|
450
450
|
| `change` | `{ field, name, value, data }` | Called when any field value changes. |
|
|
451
451
|
| `statechange` | `{ state, previousState }` | Called when form state changes. |
|
|
452
|
+
| `honeypot` | `{ data }` | Called when honeypot is triggered (for spam tracking). |
|
|
452
453
|
|
|
453
454
|
**Validation:**
|
|
454
455
|
|
|
@@ -518,6 +519,16 @@ Produces:
|
|
|
518
519
|
}
|
|
519
520
|
```
|
|
520
521
|
|
|
522
|
+
**Honeypot (Bot Detection):**
|
|
523
|
+
|
|
524
|
+
FormManager automatically rejects submissions if a honeypot field is filled. Fields matching `[data-honey]` or `[name="honey"]` are excluded from `getData()` and trigger rejection if filled.
|
|
525
|
+
|
|
526
|
+
```html
|
|
527
|
+
<!-- Hidden from users via CSS -->
|
|
528
|
+
<input type="text" name="honey" autocomplete="off" tabindex="-1"
|
|
529
|
+
style="position: absolute; left: -9999px;" aria-hidden="true">
|
|
530
|
+
```
|
|
531
|
+
|
|
521
532
|
**Checkbox Handling:**
|
|
522
533
|
|
|
523
534
|
- **Single checkbox:** Returns `true` or `false`
|
|
@@ -19,8 +19,8 @@ export default function (Manager, options) {
|
|
|
19
19
|
|
|
20
20
|
// Wait for DOM to be ready
|
|
21
21
|
webManager.dom().ready().then(() => {
|
|
22
|
-
// Setup
|
|
23
|
-
|
|
22
|
+
// Setup exit intent detection without needing the element yet
|
|
23
|
+
setupExitIntentDetection();
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
// Make this available on window object in DEV mode for testing
|
|
@@ -82,8 +82,8 @@ export default function (Manager, options) {
|
|
|
82
82
|
$modal.removeAttribute('hidden');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
function
|
|
86
|
-
//
|
|
85
|
+
function setupExitIntentDetection() {
|
|
86
|
+
// 1. Mouse leave detection (desktop) - leaving from the top
|
|
87
87
|
document.addEventListener('mouseleave', (e) => {
|
|
88
88
|
/* @dev-only:start */
|
|
89
89
|
// {
|
|
@@ -91,13 +91,74 @@ export default function (Manager, options) {
|
|
|
91
91
|
// }
|
|
92
92
|
/* @dev-only:end */
|
|
93
93
|
|
|
94
|
-
// Only trigger if
|
|
95
|
-
// 1. We should show the popup (based on timing/session)
|
|
96
|
-
// 2. Mouse is leaving from the top (Y <= 0 means exiting from top)
|
|
94
|
+
// Only trigger if mouse is leaving from the top (Y <= 0)
|
|
97
95
|
if (shouldShow && e.clientY <= 0) {
|
|
98
96
|
showExitPopup();
|
|
99
97
|
}
|
|
100
98
|
});
|
|
99
|
+
|
|
100
|
+
// 2. Window blur detection - user switches tabs or clicks outside browser
|
|
101
|
+
window.addEventListener('blur', () => {
|
|
102
|
+
/* @dev-only:start */
|
|
103
|
+
// {
|
|
104
|
+
// console.log('Window blur detected:', shouldShow);
|
|
105
|
+
// }
|
|
106
|
+
/* @dev-only:end */
|
|
107
|
+
|
|
108
|
+
if (shouldShow) {
|
|
109
|
+
showExitPopup();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// // 3. Back button detection - user attempts to navigate back
|
|
114
|
+
// // Push a dummy state so we can detect when user tries to go back
|
|
115
|
+
// history.pushState({ exitPopup: true }, '');
|
|
116
|
+
// window.addEventListener('popstate', () => {
|
|
117
|
+
// /* @dev-only:start */
|
|
118
|
+
// // {
|
|
119
|
+
// // console.log('Popstate detected:', shouldShow);
|
|
120
|
+
// // }
|
|
121
|
+
// /* @dev-only:end */
|
|
122
|
+
|
|
123
|
+
// if (shouldShow) {
|
|
124
|
+
// // Re-push state to prevent actual navigation
|
|
125
|
+
// history.pushState({ exitPopup: true }, '');
|
|
126
|
+
// showExitPopup();
|
|
127
|
+
// }
|
|
128
|
+
// });
|
|
129
|
+
|
|
130
|
+
// // 4. Mobile exit intent - rapid scroll up toward the top of the page
|
|
131
|
+
// let lastScrollY = window.scrollY;
|
|
132
|
+
// let scrollVelocity = 0;
|
|
133
|
+
// let lastScrollTime = Date.now();
|
|
134
|
+
|
|
135
|
+
// window.addEventListener('scroll', () => {
|
|
136
|
+
// const now = Date.now();
|
|
137
|
+
// const deltaTime = now - lastScrollTime;
|
|
138
|
+
// const deltaY = lastScrollY - window.scrollY; // Positive = scrolling up
|
|
139
|
+
|
|
140
|
+
// // Calculate velocity (pixels per ms)
|
|
141
|
+
// scrollVelocity = deltaTime > 0 ? deltaY / deltaTime : 0;
|
|
142
|
+
|
|
143
|
+
// /* @dev-only:start */
|
|
144
|
+
// // {
|
|
145
|
+
// // if (scrollVelocity > 1) {
|
|
146
|
+
// // console.log('Scroll velocity:', scrollVelocity, 'scrollY:', window.scrollY);
|
|
147
|
+
// // }
|
|
148
|
+
// // }
|
|
149
|
+
// /* @dev-only:end */
|
|
150
|
+
|
|
151
|
+
// // Trigger if:
|
|
152
|
+
// // - Scrolling up rapidly (velocity > 2 pixels/ms)
|
|
153
|
+
// // - Near the top of the page (within 100px)
|
|
154
|
+
// // - Should show popup
|
|
155
|
+
// if (shouldShow && scrollVelocity > 2 && window.scrollY < 100) {
|
|
156
|
+
// showExitPopup();
|
|
157
|
+
// }
|
|
158
|
+
|
|
159
|
+
// lastScrollY = window.scrollY;
|
|
160
|
+
// lastScrollTime = now;
|
|
161
|
+
// }, { passive: true });
|
|
101
162
|
}
|
|
102
163
|
|
|
103
164
|
function showExitPopup() {
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import { ready as domReady } from 'web-manager/modules/dom.js';
|
|
16
16
|
import { showNotification, getDeviceType } from 'web-manager/modules/utilities.js';
|
|
17
17
|
|
|
18
|
+
// Constants
|
|
19
|
+
const HONEYPOT_SELECTOR = '[data-honey], [name="honey"]';
|
|
20
|
+
|
|
18
21
|
export class FormManager {
|
|
19
22
|
constructor(selector, options = {}) {
|
|
20
23
|
// Get form element
|
|
@@ -35,6 +38,7 @@ export class FormManager {
|
|
|
35
38
|
warnOnUnsavedChanges: false, // Warn user before leaving page with unsaved changes
|
|
36
39
|
submittingText: 'Processing...', // Text shown on submit button during submission
|
|
37
40
|
submittedText: 'Processed!', // Text shown on submit button after submission (when allowResubmit: false)
|
|
41
|
+
inputGroup: null, // Filter getData() to only include fields with matching data-input-group (null = all fields)
|
|
38
42
|
...options,
|
|
39
43
|
};
|
|
40
44
|
|
|
@@ -48,6 +52,7 @@ export class FormManager {
|
|
|
48
52
|
validation: [],
|
|
49
53
|
submit: [],
|
|
50
54
|
statechange: [],
|
|
55
|
+
honeypot: [],
|
|
51
56
|
};
|
|
52
57
|
|
|
53
58
|
// Field errors (populated during validation)
|
|
@@ -297,6 +302,21 @@ export class FormManager {
|
|
|
297
302
|
}
|
|
298
303
|
/* @dev-only:end */
|
|
299
304
|
|
|
305
|
+
// 0. Check honeypot fields first (bot detection)
|
|
306
|
+
if (this._isHoneypotFilled()) {
|
|
307
|
+
/* @dev-only:start */
|
|
308
|
+
{
|
|
309
|
+
console.log('[Form-manager] Honeypot triggered - rejecting submission');
|
|
310
|
+
}
|
|
311
|
+
/* @dev-only:end */
|
|
312
|
+
|
|
313
|
+
// Emit honeypot event for tracking
|
|
314
|
+
this._emit('honeypot', { data });
|
|
315
|
+
|
|
316
|
+
this.showError('Something went wrong. Please try again.');
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
300
320
|
// Create setError helper for custom validation
|
|
301
321
|
const setError = (fieldName, message) => {
|
|
302
322
|
this._fieldErrors[fieldName] = message;
|
|
@@ -712,30 +732,68 @@ export class FormManager {
|
|
|
712
732
|
|
|
713
733
|
/**
|
|
714
734
|
* Collect form data as plain object (supports dot notation for nested fields)
|
|
735
|
+
* Respects inputGroup filter when set - only includes fields matching the group
|
|
715
736
|
*/
|
|
716
737
|
getData() {
|
|
717
|
-
const formData = new FormData(this.$form);
|
|
718
738
|
const data = {};
|
|
719
739
|
|
|
720
|
-
//
|
|
740
|
+
// Get all form fields
|
|
741
|
+
const $fields = this.$form.querySelectorAll('input, select, textarea');
|
|
742
|
+
|
|
743
|
+
// Count checkboxes per name to detect groups vs single (only for fields in group)
|
|
721
744
|
const checkboxCounts = {};
|
|
722
|
-
|
|
723
|
-
|
|
745
|
+
$fields.forEach(($field) => {
|
|
746
|
+
if ($field.type === 'checkbox' && this._isFieldInGroup($field)) {
|
|
747
|
+
checkboxCounts[$field.name] = (checkboxCounts[$field.name] || 0) + 1;
|
|
748
|
+
}
|
|
724
749
|
});
|
|
725
750
|
|
|
726
|
-
|
|
751
|
+
// Process non-checkbox fields
|
|
752
|
+
$fields.forEach(($field) => {
|
|
753
|
+
const name = $field.name;
|
|
754
|
+
|
|
755
|
+
// Skip fields without name
|
|
756
|
+
if (!name) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Skip if field is not in current input group
|
|
761
|
+
if (!this._isFieldInGroup($field)) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Skip honeypot fields (should never be in form data)
|
|
766
|
+
if ($field.matches(HONEYPOT_SELECTOR)) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
|
|
727
770
|
// Skip checkboxes - we handle them separately
|
|
728
|
-
if (
|
|
729
|
-
|
|
771
|
+
if ($field.type === 'checkbox') {
|
|
772
|
+
return;
|
|
730
773
|
}
|
|
731
|
-
|
|
732
|
-
|
|
774
|
+
|
|
775
|
+
// Skip radio buttons that aren't checked
|
|
776
|
+
if ($field.type === 'radio' && !$field.checked) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
this._setNested(data, name, $field.value);
|
|
781
|
+
});
|
|
733
782
|
|
|
734
783
|
// Handle checkboxes
|
|
735
784
|
const processedGroups = new Set();
|
|
736
|
-
|
|
785
|
+
$fields.forEach(($cb) => {
|
|
786
|
+
if ($cb.type !== 'checkbox') {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
737
790
|
const name = $cb.name;
|
|
738
791
|
|
|
792
|
+
// Skip if field is not in current input group
|
|
793
|
+
if (!this._isFieldInGroup($cb)) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
739
797
|
// Single checkbox: true/false
|
|
740
798
|
if (checkboxCounts[name] === 1) {
|
|
741
799
|
this._setNested(data, name, $cb.checked);
|
|
@@ -750,7 +808,10 @@ export class FormManager {
|
|
|
750
808
|
|
|
751
809
|
const values = {};
|
|
752
810
|
this.$form.querySelectorAll(`input[type="checkbox"][name="${name}"]`).forEach(($groupCb) => {
|
|
753
|
-
|
|
811
|
+
// Only include checkboxes that are in the group
|
|
812
|
+
if (this._isFieldInGroup($groupCb)) {
|
|
813
|
+
values[$groupCb.value] = $groupCb.checked;
|
|
814
|
+
}
|
|
754
815
|
});
|
|
755
816
|
this._setNested(data, name, values);
|
|
756
817
|
});
|
|
@@ -806,6 +867,83 @@ export class FormManager {
|
|
|
806
867
|
return this._isDirty;
|
|
807
868
|
}
|
|
808
869
|
|
|
870
|
+
/**
|
|
871
|
+
* Set the input group filter for getData()
|
|
872
|
+
* When set, getData() only returns fields matching the group (via data-input-group attribute)
|
|
873
|
+
* Fields without data-input-group or with empty value are considered "global" and always included
|
|
874
|
+
*
|
|
875
|
+
* @param {string|string[]|null} group - Group name(s) to filter by (e.g., 'url', ['url', 'wifi']), or null to disable filtering
|
|
876
|
+
* @returns {FormManager} - Returns this for chaining
|
|
877
|
+
*/
|
|
878
|
+
setInputGroup(group) {
|
|
879
|
+
// Normalize to array or null
|
|
880
|
+
if (group === null || group === undefined || group === '') {
|
|
881
|
+
this.config.inputGroup = null;
|
|
882
|
+
} else if (Array.isArray(group)) {
|
|
883
|
+
this.config.inputGroup = group.map((g) => g.toLowerCase());
|
|
884
|
+
} else {
|
|
885
|
+
this.config.inputGroup = [group.toLowerCase()];
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/* @dev-only:start */
|
|
889
|
+
{
|
|
890
|
+
console.log('[Form-manager] setInputGroup:', this.config.inputGroup);
|
|
891
|
+
}
|
|
892
|
+
/* @dev-only:end */
|
|
893
|
+
|
|
894
|
+
return this;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Get the current input group filter
|
|
899
|
+
* @returns {string[]|null}
|
|
900
|
+
*/
|
|
901
|
+
getInputGroup() {
|
|
902
|
+
return this.config.inputGroup;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Check if any honeypot field has been filled (bot detection)
|
|
907
|
+
* Honeypot fields are hidden from users but bots fill them automatically
|
|
908
|
+
* @returns {boolean} - true if a honeypot field has a value (bot detected)
|
|
909
|
+
*/
|
|
910
|
+
_isHoneypotFilled() {
|
|
911
|
+
const $honeypots = this.$form.querySelectorAll(HONEYPOT_SELECTOR);
|
|
912
|
+
|
|
913
|
+
for (const $field of $honeypots) {
|
|
914
|
+
if ($field.value && $field.value.trim() !== '') {
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Check if a field should be included based on input group filter
|
|
924
|
+
* @param {HTMLElement} $field - The field element to check
|
|
925
|
+
* @returns {boolean}
|
|
926
|
+
*/
|
|
927
|
+
_isFieldInGroup($field) {
|
|
928
|
+
const allowedGroups = this.config.inputGroup;
|
|
929
|
+
|
|
930
|
+
// No filter set - include all fields
|
|
931
|
+
if (!allowedGroups) {
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// Get field's group attribute
|
|
936
|
+
const fieldGroup = $field.getAttribute('data-input-group');
|
|
937
|
+
|
|
938
|
+
// No group attribute or empty = global field, always include
|
|
939
|
+
if (!fieldGroup || fieldGroup.trim() === '') {
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Check if field's group is in allowed groups
|
|
944
|
+
return allowedGroups.includes(fieldGroup.toLowerCase());
|
|
945
|
+
}
|
|
946
|
+
|
|
809
947
|
/**
|
|
810
948
|
* Set form data from a nested object (supports dot notation field names)
|
|
811
949
|
*/
|
|
@@ -32,18 +32,16 @@ function setupForm() {
|
|
|
32
32
|
resetOnSuccess: true,
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
// Track honeypot triggers (spam detection)
|
|
36
|
+
formManager.on('honeypot', () => {
|
|
37
|
+
trackContactSpam();
|
|
38
|
+
});
|
|
39
|
+
|
|
35
40
|
formManager.on('submit', async ({ data }) => {
|
|
36
41
|
const slapformId = webManager.config.brand.contact['slapform-form-id'];
|
|
37
42
|
|
|
38
43
|
console.log('Contact form submission:', data);
|
|
39
44
|
|
|
40
|
-
// Check honeypot fields (anti-spam)
|
|
41
|
-
if (data.url_check || data.slap_honey) {
|
|
42
|
-
console.warn('Honeypot field filled - potential spam');
|
|
43
|
-
trackContactSpam();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
45
|
// Check if slapformId is missing
|
|
48
46
|
if (!slapformId) {
|
|
49
47
|
webManager.sentry().captureException(new Error('Contact form is not configured - missing slapform ID'));
|
|
@@ -21,6 +21,7 @@ export default (Manager) => {
|
|
|
21
21
|
initTestFormValidation();
|
|
22
22
|
initTestFormContact();
|
|
23
23
|
initTestFormManual();
|
|
24
|
+
initTestFormGroups();
|
|
24
25
|
|
|
25
26
|
// Resolve after initialization
|
|
26
27
|
return resolve();
|
|
@@ -192,3 +193,55 @@ function initTestFormManual() {
|
|
|
192
193
|
formManager.ready();
|
|
193
194
|
}, 2000);
|
|
194
195
|
}
|
|
196
|
+
|
|
197
|
+
// Test 5: Input Groups
|
|
198
|
+
function initTestFormGroups() {
|
|
199
|
+
const formManager = new FormManager('#test-form-groups');
|
|
200
|
+
const $status = document.getElementById('groups-status');
|
|
201
|
+
const $filter = document.getElementById('groups-filter');
|
|
202
|
+
const $output = document.getElementById('groups-output');
|
|
203
|
+
const $filterBtns = document.querySelectorAll('.groups-filter-btn');
|
|
204
|
+
|
|
205
|
+
formManager.on('statechange', ({ state }) => {
|
|
206
|
+
$status.textContent = `Status: ${state}`;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Filter button click handler
|
|
210
|
+
$filterBtns.forEach(($btn) => {
|
|
211
|
+
$btn.addEventListener('click', () => {
|
|
212
|
+
// Update active state
|
|
213
|
+
$filterBtns.forEach(($b) => $b.classList.remove('active'));
|
|
214
|
+
$btn.classList.add('active');
|
|
215
|
+
|
|
216
|
+
// Set the input group filter (parse data-group as JSON array or single string)
|
|
217
|
+
const groupAttr = $btn.dataset.group;
|
|
218
|
+
let group = null;
|
|
219
|
+
if (groupAttr) {
|
|
220
|
+
try {
|
|
221
|
+
group = JSON.parse(groupAttr);
|
|
222
|
+
} catch {
|
|
223
|
+
group = groupAttr; // Single string value
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
formManager.setInputGroup(group);
|
|
227
|
+
|
|
228
|
+
// Update filter display
|
|
229
|
+
const currentGroup = formManager.getInputGroup();
|
|
230
|
+
$filter.textContent = currentGroup
|
|
231
|
+
? `Filter: ${JSON.stringify(currentGroup)}`
|
|
232
|
+
: 'Filter: none (all fields)';
|
|
233
|
+
|
|
234
|
+
console.log('[Test 5] Input group set to:', currentGroup);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
formManager.on('submit', async ({ data }) => {
|
|
239
|
+
console.log('[Test 5] getData() result:', data);
|
|
240
|
+
|
|
241
|
+
// Show the filtered data
|
|
242
|
+
$output.textContent = JSON.stringify(data, null, 2);
|
|
243
|
+
|
|
244
|
+
// Don't actually submit - just show the data
|
|
245
|
+
formManager.showSuccess('getData() returned ' + Object.keys(data).length + ' top-level keys');
|
|
246
|
+
});
|
|
247
|
+
}
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
<span class="text-muted">
|
|
71
71
|
•
|
|
72
72
|
</span>
|
|
73
|
-
<small class="ms-2 text-muted"><strong class="text-body">10k+</strong> happy subscribers
|
|
73
|
+
<small class="ms-2 text-muted"><strong class="text-body">Join 10k+</strong> happy subscribers!</small>
|
|
74
74
|
</div>
|
|
75
75
|
|
|
76
76
|
<!-- Trust indicators -->
|
|
@@ -203,9 +203,9 @@ faqs:
|
|
|
203
203
|
</div>
|
|
204
204
|
</div>
|
|
205
205
|
|
|
206
|
-
<!-- Honeypot field -->
|
|
206
|
+
<!-- Honeypot field (bot detection) -->
|
|
207
207
|
<div class="form-group mb-3" style="display: none;" aria-hidden="true">
|
|
208
|
-
<input type="text" class="form-control"
|
|
208
|
+
<input type="text" class="form-control" name="honey" tabindex="-1" autocomplete="off">
|
|
209
209
|
</div>
|
|
210
210
|
|
|
211
211
|
<div class="mb-4">
|
|
@@ -238,11 +238,6 @@ faqs:
|
|
|
238
238
|
<div class="invalid-feedback"></div>
|
|
239
239
|
</div>
|
|
240
240
|
|
|
241
|
-
<!-- Honeypot field -->
|
|
242
|
-
<div class="form-group mb-3 position-absolute" style="opacity: 0; height: 0;" aria-hidden="true">
|
|
243
|
-
<input type="text" class="form-control" id="slap_honey" name="slap_honey" placeholder="Website" tabindex="-1" autocomplete="off">
|
|
244
|
-
</div>
|
|
245
|
-
|
|
246
241
|
<div class="mb-4">
|
|
247
242
|
<label for="message" class="form-label fw-semibold">Message <span class="text-danger">*</span></label>
|
|
248
243
|
<textarea class="form-control form-control-lg" id="message" name="message" rows="3"
|
|
@@ -77,6 +77,12 @@ sitemap: false
|
|
|
77
77
|
<input type="checkbox" class="form-check-input" id="main-subscribe" name="settings.subscribe" disabled>
|
|
78
78
|
<label class="form-check-label" for="main-subscribe">settings.subscribe (single checkbox)</label>
|
|
79
79
|
</div>
|
|
80
|
+
<!-- Honeypot field for bot detection (fill to trigger rejection) -->
|
|
81
|
+
<div class="mb-3">
|
|
82
|
+
<label for="main-honey" class="form-label">honey <span class="badge bg-warning text-dark">Honeypot</span></label>
|
|
83
|
+
<input type="text" class="form-control" id="main-honey" name="honey" data-honey placeholder="Fill to trigger bot rejection" autocomplete="off" tabindex="-1" disabled>
|
|
84
|
+
<small class="text-muted">Hidden in production; fill here to test rejection</small>
|
|
85
|
+
</div>
|
|
80
86
|
</div>
|
|
81
87
|
</div>
|
|
82
88
|
<div class="d-flex gap-2">
|
|
@@ -177,5 +183,78 @@ sitemap: false
|
|
|
177
183
|
</div>
|
|
178
184
|
</div>
|
|
179
185
|
|
|
186
|
+
<!-- Test 5: Input Groups -->
|
|
187
|
+
<div class="col-lg-8">
|
|
188
|
+
<div class="card">
|
|
189
|
+
<div class="card-header">
|
|
190
|
+
<h5 class="mb-0">Test 5: Input Groups</h5>
|
|
191
|
+
<small class="text-muted">setInputGroup() filters getData() - fields without data-input-group are global</small>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="card-body">
|
|
194
|
+
<form id="test-form-groups">
|
|
195
|
+
<div class="row">
|
|
196
|
+
<!-- Global settings (no data-input-group = always included) -->
|
|
197
|
+
<div class="col-md-4">
|
|
198
|
+
<h6 class="text-muted mb-3">Global Settings <span class="badge bg-secondary">Always included</span></h6>
|
|
199
|
+
<div class="mb-3">
|
|
200
|
+
<label for="groups-name" class="form-label">settings.name</label>
|
|
201
|
+
<input type="text" class="form-control" id="groups-name" name="settings.name" value="My Project" disabled>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="mb-3">
|
|
204
|
+
<label for="groups-theme" class="form-label">settings.theme</label>
|
|
205
|
+
<select class="form-select" id="groups-theme" name="settings.theme" disabled>
|
|
206
|
+
<option value="light">Light</option>
|
|
207
|
+
<option value="dark" selected>Dark</option>
|
|
208
|
+
</select>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- Group A fields -->
|
|
213
|
+
<div class="col-md-4">
|
|
214
|
+
<h6 class="text-primary mb-3">Group A <span class="badge bg-primary">data-input-group="a"</span></h6>
|
|
215
|
+
<div class="mb-3">
|
|
216
|
+
<label for="groups-a-url" class="form-label">options.url</label>
|
|
217
|
+
<input type="url" class="form-control" id="groups-a-url" name="options.url" data-input-group="a" value="https://example.com" disabled>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="mb-3">
|
|
220
|
+
<label for="groups-a-title" class="form-label">options.title</label>
|
|
221
|
+
<input type="text" class="form-control" id="groups-a-title" name="options.title" data-input-group="a" value="My Link" disabled>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<!-- Group B fields -->
|
|
226
|
+
<div class="col-md-4">
|
|
227
|
+
<h6 class="text-success mb-3">Group B <span class="badge bg-success">data-input-group="b"</span></h6>
|
|
228
|
+
<div class="mb-3">
|
|
229
|
+
<label for="groups-b-ssid" class="form-label">options.ssid</label>
|
|
230
|
+
<input type="text" class="form-control" id="groups-b-ssid" name="options.ssid" data-input-group="b" value="MyWiFi" disabled>
|
|
231
|
+
</div>
|
|
232
|
+
<div class="mb-3">
|
|
233
|
+
<label for="groups-b-password" class="form-label">options.password</label>
|
|
234
|
+
<input type="text" class="form-control" id="groups-b-password" name="options.password" data-input-group="b" value="secret123" disabled>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div class="d-flex gap-2 flex-wrap align-items-center">
|
|
240
|
+
<span class="text-muted me-2">Select group:</span>
|
|
241
|
+
<button type="button" class="btn btn-outline-secondary btn-sm groups-filter-btn active" data-group="">All (no filter)</button>
|
|
242
|
+
<button type="button" class="btn btn-outline-primary btn-sm groups-filter-btn" data-group="a">Group A</button>
|
|
243
|
+
<button type="button" class="btn btn-outline-success btn-sm groups-filter-btn" data-group="b">Group B</button>
|
|
244
|
+
<button type="button" class="btn btn-outline-info btn-sm groups-filter-btn" data-group='["a", "b"]'>Both (a, b)</button>
|
|
245
|
+
<button type="submit" class="btn btn-primary btn-sm ms-auto" disabled>Get Data</button>
|
|
246
|
+
</div>
|
|
247
|
+
</form>
|
|
248
|
+
<div class="mt-3 d-flex gap-3">
|
|
249
|
+
<small class="text-muted" id="groups-status">Status: ready</small>
|
|
250
|
+
<small class="text-muted" id="groups-filter">Filter: none (all fields)</small>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="mt-3">
|
|
253
|
+
<pre id="groups-output" class="bg-body-secondary p-2 rounded small" style="max-height: 200px; overflow: auto;">Click "Get Data" to see filtered results...</pre>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
180
259
|
</div>
|
|
181
260
|
</div>
|
|
@@ -47,7 +47,7 @@ By vising {{ brand }}, you agree to comply.
|
|
|
47
47
|
## This is an input
|
|
48
48
|
<div class="form-group">
|
|
49
49
|
<label for="test-email" class="visually-hidden">Your email</label>
|
|
50
|
-
<input type="email" id="test-email" name="
|
|
50
|
+
<input type="email" id="test-email" name="email" class="form-control" placeholder="Your Email">
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
53
|
## This button has a title
|
package/firebase-debug.log
CHANGED
|
@@ -3154,3 +3154,87 @@
|
|
|
3154
3154
|
[debug] [2025-12-10T06:23:42.224Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3155
3155
|
[debug] [2025-12-10T06:23:42.225Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3156
3156
|
[debug] [2025-12-10T06:23:42.226Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3157
|
+
[debug] [2025-12-10T10:33:22.717Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3158
|
+
[debug] [2025-12-10T10:33:22.717Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3159
|
+
[debug] [2025-12-10T10:33:22.719Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3160
|
+
[debug] [2025-12-10T10:33:22.719Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3161
|
+
[debug] [2025-12-10T10:33:22.719Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3162
|
+
[debug] [2025-12-10T10:33:22.728Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3163
|
+
[debug] [2025-12-10T10:33:22.728Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3164
|
+
[debug] [2025-12-10T10:33:22.719Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3165
|
+
[debug] [2025-12-10T10:33:22.719Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3166
|
+
[debug] [2025-12-10T10:33:22.719Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3167
|
+
[debug] [2025-12-10T10:33:22.728Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3168
|
+
[debug] [2025-12-10T10:33:22.728Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3169
|
+
[debug] [2025-12-10T10:33:22.814Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3170
|
+
[debug] [2025-12-10T10:33:22.816Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3171
|
+
[debug] [2025-12-10T10:33:22.814Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3172
|
+
[debug] [2025-12-10T10:33:22.815Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3173
|
+
[debug] [2025-12-10T10:33:22.815Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3174
|
+
[debug] [2025-12-10T10:33:22.817Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3175
|
+
[debug] [2025-12-10T10:33:22.817Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3176
|
+
[debug] [2025-12-10T10:33:22.817Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3177
|
+
[debug] [2025-12-10T10:33:22.817Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3178
|
+
[debug] [2025-12-10T10:33:22.816Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3179
|
+
[debug] [2025-12-10T10:33:22.817Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3180
|
+
[debug] [2025-12-10T10:33:22.817Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3181
|
+
[debug] [2025-12-10T10:33:22.818Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3182
|
+
[debug] [2025-12-10T10:33:22.818Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3183
|
+
[debug] [2025-12-10T10:33:22.819Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3184
|
+
[debug] [2025-12-10T10:33:22.819Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3185
|
+
[debug] [2025-12-11T03:09:30.498Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3186
|
+
[debug] [2025-12-11T03:09:30.500Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3187
|
+
[debug] [2025-12-11T03:09:30.500Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3188
|
+
[debug] [2025-12-11T03:09:30.500Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3189
|
+
[debug] [2025-12-11T03:09:30.509Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3190
|
+
[debug] [2025-12-11T03:09:30.509Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3191
|
+
[debug] [2025-12-11T03:09:30.596Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3192
|
+
[debug] [2025-12-11T03:09:30.596Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3193
|
+
[debug] [2025-12-11T03:09:30.596Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3194
|
+
[debug] [2025-12-11T03:09:30.597Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3195
|
+
[debug] [2025-12-11T03:09:30.598Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3196
|
+
[debug] [2025-12-11T03:09:30.599Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3197
|
+
[debug] [2025-12-11T03:09:30.599Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3198
|
+
[debug] [2025-12-11T03:09:30.599Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3199
|
+
[debug] [2025-12-11T03:31:03.777Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3200
|
+
[debug] [2025-12-11T03:31:03.777Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3201
|
+
[debug] [2025-12-11T03:31:03.779Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3202
|
+
[debug] [2025-12-11T03:31:03.779Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3203
|
+
[debug] [2025-12-11T03:31:03.779Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3204
|
+
[debug] [2025-12-11T03:31:03.779Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3205
|
+
[debug] [2025-12-11T03:31:03.788Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3206
|
+
[debug] [2025-12-11T03:31:03.788Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3207
|
+
[debug] [2025-12-11T03:31:03.779Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3208
|
+
[debug] [2025-12-11T03:31:03.779Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3209
|
+
[debug] [2025-12-11T03:31:03.779Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3210
|
+
[debug] [2025-12-11T03:31:03.788Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3211
|
+
[debug] [2025-12-11T03:31:03.789Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3212
|
+
[debug] [2025-12-11T03:31:03.781Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3213
|
+
[debug] [2025-12-11T03:31:03.781Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3214
|
+
[debug] [2025-12-11T03:31:03.781Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3215
|
+
[debug] [2025-12-11T03:31:03.792Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3216
|
+
[debug] [2025-12-11T03:31:03.793Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3217
|
+
[debug] [2025-12-11T03:31:03.891Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3218
|
+
[debug] [2025-12-11T03:31:03.892Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3219
|
+
[debug] [2025-12-11T03:31:03.892Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3220
|
+
[debug] [2025-12-11T03:31:03.893Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3221
|
+
[debug] [2025-12-11T03:31:03.894Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3222
|
+
[debug] [2025-12-11T03:31:03.894Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3223
|
+
[debug] [2025-12-11T03:31:03.895Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3224
|
+
[debug] [2025-12-11T03:31:03.895Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3225
|
+
[debug] [2025-12-11T03:31:03.900Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3226
|
+
[debug] [2025-12-11T03:31:03.901Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3227
|
+
[debug] [2025-12-11T03:31:03.900Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3228
|
+
[debug] [2025-12-11T03:31:03.901Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3229
|
+
[debug] [2025-12-11T03:31:03.901Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3230
|
+
[debug] [2025-12-11T03:31:03.903Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3231
|
+
[debug] [2025-12-11T03:31:03.903Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3232
|
+
[debug] [2025-12-11T03:31:03.904Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3233
|
+
[debug] [2025-12-11T03:31:03.904Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3234
|
+
[debug] [2025-12-11T03:31:03.901Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3235
|
+
[debug] [2025-12-11T03:31:03.902Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3236
|
+
[debug] [2025-12-11T03:31:03.902Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3237
|
+
[debug] [2025-12-11T03:31:03.904Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3238
|
+
[debug] [2025-12-11T03:31:03.904Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|
|
3239
|
+
[debug] [2025-12-11T03:31:03.904Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
|
3240
|
+
[debug] [2025-12-11T03:31:03.905Z] > authorizing via signed-in user (ian.wiedenman@gmail.com)
|