/**
 * @file modal-dialog.js
 * /
import * as Dom from './utils/dom';
importiere Komponente von './komponente';
import window from 'global/window';
dokument aus 'global/document' importieren;
import keycode from 'keycode';

const MODAL_CLASS_NAME = 'vjs-modal-dialog';

/**
 * Der `ModalDialog` wird über dem Video und seinen Steuerelementen angezeigt, der blockiert
 * Interaktion mit dem Spieler, bis er geschlossen wird.
 *
 * Modale Dialoge enthalten eine Schaltfläche „Schließen“ und werden geschlossen, wenn diese Schaltfläche
 * ist aktiviert - oder wenn ESC an einer beliebigen Stelle gedrückt wird.
 *
 * @erweitert Komponente
 * /
Klasse ModalDialog erweitert Komponente {

  /**
   * Erstellen Sie eine Instanz dieser Klasse.
   *
   * @param {Player} Spieler
   *        Der `Player`, dem diese Klasse zugeordnet werden soll.
   *
   * @param {Object} [Optionen]
   *        Der Schlüssel/Wertspeicher der Playeroptionen.
   *
   * @param {Gemischt} [options.content=undefiniert]
   * Stellen Sie benutzerdefinierte Inhalte für dieses Modal bereit.
   *
   * @param {string} [Optionen.beschreibung]
   * Eine Textbeschreibung für das Modal, hauptsächlich aus Gründen der Barrierefreiheit.
   *
   * @param {boolean} [options.FillAlways=Falsch]
   * Normalerweise werden Modals nur beim ersten Mal automatisch gefüllt
   * sie öffnen. Dadurch wird das Modal angewiesen, seinen Inhalt zu aktualisieren
   * jedes Mal, wenn es geöffnet wird.
   *
   * @param {string} [options.label]
   * Eine Textbezeichnung für das Modal, hauptsächlich aus Gründen der Barrierefreiheit.
   *
   * @param {boolean} [options.pauseOnOpen=wahr]
   * Wenn `true` ist, wird die Wiedergabe angehalten, wenn
   * Das Modal wird geöffnet und beim Schließen wieder aufgenommen.
   *
   * @param {boolean} [options.temporary=wahr]
   * Wenn `true`, kann das Modal nur einmal geöffnet werden; es wird
   * wird entsorgt, sobald es geschlossen ist.
   *
   * @param {boolean} [options.uncloseable=falsch]
   * Wenn `true` ist, kann der Benutzer das Modal nicht schließen
   * über die Benutzeroberfläche auf die normale Weise. Programmatisches Schließen ist
   * immer noch möglich.
   * /
  constructor(spieler, optionen) {
    super(Spieler, Optionen);

    this.handleKeyDown_ = (e) => this.handleKeyDown(e);
    this.close_ = (e) => this.close (e);
    this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = falsch;

    this.closeable (! this.options_.uncloseable);
    this.content (this.options_.content);

    //Stellen Sie sicher, dass der ContentEl definiert ist, NACHDEM alle Kinder initialisiert wurden
    //weil wir nur den Inhalt des Modals im ContentEl haben wollen
    //(nicht die UI-Elemente wie die Schaltfläche zum Schließen).
    this.contentEl_ = Dom.createEl('div', {
      Klassenname: `$ {MODAL_CLASS_NAME} -content`
    }, {
      Rolle: 'Dokument'
    });

    this.descel_ = dom.createEl ('p', {
      Klassenname: `$ {MODAL_CLASS_NAME} -description vjs-control-text`,
      id: this.el () .getAttribute ('aria-describedby')
    });

    dom.textContent (this.descel_, this.description ());
    this.el_.appendChild (this.descel_);
    this.el_.appendChild (this.contentEL_);
  }

  /**
   * Erstelle das DOM-Element des `ModalDialog`
   *
   * @return {Element}
   *         Das DOM-Element, das erstellt wird.
   * /
  createEl() {
    return super.createEl('div', {
      className: this.buildCSSClass(),
      tabIndex: -1
    }, {
      'aria-describedby': `$ {this.id ()} _description`,
      'aria-hidden': 'wahr',
      'aria-label': this.label (),
      'Rolle': 'Dialog'
    });
  }

  dispose() {
    this.contentEl_ = null;
    this.descel_ = null;
    this.previouslyActiveEl_ = null;

    super.dispose();
  }

  /**
   * Erzeugt den Standard-DOM "Klassenname".
   *
   * @return {string}
   *         Der DOM `className` für dieses Objekt.
   * /
  buildCSSClass() {
    gib `$ {MODAL_CLASS_NAME} vjs-hidden $ {super.buildCssClass ()} `zurück;
  }

  /**
   * Gibt die Label-Zeichenfolge für dieses Modal zurück. Wird hauptsächlich für die Barrierefreiheit verwendet.
   *
   * @return {string}
   * die lokalisierte oder unformatierte Bezeichnung dieses Modals.
   * /
  label() {
    gib this.localize (this.options_.label || 'Modales Fenster') zurück;
  }

  /**
   * Gibt die Beschreibungszeichenfolge für dieses Modal zurück. Hauptsächlich verwendet für
   * Barrierefreiheit.
   *
   * @return {string}
   * Die lokalisierte oder rohe Beschreibung dieses Modals.
   * /
  description() {
    let desc = this.options_.description || this.localize ('Dies ist ein modales Fenster');

    //Hängt eine universelle Schließbarkeitsnachricht an, wenn das Modal schließbar ist.
    wenn (this.closeable ()) {
      desc += '' + this.localize ('Dieses Modal kann geschlossen werden, indem Sie die Esc-Taste drücken oder die Schaltfläche zum Schließen aktivieren. ');
    }

    Rückfahrt zum Abstieg;
  }

  /**
   * Öffnet das Modal.
   *
   * @fires modalDialog #beforemodalopen
   * @fires modalDialog #modalopen
   * /
  öffnen () {
    if (!this.opened_) {
      const player = this.player();

      /**
        * Wird ausgelöst, kurz bevor ein `ModalDialog` geöffnet wird.
        *
        * @event modalDialog #beforemodalopen
        * @Typ {EventTarget~Event}
        * /
      this.trigger ('beforemodalopen');
      this.opened_ = wahr;

      //Inhalt füllen, wenn das Modal noch nie geöffnet wurde und
      //wurde nie gefüllt.
      if (this.options_.fillAlways ||! dies. wurde geöffnet _ &&! this.hasbeenFilled_) {
        this.fill();
      }

      //Wenn der Spieler gespielt hat, pausiere es und notiere dir, wie es zuvor war
      //Spielstatus.
      this.wasPlaying_ =! player.pausiert ();

      wenn (this.options_.pauseOnOpen && this.wasPlaying_) {
        spieler.pause ();
      }

      this.on('keydown', this.handleKeyDown_);

      //Steuerelemente ausblenden und notieren, ob sie aktiviert waren.
      this.hadControls_= player.controls ();
      player.controls (falsch);

      this.show();
      this.ConditionalFocus_ ();
      this.el () .setAttribute ('aria-hidden', 'falsch');

      /**
        * Wird unmittelbar nach dem Öffnen eines `ModalDialog` ausgelöst.
        *
        * @event modalDialog #modalopen
        * @Typ {EventTarget~Event}
        * /
      this.trigger ('modalopen');
      this.hasBeenOpened_ = wahr;
    }
  }

  /**
   * Ob der `ModalDialog` gerade geöffnet oder geschlossen ist.
   *
   * @param {boolean} [Wert]
   * Falls angegeben, wird das Modal geöffnet (`true`) oder geschlossen (`false`).
   *
   * @return {boolean}
   * der aktuelle Öffnungsstatus des Modaldialogs
   * /
  geöffnet (Wert) {
    if (typeof value === 'boolean') {
      dieser [Wert? 'öffnen': 'schließen'] ();
    }
    gib this.opened_ zurück;
  }

  /**
   * Schließt das Modal, tut nichts, wenn der `ModalDialog`
   * nicht geöffnet.
   *
   * @fires modalDialog #beforemodalclose
   * @fires modalDialog #modalclose
   * /
  schließen () {
    if (!this.opened_) {
      rückkehr;
    }
    const player = this.player();

    /**
      * Wird ausgelöst, kurz bevor ein `ModalDialog` geschlossen wird.
      *
      * @event modalDialog #beforemodalclose
      * @Typ {EventTarget~Event}
      * /
    this.trigger ('beforemodalclose');
    this.opened_ = falsch;

    wenn (this.wasPlaying_ && this.options_.pauseOnOpen) {
      spieler.play ();
    }

    this.off('keydown', this.handleKeyDown_);

    wenn (this.hadControls_) {
      player.controls (wahr);
    }

    this.hide();
    this.el () .setAttribute ('aria-hidden', 'wahr');

    /**
      * Wird unmittelbar nach dem Schließen eines `ModalDialog` ausgelöst.
      *
      * @event modalDialog #modalclose
      * @Typ {EventTarget~Event}
      * /
    this.trigger ('modalclose');
    this.conditionalBlur_ ();

    wenn (this.options_.temporary) {
      this.dispose ();
    }
  }

  /**
   * Prüfen Sie, ob der `ModalDialog` über die Benutzeroberfläche geschlossen werden kann.
   *
   * @param {boolean} [Wert]
   * Wenn es als boolescher Wert angegeben wird, wird die Option `closeable` gesetzt.
   *
   * @return {boolean}
   * Gibt den Endwert der schließbaren Option zurück.
   * /
  schließbar (Wert) {
    if (typeof value === 'boolean') {
      const closeable = this.closeable_ =!! wert;
      let close = this.getChild ('closeButton');

      //Wenn dies schließbar gemacht wird und es keine Schaltfläche zum Schließen gibt, füge eine hinzu.
      if (schließbar &&! schließen) {

        //Der Schließen-Button sollte ein untergeordnetes Element des Modals sein - nicht sein
        //Inhaltselement, also ändere das Inhaltselement vorübergehend.
        const temp = this.contentEL_;

        this.contentEL_ = this.el_;
        close = this.addChild ('closeButton', {controlText: 'Modalen Dialog schließen'});
        this.contentEL_ = temporärer Inhalt;
        this.on (close, 'close', this.close_);
      }

      //Wenn dies nicht mehr geschlossen werden kann und über eine Schaltfläche zum Schließen verfügt, entfernen Sie es.
      wenn (! schließbar & schließen) {
        this.off (close, 'close', this.close_);
        this.removeChild (schließen);
        schließen.entsorgen ();
      }
    }
    gib this.closeable_ zurück;
  }

  /**
   * Füllen Sie das Inhaltselement des Modals mit der Option „Inhalt“ des Modals.
   * Das Inhaltselement wird geleert, bevor diese Änderung stattfindet.
   * /
  füllen () {
    this.fillWith (this.content ());
  }

  /**
   * Füllen Sie das Inhaltselement des Modals mit beliebigem Inhalt.
   * Das Inhaltselement wird geleert, bevor diese Änderung stattfindet.
   *
   * @fires modalDialog #beforemodalfill
   * @fires modalDialog #modalfill
   *
   * @param {Gemischt} [Inhalt]
   * Hier gelten die gleichen Regeln wie für die Option `Content`.
   * /
  fillWith (Inhalt) {
    const contentEL = this.ContentEl ();
    const parentEl = contentEL.parentNode;
    const nextSibLingel = contentEL.NextSibling;

    /**
      * Wird ausgelöst, kurz bevor ein `ModalDialog` mit Inhalt gefüllt wird.
      *
      * @event modalDialog #beforemodalfill
      * @Typ {EventTarget~Event}
      * /
    this.trigger ('beforemodalfill');
    this.hasBeenFilled_ = wahr;

    //Trenne das Inhaltselement vom DOM, bevor du es ausführst
    //Manipulation, um zu vermeiden, dass das Live-DOM mehrfach geändert wird.
    Parentel.removeChild (ContentEl);
    diese.empty ();
    dom.insertContent (ContentEl, Inhalt);
    /**
     * Wird ausgelöst, kurz nachdem ein `ModalDialog` mit Inhalt gefüllt wurde.
     *
     * @event modalDialog #modalfill
     * @Typ {EventTarget~Event}
     * /
    this.trigger ('modalfill');

    //Fügen Sie das neu gefüllte Inhaltselement erneut ein.
    wenn (nächstes Geschwisterchen) {
      Parentel.insertBefore (ContentEl, nextSiblingel);
    } sonst {
      Parentel.appendChild (ContentEl);
    }

    //stelle sicher, dass der Schließen-Button im Dialog DOM an letzter Stelle steht
    const closeButton = this.getChild ('closeButton');

    wenn (CloseButton) {
      Parentel.appendChild (CloseButton.el_);
    }
  }

  /**
   * Leert das Inhaltselement. Dies geschieht jedes Mal, wenn das Modal gefüllt ist.
   *
   * @fires modalDialog #beforemodalempty
   * @fires modalDialog #modalempty
   * /
  leer () {
    /**
    * Wird ausgelöst, kurz bevor ein `ModalDialog` geleert wird.
    *
    * @event modalDialog #beforemodalempty
    * @Typ {EventTarget~Event}
    * /
    this.trigger ('beforemodalempty');
    dom.emptyEl (this.ContentEl ());

    /**
    * Wird ausgelöst, kurz nachdem ein `ModalDialog` geleert wurde.
    *
    * @event modalDialog #modalempty
    * @Typ {EventTarget~Event}
    * /
    this.trigger ('modalempty');
  }

  /**
   * Ruft den modalen Inhalt ab oder legt ihn fest, der normalisiert wird, bevor er
   * ins DOM gerendert.
   *
   * Dies aktualisiert das DOM nicht und füllt das Modal nicht, sondern es wird während des
   * dieser Prozess.
   *
   * @param {Gemischt} [Wert]
   * Legt, falls definiert, den internen Inhaltswert fest, der auf der
   * nächste Aufrufe von `fill`. Dieser Wert wird normalisiert, bevor er
   * eingefügt. Um den internen Inhaltswert zu „löschen“, übergeben Sie `null`.
   *
   * @return {Mixed}
   * Der aktuelle Inhalt des modalen Dialogs
   * /
  Inhalt (Wert) {
    if (typeof wert !== 'undefined') {
      this.content_ = Wert;
    }
    gib this.content_ zurück;
  }

  /**
   * Fokussiere den modalen Dialog bedingt, wenn der Fokus zuvor auf dem Spieler lag.
   *
   * @privat
   * /
  Bedingter Fokus_ () {
    const activeEL = document.activeElement;
    const playerEl = this.player_.el_;

    this.previouslyActiveEl_ = null;

    if (playerEL.contains (activeEL) || playerEL === activeEL) {
      this.previouslyActiveL_ = ActiveL;

      this.focus ();
    }
  }

  /**
   * das Element bedingt verwischen und das letzte fokussierte Element neu fokussieren
   *
   * @privat
   * /
  conditionalBlur_() {
    wenn (this.previouslyActiveL_) {
      this.previous activeel_.focus ();
      this.previouslyActiveEl_ = null;
    }
  }

  /**
   * Keydown-Handler. Angehängt, wenn Modal fokussiert ist.
   *
   * @listens keydown
   * /
  handleKeyDown(event) {

    //Erlaube nicht, dass Tastenkombinationen den modalen Dialog erreichen.
    event.stopPropagation();

    if (keyCode.isEventKey (event, 'Escape') && this.closeable ()) {
      event.preventDefault();
      this.close();
      rückkehr;
    }

    //vorzeitig beenden, wenn es sich nicht um eine Tabulatortaste handelt
    if (!keycode.isEventKey(event, 'Tab')) {
      rückkehr;
    }

    const focusableEls = this.focusableEls_ ();
    const activeEL = this.el_.QuerySelector (':focus');
    lass focusIndex;

    für (sei i = 0; i < focusableEls.length; i++) {
      wenn (activeEL === focusableELs [i]) {
        FokusIndex = i;
        pause;
      }
    }

    wenn (document.activeElement === this.el_) {
      Fokusindex = 0;
    }

    wenn (event.shiftKey && focusIndex === 0) {
      focusableELS [focusableELS.length - 1] .focus ();
      event.preventDefault();
    } sonst wenn (! event.shiftKey && focusIndex === FocusableEls.length - 1) {
      focusableELS [0] .focus ();
      event.preventDefault();
    }
  }

  /**
   * hol dir alle fokussierbaren Elemente
   *
   * @privat
   * /
  FocusableELS_ () {
    const allChildren = this.el_.querySelectorAll ('*');

    return Array.prototype.filter.call (allChildren, (child) => {
      return ((untergeordnete Instanz von window.htmLanchorelement ||
               untergeordnete Instanz von window.htmlAreaElement) & child.hasAttribute ('href')) ||
             ((untergeordnete Instanz von window.htmlinputElement ||
               untergeordnete Instanz von window.htmlSelectElement ||
               untergeordnete Instanz von window.htmlTextAreaElement ||
               untergeordnete Instanz von window.htmlButtonElement) &&! child.hasAttribute ('deaktiviert') |
             (untergeordnete Instanz von window.htmliFrameElement) ||
               untergeordnete Instanz von window.htmlobjectElement ||
               untergeordnete Instanz von window.htmlembedElement) ||
             (child.hasAttribute ('tabindex') & child.getAttribute ('tabindex')! = -1) |
             (child.hasAttribute ('contenteditable'));
    });
  }
}

/**
 * Standardoptionen für die Standardoptionen von `ModalDialog`.
 *
 * @Typ {Objekt}
 * @privat
 * /
modalDialog.prototype.options_ = {
  pauseOnOpen: wahr,
  temporär: wahr
};

component.registerComponent ('modalDialog', modalDialog);
exportiert den Standard-ModalDialog;