@xibosignage/xibo-layout-renderer 1.0.26 → 1.0.28

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.
@@ -19,30 +19,6 @@ var XiboLayoutRenderer = (function (exports) {
19
19
  return !!t;
20
20
  })();
21
21
  }
22
- function _iterableToArrayLimit(r, l) {
23
- var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
24
- if (null != t) {
25
- var e,
26
- n,
27
- i,
28
- u,
29
- a = [],
30
- f = !0,
31
- o = !1;
32
- try {
33
- if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
34
- } catch (r) {
35
- o = !0, n = r;
36
- } finally {
37
- try {
38
- if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
39
- } finally {
40
- if (o) throw n;
41
- }
42
- }
43
- return a;
44
- }
45
- }
46
22
  function ownKeys(e, r) {
47
23
  var t = Object.keys(e);
48
24
  if (Object.getOwnPropertySymbols) {
@@ -521,18 +497,12 @@ var XiboLayoutRenderer = (function (exports) {
521
497
  }
522
498
  return _assertThisInitialized(self);
523
499
  }
524
- function _slicedToArray(arr, i) {
525
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray$1(arr, i) || _nonIterableRest();
526
- }
527
500
  function _toConsumableArray(arr) {
528
501
  return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray$1(arr) || _nonIterableSpread();
529
502
  }
530
503
  function _arrayWithoutHoles(arr) {
531
504
  if (Array.isArray(arr)) return _arrayLikeToArray$1(arr);
532
505
  }
533
- function _arrayWithHoles(arr) {
534
- if (Array.isArray(arr)) return arr;
535
- }
536
506
  function _iterableToArray(iter) {
537
507
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
538
508
  }
@@ -552,9 +522,6 @@ var XiboLayoutRenderer = (function (exports) {
552
522
  function _nonIterableSpread() {
553
523
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
554
524
  }
555
- function _nonIterableRest() {
556
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
557
- }
558
525
  function _createForOfIteratorHelper(o, allowArrayLike) {
559
526
  var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
560
527
  if (!it) {
@@ -755,6 +722,7 @@ var XiboLayoutRenderer = (function (exports) {
755
722
  options: {},
756
723
  playNextMedia: function playNextMedia() {},
757
724
  playPreviousMedia: function playPreviousMedia() {},
725
+ prepareMedia: function prepareMedia(_media) {},
758
726
  prepareMediaObjects: function prepareMediaObjects() {},
759
727
  prepareRegion: function prepareRegion() {},
760
728
  ready: false,
@@ -1060,6 +1028,13 @@ var XiboLayoutRenderer = (function (exports) {
1060
1028
  },
1061
1029
  gotoNextLayout: function gotoNextLayout() {},
1062
1030
  gotoPrevLayout: function gotoPrevLayout() {},
1031
+ gotoLayoutByCode: function gotoLayoutByCode(_layoutCode) {
1032
+ return Promise.resolve();
1033
+ },
1034
+ playInterruptLayout: function playInterruptLayout(_inputLayout) {
1035
+ return Promise.resolve();
1036
+ },
1037
+ triggerAction: function triggerAction(_triggerCode, _widgetId) {},
1063
1038
  init: function init() {
1064
1039
  return Promise.resolve({});
1065
1040
  },
@@ -1098,7 +1073,7 @@ var XiboLayoutRenderer = (function (exports) {
1098
1073
  renderOverlayLayouts: function renderOverlayLayouts() {
1099
1074
  return Promise.resolve();
1100
1075
  },
1101
- uniqueLayouts: {},
1076
+ uniqueLayouts: new Map(),
1102
1077
  updateInputLayout: function updateInputLayout(layoutIndex, layout) {},
1103
1078
  updateLayouts: function updateLayouts(inputLayouts) {},
1104
1079
  updateLoop: function updateLoop(inputLayouts) {
@@ -14018,11 +13993,11 @@ var XiboLayoutRenderer = (function (exports) {
14018
13993
  * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
14019
13994
  * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization
14020
13995
  */
14021
- function DOMParser$1(options){
13996
+ function DOMParser$2(options){
14022
13997
  this.options = options ||{locator:{}};
14023
13998
  }
14024
13999
 
14025
- DOMParser$1.prototype.parseFromString = function(source,mimeType){
14000
+ DOMParser$2.prototype.parseFromString = function(source,mimeType){
14026
14001
  var options = this.options;
14027
14002
  var sax = new XMLReader();
14028
14003
  var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
@@ -14269,9 +14244,9 @@ var XiboLayoutRenderer = (function (exports) {
14269
14244
 
14270
14245
  domParser.__DOMHandler = DOMHandler;
14271
14246
  domParser.normalizeLineEndings = normalizeLineEndings;
14272
- domParser.DOMParser = DOMParser$1;
14247
+ domParser.DOMParser = DOMParser$2;
14273
14248
 
14274
- var DOMParser = domParser.DOMParser;
14249
+ var DOMParser$1 = domParser.DOMParser;
14275
14250
 
14276
14251
  /*! @name mpd-parser @version 1.3.1 @license Apache-2.0 */
14277
14252
 
@@ -16895,7 +16870,7 @@ var XiboLayoutRenderer = (function (exports) {
16895
16870
  throw new Error(errors.DASH_EMPTY_MANIFEST);
16896
16871
  }
16897
16872
 
16898
- const parser = new DOMParser();
16873
+ const parser = new DOMParser$1();
16899
16874
  let xml;
16900
16875
  let mpd;
16901
16876
 
@@ -73985,6 +73960,9 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
73985
73960
  _defineProperty(this, "xml", null);
73986
73961
  _defineProperty(this, "videoHandler", void 0);
73987
73962
  _defineProperty(this, "mediaTimer", void 0);
73963
+ _defineProperty(this, "sspImpressionUrls", undefined);
73964
+ _defineProperty(this, "sspErrorUrls", undefined);
73965
+ _defineProperty(this, "isSspWidget", false);
73988
73966
  _defineProperty(this, "mediaTimeCount", 0);
73989
73967
  _defineProperty(this, "xlr", {});
73990
73968
  _defineProperty(this, "statsBC", new BroadcastChannel('statsBC'));
@@ -74021,14 +73999,13 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74021
73999
  _this.startMediaTimer(media);
74022
74000
  }
74023
74001
  } else if (media.mediaType === 'shellcommand') {
74024
- if (_this.hasCommandExecuted && !_this.loop) {
74025
- return;
74026
- }
74027
- _this.hasCommandExecuted = true;
74028
- _this.emitCommand(media);
74029
- if (media.duration > 0) {
74030
- _this.startMediaTimer(media);
74002
+ // Fire once per cycle unless loop is enabled, in which case re-fire every play.
74003
+ if (!_this.hasCommandExecuted || _this.loop) {
74004
+ _this.hasCommandExecuted = true;
74005
+ _this.emitCommand(media);
74031
74006
  }
74007
+ // Always run the timer so the layout advances and stats are recorded correctly.
74008
+ _this.startMediaTimer(media);
74032
74009
  } else {
74033
74010
  _this.startMediaTimer(media);
74034
74011
  }
@@ -74070,6 +74047,10 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74070
74047
  layoutId: media.region.layout.id
74071
74048
  });
74072
74049
  _this.xlr.emitter.emit('widgetEnd', parseInt(media.id));
74050
+ if (_this.isSspWidget) {
74051
+ var _this$sspImpressionUr, _this$sspErrorUrls;
74052
+ _this.xlr.emitter.emit('sspWidgetEnd', (_this$sspImpressionUr = _this.sspImpressionUrls) !== null && _this$sspImpressionUr !== void 0 ? _this$sspImpressionUr : [], (_this$sspErrorUrls = _this.sspErrorUrls) !== null && _this$sspErrorUrls !== void 0 ? _this$sspErrorUrls : [], _this.sspImpressionUrls ? _this.duration : 0);
74053
+ }
74073
74054
  media.region.playNextMedia();
74074
74055
  });
74075
74056
  this.on('cancelled', function (media) {
@@ -74096,7 +74077,16 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74096
74077
  layoutId: media.region.layout.id
74097
74078
  });
74098
74079
  _this.xlr.emitter.emit('widgetEnd', parseInt(media.id));
74099
- media.region.playNextMedia();
74080
+ if (_this.isSspWidget) {
74081
+ var _this$sspImpressionUr2, _this$sspErrorUrls2;
74082
+ _this.xlr.emitter.emit('sspWidgetEnd', (_this$sspImpressionUr2 = _this.sspImpressionUrls) !== null && _this$sspImpressionUr2 !== void 0 ? _this$sspImpressionUr2 : [], (_this$sspErrorUrls2 = _this.sspErrorUrls) !== null && _this$sspErrorUrls2 !== void 0 ? _this$sspErrorUrls2 : [], _this.sspImpressionUrls ? _this.duration : 0);
74083
+ }
74084
+ // Only advance the region if this media is still the active one.
74085
+ // A user-triggered next/prev action may have already moved currMedia
74086
+ // on, in which case the timer firing here would cause a double-advance.
74087
+ if (media === media.region.currMedia) {
74088
+ media.region.playNextMedia();
74089
+ }
74100
74090
  });
74101
74091
  // Initialize Media object
74102
74092
  this.init();
@@ -74196,26 +74186,33 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74196
74186
  if (this.mediaType === 'image' || this.mediaType === 'video') {
74197
74187
  resourceUrlParams.mediaType = this.mediaType;
74198
74188
  }
74199
- var tmpUrl = '';
74200
- if (this.xlr.config.platform === exports.ConsumerPlatform.CMS) {
74201
- tmpUrl = composeResourceUrlByPlatform(this.xlr.config, resourceUrlParams);
74202
- } else if (this.xlr.config.platform === exports.ConsumerPlatform.CHROMEOS) {
74203
- tmpUrl = composeResourceUrl(this.xlr.config, resourceUrlParams);
74204
- if (this.mediaType === 'image' || this.mediaType === 'video' || this.mediaType === 'audio') {
74205
- tmpUrl = composeMediaUrl(resourceUrlParams);
74189
+ // SSP widget: URL is not known until the consumer resolves an ad at play-time.
74190
+ // Skip all URL composition and leave url as null.
74191
+ if (this.mediaType === 'ssp') {
74192
+ this.url = null;
74193
+ this.isSspWidget = true;
74194
+ } else {
74195
+ var tmpUrl = '';
74196
+ if (this.xlr.config.platform === exports.ConsumerPlatform.CMS) {
74197
+ tmpUrl = composeResourceUrlByPlatform(this.xlr.config, resourceUrlParams);
74198
+ } else if (this.xlr.config.platform === exports.ConsumerPlatform.CHROMEOS) {
74199
+ tmpUrl = composeResourceUrl(this.xlr.config, resourceUrlParams);
74200
+ if (this.mediaType === 'image' || this.mediaType === 'video' || this.mediaType === 'audio') {
74201
+ tmpUrl = composeMediaUrl(resourceUrlParams);
74202
+ // this is an SSP Layout
74203
+ if (this.region.layout.layoutId === -1) {
74204
+ tmpUrl = this.uri;
74205
+ }
74206
+ }
74207
+ } else if (this.xlr.config.platform === exports.ConsumerPlatform.ELECTRON) {
74208
+ tmpUrl = composeResourceUrlByPlatform(this.xlr.config, resourceUrlParams);
74206
74209
  // this is an SSP Layout
74207
74210
  if (this.region.layout.layoutId === -1) {
74208
74211
  tmpUrl = this.uri;
74209
74212
  }
74210
74213
  }
74211
- } else if (this.xlr.config.platform === exports.ConsumerPlatform.ELECTRON) {
74212
- tmpUrl = composeResourceUrlByPlatform(this.xlr.config, resourceUrlParams);
74213
- // this is an SSP Layout
74214
- if (this.region.layout.layoutId === -1) {
74215
- tmpUrl = this.uri;
74216
- }
74214
+ this.url = tmpUrl;
74217
74215
  }
74218
- this.url = tmpUrl;
74219
74216
  // Loop if media has loop, or if region has loop and a single media
74220
74217
  this.loop = this.options['loop'] == '1' || this.region.options['loop'] == '1' && this.region.totalMediaObjects == 1;
74221
74218
  this.html = createMediaElement(this);
@@ -74322,18 +74319,49 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74322
74319
  };
74323
74320
  var getNewMedia = function getNewMedia() {
74324
74321
  var $region = _this3.region.html;
74325
- // This function is for checking whether
74326
- // the region still has to show a media item
74327
- // when another region is not finished yet
74328
- if (_this3.region.complete && !_this3.region.layout.allEnded) {
74329
- // Add currentMedia to the region
74322
+ // Re-insert the element whenever it is missing from the DOM. This covers:
74323
+ // 1. Region completed early but the layout is still running (freeze last frame).
74324
+ // 2. Backward navigation the previous media's element was removed by the
74325
+ // removeOldMedia setTimeout in transitionNodes.
74326
+ if (_this3.html) {
74330
74327
  $region.insertBefore(_this3.html, $region.lastElementChild);
74331
74328
  return _this3.html;
74332
74329
  }
74333
74330
  return null;
74334
74331
  };
74332
+ // SSP widget: if the consumer did not resolve an ad during the preload window
74333
+ // (i.e. setSspAdUrl was never called), skip this widget and advance normally.
74334
+ if (this.mediaType === 'ssp') {
74335
+ console.debug('??? XLR.debug >> Media.run() > SSP widget: no ad resolved during preload, skipping');
74336
+ this.emitter.emit('end', this);
74337
+ return;
74338
+ }
74335
74339
  showCurrentMedia();
74336
74340
  }
74341
+ }, {
74342
+ key: "setSspAdUrl",
74343
+ value: function setSspAdUrl(url, adMediaType, impressionUrls, errorUrls) {
74344
+ // Ignore if the media has already been skipped or cancelled before the ad arrived.
74345
+ if (this.state !== MediaState.IDLE) {
74346
+ console.debug('??? XLR.debug >> Media::setSspAdUrl - ignoring, media is no longer idle', {
74347
+ state: this.state
74348
+ });
74349
+ return;
74350
+ }
74351
+ // Remove the placeholder <div> so the correct element type can take its place.
74352
+ if (this.html) {
74353
+ this.html.remove();
74354
+ this.html = null;
74355
+ }
74356
+ this.url = url;
74357
+ this.mediaType = adMediaType;
74358
+ this.sspImpressionUrls = impressionUrls;
74359
+ this.sspErrorUrls = errorUrls;
74360
+ // Re-create the element now that mediaType is known, then prepare and append to region DOM.
74361
+ // Visibility and playback are handled by run() when this media's turn comes.
74362
+ this.html = createMediaElement(this);
74363
+ this.region.prepareMedia(this);
74364
+ }
74337
74365
  }, {
74338
74366
  key: "stop",
74339
74367
  value: function () {
@@ -74561,15 +74589,21 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74561
74589
  });
74562
74590
  // Add media to region for targeted actions
74563
74591
  (_this$layout$actionCo = this.layout.actionController) === null || _this$layout$actionCo === void 0 || _this$layout$actionCo.actions.forEach(function (action) {
74592
+ var _ref2, _attributes$actionTyp, _ref3, _attributes$targetId, _ref4, _attributes$widgetId, _attributes$target;
74564
74593
  var attributes = getAllAttributes(action.xml);
74565
- if (attributes.target.value === 'region' && attributes.actionType.value === 'navWidget' && attributes.targetId.value == _this.id) {
74594
+ // getAllAttributes preserves the XML attribute case. Xibo CMS may export
74595
+ // attributes in camelCase (actionType, targetId, widgetId) or lowercase.
74596
+ // Read both variants so we work with either XLF format.
74597
+ var actionType = (_ref2 = (_attributes$actionTyp = attributes['actionType']) !== null && _attributes$actionTyp !== void 0 ? _attributes$actionTyp : attributes['actiontype']) === null || _ref2 === void 0 ? void 0 : _ref2.value;
74598
+ var targetId = (_ref3 = (_attributes$targetId = attributes['targetId']) !== null && _attributes$targetId !== void 0 ? _attributes$targetId : attributes['targetid']) === null || _ref3 === void 0 ? void 0 : _ref3.value;
74599
+ var widgetId = (_ref4 = (_attributes$widgetId = attributes['widgetId']) !== null && _attributes$widgetId !== void 0 ? _attributes$widgetId : attributes['widgetid']) === null || _ref4 === void 0 ? void 0 : _ref4.value;
74600
+ if (((_attributes$target = attributes['target']) === null || _attributes$target === void 0 ? void 0 : _attributes$target.value) === 'region' && actionType === 'navWidget' && targetId == _this.id) {
74566
74601
  var _this$layout$drawer;
74567
74602
  var drawerMediaItems = Array.from(((_this$layout$drawer = _this.layout.drawer) === null || _this$layout$drawer === void 0 ? void 0 : _this$layout$drawer.getElementsByTagName('media')) || []);
74568
74603
  drawerMediaItems.forEach(function (drawerMedia) {
74569
- var _attributes$widgetId;
74570
- if (drawerMedia.id === ((_attributes$widgetId = attributes.widgetId) === null || _attributes$widgetId === void 0 ? void 0 : _attributes$widgetId.value)) {
74604
+ if (drawerMedia.getAttribute('id') === widgetId) {
74571
74605
  // Add drawer media to the region
74572
- _this.mediaObjectsActions.push(new Media(_this, (drawerMedia === null || drawerMedia === void 0 ? void 0 : drawerMedia.getAttribute('id')) || '', drawerMedia, _this.options, _this.xlr));
74606
+ _this.mediaObjectsActions.push(new Media(_this, drawerMedia.getAttribute('id') || '', drawerMedia, _this.options, _this.xlr));
74573
74607
  }
74574
74608
  });
74575
74609
  }
@@ -74586,6 +74620,13 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74586
74620
  }, {
74587
74621
  key: "prepareMedia",
74588
74622
  value: function prepareMedia(media) {
74623
+ // SSP widget: signal the consumer to fetch an ad before this media's turn to play.
74624
+ // The consumer calls media.setSspAdUrl() to resolve it; if it never arrives,
74625
+ // Media.run() skips the widget and advances normally.
74626
+ if (media.mediaType === 'ssp') {
74627
+ this.xlr.emitter.emit('sspWidgetRequest', media);
74628
+ return;
74629
+ }
74589
74630
  if (media.mediaType === 'video') {
74590
74631
  prepareVideoMedia(media, this);
74591
74632
  } else if (media.mediaType === 'image' && media.url !== null) {
@@ -74594,6 +74635,11 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74594
74635
  prepareAudioMedia(media, this);
74595
74636
  } else if ((media.render === 'html' || media.mediaType === 'webpage') && media.iframe && media.checkIframeStatus) {
74596
74637
  prepareHtmlMedia(media, this);
74638
+ } else if (media.mediaType === 'shellcommand') {
74639
+ // Shell command widgets are invisible but must be in the DOM to trigger playback.
74640
+ if (media.html) {
74641
+ this.html.appendChild(media.html);
74642
+ }
74597
74643
  }
74598
74644
  }
74599
74645
  }, {
@@ -74802,7 +74848,12 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74802
74848
  }
74803
74849
  };
74804
74850
  if (oldMedia) {
74805
- hideOldMedia();
74851
+ // Skip hiding old media when it is the same object as new media
74852
+ // (single-media loop): removing it would also remove the element
74853
+ // that is about to be shown, leaving the region blank.
74854
+ if (oldMedia !== newMedia) {
74855
+ hideOldMedia();
74856
+ }
74806
74857
  newMedia.run();
74807
74858
  } else {
74808
74859
  newMedia.run();
@@ -74812,7 +74863,17 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74812
74863
  }, {
74813
74864
  key: "playNextMedia",
74814
74865
  value: function playNextMedia() {
74815
- var _this$oldMedia3, _this$currMedia2, _this$nxtMedia, _this$currMedia3, _this$currMedia4, _this$currMedia5, _this$currMedia6, _this$oldMedia4, _this$currMedia7, _this$nxtMedia2;
74866
+ var _this$oldMedia3,
74867
+ _this$currMedia2,
74868
+ _this$nxtMedia,
74869
+ _this$currMedia3,
74870
+ _this$currMedia4,
74871
+ _this$currMedia5,
74872
+ _this$currMedia6,
74873
+ _this3 = this,
74874
+ _this$oldMedia4,
74875
+ _this$currMedia7,
74876
+ _this$nxtMedia2;
74816
74877
  console.debug('??? XLR.debug Region playing next media', {
74817
74878
  regionId: this.id,
74818
74879
  currentMediaIndex: this.currentMediaIndex,
@@ -74832,8 +74893,10 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74832
74893
  // cycle completion after the skip loop runs.
74833
74894
  var origIndex = this.currentMediaIndex;
74834
74895
  // When the region has completed and when currentMedia is html
74835
- // Then, preserve the currentMedia state
74836
- if (this.complete && ((_this$currMedia3 = this.currMedia) === null || _this$currMedia3 === void 0 ? void 0 : _this$currMedia3.render) === 'html') {
74896
+ // and there is only one media object, preserve the currentMedia state.
74897
+ // Guard is limited to single-media regions so navWidget injections
74898
+ // (which splice a second media in) are not blocked.
74899
+ if (this.complete && ((_this$currMedia3 = this.currMedia) === null || _this$currMedia3 === void 0 ? void 0 : _this$currMedia3.render) === 'html' && this.totalMediaObjects === 1) {
74837
74900
  return;
74838
74901
  }
74839
74902
  // When the region has completed and mediaObjects.length = 1
@@ -74879,6 +74942,25 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74879
74942
  }
74880
74943
  });
74881
74944
  }
74945
+ // Remove single-play media injected via navWidget actions at the end of each cycle
74946
+ if (crossedEnd && this.mediaObjects.some(function (m) {
74947
+ return m.singlePlay;
74948
+ })) {
74949
+ this.mediaObjects = this.mediaObjects.filter(function (m) {
74950
+ return !m.singlePlay;
74951
+ });
74952
+ this.totalMediaObjects = this.mediaObjects.length;
74953
+ if (this.totalMediaObjects === 0) {
74954
+ this.finished();
74955
+ return;
74956
+ }
74957
+ var newIndex = this.mediaObjects.findIndex(function (m) {
74958
+ return m === _this3.currMedia;
74959
+ });
74960
+ this.currentMediaIndex = newIndex >= 0 ? newIndex : 0;
74961
+ this.currMedia = this.mediaObjects[this.currentMediaIndex];
74962
+ this.nxtMedia = this.mediaObjects[(this.currentMediaIndex + 1) % this.totalMediaObjects];
74963
+ }
74882
74964
  console.debug('??? XLR.debug >> End Region::playNextMedia > execute transitionNodes', {
74883
74965
  regionId: this.id,
74884
74966
  currentMediaIndex: this.currentMediaIndex,
@@ -74899,14 +74981,15 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
74899
74981
  }, {
74900
74982
  key: "playPreviousMedia",
74901
74983
  value: function playPreviousMedia() {
74902
- this.currentMediaIndex = this.currentMediaIndex - 1;
74903
- if (this.currentMediaIndex < 0 || this.ended) {
74904
- this.currentMediaIndex = 0;
74984
+ if (this.currentMediaIndex <= 0 || this.ended) {
74905
74985
  return;
74906
74986
  }
74907
- this.prepareMediaObjects();
74987
+ this.oldMedia = this.currMedia;
74988
+ this.currentMediaIndex -= 1;
74989
+ this.currMedia = this.mediaObjects[this.currentMediaIndex];
74990
+ this.nxtMedia = this.mediaObjects[(this.currentMediaIndex + 1) % this.totalMediaObjects];
74991
+ this.complete = false;
74908
74992
  console.debug('region::playPreviousMedia', this);
74909
- /* Do the transition */
74910
74993
  this.transitionNodes(this.oldMedia, this.currMedia);
74911
74994
  }
74912
74995
  }, {
@@ -75105,6 +75188,10 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
75105
75188
  key: "openLayoutInNewTab",
75106
75189
  value: function openLayoutInNewTab(layoutCode, options) {
75107
75190
  var url = options.layoutPreviewUrl.replace('[layoutCode]', layoutCode) + '?findByCode=1';
75191
+ console.debug('[ActionController::openLayoutInNewTab] Navigating to layout in new tab with code', {
75192
+ layoutCode: layoutCode,
75193
+ url: url
75194
+ });
75108
75195
  // Send a postMessage to the parent frame so the CMS can handle the confirmation
75109
75196
  // and navigation (confirm() is blocked in sandboxed iframes without allow-modals).
75110
75197
  window.parent.postMessage({
@@ -75117,26 +75204,40 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
75117
75204
  }
75118
75205
  }, {
75119
75206
  key: "openLayoutInPlayer",
75120
- value: function openLayoutInPlayer(layoutCode, options) {
75121
- // this.parent.xlr.updateLoop([]);
75207
+ value: function openLayoutInPlayer(layoutCode, _options) {
75208
+ console.debug('[ActionController::openLayoutInPlayer] Navigating to layout in player with code', {
75209
+ layoutCode: layoutCode,
75210
+ options: _options
75211
+ });
75212
+ this.parent.xlr.emitter.emit('navLayout', layoutCode, '');
75122
75213
  }
75123
75214
  }, {
75124
75215
  key: "prevOrNextLayout",
75125
75216
  value: function prevOrNextLayout(targetId, actionType) {
75126
75217
  var _this$parent$xlr$curr;
75127
- // Check if currentLayout is the targetId
75128
- if (((_this$parent$xlr$curr = this.parent.xlr.currentLayout) === null || _this$parent$xlr$curr === void 0 ? void 0 : _this$parent$xlr$curr.layoutId) === parseInt(targetId)) {
75129
- if (actionType === 'next') {
75130
- this.parent.xlr.gotoNextLayout();
75131
- } else if (actionType === 'previous') {
75132
- this.parent.xlr.gotoPrevLayout();
75133
- }
75218
+ console.debug('[ActionController::prevOrNextLayout] Changing layout with data', {
75219
+ targetId: targetId,
75220
+ actionType: actionType
75221
+ });
75222
+ // For screen-level actions targetId may be "0" (the screen has no numeric ID).
75223
+ // Guard using this.parent.layoutId instead so the check always works.
75224
+ if (((_this$parent$xlr$curr = this.parent.xlr.currentLayout) === null || _this$parent$xlr$curr === void 0 ? void 0 : _this$parent$xlr$curr.layoutId) !== this.parent.layoutId) {
75225
+ return;
75226
+ }
75227
+ if (actionType === 'next') {
75228
+ this.parent.xlr.gotoNextLayout();
75229
+ } else if (actionType === 'previous') {
75230
+ this.parent.xlr.gotoPrevLayout();
75134
75231
  }
75135
75232
  }
75136
75233
  /** Change media in region (next/previous) */
75137
75234
  }, {
75138
- key: "nextMediaInRegion",
75139
- value: function nextMediaInRegion(regionId, actionType) {
75235
+ key: "gotoMediaInRegion",
75236
+ value: function gotoMediaInRegion(regionId, actionType) {
75237
+ console.debug('[ActionController::gotoMediaInRegion] Changing media in region with data', {
75238
+ regionId: regionId,
75239
+ actionType: actionType
75240
+ });
75140
75241
  // Find target region
75141
75242
  this.parent.regions.forEach(function (regionObj) {
75142
75243
  if (regionObj.id === regionId) {
@@ -75151,7 +75252,11 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
75151
75252
  }, {
75152
75253
  key: "loadMediaInRegion",
75153
75254
  value: function loadMediaInRegion(regionId, widgetId) {
75154
- var _targetRegion, _targetRegion2, _targetRegion3;
75255
+ var _targetRegion, _targetRegion2, _targetRegion3, _targetRegion4;
75256
+ console.debug('[ActionController::loadMediaInRegion] Loading media in region with data', {
75257
+ regionId: regionId,
75258
+ widgetId: widgetId
75259
+ });
75155
75260
  var self = this;
75156
75261
  // Find target region
75157
75262
  var targetRegion;
@@ -75174,27 +75279,70 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
75174
75279
  targetMedia.singlePlay = true;
75175
75280
  }
75176
75281
  // If region is empty, remove the background color and empty message
75177
- if (((_targetRegion = targetRegion) === null || _targetRegion === void 0 ? void 0 : _targetRegion.mediaObjects.length) === 0) {
75282
+ if (((_targetRegion = targetRegion) === null || _targetRegion === void 0 ? void 0 : _targetRegion.totalMediaObjects) === 0) {
75283
+ targetRegion.complete = false;
75284
+ }
75285
+ // Bail out early when the target widget was not found in the drawer
75286
+ if (!targetMedia) {
75287
+ console.debug('[ActionController::loadMediaInRegion] Target media not found in mediaObjectsActions', {
75288
+ regionId: regionId,
75289
+ widgetId: widgetId
75290
+ });
75291
+ return;
75292
+ }
75293
+ // Guard against duplicate insertion if the action fires multiple times before the widget plays
75294
+ if (targetRegion && targetRegion.mediaObjects.some(function (m) {
75295
+ return m.id === targetMedia.id;
75296
+ })) {
75297
+ console.debug('[ActionController::loadMediaInRegion] Target media already queued, skipping duplicate insertion');
75298
+ return;
75299
+ }
75300
+ // Cancel the current media's duration timer so it doesn't fire and interrupt
75301
+ // the target widget mid-playback (e.g. an Interactive Zone timer still ticking).
75302
+ if ((_targetRegion2 = targetRegion) !== null && _targetRegion2 !== void 0 && (_targetRegion2 = _targetRegion2.currMedia) !== null && _targetRegion2 !== void 0 && _targetRegion2.mediaTimer) {
75303
+ clearInterval(targetRegion.currMedia.mediaTimer);
75304
+ targetRegion.currMedia.mediaTimer = undefined;
75305
+ }
75306
+ // Reset complete so the HTML-media guard in playNextMedia() doesn't block
75307
+ // the transition (that guard is for single-media loops, not navWidget injections).
75308
+ if (targetRegion) {
75178
75309
  targetRegion.complete = false;
75179
75310
  }
75180
75311
  // Create media in region and play it next
75181
- (_targetRegion2 = targetRegion) === null || _targetRegion2 === void 0 || _targetRegion2.mediaObjects.splice(targetRegion.currentMediaIndex + 1, 0, targetMedia);
75182
- (_targetRegion3 = targetRegion) === null || _targetRegion3 === void 0 || _targetRegion3.playNextMedia();
75312
+ (_targetRegion3 = targetRegion) === null || _targetRegion3 === void 0 || _targetRegion3.mediaObjects.splice(targetRegion.currentMediaIndex + 1, 0, targetMedia);
75313
+ // Keep totalMediaObjects in sync with the actual array length
75314
+ if (targetRegion) {
75315
+ targetRegion.totalMediaObjects = targetRegion.mediaObjects.length;
75316
+ }
75317
+ // Drawer media items are never run through the normal prepareMedia pipeline,
75318
+ // so their DOM element has no background-image / src set and is not yet in the
75319
+ // region DOM. Prepare it now so Media.run() finds a ready element to show.
75320
+ if (targetRegion) {
75321
+ targetRegion.prepareMedia(targetMedia);
75322
+ }
75323
+ console.debug('[ActionController::loadMediaInRegion] Target media loaded, playing next', {
75324
+ regionId: regionId,
75325
+ widgetId: widgetId
75326
+ });
75327
+ (_targetRegion4 = targetRegion) === null || _targetRegion4 === void 0 || _targetRegion4.playNextMedia();
75183
75328
  }
75184
75329
  /** Run action based on action data */
75185
75330
  }, {
75186
75331
  key: "runAction",
75187
75332
  value: function runAction(actionData, options) {
75333
+ console.debug('[ActionController::runAction] Triggering action', {
75334
+ actionData: actionData
75335
+ });
75188
75336
  if (actionData.actiontype == 'navLayout') {
75189
75337
  if (this.parent.xlr.config.platform === exports.ConsumerPlatform.CMS) {
75190
- // Open layout preview in a new tab
75338
+ // Open layout preview in a new tab (CMS preview only)
75191
75339
  this.openLayoutInNewTab(actionData.layoutcode, options);
75192
- } else if (this.parent.xlr.config.platform === exports.ConsumerPlatform.CHROMEOS) {
75193
- // Set target layout as active layout
75340
+ } else {
75341
+ // All player platforms (Electron, ChromeOS, Android, etc.)
75194
75342
  this.openLayoutInPlayer(actionData.layoutcode, options);
75195
75343
  }
75196
75344
  } else if ((actionData.actiontype == 'previous' || actionData.actiontype == 'next') && actionData.target == 'region') {
75197
- this.nextMediaInRegion(actionData.targetid, actionData.actiontype);
75345
+ this.gotoMediaInRegion(actionData.targetid, actionData.actiontype);
75198
75346
  } else if (actionData.actiontype == 'navWidget' && actionData.target == 'region') {
75199
75347
  this.loadMediaInRegion(actionData.targetid, actionData.widgetid);
75200
75348
  } else if (actionData.target === 'screen') {
@@ -75253,6 +75401,18 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
75253
75401
  }
75254
75402
  });
75255
75403
  }
75404
+ /** Dispatch an incoming webhook trigger to any matching actions on this layout. */
75405
+ }, {
75406
+ key: "handleWebhookTrigger",
75407
+ value: function handleWebhookTrigger(triggerCode, widgetId) {
75408
+ var _this3 = this;
75409
+ this.$actionController.querySelectorAll('.action[triggertype="webhook"]').forEach(function ($el) {
75410
+ var ds = $el.dataset;
75411
+ if (ds.triggercode !== triggerCode) return;
75412
+ if (widgetId && ds.sourceid !== widgetId) return;
75413
+ _this3.runAction(ds, _this3.options);
75414
+ });
75415
+ }
75256
75416
  }, {
75257
75417
  key: "initKeyboardActions",
75258
75418
  value: function initKeyboardActions() {
@@ -76302,6 +76462,13 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76302
76462
  return _ref4.apply(this, arguments);
76303
76463
  };
76304
76464
  }());
76465
+ xlrObject.on('navLayout', function (layoutCode) {
76466
+ // Non-CMS platforms handle navLayout in their renderer via playInterruptLayout.
76467
+ // CMS navLayout is handled by ActionController (opens layout in a new tab).
76468
+ console.debug('[navLayout] XLR::on("navLayout") - received', {
76469
+ layoutCode: layoutCode
76470
+ });
76471
+ });
76305
76472
  xlrObject.emitSync = /*#__PURE__*/function () {
76306
76473
  var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(eventName) {
76307
76474
  var _xlrObject$emitter$ev;
@@ -76481,35 +76648,28 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76481
76648
  }));
76482
76649
  xlrObject.updateScheduleLayouts = /*#__PURE__*/function () {
76483
76650
  var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9(scheduleLayouts) {
76484
- var inputLayoutIds, _iterator, _step, _step$value, layoutIndex, _layout, uniqueLayout, _i, _Object$keys, layoutId;
76651
+ var next;
76485
76652
  return _regeneratorRuntime().wrap(function _callee9$(_context9) {
76486
76653
  while (1) switch (_context9.prev = _context9.next) {
76487
76654
  case 0:
76488
- console.debug('XLR::updateScheduleLayouts > Updating schedule layouts . . .');
76489
- inputLayoutIds = [];
76490
- _iterator = _createForOfIteratorHelper(scheduleLayouts.entries());
76491
- try {
76492
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
76493
- _step$value = _slicedToArray(_step.value, 2), layoutIndex = _step$value[0], _layout = _step$value[1];
76494
- uniqueLayout = _layout;
76495
- uniqueLayout.index = layoutIndex;
76496
- uniqueLayout.id = _layout.layoutId;
76497
- this.uniqueLayouts[_layout.layoutId] = uniqueLayout;
76498
- inputLayoutIds.push(_layout.layoutId);
76499
- }
76500
- // Cross-check if we need to remove non-existing layouts based on inputLayouts
76501
- } catch (err) {
76502
- _iterator.e(err);
76503
- } finally {
76504
- _iterator.f();
76505
- }
76506
- for (_i = 0, _Object$keys = Object.keys(this.uniqueLayouts); _i < _Object$keys.length; _i++) {
76507
- layoutId = _Object$keys[_i];
76508
- if (!inputLayoutIds.includes(parseInt(layoutId))) {
76509
- delete this.uniqueLayouts[layoutId];
76510
- }
76655
+ console.debug('XLR::updateScheduleLayouts > Updating schedule layouts . . .', scheduleLayouts);
76656
+ next = new Map();
76657
+ if (!(scheduleLayouts.length === 0)) {
76658
+ _context9.next = 5;
76659
+ break;
76511
76660
  }
76661
+ this.uniqueLayouts = next;
76662
+ return _context9.abrupt("return");
76512
76663
  case 5:
76664
+ scheduleLayouts.forEach(function (_layout, layoutIndex) {
76665
+ next.set(String(_layout.layoutId), _objectSpread2(_objectSpread2({}, _layout), {}, {
76666
+ index: layoutIndex,
76667
+ id: _layout.layoutId
76668
+ }));
76669
+ });
76670
+ console.debug('XLR::updateScheduleLayouts > next unique layouts', Array.from(next).values());
76671
+ this.uniqueLayouts = next;
76672
+ case 8:
76513
76673
  case "end":
76514
76674
  return _context9.stop();
76515
76675
  }
@@ -76558,6 +76718,36 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76558
76718
  case 0:
76559
76719
  console.debug('>>>> XLR.debug XLR::updateLoop > Updating schedule loop . . .');
76560
76720
  this.inputLayouts = inputLayouts;
76721
+ // Guard against a splash-only update: uniqueLayouts has no entry for layoutId 0,
76722
+ // so parseLayouts() would return undefined current/next and prepareLayoutXlf()
76723
+ // would be called with undefined. Clean up any playing layouts and show splash directly.
76724
+ if (!(inputLayouts.length === 1 && inputLayouts[0].layoutId === 0)) {
76725
+ _context11.next = 14;
76726
+ break;
76727
+ }
76728
+ if (!(this.currentLayout && this.isLayoutInDOM(this.currentLayout.containerName, this.currentLayout.index))) {
76729
+ _context11.next = 8;
76730
+ break;
76731
+ }
76732
+ // Force all regions to complete immediately
76733
+ this.currentLayout.inLoop = false;
76734
+ _context11.next = 7;
76735
+ return this.currentLayout.finishAllRegions();
76736
+ case 7:
76737
+ this.currentLayout.removeLayout();
76738
+ case 8:
76739
+ if (this.nextLayout) {
76740
+ // Discard regardless of DOM presence: nextLayout may be preloaded with
76741
+ // DOM elements and video.js players but not yet attached to the screen container.
76742
+ this.nextLayout.discardLayout(exports.LayoutPlaybackType.NEXT);
76743
+ }
76744
+ this.currentLayout = undefined;
76745
+ this.nextLayout = undefined;
76746
+ _context11.next = 13;
76747
+ return this.playSchedules(this);
76748
+ case 13:
76749
+ return _context11.abrupt("return");
76750
+ case 14:
76561
76751
  playback = this.parseLayouts(true);
76562
76752
  isCurrentLayoutValid = isLayoutValid(this.inputLayouts, (_this$currentLayout = this.currentLayout) === null || _this$currentLayout === void 0 ? void 0 : _this$currentLayout.layoutId);
76563
76753
  if (this.isSspEnabled && this.currentLayoutId === -1) {
@@ -76566,7 +76756,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76566
76756
  if (!isCurrentLayoutValid && this.currentLayout) {
76567
76757
  this.currentLayout.emitter.emit('cancelled', this.currentLayout);
76568
76758
  }
76569
- console.debug('>>>>> XLR.debug XLR::updateLoop > uniqueLayouts', this.uniqueLayouts);
76759
+ console.debug('>>>>> XLR.debug XLR::updateLoop > uniqueLayouts', Array.from(this.uniqueLayouts.values()));
76570
76760
  console.debug('>>>>> XLR.debug XLR::updateLoop > inputLayouts', this.inputLayouts);
76571
76761
  console.debug('>>>>> XLR.debug XLR::updateLoop > isCurrentLayoutValid', isCurrentLayoutValid);
76572
76762
  console.debug('>>>>> XLR.debug XLR::updateLoop > currentLayout', this.currentLayout);
@@ -76594,86 +76784,86 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76594
76784
  };
76595
76785
  }();
76596
76786
  if (isCurrentLayoutValid) {
76597
- _context11.next = 55;
76787
+ _context11.next = 67;
76598
76788
  break;
76599
76789
  }
76600
76790
  if (!playback.hasDefaultOnly) {
76601
- _context11.next = 34;
76791
+ _context11.next = 46;
76602
76792
  break;
76603
76793
  }
76604
76794
  if (!(this.currentLayout && playback.currentLayout && this.currentLayout.layoutId !== playback.currentLayout.layoutId)) {
76605
- _context11.next = 20;
76795
+ _context11.next = 32;
76606
76796
  break;
76607
76797
  }
76608
76798
  this.currentLayout.inLoop = false;
76609
- _context11.next = 19;
76799
+ _context11.next = 31;
76610
76800
  return this.currentLayout.finishAllRegions();
76611
- case 19:
76801
+ case 31:
76612
76802
  this.currentLayout.removeLayout();
76613
- case 20:
76803
+ case 32:
76614
76804
  // Discard old nextLayout before replacing it — same as the
76615
76805
  // other two branches do, otherwise the prepared DOM element
76616
76806
  // and any video.js players are orphaned.
76617
76807
  if (this.nextLayout && this.isLayoutInDOM(this.nextLayout.containerName, this.nextLayout.index)) {
76618
76808
  this.nextLayout.discardLayout(exports.LayoutPlaybackType.NEXT);
76619
76809
  }
76620
- _context11.next = 23;
76810
+ _context11.next = 35;
76621
76811
  return this.prepareLayoutXlf(playback.currentLayout);
76622
- case 23:
76812
+ case 35:
76623
76813
  this.currentLayout = _context11.sent;
76624
76814
  this.currentLayoutId = this.currentLayout.layoutId;
76625
76815
  _context11.t0 = this;
76626
- _context11.next = 28;
76816
+ _context11.next = 40;
76627
76817
  return this.prepareLayoutXlf(playback.nextLayout);
76628
- case 28:
76818
+ case 40:
76629
76819
  _context11.t1 = _context11.sent;
76630
- _context11.next = 31;
76820
+ _context11.next = 43;
76631
76821
  return _context11.t0.prepareForSsp.call(_context11.t0, _context11.t1);
76632
- case 31:
76822
+ case 43:
76633
76823
  this.nextLayout = _context11.sent;
76634
- _context11.next = 51;
76824
+ _context11.next = 63;
76635
76825
  break;
76636
- case 34:
76826
+ case 46:
76637
76827
  if (!(this.currentLayout && this.isLayoutInDOM(this.currentLayout.containerName, this.currentLayout.index))) {
76638
- _context11.next = 39;
76828
+ _context11.next = 51;
76639
76829
  break;
76640
76830
  }
76641
76831
  this.currentLayout.inLoop = false;
76642
- _context11.next = 38;
76832
+ _context11.next = 50;
76643
76833
  return this.currentLayout.finishAllRegions();
76644
- case 38:
76834
+ case 50:
76645
76835
  this.currentLayout.removeLayout();
76646
- case 39:
76836
+ case 51:
76647
76837
  if (this.nextLayout && this.isLayoutInDOM(this.nextLayout.containerName, this.nextLayout.index)) {
76648
76838
  this.nextLayout.discardLayout(exports.LayoutPlaybackType.NEXT);
76649
76839
  }
76650
76840
  if (!playback.currentLayout) {
76651
- _context11.next = 43;
76841
+ _context11.next = 55;
76652
76842
  break;
76653
76843
  }
76654
- _context11.next = 43;
76844
+ _context11.next = 55;
76655
76845
  return prepareNewCurrentLayout();
76656
- case 43:
76846
+ case 55:
76657
76847
  if (!playback.nextLayout) {
76658
- _context11.next = 51;
76848
+ _context11.next = 63;
76659
76849
  break;
76660
76850
  }
76661
76851
  _context11.t2 = this;
76662
- _context11.next = 47;
76852
+ _context11.next = 59;
76663
76853
  return this.prepareLayoutXlf(playback.nextLayout);
76664
- case 47:
76854
+ case 59:
76665
76855
  _context11.t3 = _context11.sent;
76666
- _context11.next = 50;
76856
+ _context11.next = 62;
76667
76857
  return _context11.t2.prepareForSsp.call(_context11.t2, _context11.t3);
76668
- case 50:
76858
+ case 62:
76669
76859
  this.nextLayout = _context11.sent;
76670
- case 51:
76671
- _context11.next = 53;
76860
+ case 63:
76861
+ _context11.next = 65;
76672
76862
  return this.playSchedules(this);
76673
- case 53:
76674
- _context11.next = 67;
76863
+ case 65:
76864
+ _context11.next = 79;
76675
76865
  break;
76676
- case 55:
76866
+ case 67:
76677
76867
  // Remove next layout if it is in the DOM
76678
76868
  if (this.nextLayout && this.isLayoutInDOM(this.nextLayout.containerName, this.nextLayout.index)) {
76679
76869
  this.nextLayout.discardLayout(exports.LayoutPlaybackType.NEXT);
@@ -76700,22 +76890,22 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76700
76890
  // nextLayout keeps the cycle in order; the slot after that will be
76701
76891
  // prepared by the normal prepareLayouts() call at transition time.
76702
76892
  if (!playback.currentLayout) {
76703
- _context11.next = 66;
76893
+ _context11.next = 78;
76704
76894
  break;
76705
76895
  }
76706
76896
  this.currentLayoutIndex = playback.currentLayoutIndex;
76707
76897
  _context11.t4 = this;
76708
- _context11.next = 62;
76898
+ _context11.next = 74;
76709
76899
  return this.prepareLayoutXlf(playback.currentLayout);
76710
- case 62:
76900
+ case 74:
76711
76901
  _context11.t5 = _context11.sent;
76712
- _context11.next = 65;
76902
+ _context11.next = 77;
76713
76903
  return _context11.t4.prepareForSsp.call(_context11.t4, _context11.t5);
76714
- case 65:
76904
+ case 77:
76715
76905
  this.nextLayout = _context11.sent;
76716
- case 66:
76906
+ case 78:
76717
76907
  console.debug('>>>> XLR.debug XLR::updateLoop > updated nextLayout', this.nextLayout);
76718
- case 67:
76908
+ case 79:
76719
76909
  case "end":
76720
76910
  return _context11.stop();
76721
76911
  }
@@ -76745,7 +76935,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76745
76935
  };
76746
76936
  }();
76747
76937
  xlrObject.parseLayouts = function (hasChanged) {
76748
- var _this$currentLayout2, _this$currentLayout3;
76938
+ var _this$currentLayout2, _this$currentLayout3, _this$currentLayout4, _this$nextLayout, _currentLayout2, _nextLayout2;
76749
76939
  var _currentLayout;
76750
76940
  var _nextLayout;
76751
76941
  var _hasDefaultOnly = hasDefaultOnly(this.inputLayouts);
@@ -76758,17 +76948,28 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76758
76948
  if (this.isSspEnabled && ((_this$currentLayout3 = this.currentLayout) === null || _this$currentLayout3 === void 0 ? void 0 : _this$currentLayout3.layoutId) === -1) {
76759
76949
  isCurrentLayoutValid = true;
76760
76950
  }
76951
+ console.debug('XLR::parseLayouts', {
76952
+ currentLayoutId: (_this$currentLayout4 = this.currentLayout) === null || _this$currentLayout4 === void 0 ? void 0 : _this$currentLayout4.layoutId,
76953
+ currentLayoutIndex: this.currentLayoutIndex,
76954
+ nextLayoutId: (_this$nextLayout = this.nextLayout) === null || _this$nextLayout === void 0 ? void 0 : _this$nextLayout.layoutId,
76955
+ isCurrentLayoutValid: isCurrentLayoutValid,
76956
+ hasChanged: !!hasChanged,
76957
+ inputLayoutsCount: this.inputLayouts.length,
76958
+ inputLayoutIds: this.inputLayouts.map(function (l) {
76959
+ return l.layoutId;
76960
+ }).join(', ')
76961
+ });
76761
76962
  _currentLayout = this.currentLayout;
76762
76963
  if (this.currentLayout && this.nextLayout) {
76763
76964
  // Both currentLayout and nextLayout has values
76764
76965
  if (hasLayout) {
76765
76966
  if (!isCurrentLayoutValid) {
76766
- var _this$nextLayout;
76967
+ var _this$nextLayout2;
76767
76968
  // Check if currentLayout.state is PLAYED,
76768
76969
  // then, validate nextLayout and if valid,
76769
76970
  // proceed to nextLayout as new currentLayout
76770
76971
  // Else, go back to first layout in the loop
76771
- if (this.currentLayout.state === exports.ELayoutState.PLAYED && isLayoutValid(this.inputLayouts, (_this$nextLayout = this.nextLayout) === null || _this$nextLayout === void 0 ? void 0 : _this$nextLayout.layoutId)) {
76972
+ if (this.currentLayout.state === exports.ELayoutState.PLAYED && isLayoutValid(this.inputLayouts, (_this$nextLayout2 = this.nextLayout) === null || _this$nextLayout2 === void 0 ? void 0 : _this$nextLayout2.layoutId)) {
76772
76973
  // Get nextLayout from updated loop
76773
76974
  var tempNextLayoutIndex = getLayoutIndexByLayoutId(this.inputLayouts, this.nextLayout.layoutId);
76774
76975
  _currentLayoutIndex = tempNextLayoutIndex !== null && tempNextLayoutIndex !== void 0 ? tempNextLayoutIndex : 0;
@@ -76812,7 +77013,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76812
77013
  _nextLayout = setLayoutIndex(_nextLayout, _nextLayoutIndex);
76813
77014
  }
76814
77015
  } else {
76815
- var _this$currentLayout4, _this$currentLayout5;
77016
+ var _this$currentLayout5, _this$currentLayout6;
76816
77017
  _currentLayout = this.nextLayout;
76817
77018
  _currentLayoutIndex = _currentLayout.index;
76818
77019
  // updateLoop can re-queue the same index that is currently
@@ -76822,7 +77023,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76822
77023
  // whether the queued next-to-current is at the same index as the
76823
77024
  // layout that just finished, and advance past it so the following
76824
77025
  // slot (e.g. an SSP that now has an ad) becomes current instead.
76825
- if (this.inputLayouts.length > 1 && (_this$currentLayout4 = this.currentLayout) !== null && _this$currentLayout4 !== void 0 && _this$currentLayout4.done && _currentLayoutIndex === ((_this$currentLayout5 = this.currentLayout) === null || _this$currentLayout5 === void 0 ? void 0 : _this$currentLayout5.index)) {
77026
+ if (this.inputLayouts.length > 1 && (_this$currentLayout5 = this.currentLayout) !== null && _this$currentLayout5 !== void 0 && _this$currentLayout5.done && _currentLayoutIndex === ((_this$currentLayout6 = this.currentLayout) === null || _this$currentLayout6 === void 0 ? void 0 : _this$currentLayout6.index)) {
76826
77027
  _currentLayoutIndex = (_currentLayoutIndex + 1) % this.inputLayouts.length;
76827
77028
  _currentLayout = this.getLayout(this.inputLayouts[_currentLayoutIndex]);
76828
77029
  _currentLayout = setLayoutIndex(_currentLayout, _currentLayoutIndex);
@@ -76859,6 +77060,12 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76859
77060
  _currentLayout.xlr = this;
76860
77061
  _nextLayout.xlr = this;
76861
77062
  }
77063
+ console.debug('XLR::parseLayouts result', {
77064
+ currentLayoutId: (_currentLayout2 = _currentLayout) === null || _currentLayout2 === void 0 ? void 0 : _currentLayout2.layoutId,
77065
+ currentLayoutIndex: _currentLayoutIndex,
77066
+ nextLayoutId: (_nextLayout2 = _nextLayout) === null || _nextLayout2 === void 0 ? void 0 : _nextLayout2.layoutId,
77067
+ nextLayoutIndex: _nextLayoutIndex
77068
+ });
76862
77069
  return {
76863
77070
  currentLayout: _currentLayout,
76864
77071
  nextLayout: _nextLayout,
@@ -76870,7 +77077,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76870
77077
  };
76871
77078
  xlrObject.getLayout = function (inputLayout) {
76872
77079
  var isCMS = this.config.platform === exports.ConsumerPlatform.CMS;
76873
- if (!isCMS && Object.keys(this.uniqueLayouts).length === 0) {
77080
+ if (!isCMS && this.uniqueLayouts.size === 0) {
76874
77081
  return;
76875
77082
  }
76876
77083
  var _layout = {};
@@ -76887,7 +77094,13 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76887
77094
  activeLayout.id = activeLayout.layoutId;
76888
77095
  }
76889
77096
  } else {
76890
- activeLayout = _objectSpread2({}, this.uniqueLayouts[inputLayout.layoutId]);
77097
+ var layoutFromUniqueLayouts = this.uniqueLayouts.get(String(inputLayout.layoutId));
77098
+ console.debug('XLR::getLayout > layoutFromUniqueLayouts', {
77099
+ layoutFromUniqueLayouts: layoutFromUniqueLayouts,
77100
+ inputLayout: inputLayout,
77101
+ uniqueLayouts: this.uniqueLayouts
77102
+ });
77103
+ activeLayout = layoutFromUniqueLayouts ? _objectSpread2({}, layoutFromUniqueLayouts) : _objectSpread2({}, inputLayout);
76891
77104
  }
76892
77105
  _layout = _objectSpread2(_objectSpread2({}, _layout), activeLayout);
76893
77106
  console.debug('XLR::getLayout > activeLayout from uniqueLayouts', {
@@ -76904,10 +77117,10 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76904
77117
  return iLayout;
76905
77118
  };
76906
77119
  xlrObject.getLayoutById = function (layoutId, layoutIndex) {
76907
- if (!layoutId || Object.keys(this.uniqueLayouts).length === 0) {
77120
+ if (!layoutId || this.uniqueLayouts.size === 0 || !this.uniqueLayouts.has(String(layoutId))) {
76908
77121
  return undefined;
76909
77122
  }
76910
- var _layout = _objectSpread2(_objectSpread2({}, initialLayout), this.uniqueLayouts[layoutId]);
77123
+ var _layout = _objectSpread2(_objectSpread2({}, initialLayout), this.uniqueLayouts.get(String(layoutId)));
76911
77124
  // Set layout index if available
76912
77125
  if (layoutIndex) {
76913
77126
  _layout.index = layoutIndex;
@@ -76915,71 +77128,79 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76915
77128
  return _layout;
76916
77129
  };
76917
77130
  xlrObject.prepareLayouts = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee14() {
76918
- var _layoutPlayback$curre, _layoutPlayback$curre2;
77131
+ var _layoutPlayback$curre, _layoutPlayback$nextL, _layoutPlayback$curre2, _layoutPlayback$curre3, _layouts$, _layouts$2, _layouts$3, _layouts$4;
76919
77132
  var self, layoutPlayback, currentLayoutXlf, wasCurrentReused, nextLayoutXlf, layouts;
76920
77133
  return _regeneratorRuntime().wrap(function _callee14$(_context14) {
76921
77134
  while (1) switch (_context14.prev = _context14.next) {
76922
77135
  case 0:
76923
77136
  self = xlrObject;
76924
77137
  if (!this.isUpdatingLoop) {
76925
- _context14.next = 3;
77138
+ _context14.next = 4;
76926
77139
  break;
76927
77140
  }
77141
+ console.debug('XLR::prepareLayouts - skipped (isUpdatingLoop)');
76928
77142
  return _context14.abrupt("return", Promise.resolve(self));
76929
- case 3:
77143
+ case 4:
76930
77144
  layoutPlayback = self.parseLayouts(); // Don't prepare layout if it's just the splash screen
76931
77145
  if (!(self.inputLayouts.length === 1 && self.inputLayouts[0].layoutId === 0)) {
76932
- _context14.next = 6;
77146
+ _context14.next = 8;
76933
77147
  break;
76934
77148
  }
77149
+ console.debug('XLR::prepareLayouts - skipped (splash screen only)');
76935
77150
  return _context14.abrupt("return", Promise.resolve(self));
76936
- case 6:
76937
- console.debug('??? XLR.debug prepareLayouts::playback', {
76938
- layoutPlayback: layoutPlayback,
76939
- shouldParse: false
77151
+ case 8:
77152
+ console.debug('XLR::prepareLayouts', {
77153
+ currentLayoutId: (_layoutPlayback$curre = layoutPlayback.currentLayout) === null || _layoutPlayback$curre === void 0 ? void 0 : _layoutPlayback$curre.layoutId,
77154
+ currentLayoutIndex: layoutPlayback.currentLayoutIndex,
77155
+ nextLayoutId: (_layoutPlayback$nextL = layoutPlayback.nextLayout) === null || _layoutPlayback$nextL === void 0 ? void 0 : _layoutPlayback$nextL.layoutId,
77156
+ nextLayoutIndex: layoutPlayback.nextLayoutIndex
76940
77157
  });
76941
- self.currentLayoutId = (_layoutPlayback$curre = layoutPlayback.currentLayout) === null || _layoutPlayback$curre === void 0 ? void 0 : _layoutPlayback$curre.layoutId;
77158
+ self.currentLayoutId = (_layoutPlayback$curre2 = layoutPlayback.currentLayout) === null || _layoutPlayback$curre2 === void 0 ? void 0 : _layoutPlayback$curre2.layoutId;
76942
77159
  // Only reuse the existing Layout instance if it is fully healthy —
76943
77160
  // a done=true instance was removed from the DOM (e.g. an SSP slot that
76944
77161
  // had no ad), and an empty-XLF instance has no regions so it can never
76945
77162
  // advance the cycle. In either case re-prepare from scratch so we get
76946
77163
  // a fresh request (which may now have a valid ad / XLF).
76947
- if (!((_layoutPlayback$curre2 = layoutPlayback.currentLayout) !== null && _layoutPlayback$curre2 !== void 0 && _layoutPlayback$curre2.layoutNode && !layoutPlayback.currentLayout.done && layoutPlayback.currentLayout.xlfString !== '')) {
76948
- _context14.next = 12;
77164
+ if (!((_layoutPlayback$curre3 = layoutPlayback.currentLayout) !== null && _layoutPlayback$curre3 !== void 0 && _layoutPlayback$curre3.layoutNode && !layoutPlayback.currentLayout.done && layoutPlayback.currentLayout.xlfString !== '')) {
77165
+ _context14.next = 14;
76949
77166
  break;
76950
77167
  }
76951
77168
  _context14.t0 = layoutPlayback.currentLayout;
76952
- _context14.next = 15;
77169
+ _context14.next = 17;
76953
77170
  break;
76954
- case 12:
76955
- _context14.next = 14;
76956
- return self.prepareLayoutXlf(layoutPlayback.currentLayout);
76957
77171
  case 14:
77172
+ _context14.next = 16;
77173
+ return self.prepareLayoutXlf(layoutPlayback.currentLayout);
77174
+ case 16:
76958
77175
  _context14.t0 = _context14.sent;
76959
- case 15:
77176
+ case 17:
76960
77177
  currentLayoutXlf = _context14.t0;
76961
77178
  // True when the same object was returned (reused); false when a fresh
76962
77179
  // Layout was constructed by prepareLayoutXlf above.
76963
77180
  wasCurrentReused = currentLayoutXlf === layoutPlayback.currentLayout;
76964
- _context14.next = 19;
77181
+ _context14.next = 21;
76965
77182
  return self.prepareLayoutXlf(layoutPlayback.nextLayout);
76966
- case 19:
77183
+ case 21:
76967
77184
  nextLayoutXlf = _context14.sent;
76968
77185
  _context14.t1 = Promise;
76969
77186
  _context14.t2 = currentLayoutXlf;
76970
- _context14.next = 24;
77187
+ _context14.next = 26;
76971
77188
  return self.prepareForSsp(nextLayoutXlf);
76972
- case 24:
77189
+ case 26:
76973
77190
  _context14.t3 = _context14.sent;
76974
77191
  _context14.t4 = [_context14.t2, _context14.t3];
76975
- _context14.next = 28;
77192
+ _context14.next = 30;
76976
77193
  return _context14.t1.all.call(_context14.t1, _context14.t4);
76977
- case 28:
77194
+ case 30:
76978
77195
  layouts = _context14.sent;
76979
77196
  if (!(self.isUpdatingLoop || layouts[0].done)) {
76980
- _context14.next = 33;
77197
+ _context14.next = 36;
76981
77198
  break;
76982
77199
  }
77200
+ console.debug('XLR::prepareLayouts - aborted (concurrent updateLoop)', {
77201
+ isUpdatingLoop: self.isUpdatingLoop,
77202
+ currentLayoutDone: layouts[0].done
77203
+ });
76983
77204
  // If currentLayout was freshly prepared (not reused from nextLayout),
76984
77205
  // its DOM element was just appended — discard it now so it does not
76985
77206
  // accumulate in screen_container. Also disposes any video.js players
@@ -76991,8 +77212,14 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
76991
77212
  nextLayoutXlf.discardLayout(exports.LayoutPlaybackType.NEXT);
76992
77213
  }
76993
77214
  return _context14.abrupt("return", Promise.resolve(self));
76994
- case 33:
76995
- console.debug('>>>>> XLR.debug prepared layout XLF', layouts);
77215
+ case 36:
77216
+ console.debug('XLR::prepareLayouts - layouts prepared', {
77217
+ currentLayoutId: (_layouts$ = layouts[0]) === null || _layouts$ === void 0 ? void 0 : _layouts$.layoutId,
77218
+ currentLayoutIndex: (_layouts$2 = layouts[0]) === null || _layouts$2 === void 0 ? void 0 : _layouts$2.index,
77219
+ nextLayoutId: (_layouts$3 = layouts[1]) === null || _layouts$3 === void 0 ? void 0 : _layouts$3.layoutId,
77220
+ nextLayoutIndex: (_layouts$4 = layouts[1]) === null || _layouts$4 === void 0 ? void 0 : _layouts$4.index,
77221
+ currentReused: wasCurrentReused
77222
+ });
76996
77223
  return _context14.abrupt("return", new Promise( /*#__PURE__*/function () {
76997
77224
  var _ref14 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee13(resolve) {
76998
77225
  return _regeneratorRuntime().wrap(function _callee13$(_context13) {
@@ -77026,7 +77253,7 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
77026
77253
  return _ref14.apply(this, arguments);
77027
77254
  };
77028
77255
  }()));
77029
- case 35:
77256
+ case 38:
77030
77257
  case "end":
77031
77258
  return _context14.stop();
77032
77259
  }
@@ -77189,33 +77416,48 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
77189
77416
  return _ref16.apply(this, arguments);
77190
77417
  };
77191
77418
  }();
77419
+ // Shared re-entry guard for all layout navigation methods.
77420
+ // Prevents a double-tap from advancing two layouts at once.
77421
+ var isNavigatingLayout = false;
77192
77422
  xlrObject.gotoPrevLayout = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee18() {
77193
77423
  var _this4 = this;
77194
- var _currentLayoutIndex, _assumedPrevIndex, _this$currentLayout6;
77424
+ var _currentLayoutIndex, _assumedPrevIndex, _this$currentLayout7;
77195
77425
  return _regeneratorRuntime().wrap(function _callee18$(_context18) {
77196
77426
  while (1) switch (_context18.prev = _context18.next) {
77197
77427
  case 0:
77428
+ if (!isNavigatingLayout) {
77429
+ _context18.next = 2;
77430
+ break;
77431
+ }
77432
+ return _context18.abrupt("return");
77433
+ case 2:
77434
+ isNavigatingLayout = true;
77435
+ _context18.prev = 3;
77198
77436
  _currentLayoutIndex = this.currentLayoutIndex;
77199
77437
  _assumedPrevIndex = _currentLayoutIndex - 1; // If previous layout is same as current layout or
77200
77438
  // if there's only one layout, do nothing
77201
77439
  if (!(_assumedPrevIndex < 0)) {
77202
- _context18.next = 4;
77440
+ _context18.next = 8;
77203
77441
  break;
77204
77442
  }
77205
77443
  return _context18.abrupt("return");
77206
- case 4:
77444
+ case 8:
77207
77445
  console.debug('XLR::gotoPrevLayout', {
77208
- previousLayoutIndex: _assumedPrevIndex,
77209
- method: 'XLR::gotoPrevLayout',
77210
- shouldParse: false
77446
+ previousLayoutIndex: _assumedPrevIndex
77211
77447
  });
77212
77448
  if (!Boolean(this.inputLayouts[_assumedPrevIndex])) {
77213
- _context18.next = 10;
77449
+ _context18.next = 15;
77214
77450
  break;
77215
77451
  }
77216
- _context18.next = 8;
77217
- return (_this$currentLayout6 = this.currentLayout) === null || _this$currentLayout6 === void 0 ? void 0 : _this$currentLayout6.finishAllRegions();
77218
- case 8:
77452
+ // Prevent the natural layout-end handler from also calling
77453
+ // prepareLayouts() when finishAllRegions() causes the layout
77454
+ // 'end' event to fire.
77455
+ if (this.currentLayout) {
77456
+ this.currentLayout.inLoop = false;
77457
+ }
77458
+ _context18.next = 13;
77459
+ return (_this$currentLayout7 = this.currentLayout) === null || _this$currentLayout7 === void 0 ? void 0 : _this$currentLayout7.finishAllRegions();
77460
+ case 13:
77219
77461
  // and set the previous layout as current layout
77220
77462
  this.currentLayoutIndex = _assumedPrevIndex;
77221
77463
  this.prepareLayouts().then( /*#__PURE__*/function () {
@@ -77235,30 +77477,366 @@ ${segmentInfoString(segmentInfo)}`); // If there's an init segment associated wi
77235
77477
  return _ref18.apply(this, arguments);
77236
77478
  };
77237
77479
  }());
77238
- case 10:
77480
+ case 15:
77481
+ _context18.prev = 15;
77482
+ isNavigatingLayout = false;
77483
+ return _context18.finish(15);
77484
+ case 18:
77239
77485
  case "end":
77240
77486
  return _context18.stop();
77241
77487
  }
77242
- }, _callee18, this);
77488
+ }, _callee18, this, [[3,, 15, 18]]);
77243
77489
  }));
77244
- xlrObject.gotoNextLayout = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee19() {
77245
- var _xlrObject$currentLay2;
77246
- return _regeneratorRuntime().wrap(function _callee19$(_context19) {
77247
- while (1) switch (_context19.prev = _context19.next) {
77490
+ xlrObject.gotoNextLayout = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee20() {
77491
+ var _this5 = this;
77492
+ var _this$currentLayout8, nextIndex;
77493
+ return _regeneratorRuntime().wrap(function _callee20$(_context20) {
77494
+ while (1) switch (_context20.prev = _context20.next) {
77248
77495
  case 0:
77496
+ if (!isNavigatingLayout) {
77497
+ _context20.next = 2;
77498
+ break;
77499
+ }
77500
+ return _context20.abrupt("return");
77501
+ case 2:
77502
+ isNavigatingLayout = true;
77503
+ _context20.prev = 3;
77504
+ nextIndex = this.currentLayoutIndex + 1;
77505
+ if (Boolean(this.inputLayouts[nextIndex])) {
77506
+ _context20.next = 7;
77507
+ break;
77508
+ }
77509
+ return _context20.abrupt("return");
77510
+ case 7:
77249
77511
  console.debug('XLR::gotoNextLayout', {
77250
- nextLayoutIndex: this.currentLayoutIndex + 1,
77251
- method: 'XLR::gotoNextLayout',
77252
- shouldParse: false
77512
+ nextLayoutIndex: nextIndex
77253
77513
  });
77254
- _context19.next = 3;
77255
- return (_xlrObject$currentLay2 = xlrObject.currentLayout) === null || _xlrObject$currentLay2 === void 0 ? void 0 : _xlrObject$currentLay2.finishAllRegions();
77256
- case 3:
77514
+ if (this.currentLayout) {
77515
+ this.currentLayout.inLoop = false;
77516
+ }
77517
+ _context20.next = 11;
77518
+ return (_this$currentLayout8 = this.currentLayout) === null || _this$currentLayout8 === void 0 ? void 0 : _this$currentLayout8.finishAllRegions();
77519
+ case 11:
77520
+ this.currentLayoutIndex = nextIndex;
77521
+ this.prepareLayouts().then( /*#__PURE__*/function () {
77522
+ var _ref20 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee19(xlr) {
77523
+ return _regeneratorRuntime().wrap(function _callee19$(_context19) {
77524
+ while (1) switch (_context19.prev = _context19.next) {
77525
+ case 0:
77526
+ _context19.next = 2;
77527
+ return _this5.playSchedules(xlr);
77528
+ case 2:
77529
+ case "end":
77530
+ return _context19.stop();
77531
+ }
77532
+ }, _callee19);
77533
+ }));
77534
+ return function (_x14) {
77535
+ return _ref20.apply(this, arguments);
77536
+ };
77537
+ }());
77538
+ case 13:
77539
+ _context20.prev = 13;
77540
+ isNavigatingLayout = false;
77541
+ return _context20.finish(13);
77542
+ case 16:
77257
77543
  case "end":
77258
- return _context19.stop();
77544
+ return _context20.stop();
77259
77545
  }
77260
- }, _callee19, this);
77546
+ }, _callee20, this, [[3,, 13, 16]]);
77261
77547
  }));
77548
+ xlrObject.gotoLayoutByCode = /*#__PURE__*/function () {
77549
+ var _ref21 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee22(layoutCode) {
77550
+ var _this6 = this;
77551
+ var _this$currentLayout9, targetIndex, parsedLayouts, _loop, _ret, _i, _parsedLayouts, parser, i, _inputLayout$getXlf, _doc$documentElement, inputLayout, xlfString, url, res, doc, foundCode;
77552
+ return _regeneratorRuntime().wrap(function _callee22$(_context23) {
77553
+ while (1) switch (_context23.prev = _context23.next) {
77554
+ case 0:
77555
+ if (!isNavigatingLayout) {
77556
+ _context23.next = 2;
77557
+ break;
77558
+ }
77559
+ return _context23.abrupt("return");
77560
+ case 2:
77561
+ isNavigatingLayout = true;
77562
+ _context23.prev = 3;
77563
+ targetIndex = -1; // 1. Check the two already-parsed layouts first (zero fetch cost)
77564
+ parsedLayouts = [this.layouts['current'], this.layouts['next']];
77565
+ _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
77566
+ var _layout$layoutNode;
77567
+ var layout, code;
77568
+ return _regeneratorRuntime().wrap(function _loop$(_context21) {
77569
+ while (1) switch (_context21.prev = _context21.next) {
77570
+ case 0:
77571
+ layout = _parsedLayouts[_i];
77572
+ if (layout) {
77573
+ _context21.next = 3;
77574
+ break;
77575
+ }
77576
+ return _context21.abrupt("return", 0);
77577
+ case 3:
77578
+ code = (_layout$layoutNode = layout.layoutNode) === null || _layout$layoutNode === void 0 || (_layout$layoutNode = _layout$layoutNode.documentElement) === null || _layout$layoutNode === void 0 ? void 0 : _layout$layoutNode.getAttribute('code');
77579
+ if (!(code === layoutCode)) {
77580
+ _context21.next = 7;
77581
+ break;
77582
+ }
77583
+ targetIndex = _this6.inputLayouts.findIndex(function (i) {
77584
+ return i.layoutId === layout.layoutId;
77585
+ });
77586
+ return _context21.abrupt("return", 1);
77587
+ case 7:
77588
+ case "end":
77589
+ return _context21.stop();
77590
+ }
77591
+ }, _loop);
77592
+ });
77593
+ _i = 0, _parsedLayouts = parsedLayouts;
77594
+ case 8:
77595
+ if (!(_i < _parsedLayouts.length)) {
77596
+ _context23.next = 18;
77597
+ break;
77598
+ }
77599
+ return _context23.delegateYield(_loop(), "t0", 10);
77600
+ case 10:
77601
+ _ret = _context23.t0;
77602
+ if (!(_ret === 0)) {
77603
+ _context23.next = 13;
77604
+ break;
77605
+ }
77606
+ return _context23.abrupt("continue", 15);
77607
+ case 13:
77608
+ if (!(_ret === 1)) {
77609
+ _context23.next = 15;
77610
+ break;
77611
+ }
77612
+ return _context23.abrupt("break", 18);
77613
+ case 15:
77614
+ _i++;
77615
+ _context23.next = 8;
77616
+ break;
77617
+ case 18:
77618
+ if (!(targetIndex === -1)) {
77619
+ _context23.next = 60;
77620
+ break;
77621
+ }
77622
+ parser = new DOMParser();
77623
+ i = 0;
77624
+ case 21:
77625
+ if (!(i < this.inputLayouts.length)) {
77626
+ _context23.next = 60;
77627
+ break;
77628
+ }
77629
+ inputLayout = this.inputLayouts[i]; // Fast check: code pre-populated by the player (no fetch needed)
77630
+ if (!(inputLayout.code !== undefined)) {
77631
+ _context23.next = 28;
77632
+ break;
77633
+ }
77634
+ if (!(inputLayout.code === layoutCode)) {
77635
+ _context23.next = 27;
77636
+ break;
77637
+ }
77638
+ targetIndex = i;
77639
+ return _context23.abrupt("break", 60);
77640
+ case 27:
77641
+ return _context23.abrupt("continue", 57);
77642
+ case 28:
77643
+ xlfString = void 0; // Prefer getXlf() when available (e.g. CMS platform)
77644
+ xlfString = (_inputLayout$getXlf = inputLayout.getXlf) === null || _inputLayout$getXlf === void 0 ? void 0 : _inputLayout$getXlf.call(inputLayout);
77645
+ // Otherwise fetch from the local file server (Electron / ChromeOS)
77646
+ if (!(!xlfString && this.config.appHost && inputLayout.path)) {
77647
+ _context23.next = 49;
77648
+ break;
77649
+ }
77650
+ url = this.config.appHost + inputLayout.path;
77651
+ console.debug('[gotoLayoutByCode] Fetching XLF for layoutId', inputLayout.layoutId, url);
77652
+ _context23.prev = 33;
77653
+ _context23.next = 36;
77654
+ return fetch(url);
77655
+ case 36:
77656
+ res = _context23.sent;
77657
+ if (res.ok) {
77658
+ _context23.next = 40;
77659
+ break;
77660
+ }
77661
+ console.debug('[gotoLayoutByCode] Fetch non-OK', res.status, url);
77662
+ return _context23.abrupt("continue", 57);
77663
+ case 40:
77664
+ _context23.next = 42;
77665
+ return res.text();
77666
+ case 42:
77667
+ xlfString = _context23.sent;
77668
+ _context23.next = 49;
77669
+ break;
77670
+ case 45:
77671
+ _context23.prev = 45;
77672
+ _context23.t1 = _context23["catch"](33);
77673
+ console.debug('[gotoLayoutByCode] Fetch error for', url, _context23.t1);
77674
+ return _context23.abrupt("continue", 57);
77675
+ case 49:
77676
+ if (xlfString) {
77677
+ _context23.next = 52;
77678
+ break;
77679
+ }
77680
+ console.debug('[gotoLayoutByCode] No XLF for layoutId', inputLayout.layoutId, 'path:', inputLayout.path);
77681
+ return _context23.abrupt("continue", 57);
77682
+ case 52:
77683
+ doc = parser.parseFromString(xlfString, 'text/xml');
77684
+ foundCode = (_doc$documentElement = doc.documentElement) === null || _doc$documentElement === void 0 ? void 0 : _doc$documentElement.getAttribute('code');
77685
+ if (!(foundCode === layoutCode)) {
77686
+ _context23.next = 57;
77687
+ break;
77688
+ }
77689
+ targetIndex = i;
77690
+ return _context23.abrupt("break", 60);
77691
+ case 57:
77692
+ i++;
77693
+ _context23.next = 21;
77694
+ break;
77695
+ case 60:
77696
+ if (!(targetIndex === -1)) {
77697
+ _context23.next = 63;
77698
+ break;
77699
+ }
77700
+ console.warn('XLR::gotoLayoutByCode - layout not found for code:', layoutCode);
77701
+ return _context23.abrupt("return");
77702
+ case 63:
77703
+ console.debug('XLR::gotoLayoutByCode', {
77704
+ layoutCode: layoutCode,
77705
+ targetIndex: targetIndex
77706
+ });
77707
+ // Prevent the natural layout-end handler from racing with our own
77708
+ // prepareLayouts() call (same pattern as gotoPrevLayout/gotoNextLayout).
77709
+ if (this.currentLayout) {
77710
+ this.currentLayout.inLoop = false;
77711
+ }
77712
+ _context23.next = 67;
77713
+ return (_this$currentLayout9 = this.currentLayout) === null || _this$currentLayout9 === void 0 ? void 0 : _this$currentLayout9.finishAllRegions();
77714
+ case 67:
77715
+ this.currentLayoutIndex = targetIndex;
77716
+ this.prepareLayouts().then( /*#__PURE__*/function () {
77717
+ var _ref22 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee21(xlr) {
77718
+ return _regeneratorRuntime().wrap(function _callee21$(_context22) {
77719
+ while (1) switch (_context22.prev = _context22.next) {
77720
+ case 0:
77721
+ _context22.next = 2;
77722
+ return _this6.playSchedules(xlr);
77723
+ case 2:
77724
+ case "end":
77725
+ return _context22.stop();
77726
+ }
77727
+ }, _callee21);
77728
+ }));
77729
+ return function (_x16) {
77730
+ return _ref22.apply(this, arguments);
77731
+ };
77732
+ }());
77733
+ case 69:
77734
+ _context23.prev = 69;
77735
+ isNavigatingLayout = false;
77736
+ return _context23.finish(69);
77737
+ case 72:
77738
+ case "end":
77739
+ return _context23.stop();
77740
+ }
77741
+ }, _callee22, this, [[3,, 69, 72], [33, 45]]);
77742
+ }));
77743
+ return function (_x15) {
77744
+ return _ref21.apply(this, arguments);
77745
+ };
77746
+ }();
77747
+ xlrObject.playInterruptLayout = /*#__PURE__*/function () {
77748
+ var _ref23 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee23(inputLayout) {
77749
+ var _this7 = this;
77750
+ var _this$currentLayout10, _this$currentLayout11, resumeIndex, savedNextLayout, interruptKey, wasInUniqueLayouts, interruptILayout, cleanup;
77751
+ return _regeneratorRuntime().wrap(function _callee23$(_context24) {
77752
+ while (1) switch (_context24.prev = _context24.next) {
77753
+ case 0:
77754
+ if (!isNavigatingLayout) {
77755
+ _context24.next = 2;
77756
+ break;
77757
+ }
77758
+ return _context24.abrupt("return");
77759
+ case 2:
77760
+ isNavigatingLayout = true;
77761
+ _context24.prev = 3;
77762
+ resumeIndex = this.currentLayoutIndex; // Save B (the layout that was queued to play after A) before stopping A.
77763
+ // After the interrupt ends, parseLayouts() will use this to resume the loop.
77764
+ savedNextLayout = this.nextLayout;
77765
+ console.debug('[navLayout] XLR::playInterruptLayout - Starting interrupt', {
77766
+ interruptLayoutId: inputLayout.layoutId,
77767
+ resumeIndex: resumeIndex,
77768
+ currentLayoutId: (_this$currentLayout10 = this.currentLayout) === null || _this$currentLayout10 === void 0 ? void 0 : _this$currentLayout10.layoutId,
77769
+ resumeNextLayoutId: savedNextLayout === null || savedNextLayout === void 0 ? void 0 : savedNextLayout.layoutId
77770
+ });
77771
+ // Prevent A's end handler from calling prepareLayouts (we take over).
77772
+ if (this.currentLayout) {
77773
+ this.currentLayout.inLoop = false;
77774
+ }
77775
+ _context24.next = 10;
77776
+ return (_this$currentLayout11 = this.currentLayout) === null || _this$currentLayout11 === void 0 ? void 0 : _this$currentLayout11.finishAllRegions();
77777
+ case 10:
77778
+ // Register interrupt in uniqueLayouts so getLayout()/prepareLayoutXlf() resolve it.
77779
+ // Do NOT splice into inputLayouts — keeping the original loop intact means
77780
+ // parseLayouts() will see the interrupt as "not in loop" (isCurrentLayoutValid=false)
77781
+ // after it ends, and will correctly advance to savedNextLayout (B).
77782
+ interruptKey = String(inputLayout.layoutId);
77783
+ wasInUniqueLayouts = this.uniqueLayouts.has(interruptKey);
77784
+ if (!wasInUniqueLayouts) {
77785
+ this.uniqueLayouts.set(interruptKey, _objectSpread2(_objectSpread2({}, inputLayout), {}, {
77786
+ index: resumeIndex,
77787
+ id: inputLayout.layoutId
77788
+ }));
77789
+ }
77790
+ // Prepare the interrupt ILayout (fetches XLF, builds regions).
77791
+ _context24.next = 15;
77792
+ return this.prepareLayoutXlf(this.getLayout(inputLayout));
77793
+ case 15:
77794
+ interruptILayout = _context24.sent;
77795
+ // Wire into XLR so playLayouts picks up the interrupt as current.
77796
+ // inLoop=true lets the interrupt's own end handler call prepareLayouts normally.
77797
+ interruptILayout.inLoop = true;
77798
+ this.layouts.current = interruptILayout;
77799
+ this.currentLayout = interruptILayout;
77800
+ this.currentLayoutId = interruptILayout.layoutId;
77801
+ // Restore nextLayout to B so after the interrupt ends, parseLayouts() resumes
77802
+ // the original loop from B (since interrupt.layoutId is not in inputLayouts,
77803
+ // parseLayouts sees it as invalid and advances to nextLayout).
77804
+ if (savedNextLayout) {
77805
+ this.layouts.next = savedNextLayout;
77806
+ this.nextLayout = savedNextLayout;
77807
+ }
77808
+ // Remove interrupt from uniqueLayouts once it ends.
77809
+ cleanup = this.emitter.on('layoutEnd', function (endedLayout) {
77810
+ if (endedLayout !== interruptILayout) return;
77811
+ cleanup();
77812
+ if (!wasInUniqueLayouts) {
77813
+ _this7.uniqueLayouts["delete"](interruptKey);
77814
+ }
77815
+ console.debug('[navLayout] XLR::playInterruptLayout - Interrupt ended, resuming loop', {
77816
+ interruptLayoutId: inputLayout.layoutId,
77817
+ resumeNextLayoutId: savedNextLayout === null || savedNextLayout === void 0 ? void 0 : savedNextLayout.layoutId
77818
+ });
77819
+ });
77820
+ _context24.next = 24;
77821
+ return this.playSchedules(xlrObject);
77822
+ case 24:
77823
+ _context24.prev = 24;
77824
+ isNavigatingLayout = false;
77825
+ return _context24.finish(24);
77826
+ case 27:
77827
+ case "end":
77828
+ return _context24.stop();
77829
+ }
77830
+ }, _callee23, this, [[3,, 24, 27]]);
77831
+ }));
77832
+ return function (_x17) {
77833
+ return _ref23.apply(this, arguments);
77834
+ };
77835
+ }();
77836
+ xlrObject.triggerAction = function (triggerCode, widgetId) {
77837
+ var _this$currentLayout12;
77838
+ (_this$currentLayout12 = this.currentLayout) === null || _this$currentLayout12 === void 0 || (_this$currentLayout12 = _this$currentLayout12.actionController) === null || _this$currentLayout12 === void 0 || _this$currentLayout12.handleWebhookTrigger(triggerCode, widgetId);
77839
+ };
77262
77840
  xlrObject.updateInputLayout = function (layoutIndex, layout) {
77263
77841
  var xlrInputLayout = this.inputLayouts[layoutIndex];
77264
77842
  if (layout !== null) {