attachmentmodel.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import {
  6. nbformat
  7. } from '@jupyterlab/coreutils';
  8. import {
  9. IObservableJSON, ObservableJSON
  10. } from '@jupyterlab/observables';
  11. import {
  12. IRenderMime
  13. } from '@jupyterlab/rendermime-interfaces';
  14. import {
  15. JSONExt, JSONObject, JSONValue, ReadonlyJSONObject
  16. } from '@phosphor/coreutils';
  17. import {
  18. ISignal, Signal
  19. } from '@phosphor/signaling';
  20. import {
  21. MimeModel
  22. } from './mimemodel';
  23. /**
  24. * The interface for an attachment model.
  25. */
  26. export
  27. interface IAttachmentModel extends IRenderMime.IMimeModel {
  28. /**
  29. * A signal emitted when the attachment model changes.
  30. */
  31. readonly changed: ISignal<this, void>;
  32. /**
  33. * Dispose of the resources used by the attachment model.
  34. */
  35. dispose(): void;
  36. /**
  37. * Serialize the model to JSON.
  38. */
  39. toJSON(): nbformat.IMimeBundle;
  40. }
  41. /**
  42. * The namespace for IAttachmentModel sub-interfaces.
  43. */
  44. export
  45. namespace IAttachmentModel {
  46. /**
  47. * The options used to create a notebook attachment model.
  48. */
  49. export
  50. interface IOptions {
  51. /**
  52. * The raw attachment value.
  53. */
  54. value: nbformat.IMimeBundle;
  55. }
  56. }
  57. /**
  58. * The default implementation of a notebook attachment model.
  59. */
  60. export
  61. class AttachmentModel implements IAttachmentModel {
  62. /**
  63. * Construct a new attachment model.
  64. */
  65. constructor(options: IAttachmentModel.IOptions) {
  66. let { data } = Private.getBundleOptions(options);
  67. this._data = new ObservableJSON({ values: data as JSONObject });
  68. this._rawData = data;
  69. // Make a copy of the data.
  70. let value = options.value;
  71. for (let key in value) {
  72. // Ignore data and metadata that were stripped.
  73. switch (key) {
  74. case 'data':
  75. break;
  76. default:
  77. this._raw[key] = Private.extract(value, key);
  78. }
  79. }
  80. }
  81. /**
  82. * A signal emitted when the attachment model changes.
  83. */
  84. get changed(): ISignal<this, void> {
  85. return this._changed;
  86. }
  87. /**
  88. * Dispose of the resources used by the attachment model.
  89. */
  90. dispose(): void {
  91. this._data.dispose();
  92. Signal.clearData(this);
  93. }
  94. /**
  95. * The data associated with the model.
  96. */
  97. get data(): ReadonlyJSONObject {
  98. return this._rawData;
  99. }
  100. /**
  101. * The metadata associated with the model.
  102. */
  103. get metadata(): ReadonlyJSONObject {
  104. return undefined;
  105. }
  106. /**
  107. * Set the data associated with the model.
  108. *
  109. * #### Notes
  110. * Depending on the implementation of the mime model,
  111. * this call may or may not have deferred effects,
  112. */
  113. setData(options: IRenderMime.IMimeModel.ISetDataOptions): void {
  114. if (options.data) {
  115. this._updateObservable(this._data, options.data);
  116. this._rawData = options.data;
  117. }
  118. this._changed.emit(void 0);
  119. }
  120. /**
  121. * Serialize the model to JSON.
  122. */
  123. toJSON(): nbformat.IMimeBundle {
  124. let attachment: JSONValue = {};
  125. for (let key in this._raw) {
  126. attachment[key] = Private.extract(this._raw, key);
  127. }
  128. return attachment as nbformat.IMimeBundle;
  129. }
  130. // All attachments are untrusted
  131. readonly trusted = false;
  132. /**
  133. * Update an observable JSON object using a readonly JSON object.
  134. */
  135. private _updateObservable(observable: IObservableJSON, data: ReadonlyJSONObject) {
  136. let oldKeys = observable.keys();
  137. let newKeys = Object.keys(data);
  138. // Handle removed keys.
  139. for (let key of oldKeys) {
  140. if (newKeys.indexOf(key) === -1) {
  141. observable.delete(key);
  142. }
  143. }
  144. // Handle changed data.
  145. for (let key of newKeys) {
  146. let oldValue = observable.get(key);
  147. let newValue = data[key];
  148. if (oldValue !== newValue) {
  149. observable.set(key, newValue as JSONValue);
  150. }
  151. }
  152. }
  153. private _changed = new Signal<this, void>(this);
  154. private _raw: JSONObject = {};
  155. private _rawData: ReadonlyJSONObject;
  156. private _data: IObservableJSON;
  157. }
  158. /**
  159. * The namespace for AttachmentModel statics.
  160. */
  161. export
  162. namespace AttachmentModel {
  163. /**
  164. * Get the data for an attachment.
  165. *
  166. * @params bundle - A kernel attachment MIME bundle.
  167. *
  168. * @returns - The data for the payload.
  169. */
  170. export
  171. function getData(bundle: nbformat.IMimeBundle): JSONObject {
  172. return Private.getData(bundle);
  173. }
  174. }
  175. /**
  176. * The namespace for module private data.
  177. */
  178. namespace Private {
  179. /**
  180. * Get the data from a notebook attachment.
  181. */
  182. export
  183. function getData(bundle: nbformat.IMimeBundle): JSONObject {
  184. return convertBundle(bundle);
  185. }
  186. /**
  187. * Get the bundle options given attachment model options.
  188. */
  189. export
  190. function getBundleOptions(options: IAttachmentModel.IOptions): MimeModel.IOptions {
  191. let data = getData(options.value);
  192. return { data };
  193. }
  194. /**
  195. * Extract a value from a JSONObject.
  196. */
  197. export
  198. function extract(value: JSONObject, key: string): JSONValue {
  199. let item = value[key];
  200. if (JSONExt.isPrimitive(item)) {
  201. return item;
  202. }
  203. return JSONExt.deepCopy(item);
  204. }
  205. /**
  206. * Convert a mime bundle to mime data.
  207. */
  208. function convertBundle(bundle: nbformat.IMimeBundle): JSONObject {
  209. let map: JSONObject = Object.create(null);
  210. for (let mimeType in bundle) {
  211. map[mimeType] = extract(bundle, mimeType);
  212. }
  213. return map;
  214. }
  215. }