editor.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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. document.getElementById('opennamu_edit_textarea').addEventListener("paste", pasteListener);
  37. }
  38. function pasteListener(e) {
  39. let lang_data = new FormData();
  40. lang_data.append('data', 'file_name empty save authority_error same_file_error error');
  41. if(e.clipboardData && e.clipboardData.items) {
  42. const items = e.clipboardData.items;
  43. const formData = new FormData();
  44. let haveImageInClipboard = false;
  45. let file_name = '';
  46. let file;
  47. for(let i = 0; i < items.length; i++) {
  48. if(items[i].type.indexOf("image") !== -1) {
  49. file = items[i].getAsFile();
  50. haveImageInClipboard = true;
  51. e.preventDefault();
  52. break;
  53. }
  54. }
  55. if(!haveImageInClipboard) {
  56. return;
  57. }
  58. fetch('/api/lang', {
  59. method : 'post',
  60. body : lang_data,
  61. }).then(function(res) {
  62. return res.json();
  63. }).then(function(ajax_data) {
  64. const customName = prompt(ajax_data['data'][0]);
  65. if(!customName) {
  66. return alert(ajax_data['data'][1]);
  67. }
  68. file_name = customName + ".png";
  69. const customFile = new File([file], file_name, { type: file.type });
  70. formData.append("f_data[]", customFile);
  71. fetch("/upload", {
  72. method: "POST",
  73. body: formData,
  74. }).then((res) => {
  75. if (res.status === 200 || res.status === 201) {
  76. const url = res.url;
  77. alert(ajax_data['data'][2] + ' : [[file:' + file_name + ']]');
  78. do_insert_data('[[file:' + file_name + ']]');
  79. } else {
  80. console.error("[ERROR] PasteUpload Fail :", res.statusText);
  81. if(res.status === 400) {
  82. alert(ajax_data['data'][4]);
  83. } else if(res.status === 401) {
  84. alert(ajax_data['data'][3]);
  85. } else {
  86. alert(ajax_data['data'][5]);
  87. }
  88. }
  89. }).catch((err) => {
  90. console.error("[ERROR] PasteUpload Fail :", JSON.stringify(err), err);
  91. alert(ajax_data['data'][5]);
  92. });
  93. });
  94. }
  95. }
  96. function do_stop_exit() {
  97. window.onbeforeunload = function() {
  98. do_sync_monaco_and_textarea();
  99. let data = document.getElementById('opennamu_edit_textarea').value;
  100. let origin = document.getElementById('opennamu_edit_origin').value;
  101. if(data !== origin) {
  102. return '';
  103. }
  104. }
  105. }
  106. function do_stop_exit_release() {
  107. do_sync_monaco_and_textarea();
  108. window.onbeforeunload = function () {}
  109. }
  110. function opennamu_edit_turn_off_monaco() {
  111. let now_selected = get_select_editor();
  112. let editor_list = [
  113. ['opennamu_edit_textarea', 'none'],
  114. ['opennamu_monaco_editor', 'none']
  115. ];
  116. if(now_selected === 'textarea') {
  117. editor_list[0][1] = 'block';
  118. } else if(now_selected === 'monaco') {
  119. editor_list[1][1] = 'block';
  120. } else {
  121. }
  122. for(let for_a = 0; for_a < editor_list.length; for_a++) {
  123. document.getElementById(editor_list[for_a][0]).style.display = editor_list[for_a][1];
  124. }
  125. }
  126. function do_monaco_to_textarea(set_value) {
  127. document.getElementById('opennamu_edit_textarea').value = set_value;
  128. }
  129. function do_textarea_to_manaco(set_value) {
  130. window.editor.setValue(set_value);
  131. }
  132. function get_select_editor() {
  133. let now_selected = document.getElementById("opennamu_select_editor").value;
  134. if(now_selected === 'default') {
  135. return 'textarea';
  136. } else if(now_selected === 'monaco') {
  137. return 'monaco';
  138. } else {
  139. return '';
  140. }
  141. }
  142. function do_sync_monaco_and_textarea(select = '') {
  143. let now_selected = get_select_editor();
  144. if(select === 'textarea_to' || now_selected === 'textarea') {
  145. let set_value = document.getElementById('opennamu_edit_textarea').value;
  146. do_textarea_to_manaco(set_value);
  147. } else if(now_selected === 'monaco') {
  148. let set_value = window.editor.getValue();
  149. do_monaco_to_textarea(set_value);
  150. } else {
  151. }
  152. }
  153. // https://github.com/microsoft/monaco-editor/issues/568
  154. class PlaceholderContentWidget {
  155. static ID = 'editor.widget.placeholderHint';
  156. constructor(placeholder, editor) {
  157. this.placeholder = placeholder;
  158. this.editor = editor;
  159. editor.onDidChangeModelContent(() => this.onDidChangeModelContent());
  160. this.onDidChangeModelContent();
  161. }
  162. onDidChangeModelContent() {
  163. if(this.editor.getValue() === '') {
  164. this.editor.addContentWidget(this);
  165. } else {
  166. this.editor.removeContentWidget(this);
  167. }
  168. }
  169. getId() {
  170. return PlaceholderContentWidget.ID;
  171. }
  172. getDomNode() {
  173. if(!this.domNode) {
  174. this.domNode = document.createElement('div');
  175. this.domNode.style.width = 'max-content';
  176. this.domNode.textContent = this.placeholder;
  177. this.domNode.style.fontStyle = 'italic';
  178. this.editor.applyFontInfo(this.domNode);
  179. }
  180. return this.domNode;
  181. }
  182. getPosition() {
  183. return {
  184. position: { lineNumber: 1, column: 1 },
  185. preference: [monaco.editor.ContentWidgetPositionPreference.EXACT],
  186. };
  187. }
  188. dispose() {
  189. this.editor.removeContentWidget(this);
  190. }
  191. }
  192. function do_monaco_init(monaco_thema, markup = "") {
  193. require.config({ paths: { 'vs' : 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.48.0/min/vs' }});
  194. require.config({ 'vs/nls' : { availableLanguages: { '*' : 'ko' } }});
  195. require(["vs/editor/editor.main"], function () {
  196. monaco.languages.register({ id : "namumark" });
  197. monaco.languages.setMonarchTokensProvider("namumark", {
  198. tokenizer : {
  199. root : [
  200. [/\[/, "namumark-color"],
  201. [/\]/, "namumark-color"],
  202. [/\{/, "namumark-color"],
  203. [/\}/, "namumark-color"],
  204. [/'/, "namumark-color"],
  205. [/-/, "namumark-color"],
  206. [/~/, "namumark-color"],
  207. [/=/, "namumark-color"],
  208. [/_/, "namumark-color"],
  209. [/\^/, "namumark-color"],
  210. [/,/, "namumark-color"],
  211. [/\\/, "namumark-color"],
  212. [/\*/, "namumark-color"],
  213. ],
  214. },
  215. });
  216. let thema_set = [["namumark", "vs"], ["namumark-vs-dark", "vs-dark"]]
  217. for(let for_a = 0; for_a < thema_set.length; for_a++) {
  218. monaco.editor.defineTheme(thema_set[for_a][0], {
  219. base : thema_set[for_a][1],
  220. inherit : true,
  221. rules : [
  222. { token : "namumark-color", foreground : "d94844" },
  223. ],
  224. colors : {},
  225. });
  226. }
  227. window.editor = monaco.editor.create(document.getElementById('opennamu_monaco_editor'), {
  228. value : document.getElementById('opennamu_edit_textarea').value,
  229. language : 'namumark',
  230. automaticLayout : true,
  231. wordWrap : true,
  232. theme : "namumark" + (monaco_thema === "" ? "" : "-" + monaco_thema)
  233. });
  234. new PlaceholderContentWidget(document.getElementById('opennamu_edit_textarea').placeholder, window.editor);
  235. });
  236. }
  237. function opennamu_do_editor_preview() {
  238. do_sync_monaco_and_textarea();
  239. const input = document.querySelector('#opennamu_edit_textarea');
  240. if(input !== null) {
  241. let name = "test";
  242. if(document.getElementById('opennamu_editor_doc_name')) {
  243. name = opennamu_xss_filter_decode(document.getElementById('opennamu_editor_doc_name').value);
  244. }
  245. opennamu_do_render('opennamu_preview_area', input.value, name);
  246. }
  247. }
  248. function opennamu_do_editor_temp_save() {
  249. do_sync_monaco_and_textarea();
  250. const input = document.querySelector('#opennamu_edit_textarea');
  251. if(input !== null) {
  252. localStorage.setItem("key", input.value);
  253. }
  254. }
  255. function opennamu_do_editor_temp_save_load() {
  256. const data = localStorage.getItem("key");
  257. if(data !== null) {
  258. const input = document.querySelector('#opennamu_edit_textarea');
  259. if(input !== null) {
  260. input.value = data;
  261. }
  262. do_sync_monaco_and_textarea('textarea_to');
  263. }
  264. }
  265. function opennamu_do_user_editor_insert() {
  266. let data = prompt();
  267. if(data !== null && data !== "") {
  268. let form_data = new FormData();
  269. form_data.append('data', data);
  270. fetch('/api/v2/user/setting/editor', {
  271. method : 'post',
  272. body : form_data,
  273. }).then(function() {
  274. opennnamu_do_user_editor();
  275. });
  276. }
  277. }
  278. function opennamu_do_user_editor_delete() {
  279. let data = prompt();
  280. if(data !== null && data !== "") {
  281. let form_data = new FormData();
  282. form_data.append('data', data);
  283. fetch('/api/v2/user/setting/editor', {
  284. method : 'delete',
  285. body : form_data,
  286. }).then(function() {
  287. opennnamu_do_user_editor();
  288. });
  289. }
  290. }
  291. function opennnamu_do_user_editor() {
  292. fetch('/api/v2/user/setting/editor').then(function(res) {
  293. return res.json();
  294. }).then(function(data) {
  295. if(data["response"] === "ok") {
  296. let data_html = '';
  297. for(let for_a = 0; for_a < data["data"].length; for_a++) {
  298. data_html += '<a href="javascript:do_insert_data(\'' + opennamu_xss_filter(data["data"][for_a]) + '\');">(' + opennamu_xss_filter(data["data"][for_a]) + ')</a> ';
  299. }
  300. data_html += '<a href="javascript:opennamu_do_user_editor_insert();">(+)</a> ';
  301. data_html += '<a href="javascript:opennamu_do_user_editor_delete();">(-)</a>';
  302. data_html += '<hr class="main_hr">';
  303. document.getElementById("opennamu_editor_user_button").innerHTML = data_html;
  304. }
  305. });
  306. }