@xibosignage/xibo-layout-renderer 1.0.28 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Modules/ActionController/ActionController.d.ts +4 -1
- package/dist/xibo-layout-renderer.cjs.js +108 -35
- package/dist/xibo-layout-renderer.cjs.js.map +1 -1
- package/dist/xibo-layout-renderer.d.ts +4 -1
- package/dist/xibo-layout-renderer.esm.js +108 -35
- package/dist/xibo-layout-renderer.esm.js.map +1 -1
- package/dist/xibo-layout-renderer.js +108 -35
- package/dist/xibo-layout-renderer.min.js +7 -7
- package/dist/xibo-layout-renderer.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -72,12 +72,13 @@ declare class ActionController {
|
|
|
72
72
|
$actionControllerTitle: HTMLElement | null;
|
|
73
73
|
$actionsContainer: HTMLElement | null;
|
|
74
74
|
translations: any;
|
|
75
|
+
private keyboardHandler;
|
|
75
76
|
constructor(parent: ILayout, actions: Action[], options: InactOptions);
|
|
76
77
|
init(): void;
|
|
77
78
|
openLayoutInNewTab(layoutCode: string, options: InactOptions): void;
|
|
78
79
|
openLayoutInPlayer(layoutCode: string, _options: InactOptions): void;
|
|
79
80
|
prevOrNextLayout(targetId: string, actionType: string): void;
|
|
80
|
-
/** Change media in region (next/previous) */
|
|
81
|
+
/** Change media in region (next/previous) with wrap-around at boundaries. */
|
|
81
82
|
gotoMediaInRegion(regionId: string, actionType: string): void;
|
|
82
83
|
loadMediaInRegion(regionId: string, widgetId: string): void;
|
|
83
84
|
/** Run action based on action data */
|
|
@@ -88,6 +89,8 @@ declare class ActionController {
|
|
|
88
89
|
/** Dispatch an incoming webhook trigger to any matching actions on this layout. */
|
|
89
90
|
handleWebhookTrigger(triggerCode: string, widgetId?: string): void;
|
|
90
91
|
initKeyboardActions(): void;
|
|
92
|
+
/** Remove the keydown listener registered by initKeyboardActions. Call when the layout ends or is cancelled. */
|
|
93
|
+
removeKeyboardActions(): void;
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
declare function initRenderingDOM(targetContainer: Element | null): void;
|
|
@@ -73572,7 +73572,7 @@ function createMediaElement(mediaObject) {
|
|
|
73572
73572
|
cssText += "\n visibility: hidden;\n opacity: 0;\n z-index: 0;\n ";
|
|
73573
73573
|
}
|
|
73574
73574
|
$media.style.cssText = cssText;
|
|
73575
|
-
if (self.render === 'html' || self.mediaType === 'ticker' || self.mediaType === 'webpage') {
|
|
73575
|
+
if (self.mediaType !== 'spacer' && (self.render === 'html' || self.mediaType === 'ticker' || self.mediaType === 'webpage')) {
|
|
73576
73576
|
self.checkIframeStatus = true;
|
|
73577
73577
|
self.iframe = prepareIframe(self);
|
|
73578
73578
|
} else if (self.mediaType === "image") {
|
|
@@ -73720,8 +73720,6 @@ function prepareAudioMedia(media, region) {
|
|
|
73720
73720
|
region.html.appendChild(media.html);
|
|
73721
73721
|
}
|
|
73722
73722
|
function prepareHtmlMedia(media, region) {
|
|
73723
|
-
// Set state as false ( for now )
|
|
73724
|
-
media.ready = false;
|
|
73725
73723
|
if (media.html) {
|
|
73726
73724
|
var mediaId = getMediaId(media);
|
|
73727
73725
|
// Clean up old copy of the media
|
|
@@ -73733,14 +73731,16 @@ function prepareHtmlMedia(media, region) {
|
|
|
73733
73731
|
mediaId: mediaId,
|
|
73734
73732
|
mediaInRegion: mediaInRegion
|
|
73735
73733
|
});
|
|
73736
|
-
// Append iframe
|
|
73737
|
-
media.html.innerHTML = '';
|
|
73738
|
-
media.html.appendChild(media.iframe);
|
|
73739
73734
|
if (!mediaInRegion) {
|
|
73740
|
-
//
|
|
73735
|
+
// Append iframe and insert into region only when not already in the DOM.
|
|
73736
|
+
// Calling innerHTML = '' when the element is already present detaches the
|
|
73737
|
+
// iframe, causing the browser to reload its src unnecessarily (e.g. when
|
|
73738
|
+
// a media was preloaded then skipped by a navigation action).
|
|
73739
|
+
media.html.innerHTML = '';
|
|
73740
|
+
media.html.appendChild(media.iframe);
|
|
73741
73741
|
region.html.appendChild(media.html);
|
|
73742
|
-
media.ready = true;
|
|
73743
73742
|
}
|
|
73743
|
+
media.ready = true;
|
|
73744
73744
|
}
|
|
73745
73745
|
}
|
|
73746
73746
|
var FaultCodes;
|
|
@@ -74048,7 +74048,12 @@ var Media = /*#__PURE__*/function () {
|
|
|
74048
74048
|
var _this$sspImpressionUr, _this$sspErrorUrls;
|
|
74049
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
74050
|
}
|
|
74051
|
-
media.
|
|
74051
|
+
// Only advance the region if this media is still the active one.
|
|
74052
|
+
// A user-triggered next/prev action may have already moved currMedia
|
|
74053
|
+
// on, in which case the timer firing here would cause a double-advance.
|
|
74054
|
+
if (media === media.region.currMedia) {
|
|
74055
|
+
media.region.playNextMedia();
|
|
74056
|
+
}
|
|
74052
74057
|
});
|
|
74053
74058
|
this.on('cancelled', function (media) {
|
|
74054
74059
|
if (media.state === MediaState.CANCELLED) return;
|
|
@@ -74092,6 +74097,10 @@ var Media = /*#__PURE__*/function () {
|
|
|
74092
74097
|
key: "startMediaTimer",
|
|
74093
74098
|
value: function startMediaTimer(media) {
|
|
74094
74099
|
var _this2 = this;
|
|
74100
|
+
// Always reset the counter so a media replayed after cancellation runs
|
|
74101
|
+
// for its full duration rather than the residual time left from the
|
|
74102
|
+
// previous play.
|
|
74103
|
+
this.mediaTimeCount = 0;
|
|
74095
74104
|
var preloadTimeMs = 2000;
|
|
74096
74105
|
var preloadTimeBufferMs = media.duration * 1000 / 2 - preloadTimeMs;
|
|
74097
74106
|
var isPreparingNextMedia = false;
|
|
@@ -74967,11 +74976,19 @@ var Region = /*#__PURE__*/function () {
|
|
|
74967
74976
|
nxtMedia: (_this$nxtMedia2 = this.nxtMedia) === null || _this$nxtMedia2 === void 0 ? void 0 : _this$nxtMedia2.containerName
|
|
74968
74977
|
});
|
|
74969
74978
|
if (!this.layout.isOverlay && crossedEnd) {
|
|
74979
|
+
var _this$currMedia8;
|
|
74970
74980
|
this.finished();
|
|
74971
74981
|
if (this.layout.allEnded) {
|
|
74972
74982
|
console.debug('??? XLR.debug >> Region - playNextMedia - layout all ended');
|
|
74973
74983
|
return;
|
|
74974
74984
|
}
|
|
74985
|
+
// Freeze single-media HTML at its last state while waiting for other
|
|
74986
|
+
// regions to complete. The guard at the top only catches the second
|
|
74987
|
+
// call; this one catches the first completion when complete was just
|
|
74988
|
+
// set by finished() above.
|
|
74989
|
+
if (((_this$currMedia8 = this.currMedia) === null || _this$currMedia8 === void 0 ? void 0 : _this$currMedia8.render) === 'html' && this.totalMediaObjects === 1 && this.oldMedia === this.currMedia) {
|
|
74990
|
+
return;
|
|
74991
|
+
}
|
|
74975
74992
|
}
|
|
74976
74993
|
this.transitionNodes(this.oldMedia, this.currMedia);
|
|
74977
74994
|
}
|
|
@@ -74981,11 +74998,18 @@ var Region = /*#__PURE__*/function () {
|
|
|
74981
74998
|
if (this.currentMediaIndex <= 0 || this.ended) {
|
|
74982
74999
|
return;
|
|
74983
75000
|
}
|
|
75001
|
+
var interruptedMedia = this.currMedia;
|
|
74984
75002
|
this.oldMedia = this.currMedia;
|
|
74985
75003
|
this.currentMediaIndex -= 1;
|
|
74986
75004
|
this.currMedia = this.mediaObjects[this.currentMediaIndex];
|
|
74987
75005
|
this.nxtMedia = this.mediaObjects[(this.currentMediaIndex + 1) % this.totalMediaObjects];
|
|
74988
75006
|
this.complete = false;
|
|
75007
|
+
// Cancel the interrupted media after advancing currMedia, using the same
|
|
75008
|
+
// pattern as gotoMediaInRegion — emitting after the update ensures the
|
|
75009
|
+
// handler's (media === currMedia) guard correctly skips playNextMedia.
|
|
75010
|
+
if ((interruptedMedia === null || interruptedMedia === void 0 ? void 0 : interruptedMedia.state) === MediaState.PLAYING) {
|
|
75011
|
+
interruptedMedia.emitter.emit('cancelled', interruptedMedia);
|
|
75012
|
+
}
|
|
74989
75013
|
console.debug('region::playPreviousMedia', this);
|
|
74990
75014
|
this.transitionNodes(this.oldMedia, this.currMedia);
|
|
74991
75015
|
}
|
|
@@ -75063,6 +75087,7 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75063
75087
|
_defineProperty(this, "$actionControllerTitle", void 0);
|
|
75064
75088
|
_defineProperty(this, "$actionsContainer", void 0);
|
|
75065
75089
|
_defineProperty(this, "translations", {});
|
|
75090
|
+
_defineProperty(this, "keyboardHandler", null);
|
|
75066
75091
|
this.parent = parent;
|
|
75067
75092
|
this.actions = actions;
|
|
75068
75093
|
this.options = options;
|
|
@@ -75227,7 +75252,7 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75227
75252
|
this.parent.xlr.gotoPrevLayout();
|
|
75228
75253
|
}
|
|
75229
75254
|
}
|
|
75230
|
-
/** Change media in region (next/previous) */
|
|
75255
|
+
/** Change media in region (next/previous) with wrap-around at boundaries. */
|
|
75231
75256
|
}, {
|
|
75232
75257
|
key: "gotoMediaInRegion",
|
|
75233
75258
|
value: function gotoMediaInRegion(regionId, actionType) {
|
|
@@ -75235,15 +75260,34 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75235
75260
|
regionId: regionId,
|
|
75236
75261
|
actionType: actionType
|
|
75237
75262
|
});
|
|
75238
|
-
// Find target region
|
|
75239
75263
|
this.parent.regions.forEach(function (regionObj) {
|
|
75240
|
-
if (regionObj.id
|
|
75241
|
-
|
|
75242
|
-
|
|
75243
|
-
|
|
75244
|
-
|
|
75245
|
-
|
|
75264
|
+
if (regionObj.id !== regionId || regionObj.ended) return;
|
|
75265
|
+
var total = regionObj.totalMediaObjects;
|
|
75266
|
+
if (total === 0) return;
|
|
75267
|
+
// Snapshot the currently-playing media before updating currMedia so
|
|
75268
|
+
// we can cancel it cleanly after the region state is advanced.
|
|
75269
|
+
var interruptedMedia = regionObj.currMedia;
|
|
75270
|
+
// Compute new index with wrap-around. We do NOT delegate to
|
|
75271
|
+
// playNextMedia() / playPreviousMedia() here because those carry
|
|
75272
|
+
// normal playlist-cycle semantics (finished(), regionExpired()) that
|
|
75273
|
+
// must not fire during user-driven navigation.
|
|
75274
|
+
var newIndex = actionType === 'next' ? (regionObj.currentMediaIndex + 1) % total : (regionObj.currentMediaIndex - 1 + total) % total;
|
|
75275
|
+
regionObj.oldMedia = regionObj.currMedia;
|
|
75276
|
+
regionObj.currentMediaIndex = newIndex;
|
|
75277
|
+
regionObj.currMedia = regionObj.mediaObjects[newIndex];
|
|
75278
|
+
regionObj.nxtMedia = regionObj.mediaObjects[(newIndex + 1) % total];
|
|
75279
|
+
regionObj.complete = false;
|
|
75280
|
+
// Properly cancel the interrupted media AFTER updating currMedia.
|
|
75281
|
+
// Using 'cancelled' rather than bare clearInterval ensures state is
|
|
75282
|
+
// reset from PLAYING and mediaTimeCount is zeroed — without this,
|
|
75283
|
+
// returning to a cancelled media causes run() → 'start' to bail on
|
|
75284
|
+
// the state === PLAYING guard, leaving the region stuck indefinitely.
|
|
75285
|
+
// currMedia is already updated so the handler's guard
|
|
75286
|
+
// (media === media.region.currMedia) correctly skips playNextMedia.
|
|
75287
|
+
if ((interruptedMedia === null || interruptedMedia === void 0 ? void 0 : interruptedMedia.state) === MediaState.PLAYING) {
|
|
75288
|
+
interruptedMedia.emitter.emit('cancelled', interruptedMedia);
|
|
75246
75289
|
}
|
|
75290
|
+
regionObj.transitionNodes(regionObj.oldMedia, regionObj.currMedia);
|
|
75247
75291
|
});
|
|
75248
75292
|
}
|
|
75249
75293
|
}, {
|
|
@@ -75294,11 +75338,19 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75294
75338
|
console.debug('[ActionController::loadMediaInRegion] Target media already queued, skipping duplicate insertion');
|
|
75295
75339
|
return;
|
|
75296
75340
|
}
|
|
75297
|
-
// Cancel the
|
|
75298
|
-
//
|
|
75299
|
-
|
|
75300
|
-
|
|
75301
|
-
|
|
75341
|
+
// Cancel the interrupted media so it doesn't double-advance when the playlist
|
|
75342
|
+
// returns to it. currMedia has not been advanced yet at this point (playNextMedia
|
|
75343
|
+
// does that below), so we cannot use emitter.emit('cancelled') — the handler's
|
|
75344
|
+
// currMedia guard would fire and call playNextMedia a second time. Instead we
|
|
75345
|
+
// cancel directly: clear the timer and reset state so the 'start' handler does
|
|
75346
|
+
// not bail on the state === PLAYING guard when this media is replayed.
|
|
75347
|
+
if (((_targetRegion2 = targetRegion) === null || _targetRegion2 === void 0 || (_targetRegion2 = _targetRegion2.currMedia) === null || _targetRegion2 === void 0 ? void 0 : _targetRegion2.state) === MediaState.PLAYING) {
|
|
75348
|
+
var interruptedMedia = targetRegion.currMedia;
|
|
75349
|
+
if (interruptedMedia.mediaTimer) {
|
|
75350
|
+
clearInterval(interruptedMedia.mediaTimer);
|
|
75351
|
+
interruptedMedia.mediaTimer = undefined;
|
|
75352
|
+
}
|
|
75353
|
+
interruptedMedia.state = MediaState.CANCELLED;
|
|
75302
75354
|
}
|
|
75303
75355
|
// Reset complete so the HTML-media guard in playNextMedia() doesn't block
|
|
75304
75356
|
// the transition (that guard is for single-media loops, not navWidget injections).
|
|
@@ -75327,6 +75379,12 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75327
75379
|
}, {
|
|
75328
75380
|
key: "runAction",
|
|
75329
75381
|
value: function runAction(actionData, options) {
|
|
75382
|
+
// If this layout is no longer active (being cancelled or navigated away from),
|
|
75383
|
+
// discard the action so it doesn't interfere with the outgoing transition.
|
|
75384
|
+
// inLoop is set to false synchronously before finishAllRegions() in all nav paths.
|
|
75385
|
+
if (!this.parent.inLoop) {
|
|
75386
|
+
return;
|
|
75387
|
+
}
|
|
75330
75388
|
console.debug('[ActionController::runAction] Triggering action', {
|
|
75331
75389
|
actionData: actionData
|
|
75332
75390
|
});
|
|
@@ -75414,31 +75472,37 @@ var ActionController = /*#__PURE__*/function () {
|
|
|
75414
75472
|
key: "initKeyboardActions",
|
|
75415
75473
|
value: function initKeyboardActions() {
|
|
75416
75474
|
var self = this;
|
|
75417
|
-
// Store actions in a map
|
|
75418
75475
|
var keyActions = new Map();
|
|
75419
|
-
this.$actionController.querySelectorAll('.action[
|
|
75476
|
+
this.$actionController.querySelectorAll('.action[triggertype="keyPress"]').forEach(function ($el) {
|
|
75420
75477
|
var dataset = $el.dataset;
|
|
75421
75478
|
var code = dataset.triggercode;
|
|
75422
75479
|
if (code) {
|
|
75423
|
-
// Create an empty array, if not yet set
|
|
75424
75480
|
if (!keyActions.get(code)) {
|
|
75425
75481
|
keyActions.set(code, []);
|
|
75426
75482
|
}
|
|
75427
|
-
// Add new action to array
|
|
75428
75483
|
keyActions.get(code).push(dataset);
|
|
75429
75484
|
}
|
|
75430
75485
|
});
|
|
75431
|
-
//
|
|
75432
|
-
|
|
75486
|
+
// Nothing to do if this layout has no keyboard-triggered actions.
|
|
75487
|
+
if (keyActions.size === 0) return;
|
|
75488
|
+
this.keyboardHandler = function (ev) {
|
|
75433
75489
|
var actions = keyActions.get(ev.code);
|
|
75434
|
-
// Are there action for this key code?
|
|
75435
75490
|
if (actions) {
|
|
75436
|
-
// Run all actions associated with it
|
|
75437
75491
|
actions.forEach(function (dataset) {
|
|
75438
75492
|
self.runAction(dataset, self.options);
|
|
75439
75493
|
});
|
|
75440
75494
|
}
|
|
75441
|
-
}
|
|
75495
|
+
};
|
|
75496
|
+
document.addEventListener('keydown', this.keyboardHandler);
|
|
75497
|
+
}
|
|
75498
|
+
/** Remove the keydown listener registered by initKeyboardActions. Call when the layout ends or is cancelled. */
|
|
75499
|
+
}, {
|
|
75500
|
+
key: "removeKeyboardActions",
|
|
75501
|
+
value: function removeKeyboardActions() {
|
|
75502
|
+
if (this.keyboardHandler) {
|
|
75503
|
+
document.removeEventListener('keydown', this.keyboardHandler);
|
|
75504
|
+
this.keyboardHandler = null;
|
|
75505
|
+
}
|
|
75442
75506
|
}
|
|
75443
75507
|
}]);
|
|
75444
75508
|
}();
|
|
@@ -75673,6 +75737,7 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75673
75737
|
});
|
|
75674
75738
|
this.on('end', /*#__PURE__*/function () {
|
|
75675
75739
|
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(layout) {
|
|
75740
|
+
var _layout$actionControl;
|
|
75676
75741
|
var $layout, _$layout$parentElemen;
|
|
75677
75742
|
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
75678
75743
|
while (1) switch (_context2.prev = _context2.next) {
|
|
@@ -75731,9 +75796,10 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75731
75796
|
}
|
|
75732
75797
|
// Emit layout end event
|
|
75733
75798
|
console.debug('>>>>> XLR.debug Awaited XLR::emitSync > End - Calling layoutEnd event');
|
|
75734
|
-
|
|
75799
|
+
(_layout$actionControl = layout.actionController) === null || _layout$actionControl === void 0 || _layout$actionControl.removeKeyboardActions();
|
|
75800
|
+
_context2.next = 14;
|
|
75735
75801
|
return layout.xlr.emitSync('layoutEnd', layout);
|
|
75736
|
-
case
|
|
75802
|
+
case 14:
|
|
75737
75803
|
if (_this.xlr.config.platform !== ConsumerPlatform.CMS && layout.inLoop) {
|
|
75738
75804
|
// Transition next layout to current layout and prepare next layout if exist
|
|
75739
75805
|
_this.xlr.prepareLayouts().then( /*#__PURE__*/function () {
|
|
@@ -75763,7 +75829,7 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75763
75829
|
};
|
|
75764
75830
|
}());
|
|
75765
75831
|
}
|
|
75766
|
-
case
|
|
75832
|
+
case 15:
|
|
75767
75833
|
case "end":
|
|
75768
75834
|
return _context2.stop();
|
|
75769
75835
|
}
|
|
@@ -75774,9 +75840,11 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75774
75840
|
};
|
|
75775
75841
|
}());
|
|
75776
75842
|
this.on('cancelled', function (layout) {
|
|
75843
|
+
var _layout$actionControl2;
|
|
75777
75844
|
console.debug('>>>>> XLR.debug / Layout cancelled > Layout ID > ', layout.id);
|
|
75778
75845
|
layout.state = ELayoutState.CANCELLED;
|
|
75779
75846
|
layout.inLoop = false;
|
|
75847
|
+
(_layout$actionControl2 = layout.actionController) === null || _layout$actionControl2 === void 0 || _layout$actionControl2.removeKeyboardActions();
|
|
75780
75848
|
// Dispose video handlers immediately so their stall watchdogs and error
|
|
75781
75849
|
// callbacks can't fire against a layout whose DOM is about to be removed.
|
|
75782
75850
|
var _iterator = _createForOfIteratorHelper(layout.regions),
|
|
@@ -75935,7 +76003,8 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75935
76003
|
_this2.regions.push(regionObj);
|
|
75936
76004
|
});
|
|
75937
76005
|
this.actionController.initTouchActions();
|
|
75938
|
-
|
|
76006
|
+
// Keyboard actions are registered in run() so the global document listener
|
|
76007
|
+
// is only active while the layout is actually playing, not during background preparation.
|
|
75939
76008
|
}
|
|
75940
76009
|
}, {
|
|
75941
76010
|
key: "run",
|
|
@@ -75954,6 +76023,7 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75954
76023
|
shouldParse: false
|
|
75955
76024
|
});
|
|
75956
76025
|
if ($layoutContainer) {
|
|
76026
|
+
var _this$actionControlle;
|
|
75957
76027
|
$layoutContainer.style.setProperty('visibility', 'visible');
|
|
75958
76028
|
$layoutContainer.style.setProperty('opacity', '1');
|
|
75959
76029
|
$layoutContainer.style.setProperty('z-index', this.zIndex !== null ? "".concat(this.zIndex) : '1');
|
|
@@ -75962,6 +76032,9 @@ var Layout = /*#__PURE__*/function () {
|
|
|
75962
76032
|
// Also set the background color of the player window > body
|
|
75963
76033
|
document.body.style.setProperty('background-color', "".concat(this.bgColor));
|
|
75964
76034
|
}
|
|
76035
|
+
// Register keyboard actions now that the layout is active.
|
|
76036
|
+
// Done here (not in parseXlf) so the global listener is scoped to playback time.
|
|
76037
|
+
(_this$actionControlle = this.actionController) === null || _this$actionControlle === void 0 || _this$actionControlle.initKeyboardActions();
|
|
75965
76038
|
// Emit start event
|
|
75966
76039
|
this.emitter.emit('start', this);
|
|
75967
76040
|
// Play regions
|