1 Star 1 Fork 3

xiaoyangge / heather

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

heather

2.0 (可能)

2.0不再提供左右栏的结构,而是尝试将输入的markdown文本直接转化为html,用户点击渲染的html元素时,显示原始的markdown文本以供编辑

如果你对此感兴趣,可以到 https://md.qyh.me/new.html 尝试,原型版本提供以下操作:

  1. 编辑完成之后通过 Ctrl|Cmd Enter保存编辑器的内容到一个块中
  2. 编辑工程中,通过点击 Shift Ctrl|Cmd Enter来分块,一次只能分两块,以编辑器所在光标为分块点

说明

markdown编辑器,特性如下:

  1. 支持mermaid图表、katex
  2. 自定义工具条|辅助工具条
  3. 支持拖曳html|md文件,自动将html转化为markdown
  4. 本地存储(localStorage)支持
  5. 编辑预览同步滚动
  6. 主题设置
  7. 支持手机端
  8. 离线访问
  9. mermaid、katex懒加载
  10. 全屏编辑

在线demo

https://md.qyh.me

源码

https://github.com/mhlx/heather

使用

https://github.com/mhlx/heather 下载最新的文件

引入css

<link rel="stylesheet"  href="codemirror/lib/codemirror.css" media="screen">
<link rel="stylesheet" 
   href="codemirror/addon/scroll/simplescrollbars.css" media="screen">
<link rel="stylesheet"  href="css/markdown.css" media="all">
<link href="fontawesome-free/css/all.min.css" rel="stylesheet" media="screen">
<link rel="stylesheet" href="css/style.css" media="screen">
<link rel="stylesheet" href="css/print.css" media="print">

引入js

<script src="jquery/jquery.min.js"></script>
<script src="js/htmlparser.js"></script>
<script src="js/morphdom-umd.min.js"></script>
<script src="codemirror/lib/codemirror.js"></script>
<script src="codemirror/addon/mode/overlay.js"></script>
<script src="codemirror/mode/javascript/javascript.js"></script>
<script src="codemirror/mode/xml/xml.js"></script>
<script src="codemirror/mode/markdown/markdown.js"></script>
<script src="codemirror/mode/gfm/gfm.js"></script>
<script src="codemirror/addon/selection/mark-selection.js"></script>
<script src="codemirror/addon/search/searchcursor.js"></script>
<script src="codemirror/addon/search/search.js"></script>
<script src="codemirror/addon/edit/continuelist.js"></script>
<script src="codemirror/addon/scroll/simplescrollbars.js"></script>
<script src="sweet2alert/dist/sweetalert2.all.min.js"></script>
<script src="js/turndown.js"></script>
<script src="js/turndown-plugin-gfm.js"></script>
<script src="highlight/highlight.pack.js"></script>
<script src="js/jquery.touchwipe.min.js"></script>
<script src="js/fullscreen.js"></script>
<script src="js/md.js"></script>
<script src="js/all.js"></script>

创建编辑器

var config = {};
var wrapper = EditorWrapper.create(config);

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
   <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport"
         content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
      <meta name="description" content="Heather is a online Markdown Editor">
      <meta name="keywords" content="Markdown, Heather, Editor, CodeMirror, Github, Open Source">
      <link rel="stylesheet"  href="codemirror/lib/codemirror.css" media="screen">
      <link rel="stylesheet" 
       href="codemirror/addon/scroll/simplescrollbars.css" media="screen">
      <link rel="stylesheet"  href="css/markdown.css" media="all">
      <link href="fontawesome-free/css/all.min.css" rel="stylesheet" media="screen">
      <link rel="stylesheet" href="css/style.css" media="screen">
      <link rel="stylesheet" href="css/print.css" media="print">
   </head>
   <body>
      <script src="jquery/jquery.min.js"></script>
      <script src="js/htmlparser.js"></script>
      <script src="js/morphdom-umd.min.js"></script>
      <script src="codemirror/lib/codemirror.js"></script>
      <script src="codemirror/addon/mode/overlay.js"></script>
      <script src="codemirror/mode/javascript/javascript.js"></script>
      <script src="codemirror/mode/xml/xml.js"></script>
      <script src="codemirror/mode/markdown/markdown.js"></script>
      <script src="codemirror/mode/gfm/gfm.js"></script>
      <script src="codemirror/addon/selection/mark-selection.js"></script>
      <script src="codemirror/addon/search/searchcursor.js"></script>
      <script src="codemirror/addon/search/search.js"></script>
      <script src="codemirror/addon/edit/continuelist.js"></script>
      <script src="codemirror/addon/scroll/simplescrollbars.js"></script>
      <script src="sweet2alert/dist/sweetalert2.all.min.js"></script>
      <script src="js/turndown.js"></script>
      <script src="js/turndown-plugin-gfm.js"></script>
      <script src="highlight/highlight.pack.js"></script>
      <script src="js/jquery.touchwipe.min.js"></script>
      <script src="js/fullscreen.js"></script>
      <script src="js/md.js"></script>
      <script src="js/all.js"></script>
      <script>
        var config = {};
        var wrapper = EditorWrapper.create(config);
        //https://www.qyh.me/space/web/article/598
        var userAgent = navigator.userAgent;
        if(userAgent.indexOf('Windows NT 10.0') != -1 && userAgent.indexOf('Firefox') != -1){
            var textarea = wrapper.editor.getWrapperElement().querySelector('textarea');
            textarea.style.width = "1000px";
        }
      </script>
   </body>
</html>

其他

  1. codemirror.js codemirror核心文件 本地处理过,请勿使用cdn
  2. md.js markdown-it 本地处理过,请勿使用cdn
  3. 一个页面只能存在一个EditorWrapper实例,创建另一个EditorWrapper实例时会自动销毁已经存在的实例
  4. 编辑器不会替代任何你页面的html元素,它只会额外创建一些html元素,它始终是全屏的,并且无法改变
  5. EditorWrapper依赖以下html元素(初始化实例时自动创建),请保证ID的唯一性
<div id="editor_wrapper">
  <div id="editor_toc"></div>
  <div id="editor_in">
    <div id="editor_toolbar"></div>
    <textarea  style="width: 100%; height: 100%"></textarea>
    <div id="editor_stat"></div>
    <div id="editor_innerBar"></div>
  </div>
  <div class="markdown-body " id="editor_out" ></div>
</div>

配置

配置项 说明 默认值
toolbar_icons 配置顶部工具条图标 [ 'toc','innerBar','backup','new','search','config' ]
backupEnable 是否开启备份
backup_autoSaveMs 当配置backupEnable为true时生效,当编辑器内容发生变更时,多少毫秒后自动保存编辑器内容 500
innerBar_icons 配置辅助工具条的图标 ['heading','bold','italic','quote','strikethrough','link','code','code-block','uncheck','check','table','undo','redo','close']
render_allowHtml 是否允许html标签 false
render_plugins 配置markdown渲染支持的插件 ['footnote','katex','mermaid','anchor','task-lists','sup','sub','abbr']
render_beforeRender 配置在渲染前元素处理器
render_ms 当编辑器内容发生变更时,多少毫秒后开始渲染 500
stat_enable 是否启用状态条 true
stat_formatter 当启用状态条时,用于获取状态条渲染内容的方法
sync_animateMs 配置同步滚动时滚动动画时间 0
sync_enable 是否启用同步滚动
swipe_animateMs 预览、编辑、toc切换时动画时间 500
res_mermaid_js 指定mermaid文件的加载路径 js/mermaid.min.js
res_katex_css 指定katex css文件的加载路径 katex/katex.min.css
res_katex_js 指定katex js文件的加载路径 katex/katex.min.js
res_hljsTheme 获取highlightjs主题的路径 function(theme){return themeCssUrl}
res_editorTheme 获取CodeMirror主题的路径 function(theme){return themeCssUrl}
res_colorpickerCss 指定colorpicker css文件路径 colorpicker/dist/css/bootstrap-colorpicker.min.css
res_colorpickerJs 指定colorpicker js文件路径 colorpicker/dist/js/bootstrap-colorpicker.min.js
renderAllDocEnable 代码高亮的同步预览中,由于codemirror只渲染当前视窗,因此会出现不同步的现象,开启这个选项在载入文档时可以消除这个现象,但是在文本量比较大的时候,加载非常缓慢,可以选择关闭 true

方法

编辑器

获取codemirror对象

wrapper.editor

获取编辑器内容

wrapper.getValue()

设置编辑器内容

wrapper.setValue(newValue)

获取html内容

wrapper.getHtml()

渲染内容

wrapper.doRender(patch) patch:是否patch更新

同步滚动条

wrapper.doSync()

启用编辑和预览的同步

wrapper.enableSync()

停用编辑和预览的同步

wrapper.disableSync()

切换到编辑器页面

wrapper.toEditor()

切换到TOC页面

wrapper.toToc()

切换到预览页面

wrapper.toPreview()只在手机端或者全屏模式下有效

顶部工具栏

wrapper.toolbar

添加一个图标

wrapper.toolbar.addIcon(clazz,hander,callback)

clazz为fontawesome图标的样式,例如fa fa-file icon,handler为图标被点击时触发的方法,callback则为图标元素的回调,例如为添加的图标加上id属性:

wrapper.toolbar.addIcon(clazz,hander,function(icon){
  icon.setAttribute(id,'icon-id');
})
额外的图标样式
  1. mobile-hide在手机端隐藏
  2. pc-hide在pc端隐藏
  3. nofullscreen在全屏模式下隐藏
  4. onfullscreen 只在全屏模式下出现

删除图标

wrapper.toolbar.removeIcon(function(icon){return bool})

隐藏

wrapper.toolbar.hide()

显示

wrapper.toolbar.show()

保持隐藏状态

wrapper.toolbar.hide();

辅助工具栏

wrapper.innerBar

方法同顶部工具条

主题

wrapper.theme

配置编辑器主题

theme.setEditorTheme(wrapper.editor,'abcdef',function(){
	theme.render();
})

配置代码高亮主题

theme.hljs.theme = 'a11y-light'
theme.render();

配置顶部工具条颜色

theme.toolbar.color = '#fff'
theme.render();

配置辅助工具条颜色

theme.bar.color = '#fff'
theme.render();

配置状态条字体颜色

theme.stat.color = '#fff'
theme.render();

自定义css

wrapper.theme.customCss = css;
wrapper.theme.render();

保存主题

wrapper.saveTheme();

重置主题

wrapper.resetTheme();

执行指令

wrapper.execCommand(commandName) 系统内置指令

名称 说明 快捷键_mac 快捷键_pc
search 开启|关闭一个搜索框 Ctrl S Alt S
emoji 打开emoji选择框
heading 打开heading选择框 Ctrl H Ctrl H
bold 加粗文本 Ctrl B Ctrl B
italic 倾斜文本 Ctrl I Ctrl I
quote 引用文本 Ctrl Q Ctrl Q
strikethrough 为文本添加删除线
link 插入超链接 Ctrl L Ctrl L
codeBlock 插入代码块 Shift Cmd B Alt B
code 插入行内代码
uncheck 插入待办事项 Shift Cmd U Alt U
check 插入已完成待办事项 Shift Cmd I Alt I
undo 撤回 Cmd Z Ctrl Z
redo 取消撤回 Cmd Y Ctrl Y
table 插入表格 Shift Cmd T Alt T

新增指令

EditorWrapper.commands[commandName] = commandHandler commandHandler接受一个参数wrapper,例如: 新增一个插入图片的指令,并且为该指令绑定Ctrl+G的快捷键:

EditorWrapper.commands['image'] = function(wrapper){
  var editor = wrapper.editor;
  var text = editor.getSelection();
  if (text == '') {
      editor.replaceRange("![]()", editor.getCursor());
      editor.focus();
      var start_cursor = editor.getCursor();
      var cursorLine = start_cursor.line;
      var cursorCh = start_cursor.ch;
      editor.setCursor({
          line: cursorLine,
          ch: cursorCh - 1
      });
  } else {
      editor.replaceSelection("![" + text + "]()");
  }
}
wrapper.bindKey({'Ctrl-G':'image'});

快捷键绑定

wrapper.bindKey(keyMap) keyMap对象属性为快捷键名称,例如Ctrl-A,属性值可以为string或者一个方法,如果为string类型,那么则会绑定对应名称的命令,如果为方法,则会直接绑定该方法

解除快捷键绑定

wrapper.unbindKey(keyArray) keyArray为快捷键数组,例如['Ctrl-A']

全屏编辑

wrapper.requestFullScreen() 只在PC端有效

退出全屏

wrapper.exitFullScreen() 只在PC端有效

销毁实例

wrapper.remove()

监听实例销毁

wrapper.onRemove(fun)

取消监听实例销毁

wrapper.offRemove(fun)

导出PDF

没有直接导出PDF的方法,但是可以通过以下步骤,让chrome浏览器的打印功能来实现

设置打印样式

<link rel="stylesheet"  href="codemirror/lib/codemirror.css" media="screen">
<link rel="stylesheet" 
 href="codemirror/addon/scroll/simplescrollbars.css" media="screen">
<link rel="stylesheet"  href="css/markdown.css" media="all">
<link href="fontawesome-free/css/all.min.css"  media="all" rel="stylesheet">
<link rel="stylesheet" href="css/style.css" media="screen">
<link rel="stylesheet" href="css/print.css" media="print">

上述html中,media="print"的只会在打印时使用,media="screen"的则只会在页面渲染时使用,而media="all" 则在所有情况下都会使用,请参考 https://www.w3schools.com/tags/att_link_media.asp

打印前渲染

mermaid渲染出来的元素大小会根据视窗大小自动调整,由于左右预览的原因,打印出来的大小会跟预期的大小不一致,此时可以通过监听打印事件使得在打印前再次渲染。

var wrapper = EditorWrapper.create({});
var beforePrintHandler = function(mql) {
    if (mql.matches) {
        wrapper.doRender(false);
    }
}
if (window.matchMedia) {
    var mediaQueryList = window.matchMedia('print');
    mediaQueryList.addListener(beforePrintHandler);
}
wrapper.onRemove(function(){
    mediaQueryList.removeListener(beforePrintHandler);
});

强制分页

通过添加以下代码可以让pdf文件强制分页

<div style="page-break-after: always;"></div>

文件上传(1.6)

开启

通过配置upload_urlupload_finish即可开启文件上传,例如:

var config = {
    upload_url:'https://putsreq.com/aPamE6UIaFogo0JwhL6N',
    upload_finish:function(resp){
        swal('仅供测试上传所用,固定返回同一地址')
        return {
            type : 'image',
            url : 'https://www.qyh.me/image/news/8BE085FBC2F48482047C510EE0A36C4F.jpeg/600'
        };
    }
};
var wrapper = EditorWrapper.create(config);

其中upload_url为上传的地址,upload_finish为上传成功后的回调函数,接受一个参数,该参数内容为服务器响应的内容 ,同时返回地址信息,地址信息分为三种:图片|视频|一般文件。

  1. 图片地址,固定格式为{type:'image',url:'图片地址'}
  2. 视频地址,一般格式为{type:'video',url:'视频地址'},同时可以通过设置poster属性设定一个封面,例如:{type:'video',url:'视频地址','poster':'封面图片地址'},如果需要设置多个source,可以设置sources属性,source属性应该为一个数组,单个数组元素内容格式为{'type':'video/mp4|video/ogg等','src':'视频地址'}
  3. 一般文件地址,固定格式为{type:'file',url:'文件地址'}

设定上传前参数

通过配置upload_before可以在上传前增加额外的参数,例如

config.upload_before = function(formData,file){
  formData.append("key", file.name);
}

设定文件上传名称

通过配置upload_fileName可以设定文件上传名称,默认为file

七牛云文件上传

一个简单的七牛云文件上传的例子:

前端:

var config = {
  upload_url:'http://upload.qiniu.com/',
  upload_before:function(formData,file){
      $.ajax({
          async:false,//这里一定要同步获取token
          url : 'http://localhost:8081/test/test',
          success:function(token){
              formData.append("token", token);
              formData.append("key", file.name);
          }
      })
  },
  upload_finish:function(resp){
      resp = $.parseJSON(resp);
      if(resp.error){
          swal(resp.error);
          return ;
      }
      return {
          type : 'image',
          url : 'http://img.qyh.me/'+resp.key
      };
  }
};

后端:

package test;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.qiniu.util.Auth;

public class TestServlet extends HttpServlet{
	
	private static final long serialVersionUID = 1L;
	private final String ak = "";
	private final String sk = "";
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		Auth auth = Auth.create(ak, sk);
		String upToken = auth.uploadToken("mhlx");
		resp.addHeader("Access-Control-Allow-Origin", "*");
		resp.getWriter().write(upToken);
	}
}

支持的浏览器

只在chrome上做了测试,但应该支持一些其他的现代化浏览器

感谢

  1. 采用codemirror作为编辑器
  2. 采用markdown-it渲染markdown
  3. 采用jquery简化dom操作
  4. 采用fontawesome美化图标
  5. 采用highlight.js高亮代码
  6. 采用katex渲染数学公式
  7. 采用sweet2alert美化弹出框
  8. 采用mermaid绘制流程图、甘特图和时序图
  9. 采用turndown将html转化为markdown
  10. 采用morphdom来patch更新dom

空文件

简介

markdown在线编辑器 展开 收起
JavaScript
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/xiaoyangge_admin/heather.git
git@gitee.com:xiaoyangge_admin/heather.git
xiaoyangge_admin
heather
heather
master

搜索帮助