1 |
|
2 |
|
3 | import { Dialog, SessionContext, SessionContextDialogs, showDialog, showErrorMessage } from '@jupyterlab/apputils';
|
4 | import { PathExt } from '@jupyterlab/coreutils';
|
5 | import { RenderMimeRegistry } from '@jupyterlab/rendermime';
|
6 | import { nullTranslator } from '@jupyterlab/translation';
|
7 | import { PromiseDelegate } from '@lumino/coreutils';
|
8 | import { DisposableDelegate } from '@lumino/disposable';
|
9 | import { Signal } from '@lumino/signaling';
|
10 | import { Widget } from '@lumino/widgets';
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export class Context {
|
17 | |
18 |
|
19 |
|
20 | constructor(options) {
|
21 | var _a, _b;
|
22 | this._isReady = false;
|
23 | this._isDisposed = false;
|
24 | this._isPopulated = false;
|
25 | this._path = '';
|
26 | this._lineEnding = null;
|
27 | this._contentsModel = null;
|
28 | this._populatedPromise = new PromiseDelegate();
|
29 | this._pathChanged = new Signal(this);
|
30 | this._fileChanged = new Signal(this);
|
31 | this._saveState = new Signal(this);
|
32 | this._disposed = new Signal(this);
|
33 | this._lastModifiedCheckMargin = 500;
|
34 | this._conflictModalIsOpen = false;
|
35 | const manager = (this._manager = options.manager);
|
36 | this.translator = options.translator || nullTranslator;
|
37 | this._trans = this.translator.load('jupyterlab');
|
38 | this._factory = options.factory;
|
39 | this._dialogs =
|
40 | (_a = options.sessionDialogs) !== null && _a !== void 0 ? _a : new SessionContextDialogs({ translator: options.translator });
|
41 | this._opener = options.opener || Private.noOp;
|
42 | this._path = this._manager.contents.normalize(options.path);
|
43 | this._lastModifiedCheckMargin = options.lastModifiedCheckMargin || 500;
|
44 | const localPath = this._manager.contents.localPath(this._path);
|
45 | const lang = this._factory.preferredLanguage(PathExt.basename(localPath));
|
46 | const sharedFactory = this._manager.contents.getSharedModelFactory(this._path);
|
47 | const sharedModel = sharedFactory === null || sharedFactory === void 0 ? void 0 : sharedFactory.createNew({
|
48 | path: localPath,
|
49 | format: this._factory.fileFormat,
|
50 | contentType: this._factory.contentType,
|
51 | collaborative: this._factory.collaborative
|
52 | });
|
53 | this._model = this._factory.createNew({
|
54 | languagePreference: lang,
|
55 | sharedModel,
|
56 | collaborationEnabled: (_b = sharedFactory === null || sharedFactory === void 0 ? void 0 : sharedFactory.collaborative) !== null && _b !== void 0 ? _b : false
|
57 | });
|
58 | this._readyPromise = manager.ready.then(() => {
|
59 | return this._populatedPromise.promise;
|
60 | });
|
61 | const ext = PathExt.extname(this._path);
|
62 | this.sessionContext = new SessionContext({
|
63 | sessionManager: manager.sessions,
|
64 | specsManager: manager.kernelspecs,
|
65 | path: localPath,
|
66 | type: ext === '.ipynb' ? 'notebook' : 'file',
|
67 | name: PathExt.basename(localPath),
|
68 | kernelPreference: options.kernelPreference || { shouldStart: false },
|
69 | setBusy: options.setBusy
|
70 | });
|
71 | this.sessionContext.propertyChanged.connect(this._onSessionChanged, this);
|
72 | manager.contents.fileChanged.connect(this._onFileChanged, this);
|
73 | this.urlResolver = new RenderMimeRegistry.UrlResolver({
|
74 | path: this._path,
|
75 | contents: manager.contents
|
76 | });
|
77 | }
|
78 | |
79 |
|
80 |
|
81 | get pathChanged() {
|
82 | return this._pathChanged;
|
83 | }
|
84 | |
85 |
|
86 |
|
87 | get fileChanged() {
|
88 | return this._fileChanged;
|
89 | }
|
90 | |
91 |
|
92 |
|
93 | get saveState() {
|
94 | return this._saveState;
|
95 | }
|
96 | |
97 |
|
98 |
|
99 | get disposed() {
|
100 | return this._disposed;
|
101 | }
|
102 | |
103 |
|
104 |
|
105 | get lastModifiedCheckMargin() {
|
106 | return this._lastModifiedCheckMargin;
|
107 | }
|
108 | set lastModifiedCheckMargin(value) {
|
109 | this._lastModifiedCheckMargin = value;
|
110 | }
|
111 | |
112 |
|
113 |
|
114 | get model() {
|
115 | return this._model;
|
116 | }
|
117 | |
118 |
|
119 |
|
120 | get path() {
|
121 | return this._path;
|
122 | }
|
123 | |
124 |
|
125 |
|
126 |
|
127 |
|
128 | get localPath() {
|
129 | return this._manager.contents.localPath(this._path);
|
130 | }
|
131 | |
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | get contentsModel() {
|
139 | return this._contentsModel ? { ...this._contentsModel } : null;
|
140 | }
|
141 | |
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | get factoryName() {
|
148 | return this.isDisposed ? '' : this._factory.name;
|
149 | }
|
150 | |
151 |
|
152 |
|
153 | get isDisposed() {
|
154 | return this._isDisposed;
|
155 | }
|
156 | |
157 |
|
158 |
|
159 | dispose() {
|
160 | if (this.isDisposed) {
|
161 | return;
|
162 | }
|
163 | this._isDisposed = true;
|
164 | this.sessionContext.dispose();
|
165 | this._model.dispose();
|
166 |
|
167 |
|
168 | this._model.sharedModel.dispose();
|
169 | this._disposed.emit(void 0);
|
170 | Signal.clearData(this);
|
171 | }
|
172 | |
173 |
|
174 |
|
175 | get isReady() {
|
176 | return this._isReady;
|
177 | }
|
178 | |
179 |
|
180 |
|
181 | get ready() {
|
182 | return this._readyPromise;
|
183 | }
|
184 | |
185 |
|
186 |
|
187 | get canSave() {
|
188 | var _a;
|
189 | return !!(((_a = this._contentsModel) === null || _a === void 0 ? void 0 : _a.writable) && !this._model.collaborative);
|
190 | }
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 | async initialize(isNew) {
|
199 | if (isNew) {
|
200 | await this._save();
|
201 | }
|
202 | else {
|
203 | await this._revert();
|
204 | }
|
205 | this.model.sharedModel.clearUndoHistory();
|
206 | }
|
207 | |
208 |
|
209 |
|
210 |
|
211 |
|
212 | rename(newName) {
|
213 | return this.ready.then(() => {
|
214 | return this._manager.ready.then(() => {
|
215 | return this._rename(newName);
|
216 | });
|
217 | });
|
218 | }
|
219 | |
220 |
|
221 |
|
222 | async save() {
|
223 | await this.ready;
|
224 | await this._save();
|
225 | }
|
226 | |
227 |
|
228 |
|
229 |
|
230 |
|
231 | async saveAs() {
|
232 | await this.ready;
|
233 | const localPath = this._manager.contents.localPath(this.path);
|
234 | const newLocalPath = await Private.getSavePath(localPath);
|
235 | if (this.isDisposed || !newLocalPath) {
|
236 | return;
|
237 | }
|
238 | const drive = this._manager.contents.driveName(this.path);
|
239 | const newPath = drive == '' ? newLocalPath : `${drive}:${newLocalPath}`;
|
240 | if (newPath === this._path) {
|
241 | return this.save();
|
242 | }
|
243 |
|
244 | try {
|
245 | await this._manager.ready;
|
246 | await this._manager.contents.get(newPath);
|
247 | await this._maybeOverWrite(newPath);
|
248 | }
|
249 | catch (err) {
|
250 | if (!err.response || err.response.status !== 404) {
|
251 | throw err;
|
252 | }
|
253 | await this._finishSaveAs(newPath);
|
254 | }
|
255 | }
|
256 | |
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | async download() {
|
263 | const url = await this._manager.contents.getDownloadUrl(this._path);
|
264 | const element = document.createElement('a');
|
265 | element.href = url;
|
266 | element.download = '';
|
267 | document.body.appendChild(element);
|
268 | element.click();
|
269 | document.body.removeChild(element);
|
270 | return void 0;
|
271 | }
|
272 | |
273 |
|
274 |
|
275 | async revert() {
|
276 | await this.ready;
|
277 | await this._revert();
|
278 | }
|
279 | |
280 |
|
281 |
|
282 | createCheckpoint() {
|
283 | const contents = this._manager.contents;
|
284 | return this._manager.ready.then(() => {
|
285 | return contents.createCheckpoint(this._path);
|
286 | });
|
287 | }
|
288 | |
289 |
|
290 |
|
291 | deleteCheckpoint(checkpointId) {
|
292 | const contents = this._manager.contents;
|
293 | return this._manager.ready.then(() => {
|
294 | return contents.deleteCheckpoint(this._path, checkpointId);
|
295 | });
|
296 | }
|
297 | |
298 |
|
299 |
|
300 | restoreCheckpoint(checkpointId) {
|
301 | const contents = this._manager.contents;
|
302 | const path = this._path;
|
303 | return this._manager.ready.then(() => {
|
304 | if (checkpointId) {
|
305 | return contents.restoreCheckpoint(path, checkpointId);
|
306 | }
|
307 | return this.listCheckpoints().then(checkpoints => {
|
308 | if (this.isDisposed || !checkpoints.length) {
|
309 | return;
|
310 | }
|
311 | checkpointId = checkpoints[checkpoints.length - 1].id;
|
312 | return contents.restoreCheckpoint(path, checkpointId);
|
313 | });
|
314 | });
|
315 | }
|
316 | |
317 |
|
318 |
|
319 | listCheckpoints() {
|
320 | const contents = this._manager.contents;
|
321 | return this._manager.ready.then(() => {
|
322 | return contents.listCheckpoints(this._path);
|
323 | });
|
324 | }
|
325 | |
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 | addSibling(widget, options = {}) {
|
339 | const opener = this._opener;
|
340 | if (opener) {
|
341 | opener(widget, options);
|
342 | }
|
343 | return new DisposableDelegate(() => {
|
344 | widget.close();
|
345 | });
|
346 | }
|
347 | |
348 |
|
349 |
|
350 | _onFileChanged(sender, change) {
|
351 | var _a;
|
352 | if (change.type !== 'rename') {
|
353 | return;
|
354 | }
|
355 | let oldPath = change.oldValue && change.oldValue.path;
|
356 | let newPath = change.newValue && change.newValue.path;
|
357 | if (newPath && this._path.indexOf(oldPath || '') === 0) {
|
358 | let changeModel = change.newValue;
|
359 |
|
360 |
|
361 | if (oldPath !== this._path) {
|
362 | newPath = this._path.replace(new RegExp(`^${oldPath}/`), `${newPath}/`);
|
363 | oldPath = this._path;
|
364 |
|
365 | changeModel = {
|
366 | last_modified: (_a = change.newValue) === null || _a === void 0 ? void 0 : _a.created,
|
367 | path: newPath
|
368 | };
|
369 | }
|
370 | this._updateContentsModel({
|
371 | ...this._contentsModel,
|
372 | ...changeModel
|
373 | });
|
374 | this._updatePath(newPath);
|
375 | }
|
376 | }
|
377 | |
378 |
|
379 |
|
380 | _onSessionChanged(sender, type) {
|
381 | if (type !== 'path') {
|
382 | return;
|
383 | }
|
384 |
|
385 |
|
386 | const driveName = this._manager.contents.driveName(this.path);
|
387 | let newPath = this.sessionContext.session.path;
|
388 | if (driveName) {
|
389 | newPath = `${driveName}:${newPath}`;
|
390 | }
|
391 | this._updatePath(newPath);
|
392 | }
|
393 | |
394 |
|
395 |
|
396 | _updateContentsModel(model) {
|
397 | var _a, _b, _c, _d;
|
398 | const writable = model.writable && !this._model.collaborative;
|
399 | const newModel = {
|
400 | path: model.path,
|
401 | name: model.name,
|
402 | type: model.type,
|
403 | writable,
|
404 | created: model.created,
|
405 | last_modified: model.last_modified,
|
406 | mimetype: model.mimetype,
|
407 | format: model.format,
|
408 | hash: model.hash,
|
409 | hash_algorithm: model.hash_algorithm
|
410 | };
|
411 | const mod = (_b = (_a = this._contentsModel) === null || _a === void 0 ? void 0 : _a.last_modified) !== null && _b !== void 0 ? _b : null;
|
412 | const hash = (_d = (_c = this._contentsModel) === null || _c === void 0 ? void 0 : _c.hash) !== null && _d !== void 0 ? _d : null;
|
413 | this._contentsModel = newModel;
|
414 | if (
|
415 |
|
416 | (!mod && !hash) ||
|
417 |
|
418 | (!hash && newModel.last_modified !== mod) ||
|
419 |
|
420 | (hash && newModel.hash !== hash)) {
|
421 | this._fileChanged.emit(newModel);
|
422 | }
|
423 | }
|
424 | _updatePath(newPath) {
|
425 | var _a, _b, _c, _d;
|
426 | if (this._path === newPath) {
|
427 | return;
|
428 | }
|
429 | this._path = newPath;
|
430 | const localPath = this._manager.contents.localPath(newPath);
|
431 | const name = PathExt.basename(localPath);
|
432 | if (((_a = this.sessionContext.session) === null || _a === void 0 ? void 0 : _a.path) !== localPath) {
|
433 | void ((_b = this.sessionContext.session) === null || _b === void 0 ? void 0 : _b.setPath(localPath));
|
434 | }
|
435 | if (((_c = this.sessionContext.session) === null || _c === void 0 ? void 0 : _c.name) !== name) {
|
436 | void ((_d = this.sessionContext.session) === null || _d === void 0 ? void 0 : _d.setName(name));
|
437 | }
|
438 | if (this.urlResolver.path !== newPath) {
|
439 | this.urlResolver.path = newPath;
|
440 | }
|
441 | if (this._contentsModel &&
|
442 | (this._contentsModel.path !== newPath ||
|
443 | this._contentsModel.name !== name)) {
|
444 | const contentsModel = {
|
445 | ...this._contentsModel,
|
446 | name: name,
|
447 | path: newPath
|
448 | };
|
449 | this._updateContentsModel(contentsModel);
|
450 | }
|
451 | this._pathChanged.emit(newPath);
|
452 | }
|
453 | |
454 |
|
455 |
|
456 | async _populate() {
|
457 | this._isPopulated = true;
|
458 | this._isReady = true;
|
459 | this._populatedPromise.resolve(void 0);
|
460 |
|
461 | await this._maybeCheckpoint(false);
|
462 | if (this.isDisposed) {
|
463 | return;
|
464 | }
|
465 |
|
466 | const name = this._model.defaultKernelName ||
|
467 | this.sessionContext.kernelPreference.name;
|
468 | this.sessionContext.kernelPreference = {
|
469 | ...this.sessionContext.kernelPreference,
|
470 | name,
|
471 | language: this._model.defaultKernelLanguage
|
472 | };
|
473 |
|
474 |
|
475 |
|
476 | void this.sessionContext.initialize().then(shouldSelect => {
|
477 | if (shouldSelect) {
|
478 | void this._dialogs.selectKernel(this.sessionContext);
|
479 | }
|
480 | });
|
481 | }
|
482 | |
483 |
|
484 |
|
485 |
|
486 |
|
487 | async _rename(newName) {
|
488 | const splitPath = this.localPath.split('/');
|
489 | splitPath[splitPath.length - 1] = newName;
|
490 | let newPath = PathExt.join(...splitPath);
|
491 | const driveName = this._manager.contents.driveName(this.path);
|
492 | if (driveName) {
|
493 | newPath = `${driveName}:${newPath}`;
|
494 | }
|
495 |
|
496 | await this._manager.contents.rename(this.path, newPath);
|
497 | }
|
498 | |
499 |
|
500 |
|
501 | async _save() {
|
502 | this._saveState.emit('started');
|
503 | const options = this._createSaveOptions();
|
504 | try {
|
505 | await this._manager.ready;
|
506 | const value = await this._maybeSave(options);
|
507 | if (this.isDisposed) {
|
508 | return;
|
509 | }
|
510 | this._model.dirty = false;
|
511 | this._updateContentsModel(value);
|
512 | if (!this._isPopulated) {
|
513 | await this._populate();
|
514 | }
|
515 |
|
516 | this._saveState.emit('completed');
|
517 | }
|
518 | catch (err) {
|
519 |
|
520 |
|
521 | const { name } = err;
|
522 | if (name === 'ModalCancelError' || name === 'ModalDuplicateError') {
|
523 | throw err;
|
524 | }
|
525 |
|
526 | const localPath = this._manager.contents.localPath(this._path);
|
527 | const file = PathExt.basename(localPath);
|
528 | void this._handleError(err, this._trans.__('File Save Error for %1', file));
|
529 |
|
530 | this._saveState.emit('failed');
|
531 | throw err;
|
532 | }
|
533 | }
|
534 | |
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 | _revert(initializeModel = false) {
|
541 | const opts = {
|
542 | type: this._factory.contentType,
|
543 | content: this._factory.fileFormat !== null,
|
544 | hash: this._factory.fileFormat !== null,
|
545 | ...(this._factory.fileFormat !== null
|
546 | ? { format: this._factory.fileFormat }
|
547 | : {})
|
548 | };
|
549 | const path = this._path;
|
550 | const model = this._model;
|
551 | return this._manager.ready
|
552 | .then(() => {
|
553 | return this._manager.contents.get(path, opts);
|
554 | })
|
555 | .then(contents => {
|
556 | if (this.isDisposed) {
|
557 | return;
|
558 | }
|
559 | if (contents.content) {
|
560 | if (contents.format === 'json') {
|
561 | model.fromJSON(contents.content);
|
562 | }
|
563 | else {
|
564 | let content = contents.content;
|
565 |
|
566 |
|
567 | if (content.indexOf('\r\n') !== -1) {
|
568 | this._lineEnding = '\r\n';
|
569 | content = content.replace(/\r\n/g, '\n');
|
570 | }
|
571 | else if (content.indexOf('\r') !== -1) {
|
572 | this._lineEnding = '\r';
|
573 | content = content.replace(/\r/g, '\n');
|
574 | }
|
575 | else {
|
576 | this._lineEnding = null;
|
577 | }
|
578 | model.fromString(content);
|
579 | }
|
580 | }
|
581 | this._updateContentsModel(contents);
|
582 | model.dirty = false;
|
583 | if (!this._isPopulated) {
|
584 | return this._populate();
|
585 | }
|
586 | })
|
587 | .catch(async (err) => {
|
588 | const localPath = this._manager.contents.localPath(this._path);
|
589 | const name = PathExt.basename(localPath);
|
590 | void this._handleError(err, this._trans.__('File Load Error for %1', name));
|
591 | throw err;
|
592 | });
|
593 | }
|
594 | |
595 |
|
596 |
|
597 | _maybeSave(options) {
|
598 | const path = this._path;
|
599 |
|
600 | const promise = this._manager.contents.get(path, {
|
601 | content: false,
|
602 | hash: true
|
603 | });
|
604 | return promise.then(model => {
|
605 | var _a, _b, _c, _d;
|
606 | if (this.isDisposed) {
|
607 | return Promise.reject(new Error('Disposed'));
|
608 | }
|
609 |
|
610 | const hashAvailable = ((_a = this.contentsModel) === null || _a === void 0 ? void 0 : _a.hash) !== undefined &&
|
611 | ((_b = this.contentsModel) === null || _b === void 0 ? void 0 : _b.hash) !== null &&
|
612 | model.hash !== undefined &&
|
613 | model.hash !== null;
|
614 | const hClient = (_c = this.contentsModel) === null || _c === void 0 ? void 0 : _c.hash;
|
615 | const hDisk = model.hash;
|
616 | if (hashAvailable && hClient !== hDisk) {
|
617 | console.warn(`Different hash found for ${this.path}`);
|
618 | return this._raiseConflict(model, options);
|
619 | }
|
620 |
|
621 |
|
622 |
|
623 |
|
624 | const lastModifiedCheckMargin = this._lastModifiedCheckMargin;
|
625 | const modified = (_d = this.contentsModel) === null || _d === void 0 ? void 0 : _d.last_modified;
|
626 | const tClient = modified ? new Date(modified) : new Date();
|
627 | const tDisk = new Date(model.last_modified);
|
628 | if (!hashAvailable &&
|
629 | modified &&
|
630 | tDisk.getTime() - tClient.getTime() > lastModifiedCheckMargin) {
|
631 | console.warn(`Last saving performed ${tClient} ` +
|
632 | `while the current file seems to have been saved ` +
|
633 | `${tDisk}`);
|
634 | return this._raiseConflict(model, options);
|
635 | }
|
636 | return this._manager.contents
|
637 | .save(path, options)
|
638 | .then(async (contentsModel) => {
|
639 | const model = await this._manager.contents.get(path, {
|
640 | content: false,
|
641 | hash: true
|
642 | });
|
643 | return {
|
644 | ...contentsModel,
|
645 | hash: model.hash,
|
646 | hash_algorithm: model.hash_algorithm
|
647 | };
|
648 | });
|
649 | }, err => {
|
650 | if (err.response && err.response.status === 404) {
|
651 | return this._manager.contents
|
652 | .save(path, options)
|
653 | .then(async (contentsModel) => {
|
654 | const model = await this._manager.contents.get(path, {
|
655 | content: false,
|
656 | hash: true
|
657 | });
|
658 | return {
|
659 | ...contentsModel,
|
660 | hash: model.hash,
|
661 | hash_algorithm: model.hash_algorithm
|
662 | };
|
663 | });
|
664 | }
|
665 | throw err;
|
666 | });
|
667 | }
|
668 | |
669 |
|
670 |
|
671 | async _handleError(err, title) {
|
672 | await showErrorMessage(title, err);
|
673 | return;
|
674 | }
|
675 | |
676 |
|
677 |
|
678 | _maybeCheckpoint(force) {
|
679 | let promise = Promise.resolve(void 0);
|
680 | if (!this.canSave) {
|
681 | return promise;
|
682 | }
|
683 | if (force) {
|
684 | promise = this.createCheckpoint().then( );
|
685 | }
|
686 | else {
|
687 | promise = this.listCheckpoints().then(checkpoints => {
|
688 | if (!this.isDisposed && !checkpoints.length && this.canSave) {
|
689 | return this.createCheckpoint().then( );
|
690 | }
|
691 | });
|
692 | }
|
693 | return promise.catch(err => {
|
694 |
|
695 | if (!err.response || err.response.status !== 403) {
|
696 | throw err;
|
697 | }
|
698 | });
|
699 | }
|
700 | |
701 |
|
702 |
|
703 | _raiseConflict(model, options) {
|
704 | if (this._conflictModalIsOpen) {
|
705 | const error = new Error('Modal is already displayed');
|
706 | error.name = 'ModalDuplicateError';
|
707 | return Promise.reject(error);
|
708 | }
|
709 | const body = this._trans.__(`"%1" has changed on disk since the last time it was opened or saved.
|
710 | Do you want to overwrite the file on disk with the version open here,
|
711 | or load the version on disk (revert)?`, this.path);
|
712 | const revertBtn = Dialog.okButton({
|
713 | label: this._trans.__('Revert'),
|
714 | actions: ['revert']
|
715 | });
|
716 | const overwriteBtn = Dialog.warnButton({
|
717 | label: this._trans.__('Overwrite'),
|
718 | actions: ['overwrite']
|
719 | });
|
720 | this._conflictModalIsOpen = true;
|
721 | return showDialog({
|
722 | title: this._trans.__('File Changed'),
|
723 | body,
|
724 | buttons: [Dialog.cancelButton(), revertBtn, overwriteBtn]
|
725 | }).then(result => {
|
726 | this._conflictModalIsOpen = false;
|
727 | if (this.isDisposed) {
|
728 | return Promise.reject(new Error('Disposed'));
|
729 | }
|
730 | if (result.button.actions.includes('overwrite')) {
|
731 | return this._manager.contents.save(this._path, options);
|
732 | }
|
733 | if (result.button.actions.includes('revert')) {
|
734 | return this.revert().then(() => {
|
735 | return model;
|
736 | });
|
737 | }
|
738 | const error = new Error('Cancel');
|
739 | error.name = 'ModalCancelError';
|
740 | return Promise.reject(error);
|
741 | });
|
742 | }
|
743 | |
744 |
|
745 |
|
746 | _maybeOverWrite(path) {
|
747 | const body = this._trans.__('"%1" already exists. Do you want to replace it?', path);
|
748 | const overwriteBtn = Dialog.warnButton({
|
749 | label: this._trans.__('Overwrite'),
|
750 | accept: true
|
751 | });
|
752 | return showDialog({
|
753 | title: this._trans.__('File Overwrite?'),
|
754 | body,
|
755 | buttons: [Dialog.cancelButton(), overwriteBtn]
|
756 | }).then(result => {
|
757 | if (this.isDisposed) {
|
758 | return Promise.reject(new Error('Disposed'));
|
759 | }
|
760 | if (result.button.accept) {
|
761 | return this._manager.contents.delete(path).then(() => {
|
762 | return this._finishSaveAs(path);
|
763 | });
|
764 | }
|
765 | });
|
766 | }
|
767 | |
768 |
|
769 |
|
770 | async _finishSaveAs(newPath) {
|
771 | this._saveState.emit('started');
|
772 | try {
|
773 | await this._manager.ready;
|
774 | const options = this._createSaveOptions();
|
775 | await this._manager.contents.save(newPath, options);
|
776 | await this._maybeCheckpoint(true);
|
777 |
|
778 | this._saveState.emit('completed');
|
779 | }
|
780 | catch (err) {
|
781 |
|
782 |
|
783 |
|
784 | if (err.message === 'Cancel' ||
|
785 | err.message === 'Modal is already displayed') {
|
786 | throw err;
|
787 | }
|
788 |
|
789 | const localPath = this._manager.contents.localPath(this._path);
|
790 | const name = PathExt.basename(localPath);
|
791 | void this._handleError(err, this._trans.__('File Save Error for %1', name));
|
792 |
|
793 | this._saveState.emit('failed');
|
794 | return;
|
795 | }
|
796 | }
|
797 | _createSaveOptions() {
|
798 | let content = null;
|
799 | if (this._factory.fileFormat === 'json') {
|
800 | content = this._model.toJSON();
|
801 | }
|
802 | else {
|
803 | content = this._model.toString();
|
804 | if (this._lineEnding) {
|
805 | content = content.replace(/\n/g, this._lineEnding);
|
806 | }
|
807 | }
|
808 | return {
|
809 | type: this._factory.contentType,
|
810 | format: this._factory.fileFormat,
|
811 | content
|
812 | };
|
813 | }
|
814 | }
|
815 |
|
816 |
|
817 |
|
818 | var Private;
|
819 | (function (Private) {
|
820 | |
821 |
|
822 |
|
823 | function getSavePath(path, translator) {
|
824 | translator = translator || nullTranslator;
|
825 | const trans = translator.load('jupyterlab');
|
826 | const saveBtn = Dialog.okButton({ label: trans.__('Save'), accept: true });
|
827 | return showDialog({
|
828 | title: trans.__('Save File As…'),
|
829 | body: new SaveWidget(path),
|
830 | buttons: [Dialog.cancelButton(), saveBtn]
|
831 | }).then(result => {
|
832 | var _a;
|
833 | if (result.button.accept) {
|
834 | return (_a = result.value) !== null && _a !== void 0 ? _a : undefined;
|
835 | }
|
836 | return;
|
837 | });
|
838 | }
|
839 | Private.getSavePath = getSavePath;
|
840 | |
841 |
|
842 |
|
843 | function noOp() {
|
844 |
|
845 | }
|
846 | Private.noOp = noOp;
|
847 | |
848 |
|
849 |
|
850 | class SaveWidget extends Widget {
|
851 | |
852 |
|
853 |
|
854 | constructor(path) {
|
855 | super({ node: createSaveNode(path) });
|
856 | }
|
857 | |
858 |
|
859 |
|
860 | getValue() {
|
861 | return this.node.value;
|
862 | }
|
863 | }
|
864 | |
865 |
|
866 |
|
867 | function createSaveNode(path) {
|
868 | const input = document.createElement('input');
|
869 | input.value = path;
|
870 | return input;
|
871 | }
|
872 | })(Private || (Private = {}));
|
873 |
|
\ | No newline at end of file |