diff --git a/gulpfile.js b/gulpfile.js index 6877877a..7f080380 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -92,19 +92,6 @@ gulp.task('scripts_concat_tutti', function() { .pipe(gulpif(argv.livereload, livereload())); }); -gulp.task('scripts_concat_markdown', function() { - gulp.src('src/scripts/markdown/**/*.js') - .pipe(gulpif(enabled.failCheck, plumber())) - .pipe(gulpif(enabled.maps, sourcemaps.init())) - .pipe(concat("markdown.min.js")) - .pipe(gulpif(enabled.uglify, uglify())) - .pipe(gulpif(enabled.maps, sourcemaps.write("."))) - .pipe(gulpif(enabled.chmod, chmod(644))) - .pipe(gulp.dest(destination.js)) - .pipe(gulpif(argv.livereload, livereload())); -}); - - // Combine all needed Bootstrap JavaScript into a single file. gulp.task('scripts_concat_bootstrap', function() { @@ -139,7 +126,6 @@ gulp.task('watch',function() { gulp.watch('src/templates/**/*.pug',['templates']); gulp.watch('src/scripts/*.js',['scripts']); gulp.watch('src/scripts/tutti/**/*.js',['scripts_concat_tutti']); - gulp.watch('src/scripts/markdown/**/*.js',['scripts_concat_markdown']); }); // Erases all generated files in output directories. @@ -164,6 +150,5 @@ gulp.task('default', tasks.concat([ 'templates', 'scripts', 'scripts_concat_tutti', - 'scripts_concat_markdown', 'scripts_concat_bootstrap', ])); diff --git a/src/scripts/markdown/01_markdown-converter.js b/src/scripts/markdown/01_markdown-converter.js deleted file mode 100644 index 8e92a079..00000000 --- a/src/scripts/markdown/01_markdown-converter.js +++ /dev/null @@ -1,1622 +0,0 @@ -"use strict"; -var Markdown; - -if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module - Markdown = exports; -else - Markdown = {}; - -// The following text is included for historical reasons, but should -// be taken with a pinch of salt; it's not all true anymore. - -// -// Wherever possible, Showdown is a straight, line-by-line port -// of the Perl version of Markdown. -// -// This is not a normal parser design; it's basically just a -// series of string substitutions. It's hard to read and -// maintain this way, but keeping Showdown close to the original -// design makes it easier to port new features. -// -// More importantly, Showdown behaves like markdown.pl in most -// edge cases. So web applications can do client-side preview -// in Javascript, and then build identical HTML on the server. -// -// This port needs the new RegExp functionality of ECMA 262, -// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers -// should do fine. Even with the new regular expression features, -// We do a lot of work to emulate Perl's regex functionality. -// The tricky changes in this file mostly have the "attacklab:" -// label. Major or self-explanatory changes don't. -// -// Smart diff tools like Araxis Merge will be able to match up -// this file with markdown.pl in a useful way. A little tweaking -// helps: in a copy of markdown.pl, replace "#" with "//" and -// replace "$text" with "text". Be sure to ignore whitespace -// and line endings. -// - - -// -// Usage: -// -// var text = "Markdown *rocks*."; -// -// var converter = new Markdown.Converter(); -// var html = converter.makeHtml(text); -// -// alert(html); -// -// Note: move the sample code to the bottom of this -// file before uncommenting it. -// - -(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; - - // g_urls and g_titles allow arbitrary user-entered strings as keys. This - // caused an exception (and hence stopped the rendering) when the user entered - // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this - // (since no builtin property starts with "s_"). See - // http://meta.stackexchange.com/questions/64655/strange-wmd-bug - // (granted, switching from Array() to Object() alone would have left only __proto__ - // to be a problem) - function SaveHash() { } - SaveHash.prototype = { - set: function (key, value) { - this["s_" + key] = value; - }, - get: function (key) { - return this["s_" + key]; - } - }; - - Markdown.Converter = function (OPTIONS) { - var pluginHooks = this.hooks = new HookCollection(); - - // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link - pluginHooks.addNoop("plainLinkText"); - - // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked - pluginHooks.addNoop("preConversion"); - - // called with the text once all normalizations have been completed (tabs to spaces, line endings, etc.), but before any conversions have - pluginHooks.addNoop("postNormalization"); - - // Called with the text before / after creating block elements like code blocks and lists. Note that this is called recursively - // with inner content, e.g. it's called with the full text, and then only with the content of a blockquote. The inner - // call will receive outdented text. - pluginHooks.addNoop("preBlockGamut"); - pluginHooks.addNoop("postBlockGamut"); - - // called with the text of a single block element before / after the span-level conversions (bold, code spans, etc.) have been made - pluginHooks.addNoop("preSpanGamut"); - pluginHooks.addNoop("postSpanGamut"); - - // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml - pluginHooks.addNoop("postConversion"); - - // - // Private state of the converter instance: - // - - // Global hashes, used by various utility routines - var g_urls; - var g_titles; - var g_html_blocks; - - // Used to track when we're inside an ordered or unordered list - // (see _ProcessListItems() for details): - var g_list_level; - - OPTIONS = OPTIONS || {}; - var asciify = identity, deasciify = identity; - if (OPTIONS.nonAsciiLetters) { - - /* In JavaScript regular expressions, \w only denotes [a-zA-Z0-9_]. - * That's why there's inconsistent handling e.g. with intra-word bolding - * of Japanese words. That's why we do the following if OPTIONS.nonAsciiLetters - * is true: - * - * Before doing bold and italics, we find every instance - * of a unicode word character in the Markdown source that is not - * matched by \w, and the letter "Q". We take the character's code point - * and encode it in base 51, using the "digits" - * - * A, B, ..., P, R, ..., Y, Z, a, b, ..., y, z - * - * delimiting it with "Q" on both sides. For example, the source - * - * > In Chinese, the smurfs are called 藍精靈, meaning "blue spirits". - * - * turns into - * - * > In Chinese, the smurfs are called QNIhQQMOIQQOuUQ, meaning "blue spirits". - * - * Since everything that is a letter in Unicode is now a letter (or - * several letters) in ASCII, \w and \b should always do the right thing. - * - * After the bold/italic conversion, we decode again; since "Q" was encoded - * alongside all non-ascii characters (as "QBfQ"), and the conversion - * will not generate "Q", the only instances of that letter should be our - * encoded characters. And since the conversion will not break words, the - * "Q...Q" should all still be in one piece. - * - * We're using "Q" as the delimiter because it's probably one of the - * rarest characters, and also because I can't think of any special behavior - * that would ever be triggered by this letter (to use a silly example, if we - * delimited with "H" on the left and "P" on the right, then "Ψ" would be - * encoded as "HTTP", which may cause special behavior). The latter would not - * actually be a huge issue for bold/italic, but may be if we later use it - * in other places as well. - * */ - (function () { - var lettersThatJavaScriptDoesNotKnowAndQ = /[Q\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0523\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0621-\u064a\u0660-\u0669\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07c0-\u07ea\u07f4-\u07f5\u07fa\u0904-\u0939\u093d\u0950\u0958-\u0961\u0966-\u096f\u0971-\u0972\u097b-\u097f\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09e6-\u09f1\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0ae6-\u0aef\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b66-\u0b6f\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0be6-\u0bef\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58-\u0c59\u0c60-\u0c61\u0c66-\u0c6f\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0ce6-\u0cef\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d28\u0d2a-\u0d39\u0d3d\u0d60-\u0d61\u0d66-\u0d6f\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa-\u0eab\u0ead-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edd\u0f00\u0f20-\u0f29\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8b\u1000-\u102a\u103f-\u1049\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u1090-\u1099\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1159\u115f-\u11a2\u11a8-\u11f9\u1200-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u1676\u1681-\u169a\u16a0-\u16ea\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u1900-\u191c\u1946-\u196d\u1970-\u1974\u1980-\u19a9\u19c1-\u19c7\u19d0-\u19d9\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b50-\u1b59\u1b83-\u1ba0\u1bae-\u1bb9\u1c00-\u1c23\u1c40-\u1c49\u1c4d-\u1c7d\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u2094\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2183-\u2184\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2c6f\u2c71-\u2c7d\u2c80-\u2ce4\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3006\u3031-\u3035\u303b-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31b7\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fc3\ua000-\ua48c\ua500-\ua60c\ua610-\ua62b\ua640-\ua65f\ua662-\ua66e\ua67f-\ua697\ua717-\ua71f\ua722-\ua788\ua78b-\ua78c\ua7fb-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8d0-\ua8d9\ua900-\ua925\ua930-\ua946\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa50-\uaa59\uac00-\ud7a3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]/g; - var cp_Q = "Q".charCodeAt(0); - var cp_A = "A".charCodeAt(0); - var cp_Z = "Z".charCodeAt(0); - var dist_Za = "a".charCodeAt(0) - cp_Z - 1; - - asciify = function(text) { - return text.replace(lettersThatJavaScriptDoesNotKnowAndQ, function (m) { - var c = m.charCodeAt(0); - var s = ""; - var v; - while (c > 0) { - v = (c % 51) + cp_A; - if (v >= cp_Q) - v++; - if (v > cp_Z) - v += dist_Za; - s = String.fromCharCode(v) + s; - c = c / 51 | 0; - } - return "Q" + s + "Q"; - }) - }; - - deasciify = function(text) { - return text.replace(/Q([A-PR-Za-z]{1,3})Q/g, function (m, s) { - var c = 0; - var v; - for (var i = 0; i < s.length; i++) { - v = s.charCodeAt(i); - if (v > cp_Z) - v -= dist_Za; - if (v > cp_Q) - v--; - v -= cp_A; - c = (c * 51) + v; - } - return String.fromCharCode(c); - }) - } - })(); - } - - var _DoItalicsAndBold = OPTIONS.asteriskIntraWordEmphasis ? _DoItalicsAndBold_AllowIntrawordWithAsterisk : _DoItalicsAndBoldStrict; - - this.makeHtml = function (text) { - - // - // Main function. The order in which other subs are called here is - // essential. Link and image substitutions need to happen before - // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the - // and tags get encoded. - // - - // This will only happen if makeHtml on the same converter instance is called from a plugin hook. - // Don't do that. - if (g_urls) - throw new Error("Recursive call to converter.makeHtml"); - - // Create the private state objects. - g_urls = new SaveHash(); - g_titles = new SaveHash(); - g_html_blocks = []; - g_list_level = 0; - - text = pluginHooks.preConversion(text); - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitray; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g, "~T"); - - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g, "~D"); - - // Standardize line endings - text = text.replace(/\r\n/g, "\n"); // DOS to Unix - text = text.replace(/\r/g, "\n"); // Mac to Unix - - // Make sure text begins and ends with a couple of newlines: - text = "\n\n" + text + "\n\n"; - - // Convert all tabs to spaces. - text = _Detab(text); - - // Strip any lines consisting only of spaces and tabs. - // This makes subsequent regexen easier to write, because we can - // match consecutive blank lines with /\n+/ instead of something - // contorted like /[ \t]*\n+/ . - text = text.replace(/^[ \t]+$/mg, ""); - - text = pluginHooks.postNormalization(text); - - // Turn block-level HTML blocks into hash entries - text = _HashHTMLBlocks(text); - - // Strip link definitions, store in hashes. - text = _StripLinkDefinitions(text); - - text = _RunBlockGamut(text); - - text = _UnescapeSpecialChars(text); - - // attacklab: Restore dollar signs - text = text.replace(/~D/g, "$$"); - - // attacklab: Restore tildes - text = text.replace(/~T/g, "~"); - - text = pluginHooks.postConversion(text); - - g_html_blocks = g_titles = g_urls = null; - - return text; - }; - - function _StripLinkDefinitions(text) { - // - // Strips link definitions from text, stores the URLs and titles in - // hash references. - // - - // Link defs are in the form: ^[id]: url "optional title" - - /* - text = text.replace(/ - ^[ ]{0,3}\[([^\[\]]+)\]: // id = $1 attacklab: g_tab_width - 1 - [ \t]* - \n? // maybe *one* newline - [ \t]* - ? // url = $2 - (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below - [ \t]* - \n? // maybe one newline - [ \t]* - ( // (potential) title = $3 - (\n*) // any lines skipped = $4 attacklab: lookbehind removed - [ \t]+ - ["(] - (.+?) // title = $5 - [")] - [ \t]* - )? // title is optional - (\n+) // subsequent newlines = $6, capturing because they must be put back if the potential title isn't an actual title - /gm, function(){...}); - */ - - text = text.replace(/^[ ]{0,3}\[([^\[\]]+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(\n+)/gm, - function (wholeMatch, m1, m2, m3, m4, m5, m6) { - m1 = m1.toLowerCase(); - g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive - if (m4) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return m3 + m6; - } else if (m5) { - g_titles.set(m1, m5.replace(/"/g, """)); - } - - // Completely remove the definition from the text - return ""; - } - ); - - return text; - } - - function _HashHTMLBlocks(text) { - - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math|ins|del" - var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math" - - // First, look for nested blocks, e.g.: - //

- //
- // tags for inner block must be indented. - //
- //
- // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
` and stop at the first `
`. - - // attacklab: This regex can be expensive when it fails. - - /* - text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashMatch); - - // - // Now match more liberally, simply from `\n` to `\n` - // - - /* - text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - .* // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashMatch); - - // Special case just for
. It was easier to make a special case than - // to make the other regex more complicated. - - /* - text = text.replace(/ - \n // Starting after a blank line - [ ]{0,3} - ( // save in $1 - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashMatch); - */ - text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashMatch); - - // Special case for standalone HTML comments: - - /* - text = text.replace(/ - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - ( // save in $1 - -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackexchange.com/q/95256 - > - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashMatch); - */ - text = text.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashMatch); - - // PHP and ASP-style processor instructions ( and <%...%>) - - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashMatch); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashMatch); - - return text; - } - - function hashBlock(text) { - text = text.replace(/(^\n+|\n+$)/g, ""); - // Replace the element text with a marker ("~KxK" where x is its key) - return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; - } - - function hashMatch(wholeMatch, m1) { - return hashBlock(m1); - } - - var blockGamutHookCallback = function (t) { return _RunBlockGamut(t); } - - function _RunBlockGamut(text, doNotUnhash, doNotCreateParagraphs) { - // - // These are all the transformations that form block-level - // tags like paragraphs, headers, and list items. - // - - text = pluginHooks.preBlockGamut(text, blockGamutHookCallback); - - text = _DoHeaders(text); - - // Do Horizontal Rules: - var replacement = "
\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); - - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

tags around block-level tags. - text = _HashHTMLBlocks(text); - - text = _FormParagraphs(text, doNotUnhash, doNotCreateParagraphs); - - return text; - } - - function _RunSpanGamut(text) { - // - // These are all the transformations that occur *within* block-level - // tags like paragraphs, headers, and list items. - // - - text = pluginHooks.preSpanGamut(text); - - text = _DoCodeSpans(text); - text = _EscapeSpecialCharsWithinTagAttributes(text); - text = _EncodeBackslashEscapes(text); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = _DoImages(text); - text = _DoAnchors(text); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = _DoAutoLinks(text); - - text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now - - text = _EncodeAmpsAndAngles(text); - text = _DoItalicsAndBold(text); - - // Do hard breaks: - text = text.replace(/ +\n/g, "
\n"); - - text = pluginHooks.postSpanGamut(text); - - return text; - } - - function _EscapeSpecialCharsWithinTagAttributes(text) { - // - // Within tags -- meaning between < and > -- encode [\ ` * _] so they - // don't conflict with their use in Markdown for code, italics and strong. - // - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - - // SE: changed the comment part of the regex - - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi; - - text = text.replace(regex, function (wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); - tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackexchange.com/questions/95987 - return tag; - }); - - return text; - } - - function _DoAnchors(text) { - - if (text.indexOf("[") === -1) - return text; - - // - // Turn Markdown link shortcuts into XHTML
tags. - // - // - // First, handle reference-style links: [link text] [id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - ) - ()()()() // pad remaining backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); - - // - // Next, inline-style links: [link text](url "optional title") - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - )* - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g, writeAnchorTag); - */ - - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag); - - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - ) - ()()()()() // pad rest of backreferences - /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"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; - - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - 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) { - // Special case for explicit empty url - url = ""; - } else { - return whole_match; - } - } - } - url = attributeSafeUrl(url); - - var result = ""; - - return result; - } - - function _DoImages(text) { - - if (text.indexOf("![") === -1) - return text; - - // - // Turn Markdown image shortcuts into tags. - // - - // - // First, handle reference-style labeled images: ![alt text][id] - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - ) - ()()()() // pad rest of backreferences - /g, writeImageTag); - */ - text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag); - - // - // Next, handle inline images: ![alt text](url "optional title") - // Don't forget: encode * and _ - - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - \s? // One optional whitespace character - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // src url = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // title = $7 - \6 // matching quote - [ \t]* - )? // title is optional - \) - ) - /g, writeImageTag); - */ - text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); - - return text; - } - - function attributeEncode(text) { - // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title) - // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it) - return text.replace(/>/g, ">").replace(/" + _RunSpanGamut(m1) + "\n\n"; } - ); - - text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, - function (matchFound, m1) { return "

" + _RunSpanGamut(m1) + "

\n\n"; } - ); - - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // - - /* - text = text.replace(/ - ^(\#{1,6}) // $1 = string of #'s - [ \t]* - (.+?) // $2 = Header text - [ \t]* - \#* // optional closing #'s (not counted) - \n+ - /gm, function() {...}); - */ - - text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, - function (wholeMatch, m1, m2) { - var h_level = m1.length; - return "" + _RunSpanGamut(m2) + "\n\n"; - } - ); - - return text; - } - - function _DoLists(text, isInsideParagraphlessListItem) { - // - // Form HTML ordered (numbered) and unordered (bulleted) lists. - // - - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += "~0"; - - // Re-usable pattern to match any entirel ul or ol list: - - /* - var whole_list = / - ( // $1 = whole list - ( // $2 - [ ]{0,3} // attacklab: g_tab_width - 1 - ([*+-]|\d+[.]) // $3 = first list item marker - [ \t]+ - ) - [^\r]+? - ( // $4 - ~0 // sentinel for workaround; should be $ - | - \n{2,} - (?=\S) - (?! // Negative lookahead for another list item marker - [ \t]* - (?:[*+-]|\d+[.])[ \t]+ - ) - ) - ) - /g - */ - 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 first_number; - if (list_type === "ol") - first_number = parseInt(m2, 10) - - var result = _ProcessListItems(list, list_type, isInsideParagraphlessListItem); - - // Trim any trailing whitespace, to put the closing `` - // up on the preceding line, to get it past the current stupid - // HTML block parser. This is a hack to work around the terrible - // hack that is the HTML block parser. - result = result.replace(/\s+$/, ""); - var opening = "<" + list_type; - if (first_number && first_number !== 1) - opening += " start=\"" + first_number + "\""; - result = opening + ">" + result + "\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 first_number; - if (list_type === "ol") - first_number = parseInt(m3, 10) - - var result = _ProcessListItems(list, list_type); - var opening = "<" + list_type; - if (first_number && first_number !== 1) - opening += " start=\"" + first_number + "\""; - - result = runup + opening + ">\n" + result + "\n"; - return result; - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" }; - - function _ProcessListItems(list_str, list_type, isInsideParagraphlessListItem) { - // - // Process the contents of a single ordered or unordered list, splitting it - // into individual list items. - // - // list_type is either "ul" or "ol". - - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". - - g_list_level++; - - // trim trailing blank lines: - list_str = list_str.replace(/\n{2,}$/, "\n"); - - // attacklab: add sentinel to emulate \z - list_str += "~0"; - - // In the original attacklab showdown, list_type was not given to this function, and anything - // that matched /[*+-]|\d+[.]/ would just create the next
  • , causing this mismatch: - // - // Markdown rendered by WMD rendered by MarkdownSharp - // ------------------------------------------------------------------ - // 1. first 1. first 1. first - // 2. second 2. second 2. second - // - third 3. third * third - // - // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx, - // with {MARKER} being one of \d+[.] or [*+-], depending on list_type: - - /* - list_str = list_str.replace(/ - (^[ \t]*) // leading whitespace = $1 - ({MARKER}) [ \t]+ // list marker = $2 - ([^\r]+? // list item text = $3 - (\n+) - ) - (?= - (~0 | \2 ({MARKER}) [ \t]+) - ) - /gm, function(){...}); - */ - - 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; - - var loose = contains_double_newline || last_item_had_a_double_newline; - item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true, /* doNotCreateParagraphs = */ !loose); - - last_item_had_a_double_newline = ends_with_double_newline; - return "
  • " + item + "
  • \n"; - } - ); - - // attacklab: strip sentinel - list_str = list_str.replace(/~0/g, ""); - - g_list_level--; - return list_str; - } - - function _DoCodeBlocks(text) { - // - // Process Markdown `
    ` blocks.
    -            //
    -
    -            /*
    -            text = text.replace(/
    -                (?:\n\n|^)
    -                (                               // $1 = the code block -- one or more lines, starting with a space/tab
    -                    (?:
    -                        (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    -                        .*\n+
    -                    )+
    -                )
    -                (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
    -            /g ,function(){...});
    -            */
    -
    -            // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    -            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, ""); // trim leading newlines
    -                    codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
    -
    -                    codeblock = "
    " + codeblock + "\n
    "; - - return "\n\n" + codeblock + "\n\n" + nextChar; - } - ); - - // attacklab: strip sentinel - text = text.replace(/~0/, ""); - - return text; - } - - function _DoCodeSpans(text) { - // - // * Backtick quotes are used for spans. - // - // * You can use multiple backticks as the delimiters if you want to - // include literal backticks in the code span. So, this input: - // - // Just type ``foo `bar` baz`` at the prompt. - // - // Will translate to: - // - //

    Just type foo `bar` baz at the prompt.

    - // - // There's no arbitrary limit to the number of backticks you - // can use as delimters. If you need three consecutive backticks - // in your code, use four for delimiters, etc. - // - // * You can use spaces to get literal backticks at the edges: - // - // ... type `` `bar` `` ... - // - // Turns to: - // - // ... type `bar` ... - // - - /* - text = text.replace(/ - (^|[^\\`]) // Character before opening ` can't be a backslash or backtick - (`+) // $2 = Opening run of ` - (?!`) // and no more backticks -- match the full run - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ - - text = text.replace(/(^|[^\\`])(`+)(?!`)([^\r]*?[^`])\2(?!`)/gm, - function (wholeMatch, m1, m2, m3, m4) { - var c = m3; - c = c.replace(/^([ \t]*)/g, ""); // leading whitespace - c = c.replace(/[ \t]*$/g, ""); // trailing whitespace - c = _EncodeCode(c); - c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs. - return m1 + "" + c + ""; - } - ); - - return text; - } - - function _EncodeCode(text) { - // - // Encode/escape certain characters inside Markdown code runs. - // The point is that in code, these characters are literals, - // and lose their special Markdown meanings. - // - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g, "&"); - - // Do the angle bracket song and dance: - text = text.replace(//g, ">"); - - // Now, escape characters that are magic in Markdown: - text = escapeCharacters(text, "\*_{}[]\\", false); - - // jj the line above breaks this: - //--- - - //* Item - - // 1. Subitem - - // special char: * - //--- - - return text; - } - - function _DoItalicsAndBoldStrict(text) { - - if (text.indexOf("*") === -1 && text.indexOf("_") === - 1) - return text; - - text = asciify(text); - - // must go first: - - // (^|[\W_]) Start with a non-letter or beginning of string. Store in \1. - // (?:(?!\1)|(?=^)) Either the next character is *not* the same as the previous, - // or we started at the end of the string (in which case the previous - // group had zero width, so we're still there). Because the next - // character is the marker, this means that if there are e.g. multiple - // underscores in a row, we can only match the left-most ones (which - // prevents foo___bar__ from getting bolded) - // (\*|_) The marker character itself, asterisk or underscore. Store in \2. - // \2 The marker again, since bold needs two. - // (?=\S) The first bolded character cannot be a space. - // ([^\r]*?\S) The actual bolded string. At least one character, and it cannot *end* - // with a space either. Note that like in many other places, [^\r] is - // just a workaround for JS' lack of single-line regexes; it's equivalent - // to a . in an /s regex, because the string cannot contain any \r (they - // are removed in the normalizing step). - // \2\2 The marker character, twice -- end of bold. - // (?!\2) Not followed by another marker character (ensuring that we match the - // rightmost two in a longer row)... - // (?=[\W_]|$) ...but by any other non-word character or the end of string. - text = text.replace(/(^|[\W_])(?:(?!\1)|(?=^))(\*|_)\2(?=\S)([^\r]*?\S)\2\2(?!\2)(?=[\W_]|$)/g, - "$1$3"); - - // This is almost identical to the regex, except 1) there's obviously just one marker - // character, and 2) the italicized string cannot contain the marker character. - text = text.replace(/(^|[\W_])(?:(?!\1)|(?=^))(\*|_)(?=\S)((?:(?!\2)[^\r])*?\S)\2(?!\2)(?=[\W_]|$)/g, - "$1$3"); - - return deasciify(text); - } - - function _DoItalicsAndBold_AllowIntrawordWithAsterisk(text) { - - if (text.indexOf("*") === -1 && text.indexOf("_") === - 1) - return text; - - text = asciify(text); - - // must go first: - // (?=[^\r][*_]|[*_]) Optimization only, to find potentially relevant text portions faster. Minimally slower in Chrome, but much faster in IE. - // ( Store in \1. This is the last character before the delimiter - // ^ Either we're at the start of the string (i.e. there is no last character)... - // | ... or we allow one of the following: - // (?= (lookahead; we're not capturing this, just listing legal possibilities) - // \W__ If the delimiter is __, then this last character must be non-word non-underscore (extra-word emphasis only) - // | - // (?!\*)[\W_]\*\* If the delimiter is **, then this last character can be non-word non-asterisk (extra-word emphasis)... - // | - // \w\*\*\w ...or it can be word/underscore, but only if the first bolded character is such a character as well (intra-word emphasis) - // ) - // [^\r] actually capture the character (can't use `.` since it could be \n) - // ) - // (\*\*|__) Store in \2: the actual delimiter - // (?!\2) not followed by the delimiter again (at most one more asterisk/underscore is allowed) - // (?=\S) the first bolded character can't be a space - // ( Store in \3: the bolded string - // - // (?:| Look at all bolded characters except for the last one. Either that's empty, meaning only a single character was bolded... - // [^\r]*? ... otherwise take arbitrary characters, minimally matching; that's all bolded characters except for the last *two* - // (?!\2) the last two characters cannot be the delimiter itself (because that would mean four underscores/asterisks in a row) - // [^\r] capture the next-to-last bolded character - // ) - // (?= lookahead at the very last bolded char and what comes after - // \S_ for underscore-bolding, it can be any non-space - // | - // \w for asterisk-bolding (otherwise the previous alternative would've matched, since \w implies \S), either the last char is word/underscore... - // | - // \S\*\*(?:[\W_]|$) ... or it's any other non-space, but in that case the character *after* the delimiter may not be a word character - // ) - // . actually capture the last character (can use `.` this time because the lookahead ensures \S in all cases) - // ) - // (?= lookahead; list the legal possibilities for the closing delimiter and its following character - // __(?:\W|$) for underscore-bolding, the following character (if any) must be non-word non-underscore - // | - // \*\*(?:[^*]|$) for asterisk-bolding, any non-asterisk is allowed (note we already ensured above that it's not a word character if the last bolded character wasn't one) - // ) - // \2 actually capture the closing delimiter (and make sure that it matches the opening one) - - text = text.replace(/(?=[^\r][*_]|[*_])(^|(?=\W__|(?!\*)[\W_]\*\*|\w\*\*\w)[^\r])(\*\*|__)(?!\2)(?=\S)((?:|[^\r]*?(?!\2)[^\r])(?=\S_|\w|\S\*\*(?:[\W_]|$)).)(?=__(?:\W|$)|\*\*(?:[^*]|$))\2/g, - "$1$3"); - - // now : - // (?=[^\r][*_]|[*_]) Optimization, see above. - // ( Store in \1. This is the last character before the delimiter - // ^ Either we're at the start of the string (i.e. there is no last character)... - // | ... or we allow one of the following: - // (?= (lookahead; we're not capturing this, just listing legal possibilities) - // \W_ If the delimiter is _, then this last character must be non-word non-underscore (extra-word emphasis only) - // | - // (?!\*) otherwise, we list two possiblities for * as the delimiter; in either case, the last characters cannot be an asterisk itself - // (?: - // [\W_]\* this last character can be non-word (extra-word emphasis)... - // | - // \D\*(?=\w)\D ...or it can be word (otherwise the first alternative would've matched), but only if - // a) the first italicized character is such a character as well (intra-word emphasis), and - // b) neither character on either side of the asterisk is a digit - // ) - // ) - // [^\r] actually capture the character (can't use `.` since it could be \n) - // ) - // (\*|_) Store in \2: the actual delimiter - // (?!\2\2\2) not followed by more than two more instances of the delimiter - // (?=\S) the first italicized character can't be a space - // ( Store in \3: the italicized string - // (?:(?!\2)[^\r])*? arbitrary characters except for the delimiter itself, minimally matching - // (?= lookahead at the very last italicized char and what comes after - // [^\s_]_ for underscore-italicizing, it can be any non-space non-underscore - // | - // (?=\w)\D\*\D for asterisk-italicizing, either the last char is word/underscore *and* neither character on either side of the asterisk is a digit... - // | - // [^\s*]\*(?:[\W_]|$) ... or that last char is any other non-space non-asterisk, but then the character after the delimiter (if any) must be non-word - // ) - // . actually capture the last character (can use `.` this time because the lookahead ensures \S in all cases) - // ) - // (?= lookahead; list the legal possibilities for the closing delimiter and its following character - // _(?:\W|$) for underscore-italicizing, the following character (if any) must be non-word non-underscore - // | - // \*(?:[^*]|$) for asterisk-italicizing, any non-asterisk is allowed; all other restrictions have already been ensured in the previous lookahead - // ) - // \2 actually capture the closing delimiter (and make sure that it matches the opening one) - - text = text.replace(/(?=[^\r][*_]|[*_])(^|(?=\W_|(?!\*)(?:[\W_]\*|\D\*(?=\w)\D))[^\r])(\*|_)(?!\2\2\2)(?=\S)((?:(?!\2)[^\r])*?(?=[^\s_]_|(?=\w)\D\*\D|[^\s*]\*(?:[\W_]|$)).)(?=_(?:\W|$)|\*(?:[^*]|$))\2/g, - "$1$3"); - - return deasciify(text); - } - - - function _DoBlockQuotes(text) { - - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ - - text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, - function (wholeMatch, m1) { - var bq = m1; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting - - // attacklab: clean up hack - bq = bq.replace(/~0/g, ""); - - bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines - bq = _RunBlockGamut(bq); // recurse - - bq = bq.replace(/(^|\n)/g, "$1 "); - // These leading spaces screw with
     content, so we need to fix that:
    -                    bq = bq.replace(
    -                            /(\s*
    [^\r]+?<\/pre>)/gm,
    -                        function (wholeMatch, m1) {
    -                            var pre = m1;
    -                            // attacklab: hack around Konqueror 3.5.4 bug:
    -                            pre = pre.replace(/^  /mg, "~0");
    -                            pre = pre.replace(/~0/g, "");
    -                            return pre;
    -                        });
    -
    -                    return hashBlock("
    \n" + bq + "\n
    "); - } - ); - return text; - } - - function _FormParagraphs(text, doNotUnhash, doNotCreateParagraphs) { - // - // Params: - // $text - string to process with html

    tags - // - - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ""); - text = text.replace(/\n+$/g, ""); - - var grafs = text.split(/\n{2,}/g); - var grafsOut = []; - - var markerRe = /~K(\d+)K/; - - // - // Wrap

    tags. - // - var end = grafs.length; - for (var i = 0; i < end; i++) { - var str = grafs[i]; - - // if this is an HTML marker, copy it - if (markerRe.test(str)) { - grafsOut.push(str); - } - else if (/\S/.test(str)) { - str = _RunSpanGamut(str); - str = str.replace(/^([ \t]*)/g, doNotCreateParagraphs ? "" : "

    "); - if (!doNotCreateParagraphs) - str += "

    " - grafsOut.push(str); - } - - } - // - // Unhashify HTML blocks - // - if (!doNotUnhash) { - end = grafsOut.length; - for (var i = 0; i < end; i++) { - var foundAny = true; - while (foundAny) { // we may need several runs, since the data may be nested - 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) { - // Smart processing for ampersands and angle brackets that need to be encoded. - - // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - // http://bumppo.net/projects/amputator/ - text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&"); - - // Encode naked <'s - text = text.replace(/<(?![a-z\/?!]|~D)/gi, "<"); - - return text; - } - - function _EncodeBackslashEscapes(text) { - // - // Parameter: String. - // Returns: The string, with after processing the following backslash - // escape sequences. - // - - // attacklab: The polite way to do this is with the new - // escapeCharacters() function: - // - // text = escapeCharacters(text,"\\",true); - // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); - // - // ...but we're sidestepping its use of the (slow) RegExp constructor - // as an optimization for Firefox. This function gets called a LOT. - - 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) { - - // note that at this point, all other URL in the text are already hyperlinked as
    - // *except* for the case - - // automatically add < and > around unadorned raw hyperlinks - // must be preceded by a non-word character (and not by =" or <) and followed by non-word/EOF character - // simulating the lookbehind in a consuming way is okay here, since a URL can neither and with a " nor - // with a <, so there is no risk of overlapping matches. - text = text.replace(autoLinkRegex, handleTrailingParens); - - // autolink anything like - - - var replacer = function (wholematch, m1) { - var url = attributeSafeUrl(m1); - - return "" + pluginHooks.plainLinkText(m1) + ""; - }; - text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); - - // Email addresses: - /* - text = text.replace(/ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - /gi, _DoAutoLinks_callback()); - */ - - /* disabling email autolinking, since we don't do that on the server, either - text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, - function(wholeMatch,m1) { - return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); - } - ); - */ - return text; - } - - function _UnescapeSpecialChars(text) { - // - // Swap back in all the special characters we've hidden. - // - text = text.replace(/~E(\d+)E/g, - function (wholeMatch, m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - } - ); - return text; - } - - function _Outdent(text) { - // - // Remove one level of line-leading tabs or spaces - // - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - - text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width - - // attacklab: clean up hack - 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]; - }); - } - - // - // attacklab: Utility functions - // - - function attributeSafeUrl(url) { - url = attributeEncode(url); - url = escapeCharacters(url, "*_:()[]") - return url; - } - - function escapeCharacters(text, charsToEscape, afterBackslash) { - // First we have to escape the escape characters so that - // we can build a character class out of them - 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"; - } - - }; // end of the Markdown.Converter constructor - -})(); diff --git a/src/scripts/markdown/02_markdown-sanitizer.js b/src/scripts/markdown/02_markdown-sanitizer.js deleted file mode 100644 index eabf2173..00000000 --- a/src/scripts/markdown/02_markdown-sanitizer.js +++ /dev/null @@ -1,116 +0,0 @@ -(function () { - var output, Converter; - if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module - output = exports; - Converter = require("./Markdown.Converter").Converter; - } else { - output = window.Markdown; - Converter = output.Converter; - } - - output.getSanitizingConverter = function () { - var converter = new Converter(); - converter.hooks.chain("postConversion", sanitizeHtml); - converter.hooks.chain("postConversion", balanceTags); - return converter; - } - - function sanitizeHtml(html) { - return html.replace(/<[^>]*>?/gi, sanitizeTag); - } - - // (tags that can be opened/closed) | (tags that stand alone) - var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|iframe|kbd|li|ol(?: start="\d+")?|p|pre|s|sup|sub|strong|strike|ul|video)>|<(br|hr)\s?\/?>)$/i; - // | - var a_white = /^(]+")?(\sclass="[^"<>]+")?\s?>|<\/a>)$/i; - - // Cloud custom: Allow iframe embed from YouTube, Vimeo and SoundCloud - var iframe_youtube = /^(|<\/iframe>)$/i; - var iframe_vimeo = /^(|<\/iframe>)$/i; - var iframe_soundcloud = /^(|<\/iframe>)$/i; - var iframe_googlestorage = /^(|<\/iframe>)$/i; - - // ]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i; - var video_white = //; - - function sanitizeTag(tag) { - if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white) || tag.match(iframe_youtube) || tag.match(iframe_vimeo) || tag.match(iframe_soundcloud) || tag.match(iframe_googlestorage) || tag.match(video_white)) { - return tag; - } else { - return ""; - } - } - - /// - /// attempt to balance HTML tags in the html string - /// by removing any unmatched opening or closing tags - /// IMPORTANT: we *assume* HTML has *already* been - /// sanitized and is safe/sane before balancing! - /// - /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593 - /// - function balanceTags(html) { - - if (html == "") - return ""; - - var re = /<\/?\w+[^>]*(\s|$|>)/g; - // convert everything to lower case; this makes - // our case insensitive comparisons easier - var tags = html.toLowerCase().match(re); - - // no HTML tags present? nothing to do; exit now - var tagcount = (tags || []).length; - if (tagcount == 0) - return html; - - var tagname, tag; - var ignoredtags = "



  • "; - var match; - var tagpaired = []; - var tagremove = []; - var needsRemoval = false; - - // loop through matched tags in forward order - for (var ctag = 0; ctag < tagcount; ctag++) { - tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1"); - // skip any already paired tags - // and skip tags in our ignore list; assume they're self-closed - if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1) - continue; - - tag = tags[ctag]; - match = -1; - - if (!/^<\//.test(tag)) { - // this is an opening tag - // search forwards (next tags), look for closing tags - for (var ntag = ctag + 1; ntag < tagcount; ntag++) { - if (!tagpaired[ntag] && tags[ntag] == "") { - match = ntag; - break; - } - } - } - - if (match == -1) - needsRemoval = tagremove[ctag] = true; // mark for removal - else - tagpaired[match] = true; // mark paired - } - - if (!needsRemoval) - return html; - - // delete all orphaned tags from the string - - var ctag = 0; - html = html.replace(re, function (match) { - var res = tagremove[ctag] ? "" : match; - ctag++; - return res; - }); - return html; - } -})(); diff --git a/src/scripts/markdown/03_showdown.js b/src/scripts/markdown/03_showdown.js deleted file mode 100644 index 5de0896a..00000000 --- a/src/scripts/markdown/03_showdown.js +++ /dev/null @@ -1,2295 +0,0 @@ -;/*! showdown 27-08-2015 */ -(function(){ -/** - * Created by Tivie on 13-07-2015. - */ - -function getDefaultOpts(simple) { - 'use strict'; - - var defaultOptions = { - omitExtraWLInCodeBlocks: { - default: false, - describe: 'Omit the default extra whiteline added to code blocks', - type: 'boolean' - }, - noHeaderId: { - default: false, - describe: 'Turn on/off generated header id', - type: 'boolean' - }, - prefixHeaderId: { - default: false, - describe: 'Specify a prefix to generated header ids', - type: 'string' - }, - headerLevelStart: { - default: false, - describe: 'The header blocks level start', - type: 'integer' - }, - parseImgDimensions: { - default: false, - describe: 'Turn on/off image dimension parsing', - type: 'boolean' - }, - simplifiedAutoLink: { - default: false, - describe: 'Turn on/off GFM autolink style', - type: 'boolean' - }, - literalMidWordUnderscores: { - default: false, - describe: 'Parse midword underscores as literal underscores', - type: 'boolean' - }, - strikethrough: { - default: false, - describe: 'Turn on/off strikethrough support', - type: 'boolean' - }, - tables: { - default: false, - describe: 'Turn on/off tables support', - type: 'boolean' - }, - tablesHeaderId: { - default: false, - describe: 'Add an id to table headers', - type: 'boolean' - }, - ghCodeBlocks: { - default: true, - describe: 'Turn on/off GFM fenced code blocks support', - type: 'boolean' - }, - tasklists: { - default: false, - describe: 'Turn on/off GFM tasklist support', - type: 'boolean' - }, - smoothLivePreview: { - default: false, - describe: 'Prevents weird effects in live previews due to incomplete input', - type: 'boolean' - } - }; - if (simple === false) { - return JSON.parse(JSON.stringify(defaultOptions)); - } - var ret = {}; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - ret[opt] = defaultOptions[opt].default; - } - } - return ret; -} - -/** - * Created by Tivie on 06-01-2015. - */ - -// Private properties -var showdown = {}, - parsers = {}, - extensions = {}, - globalOptions = getDefaultOpts(true), - flavor = { - github: { - omitExtraWLInCodeBlocks: true, - prefixHeaderId: 'user-content-', - simplifiedAutoLink: true, - literalMidWordUnderscores: true, - strikethrough: true, - tables: true, - tablesHeaderId: true, - ghCodeBlocks: true, - tasklists: true - }, - vanilla: getDefaultOpts(true) - }; - -/** - * helper namespace - * @type {{}} - */ -showdown.helper = {}; - -/** - * TODO LEGACY SUPPORT CODE - * @type {{}} - */ -showdown.extensions = {}; - -/** - * Set a global option - * @static - * @param {string} key - * @param {*} value - * @returns {showdown} - */ -showdown.setOption = function (key, value) { - 'use strict'; - globalOptions[key] = value; - return this; -}; - -/** - * Get a global option - * @static - * @param {string} key - * @returns {*} - */ -showdown.getOption = function (key) { - 'use strict'; - return globalOptions[key]; -}; - -/** - * Get the global options - * @static - * @returns {{}} - */ -showdown.getOptions = function () { - 'use strict'; - return globalOptions; -}; - -/** - * Reset global options to the default values - * @static - */ -showdown.resetOptions = function () { - 'use strict'; - globalOptions = getDefaultOpts(true); -}; - -/** - * Set the flavor showdown should use as default - * @param {string} name - */ -showdown.setFlavor = function (name) { - 'use strict'; - if (flavor.hasOwnProperty(name)) { - var preset = flavor[name]; - for (var option in preset) { - if (preset.hasOwnProperty(option)) { - globalOptions[option] = preset[option]; - } - } - } -}; - -/** - * Get the default options - * @static - * @param {boolean} [simple=true] - * @returns {{}} - */ -showdown.getDefaultOptions = function (simple) { - 'use strict'; - return getDefaultOpts(simple); -}; - -/** - * Get or set a subParser - * - * subParser(name) - Get a registered subParser - * subParser(name, func) - Register a subParser - * @static - * @param {string} name - * @param {function} [func] - * @returns {*} - */ -showdown.subParser = function (name, func) { - 'use strict'; - if (showdown.helper.isString(name)) { - if (typeof func !== 'undefined') { - parsers[name] = func; - } else { - if (parsers.hasOwnProperty(name)) { - return parsers[name]; - } else { - throw Error('SubParser named ' + name + ' not registered!'); - } - } - } -}; - -/** - * Gets or registers an extension - * @static - * @param {string} name - * @param {object|function=} ext - * @returns {*} - */ -showdown.extension = function (name, ext) { - 'use strict'; - - if (!showdown.helper.isString(name)) { - throw Error('Extension \'name\' must be a string'); - } - - name = showdown.helper.stdExtName(name); - - // Getter - if (showdown.helper.isUndefined(ext)) { - if (!extensions.hasOwnProperty(name)) { - throw Error('Extension named ' + name + ' is not registered!'); - } - return extensions[name]; - - // Setter - } else { - // Expand extension if it's wrapped in a function - if (typeof ext === 'function') { - ext = ext(); - } - - // Ensure extension is an array - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - - var validExtension = validate(ext, name); - - if (validExtension.valid) { - extensions[name] = ext; - } else { - throw Error(validExtension.error); - } - } -}; - -/** - * Gets all extensions registered - * @returns {{}} - */ -showdown.getAllExtensions = function () { - 'use strict'; - return extensions; -}; - -/** - * Remove an extension - * @param {string} name - */ -showdown.removeExtension = function (name) { - 'use strict'; - delete extensions[name]; -}; - -/** - * Removes all extensions - */ -showdown.resetExtensions = function () { - 'use strict'; - extensions = {}; -}; - -/** - * Validate extension - * @param {array} extension - * @param {string} name - * @returns {{valid: boolean, error: string}} - */ -function validate(extension, name) { - 'use strict'; - - var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', - ret = { - valid: true, - error: '' - }; - - if (!showdown.helper.isArray(extension)) { - extension = [extension]; - } - - for (var i = 0; i < extension.length; ++i) { - var baseMsg = errMsg + ' sub-extension ' + i + ': ', - ext = extension[i]; - if (typeof ext !== 'object') { - ret.valid = false; - ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; - return ret; - } - - if (!showdown.helper.isString(ext.type)) { - ret.valid = false; - ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; - return ret; - } - - var type = ext.type = ext.type.toLowerCase(); - - // normalize extension type - if (type === 'language') { - type = ext.type = 'lang'; - } - - if (type === 'html') { - type = ext.type = 'output'; - } - - if (type !== 'lang' && type !== 'output') { - ret.valid = false; - ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"'; - return ret; - } - - if (ext.filter) { - if (typeof ext.filter !== 'function') { - ret.valid = false; - ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; - return ret; - } - - } else if (ext.regex) { - if (showdown.helper.isString(ext.regex)) { - ext.regex = new RegExp(ext.regex, 'g'); - } - if (!ext.regex instanceof RegExp) { - ret.valid = false; - ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + - typeof ext.regex + ' given'; - return ret; - } - if (showdown.helper.isUndefined(ext.replace)) { - ret.valid = false; - ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; - return ret; - } - - } else { - ret.valid = false; - ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method'; - return ret; - } - - if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { - ret.valid = false; - ret.error = baseMsg + 'output extensions must define a filter property'; - return ret; - } - } - return ret; -} - -/** - * Validate extension - * @param {object} ext - * @returns {boolean} - */ -showdown.validateExtension = function (ext) { - 'use strict'; - - var validateExtension = validate(ext, null); - if (!validateExtension.valid) { - console.warn(validateExtension.error); - return false; - } - return true; -}; - -/** - * showdownjs helper functions - */ - -if (!showdown.hasOwnProperty('helper')) { - showdown.helper = {}; -} - -/** - * Check if var is string - * @static - * @param {string} a - * @returns {boolean} - */ -showdown.helper.isString = function isString(a) { - 'use strict'; - return (typeof a === 'string' || a instanceof String); -}; - -/** - * ForEach helper function - * @static - * @param {*} obj - * @param {function} callback - */ -showdown.helper.forEach = function forEach(obj, callback) { - 'use strict'; - if (typeof obj.forEach === 'function') { - obj.forEach(callback); - } else { - for (var i = 0; i < obj.length; i++) { - callback(obj[i], i, obj); - } - } -}; - -/** - * isArray helper function - * @static - * @param {*} a - * @returns {boolean} - */ -showdown.helper.isArray = function isArray(a) { - 'use strict'; - return a.constructor === Array; -}; - -/** - * Check if value is undefined - * @static - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - */ -showdown.helper.isUndefined = function isUndefined(value) { - 'use strict'; - return typeof value === 'undefined'; -}; - -/** - * Standardidize extension name - * @static - * @param {string} s extension name - * @returns {string} - */ -showdown.helper.stdExtName = function (s) { - 'use strict'; - return s.replace(/[_-]||\s/g, '').toLowerCase(); -}; - -function escapeCharactersCallback(wholeMatch, m1) { - 'use strict'; - var charCodeToEscape = m1.charCodeAt(0); - return '~E' + charCodeToEscape + 'E'; -} - -/** - * Callback used to escape characters when passing through String.replace - * @static - * @param {string} wholeMatch - * @param {string} m1 - * @returns {string} - */ -showdown.helper.escapeCharactersCallback = escapeCharactersCallback; - -/** - * Escape characters in a string - * @static - * @param {string} text - * @param {string} charsToEscape - * @param {boolean} afterBackslash - * @returns {XML|string|void|*} - */ -showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) { - 'use strict'; - // First we have to escape the escape characters so that - // we can build a character class out of them - var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; - - if (afterBackslash) { - regexString = '\\\\' + regexString; - } - - var regex = new RegExp(regexString, 'g'); - text = text.replace(regex, escapeCharactersCallback); - - return text; -}; - -/** - * POLYFILLS - */ -if (showdown.helper.isUndefined(console)) { - console = { - warn: function (msg) { - 'use strict'; - alert(msg); - }, - log: function (msg) { - 'use strict'; - alert(msg); - } - }; -} - -/** - * Created by Estevao on 31-05-2015. - */ - -/** - * Showdown Converter class - * @class - * @param {object} [converterOptions] - * @returns {Converter} - */ -showdown.Converter = function (converterOptions) { - 'use strict'; - - var - /** - * Options used by this converter - * @private - * @type {{}} - */ - options = {}, - - /** - * Language extensions used by this converter - * @private - * @type {Array} - */ - langExtensions = [], - - /** - * Output modifiers extensions used by this converter - * @private - * @type {Array} - */ - outputModifiers = [], - - /** - * The parser Order - * @private - * @type {string[]} - */ - parserOrder = [ - 'githubCodeBlocks', - 'hashHTMLBlocks', - 'stripLinkDefinitions', - 'blockGamut', - 'unescapeSpecialChars' - ]; - - _constructor(); - - /** - * Converter constructor - * @private - */ - function _constructor() { - converterOptions = converterOptions || {}; - - for (var gOpt in globalOptions) { - if (globalOptions.hasOwnProperty(gOpt)) { - options[gOpt] = globalOptions[gOpt]; - } - } - - // Merge options - if (typeof converterOptions === 'object') { - for (var opt in converterOptions) { - if (converterOptions.hasOwnProperty(opt)) { - options[opt] = converterOptions[opt]; - } - } - } else { - throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + - ' was passed instead.'); - } - - if (options.extensions) { - showdown.helper.forEach(options.extensions, _parseExtension); - } - } - - /** - * Parse extension - * @param {*} ext - * @param {string} [name=''] - * @private - */ - function _parseExtension(ext, name) { - - name = name || null; - // If it's a string, the extension was previously loaded - if (showdown.helper.isString(ext)) { - ext = showdown.helper.stdExtName(ext); - name = ext; - - // LEGACY_SUPPORT CODE - if (showdown.extensions[ext]) { - console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + - 'Please inform the developer that the extension should be updated!'); - legacyExtensionLoading(showdown.extensions[ext], ext); - return; - // END LEGACY SUPPORT CODE - - } else if (!showdown.helper.isUndefined(extensions[ext])) { - ext = extensions[ext]; - - } else { - throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); - } - } - - if (typeof ext === 'function') { - ext = ext(); - } - - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - - var validExt = validate(ext, name); - if (!validExt.valid) { - throw Error(validExt.error); - } - - for (var i = 0; i < ext.length; ++i) { - switch (ext[i].type) { - case 'lang': - langExtensions.push(ext[i]); - break; - - case 'output': - outputModifiers.push(ext[i]); - break; - - default: - // should never reach here - throw Error('Extension loader error: Type unrecognized!!!'); - } - } - } - - /** - * LEGACY_SUPPORT - * @param {*} ext - * @param {string} name - */ - function legacyExtensionLoading(ext, name) { - if (typeof ext === 'function') { - ext = ext(new showdown.Converter()); - } - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - var valid = validate(ext, name); - - if (!valid.valid) { - throw Error(valid.error); - } - - for (var i = 0; i < ext.length; ++i) { - switch (ext[i].type) { - case 'lang': - langExtensions.push(ext[i]); - break; - case 'output': - outputModifiers.push(ext[i]); - break; - default:// should never reach here - throw Error('Extension loader error: Type unrecognized!!!'); - } - } - } - - /** - * Converts a markdown string into HTML - * @param {string} text - * @returns {*} - */ - this.makeHtml = function (text) { - //check if text is not falsy - if (!text) { - return text; - } - - var globals = { - gHtmlBlocks: [], - gUrls: {}, - gTitles: {}, - gDimensions: {}, - gListLevel: 0, - hashLinkCounts: {}, - langExtensions: langExtensions, - outputModifiers: outputModifiers, - converter: this - }; - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitrary; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g, '~T'); - - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g, '~D'); - - // Standardize line endings - text = text.replace(/\r\n/g, '\n'); // DOS to Unix - text = text.replace(/\r/g, '\n'); // Mac to Unix - - // Make sure text begins and ends with a couple of newlines: - text = '\n\n' + text + '\n\n'; - - // detab - text = showdown.subParser('detab')(text, options, globals); - - // stripBlankLines - text = showdown.subParser('stripBlankLines')(text, options, globals); - - //run languageExtensions - showdown.helper.forEach(langExtensions, function (ext) { - text = showdown.subParser('runExtension')(ext, text, options, globals); - }); - - // Run all registered parsers - for (var i = 0; i < parserOrder.length; ++i) { - var name = parserOrder[i]; - text = parsers[name](text, options, globals); - } - - // attacklab: Restore dollar signs - text = text.replace(/~D/g, '$$'); - - // attacklab: Restore tildes - text = text.replace(/~T/g, '~'); - - // Run output modifiers - showdown.helper.forEach(outputModifiers, function (ext) { - text = showdown.subParser('runExtension')(ext, text, options, globals); - }); - - return text; - }; - - /** - * Set an option of this Converter instance - * @param {string} key - * @param {*} value - */ - this.setOption = function (key, value) { - options[key] = value; - }; - - /** - * Get the option of this Converter instance - * @param {string} key - * @returns {*} - */ - this.getOption = function (key) { - return options[key]; - }; - - /** - * Get the options of this Converter instance - * @returns {{}} - */ - this.getOptions = function () { - return options; - }; - - /** - * Add extension to THIS converter - * @param {{}} extension - * @param {string} [name=null] - */ - this.addExtension = function (extension, name) { - name = name || null; - _parseExtension(extension, name); - }; - - /** - * Use a global registered extension with THIS converter - * @param {string} extensionName Name of the previously registered extension - */ - this.useExtension = function (extensionName) { - _parseExtension(extensionName); - }; - - /** - * Set the flavor THIS converter should use - * @param {string} name - */ - this.setFlavor = function (name) { - if (flavor.hasOwnProperty(name)) { - var preset = flavor[name]; - for (var option in preset) { - if (preset.hasOwnProperty(option)) { - options[option] = preset[option]; - } - } - } - }; - - /** - * Remove an extension from THIS converter. - * Note: This is a costly operation. It's better to initialize a new converter - * and specify the extensions you wish to use - * @param {Array} extension - */ - this.removeExtension = function (extension) { - if (!showdown.helper.isArray(extension)) { - extension = [extension]; - } - for (var a = 0; a < extension.length; ++a) { - var ext = extension[a]; - for (var i = 0; i < langExtensions.length; ++i) { - if (langExtensions[i] === ext) { - langExtensions[i].splice(i, 1); - } - } - for (var ii = 0; ii < outputModifiers.length; ++i) { - if (outputModifiers[ii] === ext) { - outputModifiers[ii].splice(i, 1); - } - } - } - }; - - /** - * Get all extension of THIS converter - * @returns {{language: Array, output: Array}} - */ - this.getAllExtensions = function () { - return { - language: langExtensions, - output: outputModifiers - }; - }; -}; - -/** - * Turn Markdown link shortcuts into XHTML tags. - */ -showdown.subParser('anchors', function (text, config, globals) { - 'use strict'; - - var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { - if (showdown.helper.isUndefined(m7)) { - m7 = ''; - } - wholeMatch = m1; - var linkText = m2, - linkId = m3.toLowerCase(), - url = m4, - title = m7; - - if (!url) { - if (!linkId) { - // lower-case and turn embedded newlines into spaces - linkId = linkText.toLowerCase().replace(/ ?\n/g, ' '); - } - url = '#' + linkId; - - if (!showdown.helper.isUndefined(globals.gUrls[linkId])) { - url = globals.gUrls[linkId]; - if (!showdown.helper.isUndefined(globals.gTitles[linkId])) { - title = globals.gTitles[linkId]; - } - } else { - if (wholeMatch.search(/\(\s*\)$/m) > -1) { - // Special case for explicit empty url - url = ''; - } else { - return wholeMatch; - } - } - } - - url = showdown.helper.escapeCharacters(url, '*_', false); - var result = ''; - - return result; - }; - - // First, handle reference-style links: [link text] [id] - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - )()()()() // pad remaining backreferences - /g,_DoAnchors_callback); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); - - // - // Next, inline-style links: [link text](url "optional title") - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - ) - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // href = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g,writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, - writeAnchorTag); - - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - )()()()()() // pad rest of backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); - - return text; - -}); - -showdown.subParser('autoLinks', function (text, options) { - 'use strict'; - - //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, - - var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, - delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, - simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi, - delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; - - text = text.replace(delimUrlRegex, '$1'); - text = text.replace(delimMailRegex, replaceMail); - //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, - // Email addresses: - - if (options.simplifiedAutoLink) { - text = text.replace(simpleURLRegex, '$1'); - text = text.replace(simpleMailRegex, replaceMail); - } - - function replaceMail(wholeMatch, m1) { - var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); - return showdown.subParser('encodeEmailAddress')(unescapedStr); - } - - return text; -}); - -/** - * These are all the transformations that form block-level - * tags like paragraphs, headers, and list items. - */ -showdown.subParser('blockGamut', function (text, options, globals) { - 'use strict'; - - // we parse blockquotes first so that we can have headings and hrs - // inside blockquotes - text = showdown.subParser('blockQuotes')(text, options, globals); - text = showdown.subParser('headers')(text, options, globals); - - // Do Horizontal Rules: - var key = showdown.subParser('hashBlock')('
    ', options, globals); - text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); - text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); - text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key); - - text = showdown.subParser('lists')(text, options, globals); - text = showdown.subParser('codeBlocks')(text, options, globals); - text = showdown.subParser('tables')(text, options, globals); - - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

    tags around block-level tags. - text = showdown.subParser('hashHTMLBlocks')(text, options, globals); - text = showdown.subParser('paragraphs')(text, options, globals); - - return text; - -}); - -showdown.subParser('blockQuotes', function (text, options, globals) { - 'use strict'; - - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ - - text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { - var bq = m1; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting - - // attacklab: clean up hack - bq = bq.replace(/~0/g, ''); - - bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines - bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); - bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse - - bq = bq.replace(/(^|\n)/g, '$1 '); - // These leading spaces screw with

     content, so we need to fix that:
    -    bq = bq.replace(/(\s*
    [^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
    -      var pre = m1;
    -      // attacklab: hack around Konqueror 3.5.4 bug:
    -      pre = pre.replace(/^  /mg, '~0');
    -      pre = pre.replace(/~0/g, '');
    -      return pre;
    -    });
    -
    -    return showdown.subParser('hashBlock')('
    \n' + bq + '\n
    ', options, globals); - }); - return text; -}); - -/** - * Process Markdown `
    ` blocks.
    - */
    -showdown.subParser('codeBlocks', function (text, options, globals) {
    -  'use strict';
    -
    -  /*
    -   text = text.replace(text,
    -   /(?:\n\n|^)
    -   (								// $1 = the code block -- one or more lines, starting with a space/tab
    -   (?:
    -   (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    -   .*\n+
    -   )+
    -   )
    -   (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
    -   /g,function(){...});
    -   */
    -
    -  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    -  text += '~0';
    -
    -  var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
    -  text = text.replace(pattern, function (wholeMatch, m1, m2) {
    -    var codeblock = m1,
    -        nextChar = m2,
    -        end = '\n';
    -
    -    codeblock = showdown.subParser('outdent')(codeblock);
    -    codeblock = showdown.subParser('encodeCode')(codeblock);
    -    codeblock = showdown.subParser('detab')(codeblock);
    -    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
    -    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
    -
    -    if (options.omitExtraWLInCodeBlocks) {
    -      end = '';
    -    }
    -
    -    codeblock = '
    ' + codeblock + end + '
    '; - - return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; -}); - -/** - * - * * Backtick quotes are used for spans. - * - * * You can use multiple backticks as the delimiters if you want to - * include literal backticks in the code span. So, this input: - * - * Just type ``foo `bar` baz`` at the prompt. - * - * Will translate to: - * - *

    Just type foo `bar` baz at the prompt.

    - * - * There's no arbitrary limit to the number of backticks you - * can use as delimters. If you need three consecutive backticks - * in your code, use four for delimiters, etc. - * - * * You can use spaces to get literal backticks at the edges: - * - * ... type `` `bar` `` ... - * - * Turns to: - * - * ... type `bar` ... - */ -showdown.subParser('codeSpans', function (text) { - 'use strict'; - - //special case -> literal html code tag - text = text.replace(/(<]*?>)([^]*?)<\/code>/g, function (wholeMatch, tag, c) { - c = c.replace(/^([ \t]*)/g, ''); // leading whitespace - c = c.replace(/[ \t]*$/g, ''); // trailing whitespace - c = showdown.subParser('encodeCode')(c); - return tag + c + '
    '; - }); - - /* - text = text.replace(/ - (^|[^\\]) // Character before opening ` can't be a backslash - (`+) // $2 = Opening run of ` - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ - text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function (wholeMatch, m1, m2, m3) { - var c = m3; - c = c.replace(/^([ \t]*)/g, ''); // leading whitespace - c = c.replace(/[ \t]*$/g, ''); // trailing whitespace - c = showdown.subParser('encodeCode')(c); - return m1 + '' + c + ''; - } - ); - - return text; -}); - -/** - * Convert all tabs to spaces - */ -showdown.subParser('detab', function (text) { - 'use strict'; - - // expand first n-1 tabs - text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width - - // replace the nth with two sentinels - text = text.replace(/\t/g, '~A~B'); - - // use the sentinel to anchor our regex so it doesn't explode - text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) { - var leadingText = m1, - numSpaces = 4 - leadingText.length % 4; // g_tab_width - - // there *must* be a better way to do this: - for (var i = 0; i < numSpaces; i++) { - leadingText += ' '; - } - - return leadingText; - }); - - // clean up sentinels - text = text.replace(/~A/g, ' '); // g_tab_width - text = text.replace(/~B/g, ''); - - return text; - -}); - -/** - * Smart processing for ampersands and angle brackets that need to be encoded. - */ -showdown.subParser('encodeAmpsAndAngles', function (text) { - 'use strict'; - // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - // http://bumppo.net/projects/amputator/ - text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); - - // Encode naked <'s - text = text.replace(/<(?![a-z\/?\$!])/gi, '<'); - - return text; -}); - -/** - * Returns the string, with after processing the following backslash escape sequences. - * - * attacklab: The polite way to do this is with the new escapeCharacters() function: - * - * text = escapeCharacters(text,"\\",true); - * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); - * - * ...but we're sidestepping its use of the (slow) RegExp constructor - * as an optimization for Firefox. This function gets called a LOT. - */ -showdown.subParser('encodeBackslashEscapes', function (text) { - 'use strict'; - text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback); - return text; -}); - -/** - * Encode/escape certain characters inside Markdown code runs. - * The point is that in code, these characters are literals, - * and lose their special Markdown meanings. - */ -showdown.subParser('encodeCode', function (text) { - 'use strict'; - - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g, '&'); - - // Do the angle bracket song and dance: - text = text.replace(//g, '>'); - - // Now, escape characters that are magic in Markdown: - text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false); - - // jj the line above breaks this: - //--- - //* Item - // 1. Subitem - // special char: * - // --- - - return text; -}); - -/** - * Input: an email address, e.g. "foo@example.com" - * - * Output: the email address as a mailto link, with each character - * of the address encoded as either a decimal or hex entity, in - * the hopes of foiling most address harvesting spam bots. E.g.: - * - * foo - * @example.com - * - * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk - * mailing list: - * - */ -showdown.subParser('encodeEmailAddress', function (addr) { - 'use strict'; - - var encode = [ - function (ch) { - return '&#' + ch.charCodeAt(0) + ';'; - }, - function (ch) { - return '&#x' + ch.charCodeAt(0).toString(16) + ';'; - }, - function (ch) { - return ch; - } - ]; - - addr = 'mailto:' + addr; - - addr = addr.replace(/./g, function (ch) { - if (ch === '@') { - // this *must* be encoded. I insist. - ch = encode[Math.floor(Math.random() * 2)](ch); - } else if (ch !== ':') { - // leave ':' alone (to spot mailto: later) - var r = Math.random(); - // roughly 10% raw, 45% hex, 45% dec - ch = ( - r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) - ); - } - return ch; - }); - - addr = '' + addr + ''; - addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part - - return addr; -}); - -/** - * Within tags -- meaning between < and > -- encode [\ ` * _] so they - * don't conflict with their use in Markdown for code, italics and strong. - */ -showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) { - 'use strict'; - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; - - text = text.replace(regex, function (wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); - tag = showdown.helper.escapeCharacters(tag, '\\`*_', false); - return tag; - }); - - return text; -}); - -/** - * Handle github codeblocks prior to running HashHTML so that - * HTML contained within the codeblock gets escaped properly - * Example: - * ```ruby - * def hello_world(x) - * puts "Hello, #{x}" - * end - * ``` - */ -showdown.subParser('githubCodeBlocks', function (text, options, globals) { - 'use strict'; - - // early exit if option is not enabled - if (!options.ghCodeBlocks) { - return text; - } - - text += '~0'; - - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { - var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; - - codeblock = showdown.subParser('encodeCode')(codeblock); - codeblock = showdown.subParser('detab')(codeblock); - codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines - codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace - - codeblock = '
    ' + codeblock + end + '
    '; - - return showdown.subParser('hashBlock')(codeblock, options, globals); - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; - -}); - -showdown.subParser('hashBlock', function (text, options, globals) { - 'use strict'; - text = text.replace(/(^\n+|\n+$)/g, ''); - return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; -}); - -showdown.subParser('hashElement', function (text, options, globals) { - 'use strict'; - - return function (wholeMatch, m1) { - var blockText = m1; - - // Undo double lines - blockText = blockText.replace(/\n\n/g, '\n'); - blockText = blockText.replace(/^\n/, ''); - - // strip trailing blank lines - blockText = blockText.replace(/\n+$/g, ''); - - // Replace the element text with a marker ("~KxK" where x is its key) - blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; - - return blockText; - }; -}); - -showdown.subParser('hashHTMLBlocks', function (text, options, globals) { - 'use strict'; - - // attacklab: Double up blank lines to reduce lookaround - text = text.replace(/\n/g, '\n\n'); - - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

    s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - //var block_tags_a = - // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside'; - // var block_tags_b = - // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside'; - - // First, look for nested blocks, e.g.: - //

    - //
    - // tags for inner block must be indented. - //
    - //
    - // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
    ` and stop at the first `
    `. - - // attacklab: This regex can be expensive when it fails. - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, - showdown.subParser('hashElement')(text, options, globals)); - - // - // Now match more liberally, simply from `\n` to `\n` - // - - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|math|style|section|header|footer|nav|article|aside|address|audio|canvas|figure|hgroup|output|video)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm, - showdown.subParser('hashElement')(text, options, globals)); - - // Special case just for
    . It was easier to make a special case than - // to make the other regex more complicated. - - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? // - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // Special case for standalone HTML comments: - - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // PHP and ASP-style processor instructions ( and <%...%>) - - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // attacklab: Undo double lines (see comment at top of this function) - text = text.replace(/\n\n/g, '\n'); - return text; - -}); - -showdown.subParser('headers', function (text, options, globals) { - 'use strict'; - - var prefixHeader = options.prefixHeaderId, - headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), - - // Set text-style headers: - // Header 1 - // ======== - // - // Header 2 - // -------- - // - setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, - setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; - - text = text.replace(setextRegexH1, function (wholeMatch, m1) { - - var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', - hLevel = headerLevelStart, - hashBlock = '' + spanGamut + ''; - return showdown.subParser('hashBlock')(hashBlock, options, globals); - }); - - text = text.replace(setextRegexH2, function (matchFound, m1) { - var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', - hLevel = headerLevelStart + 1, - hashBlock = '' + spanGamut + ''; - return showdown.subParser('hashBlock')(hashBlock, options, globals); - }); - - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // - text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) { - var span = showdown.subParser('spanGamut')(m2, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', - hLevel = headerLevelStart - 1 + m1.length, - header = '' + span + ''; - - return showdown.subParser('hashBlock')(header, options, globals); - }); - - function headerId(m) { - var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase(); - - if (globals.hashLinkCounts[escapedId]) { - title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++); - } else { - title = escapedId; - globals.hashLinkCounts[escapedId] = 1; - } - - // Prefix id to prevent causing inadvertent pre-existing style matches. - if (prefixHeader === true) { - prefixHeader = 'section'; - } - - if (showdown.helper.isString(prefixHeader)) { - return prefixHeader + title; - } - return title; - } - - return text; -}); - -/** - * Turn Markdown image shortcuts into tags. - */ -showdown.subParser('images', function (text, options, globals) { - 'use strict'; - - var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g, - referenceRegExp = /!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g; - - function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { - - var gUrls = globals.gUrls, - gTitles = globals.gTitles, - gDims = globals.gDimensions; - - linkId = linkId.toLowerCase(); - - if (!title) { - title = ''; - } - - if (url === '' || url === null) { - if (linkId === '' || linkId === null) { - // lower-case and turn embedded newlines into spaces - linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); - } - url = '#' + linkId; - - if (!showdown.helper.isUndefined(gUrls[linkId])) { - url = gUrls[linkId]; - if (!showdown.helper.isUndefined(gTitles[linkId])) { - title = gTitles[linkId]; - } - if (!showdown.helper.isUndefined(gDims[linkId])) { - width = gDims[linkId].width; - height = gDims[linkId].height; - } - } else { - return wholeMatch; - } - } - - altText = altText.replace(/"/g, '"'); - altText = showdown.helper.escapeCharacters(altText, '*_', false); - url = showdown.helper.escapeCharacters(url, '*_', false); - var result = '' + altText + 'x "optional title") - text = text.replace(inlineRegExp, writeImageTag); - - return text; -}); - -showdown.subParser('italicsAndBold', function (text, options) { - 'use strict'; - - if (options.literalMidWordUnderscores) { - //underscores - // Since we are consuming a \s character, we need to add it - text = text.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm, '$1$2'); - text = text.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm, '$1$2'); - //asterisks - text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '$1'); - text = text.replace(/\*(?=\S)([^]+?)\*/g, '$1'); - - } else { - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '$2'); - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '$2'); - } - return text; -}); - -/** - * Form HTML ordered (numbered) and unordered (bulleted) lists. - */ -showdown.subParser('lists', function (text, options, globals) { - 'use strict'; - - /** - * Process the contents of a single ordered or unordered list, splitting it - * into individual list items. - * @param {string} listStr - * @param {boolean} trimTrailing - * @returns {string} - */ - function processListItems (listStr, trimTrailing) { - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". - globals.gListLevel++; - - // trim trailing blank lines: - listStr = listStr.replace(/\n{2,}$/, '\n'); - - // attacklab: add sentinel to emulate \z - listStr += '~0'; - - var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, - isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr)); - - listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { - checked = (checked && checked.trim() !== ''); - var item = showdown.subParser('outdent')(m4, options, globals), - bulletStyle = ''; - - // Support for github tasklists - if (taskbtn && options.tasklists) { - bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; - item = item.replace(/^[ \t]*\[(x| )?]/m, function () { - var otp = ' -1)) { - item = showdown.subParser('githubCodeBlocks')(item, options, globals); - item = showdown.subParser('blockGamut')(item, options, globals); - } else { - // Recursion for sub-lists: - item = showdown.subParser('lists')(item, options, globals); - item = item.replace(/\n$/, ''); // chomp(item) - if (isParagraphed) { - item = showdown.subParser('paragraphs')(item, options, globals); - } else { - item = showdown.subParser('spanGamut')(item, options, globals); - } - } - item = '\n' + item + '
  • \n'; - return item; - }); - - // attacklab: strip sentinel - listStr = listStr.replace(/~0/g, ''); - - globals.gListLevel--; - - if (trimTrailing) { - listStr = listStr.replace(/\s+$/, ''); - } - - return listStr; - } - - /** - * Check and parse consecutive lists (better fix for issue #142) - * @param {string} list - * @param {string} listType - * @param {boolean} trimTrailing - * @returns {string} - */ - function parseConsecutiveLists(list, listType, trimTrailing) { - // check if we caught 2 or more consecutive lists by mistake - // we use the counterRgx, meaning if listType is UL we look for UL and vice versa - var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm, - subLists = [], - result = ''; - - if (list.search(counterRxg) !== -1) { - (function parseCL(txt) { - var pos = txt.search(counterRxg); - if (pos !== -1) { - // slice - result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n\n'; - - // invert counterType and listType - listType = (listType === 'ul') ? 'ol' : 'ul'; - counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm; - - //recurse - parseCL(txt.slice(pos)); - } else { - result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '\n\n'; - } - })(list); - for (var i = 0; i < subLists.length; ++i) { - - } - } else { - result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '\n\n'; - } - - return result; - } - - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += '~0'; - - // Re-usable pattern to match any entire ul or ol list: - var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - - if (globals.gListLevel) { - text = text.replace(wholeList, function (wholeMatch, list, m2) { - var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; - return parseConsecutiveLists(list, listType, true); - }); - } else { - wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g; - text = text.replace(wholeList, function (wholeMatch, m1, list, m3) { - - var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; - return parseConsecutiveLists(list, listType); - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; -}); - -/** - * Remove one level of line-leading tabs or spaces - */ -showdown.subParser('outdent', function (text) { - 'use strict'; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width - - // attacklab: clean up hack - text = text.replace(/~0/g, ''); - - return text; -}); - -/** - * - */ -showdown.subParser('paragraphs', function (text, options, globals) { - 'use strict'; - - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ''); - text = text.replace(/\n+$/g, ''); - - var grafs = text.split(/\n{2,}/g), - grafsOut = [], - end = grafs.length; // Wrap

    tags - - for (var i = 0; i < end; i++) { - var str = grafs[i]; - - // if this is an HTML marker, copy it - if (str.search(/~K(\d+)K/g) >= 0) { - grafsOut.push(str); - } else if (str.search(/\S/) >= 0) { - str = showdown.subParser('spanGamut')(str, options, globals); - str = str.replace(/^([ \t]*)/g, '

    '); - str += '

    '; - grafsOut.push(str); - } - } - - /** Unhashify HTML blocks */ - end = grafsOut.length; - for (i = 0; i < end; i++) { - // if this is a marker for an html block... - while (grafsOut[i].search(/~K(\d+)K/) >= 0) { - var blockText = globals.gHtmlBlocks[RegExp.$1]; - blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs - grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText); - } - } - - return grafsOut.join('\n\n'); -}); - -/** - * Run extension - */ -showdown.subParser('runExtension', function (ext, text, options, globals) { - 'use strict'; - - if (ext.filter) { - text = ext.filter(text, globals.converter, options); - - } else if (ext.regex) { - // TODO remove this when old extension loading mechanism is deprecated - var re = ext.regex; - if (!re instanceof RegExp) { - re = new RegExp(re, 'g'); - } - text = text.replace(re, ext.replace); - } - - return text; -}); - -/** - * These are all the transformations that occur *within* block-level - * tags like paragraphs, headers, and list items. - */ -showdown.subParser('spanGamut', function (text, options, globals) { - 'use strict'; - - text = showdown.subParser('codeSpans')(text, options, globals); - text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); - text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = showdown.subParser('images')(text, options, globals); - text = showdown.subParser('anchors')(text, options, globals); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = showdown.subParser('autoLinks')(text, options, globals); - text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); - text = showdown.subParser('italicsAndBold')(text, options, globals); - text = showdown.subParser('strikethrough')(text, options, globals); - - // Do hard breaks: - text = text.replace(/ +\n/g, '
    \n'); - - return text; - -}); - -showdown.subParser('strikethrough', function (text, options) { - 'use strict'; - - if (options.strikethrough) { - text = text.replace(/(?:~T){2}([^~]+)(?:~T){2}/g, '$1'); - } - - return text; -}); - -/** - * Strip any lines consisting only of spaces and tabs. - * This makes subsequent regexs easier to write, because we can - * match consecutive blank lines with /\n+/ instead of something - * contorted like /[ \t]*\n+/ - */ -showdown.subParser('stripBlankLines', function (text) { - 'use strict'; - return text.replace(/^[ \t]+$/mg, ''); -}); - -/** - * Strips link definitions from text, stores the URLs and titles in - * hash references. - * Link defs are in the form: ^[id]: url "optional title" - * - * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - * [ \t]* - * \n? // maybe *one* newline - * [ \t]* - * ? // url = $2 - * [ \t]* - * \n? // maybe one newline - * [ \t]* - * (?: - * (\n*) // any lines skipped = $3 attacklab: lookbehind removed - * ["(] - * (.+?) // title = $4 - * [")] - * [ \t]* - * )? // title is optional - * (?:\n+|$) - * /gm, - * function(){...}); - * - */ -showdown.subParser('stripLinkDefinitions', function (text, options, globals) { - 'use strict'; - - var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm; - - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += '~0'; - - text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) { - linkId = linkId.toLowerCase(); - globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive - - if (blankLines) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return blankLines + title; - - } else { - if (title) { - globals.gTitles[linkId] = title.replace(/"|'/g, '"'); - } - if (options.parseImgDimensions && width && height) { - globals.gDimensions[linkId] = { - width: width, - height: height - }; - } - } - // Completely remove the definition from the text - return ''; - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; -}); - -showdown.subParser('tables', function (text, options, globals) { - 'use strict'; - - var table = function () { - - var tables = {}, - filter; - - tables.th = function (header, style) { - var id = ''; - header = header.trim(); - if (header === '') { - return ''; - } - if (options.tableHeaderId) { - id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; - } - header = showdown.subParser('spanGamut')(header, options, globals); - if (!style || style.trim() === '') { - style = ''; - } else { - style = ' style="' + style + '"'; - } - return '' + header + ''; - }; - - tables.td = function (cell, style) { - var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals); - if (!style || style.trim() === '') { - style = ''; - } else { - style = ' style="' + style + '"'; - } - return '' + subText + ''; - }; - - tables.ths = function () { - var out = '', - i = 0, - hs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - for (i; i < hs.length; i += 1) { - out += tables.th(hs[i], style[i]) + '\n'; - } - - return out; - }; - - tables.tds = function () { - var out = '', - i = 0, - ds = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - for (i; i < ds.length; i += 1) { - out += tables.td(ds[i], style[i]) + '\n'; - } - return out; - }; - - tables.thead = function () { - var out, - hs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - out = '\n'; - out += '\n'; - out += tables.ths.apply(this, [hs, style]); - out += '\n'; - out += '\n'; - return out; - }; - - tables.tr = function () { - var out, - cs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - out = '\n'; - out += tables.tds.apply(this, [cs, style]); - out += '\n'; - return out; - }; - - filter = function (text) { - var i = 0, - lines = text.split('\n'), - line, - hs, - out = []; - - for (i; i < lines.length; i += 1) { - line = lines[i]; - // looks like a table heading - if (line.trim().match(/^[|].*[|]$/)) { - line = line.trim(); - - var tbl = [], - align = lines[i + 1].trim(), - styles = [], - j = 0; - - if (align.match(/^[|][-=|: ]+[|]$/)) { - styles = align.substring(1, align.length - 1).split('|'); - for (j = 0; j < styles.length; ++j) { - styles[j] = styles[j].trim(); - if (styles[j].match(/^[:][-=| ]+[:]$/)) { - styles[j] = 'text-align:center;'; - - } else if (styles[j].match(/^[-=| ]+[:]$/)) { - styles[j] = 'text-align:right;'; - - } else if (styles[j].match(/^[:][-=| ]+$/)) { - styles[j] = 'text-align:left;'; - } else { - styles[j] = ''; - } - } - } - tbl.push(''); - hs = line.substring(1, line.length - 1).split('|'); - - if (styles.length === 0) { - for (j = 0; j < hs.length; ++j) { - styles.push('text-align:left'); - } - } - tbl.push(tables.thead.apply(this, [hs, styles])); - line = lines[++i]; - if (!line.trim().match(/^[|][-=|: ]+[|]$/)) { - // not a table rolling back - line = lines[--i]; - } else { - line = lines[++i]; - tbl.push(''); - while (line.trim().match(/^[|].*[|]$/)) { - line = line.trim(); - tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles])); - line = lines[++i]; - } - tbl.push(''); - tbl.push('
    '); - // we are done with this table and we move along - out.push(tbl.join('\n')); - continue; - } - } - out.push(line); - } - return out.join('\n'); - }; - return {parse: filter}; - }; - - if (options.tables) { - var tableParser = table(); - return tableParser.parse(text); - } else { - return text; - } -}); - -/** - * Swap back in all the special characters we've hidden. - */ -showdown.subParser('unescapeSpecialChars', function (text) { - 'use strict'; - - text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - }); - return text; -}); - -var root = this; - -// CommonJS/nodeJS Loader -if (typeof module !== 'undefined' && module.exports) { - module.exports = showdown; - -// AMD Loader -} else if (typeof define === 'function' && define.amd) { - define('showdown', function () { - 'use strict'; - return showdown; - }); - -// Regular Browser loader -} else { - root.showdown = showdown; -} -}).call(this); -//# sourceMappingURL=showdown.js.map diff --git a/src/scripts/markdown/04_pagedown-extra.js b/src/scripts/markdown/04_pagedown-extra.js deleted file mode 100644 index 2a5e35f5..00000000 --- a/src/scripts/markdown/04_pagedown-extra.js +++ /dev/null @@ -1,874 +0,0 @@ -(function () { - // A quick way to make sure we're only keeping span-level tags when we need to. - // This isn't supposed to be foolproof. It's just a quick way to make sure we - // keep all span-level tags returned by a pagedown converter. It should allow - // all span-level tags through, with or without attributes. - 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'); - - /****************************************************************** - * Utility Functions * - *****************************************************************/ - - // patch for ie7 - 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, ''); - } - - // Remove one level of indentation from text. Indent is 4 spaces. - function outdent(text) { - return text.replace(new RegExp('^(\\t|[ ]{1,4})', 'gm'), ''); - } - - function contains(str, substr) { - return str.indexOf(substr) != -1; - } - - // Sanitize html, removing tags that aren't in the whitelist - function sanitizeHtml(html, whitelist) { - return html.replace(/<[^>]*>?/gi, function(tag) { - return tag.match(whitelist) ? tag : ''; - }); - } - - // Merge two arrays, keeping only unique elements. - 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; - } - - // JS regexes don't support \A or \Z, so we add sentinels, as Pagedown - // does. In this case, we add the ascii codes for start of text (STX) and - // end of text (ETX), an idea borrowed from: - // https://github.com/tanakahisateru/js-markdown-extra - function addAnchors(text) { - if(text.charAt(0) != '\x02') - text = '\x02' + text; - if(text.charAt(text.length - 1) != '\x03') - text = text + '\x03'; - return text; - } - - // Remove STX and ETX sentinels. - 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; - } - - // Convert markdown within an element, retaining only span-level tags - function convertSpans(text, extra) { - return sanitizeHtml(convertAll(text, extra), inlineTags); - } - - // Convert internal markdown using the stock pagedown converter - function convertAll(text, extra) { - var result = extra.blockGamutHookCallback(text); - // We need to perform these operations since we skip the steps in the converter - result = unescapeSpecialChars(result); - result = result.replace(/~D/g, "$$").replace(/~T/g, "~"); - result = extra.previousPostConversion(result); - return result; - } - - // Convert escaped special characters - function processEscapesStep1(text) { - // Markdown extra adds two escapable characters, `:` and `|` - return text.replace(/\\\|/g, '~I').replace(/\\:/g, '~i'); - } - function processEscapesStep2(text) { - return text.replace(/~I/g, '|').replace(/~i/g, ':'); - } - - // Duplicated from PageDown converter - function unescapeSpecialChars(text) { - // Swap back in all the special characters we've hidden. - 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 spaces with - - .replace(/[^\w\-]+/g, '') // Remove all non-word chars - .replace(/\-\-+/g, '-') // Replace multiple - with single - - .replace(/^-+/, '') // Trim - from start of text - .replace(/-+$/, ''); // Trim - from end of text - } - - /***************************************************************************** - * Markdown.Extra * - ****************************************************************************/ - - Markdown.Extra = function() { - // For converting internal markdown (in tables for instance). - // This is necessary since these methods are meant to be called as - // preConversion hooks, and the Markdown converter passed to init() - // won't convert any markdown contained in the html tags we return. - this.converter = null; - - // Stores html blocks we generate in hooks so that - // they're not destroyed if the user is using a sanitizing converter - this.hashBlocks = []; - - // Stores footnotes - this.footnotes = {}; - this.usedFootnotes = []; - - // Special attribute blocks for fenced code blocks and headers enabled. - this.attributeBlocks = false; - - // Fenced code block options - this.googleCodePrettify = false; - this.highlightJs = false; - - // Table options - this.tableClass = ''; - - this.tabWidth = 4; - }; - - Markdown.Extra.init = function(converter, options) { - // Each call to init creates a new instance of Markdown.Extra so it's - // safe to have multiple converters, with different options, on a single page - 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")) { - // This step will convert fcb inside list items and blockquotes - preBlockGamutTransformations.push("fencedCodeBlocks"); - // This extra step is to prevent html blocks hashing and link definition/footnotes stripping inside fcb - 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) { - // Keep a reference to the block gamut callback to run recursively - 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); - }); - - // Keep a reference to the hook chain running before doPostConversion to apply on hashed extra blocks - extra.previousPostConversion = converter.hooks.postConversion; - converter.hooks.chain("postConversion", function(text) { - text = extra.doTransform(postConversionTransformations, text); - // Clear state vars that may use unnecessary memory - 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; - } - - extra.converter = converter; - - // Caller usually won't need this, but it's handy for testing. - return extra; - }; - - // Do transformations - Markdown.Extra.prototype.doTransform = function(transformations, text) { - for(var i = 0; i < transformations.length; i++) - text = this[transformations[i]](text); - return text; - }; - - // Return a placeholder containing a key, which is the block's index in the - // hashBlocks array. We wrap our output in a

    tag here so Pagedown won't. - Markdown.Extra.prototype.hashExtraBlock = function(block) { - return '\n

    ~X' + (this.hashBlocks.push(block) - 1) + 'X

    \n'; - }; - Markdown.Extra.prototype.hashExtraInline = function(block) { - return '~X' + (this.hashBlocks.push(block) - 1) + 'X'; - }; - - // Replace placeholder blocks in `text` with their corresponding - // html blocks in the hashBlocks array. - Markdown.Extra.prototype.unHashExtraBlocks = function(text) { - var self = this; - function recursiveUnHash() { - var hasHash = false; - text = text.replace(/(?:

    )?~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; - }; - - // Wrap headers to make sure they won't be in def lists - 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; - }; - - - /****************************************************************** - * Attribute Blocks * - *****************************************************************/ - - // TODO: use sentinels. Should we just add/remove them in doConversion? - // TODO: better matches for id / class attributes - 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"); // underline lookahead - var fcbAttributes = new RegExp("^(```[^`\\n]*)[ \\t]+" + attrBlock + "[ \\t]*\\n" + - "(?=([\\s\\S]*?)\\n```[ \\t]*(\\n|0x03))", "gm"); - - // Extract headers attribute blocks, move them above the element they will be - // applied to, and hash them for later. - Markdown.Extra.prototype.hashHeaderAttributeBlocks = function(text) { - - var self = this; - function attributeCallback(wholeMatch, pre, attr) { - return '

    ~XX' + (self.hashBlocks.push(attr) - 1) + 'XX

    \n' + pre + "\n"; - } - - text = text.replace(hdrAttributesA, attributeCallback); // ## headers - text = text.replace(hdrAttributesB, attributeCallback); // underline headers - return text; - }; - - // Extract FCB attribute blocks, move them above the element they will be - // applied to, and hash them for later. - Markdown.Extra.prototype.hashFcbAttributeBlocks = function(text) { - // TODO: use sentinels. Should we just add/remove them in doConversion? - // TODO: better matches for id / class attributes - - var self = this; - function attributeCallback(wholeMatch, pre, attr) { - return '

    ~XX' + (self.hashBlocks.push(attr) - 1) + 'XX

    \n' + pre + "\n"; - } - - return text.replace(fcbAttributes, attributeCallback); - }; - - Markdown.Extra.prototype.applyAttributeBlocks = function(text) { - var self = this; - var blockRe = new RegExp('

    ~XX(\\d+)XX

    [\\s]*' + - '(?:<(h[1-6]|pre)(?: +class="(\\S+)")?(>[\\s\\S]*?))', "gm"); - text = text.replace(blockRe, function(wholeMatch, k, tag, cls, rest) { - if (!tag) // no following header or fenced code block. - return ''; - - // get attributes list from hash - var key = parseInt(k, 10); - var attributes = self.hashBlocks[key]; - - // get id - var id = attributes.match(/#[^\s#.]+/g) || []; - var idStr = id[0] ? ' id="' + id[0].substr(1, id[0].length - 1) + '"' : ''; - - // get classes and merge with existing classes - var classes = attributes.match(/\.[^\s#.]+/g) || []; - for (var i = 0; i < classes.length; i++) // Remove leading dot - 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; - }; - - /****************************************************************** - * Tables * - *****************************************************************/ - - // Find and convert Markdown Extra tables into html. - Markdown.Extra.prototype.tables = function(text) { - var self = this; - - var leadingPipe = new RegExp( - ['^' , - '[ ]{0,3}' , // Allowed whitespace - '[|]' , // Initial pipe - '(.+)\\n' , // $1: Header Row - - '[ ]{0,3}' , // Allowed whitespace - '[|]([ ]*[-:]+[-| :]*)\\n' , // $2: Separator - - '(' , // $3: Table Body - '(?:[ ]*[|].*\\n?)*' , // Table rows - ')', - '(?:\\n|$)' // Stop at final newline - ].join(''), - 'gm' - ); - - var noLeadingPipe = new RegExp( - ['^' , - '[ ]{0,3}' , // Allowed whitespace - '(\\S.*[|].*)\\n' , // $1: Header Row - - '[ ]{0,3}' , // Allowed whitespace - '([-:]+[ ]*[|][-| :]*)\\n' , // $2: Separator - - '(' , // $3: Table Body - '(?:.*[|].*\\n?)*' , // Table rows - ')' , - '(?:\\n|$)' // Stop at final newline - ].join(''), - 'gm' - ); - - text = text.replace(leadingPipe, doTable); - text = text.replace(noLeadingPipe, doTable); - - // $1 = header, $2 = separator, $3 = body - function doTable(match, header, separator, body, offset, string) { - // remove any leading pipes and whitespace - header = header.replace(/^ *[|]/m, ''); - separator = separator.replace(/^ *[|]/m, ''); - body = body.replace(/^ *[|]/gm, ''); - - // remove trailing pipes and whitespace - header = header.replace(/[|] *$/m, ''); - separator = separator.replace(/[|] *$/m, ''); - body = body.replace(/[|] *$/gm, ''); - - // determine column alignments - var alignspecs = separator.split(/ *[|] */); - var 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] = ''; - } - - // TODO: parse spans in header and rows before splitting, so that pipes - // inside of tags are not interpreted as separators - var headers = header.split(/ *[|] */); - var colCount = headers.length; - - // build html - var cls = self.tableClass ? ' class="' + self.tableClass + '"' : ''; - var html = ['\n', '\n', '\n'].join(''); - - // build column headers. - for (i = 0; i < colCount; i++) { - var headerHtml = convertSpans(trim(headers[i]), self); - html += [" ", headerHtml, "\n"].join(''); - } - html += "\n\n"; - - // build rows - var rows = body.split('\n'); - for (i = 0; i < rows.length; i++) { - if (rows[i].match(/^\s*$/)) // can apply to final row - continue; - - // ensure number of rowCells matches colCount - var rowCells = rows[i].split(/ *[|] */); - var lenDiff = colCount - rowCells.length; - for (var j = 0; j < lenDiff; j++) - rowCells.push(''); - - html += "\n"; - for (j = 0; j < colCount; j++) { - var colHtml = convertSpans(trim(rowCells[j]), self); - html += [" ", colHtml, "\n"].join(''); - } - html += "\n"; - } - - html += "\n"; - - // replace html with placeholder until postConversion step - return self.hashExtraBlock(html); - } - - return text; - }; - - - /****************************************************************** - * Footnotes * - *****************************************************************/ - - // Strip footnote, store in hashes. - 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; - }; - - - // Find and convert footnotes references. - 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 = self.footnotes[id]; - if (footnote === undefined) { - return wholeMatch; - } - footnoteCounter++; - self.usedFootnotes.push(id); - var html = '' + footnoteCounter - + ''; - return self.hashExtraInline(html); - }); - - return text; - }; - - // Print footnotes at the end of the document - Markdown.Extra.prototype.printFootnotes = function(text) { - var self = this; - - if (self.usedFootnotes.length === 0) { - return text; - } - - text += '\n\n
    \n
    \n
      \n\n'; - for(var i=0; i' - + formattedfootnote - + ' \n\n'; - } - text += '
    \n
    '; - return text; - }; - - - /****************************************************************** - * Fenced Code Blocks (gfm) * - ******************************************************************/ - - // Find and convert gfm-inspired fenced code blocks into html. - Markdown.Extra.prototype.fencedCodeBlocks = function(text) { - function encodeCode(code) { - code = code.replace(/&/g, "&"); - code = code.replace(//g, ">"); - // These were escaped by PageDown before postNormalization - 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; - - // adhere to specified options - var preclass = self.googleCodePrettify ? ' class="prettyprint"' : ''; - var codeclass = ''; - if (language) { - if (self.googleCodePrettify || self.highlightJs) { - // use html5 language- class names. supported by both prettify and highlight.js - codeclass = ' class="language-' + language + '"'; - } else { - codeclass = ' class="' + language + '"'; - } - } - - var html = ['', - encodeCode(codeblock), '
    '].join(''); - - // replace codeblock with placeholder until postConversion step - return self.hashExtraBlock(html); - }); - - return text; - }; - - - /****************************************************************** - * SmartyPants * - ******************************************************************/ - - Markdown.Extra.prototype.educatePants = function(text) { - var self = this; - var result = ''; - var blockOffset = 0; - // Here we parse HTML in a very bad manner - text.replace(/(?:)|(<)([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) { - // Skip commentary - result += wholeMatch; - return; - } - // Skip special tags - 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) { - // Dashes - text = text.replace(/---/g, "—").replace(/--/g, "–"); - // Ellipses - text = text.replace(/\.\.\./g, "…").replace(/\.\s\.\s\./g, "…"); - // Backticks - text = text.replace(/``/g, "“").replace (/''/g, "”"); - - if(/^'$/.test(text)) { - // Special case: single-character ' token - if(/\S/.test(this.smartyPantsLastChar)) { - return "’"; - } - return "‘"; - } - if(/^"$/.test(text)) { - // Special case: single-character " token - if(/\S/.test(this.smartyPantsLastChar)) { - return "”"; - } - return "“"; - } - - // Special case if the very first character is a quote - // followed by punctuation at a non-word-break. Close the quotes by brute force: - text = text.replace (/^'(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/, "’"); - text = text.replace (/^"(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/, "”"); - - // Special case for double sets of quotes, e.g.: - //

    He said, "'Quoted' words in a larger quote."

    - text = text.replace(/"'(?=\w)/g, "“‘"); - text = text.replace(/'"(?=\w)/g, "‘“"); - - // Special case for decade abbreviations (the '80s): - text = text.replace(/'(?=\d{2}s)/g, "’"); - - // Get most opening single quotes: - text = text.replace(/(\s| |--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)'(?=\w)/g, "$1‘"); - - // Single closing quotes: - text = text.replace(/([^\s\[\{\(\-])'/g, "$1’"); - text = text.replace(/'(?=\s|s\b)/g, "’"); - - // Any remaining single quotes should be opening ones: - text = text.replace(/'/g, "‘"); - - // Get most opening double quotes: - text = text.replace(/(\s| |--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)"(?=\w)/g, "$1“"); - - // Double closing quotes: - text = text.replace(/([^\s\[\{\(\-])"/g, "$1”"); - text = text.replace(/"(?=\s)/g, "”"); - - // Any remaining quotes should be opening ones. - text = text.replace(/"/ig, "“"); - return text; - }; - - // Find and convert markdown extra definition lists into html. - Markdown.Extra.prototype.runSmartyPants = function(text) { - this.smartyPantsLastChar = ''; - text = this.educatePants(text); - // Clean everything inside html tags (some of them may have been converted due to our rough html parsing) - text = text.replace(/(<([a-zA-Z1-6]+)\b([^\n>]*?)(\/)?>)/g, revertPants); - return text; - }; - - /****************************************************************** - * Definition Lists * - ******************************************************************/ - - // Find and convert markdown extra definition lists into html. - Markdown.Extra.prototype.definitionLists = function(text) { - var wholeList = new RegExp( - ['(\\x02\\n?|\\n\\n)' , - '(?:' , - '(' , // $1 = whole list - '(' , // $2 - '[ ]{0,3}' , - '((?:[ \\t]*\\S.*\\n)+)', // $3 = defined term - '\\n?' , - '[ ]{0,3}:[ ]+' , // colon starting definition - ')' , - '([\\s\\S]+?)' , - '(' , // $4 - '(?=\\0x03)' , // \z - '|' , - '(?=' , - '\\n{2,}' , - '(?=\\S)' , - '(?!' , // Negative lookahead for another term - '[ ]{0,3}' , - '(?:\\S.*\\n)+?' , // defined term - '\\n?' , - '[ ]{0,3}:[ ]+' , // colon starting definition - ')' , - '(?!' , // Negative lookahead for another definition - '[ ]{0,3}:[ ]+' , // colon starting definition - ')' , - ')' , - ')' , - ')' , - ')' - ].join(''), - 'gm' - ); - - var self = this; - text = addAnchors(text); - - text = text.replace(wholeList, function(match, pre, list) { - var result = trim(self.processDefListItems(list)); - result = "
    \n" + result + "\n
    "; - return pre + self.hashExtraBlock(result) + "\n\n"; - }); - - return removeAnchors(text); - }; - - // Process the contents of a single definition list, splitting it - // into individual term and definition list items. - Markdown.Extra.prototype.processDefListItems = function(listStr) { - var self = this; - - var dt = new RegExp( - ['(\\x02\\n?|\\n\\n+)' , // leading line - '(' , // definition terms = $1 - '[ ]{0,3}' , // leading whitespace - '(?![:][ ]|[ ])' , // negative lookahead for a definition - // mark (colon) or more whitespace - '(?:\\S.*\\n)+?' , // actual term (not whitespace) - ')' , - '(?=\\n?[ ]{0,3}:[ ])' // lookahead for following line feed - ].join(''), // with a definition mark - 'gm' - ); - - var dd = new RegExp( - ['\\n(\\n+)?' , // leading line = $1 - '(' , // marker space = $2 - '[ ]{0,3}' , // whitespace before colon - '[:][ ]+' , // definition mark (colon) - ')' , - '([\\s\\S]+?)' , // definition text = $3 - '(?=\\n*' , // stop at next definition mark, - '(?:' , // next term or end of text - '\\n[ ]{0,3}[:][ ]|' , - '
    |\\x03' , // \z - ')' , - ')' - ].join(''), - 'gm' - ); - - listStr = addAnchors(listStr); - // trim trailing blank lines: - listStr = listStr.replace(/\n{2,}(?=\\x03)/, "\n"); - - // Process definition terms. - 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]; - // process spans inside dt - term = convertSpans(trim(term), self); - text += "\n
    " + term + "
    "; - } - return text + "\n"; - }); - - // Process actual definitions. - listStr = listStr.replace(dd, function(match, leadingLine, markerSpace, def) { - if (leadingLine || def.match(/\n{2,}/)) { - // replace marker with the appropriate whitespace indentation - def = Array(markerSpace.length + 1).join(' ') + def; - // process markdown inside definition - // TODO?: currently doesn't apply extensions - def = outdent(def) + "\n\n"; - def = "\n" + convertAll(def, self) + "\n"; - } else { - // convert span-level markdown inside definition - def = rtrim(def); - def = convertSpans(outdent(def), self); - } - - return "\n
    " + def + "
    \n"; - }); - - return removeAnchors(listStr); - }; - - - /*********************************************************** - * Strikethrough * - ************************************************************/ - - Markdown.Extra.prototype.strikethrough = function(text) { - // Pretty much duplicated from _DoItalicsAndBold - return text.replace(/([\W_]|^)~T~T(?=\S)([^\r]*?\S[\*_]*)~T~T([\W_]|$)/g, - "$1$2$3"); - }; - - - /*********************************************************** - * New lines * - ************************************************************/ - - Markdown.Extra.prototype.newlines = function(text) { - // We have to ignore already converted newlines and line breaks in sub-list items - return text.replace(/(<(?:br|\/li)>)?\n/g, function(wholeMatch, previousTag) { - return previousTag ? wholeMatch : "
    \n"; - }); - }; - -})(); - diff --git a/src/templates/layout.pug b/src/templates/layout.pug index fdfd8d52..e5bb9c05 100644 --- a/src/templates/layout.pug +++ b/src/templates/layout.pug @@ -42,7 +42,6 @@ html(lang="en") loadCSS( "//fonts.googleapis.com/css?family=Roboto:300,400" ); - script(src="{{ url_for('static_pillar', filename='assets/js/markdown.min.js') }}") script(src="{{ url_for('static_pillar', filename='assets/js/tutti.min.js') }}") link(href="{{ url_for('static', filename='assets/img/favicon.png') }}", rel="shortcut icon")