(function () { /** * Block-Level Grammar */ var block = { newline: /^\n+/, code: /^( {4}[^\n]+\n*)+/, fences: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, nptable: noop, lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/, def: /^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, table: noop, paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, text: /^[^\n]+/, }; block.bullet = /(?:[*+-]|\d+\.)/; block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; block.item = replace(block.item, "gm")(/bull/g, block.bullet)(); block.list = replace(block.list)(/bull/g, block.bullet)("hr", /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)(); block._tag = "(?!(?:" + "a|em|strong|small|s|cite|q|dfn|abbr|data|time|code" + "|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo" + "|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b"; block.html = replace(block.html)("comment", //)("closed", /<(tag)[\s\S]+?<\/\1>/)( "closing", /])*?>/ )(/tag/g, block._tag)(); block.paragraph = replace(block.paragraph)("hr", block.hr)("heading", block.heading)("lheading", block.lheading)( "blockquote", block.blockquote )("tag", "<" + block._tag)("def", block.def)(); /** * Normal Block Grammar */ block.normal = merge({}, block); /** * GFM Block Grammar */ block.gfm = merge({}, block.normal, { fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/, paragraph: /^/, }); block.gfm.paragraph = replace(block.paragraph)( "(?!", "(?!" + block.gfm.fences.source.replace("\\1", "\\2") + "|" )(); /** * GFM + Tables Block Grammar */ block.tables = merge({}, block.gfm, { nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/, }); /** * Block Lexer */ function Lexer(options) { this.tokens = []; this.tokens.links = {}; this.options = options || marked.defaults; this.rules = block.normal; if (this.options.gfm) { if (this.options.tables) { this.rules = block.tables; } else { this.rules = block.gfm; } } } /** * Expose Block Rules */ Lexer.rules = block; /** * Static Lex Method */ Lexer.lex = function (src, options) { var lexer = new Lexer(options); return lexer.lex(src); }; /** * Preprocessing */ Lexer.prototype.lex = function (src) { src = src .replace(/\r\n|\r/g, "\n") .replace(/\t/g, " ") .replace(/\u00a0/g, " ") .replace(/\u2424/g, "\n"); return this.token(src, true); }; /** * Lexing */ Lexer.prototype.token = function (src, top) { var src = src.replace(/^ +$/gm, ""), next, loose, cap, bull, b, item, space, i, l; while (src) { // newline if ((cap = this.rules.newline.exec(src))) { src = src.substring(cap[0].length); if (cap[0].length > 1) { this.tokens.push({ type: "space", }); } } // code if ((cap = this.rules.code.exec(src))) { src = src.substring(cap[0].length); cap = cap[0].replace(/^ {4}/gm, ""); this.tokens.push({ type: "code", text: !this.options.pedantic ? cap.replace(/\n+$/, "") : cap, }); continue; } // fences (gfm) if ((cap = this.rules.fences.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "code", lang: cap[2], text: cap[3], }); continue; } // heading if ((cap = this.rules.heading.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "heading", depth: cap[1].length, text: cap[2], }); continue; } // table no leading pipe (gfm) if (top && (cap = this.rules.nptable.exec(src))) { src = src.substring(cap[0].length); item = { type: "table", header: cap[1].replace(/^ *| *\| *$/g, "").split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, "").split(/ *\| */), cells: cap[3].replace(/\n$/, "").split("\n"), }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = "right"; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = "center"; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = "left"; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i].split(/ *\| */); } this.tokens.push(item); continue; } // lheading if ((cap = this.rules.lheading.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "heading", depth: cap[2] === "=" ? 1 : 2, text: cap[1], }); continue; } // hr if ((cap = this.rules.hr.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "hr", }); continue; } // blockquote if ((cap = this.rules.blockquote.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "blockquote_start", }); cap = cap[0].replace(/^ *> ?/gm, ""); // Pass `top` to keep the current // "toplevel" state. This is exactly // how markdown.pl works. this.token(cap, top); this.tokens.push({ type: "blockquote_end", }); continue; } // list if ((cap = this.rules.list.exec(src))) { src = src.substring(cap[0].length); bull = cap[2]; this.tokens.push({ type: "list_start", ordered: bull.length > 1, }); // Get each top-level item. cap = cap[0].match(this.rules.item); next = false; l = cap.length; i = 0; for (; i < l; i++) { item = cap[i]; // Remove the list item's bullet // so it is seen as the next token. space = item.length; item = item.replace(/^ *([*+-]|\d+\.) +/, ""); // Outdent whatever the // list item contains. Hacky. if (~item.indexOf("\n ")) { space -= item.length; item = !this.options.pedantic ? item.replace(new RegExp("^ {1," + space + "}", "gm"), "") : item.replace(/^ {1,4}/gm, ""); } // Determine whether the next list item belongs here. // Backpedal if it does not belong in this list. if (this.options.smartLists && i !== l - 1) { b = block.bullet.exec(cap[i + 1])[0]; if (bull !== b && !(bull.length > 1 && b.length > 1)) { src = cap.slice(i + 1).join("\n") + src; i = l - 1; } } // Determine whether item is loose or not. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ // for discount behavior. loose = next || /\n\n(?!\s*$)/.test(item); if (i !== l - 1) { next = item[item.length - 1] === "\n"; if (!loose) loose = next; } this.tokens.push({ type: loose ? "loose_item_start" : "list_item_start", }); // Recurse. this.token(item, false); this.tokens.push({ type: "list_item_end", }); } this.tokens.push({ type: "list_end", }); continue; } // html if ((cap = this.rules.html.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: this.options.sanitize ? "paragraph" : "html", pre: cap[1] === "pre" || cap[1] === "script", text: cap[0], }); continue; } // def if (top && (cap = this.rules.def.exec(src))) { src = src.substring(cap[0].length); this.tokens.links[cap[1].toLowerCase()] = { href: cap[2], title: cap[3], }; continue; } // table (gfm) if (top && (cap = this.rules.table.exec(src))) { src = src.substring(cap[0].length); item = { type: "table", header: cap[1].replace(/^ *| *\| *$/g, "").split(/ *\| */), align: cap[2].replace(/^ *|\| *$/g, "").split(/ *\| */), cells: cap[3].replace(/(?: *\| *)?\n$/, "").split("\n"), }; for (i = 0; i < item.align.length; i++) { if (/^ *-+: *$/.test(item.align[i])) { item.align[i] = "right"; } else if (/^ *:-+: *$/.test(item.align[i])) { item.align[i] = "center"; } else if (/^ *:-+ *$/.test(item.align[i])) { item.align[i] = "left"; } else { item.align[i] = null; } } for (i = 0; i < item.cells.length; i++) { item.cells[i] = item.cells[i].replace(/^ *\| *| *\| *$/g, "").split(/ *\| */); } this.tokens.push(item); continue; } // top-level paragraph if (top && (cap = this.rules.paragraph.exec(src))) { src = src.substring(cap[0].length); this.tokens.push({ type: "paragraph", text: cap[1][cap[1].length - 1] === "\n" ? cap[1].slice(0, -1) : cap[1], }); continue; } // text if ((cap = this.rules.text.exec(src))) { // Top-level should never reach here. src = src.substring(cap[0].length); this.tokens.push({ type: "text", text: cap[0], }); continue; } if (src) { throw new Error("Infinite loop on byte: " + src.charCodeAt(0)); } } return this.tokens; }; /** * Inline-Level Grammar */ var inline = { escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, url: noop, tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, link: /^!?\[(inside)\]\(href\)/, reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, br: /^ {2,}\n(?!\s*$)/, del: noop, text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; inline.link = replace(inline.link)("inside", inline._inside)("href", inline._href)(); inline.reflink = replace(inline.reflink)("inside", inline._inside)(); /** * Normal Inline Grammar */ inline.normal = merge({}, inline); /** * Pedantic Inline Grammar */ inline.pedantic = merge({}, inline.normal, { strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/, }); /** * GFM Inline Grammar */ inline.gfm = merge({}, inline.normal, { escape: replace(inline.escape)("])", "~|])")(), url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, del: /^~~(?=\S)([\s\S]*?\S)~~/, text: replace(inline.text)("]|", "~]|")("|", "|https?://|")(), }); /** * GFM + Line Breaks Inline Grammar */ inline.breaks = merge({}, inline.gfm, { br: replace(inline.br)("{2,}", "*")(), text: replace(inline.gfm.text)("{2,}", "*")(), }); /** * Inline Lexer & Compiler */ function InlineLexer(links, options) { this.options = options || marked.defaults; this.links = links; this.rules = inline.normal; if (!this.links) { throw new Error("Tokens array requires a `links` property."); } if (this.options.gfm) { if (this.options.breaks) { this.rules = inline.breaks; } else { this.rules = inline.gfm; } } else if (this.options.pedantic) { this.rules = inline.pedantic; } } /** * Expose Inline Rules */ InlineLexer.rules = inline; /** * Static Lexing/Compiling Method */ InlineLexer.output = function (src, links, options) { var inline = new InlineLexer(links, options); return inline.output(src); }; /** * Lexing/Compiling */ InlineLexer.prototype.output = function (src) { var out = "", link, text, href, cap; while (src) { // escape if ((cap = this.rules.escape.exec(src))) { src = src.substring(cap[0].length); out += cap[1]; continue; } // autolink if ((cap = this.rules.autolink.exec(src))) { src = src.substring(cap[0].length); if (cap[2] === "@") { text = cap[1][6] === ":" ? this.mangle(cap[1].substring(7)) : this.mangle(cap[1]); href = this.mangle("mailto:") + text; } else { text = escape(cap[1]); href = text; } out += '' + text + ""; continue; } // url (gfm) if ((cap = this.rules.url.exec(src))) { src = src.substring(cap[0].length); text = escape(cap[1]); href = text; out += '' + text + ""; continue; } // tag if ((cap = this.rules.tag.exec(src))) { src = src.substring(cap[0].length); out += this.options.sanitize ? escape(cap[0]) : cap[0]; continue; } // link if ((cap = this.rules.link.exec(src))) { src = src.substring(cap[0].length); out += this.outputLink(cap, { href: cap[2], title: cap[3], }); continue; } // reflink, nolink if ((cap = this.rules.reflink.exec(src)) || (cap = this.rules.nolink.exec(src))) { src = src.substring(cap[0].length); link = (cap[2] || cap[1]).replace(/\s+/g, " "); link = this.links[link.toLowerCase()]; if (!link || !link.href) { out += cap[0][0]; src = cap[0].substring(1) + src; continue; } out += this.outputLink(cap, link); continue; } // strong if ((cap = this.rules.strong.exec(src))) { src = src.substring(cap[0].length); out += "" + this.output(cap[2] || cap[1]) + ""; continue; } // em if ((cap = this.rules.em.exec(src))) { src = src.substring(cap[0].length); out += "" + this.output(cap[2] || cap[1]) + ""; continue; } // code if ((cap = this.rules.code.exec(src))) { src = src.substring(cap[0].length); out += "" + escape(cap[2], true) + ""; continue; } // br if ((cap = this.rules.br.exec(src))) { src = src.substring(cap[0].length); out += "
"; continue; } // del (gfm) if ((cap = this.rules.del.exec(src))) { src = src.substring(cap[0].length); out += "" + this.output(cap[1]) + ""; continue; } // text if ((cap = this.rules.text.exec(src))) { src = src.substring(cap[0].length); out += escape(cap[0]); continue; } if (src) { throw new Error("Infinite loop on byte: " + src.charCodeAt(0)); } } return out; }; /** * Compile Link */ InlineLexer.prototype.outputLink = function (cap, link) { if (cap[0][0] !== "!") { return ( '" + this.output(cap[1]) + "" ); } else { return ( '' +
                escape(cap[1]) +
                '" ); } }; /** * Smartypants Transformations */ InlineLexer.prototype.smartypants = function (text) { if (!this.options.smartypants) return text; return text .replace(/--/g, "—") .replace(/'([^']*)'/g, "‘$1’") .replace(/"([^"]*)"/g, "“$1”") .replace(/\.{3}/g, "…"); }; /** * Mangle Links */ InlineLexer.prototype.mangle = function (text) { var out = "", l = text.length, i = 0, ch; for (; i < l; i++) { ch = text.charCodeAt(i); if (Math.random() > 0.5) { ch = "x" + ch.toString(16); } out += "&#" + ch + ";"; } return out; }; /** * Parsing & Compiling */ function Parser(options) { this.tokens = []; this.token = null; this.options = options || marked.defaults; } /** * Static Parse Method */ Parser.parse = function (src, options) { var parser = new Parser(options); return parser.parse(src); }; /** * Parse Loop */ Parser.prototype.parse = function (src) { this.inline = new InlineLexer(src.links, this.options); this.tokens = src.reverse(); var out = ""; while (this.next()) { out += this.tok(); } return out; }; /** * Next Token */ Parser.prototype.next = function () { return (this.token = this.tokens.pop()); }; /** * Preview Next Token */ Parser.prototype.peek = function () { return this.tokens[this.tokens.length - 1] || 0; }; /** * Parse Text Tokens */ Parser.prototype.parseText = function () { var body = this.token.text; while (this.peek().type === "text") { body += "\n" + this.next().text; } return this.inline.output(body); }; /** * Parse Current Token */ Parser.prototype.tok = function () { switch (this.token.type) { case "space": { return ""; } case "hr": { return "
\n"; } case "heading": { return ( "" + this.inline.output(this.token.text) + "\n" ); } case "code": { if (this.options.highlight) { var code = this.options.highlight(this.token.text, this.token.lang); if (code != null && code !== this.token.text) { this.token.escaped = true; this.token.text = code; } } if (!this.token.escaped) { this.token.text = escape(this.token.text, true); } return ( "
" +
                    this.token.text +
                    "
\n" ); } case "table": { var body = "", heading, i, row, cell, j; // header body += "\n\n"; for (i = 0; i < this.token.header.length; i++) { heading = this.inline.output(this.token.header[i]); body += this.token.align[i] ? '' + heading + "\n" : "" + heading + "\n"; } body += "\n\n"; // body body += "\n"; for (i = 0; i < this.token.cells.length; i++) { row = this.token.cells[i]; body += "\n"; for (j = 0; j < row.length; j++) { cell = this.inline.output(row[j]); body += this.token.align[j] ? '' + cell + "\n" : "" + cell + "\n"; } body += "\n"; } body += "\n"; return "\n" + body + "
\n"; } case "blockquote_start": { var body = ""; while (this.next().type !== "blockquote_end") { body += this.tok(); } return "
\n" + body + "
\n"; } case "list_start": { var type = this.token.ordered ? "ol" : "ul", body = ""; while (this.next().type !== "list_end") { body += this.tok(); } return "<" + type + ">\n" + body + "\n"; } case "list_item_start": { var body = ""; while (this.next().type !== "list_item_end") { body += this.token.type === "text" ? this.parseText() : this.tok(); } return "
  • " + body + "
  • \n"; } case "loose_item_start": { var body = ""; while (this.next().type !== "list_item_end") { body += this.tok(); } return "
  • " + body + "
  • \n"; } case "html": { return !this.token.pre && !this.options.pedantic ? this.inline.output(this.token.text) : this.token.text; } case "paragraph": { return "

    " + this.inline.output(this.token.text) + "

    \n"; } case "text": { return "

    " + this.parseText() + "

    \n"; } } }; /** * Helpers */ function escape(html, encode) { return html .replace(!encode ? /&(?!#?\w+;)/g : /&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function replace(regex, opt) { regex = regex.source; opt = opt || ""; return function self(name, val) { if (!name) return new RegExp(regex, opt); val = val.source || val; val = val.replace(/(^|[^\[])\^/g, "$1"); regex = regex.replace(name, val); return self; }; } function noop() {} noop.exec = noop; function merge(obj) { var i = 1, target, key; for (; i < arguments.length; i++) { target = arguments[i]; for (key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { obj[key] = target[key]; } } } return obj; } /** * Marked */ function marked(src, opt, callback) { if (callback || typeof opt === "function") { if (!callback) { callback = opt; opt = null; } if (opt) opt = merge({}, marked.defaults, opt); var tokens = Lexer.lex(tokens, opt), highlight = opt.highlight, pending = 0, l = tokens.length, i = 0; if (!highlight || highlight.length < 3) { return callback(null, Parser.parse(tokens, opt)); } var done = function () { delete opt.highlight; var out = Parser.parse(tokens, opt); opt.highlight = highlight; return callback(null, out); }; for (; i < l; i++) { (function (token) { if (token.type !== "code") return; pending++; return highlight(token.text, token.lang, function (err, code) { if (code == null || code === token.text) { return --pending || done(); } token.text = code; token.escaped = true; --pending || done(); }); })(tokens[i]); } return; } try { if (opt) opt = merge({}, marked.defaults, opt); return Parser.parse(Lexer.lex(src, opt), opt); } catch (e) { e.message += "\nPlease report this to https://github.com/chjj/marked."; if ((opt || marked.defaults).silent) { return "

    An error occured:

    " + escape(e.message + "", true) + "
    "; } throw e; } } /** * Options */ marked.options = marked.setOptions = function (opt) { merge(marked.defaults, opt); return marked; }; marked.defaults = { gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: false, silent: false, highlight: null, langPrefix: "lang-", }; /** * Expose */ marked.Parser = Parser; marked.parser = Parser.parse; marked.Lexer = Lexer; marked.lexer = Lexer.lex; marked.InlineLexer = InlineLexer; marked.inlineLexer = InlineLexer.output; marked.parse = marked; if (typeof exports === "object") { module.exports = marked; } else if (typeof define === "function" && define.amd) { define(function () { return marked; }); } else { this.marked = marked; } }).call( (function () { return this || (typeof window !== "undefined" ? window : global); })() ); (function ($) { "use strict"; // hide the whole page so we dont see the DOM flickering // will be shown upon page load complete or error $("html").addClass("md-hidden-load"); // register our $.md object $.md = function (method) { if ($.md.publicMethods[method]) { return $.md.publicMethods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else { $.error("Method " + method + " does not exist on jquery.md"); } }; // default config $.md.config = { title: null, useSideMenu: true, lineBreaks: "gfm", additionalFooterText: "", anchorCharacter: "¶", tocAnchor: "[ ↑ ]", }; $.md.gimmicks = []; $.md.stages = []; // the location of the main markdown file we display $.md.mainHref = ""; // the in-page anchor that is specified after the ! $.md.inPageAnchor = ""; $.md.loglevel = { TRACE: 10, DEBUG: 20, INFO: 30, WARN: 40, ERROR: 50, FATAL: 60, }; // $.md.logThreshold = $.md.loglevel.DEBUG; $.md.logThreshold = $.md.loglevel.WARN; })(jQuery); (function ($) { "use strict"; $.md.getLogger = function () { var loglevel = $.md.loglevel; var log = function (logtarget) { var self = this; var level = loglevel[logtarget]; return function (msg) { if ($.md.logThreshold <= level) { console.log("[" + logtarget + "] " + msg); } }; }; var logger = {}; logger.trace = log("TRACE"); logger.debug = log("DEBUG"); logger.info = log("INFO"); logger.warn = log("WARN"); logger.error = log("ERROR"); logger.fatal = log("FATAL"); return logger; }; })(jQuery); (function ($) { "use strict"; var log = $.md.getLogger(); $.Stage = function (name) { var self = $.extend($.Deferred(), {}); self.name = name; self.events = []; self.started = false; self.reset = function () { self.complete = $.Deferred(); self.outstanding = []; }; self.reset(); self.subscribe = function (fn) { if (self.started) { $.error("Subscribing to stage which already started!"); } self.events.push(fn); }; self.unsubscribe = function (fn) { self.events.remove(fn); }; self.executeSubscribedFn = function (fn) { var d = $.Deferred(); self.outstanding.push(d); // display an error if our done() callback is not called $.md.util.wait(2500).done(function () { if (d.state() !== "resolved") { log.fatal( "Timeout reached for done callback in stage: " + self.name + ". Did you forget a done() call in a .subscribe() ?" ); log.fatal("stage " + name + " failed running subscribed function: " + fn); } }); var done = function () { d.resolve(); }; fn(done); }; self.run = function () { self.started = true; $(self.events).each(function (i, fn) { self.executeSubscribedFn(fn); }); // if no events are in our queue, we resolve immediately if (self.outstanding.length === 0) { self.resolve(); } // we resolve when all our registered events have completed $.when .apply($, self.outstanding) .done(function () { self.resolve(); }) .fail(function () { self.resolve(); }); }; self.done(function () { log.debug("stage " + self.name + " completed successfully."); }); self.fail(function () { log.debug("stage " + self.name + " completed with errors!"); }); return self; }; })(jQuery); (function ($) { "use strict"; var log = $.md.getLogger(); function init() { $.md.stages = [ $.Stage("init"), // loads config, initial markdown and navigation $.Stage("load"), // will transform the markdown to html $.Stage("transform"), // HTML transformation finished $.Stage("ready"), // after we have a polished html skeleton $.Stage("skel_ready"), // will bootstrapify the skeleton $.Stage("bootstrap"), // before we run any gimmicks $.Stage("pregimmick"), // after we have bootstrapified the skeleton $.Stage("gimmick"), // postprocess $.Stage("postgimmick"), $.Stage("all_ready"), // used for integration tests, not intended to use in MDwiki itself $.Stage("final_tests"), ]; $.md.stage = function (name) { var m = $.grep($.md.stages, function (e, i) { return e.name === name; }); if (m.length === 0) { $.error("A stage by name " + name + " does not exist"); } else { return m[0]; } }; } init(); function resetStages() { var old_stages = $.md.stages; $.md.stages = []; $(old_stages).each(function (i, e) { $.md.stages.push($.Stage(e.name)); }); } var publicMethods = {}; $.md.publicMethods = $.extend({}, $.md.publicMethods, publicMethods); function transformMarkdown(markdown) { var options = { gfm: true, tables: true, breaks: true, }; if ($.md.config.lineBreaks === "original") options.breaks = false; else if ($.md.config.lineBreaks === "gfm") options.breaks = true; marked.setOptions(options); // get sample markdown var uglyHtml = marked(markdown); return uglyHtml; } function registerFetchMarkdown() { var md = ""; $.md.stage("init").subscribe(function (done) { var ajaxReq = { url: $.md.mainHref, dataType: "text", }; // Request the md page $.ajax(ajaxReq) .done(function (data) { md = data; done(); }) // Failed to find the md page we were looking for .fail(function () { // Warn that this page wasn't found var log = $.md.getLogger(); log.warn("Could not get " + $.md.mainHref); // Attempt to gracefully recover by displaying a user defined 404 page ajaxReq.url = "404.md"; $.ajax(ajaxReq) .done(function (data) { md = data; done(); }) // The user has not defined a 404.md. .fail(function (data) { log.fatal("Could not get a user defined 404.md"); // Our last attempt to provide a good user expierence by proving a hard coded // 'page not found' text. md = "# Page Not Found"; done(); }); }); }); // find baseUrl $.md.stage("transform").subscribe(function (done) { var len = $.md.mainHref.lastIndexOf("/"); var baseUrl = $.md.mainHref.substring(0, len + 1); $.md.baseUrl = baseUrl; done(); }); $.md.stage("transform").subscribe(function (done) { var uglyHtml = transformMarkdown(md); $("#md-content").html(uglyHtml); md = ""; var dfd = $.Deferred(); loadExternalIncludes(dfd); dfd.always(function () { done(); }); }); } // load [include](/foo/bar.md) external links function loadExternalIncludes(parent_dfd) { function findExternalIncludes() { return $("a").filter(function () { var href = $(this).attr("href"); var text = $(this).toptext(); var isMarkdown = $.md.util.hasMarkdownFileExtension(href); var isInclude = text === "include"; var isPreview = text.startsWith("preview:"); return (isInclude || isPreview) && isMarkdown; }); } function selectPreviewElements($jqcol, num_elements) { function isTextNode(node) { return node.nodeType === 3; } var count = 0; var elements = []; $jqcol.each(function (i, e) { if (count < num_elements) { elements.push(e); if (!isTextNode(e)) count++; } }); return $(elements); } var external_links = findExternalIncludes(); // continue execution when all external resources are fully loaded var latch = $.md.util.countDownLatch(external_links.length); latch.always(function () { parent_dfd.resolve(); }); external_links.each(function (i, e) { var $el = $(e); var href = $el.attr("href"); var text = $el.toptext(); $.ajax({ url: href, dataType: "text", }) .done(function (data) { var $html = $(transformMarkdown(data)); if (text.startsWith("preview:")) { // only insert the selected number of paragraphs; default 3 var num_preview_elements = parseInt(text.substring(8), 10) || 3; var $preview = selectPreviewElements($html, num_preview_elements); $preview.last().append(' ...read more ➜'); $preview.insertBefore($el.parent("p").eq(0)); $el.remove(); } else { $html.insertAfter($el.parents("p")); $el.remove(); } }) .always(function () { latch.countDown(); }); }); } function isSpecialLink(href) { if (!href) return false; if (href.lastIndexOf("data:") >= 0) return true; if (href.startsWith("mailto:")) return true; if (href.startsWith("file:")) return true; if (href.startsWith("ftp:")) return true; // TODO capture more special links: every non-http link with : like // torrent:// etc. } // modify internal links so we load them through our engine function processPageLinks(domElement, baseUrl) { var html = $(domElement); if (baseUrl === undefined) { baseUrl = ""; } // HACK against marked: empty links will have empy href attribute // we remove the href attribute from the a tag html.find("a") .not("#md-menu a") .filter(function () { var $this = $(this); var attr = $this.attr("href"); if (!attr || attr.length === 0) $this.removeAttr("href"); }); html.find("a, img").each(function (i, e) { var link = $(e); // link must be jquery collection var isImage = false; var hrefAttribute = "href"; if (!link.attr(hrefAttribute)) { isImage = true; hrefAttribute = "src"; } var href = link.attr(hrefAttribute); if (href && href.lastIndexOf("#!") >= 0) return; if (isSpecialLink(href)) return; if (!isImage && href.startsWith("#") && !href.startsWith("#!")) { // in-page link link.click(function (ev) { ev.preventDefault(); $.md.scrollToInPageAnchor(href); }); } if (!$.md.util.isRelativeUrl(href)) return; if (isImage && !$.md.util.isRelativePath(href)) return; if (!isImage && $.md.util.isGimmickLink(link)) return; function build_link(url) { if ($.md.util.hasMarkdownFileExtension(url)) return "#!" + url; else return url; } var newHref = baseUrl + href; if (isImage) link.attr(hrefAttribute, newHref); else if ($.md.util.isRelativePath(href)) link.attr(hrefAttribute, build_link(newHref)); else link.attr(hrefAttribute, build_link(href)); }); } var navMD = ""; $.md.NavigationDfd = $.Deferred(); var ajaxReq = { url: "navigation.md", dataType: "text", }; $.ajax(ajaxReq) .done(function (data) { navMD = data; $.md.NavigationDfd.resolve(); }) .fail(function () { $.md.NavigationDfd.reject(); }); function registerBuildNavigation() { $.md.stage("init").subscribe(function (done) { $.md.NavigationDfd.done(function () { done(); }).fail(function () { done(); }); }); $.md.stage("transform").subscribe(function (done) { if (navMD === "") { var log = $.md.getLogger(); log.info("no navgiation.md found, not using a navbar"); done(); return; } var navHtml = marked(navMD); // TODO why are