blog-editor.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. function blog_editor_init(name, editor_config) {
  2. if (editor_config === undefined) {
  3. editor_config = {};
  4. }
  5. editor_config = $.extend({
  6. type: 'blog'
  7. }, editor_config);
  8. var input_title = $("#input-" + name + "_title");
  9. var input_tags = $("#input-" + name + "_tags");
  10. var input_content_md = $("#input-" + name + "_content_md");
  11. var input_is_hidden = $("#input-" + name + "_is_hidden");
  12. var this_form = input_content_md[0].form;
  13. var is_saved;
  14. var last_save_done = true;
  15. // init buttons
  16. var save_btn = $('<button type="button" class="btn btn-sm"></button>');
  17. var preview_btn = $('<button type="button" class="btn btn-secondary btn-sm"><span class="glyphicon glyphicon-eye-open"></span></button>');
  18. var bold_btn = $('<button type="button" class="btn btn-secondary btn-sm ml-2"><span class="glyphicon glyphicon-bold"></span></button>');
  19. var italic_btn = $('<button type="button" class="btn btn-secondary btn-sm"><span class="glyphicon glyphicon-italic"></span></button>');
  20. save_btn.tooltip({ container: 'body', title: '保存 (Ctrl-S)' });
  21. preview_btn.tooltip({ container: 'body', title: '预览 (Ctrl-D)' });
  22. bold_btn.tooltip({ container: 'body', title: '粗体 (Ctrl-B)' });
  23. italic_btn.tooltip({ container: 'body', title: '斜体 (Ctrl-I)' });
  24. var all_btn = [save_btn, preview_btn, bold_btn, italic_btn];
  25. // init toolbar
  26. var toolbar = $('<div class="btn-toolbar"></div>');
  27. toolbar.append($('<div class="btn-group"></div>')
  28. .append(save_btn)
  29. .append(preview_btn)
  30. );
  31. toolbar.append($('<div class="btn-group"></div>')
  32. .append(bold_btn)
  33. .append(italic_btn)
  34. );
  35. function set_saved(val) {
  36. is_saved = val;
  37. if (val) {
  38. save_btn.removeClass('btn-warning');
  39. save_btn.addClass('btn-success');
  40. save_btn.html('<span class="glyphicon glyphicon-saved"></span>');
  41. before_window_unload_message = null;
  42. } else {
  43. save_btn.removeClass('btn-success');
  44. save_btn.addClass('btn-warning');
  45. save_btn.html('<span class="glyphicon glyphicon-save"></span>');
  46. before_window_unload_message = '您所编辑的内容尚未保存';
  47. }
  48. }
  49. function set_preview_status(status) {
  50. // 0: normal
  51. // 1: loading
  52. // 2: loaded
  53. if (status == 0) {
  54. preview_btn.removeClass('active');
  55. for (var i = 0; i < all_btn.length; i++) {
  56. if (all_btn[i] != preview_btn) {
  57. all_btn[i].prop('disabled', false);
  58. }
  59. }
  60. } else if (status == 1) {
  61. for (var i = 0; i < all_btn.length; i++) {
  62. if (all_btn[i] != preview_btn) {
  63. all_btn[i].prop('disabled', true);
  64. }
  65. }
  66. preview_btn.addClass('active');
  67. }
  68. }
  69. set_saved(true);
  70. // init codemirror
  71. input_content_md.wrap('<div class="blog-content-md-editor"></div>');
  72. var blog_contend_md_editor = input_content_md.parent();
  73. input_content_md.before($('<div class="blog-content-md-editor-toolbar"></div>')
  74. .append(toolbar)
  75. );
  76. input_content_md.wrap('<div class="blog-content-md-editor-in"></div>');
  77. var codeeditor;
  78. if (editor_config.type == 'blog') {
  79. codeeditor = CodeMirror.fromTextArea(input_content_md[0], {
  80. mode: 'gfm',
  81. lineNumbers: true,
  82. matchBrackets: true,
  83. lineWrapping: true,
  84. styleActiveLine: true,
  85. theme: 'default'
  86. });
  87. } else if (editor_config.type == 'slide') {
  88. codeeditor = CodeMirror.fromTextArea(input_content_md[0], {
  89. mode: 'plain',
  90. lineNumbers: true,
  91. matchBrackets: true,
  92. lineWrapping: true,
  93. styleActiveLine: true,
  94. theme: 'default'
  95. });
  96. }
  97. function preview(html) {
  98. var iframe = $('<iframe frameborder="0"></iframe>');
  99. blog_contend_md_editor.append(
  100. $('<div class="blog-content-md-editor-preview" style="display: none;"></div>')
  101. .append(iframe)
  102. );
  103. var iframe_document = iframe[0].contentWindow.document;
  104. iframe_document.open();
  105. iframe_document.write(html);
  106. iframe_document.close();
  107. $(iframe_document).bind('keydown', 'ctrl+d', function() {
  108. preview_btn.click();
  109. return false;
  110. });
  111. blog_contend_md_editor.find('.blog-content-md-editor-in').slideUp('fast');
  112. blog_contend_md_editor.find('.blog-content-md-editor-preview').slideDown('fast', function() {
  113. set_preview_status(2);
  114. iframe.focus();
  115. iframe.find('body').focus();
  116. });
  117. }
  118. function save(config) {
  119. if (config == undefined) {
  120. config = {};
  121. }
  122. config = $.extend({
  123. need_preview: false,
  124. fail: function() {
  125. },
  126. done: function() {
  127. }
  128. }, config);
  129. if (!last_save_done) {
  130. config.fail();
  131. config.done();
  132. return;
  133. }
  134. last_save_done = false;
  135. if (config.need_preview) {
  136. set_preview_status(1);
  137. }
  138. var post_data = {};
  139. $($(this_form).serializeArray()).each(function() {
  140. post_data[this["name"]] = this["value"];
  141. });
  142. if (config.need_preview) {
  143. post_data['need_preview'] = 'on';
  144. }
  145. post_data["save-" + name] = '';
  146. $.ajax({
  147. type : 'POST',
  148. data : post_data,
  149. url : window.location.href,
  150. success : function(data) {
  151. try {
  152. data = JSON.parse(data)
  153. } catch (e) {
  154. alert(data);
  155. if (config.need_preview) {
  156. set_preview_status(0);
  157. }
  158. config.fail();
  159. return;
  160. }
  161. var ok = true;
  162. $(['title', 'content_md', 'tags']).each(function() {
  163. ok &= showErrorHelp(name + '_' + this, data[this]);
  164. });
  165. if (data.extra !== undefined) {
  166. alert(data.extra);
  167. ok = false;
  168. }
  169. if (!ok) {
  170. if (config.need_preview) {
  171. set_preview_status(0);
  172. }
  173. config.fail();
  174. return;
  175. }
  176. set_saved(true);
  177. if (config.need_preview) {
  178. preview(data.html);
  179. }
  180. if (data.blog_write_url) {
  181. window.history.replaceState({}, document.title, data.blog_write_url);
  182. }
  183. if (data.blog_url) {
  184. $('#a-' + name + '_view_blog').attr('href', data.blog_url).show();
  185. }
  186. }
  187. }).fail(function() {
  188. if (config.need_preview) {
  189. set_preview_status(0);
  190. }
  191. config.fail();
  192. }).always(function() {
  193. last_save_done = true;
  194. config.done();
  195. });
  196. }
  197. function add_around(sl, sr) {
  198. codeeditor.replaceSelection(sl + codeeditor.getSelection() + sr);
  199. }
  200. // event
  201. codeeditor.on('change', function() {
  202. codeeditor.save();
  203. set_saved(false);
  204. });
  205. $.merge(input_title, input_tags).on('input', function() {
  206. set_saved(false);
  207. });
  208. save_btn.click(function() {
  209. save();
  210. });
  211. preview_btn.click(function() {
  212. if (preview_btn.hasClass('active')) {
  213. set_preview_status(0);
  214. blog_contend_md_editor.find('.blog-content-md-editor-in').slideDown('fast');
  215. blog_contend_md_editor.find('.blog-content-md-editor-preview').slideUp('fast', function() {
  216. $(this).remove();
  217. });
  218. codeeditor.focus();
  219. } else {
  220. save({need_preview: true});
  221. }
  222. });
  223. bold_btn.click(function() {
  224. add_around("**", "**");
  225. codeeditor.focus();
  226. });
  227. italic_btn.click(function() {
  228. add_around("*", "*");
  229. codeeditor.focus();
  230. });
  231. input_is_hidden.on('switchChange.bootstrapSwitch', function(e, state) {
  232. var ok = true;
  233. if (!state && !confirm("你确定要公开吗?")) {
  234. ok = false;
  235. }
  236. if (!ok) {
  237. input_is_hidden.bootstrapSwitch('toggleState', true);
  238. } else {
  239. input_is_hidden.bootstrapSwitch('readonly', true);
  240. var succ = true;
  241. save({
  242. fail: function() {
  243. succ = false;
  244. },
  245. done: function() {
  246. input_is_hidden.bootstrapSwitch('readonly', false);
  247. if (!succ) {
  248. input_is_hidden.bootstrapSwitch('toggleState', true);
  249. }
  250. }
  251. });
  252. }
  253. });
  254. // init hot keys
  255. codeeditor.setOption("extraKeys", {
  256. "Ctrl-S": function(cm) {
  257. save_btn.click();
  258. },
  259. "Ctrl-B": function(cm) {
  260. bold_btn.click();
  261. },
  262. "Ctrl-D": function(cm) {
  263. preview_btn.click();
  264. },
  265. "Ctrl-I": function(cm) {
  266. italic_btn.click();
  267. }
  268. });
  269. $(document).bind('keydown', 'ctrl+d', function() {
  270. preview_btn.click();
  271. return false;
  272. });
  273. $.merge(input_title, input_tags).bind('keydown', 'ctrl+s', function() {
  274. save_btn.click();
  275. return false;
  276. });
  277. if (this_form) {
  278. $(this_form).submit(function() {
  279. before_window_unload_message = null;
  280. });
  281. }
  282. }