/* * Editor.md * @file editormd.js * @version v1.0.0 * @description A simple online markdown editor. * @license MIT License * @author Pandao * {@link https://github.com/pandao/editor.md} * @updateTime 2015-02-08 */ /** * @fileOverview Editor.md * @author pandao * @version 1.0.0 */ ;(function(factory) { "use strict"; // CommonJS/Node.js if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { module.exports = factory(); } else if (typeof define === "function") // AMD/CMD/Sea.js { define(["jquery"], factory); } else { window.editormd = factory(); } }(function() { /** * editormd * * @param {String} id 编辑器的ID * @param {Object} options 配置选项 Key/Value * @returns {Object} editormd 返回editormd对象 */ "use strict"; var $ = jQuery; var editormd = function (id, options) { return new editormd.fn.init(id, options); }; editormd.title = "Editor.md"; editormd.version = "1.0.0"; editormd.homePage = "https://github.com/pandao/editor.md"; editormd.description = "A simple markdown doucment online editor."; editormd.classPrefix = "editormd-"; editormd.defaults = { mode : "gfm", //gfm or markdown markdown : "", width : "100%", height : "100%", path : "./lib/", watch : true, onload : function() {}, onchange : function() {}, onfullscreen : function() {}, onfullscreenExit : function() {}, toc : true, tocStartLevel : 2, fontSize : "13px", flowChart : false, // flowChart.js only support IE9+ mathjax : false, sequenceDiagram : false, // sequenceDiagram.js only support IE9+ previewCodeHighlight : true, inRequirejs : false, toolbar : true, toolbarIcons : [ "undo", "redo", "|", "bold", "del", "italic", "quote", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "picture", "code", "code-block-tab", "code-block", "datetime", "|", "watch", "preview", "fullscreen", "|", "info" ], toolbarIconsClass : { undo : "fa-undo", redo : "fa-repeat", bold : "fa-bold", del : "fa-strikethrough", italic : "fa-italic", quote : "fa-quote-left", h1 : editormd.classPrefix + "bold", h2 : editormd.classPrefix + "bold", h3 : editormd.classPrefix + "bold", h4 : editormd.classPrefix + "bold", h5 : editormd.classPrefix + "bold", h6 : editormd.classPrefix + "bold", "list-ul" : "fa-list-ul", "list-ol" : "fa-list-ol", hr : "fa-minus", link : "fa-link", picture : "fa-picture-o", code : "fa-code", "code-block-tab" : "fa-file-code-o", "code-block" : "fa-file-code-o", datetime : "fa-clock-o", watch : "fa-eye-slash", unwatch : "fa-eye", preview : "fa-search", fullscreen : "fa-arrows-alt", info : "fa-info-circle" }, lang : { toolbar : { undo : "撤销", redo : "重做", bold : "粗体", del : "删除线", italic : "斜体", quote : "引用", h1 : "标题1", h2 : "标题2", h3 : "标题3", h4 : "标题4", h5 : "标题5", h6 : "标题6", "list-ul" : "无序列表", "list-ol" : "有序列表", hr : "横线", link : "链接", picture : "图片", code : "行内代码", "code-block-tab" : "代码块(缩进风格)", "code-block" : "代码块(多语言风格)", datetime : "日期时间", watch : "关闭实时预览", unwatch : "开启实时预览", preview : "预览HTML(按ESC还原)", fullscreen : "全屏(按ESC还原)", info : "关于" + editormd.title } }, codemirror : { modes : [ "css", "sass", "shell", "sql", "clike", "php", "xml", "markdown", "javascript", "htmlmixed", "gfm", "http", "go", "dart", "coffeescript", "nginx", "python", "perl", "lua", //"r", "ruby", "rst", "smartymixed", //"vb", //"vbscript", //"velocity", //"xquery", "yaml" ], addons : [ "edit/trailingspace", "dialog/dialog", "search/searchcursor", "search/search", "scroll/annotatescrollbar", "search/matchesonscrollbar", "display/placeholder", "edit/closetag", "fold/xml-fold", "mode/overlay", "selection/active-line", "edit/closebrackets", "display/fullscreen", "search/searchcursor", "search/match-highlighter" ] } }; editormd.$marked = null; editormd.$CodeMirror = null; editormd.$prettyPrint = null; editormd.prototype = editormd.fn = { state : { preview : false, fullscreen : false }, /** * 构造函数/实例初始化 * @param {String} id 编辑器的ID * @param {Object} [options={}] 配置选项 Key/Value * @returns {editormd} 返回editormd的实例对象 */ init : function (id, options) { options = options || {}; var _this = this; var classPrefix = this.classPrefix = editormd.classPrefix; var editor = this.editor = $("#" + id); var settings = this.settings = $.extend(true, editormd.defaults, options); var markdownDoc = (settings.markdown === "") ? editor.children("[type=\"text/markdown\"]").html() : settings.markdown; this.id = id; this.classNames = { textarea : { html : this.classPrefix + "html-textarea", markdown : this.classPrefix + "markdown-textarea" } }; editor.css({ width : (typeof settings.width === "number") ? settings.width + "px" : settings.width, height : (typeof settings.height === "number") ? settings.height + "px" : settings.height }); editor.children("[type=\"text/markdown\"]").remove(); var infoDialogHTML = [ "
", "", "
", "

" + editormd.title + "v" + editormd.version + "

", "

" + editormd.description + "

", "

Home page: " + editormd.homePage + "

", "

License: MIT

", "
", "
" ].join("\n"); var appendElements = [ '
', '', '', '
', "
" ].join("\n"); editor.append(infoDialogHTML).append(appendElements); this.preview = editor.find("." + classPrefix + "preview"); this.toolbar = editor.find("." + classPrefix + "toolbar"); this.previewContainer = this.preview.children("." + classPrefix + "preview-container"); this.infoDialog = editor.find("." + classPrefix + "dialog-info"); this.toolbarIconHandlers = {}; editor.addClass(classPrefix + "vertical"); if (!settings.inRequirejs) { this.loadQueues(); } else { _this.setCodeEditor(); _this.setToolbar(); _this.toolbarHandler(); _this.setMarked().loadedDisplay(); } return this; }, /** * 所需组件加载队列 * @returns {editormd} 返回editormd的实例对象 */ loadQueues : function() { var _this = this; var settings = this.settings; var cmModeIndex = 0, cmModeTotal = settings.codemirror.modes.length; var cmAddonIndex = 0, cmAddonTotal = settings.codemirror.addons.length; var loadPath = settings.path; var loadFlowChartOrSequenceDiagram = function() { if (settings.flowChart || settings.sequenceDiagram) { editormd.loadScript(loadPath + "raphael.min", function() { editormd.loadScript(loadPath + "underscore.min", function() { if (!settings.flowChart && settings.sequenceDiagram) { editormd.loadScript(loadPath + "sequence-diagram.min", function() { _this.setMarked().loadedDisplay(); }); } else if (settings.flowChart && !settings.sequenceDiagram) { editormd.loadScript(loadPath + "flowchart.min", function() { editormd.loadScript(loadPath + "jquery.flowchart.min", function() { _this.setMarked().loadedDisplay(); }); }); } else if (settings.flowChart && settings.sequenceDiagram) { editormd.loadScript(loadPath + "flowchart.min", function() { editormd.loadScript(loadPath + "jquery.flowchart.min", function() { editormd.loadScript(loadPath + "sequence-diagram.min", function() { _this.setMarked().loadedDisplay(); }); }); }); } }); }); } else { _this.setMarked().loadedDisplay(); } }; var loadCodeMirrorAddons = function() { var addonName = settings.codemirror.addons[cmAddonIndex]; editormd.loadScript(loadPath + "codemirror/addon/" + addonName, function() { if(cmAddonIndex < cmAddonTotal - 1) { cmAddonIndex ++; loadCodeMirrorAddons(); } else { _this.setCodeEditor(); _this.setToolbar(); _this.toolbarHandler(); editormd.loadScript(loadPath + "marked.min", function() { editormd.$marked = marked; //_this.marked = marked; if (settings.previewCodeHighlight) { editormd.loadScript(loadPath + "prettify.min", function() { loadFlowChartOrSequenceDiagram(); }); } else { loadFlowChartOrSequenceDiagram(); } }); } }); }; var loadCodeMirrorModes = function(){ var modeName = settings.codemirror.modes[cmModeIndex]; editormd.loadScript(loadPath + "codemirror/mode/" + modeName + "/" + modeName, function() { if(cmModeIndex < cmModeTotal - 1) { cmModeIndex ++; loadCodeMirrorModes(); } else { loadCodeMirrorAddons(); } }); }; //editormd.loadCSS(loadPath + "font-awesome.min"); editormd.loadCSS(loadPath + "codemirror/lib/codemirror.min"); editormd.loadScript(loadPath + "codemirror/lib/codemirror.min", function() { editormd.$CodeMirror = CodeMirror; loadCodeMirrorModes(); }); return this; }, /** * 配置和初始化CodeMirror组件 * @returns {editormd} 返回editormd的实例对象 */ setCodeEditor : function() { var settings = this.settings; var codeMirrorConfig = { mode: this.settings.mode, theme: "default", tabSize: 4, dragDrop: false, autofocus: true, indentUnit : 4, lineNumbers: true, lineWrapping: true, matchBrackets: true, indentWithTabs: true, styleActiveLine: true, styleSelectedText: true, autoCloseBrackets: true, showTrailingSpace: true, highlightSelectionMatches: { showToken: /\w/ } }; this.codeEditor = editormd.$CodeMirror.fromTextArea(this.editor.find("." + this.classNames.textarea.markdown)[0], codeMirrorConfig); this.codeMirror = this.editor.find(".CodeMirror"); this.codeMirror.css("font-size", this.settings.fontSize); return this; }, /** * 显示工具栏 * @returns {editormd} 返回editormd的实例对象 */ showToolbar : function() { this.settings.toolbar = true; this.toolbar.show(); this.resize(); return this; }, /** * 隐藏工具栏 * @returns {editormd} 返回editormd的实例对象 */ hideToolbar : function() { this.settings.toolbar = false; this.toolbar.hide(); this.resize(); return this; }, /** * 配置和初始化工具栏 * @returns {editormd} 返回editormd的实例对象 */ setToolbar : function() { var settings = this.settings; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; if (!settings.toolbar) { toolbar.hide(); return ; } else { toolbar.show(); } var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = ""; for (var i = 0, len = settings.toolbarIcons.length; i < len; i++) { var name = settings.toolbarIcons[i]; if (name !== "|") { var isHeader = (/h(\d)/.test(name)); menu += "
  • "+((isHeader) ? name : "")+"
  • "; } else { menu += "
  • |
  • "; } } toolbarMenu.append(menu); return this; }, /** * 工具栏图标事件处理器 * @returns {editormd} 返回editormd的实例对象 */ toolbarHandler : function() { var settings = this.settings; if (!settings.toolbar) { return ; } var _this = this; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var codeEditor = this.codeEditor; var codeMirror = this.codeMirror; var previewContainer = this.previewContainer; var toolbarIcons = this.toolbarIcons = toolbar.find("." + this.classPrefix + "menu .fa"); toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) { var icon = $(this); var name = icon.attr("name"); var cursor = codeEditor.getCursor(); var selection = codeEditor.getSelection(); if (name === "") { return ; } //console.log("toolbarIcons.click =>", name); var toolbarIconHandlers = _this.toolbarIconHandlers = { undo : function() { codeEditor.undo(); }, redo : function() { codeEditor.redo(); }, bold : function() { codeEditor.replaceSelection("**" + selection + "**"); if(selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 2); } }, del : function() { codeEditor.replaceSelection("~~" + selection + "~~"); if(selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 2); } }, italic : function() { codeEditor.replaceSelection("*" + selection + "*"); if(selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 1); } }, quote : function() { codeEditor.replaceSelection((selection === "") ? ["> " + selection, ""].join("\n") : "> " + selection); codeEditor.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2); }, h1 : function() { codeEditor.replaceSelection("#" + selection); }, h2 : function() { codeEditor.replaceSelection("##" + selection); }, h3 : function() { codeEditor.replaceSelection("###" + selection); }, h4 : function() { codeEditor.replaceSelection("####" + selection); }, h5 : function() { codeEditor.replaceSelection("#####" + selection); }, h6 : function() { codeEditor.replaceSelection("######" + selection); }, "list-ul" : function() { if (selection === "") { codeEditor.replaceSelection("- " + selection); } else { var selectionText = selection.split("\n"); for (var i = 0, len = selectionText.length; i < len; i++) { selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i]; } codeEditor.replaceSelection(selectionText.join("\n")); } }, "list-ol" : function() { if(selection === "") { codeEditor.replaceSelection("1. " + selection); } else { var selectionText = selection.split("\n"); for (var i = 0, len = selectionText.length; i < len; i++) { selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i]; } codeEditor.replaceSelection(selectionText.join("\n")); } }, hr : function() { codeEditor.replaceSelection("------------"); }, link : function() { codeEditor.replaceSelection("[" + selection + "](" + selection + " \""+selection+"\")"); }, picture : function() { codeEditor.replaceSelection("![" + selection + "](" + selection + " \""+selection+"\")"); }, code : function() { codeEditor.replaceSelection("`" + selection + "`"); if (selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 1); } }, "code-block-tab" : function() { codeEditor.replaceSelection(" " + selection); }, "code-block" : function() { codeEditor.replaceSelection(["```", selection, "```"].join("\n")); if (selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 3); } }, datetime : function() { var date = new Date(); codeEditor.replaceSelection(editormd.dateFormat() + " " + editormd.dateFormat("cn-week-day")); }, watch : function() { if (_this.settings.watch) { _this.unwatch(); } else { _this.watch(); } event.preventDefault(); return false; }, preview : function() { _this.previewing(); event.preventDefault(); return false; }, fullscreen : function() { _this.fullscreen(); event.preventDefault(); return false; }, info : function() { _this.showInfoDialog(); event.preventDefault(); return false; } }; toolbarIconHandlers[name](); codeEditor.focus(); event.preventDefault(); return false; }); return this; }, /** * 显示关于Editor.md * @returns {editormd} 返回editormd的实例对象 */ showInfoDialog : function() { $("html,body").css("overflow-x", "hidden"); this.editor.find("." + this.classPrefix + "dialog-info").fadeIn(); return this; }, /** * 隐藏关于Editor.md * @returns {editormd} 返回editormd的实例对象 */ hideInfoDialog : function() { $("html,body").css("overflow-x", ""); this.editor.find("." + this.classPrefix + "dialog-info").fadeOut(); return this; }, /** * 配置和初始化marked组件 * @returns {editormd} 返回editormd的实例对象 */ setMarked : function() { var marked = editormd.$marked; var markdownToC = this.markdownToC = []; marked.setOptions({ renderer : editormd.markedRenderer(markdownToC), gfm : true, tables : true, breaks : false, pedantic : false, sanitize : true, smartLists : true, smartypants : true }); return this; }, /** * 加载队列完成之后的显示处理 * @returns {editormd} 返回editormd的实例对象 */ loadedDisplay : function() { this.state.loaded = true; var _this = this; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var settings = this.settings; var codeEditor = this.codeEditor; var codeMirror = this.codeMirror; var previewContainer = this.previewContainer; var mouseOrTouch = editormd.mouseOrTouch; var htmlTextarea = this.htmlTextarea = editor.find("."+this.classNames.textarea.html); var markdownTextarea = this.markdownTextarea = editor.find("."+this.classNames.textarea.markdown); editor.css("background", "none"); this.saveToTextareas(); preview.show(); if (settings.previewCodeHighlight) { previewContainer.find("pre").addClass("prettyprint linenums"); prettyPrint(); } if (settings.flowChart) { previewContainer.find(".flowchart").flowChart(); } if (settings.sequenceDiagram) { previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); } if (settings.mathjax) { editormd.setMathJaxConfig(function() { editormd.loadMathJax(); }); } editor.data({ oldWidth : editor.outerWidth(), oldHeight : editor.outerHeight() }); this.resize(); $(window).resize(function(){ _this.resize(); }); $.proxy(this.settings.onload, this)(); editor.find("." + this.classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() { _this.hideInfoDialog(); }); var codeEditorBindScroll = function() { codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function() { var height = $(this).outerHeight(); var scrollTop = $(this).scrollTop(); var percent = (scrollTop / $(this)[0].scrollHeight); if (scrollTop === 0) { preview.scrollTop(0); } else if (scrollTop + height >= $(this)[0].scrollHeight) { preview.scrollTop(preview[0].scrollHeight); } else { preview.scrollTop(preview[0].scrollHeight * percent); } }); }; var codeEditorUnbindScroll = function() { codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove")); }; var previewBindScroll = function() { preview.bind(mouseOrTouch("scroll", "touchmove"), function() { var height = $(this).outerHeight(); var scrollTop = $(this).scrollTop(); var percent = (scrollTop / $(this)[0].scrollHeight); var codeView = codeMirror.find(".CodeMirror-scroll"); if(scrollTop === 0) { codeView.scrollTop(0); } else if (scrollTop + height >= $(this)[0].scrollHeight) { codeView.scrollTop(codeView[0].scrollHeight); } else { codeView.scrollTop(codeView[0].scrollHeight * percent); } }); }; var previewUnbindScroll = function() { preview.unbind(mouseOrTouch("scroll", "touchmove")); }; codeMirror.hover(codeEditorBindScroll, codeEditorUnbindScroll).bind("touchstart", codeEditorBindScroll).bind("touchend", codeEditorUnbindScroll); preview.hover(previewBindScroll, previewUnbindScroll).bind("touchstart", previewBindScroll).bind("touchend", previewUnbindScroll); codeEditor.on("change", function(cm, changeObj) { if (!settings.watch) { return ; } _this.saveToTextareas(); if (settings.previewCodeHighlight) { preview.find("pre").addClass("prettyprint linenums"); prettyPrint(); } if (settings.flowChart) { previewContainer.find(".flowchart").flowChart(); } if (settings.sequenceDiagram) { previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); } if (settings.mathjax) { MathJax.Hub.Queue(function () { previewContainer.find(".mathjax-code").each(function() { var mathjaxDoc = $(this).html().replace(/\$\$(.*)\$\$/, function(s1, s2) { return s2; }); var jaxScript = ""; $(this).html($(jaxScript)); //MathJax.Hub.Queue(["Typeset", MathJax.Hub, "mathjax-code"]); MathJax.Hub.Queue(["Typeset", MathJax.Hub, $(this)[0]]); }); }); } $.proxy(settings.onchange, _this)(); }); return this; }, /** * 设置编辑器的宽度 * @param {Number|String} width 编辑器宽度值 * @returns {editormd} 返回editormd的实例对象 */ width : function(width) { this.editor.css({ width : (typeof width === "number") ? width + "px" : width }); this.resize(); return this; }, /** * 设置编辑器的高度 * @param {Number|String} height 编辑器高度值 * @returns {editormd} 返回editormd的实例对象 */ height : function(height) { this.editor.css({ height : (typeof height === "number") ? height + "px" : height }); this.resize(); return this; }, /** * 调整编辑器的尺寸和布局 * @param {Number|String} [width=null] 编辑器宽度值 * @param {Number|String} [height=null] 编辑器高度值 * @returns {editormd} 返回editormd的实例对象 */ resize : function(width, height) { width = width || null; height = height || null; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var settings = this.settings; var infoDialog = this.infoDialog; var codeEditor = this.codeEditor; var codeMirror = this.codeMirror; if(width && height) { editor.css({ width : (typeof width === "number") ? width + "px" : width, height : (typeof height === "number") ? height + "px" : height }); } infoDialog.css({ top : (editor.height() - infoDialog.height()) / 2, left : (editor.width() - infoDialog.width()) / 2 }); if (settings.toolbar) { codeMirror.css("margin-top", toolbar.outerHeight()).outerHeight(editor.height() - toolbar.outerHeight()); } else { codeMirror.css("margin-top", 0).outerHeight(editor.height()); } if(this.settings.watch) { codeMirror.outerWidth(editor.width() / 2); preview.outerWidth(editor.width() / 2); if (settings.toolbar) { preview.css("top", toolbar.outerHeight()).outerHeight(editor.height() - toolbar.outerHeight()); } else { preview.css("top", 0).outerHeight(editor.height()); } } else { codeMirror.outerWidth(editor.width()); preview.hide(); } return this; }, /** * 分别将Markdown源码和解析的HTML源码保存到对应的textarea * @returns {editormd} 返回editormd的实例对象 */ saveToTextareas : function() { var settings = this.settings; var codeEditor = this.codeEditor; var previewContainer = this.previewContainer; codeEditor.save(); var markdownToC = this.markdownToC = []; var newMarkdownDoc = editormd.$marked(codeEditor.getValue(), {renderer : editormd.markedRenderer(markdownToC)}); this.markdownTextarea.html(codeEditor.getValue()); this.htmlTextarea.html(newMarkdownDoc); previewContainer.html(newMarkdownDoc); if (settings.toc) { editormd.markdownToCRenderer(markdownToC, previewContainer, settings.tocStartLevel); } return this; }, /** * 设置和传入编辑器的markdown源文档 * @param {String} md 要传入的markdown源文档 * @returns {editormd} 返回editormd的实例对象 */ setMarkdown : function(md) { this.codeEditor.setValue(md); this.saveToTextareas(); return this; }, /** * 获取编辑器的markdown源文档 * @returns {editormd} 返回editormd的实例对象 */ getMarkdown : function() { return this.codeEditor.getValue(); }, /** * 获取解析后的HTML源码 * @returns {editormd} 返回editormd的实例对象 */ getHTML : function() { return this.editor.find("." + this.classNames.textarea.html).val(); }, /** * 开启实时预览 * @returns {editormd} 返回editormd的实例对象 */ watch : function(callback) { callback = callback || function() {}; this.settings.watch = true; this.preview.show(); var watchIcon = this.settings.toolbarIconsClass.watch; var unWatchIcon = this.settings.toolbarIconsClass.unwatch; var icon = this.toolbar.find(".fa[name=watch]"); icon.parent().attr("title", this.settings.lang.toolbar.watch); icon.removeClass(unWatchIcon).addClass(watchIcon); this.codeMirror.css("border-right", "1px solid #ddd").outerWidth(this.editor.width() / 2); this.saveToTextareas().resize(); $.proxy(callback, this)(); return this; }, /** * 关闭实时预览 * @returns {editormd} 返回editormd的实例对象 */ unwatch : function(callback) { callback = callback || function() {}; this.settings.watch = false; this.preview.hide(); var watchIcon = this.settings.toolbarIconsClass.watch; var unWatchIcon = this.settings.toolbarIconsClass.unwatch; var icon = this.toolbar.find(".fa[name=watch]"); icon.parent().attr("title", this.settings.lang.toolbar.unwatch); icon.removeClass(watchIcon).addClass(unWatchIcon); this.codeMirror.css("border-right", "none").outerWidth(this.editor.width()); this.resize(); $.proxy(callback, this)(); return this; }, /** * 显示编辑器 * @param {Function} [callback=function()] 回调函数 * @returns {editormd} 返回editormd的实例对象 */ show : function(callback) { callback = callback || function() {}; var _this = this; this.editor.show(function(){ $.proxy(callback, _this)(); }); return this; }, /** * 隐藏编辑器 * @param {Function} [callback=function()] 回调函数 * @returns {editormd} 返回editormd的实例对象 */ hide : function(callback) { callback = callback || function() {}; var _this = this; this.editor.hide(function(){ $.proxy(callback, _this)(); }); return this; }, /** * 隐藏编辑器部分,只预览HTML * @returns {editormd} 返回editormd的实例对象 */ previewing : function() { var _this = this; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var settings = this.settings; var codeMirror = this.codeMirror; if (settings.toolbar) { toolbar.toggle(); } toolbar.find(".fa[name=preview]").toggleClass("active"); codeMirror.toggle(); if(codeMirror.is(":hidden")) { this.state.preview = true; if(this.state.fullscreen) { preview.css("background", "#fff"); } preview.show().css({ top : 0, //borderTop : "none", width : editor.width(), height : editor.height() }); } else { this.previewed(); } $(window).keyup(function(event) { if (event.keyCode === 27) { _this.previewed(); } }); }, /** * 显示编辑器部分,退出只预览HTML * @returns {editormd} 返回editormd的实例对象 */ previewed : function() { var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var settings = this.settings; var codeMirror = this.codeMirror; this.state.preview = false; codeMirror.show(); if (settings.toolbar) { toolbar.show(); } if(this.settings.watch) { preview.show(); } else { preview.hide(); } preview.css({ background : null, //borderTop : "1px solid #ddd", width : editor.width() / 2, height : editor.height() - toolbar.outerHeight(), top : (settings.toolbar) ? toolbar.outerHeight() : 0 }); return this; }, /** * 编辑器全屏显示 * @returns {editormd} 返回editormd的实例对象 */ fullscreen : function() { var _this = this; var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var fullscreenClass = this.classPrefix + "fullscreen"; toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active"); if (!editor.hasClass(fullscreenClass)) { this.state.fullscreen = true; $("html,body").css("overflow", "hidden"); editor.css({ position : "fixed", top : 0, left : 0, margin : 0, border : "none", width : $(window).width(), height : $(window).height() }).addClass(fullscreenClass); this.resize(); $.proxy(this.settings.onfullscreen, this)(); } else { this.fullscreenExit(); } $(window).keyup(function(event) { if (_this.state.preview) { return ; } if (event.keyCode === 27) { _this.fullscreenExit(); } }); return this; }, /** * 编辑器退出全屏显示 * @returns {editormd} 返回editormd的实例对象 */ fullscreenExit : function() { var editor = this.editor; var fullscreenClass = this.classPrefix + "fullscreen"; this.state.fullscreen = false; this.toolbar.find(".fa[name=fullscreen]").parent().removeClass("active"); $("html,body").css("overflow", ""); editor.css({ position : "", top : "", left : "", margin : "0 auto", width : editor.data("oldWidth"), height : editor.data("oldHeight"), border : "1px solid #ddd" }).removeClass(fullscreenClass); this.resize(); $.proxy(this.settings.onfullscreenExit, this)(); return this; } }; editormd.fn.init.prototype = editormd.fn; /** * 自定义marked的解析器 * @param {Array} markdownToC 传入用于接收TOC的数组 * @returns {Renderer} markedRenderer 返回marked的Renderer自定义对象 */ editormd.markedRenderer = function(markdownToC) { var marked = editormd.$marked; var markedRenderer = new marked.Renderer(); markdownToC = markdownToC || []; markedRenderer.heading = function(text, level, raw) { var escapedText = text.toLowerCase().replace(/[^\w]+/g, "-"); //console.log("escapedText", text, escapedText, level, raw); var toc = { text : text, level : level, slug : escapedText }; markdownToC.push(toc); return "" + text + ""; }; var mathJaxList = []; markedRenderer.paragraph = function(text) { var isMathJax = /\$\$(.*)\$\$/.test(text); var mathjaxClassName = (isMathJax) ? " class=\"mathjax-code\"" : ""; var isToC = /^\[TOC\]$/.test(text); if (isMathJax) { mathJaxList.push(text); } return (isToC) ? "
    " : "" + text + "

    \n"; }; markedRenderer.code = function (code, lang, escaped) { if (lang === "seq") { return "
    " + code + "
    "; } else if ( lang === "flow") { return "
    " + code + "
    "; } else { return marked.Renderer.prototype.code.apply(this, arguments); } }; return markedRenderer; }; /** * 生成TOC(Table of Contents) * @param {Array} toc 从marked获取的TOC数组列表 * @param {Element} container 插入TOC的容器元素 * @param {Integer} startLevel Hx 起始层级 */ editormd.markdownToCRenderer = function(toc, container, startLevel) { var html = ""; var lastLevel = 0; startLevel = startLevel || 2; for (var i = 0, len = toc.length; i < len; i++) { var text = toc[i].text; var level = toc[i].level; if (level < startLevel) { continue; } if (level > lastLevel) { html += ""; } else if (level < lastLevel) { html += (new Array(lastLevel - level + 2)).join(""); } else { html += ""; } html += "
  • " + text + "