bitmovin-player-ui
Version:
Bitmovin Player UI Framework
206 lines (205 loc) • 8.8 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventDispatcher = void 0;
var ArrayUtils_1 = require("./utils/ArrayUtils");
var Timeout_1 = require("./utils/Timeout");
/**
* Event dispatcher to subscribe and trigger events. Each event should have its own dispatcher.
*/
var EventDispatcher = /** @class */ (function () {
function EventDispatcher() {
this.listeners = [];
}
/**
* {@inheritDoc}
*/
EventDispatcher.prototype.subscribe = function (listener) {
this.listeners.push(new EventListenerWrapper(listener));
};
/**
* {@inheritDoc}
*/
EventDispatcher.prototype.subscribeOnce = function (listener) {
this.listeners.push(new EventListenerWrapper(listener, true));
};
/**
* {@inheritDoc}
*/
EventDispatcher.prototype.subscribeRateLimited = function (listener, rateMs) {
this.listeners.push(new RateLimitedEventListenerWrapper(listener, rateMs));
};
/**
* {@inheritDoc}
*/
EventDispatcher.prototype.unsubscribe = function (listener) {
// Iterate through listeners, compare with parameter, and remove if found
// NOTE: In case we ever remove all matching listeners instead of just the first, we need to reverse-iterate here
for (var i = 0; i < this.listeners.length; i++) {
var subscribedListener = this.listeners[i];
if (subscribedListener.listener === listener) {
subscribedListener.clear();
ArrayUtils_1.ArrayUtils.remove(this.listeners, subscribedListener);
return true;
}
}
return false;
};
/**
* Removes all listeners from this dispatcher.
*/
EventDispatcher.prototype.unsubscribeAll = function () {
// In case of RateLimitedEventListenerWrapper we need to make sure that the timeout callback won't be called
for (var _i = 0, _a = this.listeners; _i < _a.length; _i++) {
var listener = _a[_i];
listener.clear();
}
this.listeners = [];
};
/**
* Dispatches an event to all subscribed listeners.
* @param sender the source of the event
* @param args the arguments for the event
*/
EventDispatcher.prototype.dispatch = function (sender, args) {
if (args === void 0) { args = null; }
var listenersToRemove = [];
// Call every listener
// We iterate over a copy of the array of listeners to avoid the case where events are not fired on listeners when
// listeners are unsubscribed from within the event handlers during a dispatch (because the indices change and
// listeners are shifted within the array).
// This means that listener x+1 will still be called if unsubscribed from within the handler of listener x, as well
// as listener y+1 will not be called when subscribed from within the handler of listener y.
// Array.slice(0) is the fastest array copy method according to: https://stackoverflow.com/a/21514254/370252
var listeners = this.listeners.slice(0);
for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) {
var listener = listeners_1[_i];
listener.fire(sender, args);
if (listener.isOnce()) {
listenersToRemove.push(listener);
}
}
// Remove one-time listener
for (var _a = 0, listenersToRemove_1 = listenersToRemove; _a < listenersToRemove_1.length; _a++) {
var listenerToRemove = listenersToRemove_1[_a];
ArrayUtils_1.ArrayUtils.remove(this.listeners, listenerToRemove);
}
};
/**
* Returns the event that this dispatcher manages and on which listeners can subscribe and unsubscribe event handlers.
* @returns {Event}
*/
EventDispatcher.prototype.getEvent = function () {
// For now, just cast the event dispatcher to the event interface. At some point in the future when the
// codebase grows, it might make sense to split the dispatcher into separate dispatcher and event classes.
return this;
};
return EventDispatcher;
}());
exports.EventDispatcher = EventDispatcher;
/**
* A basic event listener wrapper to manage listeners within the {@link EventDispatcher}. This is a 'private' class
* for internal dispatcher use and it is therefore not exported.
*/
var EventListenerWrapper = /** @class */ (function () {
function EventListenerWrapper(listener, once) {
if (once === void 0) { once = false; }
this.eventListener = listener;
this.once = once;
}
Object.defineProperty(EventListenerWrapper.prototype, "listener", {
/**
* Returns the wrapped event listener.
* @returns {EventListener<Sender, Args>}
*/
get: function () {
return this.eventListener;
},
enumerable: false,
configurable: true
});
/**
* Fires the wrapped event listener with the given arguments.
* @param sender
* @param args
*/
EventListenerWrapper.prototype.fire = function (sender, args) {
this.eventListener(sender, args);
};
/**
* Checks if this listener is scheduled to be called only once.
* @returns {boolean} once if true
*/
EventListenerWrapper.prototype.isOnce = function () {
return this.once;
};
EventListenerWrapper.prototype.clear = function () { };
return EventListenerWrapper;
}());
/**
* Extends the basic {@link EventListenerWrapper} with rate-limiting functionality.
*/
var RateLimitedEventListenerWrapper = /** @class */ (function (_super) {
__extends(RateLimitedEventListenerWrapper, _super);
function RateLimitedEventListenerWrapper(listener, rateMs) {
var _this = _super.call(this, listener) || this; // sets the event listener sink
_this.rateMs = rateMs;
// starting limiting the events to the given value
var startRateLimiting = function () {
_this.rateLimitTimout.start();
};
// timout for limiting the events
_this.rateLimitTimout = new Timeout_1.Timeout(_this.rateMs, function () {
if (_this.lastSeenEvent) {
_this.fireSuper(_this.lastSeenEvent.sender, _this.lastSeenEvent.args);
startRateLimiting(); // start rateLimiting again to keep rate limit active even after firing the last seen event
_this.lastSeenEvent = null;
}
});
// In case the events stopping during the rateLimiting we need to track the last seen one and delegate after the
// rate limiting is finished. This prevents missing the last update due to the rate limit.
_this.rateLimitingEventListener = function (sender, args) {
// only fire events if the rateLimiting is not running
if (_this.shouldFireEvent()) {
_this.fireSuper(sender, args);
startRateLimiting();
return;
}
_this.lastSeenEvent = {
sender: sender,
args: args,
};
};
return _this;
}
RateLimitedEventListenerWrapper.prototype.shouldFireEvent = function () {
return !this.rateLimitTimout.isActive();
};
RateLimitedEventListenerWrapper.prototype.fireSuper = function (sender, args) {
// Fire the actual external event listener
_super.prototype.fire.call(this, sender, args);
};
RateLimitedEventListenerWrapper.prototype.fire = function (sender, args) {
// Fire the internal rate-limiting listener instead of the external event listener
this.rateLimitingEventListener(sender, args);
};
RateLimitedEventListenerWrapper.prototype.clear = function () {
_super.prototype.clear.call(this);
this.rateLimitTimout.clear();
};
return RateLimitedEventListenerWrapper;
}(EventListenerWrapper));