UNPKG

10.5 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.ShouldSaveDialog = exports.setDirty = exports.SaveableWidget = exports.close = exports.Saveable = exports.DelegatingSaveable = void 0;
19const event_1 = require("../common/event");
20const keys_1 = require("./keyboard/keys");
21const dialogs_1 = require("./dialogs");
22const widgets_1 = require("./widgets");
23const nls_1 = require("../common/nls");
24const common_1 = require("../common");
25class DelegatingSaveable {
26 constructor() {
27 this.dirty = false;
28 this.onDirtyChangedEmitter = new event_1.Emitter();
29 this.autoSave = 'off';
30 }
31 get onDirtyChanged() {
32 return this.onDirtyChangedEmitter.event;
33 }
34 async save(options) {
35 var _a;
36 await ((_a = this._delegate) === null || _a === void 0 ? void 0 : _a.save(options));
37 }
38 set delegate(delegate) {
39 var _a, _b, _c, _d;
40 (_a = this.toDispose) === null || _a === void 0 ? void 0 : _a.dispose();
41 this._delegate = delegate;
42 this.toDispose = delegate.onDirtyChanged(() => {
43 this.dirty = delegate.dirty;
44 this.onDirtyChangedEmitter.fire();
45 });
46 this.autoSave = delegate.autoSave;
47 if (this.dirty !== delegate.dirty) {
48 this.dirty = delegate.dirty;
49 this.onDirtyChangedEmitter.fire();
50 }
51 this.revert = (_b = delegate.revert) === null || _b === void 0 ? void 0 : _b.bind(delegate);
52 this.createSnapshot = (_c = delegate.createSnapshot) === null || _c === void 0 ? void 0 : _c.bind(delegate);
53 this.applySnapshot = (_d = delegate.applySnapshot) === null || _d === void 0 ? void 0 : _d.bind(delegate);
54 }
55}
56exports.DelegatingSaveable = DelegatingSaveable;
57var Saveable;
58(function (Saveable) {
59 function isSource(arg) {
60 return (0, common_1.isObject)(arg) && is(arg.saveable);
61 }
62 Saveable.isSource = isSource;
63 function is(arg) {
64 return (0, common_1.isObject)(arg) && 'dirty' in arg && 'onDirtyChanged' in arg;
65 }
66 Saveable.is = is;
67 function get(arg) {
68 if (is(arg)) {
69 return arg;
70 }
71 if (isSource(arg)) {
72 return arg.saveable;
73 }
74 return undefined;
75 }
76 Saveable.get = get;
77 function getDirty(arg) {
78 const saveable = get(arg);
79 if (saveable && saveable.dirty) {
80 return saveable;
81 }
82 return undefined;
83 }
84 Saveable.getDirty = getDirty;
85 function isDirty(arg) {
86 return !!getDirty(arg);
87 }
88 Saveable.isDirty = isDirty;
89 async function save(arg, options) {
90 const saveable = get(arg);
91 if (saveable) {
92 await saveable.save(options);
93 }
94 }
95 Saveable.save = save;
96 async function closeWithoutSaving(doRevert = true) {
97 const saveable = get(this);
98 if (saveable && doRevert && saveable.dirty && saveable.revert) {
99 await saveable.revert();
100 }
101 this[exports.close]();
102 return (0, widgets_1.waitForClosed)(this);
103 }
104 function createCloseWithSaving(getOtherSaveables, doSave) {
105 let closing = false;
106 return async function (options) {
107 var _a;
108 if (closing) {
109 return;
110 }
111 const saveable = get(this);
112 if (!saveable) {
113 return;
114 }
115 closing = true;
116 try {
117 const result = await shouldSave(saveable, () => {
118 var _a;
119 const notLastWithDocument = !closingWidgetWouldLoseSaveable(this, (_a = getOtherSaveables === null || getOtherSaveables === void 0 ? void 0 : getOtherSaveables()) !== null && _a !== void 0 ? _a : []);
120 if (notLastWithDocument) {
121 return this.closeWithoutSaving(false).then(() => undefined);
122 }
123 if (options && options.shouldSave) {
124 return options.shouldSave();
125 }
126 return new ShouldSaveDialog(this).open();
127 });
128 if (typeof result === 'boolean') {
129 if (result) {
130 await ((_a = doSave === null || doSave === void 0 ? void 0 : doSave(this)) !== null && _a !== void 0 ? _a : Saveable.save(this));
131 if (!isDirty(this)) {
132 await this.closeWithoutSaving();
133 }
134 }
135 else {
136 await this.closeWithoutSaving();
137 }
138 }
139 }
140 finally {
141 closing = false;
142 }
143 };
144 }
145 async function confirmSaveBeforeClose(toClose, others) {
146 var _a;
147 for (const widget of toClose) {
148 const saveable = Saveable.get(widget);
149 if (saveable === null || saveable === void 0 ? void 0 : saveable.dirty) {
150 if (!closingWidgetWouldLoseSaveable(widget, others)) {
151 continue;
152 }
153 const userWantsToSave = await new ShouldSaveDialog(widget).open();
154 if (userWantsToSave === undefined) { // User clicked cancel.
155 return undefined;
156 }
157 else if (userWantsToSave) {
158 await saveable.save();
159 }
160 else {
161 await ((_a = saveable.revert) === null || _a === void 0 ? void 0 : _a.call(saveable));
162 }
163 }
164 }
165 return true;
166 }
167 Saveable.confirmSaveBeforeClose = confirmSaveBeforeClose;
168 /**
169 * @param widget the widget that may be closed
170 * @param others widgets that will not be closed.
171 * @returns `true` if widget is saveable and no widget among the `others` refers to the same saveable. `false` otherwise.
172 */
173 function closingWidgetWouldLoseSaveable(widget, others) {
174 const saveable = get(widget);
175 return !!saveable && !others.some(otherWidget => otherWidget !== widget && get(otherWidget) === saveable);
176 }
177 function apply(widget, getOtherSaveables, doSave) {
178 if (SaveableWidget.is(widget)) {
179 return widget;
180 }
181 const saveable = Saveable.get(widget);
182 if (!saveable) {
183 return undefined;
184 }
185 const saveableWidget = widget;
186 setDirty(saveableWidget, saveable.dirty);
187 saveable.onDirtyChanged(() => setDirty(saveableWidget, saveable.dirty));
188 const closeWithSaving = createCloseWithSaving(getOtherSaveables, doSave);
189 return Object.assign(saveableWidget, {
190 closeWithoutSaving,
191 closeWithSaving,
192 close: closeWithSaving,
193 [exports.close]: saveableWidget.close,
194 });
195 }
196 Saveable.apply = apply;
197 async function shouldSave(saveable, cb) {
198 if (!saveable.dirty) {
199 return false;
200 }
201 if (saveable.autoSave !== 'off') {
202 return true;
203 }
204 return cb();
205 }
206 Saveable.shouldSave = shouldSave;
207})(Saveable = exports.Saveable || (exports.Saveable = {}));
208exports.close = Symbol('close');
209var SaveableWidget;
210(function (SaveableWidget) {
211 function is(widget) {
212 return !!widget && 'closeWithoutSaving' in widget;
213 }
214 SaveableWidget.is = is;
215 function getDirty(widgets) {
216 return get(widgets, Saveable.isDirty);
217 }
218 SaveableWidget.getDirty = getDirty;
219 function* get(widgets, filter = () => true) {
220 for (const widget of widgets) {
221 if (SaveableWidget.is(widget) && filter(widget)) {
222 yield widget;
223 }
224 }
225 }
226 SaveableWidget.get = get;
227})(SaveableWidget = exports.SaveableWidget || (exports.SaveableWidget = {}));
228;
229/**
230 * The class name added to the dirty widget's title.
231 */
232const DIRTY_CLASS = 'theia-mod-dirty';
233function setDirty(widget, dirty) {
234 const dirtyClass = ` ${DIRTY_CLASS}`;
235 widget.title.className = widget.title.className.replace(dirtyClass, '');
236 if (dirty) {
237 widget.title.className += dirtyClass;
238 }
239}
240exports.setDirty = setDirty;
241class ShouldSaveDialog extends dialogs_1.AbstractDialog {
242 constructor(widget) {
243 super({
244 title: nls_1.nls.localizeByDefault('Do you want to save the changes you made to {0}?', widget.title.label || widget.title.caption)
245 }, {
246 node: widget.node.ownerDocument.createElement('div')
247 });
248 this.shouldSave = true;
249 const messageNode = this.node.ownerDocument.createElement('div');
250 messageNode.textContent = nls_1.nls.localizeByDefault("Your changes will be lost if you don't save them.");
251 messageNode.setAttribute('style', 'flex: 1 100%; padding-bottom: calc(var(--theia-ui-padding)*3);');
252 this.contentNode.appendChild(messageNode);
253 this.appendCloseButton();
254 this.dontSaveButton = this.appendDontSaveButton();
255 this.appendAcceptButton(nls_1.nls.localizeByDefault('Save'));
256 }
257 appendDontSaveButton() {
258 const button = this.createButton(nls_1.nls.localizeByDefault("Don't Save"));
259 this.controlPanel.appendChild(button);
260 button.classList.add('secondary');
261 return button;
262 }
263 onAfterAttach(msg) {
264 super.onAfterAttach(msg);
265 this.addKeyListener(this.dontSaveButton, keys_1.Key.ENTER, () => {
266 this.shouldSave = false;
267 this.accept();
268 }, 'click');
269 }
270 get value() {
271 return this.shouldSave;
272 }
273}
274exports.ShouldSaveDialog = ShouldSaveDialog;
275//# sourceMappingURL=saveable.js.map
\No newline at end of file