1. 1 : /**
  2. 2 : * A helper for working with the Voyant Notebook app.
  3. 3 : * @memberof Spyral
  4. 4 : */
  5. 5 : class Notebook {
  6. 6 :
  7. 7 : /**
  8. 8 : *
  9. 9 : * @param {*} contents
  10. 10 : * @param {*} config
  11. 11 : * @static
  12. 12 : */
  13. 13 : static show(contents, config) {
  14. 14 : var contents = Spyral.Util.toString(contents);
  15. 15 : if (contents instanceof Promise) {
  16. 16 : contents.then(c => Spyral.Util.show(c))
  17. 17 : } else {
  18. 18 : Spyral.Util.show(contents);
  19. 19 : }
  20. 20 : }
  21. 21 : /**
  22. 22 : * Returns the first DIV element that's a child of the document body. If none exists then one will be created.
  23. 23 : * @returns {element}
  24. 24 : * @static
  25. 25 : */
  26. 26 : static getTarget() {
  27. 27 : if (document.body.firstElementChild !== null && document.body.firstElementChild.nodeName === 'DIV') {
  28. 28 : return document.body.firstElementChild
  29. 29 : }
  30. 30 : const target = document.createElement("div");
  31. 31 : document.body.appendChild(target);
  32. 32 : return target;
  33. 33 : }
  34. 34 :
  35. 35 : /**
  36. 36 : * Returns a new promise
  37. 37 : * @returns {Promise} A promise
  38. 38 : * @static
  39. 39 : */
  40. 40 : static getPromise() {
  41. 41 : return new Promise();
  42. 42 : }
  43. 43 :
  44. 44 : /**
  45. 45 : * Fetch and return the content of a notebook or a particular cell in a notebook
  46. 46 : * @param {string} url The URL of the notebook to import
  47. 47 : * @param {number} [cellIndex] The index of the cell to import
  48. 48 : * @static
  49. 49 : */
  50. 50 : static async import(url, cellIndex=undefined) {
  51. 51 : const urlHasHash = url.indexOf('#') !== -1;
  52. 52 : const isAbsoluteUrl = url.indexOf('http') === 0;
  53. 53 :
  54. 54 : let notebookId = '';
  55. 55 : let cellId = undefined;
  56. 56 : if (isAbsoluteUrl) {
  57. 57 : const urlParts = url.match(/\/[\w-]+/g);
  58. 58 : if (urlParts !== null) {
  59. 59 : notebookId = urlParts[urlParts.length-1].replace('/', '');
  60. 60 : } else {
  61. 61 : return;
  62. 62 : }
  63. 63 : if (urlHasHash) {
  64. 64 : cellId = url.split('#')[1];
  65. 65 : }
  66. 66 : } else {
  67. 67 : if (urlHasHash) {
  68. 68 : [notebookId, cellId] = url.split('#');
  69. 69 : } else {
  70. 70 : notebookId = url;
  71. 71 : }
  72. 72 : }
  73. 73 :
  74. 74 : let json;
  75. 75 : try {
  76. 76 : json = await Spyral.Load.trombone({
  77. 77 : tool: 'notebook.GitNotebookManager',
  78. 78 : action: 'load',
  79. 79 : id: notebookId,
  80. 80 : noCache: 1
  81. 81 : })
  82. 82 : } catch (e) {
  83. 83 : throw new Error('There was an error importing the notebook. Please ensure the URL and ID are correct.');
  84. 84 : }
  85. 85 :
  86. 86 : const notebook = JSON.parse(json.notebook.data);
  87. 87 :
  88. 88 : function getCodeStringForDataCell(dataCellContent) {
  89. 89 : let code = '';
  90. 90 : switch(dataCellContent.mode) {
  91. 91 : case 'text':
  92. 92 : code = dataCellContent.dataName+'=`'+dataCellContent.input+'`';
  93. 93 : break;
  94. 94 : case 'json':
  95. 95 : code = dataCellContent.dataName+'= JSON.parse(`'+dataCellContent.input+'`)';
  96. 96 : break;
  97. 97 : case 'xml':
  98. 98 : code = dataCellContent.dataName+'= new DOMParser().parseFromString(`'+dataCellContent.input+'`, "application/xml")';
  99. 99 : break;
  100. 100 : case 'html':
  101. 101 : code = dataCellContent.dataName+'= new DOMParser().parseFromString(`'+dataCellContent.input+'`, "application/xml")';
  102. 102 : break;
  103. 103 : }
  104. 104 : return code;
  105. 105 : }
  106. 106 :
  107. 107 : const cells2import = notebook.cells.filter((cell, index) => {
  108. 108 : if (cell.type === 'code' || cell.type === 'data') {
  109. 109 : if (cellId === undefined && cellIndex === undefined) {
  110. 110 : return true;
  111. 111 : } else if (cell.cellId === cellId) {
  112. 112 : return true;
  113. 113 : } else if (cellIndex !== undefined && cellIndex === index+1) { // the index shown notebook counter is one-based
  114. 114 : return true;
  115. 115 : }
  116. 116 : }
  117. 117 : });
  118. 118 :
  119. 119 : const importedCode = cells2import.map((cell) => {
  120. 120 : if (cell.type === 'data') {
  121. 121 : return getCodeStringForDataCell(cell.content);
  122. 122 : } else {
  123. 123 : return cell.content.input;
  124. 124 : }
  125. 125 : })
  126. 126 :
  127. 127 : console.log('imported:', importedCode);
  128. 128 :
  129. 129 : async function __doRun(code) {
  130. 130 : // console.log('running:',code);
  131. 131 : try {
  132. 132 : const result = eval.call(window, code);
  133. 133 : const prResult = await Promise.resolve(result);
  134. 134 : if (importedCode.length > 0) {
  135. 135 : return __doRun(importedCode.shift());
  136. 136 : } else {
  137. 137 : return prResult;
  138. 138 : }
  139. 139 : } catch(e) {
  140. 140 : throw new Error('There was an error importing the notebook: '+e.message);
  141. 141 : }
  142. 142 : }
  143. 143 :
  144. 144 : if (importedCode.length > 0) {
  145. 145 : return __doRun(importedCode.shift());
  146. 146 : } else {
  147. 147 : throw new Error('There was an error importing the notebook. No code found for the specified '+ (cellId === undefined && cellIndex === undefined ? 'notebook' : 'cell')+'.');
  148. 148 : }
  149. 149 : }
  150. 150 : }
  151. 151 :
  152. 152 : export default Notebook