froala-editor.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. $(function () {
  2. //超大屏幕
  3. var toolbarButtons = ['fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'fontFamily', 'fontSize', '|', 'color', 'emoticons', 'inlineStyle', 'paragraphStyle', '|', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', '-', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting', 'selectAll', 'html'];
  4. //大屏幕
  5. var toolbarButtonsMD = ['fullscreen', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'color', 'paragraphStyle', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting'];
  6. //小屏幕
  7. var toolbarButtonsSM = ['fullscreen', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'insertLink', 'insertImage', 'insertTable', 'undo', 'redo'];
  8. //手机
  9. var toolbarButtonsXS = ['bold', 'italic', 'fontFamily', 'fontSize', 'undo', 'redo'];
  10. window.addDocumentModalFormHtml = $(this).find("form").html();
  11. window.editor = new FroalaEditor("#froalaEditor", {
  12. // enter: $.FroalaEditor.ENTER_P,
  13. placeholderText: '请输入内容',
  14. charCounterCount: true, //默认
  15. // charCounterMax : -1,//默认
  16. saveInterval: 0, //不自动保存,默认10000
  17. // theme : "red",
  18. height: "100%",
  19. toolbarBottom: false, //默认
  20. toolbarButtonsMD: toolbarButtonsMD,
  21. toolbarButtonsSM: toolbarButtonsSM,
  22. toolbarButtonsXS: toolbarButtonsXS,
  23. toolbarInline: false, //true选中设置样式,默认false
  24. imageUploadMethod: 'POST',
  25. // heightMin: 450,
  26. charCounterMax: 3000,
  27. // imageUploadURL: "uploadImgEditor",
  28. // imageParams: { postId: "123" },
  29. params: {
  30. // acl: '01',
  31. // AWSAccessKeyId: '02',
  32. // policy: '03',
  33. // signature: '04',
  34. editor : "froalaEditor",
  35. },
  36. autosave: true,
  37. autosaveInterval: 2500,
  38. // saveURL: 'hander/FroalaHandler.ashx',
  39. saveParams: { postId: '1' },
  40. spellcheck: false,
  41. imageUploadURL: window.imageUploadURL, //'/uploadimg', //上传到本地服务器
  42. // imageUploadParams: { pid: '{{.product.ProjectId}}' },
  43. // imageDeleteURL: 'lib/delete_image.php', //删除图片
  44. // imagesLoadURL: 'lib/load_images.php', //管理图片
  45. videoUploadURL: window.imageUploadURL,
  46. // videoUploadParams: { pid: '{{.product.ProjectId}}' },
  47. fileUploadURL: window.imageUploadURL,
  48. // fileUploadParams: { pid: '{{.product.ProjectId}}' },
  49. // enter: $.FroalaEditor.ENTER_BR,
  50. language: 'zh_cn',
  51. // Add the custom buttons in the toolbarButtons list, after the separator.
  52. toolbarButtons: [toolbarButtons, ['saveIcon', 'insert','alert']]
  53. // toolbarButtons: ['bold', 'italic', 'underline', 'paragraphFormat', 'align','color','fontSize','insertImage','insertTable','undo', 'redo']
  54. });
  55. FroalaEditor.DefineIcon('alert', {NAME: 'info', SVG_KEY: 'user'});
  56. FroalaEditor.RegisterCommand('alert', {
  57. title: 'Hello',
  58. focus: false,
  59. undo: false,
  60. refreshAfterCallback: false,
  61. callback: function () {
  62. alert('Hello!');
  63. }
  64. });
  65. // FroalaEditor.DefineIcon('magicIcon', {NAME: 'magic'});
  66. FroalaEditor.DefineIcon('saveIcon', {NAME: 'plus', SVG_KEY: 'cogs'});
  67. FroalaEditor.RegisterCommand('saveIcon', {
  68. title: '保存',
  69. focus: false,
  70. undo: true,
  71. refreshAfterCallback: true,
  72. callback: function () {
  73. saveDocument();
  74. }
  75. });
  76. FroalaEditor.DefineIcon('insert', {NAME: 'plus', SVG_KEY: 'add'});
  77. FroalaEditor.RegisterCommand('insert', {
  78. title: '发布',
  79. focus: true,
  80. undo: true,
  81. refreshAfterCallback: true,
  82. callback: function () {
  83. // this.html.insert('My New HTML');
  84. // saveDocument(true,callback);
  85. releaseBook();
  86. }
  87. });
  88. // new FroalaEditor('div#froala-editor', {
  89. // // Add the custom buttons in the toolbarButtons list, after the separator.
  90. // toolbarButtons: [['undo', 'redo' , 'bold'], ['alert', 'clear', 'insert']]
  91. // })
  92. // editor.config.mapAk = window.baiduMapKey;
  93. // editor.config.printLog = false;
  94. // editor.config.showMenuTooltips = true
  95. // editor.config.menuTooltipPosition = 'down'
  96. // editor.config.uploadImgUrl = window.imageUploadURL;
  97. // editor.config.uploadImgFileName = "editormd-image-file";
  98. // editor.config.uploadParams = {
  99. // "editor" : "froalaEditor"
  100. // };
  101. // editor.config.uploadImgServer = window.imageUploadURL;
  102. // editor.config.customUploadImg = function (resultFiles, insertImgFn) {
  103. // // resultFiles 是 input 中选中的文件列表
  104. // // insertImgFn 是获取图片 url 后,插入到编辑器的方法
  105. // var file = resultFiles[0];
  106. // // file type is only image.
  107. // if (/^image\//.test(file.type)) {
  108. // var form = new FormData();
  109. // form.append('editormd-image-file', file, file.name);
  110. // var layerIndex = 0;
  111. // $.ajax({
  112. // url: window.imageUploadURL,
  113. // type: "POST",
  114. // dataType: "json",
  115. // data: form,
  116. // processData: false,
  117. // contentType: false,
  118. // error: function() {
  119. // layer.close(layerIndex);
  120. // layer.msg("图片上传失败");
  121. // },
  122. // success: function(data) {
  123. // layer.close(layerIndex);
  124. // if(data.errcode !== 0){
  125. // layer.msg(data.message);
  126. // }else{
  127. // insertImgFn(data.url);
  128. // }
  129. // }
  130. // });
  131. // } else {
  132. // console.warn('You could only upload images.');
  133. // }
  134. // };
  135. // editor.config.lang = window.lang;
  136. // editor.i18next = window.i18next;
  137. // editor.config.languages['en']['froalaEditor']['menus']['title']['保存'] = 'save';
  138. // editor.config.languages['en']['froalaEditor']['menus']['title']['发布'] = 'publish';
  139. // editor.config.languages['en']['froalaEditor']['menus']['title']['附件'] = 'attachment';
  140. // editor.config.languages['en']['froalaEditor']['menus']['title']['history'] = 'history';
  141. window.editormdLocales = {
  142. 'zh-CN': {
  143. placeholder: '本编辑器支持 Markdown 编辑,左边编写,右边预览。',
  144. contentUnsaved: '编辑内容未保存,需要保存吗?',
  145. noDocNeedPublish: '没有需要发布的文档',
  146. loadDocFailed: '文档加载失败',
  147. fetchDocFailed: '获取当前文档信息失败',
  148. cannotAddToEmptyNode: '空节点不能添加内容',
  149. overrideModified: '文档已被其他人修改确定覆盖已存在的文档吗?',
  150. confirm: '确定',
  151. cancel: '取消',
  152. contentsNameEmpty: '目录名称不能为空',
  153. addDoc: '添加文档',
  154. edit: '编辑',
  155. delete: '删除',
  156. loadFailed: '加载失败请重试',
  157. tplNameEmpty: '模板名称不能为空',
  158. tplContentEmpty: '模板内容不能为空',
  159. saveSucc: '保存成功',
  160. serverExcept: '服务器异常',
  161. paramName: '参数名称',
  162. paramType: '参数类型',
  163. example: '示例值',
  164. remark: '备注',
  165. },
  166. 'en': {
  167. placeholder: 'This editor supports Markdown editing, writing on the left and previewing on the right.',
  168. contentUnsaved: 'The edited content is not saved, need to save it?',
  169. noDocNeedPublish: 'No Document need to be publish',
  170. loadDocFailed: 'Load Document failed',
  171. fetchDocFailed: 'Fetch Document info failed',
  172. cannotAddToEmptyNode: 'Cannot add content to empty node',
  173. overrideModified: 'The document has been modified by someone else, are you sure to overwrite the document?',
  174. confirm: 'Confirm',
  175. cancel: 'Cancel',
  176. contentsNameEmpty: 'Document Name cannot be empty',
  177. addDoc: 'Add Document',
  178. edit: 'Edit',
  179. delete: 'Delete',
  180. loadFailed: 'Failed to load, please try again',
  181. tplNameEmpty: 'Template name cannot be empty',
  182. tplContentEmpty: 'Template content cannot be empty',
  183. saveSucc: 'Save success',
  184. serverExcept: 'Server Exception',
  185. paramName: 'Parameter',
  186. paramType: 'Type',
  187. example: 'Example',
  188. remark: 'Remark',
  189. }
  190. };
  191. // window.editor.config.onchange = function (newHtml) {
  192. // var saveMenu = window.editor.menus.menuList.find((item) => item.key == 'save');
  193. // // 判断内容是否改变
  194. // if (window.source !== window.editor.txt.html()) {
  195. // saveMenu.$elem.addClass('selected');
  196. // } else {
  197. // saveMenu.$elem.removeClass('selected');
  198. // }
  199. // };
  200. // window.editor.create();
  201. // $("#froalaEditor").css("height","100%");
  202. if(window.documentCategory.length > 0){
  203. var item = window.documentCategory[0];
  204. var $select_node = { node : {id : item.id}};
  205. loadDocument($select_node);
  206. }
  207. /***
  208. * 加载指定的文档到编辑器中
  209. * @param $node
  210. */
  211. function loadDocument($node) {
  212. var index = layer.load(1, {
  213. shade: [0.1,'#fff'] //0.1透明度的白色背景
  214. });
  215. $.get(window.editURL + $node.node.id ).done(function (res) {
  216. layer.close(index);
  217. if(res.errcode === 0){
  218. window.isLoad = true;
  219. // window.editor.txt.clear();
  220. // window.editor.txt.html(res.data.content);
  221. window.editor.html.set('');
  222. window.editor.events.focus();
  223. window.editor.html.set(res.data.content);
  224. // 将原始内容备份
  225. window.source = res.data.content;
  226. var node = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version};
  227. pushDocumentCategory(node);
  228. window.selectNode = node;
  229. pushVueLists(res.data.attach);
  230. }else{
  231. layer.msg("文档加载失败");
  232. }
  233. }).fail(function () {
  234. layer.close(index);
  235. layer.msg("文档加载失败");
  236. });
  237. }
  238. /**
  239. * 保存文档到服务器
  240. * @param $is_cover 是否强制覆盖
  241. */
  242. function saveDocument($is_cover,callback) {
  243. var index = null;
  244. var node = window.selectNode;
  245. // var html = window.editor.txt.html() ;
  246. var html = window.editor.html.get();
  247. var content = "";
  248. if($.trim(html) !== ""){
  249. content = toMarkdown(html, { gfm: true });
  250. }
  251. var version = "";
  252. if (!node) {
  253. layer.msg(editormdLocales[lang].fetchDocFailed);
  254. return;
  255. }
  256. var doc_id = parseInt(node.id);
  257. for(var i in window.documentCategory){
  258. var item = window.documentCategory[i];
  259. if(item.id === doc_id){
  260. version = item.version;
  261. break;
  262. }
  263. }
  264. $.ajax({
  265. beforeSend : function () {
  266. index = layer.load(1, {shade: [0.1,'#fff'] });
  267. },
  268. url : window.editURL,
  269. data : {"identify" : window.book.identify,"doc_id" : doc_id,"markdown" : content,"html" : html,"cover" : $is_cover ? "yes":"no","version": version},
  270. type :"post",
  271. dataType :"json",
  272. success : function (res) {
  273. layer.close(index);
  274. if(res.errcode === 0){
  275. for(var i in window.documentCategory){
  276. var item = window.documentCategory[i];
  277. if(item.id === doc_id){
  278. window.documentCategory[i].version = res.data.version;
  279. break;
  280. }
  281. }
  282. // 更新内容备份
  283. window.source = res.data.content;
  284. // 触发编辑器 onchange 回调函数
  285. // window.editor.config.onchange();
  286. // window.editor.onchange();
  287. // window.editor.events: {
  288. // 'contentChanged': function () {
  289. // // Do something here.
  290. // // this is the editor instance.
  291. // console.log(this);
  292. // }
  293. // }
  294. if(typeof callback === "function"){
  295. callback();
  296. }
  297. }else if(res.errcode === 6005){
  298. var confirmIndex = layer.confirm(editormdLocales[lang].overrideModified, {
  299. btn: [editormdLocales[lang].confirm, editormdLocales[lang].cancel] // 按钮
  300. }, function () {
  301. layer.close(confirmIndex);
  302. saveDocument(true,callback);
  303. });
  304. }else{
  305. layer.msg(res.message);
  306. }
  307. }
  308. });
  309. }
  310. /**
  311. * 添加顶级文档
  312. */
  313. $("#addDocumentForm").ajaxForm({
  314. beforeSubmit : function () {
  315. var doc_name = $.trim($("#documentName").val());
  316. if (doc_name === ""){
  317. return showError("目录名称不能为空","#add-error-message")
  318. }
  319. window.addDocumentFormIndex = layer.load(1, { shade: [0.1,'#fff'] });
  320. return true;
  321. },
  322. success : function (res) {
  323. if(res.errcode === 0){
  324. var data = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version};
  325. var node = window.treeCatalog.get_node(data.id);
  326. if(node){
  327. window.treeCatalog.rename_node({"id":data.id},data.text);
  328. }else {
  329. window.treeCatalog.create_node(data.parent, data);
  330. window.treeCatalog.deselect_all();
  331. window.treeCatalog.select_node(data);
  332. }
  333. pushDocumentCategory(data);
  334. $("#markdown-save").removeClass('change').addClass('disabled');
  335. $("#addDocumentModal").modal('hide');
  336. }else{
  337. showError(res.message,"#add-error-message")
  338. }
  339. layer.close(window.addDocumentFormIndex);
  340. }
  341. });
  342. /**
  343. * 文档目录树
  344. */
  345. $("#sidebar").jstree({
  346. 'plugins': ["wholerow", "types", 'dnd', 'contextmenu'],
  347. "types": {
  348. "default": {
  349. "icon": false // 删除默认图标
  350. }
  351. },
  352. 'core': {
  353. 'check_callback': true,
  354. "multiple": false,
  355. 'animation': 0,
  356. "data": window.documentCategory
  357. },
  358. "contextmenu": {
  359. show_at_node: false,
  360. select_node: false,
  361. "items": {
  362. "添加文档": {
  363. "separator_before": false,
  364. "separator_after": true,
  365. "_disabled": false,
  366. "label": window.editormdLocales[window.lang].addDoc,//"添加文档",
  367. "icon": "fa fa-plus",
  368. "action": function (data) {
  369. var inst = $.jstree.reference(data.reference),
  370. node = inst.get_node(data.reference);
  371. openCreateCatalogDialog(node);
  372. }
  373. },
  374. "编辑": {
  375. "separator_before": false,
  376. "separator_after": true,
  377. "_disabled": false,
  378. "label": window.editormdLocales[window.lang].edit,
  379. "icon": "fa fa-edit",
  380. "action": function (data) {
  381. var inst = $.jstree.reference(data.reference);
  382. var node = inst.get_node(data.reference);
  383. openEditCatalogDialog(node);
  384. }
  385. },
  386. "删除": {
  387. "separator_before": false,
  388. "separator_after": true,
  389. "_disabled": false,
  390. "label": window.editormdLocales[window.lang].delete,
  391. "icon": "fa fa-trash-o",
  392. "action": function (data) {
  393. var inst = $.jstree.reference(data.reference);
  394. var node = inst.get_node(data.reference);
  395. openDeleteDocumentDialog(node);
  396. }
  397. }
  398. }
  399. }
  400. }).on('loaded.jstree', function () {
  401. window.treeCatalog = $(this).jstree();
  402. }).on('select_node.jstree', function (node, selected, event) {
  403. // if(window.editor.menus.menuList.find((item) => item.key == 'save').$elem.hasClass('selected')) {
  404. // if (confirm(window.editormdLocales[window.lang].contentUnsaved)) {
  405. // saveDocument(false,function () {
  406. // loadDocument(selected);
  407. // });
  408. // return true;
  409. // }
  410. // }
  411. loadDocument(selected);
  412. }).on("move_node.jstree", jstree_save);
  413. window.saveDocument = saveDocument;
  414. window.releaseBook = function () {
  415. // if(Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0){
  416. // if(window.editor.menus.menuList.find((item) => item.key == 'save').$elem.hasClass('selected')) {
  417. // if(confirm(editormdLocales[lang].contentUnsaved)) {
  418. // saveDocument();
  419. // }
  420. // }
  421. locales = {
  422. 'zh-CN': {
  423. publishToQueue: '发布任务已推送到任务队列,稍后将在后台执行。',
  424. },
  425. 'en': {
  426. publishToQueue: 'The publish task has been pushed to the queue</br> and will be executed soon.',
  427. }
  428. }
  429. $.ajax({
  430. url: window.releaseURL,
  431. data: {"identify": window.book.identify},
  432. type: "post",
  433. dataType: "json",
  434. success: function (res) {
  435. if (res.errcode === 0) {
  436. layer.msg(locales[lang].publishToQueue);
  437. } else {
  438. layer.msg(res.message);
  439. }
  440. }
  441. });
  442. // }else{
  443. // layer.msg(editormdLocales[lang].noDocNeedPublish)
  444. // }
  445. };
  446. // $(window).resize(function(e) {
  447. // var $container = $(editor.$textContainerElem.elems[0]);
  448. // var $manual = $container.closest('.manual-froalaEditor');
  449. // var maxHeight = $manual.closest('.manual-editor-container').innerHeight();
  450. // var statusHeight = $manual.siblings('.manual-editor-status').outerHeight(true);
  451. // var manualHeihgt = maxHeight - statusHeight;
  452. // $manual.height(manualHeihgt);
  453. // var toolbarHeight = $container.siblings('.w-e-toolbar').outerHeight(true);
  454. // $container.height($container.parent().innerHeight() - toolbarHeight);
  455. // });
  456. // $(window).trigger('resize');
  457. });