UNPKG

@jianghujs/jianghu

Version:

Progressive Enterprise Framework

1,463 lines (1,189 loc) 173 kB
/* * Editor.md * * @file editormd.js * @version v1.5.0 * @description Open source online markdown editor. * @license MIT License * @author Pandao * {@link https://github.com/pandao/editor.md} * @updateTime 2015-06-09 */ ; (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 { if (define.amd) // for Require.js { /* Require.js define replace */ } else { define(["jquery"], factory); // for Sea.js } } else { window.editormd = factory(); } }(function () { /* Require.js assignment replace */ "use strict"; var $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto; if (typeof ($) === "undefined") { return; } /** * editormd * * @param {String} id 编辑器的ID * @param {Object} options 配置选项 Key/Value * @returns {Object} editormd 返回editormd对象 */ var editormd = function (id, options) { return new editormd.fn.init(id, options); }; editormd.title = editormd.$name = "Editor.md"; editormd.version = "1.5.0"; editormd.homePage = "https://pandao.github.io/editor.md/"; editormd.classPrefix = "editormd-"; // 编辑器工具栏模式 editormd.toolbarModes = { full: [ "undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info" ], simple: [ "undo", "redo", "|", "bold", "del", "italic", "quote", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "watch", "preview", "fullscreen", "|", "help", "info" ], mini: [ "undo", "redo", "|", "watch", "preview", "|", "help", "info" ] }; // 编辑器默认配置 editormd.defaults = { mode: "gfm", //gfm or markdown name: "", // Form element name value: "", // value for CodeMirror, if mode not gfm/markdown theme: "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty editorTheme: "default", // Editor area, this is CodeMirror theme at v1.5.0 previewTheme: "", // Preview area theme, default empty markdown: "", // Markdown source code appendMarkdown: "", // if in init textarea value not empty, append markdown to textarea width: "100%", height: "100%", path: "./lib/", // Dependents module file directory pluginPath: "", // If this empty, default use settings.path + "../plugins/" delay: 300, // Delay parse markdown to html, Uint : ms autoLoadModules: true, // Automatic load dependent module files watch: true, placeholder: "Enjoy Markdown! coding now...", gotoLine: true, codeFold: false, autoHeight: false, autoFocus: true, autoCloseTags: true, searchReplace: true, syncScrolling: true, // true | false | "single", default true readOnly: false, tabSize: 4, indentUnit: 4, lineNumbers: true, lineWrapping: true, autoCloseBrackets: true, showTrailingSpace: true, matchBrackets: true, indentWithTabs: true, styleSelectedText: true, matchWordHighlight: true, // options: true, false, "onselected" styleActiveLine: true, // Highlight the current line dialogLockScreen: true, dialogShowMask: true, dialogDraggable: true, dialogMaskBgColor: "#fff", dialogMaskOpacity: 0.1, fontSize: "15px", saveHTMLToTextarea: false, disabledKeyMaps: [], onload: function () { }, onresize: function () { }, onchange: function () { }, onwatch: null, onunwatch: null, onpreviewing: function () { }, onpreviewed: function () { }, onfullscreen: function () { }, onfullscreenExit: function () { }, onscroll: function () { }, onpreviewscroll: function () { }, imageUpload: false, imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL: "", crossDomainUpload: false, uploadCallbackURL: "", toc: true, // Table of contents tocm: false, // Using [TOCM], auto create ToC dropdown menu tocTitle: "", // for ToC dropdown menu btn tocDropdown: false, tocContainer: "", tocStartLevel: 1, // Said from H1 to create ToC htmlDecode: false, // Open the HTML tag identification pageBreak: true, // Enable parse page break [========] atLink: false, // for @link emailLink: true, // for email address auto link taskList: true, // Enable Github Flavored Markdown task lists emoji: false, // :emoji: , Support Github emoji, Twitter Emoji (Twemoji); // Support FontAwesome icon emoji :fa-xxx: > Using fontAwesome icon web fonts; // Support Editor.md logo icon emoji :editormd-logo: :editormd-logo-1x: > 1~8x; tex: false, // TeX(LaTeX), based on KaTeX flowChart: false, // flowChart.js only support IE9+ sequenceDiagram: false, // sequenceDiagram.js only support IE9+ mindMap: true, // 脑图 echart: true, // echart 图表 previewCodeHighlight: true, toolbar: true, // show/hide toolbar toolbarAutoFixed: true, // on window scroll auto fixed position toolbarIcons: "full", toolbarTitles: {}, toolbarHandlers: { ucwords: function () { return editormd.toolbarHandlers.ucwords; }, lowercase: function () { return editormd.toolbarHandlers.lowercase; } }, toolbarCustomIcons: { // using html tag create toolbar icon, unused default <a> tag. lowercase: "<a href=\"javascript:;\" title=\"Lowercase\" unselectable=\"on\"><i class=\"fa\" name=\"lowercase\" style=\"font-size:24px;margin-top: -10px;\">a</i></a>", "ucwords": "<a href=\"javascript:;\" title=\"ucwords\" unselectable=\"on\"><i class=\"fa\" name=\"ucwords\" style=\"font-size:20px;margin-top: -3px;\">Aa</i></a>" }, toolbarIconsClass: { "undo": "fa-undo", "redo": "fa-repeat", "bold": "fa-bold", "mark": "fa-paint-brush", "del": "fa-strikethrough", "italic": "fa-italic", "quote": "fa-quote-left", "uppercase": "fa-font", "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", "reference-link": "fa-anchor", "image": "fa-picture-o", "code": "fa-code", "preformatted-text": "fa-file-code-o", "code-block": "fa-file-code-o", "table": "fa-table", "datetime": "fa-clock-o", "emoji": "fa-smile-o", "html-entities": "fa-copyright", "pagebreak": "fa-newspaper-o", "goto-line": "fa-terminal", // fa-crosshairs "watch": "fa-eye-slash", "unwatch": "fa-eye", "preview": "fa-desktop", "search": "fa-search", "fullscreen": "fa-arrows-alt", "clear": "fa-eraser", "help": "fa-question-circle", "info": "fa-info-circle" }, toolbarIconTexts: {}, toolbarIconSvgs: {}, lang: { name: "zh-cn", description: "开源在线Markdown编辑器<br/>Open source online Markdown editor.", tocTitle: "目录", toolbar: { undo: "撤销(Ctrl+Z)", redo: "重做(Ctrl+Y)", bold: "粗体", mark: "文本高亮", del: "删除线", italic: "斜体", quote: "引用", ucwords: "将每个单词首字母转成大写", uppercase: "将所选转换成大写", lowercase: "将所选转换成小写", h1: "标题1", h2: "标题2", h3: "标题3", h4: "标题4", h5: "标题5", h6: "标题6", "list-ul": "无序列表", "list-ol": "有序列表", hr: "横线", link: "链接", "reference-link": "引用链接", image: "添加图片", code: "行内代码", "preformatted-text": "预格式文本 / 代码块(缩进风格)", "code-block": "代码块(多语言风格)", table: "添加表格", datetime: "日期时间", emoji: "Emoji表情", "html-entities": "HTML实体字符", pagebreak: "插入分页符", "goto-line": "跳转到行", watch: "关闭实时预览", unwatch: "开启实时预览", preview: "全窗口预览HTML(按 Shift + ESC还原)", fullscreen: "全屏(按ESC还原)", clear: "清空", search: "搜索", help: "使用帮助", info: "关于" + editormd.title }, buttons: { enter: "确定", cancel: "取消", close: "关闭" }, dialog: { link: { title: "添加链接", url: "链接地址", urlTitle: "链接标题", urlEmpty: "错误:请填写链接地址。" }, referenceLink: { title: "添加引用链接", name: "引用名称", url: "链接地址", urlId: "链接ID", urlTitle: "链接标题", nameEmpty: "错误:引用链接的名称不能为空。", idEmpty: "错误:请填写引用链接的ID。", urlEmpty: "错误:请填写引用链接的URL地址。" }, image: { title: "添加图片", url: "图片地址", link: "图片链接", alt: "图片描述", uploadButton: "本地上传", imageURLEmpty: "错误:图片地址不能为空。", uploadFileEmpty: "错误:上传的图片不能为空。", formatNotAllowed: "错误:只允许上传图片文件,允许上传的图片文件格式有:" }, preformattedText: { title: "添加预格式文本或代码块", emptyAlert: "错误:请填写预格式文本或代码的内容。" }, codeBlock: { title: "添加代码块", selectLabel: "代码语言:", selectDefaultText: "请选择代码语言", otherLanguage: "其他语言", unselectedLanguageAlert: "错误:请选择代码所属的语言类型。", codeEmptyAlert: "错误:请填写代码内容。" }, htmlEntities: { title: "HTML 实体字符" }, help: { title: "使用帮助" } } } }; editormd.classNames = { tex: editormd.classPrefix + "tex" }; editormd.dialogZindex = 99999; editormd.$katex = null; editormd.$marked = null; editormd.$CodeMirror = null; editormd.$prettyPrint = null; var timer, flowchartTimer; editormd.prototype = editormd.fn = { state: { watching: false, loaded: false, preview: false, fullscreen: false }, /** * 构造函数/实例初始化 * Constructor / instance initialization * * @param {String} id 编辑器的ID * @param {Object} [options={}] 配置选项 Key/Value * @returns {editormd} 返回editormd的实例对象 */ init: function (id, options) { options = options || {}; if (typeof id === "object") { options = id; } var _this = this; var classPrefix = this.classPrefix = editormd.classPrefix; var settings = this.settings = $.extend(true, editormd.defaults, options); id = (typeof id === "object") ? settings.id : id; var editor = this.editor = $("#" + id); this.id = id; this.lang = settings.lang; var classNames = this.classNames = { textarea: { html: classPrefix + "html-textarea", markdown: classPrefix + "markdown-textarea" } }; settings.pluginPath = (settings.pluginPath === "") ? settings.path + "../plugins/" : settings.pluginPath; this.state.watching = (settings.watch) ? true : false; 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 }); if (settings.autoHeight) { editor.css("height", "auto"); } var markdownTextarea = this.markdownTextarea = editor.children("textarea"); // if (markdownTextarea.length < 1) // { // editor.append("<textarea></textarea>"); // markdownTextarea = this.markdownTextarea = editor.children("textarea"); // } markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder); if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "") { markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc"); } var appendElements = [ (!settings.readOnly) ? "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "preview-close-btn\"></a>" : "", ((settings.saveHTMLToTextarea) ? "<textarea class=\"" + classNames.textarea.html + "\" name=\"" + id + "-html-code\"></textarea>" : ""), "<div class=\"" + classPrefix + "preview\"><div class=\"markdown-body " + classPrefix + "preview-container\"></div></div>", "<div class=\"" + classPrefix + "container-mask\" style=\"display:block;\"></div>", "<div class=\"" + classPrefix + "mask\"></div>" ].join("\n"); editor.append(appendElements).addClass(classPrefix + "vertical"); if (settings.theme !== "") { editor.addClass(classPrefix + "theme-" + settings.theme); } this.mask = editor.children("." + classPrefix + "mask"); this.containerMask = editor.children("." + classPrefix + "container-mask"); if (settings.markdown !== "") { markdownTextarea.val(settings.markdown); } if (settings.appendMarkdown !== "") { markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown); } this.htmlTextarea = editor.children("." + classNames.textarea.html); this.preview = editor.children("." + classPrefix + "preview"); this.previewContainer = this.preview.children("." + classPrefix + "preview-container"); if (settings.previewTheme !== "") { this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme); } if (typeof define === "function" && define.amd) { if (typeof katex !== "undefined") { editormd.$katex = katex; } if (settings.searchReplace && !settings.readOnly) { editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog"); editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar"); } } if ((typeof define === "function" && define.amd) || !settings.autoLoadModules) { if (typeof CodeMirror !== "undefined") { editormd.$CodeMirror = CodeMirror; } if (typeof marked !== "undefined") { editormd.$marked = marked; } this.setCodeMirror().setToolbar().loadedDisplay(); } else { this.loadQueues(); } return this; }, /** * 所需组件加载队列 * Required components loading queue * * @returns {editormd} 返回editormd的实例对象 */ loadQueues: function () { var _this = this; var settings = this.settings; var loadPath = settings.path; var loadFlowChartOrSequenceDiagram = function () { if (editormd.isIE8) { _this.loadedDisplay(); return; } // 加载流程图和序列图 JS 文件 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.loadedDisplay(); }); } else if (settings.flowChart && !settings.sequenceDiagram) { editormd.loadScript(loadPath + "flowchart.min", function () { editormd.loadScript(loadPath + "jquery.flowchart.min", function () { _this.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.loadedDisplay(); }); }); }); } }); }); } else { _this.loadedDisplay(); } }; // 加载 echarts.min.js // editormd.loadScript(loadPath + "echarts.min", function () { // // _this.loadedDisplay(); // }); // // // 加载 mindmap 相关js // editormd.loadScript(loadPath + 'mindmap/d3@5', function () { // editormd.loadScript(loadPath + 'mindmap/transform.min', function () { // editormd.loadScript(loadPath + 'mindmap/view.min', function () { // // }) // }) // }) // 加载DOMPurify过滤HTML editormd.loadScript(loadPath + 'purify.min', function () { }); editormd.loadCSS(loadPath + "codemirror/lib/codemirror"); if (settings.searchReplace && !settings.readOnly) { editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog"); editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar"); editormd.loadCSS(loadPath + "codemirror/addon/hint/show-hint"); } if (settings.codeFold) { editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter"); } editormd.loadScript(loadPath + "codemirror/lib/codemirror", function () { editormd.$CodeMirror = CodeMirror; editormd.loadScript(loadPath + "codemirror/modes", function () { // editormd.loadScript(loadPath + "codemirror/mode/meta", function() { editormd.loadScript(loadPath + "codemirror/addons", function () { // editormd.loadScript(loadPath + "codemirror/addon/display/placeholder", function() { _this.setCodeMirror(); if (settings.mode !== "gfm" && settings.mode !== "markdown") { _this.loadedDisplay(); return false; } _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; }, /** * 设置 Editor.md 的整体主题,主要是工具栏 * Setting Editor.md theme * * @returns {editormd} 返回editormd的实例对象 */ setTheme: function (theme) { var editor = this.editor; var oldTheme = this.settings.theme; var themePrefix = this.classPrefix + "theme-"; editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); this.settings.theme = theme; return this; }, /** * 设置 CodeMirror(编辑区)的主题 * Setting CodeMirror (Editor area) theme * * @returns {editormd} 返回editormd的实例对象 */ setEditorTheme: function (theme) { var settings = this.settings; settings.editorTheme = theme; if (theme !== "default") { editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); } this.cm.setOption("theme", theme); return this; }, /** * setEditorTheme() 的别名 * setEditorTheme() alias * * @returns {editormd} 返回editormd的实例对象 */ setCodeMirrorTheme: function (theme) { this.setEditorTheme(theme); return this; }, /** * 设置 Editor.md 的主题 * Setting Editor.md theme * * @returns {editormd} 返回editormd的实例对象 */ setPreviewTheme: function (theme) { var preview = this.preview; var oldTheme = this.settings.previewTheme; var themePrefix = this.classPrefix + "preview-theme-"; preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); this.settings.previewTheme = theme; return this; }, /** * 配置和初始化CodeMirror组件 * CodeMirror initialization * * @returns {editormd} 返回editormd的实例对象 */ setCodeMirror: function () { var settings = this.settings; var editor = this.editor; if (settings.editorTheme !== "default") { editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); } var md_comp = [ ["#", "##", "###", "####", "#####", "######", "#######"], ["`", "``", "```", "```\n\n```", "```echart\n\n```", "```mindmap\n\n```"], ["*", "**"], ["[", "[]()"], ["!", "![]()"], ["-", "- [] ", "- [x] "], ] function markdownHint(cm, option) { return new Promise(function (accept) { setTimeout(function () { var cursor = cm.getCursor(), line = cm.getLine(cursor.line) console.log(cursor) var start = cursor.ch, end = cursor.ch while (start && /\W/.test(line.charAt(start - 1))) --start while (end < line.length && /\W/.test(line.charAt(end))) ++end console.log(line, start, end) var word = line.slice(start, end).toLowerCase() console.log(word) for (var i = 0; i < md_comp.length; i++) if (md_comp[i].indexOf(word) != -1) return accept({ list: md_comp[i], from: editormd.$CodeMirror.Pos(cursor.line, start), to: editormd.$CodeMirror.Pos(cursor.line, end) }) return accept(null) }, 100) }) } var codeMirrorConfig = { mode: settings.mode, theme: settings.editorTheme, tabSize: settings.tabSize, dragDrop: false, autofocus: settings.autoFocus, autoCloseTags: settings.autoCloseTags, readOnly: (settings.readOnly) ? "nocursor" : false, indentUnit: settings.indentUnit, lineNumbers: settings.lineNumbers, lineWrapping: settings.lineWrapping, extraKeys: { // "Ctrl-Q": function(cm) { // cm.foldCode(cm.getCursor()); // }, "Ctrl-Q": "autocomplete" }, foldGutter: settings.codeFold, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], matchBrackets: settings.matchBrackets, indentWithTabs: settings.indentWithTabs, styleActiveLine: settings.styleActiveLine, styleSelectedText: settings.styleSelectedText, autoCloseBrackets: settings.autoCloseBrackets, showTrailingSpace: settings.showTrailingSpace, hintOptions: { hint: markdownHint }, highlightSelectionMatches: ((!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ }) }; this.codeEditor = this.cm = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig); this.codeMirror = this.cmElement = editor.children(".CodeMirror"); if (settings.value !== "") { this.cm.setValue(settings.value); } this.codeMirror.css({ fontSize: settings.fontSize, width: (!settings.watch) ? "100%" : "50%" }); if (settings.autoHeight) { this.codeMirror.css("height", "auto"); this.cm.setOption("viewportMargin", Infinity); } if (!settings.lineNumbers) { this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none"); } return this; }, /** * 获取CodeMirror的配置选项 * Get CodeMirror setting options * * @returns {Mixed} return CodeMirror setting option value */ getCodeMirrorOption: function (key) { return this.cm.getOption(key); }, /** * 配置和重配置CodeMirror的选项 * CodeMirror setting options / resettings * * @returns {editormd} 返回editormd的实例对象 */ setCodeMirrorOption: function (key, value) { this.cm.setOption(key, value); return this; }, /** * 添加 CodeMirror 键盘快捷键 * Add CodeMirror keyboard shortcuts key map * * @returns {editormd} 返回editormd的实例对象 */ addKeyMap: function (map, bottom) { this.cm.addKeyMap(map, bottom); return this; }, /** * 移除 CodeMirror 键盘快捷键 * Remove CodeMirror keyboard shortcuts key map * * @returns {editormd} 返回editormd的实例对象 */ removeKeyMap: function (map) { this.cm.removeKeyMap(map); return this; }, /** * 跳转到指定的行 * Goto CodeMirror line * * @param {String|Intiger} line line number or "first"|"last" * @returns {editormd} 返回editormd的实例对象 */ gotoLine: function (line) { var settings = this.settings; if (!settings.gotoLine) { return this; } var cm = this.cm; var editor = this.editor; var count = cm.lineCount(); var preview = this.preview; if (typeof line === "string") { if (line === "last") { line = count; } if (line === "first") { line = 1; } } if (typeof line !== "number") { alert("Error: The line number must be an integer."); return this; } line = parseInt(line) - 1; if (line > count) { alert("Error: The line number range 1-" + count); return this; } cm.setCursor({ line: line, ch: 0 }); var scrollInfo = cm.getScrollInfo(); var clientHeight = scrollInfo.clientHeight; var coords = cm.charCoords({ line: line, ch: 0 }, "local"); cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2); if (settings.watch) { var cmScroll = this.codeMirror.find(".CodeMirror-scroll")[0]; var height = $(cmScroll).height(); var scrollTop = cmScroll.scrollTop; var percent = (scrollTop / cmScroll.scrollHeight); if (scrollTop === 0) { preview.scrollTop(0); } else if (scrollTop + height >= cmScroll.scrollHeight - 16) { preview.scrollTop(preview[0].scrollHeight); } else { preview.scrollTop(preview[0].scrollHeight * percent); } } cm.focus(); return this; }, /** * 扩展当前实例对象,可同时设置多个或者只设置一个 * Extend editormd instance object, can mutil setting. * * @returns {editormd} this(editormd instance object.) */ extend: function () { if (typeof arguments[1] !== "undefined") { if (typeof arguments[1] === "function") { arguments[1] = $.proxy(arguments[1], this); } this[arguments[0]] = arguments[1]; } if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined") { $.extend(true, this, arguments[0]); } return this; }, /** * 设置或扩展当前实例对象,单个设置 * Extend editormd instance object, one by one * * @param {String|Object} key option key * @param {String|Object} value option value * @returns {editormd} this(editormd instance object.) */ set: function (key, value) { if (typeof value !== "undefined" && typeof value === "function") { value = $.proxy(value, this); } this[key] = value; return this; }, /** * 重新配置 * Resetting editor options * * @param {String|Object} key option key * @param {String|Object} value option value * @returns {editormd} this(editormd instance object.) */ config: function (key, value) { var settings = this.settings; if (typeof key === "object") { settings = $.extend(true, settings, key); } if (typeof key === "string") { settings[key] = value; } this.settings = settings; this.recreate(); return this; }, /** * 注册事件处理方法 * Bind editor event handle * * @param {String} eventType event type * @param {Function} callback 回调函数 * @returns {editormd} this(editormd instance object.) */ on: function (eventType, callback) { var settings = this.settings; if (typeof settings["on" + eventType] !== "undefined") { settings["on" + eventType] = $.proxy(callback, this); } return this; }, /** * 解除事件处理方法 * Unbind editor event handle * * @param {String} eventType event type * @returns {editormd} this(editormd instance object.) */ off: function (eventType) { var settings = this.settings; if (typeof settings["on" + eventType] !== "undefined") { settings["on" + eventType] = function () { }; } return this; }, /** * 显示工具栏 * Display toolbar * * @param {Function} [callback=function(){}] 回调函数 * @returns {editormd} 返回editormd的实例对象 */ showToolbar: function (callback) { var settings = this.settings; if (settings.readOnly) { return this; } if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "")) { this.setToolbar(); } settings.toolbar = true; this.toolbar.show(); this.resize(); $.proxy(callback || function () { }, this)(); return this; }, /** * 隐藏工具栏 * Hide toolbar * * @param {Function} [callback=function(){}] 回调函数 * @returns {editormd} this(editormd instance object.) */ hideToolbar: function (callback) { var settings = this.settings; settings.toolbar = false; this.toolbar.hide(); this.resize(); $.proxy(callback || function () { }, this)(); return this; }, /** * 页面滚动时工具栏的固定定位 * Set toolbar in window scroll auto fixed position * * @returns {editormd} 返回editormd的实例对象 */ setToolbarAutoFixed: function (fixed) { var state = this.state; var editor = this.editor; var toolbar = this.toolbar; var settings = this.settings; if (typeof fixed !== "undefined") { settings.toolbarAutoFixed = fixed; } var autoFixedHandle = function () { var $window = $(window); var top = $window.scrollTop(); if (!settings.toolbarAutoFixed) { return false; } // if (top - editor.offset().top > 10 && top < editor.height()) if (top - editor.offset().top > 10 && top - editor.offset().top < editor.height() - toolbar.height()) { toolbar.css({ position: "fixed", width: editor.width() + "px", // left : ($window.width() - editor.width()) / 2 + "px" left: editor.offset().left + "px" }); } else { toolbar.css({ position: "absolute", width: "100%", left: 0 }); } }; if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed) { $(window).bind("scroll", autoFixedHandle); } return this; }, /** * 配置和初始化工具栏 * Set toolbar and Initialization * * @returns {editormd} 返回editormd的实例对象 */ setToolbar: function () { var settings = this.settings; if (settings.readOnly) { return this; } var editor = this.editor; var preview = this.preview; var classPrefix = this.classPrefix; var toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); if (settings.toolbar && toolbar.length < 1) { var toolbarHTML = "<div class=\"" + classPrefix + "toolbar\"><div class=\"" + classPrefix + "toolbar-container\"><ul class=\"" + classPrefix + "menu\"></ul></div></div>"; editor.append(toolbarHTML); toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); } if (!settings.toolbar) { toolbar.hide(); return this; } toolbar.show(); var icons = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons() : ((typeof settings.toolbarIcons === "string") ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons); var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = ""; var pullRight = false; for (var i = 0, len = icons.length; i < len; i++) { var name = icons[i]; if (name === "||") { pullRight = true; } else if (name === "|") { menu += "<li class=\"divider\" unselectable=\"on\">|</li>"; } else { 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 iconSvgs = settings.toolbarIconSvgs[index]; var iconClass = settings.toolbarIconsClass[index]; title = (typeof title === "undefined") ? "" : title; iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts; iconSvgs = (typeof iconSvgs === "undefined") ? "" : iconSvgs; iconClass = (typeof iconClass === "undefined") ? "" : iconClass; var menuItem = pullRight ? "<li class=\"pull-right\">" : "<li>"; if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function") { menuItem += settings.toolbarCustomIcons[name]; } else { menuItem += "<a href=\"javascript:;\" title=\"" + title + "\" unselectable=\"on\">"; menuItem += "<i class=\"fa " + iconClass + "\" name=\"" + name + "\" unselectable=\"on\">" + ((isHeader) ? name.toUpperCase() : ((iconClass === "") ? iconTexts : "")) + "</i>"; menuItem += "</a>"; } menuItem += "</li>"; menu = pullRight ? menuItem + menu : menu + menuItem; } } toolbarMenu.html(menu); toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase); toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords); this.setToolbarHandler(); this.setToolbarAutoFixed(); return this; }, /** * 工具栏图标事件处理对象序列 * Get toolbar icons event handlers * * @param {Object} cm CodeMirror的实例对象 * @param {String} name 要获取的事件处理器名称 * @returns {Object} 返回处理对象序列 */ dialogLockScreen: function () { $.proxy(editormd.dialogLockScreen, this)(); return this; }, dialogShowMask: function (dialog) { $.proxy(editormd.dialogShowMask, this)(dialog); return this; }, getToolbarHandles: function (name) { var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers; return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers; }, /** * 工具栏图标事件处理器 * Bind toolbar icons event handle * * @returns {editormd} 返回editormd的实例对象 */ setToolbarHandler: function () { var _this = this; var settings = this.settings; if (!settings.toolbar || settings.readOnly) { return this; } var toolbar = this.toolbar; var cm = this.cm; var classPrefix = this.classPrefix; var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > 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 = cm.getCursor(); var selection = cm.getSelection(); if (name === "") { return; } _this.activeIcon = icon; if (typeof toolbarIconHandlers[name] !== "undefined") { $.proxy(toolbarIconHandlers[name], _this)(cm); } else { if (typeof settings.toolbarHandlers[name] !== "undefined") { $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection); } } if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" && name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info") { cm.focus(); } return false; }); return this; }, /** * 动态创建对话框 * Creating custom dialogs * * @param {Object} options 配置项键值对 Key/Value * @returns {dialog} 返回创建的dialog的jQuery实例对象 */ createDialog: function (options) { return $.proxy(editormd.createDialog, this)(options); }, /** * 创建关于Editor.md的对话框 * Create about Editor.md dialog * * @returns {editormd} 返回editormd的实例对象 */ createInfoDialog: function () { var _this = this; var editor = this.editor; var classPrefix = this.classPrefix; var infoDialogHTML = [ "<div class=\"" + classPrefix + "dialog " + classPrefix + "dialog-info\" style=\"\">", "<div class=\"" + classPrefix + "dialog-container\">", "<h1><i class=\"editormd-logo editormd-logo-lg editormd-logo-color\"></i> " + editormd.title + "<small>v" + editormd.version + "</small></h1>", "<p>" + this.lang.description + "</p>", "<p style=\"margin: 10px 0 20px 0;\"><a href=\"" + editormd.homePage + "\" target=\"_blank\">" + editormd.homePage + " <i class=\"fa fa-external-link\"></i></a></p>", "<p style=\"font-size: 0.85em;\">Copyright &copy; 2015 <a href=\"https://github.com/pandao\" target=\"_blank\" class=\"hover-link\">Pandao</a>, The <a href=\"https://github.com/pandao/editor.md/blob/master/LICENSE\" target=\"_blank\" class=\"hover-link\">MIT</a> License.</p>", "</div>", "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>", "</div>" ].join("\n"); editor.append(infoDialogHTML); var infoDialog = this.infoDialog = editor.children("." + classPrefix + "dialog-info"); infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function () { _this.hideInfoDialog(); }); infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show(); this.infoDialogPosition(); return this; }, /** * 关于Editor.md对话居中定位 * Editor.md dialog position handle * * @returns {editormd} 返回editormd的实例对象 */ infoDialogPosition: function () { var infoDialog = this.infoDialog; 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 * Display about Editor.md dialog * * @returns {editormd} 返回editormd的实例对象 */ showInfoDialog: function () { $("html,body").css("overflow-x", "hidden"); var _this = this; var editor = this.editor; var settings = this.settings; var infoDialog = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info"); if (infoDialog.length < 1) { this.createInfoDialog(); } this.lockScreen(true); this.mask.css({ opacity: settings.dialogMaskOpacity, backgroundColor: settings.dialogMaskBgColor }).show(); infoDialog.css("z-index", editormd.dialogZindex).show(); this.infoDialogPosition(); return this; }, /** * 隐藏关于Editor.md * Hide about Editor.md dialog * * @returns {editormd} 返回editormd的实例对象 */ hideInfoDialog: function () { $("html,body").css("overflow-x", ""); this.infoDialog.hide(); this.mask.hide(); this.lockScreen(false); return this; }, /** * 锁屏 * lock screen * * @param {Boolean} lock Boolean 布尔值,是否锁屏 * @returns {editormd} 返回editormd的实例对象 */ lockScreen: function (lock) { editormd.lockScreen(lock); this.resize(); return this; }, /** * 编辑器界面重建,用于动态语言包或模块加载等 * Recreate editor * * @returns {editormd} 返回editormd的实例对象 */ recreate: function () { var _this = this; var editor = this.editor; var settings = this.settings; this.codeMirror.remove(); this.setCodeMirror(); if (!settings.readOnly) { if (editor.find(".editormd-dialog").length > 0) { editor.find(".editormd-dialog").remove(); } if (settings.toolbar) { this.getToolbarHandles(); this.setToolbar(); } } this.loadedDisplay(true); return this; }, /** * 高亮预览HTML的pre代码部分 * highlight of preview codes * * @returns {editormd} 返回editormd的实例对象 */ previewCodeHighlight: function () {