# Source: app/AutoSave.js

    /**
     * Implements the autosave feature of {@link XMLEditorApp}.
     * <p>This class may be used independently from <code>XMLEditorApp</code>.
     */
    export class AutoSave {
        /**
         * Constructs an <code>AutoSave</code> object.
         *
         * @param {XMLEditor} xmlEditor - the XML editor.
         * @param {string} mode - autosave mode:
         * <code>"local"</code> (autosave local files),
         * <code>"remote"</code> (autosave remote files) or
         * <code>"both"</code> (autosave both local and remotefiles).
         * @param {number} intervalMillis - autosave interval in milliseconds;
         * must be at least 10000 (10s).
         */
        constructor(xmlEditor, mode, intervalMillis) {
            this._xmlEditor = xmlEditor;

            if (intervalMillis < 10000) {
                intervalMillis = 10000;
            }
            this._intervalMillis = intervalMillis;
            this._saveTimer = null;

            if (!mode ||
                !(mode === "local" || mode === "remote" || mode === "both")) {
                mode = "remote";
            }
            if (!FSAccess.isAvailable() &&
                (mode === "both" || mode === "local")) {
                mode = (mode === "local")? null : "remote";
            }
            this._mode = mode;

            this._docOpenedHandler = this._docClosedHandler = null;
            if (this._mode === null) {
                // AutoSave not activable.
                return;
            }

            // ---

            this._docOpenedHandler = this.onDocumentOpened.bind(this);
            xmlEditor.addEventListener("documentCreated", this._docOpenedHandler);
            xmlEditor.addEventListener("documentOpened", this._docOpenedHandler);
            xmlEditor.addEventListener("documentRecovered", this._docOpenedHandler);

            this._docClosedHandler = this.onDocumentClosed.bind(this);
            xmlEditor.addEventListener("documentClosed", this._docClosedHandler);

            // ---

            if (this._xmlEditor.documentIsOpened) {
                this.onDocumentOpened(/*event*/ null);
            }
        }

        /**
         * Dispose this <code>AutoSave</code> object when it is not needed anymore.
         */
        dispose() {
            if (this._docOpenedHandler !== null) {
                this._xmlEditor.removeEventListener("documentCreated",
                                                    this._docOpenedHandler);
                this._xmlEditor.removeEventListener("documentOpened",
                                                    this._docOpenedHandler);
                this._xmlEditor.removeEventListener("documentRecovered",
                                                    this._docOpenedHandler);
                this._docOpenedHandler = null;
            }

            if (this._docClosedHandler !== null) {
                this._xmlEditor.removeEventListener("documentClosed",
                                                    this._docClosedHandler);
                this._docClosedHandler = null;
            }

            this.cancelSaveTimer();
        }

        cancelSaveTimer() {
            if (this._saveTimer !== null) {
                clearInterval(this._saveTimer);
                this._saveTimer = null;
            }
        }

        /**
         * Get the <code>xmlEditor</code> property of this <code>AutoSave</code>
         * object. See constructor.
         *
         * @type {XMLEditor}
         */
        get xmlEditor() {
            return this._xmlEditor;
        }

        /**
         * Get the <code>mode</code> property of this <code>AutoSave</code>
         * object. See constructor.
         *
         * @type {string}
         */
        get mode() {
            return this._mode;
        }

        /**
         * Get the <code>intervalMillis</code> property of
         * this <code>AutoSave</code> object. See constructor.
         *
         * @type {number}
         */
        get intervalMillis() {
            return this._intervalMillis;
        }

        onDocumentOpened(event) {
            this.cancelSaveTimer();

            if (this.activable) {
                this._saveTimer = setInterval(() => {
                    this.saveDocument();
                }, this._intervalMillis);
            }
        }

        /**
         * Get the <code>activable</code> property of
         * this <code>AutoSave</code> object which may be used to test
         * whether it will be active after a document is created, opened
         * or recovered.
         * <p>Typically invoked from a {@link DocumentOpenedEvent} (of any kind)
         * event handler to determine whether this <code>AutoSave</code> object
         * will be active given opened document and chosen autosave mode.
         *
         * @type {boolean}
         */
        get activable() {
            return (this._mode !== null &&
                    this._xmlEditor.documentIsOpened &&
                    !this._xmlEditor.readOnlyDocument &&
                    (this._xmlEditor.isRemoteFile &&
                     (this._mode === "both" || this._mode === "remote")) ||
                    (!this._xmlEditor.isRemoteFile &&
                     (this._mode === "both" || this._mode === "local")));
        }

        saveDocument() {
            if (!this._xmlEditor.connected ||
                !this._xmlEditor.documentIsOpened ||
                !this._xmlEditor.saveNeeded) {
                // Not needed.
                return;
            }

            if (this._xmlEditor.saveAsNeeded) {
                // May be later.
                this._xmlEditor.showStatus(
                    "No autosave: \"Save As\" needed first.");
                return;
            }

            const remote = this._xmlEditor.isRemoteFile;
            if (remote) {
                this.doSaveDocument(true);
                return;
            }

            if (this._xmlEditor.documentFileHandle === null) {
                // May be later.
                this._xmlEditor.showStatus(
                    "No autosave: interactive \"Save\" needed first.");
                return;
            }

            // Local file having a handle but can it be used to save the document?

            this._xmlEditor.documentFileHandle.queryPermission({mode: "readwrite"})
                .then((status) => {
                    if (status === "granted") {
                        this.doSaveDocument(false);
                    } else {
                        this._xmlEditor.showStatus(
                            "No autosave: interactive \"Save\" needed first.");
                    }
                })
                .catch((error) => {
                    this._xmlEditor.showStatus(`Autosave error: ${error}`);
                });
        }

        doSaveDocument(remote) {
            XMLEditorApp.doSaveDocument(this._xmlEditor, remote)
                .then((saved) => {
                    this._xmlEditor.showStatus(saved? "Autosave." :
                                               "No autosave: unknown reason.");
                })
                .catch((error) => {
                    this._xmlEditor.showStatus(`Autosave error: ${error}`);
                });
        }

        onDocumentClosed(event) {
            this.cancelSaveTimer();
        }
    }