editor.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. "use strict";
  2. function do_insert_data(data) {
  3. const name = 'opennamu_edit_textarea';
  4. if(get_select_editor() === 'textarea') {
  5. // https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
  6. if(document.selection) {
  7. document.getElementById(name).focus();
  8. let sel = document.selection.createRange();
  9. sel.text = data;
  10. } else if(
  11. document.getElementById(name).selectionStart ||
  12. document.getElementById(name).selectionStart == '0'
  13. ) {
  14. let startPos = document.getElementById(name).selectionStart;
  15. let endPos = document.getElementById(name).selectionEnd;
  16. let myPos = document.getElementById(name).value;
  17. document.getElementById(name).value = myPos.substring(0, startPos) + data + myPos.substring(endPos, myPos.length);
  18. } else {
  19. document.getElementById(name).value += data;
  20. }
  21. } else {
  22. let selection = editor.getSelection();
  23. let id = { major: 1, minor: 1 };
  24. let text = data;
  25. let op = {
  26. identifier: id,
  27. range: selection,
  28. text: text,
  29. forceMoveMarkers: true
  30. };
  31. editor.executeEdits("my-source", [op]);
  32. }
  33. }
  34. // 아직 개편이 더 필요함
  35. function do_paste_image() {
  36. const name = 'opennamu_edit_textarea';
  37. window.addEventListener('DOMContentLoaded', async function() {
  38. let set = await opennamu_get_main_skin_set("main_css_image_paste");
  39. if(set === 'use') {
  40. document.getElementById(name).addEventListener("paste", pasteListener);
  41. }
  42. });
  43. }
  44. function pasteListener(e) {
  45. // find file
  46. if(e.clipboardData && e.clipboardData.items) {
  47. const items = e.clipboardData.items;
  48. const formData = new FormData();
  49. let haveImageInClipboard = false;
  50. let file_name = '';
  51. for(let i = 0; i < items.length; i++) {
  52. if(items[i].type.indexOf("image") !== -1) {
  53. const file = items[i].getAsFile();
  54. const customName = prompt("파일 이름 (확장자 제외)");
  55. if(!customName) {
  56. return alert("파일 이름 없음");
  57. }
  58. file_name = customName + ".png";
  59. const customFile = new File([file], file_name, { type: file.type });
  60. formData.append("f_data[]", customFile);
  61. haveImageInClipboard = true;
  62. e.preventDefault();
  63. break;
  64. }
  65. }
  66. if(!haveImageInClipboard) {
  67. return;
  68. }
  69. // send to server
  70. fetch("/upload", {
  71. method: "POST",
  72. body: formData,
  73. }).then((res) => {
  74. if (res.status === 200 || res.status === 201) {
  75. const url = res.url;
  76. alert(
  77. '업로드 완료 : ' +
  78. '[[파일:' + file_name + ']]'
  79. );
  80. do_insert_data('[[file:' + file_name + ']]');
  81. } else {
  82. console.error("[ERROR] PasteUpload Fail :", res.statusText);
  83. if(res.status === 400) {
  84. alert("파일 이름 중복");
  85. } else if(res.status === 401) {
  86. alert("권한 부족");
  87. } else {
  88. alert("업로드 실패");
  89. }
  90. }
  91. }).catch((err) => {
  92. console.error("오류 내역 :", JSON.stringify(err), err);
  93. alert("업로드 실패");
  94. });
  95. }
  96. }
  97. function do_stop_exit() {
  98. window.onbeforeunload = function() {
  99. do_sync_monaco_and_textarea();
  100. let data = document.getElementById('opennamu_edit_textarea').value;
  101. let origin = document.getElementById('opennamu_edit_origin').value;
  102. if(data !== origin) {
  103. return '';
  104. }
  105. }
  106. }
  107. function do_stop_exit_release() {
  108. do_sync_monaco_and_textarea();
  109. window.onbeforeunload = function () {}
  110. }
  111. function opennamu_edit_turn_off_monaco() {
  112. do_sync_monaco_and_textarea();
  113. if(get_select_editor() === 'textarea') {
  114. document.getElementById('opennamu_edit_textarea').style.display = 'none';
  115. document.getElementById('opennamu_monaco_editor').style.display = 'block';
  116. } else {
  117. document.getElementById('opennamu_edit_textarea').style.display = 'block';
  118. document.getElementById('opennamu_monaco_editor').style.display = 'none';
  119. }
  120. }
  121. function do_monaco_to_textarea() {
  122. document.getElementById('opennamu_edit_textarea').value = window.editor.getValue();
  123. }
  124. function do_textarea_to_manaco() {
  125. window.editor.setValue(document.getElementById('opennamu_edit_textarea').value);
  126. }
  127. function get_select_editor() {
  128. if(document.getElementById('opennamu_monaco_editor').style.display === 'none') {
  129. return 'textarea'
  130. } else {
  131. return 'monaco'
  132. }
  133. }
  134. function do_sync_monaco_and_textarea(select = '') {
  135. if(select === 'textarea_to_monaco' || get_select_editor() === 'textarea') {
  136. do_textarea_to_manaco();
  137. } else {
  138. do_monaco_to_textarea();
  139. }
  140. }
  141. // https://github.com/microsoft/monaco-editor/issues/568
  142. class PlaceholderContentWidget {
  143. static ID = 'editor.widget.placeholderHint';
  144. constructor(placeholder, editor) {
  145. this.placeholder = placeholder;
  146. this.editor = editor;
  147. editor.onDidChangeModelContent(() => this.onDidChangeModelContent());
  148. this.onDidChangeModelContent();
  149. }
  150. onDidChangeModelContent() {
  151. if(this.editor.getValue() === '') {
  152. this.editor.addContentWidget(this);
  153. } else {
  154. this.editor.removeContentWidget(this);
  155. }
  156. }
  157. getId() {
  158. return PlaceholderContentWidget.ID;
  159. }
  160. getDomNode() {
  161. if(!this.domNode) {
  162. this.domNode = document.createElement('div');
  163. this.domNode.style.width = 'max-content';
  164. this.domNode.textContent = this.placeholder;
  165. this.domNode.style.fontStyle = 'italic';
  166. this.editor.applyFontInfo(this.domNode);
  167. }
  168. return this.domNode;
  169. }
  170. getPosition() {
  171. return {
  172. position: { lineNumber: 1, column: 1 },
  173. preference: [monaco.editor.ContentWidgetPositionPreference.EXACT],
  174. };
  175. }
  176. dispose() {
  177. this.editor.removeContentWidget(this);
  178. }
  179. }
  180. function do_monaco_init(monaco_thema, markup = "") {
  181. require.config({ paths: { 'vs' : 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.40.0/min/vs' }});
  182. require.config({ 'vs/nls' : { availableLanguages: { '*' : 'ko' } }});
  183. require(["vs/editor/editor.main"], function () {
  184. monaco.languages.register({ id : "namumark" });
  185. monaco.languages.setMonarchTokensProvider("namumark", {
  186. tokenizer : {
  187. root : [
  188. [/\[/, "namumark-color"],
  189. [/\]/, "namumark-color"],
  190. [/\{/, "namumark-color"],
  191. [/\}/, "namumark-color"],
  192. [/'/, "namumark-color"],
  193. [/-/, "namumark-color"],
  194. [/~/, "namumark-color"],
  195. [/=/, "namumark-color"],
  196. [/_/, "namumark-color"],
  197. [/\^/, "namumark-color"],
  198. [/,/, "namumark-color"],
  199. [/\\/, "namumark-color"],
  200. [/\*/, "namumark-color"],
  201. ],
  202. },
  203. });
  204. let thema_set = [["namumark", "vs"], ["namumark-vs-dark", "vs-dark"]]
  205. for(let for_a = 0; for_a < thema_set.length; for_a++) {
  206. monaco.editor.defineTheme(thema_set[for_a][0], {
  207. base : thema_set[for_a][1],
  208. inherit : true,
  209. rules : [
  210. { token : "namumark-color", foreground : "d94844" },
  211. ],
  212. colors : {},
  213. });
  214. }
  215. window.editor = monaco.editor.create(document.getElementById('opennamu_monaco_editor'), {
  216. value : document.getElementById('opennamu_edit_textarea').value,
  217. language : 'namumark',
  218. automaticLayout : true,
  219. wordWrap : true,
  220. theme : "namumark" + (monaco_thema === "" ? "" : "-" + monaco_thema)
  221. });
  222. new PlaceholderContentWidget(document.getElementById('opennamu_edit_textarea').placeholder, window.editor);
  223. });
  224. }
  225. function opennamu_do_editor_preview() {
  226. do_sync_monaco_and_textarea();
  227. const input = document.querySelector('#opennamu_edit_textarea');
  228. if(input !== null) {
  229. let name = "test";
  230. if(document.getElementById('opennamu_editor_doc_name')) {
  231. name = document.getElementById('opennamu_editor_doc_name').value.replace(/&amp;/g, '&');
  232. }
  233. opennamu_do_render('opennamu_preview_area', input.value, name);
  234. }
  235. }
  236. function opennamu_do_editor_temp_save() {
  237. do_sync_monaco_and_textarea();
  238. const input = document.querySelector('#opennamu_edit_textarea');
  239. if(input !== null) {
  240. localStorage.setItem("key", input.value);
  241. }
  242. }
  243. function opennamu_do_editor_temp_save_load() {
  244. const data = localStorage.getItem("key");
  245. if(data !== null) {
  246. const input = document.querySelector('#opennamu_edit_textarea');
  247. if(input !== null) {
  248. input.value = data;
  249. }
  250. do_textarea_to_manaco();
  251. }
  252. }