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