agent-for-hackmd.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. /**
  18. * return the value of CodeMirror
  19. */
  20. function getValueOfCodemirror() {
  21. // get CodeMirror instance
  22. const editor = window.editor;
  23. return editor.doc.getValue();
  24. }
  25. /**
  26. * set the specified document to CodeMirror
  27. * @param {string} value
  28. */
  29. function setValueToCodemirror(value) {
  30. // get CodeMirror instance
  31. const editor = window.editor;
  32. editor.doc.setValue(value);
  33. }
  34. /**
  35. * set the specified document to CodeMirror on window loaded
  36. * @param {string} value
  37. */
  38. function setValueToCodemirrorOnInit(newValue) {
  39. if (window.loaded && window.cmClient != null) {
  40. setValueToCodemirror(newValue);
  41. return;
  42. }
  43. const setValueIfCodemirrorInitialized = () => {
  44. const intervalId = setInterval(() => {
  45. if (window.cmClient != null) {
  46. clearInterval(intervalId);
  47. setValueToCodemirror(newValue);
  48. }
  49. }, 250);
  50. };
  51. if (window.loaded) {
  52. setValueIfCodemirrorInitialized();
  53. }
  54. else {
  55. window.addEventListener('load', (event) => {
  56. setValueIfCodemirrorInitialized();
  57. });
  58. }
  59. }
  60. /**
  61. * postMessage to GROWI to notify body changes
  62. * @param {string} body
  63. */
  64. function postParentToNotifyBodyChanges(body) {
  65. window.growi.notifyBodyChanges(body);
  66. }
  67. // generate debounced function
  68. const debouncedPostParentToNotifyBodyChanges = debounce(1500, postParentToNotifyBodyChanges);
  69. /**
  70. * postMessage to GROWI to save with shortcut
  71. * @param {string} document
  72. */
  73. function postParentToSaveWithShortcut(document) {
  74. window.growi.saveWithShortcut(document);
  75. }
  76. function addEventListenersToCodemirror() {
  77. // get CodeMirror instance
  78. const codemirror = window.CodeMirror;
  79. // get CodeMirror editor instance
  80. const editor = window.editor;
  81. // e.g. 404 not found
  82. if (codemirror == null || editor == null) {
  83. return;
  84. }
  85. //// change event
  86. editor.on('change', (cm, change) => {
  87. debouncedPostParentToNotifyBodyChanges(cm.doc.getValue());
  88. });
  89. //// save event
  90. // Reset save commands and Cmd-S/Ctrl-S shortcuts that initialized by HackMD
  91. codemirror.commands.save = function(cm) {
  92. postParentToSaveWithShortcut(cm.doc.getValue());
  93. };
  94. delete editor.options.extraKeys['Cmd-S'];
  95. delete editor.options.extraKeys['Ctrl-S'];
  96. }
  97. function connectToParentWithPenpal() {
  98. const connection = Penpal.connectToParent({
  99. parentOrigin: allowedOrigin,
  100. // Methods child is exposing to parent
  101. methods: {
  102. getValue() {
  103. return getValueOfCodemirror();
  104. },
  105. setValue(newValue) {
  106. setValueToCodemirror(newValue);
  107. },
  108. setValueOnInit(newValue) {
  109. setValueToCodemirrorOnInit(newValue);
  110. }
  111. }
  112. });
  113. connection.promise.then(parent => {
  114. window.growi = parent;
  115. });
  116. }
  117. /**
  118. * main
  119. */
  120. (function() {
  121. // check HackMD is in iframe
  122. if (window === window.parent) {
  123. console.log('[GROWI] Loading agent for HackMD is not processed because currently not in iframe');
  124. return;
  125. }
  126. console.log('[HackMD] Loading GROWI agent for HackMD...');
  127. window.addEventListener('load', (event) => {
  128. console.log('loaded');
  129. addEventListenersToCodemirror();
  130. });
  131. connectToParentWithPenpal();
  132. console.log('[HackMD] GROWI agent for HackMD has successfully loaded.');
  133. }());