/* * Editor.md * @file editormd.js * @version v1.1.0 * @description A simple online markdown editor. * @license MIT License * @author Pandao * {@link https://github.com/pandao/editor.md} * @updateTime 2015-02-15 */ /** * @fileOverview Editor.md * @author pandao * @version 1.1.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 $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto; if (typeof ($) === "undefined") { return ; } var editormd = function (id, options) { return new editormd.fn.init(id, options); }; editormd.title = editormd.$name = "Editor.md"; editormd.version = "1.1.0"; editormd.homePage = "https://pandao.github.io/editor.md/"; editormd.classPrefix = "editormd-"; editormd.defaults = { mode : "gfm", //gfm or markdown markdown : "", width : "100%", height : "100%", path : "./lib/", watch : true, placeholder : "now coding markdown...", readOnly : false, lineNumbers : true, styleActiveLine : true, // 高亮显示当前行 dialogLockScreen : true, dialogShowMask : true, dialogDraggable : true, dialogMaskBgColor : "#fff", dialogMaskOpacity : 0.1, onload : function() {}, onchange : function() {}, onfullscreen : function() {}, onfullscreenExit : function() {}, imageUpload : false, imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL : "", crossDomainUpload : false, uploadCallbackURL : "", saveHTMLToTextarea : false, toc : true, tocStartLevel : 1, // 表示从H1开始生成ToC fontSize : "13px", htmlDecode : false, // 是否开启HTML标签识别 tex : false, flowChart : false, // flowChart.js only support IE9+ sequenceDiagram : false, // sequenceDiagram.js only support IE9+ previewCodeHighlight : true, inRequirejs : false, toolbar : true, toolbarIcons : function() { return [ "undo", "redo", "|", "bold", "del", "italic", "quote", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "anchor", "image", "code", "code-block-tab", "code-block", "datetime", "|", "watch", "preview", "fullscreen", "clear", "|", "info" ] }, toolbarTitles : {}, toolbarHandlers : {}, // 工具栏按钮的点击后事件处理 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", anchor : "fa-anchor", image : "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", clear : "fa-eraser", info : "fa-info-circle" }, toolbarIconTexts : {}, lang : { name : "zh-cn", description : "开源在线Markdown编辑器
A simple markdown doucment online editor.", toolbar : { undo : "撤销(Ctrl+Z)", redo : "重做(Ctrl+Y)", bold : "粗体", del : "删除线", italic : "斜体", quote : "引用", h1 : "标题1", h2 : "标题2", h3 : "标题3", h4 : "标题4", h5 : "标题5", h6 : "标题6", "list-ul" : "无序列表", "list-ol" : "有序列表", hr : "横线", link : "链接", anchor : "锚点", image : "图片", code : "行内代码", "code-block-tab" : "预格式文本 / 代码块(缩进风格)", "code-block" : "代码块(多语言风格)", datetime : "日期时间", watch : "关闭实时预览", unwatch : "开启实时预览", preview : "全窗口预览HTML(可按ESC还原)", fullscreen : "全屏(按ESC还原)", clear : "清空", info : "关于" + editormd.title }, buttons : { enter : "确定", cancel : "取消" }, dialog : { link : { title : "添加链接", url : "链接地址", urlTitle : "链接标题", urlEmpty : "错误:请填写链接地址。", titleEmpty : "错误:请填写链接标题。" }, anchor : { title : "添加锚点链接", name : "锚点名称", url : "链接地址", urlTitle : "链接标题", nameEmpty: "错误:锚点名称不能为空。", titleEmpty : "错误:请填写锚点链接标题。", urlEmpty : "错误:请填写锚点链接地址。" }, image : { title : "添加图片", url : "图片地址", link : "图片链接", alt : "图片描述", uploadButton : "本地上传", imageURLEmpty : "错误:图片地址不能为空。", uploadFileEmpty : "错误:上传的图片不能为空。", formatNotAllowed : "错误:只允许上传图片文件,允许上传的图片文件格式有:" }, tabCodeBlock : { title : "添加预格式文本或代码块", codeEmptyAlert : "错误:请填写预格式文本或代码的内容。" }, codeBlock : { title : "添加代码块", selectLabel : "代码语言:", selectDefaultText : "请选择代码语言", otherLanguage : "其他语言", unselectedLanguageAlert : "错误:请选择代码所属的语言类型。", codeEmptyAlert : "错误:请填写代码内容。" } } } }; editormd.classNames = { tex : editormd.classPrefix + "tex" }; editormd.dialogZindex = 99999; editormd.$katex = null; 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); this.id = id; this.lang = settings.lang; var classNames = this.classNames = { textarea : { html : this.classPrefix + "html-textarea", markdown : this.classPrefix + "markdown-textarea" } }; if (!editor.hasClass("editormd")) { editor.addClass("editormd"); } editor.css({ width : (typeof settings.width === "number") ? settings.width + "px" : settings.width, height : (typeof settings.height === "number") ? settings.height + "px" : settings.height }); var markdownTextarea = this.markdownTextarea = editor.children("textarea"); if (markdownTextarea.length < 1) { editor.append(""); markdownTextarea = this.markdownTextarea = editor.children("textarea"); } markdownTextarea.addClass(classNames.textarea.markdown).attr("name", id + "-markdown-doc").attr("placeholder", settings.placeholder); var appendElements = [ (!settings.readOnly) ? "" : "", ( (settings.saveHTMLToTextarea) ? "" : "" ), "
", "
", "
" ].join("\n"); editor.append(appendElements).addClass(classPrefix + "vertical"); if (settings.markdown !== "") { markdownTextarea.val(settings.markdown); } this.htmlTextarea = editor.find("." + classNames.textarea.html); this.preview = editor.find("." + classPrefix + "preview"); this.previewContainer = this.preview.children("." + classPrefix + "preview-container"); this.toolbarIconHandlers = {}; if (!settings.inRequirejs) { this.loadQueues(); } else { _this.setCodeMirror(); _this.setToolbar(); _this.setMarked().loadedDisplay(); } return this; }, /** * 所需组件加载队列 * @returns {editormd} 返回editormd的实例对象 */ loadQueues : function() { var _this = this; var settings = this.settings; var loadPath = settings.path; var loadFlowChartOrSequenceDiagram = function() { if (editormd.isIE8) { _this.setMarked().loadedDisplay(); return ; } 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(); } }; editormd.loadCSS(loadPath + "codemirror/codemirror.min"); editormd.loadScript(loadPath + "codemirror/codemirror.min", function() { editormd.$CodeMirror = CodeMirror; editormd.loadScript(loadPath + "codemirror/modes.min", function() { editormd.loadScript(loadPath + "codemirror/addons.min", function() { _this.setCodeMirror(); _this.setToolbar(); editormd.loadScript(loadPath + "marked.min", function() { editormd.$marked = marked; if (settings.previewCodeHighlight) { editormd.loadScript(loadPath + "prettify.min", function() { loadFlowChartOrSequenceDiagram(); }); } else { loadFlowChartOrSequenceDiagram(); } }); }); }); }); return this; }, /** * 配置和初始化CodeMirror组件 * @returns {editormd} 返回editormd的实例对象 */ setCodeMirror : function() { var settings = this.settings; var codeMirrorConfig = { mode : settings.mode, theme : "default", tabSize : 4, dragDrop : false, autofocus : true, readOnly : (settings.readOnly) ? "nocursor" : false, indentUnit : 4, lineNumbers : settings.lineNumbers, lineWrapping : true, matchBrackets : true, indentWithTabs : true, styleActiveLine : settings.styleActiveLine, styleSelectedText : true, autoCloseBrackets : true, showTrailingSpace : true, highlightSelectionMatches : { showToken: /\w/ } }; this.codeEditor = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig); this.codeMirror = this.editor.find(".CodeMirror"); this.codeMirror.css({ fontSize : this.settings.fontSize, width : (!settings.watch) ? "100%" : "50%" }); return this; }, /** * 显示工具栏 * @returns {editormd} 返回editormd的实例对象 */ showToolbar : function() { var settings = this.settings; if(settings.readOnly) return ; settings.toolbar = true; this.toolbar.show(); this.resize(); return this; }, /** * 隐藏工具栏 * @returns {editormd} 返回editormd的实例对象 */ hideToolbar : function() { var settings = this.settings; settings.toolbar = false; this.toolbar.hide(); this.resize(); return this; }, /** * 配置和初始化工具栏 * @returns {editormd} 返回editormd的实例对象 */ setToolbar : function() { var settings = this.settings; if(settings.readOnly) return ; var editor = this.editor; var preview = this.preview; var classPrefix = this.classPrefix; editor.append('
'); var toolbar = this.toolbar = editor.find("." + classPrefix + "toolbar"); if (!settings.toolbar) { toolbar.hide(); return ; } else { toolbar.show(); } var icons = settings.toolbarIcons(); var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = ""; for (var i = 0, len = icons.length; i < len; i++) { var name = icons[i]; if (name !== "|") { var isHeader = (/h(\d)/.test(name)); var index = name; if (name === "watch" && !settings.watch) { index = "unwatch"; } var title = settings.lang.toolbar[index]; var iconTexts = settings.toolbarIconTexts[index]; var iconClass = settings.toolbarIconsClass[index]; title = (typeof title === "undefined") ? "" : title; iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts; iconClass = (typeof iconClass === "undefined") ? "" : iconClass; menu += "
  • " + ""+((isHeader) ? name : ( (iconClass === "") ? iconTexts : "") ) + "
  • "; } else { menu += "
  • |
  • "; } } toolbarMenu.html(menu); this.setToolbarHandler(); return this; }, /** * 工具栏图标事件处理对象序列 * @param {String} name 要获取的事件处理器名称 * @returns {Object} 返回处理对象序列 */ getToolbarHandles : function(name) { var _this = this; var settings = this.settings; var lang = settings.lang; var editor = this.editor; var codeEditor = this.codeEditor; var cursor = codeEditor.getCursor(); var selection = codeEditor.getSelection(); var classPrefix = this.classPrefix; var dialogLockScreen = function() { if (settings.dialogLockScreen) { $("html,body").css("overflow", "hidden"); } }; var dialogShowMask = function(dialog) { dialog.css({ top : ($(window).height() - dialog.height()) / 2 + "px", left : ($(window).width() - dialog.width()) / 2 + "px" }); if (settings.dialogShowMask) { editor.find("." + classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show(); } }; var toolbarHandlers = this.toolbarHandlers = { 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() { var linkLang = lang.dialog.link; var linkDialogName = classPrefix + "link-dialog", linkDialog; if (editor.find("." + linkDialogName).length > 0) { linkDialog = editor.find("." + linkDialogName); linkDialog.find("[data-url]").val("http://"); linkDialog.find("[data-title]").val(selection); dialogShowMask(linkDialog); dialogLockScreen(); linkDialog.show(); } else { var linkDialogHTML = "
    " + "" + "" + "
    " + "" + "" + "
    " + "
    "; linkDialog = _this.createDialog({ title : linkLang.title, width : 380, height : 210, content : linkDialogHTML, mask : settings.dialogShowMask, drag : settings.dialogDraggable, lockScreen : settings.dialogLockScreen, maskStyle : { opacity : settings.dialogMaskOpacity, backgroundColor : settings.dialogMaskBgColor }, buttons : { enter : [lang.buttons.enter, function() { var url = this.find("[data-url]").val(); var title = this.find("[data-title]").val(); if (url === "http://" || url === "") { alert(linkLang.urlEmpty); return false; } if (title === "") { alert(linkLang.titleEmpty); return false; } codeEditor.replaceSelection("[" + title + "](" + url + " \""+title+"\")"); this.hide().lockScreen(false).hideMask(); return false; }], cancel : [lang.buttons.cancel, function() { this.hide().lockScreen(false).hideMask(); return false; }] } }); } }, anchor : function() { var anchorLang = lang.dialog.anchor; var anchorDialogName = classPrefix + "anchor-dialog", anchorDialog; if (editor.find("." + anchorDialogName).length > 0) { anchorDialog = editor.find("." + anchorDialogName); anchorDialog.find("[data-name]").val(""); anchorDialog.find("[data-url]").val("http://"); anchorDialog.find("[data-title]").val(selection); dialogShowMask(anchorDialog); dialogLockScreen(); anchorDialog.show(); } else { var anchorDialogHTML = "
    " + "" + "" + "
    " + "" + "" + "
    " + "" + "" + "
    " + "
    "; anchorDialog = _this.createDialog({ name : anchorDialogName, title : anchorLang.title, width : 380, height : 250, content : anchorDialogHTML, mask : settings.dialogShowMask, drag : settings.dialogDraggable, lockScreen : settings.dialogLockScreen, maskStyle : { opacity : settings.dialogMaskOpacity, backgroundColor : settings.dialogMaskBgColor }, buttons : { enter : [lang.buttons.enter, function() { var name = this.find("[data-name]").val(); var url = this.find("[data-url]").val(); var title = this.find("[data-title]").val(); if (name === "") { alert(anchorLang.nameEmpty); return false; } if (url === "http://" || url === "") { alert(anchorLang.urlEmpty); return false; } if (title === "") { alert(anchorLang.titleEmpty); return false; } codeEditor.replaceSelection("[" + title + "][" + name + "]\n[" + name + "]: " + url + ""); if (selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 1); } this.hide().lockScreen(false).hideMask(); return false; }], cancel : [lang.buttons.cancel, function() { this.hide().lockScreen(false).hideMask(); return false; }] } }); } }, image : function() { var imageLang = lang.dialog.image; var iframeName = classPrefix + "image-iframe"; var imageDialogName = classPrefix + "image-dialog", imageDialog; if (editor.find("." + imageDialogName).length > 0) { imageDialog = editor.find("." + imageDialogName); imageDialog.find("[type=\"text\"]").val(""); imageDialog.find("[type=\"file\"]").val(""); imageDialog.find("[data-link]").val("http://"); dialogShowMask(imageDialog); dialogLockScreen(); imageDialog.show(); } else { var guid = (new Date).getTime(); var action = settings.imageUploadURL + "?guid=" + guid; if (settings.crossDomainUpload) { action += "&callback=" + settings.uploadCallbackURL + "&dialog_id=editormd-image-dialog-" + guid; } var imageDialogHTML = ( (settings.imageUpload) ? "
    " : "
    " ) + ( (settings.imageUpload) ? "" : "" ) + "" + "" + (function(){ return (settings.imageUpload) ? "
    " + "" + "" + "
    " : ""; })() + "
    " + "" + "" + "
    " + "" + "" + "
    " + ( (settings.imageUpload) ? "" : "
    "); //var imageFooterHTML = ""; imageDialog = _this.createDialog({ title : imageLang.title, width : (settings.imageUpload) ? 465 : 380, height : 250, name : imageDialogName, content : imageDialogHTML, mask : settings.dialogShowMask, drag : settings.dialogDraggable, lockScreen : settings.dialogLockScreen, maskStyle : { opacity : settings.dialogMaskOpacity, backgroundColor : settings.dialogMaskBgColor }, buttons : { enter : [lang.buttons.enter, function() { var url = this.find("[data-url]").val(); var alt = this.find("[data-alt]").val(); var link = this.find("[data-link]").val(); if (url === "") { alert(imageLang.imageURLEmpty); return false; } if (link === "" || link === "http://") { codeEditor.replaceSelection("![" + alt + "](" + url + " \"" + alt + "\")"); } else { codeEditor.replaceSelection("[![" + alt + "](" + url + " \"" + alt + "\")](" + link + " \"" + alt + "\")"); } if (alt === "") { codeEditor.setCursor(cursor.line, cursor.ch + 2); } this.hide().lockScreen(false).hideMask(); return false; }], cancel : [lang.buttons.cancel, function() { this.hide().lockScreen(false).hideMask(); return false; }] } }); imageDialog.attr("id", classPrefix + "image-dialog-" + guid); } var fileInput = imageDialog.find("[name=\"" + classPrefix + "image-file\"]"); fileInput.bind("change", function() { var fileName = fileInput.val(); var isImage = new RegExp("(\\.(" + settings.imageFormats.join("|") + "))$"); // /(\.(webp|jpg|jpeg|gif|bmp|png))$/ if (fileName === "") { alert(imageLang.uploadFileEmpty); } else if (!isImage.test(fileName)) { alert(imageLang.formatNotAllowed + settings.imageFormats.join(", ")); } else { imageDialog.loading(true); var submitHandler = function() { if (settings.crossDomainUpload) { imageDialog.loading(false); return ; } var uploadIframe = document.getElementById(iframeName); uploadIframe.onload = function() { imageDialog.loading(false); var json = uploadIframe.contentWindow.document.body.innerHTML; json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")"); if (json.success === 1) { imageDialog.find("[data-url]").val(json.url); } else { alert(json.message); } return false; }; }; imageDialog.find("[type=\"submit\"]").bind(editormd.mouseOrTouch("click", "touchend"), submitHandler).trigger("click"); } return false; }); }, code : function() { codeEditor.replaceSelection("`" + selection + "`"); if (selection === "") { codeEditor.setCursor(cursor.line, cursor.ch + 1); } }, "code-block-tab" : function() { var tabCodeBlockDialogName = classPrefix + "dialog-tab-code-block", tabCodeBlockDialog; if (editor.find("." + tabCodeBlockDialogName).length > 0) { tabCodeBlockDialog = editor.find("." + tabCodeBlockDialogName); tabCodeBlockDialog.find("textarea").val(""); dialogShowMask(tabCodeBlockDialog); dialogLockScreen(); tabCodeBlockDialog.show(); } else { var tabCodeBlockDialogHTML = ""; tabCodeBlockDialog = _this.createDialog({ name : tabCodeBlockDialogName, title : lang.dialog.tabCodeBlock.title, width : 750, height : 470, mask : settings.dialogShowMask, drag : settings.dialogDraggable, content : tabCodeBlockDialogHTML, lockScreen : settings.dialogLockScreen, maskStyle : { opacity : settings.dialogMaskOpacity, backgroundColor : settings.dialogMaskBgColor }, buttons : { enter : [lang.buttons.enter, function() { var codeTexts = this.find("textarea").val(); if (codeTexts === "") { alert(lang.dialog.tabCodeBlock.codeEmptyAlert); return false; } codeTexts = codeTexts.split("\n"); for (var i in codeTexts) { codeTexts[i] = " " + codeTexts[i]; } codeEditor.replaceSelection(codeTexts.join("\n")); this.hide().lockScreen(false).hideMask(); return false; }], cancel : [lang.buttons.cancel, function() { this.hide().lockScreen(false).hideMask(); return false; }] } }); } }, "code-block" : function() { var codeBlockDialogName = classPrefix + "dialog-code-block", codeBlockDialog; if (editor.find("." + codeBlockDialogName).length > 0) { codeBlockDialog = editor.find("." + codeBlockDialogName); codeBlockDialog.find("option:first").attr("selected", "selected"); codeBlockDialog.find("textarea").val(""); dialogShowMask(codeBlockDialog); dialogLockScreen(); codeBlockDialog.show(); } else { var codeBlockDialogHTML = "
    " + lang.dialog.codeBlock.selectLabel + "" + "
    " + ""; codeBlockDialog = _this.createDialog({ name : codeBlockDialogName, title : lang.dialog.codeBlock.title, width : 752, height : 565, mask : settings.dialogShowMask, drag : settings.dialogDraggable, content : codeBlockDialogHTML, lockScreen : settings.dialogLockScreen, maskStyle : { opacity : settings.dialogMaskOpacity, backgroundColor : settings.dialogMaskBgColor }, buttons : { enter : [lang.buttons.enter, function() { var codeTexts = this.find("textarea").val(); var langName = this.find("select").val(); if (langName === "") { alert(lang.dialog.codeBlock.unselectedLanguageAlert); return false; } if (codeTexts === "") { alert(lang.dialog.codeBlock.codeEmptyAlert); return false; } langName = (langName === "other") ? "" : langName; codeEditor.replaceSelection(["```" + langName, codeTexts, "```"].join("\n")); if (langName === "") { codeEditor.setCursor(cursor.line, cursor.ch + 3); } this.hide().lockScreen(false).hideMask(); return false; }], cancel : [lang.buttons.cancel, function() { this.hide().lockScreen(false).hideMask(); return false; }] } }); var langSelect = codeBlockDialog.find("select"); for (var key in editormd.codeLanguages) { var codeLang = editormd.codeLanguages[key]; langSelect.append(""); } langSelect.append(""); } }, datetime : function() { var date = new Date(); var langName = settings.lang.name; codeEditor.replaceSelection(editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day")); }, watch : function() { _this[_this.settings.watch ? "unwatch" : "watch"](); }, preview : function() { _this.previewing(); }, fullscreen : function() { _this.fullscreen(); }, clear : function() { _this.clear(); }, info : function() { _this.showInfoDialog(); } }; return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers; }, /** * 工具栏图标事件处理器 * @returns {editormd} 返回editormd的实例对象 */ setToolbarHandler : function() { var _this = this; var settings = this.settings; if (!settings.toolbar || settings.readOnly) { return ; } var editor = this.editor; var preview = this.preview; var toolbar = this.toolbar; var codeEditor = this.codeEditor; var codeMirror = this.codeMirror; var classPrefix = this.classPrefix; var previewContainer = this.previewContainer; var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu a"); var toolbarIconHandlers = _this.getToolbarHandles(); toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) { var icon = $(this).children(".fa"); var name = icon.attr("name"); var cursor = codeEditor.getCursor(); var selection = codeEditor.getSelection(); if (name === "") { return ; } _this.activeIcon = icon; if (typeof toolbarIconHandlers[name] !== "undefined") { toolbarIconHandlers[name](); } else { if (typeof settings.toolbarHandlers[name] !== "undefined") { $.proxy(settings.toolbarHandlers[name], _this)(codeEditor, icon, cursor, selection); } } if (name !== "link" && name !== "anchor" && name !== "image" && name !== "code-block" && name !== "code-block-tab" && name !== "watch" && name !== "preview" && name !== "fullscreen" && name !== "info") { codeEditor.focus(); } return false; }); return this; }, /** * 动态创建对话框 * @param {Object} options 配置项键值对 Key/Value * @returns {dialog} 返回创建的dialog的jQuery实例对象 */ createDialog : function(options) { var defaults = { name : "", width : 420, height: 240, title : "", drag : true, closed : true, content : "", mask : true, maskStyle : { backgroundColor : "#fff", opacity : 0.1 }, lockScreen : true, footer : true, buttons : false }; options = $.extend(true, defaults, options); var editor = this.editor; var classPrefix = this.classPrefix; var guid = (new Date).getTime(); var dialogName = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name); var mouseOrTouch = editormd.mouseOrTouch; var html = "
    "; if (options.title !== "") { html += "
    "; html += "" + options.title + ""; html += "
    "; } if (options.closed) { html += ""; } html += "
    " + options.content; if (options.footer || typeof options.footer === "string") { html += "
    " + ( (typeof options.footer === "boolean") ? "" : options.footer) + "
    "; } html += "
    "; html += "
    "; html += "
    "; html += "
    "; editor.append(html); var dialog = editor.find("." + dialogName); dialog.lockScreen = function(lock) { if (options.lockScreen) { $("html,body").css("overflow", (lock) ? "hidden" : ""); } return dialog; }; dialog.showMask = function() { if (options.mask) { editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show(); } return dialog; }; dialog.hideMask = function() { if (options.mask) { editor.find("." + classPrefix + "mask").hide(); } return dialog; }; dialog.loading = function(show) { var loading = dialog.find("." + classPrefix + "dialog-mask"); loading[(show) ? "show" : "hide"](); return dialog; }; dialog.lockScreen(true).showMask(); dialog.show().css({ zIndex : editormd.dialogZindex, border : (editormd.isIE8) ? "1px solid #ddd" : "", width : (typeof options.width === "number") ? options.width + "px" : options.width, height : (typeof options.height === "number") ? options.height + "px" : options.height }); var dialogPosition = function(){ dialog.css({ top : ($(window).height() - dialog.height()) / 2 + "px", left : ($(window).width() - dialog.width()) / 2 + "px" }); }; dialogPosition(); $(window).resize(dialogPosition); dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() { dialog.hide().lockScreen(false).hideMask(); }); if (typeof options.buttons == "object") { var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer"); for (var key in options.buttons) { var btn = options.buttons[key]; var btnClassName = classPrefix + key + "-btn"; footer.append(""); btn[1] = $.proxy(btn[1], dialog); footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]); } } if (options.title !== "" && options.drag) { var posX, posY; var dialogHeader = dialog.children("." + classPrefix + "dialog-header"); if (!options.mask) { dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){ editormd.dialogZindex += 2; dialog.css("z-index", editormd.dialogZindex); }); } dialogHeader.mousedown(function(e) { e = e || window.event; //IE posX = e.clientX - parseInt(dialog[0].style.left); posY = e.clientY - parseInt(dialog[0].style.top); document.onmousemove = moveAction; }); var userCanSelect = function (obj) { obj.removeClass(classPrefix + "user-unselect").off("selectstart"); } var userUnselect = function (obj) { obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE return false; }); } var moveAction = function (e) { e = e || window.event; //IE var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top); if( nowLeft >= 0 ) { if( nowLeft + dialog.width() <= $(window).width()) { left = e.clientX - posX; } else { left = $(window).width() - dialog.width(); document.onmousemove = null; } } else { left = 0; document.onmousemove = null; } if( nowTop >= 0 ) { top = e.clientY - posY; } else { top = 0; document.onmousemove = null; } document.onselectstart = function() { return false; }; userUnselect($("body")); userUnselect(dialog); dialog[0].style.left = left + "px"; dialog[0].style.top = top + "px"; } document.onmouseup = function() { userCanSelect($("body")); userCanSelect(dialog); document.onselectstart = null; document.onmousemove = null; }; dialogHeader.touchDraggable = function() { var offset = null; var start = function(e) { var orig = e.originalEvent; var pos = $(this).parent().position(); offset = { x : orig.changedTouches[0].pageX - pos.left, y : orig.changedTouches[0].pageY - pos.top }; }; var move = function(e) { e.preventDefault(); var orig = e.originalEvent; $(this).parent().css({ top : orig.changedTouches[0].pageY - offset.y, left : orig.changedTouches[0].pageX - offset.x }); }; this.bind("touchstart", start).bind("touchmove", move); }; dialogHeader.touchDraggable(); } editormd.dialogZindex += 2; return dialog; }, /** * 创建关于Editor.md的对话框 * @returns {editormd} 返回editormd的实例对象 */ createInfoDialog : function() { var _this = this; var editor = this.editor; var classPrefix = this.classPrefix; var infoDialogHTML = [ "
    ", "
    ", "

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

    ", "

    " + this.lang.description + "

    ", "

    Home page: " + editormd.homePage + "

    ", "

    License: MIT

    ", "
    ", "", "
    " ].join("\n"); editor.append(infoDialogHTML); var infoDialog = this.infoDialog = editor.find("." + classPrefix + "dialog-info"); infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() { _this.hideInfoDialog(); }); infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : ""); return this; }, /** * 显示关于Editor.md * @returns {editormd} 返回editormd的实例对象 */ showInfoDialog : function() { $("html,body").css("overflow-x", "hidden"); var _this = this; var editor = this.editor; var classPrefix = this.classPrefix; var infoDialog = this.infoDialog = editor.find("." + classPrefix + "dialog-info"); if (infoDialog.length < 1) { this.createInfoDialog(); } infoDialog.show(); var infoDialogPosition = function() { infoDialog.css({ top : ($(window).height() - infoDialog.height()) / 2 + "px", left : ($(window).width() - infoDialog.width()) / 2 + "px" }); }; infoDialogPosition(); $(window).resize(infoDialogPosition); return this; }, /** * 隐藏关于Editor.md * @returns {editormd} 返回editormd的实例对象 */ hideInfoDialog : function() { $("html,body").css("overflow-x", ""); this.infoDialog.hide(); 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 : true, pedantic : false, sanitize : (this.settings.htmlDecode) ? false : true, // 关闭忽略HTML标签,即开启识别HTML标签,默认为false smartLists : true, smartypants : true }); return this; }, /** * 编辑器界面重建,用于动态语言包或模块加载等 * @returns {editormd} 返回editormd的实例对象 */ recreateEditor : function() { var _this = this; var editor = this.editor; var settings = this.settings; var toolbarIcons = settings.toolbarIcons(); if (typeof this.infoDialog !== "undefined") { this.infoDialog.remove(); } if (typeof toolbarIcons["info"] !== "undefined") { createInfoDialog(); } if (!settings.readOnly) { this.getToolbarHandles(); this.setToolbar(); } this.resize(); return this; }, /** * 加载队列完成之后的显示处理 * @returns {editormd} 返回editormd的实例对象 */ loadedDisplay : function() { this.state.loaded = true; var _this = this; var editor = this.editor; var preview = this.preview; var settings = this.settings; var codeEditor = this.codeEditor; var codeMirror = this.codeMirror; var previewContainer = this.previewContainer; var mouseOrTouch = editormd.mouseOrTouch; editor.css("background", "none"); this.saveToTextareas(); if(settings.watch) { preview.show(); } var codeHighlight = function() { if (settings.previewCodeHighlight) { previewContainer.find("pre").addClass("prettyprint linenums"); prettyPrint(); } }; var flowChartAndSequenceDiagramHandle = function() { if (editormd.isIE8) return ; if (settings.flowChart) { previewContainer.find(".flowchart").flowChart(); } if (settings.sequenceDiagram) { previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); } }; codeHighlight(); flowChartAndSequenceDiagramHandle(); var katexHandle = function() { previewContainer.find("." + editormd.classNames.tex).each(function(){ var tex = $(this); editormd.$katex.render(tex.html(), tex[0]); }); }; if (settings.tex) { if (!settings.inRequirejs) { editormd.loadKaTeX(function(){ editormd.$katex = katex; katexHandle(); }); } else { katexHandle(); } } editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto this.resize(); $(window).resize(function(){ _this.resize(); }); $.proxy(settings.onload, this)(); var codeEditorBindScroll = function() { codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function() { var height = $(this).height(); 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).height(); 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.bind({ mouseover : codeEditorBindScroll, mouseout : codeEditorUnbindScroll, touchstart : codeEditorBindScroll, touchend : codeEditorUnbindScroll }); preview.bind({ mouseover : previewBindScroll, mouseout : previewUnbindScroll, touchstart : previewBindScroll, touchend : previewUnbindScroll }); codeEditor.on("change", function(cm, changeObj) { if (!settings.watch) { return ; } _this.saveToTextareas(); codeHighlight(); flowChartAndSequenceDiagramHandle(); if (settings.tex) { katexHandle(); } $.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 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 }); } if (settings.toolbar && !settings.readOnly) { codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height()); } else { codeMirror.css("margin-top", 0).height(editor.height()); } codeMirror.find(".CodeMirror-gutters").height(codeMirror.height()); if(settings.watch) { codeMirror.width(editor.width() / 2); preview.width(editor.width() / 2); if (settings.toolbar && !settings.readOnly) { preview.css("top", toolbar.height()).height(editor.height() - toolbar.height()); } else { preview.css("top", 0).height(editor.height()); } } else { codeMirror.width(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.val(codeEditor.getValue()); if (settings.saveHTMLToTextarea) { this.htmlTextarea.html(newMarkdownDoc); } if(settings.watch) { previewContainer.html(newMarkdownDoc); if (settings.toc) { editormd.markdownToCRenderer(markdownToC, previewContainer, settings.tocStartLevel); } } return this; }, /** * 聚焦光标位置 * @returns {editormd} 返回editormd的实例对象 */ focus : function() { this.codeEditor.focus(); return this; }, /** * 设置光标的位置 * @param {Object} cursor 要设置的光标位置键值对象,例:{line:1, ch:0} * @returns {editormd} 返回editormd的实例对象 */ setCursor : function(cursor) { this.codeEditor.setCursor(cursor); return this; }, /** * 获取当前光标的位置 * @returns {Cursor} 返回一个光标Cursor对象 */ getCursor : function() { return this.codeEditor.getCursor(); }, /** * 设置光标选中的范围 * @param {Object} from 开始位置的光标键值对象,例:{line:1, ch:0} * @param {Object} to 结束位置的光标键值对象,例:{line:1, ch:0} * @returns {editormd} 返回editormd的实例对象 */ setSelection : function(from, to) { this.codeEditor.setSelection(from, to); return this; }, /** * 获取光标选中的文本 * @returns {String} 返回选中文本的字符串形式 */ getSelection : function() { return this.codeEditor.getSelection(); }, /** * 替换当前光标选中的文本或在当前光标处插入新字符 * @param {String} value 要插入的字符值 * @returns {editormd} 返回editormd的实例对象 */ replaceSelection : function(value) { this.codeEditor.replaceSelection(value); return this; }, /** * 在当前光标处插入新字符 * * 同replaceSelection()方法 * @param {String} value 要插入的字符值 * @returns {editormd} 返回editormd的实例对象 */ insertValue : function(value) { this.replaceSelection(value); 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(); }, /** * 清空编辑器 * @returns {editormd} 返回editormd的实例对象 */ clear : function() { this.codeEditor.setValue(""); this.saveToTextareas(); return this; }, /** * 获取解析后存放在Textarea的HTML源码 * @returns {String} 返回HTML源码 */ getHTML : function() { if (!settings.saveHTMLToTextarea) { alert("Error: settings.saveHTMLToTextarea == false"); return false; } return this.htmlTextarea.html(); }, /** * getHTML()的别名 * @returns {editormd} 返回HTML源码 */ getTextareaSavedHTML : function() { return this.getHTML(); }, /** * 获取预览窗口的HTML源码 * @returns {editormd} 返回editormd的实例对象 */ getPreviewedHTML : function() { if (!settings.watch) { alert("Error: settings.watch == false"); return false; } return this.preivewContainer.html(); }, /** * 开启实时预览 * @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").width(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").width(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.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden") { this.state.preview = true; if(this.state.fullscreen) { preview.css("background", "#fff"); } editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){ _this.previewed(); }); preview.show().css({ top : 0, 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; var previewCloseBtn = editor.find("." + this.classPrefix + "preview-close-btn"); this.state.preview = false; codeMirror.show(); if (settings.toolbar) { toolbar.show(); } if(this.settings.watch) { preview.show(); } else { preview.hide(); } previewCloseBtn.hide().bind(editormd.mouseOrTouch("click", "touchend")); preview.css({ background : null, width : editor.width() / 2, height : editor.height() - toolbar.height(), top : (settings.toolbar) ? toolbar.height() : 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, "-"); var toc = { text : text, level : level, slug : escapedText }; markdownToC.push(toc); return "" + "" + "" + text + ""; }; markedRenderer.paragraph = function(text) { var isTeXInline = /\$\$(.*)\$\$/g.test(text); var isTeXLine = /^\$\$(.*)\$\$$/.test(text); var isTeXAddClass = (isTeXLine) ? " class=\"" + editormd.classNames.tex + "\"" : ""; var isToC = /^\[TOC\]$/.test(text); if (!isTeXLine && isTeXInline) { text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) { return "" + $2.replace(/\$/g, "") + ""; }); } else { text = (isTeXLine) ? text.replace(/\$/g, "") : text; } return (isToC) ? "
    " : "" + text + "

    \n"; }; markedRenderer.code = function (code, lang, escaped) { if (lang === "seq" || lang === "sequence") { 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 起始层级 * @returns {Object} tocContainer 返回ToC列表容器层的jQuery对象元素 */ editormd.markdownToCRenderer = function(toc, container, startLevel) { var html = ""; var lastLevel = 0; startLevel = startLevel || 1; 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 + "