videomail-client 16.5.0 → 16.7.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/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
  }
@@ -7206,7 +7218,7 @@
7206
7218
  const qs = __webpack_require__("b");
7207
7219
  const RequestBase = __webpack_require__("d");
7208
7220
  const { isObject, mixin, hasOwn, isSafeKey } = __webpack_require__("Dp");
7209
- const ResponseBase = __webpack_require__("es");
7221
+ const ResponseBase = __webpack_require__("e");
7210
7222
  const Agent = __webpack_require__("pf");
7211
7223
  function noop() {}
7212
7224
  module1.exports = function(method, url) {
@@ -7920,7 +7932,7 @@
7920
7932
  }, this._responseTimeout);
7921
7933
  };
7922
7934
  },
7923
- es (module1, __unused_rspack_exports, __webpack_require__) {
7935
+ e (module1, __unused_rspack_exports, __webpack_require__) {
7924
7936
  "use strict";
7925
7937
  const utils = __webpack_require__("Dp");
7926
7938
  module1.exports = ResponseBase;
@@ -10631,7 +10643,7 @@
10631
10643
  return i[1];
10632
10644
  };
10633
10645
  },
10634
- R (module1) {
10646
+ RR (module1) {
10635
10647
  "use strict";
10636
10648
  var stylesInDOM = [];
10637
10649
  function getIndexByIdentifier(identifier) {
@@ -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.7.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}"]`);
@@ -15700,7 +15724,7 @@
15700
15724
  return newOptions;
15701
15725
  }
15702
15726
  const options_mergeWithDefaultOptions = mergeWithDefaultOptions;
15703
- var injectStylesIntoStyleTag = __webpack_require__("R");
15727
+ var injectStylesIntoStyleTag = __webpack_require__("RR");
15704
15728
  var injectStylesIntoStyleTag_default = /*#__PURE__*/ __webpack_require__.n(injectStylesIntoStyleTag);
15705
15729
  var styleDomAPI = __webpack_require__("Bd");
15706
15730
  var styleDomAPI_default = /*#__PURE__*/ __webpack_require__.n(styleDomAPI);
@@ -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()
@@ -19041,6 +19070,18 @@
19041
19070
  canvas_to_buffer_modern_r.atob = void 0;
19042
19071
  var websocket_stream_stream = __webpack_require__("U");
19043
19072
  var stream_default = /*#__PURE__*/ __webpack_require__.n(websocket_stream_stream);
19073
+ function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19074
+ return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19075
+ }
19076
+ const util_isAutomatedUserAgent = isAutomatedUserAgent;
19077
+ function getAutomationSignals() {
19078
+ const webdriver = navigator.webdriver;
19079
+ return {
19080
+ webdriver: true === webdriver,
19081
+ userAgentMatched: util_isAutomatedUserAgent()
19082
+ };
19083
+ }
19084
+ const util_getAutomationSignals = getAutomationSignals;
19044
19085
  function getEdgeCodes(value, amount, fromEnd) {
19045
19086
  const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
19046
19087
  const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
@@ -19068,15 +19109,16 @@
19068
19109
  if (languageList.length > 0) return languageList.join(",");
19069
19110
  return navigator.language || "none";
19070
19111
  }
19071
- function getAutomationSignals(probeBase, probeSimpleQuery) {
19112
+ function getDiagnosticSignals(probeBase, probeSimpleQuery) {
19072
19113
  const reasons = [];
19073
- if (navigator.webdriver) reasons.push("webdriver=true");
19074
- if (/HeadlessChrome|PhantomJS|puppeteer|playwright/iu.test(navigator.userAgent)) reasons.push("headless_ua_marker");
19114
+ const automation = util_getAutomationSignals();
19115
+ if (automation.webdriver) reasons.push("webdriver=true");
19116
+ if (automation.userAgentMatched) reasons.push("headless_ua_marker");
19075
19117
  if (0 === navigator.languages.length) reasons.push("no_languages");
19076
19118
  if ("ok" !== probeBase) reasons.push("probe_base_failed");
19077
19119
  if ("ok" !== probeSimpleQuery) reasons.push("probe_simple_query_failed");
19078
19120
  return {
19079
- looksAutomated: reasons.length > 0,
19121
+ looksAutomated: automation.webdriver || automation.userAgentMatched,
19080
19122
  reasons: reasons.length > 0 ? reasons.join(",") : "none"
19081
19123
  };
19082
19124
  }
@@ -19093,7 +19135,7 @@
19093
19135
  const probeBase = probeWebSocketConstructor("wss://videomail.io");
19094
19136
  const probeWithSimpleQuery = probeWebSocketConstructor("wss://videomail.io/?a=b");
19095
19137
  const languages = getLanguagesValue();
19096
- const automationSignals = getAutomationSignals(probeBase, probeWithSimpleQuery);
19138
+ const automationSignals = getDiagnosticSignals(probeBase, probeWithSimpleQuery);
19097
19139
  const diagnosticPairs = [
19098
19140
  `webdriver=${String(navigator.webdriver)}`,
19099
19141
  `wsCtorNative=${String(wsCtorNative)}`,
@@ -19120,10 +19162,6 @@
19120
19162
  };
19121
19163
  }
19122
19164
  const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
19123
- function isAutomatedUserAgent(userAgent = navigator.userAgent) {
19124
- return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
19125
- }
19126
- const util_isAutomatedUserAgent = isAutomatedUserAgent;
19127
19165
  const DEFAULT_MAX_QUEUED_BYTES = 1048576;
19128
19166
  class BoundedWriteQueue {
19129
19167
  maxBytes;
@@ -19317,6 +19355,9 @@
19317
19355
  explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
19318
19356
  options: this.options,
19319
19357
  cause: {
19358
+ transportPhase: "recording",
19359
+ mediaTransmissionStarted: true,
19360
+ automation: util_getAutomationSignals(),
19320
19361
  unconfirmedFrames,
19321
19362
  unconfirmedSamples
19322
19363
  }
@@ -19358,6 +19399,7 @@
19358
19399
  lastCloseEvent;
19359
19400
  lastSocketError;
19360
19401
  pingInterval;
19402
+ connectionOpenedAt;
19361
19403
  constructor(options){
19362
19404
  super(options);
19363
19405
  }
@@ -19386,15 +19428,32 @@
19386
19428
  this.resumeAfterDrain = false;
19387
19429
  this.reconnecting = true;
19388
19430
  this.initSocket();
19389
- } else if (this.shouldReportStreamClose()) window.setTimeout(()=>{
19390
- this.handleConnectionFailure({
19391
- url2Connect,
19392
- cause: "closed"
19393
- });
19394
- }, 0);
19431
+ } else if (this.shouldReportStreamClose()) {
19432
+ const stream = this.stream;
19433
+ window.setTimeout(()=>{
19434
+ if (this.stream === stream) this.handleConnectionFailure({
19435
+ url2Connect,
19436
+ cause: "closed"
19437
+ });
19438
+ }, 0);
19439
+ }
19395
19440
  }
19396
19441
  shouldIgnoreStreamClose() {
19397
- return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
19442
+ return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19443
+ }
19444
+ closeIdleConnection(reason) {
19445
+ if (this.mediaTransmissionStarted) return;
19446
+ this.options.logger.debug(string_summarize("Recorder: closing idle connection", {
19447
+ reason,
19448
+ connected: this.connected,
19449
+ connecting: this.connecting,
19450
+ visibilityState: document.visibilityState
19451
+ }));
19452
+ this.connected = this.connecting = this.reconnecting = false;
19453
+ this.clearConnectionTimeout();
19454
+ this.clearReconnectTimeout();
19455
+ this.stopPings();
19456
+ this.discardSocket();
19398
19457
  }
19399
19458
  handleStreamError(url2Connect, err) {
19400
19459
  if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
@@ -19403,28 +19462,20 @@
19403
19462
  error: serializeError(err)
19404
19463
  });
19405
19464
  this.options.logger.debug(debugLine);
19406
- if (this.validate()) {
19465
+ if (this.shouldIgnoreStreamClose()) {
19407
19466
  this.connected = false;
19408
19467
  this.stopPings();
19409
19468
  this.discardSocket();
19410
19469
  return;
19411
19470
  }
19412
- if (!this.connected) return void this.handleConnectionFailure({
19471
+ if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19472
+ this.reconnecting ||= this.canReconnectAfterStreamClose();
19473
+ this.connected = false;
19474
+ this.stopPings();
19475
+ this.handleConnectionFailure({
19413
19476
  url2Connect,
19414
19477
  cause: "error"
19415
19478
  });
19416
- const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
19417
- cause: this.lastSocketError ?? err
19418
- });
19419
- if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
19420
- this.emit("ERROR", {
19421
- err: error_createError({
19422
- message: "WebSocket stream error",
19423
- explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
19424
- options: this.options,
19425
- exc: streamError
19426
- })
19427
- });
19428
19479
  }
19429
19480
  sendPings() {
19430
19481
  this.stopPings();
@@ -19484,6 +19535,7 @@
19484
19535
  this.connectionFailed = true;
19485
19536
  this.connecting = false;
19486
19537
  this.reconnecting = false;
19538
+ const reconnectAttempts = this.reconnectAttempts;
19487
19539
  this.reconnectAttempts = 0;
19488
19540
  this.clearReconnectTimeout();
19489
19541
  this.clearConnectionTimeout();
@@ -19494,6 +19546,14 @@
19494
19546
  const socketError = this.lastSocketError;
19495
19547
  const contents = {
19496
19548
  cause,
19549
+ transportPhase: void 0 === this.connectionOpenedAt ? "initial" : "idle",
19550
+ mediaTransmissionStarted: this.mediaTransmissionStarted,
19551
+ recordingActive: this.recordingActive,
19552
+ framesCount: this.framesCount,
19553
+ samplesCount: this.samplesCount,
19554
+ reconnectAttempts,
19555
+ connectionOpenedAt: this.connectionOpenedAt,
19556
+ automation: util_getAutomationSignals(),
19497
19557
  closeCode: closeEvent?.code,
19498
19558
  closeReason: closeEvent?.reason,
19499
19559
  elapsedMs,
@@ -19535,7 +19595,7 @@
19535
19595
  });
19536
19596
  }
19537
19597
  shouldIgnoreConnectionFailure() {
19538
- return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
19598
+ return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
19539
19599
  }
19540
19600
  initSocket(cb) {
19541
19601
  if (this.connected) {
@@ -19545,7 +19605,8 @@
19545
19605
  }
19546
19606
  if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
19547
19607
  this.clearReconnectTimeout();
19548
- if (util_isAutomatedUserAgent()) {
19608
+ const automation = util_getAutomationSignals();
19609
+ if (automation.userAgentMatched) {
19549
19610
  this.connecting = false;
19550
19611
  this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
19551
19612
  return;
@@ -19579,6 +19640,7 @@
19579
19640
  try {
19580
19641
  nativeSocket = new WebSocket(url2Connect);
19581
19642
  nativeSocket.addEventListener("close", (event)=>{
19643
+ if (this.stream?.socket !== nativeSocket) return;
19582
19644
  this.lastCloseEvent = {
19583
19645
  code: event.code,
19584
19646
  reason: event.reason,
@@ -19586,6 +19648,7 @@
19586
19648
  };
19587
19649
  });
19588
19650
  nativeSocket.addEventListener("error", (event)=>{
19651
+ if (this.stream?.socket !== nativeSocket) return;
19589
19652
  this.lastSocketError = error_getEventDetails(event);
19590
19653
  });
19591
19654
  } catch (exc) {
@@ -19597,7 +19660,10 @@
19597
19660
  message,
19598
19661
  explanation,
19599
19662
  options: this.options,
19600
- exc
19663
+ exc,
19664
+ cause: {
19665
+ automation
19666
+ }
19601
19667
  });
19602
19668
  this.emit("ERROR", {
19603
19669
  err
@@ -19635,14 +19701,16 @@
19635
19701
  });
19636
19702
  }, connectionTimeoutMs);
19637
19703
  stream.on("close", ()=>{
19638
- this.handleStreamClose(url2Connect);
19704
+ if (this.stream === stream) this.handleStreamClose(url2Connect);
19639
19705
  });
19640
19706
  stream.on("connect", (args)=>{
19707
+ if (this.stream !== stream) return;
19641
19708
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
19642
19709
  this.clearConnectionTimeout();
19643
- const isClosing = this.stream?.socket.readyState === WebSocket.CLOSING;
19710
+ const isClosing = stream.socket.readyState === WebSocket.CLOSING;
19644
19711
  if (!this.connected && !isClosing && !this.unloaded) {
19645
19712
  this.connected = true;
19713
+ this.connectionOpenedAt = Date.now();
19646
19714
  this.connecting = this.unloaded = false;
19647
19715
  this.reconnecting = false;
19648
19716
  this.reconnectAttempts = 0;
@@ -19652,6 +19720,7 @@
19652
19720
  }
19653
19721
  });
19654
19722
  stream.on("data", (data)=>{
19723
+ if (this.stream !== stream) return;
19655
19724
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
19656
19725
  try {
19657
19726
  const command = JSON.parse(data.toString());
@@ -19670,10 +19739,10 @@
19670
19739
  }
19671
19740
  });
19672
19741
  stream.on("error", (err)=>{
19673
- this.handleStreamError(url2Connect, err);
19742
+ if (this.stream === stream) this.handleStreamError(url2Connect, err);
19674
19743
  });
19675
19744
  stream.on("drain", ()=>{
19676
- this.handleStreamDrain(stream);
19745
+ if (this.stream === stream) this.handleStreamDrain(stream);
19677
19746
  });
19678
19747
  stream.on("preend", (args)=>{
19679
19748
  this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
@@ -19795,6 +19864,7 @@
19795
19864
  const characteristics = this.userMedia?.getCharacteristics();
19796
19865
  this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
19797
19866
  const errorListeners = this.getListeners("ERROR");
19867
+ if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) this.closeIdleConnection("camera-access-failed");
19798
19868
  if (errorListeners?.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
19799
19869
  const videomailError = error_createError({
19800
19870
  err,
@@ -20277,6 +20347,9 @@
20277
20347
  if (this.streamBackpressured) this.resumeAfterDrain = true;
20278
20348
  else loop.start();
20279
20349
  }
20350
+ resumeMediaForCountdown() {
20351
+ this.userMedia?.resume();
20352
+ }
20280
20353
  onFlushed(opts) {
20281
20354
  const frameNumber = opts.frameNumber;
20282
20355
  if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
@@ -21436,6 +21509,7 @@
21436
21509
  this.cancelCameraRecovery();
21437
21510
  this.blocking = true;
21438
21511
  this.clearUserMediaTimeout();
21512
+ this.closeIdleConnection("blocking-error");
21439
21513
  });
21440
21514
  this.on("PREVIEW", ()=>{
21441
21515
  this.hide();
@@ -21967,21 +22041,36 @@
21967
22041
  this.clearCameraRecoveryNotice();
21968
22042
  return this.recorder.recoverCamera();
21969
22043
  }
21970
- resume() {
22044
+ canResumeCountdown(restored) {
22045
+ return restored && Boolean(this.recorderInsides.isCountingDown());
22046
+ }
22047
+ async resumeCountdown() {
22048
+ const restored = await this.recoverCamera();
22049
+ if (!this.canResumeCountdown(restored)) return;
22050
+ this.recorder.resumeMediaForCountdown();
22051
+ this.recorderInsides.resumeCountdown();
22052
+ this.recorderInsides.hidePause();
22053
+ }
22054
+ async resume() {
21971
22055
  this.clearCameraRecoveryNotice();
21972
- if (this.recorderInsides.isCountingDown()) {
21973
- this.recorderInsides.resumeCountdown();
21974
- this.recorderInsides.hidePause();
21975
- } else {
22056
+ if (this.recorderInsides.isCountingDown()) await this.resumeCountdown();
22057
+ else {
21976
22058
  this.recorder.resume();
21977
22059
  if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
21978
22060
  }
21979
22061
  }
22062
+ resumeFromControl() {
22063
+ this.resume().catch((exc)=>{
22064
+ this.emit("ERROR", {
22065
+ exc
22066
+ });
22067
+ });
22068
+ }
21980
22069
  pauseOrResume() {
21981
- if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resume();
22070
+ if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resumeFromControl();
21982
22071
  else if (this.isRecordable()) {
21983
22072
  if (this.isRecording()) this.pause();
21984
- else if (this.recorder.isPaused()) this.resume();
22073
+ else if (this.recorder.isPaused()) this.resumeFromControl();
21985
22074
  else if (this.recorder.isReady()) this.recorder.record();
21986
22075
  }
21987
22076
  }
@@ -22010,8 +22099,8 @@
22010
22099
  isPaused() {
22011
22100
  return this.recorder.isPaused();
22012
22101
  }
22013
- error(err) {
22014
- this.notifier.error(err);
22102
+ error(err, blocking = true) {
22103
+ this.notifier.error(err, blocking);
22015
22104
  }
22016
22105
  hide() {
22017
22106
  if (this.visualsElement) {
@@ -22102,6 +22191,7 @@
22102
22191
  }
22103
22192
  }
22104
22193
  const wrappers_visuals = Visuals;
22194
+ 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
22195
  class Container extends util_Despot {
22106
22196
  visibility = document_visibility_default()();
22107
22197
  htmlElement = document.querySelector("html");
@@ -22125,14 +22215,16 @@
22125
22215
  });
22126
22216
  };
22127
22217
  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
- }
22218
+ const containerElement = this.containerElement;
22219
+ if (!containerElement) return;
22220
+ const eventPath = e.composedPath();
22221
+ const targetContainerIndex = eventPath.findIndex((eventTarget)=>eventTarget instanceof Element && eventTarget.classList.contains(this.options.selectors.containerClass));
22222
+ const targetContainer = eventPath[targetContainerIndex];
22223
+ const isInteractiveTarget = eventPath.slice(0, targetContainerIndex + 1).some((eventTarget)=>eventTarget instanceof Element && eventTarget.matches(INTERACTIVE_SPACE_TARGETS));
22224
+ if (!eventPath.includes(containerElement) || targetContainer !== containerElement || isInteractiveTarget || e.defaultPrevented || e.repeat || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || "Space" !== e.code) return;
22225
+ e.preventDefault();
22226
+ if (this.options.enablePause) this.visuals.pauseOrResume();
22227
+ else this.visuals.recordOrStop();
22136
22228
  };
22137
22229
  constructor(options){
22138
22230
  super("Container", options);
@@ -22210,9 +22302,9 @@
22210
22302
  if (params.exc.stack) this.options.logger.error(params.exc.stack);
22211
22303
  else if (params.exc.message) this.options.logger.error(params.exc.message);
22212
22304
  } 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();
22305
+ if (this.options.displayErrors && params.err) this.visuals.error(params.err, !params.recoverable);
22306
+ else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc, !params.recoverable);
22307
+ else if (!params.recoverable) this.visuals.reset();
22216
22308
  }
22217
22309
  initEvents(playerOnly = false) {
22218
22310
  this.options.logger.debug(`Container: initEvents (playerOnly = ${playerOnly})`);
@@ -22230,7 +22322,7 @@
22230
22322
  }
22231
22323
  });
22232
22324
  if (this.options.enableSpace) {
22233
- if (!playerOnly) window.addEventListener("keydown", this.handleKeyDown);
22325
+ if (!playerOnly) this.containerElement?.addEventListener("keydown", this.handleKeyDown);
22234
22326
  }
22235
22327
  this.on("ERROR", (params)=>{
22236
22328
  this.processError(params);
@@ -22270,7 +22362,7 @@
22270
22362
  removeBrowserEventListeners() {
22271
22363
  window.removeEventListener("resize", this.handleWindowResize);
22272
22364
  window.removeEventListener("beforeunload", this.handleBeforeUnload);
22273
- window.removeEventListener("keydown", this.handleKeyDown);
22365
+ this.containerElement?.removeEventListener("keydown", this.handleKeyDown);
22274
22366
  this.stopListeningToVisibility?.();
22275
22367
  this.stopListeningToVisibility = void 0;
22276
22368
  }
@@ -22509,6 +22601,7 @@
22509
22601
  async submitAll(formData, method, url, hasHostAction = false) {
22510
22602
  let response;
22511
22603
  let hostResponse;
22604
+ let submissionFailed = false;
22512
22605
  const videomailKey = formData[this.options.selectors.keyInputName];
22513
22606
  try {
22514
22607
  const hasVideomailKey = Boolean(videomailKey);
@@ -22519,7 +22612,7 @@
22519
22612
  ].filter(Boolean).join(": ");
22520
22613
  this.options.logger.debug(`Container: submitAll(${output})`);
22521
22614
  this.beginWaiting();
22522
- if (this.options.disableFormWhenSubmitting) this.disableForm(true);
22615
+ if (this.options.disableFormWhenSubmitting) this.form?.disableForSubmission();
22523
22616
  this.emit("SUBMITTING");
22524
22617
  if (hasVideomailKey) {
22525
22618
  response = this.getPendingHostSubmissionResponse(videomailKey) ?? await this.submitVideomail(formData, method);
@@ -22549,15 +22642,18 @@
22549
22642
  });
22550
22643
  }
22551
22644
  } catch (exc) {
22645
+ submissionFailed = true;
22552
22646
  const err = error_createError({
22553
22647
  exc,
22554
22648
  options: this.options
22555
22649
  });
22556
22650
  this.emit("ERROR", {
22557
- err
22651
+ err,
22652
+ recoverable: true
22558
22653
  });
22559
22654
  } finally{
22560
22655
  const finalResponse = hostResponse ?? response;
22656
+ if (submissionFailed && this.options.disableFormWhenSubmitting) this.form?.restoreAfterFailedSubmission();
22561
22657
  if (finalResponse?.text && "text/html" === finalResponse.type) document.body.innerHTML = purify_default.sanitize(finalResponse.text);
22562
22658
  this.endWaiting();
22563
22659
  }
@@ -22611,7 +22707,11 @@
22611
22707
  this.visuals.record();
22612
22708
  }
22613
22709
  resume() {
22614
- this.visuals.resume();
22710
+ this.visuals.resume().catch((exc)=>{
22711
+ this.emit("ERROR", {
22712
+ exc
22713
+ });
22714
+ });
22615
22715
  }
22616
22716
  stop() {
22617
22717
  this.visuals.stop();