agent-for-hackmd.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * GROWI agent for HackMD
  3. *
  4. * This file will be transpiled as a single JS
  5. * and should be load from HackMD head via 'lib/routes/hackmd.js' route
  6. *
  7. * USAGE:
  8. * <script src="${hostname of GROWI}/_hackmd/load-agent"></script>
  9. *
  10. * @author Yuki Takei <yuki@weseek.co.jp>
  11. */
  12. import Penpal from 'penpal';
  13. // Penpal.debug = true;
  14. import { debounce } from 'throttle-debounce';
  15. /* eslint-disable no-console */
  16. const allowedOrigin = '{{origin}}'; // will be replaced by swig
  17. const styleFilePath = '{{styleFilePath}}'; // will be replaced by swig
  18. /**
  19. * Insert link tag to load style file
  20. */
  21. function insertStyle() {
  22. const element = document.createElement('link');
  23. element.href = styleFilePath;
  24. element.rel = 'stylesheet';
  25. document.getElementsByTagName('head')[0].appendChild(element);
  26. }
  27. /**
  28. * return the value of CodeMirror
  29. */
  30. function getValueOfCodemirror() {
  31. // get CodeMirror instance
  32. const editor = window.editor;
  33. return editor.doc.getValue();
  34. }
  35. /**
  36. * set the specified document to CodeMirror
  37. * @param {string} value
  38. */
  39. function setValueToCodemirror(value) {
  40. // get CodeMirror instance
  41. const editor = window.editor;
  42. editor.doc.setValue(value);
  43. }
  44. /**
  45. * set the specified document to CodeMirror on window loaded
  46. * @param {string} value
  47. */
  48. function setValueToCodemirrorOnInit(newValue) {
  49. if (window.loaded && window.cmClient != null) {
  50. setValueToCodemirror(newValue);
  51. return;
  52. }
  53. const setValueIfCodemirrorInitialized = () => {
  54. const intervalId = setInterval(() => {
  55. if (window.cmClient != null) {
  56. clearInterval(intervalId);
  57. setValueToCodemirror(newValue);
  58. }
  59. }, 250);
  60. };
  61. if (window.loaded) {
  62. setValueIfCodemirrorInitialized();
  63. }
  64. else {
  65. window.addEventListener('load', (event) => {
  66. setValueIfCodemirrorInitialized();
  67. });
  68. }
  69. }
  70. /**
  71. * postMessage to GROWI to notify body changes
  72. * @param {string} body
  73. */
  74. function postParentToNotifyBodyChanges(body) {
  75. window.growi.notifyBodyChanges(body);
  76. }
  77. // generate debounced function
  78. const debouncedPostParentToNotifyBodyChanges = debounce(1500, postParentToNotifyBodyChanges);
  79. /**
  80. * postMessage to GROWI to save with shortcut
  81. * @param {string} document
  82. */
  83. function postParentToSaveWithShortcut(document) {
  84. window.growi.saveWithShortcut(document);
  85. }
  86. function addEventListenersToCodemirror() {
  87. // get CodeMirror instance
  88. const codemirror = window.CodeMirror;
  89. // get CodeMirror editor instance
  90. const editor = window.editor;
  91. // e.g. 404 not found
  92. if (codemirror == null || editor == null) {
  93. return;
  94. }
  95. //// change event
  96. editor.on('change', (cm, change) => {
  97. debouncedPostParentToNotifyBodyChanges(cm.doc.getValue());
  98. });
  99. //// save event
  100. // Reset save commands and Cmd-S/Ctrl-S shortcuts that initialized by HackMD
  101. codemirror.commands.save = function(cm) {
  102. postParentToSaveWithShortcut(cm.doc.getValue());
  103. };
  104. delete editor.options.extraKeys['Cmd-S'];
  105. delete editor.options.extraKeys['Ctrl-S'];
  106. }
  107. function connectToParentWithPenpal() {
  108. const connection = Penpal.connectToParent({
  109. parentOrigin: allowedOrigin,
  110. // Methods child is exposing to parent
  111. methods: {
  112. getValue() {
  113. return getValueOfCodemirror();
  114. },
  115. setValue(newValue) {
  116. setValueToCodemirror(newValue);
  117. },
  118. setValueOnInit(newValue) {
  119. setValueToCodemirrorOnInit(newValue);
  120. }
  121. }
  122. });
  123. connection.promise.then(parent => {
  124. window.growi = parent;
  125. });
  126. }
  127. /**
  128. * main
  129. */
  130. (function() {
  131. // check HackMD is in iframe
  132. if (window === window.parent) {
  133. console.log('[GROWI] Loading agent for HackMD is not processed because currently not in iframe');
  134. return;
  135. }
  136. console.log('[HackMD] Loading GROWI agent for HackMD...');
  137. insertStyle();
  138. window.addEventListener('load', (event) => {
  139. console.log('loaded');
  140. addEventListenersToCodemirror();
  141. });
  142. connectToParentWithPenpal();
  143. console.log('[HackMD] GROWI agent for HackMD has successfully loaded.');
  144. }());