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