plugin.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. utils
  5. } from '@jupyterlab/services';
  6. import {
  7. installMessageHook, Message
  8. } from 'phosphor/lib/core/messaging';
  9. import {
  10. Menu
  11. } from 'phosphor/lib/ui/menu';
  12. import {
  13. WidgetMessage
  14. } from 'phosphor/lib/ui/widget';
  15. import {
  16. CommandIDs as AboutCommandIDs
  17. } from '../about';
  18. import {
  19. JupyterLab, JupyterLabPlugin
  20. } from '../application';
  21. import {
  22. IFrame
  23. } from '../common/iframe';
  24. import {
  25. InstanceTracker
  26. } from '../common/instancetracker';
  27. import {
  28. ICommandPalette
  29. } from '../commandpalette';
  30. import {
  31. CommandIDs as FAQCommandIDs
  32. } from '../faq';
  33. import {
  34. IInstanceRestorer
  35. } from '../instancerestorer';
  36. import {
  37. IMainMenu
  38. } from '../mainmenu';
  39. import {
  40. CommandIDs as StateDBCommandIDs
  41. } from '../statedb';
  42. import {
  43. CommandIDs
  44. } from './';
  45. /**
  46. * A flag denoting whether the application is loaded over HTTPS.
  47. */
  48. const LAB_IS_SECURE = window.location.protocol === 'https:';
  49. /**
  50. * The class name added to the help widget.
  51. */
  52. const HELP_CLASS = 'jp-Help';
  53. /**
  54. * A list of help resources.
  55. */
  56. const RESOURCES = [
  57. {
  58. text: 'Scipy Lecture Notes',
  59. url: 'http://www.scipy-lectures.org/'
  60. },
  61. {
  62. text: 'Numpy Reference',
  63. url: 'https://docs.scipy.org/doc/numpy/reference/'
  64. },
  65. {
  66. text: 'Scipy Reference',
  67. url: 'https://docs.scipy.org/doc/scipy/reference/'
  68. },
  69. {
  70. text: 'Notebook Tutorial',
  71. url: 'https://nbviewer.jupyter.org/github/jupyter/notebook/' +
  72. 'blob/master/docs/source/examples/Notebook/Notebook Basics.ipynb'
  73. },
  74. {
  75. text: 'Python Reference',
  76. url: 'https://docs.python.org/3.5/'
  77. },
  78. {
  79. text: 'IPython Reference',
  80. url: 'https://ipython.org/documentation.html?v=20160707164940'
  81. },
  82. {
  83. text: 'Matplotlib Reference',
  84. url: 'http://matplotlib.org/contents.html?v=20160707164940'
  85. },
  86. {
  87. text: 'SymPy Reference',
  88. url: 'http://docs.sympy.org/latest/index.html?v=20160707164940'
  89. },
  90. {
  91. text: 'Pandas Reference',
  92. url: 'http://pandas.pydata.org/pandas-docs/stable/?v=20160707164940'
  93. },
  94. {
  95. text: 'Markdown Reference',
  96. url: 'https://help.github.com/articles/' +
  97. 'getting-started-with-writing-and-formatting-on-github/'
  98. }
  99. ];
  100. RESOURCES.sort((a: any, b: any) => {
  101. return a.text.localeCompare(b.text);
  102. });
  103. /**
  104. * The help handler extension.
  105. */
  106. const plugin: JupyterLabPlugin<void> = {
  107. activate,
  108. id: 'jupyter.extensions.help-handler',
  109. requires: [IMainMenu, ICommandPalette, IInstanceRestorer],
  110. autoStart: true
  111. };
  112. /**
  113. * Export the plugin as default.
  114. */
  115. export default plugin;
  116. /**
  117. * Activate the help handler extension.
  118. *
  119. * @param app - The phosphide application object.
  120. *
  121. * returns A promise that resolves when the extension is activated.
  122. */
  123. function activate(app: JupyterLab, mainMenu: IMainMenu, palette: ICommandPalette, restorer: IInstanceRestorer): void {
  124. let iframe: IFrame = null;
  125. const category = 'Help';
  126. const namespace = 'help-doc';
  127. const command = CommandIDs.open;
  128. const menu = createMenu();
  129. const tracker = new InstanceTracker<IFrame>({ namespace });
  130. // Handle state restoration.
  131. restorer.restore(tracker, {
  132. command,
  133. args: widget => ({ isHidden: widget.isHidden, url: widget.url }),
  134. name: widget => namespace
  135. });
  136. /**
  137. * Create a new IFrame widget.
  138. */
  139. function newIFrame(url: string): IFrame {
  140. let iframe = new IFrame();
  141. iframe.addClass(HELP_CLASS);
  142. iframe.title.label = category;
  143. iframe.id = `${namespace}`;
  144. iframe.url = url;
  145. // Add the iframe to the instance tracker.
  146. tracker.add(iframe);
  147. // If the help widget visibility changes, update the tracker.
  148. installMessageHook(iframe, (iframe: IFrame, msg: Message) => {
  149. switch (msg) {
  150. case WidgetMessage.AfterShow:
  151. case WidgetMessage.BeforeHide:
  152. // Wait until hide has completed.
  153. requestAnimationFrame(() => { tracker.save(iframe); });
  154. break;
  155. default:
  156. break;
  157. }
  158. return true;
  159. });
  160. return iframe;
  161. }
  162. /**
  163. * Create a menu for the help plugin.
  164. */
  165. function createMenu(): Menu {
  166. let { commands, keymap } = app;
  167. let menu = new Menu({ commands, keymap });
  168. menu.title.label = category;
  169. menu.addItem({ command: AboutCommandIDs.open });
  170. menu.addItem({ command: FAQCommandIDs.open });
  171. menu.addItem({ command: CommandIDs.launchClassic });
  172. menu.addItem({ type: 'separator' });
  173. RESOURCES.forEach(args => { menu.addItem({ args, command }); });
  174. menu.addItem({ type: 'separator' });
  175. menu.addItem({ command: StateDBCommandIDs.clear });
  176. return menu;
  177. }
  178. /**
  179. * Attach the help iframe widget to the application shell.
  180. */
  181. function attachHelp(): void {
  182. if (!iframe.isAttached) {
  183. app.shell.addToRightArea(iframe);
  184. }
  185. }
  186. /**
  187. * Show the help widget.
  188. */
  189. function showHelp(): void {
  190. app.shell.activateRight(iframe.id);
  191. }
  192. /**
  193. * Hide the help widget.
  194. */
  195. function hideHelp(): void {
  196. if (!iframe.isHidden) {
  197. app.shell.collapseRight();
  198. }
  199. }
  200. /**
  201. * Toggle whether the help widget is shown or hidden.
  202. */
  203. function toggleHelp(): void {
  204. if (iframe.isHidden) {
  205. showHelp();
  206. } else {
  207. hideHelp();
  208. }
  209. }
  210. app.commands.addCommand(command, {
  211. label: args => args['text'] as string,
  212. execute: args => {
  213. const url = args['url'] as string;
  214. const isHidden = args['isHidden'] as boolean || false;
  215. // If help resource will generate a mixed content error, load externally.
  216. if (LAB_IS_SECURE && utils.urlParse(url).protocol !== 'https:') {
  217. window.open(url);
  218. return;
  219. }
  220. if (iframe) {
  221. iframe.url = url;
  222. tracker.save(iframe);
  223. } else {
  224. iframe = newIFrame(url);
  225. }
  226. attachHelp();
  227. if (isHidden) {
  228. hideHelp();
  229. } else {
  230. showHelp();
  231. }
  232. }
  233. });
  234. app.commands.addCommand(CommandIDs.show, {
  235. execute: () => { showHelp(); }
  236. });
  237. app.commands.addCommand(CommandIDs.hide, {
  238. execute: () => { hideHelp(); }
  239. });
  240. app.commands.addCommand(CommandIDs.toggle, {
  241. execute: () => { toggleHelp(); }
  242. });
  243. RESOURCES.forEach(args => { palette.addItem({ args, command, category }); });
  244. palette.addItem({ command: StateDBCommandIDs.clear, category });
  245. app.commands.addCommand(CommandIDs.launchClassic, {
  246. label: 'Launch Classic Notebook',
  247. execute: () => { window.open(utils.getBaseUrl() + 'tree'); }
  248. });
  249. palette.addItem({ command: CommandIDs.launchClassic, category });
  250. mainMenu.addMenu(menu, {});
  251. }