/**
* Player Component - Basisklasse für alle UI-Objekte
*
* @Datei component.js
* /
import window from 'global/window';
importiere evented von './mixins/evented';
import stateful from './mixins/stateful';
importiere * as Dom aus './utils/dom.js';
import * as Fn from './utils/fn.js';
importiere * als Guid aus './utils/guid.js';
importiere {toTitleCase, toLowerCase} aus './utils/string-cases.js';
importiere mergeOptions aus './utils/merge-options.js';
import computedStyle from './utils/computed-style';
importiere Map aus './utils/map.js';
importieren Sie Set aus './utils/set.js';
import keycode from 'keycode';
/**
* Basisklasse für alle UI-Komponenten.
* Komponenten sind UI-Objekte, die sowohl ein Javascript-Objekt als auch ein Element darstellen
* im DOM. Sie können Kinder von anderen Komponenten sein und können
* kinder selbst.
*
* Komponenten können auch Methoden von {@link EventTarget} verwenden
* /
klasse Component {
/**
* Ein Callback, der aufgerufen wird, wenn eine Komponente bereit ist. Enthält keine
* parameter und alle Rückrufwerte werden ignoriert.
*
* @callback Komponente~ReadyCallback
* @diese Komponente
* /
/**
* Erzeugt eine Instanz dieser Klasse.
*
* @param {Player} Spieler
* Der `Player`, dem diese Klasse zugeordnet werden soll.
*
* @param {Object} [Optionen]
* Der Schlüssel/Wertspeicher der Komponentenoptionen.
*
* @param {Object[]} [options.children]
* Ein Array von Child-Objekten, mit denen diese Komponente intialisiert werden soll. Kinder haben Objekte
* eine Namenseigenschaft, die verwendet wird, wenn mehr als eine Komponente des gleichen Typs verwendet werden soll
* hinzugefügt.
*
* @param {string} [options.className]
* Eine Klasse oder eine durch Leerzeichen getrennte Liste von Klassen zum Hinzufügen der Komponente
*
* @param {Component~ReadyCallback} [ready]
* Funktion, die aufgerufen wird, wenn die "Komponente" fertig ist.
* /
constructor(player, options, ready) {
// Die Komponente könnte der Player selbst sein und wir können `this` nicht an super übergeben
if (!player && this.play) {
this.player_ = player = this; // eslint-disable-line
} else {
this.player_ = player;
}
this.isDisposed_ = false;
// Den Verweis auf die übergeordnete Komponente über die Methode `addChild` halten
this.parentComponent_ = null;
// Erstellen Sie eine Kopie von prototype.options_, um ein Überschreiben der Standardeinstellungen zu verhindern
this.options_ = mergeOptions({}, this.options_);
// Aktualisierte Optionen mit gelieferten Optionen
options = this.options_ = mergeOptions(this.options_, options);
// Holt die ID aus den Optionen oder dem Optionselement, falls eines angegeben ist
this.id_ = options.id || (options.el && options.el.id);
// Wenn keine ID in den Optionen angegeben wurde, wird eine erzeugt
if (!this.id_) {
// Im Falle von Scheinspielern ist die Funktion Spieler-ID nicht erforderlich
const id = player && player.id && player.id() || 'no_player';
this.id_ = `${id}_component_${Guid.newGUID()}`;
}
this.name_ = options.name || null;
// Element erstellen, wenn in den Optionen kein Element angegeben wurde
if (options.el) {
this.el_ = options.el;
} else if (options.createEl !== false) {
this.el_ = this.createEl();
}
if (options.className && this.el_) {
options.className.split(' ').forEach(c => this.addClass(c));
}
// wenn evented irgendetwas anderes als false ist, wollen wir in evented mixin
if (options.evented !== false) {
// Machen Sie dies zu einem ereignisgesteuerten Objekt und verwenden Sie `el_`, falls vorhanden, als seinen Ereignisbus
evented(this, {eventBusKey: this.el_ ? 'el_' : null});
this.handleLanguagechange = this.handleLanguagechange.bind(this);
this.on(this.player_, 'languagechange', this.handleLanguagechange);
}
stateful(this, this.constructor.defaultState);
this.children_ = [];
this.childIndex_ = {};
this.childNameIndex_ = {};
this.setTimeoutIds_ = new Set();
this.setIntervalIds_ = new Set();
this.rafIds_ = new Set();
this.namedRafs_ = new Map();
this.clearingTimersOnDispose_ = false;
// Hinzufügen von untergeordneten Komponenten in Optionen
if (options.initChildren !== false) {
this.initChildren();
}
// Wir wollen hier nicht "ready" auslösen, sonst geht es los, bevor "init" tatsächlich ist
// fertig für alle Kinder, die diesen Konstruktor ausführen
this.ready(ready);
if (options.reportTouchActivity !== false) {
this.enableTouchActivity();
}
}
/**
* Entsorgt die Komponente und alle untergeordneten Komponenten.
*
* @feuert Komponente#entsorgen
*
* @param {Object} options
* @param {Element} options.originalEl Element, mit dem das Player-Element ersetzt werden soll
* /
dispose(options = {}) {
// Bailout, wenn die Komponente bereits entsorgt wurde.
if (this.isDisposed_) {
rückkehr;
}
if (this.readyQueue_) {
this.readyQueue_.length = 0;
}
/**
* Wird ausgelöst, wenn eine "Komponente" entsorgt wird.
*
* @event Komponente#dispose
* @Typ {EventTarget~Event}
*
* @property {boolean} [bubbles=false]
* auf false gesetzt, damit das Dispose-Ereignis nicht
* aufsprudeln
* /
this.trigger({type: 'dispose', bubbles: false});
this.isDisposed_ = true;
// Alle Kinder entsorgen.
if (this.children_) {
for (let i = this.children_.length - 1; i >= 0; i--) {
if (this.children_[i].dispose) {
this.children_[i].dispose();
}
}
}
// Untergeordnete Referenzen löschen
this.children_ = null;
this.childIndex_ = null;
this.childNameIndex_ = null;
this.parentComponent_ = null;
if (this.el_) {
// Element aus dem DOM entfernen
if (this.el_.parentNode) {
if (options.restoreEl) {
this.el_.parentNode.replaceChild(options.restoreEl, this.el_);
} else {
this.el_.parentNode.removeChild(this.el_);
}
}
this.el_ = null;
}
// Entfernen Sie den Verweis auf den Player, nachdem Sie das Element entsorgt haben
this.player_ = null;
}
/**
* Ermitteln Sie, ob diese Komponente entsorgt wurde oder nicht.
*
* @return {boolean}
* Wenn die Komponente entsorgt wurde, wird `true` angezeigt. Andernfalls `false`.
* /
isDisposed() {
return Boolean(this.isDisposed_);
}
/**
* Gibt den {@link Player} zurück, an den die "Komponente" angehängt ist.
*
* @return {Player}
* Der Spieler, an den diese Komponente angeschlossen ist.
* /
player() {
return this.player_;
}
/**
* Tiefes Zusammenführen von Optionsobjekten mit neuen Optionen.
* > Anmerkung: Wenn sowohl `obj` als auch `options` Eigenschaften enthalten, deren Werte Objekte sind.
* Die beiden Eigenschaften werden mit {@link module:mergeOptions} zusammengeführt
*
* @param {Object} obj
* Das Objekt, das neue Optionen enthält.
*
* @return {Object}
* Ein neues Objekt aus `this.options_` und `obj` zusammengefügt.
* /
optionen(obj) {
if (!obj) {
return this.options_;
}
this.options_ = mergeOptions(this.options_, obj);
return this.options_;
}
/**
* Das DOM-Element "Komponente" abrufen
*
* @return {Element}
* Das DOM-Element für diese "Komponente".
* /
el() {
return this.el_;
}
/**
* Erstellen Sie das DOM-Element "Komponente".
*
* @param {string} [tagName]
* Der DOM-Knotentyp des Elements. z.B. 'div'
*
* @param {Object} [Eigenschaften]
* Ein Objekt mit Eigenschaften, die festgelegt werden sollen.
*
* @param {Object} [Attribute]
* Ein Objekt mit Attributen, die gesetzt werden sollen.
*
* @return {Element}
* Das Element, das erstellt wird.
* /
createEl(tagName, properties, attributes) {
return Dom.createEl(tagName, properties, attributes);
}
/**
* Lokalisieren Sie eine Zeichenkette, die auf Englisch vorliegt.
*
* Wenn Token angegeben werden, wird versucht, die angegebene Zeichenkette durch ein einfaches Token zu ersetzen.
* Die Token, nach denen gesucht wird, sehen aus wie `{1}`, wobei der Index im Token-Array mit 1 indiziert ist.
*
* Wenn ein `defaultValue` angegeben wird, wird dieser über `string` verwendet,
* wenn ein Wert in den bereitgestellten Sprachdateien nicht gefunden wird.
* Dies ist nützlich, wenn Sie einen beschreibenden Schlüssel für die Token-Ersetzung haben möchten
* aber eine knappe lokalisierte Zeichenkette haben und nicht verlangen, dass "de.json" enthalten ist.
*
* Derzeit wird sie für die Zeitmessung des Fortschrittsbalkens verwendet.
* ``js
* {
* "Fortschrittsbalken-Timing: currentTime={1} duration={2}": "{1} von {2}"
* }
* ```
* Sie wird dann wie folgt verwendet:
* ``js
* this.localize('progress bar timing: currentTime={1} duration{2}',
* [this.player_.currentTime(), this.player_.duration()],
* '{1} von {2}');
* ```
*
* Das Ergebnis ist etwa so: `01:23 von 24:56`.
*
*
* @param {string} string
* Die zu lokalisierende Zeichenkette und der Schlüssel, nach dem in den Sprachdateien gesucht werden soll.
* @param {string[]} [tokens]
* Wenn das aktuelle Element über Token-Ersetzungen verfügt, geben Sie die Token hier an.
* @param {string} [defaultValue]
* Der Standardwert ist `string`. Kann ein Standardwert sein, der für die Ersetzung von Token verwendet wird
* wenn der Nachschlageschlüssel getrennt sein muss.
*
* @return {string}
* Die lokalisierte Zeichenkette oder, falls keine Lokalisierung existiert, die englische Zeichenkette.
* /
localize(string, tokens, defaultValue = string) {
const code = this.player_.language && this.player_.language();
const languages = this.player_.languages && this.player_.languages();
const language = languages && languages[code];
const primaryCode = code && code.split('-')[0];
const primaryLang = languages && languages[primaryCode];
let localizedString = defaultValue;
if (language && language[string]) {
localizedString = language[string];
} else if (primaryLang && primaryLang[string]) {
localizedString = primaryLang[string];
}
wenn (Token) {
localizedString = localizedString.replace(/\{(\d+)\}/g, function(match, index) {
const value = tokens[index - 1];
let ret = Wert;
if (typeof wert === 'undefined') {
ret = Treffer;
}
return ret;
});
}
return localizedString;
}
/**
* Handhabt den Sprachwechsel für den Player in Komponenten. Sollte durch Unterkomponenten außer Kraft gesetzt werden.
*
* @Abstrakt
* /
handleLanguagechange() {}
/**
* Rückgabe des DOM-Elements `Komponente`. Hier werden Kinder eingefügt.
* Dies ist normalerweise dasselbe wie das Element, das in {@link Component#el} zurückgegeben wird.
*
* @return {Element}
* Das Inhaltselement für diese "Komponente".
* /
contentEl() {
return this.contentEl_ || this.el_;
}
/**
* Die ID dieser Komponente abrufen
*
* @return {string}
* Die Kennung dieser `Komponente`
* /
id() {
return this.id_;
}
/**
* Ermittelt den Namen der Komponente. Der Name wird verwendet, um auf die "Komponente" zu verweisen
* und wird bei der Registrierung festgelegt.
*
* @return {string}
* Der Name dieser "Komponente".
* /
name() {
return this.name_;
}
/**
* Abrufen eines Arrays aller untergeordneten Komponenten
*
* @return {Array}
* Die Kinder
* /
kinder() {
return this.children_;
}
/**
* Gibt die untergeordnete "Komponente" mit der angegebenen "ID" zurück.
*
* @param {string} id
* Die ID der zu erhaltenden untergeordneten Komponente.
*
* @return {Komponente|undefined}
* Die untergeordnete `Komponente` mit der angegebenen `ID` oder undefiniert.
* /
getChildById(id) {
return this.childIndex_[id];
}
/**
* Gibt die untergeordnete `Komponente` mit dem angegebenen `Name` zurück.
*
* @param {string} name
* Der Name der zu erhaltenden untergeordneten Komponente.
*
* @return {Komponente|undefined}
* Die untergeordnete `Komponente` mit dem angegebenen `Name` oder undefiniert.
* /
getChild(name) {
if (!name) {
rückkehr;
}
return this.childNameIndex_[name];
}
/**
* Liefert die auf die angegebene Komponente folgende abhängige Komponente
* nachkomme `Namen`. Zum Beispiel würde ['foo', 'bar', 'baz']
* versuchen, 'foo' auf der aktuellen Komponente zu erhalten, 'bar' auf der 'foo'
* komponente und 'baz' auf die 'bar'-Komponente und geben undefiniert zurück
* wenn eine davon nicht vorhanden ist.
*
* @param {...string[]|...string} names
* Der Name der zu erhaltenden untergeordneten Komponente.
*
* @return {Komponente|undefined}
* Der auf den angegebenen Nachkommen folgende Nachkomme `Component`
* `Namen` oder undefiniert.
* /
getDescendant(...names) {
// Array-Argumente in das Hauptarray umwandeln
names = names.reduce((acc, n) => acc.concat(n), []);
let currentChild = this;
for (let i = 0; i < names.length; i++) {
currentChild = currentChild.getChild(names[i]);
if (!currentChild || !currentChild.getChild) {
rückkehr;
}
}
return currentChild;
}
/**
* Hinzufügen einer untergeordneten `Komponente` innerhalb der aktuellen `Komponente`.
*
*
* @param {String|Komponente} Kind
* Der Name oder die Instanz eines hinzuzufügenden Kindes.
*
* @param {Object} [options={}]
* Der Schlüssel/Wertspeicher der Optionen, die an die Kinder von
* das Kind.
*
* @param {Anzahl} [index=this.children_.length]
* Der Index, in dem versucht wird, ein Kind hinzuzufügen.
*
* @return {Komponente}
* Die Komponente, die als untergeordnetes Element hinzugefügt wird. Bei Verwendung einer Zeichenkette wird die
* komponente" wird durch diesen Prozess erstellt.
* /
addChild(child, options = {}, index = this.children_.length) {
lassen Sie Komponente;
let componentName;
// Wenn Kind ein String ist, Komponente mit Optionen erstellen
if (typeof kind === 'string') {
componentName = toTitleCase(child);
const componentClassName = options.componentClass || componentName;
// Name über Optionen festlegen
options.name = componentName;
// Erstellen eines neuen Objekts & Element für dieses Kontrollset
// Wenn kein .player_ vorhanden ist, ist dies ein Spieler
const ComponentClass = Component.getComponent(componentClassName);
if (!ComponentClass) {
throw new Error(`Komponente ${componentClassName} existiert nicht`);
}
// Die direkt im videojs-Objekt gespeicherten Daten können
// fälschlicherweise als zu behaltende Komponente identifiziert
// Abwärtskompatibilität mit 4.x. Prüfen Sie, ob die
// Komponentenklasse kann instanziiert werden.
if (typeof ComponentClass !== 'function') {
null zurückgeben;
}
component = new ComponentClass(this.player_ || this, options);
// Kind ist eine Komponenteninstanz
} else {
komponente = Kind;
}
if (component.parentComponent_) {
component.parentComponent_.removeChild(component);
}
this.children_.splice(index, 0, component);
component.parentComponent_ = this;
if (typeof component.id === 'function') {
this.childIndex_[component.id()] = component;
}
// Wenn kein Name zum Erstellen der Komponente verwendet wurde, prüfen Sie, ob wir die
// Name der Funktion der Komponente
componentName = componentName || (component.name && toTitleCase(component.name()));
if (Komponentenname) {
this.childNameIndex_[Komponentenname] = Komponente;
this.childNameIndex_[toLowerCase(componentName)] = component;
}
// Hinzufügen des Elements des UI-Objekts zum Container-Div (Box)
// Das Vorhandensein eines Elements ist nicht erforderlich
if (typeof component.el === 'function' && component.el()) {
// Wenn vor einer Komponente eingefügt wird, vor dem Element dieser Komponente einfügen
let refNode = null;
if (this.children_[index + 1]) {
// Die meisten untergeordneten Elemente sind Komponenten, aber die Videotechnik ist ein HTML-Element
if (this.children_[index + 1].el_) {
refNode = this.children_[index + 1].el_;
} else if (Dom.isEl(this.children_[index + 1])) {
refNode = this.children_[index + 1];
}
}
this.contentEl().insertBefore(component.el(), refNode);
}
// Zurückgeben, damit es auf dem übergeordneten Objekt gespeichert werden kann, falls gewünscht.
komponente zurück;
}
/**
* Entfernen einer untergeordneten Komponente aus der Liste der untergeordneten Komponenten dieser Komponente. Entfernt auch
* das untergeordnete "Komponenten"-Element von diesem "Komponenten"-Element.
*
* @param {Komponente} Komponente
* Die zu entfernende untergeordnete `Komponente`.
* /
removeChild(component) {
if (typeof component === 'string') {
component = this.getChild(component);
}
if (!component || !this.children_) {
rückkehr;
}
let childFound = false;
for (let i = this.children_.length - 1; i >= 0; i--) {
if (this.children_[i] === component) {
childFound = true;
this.children_.splice(i, 1);
pause;
}
}
if (!childFound) {
rückkehr;
}
component.parentComponent_ = null;
this.childIndex_[component.id()] = null;
this.childNameIndex_[toTitleCase(component.name())] = null;
this.childNameIndex_[toLowerCase(component.name())] = null;
const compEl = component.el();
if (compEl && compEl.parentNode === this.contentEl()) {
this.contentEl().removeChild(component.el());
}
}
/**
* Hinzufügen und Initialisieren von untergeordneten Standard-Komponenten auf der Grundlage von Optionen.
* /
initChildren() {
const children = this.options_.children;
wenn (Kinder) {
// `das` ist `Elternteil`
const parentOptions = this.options_;
const handleAdd = (Kind) => {
const name = kind.name;
let opts = child.opts;
// Zulassen, dass Optionen für Kinder an den Elternoptionen eingestellt werden können
// z.B. videojs(id, { controlBar: false });
// anstelle von videojs(id, { children: { controlBar: false });
if (parentOptions[name] !== undefined) {
opts = parentOptions[name];
}
// Deaktivieren von Standardkomponenten ermöglichen
// z.B. options['children']['posterImage'] = false
if (opts === false) {
rückkehr;
}
// Erlaubt die Übergabe von Optionen als einfachen Booleschen Wert, wenn keine Konfiguration vorliegt
// notwendig ist.
if (opts === true) {
opts = {};
}
// Wir wollen auch die ursprünglichen Spieleroptionen übergeben
// zu jeder Komponente, so dass sie nicht in der Lage sind
// für spätere Optionen wieder in den Player greifen.
opts.playerOptions = this.options_.playerOptions;
// Erstellen und Hinzufügen der untergeordneten Komponente.
// Fügen Sie der übergeordneten Instanz einen direkten Verweis auf das Kind über den Namen hinzu.
// Wenn zwei gleiche Komponenten verwendet werden, sollten unterschiedliche Namen angegeben werden
// für jede
const newChild = this.addChild(name, opts);
if (newChild) {
this[name] = newChild;
}
};
// Erlaubt die Übergabe eines Arrays von Kinderdetails in den Optionen
let workingChildren;
const Tech = Component.getComponent('Tech');
if (Array.isArray(Kinder)) {
workingChildren = Kinder;
} else {
workingChildren = Object.keys(children);
}
workingChildren
// Kinder, die in this.options_, aber auch in workingChildren enthalten sind, würden
// geben uns zusätzliche Kinder, die wir nicht wollen. Wir wollen sie also herausfiltern.
.concat(Object.keys(this.options_)
.filter(function(child) {
return !workingChildren.some(function(wchild) {
if (typeof wchild === 'string') {
return child === wchild;
}
return kind === wchild.name;
});
}))
.map((Kind) => {
name lassen;
let opts;
if (typeof kind === 'string') {
name = Kind;
opts = children[name] || this.options_[name] || {};
} else {
name = kind.name;
opts = Kind;
}
return {Name, opts};
})
.filter((Kind) => {
// Wir müssen sicherstellen, dass child.name nicht in der techOrder enthalten ist, da
// Techs sind als Komponenten registriert, können aber nicht kompatibel sein
// Siehe https://github.com/videojs/video.js/issues/2772
const c = Component.getComponent(child.opts.componentClass ||
toTitleCase(child.name));
return c && !Tech.isTech(c);
})
.forEach(handleAdd);
}
}
/**
* Erzeugt den Standardnamen der DOM-Klasse. Sollte durch Unterkomponenten außer Kraft gesetzt werden.
*
* @return {string}
* Der DOM-Klassenname für dieses Objekt.
*
* @Abstrakt
* /
buildCSSClass() {
// Untergeordnete Klassen können eine Funktion enthalten, die dies tut:
// return 'CLASS NAME' + this._super();
zurückgeben '';
}
/**
* Binden Sie einen Listener an den Bereitschaftsstatus der Komponente.
* Anders als bei Ereignis-Listenern, wenn das Ereignis ready bereits eingetreten ist
* wird die Funktion sofort ausgelöst.
*
* @return {Komponente}
* Gibt sich selbst zurück; die Methode kann verkettet werden.
* /
ready(fn, sync = false) {
if (!fn) {
rückkehr;
}
if (!this.isReady_) {
this.readyQueue_ = this.readyQueue_ || [];
this.readyQueue_.push(fn);
rückkehr;
}
wenn (sync) {
fn.call(this);
} else {
// Rufen Sie die Funktion aus Konsistenzgründen standardmäßig asynchron auf
this.setTimeout(fn, 1);
}
}
/**
* Löst alle bereitstehenden Zuhörer für diese Komponente aus.
*
* @feuer komponente#bereit
* /
triggerReady() {
this.isReady_ = true;
// Sicherstellen, dass die Bereitschaft asynchron ausgelöst wird
this.setTimeout(function() {
const readyQueue = this.readyQueue_;
// Bereitschaftswarteschlange zurücksetzen
this.readyQueue_ = [];
if (readyQueue && readyQueue.length > 0) {
readyQueue.forEach(function(fn) {
fn.call(this);
}, this);
}
// Erlaubt auch die Verwendung von Ereignis-Listenern
/**
* Wird ausgelöst, wenn eine "Komponente" bereit ist.
*
* @Ereignis Komponente#ready
* @Typ {EventTarget~Event}
* /
this.trigger('ready');
}, 1);
}
/**
* Ein einzelnes DOM-Element finden, das einem "Selektor" entspricht. Dies kann innerhalb der `Component`s
* `contentEl()` oder einen anderen benutzerdefinierten Kontext.
*
* @param {string} selector
* Ein gültiger CSS-Selektor, der an `querySelector` übergeben wird.
*
* @param {Element|String} [context=this.contentEl()]
* Ein DOM-Element, innerhalb dessen die Abfrage erfolgen soll. Kann auch ein Selektorstring sein in
* in diesem Fall wird das erste übereinstimmende Element als Kontext verwendet. Wenn
* fehlende `this.contentEl()` wird verwendet. Wenn `this.contentEl()` zurückkommt
* nichts fällt es auf `Dokument` zurück.
*
* @return {Element|null}
* das gefundene Dom-Element oder null
*
* @siehe [Informationen zu CSS-Selektoren](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
* /
$(Selektor, Kontext) {
return Dom.$(selector, context || this.contentEl());
}
/**
* Findet alle DOM-Elemente, die einem "Selektor" entsprechen. Dies kann innerhalb der `Component`s
* `contentEl()` oder einen anderen benutzerdefinierten Kontext.
*
* @param {string} selector
* Ein gültiger CSS-Selektor, der an `querySelectorAll` übergeben wird.
*
* @param {Element|String} [context=this.contentEl()]
* Ein DOM-Element, innerhalb dessen die Abfrage erfolgen soll. Kann auch ein Selektorstring sein in
* in diesem Fall wird das erste übereinstimmende Element als Kontext verwendet. Wenn
* fehlende `this.contentEl()` wird verwendet. Wenn `this.contentEl()` zurückkommt
* nichts fällt es auf `Dokument` zurück.
*
* @return {NodeList}
* eine Liste der gefundenen Dom-Elemente
*
* @siehe [Informationen zu CSS-Selektoren](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
* /
$$(Selektor, Kontext) {
return Dom.$$(selector, context || this.contentEl());
}
/**
* Prüfen Sie, ob das Element einer Komponente einen CSS-Klassennamen hat.
*
* @param {string} classToCheck
* Name der zu prüfenden CSS-Klasse.
*
* @return {boolean}
* - Wahr, wenn die "Komponente" die Klasse hat.
* - False, wenn die "Komponente" nicht die "Klasse" hat
* /
hasClass(classToCheck) {
return Dom.hasClass(this.el_, classToCheck);
}
/**
* Fügen Sie dem Element "Komponente" einen CSS-Klassennamen hinzu.
*
* @param {string} classToAdd
* CSS-Klassenname zum Hinzufügen
* /
addClass(classToAdd) {
Dom.addClass(this.el_, classToAdd);
}
/**
* Entfernen eines CSS-Klassennamens aus dem Element "Component".
*
* @param {string} classToRemove
* Name der zu entfernenden CSS-Klasse
* /
removeClass(classToRemove) {
Dom.removeClass(this.el_, classToRemove);
}
/**
* Hinzufügen oder Entfernen eines CSS-Klassennamens aus dem Element der Komponente.
* - ClassToToggle" wird hinzugefügt, wenn {@link Component#hasClass} false zurückgeben würde.
* - ClassToToggle" wird entfernt, wenn {@link Component#hasClass} true zurückgeben würde.
*
* @param {string} classToToggle
* Die hinzuzufügende oder zu entfernende Klasse, basierend auf (@link Component#hasClass}
*
* @param {boolean|Dom~predicate} [predicate]
* Eine {@link Dom~predicate}-Funktion oder ein boolescher
* /
toggleClass(classToToggle, predicate) {
Dom.toggleClass(this.el_, classToToggle, predicate);
}
/**
* Zeigen Sie das Element "Komponente" an, wenn es durch Entfernen des Feldes "Komponente" verborgen ist
* 'vjs-hidden' Klassenname aus.
* /
show() {
this.removeClass('vjs-hidden');
}
/**
* Verstecken Sie das Element `Component`, wenn es gerade angezeigt wird, indem Sie die
* 'vjs-hidden'-Klassenname hinzugefügt werden.
* /
hide() {
this.addClass('vjs-hidden');
}
/**
* Sperren Sie ein "Komponenten"-Element in seinem sichtbaren Zustand, indem Sie die Option "vjs-lock-showing" hinzufügen
* klassennamen zu. Wird während des Ein- und Ausblendens verwendet.
*
* @privat
* /
lockShowing() {
this.addClass('vjs-lock-showing');
}
/**
* Entsperren eines "Komponenten"-Elements aus seinem sichtbaren Zustand durch Entfernen der "vjs-lock-showing"-Einstellung
* klassennamen aus. Wird während des Ein- und Ausblendens verwendet.
*
* @privat
* /
unlockShowing() {
this.removeClass('vjs-lock-showing');
}
/**
* Ermittelt den Wert eines Attributs des Elements "Komponente".
*
* @param {string} attribut
* Name des Attributs, von dem der Wert abgeleitet werden soll.
*
* @return {string|null}
* - Der Wert des Attributs, nach dem gefragt wurde.
* - Kann bei einigen Browsern ein leerer String sein, wenn das Attribut nicht existiert
* oder keinen Wert hat
* - Die meisten Browser geben null zurück, wenn das Attribut nicht existiert oder eine
* keinen Wert.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
* /
getAttribute(attribute) {
return Dom.getAttribute(this.el_, attribute);
}
/**
* Den Wert eines Attributs des Elements "Komponente" festlegen
*
* @param {string} attribut
* Name des zu setzenden Attributs.
*
* @param {string} Wert
* Wert, auf den das Attribut gesetzt werden soll.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
* /
setAttribute(attribut, wert) {
Dom.setAttribute(this.el_, attribute, value);
}
/**
* Entfernen Sie ein Attribut aus dem Element "Komponente".
*
* @param {string} attribut
* Name des zu entfernenden Attributs.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
* /
removeAttribute(attribute) {
Dom.removeAttribute(this.el_, attribute);
}
/**
* Holt oder setzt die Breite der Komponente basierend auf den CSS-Styles.
* Siehe {@link Component#dimension} für weitere Informationen.
*
* @param {Zahl|String} [num]
* Die Breite, die Sie mit '%', 'px' oder gar nicht setzen wollen.
*
* @param {boolean} [skipListeners]
* Überspringen Sie den Ereignisauslöser componentresize
*
* @return {Zahl|String}
* Die Breite beim Erhalten, Null, wenn es keine Breite gibt. Kann eine Zeichenfolge sein
* nachgestellt mit '%' oder 'px'.
* /
width(num, skipListeners) {
return this.dimension('width', num, skipListeners);
}
/**
* Holt oder setzt die Höhe der Komponente basierend auf den CSS-Styles.
* Siehe {@link Component#dimension} für weitere Informationen.
*
* @param {Zahl|String} [num]
* Die Höhe, die Sie mit '%', 'px' oder gar nicht setzen wollen.
*
* @param {boolean} [skipListeners]
* Überspringen Sie den Ereignisauslöser componentresize
*
* @return {Zahl|String}
* Die Breite beim Erhalten, Null, wenn es keine Breite gibt. Kann eine Zeichenfolge sein
* nachgestellt mit '%' oder 'px'.
* /
height(num, skipListeners) {
return this.dimension('Höhe', num, skipListeners);
}
/**
* Setzen Sie gleichzeitig die Breite und Höhe des Elements "Komponente".
*
* @param {Zahl|String} Breite
* Breite, auf die das Element `Component` gesetzt werden soll.
*
* @param {Zahl|String} Höhe
* Höhe, auf die das Element `Komponente` gesetzt werden soll.
* /
dimensions(width, height) {
// Komponententresize-Listener auf Breite zur Optimierung überspringen
this.width(width, true);
this.height(height);
}
/**
* Holt oder setzt die Breite oder Höhe des Elements "Component". Dies ist der gemeinsame Code
* für die {@link Component#width} und {@link Component#height}.
*
* Wissenswertes:
* - Wenn die Breite oder Höhe in einer Zahl angegeben ist, wird die Zahl mit dem Postfix 'px' zurückgegeben.
* - Wenn die Breite/Höhe in Prozent angegeben ist, wird der Prozentsatz mit dem Postfix '%' zurückgegeben
* - Ausgeblendete Elemente haben mit `window.getComputedStyle` eine Breite von 0. Diese Funktion
* standardmäßig auf die `style.width` der Komponente und greift auf `window.getComputedStyle` zurück.
* Siehe [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
* für weitere Informationen
* - Wenn Sie den berechneten Stil der Komponente wünschen, verwenden Sie {@link Component#currentWidth}
* und {@link {Component#currentHeight}
*
* @feuert Komponente#Komponententresize
*
* @param {string} widthOrHeight
8 "Breite" oder "Höhe
*
* @param {Zahl|String} [num]
8 Neue Dimension
*
* @param {boolean} [skipListeners]
* Ereignisauslöser componentresize überspringen
*
* @return {number}
* Die Dimension, wenn sie erhalten wird oder 0, wenn sie nicht gesetzt ist
* /
dimension(widthOrHeight, num, skipListeners) {
if (num !== undefiniert) {
// Auf Null setzen, wenn null oder buchstäblich NaN (NaN !== NaN)
if (num === null || num !== num) {
num = 0;
}
// Prüfen Sie, ob Sie css width/height (% oder px) verwenden und passen Sie es an
if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
this.el_.style[widthOrHeight] = num;
} else if (num === 'auto') {
this.el_.style[widthOrHeight] = '';
} else {
this.el_.style[widthOrHeight] = num + 'px';
}
// skipListeners ermöglicht es uns, das Auslösen des Größenänderungsereignisses zu vermeiden, wenn sowohl Breite als auch Höhe eingestellt werden
if (!skipListeners) {
/**
* Wird ausgelöst, wenn die Größe einer Komponente geändert wird.
*
* @event Komponente#componentresize
* @Typ {EventTarget~Event}
* /
this.trigger('componentresize');
}
rückkehr;
}
// Es wird kein Wert gesetzt, also wird er ermittelt
// Sicherstellen, dass das Element existiert
if (!this.el_) {
0 zurückgeben;
}
// Abfrage des Dimensionswerts aus dem Stil
const val = this.el_.style[widthOrHeight];
const pxIndex = val.indexOf('px');
if (pxIndex !== -1) {
// Rückgabe des Pixelwerts ohne 'px'
return parseInt(val.slice(0, pxIndex), 10);
}
// Kein px, also % verwenden oder kein Stil wurde gesetzt, also Rückgriff auf offsetWidth/height
// Wenn die Komponente display:none hat, wird 0 zurückgegeben
// TODO: Handhabung von display:none und no dimension style mit px
return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
}
/**
* Ermittelt die berechnete Breite oder Höhe des Elements der Komponente.
*
* Verwendet `window.getComputedStyle`.
*
* @param {string} widthOrHeight
* Eine Zeichenkette, die 'Breite' oder 'Höhe' enthält. Welchen auch immer du kriegen willst.
*
* @return {number}
* Die Dimension, nach der gefragt wird, oder 0, wenn nichts festgelegt wurde
* für diese Dimension.
* /
currentDimension(widthOrHeight) {
let computedWidthOrHeight = 0;
if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
throw new Error('currentDimension akzeptiert nur den Wert für Breite oder Höhe');
}
computedWidthOrHeight = computedStyle(this.el_, widthOrHeight);
// px" aus der Variable entfernen und als Ganzzahl auswerten
computedWidthOrHeight = parseFloat(computedWidthOrHeight);
// Wenn der berechnete Wert immer noch 0 ist, lügt der Browser möglicherweise
// und wir wollen die Offset-Werte überprüfen.
// Dieser Code wird auch ausgeführt, wenn getComputedStyle nicht vorhanden ist.
if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
const rule = `offset${toTitleCase(widthOrHeight)}`;
computedWidthOrHeight = this.el_[rule];
}
return computedWidthOrHeight;
}
/**
* Ein Objekt, das die Werte für Breite und Höhe der Komponente enthält
* berechneter Stil. Verwendet `window.getComputedStyle`.
*
* @typedef {Object} Komponente~DimensionObject
*
* @Eigenschaft {Nummer} Breite
* Die Breite des berechneten Stils der "Komponente".
*
* @Eigenschaft {Zahl} Höhe
* Die Höhe des berechneten Stils der "Komponente".
* /
/**
* Holt ein Objekt, das die berechneten Werte für Breite und Höhe des
* element der Komponente.
*
* Verwendet `window.getComputedStyle`.
*
* @return {Component~DimensionObject}
* Die berechneten Abmessungen des Elements der Komponente.
* /
currentDimensions() {
Rückkehr {
breite: this.currentDimension('Breite'),
höhe: this.currentDimension('Höhe')
};
}
/**
* Ermittelt die berechnete Breite des Elements der Komponente.
*
* Verwendet `window.getComputedStyle`.
*
* @return {number}
* Die berechnete Breite des Elements der Komponente.
* /
currentWidth() {
return this.currentDimension('width');
}
/**
* Ermittelt die berechnete Höhe des Elements der Komponente.
*
* Verwendet `window.getComputedStyle`.
*
* @return {number}
* Die berechnete Höhe des Elements der Komponente.
* /
currentHeight() {
return this.currentDimension('Höhe');
}
/**
* Den Fokus auf diese Komponente setzen
* /
focus() {
this.el_.focus();
}
/**
* Den Fokus von dieser Komponente entfernen
* /
blur() {
this.el_.blur();
}
/**
* Wenn diese Komponente ein "Keydown"-Ereignis empfängt, das sie nicht verarbeitet,
* wird das Ereignis zur Bearbeitung an den Player weitergegeben.
*
* @param {EventTarget~Event} event
* Das "Keydown"-Ereignis, das zum Aufruf dieser Funktion geführt hat.
* /
handleKeyDown(event) {
if (this.player_) {
// Wir stoppen die Ausbreitung hier nur, weil wir wollen, dass unbehandelte Ereignisse fallen
// zurück zum Browser. Tabulator für Fokus-Trapping ausschließen.
if (!keycode.isEventKey(event, 'Tab')) {
event.stopPropagation();
}
this.player_.handleKeyDown(event);
}
}
/**
* Viele Komponenten hatten früher eine Methode `handleKeyPress`, die schlecht
* benannt, weil es auf ein "Keydown"-Ereignis reagiert hat. Dieser Methodenname lautet jetzt
* delegiert an `handleKeyDown`. Das bedeutet, dass jeder Aufruf von `handleKeyPress`
* nicht erleben, dass ihre Methodenaufrufe nicht mehr funktionieren.
*
* @param {EventTarget~Event} event
* Das Ereignis, das zum Aufruf dieser Funktion geführt hat.
* /
handleKeyPress(event) {
this.handleKeyDown(event);
}
/**
* Ein "Tap"-Ereignis ausgeben, wenn die Unterstützung für Berührungsereignisse erkannt wird. Dies wird genutzt, um
* unterstützung des Umschaltens der Steuerung durch Antippen des Videos. Sie werden aktiviert
* weil jede Unterkomponente sonst zusätzlichen Aufwand verursachen würde.
*
* @privat
* @Feuer Komponente#tap
* @listens Komponente#touchstart
* @listens Komponente#touchmove
* @listens Komponente#touchleave
* @listens Komponente#touchcancel
* @listens Komponente#touchend
* /
emitTapEvents() {
// Erfassen Sie die Startzeit, damit wir feststellen können, wie lange die Berührung gedauert hat
let touchStart = 0;
let firstTouch = null;
// Maximale Bewegung, die während eines Berührungsereignisses erlaubt ist, um noch als Tippen zu gelten
// Andere populäre Bibliotheken verwenden zwischen 2 (hammer.js) und 15,
// 10 scheint also eine schöne, runde Zahl zu sein.
const tapMovementThreshold = 10;
// Die maximale Länge einer Berührung, die noch als Antippen gewertet werden kann
const touchTimeThreshold = 200;
let couldBeTap;
this.on('touchstart', function(event) {
// Bei mehr als einem Finger ist dies nicht als Klick zu werten
if (event.touches.length === 1) {
// Kopieren von pageX/pageY aus dem Objekt
firstTouch = {
pageX: event.touches[0].pageX,
pageY: event.touches[0].pageY
};
// Aufzeichnung der Startzeit, damit wir ein Antippen im Gegensatz zu "Berühren und Halten" erkennen können
touchStart = window.performance.now();
// Zurücksetzen der couldBeTap-Verfolgung
couldBeTap = true;
}
});
this.on('touchmove', function(event) {
// Bei mehr als einem Finger ist dies nicht als Klick zu werten
if (event.touches.length > 1) {
couldBeTap = false;
} else if (firstTouch) {
// Einige Geräte lösen bei jeder noch so kleinen Berührung Touchmoves aus.
// Wenn wir uns also nur ein kleines Stück bewegen, könnte dies immer noch ein Wasserhahn sein
const xdiff = event.touches[0].pageX - firstTouch.pageX;
const ydiff = event.touches[0].pageY - firstTouch.pageY;
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
if (touchDistance > tapMovementThreshold) {
couldBeTap = false;
}
}
});
const noTap = function() {
couldBeTap = false;
};
// TODO: Hören Sie sich das ursprüngliche Ziel an. http://youtu.be/DujfpXOKUp8?t=13m8s
this.on('touchleave', noTap);
this.on('touchcancel', noTap);
// Wenn die Berührung endet, messen Sie, wie lange sie gedauert hat, und lösen Sie die entsprechende
// Ereignis
this.on('touchend', function(event) {
firstTouch = null;
// Nur fortfahren, wenn das Ereignis touchmove/leave/cancel nicht stattgefunden hat
if (couldBeTap === true) {
// Messen, wie lange die Berührung gedauert hat
const touchTime = window.performance.now() - touchStart;
// Vergewissern Sie sich, dass die Berührung unter dem Schwellenwert lag, um als Antippen zu gelten
wenn (touchTime < touchTimeThreshold) {
// Lassen Sie nicht zu, dass der Browser dies in einen Klick verwandelt
event.preventDefault();
/**
* Wird ausgelöst, wenn eine "Komponente" angetippt wird.
*
* @event Komponente#tap
* @Typ {EventTarget~Event}
* /
this.trigger('tap');
// Es kann sinnvoll sein, das berührende Ereignisobjekt zu kopieren und die
// Typ zu tippen, wenn die anderen Ereigniseigenschaften nicht exakt sind nach
// Events.fixEvent läuft (z.B. event.target)
}
}
});
}
/**
* Diese Funktion meldet Benutzeraktivitäten, wenn Berührungsereignisse auftreten. Dies kann zu
* von allen Unterkomponenten ausgeschaltet werden, die Berührungsereignisse auf eine andere Weise wirken lassen wollen.
*
* Berichten Sie die Berührungsaktivitäten des Benutzers, wenn Berührungsereignisse auftreten. Benutzeraktivität wird genutzt, um
* bestimmen, wann Steuerelemente ein- und ausgeblendet werden sollen. Es ist ganz einfach, wenn es um die Maus geht
* ereignisse, denn jedes Mausereignis sollte die Steuerelemente anzeigen. So erfassen wir die Maus
* ereignisse, die auf den Spieler zukommen, und melden Aktivitäten, wenn dies geschieht.
* Bei Berührungsereignissen ist es nicht so einfach, den Spieler zwischen "Touchstart" und "Touchende" umzuschalten
* kontrollen. Touch-Events können uns also auch auf Spielerebene nicht helfen.
*
* Die Benutzeraktivität wird asynchron überprüft. Was also passieren könnte, ist ein Tap-Ereignis
* auf dem Video schaltet die Steuerung aus. Dann sprudelt das "berührende" Ereignis bis zu
* den Spieler. Wenn es Benutzeraktivitäten melden würde, würde es die Kontrollen nach rechts drehen
* wieder auf. Wir möchten auch nicht vollständig verhindern, dass Touch-Events aufblasen.
* Außerdem sollte ein "touchmove"-Ereignis und alles andere als ein Antippen nicht zu
* kontrollen wieder ein.
*
* @listens Komponente#touchstart
* @listens Komponente#touchmove
* @listens Komponente#touchend
* @listens Komponente#touchcancel
* /
enableTouchActivity() {
// Nicht fortfahren, wenn der Stammplayer die Meldung von Benutzeraktivitäten nicht unterstützt
if (!this.player() || !this.player().reportUserActivity) {
rückkehr;
}
// Listener für die Meldung, dass der Benutzer aktiv ist
const report = Fn.bind(this.player(), this.player().reportUserActivity);
let touchHolding;
this.on('touchstart', function() {
report();
// Solange sie das Gerät berühren oder die Maus gedrückt halten,
// Wir betrachten sie als aktiv, auch wenn sie ihren Finger oder ihre Maus nicht bewegen.
// Wir wollen also weiterhin aktualisieren, dass sie aktiv sind
this.clearInterval(touchHolding);
// Bericht in demselben Intervall wie activityCheck
touchHolding = this.setInterval(report, 250);
});
const touchEnd = function(event) {
report();
// Anhalten des Intervalls, das die Aktivität aufrechterhält, wenn die Berührung gehalten wird
this.clearInterval(touchHolding);
};
this.on('touchmove', report);
this.on('touchend', touchEnd);
this.on('touchcancel', touchEnd);
}
/**
* Ein Callback, der keine Parameter hat und in den `Component`s Kontext gebunden ist.
*
* @callback Component~GenericCallback
* @diese Komponente
* /
/**
* Erzeugt eine Funktion, die nach einer Zeitüberschreitung von `x` Millisekunden ausgeführt wird. Diese Funktion ist eine
* umhüllung von `window.setTimeout`. Es gibt einige Gründe, die für diese Variante sprechen
* stattdessen aber:
* 1. Es wird über {@link Component#clearTimeout} gelöscht, wenn
* {@link Component#dispose} wird aufgerufen.
* 2. Der Funktions-Callback wird in einen {@link Component~GenericCallback} umgewandelt
*
* > Anmerkung: Sie können `window.cleArtimeOut` für die von dieser Funktion zurückgegebene ID nicht verwenden. Diese
* wird dazu führen, dass sein Dispose-Listener nicht bereinigt wird! Bitte verwenden Sie
* {@link Component#clearTimeout} oder {@link Component#dispose} stattdessen.
*
* @param {Component~GenericCallback} fn
* Die Funktion, die nach `timeout` ausgeführt wird.
*
* @param {number} timeout
* Zeitüberschreitung in Millisekunden, die vor der Ausführung der angegebenen Funktion verstreichen soll.
*
* @return {number}
* Gibt eine Timeout-ID zurück, die zur Identifizierung des Timeouts verwendet wird. Sie kann auch
* wird in {@link Component#clearTimeout} verwendet, um die Zeitüberschreitung zu löschen, die
* festgelegt wurde.
*
* @listens Komponente#dispose
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
* /
setTimeout(fn, timeout) {
// als Variablen deklarieren, damit sie in der Timeout-Funktion ordnungsgemäß verfügbar sind
// eslint-disable-next-line
var timeoutId, disposeFn;
fn = Fn.bind(this, fn);
this.clearTimersOnDispose_();
timeoutId = window.setTimeout(() => {
if (this.setTimeoutIds_.has(timeoutId)) {
this.setTimeoutIds_.delete(timeoutId);
}
fn();
}, timeout);
this.setTimeoutIds_.add(timeoutId);
return timeoutId;
}
/**
* Löscht eine Zeitüberschreitung, die durch `window.setTimeout` oder
* {@link Component#setTimeout}. Wenn Sie über {@link Component#setTimeout} eine Zeitüberschreitung festlegen
* diese Funktion anstelle von `window.clearTimout` verwenden. Wenn nicht, entsorgen Sie
* listener wird erst nach {@link Component#dispose} aufgeräumt!
*
* @param {Nummer} timeoutId
* Die ID der zu löschenden Zeitüberschreitung. Der Rückgabewert von
* {@link Component#setTimeout} oder `window.setTimeout`.
*
* @return {number}
* Gibt die Timeout-ID zurück, die gelöscht wurde.
*
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
* /
clearTimeout(timeoutId) {
if (this.setTimeoutIds_.has(timeoutId)) {
this.setTimeoutIds_.delete(timeoutId);
window.clearTimeout(timeoutId);
}
return timeoutId;
}
/**
* Erzeugt eine Funktion, die alle `x` Millisekunden ausgeführt wird. Diese Funktion ist ein Wrapper
* um `window.setInterval`. Es gibt jedoch ein paar Gründe, diesen stattdessen zu verwenden.
* 1. Es wird über {@link Component#clearInterval} gelöscht, wenn
* {@link Component#dispose} wird aufgerufen.
* 2. Der Funktions-Callback ist ein {@link Component~GenericCallback}
*
* @param {Component~GenericCallback} fn
* Die Funktion, die alle `x` Sekunden ausgeführt werden soll.
*
* @param {Anzahl} Intervall
* Führt die angegebene Funktion alle `x` Millisekunden aus.
*
* @return {number}
* Gibt eine ID zurück, die zur Identifizierung des Intervalls verwendet werden kann. Es kann auch verwendet werden in
* {@link Component#clearInterval}, um das Intervall zu löschen.
*
* @listens Komponente#dispose
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
* /
setInterval(fn, interval) {
fn = Fn.bind(this, fn);
this.clearTimersOnDispose_();
const intervalId = window.setInterval(fn, interval);
this.setIntervalIds_.add(intervalId);
return intervalId;
}
/**
* Löscht ein Intervall, das mit `window.setInterval` erstellt wurde oder
* {@link Component#setInterval}. Wenn Sie einen Inteval über {@link Component#setInterval} setzen
* diese Funktion anstelle von `window.clearInterval` verwenden. Wenn nicht, entsorgen Sie
* listener wird erst nach {@link Component#dispose} aufgeräumt!
*
* @param {Nummer} intervalId
* Die ID des zu löschenden Intervalls. Der Rückgabewert von
* {@link Component#setInterval} oder `window.setInterval`.
*
* @return {number}
* Gibt die Intervall-ID zurück, die gelöscht wurde.
*
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
* /
clearInterval(intervalId) {
if (this.setIntervalIds_.has(intervalId)) {
this.setIntervalIds_.delete(intervalId);
window.clearInterval(intervalId);
}
return intervalId;
}
/**
* Stellt einen Callback in die Warteschlange, der an requestAnimationFrame (rAF) übergeben wird, aber
* mit ein paar zusätzlichen Boni:
*
* - Unterstützt Browser, die rAF nicht unterstützen, indem sie auf
* {@link Component#setTimeout}.
*
* - Der Callback wird in einen {@link Component~GenericCallback} (d.h..
* an die Komponente gebunden).
*
* - Der automatische Abbruch des RAF-Callbacks erfolgt, wenn die Komponente
* entsorgt wird, bevor es aufgerufen wird.
*
* @param {Component~GenericCallback} fn
* Eine Funktion, die an diese Komponente gebunden ist und gerade ausgeführt wird
* vor dem nächsten Repaint des Browsers.
*
* @return {number}
* Gibt eine RAF-ID zurück, die zur Identifizierung der Zeitüberschreitung verwendet wird. Sie kann
* kann auch in {@link Component#cancelAnimationFrame} zum Abbrechen verwendet werden
* der Animationsrahmen-Rückruf.
*
* @listens Komponente#dispose
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
* /
requestAnimationFrame(fn) {
// Zurück zur Verwendung eines Timers.
if (!this.supportsRaf_) {
return this.setTimeout(fn, 1000 / 60);
}
this.clearTimersOnDispose_();
// als Variablen deklarieren, damit sie in der RAF-Funktion ordnungsgemäß verfügbar sind
// eslint-disable-next-line
var id;
fn = Fn.bind(this, fn);
id = window.requestAnimationFrame(() => {
if (this.rafIds_.has(id)) {
this.rafIds_.delete(id);
}
fn();
});
this.rafIds_.add(id);
return id;
}
/**
* Einen Animationsrahmen anfordern, aber nur eine benannte Animation
* rahmen wird in die Warteschlange gestellt. Eine weitere wird erst dann hinzugefügt, wenn
* die vorhergehende beendet ist.
*
* @param {string} name
* Der Name, der diesem requestAnimationFrame gegeben werden soll
*
* @param {Component~GenericCallback} fn
* Eine Funktion, die an diese Komponente gebunden ist und gerade ausgeführt wird
* vor dem nächsten Repaint des Browsers.
* /
requestNamedAnimationFrame(name, fn) {
if (this.namedRafs_.has(name)) {
rückkehr;
}
this.clearTimersOnDispose_();
fn = Fn.bind(this, fn);
const id = this.requestAnimationFrame(() => {
fn();
if (this.namedRafs_.has(name)) {
this.namedRafs_.delete(name);
}
});
this.namedRafs_.set(name, id);
name zurückgeben;
}
/**
* Bricht ein aktuelles benanntes Animationsbild ab, wenn es existiert.
*
* @param {string} name
* Der Name des abzubrechenden requestAnimationFrame.
* /
cancelNamedAnimationFrame(name) {
if (!this.namedRafs_.has(name)) {
rückkehr;
}
this.cancelAnimationFrame(this.namedRafs_.get(name));
this.namedRafs_.delete(name);
}
/**
* Bricht einen an {@link Component#requestAnimationFrame} übergebenen Warteschlangen-Callback ab
* (rAF).
*
* Wenn Sie einen rAF-Callback über {@link Component#requestAnimationFrame} in die Warteschlange stellen,
* diese Funktion anstelle von `window.cancelAnimationFrame` verwenden. Wenn Sie das nicht tun,
* ihr Dispose-Listener wird nicht aufgeräumt, bis {@link Component#dispose}!
*
* @param {Nummer} id
* Die RAF-ID ist zu löschen. Der Rückgabewert von {@link Component#requestAnimationFrame}.
*
* @return {number}
* Gibt die RAF-ID zurück, die gelöscht wurde.
*
* @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
* /
cancelAnimationFrame(id) {
// Zurück zur Verwendung eines Timers.
if (!this.supportsRaf_) {
return this.clearTimeout(id);
}
if (this.rafIds_.has(id)) {
this.rafIds_.delete(id);
window.cancelAnimationFrame(id);
}
return id;
}
/**
* Eine Funktion zur Einrichtung von `requestAnimationFrame`, `setTimeout`,
* und `setInterval`, Löschung bei Dispose.
*
* > Zuvor fügte jeder Timer selbständig Dispose-Listener hinzu und entfernte sie wieder.
* Um die Leistung zu verbessern, wurde beschlossen, sie alle zu stapeln und "Set" zu verwenden
* um ausstehende Timer-IDs zu verfolgen.
*
* @privat
* /
clearTimersOnDispose_() {
if (this.clearingTimersOnDispose_) {
rückkehr;
}
this.clearingTimersOnDispose_ = true;
this.one('dispose', () => {
[
['namedRafs_', 'cancelNamedAnimationFrame'],
['rafIds_', 'cancelAnimationFrame'],
['setTimeoutIds_', 'clearTimeout'],
['setIntervalIds_', 'clearInterval']
].forEach(([idName, cancelName]) => {
// für einen "Set"-Schlüssel wird tatsächlich wieder der Wert sein
// also forEach((val, val) =>` aber für Maps wollen wir verwenden
// den Schlüssel.
this[idName].forEach((val, key) => this[cancelName](key));
});
this.clearingTimersOnDispose_ = false;
});
}
/**
* Registrieren Sie eine "Komponente" mit "videojs" unter Angabe des Namens und der Komponente.
*
* > HINWEIS: {@link Tech}s sollten nicht als `Komponente` registriert werden. {@link Tech}s
* sollte mit {@link Tech.registerTech} registriert werden oder
* {@link videojs:videojs.registerTech}.
*
* > HINWEIS: Diese Funktion ist auch bei videojs zu sehen als
* {@link videojs:videojs.registerComponent}.
*
* @param {string} name
* Der Name der zu registrierenden `Komponente`.
*
* @param {Component} ComponentToRegister
* Die zu registrierende `Komponenten`-Klasse.
*
* @return {Komponente}
* Die "Komponente", die registriert wurde.
* /
static registerComponent(name, ComponentToRegister) {
if (typeof name !== 'string' || !name) {
throw new Error(`Ungültiger Komponentenname, "${name}"; muss eine nicht leere Zeichenkette sein.`);
}
const Tech = Component.getComponent('Tech');
// Wir müssen sicherstellen, dass diese Prüfung nur durchgeführt wird, wenn Tech registriert wurde.
const isTech = Tech && Tech.isTech(ComponentToRegister);
const isComp = Component === ComponentToRegister ||
Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
if (isTech || !isComp) {
grund lassen;
wenn (isTech) {
reason = 'Techs müssen mit Tech.registerTech() registriert werden';
} else {
grund = 'muss eine Komponenten-Unterklasse sein';
}
throw new Error(`Unzulässige Komponente, "${Name}"; ${Grund}.`);
}
name = toTitleCase(name);
if (!Component.components_) {
Component.components_ = {};
}
const Player = Component.getComponent('Player');
if (name === 'Player' && Player && Player.players) {
const players = Player.players;
const playerNames = Object.keys(players);
// Wenn wir Spieler haben, die entsorgt wurden, dann wird ihr Name immer noch
// in Players.players. Wir müssen also eine Schleife durchlaufen und überprüfen, ob der Wert
// für jedes Element ist nicht null. Dies ermöglicht die Registrierung der Player-Komponente
// nachdem alle Spieler entsorgt wurden oder bevor welche erstellt wurden.
wenn (Spieler &&
playerNames.length > 0 &&
playerNames.map((pname) => players[pname]).every(Boolean)) {
throw new Error('Kann die Komponente Player nicht registrieren, nachdem der Player erstellt wurde.');
}
}
Component.components_[name] = ComponentToRegister;
Component.components_[toLowerCase(name)] = ComponentToRegister;
return ComponentToRegister;
}
/**
* Ermittelt eine "Komponente" anhand des Namens, unter dem sie registriert wurde.
*
* @param {string} name
* Der Name der zu ermittelnden Komponente.
*
* @return {Komponente}
* Die "Komponente", die unter dem angegebenen Namen registriert wurde.
* /
static getComponent(name) {
if (!name || !Component.components_) {
rückkehr;
}
return Component.components_[name];
}
}
/**
* Ob diese Komponente `requestAnimationFrame` unterstützt oder nicht.
*
* Dies wird in erster Linie zu Testzwecken ausgesetzt.
*
* @privat
* @Typ {Boolean}
* /
Component.prototype.supportsRaf_ = typeof window.requestAnimationFrame === 'function' &&
typeof window.cancelAnimationFrame === 'function';
Component.registerComponent('Component', Component);
standardkomponente exportieren;