1 |
|
2 |
|
3 | import { MainAreaWidget, setToolbar } from '@jupyterlab/apputils';
|
4 | import { CodeEditor } from '@jupyterlab/codeeditor';
|
5 | import { PathExt } from '@jupyterlab/coreutils';
|
6 | import { nullTranslator } from '@jupyterlab/translation';
|
7 | import { Signal } from '@lumino/signaling';
|
8 | import { createReadonlyLabel } from './components';
|
9 |
|
10 |
|
11 |
|
12 | export class DocumentModel extends CodeEditor.Model {
|
13 | |
14 |
|
15 |
|
16 | constructor(options = {}) {
|
17 | var _a;
|
18 | super({ sharedModel: options.sharedModel });
|
19 | this._defaultLang = '';
|
20 | this._dirty = false;
|
21 | this._readOnly = false;
|
22 | this._contentChanged = new Signal(this);
|
23 | this._stateChanged = new Signal(this);
|
24 | this._defaultLang = (_a = options.languagePreference) !== null && _a !== void 0 ? _a : '';
|
25 | this._collaborationEnabled = !!options.collaborationEnabled;
|
26 | this.sharedModel.changed.connect(this._onStateChanged, this);
|
27 | }
|
28 | |
29 |
|
30 |
|
31 | get contentChanged() {
|
32 | return this._contentChanged;
|
33 | }
|
34 | |
35 |
|
36 |
|
37 | get stateChanged() {
|
38 | return this._stateChanged;
|
39 | }
|
40 | |
41 |
|
42 |
|
43 | get dirty() {
|
44 | return this._dirty;
|
45 | }
|
46 | set dirty(newValue) {
|
47 | const oldValue = this._dirty;
|
48 | if (newValue === oldValue) {
|
49 | return;
|
50 | }
|
51 | this._dirty = newValue;
|
52 | this.triggerStateChange({
|
53 | name: 'dirty',
|
54 | oldValue,
|
55 | newValue
|
56 | });
|
57 | }
|
58 | |
59 |
|
60 |
|
61 | get readOnly() {
|
62 | return this._readOnly;
|
63 | }
|
64 | set readOnly(newValue) {
|
65 | if (newValue === this._readOnly) {
|
66 | return;
|
67 | }
|
68 | const oldValue = this._readOnly;
|
69 | this._readOnly = newValue;
|
70 | this.triggerStateChange({ name: 'readOnly', oldValue, newValue });
|
71 | }
|
72 | |
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | get defaultKernelName() {
|
79 | return '';
|
80 | }
|
81 | |
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | get defaultKernelLanguage() {
|
88 | return this._defaultLang;
|
89 | }
|
90 | |
91 |
|
92 |
|
93 | get collaborative() {
|
94 | return this._collaborationEnabled;
|
95 | }
|
96 | |
97 |
|
98 |
|
99 | toString() {
|
100 | return this.sharedModel.getSource();
|
101 | }
|
102 | |
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | fromString(value) {
|
109 | this.sharedModel.setSource(value);
|
110 | }
|
111 | |
112 |
|
113 |
|
114 | toJSON() {
|
115 | return JSON.parse(this.sharedModel.getSource() || 'null');
|
116 | }
|
117 | |
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | fromJSON(value) {
|
124 | this.fromString(JSON.stringify(value));
|
125 | }
|
126 | |
127 |
|
128 |
|
129 | initialize() {
|
130 | return;
|
131 | }
|
132 | |
133 |
|
134 |
|
135 | triggerStateChange(args) {
|
136 | this._stateChanged.emit(args);
|
137 | }
|
138 | |
139 |
|
140 |
|
141 | triggerContentChange() {
|
142 | this._contentChanged.emit(void 0);
|
143 | this.dirty = true;
|
144 | }
|
145 | _onStateChanged(sender, changes) {
|
146 | if (changes.sourceChange) {
|
147 | this.triggerContentChange();
|
148 | }
|
149 | if (changes.stateChange) {
|
150 | changes.stateChange.forEach(value => {
|
151 | if (value.name === 'dirty') {
|
152 |
|
153 |
|
154 |
|
155 | this.dirty = value.newValue;
|
156 | }
|
157 | else if (value.oldValue !== value.newValue) {
|
158 | this.triggerStateChange({
|
159 | newValue: undefined,
|
160 | oldValue: undefined,
|
161 | ...value
|
162 | });
|
163 | }
|
164 | });
|
165 | }
|
166 | }
|
167 | }
|
168 |
|
169 |
|
170 |
|
171 | export class TextModelFactory {
|
172 | |
173 |
|
174 |
|
175 | constructor(collaborative) {
|
176 | this._isDisposed = false;
|
177 | this._collaborative = collaborative !== null && collaborative !== void 0 ? collaborative : true;
|
178 | }
|
179 | |
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 | get name() {
|
186 | return 'text';
|
187 | }
|
188 | |
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | get contentType() {
|
195 | return 'file';
|
196 | }
|
197 | |
198 |
|
199 |
|
200 |
|
201 |
|
202 | get fileFormat() {
|
203 | return 'text';
|
204 | }
|
205 | |
206 |
|
207 |
|
208 | get collaborative() {
|
209 | return this._collaborative;
|
210 | }
|
211 | |
212 |
|
213 |
|
214 | get isDisposed() {
|
215 | return this._isDisposed;
|
216 | }
|
217 | |
218 |
|
219 |
|
220 | dispose() {
|
221 | this._isDisposed = true;
|
222 | }
|
223 | |
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 | createNew(options = {}) {
|
231 | const collaborative = options.collaborationEnabled && this.collaborative;
|
232 | return new DocumentModel({
|
233 | ...options,
|
234 | collaborationEnabled: collaborative
|
235 | });
|
236 | }
|
237 | |
238 |
|
239 |
|
240 | preferredLanguage(path) {
|
241 | return '';
|
242 | }
|
243 | }
|
244 |
|
245 |
|
246 |
|
247 | export class Base64ModelFactory extends TextModelFactory {
|
248 | |
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | get name() {
|
255 | return 'base64';
|
256 | }
|
257 | |
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | get contentType() {
|
264 | return 'file';
|
265 | }
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 | get fileFormat() {
|
272 | return 'base64';
|
273 | }
|
274 | }
|
275 |
|
276 |
|
277 |
|
278 | export class ABCWidgetFactory {
|
279 | |
280 |
|
281 |
|
282 | constructor(options) {
|
283 | this._isDisposed = false;
|
284 | this._widgetCreated = new Signal(this);
|
285 | this._translator = options.translator || nullTranslator;
|
286 | this._name = options.name;
|
287 | this._label = options.label || options.name;
|
288 | this._readOnly = options.readOnly === undefined ? false : options.readOnly;
|
289 | this._defaultFor = options.defaultFor ? options.defaultFor.slice() : [];
|
290 | this._defaultRendered = (options.defaultRendered || []).slice();
|
291 | this._fileTypes = options.fileTypes.slice();
|
292 | this._modelName = options.modelName || 'text';
|
293 | this._preferKernel = !!options.preferKernel;
|
294 | this._canStartKernel = !!options.canStartKernel;
|
295 | this._shutdownOnClose = !!options.shutdownOnClose;
|
296 | this._autoStartDefault = !!options.autoStartDefault;
|
297 | this._toolbarFactory = options.toolbarFactory;
|
298 | }
|
299 | |
300 |
|
301 |
|
302 | get widgetCreated() {
|
303 | return this._widgetCreated;
|
304 | }
|
305 | |
306 |
|
307 |
|
308 | get isDisposed() {
|
309 | return this._isDisposed;
|
310 | }
|
311 | |
312 |
|
313 |
|
314 | dispose() {
|
315 | if (this.isDisposed) {
|
316 | return;
|
317 | }
|
318 | this._isDisposed = true;
|
319 | Signal.clearData(this);
|
320 | }
|
321 | |
322 |
|
323 |
|
324 | get readOnly() {
|
325 | return this._readOnly;
|
326 | }
|
327 | |
328 |
|
329 |
|
330 | get name() {
|
331 | return this._name;
|
332 | }
|
333 | |
334 |
|
335 |
|
336 |
|
337 | get label() {
|
338 | return this._label;
|
339 | }
|
340 | |
341 |
|
342 |
|
343 | get fileTypes() {
|
344 | return this._fileTypes.slice();
|
345 | }
|
346 | |
347 |
|
348 |
|
349 | get modelName() {
|
350 | return this._modelName;
|
351 | }
|
352 | |
353 |
|
354 |
|
355 | get defaultFor() {
|
356 | return this._defaultFor.slice();
|
357 | }
|
358 | |
359 |
|
360 |
|
361 |
|
362 | get defaultRendered() {
|
363 | return this._defaultRendered.slice();
|
364 | }
|
365 | |
366 |
|
367 |
|
368 | get preferKernel() {
|
369 | return this._preferKernel;
|
370 | }
|
371 | |
372 |
|
373 |
|
374 | get canStartKernel() {
|
375 | return this._canStartKernel;
|
376 | }
|
377 | |
378 |
|
379 |
|
380 | get translator() {
|
381 | return this._translator;
|
382 | }
|
383 | |
384 |
|
385 |
|
386 | get shutdownOnClose() {
|
387 | return this._shutdownOnClose;
|
388 | }
|
389 | set shutdownOnClose(value) {
|
390 | this._shutdownOnClose = value;
|
391 | }
|
392 | |
393 |
|
394 |
|
395 | get autoStartDefault() {
|
396 | return this._autoStartDefault;
|
397 | }
|
398 | set autoStartDefault(value) {
|
399 | this._autoStartDefault = value;
|
400 | }
|
401 | |
402 |
|
403 |
|
404 |
|
405 |
|
406 |
|
407 | createNew(context, source) {
|
408 | var _a;
|
409 |
|
410 | const widget = this.createNewWidget(context, source);
|
411 |
|
412 | setToolbar(widget, (_a = this._toolbarFactory) !== null && _a !== void 0 ? _a : this.defaultToolbarFactory.bind(this));
|
413 |
|
414 | this._widgetCreated.emit(widget);
|
415 | return widget;
|
416 | }
|
417 | |
418 |
|
419 |
|
420 | defaultToolbarFactory(widget) {
|
421 | return [];
|
422 | }
|
423 | }
|
424 |
|
425 |
|
426 |
|
427 | const DIRTY_CLASS = 'jp-mod-dirty';
|
428 |
|
429 |
|
430 |
|
431 | export class DocumentWidget extends MainAreaWidget {
|
432 | constructor(options) {
|
433 | var _a;
|
434 |
|
435 | options.reveal = Promise.all([options.reveal, options.context.ready]);
|
436 | super(options);
|
437 | this._trans = ((_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator).load('jupyterlab');
|
438 | this.context = options.context;
|
439 |
|
440 | this.context.pathChanged.connect(this._onPathChanged, this);
|
441 | this._onPathChanged(this.context, this.context.path);
|
442 |
|
443 | this.context.model.stateChanged.connect(this._onModelStateChanged, this);
|
444 | void this.context.ready.then(() => {
|
445 | this._handleDirtyState();
|
446 | });
|
447 |
|
448 | this.title.changed.connect(this._onTitleChanged, this);
|
449 | }
|
450 | |
451 |
|
452 |
|
453 | setFragment(fragment) {
|
454 |
|
455 | }
|
456 | |
457 |
|
458 |
|
459 | async _onTitleChanged(_sender) {
|
460 | const validNameExp = /[\/\\:]/;
|
461 | const name = this.title.label;
|
462 |
|
463 | const filename = this.context.localPath.split('/').pop() || this.context.localPath;
|
464 | if (name === filename) {
|
465 | return;
|
466 | }
|
467 | if (name.length > 0 && !validNameExp.test(name)) {
|
468 | const oldPath = this.context.path;
|
469 | await this.context.rename(name);
|
470 | if (this.context.path !== oldPath) {
|
471 |
|
472 | return;
|
473 | }
|
474 | }
|
475 |
|
476 | this.title.label = filename;
|
477 | }
|
478 | |
479 |
|
480 |
|
481 | _onPathChanged(sender, path) {
|
482 | this.title.label = PathExt.basename(sender.localPath);
|
483 |
|
484 | this.isUntitled = false;
|
485 | }
|
486 | |
487 |
|
488 |
|
489 | _onModelStateChanged(sender, args) {
|
490 | var _a;
|
491 | if (args.name === 'dirty') {
|
492 | this._handleDirtyState();
|
493 | }
|
494 | if (!this.context.model.dirty) {
|
495 | if (!this.context.model.collaborative) {
|
496 | if (!((_a = this.context.contentsModel) === null || _a === void 0 ? void 0 : _a.writable)) {
|
497 | const readOnlyIndicator = createReadonlyLabel(this);
|
498 | let roi = this.toolbar.insertBefore('kernelName', 'read-only-indicator', readOnlyIndicator);
|
499 | if (!roi) {
|
500 | this.toolbar.addItem('read-only-indicator', readOnlyIndicator);
|
501 | }
|
502 | }
|
503 | }
|
504 | }
|
505 | }
|
506 | |
507 |
|
508 |
|
509 | _handleDirtyState() {
|
510 | if (this.context.model.dirty &&
|
511 | !this.title.className.includes(DIRTY_CLASS)) {
|
512 | this.title.className += ` ${DIRTY_CLASS}`;
|
513 | }
|
514 | else {
|
515 | this.title.className = this.title.className.replace(DIRTY_CLASS, '');
|
516 | }
|
517 | }
|
518 | }
|
519 |
|
\ | No newline at end of file |