1、简介
editor.md是国内开发的开源项目,单纯基于前端JavaScript,可以实现markdown编辑器的所有功能。
主要特性
- 支持通用 Markdown / CommonMark 和 GFM (GitHub Flavored Markdown) 风格的语法,也可变身为代码编辑器;
- 支持实时预览、图片(跨域)上传、预格式文本/代码/表格插入、代码折叠、跳转到行、搜索替换、只读模式、自定义样式主题和多语言语法高亮等功能;
- 支持 ToC(Table of Contents)、Emoji表情、Task lists、@链接等 Markdown 扩展语法;
- 支持 TeX 科学公式(基于 KaTeX)、流程图 Flowchart 和 时序图 Sequence Diagram;
- 支持识别和解析 HTML 标签,并且支持自定义过滤标签及属性解析,具有可靠的安全性和几乎无限的扩展性;
- 支持 AMD / CMD 模块化加载(支持 Require.js & Sea.js),并且支持自定义扩展插件;
- 兼容主流的浏览器(IE8+)和 Zepto.js,且支持 iPad 等平板设备;
官网:https://pandao.github.io/editor.md/,下载解压后的目录如下图所示: 说明:
- css目录中可选择editormd.min.css放在对应的项目css目录中;
- js可选择editormd.min.js放置在对应项目的js目录中,需要注意的是同时需要引入jQuery,这里使用jquery.min.js;
- examples文件夹中是一部分核心功能的demo,在使用的过程中用到对应的组件或功能可打开参考;
- fonts是需要用到字体,可一并引入项目;
- images是一些加载类的图片;
- lib是editor.md依赖的第三方js资源,比如流程图的js资源;
- plugins主要是编辑器上面的操作功能插件,比如图片上传等,可选择使用的进行加载;
在examples文件夹中有一个简单的示例simple.html,源代码为:
DOCTYPE html>
示例
Simple example
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "90%", //宽度
height : 640, //高度
syncScrolling : "single", //
path : "../lib/",
autoFocus:false //页面打开时不自动获取焦点
});
});
注意
是默认的显示mark文本的区域,默认的name值是test-editormd-markdown-doc,如果想更换名称为content,只需要修改为:
即可
-
第一步:创建一个Maven形式的Web项目,然后将editor.md解压后的文件全部拷贝到项目的webapp目录下:
-
第二步:将editormd/examples下的simple.html的源代码拷贝到index.jsp,然后调整为:
示例 editor.md示例 var testEditor; $(function() { testEditor = editormd("test-editormd", { width : "90%", height : 640, syncScrolling : "single", path : "${pageContext.request.contextPath}/editormd/lib/", //这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 saveHTMLToTextarea : true }); });
代码解释:
- path:需要指定到项目中对应的lib的路径,如果设置不对markdown 无法渲染出来。
- saveHTMLToTextarea设置为true表示,转化为html格式的内容也同样提交到后台。
- 通过form表单提交时后台可通过request.getParameter(“editormd”)获取到对应的markdown文档内容。
注意:
-
无论需要html格式的内容还是markdown格式的内容,都只需要写一个textarea。此处有一个很大的坑。不少其他教程中说需要两个textarea,那么会导致后一个textarea后台获得的数据是一个数组,而不是单纯的HTML内容。
-
运行
这样就完成了一个最简单的editor.md的编辑器了,可以在打开的页面书写熟悉的Markdown文档,里面可以包含代码,右侧有实时的预览。
后端返回的报文必须json 格式:
{
success : 0 | 1, //0表示上传失败;1表示上传成功
message : "提示的信息",
url : "图片地址" //上传成功时才返回
}
说明:
- 0,1 必须是数字;
- url :就是你图片存在的地址,这个就是返回到弹框中的图片地址。
-
第一步:页面代码
示例 editor.md示例 var testEditor; $(function() {//Editor初始化: testEditor = editormd("test-editormd", { width : "90%", height : 640, syncScrolling : "single", // theme : "dark", path : "${pageContext.request.contextPath}/editormd/lib/", //依赖lib文件夹路径 //这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单 saveHTMLToTextarea : true, saveHTMLToTextarea : true, // 保存 HTML 到 Textarea,true表示转化为html格式的内容也同样提交到后台 toolbarAutoFixed:true,//工具栏自动固定定位的开启与禁用 imageUpload : true, imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL : "${pageContext.request.contextPath}/imageUpload", }); });
说明:
- imageUpload:设置为true表示支持上传,此时需要plugins中的image-dialog.js插件(默认调用,放在指定路径即可)
- imageFormats:为支持上传的图片类型
- imageUploadURL:要上传图片的后台服务器路径
-
第二步:后台处理文件上传的Servlet
@MultipartConfig //标识Servlet支持文件上传 @WebServlet(urlPatterns = "/imageUpload") public class ImageUploadServlet extends HttpServlet { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { JSONObject res = new JSONObject(); request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String savePath = request.getServletContext().getRealPath("/uploadFile"); //存储路径 File path = new File(savePath); if (!path.exists()) { //如果路径不存在则创建 path.mkdirs(); } Collection parts = request.getParts(); //获取上传的文件集合 if (parts != null && parts.size() == 1) { //上传单个文件 Part part = request.getPart("editormd-image-file"); //-----① 通过表单file控件()的名字直接获取Part对象 String header = part.getHeader("content-disposition");//获取请求头 String fileName = getFileName(header); String url = savePath + File.separator + fileName; part.write(url); //把文件写到指定路径 res.put("success", 1); res.put("message", "上传成功"); res.put("url", "http://localhost:8080/EditorMarkDownDemo_war/imageShow?imageFileName=" + fileName); } else { res.put("success", 0); res.put("message", "上传失败"); } PrintWriter out = response.getWriter(); out.println(res.toJSONString()); out.flush(); out.close(); } /** * 根据请求头解析出文件名 * @param header 请求头 * @return 文件名 */ public String getFileName(String header) { String[] temp = header.split(";")[2].split("="); //获取文件名,兼容各种浏览器的写法 String fileName = temp[1].substring(temp[1].lastIndexOf("\\") + 1).replaceAll("\"", ""); return fileName; } }
编号①处之所以通过editormd-image-file获取Part的原因如下图所示:
-
第三步:后台处理图片回显的Servlet
@WebServlet(urlPatterns = "/imageShow") public class ImageShowServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String imageFileName = request.getParameter("imageFileName"); response.setContentType("text/html; charset=UTF-8"); response.setContentType("image/jpeg"); OutputStream os = response.getOutputStream(); String savePath = request.getServletContext().getRealPath("/uploadFile"); //存储路径 try (FileImageInputStream input = new FileImageInputStream(new File(savePath + File.separator +imageFileName)); ByteArrayOutputStream output = new ByteArrayOutputStream();) { byte[] buf = new byte[1024]; int len = -1; while ((len = input.read(buf)) != -1) { output.write(buf, 0, len); } byte[] data = output.toByteArray(); os.write(data); os.flush(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } } }
-
结果:
-
前端页面:
示例 editor.md示例 var testEditor; $(function() {//Editor初始化: testEditor = editormd("test-editormd", { width : "90%", height : 640, syncScrolling : "single", // theme : "dark", path : "${pageContext.request.contextPath}/editormd/lib/", //依赖lib文件夹路径 //这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单 // saveHTMLToTextarea : true, // 保存 HTML 到 Textarea,true表示转化为html格式的内容也同样提交到后台 toolbarAutoFixed:true,//工具栏自动固定定位的开启与禁用 imageUpload : true, imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL : "${pageContext.request.contextPath}/imageUpload", }); $("#saveBtn").click(function () { var data = $("#editormdData").val(); //alert(html); $.ajax({ data:{'data':data}, dataType:'text', type:'post', url:'${pageContext.request.contextPath}/saveData', success:function (msg) { alert(msg); } }) }); }); 保存代码
-
后台Servlet:
@WebServlet(urlPatterns = "/saveData") public class SaveDataServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String data = request.getParameter("data"); System.out.println(data); String savePath = request.getServletContext().getRealPath("/md"); //存储路径 File file = new File(savePath); if(!file.exists()){ file.mkdirs(); } FileWriter out = new FileWriter(savePath+"/demo.md"); out.write(data); out.flush(); out.close(); response.getWriter().write("success"); } }
-
运行,单击保存按钮,在指定的目录下会找到保存好的文件
${content}
editormd.markdownToHTML("content",{emoji:true});