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