model.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import {
  6. JSONExt, JSONValue
  7. } from '@phosphor/coreutils';
  8. import {
  9. ISignal, Signal
  10. } from '@phosphor/signaling';
  11. import {
  12. CodeEditor
  13. } from '@jupyterlab/codeeditor';
  14. import {
  15. IChangedArgs, nbformat, uuid
  16. } from '@jupyterlab/coreutils';
  17. import {
  18. IObservableJSON, IModelDB, IObservableValue, ObservableValue
  19. } from '@jupyterlab/coreutils';
  20. import {
  21. IOutputAreaModel, OutputAreaModel
  22. } from '@jupyterlab/outputarea';
  23. /**
  24. * The definition of a model object for a cell.
  25. */
  26. export
  27. interface ICellModel extends CodeEditor.IModel {
  28. /**
  29. * The type of the cell.
  30. */
  31. readonly type: nbformat.CellType;
  32. /**
  33. * A unique identifier for the cell.
  34. */
  35. readonly id: string;
  36. /**
  37. * A signal emitted when the content of the model changes.
  38. */
  39. readonly contentChanged: ISignal<ICellModel, void>;
  40. /**
  41. * A signal emitted when a model state changes.
  42. */
  43. readonly stateChanged: ISignal<ICellModel, IChangedArgs<any>>;
  44. /**
  45. * Whether the cell is trusted.
  46. */
  47. trusted: boolean;
  48. /**
  49. * The metadata associated with the cell.
  50. */
  51. readonly metadata: IObservableJSON;
  52. /**
  53. * Serialize the model to JSON.
  54. */
  55. toJSON(): nbformat.ICell;
  56. }
  57. /**
  58. * The definition of a code cell.
  59. */
  60. export
  61. interface ICodeCellModel extends ICellModel {
  62. /**
  63. * The type of the cell.
  64. *
  65. * #### Notes
  66. * This is a read-only property.
  67. */
  68. type: 'code';
  69. /**
  70. * The code cell's prompt number. Will be null if the cell has not been run.
  71. */
  72. executionCount: nbformat.ExecutionCount;
  73. /**
  74. * The cell outputs.
  75. */
  76. outputs: IOutputAreaModel;
  77. }
  78. /**
  79. * The definition of a markdown cell.
  80. */
  81. export
  82. interface IMarkdownCellModel extends ICellModel {
  83. /**
  84. * The type of the cell.
  85. */
  86. type: 'markdown';
  87. }
  88. /**
  89. * The definition of a raw cell.
  90. */
  91. export
  92. interface IRawCellModel extends ICellModel {
  93. /**
  94. * The type of the cell.
  95. */
  96. type: 'raw';
  97. }
  98. /**
  99. * An implementation of the cell model.
  100. */
  101. export
  102. class CellModel extends CodeEditor.Model implements ICellModel {
  103. /**
  104. * Construct a cell model from optional cell content.
  105. */
  106. constructor(options: CellModel.IOptions) {
  107. super({modelDB: options.modelDB});
  108. this.id = options.id || uuid();
  109. this.value.changed.connect(this.onGenericChange, this);
  110. let cellType = this.modelDB.createValue('type');
  111. cellType.set(this.type);
  112. let observableMetadata = this.modelDB.createMap('metadata');
  113. observableMetadata.changed.connect(this.onGenericChange, this);
  114. let cell = options.cell;
  115. let trusted = this.modelDB.createValue('trusted');
  116. trusted.changed.connect(this.onTrustedChanged, this);
  117. if (!cell) {
  118. trusted.set(false);
  119. return;
  120. }
  121. trusted.set(!!cell.metadata['trusted']);
  122. delete cell.metadata['trusted'];
  123. if (Array.isArray(cell.source)) {
  124. this.value.text = (cell.source as string[]).join('');
  125. } else {
  126. this.value.text = cell.source as string;
  127. }
  128. let metadata = JSONExt.deepCopy(cell.metadata);
  129. if (this.type !== 'raw') {
  130. delete metadata['format'];
  131. }
  132. if (this.type !== 'code') {
  133. delete metadata['collapsed'];
  134. delete metadata['scrolled'];
  135. }
  136. for (let key in metadata) {
  137. observableMetadata.set(key, metadata[key]);
  138. }
  139. }
  140. /**
  141. * The type of cell.
  142. */
  143. readonly type: nbformat.CellType;
  144. /**
  145. * A signal emitted when the state of the model changes.
  146. */
  147. readonly contentChanged = new Signal<this, void>(this);
  148. /**
  149. * A signal emitted when a model state changes.
  150. */
  151. readonly stateChanged = new Signal<this, IChangedArgs<any>>(this);
  152. /**
  153. * The id for the cell.
  154. */
  155. readonly id: string;
  156. /**
  157. * The metadata associated with the cell.
  158. */
  159. get metadata(): IObservableJSON {
  160. return this.modelDB.get('metadata') as IObservableJSON;
  161. }
  162. /**
  163. * Get the trusted state of the model.
  164. */
  165. get trusted(): boolean {
  166. return this.modelDB.getValue('trusted') as boolean;
  167. }
  168. /**
  169. * Set the trusted state of the model.
  170. */
  171. set trusted(newValue: boolean) {
  172. let oldValue = this.trusted;
  173. if (oldValue === newValue) {
  174. return;
  175. }
  176. this.modelDB.setValue('trusted', newValue);
  177. }
  178. /**
  179. * Serialize the model to JSON.
  180. */
  181. toJSON(): nbformat.ICell {
  182. let metadata: nbformat.IBaseCellMetadata = Object.create(null);
  183. for (let key of this.metadata.keys()) {
  184. let value = JSON.parse(JSON.stringify(this.metadata.get(key)));
  185. metadata[key] = value as JSONValue;
  186. }
  187. if (this.trusted) {
  188. metadata['trusted'] = true;
  189. }
  190. return {
  191. cell_type: this.type,
  192. source: this.value.text,
  193. metadata,
  194. } as nbformat.ICell;
  195. }
  196. /**
  197. * Handle a change to the trusted state.
  198. *
  199. * The default implementation is a no-op.
  200. */
  201. onTrustedChanged(trusted: IObservableValue, args: ObservableValue.IChangedArgs): void { /* no-op */ }
  202. /**
  203. * Handle a change to the observable value.
  204. */
  205. protected onGenericChange(): void {
  206. this.contentChanged.emit(void 0);
  207. }
  208. }
  209. /**
  210. * The namespace for `CellModel` statics.
  211. */
  212. export
  213. namespace CellModel {
  214. /**
  215. * The options used to initialize a `CellModel`.
  216. */
  217. export interface IOptions {
  218. /**
  219. * The source cell data.
  220. */
  221. cell?: nbformat.IBaseCell;
  222. /**
  223. * An IModelDB in which to store cell data.
  224. */
  225. modelDB?: IModelDB;
  226. /**
  227. * A unique identifier for this cell.
  228. */
  229. id?: string;
  230. }
  231. }
  232. /**
  233. * An implementation of a raw cell model.
  234. */
  235. export
  236. class RawCellModel extends CellModel {
  237. /**
  238. * The type of the cell.
  239. */
  240. get type(): 'raw' {
  241. return 'raw';
  242. }
  243. }
  244. /**
  245. * An implementation of a markdown cell model.
  246. */
  247. export
  248. class MarkdownCellModel extends CellModel {
  249. /**
  250. * Construct a markdown cell model from optional cell content.
  251. */
  252. constructor(options: CellModel.IOptions) {
  253. super(options);
  254. // Use the Github-flavored markdown mode.
  255. this.mimeType = 'text/x-ipythongfm';
  256. }
  257. /**
  258. * The type of the cell.
  259. */
  260. get type(): 'markdown' {
  261. return 'markdown';
  262. }
  263. }
  264. /**
  265. * An implementation of a code cell Model.
  266. */
  267. export
  268. class CodeCellModel extends CellModel implements ICodeCellModel {
  269. /**
  270. * Construct a new code cell with optional original cell content.
  271. */
  272. constructor(options: CodeCellModel.IOptions) {
  273. super(options);
  274. let factory = (options.contentFactory ||
  275. CodeCellModel.defaultContentFactory
  276. );
  277. let trusted = this.trusted;
  278. let cell = options.cell as nbformat.ICodeCell;
  279. let outputs: nbformat.IOutput[] = [];
  280. let executionCount = this.modelDB.createValue('executionCount');
  281. if (!executionCount.get()) {
  282. if (cell && cell.cell_type === 'code') {
  283. executionCount.set(cell.execution_count || null);
  284. outputs = cell.outputs;
  285. } else {
  286. executionCount.set(null);
  287. }
  288. }
  289. executionCount.changed.connect(this._onExecutionCountChanged, this);
  290. this._outputs = factory.createOutputArea({
  291. trusted,
  292. values: outputs,
  293. modelDB: this.modelDB
  294. });
  295. this._outputs.stateChanged.connect(this.onGenericChange, this);
  296. }
  297. /**
  298. * The type of the cell.
  299. */
  300. get type(): 'code' {
  301. return 'code';
  302. }
  303. /**
  304. * The execution count of the cell.
  305. */
  306. get executionCount(): nbformat.ExecutionCount {
  307. return this.modelDB.getValue('executionCount') as nbformat.ExecutionCount;
  308. }
  309. set executionCount(newValue: nbformat.ExecutionCount) {
  310. let oldValue = this.executionCount;
  311. if (newValue === oldValue) {
  312. return;
  313. }
  314. this.modelDB.setValue('executionCount', newValue || null);
  315. }
  316. /**
  317. * The cell outputs.
  318. */
  319. get outputs(): IOutputAreaModel {
  320. return this._outputs;
  321. }
  322. /**
  323. * Dispose of the resources held by the model.
  324. */
  325. dispose(): void {
  326. if (this.isDisposed) {
  327. return;
  328. }
  329. this._outputs.dispose();
  330. this._outputs = null;
  331. super.dispose();
  332. }
  333. /**
  334. * Serialize the model to JSON.
  335. */
  336. toJSON(): nbformat.ICodeCell {
  337. let cell = super.toJSON() as nbformat.ICodeCell;
  338. cell.execution_count = this.executionCount || null;
  339. cell.outputs = this.outputs.toJSON();
  340. return cell;
  341. }
  342. /**
  343. * Handle a change to the trusted state.
  344. */
  345. onTrustedChanged(trusted: IObservableValue, args: ObservableValue.IChangedArgs): void {
  346. if (this._outputs) {
  347. this._outputs.trusted = args.newValue as boolean;
  348. }
  349. this.stateChanged.emit({
  350. name: 'trusted',
  351. oldValue: args.oldValue,
  352. newValue: args.newValue
  353. });
  354. }
  355. /**
  356. * Handle a change to the execution count.
  357. */
  358. private _onExecutionCountChanged(count: IObservableValue, args: ObservableValue.IChangedArgs): void {
  359. this.contentChanged.emit(void 0);
  360. this.stateChanged.emit({
  361. name: 'executionCount',
  362. oldValue: args.oldValue,
  363. newValue: args.newValue });
  364. }
  365. private _outputs: IOutputAreaModel = null;
  366. }
  367. /**
  368. * The namespace for `CodeCellModel` statics.
  369. */
  370. export
  371. namespace CodeCellModel {
  372. /**
  373. * The options used to initialize a `CodeCellModel`.
  374. */
  375. export
  376. interface IOptions extends CellModel.IOptions {
  377. /**
  378. * The factory for output area model creation.
  379. */
  380. contentFactory?: IContentFactory;
  381. }
  382. /**
  383. * A factory for creating code cell model content.
  384. */
  385. export
  386. interface IContentFactory {
  387. /**
  388. * Create an output area.
  389. */
  390. createOutputArea(options: IOutputAreaModel.IOptions): IOutputAreaModel;
  391. }
  392. /**
  393. * The default implementation of an `IContentFactory`.
  394. */
  395. export
  396. class ContentFactory {
  397. /**
  398. * Create an output area.
  399. */
  400. createOutputArea(options: IOutputAreaModel.IOptions): IOutputAreaModel {
  401. return new OutputAreaModel(options);
  402. }
  403. }
  404. /**
  405. * The shared `ConetntFactory` instance.
  406. */
  407. export
  408. const defaultContentFactory = new ContentFactory();
  409. }