manager.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. 'use strict';
  4. import {
  5. IContentsModel
  6. } from 'jupyter-js-services';
  7. import * as arrays
  8. from 'phosphor-arrays';
  9. import {
  10. ISignal, Signal
  11. } from 'phosphor-signaling';
  12. import {
  13. Widget
  14. } from 'phosphor-widget';
  15. import {
  16. AbstractFileHandler
  17. } from './handler';
  18. /**
  19. * The class name added to document widgets.
  20. */
  21. export
  22. const DOCUMENT_CLASS = 'jp-Document';
  23. /**
  24. * A document manager for Jupyter.
  25. */
  26. export
  27. class DocumentManager {
  28. /**
  29. * Register a file handler.
  30. */
  31. register(handler: AbstractFileHandler<Widget>): void {
  32. this._handlers.push(handler);
  33. handler.activated.connect(this._onActivated, this);
  34. }
  35. /**
  36. * Register a default file handler.
  37. */
  38. registerDefault(handler: AbstractFileHandler<Widget>): void {
  39. if (this._defaultHandler) {
  40. throw Error('Default handler already registered');
  41. }
  42. this.register(handler);
  43. this._defaultHandler = handler;
  44. }
  45. /**
  46. * Open a contents model and return a widget.
  47. */
  48. open(model: IContentsModel): Widget {
  49. if (this._handlers.length === 0) {
  50. return;
  51. }
  52. let path = model.path;
  53. let ext = '.' + path.split('.').pop();
  54. let handlers: AbstractFileHandler<Widget>[] = [];
  55. // Look for matching file extensions.
  56. for (let h of this._handlers) {
  57. if (h.fileExtensions.indexOf(ext) !== -1) handlers.push(h);
  58. }
  59. let widget: Widget;
  60. // If there was only one match, use it.
  61. if (handlers.length === 1) {
  62. widget = this._open(handlers[0], model);
  63. // If there were no matches, use default handler.
  64. } else if (handlers.length === 0) {
  65. if (this._defaultHandler) {
  66. widget = this._open(this._defaultHandler, model);
  67. } else {
  68. throw new Error(`Could not open file '${path}'`);
  69. }
  70. // There are more than one possible handlers.
  71. } else {
  72. // TODO: Ask the user to choose one.
  73. widget = this._open(handlers[0], model);
  74. }
  75. widget.addClass(DOCUMENT_CLASS);
  76. return widget;
  77. }
  78. /**
  79. * Rename a file.
  80. */
  81. rename(oldPath: string, newPath: string): boolean {
  82. for (let h of this._handlers) {
  83. if (h.rename(oldPath, newPath)) {
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. /**
  90. * Save the active document.
  91. *
  92. * returns A promise that resolves to the contents of the widget.
  93. */
  94. save(): Promise<IContentsModel> {
  95. if (this._activeHandler) {
  96. return this._activeHandler.save();
  97. }
  98. }
  99. /**
  100. * Revert the active document.
  101. *
  102. * returns A promise that resolves to the new contents of the widget.
  103. */
  104. revert(): Promise<IContentsModel> {
  105. if (this._activeHandler) {
  106. return this._activeHandler.revert();
  107. }
  108. }
  109. /**
  110. * Close the active document.
  111. *
  112. * returns A promise that resolves with a boolean indicating whether the
  113. widget was closed.
  114. */
  115. close(): Promise<boolean> {
  116. if (this._activeHandler) {
  117. return this._activeHandler.close();
  118. }
  119. return Promise.resolve(false);
  120. }
  121. /**
  122. * Close all documents.
  123. */
  124. closeAll(): void {
  125. this._handlers.map(handler => handler.closeAll());
  126. }
  127. /**
  128. * Open a file and emit an `openRequested` signal.
  129. */
  130. private _open(handler: AbstractFileHandler<Widget>, model: IContentsModel): Widget {
  131. let widget = handler.open(model);
  132. // Clear all other active widgets.
  133. for (let h of this._handlers) {
  134. if (h !== handler) handler.deactivate();
  135. }
  136. return widget;
  137. }
  138. /**
  139. * A handler for handler activated signals.
  140. */
  141. private _onActivated(handler: AbstractFileHandler<Widget>) {
  142. this._activeHandler = handler;
  143. for (let h of this._handlers) {
  144. if (h !== handler) h.deactivate();
  145. }
  146. }
  147. private _handlers: AbstractFileHandler<Widget>[] = [];
  148. private _defaultHandler: AbstractFileHandler<Widget> = null;
  149. private _activeHandler: AbstractFileHandler<Widget> = null;
  150. }