玛氪宕·梦魔(Markdown Memo),使用Markdown的云端备忘录,百度IFE的RIA启航班的不合格的作业,才……才没有什么阴谋呢! 源gitee链接https://gitee.com/arathi/MarkdownMemo?_from=gitee_search
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1832 lines
64 KiB

/*
* 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 = [
"<div class=\"" + classPrefix + "dialog " + classPrefix + "dialog-info\">",
"<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>",
"<div class=\"" + classPrefix + "dialog-container\">",
"<h1><i class=\"fa fa-lg fa-edit\"></i>" + editormd.title + "<small>v" + editormd.version + "</small></h1>",
"<p>" + editormd.description + "</p>",
"<p>Home page: <a href=\"" + editormd.homePage + "\" traget=\"_blank\">" + editormd.homePage + "</a></p>",
"<p>License: MIT</p>",
"</div>",
"</div>"
].join("\n");
var appendElements = [
'<div class="'+classPrefix+'toolbar"><div class="'+classPrefix+'toolbar-container"><ul class="'+classPrefix+'menu"></ul></div></div>',
'<textarea id="test123" class="'+this.classNames.textarea.markdown+'" name="'+id+'-markdown-doc" placeholder="now coding markdown...">'+markdownDoc+'</textarea>',
'<textarea class="'+this.classNames.textarea.html+'" name="'+id+'-html-code"></textarea>',
'<div class="'+classPrefix+'preview"><div class="markdown-body '+classPrefix+'preview-container"></div>',
"</div>"
].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 += "<li><a href=\"javascript:;\" title=\""+settings.lang.toolbar[name]+"\"><i class=\"fa "+settings.toolbarIconsClass[name]+"\" name=\""+name+"\">"+((isHeader) ? name : "")+"</i></a></li>";
}
else
{
menu += "<li class=\"divider\">|</li>";
}
}
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 = "<script type=\"math/tex; mode=display\">" + mathjaxDoc + "</script>";
$(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 "<h" + level + " id=\""+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+"\"><a href=\"#" + text + "\" name=\"" + text + "\" class=\"anchor\"></a><span class=\"header-link\"></span>" + text + "</h" + level + ">";
};
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) ? "<div class=\"markdown-toc\"><ul class=\"markdown-toc-list\">" + text + "</ul></div>" : "<p" + mathjaxClassName + ">" + text + "</p>\n";
};
markedRenderer.code = function (code, lang, escaped) {
if (lang === "seq")
{
return "<div class=\"sequence-diagram\">" + code + "</div>";
}
else if ( lang === "flow")
{
return "<div class=\"flowchart\">" + code + "</div>";
}
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("</ul></li>");
}
else
{
html += "</ul></li>";
}
html += "<li><a class=\"toc-level-" + level + "\" href=\"#" + text + "\" level=\"" + level + "\">" + text + "</a><ul>";
lastLevel = level;
}
container.find('.markdown-toc-list').html("").html(html);
};
/**
* 将Markdown文档解析为HTML用于前台显示
* @param {String} id 用于显示HTML的对象ID
* @param {Object} [options={}] 配置选项,可选
*/
editormd.markdownToHTML = function(id, options) {
options = options || {};
var defaults = {
toc : true,
tocStartLevel : 2,
markdown : "",
mathjax : false,
previewCodeHighlight : true,
flowChart : false,
sequenceDiagram : false
};
editormd.$marked = marked;
var settings = $.extend(true, defaults, options);
var div = $("#" + id);
var saveTo = div.find("[type=\"text/markdown\"]");
var markdownDoc = (settings.markdown === "") ? saveTo.html() : settings.markdown;
var markdownToC = [];
var markedOptions = {
renderer : editormd.markedRenderer(markdownToC),
gfm : true,
tables : true,
breaks : false,
pedantic : false,
sanitize : true,
smartLists : true,
smartypants : true
};
var markdownParsed = marked(markdownDoc, markedOptions);
saveTo.html(markdownDoc);
div.addClass("markdown-body").append(markdownParsed);
if (settings.toc) {
editormd.markdownToCRenderer(markdownToC, div, settings.tocStartLevel);
}
if (settings.previewCodeHighlight)
{
div.find("pre").addClass("prettyprint linenums");
prettyPrint();
}
if (settings.flowChart) {
div.find(".flowchart").flowChart();
}
if (settings.sequenceDiagram) {
div.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
}
if (settings.mathjax)
{
editormd.setMathJaxConfig(function() {
editormd.loadMathJax();
});
}
};
/**
* 用于支持Require.js加载的方法
* @param {Function} CodeMirror CodeMirror对象
* @param {Function} marked marked对象
* @param {Function} prettyPrint prettyPrint函数
*/
editormd.requirejsInit = function(CodeMirror, marked, prettyPrint) {
editormd.$CodeMirror = CodeMirror;
editormd.$marked = marked;
editormd.$prettyPrint = prettyPrint;
};
/**
* 用于Require.js加载的模块队列
* @param {String} [loadPath=""] 基本路径,默认为空
* @returns {Array} modules 返回队列路径数组
*/
editormd.requireModules = function(loadPath) {
loadPath = loadPath || "";
var settings = editormd.defaults;
var modules = [];
modules.push(loadPath + "codemirror/lib/codemirror.min");
for (var i = 0, len = settings.codemirror.modes.length; i < len; i++)
{
var modeName = settings.codemirror.modes[i];
modules.push(loadPath + "codemirror/mode/" + modeName + "/" + modeName);
}
for (var i = 0, len = settings.codemirror.addons.length; i < len; i++)
{
var addonName = settings.codemirror.addons[i];
modules.push(loadPath + "codemirror/addon/" + addonName);
}
return modules;
};
/**
* 动态加载CSS文件的方法
* @param {String} fileName CSS文件名
* @param {Function} [callback=function()] 加载成功后执行的回调函数
* @param {String} [into="head"] 嵌入页面的位置
*/
editormd.loadCSS = function(fileName, callback, into) {
into = into || "head";
callback = callback || function() {};
var css = document.createElement("link");
css.type = "text/css";
css.rel = "stylesheet";
css.onload = css.onreadystatechange = function() {
callback();
};
css.href = fileName + ".css";
if(into === "head") {
document.getElementsByTagName("head")[0].appendChild(css);
} else {
document.body.appendChild(css);
}
};
/**
* 动态加载JS文件的方法
* @param {String} fileName JS文件名
* @param {Function} [callback=function()] 加载成功后执行的回调函数
* @param {String} [into="head"] 嵌入页面的位置
*/
editormd.loadScript = function(fileName, callback, into) {
into = into || "head";
callback = callback || function() {};
var script = document.createElement("script");
script.type = "text/javascript";
script.onload = script.onreadystatechange = function() {
if(script.readyState)
{
if (script.readyState === "loaded" || script.readyState === "complete")
{
script.onreadystatechange = null;
callback();
}
}
else
{
callback();
}
};
script.src = fileName + ".js";
//console.log("script.src =>", script.src);
if (into === "head") {
document.getElementsByTagName("head")[0].appendChild(script);
} else {
document.body.appendChild(script);
}
};
/**
* MathJax配置信息
* @param {Function} [callback=function()] 加载成功后执行的回调函数
*/
editormd.setMathJaxConfig = function (callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.className = "mathjax-config";
script.type = "text/x-mathjax-config";
script.text = 'MathJax.Hub.Config({' +
'extensions: ["tex2jax.js"],'+
'jax: ["input/TeX","output/HTML-CSS"],'+
'tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}'+
'});';
document.getElementsByTagName("head")[0].appendChild(script);
callback();
};
// 注:国内可以采用这个CDN,http://cdn.bootcss.com/mathjax/2.4.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML
editormd.mathjaxURL = "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
/**
* 加载MathJax文件
* @param {Function} [callback=function()] 加载成功后执行的回调函数
*/
editormd.loadMathJax = function (callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.type = "text/javascript";
script.className = "mathjax-script";
script.onload = script.onreadystatechange = function() {
if (script.readyState)
{
if (script.readyState === "loaded" || script.readyState === "complete")
{
script.onreadystatechange = null;
callback();
}
}
else
{
callback();
}
};
script.src = editormd.mathjaxURL;
document.getElementsByTagName("head")[0].appendChild(script);
};
/**
* 鼠标和触摸事件的判断/选择方法
* @param {String} [mouseEventType="click"] 供选择的鼠标事件
* @param {String} [touchEventType="touchend"] 供选择的触摸事件
* @returns {String} EventType 返回事件类型名称
*/
editormd.mouseOrTouch = function(mouseEventType, touchEventType) {
mouseEventType = mouseEventType || "click";
touchEventType = touchEventType || "touchend";
var eventType = mouseEventType;
try
{
document.createEvent("TouchEvent");
eventType = touchEventType;
}
catch(e) {
}
return eventType;
};
/**
* 日期时间的格式化方法
* @param {String} [format=""] 日期时间的格式,类似PHP的格式
* @returns {String} datefmt 返回格式化后的日期时间字符串
*/
editormd.dateFormat = function(format) {
format = format || "";
var addZero = function(d) {
return (d < 10) ? "0" + d : d;
};
var date = new Date;
var year = date.getFullYear();
var year2 = year.toString().slice(2, 4);
var month = addZero(date.getMonth() + 1);
var day = addZero(date.getDate());
var weekDay = date.getDay();
var hour = addZero(date.getHours());
var min = addZero(date.getMinutes());
var second = addZero(date.getSeconds());
var ms = addZero(date.getMilliseconds());
var datefmt = "";
var ymd = year2 + "-" + month + "-" + day;
var fymd = year + "-" + month + "-" + day;
var hms = hour + ":" + min + ":" + second;
switch (format)
{
case "UNIX Time" :
datefmt = date.getTime();
break;
case "UTC" :
datefmt = date.toUTCString();
break;
case "yy" :
datefmt = year2;
break;
case "year" :
case "yyyy" :
datefmt = year;
break;
case "month" :
case "mm" :
datefmt = month;
break;
case "cn-week-day" :
case "cn-wd" :
var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"];
datefmt = "星期" + cnWeekDays[weekDay];
break;
case "week-day" :
case "wd" :
datefmt = weekDay;
break;
case "day" :
case "dd" :
datefmt = day;
break;
case "hour" :
case "hh" :
datefmt = hour;
break;
case "min" :
case "ii" :
datefmt = min;
break;
case "second" :
case "ss" :
datefmt = second;
break;
case "ms" :
datefmt = ms;
break;
case "yy-mm-dd" :
datefmt = ymd;
break;
case "yyyy-mm-dd" :
datefmt = fymd;
break;
case "yyyy-mm-dd h:i:s ms" :
case "full + ms" :
datefmt = fymd + " " + hms + " " + ms;
break;
case "full" :
case "yyyy-mm-dd h:i:s" :
default:
datefmt = fymd + " " + hms;
break;
}
return datefmt;
};
return editormd;
}));