videomail-client 16.0.2 → 16.0.3

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/README.md CHANGED
@@ -260,25 +260,7 @@ You can also retrieve this data with `videomailClient.getByKey()`.
260
260
 
261
261
  ## Form Submissions
262
262
 
263
- With `enableAutoSubmission` enabled (the default), a recorded video and an explicit form `action` produce two requests: the client sends Videomail first, then POSTs the original form fields to that action after success. Without a recorded video, the client POSTs the form directly to its action.
264
-
265
- For first-party Videomail forms without an explicit `action`, a recorded video is sent in one request to the Videomail API; listen for `SUBMITTED` to handle the response. Set `enableAutoSubmission: false` when a host framework owns form submission, then let that integration resume its submission after `SUBMITTED`.
266
-
267
- If this does not work, verify that the configured selectors identify the form and its submit button:
268
-
269
- ```ts
270
- selectors: {
271
- formId: undefined,
272
- submitButtonId: undefined,
273
- submitButtonSelector: undefined,
274
- }
275
- ```
276
-
277
- When these values are `undefined` (the defaults), the client detects the nearest form and a button with `type="submit"` automatically.
278
-
279
- ### Include videomail meta data in Form Submissions
280
-
281
- When the client sends the second, host-form request, enable `submitWithVideomail` to include full videomail metadata under the `videomail` key. Otherwise the original form fields include the videomail alias, which can later be resolved with `videomailClient.getByAlias(alias)`.
263
+ How to configure, inject meta data and test form submissions with the Videomail client can be found in [Form Submissions ✏️](./docs/FORM_SUBMISSIONS.md).
282
264
 
283
265
  ## Privacy and error reporting
284
266
 
@@ -11115,7 +11115,7 @@ var __webpack_exports__ = {};
11115
11115
  var client = __webpack_require__("Q");
11116
11116
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11117
11117
  var package_namespaceObject = {
11118
- rE: "16.0.2"
11118
+ rE: "16.0.3"
11119
11119
  };
11120
11120
  function isAudioEnabled(options) {
11121
11121
  return Boolean(options.audio.enabled);
@@ -15271,13 +15271,35 @@ var __webpack_exports__ = {};
15271
15271
  if ((null == type ? void 0 : type.toLowerCase()) === "hidden") inputElement.value = "";
15272
15272
  }
15273
15273
  }
15274
- startListeningToSubmitEvents() {
15274
+ connectSubmitButton() {
15275
15275
  const submitButton = this.container.getSubmitButton();
15276
- if (submitButton) submitButton.onclick = this.doTheSubmit.bind(this);
15276
+ if (!submitButton) return;
15277
+ if (submitButton.form === this.formElement) return;
15278
+ if (!this.formElement.id) {
15279
+ let formId;
15280
+ do formId = `videomail-form-${Form.nextFormId++}`;
15281
+ while (document.getElementById(formId))
15282
+ this.formElement.id = formId;
15283
+ this.generatedFormId = formId;
15284
+ }
15285
+ this.submitButton = submitButton;
15286
+ this.submitButtonFormAttribute = submitButton.getAttribute("form");
15287
+ submitButton.setAttribute("form", this.formElement.id);
15288
+ }
15289
+ startListeningToSubmitEvents() {
15290
+ this.connectSubmitButton();
15291
+ this.formElement.addEventListener("submit", this.handleSubmit);
15277
15292
  }
15278
15293
  stopListeningToSubmitEvents() {
15279
- const submitButton = this.container.getSubmitButton();
15280
- if (submitButton) submitButton.onclick = null;
15294
+ this.formElement.removeEventListener("submit", this.handleSubmit);
15295
+ if (this.submitButton) {
15296
+ if (null === this.submitButtonFormAttribute) this.submitButton.removeAttribute("form");
15297
+ else this.submitButton.setAttribute("form", this.submitButtonFormAttribute);
15298
+ this.submitButton = void 0;
15299
+ this.submitButtonFormAttribute = null;
15300
+ }
15301
+ if (this.generatedFormId === this.formElement.id) this.formElement.removeAttribute("id");
15302
+ this.generatedFormId = void 0;
15281
15303
  }
15282
15304
  doTheSubmit(e) {
15283
15305
  return _async_to_generator(function*() {
@@ -15286,6 +15308,7 @@ var __webpack_exports__ = {};
15286
15308
  this.options.logger.debug(`Form: doTheSubmit(${string_pretty(e)})`);
15287
15309
  e.preventDefault();
15288
15310
  } else this.options.logger.debug("Form: doTheSubmit()");
15311
+ if (this.isSubmitting) return false;
15289
15312
  const hasHostAction = this.formElement.hasAttribute("action");
15290
15313
  const url = null != (_this_formElement_getAttribute = this.formElement.getAttribute("action")) ? _this_formElement_getAttribute : this.options.apiUrl;
15291
15314
  const method = this.formElement.getAttribute("method");
@@ -15300,7 +15323,14 @@ var __webpack_exports__ = {};
15300
15323
  default:
15301
15324
  chosenMethod = FormMethod.POST;
15302
15325
  }
15303
- if (this.container.hasElement()) yield this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
15326
+ if (this.container.hasElement()) {
15327
+ this.isSubmitting = true;
15328
+ try {
15329
+ yield this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
15330
+ } finally{
15331
+ this.isSubmitting = false;
15332
+ }
15333
+ }
15304
15334
  return false;
15305
15335
  }).call(this);
15306
15336
  }
@@ -15322,9 +15352,11 @@ var __webpack_exports__ = {};
15322
15352
  html_showElement(this.formElement);
15323
15353
  }
15324
15354
  constructor(container, formElement, options){
15325
- super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "validate", (event)=>{
15355
+ super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "keyInput", void 0), form_define_property(this, "submitButton", void 0), form_define_property(this, "submitButtonFormAttribute", null), form_define_property(this, "generatedFormId", void 0), form_define_property(this, "isSubmitting", false), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
15326
15356
  this.container.validate(event);
15327
- }), form_define_property(this, "keyInput", void 0), form_define_property(this, "FORM_FIELDS", {});
15357
+ }), form_define_property(this, "handleSubmit", (event)=>{
15358
+ if (!event.defaultPrevented) this.doTheSubmit(event);
15359
+ });
15328
15360
  this.container = container;
15329
15361
  this.formElement = formElement;
15330
15362
  this.FORM_FIELDS = {
@@ -15340,6 +15372,7 @@ var __webpack_exports__ = {};
15340
15372
  };
15341
15373
  }
15342
15374
  }
15375
+ form_define_property(Form, "nextFormId", 0);
15343
15376
  const wrappers_form = Form;
15344
15377
  function resource_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
15345
15378
  try {
@@ -18025,7 +18058,7 @@ var __webpack_exports__ = {};
18025
18058
  html_disableElement(radioButtonElement);
18026
18059
  return radioButtonElement;
18027
18060
  }
18028
- makeButton(buttonClass, text, clickHandler, show, id, type, selector, disabled = true) {
18061
+ makeButton(buttonClass, text, clickHandler, show, id, type = "button", selector, disabled = true) {
18029
18062
  let buttonElement;
18030
18063
  if (id) buttonElement = document.querySelector(`#${id}`);
18031
18064
  else if (selector) buttonElement = document.querySelector(selector);
package/dist/esm/index.js CHANGED
@@ -3620,7 +3620,7 @@ const constants = {
3620
3620
  }
3621
3621
  };
3622
3622
  var package_namespaceObject = {
3623
- rE: "16.0.2"
3623
+ rE: "16.0.3"
3624
3624
  };
3625
3625
  function isAudioEnabled(options) {
3626
3626
  return Boolean(options.audio.enabled);
@@ -4368,19 +4368,42 @@ class Form extends util_Despot {
4368
4368
  if (type?.toLowerCase() === "hidden") inputElement.value = "";
4369
4369
  }
4370
4370
  }
4371
- startListeningToSubmitEvents() {
4371
+ connectSubmitButton() {
4372
4372
  const submitButton = this.container.getSubmitButton();
4373
- if (submitButton) submitButton.onclick = this.doTheSubmit.bind(this);
4373
+ if (!submitButton) return;
4374
+ if (submitButton.form === this.formElement) return;
4375
+ if (!this.formElement.id) {
4376
+ let formId;
4377
+ do formId = `videomail-form-${Form.nextFormId++}`;
4378
+ while (document.getElementById(formId))
4379
+ this.formElement.id = formId;
4380
+ this.generatedFormId = formId;
4381
+ }
4382
+ this.submitButton = submitButton;
4383
+ this.submitButtonFormAttribute = submitButton.getAttribute("form");
4384
+ submitButton.setAttribute("form", this.formElement.id);
4385
+ }
4386
+ startListeningToSubmitEvents() {
4387
+ this.connectSubmitButton();
4388
+ this.formElement.addEventListener("submit", this.handleSubmit);
4374
4389
  }
4375
4390
  stopListeningToSubmitEvents() {
4376
- const submitButton = this.container.getSubmitButton();
4377
- if (submitButton) submitButton.onclick = null;
4391
+ this.formElement.removeEventListener("submit", this.handleSubmit);
4392
+ if (this.submitButton) {
4393
+ if (null === this.submitButtonFormAttribute) this.submitButton.removeAttribute("form");
4394
+ else this.submitButton.setAttribute("form", this.submitButtonFormAttribute);
4395
+ this.submitButton = void 0;
4396
+ this.submitButtonFormAttribute = null;
4397
+ }
4398
+ if (this.generatedFormId === this.formElement.id) this.formElement.removeAttribute("id");
4399
+ this.generatedFormId = void 0;
4378
4400
  }
4379
4401
  async doTheSubmit(e) {
4380
4402
  if (e) {
4381
4403
  this.options.logger.debug(`Form: doTheSubmit(${string_pretty(e)})`);
4382
4404
  e.preventDefault();
4383
4405
  } else this.options.logger.debug("Form: doTheSubmit()");
4406
+ if (this.isSubmitting) return false;
4384
4407
  const hasHostAction = this.formElement.hasAttribute("action");
4385
4408
  const url = this.formElement.getAttribute("action") ?? this.options.apiUrl;
4386
4409
  const method = this.formElement.getAttribute("method");
@@ -4395,7 +4418,14 @@ class Form extends util_Despot {
4395
4418
  default:
4396
4419
  chosenMethod = FormMethod.POST;
4397
4420
  }
4398
- if (this.container.hasElement()) await this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
4421
+ if (this.container.hasElement()) {
4422
+ this.isSubmitting = true;
4423
+ try {
4424
+ await this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
4425
+ } finally{
4426
+ this.isSubmitting = false;
4427
+ }
4428
+ }
4399
4429
  return false;
4400
4430
  }
4401
4431
  getInvalidElement() {
@@ -4416,9 +4446,11 @@ class Form extends util_Despot {
4416
4446
  html_showElement(this.formElement);
4417
4447
  }
4418
4448
  constructor(container, formElement, options){
4419
- super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "validate", (event)=>{
4449
+ super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "keyInput", void 0), form_define_property(this, "submitButton", void 0), form_define_property(this, "submitButtonFormAttribute", null), form_define_property(this, "generatedFormId", void 0), form_define_property(this, "isSubmitting", false), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
4420
4450
  this.container.validate(event);
4421
- }), form_define_property(this, "keyInput", void 0), form_define_property(this, "FORM_FIELDS", {});
4451
+ }), form_define_property(this, "handleSubmit", (event)=>{
4452
+ if (!event.defaultPrevented) this.doTheSubmit(event);
4453
+ });
4422
4454
  this.container = container;
4423
4455
  this.formElement = formElement;
4424
4456
  this.FORM_FIELDS = {
@@ -4434,6 +4466,7 @@ class Form extends util_Despot {
4434
4466
  };
4435
4467
  }
4436
4468
  }
4469
+ form_define_property(Form, "nextFormId", 0);
4437
4470
  const wrappers_form = Form;
4438
4471
  function resource_define_property(obj, key, value) {
4439
4472
  if (key in obj) Object.defineProperty(obj, key, {
@@ -4970,7 +5003,7 @@ class Buttons extends util_Despot {
4970
5003
  html_disableElement(radioButtonElement);
4971
5004
  return radioButtonElement;
4972
5005
  }
4973
- makeButton(buttonClass, text, clickHandler, show, id, type, selector, disabled = true) {
5006
+ makeButton(buttonClass, text, clickHandler, show, id, type = "button", selector, disabled = true) {
4974
5007
  let buttonElement;
4975
5008
  buttonElement = id ? document.querySelector(`#${id}`) : selector ? document.querySelector(selector) : this.buttonsElement?.querySelector(`.${buttonClass}`);
4976
5009
  if (buttonElement) buttonElement = html_adjustButton(buttonElement, show, type, disabled);
@@ -10,12 +10,18 @@ export declare const FormMethod: {
10
10
  };
11
11
  export type FormMethodType = (typeof FormMethod)[keyof typeof FormMethod];
12
12
  declare class Form extends Despot {
13
+ private static nextFormId;
13
14
  private readonly container;
14
15
  private readonly formElement;
15
- private readonly validate;
16
16
  private keyInput?;
17
+ private submitButton;
18
+ private submitButtonFormAttribute;
19
+ private generatedFormId;
20
+ private isSubmitting;
17
21
  private readonly FORM_FIELDS;
18
22
  constructor(container: Container, formElement: HTMLFormElement, options: VideomailClientOptions);
23
+ private readonly validate;
24
+ private readonly handleSubmit;
19
25
  private getData;
20
26
  transformFormData(formInputs: FormInputs): {
21
27
  url?: string;
@@ -405,9 +411,10 @@ declare class Form extends Despot {
405
411
  private hideSubmitButton;
406
412
  unload(): void;
407
413
  private resetForm;
414
+ private connectSubmitButton;
408
415
  private startListeningToSubmitEvents;
409
416
  private stopListeningToSubmitEvents;
410
- doTheSubmit(e?: any): Promise<boolean>;
417
+ doTheSubmit(e?: Event): Promise<boolean>;
411
418
  getInvalidElement(): Element | null;
412
419
  findSubmitButton(): HTMLButtonElement | null;
413
420
  hide(): void;
package/dist/umd/index.js CHANGED
@@ -11121,7 +11121,7 @@
11121
11121
  var client = __webpack_require__("Q");
11122
11122
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11123
11123
  var package_namespaceObject = {
11124
- rE: "16.0.2"
11124
+ rE: "16.0.3"
11125
11125
  };
11126
11126
  function isAudioEnabled(options) {
11127
11127
  return Boolean(options.audio.enabled);
@@ -15029,12 +15029,14 @@
15029
15029
  GET: "get"
15030
15030
  };
15031
15031
  class Form extends util_Despot {
15032
+ static nextFormId = 0;
15032
15033
  container;
15033
15034
  formElement;
15034
- validate = (event)=>{
15035
- this.container.validate(event);
15036
- };
15037
15035
  keyInput;
15036
+ submitButton;
15037
+ submitButtonFormAttribute = null;
15038
+ generatedFormId;
15039
+ isSubmitting = false;
15038
15040
  FORM_FIELDS = {};
15039
15041
  constructor(container, formElement, options){
15040
15042
  super("Form", options);
@@ -15052,6 +15054,12 @@
15052
15054
  sendCopy: options.selectors.sendCopyInputName
15053
15055
  };
15054
15056
  }
15057
+ validate = (event)=>{
15058
+ this.container.validate(event);
15059
+ };
15060
+ handleSubmit = (event)=>{
15061
+ if (!event.defaultPrevented) this.doTheSubmit(event);
15062
+ };
15055
15063
  getData() {
15056
15064
  return getFormData(this.formElement, {
15057
15065
  includeDisabled: true
@@ -15235,19 +15243,42 @@
15235
15243
  if (type?.toLowerCase() === "hidden") inputElement.value = "";
15236
15244
  }
15237
15245
  }
15238
- startListeningToSubmitEvents() {
15246
+ connectSubmitButton() {
15239
15247
  const submitButton = this.container.getSubmitButton();
15240
- if (submitButton) submitButton.onclick = this.doTheSubmit.bind(this);
15248
+ if (!submitButton) return;
15249
+ if (submitButton.form === this.formElement) return;
15250
+ if (!this.formElement.id) {
15251
+ let formId;
15252
+ do formId = `videomail-form-${Form.nextFormId++}`;
15253
+ while (document.getElementById(formId))
15254
+ this.formElement.id = formId;
15255
+ this.generatedFormId = formId;
15256
+ }
15257
+ this.submitButton = submitButton;
15258
+ this.submitButtonFormAttribute = submitButton.getAttribute("form");
15259
+ submitButton.setAttribute("form", this.formElement.id);
15260
+ }
15261
+ startListeningToSubmitEvents() {
15262
+ this.connectSubmitButton();
15263
+ this.formElement.addEventListener("submit", this.handleSubmit);
15241
15264
  }
15242
15265
  stopListeningToSubmitEvents() {
15243
- const submitButton = this.container.getSubmitButton();
15244
- if (submitButton) submitButton.onclick = null;
15266
+ this.formElement.removeEventListener("submit", this.handleSubmit);
15267
+ if (this.submitButton) {
15268
+ if (null === this.submitButtonFormAttribute) this.submitButton.removeAttribute("form");
15269
+ else this.submitButton.setAttribute("form", this.submitButtonFormAttribute);
15270
+ this.submitButton = void 0;
15271
+ this.submitButtonFormAttribute = null;
15272
+ }
15273
+ if (this.generatedFormId === this.formElement.id) this.formElement.removeAttribute("id");
15274
+ this.generatedFormId = void 0;
15245
15275
  }
15246
15276
  async doTheSubmit(e) {
15247
15277
  if (e) {
15248
15278
  this.options.logger.debug(`Form: doTheSubmit(${string_pretty(e)})`);
15249
15279
  e.preventDefault();
15250
15280
  } else this.options.logger.debug("Form: doTheSubmit()");
15281
+ if (this.isSubmitting) return false;
15251
15282
  const hasHostAction = this.formElement.hasAttribute("action");
15252
15283
  const url = this.formElement.getAttribute("action") ?? this.options.apiUrl;
15253
15284
  const method = this.formElement.getAttribute("method");
@@ -15262,7 +15293,14 @@
15262
15293
  default:
15263
15294
  chosenMethod = FormMethod.POST;
15264
15295
  }
15265
- if (this.container.hasElement()) await this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
15296
+ if (this.container.hasElement()) {
15297
+ this.isSubmitting = true;
15298
+ try {
15299
+ await this.container.submitAll(this.getData(), chosenMethod, url, hasHostAction);
15300
+ } finally{
15301
+ this.isSubmitting = false;
15302
+ }
15303
+ }
15266
15304
  return false;
15267
15305
  }
15268
15306
  getInvalidElement() {
@@ -17888,7 +17926,7 @@
17888
17926
  html_disableElement(radioButtonElement);
17889
17927
  return radioButtonElement;
17890
17928
  }
17891
- makeButton(buttonClass, text, clickHandler, show, id, type, selector, disabled = true) {
17929
+ makeButton(buttonClass, text, clickHandler, show, id, type = "button", selector, disabled = true) {
17892
17930
  let buttonElement;
17893
17931
  buttonElement = id ? document.querySelector(`#${id}`) : selector ? document.querySelector(selector) : this.buttonsElement?.querySelector(`.${buttonClass}`);
17894
17932
  if (buttonElement) buttonElement = html_adjustButton(buttonElement, show, type, disabled);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "videomail-client",
3
- "version": "16.0.2",
3
+ "version": "16.0.3",
4
4
  "description": "A wicked npm package to record videos directly in the browser, for Deaf and Sign Language!",
5
5
  "keywords": [
6
6
  "webcam",
@@ -53,7 +53,7 @@
53
53
  "prettier:fix": "prettier --write ./etc ./src ./.storybook *.ts *.js",
54
54
  "quality": "npm run test && npm run prettier && npm run lint && npm run types && npm run audit",
55
55
  "release": "release-it --only-version --config ./etc/release-it.ts",
56
- "storybook": "cross-env BROWSER=chromium storybook dev -p 8443 --https --ssl-cert ./etc/ssl-certs/localhost.crt --ssl-key ./etc/ssl-certs/localhost.key",
56
+ "storybook": "cross-env BROWSER=chromium storybook dev -p 8443 -h localhost --https --ssl-cert ./etc/ssl-certs/localhost.crt --ssl-key ./etc/ssl-certs/localhost.key",
57
57
  "test": "cross-env ENVIRON=test vitest --watch=false",
58
58
  "types": "tsc --noEmit",
59
59
  "types:config": "tsc --showConfig"
@@ -113,7 +113,7 @@
113
113
  "eslint-plugin-simple-import-sort": "14.0.0",
114
114
  "globals": "17.13.0",
115
115
  "jsdom": "30.1.3",
116
- "msw": "2.15.0",
116
+ "msw": "3.0.3",
117
117
  "msw-storybook-addon": "3.0.3",
118
118
  "prettier": "3.9.10",
119
119
  "prettier-plugin-curly": "0.4.1",