model.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. 'use strict';
  4. import * as utils
  5. from 'jupyter-js-utils';
  6. import {
  7. IDisposable
  8. } from 'phosphor-disposable';
  9. import {
  10. ISignal, Signal, clearSignalData
  11. } from 'phosphor-signaling';
  12. import {
  13. CellType, IBaseCell, ICodeCell
  14. } from '../notebook/nbformat';
  15. import {
  16. ObservableOutputs
  17. } from '../output-area';
  18. /**
  19. * The definition of a model object for a cell.
  20. */
  21. export
  22. interface ICellModel extends IDisposable {
  23. /**
  24. * The type of cell.
  25. */
  26. type: CellType;
  27. /**
  28. * A signal emitted when the content of the model changes.
  29. */
  30. contentChanged: ISignal<ICellModel, string>;
  31. /**
  32. * The input content of the cell.
  33. */
  34. source: string;
  35. /**
  36. * Serialize the model to JSON.
  37. */
  38. toJSON(): any;
  39. /**
  40. * Get a metadata cursor for the cell.
  41. *
  42. * #### Notes
  43. * Metadata associated with the nbformat spec are set directly
  44. * on the model. This method is used to interact with a namespaced
  45. * set of metadata on the cell.
  46. */
  47. getMetadata(name: string): IMetadataCursor;
  48. /**
  49. * List the metadata namespace keys for the notebook.
  50. *
  51. * #### Notes
  52. * Metadata associated with the nbformat are not included.
  53. */
  54. listMetadata(): string[];
  55. }
  56. /**
  57. * The definition of a code cell.
  58. */
  59. export
  60. interface ICodeCellModel extends ICellModel {
  61. /**
  62. * The code cell's prompt number. Will be null if the cell has not been run.
  63. */
  64. executionCount: number;
  65. /**
  66. * The cell outputs.
  67. */
  68. outputs: ObservableOutputs;
  69. }
  70. /**
  71. * An implementation of the cell model.
  72. */
  73. export
  74. class CellModel implements ICellModel {
  75. /**
  76. * Construct a cell model from optional cell content.
  77. */
  78. constructor(cell?: IBaseCell) {
  79. if (!cell) {
  80. return;
  81. }
  82. this.source = cell.source;
  83. let metadata = utils.copy(cell.metadata);
  84. if (this.type !== 'raw') {
  85. delete metadata['format'];
  86. }
  87. if (this.type !== 'code') {
  88. delete metadata['collapsed'];
  89. delete metadata['scrolled'];
  90. }
  91. this._metadata = metadata;
  92. }
  93. /**
  94. * A signal emitted when the state of the model changes.
  95. */
  96. get contentChanged(): ISignal<ICellModel, string> {
  97. return Private.contentChangedSignal.bind(this);
  98. }
  99. /**
  100. * The input content of the cell.
  101. */
  102. get source(): string {
  103. return this._source;
  104. }
  105. set source(value: string) {
  106. if (this._source === value) {
  107. return;
  108. }
  109. this._source = value;
  110. this.contentChanged.emit('source');
  111. }
  112. /**
  113. * Get whether the model is disposed.
  114. *
  115. * #### Notes
  116. * This is a read-only property.
  117. */
  118. get isDisposed(): boolean {
  119. return this._metadata === null;
  120. }
  121. /**
  122. * Dispose of the resources held by the model.
  123. */
  124. dispose(): void {
  125. // Do nothing if already disposed.
  126. if (this.isDisposed) {
  127. return;
  128. }
  129. clearSignalData(this);
  130. for (let cursor of this._cursors) {
  131. cursor.dispose();
  132. }
  133. this._cursors = null;
  134. this._metadata = null;
  135. }
  136. /**
  137. * Serialize the model to JSON.
  138. */
  139. toJSON(): IBaseCell {
  140. return {
  141. cell_type: this.type,
  142. source: this.source,
  143. metadata: utils.copy(this._metadata)
  144. };
  145. }
  146. /**
  147. * Get a metadata cursor for the cell.
  148. *
  149. * #### Notes
  150. * Metadata associated with the nbformat spec are set directly
  151. * on the model. This method is used to interact with a namespaced
  152. * set of metadata on the cell.
  153. */
  154. getMetadata(name: string): IMetadataCursor {
  155. let cursor = new MetadataCursor(
  156. name,
  157. () => {
  158. return this._metadata[name];
  159. },
  160. (value: string) => {
  161. this.setCursorData(name, value);
  162. }
  163. );
  164. this._cursors.push(cursor);
  165. return cursor;
  166. }
  167. /**
  168. * List the metadata namespace keys for the notebook.
  169. *
  170. * #### Notes
  171. * Metadata associated with the nbformat are not included.
  172. */
  173. listMetadata(): string[] {
  174. return Object.keys(this._metadata);
  175. }
  176. /**
  177. * Set the cursor data for a given field.
  178. */
  179. protected setCursorData(name: string, value: string): void {
  180. if (this._metadata[name] === value) {
  181. return;
  182. }
  183. this._metadata[name] = value;
  184. this.contentChanged.emit(`metadata.${name}`);
  185. }
  186. /**
  187. * The type of cell.
  188. */
  189. type: CellType;
  190. private _metadata: { [key: string]: string } = Object.create(null);
  191. private _cursors: MetadataCursor[] = [];
  192. private _source = '';
  193. }
  194. /**
  195. * An implementation of a raw cell model.
  196. */
  197. export
  198. class RawCellModel extends CellModel {
  199. type: CellType = 'code';
  200. }
  201. /**
  202. * An implementation of a markdown cell model.
  203. */
  204. export
  205. class MarkdownCellModel extends CellModel {
  206. type: CellType = 'markdown';
  207. }
  208. /**
  209. * An implementation of a code cell Model.
  210. */
  211. export
  212. class CodeCellModel extends CellModel implements ICodeCellModel {
  213. /**
  214. * Construct a new code cell with optional original cell content.
  215. */
  216. constructor(cell?: IBaseCell) {
  217. super(cell);
  218. this._outputs = new ObservableOutputs();
  219. if (cell && cell.cell_type === 'code') {
  220. this.executionCount = (cell as ICodeCell).execution_count;
  221. this._outputs.assign((cell as ICodeCell).outputs);
  222. }
  223. this._outputs.changed.connect(() => {
  224. this.contentChanged.emit('outputs');
  225. });
  226. }
  227. /**
  228. * The execution count of the cell.
  229. */
  230. get executionCount(): number {
  231. return this._executionCount;
  232. }
  233. set executionCount(value: number) {
  234. if (value === this._executionCount) {
  235. return;
  236. }
  237. this._executionCount = value;
  238. this.contentChanged.emit('executionCount');
  239. }
  240. /**
  241. * The cell outputs.
  242. *
  243. * #### Notes
  244. * This is a read-only property.
  245. */
  246. get outputs(): ObservableOutputs {
  247. return this._outputs;
  248. }
  249. /**
  250. * Dispose of the resources held by the model.
  251. */
  252. dispose(): void {
  253. if (this.isDisposed) {
  254. return;
  255. }
  256. this._outputs.clear(false);
  257. this._outputs = null;
  258. super.dispose();
  259. }
  260. /**
  261. * Serialize the model to JSON.
  262. */
  263. toJSON(): ICodeCell {
  264. let cell = super.toJSON() as ICodeCell;
  265. cell.execution_count = this.executionCount;
  266. let outputs = this.outputs;
  267. cell.outputs = [];
  268. for (let i = 0; i < outputs.length; i++) {
  269. cell.outputs.push(outputs.get(i));
  270. }
  271. return cell;
  272. }
  273. type: CellType = 'code';
  274. private _outputs: ObservableOutputs = null;
  275. private _executionCount: number = null;
  276. }
  277. /**
  278. * A class used to interact with user level metadata.
  279. */
  280. export
  281. interface IMetadataCursor extends IDisposable {
  282. /**
  283. * The metadata namespace.
  284. */
  285. name: string;
  286. /**
  287. * Get the value of the metadata.
  288. */
  289. getValue(): any;
  290. /**
  291. * Set the value of the metdata.
  292. */
  293. setValue(value: any): void;
  294. }
  295. /**
  296. * An implementation of a metadata cursor.
  297. */
  298. export
  299. class MetadataCursor implements IMetadataCursor {
  300. /**
  301. * Construct a new metadata cursor.
  302. *
  303. * @param name - the metadata namespace key.
  304. *
  305. * @param value - this initial value of the namespace.
  306. *
  307. * @param cb - a change callback.
  308. */
  309. constructor(name: string, read: () => string, write: (value: string) => void) {
  310. this._name = name;
  311. this._read = read;
  312. this._write = write;
  313. }
  314. /**
  315. * Get the namespace key of the metadata.
  316. *
  317. * #### Notes
  318. * This is a read-only property.
  319. */
  320. get name(): string {
  321. return this._name;
  322. }
  323. /**
  324. * Get whether the cursor is disposed.
  325. */
  326. get isDisposed(): boolean {
  327. return this._read === null;
  328. }
  329. /**
  330. * Dispose of the resources used by the cursor.
  331. */
  332. dispose(): void {
  333. if (this.isDisposed) {
  334. return;
  335. }
  336. this._read = null;
  337. this._write = null;
  338. }
  339. /**
  340. * Get the value of the namespace data.
  341. */
  342. getValue(): any {
  343. let value = this._read.call(void 0);
  344. return JSON.parse(value || 'null');
  345. }
  346. /**
  347. * Set the value of the namespace data.
  348. */
  349. setValue(value: any): any {
  350. this._write.call(void 0, JSON.stringify(value));
  351. }
  352. private _name = '';
  353. private _read: () => string = null;
  354. private _write: (value: string) => void = null;
  355. }
  356. /**
  357. * A namespace for cell private data.
  358. */
  359. namespace Private {
  360. /**
  361. * A signal emitted when the state of the model changes.
  362. */
  363. export
  364. const contentChangedSignal = new Signal<ICellModel, string>();
  365. }