editor.js 12 KB

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