From 83365a3756085563d34b7ca91f1e8863792280cb Mon Sep 17 00:00:00 2001 From: Aki Sasaki Date: Tue, 18 Aug 2020 13:50:23 -0700 Subject: [PATCH] confirm if leaving a modified form without saving --- src/display/annotation_storage.js | 29 +++++++++++++++++++ src/display/api.js | 16 +++++++---- test/unit/annotation_storage_spec.js | 43 ++++++++++++++++++++++++++++ web/base_viewer.js | 12 ++++++++ 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/display/annotation_storage.js b/src/display/annotation_storage.js index d9c7bc21a776b..96e7302235c2c 100644 --- a/src/display/annotation_storage.js +++ b/src/display/annotation_storage.js @@ -19,6 +19,14 @@ class AnnotationStorage { constructor() { this._storage = new Map(); + this._modified = false; + + // Callbacks to signal when the modification state is set or reset. + // This is used by the viewer to only bind on `beforeunload` if forms + // are actually edited to prevent doing so unconditionally since that + // can have undesirable efffects. + this.onSetModified = null; + this.onResetModified = null; } /** @@ -49,6 +57,9 @@ class AnnotationStorage { * @param {Object} value */ setValue(key, value) { + if (this._storage.get(key) !== value) { + this.setModified(); + } this._storage.set(key, value); } @@ -62,6 +73,24 @@ class AnnotationStorage { get size() { return this._storage.size; } + + setModified() { + if (!this._modified) { + this._modified = true; + if (typeof this.onSetModified === "function") { + this.onSetModified(); + } + } + } + + resetModified() { + if (this._modified) { + this._modified = false; + if (typeof this.onResetModified === "function") { + this.onResetModified(); + } + } + } } export { AnnotationStorage }; diff --git a/src/display/api.js b/src/display/api.js index ff17ffec8afb4..2c4d4c6d261a4 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -2536,12 +2536,16 @@ class WorkerTransport { } saveDocument(annotationStorage) { - return this.messageHandler.sendWithPromise("SaveDocument", { - numPages: this._numPages, - annotationStorage: - (annotationStorage && annotationStorage.getAll()) || null, - filename: this._fullReader.filename, - }); + return this.messageHandler + .sendWithPromise("SaveDocument", { + numPages: this._numPages, + annotationStorage: + (annotationStorage && annotationStorage.getAll()) || null, + filename: this._fullReader.filename, + }) + .finally(() => { + annotationStorage.resetModified(); + }); } getDestinations() { diff --git a/test/unit/annotation_storage_spec.js b/test/unit/annotation_storage_spec.js index 15585548a7f8b..1f55da3f999d4 100644 --- a/test/unit/annotation_storage_spec.js +++ b/test/unit/annotation_storage_spec.js @@ -38,5 +38,48 @@ describe("AnnotationStorage", function () { expect(value).toEqual("an other string"); done(); }); + + it("should call onSetModified() if value is changed", function (done) { + const annotationStorage = new AnnotationStorage(); + let called = false; + const callback = function () { + called = true; + }; + annotationStorage.onSetModified = callback; + annotationStorage.getOrCreateValue("asdf", "original"); + expect(called).toBe(false); + + // not changing value + annotationStorage.setValue("asdf", "original"); + expect(called).toBe(false); + + // changing value + annotationStorage.setValue("asdf", "modified"); + expect(called).toBe(true); + done(); + }); + }); + + describe("ResetModified", function () { + it("should call onResetModified() if set", function (done) { + const annotationStorage = new AnnotationStorage(); + let called = false; + const callback = function () { + called = true; + }; + annotationStorage.onResetModified = callback; + annotationStorage.getOrCreateValue("asdf", "original"); + + // not changing value + annotationStorage.setValue("asdf", "original"); + annotationStorage.resetModified(); + expect(called).toBe(false); + + // changing value + annotationStorage.setValue("asdf", "modified"); + annotationStorage.resetModified(); + expect(called).toBe(true); + done(); + }); }); }); diff --git a/web/base_viewer.js b/web/base_viewer.js index e4dce389ce784..385a2ffe0d230 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -127,6 +127,12 @@ function isSameScale(oldScale, newScale) { return false; } +function beforeUnload(evt) { + evt.preventDefault(); + evt.returnValue = ""; + return false; +} + /** * Simple viewer control to display PDF content/pages. * @implements {IRenderableView} @@ -436,6 +442,12 @@ class BaseViewer { const firstPagePromise = pdfDocument.getPage(1); const annotationStorage = pdfDocument.annotationStorage; + annotationStorage.onSetModified = function () { + window.addEventListener("beforeunload", beforeUnload); + }; + annotationStorage.onResetModified = function () { + window.removeEventListener("beforeunload", beforeUnload); + }; this._pagesCapability.promise.then(() => { this.eventBus.dispatch("pagesloaded", {