/**
* 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();
}
}