videomail-client 16.0.1 → 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 +1 -19
- package/dist/cjs/index.cjs +80 -35
- package/dist/esm/index.js +79 -34
- package/dist/esm/wrappers/form.d.ts +9 -2
- package/dist/esm/wrappers/visuals/recorder.d.ts +1 -0
- package/dist/umd/index.js +85 -35
- package/package.json +3 -3
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
|
-
|
|
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
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
15274
|
+
connectSubmitButton() {
|
|
15275
15275
|
const submitButton = this.container.getSubmitButton();
|
|
15276
|
-
if (submitButton)
|
|
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
|
-
|
|
15280
|
-
if (submitButton)
|
|
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())
|
|
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, "
|
|
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);
|
|
@@ -20096,6 +20129,7 @@ var __webpack_exports__ = {};
|
|
|
20096
20129
|
this.options.logger.debug(debugLine);
|
|
20097
20130
|
const canReconnect = this.canReconnectAfterStreamClose();
|
|
20098
20131
|
this.connected = false;
|
|
20132
|
+
this.stopPings();
|
|
20099
20133
|
if (this.shouldIgnoreStreamClose()) return;
|
|
20100
20134
|
if (this.mediaTransmissionStarted) this.failInterruptedRecording();
|
|
20101
20135
|
else if (canReconnect) {
|
|
@@ -20112,7 +20146,38 @@ var __webpack_exports__ = {};
|
|
|
20112
20146
|
}, 0);
|
|
20113
20147
|
}
|
|
20114
20148
|
shouldIgnoreStreamClose() {
|
|
20115
|
-
return this.recordingTransportFailed || Boolean(this.unloaded);
|
|
20149
|
+
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
20150
|
+
}
|
|
20151
|
+
handleStreamError(url2Connect, err) {
|
|
20152
|
+
var _this_lastSocketError;
|
|
20153
|
+
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
20154
|
+
else this.lastSocketError = serializeError(err);
|
|
20155
|
+
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
20156
|
+
error: serializeError(err)
|
|
20157
|
+
});
|
|
20158
|
+
this.options.logger.debug(debugLine);
|
|
20159
|
+
if (this.validate()) {
|
|
20160
|
+
this.connected = false;
|
|
20161
|
+
this.stopPings();
|
|
20162
|
+
this.discardSocket();
|
|
20163
|
+
return;
|
|
20164
|
+
}
|
|
20165
|
+
if (!this.connected) return void this.handleConnectionFailure({
|
|
20166
|
+
url2Connect,
|
|
20167
|
+
cause: "error"
|
|
20168
|
+
});
|
|
20169
|
+
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
20170
|
+
cause: null != (_this_lastSocketError = this.lastSocketError) ? _this_lastSocketError : err
|
|
20171
|
+
});
|
|
20172
|
+
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
20173
|
+
this.emit("ERROR", {
|
|
20174
|
+
err: error_createError({
|
|
20175
|
+
message: "WebSocket stream error",
|
|
20176
|
+
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
20177
|
+
options: this.options,
|
|
20178
|
+
exc: streamError
|
|
20179
|
+
})
|
|
20180
|
+
});
|
|
20116
20181
|
}
|
|
20117
20182
|
sendPings() {
|
|
20118
20183
|
this.stopPings();
|
|
@@ -20123,6 +20188,7 @@ var __webpack_exports__ = {};
|
|
|
20123
20188
|
}
|
|
20124
20189
|
stopPings() {
|
|
20125
20190
|
if (void 0 === this.pingInterval) return;
|
|
20191
|
+
this.options.logger.debug("Recorder: stopping pings...");
|
|
20126
20192
|
window.clearInterval(this.pingInterval);
|
|
20127
20193
|
this.pingInterval = void 0;
|
|
20128
20194
|
}
|
|
@@ -20275,7 +20341,7 @@ var __webpack_exports__ = {};
|
|
|
20275
20341
|
});
|
|
20276
20342
|
}
|
|
20277
20343
|
shouldIgnoreConnectionFailure() {
|
|
20278
|
-
return this.connectionFailed || this.connected || Boolean(this.unloaded);
|
|
20344
|
+
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
|
|
20279
20345
|
}
|
|
20280
20346
|
calculateFrameProgress() {
|
|
20281
20347
|
return `${(this.confirmedFrameNumber / (this.framesCount || 1) * 100).toFixed(2)}%`;
|
|
@@ -20483,29 +20549,7 @@ var __webpack_exports__ = {};
|
|
|
20483
20549
|
}
|
|
20484
20550
|
});
|
|
20485
20551
|
stream.on("error", (err)=>{
|
|
20486
|
-
|
|
20487
|
-
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
20488
|
-
else this.lastSocketError = serializeError(err);
|
|
20489
|
-
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
20490
|
-
error: serializeError(err)
|
|
20491
|
-
});
|
|
20492
|
-
this.options.logger.debug(debugLine);
|
|
20493
|
-
if (!this.connected) return void this.handleConnectionFailure({
|
|
20494
|
-
url2Connect,
|
|
20495
|
-
cause: "error"
|
|
20496
|
-
});
|
|
20497
|
-
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
20498
|
-
cause: null != (_this_lastSocketError = this.lastSocketError) ? _this_lastSocketError : err
|
|
20499
|
-
});
|
|
20500
|
-
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
20501
|
-
this.emit("ERROR", {
|
|
20502
|
-
err: error_createError({
|
|
20503
|
-
message: "WebSocket stream error",
|
|
20504
|
-
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
20505
|
-
options: this.options,
|
|
20506
|
-
exc: streamError
|
|
20507
|
-
})
|
|
20508
|
-
});
|
|
20552
|
+
this.handleStreamError(url2Connect, err);
|
|
20509
20553
|
});
|
|
20510
20554
|
stream.on("drain", ()=>{
|
|
20511
20555
|
this.handleStreamDrain(stream);
|
|
@@ -20853,6 +20897,7 @@ var __webpack_exports__ = {};
|
|
|
20853
20897
|
}
|
|
20854
20898
|
back(cb) {
|
|
20855
20899
|
this.emit("GOING_BACK");
|
|
20900
|
+
this.key = void 0;
|
|
20856
20901
|
this.recordingDimensions = void 0;
|
|
20857
20902
|
this.unloaded = false;
|
|
20858
20903
|
this.show();
|
|
@@ -20919,7 +20964,7 @@ var __webpack_exports__ = {};
|
|
|
20919
20964
|
this.stopTimeout = void 0;
|
|
20920
20965
|
}
|
|
20921
20966
|
validate() {
|
|
20922
|
-
return this.
|
|
20967
|
+
return Boolean(this.key) && void 0 === this.canvas;
|
|
20923
20968
|
}
|
|
20924
20969
|
isReady() {
|
|
20925
20970
|
var _this_userMedia;
|
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.
|
|
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
|
-
|
|
4371
|
+
connectSubmitButton() {
|
|
4372
4372
|
const submitButton = this.container.getSubmitButton();
|
|
4373
|
-
if (submitButton)
|
|
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
|
-
|
|
4377
|
-
if (submitButton)
|
|
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())
|
|
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, "
|
|
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);
|
|
@@ -6875,6 +6908,7 @@ class Recorder extends util_Despot {
|
|
|
6875
6908
|
this.options.logger.debug(debugLine);
|
|
6876
6909
|
const canReconnect = this.canReconnectAfterStreamClose();
|
|
6877
6910
|
this.connected = false;
|
|
6911
|
+
this.stopPings();
|
|
6878
6912
|
if (this.shouldIgnoreStreamClose()) return;
|
|
6879
6913
|
if (this.mediaTransmissionStarted) this.failInterruptedRecording();
|
|
6880
6914
|
else if (canReconnect) {
|
|
@@ -6891,7 +6925,37 @@ class Recorder extends util_Despot {
|
|
|
6891
6925
|
}, 0);
|
|
6892
6926
|
}
|
|
6893
6927
|
shouldIgnoreStreamClose() {
|
|
6894
|
-
return this.recordingTransportFailed || Boolean(this.unloaded);
|
|
6928
|
+
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
6929
|
+
}
|
|
6930
|
+
handleStreamError(url2Connect, err) {
|
|
6931
|
+
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
6932
|
+
else this.lastSocketError = serializeError(err);
|
|
6933
|
+
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
6934
|
+
error: serializeError(err)
|
|
6935
|
+
});
|
|
6936
|
+
this.options.logger.debug(debugLine);
|
|
6937
|
+
if (this.validate()) {
|
|
6938
|
+
this.connected = false;
|
|
6939
|
+
this.stopPings();
|
|
6940
|
+
this.discardSocket();
|
|
6941
|
+
return;
|
|
6942
|
+
}
|
|
6943
|
+
if (!this.connected) return void this.handleConnectionFailure({
|
|
6944
|
+
url2Connect,
|
|
6945
|
+
cause: "error"
|
|
6946
|
+
});
|
|
6947
|
+
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
6948
|
+
cause: this.lastSocketError ?? err
|
|
6949
|
+
});
|
|
6950
|
+
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
6951
|
+
this.emit("ERROR", {
|
|
6952
|
+
err: error_createError({
|
|
6953
|
+
message: "WebSocket stream error",
|
|
6954
|
+
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
6955
|
+
options: this.options,
|
|
6956
|
+
exc: streamError
|
|
6957
|
+
})
|
|
6958
|
+
});
|
|
6895
6959
|
}
|
|
6896
6960
|
sendPings() {
|
|
6897
6961
|
this.stopPings();
|
|
@@ -6902,6 +6966,7 @@ class Recorder extends util_Despot {
|
|
|
6902
6966
|
}
|
|
6903
6967
|
stopPings() {
|
|
6904
6968
|
if (void 0 === this.pingInterval) return;
|
|
6969
|
+
this.options.logger.debug("Recorder: stopping pings...");
|
|
6905
6970
|
window.clearInterval(this.pingInterval);
|
|
6906
6971
|
this.pingInterval = void 0;
|
|
6907
6972
|
}
|
|
@@ -7053,7 +7118,7 @@ class Recorder extends util_Despot {
|
|
|
7053
7118
|
});
|
|
7054
7119
|
}
|
|
7055
7120
|
shouldIgnoreConnectionFailure() {
|
|
7056
|
-
return this.connectionFailed || this.connected || Boolean(this.unloaded);
|
|
7121
|
+
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
|
|
7057
7122
|
}
|
|
7058
7123
|
calculateFrameProgress() {
|
|
7059
7124
|
return `${(this.confirmedFrameNumber / (this.framesCount || 1) * 100).toFixed(2)}%`;
|
|
@@ -7259,28 +7324,7 @@ class Recorder extends util_Despot {
|
|
|
7259
7324
|
}
|
|
7260
7325
|
});
|
|
7261
7326
|
stream.on("error", (err)=>{
|
|
7262
|
-
|
|
7263
|
-
else this.lastSocketError = serializeError(err);
|
|
7264
|
-
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
7265
|
-
error: serializeError(err)
|
|
7266
|
-
});
|
|
7267
|
-
this.options.logger.debug(debugLine);
|
|
7268
|
-
if (!this.connected) return void this.handleConnectionFailure({
|
|
7269
|
-
url2Connect,
|
|
7270
|
-
cause: "error"
|
|
7271
|
-
});
|
|
7272
|
-
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
7273
|
-
cause: this.lastSocketError ?? err
|
|
7274
|
-
});
|
|
7275
|
-
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
7276
|
-
this.emit("ERROR", {
|
|
7277
|
-
err: error_createError({
|
|
7278
|
-
message: "WebSocket stream error",
|
|
7279
|
-
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
7280
|
-
options: this.options,
|
|
7281
|
-
exc: streamError
|
|
7282
|
-
})
|
|
7283
|
-
});
|
|
7327
|
+
this.handleStreamError(url2Connect, err);
|
|
7284
7328
|
});
|
|
7285
7329
|
stream.on("drain", ()=>{
|
|
7286
7330
|
this.handleStreamDrain(stream);
|
|
@@ -7621,6 +7665,7 @@ class Recorder extends util_Despot {
|
|
|
7621
7665
|
}
|
|
7622
7666
|
back(cb) {
|
|
7623
7667
|
this.emit("GOING_BACK");
|
|
7668
|
+
this.key = void 0;
|
|
7624
7669
|
this.recordingDimensions = void 0;
|
|
7625
7670
|
this.unloaded = false;
|
|
7626
7671
|
this.show();
|
|
@@ -7685,7 +7730,7 @@ class Recorder extends util_Despot {
|
|
|
7685
7730
|
this.stopTimeout = void 0;
|
|
7686
7731
|
}
|
|
7687
7732
|
validate() {
|
|
7688
|
-
return this.
|
|
7733
|
+
return Boolean(this.key) && void 0 === this.canvas;
|
|
7689
7734
|
}
|
|
7690
7735
|
isReady() {
|
|
7691
7736
|
return this.userMedia?.isReady();
|
|
@@ -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?:
|
|
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.
|
|
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
|
-
|
|
15246
|
+
connectSubmitButton() {
|
|
15239
15247
|
const submitButton = this.container.getSubmitButton();
|
|
15240
|
-
if (submitButton)
|
|
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
|
-
|
|
15244
|
-
if (submitButton)
|
|
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())
|
|
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);
|
|
@@ -19852,6 +19890,7 @@
|
|
|
19852
19890
|
this.options.logger.debug(debugLine);
|
|
19853
19891
|
const canReconnect = this.canReconnectAfterStreamClose();
|
|
19854
19892
|
this.connected = false;
|
|
19893
|
+
this.stopPings();
|
|
19855
19894
|
if (this.shouldIgnoreStreamClose()) return;
|
|
19856
19895
|
if (this.mediaTransmissionStarted) this.failInterruptedRecording();
|
|
19857
19896
|
else if (canReconnect) {
|
|
@@ -19868,7 +19907,37 @@
|
|
|
19868
19907
|
}, 0);
|
|
19869
19908
|
}
|
|
19870
19909
|
shouldIgnoreStreamClose() {
|
|
19871
|
-
return this.recordingTransportFailed || Boolean(this.unloaded);
|
|
19910
|
+
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
19911
|
+
}
|
|
19912
|
+
handleStreamError(url2Connect, err) {
|
|
19913
|
+
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
19914
|
+
else this.lastSocketError = serializeError(err);
|
|
19915
|
+
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
19916
|
+
error: serializeError(err)
|
|
19917
|
+
});
|
|
19918
|
+
this.options.logger.debug(debugLine);
|
|
19919
|
+
if (this.validate()) {
|
|
19920
|
+
this.connected = false;
|
|
19921
|
+
this.stopPings();
|
|
19922
|
+
this.discardSocket();
|
|
19923
|
+
return;
|
|
19924
|
+
}
|
|
19925
|
+
if (!this.connected) return void this.handleConnectionFailure({
|
|
19926
|
+
url2Connect,
|
|
19927
|
+
cause: "error"
|
|
19928
|
+
});
|
|
19929
|
+
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
19930
|
+
cause: this.lastSocketError ?? err
|
|
19931
|
+
});
|
|
19932
|
+
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
19933
|
+
this.emit("ERROR", {
|
|
19934
|
+
err: error_createError({
|
|
19935
|
+
message: "WebSocket stream error",
|
|
19936
|
+
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
19937
|
+
options: this.options,
|
|
19938
|
+
exc: streamError
|
|
19939
|
+
})
|
|
19940
|
+
});
|
|
19872
19941
|
}
|
|
19873
19942
|
sendPings() {
|
|
19874
19943
|
this.stopPings();
|
|
@@ -19879,6 +19948,7 @@
|
|
|
19879
19948
|
}
|
|
19880
19949
|
stopPings() {
|
|
19881
19950
|
if (void 0 === this.pingInterval) return;
|
|
19951
|
+
this.options.logger.debug("Recorder: stopping pings...");
|
|
19882
19952
|
window.clearInterval(this.pingInterval);
|
|
19883
19953
|
this.pingInterval = void 0;
|
|
19884
19954
|
}
|
|
@@ -20030,7 +20100,7 @@
|
|
|
20030
20100
|
});
|
|
20031
20101
|
}
|
|
20032
20102
|
shouldIgnoreConnectionFailure() {
|
|
20033
|
-
return this.connectionFailed || this.connected || Boolean(this.unloaded);
|
|
20103
|
+
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
|
|
20034
20104
|
}
|
|
20035
20105
|
calculateFrameProgress() {
|
|
20036
20106
|
return `${(this.confirmedFrameNumber / (this.framesCount || 1) * 100).toFixed(2)}%`;
|
|
@@ -20236,28 +20306,7 @@
|
|
|
20236
20306
|
}
|
|
20237
20307
|
});
|
|
20238
20308
|
stream.on("error", (err)=>{
|
|
20239
|
-
|
|
20240
|
-
else this.lastSocketError = serializeError(err);
|
|
20241
|
-
const debugLine = string_summarize(`${PIPE_SYMBOL}Stream *error* event emitted:`, {
|
|
20242
|
-
error: serializeError(err)
|
|
20243
|
-
});
|
|
20244
|
-
this.options.logger.debug(debugLine);
|
|
20245
|
-
if (!this.connected) return void this.handleConnectionFailure({
|
|
20246
|
-
url2Connect,
|
|
20247
|
-
cause: "error"
|
|
20248
|
-
});
|
|
20249
|
-
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
20250
|
-
cause: this.lastSocketError ?? err
|
|
20251
|
-
});
|
|
20252
|
-
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
20253
|
-
this.emit("ERROR", {
|
|
20254
|
-
err: error_createError({
|
|
20255
|
-
message: "WebSocket stream error",
|
|
20256
|
-
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
20257
|
-
options: this.options,
|
|
20258
|
-
exc: streamError
|
|
20259
|
-
})
|
|
20260
|
-
});
|
|
20309
|
+
this.handleStreamError(url2Connect, err);
|
|
20261
20310
|
});
|
|
20262
20311
|
stream.on("drain", ()=>{
|
|
20263
20312
|
this.handleStreamDrain(stream);
|
|
@@ -20598,6 +20647,7 @@
|
|
|
20598
20647
|
}
|
|
20599
20648
|
back(cb) {
|
|
20600
20649
|
this.emit("GOING_BACK");
|
|
20650
|
+
this.key = void 0;
|
|
20601
20651
|
this.recordingDimensions = void 0;
|
|
20602
20652
|
this.unloaded = false;
|
|
20603
20653
|
this.show();
|
|
@@ -20662,7 +20712,7 @@
|
|
|
20662
20712
|
this.stopTimeout = void 0;
|
|
20663
20713
|
}
|
|
20664
20714
|
validate() {
|
|
20665
|
-
return this.
|
|
20715
|
+
return Boolean(this.key) && void 0 === this.canvas;
|
|
20666
20716
|
}
|
|
20667
20717
|
isReady() {
|
|
20668
20718
|
return this.userMedia?.isReady();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videomail-client",
|
|
3
|
-
"version": "16.0.
|
|
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": "
|
|
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",
|