export pdf ok

This commit is contained in:
lealife committed 2015-10-17 17:15:33 +08:00
1 parent 90fea722aa
commit 638b5b51e0
10 files changed
+1072 -21

No files matched your search

+166 -4
View File
@@ -3,14 +3,17 @@ package controllers
import (
"github.com/revel/revel"
// "encoding/json"
"gopkg.in/mgo.v2/bson"
. "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson"
"os"
"os/exec"
"regexp"
"strings"
// "time"
"time"
"fmt"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
// "bytes"
// "os"
)
@@ -293,6 +296,165 @@ func (c Note) SearchNoteByTags(tags []string) revel.Result {
return c.RenderJson(blogs)
}
// 生成PDF
func (c Note) ToPdf(noteId, appKey string) revel.Result {
// 虽然传了cookie但是这里还是不能得到userId, 所以还是通过appKey来验证之
appKeyTrue, _ := revel.Config.String("app.secret")
if appKeyTrue != appKey {
return c.RenderText("error")
}
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
return c.RenderText("error")
}
noteUserId := note.UserId.Hex()
content := noteService.GetNoteContent(noteId, noteUserId)
userInfo := userService.GetUserInfo(noteUserId)
//------------------
// 将content的图片转换为base64
contentStr := content.Content
siteUrlPattern := configService.GetSiteUrl()
if strings.Contains(siteUrlPattern, "https") {
siteUrlPattern = strings.Replace(siteUrlPattern, "https", "https*", 1)
} else {
siteUrlPattern = strings.Replace(siteUrlPattern, "http", "https*", 1)
}
regImage, _ := regexp.Compile(`<img .*?(src=('|")` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})("|'))`)
findsImage := regImage.FindAllStringSubmatch(contentStr, -1) // 查找所有的
// [<img src="http://leanote.com/api/getImage?fileId=3354672e8d38f411286b000069" alt="" width="692" height="302" data-mce-src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069" " file/outputImage 54672e8d38f411286b000069 "]
for _, eachFind := range findsImage {
if len(eachFind) == 6 {
fileId := eachFind[4]
// 得到base64编码文件
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
if fileBase64 == "" {
continue
}
// 1
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
allFixed := strings.Replace(eachFind[0], eachFind[1], "src=\""+fileBase64+"\"", -1)
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
}
}
// markdown
if note.IsMarkdown {
// ![enter image description here](url)
regImageMarkdown, _ := regexp.Compile(`!\[.*?\]\(` + siteUrlPattern + `/(file/outputImage|api/file/getImage)\?fileId=([a-z0-9A-Z]{24})\)`)
findsImageMarkdown := regImageMarkdown.FindAllStringSubmatch(contentStr, -1) // 查找所有的
for _, eachFind := range findsImageMarkdown {
if len(eachFind) == 3 {
fileId := eachFind[2]
// 得到base64编码文件
fileBase64 := fileService.GetImageBase64(noteUserId, fileId)
if fileBase64 == "" {
continue
}
// 1
// src="http://leanote.com/file/outputImage?fileId=54672e8d38f411286b000069"
allFixed := "![](" + fileBase64 + ")"
contentStr = strings.Replace(contentStr, eachFind[0], allFixed, -1)
}
}
}
if note.Tags != nil && len(note.Tags) > 0 && note.Tags[0] != "" {
} else {
note.Tags = nil
}
c.RenderArgs["blog"] = note
c.RenderArgs["content"] = contentStr
c.RenderArgs["userInfo"] = userInfo
userBlog := blogService.GetUserBlog(noteUserId)
c.RenderArgs["userBlog"] = userBlog
return c.RenderTemplate("file/pdf.html")
}
// 导出成PDF
func (c Note) ExportPdf(noteId string) revel.Result {
re := info.NewRe()
userId := c.GetUserId()
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "No Note"
return c.RenderText("error")
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "No Perm"
return c.RenderText("error")
}
}
// path 判断是否需要重新生成之
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
return c.RenderText("error, no dir")
}
filename := guid + ".pdf"
path := dir + "/" + filename
// leanote.com的secret
appKey, _ := revel.Config.String("app.secretLeanote")
if appKey == "" {
appKey, _ = revel.Config.String("app.secret")
}
// 生成之
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径
if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf"
}
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
// cc := binPath + " --no-stop-slow-scripts --javascript-delay 10000 \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc := binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// 等待--window-status为done的状态
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
// wkhtmltopdf参数大全
var cc string
if note.IsMarkdown {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
_, err := cmd.Output()
if err != nil {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
}
file, err := os.Open(path)
if err != nil {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))
}
// http://stackoverflow.com/questions/8588818/chrome-pdf-display-duplicate-headers-received-from-the-server
// filenameReturn = strings.Replace(filenameReturn, ",", "-", -1)
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf"
} else {
filenameReturn += ".pdf"
}
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
}
// 设置/取消Blog; 置顶
func (c Note) SetNote2Blog(noteId string, isBlog, isTop bool) revel.Result {
re := noteService.ToBlog(c.GetUserId(), noteId, isBlog, isTop)
+81
View File
@@ -9,6 +9,8 @@ import (
"regexp"
"strings"
"time"
"os/exec"
"os"
// "github.com/leanote/leanote/app/types"
// "io/ioutil"
// "fmt"
@@ -580,3 +582,82 @@ func (c ApiNote) GetHistories(noteId string) revel.Result {
return c.RenderJson(re)
}
*/
// 0.2 新增
// 导出成PDF
func (c ApiNote) ExportPdf(noteId string) revel.Result {
re := info.NewApiRe()
userId := c.getUserId()
if noteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
note := noteService.GetNoteById(noteId)
if note.NoteId == "" {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
noteUserId := note.UserId.Hex()
// 是否有权限
if noteUserId != userId {
// 是否是有权限协作的
if !note.IsBlog && !shareService.HasReadPerm(noteUserId, userId, noteId) {
re.Msg = "noteNotExists"
return c.RenderJson(re)
}
}
// path 判断是否需要重新生成之
guid := NewGuid()
fileUrlPath := "files/" + Digest3(noteUserId) + "/" + noteUserId + "/" + Digest2(guid) + "/images/pdf"
dir := revel.BasePath + "/" + fileUrlPath
if !MkdirAll(dir) {
re.Msg = "noDir"
return c.RenderJson(re)
}
filename := guid + ".pdf"
path := dir + "/" + filename
appKey, _ := revel.Config.String("app.secretLeanote")
if appKey == "" {
appKey, _ = revel.Config.String("app.secret")
}
// 生成之
binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径
if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf"
}
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
var cc string
if(note.IsMarkdown) {
cc = binPath + " --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
} else {
cc = binPath + " \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
}
cmd := exec.Command("/bin/sh", "-c", cc)
_, err := cmd.Output()
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
file, err := os.Open(path)
if err != nil {
re.Msg = "sysError"
return c.RenderJson(re)
}
filenameReturn := note.Title
filenameReturn = FixFilename(filenameReturn)
if filenameReturn == "" {
filenameReturn = "Untitled.pdf"
} else {
filenameReturn += ".pdf"
}
return c.RenderBinary(file, filenameReturn, revel.Attachment, time.Now()) // revel.Attachment
}
+1 -1
View File
@@ -48,7 +48,7 @@ var commonUrl = map[string]map[string]bool{"Index": map[string]bool{"Index": tru
"FindPasswordUpdate": true,
"Suggestion": true,
},
"Note": map[string]bool{"ToImage": true},
"Note": map[string]bool{"ToPdf": true},
"Blog": map[string]bool{"Index": true,
"View": true,
"AboutMe": true,
+54 -3
View File
@@ -1,14 +1,18 @@
package service
import (
"encoding/base64"
"fmt"
"github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea"
"github.com/revel/revel"
"github.com/leanote/leanote/app/info"
"github.com/leanote/leanote/app/db"
"gopkg.in/mgo.v2/bson"
"time"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
const DEFAULT_ALBUM_ID = "52d3e8ac99c37b7f0d000001"
@@ -115,6 +119,53 @@ func (this *FileService) UpdateImage(userId, fileId, title string) bool {
return db.UpdateByIdAndUserIdField(db.Files, fileId, userId, "Title", title)
}
func (this *FileService) GetFileBase64(userId, fileId string) (str string, mine string) {
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容,55
}
}()
path := this.GetFile(userId, fileId)
if path == "" {
return "", ""
}
path = revel.BasePath + "/" + strings.TrimLeft(path, "/")
ff, err := ioutil.ReadFile(path)
if err != nil {
return "", ""
}
e64 := base64.StdEncoding
maxEncLen := e64.EncodedLen(len(ff))
encBuf := make([]byte, maxEncLen)
e64.Encode(encBuf, ff)
mime := http.DetectContentType(ff)
str = string(encBuf)
return str, mime
}
// 得到图片base64, 图片要在之前添加data:image/png;base64,
func (this *FileService) GetImageBase64(userId, fileId string) string {
str, mime := this.GetFileBase64(userId, fileId)
if str == "" {
return ""
}
switch mime {
case "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff":
return fmt.Sprintf("data:%s;base64,%s", mime, str)
default:
}
return "data:image/png;base64," + str
}
// 获取文件路径
// 要判断是否具有权限
// userId是否具有fileId的访问权限
+124
View File
@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.title}}</title>
<link href="/css/bootstrap.css" rel="stylesheet">
<link id="styleLink" href="/css/pdf.css" rel="stylesheet">
<style>
body {
margin-top: 30px;
}
table {
margin-bottom: 16px;
}
table th, table td {
padding: 6px 13px;
border: 1px solid #ddd;
}
table th {
font-weight: bold;
}
table tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
border: 1px solid #ddd;
border-collapse: collapse;
padding: 6px 13px;
}
</style>
</head>
<body>
<div id="content">
<h1 class="title tex2jax_ignore">
{{if .blog.Title}}
{{.blog.Title}}
{{else}}
Untitled
{{end}}
</h1>
<div class="created-time">
<!--
{{ if .userBlog.Logo}}
<img src="{{.userBlog.Logo}}" id="logo">
{{else}}
<img src="{{$.siteUrl}}/images/blog/default_avatar.png" id="logo">
{{end}}
{{.userInfo.Username}}
-->
{{if .blog.Tags}}
<img src="{{$.siteUrl}}/images/blog/tag.png" id="tag">
{{blogTagsForExport $ .blog.Tags}}
{{end}}
</div>
<div class="desc">
{{if .blog.IsMarkdown }}
<div id="markdownContent" style="display: none">
<!-- 用textarea装html, 防止得到的值失真 -->
<textarea>
{{.content | raw}}
</textarea>
</div>
<div id="parsedContent">
</div>
{{else}}
{{.content | raw}}
{{end}}
</div>
</div>
<!--
<div id="footer">
<p class="split"></p>
<a href="http://leanote.com"><img src="{{.siteUrl}}/images/logo/leanote_icon_blue.png" id="leanote_logo"/></a>
<p>
<a href="http://leanote.com">Leanote</a>
<br />
<a href="http://leanote.com/service">Upgrade Account</a>
</p>
</div>
-->
<script src="{{.siteUrl}}/js/jquery-1.9.0.min.js"></script>
<link href="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.css" type="text/css" rel="stylesheet">
<script src="{{.siteUrl}}/public/mdeditor/editor/google-code-prettify/prettify.js"></script>
<script>
function ok() {
setTimeout(function() {
window.status = 'done';
}, 0);
}
</script>
{{if not .blog.IsMarkdown }}
<script>
$("pre").addClass("prettyprint");
prettyPrint();
ok();
</script>
{{end}}
{{if .blog.IsMarkdown }}
<script src="/public/libs/md2html/md2html_for_export.js"></script>
<script>
var content = $.trim($("#markdownContent textarea").val());
md2Html(content, $("#parsedContent"), function(html) {
$("pre").addClass("prettyprint");
prettyPrint();
ok();
});
</script>
{{end}}
</body>
</html>
+2
View File
@@ -38,6 +38,8 @@ POST /findPasswordUpdate Auth.FindPasswordUpdate
* /note/copySharedNote Note.CopySharedNote
* /note/searchNoteByTags Note.SearchNoteByTags
* /note/setNote2Blog Note.SetNote2Blog
* /note/exportPdf Note.ExportPDF
* /note/toPdf Note.ToPdf
# pjax
GET /note/:noteId Note.Index
@@ -1,7 +1,3 @@
@font-face {
font-family: Si;
src: url("/public/fonts/MSYH.TTF");
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
@@ -28,14 +24,14 @@
}
*,
body {
font-family: Si, 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
font-family: 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
font-weight: 300;
font-size: 14px;
}
h1,
h2,
h3 {
font-family: Si, 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
font-family: 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Helvetica Neue', Arial, 'Hiragino Sans GB';
}
* {
font-size: 16px;
@@ -1,8 +1,3 @@
@font-face {
font-family: Si;
src:url("/public/fonts/MSYH.TTF");
}
// font
@font-face {
font-family: 'Open Sans';
@@ -31,7 +26,7 @@
@bgColor: #fff;
@headerBgColor: #fff;
@fontFamily: Si, 'Microsoft YaHei','WenQuanYi Micro Hei','Helvetica Neue',Arial,'Hiragino Sans GB';
@fontFamily: 'Microsoft YaHei','WenQuanYi Micro Hei','Helvetica Neue',Arial,'Hiragino Sans GB';
@green: #65bd77;
*, body {
+9 -1
View File
@@ -1096,6 +1096,12 @@ Note.download = function(url, params) {
$('<form target="mdImageManager" action="' + url + '" method="GET">' + inputs + '</form>').appendTo('body').submit().remove();
};
// 导出成PDF
Note.exportPDF = function(target) {
var noteId = $(target).attr("noteId");
$('<form target="mdImageManager" action="/note/exportPdf" method="GET"><input name="noteId" value="' + noteId + '"/></form>').appendTo('body').submit().remove();
};
//--------------
// read only
@@ -1406,7 +1412,6 @@ Note.toggleReadOnly = function() {
}
note.readOnly = true;
Note.readOnly = true;
};
// 切换到编辑模式
LEA.toggleWriteable = Note.toggleWriteable = function() {
@@ -1505,6 +1510,9 @@ Note.initContextmenu = function() {
{ text: getMsg("publicAsBlog"), alias: 'set2Blog', faIcon: "fa-bold", action: Note.setNote2Blog },
{ text: getMsg("cancelPublic"), alias: 'unset2Blog', faIcon: "fa-undo", action: Note.setNote2Blog },
{ type: "splitLine" },
// { text: "分享到社区", alias: 'html2Image', icon: "", action: Note.html2Image},
{ text: getMsg("exportPdf"), alias: 'exportPDF', faIcon: "fa-file-pdf-o", action: Note.exportPDF},
{ type: "splitLine" },
{ text: getMsg("delete"), icon: "", faIcon: "fa-trash-o", action: Note.deleteNote },
{ text: getMsg("move"), alias: "move", faIcon: "fa-arrow-right",
type: "group",
+632
View File
@@ -0,0 +1,632 @@
/**
* Markdown convert to html
*
<script src="md2html.js"></script>
<script>
md2Html(document.getElementById('md').value, $('#content'));
md2Html('hello, **leanote**', document.getElementById('content2'), function allRendered(html) {
alert(html);
});
</script>
*
* @author leanote.com
* @date 2015/04/11
*/
// Markdown.Converter.js
var Markdown;if(typeof exports==="object"&&typeof require==="function"){Markdown=exports}else{Markdown={}}(function(){function identity(x){return x}function returnFalse(x){return false}function HookCollection(){}HookCollection.prototype={chain:function(hookname,func){var original=this[hookname];if(!original){throw new Error("unknown hook "+hookname)}if(original===identity){this[hookname]=func}else{this[hookname]=function(text){var args=Array.prototype.slice.call(arguments,0);args[0]=original.apply(null,args);return func.apply(null,args)}}},set:function(hookname,func){if(!this[hookname]){throw new Error("unknown hook "+hookname)}this[hookname]=func},addNoop:function(hookname){this[hookname]=identity},addFalse:function(hookname){this[hookname]=returnFalse}};Markdown.HookCollection=HookCollection;function SaveHash(){}SaveHash.prototype={set:function(key,value){this["s_"+key]=value},get:function(key){return this["s_"+key]}};Markdown.Converter=function(){var options={};this.setOptions=function(optionsParam){options=optionsParam};var pluginHooks=this.hooks=new HookCollection();pluginHooks.addNoop("plainLinkText");pluginHooks.addNoop("preConversion");pluginHooks.addNoop("postNormalization");pluginHooks.addNoop("preBlockGamut");pluginHooks.addNoop("postBlockGamut");pluginHooks.addNoop("preSpanGamut");pluginHooks.addNoop("postSpanGamut");pluginHooks.addNoop("postConversion");var g_urls;var g_titles;var g_html_blocks;var g_list_level;this.makeHtml=function(text){if(g_urls){throw new Error("Recursive call to converter.makeHtml")}g_urls=new SaveHash();g_titles=new SaveHash();g_html_blocks=[];g_list_level=0;text=pluginHooks.preConversion(text);text=text.replace(/~/g,"~T");text=text.replace(/\$/g,"~D");text=text.replace(/\r\n/g,"\n");text=text.replace(/\r/g,"\n");text="\n\n"+text+"\n\n";text=_Detab(text);text=text.replace(/^[ \t]+$/mg,"");text=pluginHooks.postNormalization(text);text=_HashHTMLBlocks(text);text=_StripLinkDefinitions(text);text=_RunBlockGamut(text);text=_UnescapeSpecialChars(text);text=text.replace(/~D/g,"$$");text=text.replace(/~T/g,"~");text=pluginHooks.postConversion(text);g_html_blocks=g_titles=g_urls=null;return text};function _StripLinkDefinitions(text){text=text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,function(wholeMatch,m1,m2,m3,m4,m5){m1=m1.toLowerCase();g_urls.set(m1,_EncodeAmpsAndAngles(m2));if(m4){return m3}else{if(m5){g_titles.set(m1,m5.replace(/"/g,"&quot;"))}}return""});return text}function _HashHTMLBlocks(text){var block_tags_a="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";var block_tags_b="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";text=text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);text=text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);text=text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);text=text.replace(/\n\n[ ]{0,3}(<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g,hashElement);text=text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);return text}function hashElement(wholeMatch,m1){var blockText=m1;blockText=blockText.replace(/^\n+/,"");blockText=blockText.replace(/\n+$/g,"");blockText="\n\n~K"+(g_html_blocks.push(blockText)-1)+"K\n\n";return blockText}var blockGamutHookCallback=function(t){return _RunBlockGamut(t)};function _RunBlockGamut(text,doNotUnhash){text=pluginHooks.preBlockGamut(text,blockGamutHookCallback);text=_DoHeaders(text);var replacement="<hr />\n";text=text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,replacement);text=text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm,replacement);text=text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm,replacement);text=_DoLists(text);text=_DoCodeBlocks(text);text=_DoBlockQuotes(text);text=pluginHooks.postBlockGamut(text,blockGamutHookCallback);text=_HashHTMLBlocks(text);text=_FormParagraphs(text,doNotUnhash);return text}function _RunSpanGamut(text){text=pluginHooks.preSpanGamut(text);text=_DoCodeSpans(text);text=_EscapeSpecialCharsWithinTagAttributes(text);text=_EncodeBackslashEscapes(text);text=_DoImages(text);text=_DoAnchors(text);text=_DoAutoLinks(text);text=text.replace(/~P/g,"://");text=_EncodeAmpsAndAngles(text);text=options._DoItalicsAndBold?options._DoItalicsAndBold(text):_DoItalicsAndBold(text);text=text.replace(/ +\n/g," <br>\n");text=pluginHooks.postSpanGamut(text);return text}function _EscapeSpecialCharsWithinTagAttributes(text){var regex=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;text=text.replace(regex,function(wholeMatch){var tag=wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");tag=escapeCharacters(tag,wholeMatch.charAt(1)=="!"?"\\Line truncated
return tag});return text}function _DoAnchors(text){text=text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);text=text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?((?:\([^)]*\)|[^()\s])*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);text=text.replace(/(\[([^\[\]]+)\])()()()()()/g,writeAnchorTag);return text}function writeAnchorTag(wholeMatch,m1,m2,m3,m4,m5,m6,m7){if(m7==undefined){m7=""}var whole_match=m1;var link_text=m2.replace(/:\/\//g,"~P");var link_id=m3.toLowerCase();var url=m4;var title=m7;if(url==""){if(link_id==""){link_id=link_text.toLowerCase().replace(/ ?\n/g," ")}url="#"+link_id;if(g_urls.get(link_id)!=undefined){url=g_urls.get(link_id);if(g_titles.get(link_id)!=undefined){title=g_titles.get(link_id)}}else{if(whole_match.search(/\(\s*\)$/m)>-1){url=""}else{return whole_match}}}url=encodeProblemUrlChars(url);url=escapeCharacters(url,"*_");var result='<a href="'+url+'"';if(title!=""){title=attributeEncode(title);title=escapeCharacters(title,"*_");result+=' title="'+title+'"'}result+=">"+link_text+"</a>";return result}function _DoImages(text){text=text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);text=text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);return text}function attributeEncode(text){return text.replace(/>/g,"&gt;").replace(/</g,"&lt;").replace(/"/g,"&quot;")}function writeImageTag(wholeMatch,m1,m2,m3,m4,m5,m6,m7){var whole_match=m1;var alt_text=m2;var link_id=m3.toLowerCase();var url=m4;var title=m7;if(!title){title=""}if(url==""){if(link_id==""){link_id=alt_text.toLowerCase().replace(/ ?\n/g," ")}url="#"+link_id;if(g_urls.get(link_id)!=undefined){url=g_urls.get(link_id);if(g_titles.get(link_id)!=undefined){title=g_titles.get(link_id)}}else{return whole_match}}alt_text=escapeCharacters(attributeEncode(alt_text),"*_[]()");url=escapeCharacters(url,"*_");var result='<img src="'+url+'" alt="'+alt_text+'"';title=attributeEncode(title);title=escapeCharacters(title,"*_");result+=' title="'+title+'"';result+=" />";return result}function _DoHeaders(text){text=text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(wholeMatch,m1){return"<h1>"+_RunSpanGamut(m1)+"</h1>\n\n"});text=text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(matchFound,m1){return"<h2>"+_RunSpanGamut(m1)+"</h2>\n\n"});text=text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(wholeMatch,m1,m2){var h_level=m1.length;return"<h"+h_level+">"+_RunSpanGamut(m2)+"</h"+h_level+">\n\n"});return text}function _DoLists(text,isInsideParagraphlessListItem){text+="~0";var whole_list=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;if(g_list_level){text=text.replace(whole_list,function(wholeMatch,m1,m2){var list=m1;var list_type=(m2.search(/[*+-]/g)>-1)?"ul":"ol";var result=_ProcessListItems(list,list_type,isInsideParagraphlessListItem);result=result.replace(/\s+$/,"");result="<"+list_type+">"+result+"</"+list_type+">\n";return result})}else{whole_list=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;text=text.replace(whole_list,function(wholeMatch,m1,m2,m3){var runup=m1;var list=m2;var list_type=(m3.search(/[*+-]/g)>-1)?"ul":"ol";var result=_ProcessListItems(list,list_type);result=runup+"<"+list_type+">\n"+result+"</"+list_type+">\n";return result})}text=text.replace(/~0/,"");return text}var _listItemMarkers={ol:"\\d+[.]",ul:"[*+-]"};function _ProcessListItems(list_str,list_type,isInsideParagraphlessListItem){g_list_level++;list_str=list_str.replace(/\n{2,}$/,"\n");list_str+="~0";var marker=_listItemMarkers[list_type];var re=new RegExp("(^[ \\t]*)("+marker+")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1("+marker+")[ \\t]+))","gm");var last_item_had_a_double_newline=false;list_str=list_str.replace(re,function(wholeMatch,m1,m2,m3){var item=m3;var leading_space=m1;var ends_with_double_newline=/\n\n$/.test(item);var contains_double_newline=ends_with_double_newline||item.search(/\n{2,}/)>-1;if(contains_double_newline||last_item_had_a_double_newline){item=_RunBlockGamut(_Outdent(item),true)}else{item=_DoLists(_Outdent(item),true);item=item.replace(/\n$/,"");if(!isInsideParagraphlessListItem){item=_RunSpanGamut(item)}}last_item_had_a_double_newline=ends_with_double_newline;return"<li>"+item+"</li>\n"});list_str=list_str.replace(/~0/g,"");g_list_level--;return list_str}function _DoCodeBlocks(text){text+="~0";text=text.replace(/(?:\n\n|^\n?)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(wholeMatch,m1,m2){var codeblock=m1;var nextChar=m2;codeblock=_EncodeCode(_Outdent(codeblock));codeblock=_Detab(codeblock);codeblock=codeblock.replace(/^\n+/g,"");codeblock=codeblock.replace(/\n+$/g,"");codeblock="<pre><code>"+codeblock+"\n</code></pre>";return"\n\n"+codeblock+"\n\n"+nextChar});text=text.replace(/~0/,"");return text}function hashBlock(text){text=text.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(g_html_blocks.push(text)-1)Line truncated
}function _DoCodeSpans(text){text=text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(wholeMatch,m1,m2,m3,m4){var c=m3;c=c.replace(/^([ \t]*)/g,"");c=c.replace(/[ \t]*$/g,"");c=_EncodeCode(c);c=c.replace(/:\/\//g,"~P");return m1+"<code>"+c+"</code>"});return text}function _EncodeCode(text){text=text.replace(/&/g,"&amp;");text=text.replace(/</g,"&lt;");text=text.replace(/>/g,"&gt;");text=escapeCharacters(text,"*_{}[]\\",false);return text}function _DoItalicsAndBold(text){text=text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,"$1<strong>$3</strong>$4");text=text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,"$1<em>$3</em>$4");return text}function _DoBlockQuotes(text){text=text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(wholeMatch,m1){var bq=m1;bq=bq.replace(/^[ \t]*>[ \t]?/gm,"~0");bq=bq.replace(/~0/g,"");bq=bq.replace(/^[ \t]+$/gm,"");bq=_RunBlockGamut(bq);bq=bq.replace(/(^|\n)/g,"$1 ");bq=bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(wholeMatch,m1){var pre=m1;pre=pre.replace(/^ /mg,"~0");pre=pre.replace(/~0/g,"");return pre});return hashBlock("<blockquote>\n"+bq+"\n</blockquote>")});return text}function _FormParagraphs(text,doNotUnhash){text=text.replace(/^\n+/g,"");text=text.replace(/\n+$/g,"");var grafs=text.split(/\n{2,}/g);var grafsOut=[];var markerRe=/~K(\d+)K/;var end=grafs.length;for(var i=0;i<end;i++){var str=grafs[i];if(markerRe.test(str)){grafsOut.push(str)}else{if(/\S/.test(str)){str=_RunSpanGamut(str);str=str.replace(/^([ \t]*)/g,"<p>");str+="</p>";grafsOut.push(str)}}}if(!doNotUnhash){end=grafsOut.length;for(var i=0;i<end;i++){var foundAny=true;while(foundAny){foundAny=false;grafsOut[i]=grafsOut[i].replace(/~K(\d+)K/g,function(wholeMatch,id){foundAny=true;return g_html_blocks[id]})}}}return grafsOut.join("\n\n")}function _EncodeAmpsAndAngles(text){text=text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");text=text.replace(/<(?![a-z\/?!]|~D)/gi,"&lt;");return text}function _EncodeBackslashEscapes(text){text=text.replace(/\\(\\)/g,escapeCharacters_callback);text=text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);return text}var charInsideUrl="[-A-Z0-9+&@#/%?=~_|[\\]()!:,.;]",charEndingUrl="[-A-Z0-9+&@#/%=~_|[\\])]",autoLinkRegex=new RegExp('(="|<)?\\b(https?|ftp)(://'+charInsideUrl+"*"+charEndingUrl+")(?=$|\\W)","gi"),endCharRegex=new RegExp(charEndingUrl,"i");function handleTrailingParens(wholeMatch,lookbehind,protocol,link){if(lookbehind){return wholeMatch}if(link.charAt(link.length-1)!==")"){return"<"+protocol+link+">"}var parens=link.match(/[()]/g);var level=0;for(var i=0;i<parens.length;i++){if(parens[i]==="("){if(level<=0){level=1}else{level++}}else{level--}}var tail="";if(level<0){var re=new RegExp("\\){1,"+(-level)+"}$");link=link.replace(re,function(trailingParens){tail=trailingParens;return""})}if(tail){var lastChar=link.charAt(link.length-1);if(!endCharRegex.test(lastChar)){tail=lastChar+tail;link=link.substr(0,link.length-1)}}return"<"+protocol+link+">"+tail}function _DoAutoLinks(text){text=text.replace(autoLinkRegex,handleTrailingParens);var replacer=function(wholematch,m1){return'<a href="'+m1+'">'+pluginHooks.plainLinkText(m1)+"</a>"};text=text.replace(/<((https?|ftp):[^'">\s]+)>/gi,replacer);return text}function _UnescapeSpecialChars(text){text=text.replace(/~E(\d+)E/g,function(wholeMatch,m1){var charCodeToReplace=parseInt(m1);return String.fromCharCode(charCodeToReplace)});return text}function _Outdent(text){text=text.replace(/^(\t|[ ]{1,4})/gm,"~0");text=text.replace(/~0/g,"");return text}function _Detab(text){if(!/\t/.test(text)){return text}var spaces=[" "," "," "," "],skew=0,v;return text.replace(/[\n\t]/g,function(match,offset){if(match==="\n"){skew=offset+1;return match}v=(offset-skew)%4;skew=offset+1;return spaces[v]})}var _problemUrlChars=/(?:["'*()[\]:]|~D)/g;function encodeProblemUrlChars(url){if(!url){return""}var len=url.length;return url.replace(_problemUrlChars,function(match,offset){if(match=="~D"){return"%24"}if(match==":"){return":"}return"%"+match.charCodeAt(0).toString(16)})}function escapeCharacters(text,charsToEscape,afterBackslash){var regexString="(["+charsToEscape.replace(/([\[\]\\])/g,"\\$1")+"])";if(afterBackslash){regexString="\\\\"+regexString}var regex=new RegExp(regexString,"g");text=text.replace(regex,escapeCharacters_callback);return text}function escapeCharacters_callback(wholeMatch,m1){var charCodeToEscape=m1.charCodeAt(0);return"~E"+charCodeToEscape+"E"}}})();
// Markdown.Extra.js
(function(){var inlineTags=new RegExp(["^(<\\/?(a|abbr|acronym|applet|area|b|basefont|","bdo|big|button|cite|code|del|dfn|em|figcaption|","font|i|iframe|img|input|ins|kbd|label|map|","mark|meter|object|param|progress|q|ruby|rp|rt|s|","samp|script|select|small|span|strike|strong|","sub|sup|textarea|time|tt|u|var|wbr)[^>]*>|","<(br)\\s?\\/?>)$"].join(""),"i");if(!Array.indexOf){Array.prototype.indexOf=function(obj){for(var i=0;i<this.length;i++){if(this[i]==obj){return i}}return -1}}function trim(str){return str.replace(/^\s+|\s+$/g,"")}function rtrim(str){return str.replace(/\s+$/g,"")}function outdent(text){return text.replace(new RegExp("^(\\t|[ ]{1,4})","gm"),"")}function contains(str,substr){return str.indexOf(substr)!=-1}function sanitizeHtml(html,whitelist){return html.replace(/<[^>]*>?/gi,function(tag){return tag.match(whitelist)?tag:""})}function union(x,y){var obj={};for(var i=0;i<x.length;i++){obj[x[i]]=x[i]}for(i=0;i<y.length;i++){obj[y[i]]=y[i]}var res=[];for(var k in obj){if(obj.hasOwnProperty(k)){res.push(obj[k])}}return res}function addAnchors(text){if(text.charAt(0)!="\x02"){text="\x02"+text}if(text.charAt(text.length-1)!="\x03"){text=text+"\x03"}return text}function removeAnchors(text){if(text.charAt(0)=="\x02"){text=text.substr(1)}if(text.charAt(text.length-1)=="\x03"){text=text.substr(0,text.length-1)}return text}function convertSpans(text,extra){return sanitizeHtml(convertAll(text,extra),inlineTags)}function convertAll(text,extra){var result=extra.blockGamutHookCallback(text);result=unescapeSpecialChars(result);result=result.replace(/~D/g,"$$").replace(/~T/g,"~");result=extra.previousPostConversion(result);return result}function processEscapesStep1(text){return text.replace(/\\\|/g,"~I").replace(/\\:/g,"~i")}function processEscapesStep2(text){return text.replace(/~I/g,"|").replace(/~i/g,":")}function unescapeSpecialChars(text){text=text.replace(/~E(\d+)E/g,function(wholeMatch,m1){var charCodeToReplace=parseInt(m1);return String.fromCharCode(charCodeToReplace)});return text}function slugify(text){return text.toLowerCase().replace(/\s+/g,"-").replace(/[^\w\-]+/g,"").replace(/\-\-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}Markdown.Extra=function(){this.converter=null;this.hashBlocks=[];this.footnotes={};this.usedFootnotes=[];this.attributeBlocks=false;this.googleCodePrettify=false;this.highlightJs=false;this.tableClass="";this.tabWidth=4};Markdown.Extra.init=function(converter,options){var extra=new Markdown.Extra();var postNormalizationTransformations=[];var preBlockGamutTransformations=[];var postSpanGamutTransformations=[];var postConversionTransformations=["unHashExtraBlocks"];options=options||{};options.extensions=options.extensions||["all"];if(contains(options.extensions,"all")){options.extensions=["tables","fenced_code_gfm","def_list","attr_list","footnotes","smartypants","strikethrough","newlines"]}preBlockGamutTransformations.push("wrapHeaders");if(contains(options.extensions,"attr_list")){postNormalizationTransformations.push("hashFcbAttributeBlocks");preBlockGamutTransformations.push("hashHeaderAttributeBlocks");postConversionTransformations.push("applyAttributeBlocks");extra.attributeBlocks=true}if(contains(options.extensions,"fenced_code_gfm")){preBlockGamutTransformations.push("fencedCodeBlocks");postNormalizationTransformations.push("fencedCodeBlocks")}if(contains(options.extensions,"tables")){preBlockGamutTransformations.push("tables")}if(contains(options.extensions,"def_list")){preBlockGamutTransformations.push("definitionLists")}if(contains(options.extensions,"footnotes")){postNormalizationTransformations.push("stripFootnoteDefinitions");preBlockGamutTransformations.push("doFootnotes");postConversionTransformations.push("printFootnotes")}if(contains(options.extensions,"smartypants")){postConversionTransformations.push("runSmartyPants")}if(contains(options.extensions,"strikethrough")){postSpanGamutTransformations.push("strikethrough")}if(contains(options.extensions,"newlines")){postSpanGamutTransformations.push("newlines")}converter.hooks.chain("postNormalization",function(text){return extra.doTransform(postNormalizationTransformations,text)+"\n"});converter.hooks.chain("preBlockGamut",function(text,blockGamutHookCallback){extra.blockGamutHookCallback=blockGamutHookCallback;text=processEscapesStep1(text);text=extra.doTransform(preBlockGamutTransformations,text)+"\n";text=processEscapesStep2(text);return text});converter.hooks.chain("postSpanGamut",function(text){return extra.doTransform(postSpanGamutTransformations,text)});extra.previousPostConversion=converter.hooks.postConversion;converter.hooks.chain("postConversion",function(text){text=extra.doTransform(postConversionTransformations,text);extra.hashBlocks=[];extra.footnotes={};extra.usedFootnotes=[];return text});if("highlighter" in options){extra.googleCodePrettify=options.highlighter==="prettify";extra.highlightJs=options.highlighter==="highlight"}if("table_class" in options){extra.tableClass=options.table_class}extLine truncated
return extra};Markdown.Extra.prototype.doTransform=function(transformations,text){for(var i=0;i<transformations.length;i++){text=this[transformations[i]](text)}return text};Markdown.Extra.prototype.hashExtraBlock=function(block){return"\n<p>~X"+(this.hashBlocks.push(block)-1)+"X</p>\n"};Markdown.Extra.prototype.hashExtraInline=function(block){return"~X"+(this.hashBlocks.push(block)-1)+"X"};Markdown.Extra.prototype.unHashExtraBlocks=function(text){var self=this;function recursiveUnHash(){var hasHash=false;text=text.replace(/(?:<p>)?~X(\d+)X(?:<\/p>)?/g,function(wholeMatch,m1){hasHash=true;var key=parseInt(m1,10);return self.hashBlocks[key]});if(hasHash===true){recursiveUnHash()}}recursiveUnHash();return text};Markdown.Extra.prototype.wrapHeaders=function(text){function wrap(text){return"\n"+text+"\n"}text=text.replace(/^.+[ \t]*\n=+[ \t]*\n+/gm,wrap);text=text.replace(/^.+[ \t]*\n-+[ \t]*\n+/gm,wrap);text=text.replace(/^\#{1,6}[ \t]*.+?[ \t]*\#*\n+/gm,wrap);return text};var attrBlock="\\{[ \\t]*((?:[#.][-_:a-zA-Z0-9]+[ \\t]*)+)\\}";var hdrAttributesA=new RegExp("^(#{1,6}.*#{0,6})[ \\t]+"+attrBlock+"[ \\t]*(?:\\n|0x03)","gm");var hdrAttributesB=new RegExp("^(.*)[ \\t]+"+attrBlock+"[ \\t]*\\n"+"(?=[\\-|=]+\\s*(?:\\n|0x03))","gm");var fcbAttributes=new RegExp("^(```[^`\\n]*)[ \\t]+"+attrBlock+"[ \\t]*\\n"+"(?=([\\s\\S]*?)\\n```[ \\t]*(\\n|0x03))","gm");Markdown.Extra.prototype.hashHeaderAttributeBlocks=function(text){var self=this;function attributeCallback(wholeMatch,pre,attr){return"<p>~XX"+(self.hashBlocks.push(attr)-1)+"XX</p>\n"+pre+"\n"}text=text.replace(hdrAttributesA,attributeCallback);text=text.replace(hdrAttributesB,attributeCallback);return text};Markdown.Extra.prototype.hashFcbAttributeBlocks=function(text){var self=this;function attributeCallback(wholeMatch,pre,attr){return"<p>~XX"+(self.hashBlocks.push(attr)-1)+"XX</p>\n"+pre+"\n"}return text.replace(fcbAttributes,attributeCallback)};Markdown.Extra.prototype.applyAttributeBlocks=function(text){var self=this;var blockRe=new RegExp("<p>~XX(\\d+)XX</p>[\\s]*"+'(?:<(h[1-6]|pre)(?: +class="(\\S+)")?(>[\\s\\S]*?</\\2>))',"gm");text=text.replace(blockRe,function(wholeMatch,k,tag,cls,rest){if(!tag){return""}var key=parseInt(k,10);var attributes=self.hashBlocks[key];var id=attributes.match(/#[^\s#.]+/g)||[];var idStr=id[0]?' id="'+id[0].substr(1,id[0].length-1)+'"':"";var classes=attributes.match(/\.[^\s#.]+/g)||[];for(var i=0;i<classes.length;i++){classes[i]=classes[i].substr(1,classes[i].length-1)}var classStr="";if(cls){classes=union(classes,[cls])}if(classes.length>0){classStr=' class="'+classes.join(" ")+'"'}return"<"+tag+idStr+classStr+rest});return text};Markdown.Extra.prototype.tables=function(text){var self=this;var leadingPipe=new RegExp(["^","[ ]{0,3}","[|]","(.+)\\n","[ ]{0,3}","[|]([ ]*[-:]+[-| :]*)\\n","(","(?:[ ]*[|].*\\n?)*",")","(?:\\n|$)"].join(""),"gm");var noLeadingPipe=new RegExp(["^","[ ]{0,3}","(\\S.*[|].*)\\n","[ ]{0,3}","([-:]+[ ]*[|][-| :]*)\\n","(","(?:.*[|].*\\n?)*",")","(?:\\n|$)"].join(""),"gm");text=text.replace(leadingPipe,doTable);text=text.replace(noLeadingPipe,doTable);function doTable(match,header,separator,body,offset,string){header=header.replace(/^ *[|]/m,"");separator=separator.replace(/^ *[|]/m,"");body=body.replace(/^ *[|]/gm,"");header=header.replace(/[|] *$/m,"");separator=separator.replace(/[|] *$/m,"");body=body.replace(/[|] *$/gm,"");alignspecs=separator.split(/ *[|] */);align=[];for(var i=0;i<alignspecs.length;i++){var spec=alignspecs[i];if(spec.match(/^ *-+: *$/m)){align[i]=' align="right"'}else{if(spec.match(/^ *:-+: *$/m)){align[i]=' align="center"'}else{if(spec.match(/^ *:-+ *$/m)){align[i]=' align="left"'}else{align[i]=""}}}}var headers=header.split(/ *[|] */);var colCount=headers.length;var cls=self.tableClass?' class="'+self.tableClass+'"':"";var html=["<table",cls,">\n","<thead>\n","<tr>\n"].join("");for(i=0;i<colCount;i++){var headerHtml=convertSpans(trim(headers[i]),self);html+=[" <th",align[i],">",headerHtml,"</th>\n"].join("")}html+="</tr>\n</thead>\n";var rows=body.split("\n");for(i=0;i<rows.length;i++){if(rows[i].match(/^\s*$/)){continue}var rowCells=rows[i].split(/ *[|] */);var lenDiff=colCount-rowCells.length;for(var j=0;j<lenDiff;j++){rowCells.push("")}html+="<tr>\n";for(j=0;j<colCount;j++){var colHtml=convertSpans(trim(rowCells[j]),self);html+=[" <td",align[j],">",colHtml,"</td>\n"].join("")}html+="</tr>\n"}html+="</table>\n";return self.hashExtraBlock(html)}return text};Markdown.Extra.prototype.stripFootnoteDefinitions=function(text){var self=this;text=text.replace(/\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/g,function(wholeMatch,m1,m2){m1=slugify(m1);m2+="\n";m2=m2.replace(/^[ ]{0,3}/g,"");self.footnotes[m1]=m2;return"\n"});return text};Markdown.Extra.prototype.doFootnotes=function(text){var self=this;if(self.isConvertingFootnote===true){return text}var footnoteCounter=0;text=text.replace(/\[\^(.+?)\]/g,function(wholeMatch,m1){var id=slugify(m1);var footnote=sLine truncated
if(footnote===undefined){return wholeMatch}footnoteCounter++;self.usedFootnotes.push(id);var html='<a href="#fn:'+id+'" id="fnref:'+id+'" title="See footnote" class="footnote">'+footnoteCounter+"</a>";return self.hashExtraInline(html)});return text};Markdown.Extra.prototype.printFootnotes=function(text){var self=this;if(self.usedFootnotes.length===0){return text}text+='\n\n<div class="footnotes">\n<hr>\n<ol>\n\n';for(var i=0;i<self.usedFootnotes.length;i++){var id=self.usedFootnotes[i];var footnote=self.footnotes[id];self.isConvertingFootnote=true;var formattedfootnote=convertSpans(footnote,self);delete self.isConvertingFootnote;text+='<li id="fn:'+id+'">'+formattedfootnote+' <a href="#fnref:'+id+'" title="Return to article" class="reversefootnote">&#8617;</a></li>\n\n'}text+="</ol>\n</div>";return text};Markdown.Extra.prototype.fencedCodeBlocks=function(text){function encodeCode(code){code=code.replace(/&/g,"&amp;");code=code.replace(/</g,"&lt;");code=code.replace(/>/g,"&gt;");code=code.replace(/~D/g,"$$");code=code.replace(/~T/g,"~");return code}var self=this;text=text.replace(/(?:^|\n)```([^`\n]*)\n([\s\S]*?)\n```[ \t]*(?=\n)/g,function(match,m1,m2){var language=trim(m1),codeblock=m2;var preclass=self.googleCodePrettify?' class="prettyprint"':"";var codeclass="";if(language){if(self.googleCodePrettify||self.highlightJs){codeclass=' class="language-'+language+'"'}else{codeclass=' class="'+language+'"'}}var html=["<pre",preclass,"><code",codeclass,">",encodeCode(codeblock),"</code></pre>"].join("");return self.hashExtraBlock(html)});return text};Markdown.Extra.prototype.educatePants=function(text){var self=this;var result="";var blockOffset=0;text.replace(/(?:<!--[\s\S]*?-->)|(<)([a-zA-Z1-6]+)([^\n]*?>)([\s\S]*?)(<\/\2>)/g,function(wholeMatch,m1,m2,m3,m4,m5,offset){var token=text.substring(blockOffset,offset);result+=self.applyPants(token);self.smartyPantsLastChar=result.substring(result.length-1);blockOffset=offset+wholeMatch.length;if(!m1){result+=wholeMatch;return}if(!/code|kbd|pre|script|noscript|iframe|math|ins|del|pre/i.test(m2)){m4=self.educatePants(m4)}else{self.smartyPantsLastChar=m4.substring(m4.length-1)}result+=m1+m2+m3+m4+m5});var lastToken=text.substring(blockOffset);result+=self.applyPants(lastToken);self.smartyPantsLastChar=result.substring(result.length-1);return result};function revertPants(wholeMatch,m1){var blockText=m1;blockText=blockText.replace(/&\#8220;/g,'"');blockText=blockText.replace(/&\#8221;/g,'"');blockText=blockText.replace(/&\#8216;/g,"'");blockText=blockText.replace(/&\#8217;/g,"'");blockText=blockText.replace(/&\#8212;/g,"---");blockText=blockText.replace(/&\#8211;/g,"--");blockText=blockText.replace(/&\#8230;/g,"...");return blockText}Markdown.Extra.prototype.applyPants=function(text){text=text.replace(/---/g,"&#8212;").replace(/--/g,"&#8211;");text=text.replace(/\.\.\./g,"&#8230;").replace(/\.\s\.\s\./g,"&#8230;");text=text.replace(/``/g,"&#8220;").replace(/''/g,"&#8221;");if(/^'$/.test(text)){if(/\S/.test(this.smartyPantsLastChar)){return"&#8217;"}return"&#8216;"}if(/^"$/.test(text)){if(/\S/.test(this.smartyPantsLastChar)){return"&#8221;"}return"&#8220;"}text=text.replace(/^'(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/,"&#8217;");text=text.replace(/^"(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/,"&#8221;");text=text.replace(/"'(?=\w)/g,"&#8220;&#8216;");text=text.replace(/'"(?=\w)/g,"&#8216;&#8220;");text=text.replace(/'(?=\d{2}s)/g,"&#8217;");text=text.replace(/(\s|&nbsp;|--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)'(?=\w)/g,"$1&#8216;");text=text.replace(/([^\s\[\{\(\-])'/g,"$1&#8217;");text=text.replace(/'(?=\s|s\b)/g,"&#8217;");text=text.replace(/'/g,"&#8216;");text=text.replace(/(\s|&nbsp;|--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)"(?=\w)/g,"$1&#8220;");text=text.replace(/([^\s\[\{\(\-])"/g,"$1&#8221;");text=text.replace(/"(?=\s)/g,"&#8221;");text=text.replace(/"/ig,"&#8220;");return text};Markdown.Extra.prototype.runSmartyPants=function(text){this.smartyPantsLastChar="";text=this.educatePants(text);text=text.replace(/(<([a-zA-Z1-6]+)\b([^\n>]*?)(\/)?>)/g,revertPants);return text};Markdown.Extra.prototype.definitionLists=function(text){var wholeList=new RegExp(["(\\x02\\n?|\\n\\n)","(?:","(","(","[ ]{0,3}","((?:[ \\t]*\\S.*\\n)+)","\\n?","[ ]{0,3}:[ ]+",")","([\\s\\S]+?)","(","(?=\\0x03)","|","(?=","\\n{2,}","(?=\\S)","(?!","[ ]{0,3}","(?:\\S.*\\n)+?","\\n?","[ ]{0,3}:[ ]+",")","(?!","[ ]{0,3}:[ ]+",")",")",")",")",")"].join(""),"gm");var self=this;text=addAnchors(text);text=text.replace(wholeList,function(match,pre,list){var result=trim(self.processDefListItems(list));result="<dl>\n"+result+"\n</dl>";return pre+self.hashExtraBlock(result)+"\n\n"});return removeAnchors(text)};Markdown.Extra.prototype.processDefListItems=function(listStr){var self=this;var dt=new RegExp(["(\\x02\\n?|\\n\\n+)","(","[ ]{0,3}","(?![:][ ]|[ ])","(?:\\S.*\\n)+?",")","(?=\\n?[ ]{0,3}:[ ])"].join(""),"gm");var dd=new RegExp(["\\n(\\n+)?","(","[ ]{0,3}","[:][ ]+",")","([\\s\\S]Line truncated
listStr=addAnchors(listStr);listStr=listStr.replace(/\n{2,}(?=\\x03)/,"\n");listStr=listStr.replace(dt,function(match,pre,termsStr){var terms=trim(termsStr).split("\n");var text="";for(var i=0;i<terms.length;i++){var term=terms[i];term=convertSpans(trim(term),self);text+="\n<dt>"+term+"</dt>"}return text+"\n"});listStr=listStr.replace(dd,function(match,leadingLine,markerSpace,def){if(leadingLine||def.match(/\n{2,}/)){def=Array(markerSpace.length+1).join(" ")+def;def=outdent(def)+"\n\n";def="\n"+convertAll(def,self)+"\n"}else{def=rtrim(def);def=convertSpans(outdent(def),self)}return"\n<dd>"+def+"</dd>\n"});return removeAnchors(listStr)};Markdown.Extra.prototype.strikethrough=function(text){return text.replace(/([\W_]|^)~T~T(?=\S)([^\r]*?\S[\*_]*)~T~T([\W_]|$)/g,"$1<del>$2</del>$3")};Markdown.Extra.prototype.newlines=function(text){return text.replace(/(<(?:br|\/li)>)?\n/g,function(wholeMatch,previousTag){return previousTag?wholeMatch:" <br>\n"})}})();
(function() {
// Create the converter and the editor
var converter = new Markdown.Converter();
var options = {
_DoItalicsAndBold: function(text) {
// Restore original markdown implementation
text = text.replace(/(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\1/g,
"<strong>$2</strong>");
text = text.replace(/(\*|_)(?=\S)(.+?)(?=\S)\1/g,
"<em>$2</em>");
return text;
}
};
converter.setOptions(options);
function loadJs(src, callback) {
var _doc = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', src);
_doc.appendChild(script);
script.onload = script.onreadystatechange = function() {
if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){
callback && callback();
}
script.onload = script.onreadystatechange = null;
}
}
function _each(list, callback) {
if(list && list.length > 0) {
for(var i = 0; i < list.length; i++) {
callback(list[i]);
}
}
}
function _has(obj, key) {
return hasOwnProperty.call(obj, key);
};
// markdown extra
function initMarkdownExtra() {
// Create the converter and the editor
// var converter = new Markdown.Converter();
var options = {
_DoItalicsAndBold: function(text) {
// Restore original markdown implementation
text = text.replace(/(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\1/g,
"<strong>$2</strong>");
text = text.replace(/(\*|_)(?=\S)(.+?)(?=\S)\1/g,
"<em>$2</em>");
return text;
}
};
converter.setOptions(options);
//================
// markdown exstra
var markdownExtra = {};
markdownExtra.config = {
extensions: [
"fenced_code_gfm",
"tables",
"def_list",
"attr_list",
"footnotes",
"smartypants",
"strikethrough",
"newlines"
],
intraword: true,
comments: true,
highlighter: "highlight"
};
var extraOptions = {
extensions: markdownExtra.config.extensions,
highlighter: "prettify"
};
if(markdownExtra.config.intraword === true) {
var converterOptions = {
_DoItalicsAndBold: function(text) {
text = text.replace(/([^\w*]|^)(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\2(?=[^\w*]|$)/g, "$1<strong>$3</strong>");
text = text.replace(/([^\w*]|^)(\*|_)(?=\S)(.+?)(?=\S)\2(?=[^\w*]|$)/g, "$1<em>$3</em>");
// Redo bold to handle _**word**_
text = text.replace(/([^\w*]|^)(\*\*|__)(?=\S)(.+?[*_]*)(?=\S)\2(?=[^\w*]|$)/g, "$1<strong>$3</strong>");
return text;
}
};
converter.setOptions(converterOptions);
}
if(markdownExtra.config.comments === true) {
converter.hooks.chain("postConversion", function(text) {
return text.replace(/<!--.*?-->/g, function(wholeMatch) {
return wholeMatch.replace(/^<!---(.+?)-?-->$/, ' <span class="comment label label-danger">$1</span> ');
});
});
}
Markdown.Extra.init(converter, extraOptions);
}
//==============
// toc start
function initToc() {
var toc = {};
toc.config = {
marker: "\\[(TOC|toc)\\]",
maxDepth: 6,
button: true,
};
// TOC element description
function TocElement(tagName, anchor, text) {
this.tagName = tagName;
this.anchor = anchor;
this.text = text;
this.children = [];
}
TocElement.prototype.childrenToString = function() {
if(this.children.length === 0) {
return "";
}
var result = "<ul>\n";
_each(this.children, function(child) {
result += child.toString();
});
result += "</ul>\n";
return result;
};
TocElement.prototype.toString = function() {
var result = "<li>";
if(this.anchor && this.text) {
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
}
result += this.childrenToString() + "</li>\n";
return result;
};
// Transform flat list of TocElement into a tree
function groupTags(array, level) {
level = level || 1;
var tagName = "H" + level;
var result = [];
var currentElement;
function pushCurrentElement() {
if(currentElement !== undefined) {
if(currentElement.children.length > 0) {
currentElement.children = groupTags(currentElement.children, level + 1);
}
result.push(currentElement);
}
}
_each(array, function(element) {
if(element.tagName != tagName) {
if(level !== toc.config.maxDepth) {
if(currentElement === undefined) {
currentElement = new TocElement();
}
currentElement.children.push(element);
}
}
else {
pushCurrentElement();
currentElement = element;
}
});
pushCurrentElement();
return result;
}
var utils = {};
var nonWordChars = new RegExp('[^\\p{L}\\p{N}-]', 'g');
utils.slugify = function(text) {
return text.toLowerCase().replace(/\s/g, '-') // Replace spaces with -
.replace(nonWordChars, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
};
// Build the TOC
var previewContentsElt;
function buildToc(previewContentsElt) {
var anchorList = {};
function createAnchor(element) {
var id = element.id || utils.slugify(element.textContent) || 'title';
var anchor = id;
var index = 0;
while (_has(anchorList, anchor)) {
anchor = id + "-" + (++index);
}
anchorList[anchor] = true;
// Update the id of the element
element.id = anchor;
return anchor;
}
var elementList = [];
_each(previewContentsElt.querySelectorAll('h1, h2, h3, h4, h5, h6'), function(elt) {
elementList.push(new TocElement(elt.tagName, createAnchor(elt), elt.textContent));
});
elementList = groupTags(elementList);
return '<div class="toc">\n<ul>\n' + elementList.join("") + '</ul>\n</div>\n';
}
toc.convert = function(previewContentsElt) {
var tocExp = new RegExp("^\\s*" + toc.config.marker + "\\s*$");
var tocEltList = document.querySelectorAll('.table-of-contents, .toc');
var htmlToc = buildToc(previewContentsElt);
// Replace toc paragraphs
_each(previewContentsElt.getElementsByTagName('p'), function(elt) {
if(tocExp.test(elt.innerHTML)) {
elt.innerHTML = htmlToc;
}
});
// Add toc in the TOC button
_each(tocEltList, function(elt) {
elt.innerHTML = htmlToc;
});
}
return toc;
}
//===========
// mathjax
function initMathJax() {
// 配置
MathJax.Hub.Config({
skipStartupTypeset: true,
"HTML-CSS": {
preferredFont: "TeX",
availableFonts: [
"STIX",
"TeX"
],
linebreaks: {
automatic: true
},
EqnChunk: 10,
imageFont: null
},
tex2jax: { inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true },
TeX: {
noUndefined: {
attributes: {
mathcolor: "red",
mathbackground: "#FFEEEE",
mathsize: "90%"
}
},
Safe: {
allow: {
URLs: "safe",
classes: "safe",
cssIDs: "safe",
styles: "safe",
fontsize: "all"
}
}
},
messageStyle: "none"
});
var mathJax = {};
mathJax.config = {
tex : "{}",
tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }'
};
mathJax.init = function(p) {
converter.hooks.chain("preConversion", removeMath);
converter.hooks.chain("postConversion", replaceMath);
};
// From math.stackexchange.com...
//
// The math is in blocks i through j, so
// collect it into one block and clear the others.
// Replace &, <, and > by named entities.
// For IE, put <br> at the ends of comments since IE removes \n.
// Clear the current math positions and store the index of the
// math, then push the math string onto the storage array.
//
function processMath(i, j, unescape) {
var block = blocks.slice(i, j + 1).join("")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
for(HUB.Browser.isMSIE && (block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")); j > i;)
blocks[j] = "", j--;
blocks[i] = "@@" + math.length + "@@";
unescape && (block = unescape(block));
math.push(block);
start = end = last = null;
}
function removeMath(text) {
if(!text) {
return;
}
start = end = last = null;
math = [];
var unescape;
if(/`/.test(text)) {
text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function(text) {
return text.replace(/\$/g, "~D")
});
unescape = function(text) {
return text.replace(/~([TD])/g,
function(match, n) {
return {T: "~", D: "$"}[n]
})
};
} else {
unescape = function(text) {
return text
};
}
//
// The pattern for math delimiters and special symbols
// needed for searching for math in the page.
//
var splitDelimiter = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
var split;
if(3 === "aba".split(/(b)/).length) {
split = function(text, delimiter) {
return text.split(delimiter)
};
} else {
split = function(text, delimiter) {
var b = [], c;
if(!delimiter.global) {
c = delimiter.toString();
var d = "";
c = c.replace(/^\/(.*)\/([im]*)$/, function(a, c, b) {
d = b;
return c
});
delimiter = RegExp(c, d + "g")
}
for(var e = delimiter.lastIndex = 0; c = delimiter.exec(text);) {
b.push(text.substring(e, c.index));
b.push.apply(b, c.slice(1));
e = c.index + c[0].length;
}
b.push(text.substring(e));
return b
};
}
blocks = split(text.replace(/\r\n?/g, "\n"), splitDelimiter);
for(var i = 1, m = blocks.length; i < m; i += 2) {
var block = blocks[i];
if("@" === block.charAt(0)) {
//
// Things that look like our math markers will get
// stored and then retrieved along with the math.
//
blocks[i] = "@@" + math.length + "@@";
math.push(block)
} else if(start) {
// Ignore inline maths that are actually multiline (fixes #136)
if(end == inline && block.charAt(0) == '\n') {
if(last) {
i = last;
processMath(start, i, unescape);
}
start = end = last = null;
braces = 0;
}
//
// If we are in math, look for the end delimiter,
// but don't go past double line breaks, and
// and balance braces within the math.
//
else if(block === end) {
if(braces) {
last = i
} else {
processMath(start, i, unescape)
}
} else {
if(block.match(/\n.*\n/)) {
if(last) {
i = last;
processMath(start, i, unescape);
}
start = end = last = null;
braces = 0;
} else {
if("{" === block) {
braces++
} else {
"}" === block && braces && braces--
}
}
}
} else {
if(block === inline || "$$" === block) {
start = i;
end = block;
braces = 0;
} else {
if("begin" === block.substr(1, 5)) {
start = i;
end = "\\end" + block.substr(6);
braces = 0;
}
}
}
}
last && processMath(start, last, unescape);
return unescape(blocks.join(""));
}
//
// Put back the math strings that were saved,
// and clear the math array (no need to keep it around).
//
function replaceMath(text) {
text = text.replace(/@@(\d+)@@/g, function(match, n) {
return math[n]
});
math = null;
return text
}
//
// This is run to restart MathJax after it has finished
// the previous run (that may have been canceled)
//
function startMJ(toElem, callback) {
var preview = toElem;
pending = false;
HUB.cancelTypeset = false;
HUB.Queue([
"Typeset",
HUB,
preview
]);
// 执行完后, 再执行
HUB.Queue(function() {
callback && callback();
});
}
var ready = false, pending = false, preview = null, inline = "$", blocks, start, end, last, braces, math, HUB = MathJax.Hub;
//
// Runs after initial typeset
//
HUB.Queue(function() {
ready = true;
HUB.processUpdateTime = 50;
HUB.Config({"HTML-CSS": {EqnChunk: 10, EqnChunkFactor: 1}, SVG: {EqnChunk: 10, EqnChunkFactor: 1}})
});
mathJax.init();
return {
convert: startMJ
}
}
function initUml() {
//===========
// uml
var umlDiagrams = {};
umlDiagrams.config = {
flowchartOptions: [
'{',
' "line-width": 2,',
' "font-family": "sans-serif",',
' "font-weight": "normal"',
'}'
].join('\n')
};
var _loadUmlJs = false;
// callback 执行完后执行
umlDiagrams.convert = function(target, callback) {
var previewContentsElt = target;
var sequenceElems = previewContentsElt.querySelectorAll('.prettyprint > .language-sequence');
var flowElems = previewContentsElt.querySelectorAll('.prettyprint > .language-flow');
function convert() {
_each(sequenceElems, function(elt) {
try {
var diagram = Diagram.parse(elt.textContent);
var preElt = elt.parentNode;
var containerElt = crel('div', {
class: 'sequence-diagram'
});
preElt.parentNode.replaceChild(containerElt, preElt);
diagram.drawSVG(containerElt, {
theme: 'simple'
});
}
catch(e) {
console.trace(e);
}
});
_each(flowElems, function(elt) {
try {
var chart = flowchart.parse(elt.textContent);
var preElt = elt.parentNode;
var containerElt = crel('div', {
class: 'flow-chart'
});
preElt.parentNode.replaceChild(containerElt, preElt);
chart.drawSVG(containerElt, JSON.parse(umlDiagrams.config.flowchartOptions));
}
catch(e) {
console.error(e);
}
});
callback && callback();
}
if(sequenceElems.length > 0 || flowElems.length > 0) {
if(!_loadUmlJs) {
loadJs('/public/libs/md2html/uml.js', function() {
_loadUmlJs = true;
convert();
});
} else {
convert();
}
} else {
callback && callback();
}
};
return umlDiagrams;
}
// extra是实时的, 同步进行
initMarkdownExtra();
var m;
window.md2Html = function(mdText, toElem, callback) {
var _umlEnd = false;
var _mathJaxEnd = false;
// 如果是jQuery对象
if(!toElem['querySelectorAll'] && toElem['get']) {
toElem = toElem.get(0);
}
function _go(mdText, toElem) {
var htmlParsed = converter.makeHtml(mdText);
toElem.innerHTML = htmlParsed;
// 同步执行
var toc = initToc();
toc.convert(toElem);
// 异步执行
var umlDiagrams = initUml();
umlDiagrams.convert(toElem, function() {
_umlEnd = true;
if(_mathJaxEnd) {
callback && callback(toElem.innerHTML);
}
});
}
// 表示有mathjax?
// 加载mathJax
if(mdText.indexOf('$') !== -1) {
loadJs("/public/libs/MathJax/MathJax.js?config=TeX-AMS_HTML", function() {
// loadJs("http://cdn.bootcss.com/mathjax/2.5.3/MathJax.js?config=TeX-AMS_HTML", function() {
if(!m) {
var m = initMathJax();
}
// 放到后面, 不然removeMathJax()不运行, bug
_go(mdText, toElem);
m.convert(toElem, function() {
_mathJaxEnd = true;
if(_umlEnd) {
callback && callback(toElem.innerHTML);
}
});
});
} else {
_mathJaxEnd = true;
_go(mdText, toElem);
}
}
})();