web-mojo 2.1.266 → 2.1.278
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/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +14 -13
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +3 -4
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +2 -2
- package/dist/chunks/{ContextMenu-zFaO5-N4.js → ContextMenu-B5-M-xk4.js} +2 -2
- package/dist/chunks/{ContextMenu-zFaO5-N4.js.map → ContextMenu-B5-M-xk4.js.map} +1 -1
- package/dist/chunks/{ContextMenu-4ltJ5APp.js → ContextMenu-BGyC31Bb.js} +2 -2
- package/dist/chunks/{ContextMenu-4ltJ5APp.js.map → ContextMenu-BGyC31Bb.js.map} +1 -1
- package/dist/chunks/{DataView-iOFKWlbv.js → DataView-D5GAnAwy.js} +2 -2
- package/dist/chunks/{DataView-iOFKWlbv.js.map → DataView-D5GAnAwy.js.map} +1 -1
- package/dist/chunks/{DataView-Cejy1Cr8.js → DataView-b_MvCVHp.js} +2 -2
- package/dist/chunks/{DataView-Cejy1Cr8.js.map → DataView-b_MvCVHp.js.map} +1 -1
- package/dist/chunks/{Dialog-BJ18jw7T.js → Dialog-Cb3hStew.js} +2 -2
- package/dist/chunks/{Dialog-BJ18jw7T.js.map → Dialog-Cb3hStew.js.map} +1 -1
- package/dist/chunks/{Dialog-DTP5nS69.js → Dialog-DL2qXPTj.js} +5 -5
- package/dist/chunks/{Dialog-DTP5nS69.js.map → Dialog-DL2qXPTj.js.map} +1 -1
- package/dist/chunks/{FilePreviewView-DUa8Q8kR.js → FilePreviewView-BrrJorTn.js} +6 -6
- package/dist/chunks/{FilePreviewView-DUa8Q8kR.js.map → FilePreviewView-BrrJorTn.js.map} +1 -1
- package/dist/chunks/{FilePreviewView-B_9CFtFI.js → FilePreviewView-DfSMNOwF.js} +2 -2
- package/dist/chunks/{FilePreviewView-B_9CFtFI.js.map → FilePreviewView-DfSMNOwF.js.map} +1 -1
- package/dist/chunks/{FormView-J-1RnLu0.js → FormView-BCnSXvaY.js} +192 -3
- package/dist/chunks/FormView-BCnSXvaY.js.map +1 -0
- package/dist/chunks/FormView-FTVjKzXh.js +2 -0
- package/dist/chunks/FormView-FTVjKzXh.js.map +1 -0
- package/dist/chunks/{MetricsChart-igTZsC5W.js → MetricsChart-B0YqU0CD.js} +4 -4
- package/dist/chunks/{MetricsChart-igTZsC5W.js.map → MetricsChart-B0YqU0CD.js.map} +1 -1
- package/dist/chunks/{MetricsChart-BCYtvXip.js → MetricsChart-DFNBNWw7.js} +2 -2
- package/dist/chunks/{MetricsChart-BCYtvXip.js.map → MetricsChart-DFNBNWw7.js.map} +1 -1
- package/dist/chunks/{PDFViewer-BPS5xKz0.js → PDFViewer-B9OppITV.js} +3 -3
- package/dist/chunks/{PDFViewer-BPS5xKz0.js.map → PDFViewer-B9OppITV.js.map} +1 -1
- package/dist/chunks/{PDFViewer-B0h2W3v3.js → PDFViewer-uSCAzgto.js} +2 -2
- package/dist/chunks/{PDFViewer-B0h2W3v3.js.map → PDFViewer-uSCAzgto.js.map} +1 -1
- package/dist/chunks/{Page-CL6mad8S.js → Page-BnWHjjZe.js} +2 -2
- package/dist/chunks/{Page-CL6mad8S.js.map → Page-BnWHjjZe.js.map} +1 -1
- package/dist/chunks/{Page-BfSiGr0L.js → Page-lvFkx9ma.js} +2 -2
- package/dist/chunks/{Page-BfSiGr0L.js.map → Page-lvFkx9ma.js.map} +1 -1
- package/dist/chunks/{TopNav-LneaWujS.js → TopNav-8K9q8FgL.js} +2 -2
- package/dist/chunks/{TopNav-LneaWujS.js.map → TopNav-8K9q8FgL.js.map} +1 -1
- package/dist/chunks/{TopNav-Y9xm0v2v.js → TopNav-C86aDfet.js} +2 -2
- package/dist/chunks/{TopNav-Y9xm0v2v.js.map → TopNav-C86aDfet.js.map} +1 -1
- package/dist/chunks/{User-BgVd3vvo.js → User-DIhA4ryO.js} +2 -2
- package/dist/chunks/{User-BgVd3vvo.js.map → User-DIhA4ryO.js.map} +1 -1
- package/dist/chunks/{User-CifH24ZI.js → User-DbeMvd6x.js} +2 -2
- package/dist/chunks/{User-CifH24ZI.js.map → User-DbeMvd6x.js.map} +1 -1
- package/dist/chunks/{WebApp-BTURAtHS.js → WebApp-BIxs8_cJ.js} +2 -2
- package/dist/chunks/{WebApp-BTURAtHS.js.map → WebApp-BIxs8_cJ.js.map} +1 -1
- package/dist/chunks/{WebApp-Ri8Knc5O.js → WebApp-_cIASgB0.js} +14 -14
- package/dist/chunks/{WebApp-Ri8Knc5O.js.map → WebApp-_cIASgB0.js.map} +1 -1
- package/dist/chunks/{WebSocketClient-Dvl3AYx1.js → WebSocketClient-B6ribe3B.js} +145 -6
- package/dist/chunks/WebSocketClient-B6ribe3B.js.map +1 -0
- package/dist/chunks/WebSocketClient-Dbz1XNJA.js +2 -0
- package/dist/chunks/WebSocketClient-Dbz1XNJA.js.map +1 -0
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +6 -6
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +83 -15
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/package.json +1 -1
- package/dist/chunks/FormView-CG2dIaZ6.js +0 -2
- package/dist/chunks/FormView-CG2dIaZ6.js.map +0 -1
- package/dist/chunks/FormView-J-1RnLu0.js.map +0 -1
- package/dist/chunks/WebSocketClient-D6i85jl2.js +0 -2
- package/dist/chunks/WebSocketClient-D6i85jl2.js.map +0 -1
- package/dist/chunks/WebSocketClient-Dvl3AYx1.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-
|
|
1
|
+
import { M as Mustache, h as MOJOUtils, V as View } from "./WebApp-_cIASgB0.js";
|
|
2
2
|
class FormBuilder {
|
|
3
3
|
constructor(config = {}) {
|
|
4
4
|
this.fields = config.fields || [];
|
|
@@ -58,6 +58,48 @@ class FormBuilder {
|
|
|
58
58
|
{{#error}}<div class="{{errorClass}}">{{error}}</div>{{/error}}
|
|
59
59
|
</div>
|
|
60
60
|
`,
|
|
61
|
+
password: `
|
|
62
|
+
<div class="mojo-form-control">
|
|
63
|
+
{{#label}}
|
|
64
|
+
<label for="{{fieldId}}" class="{{labelClass}}">
|
|
65
|
+
{{label}}{{#required}}<span class="text-danger">*</span>{{/required}}
|
|
66
|
+
</label>
|
|
67
|
+
{{/label}}
|
|
68
|
+
<div class="input-group">
|
|
69
|
+
<input type="password" id="{{fieldId}}" name="{{name}}"
|
|
70
|
+
class="{{inputClass}}{{#error}} is-invalid{{/error}}"
|
|
71
|
+
value="{{fieldValue}}" {{#placeholder}}placeholder="{{placeholder}}"{{/placeholder}}
|
|
72
|
+
{{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}
|
|
73
|
+
{{#readonly}}readonly{{/readonly}} data-change-action="validate-field"
|
|
74
|
+
data-field-type="password" {{{attrs}}}>
|
|
75
|
+
{{#showToggle}}
|
|
76
|
+
<button type="button" class="btn btn-outline-secondary"
|
|
77
|
+
data-action="toggle-password"
|
|
78
|
+
data-target="{{fieldId}}"
|
|
79
|
+
aria-label="Show password" aria-pressed="false">
|
|
80
|
+
<i class="bi bi-eye"></i>
|
|
81
|
+
</button>
|
|
82
|
+
{{/showToggle}}
|
|
83
|
+
</div>
|
|
84
|
+
{{#strengthMeter}}
|
|
85
|
+
<div class="mt-2">
|
|
86
|
+
<div class="progress" style="height: 4px;">
|
|
87
|
+
<div class="progress-bar bg-secondary" role="progressbar"
|
|
88
|
+
style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
|
89
|
+
id="{{fieldId}}_strength_bar"></div>
|
|
90
|
+
</div>
|
|
91
|
+
<small class="{{helpClass}}" id="{{fieldId}}_strength_text">Strength</small>
|
|
92
|
+
</div>
|
|
93
|
+
{{/strengthMeter}}
|
|
94
|
+
{{#capsLockWarning}}
|
|
95
|
+
<div class="{{helpClass}} text-warning d-none" id="{{fieldId}}_caps_warning">
|
|
96
|
+
Caps Lock is on
|
|
97
|
+
</div>
|
|
98
|
+
{{/capsLockWarning}}
|
|
99
|
+
{{#help}}<div class="{{helpClass}}">{{help}}</div>{{/help}}
|
|
100
|
+
{{#error}}<div class="{{errorClass}}">{{error}}</div>{{/error}}
|
|
101
|
+
</div>
|
|
102
|
+
`,
|
|
61
103
|
textarea: `
|
|
62
104
|
<div class="mojo-form-control">
|
|
63
105
|
{{#label}}
|
|
@@ -589,7 +631,21 @@ class FormBuilder {
|
|
|
589
631
|
* @returns {string} Field HTML
|
|
590
632
|
*/
|
|
591
633
|
renderPasswordField(field) {
|
|
592
|
-
|
|
634
|
+
const passwordUsage = field.passwordUsage || "current";
|
|
635
|
+
const inferredAutocomplete = passwordUsage === "new" || passwordUsage === "new-password" ? "new-password" : "current-password";
|
|
636
|
+
const attributes = {
|
|
637
|
+
...field.attributes || {},
|
|
638
|
+
autocomplete: field.attributes && field.attributes.autocomplete || inferredAutocomplete
|
|
639
|
+
};
|
|
640
|
+
return this.renderInputField(
|
|
641
|
+
{
|
|
642
|
+
...field,
|
|
643
|
+
showToggle: field.showToggle !== false,
|
|
644
|
+
// default to showing the toggle unless explicitly disabled
|
|
645
|
+
attributes
|
|
646
|
+
},
|
|
647
|
+
"password"
|
|
648
|
+
);
|
|
593
649
|
}
|
|
594
650
|
/**
|
|
595
651
|
* Render number input field
|
|
@@ -751,6 +807,24 @@ class FormBuilder {
|
|
|
751
807
|
readonly,
|
|
752
808
|
attrs
|
|
753
809
|
};
|
|
810
|
+
if (type === "password" && (field.showToggle || field.strengthMeter || field.capsLockWarning)) {
|
|
811
|
+
const enhancedContext = {
|
|
812
|
+
...context,
|
|
813
|
+
showToggle: !!field.showToggle,
|
|
814
|
+
strengthMeter: !!field.strengthMeter,
|
|
815
|
+
capsLockWarning: !!field.capsLockWarning
|
|
816
|
+
};
|
|
817
|
+
return Mustache.render(this.templates.password, enhancedContext);
|
|
818
|
+
}
|
|
819
|
+
if (type === "password") {
|
|
820
|
+
const enhancedContext = {
|
|
821
|
+
...context,
|
|
822
|
+
showToggle: field.showToggle !== false,
|
|
823
|
+
strengthMeter: !!field.strengthMeter,
|
|
824
|
+
capsLockWarning: !!field.capsLockWarning
|
|
825
|
+
};
|
|
826
|
+
return Mustache.render(this.templates.password, enhancedContext);
|
|
827
|
+
}
|
|
754
828
|
return Mustache.render(this.templates.input, context);
|
|
755
829
|
}
|
|
756
830
|
/**
|
|
@@ -4090,6 +4164,7 @@ class FormView extends View {
|
|
|
4090
4164
|
await super.onAfterRender();
|
|
4091
4165
|
this.initializeImageFields();
|
|
4092
4166
|
this.initializeCustomComponents();
|
|
4167
|
+
this.initializePasswordFields();
|
|
4093
4168
|
}
|
|
4094
4169
|
/**
|
|
4095
4170
|
* Initialize image fields with FileDropMixin
|
|
@@ -5210,6 +5285,120 @@ class FormView extends View {
|
|
|
5210
5285
|
});
|
|
5211
5286
|
await super.onBeforeDestroy();
|
|
5212
5287
|
}
|
|
5288
|
+
/**
|
|
5289
|
+
* Initialize password fields: strength meter, caps lock warning, and hold-to-reveal
|
|
5290
|
+
*/
|
|
5291
|
+
initializePasswordFields() {
|
|
5292
|
+
if (!this.element) return;
|
|
5293
|
+
const inputs = this.element.querySelectorAll('input[data-field-type="password"], input[type="password"]');
|
|
5294
|
+
inputs.forEach((input) => {
|
|
5295
|
+
this.updatePasswordStrengthUI(input);
|
|
5296
|
+
const onInput = (e) => {
|
|
5297
|
+
this.updatePasswordStrengthUI(e.target);
|
|
5298
|
+
};
|
|
5299
|
+
input.addEventListener("input", onInput);
|
|
5300
|
+
const capsHandler = (e) => {
|
|
5301
|
+
if (typeof e.getModifierState === "function") {
|
|
5302
|
+
const caps = e.getModifierState("CapsLock");
|
|
5303
|
+
this.updateCapsLockWarning(input, !!caps);
|
|
5304
|
+
}
|
|
5305
|
+
};
|
|
5306
|
+
input.addEventListener("keydown", capsHandler);
|
|
5307
|
+
input.addEventListener("keyup", capsHandler);
|
|
5308
|
+
this.updateCapsLockWarning(input, false);
|
|
5309
|
+
});
|
|
5310
|
+
}
|
|
5311
|
+
/**
|
|
5312
|
+
* Toggle password visibility on click (persistent toggle)
|
|
5313
|
+
* Ignored if currently in press-and-hold mode.
|
|
5314
|
+
*/
|
|
5315
|
+
async onActionTogglePassword(event, element) {
|
|
5316
|
+
event.preventDefault();
|
|
5317
|
+
const targetId = element.getAttribute("data-target");
|
|
5318
|
+
let input = null;
|
|
5319
|
+
if (targetId) {
|
|
5320
|
+
input = this.element.querySelector("#" + targetId);
|
|
5321
|
+
}
|
|
5322
|
+
if (!input) {
|
|
5323
|
+
const group = element.closest(".input-group");
|
|
5324
|
+
if (group) {
|
|
5325
|
+
input = group.querySelector('input[type="password"], input[data-field-type="password"], input[type="text"]');
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
if (!input) return;
|
|
5329
|
+
const isHidden = input.type === "password";
|
|
5330
|
+
input.type = isHidden ? "text" : "password";
|
|
5331
|
+
element.setAttribute("aria-pressed", isHidden ? "true" : "false");
|
|
5332
|
+
element.setAttribute("aria-label", isHidden ? "Hide password" : "Show password");
|
|
5333
|
+
const icon = element.querySelector("i");
|
|
5334
|
+
if (icon) {
|
|
5335
|
+
icon.classList.toggle("bi-eye", !isHidden);
|
|
5336
|
+
icon.classList.toggle("bi-eye-slash", isHidden);
|
|
5337
|
+
}
|
|
5338
|
+
input.focus();
|
|
5339
|
+
try {
|
|
5340
|
+
const len = input.value?.length ?? 0;
|
|
5341
|
+
input.setSelectionRange(len, len);
|
|
5342
|
+
} catch (_e) {
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
/**
|
|
5346
|
+
* Compute a simple password strength score and associated UI metadata
|
|
5347
|
+
*/
|
|
5348
|
+
computePasswordStrength(password = "") {
|
|
5349
|
+
const len = password.length;
|
|
5350
|
+
let score = 0;
|
|
5351
|
+
if (len >= 6) score++;
|
|
5352
|
+
if (len >= 8) score++;
|
|
5353
|
+
if (len >= 12) score++;
|
|
5354
|
+
const hasLower = /[a-z]/.test(password);
|
|
5355
|
+
const hasUpper = /[A-Z]/.test(password);
|
|
5356
|
+
const hasDigit = /\d/.test(password);
|
|
5357
|
+
const hasSymbol = /[^A-Za-z0-9]/.test(password);
|
|
5358
|
+
const variety = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;
|
|
5359
|
+
if (variety >= 2) score++;
|
|
5360
|
+
if (variety >= 3) score++;
|
|
5361
|
+
score = Math.max(0, Math.min(4, score));
|
|
5362
|
+
const map = [
|
|
5363
|
+
{ percent: 0, label: "Too short", barClass: "bg-secondary" },
|
|
5364
|
+
{ percent: 25, label: "Weak", barClass: "bg-danger" },
|
|
5365
|
+
{ percent: 50, label: "Fair", barClass: "bg-warning" },
|
|
5366
|
+
{ percent: 75, label: "Good", barClass: "bg-info" },
|
|
5367
|
+
{ percent: 100, label: "Strong", barClass: "bg-success" }
|
|
5368
|
+
];
|
|
5369
|
+
return map[score];
|
|
5370
|
+
}
|
|
5371
|
+
/**
|
|
5372
|
+
* Update strength meter UI if present for a given password input
|
|
5373
|
+
*/
|
|
5374
|
+
updatePasswordStrengthUI(input) {
|
|
5375
|
+
if (!input || !input.id) return;
|
|
5376
|
+
const bar = this.element.querySelector(`#${input.id}_strength_bar`);
|
|
5377
|
+
const text = this.element.querySelector(`#${input.id}_strength_text`);
|
|
5378
|
+
if (!bar && !text) return;
|
|
5379
|
+
const { percent, label, barClass } = this.computePasswordStrength(input.value || "");
|
|
5380
|
+
if (bar) {
|
|
5381
|
+
bar.className = `progress-bar ${barClass}`;
|
|
5382
|
+
bar.style.width = `${percent}%`;
|
|
5383
|
+
bar.setAttribute("aria-valuenow", String(percent));
|
|
5384
|
+
}
|
|
5385
|
+
if (text) {
|
|
5386
|
+
text.textContent = label;
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
/**
|
|
5390
|
+
* Show/Hide a caps lock warning element if present for the given input
|
|
5391
|
+
*/
|
|
5392
|
+
updateCapsLockWarning(input, capsOn) {
|
|
5393
|
+
if (!input || !input.id) return;
|
|
5394
|
+
const warn = this.element.querySelector(`#${input.id}_caps_warning`);
|
|
5395
|
+
if (!warn) return;
|
|
5396
|
+
if (capsOn) {
|
|
5397
|
+
warn.classList.remove("d-none");
|
|
5398
|
+
} else {
|
|
5399
|
+
warn.classList.add("d-none");
|
|
5400
|
+
}
|
|
5401
|
+
}
|
|
5213
5402
|
}
|
|
5214
5403
|
const FormView$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
5215
5404
|
__proto__: null,
|
|
@@ -5221,4 +5410,4 @@ export {
|
|
|
5221
5410
|
applyFileDropMixin as a,
|
|
5222
5411
|
FormView$1 as b
|
|
5223
5412
|
};
|
|
5224
|
-
//# sourceMappingURL=FormView-
|
|
5413
|
+
//# sourceMappingURL=FormView-BCnSXvaY.js.map
|