videomail-client 16.5.0 → 16.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -165,7 +165,7 @@ Returns a `Promise<boolean>`: `true` if camera recovery succeeds.
165
165
  - Shares pending recovery across duplicate calls.
166
166
  - Returns `false` without changes if the client is unbuilt, has an unsent preview, is encoding, or is submitting.
167
167
 
168
- Recovery keeps frame dimensions, frame/sample counts, and the audio sample rate. It does **not** resume recording or a countdown on its own. The user chooses Resume, which waits for recovery before sending more frames.
168
+ Recovery keeps frame dimensions, frame/sample counts, and the audio sample rate. It does **not** resume recording or a countdown on its own. The user chooses Resume, which waits for recovery before sending more frames or restarting a paused countdown.
169
169
 
170
170
  With automatic pause enabled, hiding the page pauses recording. Returning to it recovers the camera. Native apps should call `pause()` when backgrounded and `recoverCamera()` when foregrounded. Wire these into your app's lifecycle; no Capacitor dependency is needed. Use recovery, not `startOver()`, to keep a paused take.
171
171
 
@@ -5987,6 +5987,7 @@ var __webpack_modules__ = {
5987
5987
  var has = Object.prototype.hasOwnProperty;
5988
5988
  var isArray = Array.isArray;
5989
5989
  var getPrototypeOf = Object.getPrototypeOf;
5990
+ var fromCharCode = String.fromCharCode;
5990
5991
  var isPlainContainer = function(value) {
5991
5992
  if (!value || 'object' != typeof value) return false;
5992
5993
  if (isArray(value)) return true;
@@ -6059,7 +6060,12 @@ var __webpack_modules__ = {
6059
6060
  };
6060
6061
  var interpretNumericEntities = function(str) {
6061
6062
  return str.replace(/&#(\d+);/g, function($0, numberStr) {
6062
- return String.fromCharCode(parseInt(numberStr, 10));
6063
+ var c = parseInt(numberStr, 10);
6064
+ if (c > 0xFFFF && c <= 0x10FFFF) {
6065
+ c -= 0x10000;
6066
+ return fromCharCode(0xD800 | c >> 10, 0xDC00 | 0x3FF & c);
6067
+ }
6068
+ return fromCharCode(c);
6063
6069
  });
6064
6070
  };
6065
6071
  var parseArrayValue = function(val, options, currentArrayLength) {
@@ -6131,6 +6137,9 @@ var __webpack_modules__ = {
6131
6137
  }
6132
6138
  return obj;
6133
6139
  };
6140
+ var stripWrappingBrackets = function(key) {
6141
+ return '[' === key.charAt(0) && ']' === key.charAt(key.length - 1) ? key.slice(1, -1) : key;
6142
+ };
6134
6143
  var parseObject = function(chain, val, options, valuesParsed) {
6135
6144
  var currentArrayLength = 0;
6136
6145
  if (chain.length > 0 && '[]' === chain[chain.length - 1]) {
@@ -6146,7 +6155,7 @@ var __webpack_modules__ = {
6146
6155
  obj = options.plainObjects ? {
6147
6156
  __proto__: null
6148
6157
  } : {};
6149
- var cleanRoot = '[' === root.charAt(0) && ']' === root.charAt(root.length - 1) ? root.slice(1, -1) : root;
6158
+ var cleanRoot = stripWrappingBrackets(root);
6150
6159
  var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/gi, '.') : cleanRoot;
6151
6160
  var index = parseInt(decodedRoot, 10);
6152
6161
  var isValidArrayIndex = !isNaN(index) && root !== decodedRoot && String(index) === decodedRoot && index >= 0 && options.parseArrays;
@@ -6159,9 +6168,7 @@ var __webpack_modules__ = {
6159
6168
  obj[index] = leaf;
6160
6169
  utils.markOverflow(obj, index);
6161
6170
  } else if ('__proto__' !== decodedRoot) obj[decodedRoot] = leaf;
6162
- } else obj = {
6163
- 0: leaf
6164
- };
6171
+ } else obj[0] = leaf;
6165
6172
  }
6166
6173
  leaf = obj;
6167
6174
  }
@@ -6191,7 +6198,7 @@ var __webpack_modules__ = {
6191
6198
  var splitKeyIntoSegments = function(originalKey, options) {
6192
6199
  var key = options.allowDots ? topLevelDotsToBrackets(originalKey) : originalKey;
6193
6200
  if (options.depth <= 0) {
6194
- if (!options.plainObjects && has.call(Object.prototype, key)) {
6201
+ if (!options.plainObjects && has.call(Object.prototype, stripWrappingBrackets(key))) {
6195
6202
  if (!options.allowPrototypes) return;
6196
6203
  }
6197
6204
  return [
@@ -6575,6 +6582,11 @@ var __webpack_modules__ = {
6575
6582
  }
6576
6583
  return max;
6577
6584
  };
6585
+ var countElements = function(arr) {
6586
+ var count = 0;
6587
+ for(var i = 0; i < arr.length; ++i)if (has.call(arr, i)) count += 1;
6588
+ return count;
6589
+ };
6578
6590
  var limitedPair = function(first, second, options) {
6579
6591
  var pair = [
6580
6592
  first,
@@ -6591,7 +6603,7 @@ var __webpack_modules__ = {
6591
6603
  if ('object' != typeof source && 'function' != typeof source) {
6592
6604
  if (isArray(target)) {
6593
6605
  var nextIndex = target.length;
6594
- if (options && 'number' == typeof options.arrayLimit && nextIndex >= options.arrayLimit) {
6606
+ if (options && 'number' == typeof options.arrayLimit && countElements(target) >= options.arrayLimit) {
6595
6607
  if (options.throwOnLimitExceeded) throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (1 === options.arrayLimit ? '' : 's') + ' allowed in an array.');
6596
6608
  return markOverflow(arrayToObject(target.concat(source), options), nextIndex);
6597
6609
  }
@@ -6649,7 +6661,7 @@ var __webpack_modules__ = {
6649
6661
  else target[target.length] = item;
6650
6662
  } else target[i] = item;
6651
6663
  });
6652
- if (options && 'number' == typeof options.arrayLimit && target.length > options.arrayLimit) {
6664
+ if (options && 'number' == typeof options.arrayLimit && countElements(target) > options.arrayLimit) {
6653
6665
  if (options.throwOnLimitExceeded) throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (1 === options.arrayLimit ? '' : 's') + ' allowed in an array.');
6654
6666
  return markOverflow(arrayToObject(target, options), target.length - 1);
6655
6667
  }
@@ -11126,7 +11138,7 @@ var __webpack_exports__ = {};
11126
11138
  var client = __webpack_require__("Q");
11127
11139
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11128
11140
  var package_namespaceObject = {
11129
- rE: "16.5.0"
11141
+ rE: "16.6.0"
11130
11142
  };
11131
11143
  function isAudioEnabled(options) {
11132
11144
  return Boolean(options.audio.enabled);
@@ -15198,6 +15210,17 @@ var __webpack_exports__ = {};
15198
15210
  enable(buttonsToo) {
15199
15211
  this.setDisabled(false, buttonsToo);
15200
15212
  }
15213
+ disableForSubmission() {
15214
+ for (const formControl of this.formElement.elements){
15215
+ this.submissionDisabledStates.set(formControl, formControl.hasAttribute("disabled"));
15216
+ formControl.setAttribute("disabled", "disabled");
15217
+ }
15218
+ }
15219
+ restoreAfterFailedSubmission() {
15220
+ for (const [formControl, wasDisabled] of this.submissionDisabledStates)if (wasDisabled) formControl.setAttribute("disabled", "disabled");
15221
+ else formControl.removeAttribute("disabled");
15222
+ this.submissionDisabledStates.clear();
15223
+ }
15201
15224
  build() {
15202
15225
  this.options.logger.debug("Form: build()");
15203
15226
  this.keyInput = this.formElement.querySelector(`input[name="${this.options.selectors.keyInputName}"]`);
@@ -15363,7 +15386,7 @@ var __webpack_exports__ = {};
15363
15386
  html_showElement(this.formElement);
15364
15387
  }
15365
15388
  constructor(container, formElement, options){
15366
- 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)=>{
15389
+ 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, "submissionDisabledStates", new Map()), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
15367
15390
  this.container.validate(event);
15368
15391
  }), form_define_property(this, "handleSubmit", (event)=>{
15369
15392
  if (!event.defaultPrevented) this.doTheSubmit(event);
@@ -18162,8 +18185,13 @@ var __webpack_exports__ = {};
18162
18185
  this.hide();
18163
18186
  }
18164
18187
  onUserMediaReady(params) {
18165
- this.onFormReady();
18188
+ this.onFormReady(params);
18166
18189
  html_showElement(this.buttonsElement);
18190
+ if (params.recovering && params.paused) {
18191
+ html_hideElement(this.recordButton);
18192
+ html_disableElement(this.recordButton);
18193
+ return;
18194
+ }
18167
18195
  html_showElement(this.audioOnRadioPair);
18168
18196
  html_showElement(this.audioOffRadioPair);
18169
18197
  if (html_isShown(this.recordButton) && !params.recordWhenReady) html_enableElement(this.recordButton);
@@ -18886,8 +18914,8 @@ var __webpack_exports__ = {};
18886
18914
  this.on("PAUSED", ()=>{
18887
18915
  this.pauseRecording();
18888
18916
  });
18889
- this.on("ERROR", ()=>{
18890
- this.onResetting();
18917
+ this.on("ERROR", ({ recoverable })=>{
18918
+ if (!recoverable) this.onResetting();
18891
18919
  });
18892
18920
  this.on("RESETTING", ()=>{
18893
18921
  this.onResetting();
@@ -19078,12 +19106,12 @@ var __webpack_exports__ = {};
19078
19106
  this.entertainTimeoutId = void 0;
19079
19107
  this.entertaining = false;
19080
19108
  }
19081
- error(err) {
19109
+ error(err, blocking = true) {
19082
19110
  const message = err.message;
19083
19111
  const explanation = err.explanation;
19084
19112
  if (!message) this.options.logger.debug(`Weird empty error message generated for error ${string_pretty(err)}`);
19085
19113
  this.notify(message, explanation, {
19086
- blocking: true,
19114
+ blocking,
19087
19115
  problem: true,
19088
19116
  classList: err.getClassList(),
19089
19117
  removeDimensions: util_getBrowser(this.options).isMobile()
@@ -20537,6 +20565,10 @@ var __webpack_exports__ = {};
20537
20565
  if (this.streamBackpressured) this.resumeAfterDrain = true;
20538
20566
  else loop.start();
20539
20567
  }
20568
+ resumeMediaForCountdown() {
20569
+ var _this_userMedia;
20570
+ null == (_this_userMedia = this.userMedia) || _this_userMedia.resume();
20571
+ }
20540
20572
  onFlushed(opts) {
20541
20573
  const frameNumber = opts.frameNumber;
20542
20574
  if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
@@ -22166,6 +22198,32 @@ var __webpack_exports__ = {};
22166
22198
  }
22167
22199
  }
22168
22200
  const visuals_replay = Replay;
22201
+ function visuals_asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
22202
+ try {
22203
+ var info = gen[key](arg);
22204
+ var value = info.value;
22205
+ } catch (error) {
22206
+ reject(error);
22207
+ return;
22208
+ }
22209
+ if (info.done) resolve(value);
22210
+ else Promise.resolve(value).then(_next, _throw);
22211
+ }
22212
+ function visuals_async_to_generator(fn) {
22213
+ return function() {
22214
+ var self1 = this, args = arguments;
22215
+ return new Promise(function(resolve, reject) {
22216
+ var gen = fn.apply(self1, args);
22217
+ function _next(value) {
22218
+ visuals_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22219
+ }
22220
+ function _throw(err) {
22221
+ visuals_asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
22222
+ }
22223
+ _next(void 0);
22224
+ });
22225
+ };
22226
+ }
22169
22227
  function visuals_define_property(obj, key, value) {
22170
22228
  if (key in obj) Object.defineProperty(obj, key, {
22171
22229
  value: value,
@@ -22360,21 +22418,40 @@ var __webpack_exports__ = {};
22360
22418
  this.clearCameraRecoveryNotice();
22361
22419
  return this.recorder.recoverCamera();
22362
22420
  }
22363
- resume() {
22364
- this.clearCameraRecoveryNotice();
22365
- if (this.recorderInsides.isCountingDown()) {
22421
+ canResumeCountdown(restored) {
22422
+ return restored && Boolean(this.recorderInsides.isCountingDown());
22423
+ }
22424
+ resumeCountdown() {
22425
+ return visuals_async_to_generator(function*() {
22426
+ const restored = yield this.recoverCamera();
22427
+ if (!this.canResumeCountdown(restored)) return;
22428
+ this.recorder.resumeMediaForCountdown();
22366
22429
  this.recorderInsides.resumeCountdown();
22367
22430
  this.recorderInsides.hidePause();
22368
- } else {
22369
- this.recorder.resume();
22370
- if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
22371
- }
22431
+ }).call(this);
22432
+ }
22433
+ resume() {
22434
+ return visuals_async_to_generator(function*() {
22435
+ this.clearCameraRecoveryNotice();
22436
+ if (this.recorderInsides.isCountingDown()) yield this.resumeCountdown();
22437
+ else {
22438
+ this.recorder.resume();
22439
+ if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
22440
+ }
22441
+ }).call(this);
22442
+ }
22443
+ resumeFromControl() {
22444
+ this.resume().catch((exc)=>{
22445
+ this.emit("ERROR", {
22446
+ exc
22447
+ });
22448
+ });
22372
22449
  }
22373
22450
  pauseOrResume() {
22374
- if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
22451
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resumeFromControl();
22375
22452
  else if (this.isRecordable()) {
22376
22453
  if (this.isRecording()) this.pause();
22377
- else if (this.recorder.isPaused()) this.resume();
22454
+ else if (this.recorder.isPaused()) this.resumeFromControl();
22378
22455
  else if (this.recorder.isReady()) this.recorder.record();
22379
22456
  }
22380
22457
  }
@@ -22403,8 +22480,8 @@ var __webpack_exports__ = {};
22403
22480
  isPaused() {
22404
22481
  return this.recorder.isPaused();
22405
22482
  }
22406
- error(err) {
22407
- this.notifier.error(err);
22483
+ error(err, blocking = true) {
22484
+ this.notifier.error(err, blocking);
22408
22485
  }
22409
22486
  hide() {
22410
22487
  if (this.visualsElement) {
@@ -22553,6 +22630,7 @@ var __webpack_exports__ = {};
22553
22630
  }
22554
22631
  return target;
22555
22632
  }
22633
+ const INTERACTIVE_SPACE_TARGETS = "button, input, select, textarea, a[href], summary, label, audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link'], [role='checkbox'], [role='radio'], [role='switch'], [role='menuitem'], [role='tab'], [tabindex]:not([tabindex='-1'])";
22556
22634
  class Container extends util_Despot {
22557
22635
  buildChildren(playerOnly = false, parentElement) {
22558
22636
  const parentElementInfo = parentElement ? `, parentElement="${string_pretty(parentElement)}"` : "";
@@ -22627,9 +22705,9 @@ var __webpack_exports__ = {};
22627
22705
  if (params.exc.stack) this.options.logger.error(params.exc.stack);
22628
22706
  else if (params.exc.message) this.options.logger.error(params.exc.message);
22629
22707
  } else this.options.logger.error(params.exc);
22630
- if (this.options.displayErrors && params.err) this.visuals.error(params.err);
22631
- else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc);
22632
- else this.visuals.reset();
22708
+ if (this.options.displayErrors && params.err) this.visuals.error(params.err, !params.recoverable);
22709
+ else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc, !params.recoverable);
22710
+ else if (!params.recoverable) this.visuals.reset();
22633
22711
  }
22634
22712
  initEvents(playerOnly = false) {
22635
22713
  this.options.logger.debug(`Container: initEvents (playerOnly = ${playerOnly})`);
@@ -22647,7 +22725,10 @@ var __webpack_exports__ = {};
22647
22725
  }
22648
22726
  });
22649
22727
  if (this.options.enableSpace) {
22650
- if (!playerOnly) window.addEventListener("keydown", this.handleKeyDown);
22728
+ if (!playerOnly) {
22729
+ var _this_containerElement;
22730
+ null == (_this_containerElement = this.containerElement) || _this_containerElement.addEventListener("keydown", this.handleKeyDown);
22731
+ }
22651
22732
  }
22652
22733
  this.on("ERROR", (params)=>{
22653
22734
  this.processError(params);
@@ -22685,10 +22766,10 @@ var __webpack_exports__ = {};
22685
22766
  this.endWaiting();
22686
22767
  }
22687
22768
  removeBrowserEventListeners() {
22688
- var _this_stopListeningToVisibility, _this;
22769
+ var _this_containerElement, _this_stopListeningToVisibility, _this;
22689
22770
  window.removeEventListener("resize", this.handleWindowResize);
22690
22771
  window.removeEventListener("beforeunload", this.handleBeforeUnload);
22691
- window.removeEventListener("keydown", this.handleKeyDown);
22772
+ null == (_this_containerElement = this.containerElement) || _this_containerElement.removeEventListener("keydown", this.handleKeyDown);
22692
22773
  null == (_this_stopListeningToVisibility = (_this = this).stopListeningToVisibility) || _this_stopListeningToVisibility.call(_this);
22693
22774
  this.stopListeningToVisibility = void 0;
22694
22775
  }
@@ -22938,6 +23019,7 @@ var __webpack_exports__ = {};
22938
23019
  return container_async_to_generator(function*() {
22939
23020
  let response;
22940
23021
  let hostResponse;
23022
+ let submissionFailed = false;
22941
23023
  const videomailKey = formData[this.options.selectors.keyInputName];
22942
23024
  try {
22943
23025
  const hasVideomailKey = Boolean(videomailKey);
@@ -22948,7 +23030,10 @@ var __webpack_exports__ = {};
22948
23030
  ].filter(Boolean).join(": ");
22949
23031
  this.options.logger.debug(`Container: submitAll(${output})`);
22950
23032
  this.beginWaiting();
22951
- if (this.options.disableFormWhenSubmitting) this.disableForm(true);
23033
+ if (this.options.disableFormWhenSubmitting) {
23034
+ var _this_form;
23035
+ null == (_this_form = this.form) || _this_form.disableForSubmission();
23036
+ }
22952
23037
  this.emit("SUBMITTING");
22953
23038
  if (hasVideomailKey) {
22954
23039
  var _this_getPendingHostSubmissionResponse;
@@ -22977,15 +23062,21 @@ var __webpack_exports__ = {};
22977
23062
  });
22978
23063
  }
22979
23064
  } catch (exc) {
23065
+ submissionFailed = true;
22980
23066
  const err = error_createError({
22981
23067
  exc,
22982
23068
  options: this.options
22983
23069
  });
22984
23070
  this.emit("ERROR", {
22985
- err
23071
+ err,
23072
+ recoverable: true
22986
23073
  });
22987
23074
  } finally{
22988
23075
  const finalResponse = null != hostResponse ? hostResponse : response;
23076
+ if (submissionFailed && this.options.disableFormWhenSubmitting) {
23077
+ var _this_form1;
23078
+ null == (_this_form1 = this.form) || _this_form1.restoreAfterFailedSubmission();
23079
+ }
22989
23080
  if ((null == finalResponse ? void 0 : finalResponse.text) && "text/html" === finalResponse.type) document.body.innerHTML = purify_default.sanitize(finalResponse.text);
22990
23081
  this.endWaiting();
22991
23082
  }
@@ -23045,7 +23136,11 @@ var __webpack_exports__ = {};
23045
23136
  this.visuals.record();
23046
23137
  }
23047
23138
  resume() {
23048
- this.visuals.resume();
23139
+ this.visuals.resume().catch((exc)=>{
23140
+ this.emit("ERROR", {
23141
+ exc
23142
+ });
23143
+ });
23049
23144
  }
23050
23145
  stop() {
23051
23146
  this.visuals.stop();
@@ -23065,14 +23160,16 @@ var __webpack_exports__ = {};
23065
23160
  e
23066
23161
  });
23067
23162
  }), container_define_property(this, "handleKeyDown", (e)=>{
23068
- const element = e.target;
23069
- const tagName = element.tagName;
23070
- const isEditable = element.isContentEditable || "true" === element.contentEditable;
23071
- if (!isEditable && tagName && "INPUT" !== tagName.toUpperCase() && "TEXTAREA" !== tagName.toUpperCase() && "Space" === e.code) {
23072
- e.preventDefault();
23073
- if (this.options.enablePause) this.visuals.pauseOrResume();
23074
- else this.visuals.recordOrStop();
23075
- }
23163
+ const containerElement = this.containerElement;
23164
+ if (!containerElement) return;
23165
+ const eventPath = e.composedPath();
23166
+ const targetContainerIndex = eventPath.findIndex((eventTarget)=>eventTarget instanceof Element && eventTarget.classList.contains(this.options.selectors.containerClass));
23167
+ const targetContainer = eventPath[targetContainerIndex];
23168
+ const isInteractiveTarget = eventPath.slice(0, targetContainerIndex + 1).some((eventTarget)=>eventTarget instanceof Element && eventTarget.matches(INTERACTIVE_SPACE_TARGETS));
23169
+ if (!eventPath.includes(containerElement) || targetContainer !== containerElement || isInteractiveTarget || e.defaultPrevented || e.repeat || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || "Space" !== e.code) return;
23170
+ e.preventDefault();
23171
+ if (this.options.enablePause) this.visuals.pauseOrResume();
23172
+ else this.visuals.recordOrStop();
23076
23173
  });
23077
23174
  this.visuals = new wrappers_visuals(this, options);
23078
23175
  this.buttons = new buttons(this, options);
package/dist/esm/index.js CHANGED
@@ -3620,7 +3620,7 @@ const constants = {
3620
3620
  }
3621
3621
  };
3622
3622
  var package_namespaceObject = {
3623
- rE: "16.5.0"
3623
+ rE: "16.6.0"
3624
3624
  };
3625
3625
  function isAudioEnabled(options) {
3626
3626
  return Boolean(options.audio.enabled);
@@ -4286,6 +4286,17 @@ class Form extends util_Despot {
4286
4286
  enable(buttonsToo) {
4287
4287
  this.setDisabled(false, buttonsToo);
4288
4288
  }
4289
+ disableForSubmission() {
4290
+ for (const formControl of this.formElement.elements){
4291
+ this.submissionDisabledStates.set(formControl, formControl.hasAttribute("disabled"));
4292
+ formControl.setAttribute("disabled", "disabled");
4293
+ }
4294
+ }
4295
+ restoreAfterFailedSubmission() {
4296
+ for (const [formControl, wasDisabled] of this.submissionDisabledStates)if (wasDisabled) formControl.setAttribute("disabled", "disabled");
4297
+ else formControl.removeAttribute("disabled");
4298
+ this.submissionDisabledStates.clear();
4299
+ }
4289
4300
  build() {
4290
4301
  this.options.logger.debug("Form: build()");
4291
4302
  this.keyInput = this.formElement.querySelector(`input[name="${this.options.selectors.keyInputName}"]`);
@@ -4446,7 +4457,7 @@ class Form extends util_Despot {
4446
4457
  html_showElement(this.formElement);
4447
4458
  }
4448
4459
  constructor(container, formElement, options){
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)=>{
4460
+ 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, "submissionDisabledStates", new Map()), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
4450
4461
  this.container.validate(event);
4451
4462
  }), form_define_property(this, "handleSubmit", (event)=>{
4452
4463
  if (!event.defaultPrevented) this.doTheSubmit(event);
@@ -5085,8 +5096,13 @@ class Buttons extends util_Despot {
5085
5096
  this.hide();
5086
5097
  }
5087
5098
  onUserMediaReady(params) {
5088
- this.onFormReady();
5099
+ this.onFormReady(params);
5089
5100
  html_showElement(this.buttonsElement);
5101
+ if (params.recovering && params.paused) {
5102
+ html_hideElement(this.recordButton);
5103
+ html_disableElement(this.recordButton);
5104
+ return;
5105
+ }
5090
5106
  html_showElement(this.audioOnRadioPair);
5091
5107
  html_showElement(this.audioOffRadioPair);
5092
5108
  if (html_isShown(this.recordButton) && !params.recordWhenReady) html_enableElement(this.recordButton);
@@ -5789,8 +5805,8 @@ class RecorderInsides extends util_Despot {
5789
5805
  this.on("PAUSED", ()=>{
5790
5806
  this.pauseRecording();
5791
5807
  });
5792
- this.on("ERROR", ()=>{
5793
- this.onResetting();
5808
+ this.on("ERROR", ({ recoverable })=>{
5809
+ if (!recoverable) this.onResetting();
5794
5810
  });
5795
5811
  this.on("RESETTING", ()=>{
5796
5812
  this.onResetting();
@@ -5973,12 +5989,12 @@ class Notifier extends util_Despot {
5973
5989
  this.entertainTimeoutId = void 0;
5974
5990
  this.entertaining = false;
5975
5991
  }
5976
- error(err) {
5992
+ error(err, blocking = true) {
5977
5993
  const message = err.message;
5978
5994
  const explanation = err.explanation;
5979
5995
  if (!message) this.options.logger.debug(`Weird empty error message generated for error ${string_pretty(err)}`);
5980
5996
  this.notify(message, explanation, {
5981
- blocking: true,
5997
+ blocking,
5982
5998
  problem: true,
5983
5999
  classList: err.getClassList(),
5984
6000
  removeDimensions: util_getBrowser(this.options).isMobile()
@@ -7332,6 +7348,9 @@ class RecorderCapture extends media {
7332
7348
  if (this.streamBackpressured) this.resumeAfterDrain = true;
7333
7349
  else loop.start();
7334
7350
  }
7351
+ resumeMediaForCountdown() {
7352
+ this.userMedia?.resume();
7353
+ }
7335
7354
  onFlushed(opts) {
7336
7355
  const frameNumber = opts.frameNumber;
7337
7356
  if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
@@ -9009,21 +9028,36 @@ class Visuals extends util_Despot {
9009
9028
  this.clearCameraRecoveryNotice();
9010
9029
  return this.recorder.recoverCamera();
9011
9030
  }
9012
- resume() {
9031
+ canResumeCountdown(restored) {
9032
+ return restored && Boolean(this.recorderInsides.isCountingDown());
9033
+ }
9034
+ async resumeCountdown() {
9035
+ const restored = await this.recoverCamera();
9036
+ if (!this.canResumeCountdown(restored)) return;
9037
+ this.recorder.resumeMediaForCountdown();
9038
+ this.recorderInsides.resumeCountdown();
9039
+ this.recorderInsides.hidePause();
9040
+ }
9041
+ async resume() {
9013
9042
  this.clearCameraRecoveryNotice();
9014
- if (this.recorderInsides.isCountingDown()) {
9015
- this.recorderInsides.resumeCountdown();
9016
- this.recorderInsides.hidePause();
9017
- } else {
9043
+ if (this.recorderInsides.isCountingDown()) await this.resumeCountdown();
9044
+ else {
9018
9045
  this.recorder.resume();
9019
9046
  if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
9020
9047
  }
9021
9048
  }
9049
+ resumeFromControl() {
9050
+ this.resume().catch((exc)=>{
9051
+ this.emit("ERROR", {
9052
+ exc
9053
+ });
9054
+ });
9055
+ }
9022
9056
  pauseOrResume() {
9023
- if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
9057
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resumeFromControl();
9024
9058
  else if (this.isRecordable()) {
9025
9059
  if (this.isRecording()) this.pause();
9026
- else if (this.recorder.isPaused()) this.resume();
9060
+ else if (this.recorder.isPaused()) this.resumeFromControl();
9027
9061
  else if (this.recorder.isReady()) this.recorder.record();
9028
9062
  }
9029
9063
  }
@@ -9052,8 +9086,8 @@ class Visuals extends util_Despot {
9052
9086
  isPaused() {
9053
9087
  return this.recorder.isPaused();
9054
9088
  }
9055
- error(err) {
9056
- this.notifier.error(err);
9089
+ error(err, blocking = true) {
9090
+ this.notifier.error(err, blocking);
9057
9091
  }
9058
9092
  hide() {
9059
9093
  if (this.visualsElement) {
@@ -9162,6 +9196,7 @@ function container_define_property(obj, key, value) {
9162
9196
  else obj[key] = value;
9163
9197
  return obj;
9164
9198
  }
9199
+ const INTERACTIVE_SPACE_TARGETS = "button, input, select, textarea, a[href], summary, label, audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link'], [role='checkbox'], [role='radio'], [role='switch'], [role='menuitem'], [role='tab'], [tabindex]:not([tabindex='-1'])";
9165
9200
  class Container extends util_Despot {
9166
9201
  buildChildren(playerOnly = false, parentElement) {
9167
9202
  const parentElementInfo = parentElement ? `, parentElement="${string_pretty(parentElement)}"` : "";
@@ -9233,9 +9268,9 @@ class Container extends util_Despot {
9233
9268
  if (params.exc.stack) this.options.logger.error(params.exc.stack);
9234
9269
  else if (params.exc.message) this.options.logger.error(params.exc.message);
9235
9270
  } else this.options.logger.error(params.exc);
9236
- if (this.options.displayErrors && params.err) this.visuals.error(params.err);
9237
- else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc);
9238
- else this.visuals.reset();
9271
+ if (this.options.displayErrors && params.err) this.visuals.error(params.err, !params.recoverable);
9272
+ else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc, !params.recoverable);
9273
+ else if (!params.recoverable) this.visuals.reset();
9239
9274
  }
9240
9275
  initEvents(playerOnly = false) {
9241
9276
  this.options.logger.debug(`Container: initEvents (playerOnly = ${playerOnly})`);
@@ -9253,7 +9288,7 @@ class Container extends util_Despot {
9253
9288
  }
9254
9289
  });
9255
9290
  if (this.options.enableSpace) {
9256
- if (!playerOnly) window.addEventListener("keydown", this.handleKeyDown);
9291
+ if (!playerOnly) this.containerElement?.addEventListener("keydown", this.handleKeyDown);
9257
9292
  }
9258
9293
  this.on("ERROR", (params)=>{
9259
9294
  this.processError(params);
@@ -9293,7 +9328,7 @@ class Container extends util_Despot {
9293
9328
  removeBrowserEventListeners() {
9294
9329
  window.removeEventListener("resize", this.handleWindowResize);
9295
9330
  window.removeEventListener("beforeunload", this.handleBeforeUnload);
9296
- window.removeEventListener("keydown", this.handleKeyDown);
9331
+ this.containerElement?.removeEventListener("keydown", this.handleKeyDown);
9297
9332
  this.stopListeningToVisibility?.();
9298
9333
  this.stopListeningToVisibility = void 0;
9299
9334
  }
@@ -9532,6 +9567,7 @@ class Container extends util_Despot {
9532
9567
  async submitAll(formData, method, url, hasHostAction = false) {
9533
9568
  let response;
9534
9569
  let hostResponse;
9570
+ let submissionFailed = false;
9535
9571
  const videomailKey = formData[this.options.selectors.keyInputName];
9536
9572
  try {
9537
9573
  const hasVideomailKey = Boolean(videomailKey);
@@ -9542,7 +9578,7 @@ class Container extends util_Despot {
9542
9578
  ].filter(Boolean).join(": ");
9543
9579
  this.options.logger.debug(`Container: submitAll(${output})`);
9544
9580
  this.beginWaiting();
9545
- if (this.options.disableFormWhenSubmitting) this.disableForm(true);
9581
+ if (this.options.disableFormWhenSubmitting) this.form?.disableForSubmission();
9546
9582
  this.emit("SUBMITTING");
9547
9583
  if (hasVideomailKey) {
9548
9584
  response = this.getPendingHostSubmissionResponse(videomailKey) ?? await this.submitVideomail(formData, method);
@@ -9572,15 +9608,18 @@ class Container extends util_Despot {
9572
9608
  });
9573
9609
  }
9574
9610
  } catch (exc) {
9611
+ submissionFailed = true;
9575
9612
  const err = error_createError({
9576
9613
  exc,
9577
9614
  options: this.options
9578
9615
  });
9579
9616
  this.emit("ERROR", {
9580
- err
9617
+ err,
9618
+ recoverable: true
9581
9619
  });
9582
9620
  } finally{
9583
9621
  const finalResponse = hostResponse ?? response;
9622
+ if (submissionFailed && this.options.disableFormWhenSubmitting) this.form?.restoreAfterFailedSubmission();
9584
9623
  if (finalResponse?.text && "text/html" === finalResponse.type) document.body.innerHTML = dompurify.sanitize(finalResponse.text);
9585
9624
  this.endWaiting();
9586
9625
  }
@@ -9634,7 +9673,11 @@ class Container extends util_Despot {
9634
9673
  this.visuals.record();
9635
9674
  }
9636
9675
  resume() {
9637
- this.visuals.resume();
9676
+ this.visuals.resume().catch((exc)=>{
9677
+ this.emit("ERROR", {
9678
+ exc
9679
+ });
9680
+ });
9638
9681
  }
9639
9682
  stop() {
9640
9683
  this.visuals.stop();
@@ -9654,14 +9697,16 @@ class Container extends util_Despot {
9654
9697
  e
9655
9698
  });
9656
9699
  }), container_define_property(this, "handleKeyDown", (e)=>{
9657
- const element = e.target;
9658
- const tagName = element.tagName;
9659
- const isEditable = element.isContentEditable || "true" === element.contentEditable;
9660
- if (!isEditable && tagName && "INPUT" !== tagName.toUpperCase() && "TEXTAREA" !== tagName.toUpperCase() && "Space" === e.code) {
9661
- e.preventDefault();
9662
- if (this.options.enablePause) this.visuals.pauseOrResume();
9663
- else this.visuals.recordOrStop();
9664
- }
9700
+ const containerElement = this.containerElement;
9701
+ if (!containerElement) return;
9702
+ const eventPath = e.composedPath();
9703
+ const targetContainerIndex = eventPath.findIndex((eventTarget)=>eventTarget instanceof Element && eventTarget.classList.contains(this.options.selectors.containerClass));
9704
+ const targetContainer = eventPath[targetContainerIndex];
9705
+ const isInteractiveTarget = eventPath.slice(0, targetContainerIndex + 1).some((eventTarget)=>eventTarget instanceof Element && eventTarget.matches(INTERACTIVE_SPACE_TARGETS));
9706
+ if (!eventPath.includes(containerElement) || targetContainer !== containerElement || isInteractiveTarget || e.defaultPrevented || e.repeat || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || "Space" !== e.code) return;
9707
+ e.preventDefault();
9708
+ if (this.options.enablePause) this.visuals.pauseOrResume();
9709
+ else this.visuals.recordOrStop();
9665
9710
  });
9666
9711
  this.visuals = new wrappers_visuals(this, options);
9667
9712
  this.buttons = new buttons(this, options);
@@ -14,6 +14,7 @@ export interface VideomailCameraRecoveryFailedParams {
14
14
  export interface VideomailErrorParams {
15
15
  exc?: unknown;
16
16
  err?: VideomailError;
17
+ recoverable?: boolean | undefined;
17
18
  }
18
19
  export interface VideomailRecordingParams {
19
20
  framesCount: number;
@@ -18,6 +18,7 @@ declare class Form extends Despot {
18
18
  private submitButtonFormAttribute;
19
19
  private generatedFormId;
20
20
  private isSubmitting;
21
+ private readonly submissionDisabledStates;
21
22
  private readonly FORM_FIELDS;
22
23
  constructor(container: Container, formElement: HTMLFormElement, options: VideomailClientOptions);
23
24
  private readonly validate;
@@ -406,6 +407,8 @@ declare class Form extends Despot {
406
407
  private getRegisteredFormElements;
407
408
  disable(buttonsToo: boolean): void;
408
409
  enable(buttonsToo: boolean): void;
410
+ disableForSubmission(): void;
411
+ restoreAfterFailedSubmission(): void;
409
412
  build(): void;
410
413
  private removeAllInputListeners;
411
414
  private hideSubmitButton;
@@ -31,7 +31,7 @@ declare class Notifier extends Despot {
31
31
  private show;
32
32
  private runEntertainment;
33
33
  private cancelEntertainment;
34
- error(err: VideomailError): void;
34
+ error(err: VideomailError, blocking?: boolean): void;
35
35
  private getMessageElement;
36
36
  private setMessage;
37
37
  private setExplanation;
@@ -66,6 +66,7 @@ declare abstract class RecorderCapture extends RecorderMedia {
66
66
  validate(): boolean;
67
67
  pause(params?: PauseParams): void;
68
68
  resume(): void;
69
+ resumeMediaForCountdown(): void;
69
70
  private onFlushed;
70
71
  private draw;
71
72
  private createLoop;
@@ -40,7 +40,10 @@ declare class Visuals extends Despot {
40
40
  }): void;
41
41
  private clearCameraRecoveryNotice;
42
42
  recoverCamera(): Promise<boolean>;
43
- resume(): void;
43
+ private canResumeCountdown;
44
+ private resumeCountdown;
45
+ resume(): Promise<void>;
46
+ private resumeFromControl;
44
47
  pauseOrResume(): void;
45
48
  recordOrStop(): void;
46
49
  getRecorder(): Recorder;
@@ -52,7 +55,7 @@ declare class Visuals extends Despot {
52
55
  } | undefined;
53
56
  getAudioSampleRate(): number | undefined;
54
57
  isPaused(): boolean | undefined;
55
- error(err: VideomailError): void;
58
+ error(err: VideomailError, blocking?: boolean): void;
56
59
  hide(): void;
57
60
  isHidden(): boolean;
58
61
  showVisuals(): void;
package/dist/umd/index.js CHANGED
@@ -5993,6 +5993,7 @@
5993
5993
  var has = Object.prototype.hasOwnProperty;
5994
5994
  var isArray = Array.isArray;
5995
5995
  var getPrototypeOf = Object.getPrototypeOf;
5996
+ var fromCharCode = String.fromCharCode;
5996
5997
  var isPlainContainer = function(value) {
5997
5998
  if (!value || 'object' != typeof value) return false;
5998
5999
  if (isArray(value)) return true;
@@ -6065,7 +6066,12 @@
6065
6066
  };
6066
6067
  var interpretNumericEntities = function(str) {
6067
6068
  return str.replace(/&#(\d+);/g, function($0, numberStr) {
6068
- return String.fromCharCode(parseInt(numberStr, 10));
6069
+ var c = parseInt(numberStr, 10);
6070
+ if (c > 0xFFFF && c <= 0x10FFFF) {
6071
+ c -= 0x10000;
6072
+ return fromCharCode(0xD800 | c >> 10, 0xDC00 | 0x3FF & c);
6073
+ }
6074
+ return fromCharCode(c);
6069
6075
  });
6070
6076
  };
6071
6077
  var parseArrayValue = function(val, options, currentArrayLength) {
@@ -6137,6 +6143,9 @@
6137
6143
  }
6138
6144
  return obj;
6139
6145
  };
6146
+ var stripWrappingBrackets = function(key) {
6147
+ return '[' === key.charAt(0) && ']' === key.charAt(key.length - 1) ? key.slice(1, -1) : key;
6148
+ };
6140
6149
  var parseObject = function(chain, val, options, valuesParsed) {
6141
6150
  var currentArrayLength = 0;
6142
6151
  if (chain.length > 0 && '[]' === chain[chain.length - 1]) {
@@ -6152,7 +6161,7 @@
6152
6161
  obj = options.plainObjects ? {
6153
6162
  __proto__: null
6154
6163
  } : {};
6155
- var cleanRoot = '[' === root.charAt(0) && ']' === root.charAt(root.length - 1) ? root.slice(1, -1) : root;
6164
+ var cleanRoot = stripWrappingBrackets(root);
6156
6165
  var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/gi, '.') : cleanRoot;
6157
6166
  var index = parseInt(decodedRoot, 10);
6158
6167
  var isValidArrayIndex = !isNaN(index) && root !== decodedRoot && String(index) === decodedRoot && index >= 0 && options.parseArrays;
@@ -6165,9 +6174,7 @@
6165
6174
  obj[index] = leaf;
6166
6175
  utils.markOverflow(obj, index);
6167
6176
  } else if ('__proto__' !== decodedRoot) obj[decodedRoot] = leaf;
6168
- } else obj = {
6169
- 0: leaf
6170
- };
6177
+ } else obj[0] = leaf;
6171
6178
  }
6172
6179
  leaf = obj;
6173
6180
  }
@@ -6197,7 +6204,7 @@
6197
6204
  var splitKeyIntoSegments = function(originalKey, options) {
6198
6205
  var key = options.allowDots ? topLevelDotsToBrackets(originalKey) : originalKey;
6199
6206
  if (options.depth <= 0) {
6200
- if (!options.plainObjects && has.call(Object.prototype, key)) {
6207
+ if (!options.plainObjects && has.call(Object.prototype, stripWrappingBrackets(key))) {
6201
6208
  if (!options.allowPrototypes) return;
6202
6209
  }
6203
6210
  return [
@@ -6581,6 +6588,11 @@
6581
6588
  }
6582
6589
  return max;
6583
6590
  };
6591
+ var countElements = function(arr) {
6592
+ var count = 0;
6593
+ for(var i = 0; i < arr.length; ++i)if (has.call(arr, i)) count += 1;
6594
+ return count;
6595
+ };
6584
6596
  var limitedPair = function(first, second, options) {
6585
6597
  var pair = [
6586
6598
  first,
@@ -6597,7 +6609,7 @@
6597
6609
  if ('object' != typeof source && 'function' != typeof source) {
6598
6610
  if (isArray(target)) {
6599
6611
  var nextIndex = target.length;
6600
- if (options && 'number' == typeof options.arrayLimit && nextIndex >= options.arrayLimit) {
6612
+ if (options && 'number' == typeof options.arrayLimit && countElements(target) >= options.arrayLimit) {
6601
6613
  if (options.throwOnLimitExceeded) throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (1 === options.arrayLimit ? '' : 's') + ' allowed in an array.');
6602
6614
  return markOverflow(arrayToObject(target.concat(source), options), nextIndex);
6603
6615
  }
@@ -6655,7 +6667,7 @@
6655
6667
  else target[target.length] = item;
6656
6668
  } else target[i] = item;
6657
6669
  });
6658
- if (options && 'number' == typeof options.arrayLimit && target.length > options.arrayLimit) {
6670
+ if (options && 'number' == typeof options.arrayLimit && countElements(target) > options.arrayLimit) {
6659
6671
  if (options.throwOnLimitExceeded) throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (1 === options.arrayLimit ? '' : 's') + ' allowed in an array.');
6660
6672
  return markOverflow(arrayToObject(target, options), target.length - 1);
6661
6673
  }
@@ -11132,7 +11144,7 @@
11132
11144
  var client = __webpack_require__("Q");
11133
11145
  var client_default = /*#__PURE__*/ __webpack_require__.n(client);
11134
11146
  var package_namespaceObject = {
11135
- rE: "16.5.0"
11147
+ rE: "16.6.0"
11136
11148
  };
11137
11149
  function isAudioEnabled(options) {
11138
11150
  return Boolean(options.audio.enabled);
@@ -15048,6 +15060,7 @@
15048
15060
  submitButtonFormAttribute = null;
15049
15061
  generatedFormId;
15050
15062
  isSubmitting = false;
15063
+ submissionDisabledStates = new Map();
15051
15064
  FORM_FIELDS = {};
15052
15065
  constructor(container, formElement, options){
15053
15066
  super("Form", options);
@@ -15172,6 +15185,17 @@
15172
15185
  enable(buttonsToo) {
15173
15186
  this.setDisabled(false, buttonsToo);
15174
15187
  }
15188
+ disableForSubmission() {
15189
+ for (const formControl of this.formElement.elements){
15190
+ this.submissionDisabledStates.set(formControl, formControl.hasAttribute("disabled"));
15191
+ formControl.setAttribute("disabled", "disabled");
15192
+ }
15193
+ }
15194
+ restoreAfterFailedSubmission() {
15195
+ for (const [formControl, wasDisabled] of this.submissionDisabledStates)if (wasDisabled) formControl.setAttribute("disabled", "disabled");
15196
+ else formControl.removeAttribute("disabled");
15197
+ this.submissionDisabledStates.clear();
15198
+ }
15175
15199
  build() {
15176
15200
  this.options.logger.debug("Form: build()");
15177
15201
  this.keyInput = this.formElement.querySelector(`input[name="${this.options.selectors.keyInputName}"]`);
@@ -18019,8 +18043,13 @@
18019
18043
  this.hide();
18020
18044
  }
18021
18045
  onUserMediaReady(params) {
18022
- this.onFormReady();
18046
+ this.onFormReady(params);
18023
18047
  html_showElement(this.buttonsElement);
18048
+ if (params.recovering && params.paused) {
18049
+ html_hideElement(this.recordButton);
18050
+ html_disableElement(this.recordButton);
18051
+ return;
18052
+ }
18024
18053
  html_showElement(this.audioOnRadioPair);
18025
18054
  html_showElement(this.audioOffRadioPair);
18026
18055
  if (html_isShown(this.recordButton) && !params.recordWhenReady) html_enableElement(this.recordButton);
@@ -18676,8 +18705,8 @@
18676
18705
  this.on("PAUSED", ()=>{
18677
18706
  this.pauseRecording();
18678
18707
  });
18679
- this.on("ERROR", ()=>{
18680
- this.onResetting();
18708
+ this.on("ERROR", ({ recoverable })=>{
18709
+ if (!recoverable) this.onResetting();
18681
18710
  });
18682
18711
  this.on("RESETTING", ()=>{
18683
18712
  this.onResetting();
@@ -18852,12 +18881,12 @@
18852
18881
  this.entertainTimeoutId = void 0;
18853
18882
  this.entertaining = false;
18854
18883
  }
18855
- error(err) {
18884
+ error(err, blocking = true) {
18856
18885
  const message = err.message;
18857
18886
  const explanation = err.explanation;
18858
18887
  if (!message) this.options.logger.debug(`Weird empty error message generated for error ${string_pretty(err)}`);
18859
18888
  this.notify(message, explanation, {
18860
- blocking: true,
18889
+ blocking,
18861
18890
  problem: true,
18862
18891
  classList: err.getClassList(),
18863
18892
  removeDimensions: util_getBrowser(this.options).isMobile()
@@ -20277,6 +20306,9 @@
20277
20306
  if (this.streamBackpressured) this.resumeAfterDrain = true;
20278
20307
  else loop.start();
20279
20308
  }
20309
+ resumeMediaForCountdown() {
20310
+ this.userMedia?.resume();
20311
+ }
20280
20312
  onFlushed(opts) {
20281
20313
  const frameNumber = opts.frameNumber;
20282
20314
  if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
@@ -21967,21 +21999,36 @@
21967
21999
  this.clearCameraRecoveryNotice();
21968
22000
  return this.recorder.recoverCamera();
21969
22001
  }
21970
- resume() {
22002
+ canResumeCountdown(restored) {
22003
+ return restored && Boolean(this.recorderInsides.isCountingDown());
22004
+ }
22005
+ async resumeCountdown() {
22006
+ const restored = await this.recoverCamera();
22007
+ if (!this.canResumeCountdown(restored)) return;
22008
+ this.recorder.resumeMediaForCountdown();
22009
+ this.recorderInsides.resumeCountdown();
22010
+ this.recorderInsides.hidePause();
22011
+ }
22012
+ async resume() {
21971
22013
  this.clearCameraRecoveryNotice();
21972
- if (this.recorderInsides.isCountingDown()) {
21973
- this.recorderInsides.resumeCountdown();
21974
- this.recorderInsides.hidePause();
21975
- } else {
22014
+ if (this.recorderInsides.isCountingDown()) await this.resumeCountdown();
22015
+ else {
21976
22016
  this.recorder.resume();
21977
22017
  if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
21978
22018
  }
21979
22019
  }
22020
+ resumeFromControl() {
22021
+ this.resume().catch((exc)=>{
22022
+ this.emit("ERROR", {
22023
+ exc
22024
+ });
22025
+ });
22026
+ }
21980
22027
  pauseOrResume() {
21981
- if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
22028
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resumeFromControl();
21982
22029
  else if (this.isRecordable()) {
21983
22030
  if (this.isRecording()) this.pause();
21984
- else if (this.recorder.isPaused()) this.resume();
22031
+ else if (this.recorder.isPaused()) this.resumeFromControl();
21985
22032
  else if (this.recorder.isReady()) this.recorder.record();
21986
22033
  }
21987
22034
  }
@@ -22010,8 +22057,8 @@
22010
22057
  isPaused() {
22011
22058
  return this.recorder.isPaused();
22012
22059
  }
22013
- error(err) {
22014
- this.notifier.error(err);
22060
+ error(err, blocking = true) {
22061
+ this.notifier.error(err, blocking);
22015
22062
  }
22016
22063
  hide() {
22017
22064
  if (this.visualsElement) {
@@ -22102,6 +22149,7 @@
22102
22149
  }
22103
22150
  }
22104
22151
  const wrappers_visuals = Visuals;
22152
+ const INTERACTIVE_SPACE_TARGETS = "button, input, select, textarea, a[href], summary, label, audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link'], [role='checkbox'], [role='radio'], [role='switch'], [role='menuitem'], [role='tab'], [tabindex]:not([tabindex='-1'])";
22105
22153
  class Container extends util_Despot {
22106
22154
  visibility = document_visibility_default()();
22107
22155
  htmlElement = document.querySelector("html");
@@ -22125,14 +22173,16 @@
22125
22173
  });
22126
22174
  };
22127
22175
  handleKeyDown = (e)=>{
22128
- const element = e.target;
22129
- const tagName = element.tagName;
22130
- const isEditable = element.isContentEditable || "true" === element.contentEditable;
22131
- if (!isEditable && tagName && "INPUT" !== tagName.toUpperCase() && "TEXTAREA" !== tagName.toUpperCase() && "Space" === e.code) {
22132
- e.preventDefault();
22133
- if (this.options.enablePause) this.visuals.pauseOrResume();
22134
- else this.visuals.recordOrStop();
22135
- }
22176
+ const containerElement = this.containerElement;
22177
+ if (!containerElement) return;
22178
+ const eventPath = e.composedPath();
22179
+ const targetContainerIndex = eventPath.findIndex((eventTarget)=>eventTarget instanceof Element && eventTarget.classList.contains(this.options.selectors.containerClass));
22180
+ const targetContainer = eventPath[targetContainerIndex];
22181
+ const isInteractiveTarget = eventPath.slice(0, targetContainerIndex + 1).some((eventTarget)=>eventTarget instanceof Element && eventTarget.matches(INTERACTIVE_SPACE_TARGETS));
22182
+ if (!eventPath.includes(containerElement) || targetContainer !== containerElement || isInteractiveTarget || e.defaultPrevented || e.repeat || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || "Space" !== e.code) return;
22183
+ e.preventDefault();
22184
+ if (this.options.enablePause) this.visuals.pauseOrResume();
22185
+ else this.visuals.recordOrStop();
22136
22186
  };
22137
22187
  constructor(options){
22138
22188
  super("Container", options);
@@ -22210,9 +22260,9 @@
22210
22260
  if (params.exc.stack) this.options.logger.error(params.exc.stack);
22211
22261
  else if (params.exc.message) this.options.logger.error(params.exc.message);
22212
22262
  } else this.options.logger.error(params.exc);
22213
- if (this.options.displayErrors && params.err) this.visuals.error(params.err);
22214
- else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc);
22215
- else this.visuals.reset();
22263
+ if (this.options.displayErrors && params.err) this.visuals.error(params.err, !params.recoverable);
22264
+ else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc, !params.recoverable);
22265
+ else if (!params.recoverable) this.visuals.reset();
22216
22266
  }
22217
22267
  initEvents(playerOnly = false) {
22218
22268
  this.options.logger.debug(`Container: initEvents (playerOnly = ${playerOnly})`);
@@ -22230,7 +22280,7 @@
22230
22280
  }
22231
22281
  });
22232
22282
  if (this.options.enableSpace) {
22233
- if (!playerOnly) window.addEventListener("keydown", this.handleKeyDown);
22283
+ if (!playerOnly) this.containerElement?.addEventListener("keydown", this.handleKeyDown);
22234
22284
  }
22235
22285
  this.on("ERROR", (params)=>{
22236
22286
  this.processError(params);
@@ -22270,7 +22320,7 @@
22270
22320
  removeBrowserEventListeners() {
22271
22321
  window.removeEventListener("resize", this.handleWindowResize);
22272
22322
  window.removeEventListener("beforeunload", this.handleBeforeUnload);
22273
- window.removeEventListener("keydown", this.handleKeyDown);
22323
+ this.containerElement?.removeEventListener("keydown", this.handleKeyDown);
22274
22324
  this.stopListeningToVisibility?.();
22275
22325
  this.stopListeningToVisibility = void 0;
22276
22326
  }
@@ -22509,6 +22559,7 @@
22509
22559
  async submitAll(formData, method, url, hasHostAction = false) {
22510
22560
  let response;
22511
22561
  let hostResponse;
22562
+ let submissionFailed = false;
22512
22563
  const videomailKey = formData[this.options.selectors.keyInputName];
22513
22564
  try {
22514
22565
  const hasVideomailKey = Boolean(videomailKey);
@@ -22519,7 +22570,7 @@
22519
22570
  ].filter(Boolean).join(": ");
22520
22571
  this.options.logger.debug(`Container: submitAll(${output})`);
22521
22572
  this.beginWaiting();
22522
- if (this.options.disableFormWhenSubmitting) this.disableForm(true);
22573
+ if (this.options.disableFormWhenSubmitting) this.form?.disableForSubmission();
22523
22574
  this.emit("SUBMITTING");
22524
22575
  if (hasVideomailKey) {
22525
22576
  response = this.getPendingHostSubmissionResponse(videomailKey) ?? await this.submitVideomail(formData, method);
@@ -22549,15 +22600,18 @@
22549
22600
  });
22550
22601
  }
22551
22602
  } catch (exc) {
22603
+ submissionFailed = true;
22552
22604
  const err = error_createError({
22553
22605
  exc,
22554
22606
  options: this.options
22555
22607
  });
22556
22608
  this.emit("ERROR", {
22557
- err
22609
+ err,
22610
+ recoverable: true
22558
22611
  });
22559
22612
  } finally{
22560
22613
  const finalResponse = hostResponse ?? response;
22614
+ if (submissionFailed && this.options.disableFormWhenSubmitting) this.form?.restoreAfterFailedSubmission();
22561
22615
  if (finalResponse?.text && "text/html" === finalResponse.type) document.body.innerHTML = purify_default.sanitize(finalResponse.text);
22562
22616
  this.endWaiting();
22563
22617
  }
@@ -22611,7 +22665,11 @@
22611
22665
  this.visuals.record();
22612
22666
  }
22613
22667
  resume() {
22614
- this.visuals.resume();
22668
+ this.visuals.resume().catch((exc)=>{
22669
+ this.emit("ERROR", {
22670
+ exc
22671
+ });
22672
+ });
22615
22673
  }
22616
22674
  stop() {
22617
22675
  this.visuals.stop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "videomail-client",
3
- "version": "16.5.0",
3
+ "version": "16.6.0",
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",
@@ -103,7 +103,7 @@
103
103
  "audit-ci": "7.1.0",
104
104
  "chromatic": "18.13.6",
105
105
  "cross-env": "10.1.0",
106
- "eslint": "10.13.0",
106
+ "eslint": "10.14.0",
107
107
  "eslint-import-resolver-typescript": "4.4.5",
108
108
  "eslint-plugin-de-morgan": "2.3.0",
109
109
  "eslint-plugin-depend": "1.5.0",
@@ -120,7 +120,7 @@
120
120
  "prettier-plugin-jsdoc": "1.8.1",
121
121
  "prettier-plugin-packagejson": "3.0.3",
122
122
  "prettier-plugin-sh": "0.20.2",
123
- "release-it": "21.1.1",
123
+ "release-it": "21.1.2",
124
124
  "storybook": "10.6.5",
125
125
  "storybook-html-rsbuild": "3.6.1",
126
126
  "type-fest": "5.10.0",