tags around block-level tags. text = showdown.subParser('hashHTMLBlocks')(text, options, globals); text = showdown.subParser('paragraphs')(text, options, globals); text = globals.converter._dispatch('blockGamut.after', text, options, globals); return text; }); showdown.subParser('blockQuotes', function (text, options, globals) { 'use strict'; text = globals.converter._dispatch('blockQuotes.before', text, options, globals); // add a couple extra lines after the text and endtext mark text = text + '\n\n'; var rgx = /(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm; if (options.splitAdjacentBlockquotes) { rgx = /^ {0,3}>[\s\S]*?(?:\n\n)/gm; } text = text.replace(rgx, function (bq) { // attacklab: hack around Konqueror 3.5.4 bug: // "----------bug".replace(/^-/g,"") == "bug" bq = bq.replace(/^[ \t]*>[ \t]?/gm, ''); // 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);
});
text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
return text;
});
/**
* Process Markdown `` blocks.
*/
showdown.subParser('codeBlocks', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
// 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, options, globals);
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(codeblock, options, globals);
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;
});
// strip sentinel
text = text.replace(/¨0/, '');
text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
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, options, globals) {
'use strict';
text = globals.converter._dispatch('codeSpans.before', text, options, globals);
if (typeof text === 'undefined') {
text = '';
}
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, options, globals);
c = m1 + '' + c + '';
c = showdown.subParser('hashHTMLSpans')(c, options, globals);
return c;
}
);
text = globals.converter._dispatch('codeSpans.after', text, options, globals);
return text;
});
/**
* Create a full HTML document from the processed markdown
*/
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
'use strict';
if (!options.completeHTMLDocument) {
return text;
}
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
var doctype = 'html',
doctypeParsed = '\n',
title = '',
charset = '\n',
lang = '',
metadata = '';
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
doctypeParsed = '\n';
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
if (doctype === 'html' || doctype === 'html5') {
charset = '';
}
}
for (var meta in globals.metadata.parsed) {
if (globals.metadata.parsed.hasOwnProperty(meta)) {
switch (meta.toLowerCase()) {
case 'doctype':
break;
case 'title':
title = '' + globals.metadata.parsed.title + ' \n';
break;
case 'charset':
if (doctype === 'html' || doctype === 'html5') {
charset = '\n';
} else {
charset = '\n';
}
break;
case 'language':
case 'lang':
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
metadata += '\n';
break;
default:
metadata += '\n';
}
}
}
text = doctypeParsed + '\n\n' + title + charset + metadata + '\n\n' + text.trim() + '\n\n';
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
return text;
});
/**
* Convert all tabs to spaces
*/
showdown.subParser('detab', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('detab.before', text, options, globals);
// 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, '');
text = globals.converter._dispatch('detab.after', text, options, globals);
return text;
});
showdown.subParser('ellipsis', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('ellipsis.before', text, options, globals);
//text = text.replace(/\.\.\./g, '…');
text = globals.converter._dispatch('ellipsis.after', text, options, globals);
return text;
});
/**
* Turn emoji codes into emojis
*
* List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis
*/
showdown.subParser('emoji', function (text, options, globals) {
'use strict';
if (!options.emoji) {
return text;
}
text = globals.converter._dispatch('emoji.before', text, options, globals);
var emojiRgx = /:([\S]+?):/g;
text = text.replace(emojiRgx, function (wm, emojiCode) {
if (showdown.helper.emojis.hasOwnProperty(emojiCode)) {
return showdown.helper.emojis[emojiCode];
}
return wm;
});
text = globals.converter._dispatch('emoji.after', text, options, globals);
return text;
});
/**
* Smart processing for ampersands and angle brackets that need to be encoded.
*/
showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('encodeAmpsAndAngles.before', text, options, globals);
// 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, '<');
// Encode <
text = text.replace(/
text = text.replace(/>/g, '>');
text = globals.converter._dispatch('encodeAmpsAndAngles.after', text, options, globals);
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, options, globals) {
'use strict';
text = globals.converter._dispatch('encodeBackslashEscapes.before', text, options, globals);
text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
text = text.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('encodeBackslashEscapes.after', text, options, globals);
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, options, globals) {
'use strict';
text = globals.converter._dispatch('encodeCode.before', text, options, globals);
// Encode all ampersands; HTML entities are not
// entities within a Markdown code span.
text = text
.replace(/&/g, '&')
// Do the angle bracket song and dance:
.replace(//g, '>')
// Now, escape characters that are magic in Markdown:
.replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('encodeCode.after', text, options, globals);
return text;
});
/**
* 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, options, globals) {
'use strict';
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.before', text, options, globals);
// Build a regex to find HTML tags.
var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
comments = /-]|-[^>])(?:[^-]|-[^-])*)--)>/gi;
text = text.replace(tags, function (wholeMatch) {
return wholeMatch
.replace(/(.)<\/?code>(?=.)/g, '$1`')
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
});
text = text.replace(comments, function (wholeMatch) {
return wholeMatch
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
});
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.after', text, options, globals);
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 = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
text += '¨0';
text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) {
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
// First parse the github code block
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(codeblock, options, globals);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
codeblock = '' + codeblock + end + '
';
codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
// Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var,
// and then return a token
return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
});
// attacklab: strip sentinel
text = text.replace(/¨0/, '');
return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
});
showdown.subParser('hashBlock', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('hashBlock.before', text, options, globals);
text = text.replace(/(^\n+|\n+$)/g, '');
text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
text = globals.converter._dispatch('hashBlock.after', text, options, globals);
return text;
});
/**
* Hash and escape elements that should not be parsed as markdown
*/
showdown.subParser('hashCodeTags', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('hashCodeTags.before', text, options, globals);
var repFunc = function (wholeMatch, match, left, right) {
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
};
// Hash naked
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, ']*>', '', 'gim');
text = globals.converter._dispatch('hashCodeTags.after', text, options, globals);
return text;
});
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';
text = globals.converter._dispatch('hashHTMLBlocks.before', text, options, globals);
var blockTags = [
'pre',
'div',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'blockquote',
'table',
'dl',
'ol',
'ul',
'script',
'noscript',
'form',
'fieldset',
'iframe',
'math',
'style',
'section',
'header',
'footer',
'nav',
'article',
'aside',
'address',
'audio',
'canvas',
'figure',
'hgroup',
'output',
'video',
'p'
],
repFunc = function (wholeMatch, match, left, right) {
var txt = wholeMatch;
// check if this html element is marked as markdown
// if so, it's contents should be parsed as markdown
if (left.search(/\bmarkdown\b/) !== -1) {
txt = left + globals.converter.makeHtml(match) + right;
}
return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
};
if (options.backslashEscapesHTMLTags) {
// encode backslash escaped HTML tags
text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) {
return '<' + inside + '>';
});
}
// hash HTML Blocks
for (var i = 0; i < blockTags.length; ++i) {
var opTagPos,
rgx1 = new RegExp('^ {0,3}(<' + blockTags[i] + '\\b[^>]*>)', 'im'),
patLeft = '<' + blockTags[i] + '\\b[^>]*>',
patRight = '' + blockTags[i] + '>';
// 1. Look for the first position of the first opening HTML tag in the text
while ((opTagPos = showdown.helper.regexIndexOf(text, rgx1)) !== -1) {
// if the HTML tag is \ escaped, we need to escape it and break
//2. Split the text in that position
var subTexts = showdown.helper.splitAtIndex(text, opTagPos),
//3. Match recursively
newSubText1 = showdown.helper.replaceRecursiveRegExp(subTexts[1], repFunc, patLeft, patRight, 'im');
// prevent an infinite loop
if (newSubText1 === subTexts[1]) {
break;
}
text = subTexts[0].concat(newSubText1);
}
}
// HR SPECIAL CASE
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 = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
}, '^ {0,3}', 'gm');
// PHP and ASP-style processor instructions (...?> and <%...%>)
text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
showdown.subParser('hashElement')(text, options, globals));
text = globals.converter._dispatch('hashHTMLBlocks.after', text, options, globals);
return text;
});
/**
* Hash span elements that should not be parsed as markdown
*/
showdown.subParser('hashHTMLSpans', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('hashHTMLSpans.before', text, options, globals);
function hashHTMLSpan (html) {
return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
}
// Hash Self Closing tags
text = text.replace(/<[^>]+?\/>/gi, function (wm) {
return hashHTMLSpan(wm);
});
// Hash tags without properties
text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
return hashHTMLSpan(wm);
});
// Hash tags with properties
text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
return hashHTMLSpan(wm);
});
// Hash self closing tags without />
text = text.replace(/<[^>]+?>/gi, function (wm) {
return hashHTMLSpan(wm);
});
/*showdown.helper.matchRecursiveRegExp(text, ']*>', '', 'gi');*/
text = globals.converter._dispatch('hashHTMLSpans.after', text, options, globals);
return text;
});
/**
* Unhash HTML spans
*/
showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
var repText = globals.gHtmlSpans[i],
// limiter to prevent infinite loop (assume 10 as limit for recurse)
limit = 0;
while (/¨C(\d+)C/.test(repText)) {
var num = RegExp.$1;
repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
if (limit === 10) {
console.error('maximum nesting of 10 spans reached!!!');
break;
}
++limit;
}
text = text.replace('¨C' + i + 'C', repText);
}
text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
return text;
});
/**
* Hash and escape elements that should not be parsed as markdown
*/
showdown.subParser('hashPreCodeTags', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('hashPreCodeTags.before', text, options, globals);
var repFunc = function (wholeMatch, match, left, right) {
// encode html entities
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
};
// Hash
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}]*>\\s*]*>', '^ {0,3}\\s*
', 'gim');
text = globals.converter._dispatch('hashPreCodeTags.after', text, options, globals);
return text;
});
showdown.subParser('headers', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('headers.before', text, options, globals);
var 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
//
var atxStyle = (options.requireSpaceBeforeHeadingText) ? /^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm : /^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;
text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
var hText = m2;
if (options.customizedHeaderId) {
hText = m2.replace(/\s?\{([^{]+?)}\s*$/, '');
}
var span = showdown.subParser('spanGamut')(hText, 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,
prefix;
// It is separate from other options to allow combining prefix and customized
if (options.customizedHeaderId) {
var match = m.match(/\{([^{]+?)}\s*$/);
if (match && match[1]) {
m = match[1];
}
}
title = m;
// Prefix id to prevent causing inadvertent pre-existing style matches.
if (showdown.helper.isString(options.prefixHeaderId)) {
prefix = options.prefixHeaderId;
} else if (options.prefixHeaderId === true) {
prefix = 'section-';
} else {
prefix = '';
}
if (!options.rawPrefixHeaderId) {
title = prefix + title;
}
if (options.ghCompatibleHeaderId) {
title = title
.replace(/ /g, '-')
// replace previously escaped chars (&, ¨ and $)
.replace(/&/g, '')
.replace(/¨T/g, '')
.replace(/¨D/g, '')
// replace rest of the chars (&~$ are repeated as they might have been escaped)
// borrowed from github's redcarpet (some they should produce similar results)
.replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, '')
.toLowerCase();
} else if (options.rawHeaderId) {
title = title
.replace(/ /g, '-')
// replace previously escaped chars (&, ¨ and $)
.replace(/&/g, '&')
.replace(/¨T/g, '¨')
.replace(/¨D/g, '$')
// replace " and '
.replace(/["']/g, '-')
.toLowerCase();
} else {
title = title
.replace(/[^\w]/g, '')
.toLowerCase();
}
if (options.rawPrefixHeaderId) {
title = prefix + title;
}
if (globals.hashLinkCounts[title]) {
title = title + '-' + (globals.hashLinkCounts[title]++);
} else {
globals.hashLinkCounts[title] = 1;
}
return title;
}
text = globals.converter._dispatch('headers.after', text, options, globals);
return text;
});
/**
* Turn Markdown link shortcuts into XHTML tags.
*/
showdown.subParser('horizontalRule', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('horizontalRule.before', text, options, globals);
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 = globals.converter._dispatch('horizontalRule.after', text, options, globals);
return text;
});
/**
* Turn Markdown image shortcuts into
tags.
*/
showdown.subParser('images', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('images.before', text, options, globals);
var inlineRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
crazyRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
base64RegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,
refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) {
url = url.replace(/\s/g, '');
return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
}
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 = '';
}
// Special case for explicit empty url
if (wholeMatch.search(/\(\s*>? ?(['"].*['"])?\)$/m) > -1) {
url = '';
} else 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);
.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
//url = showdown.helper.escapeCharacters(url, '*_', false);
url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
var result = '
';
return result;
}
// First, handle reference-style labeled images: ![alt text][id]
text = text.replace(referenceRegExp, writeImageTag);
// Next, handle inline images: 
// base64 encoded images
text = text.replace(base64RegExp, writeImageTagBase64);
// cases with crazy urls like ./image/cat1).png
text = text.replace(crazyRegExp, writeImageTag);
// normal cases
text = text.replace(inlineRegExp, writeImageTag);
// handle reference-style shortcuts: ![img text]
text = text.replace(refShortcutRegExp, writeImageTag);
text = globals.converter._dispatch('images.after', text, options, globals);
return text;
});
showdown.subParser('italicsAndBold', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
// it's faster to have 3 separate regexes for each case than have just one
// because of backtracing, in some cases, it could lead to an exponential effect
// called "catastrophic backtrace". Ominous!
function parseInside (txt, left, right) {
/*
if (options.simplifiedAutoLink) {
txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
}
*/
return left + txt + right;
}
// Parse underscores
if (options.literalMidWordUnderscores) {
text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
return parseInside (txt, '', '');
});
text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
return parseInside (txt, '', '');
});
text = text.replace(/\b_(\S[\s\S]*?)_\b/g, function (wm, txt) {
return parseInside (txt, '', '');
});
} else {
text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
text = text.replace(/_([^\s_][\s\S]*?)_/g, function (wm, m) {
// !/^_[^_]/.test(m) - test if it doesn't start with __ (since it seems redundant, we removed it)
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
}
// Now parse asterisks
if (options.literalMidWordAsterisks) {
text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) {
return parseInside (txt, lead + '', '');
});
text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g, function (wm, lead, txt) {
return parseInside (txt, lead + '', '');
});
text = text.replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g, function (wm, lead, txt) {
return parseInside (txt, lead + '', '');
});
} else {
text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
// !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
return (/\S$/.test(m)) ? parseInside (m, '', '') : wm;
});
}
text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
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)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
// Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
// which is a syntax breaking change
// activating this option reverts to old behavior
if (options.disableForced4SpacesIndentedSublists) {
rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
}
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|X| )?]/m, function () {
var otp = '';
return otp;
});
}
// ISSUE #312
// This input: - - - a
// causes trouble to the parser, since it interprets it as:
// - a
// instead of:
// - - - a
// So, to prevent it, we will put a marker (¨A)in the beginning of the line
// Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser
item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) {
return '¨A' + wm2;
});
// m1 - Leading line or
// Has a double return (multi paragraph) or
// Has sublist
if (m1 || (item.search(/\n{2,}/) > -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)
item = showdown.subParser('hashHTMLBlocks')(item, options, globals);
// Colapse double linebreaks
item = item.replace(/\n\n+/g, '\n\n');
if (isParagraphed) {
item = showdown.subParser('paragraphs')(item, options, globals);
} else {
item = showdown.subParser('spanGamut')(item, options, globals);
}
}
// now we need to remove the marker (¨A)
item = item.replace('¨A', '');
// we can finally wrap the line in list item tags
item = '' + item + ' \n';
return item;
});
// attacklab: strip sentinel
listStr = listStr.replace(/¨0/g, '');
globals.gListLevel--;
if (trimTrailing) {
listStr = listStr.replace(/\s+$/, '');
}
return listStr;
}
function styleStartNumber (list, listType) {
// check if ol and starts by a number different than 1
if (listType === 'ol') {
var res = list.match(/^ *(\d+)\./);
if (res && res[1] !== '1') {
return ' start="' + res[1] + '"';
}
}
return '';
}
/**
* 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 OL and vice versa
var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
counterRxg = (listType === 'ul') ? olRgx : ulRgx,
result = '';
if (list.search(counterRxg) !== -1) {
(function parseCL (txt) {
var pos = txt.search(counterRxg),
style = styleStartNumber(list, listType);
if (pos !== -1) {
// slice
result += '\n\n<' + listType + style + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '' + listType + '>\n';
// invert counterType and listType
listType = (listType === 'ul') ? 'ol' : 'ul';
counterRxg = (listType === 'ul') ? olRgx : ulRgx;
//recurse
parseCL(txt.slice(pos));
} else {
result += '\n\n<' + listType + style + '>\n' + processListItems(txt, !!trimTrailing) + '' + listType + '>\n';
}
})(list);
} else {
var style = styleStartNumber(list, listType);
result = '\n\n<' + listType + style + '>\n' + processListItems(list, !!trimTrailing) + '' + listType + '>\n';
}
return result;
}
/** Start of list parsing **/
text = globals.converter._dispatch('lists.before', text, options, globals);
// add sentinel to hack around khtml/safari bug:
// http://bugs.webkit.org/show_bug.cgi?id=11231
text += '¨0';
if (globals.gListLevel) {
text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
function (wholeMatch, list, m2) {
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
return parseConsecutiveLists(list, listType, true);
}
);
} else {
text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
function (wholeMatch, m1, list, m3) {
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
return parseConsecutiveLists(list, listType, false);
}
);
}
// strip sentinel
text = text.replace(/¨0/, '');
text = globals.converter._dispatch('lists.after', text, options, globals);
return text;
});
/**
* Parse metadata at the top of the document
*/
showdown.subParser('metadata', function (text, options, globals) {
'use strict';
if (!options.metadata) {
return text;
}
text = globals.converter._dispatch('metadata.before', text, options, globals);
function parseMetadataContents (content) {
// raw is raw so it's not changed in any way
globals.metadata.raw = content;
// escape chars forbidden in html attributes
// double quotes
content = content
// ampersand first
.replace(/&/g, '&')
// double quotes
.replace(/"/g, '"');
content = content.replace(/\n {4}/g, ' ');
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
globals.metadata.parsed[key] = value;
return '';
});
}
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
if (format) {
globals.metadata.format = format;
}
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/¨M/g, '');
text = globals.converter._dispatch('metadata.after', text, options, globals);
return text;
});
/**
* Remove one level of line-leading tabs or spaces
*/
showdown.subParser('outdent', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('outdent.before', text, options, globals);
// 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, '');
text = globals.converter._dispatch('outdent.after', text, options, globals);
return text;
});
/**
*
*/
showdown.subParser('paragraphs', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('paragraphs.before', text, options, globals);
// 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|G)(\d+)\1/g) >= 0) {
grafsOut.push(str);
// test for presence of characters to prevent empty lines being parsed
// as paragraphs (resulting in undesired extra empty paragraphs)
} 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++) {
var blockText = '',
grafsOutIt = grafsOut[i],
codeFlag = false;
// if this is a marker for an html block...
// use RegExp.test instead of string.search because of QML bug
while (/¨(K|G)(\d+)\1/.test(grafsOutIt)) {
var delim = RegExp.$1,
num = RegExp.$2;
if (delim === 'K') {
blockText = globals.gHtmlBlocks[num];
} else {
// we need to check if ghBlock is a false positive
if (codeFlag) {
// use encoded version of all text
blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text, options, globals);
} else {
blockText = globals.ghCodeBlocks[num].codeblock;
}
}
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOutIt = grafsOutIt.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, blockText);
// Check if grafsOutIt is a pre->code
if (/^]*>\s*]*>/.test(grafsOutIt)) {
codeFlag = true;
}
}
grafsOut[i] = grafsOutIt;
}
text = grafsOut.join('\n');
// Strip leading and trailing lines:
text = text.replace(/^\n+/g, '');
text = text.replace(/\n+$/g, '');
return globals.converter._dispatch('paragraphs.after', text, options, globals);
});
/**
* 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 = globals.converter._dispatch('spanGamut.before', text, options, globals);
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 anchors, because you can use < and >
// delimiters in inline links like [this]().
text = showdown.subParser('autoLinks')(text, options, globals);
text = showdown.subParser('simplifiedAutoLinks')(text, options, globals);
text = showdown.subParser('emoji')(text, options, globals);
text = showdown.subParser('underline')(text, options, globals);
text = showdown.subParser('italicsAndBold')(text, options, globals);
text = showdown.subParser('strikethrough')(text, options, globals);
text = showdown.subParser('ellipsis')(text, options, globals);
// we need to hash HTML tags inside spans
text = showdown.subParser('hashHTMLSpans')(text, options, globals);
// now we encode amps and angles
text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
// Do hard breaks
if (options.simpleLineBreaks) {
// GFM style hard breaks
// only add line breaks if the text does not contain a block (special case for lists)
if (!/\n\n¨K/.test(text)) {
text = text.replace(/\n+/g, '
\n');
}
} else {
// Vanilla hard breaks
text = text.replace(/ +\n/g, '
\n');
}
text = globals.converter._dispatch('spanGamut.after', text, options, globals);
return text;
});
showdown.subParser('strikethrough', function (text, options, globals) {
'use strict';
function parseInside (txt) {
if (options.simplifiedAutoLink) {
txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
}
return '' + txt + '';
}
if (options.strikethrough) {
text = globals.converter._dispatch('strikethrough.before', text, options, globals);
text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return parseInside(txt); });
text = globals.converter._dispatch('strikethrough.after', text, options, globals);
}
return text;
});
/**
* Strips link definitions from text, stores the URLs and titles in
* hash references.
* Link defs are in the form: ^[id]: url "optional title"
*/
showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
'use strict';
var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += '¨0';
var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
linkId = linkId.toLowerCase();
if (url.match(/^data:.+?\/.+?;base64,/)) {
// remove newlines
globals.gUrls[linkId] = url.replace(/\s/g, '');
} else {
globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals); // 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 '';
};
// first we try to find base64 link references
text = text.replace(base64Regex, replaceFunc);
text = text.replace(regex, replaceFunc);
// attacklab: strip sentinel
text = text.replace(/¨0/, '');
return text;
});
showdown.subParser('tables', function (text, options, globals) {
'use strict';
if (!options.tables) {
return text;
}
var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,
//singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm;
singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm;
function parseStyles (sLine) {
if (/^:[ \t]*--*$/.test(sLine)) {
return ' style="text-align:left;"';
} else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
return ' style="text-align:right;"';
} else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
return ' style="text-align:center;"';
} else {
return '';
}
}
function parseHeaders (header, style) {
var id = '';
header = header.trim();
// support both tablesHeaderId and tableHeaderId due to error in documentation so we don't break backwards compatibility
if (options.tablesHeaderId || options.tableHeaderId) {
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
}
header = showdown.subParser('spanGamut')(header, options, globals);
return '' + header + ' \n';
}
function parseCells (cell, style) {
var subText = showdown.subParser('spanGamut')(cell, options, globals);
return '' + subText + ' \n';
}
function buildTable (headers, cells) {
var tb = '\n\n\n',
tblLgn = headers.length;
for (var i = 0; i < tblLgn; ++i) {
tb += headers[i];
}
tb += ' \n\n\n';
for (i = 0; i < cells.length; ++i) {
tb += '\n';
for (var ii = 0; ii < tblLgn; ++ii) {
tb += cells[i][ii];
}
tb += ' \n';
}
tb += '\n
\n';
return tb;
}
function parseTable (rawTable) {
var i, tableLines = rawTable.split('\n');
for (i = 0; i < tableLines.length; ++i) {
// strip wrong first and last column if wrapped tables are used
if (/^ {0,3}\|/.test(tableLines[i])) {
tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, '');
}
if (/\|[ \t]*$/.test(tableLines[i])) {
tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
}
// parse code spans first, but we only support one line code spans
tableLines[i] = showdown.subParser('codeSpans')(tableLines[i], options, globals);
}
var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
rawCells = [],
headers = [],
styles = [],
cells = [];
tableLines.shift();
tableLines.shift();
for (i = 0; i < tableLines.length; ++i) {
if (tableLines[i].trim() === '') {
continue;
}
rawCells.push(
tableLines[i]
.split('|')
.map(function (s) {
return s.trim();
})
);
}
if (rawHeaders.length < rawStyles.length) {
return rawTable;
}
for (i = 0; i < rawStyles.length; ++i) {
styles.push(parseStyles(rawStyles[i]));
}
for (i = 0; i < rawHeaders.length; ++i) {
if (showdown.helper.isUndefined(styles[i])) {
styles[i] = '';
}
headers.push(parseHeaders(rawHeaders[i], styles[i]));
}
for (i = 0; i < rawCells.length; ++i) {
var row = [];
for (var ii = 0; ii < headers.length; ++ii) {
if (showdown.helper.isUndefined(rawCells[i][ii])) {
}
row.push(parseCells(rawCells[i][ii], styles[ii]));
}
cells.push(row);
}
return buildTable(headers, cells);
}
text = globals.converter._dispatch('tables.before', text, options, globals);
// find escaped pipe characters
text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback);
// parse multi column tables
text = text.replace(tableRgx, parseTable);
// parse one column tables
text = text.replace(singeColTblRgx, parseTable);
text = globals.converter._dispatch('tables.after', text, options, globals);
return text;
});
showdown.subParser('underline', function (text, options, globals) {
'use strict';
if (!options.underline) {
return text;
}
text = globals.converter._dispatch('underline.before', text, options, globals);
if (options.literalMidWordUnderscores) {
text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
return '' + txt + '';
});
text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
return '' + txt + '';
});
} else {
text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
return (/\S$/.test(m)) ? '' + m + '' : wm;
});
text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
return (/\S$/.test(m)) ? '' + m + '' : wm;
});
}
// escape remaining underscores to prevent them being parsed by italic and bold
text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('underline.after', text, options, globals);
return text;
});
/**
* Swap back in all the special characters we've hidden.
*/
showdown.subParser('unescapeSpecialChars', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('unescapeSpecialChars.before', text, options, globals);
text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) {
var charCodeToReplace = parseInt(m1);
return String.fromCharCode(charCodeToReplace);
});
text = globals.converter._dispatch('unescapeSpecialChars.after', text, options, globals);
return text;
});
showdown.subParser('makeMarkdown.blockquote', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals);
if (innerTxt === '') {
continue;
}
txt += innerTxt;
}
}
// cleanup
txt = txt.trim();
txt = '> ' + txt.split('\n').join('\n> ');
return txt;
});
showdown.subParser('makeMarkdown.codeBlock', function (node, globals) {
'use strict';
var lang = node.getAttribute('language'),
num = node.getAttribute('precodenum');
return '```' + lang + '\n' + globals.preList[num] + '\n```';
});
showdown.subParser('makeMarkdown.codeSpan', function (node) {
'use strict';
return '`' + node.innerHTML + '`';
});
showdown.subParser('makeMarkdown.emphasis', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '*';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '*';
}
return txt;
});
showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) {
'use strict';
var headerMark = new Array(headerLevel + 1).join('#'),
txt = '';
if (node.hasChildNodes()) {
txt = headerMark + ' ';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
}
return txt;
});
showdown.subParser('makeMarkdown.hr', function () {
'use strict';
return '---';
});
showdown.subParser('makeMarkdown.image', function (node) {
'use strict';
var txt = '';
if (node.hasAttribute('src')) {
txt += ' + '>';
if (node.hasAttribute('width') && node.hasAttribute('height')) {
txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height');
}
if (node.hasAttribute('title')) {
txt += ' "' + node.getAttribute('title') + '"';
}
txt += ')';
}
return txt;
});
showdown.subParser('makeMarkdown.links', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes() && node.hasAttribute('href')) {
var children = node.childNodes,
childrenLength = children.length;
txt = '[';
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '](';
txt += '<' + node.getAttribute('href') + '>';
if (node.hasAttribute('title')) {
txt += ' "' + node.getAttribute('title') + '"';
}
txt += ')';
}
return txt;
});
showdown.subParser('makeMarkdown.list', function (node, globals, type) {
'use strict';
var txt = '';
if (!node.hasChildNodes()) {
return '';
}
var listItems = node.childNodes,
listItemsLenght = listItems.length,
listNum = node.getAttribute('start') || 1;
for (var i = 0; i < listItemsLenght; ++i) {
if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') {
continue;
}
// define the bullet to use in list
var bullet = '';
if (type === 'ol') {
bullet = listNum.toString() + '. ';
} else {
bullet = '- ';
}
// parse list item
txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals);
++listNum;
}
// add comment at the end to prevent consecutive lists to be parsed as one
txt += '\n\n';
return txt.trim();
});
showdown.subParser('makeMarkdown.listItem', function (node, globals) {
'use strict';
var listItemTxt = '';
var children = node.childNodes,
childrenLenght = children.length;
for (var i = 0; i < childrenLenght; ++i) {
listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
// if it's only one liner, we need to add a newline at the end
if (!/\n$/.test(listItemTxt)) {
listItemTxt += '\n';
} else {
// it's multiparagraph, so we need to indent
listItemTxt = listItemTxt
.split('\n')
.join('\n ')
.replace(/^ {4}$/gm, '')
.replace(/\n\n+/g, '\n\n');
}
return listItemTxt;
});
showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) {
'use strict';
spansOnly = spansOnly || false;
var txt = '';
// edge case of text without wrapper paragraph
if (node.nodeType === 3) {
return showdown.subParser('makeMarkdown.txt')(node, globals);
}
// HTML comment
if (node.nodeType === 8) {
return '\n\n';
}
// process only node elements
if (node.nodeType !== 1) {
return '';
}
var tagName = node.tagName.toLowerCase();
switch (tagName) {
//
// BLOCKS
//
case 'h1':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; }
break;
case 'h2':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; }
break;
case 'h3':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; }
break;
case 'h4':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; }
break;
case 'h5':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; }
break;
case 'h6':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; }
break;
case 'p':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; }
break;
case 'blockquote':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; }
break;
case 'hr':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; }
break;
case 'ol':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; }
break;
case 'ul':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; }
break;
case 'precode':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; }
break;
case 'pre':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; }
break;
case 'table':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; }
break;
//
// SPANS
//
case 'code':
txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals);
break;
case 'em':
case 'i':
txt = showdown.subParser('makeMarkdown.emphasis')(node, globals);
break;
case 'strong':
case 'b':
txt = showdown.subParser('makeMarkdown.strong')(node, globals);
break;
case 'del':
txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals);
break;
case 'a':
txt = showdown.subParser('makeMarkdown.links')(node, globals);
break;
case 'img':
txt = showdown.subParser('makeMarkdown.image')(node, globals);
break;
default:
txt = node.outerHTML + '\n\n';
}
// common normalization
// TODO eventually
return txt;
});
showdown.subParser('makeMarkdown.paragraph', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
}
// some text normalization
txt = txt.trim();
return txt;
});
showdown.subParser('makeMarkdown.pre', function (node, globals) {
'use strict';
var num = node.getAttribute('prenum');
return '' + globals.preList[num] + '
';
});
showdown.subParser('makeMarkdown.strikethrough', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '~~';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '~~';
}
return txt;
});
showdown.subParser('makeMarkdown.strong', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '**';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '**';
}
return txt;
});
showdown.subParser('makeMarkdown.table', function (node, globals) {
'use strict';
var txt = '',
tableArray = [[], []],
headings = node.querySelectorAll('thead>tr>th'),
rows = node.querySelectorAll('tbody>tr'),
i, ii;
for (i = 0; i < headings.length; ++i) {
var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals),
allign = '---';
if (headings[i].hasAttribute('style')) {
var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, '');
switch (style) {
case 'text-align:left;':
allign = ':---';
break;
case 'text-align:right;':
allign = '---:';
break;
case 'text-align:center;':
allign = ':---:';
break;
}
}
tableArray[0][i] = headContent.trim();
tableArray[1][i] = allign;
}
for (i = 0; i < rows.length; ++i) {
var r = tableArray.push([]) - 1,
cols = rows[i].getElementsByTagName('td');
for (ii = 0; ii < headings.length; ++ii) {
var cellContent = ' ';
if (typeof cols[ii] !== 'undefined') {
cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals);
}
tableArray[r].push(cellContent);
}
}
var cellSpacesCount = 3;
for (i = 0; i < tableArray.length; ++i) {
for (ii = 0; ii < tableArray[i].length; ++ii) {
var strLen = tableArray[i][ii].length;
if (strLen > cellSpacesCount) {
cellSpacesCount = strLen;
}
}
}
for (i = 0; i < tableArray.length; ++i) {
for (ii = 0; ii < tableArray[i].length; ++ii) {
if (i === 1) {
if (tableArray[i][ii].slice(-1) === ':') {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':';
} else {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-');
}
} else {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount);
}
}
txt += '| ' + tableArray[i].join(' | ') + ' |\n';
}
return txt.trim();
});
showdown.subParser('makeMarkdown.tableCell', function (node, globals) {
'use strict';
var txt = '';
if (!node.hasChildNodes()) {
return '';
}
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true);
}
return txt.trim();
});
showdown.subParser('makeMarkdown.txt', function (node) {
'use strict';
var txt = node.nodeValue;
// multiple spaces are collapsed
txt = txt.replace(/ +/g, ' ');
// replace the custom ¨NBSP; with a space
txt = txt.replace(/¨NBSP;/g, ' ');
// ", <, > and & should replace escaped html entities
txt = showdown.helper.unescapeHTMLEntities(txt);
// escape markdown magic characters
// emphasis, strong and strikethrough - can appear everywhere
// we also escape pipe (|) because of tables
// and escape ` because of code blocks and spans
txt = txt.replace(/([*_~|`])/g, '\\$1');
// escape > because of blockquotes
txt = txt.replace(/^(\s*)>/g, '\\$1>');
// hash character, only troublesome at the beginning of a line because of headers
txt = txt.replace(/^#/gm, '\\#');
// horizontal rules
txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3');
// dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer
txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.');
// +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped)
txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2');
// images and links, ] followed by ( is problematic, so we escape it
txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\(');
// reference URIs must also be escaped
txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:');
return txt;
});
var root = window;
// AMD Loader
if (typeof define === 'function' && define.amd) {
define(function () {
'use strict';
return showdown;
});
// CommonJS/nodeJS Loader
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = showdown;
// Regular Browser loader
} else {
root.showdown = showdown;
}
}).call(this);
showdown.setOption("strikethrough", true);
showdown.setOption("ghMentions", true);
showdown.setOption("emoji", true);
showdown.setOption("tables", false);
showdown.setOption("simpleLineBreaks", true);
showdown.setOption("simplifiedAutoLink", true);
showdown.setOption("ghMentionsLink", "https://anilist.co/user/{u}/");
const converter = new showdown.Converter();
let makeHtml = function(markdown){
markdown = markdown.replace("----","---");
let centerSplit = markdown.split("~~~");
let imgRegex = /img(\d+%?)?\(.+?\)/gi;
centerSplit = centerSplit.map(component => {
let images = component.match(imgRegex);
if(images){
images.forEach(image => {
let imageParts = image.match(/^img(\d+%?)?\((.+?)\)$/i);
component = component.replace(image,`
`)
})
return component
}
else{
return component
}
})
let youtubeRegex = /youtube\(.+?\)/gi;
centerSplit = centerSplit.map(component => {
let videos = component.match(youtubeRegex);
if(videos){
videos.forEach(video => {
let videoParts = video.match(/^youtube\((.+?)\)$/i);
component = component.replace(video,`${videoParts[1]}`)
})
}
return component
});
let preProcessed = [centerSplit[0]];
let openCenter = false;
for(let i=1;i");
}
else{
preProcessed.push("");
}
preProcessed.push(centerSplit[i]);
openCenter = !openCenter
}
preProcessed = preProcessed.map(element => {
if(/~!/.test(element) || /!~/.test(element)){
return element.replace(/~!/g,"").replace(/!~/g,"");
}
return element
})
return converter.makeHtml(preProcessed.join(""))
}
function returnList(list,skipProcessing){
if(!list){
return null
};
let retl = [];
list.data.MediaListCollection.lists.forEach(mediaList => {
mediaList.entries.forEach(entry => {
if(!skipProcessing){
entry.isCustomList = mediaList.isCustomList;
if(entry.isCustomList){
entry.listLocations = [mediaList.name]
}
else{
entry.listLocations = []
};
entry.scoreRaw = Math.min(entry.scoreRaw,100);
if(!entry.media.episodes && entry.media.nextAiringEpisode){
entry.media.episodes = entry.media.nextAiringEpisode.episode - 1
}
if(entry.notes){
entry.listJSON = parseListJSON(entry.notes)
};
if(entry.media.a){
entry.media.staff = removeGroupedDuplicates(
entry.media.a.nodes.concat(
entry.media.b.nodes
),
e => e.id
);
delete entry.media.a;
delete entry.media.b;
}
if(entry.repeat > 10000){//counting eps as repeat, 10x One Piece as the plausibility baseline
entry.repeat = 0
}
if(entry.status === "REPEATING" && entry.repeat === 0){
entry.repeat = 1
}
};
retl.push(entry);
})
})
return removeGroupedDuplicates(
retl,
e => e.mediaId,
(oldElement,newElement) => {
if(!skipProcessing){
newElement.listLocations = newElement.listLocations.concat(oldElement.listLocations);
newElement.isCustomList = oldElement.isCustomList || newElement.isCustomList;
}
}
)
};
function parseListJSON(listNote){
if(!listNote){
return null
};
let commandMatches = listNote.match(/\$({.*})\$/);
if(commandMatches){
try{
let noteContent = JSON.parse(commandMatches[1]);
noteContent.adjustValue = noteContent.adjust || 0;
let rangeParser = function(thing){
if(typeof thing === "number"){
return 1
}
else if(typeof thing === "string"){
thing = thing.split(",").map(a => a.trim())
};
return thing.reduce(function(acc,item){
if(typeof item === "number"){
return acc + 1
};
let multiplierPresent = item.split("x").map(a => a.trim());
let value = 1;
let rangePresent = multiplierPresent[0].split("-").map(a => a.trim());
if(rangePresent.length === 2){//range
let minRange = parseFloat(rangePresent[0]);
let maxRange = parseFloat(rangePresent[1]);
if(minRange && maxRange){
value = maxRange - minRange + 1
}
}
if(multiplierPresent.length === 1){//no multiplier
return acc + value
}
if(multiplierPresent.length === 2){//possible multiplier
let multiplier = parseFloat(multiplierPresent[1]);
if(multiplier || multiplier === 0){
return acc + value*multiplier
}
else{
return acc + 1
}
}
else{//unparsable
return acc + 1
}
},0);
};
if(noteContent.more){
noteContent.adjustValue += rangeParser(noteContent.more)
};
if(noteContent.skip){
noteContent.adjustValue -= rangeParser(noteContent.skip)
};
return noteContent;
}
catch(e){
console.warn("Unable to parse JSON in list note",commandMatches)
}
}
else{
return null
}
};
function formatCompat(compatData,targetLocation,name){
let differenceSpan = create("span",false,compatData.difference.roundPlaces(3));
if(compatData.difference < 0.9){
differenceSpan.style.color = "green"
}
else if(compatData.difference > 1.1){
differenceSpan.style.color = "red"
};
targetLocation.innerText = "";
targetLocation.appendChild(differenceSpan);
let countSpan = create("span",false," based on " + compatData.shared + " shared entries. Lower is better. 0.8 - 1.1 is common\nFormally, this is 'standard deviation between normalized ratings'",targetLocation);
let canvas = create("canvas",false,false,targetLocation,"display:block;");
canvas.title = "Blue = " + name + "\nRed = you";
canvas.width = 200;
canvas.height = 100;
let r1 = Math.sqrt(compatData.list1/(compatData.list1 + compatData.list2));
let r2 = Math.sqrt(compatData.list2/(compatData.list1 + compatData.list2));
let distance;
if(compatData.shared === compatData.list1 || compatData.shared === compatData.list2){
distance = Math.abs(r1 - r2)
}
else if(compatData.shared === 0){
distance = r1 + r2
}
else{
let areaOfIntersection = function(d,r0,r1){
let rr0 = r0 * r0;
let rr1 = r1 * r1;
let phi = (Math.acos((rr0 + (d * d) - rr1) / (2 * r0 * d))) * 2;
let theta = (Math.acos((rr1 + (d * d) - rr0) / (2 * r1 * d))) * 2;
let area1 = (theta * rr1 - rr1 * Math.sin(theta))/2;
let area2 = (phi * rr0 - rr0 * Math.sin(phi))/2;
return area1 + area2;
};
let overlapArea = Math.PI*compatData.shared/(compatData.list1 + compatData.list2);
let pivot0 = Math.abs(r1 - r2);
let pivot1 = r1 + r2;
while(pivot1 - pivot0 > (r1 + r2)/100){
distance = (pivot0 + pivot1)/2;
if(areaOfIntersection(distance,r1,r2) > overlapArea){
pivot0 = distance
}
else{
pivot1 = distance
}
}
}
let ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.fillStyle = "rgb(61,180,242)";
ctx.arc(50,50,50*r1,0,2*Math.PI);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "rgb(250,122,122)";
ctx.arc(50 + 50*distance,50,50*r2,0,2*Math.PI);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "rgb(61,180,242,0.5)";
ctx.arc(50,50,50*r1,0,2*Math.PI);
ctx.fill();
}
function compatCheck(list,name,type,callback){
const variables = {
name: name,
listType: type
};
generalAPIcall(queryMediaListCompat,variables,function(data){
list.sort((a,b) => a.mediaId - b.mediaId);
let list2 = returnList(data).filter(element => element.scoreRaw);
let list3 = [];
let indeks1 = 0;
let indeks2 = 0;
while(indeks1 < list.length && indeks2 < list2.length){
if(list2[indeks2].mediaId > list[indeks1].mediaId){
indeks1++;
continue
};
if(list2[indeks2].mediaId < list[indeks1].mediaId){
indeks2++;
continue
};
if(list2[indeks2].mediaId === list[indeks1].mediaId){
list3.push({
mediaId: list[indeks1].mediaId,
score1: list[indeks1].scoreRaw,
score2: list2[indeks2].scoreRaw
});
indeks1++;
indeks2++
}
};
let average1 = 0;
let average2 = 0;
list3.forEach(item => {
average1 += item.score1;
average2 += item.score2;
item.sdiff = item.score1 - item.score2
});
average1 = average1/list3.length;
average2 = average2/list3.length;
let standev1 = 0;
let standev2 = 0;
list3.forEach(item => {
standev1 += Math.pow(item.score1 - average1,2);
standev2 += Math.pow(item.score2 - average2,2)
});
standev1 = Math.sqrt(standev1/(list3.length - 1));
standev2 = Math.sqrt(standev2/(list3.length - 1));
let difference = 0;
list3.forEach(item => {
difference += Math.abs(
(item.score1 - average1)/standev1
- (item.score2 - average2)/standev2
)
});
difference = difference/list3.length;
callback({
difference: difference,
shared: list3.length,
list1: list.length,
list2: list2.length,
user: name
})
})
}
//used by the stats module, and to safeguard the manga chapter guesses
//publishing manga is a bit tricky, since Anilist doesn't track chapters
const commonUnfinishedManga = {
"30002":{
"chapters":362,
"volumes":40,
"comment":"berserk"
},
"30013":{
"chapters":1001,
"volumes":92,
"comment":"one piece"
},
"85486":{
"chapters":297,
"volumes":20,
"comment":"mha"
},
"74347":{
"chapters":133,
"volumes":21,
"comment":"opm"
},
"30026":{
"chapters":390,
"volumes":36,
"comment":"HxH"
},
"30656":{
"chapters":327,
"volumes":37,
"comment":"vagabond"
},
"30105":{
"chapters":106,
"volumes":14,
"comment":"yotsuba&"
},
"105398":{
"chapters":136,
"volumes":0,
"comment":"solo leveling"
},
"86635":{
"chapters":214,
"volumes":19,
"comment":"kaguya"
}
}
if(NOW() - new Date(2021,0,1) > 365*24*60*60*1000){
console.log("remind hoh to update the commonUnfinishedManga list")
}
function uniqueBy(a,key){
let seen = new Set();
return a.filter(item => {
let k = key(item);
return seen.has(k) ? false : seen.add(k)
})
}
function dateToJST(string){
let splited = string.split("-");
let year = parseInt(splited[0]);
let month = parseInt(splited[1]);
let day = parseInt(splited[2]);
return Date.UTC(year,month-1,day,-9)
}
//idea by GoBusto: https://gitlab.com/gobusto/unicodifier
function emojiSanitize(string){
return Array.from(string).map(char => {
let codePoint = char.codePointAt(0);
if(codePoint > 0xFFFF){
return "" + codePoint + ";"
}
return char
}).join("")
}
function looseMatcher(string,searcher){
return string.toLowerCase().includes(searcher.toLowerCase())
|| RegExp(searcher,"i").test(string.toLowerCase())
}
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
//end "utilities.js"
/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.DOMPurify = factory());
}(window, function () { 'use strict';
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var hasOwnProperty = Object.hasOwnProperty,
setPrototypeOf = Object.setPrototypeOf,
isFrozen = Object.isFrozen,
getPrototypeOf = Object.getPrototypeOf,
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
var freeze = Object.freeze,
seal = Object.seal,
create = Object.create; // eslint-disable-line import/no-mutable-exports
var _ref = typeof Reflect !== 'undefined' && Reflect,
apply = _ref.apply,
construct = _ref.construct;
if (!apply) {
apply = function apply(fun, thisValue, args) {
return fun.apply(thisValue, args);
};
}
if (!freeze) {
freeze = function freeze(x) {
return x;
};
}
if (!seal) {
seal = function seal(x) {
return x;
};
}
if (!construct) {
construct = function construct(Func, args) {
return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
};
}
var arrayForEach = unapply(Array.prototype.forEach);
var arrayPop = unapply(Array.prototype.pop);
var arrayPush = unapply(Array.prototype.push);
var stringToLowerCase = unapply(String.prototype.toLowerCase);
var stringMatch = unapply(String.prototype.match);
var stringReplace = unapply(String.prototype.replace);
var stringIndexOf = unapply(String.prototype.indexOf);
var stringTrim = unapply(String.prototype.trim);
var regExpTest = unapply(RegExp.prototype.test);
var typeErrorCreate = unconstruct(TypeError);
function unapply(func) {
return function (thisArg) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return apply(func, thisArg, args);
};
}
function unconstruct(func) {
return function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return construct(func, args);
};
}
/* Add properties to a lookup table */
function addToSet(set, array) {
if (setPrototypeOf) {
// Make 'in' and truthy checks like Boolean(set.constructor)
// independent of any properties defined on Object.prototype.
// Prevent prototype setters from intercepting set as a this value.
setPrototypeOf(set, null);
}
var l = array.length;
while (l--) {
var element = array[l];
if (typeof element === 'string') {
var lcElement = stringToLowerCase(element);
if (lcElement !== element) {
// Config presets (e.g. tags.js, attrs.js) are immutable.
if (!isFrozen(array)) {
array[l] = lcElement;
}
element = lcElement;
}
}
set[element] = true;
}
return set;
}
/* Shallow clone an object */
function clone(object) {
var newObject = create(null);
var property = void 0;
for (property in object) {
if (apply(hasOwnProperty, object, [property])) {
newObject[property] = object[property];
}
}
return newObject;
}
/* IE10 doesn't support __lookupGetter__ so lets'
* simulate it. It also automatically checks
* if the prop is function or getter and behaves
* accordingly. */
function lookupGetter(object, prop) {
while (object !== null) {
var desc = getOwnPropertyDescriptor(object, prop);
if (desc) {
if (desc.get) {
return unapply(desc.get);
}
if (typeof desc.value === 'function') {
return unapply(desc.value);
}
}
object = getPrototypeOf(object);
}
function fallbackValue(element) {
console.warn('fallback value for', element);
return null;
}
return fallbackValue;
}
var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
// SVG
var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
// List of SVG elements that are disallowed by default.
// We still need to know them so that we can do namespace
// checks properly in case one wants to add them to
// allow-list.
var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);
// Similarly to SVG, we want to know all MathML elements,
// even those that we disallow by default.
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
var text = freeze(['#text']);
var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
// eslint-disable-next-line unicorn/better-regex
var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
);
var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
);
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var getGlobal = function getGlobal() {
return typeof window === 'undefined' ? null : window;
};
/**
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
* @param {Document} document The document object (to determine policy name suffix)
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
* are not supported).
*/
var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
return null;
}
// Allow the callers to control the unique policy name
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
// Policy creation with duplicate names throws in Trusted Types.
var suffix = null;
var ATTR_NAME = 'data-tt-policy-suffix';
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
suffix = document.currentScript.getAttribute(ATTR_NAME);
}
var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
try {
return trustedTypes.createPolicy(policyName, {
createHTML: function createHTML(html$$1) {
return html$$1;
}
});
} catch (_) {
// Policy creation failed (most likely another DOMPurify script has
// already run). Skip creating the policy, as this will only cause errors
// if TT are enforced.
console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
return null;
}
};
function createDOMPurify() {
var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
var DOMPurify = function DOMPurify(root) {
return createDOMPurify(root);
};
/**
* Version label, exposed for easier checks
* if DOMPurify is up to date or not
*/
DOMPurify.version = '2.2.8';
/**
* Array of elements that DOMPurify removed during sanitation.
* Empty if nothing was removed.
*/
DOMPurify.removed = [];
if (!window || !window.document || window.document.nodeType !== 9) {
// Not running in a browser, provide a factory function
// so that you can pass your own Window
DOMPurify.isSupported = false;
return DOMPurify;
}
var originalDocument = window.document;
var document = window.document;
var DocumentFragment = window.DocumentFragment,
HTMLTemplateElement = window.HTMLTemplateElement,
Node = window.Node,
Element = window.Element,
NodeFilter = window.NodeFilter,
_window$NamedNodeMap = window.NamedNodeMap,
NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
Text = window.Text,
Comment = window.Comment,
DOMParser = window.DOMParser,
trustedTypes = window.trustedTypes;
var ElementPrototype = Element.prototype;
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
// As per issue #47, the web-components registry is inherited by a
// new document created via createHTMLDocument. As per the spec
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
// a new empty registry is used when creating a template contents owner
// document, so we use that as our parent document to ensure nothing
// is inherited.
if (typeof HTMLTemplateElement === 'function') {
var template = document.createElement('template');
if (template.content && template.content.ownerDocument) {
document = template.content.ownerDocument;
}
}
var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
var _document = document,
implementation = _document.implementation,
createNodeIterator = _document.createNodeIterator,
createDocumentFragment = _document.createDocumentFragment;
var importNode = originalDocument.importNode;
var documentMode = {};
try {
documentMode = clone(document).documentMode ? document.documentMode : {};
} catch (_) {}
var hooks = {};
/**
* Expose whether this browser supports running the full DOMPurify.
*/
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
ERB_EXPR$$1 = ERB_EXPR,
DATA_ATTR$$1 = DATA_ATTR,
ARIA_ATTR$$1 = ARIA_ATTR,
IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
/**
* We consider the elements and attributes below to be safe. Ideally
* don't add any new ones but feel free to remove unwanted ones.
*/
/* allowed element names */
var ALLOWED_TAGS = null;
var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));
/* Allowed attribute names */
var ALLOWED_ATTR = null;
var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
var FORBID_TAGS = null;
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
var FORBID_ATTR = null;
/* Decide if ARIA attributes are okay */
var ALLOW_ARIA_ATTR = true;
/* Decide if custom data attributes are okay */
var ALLOW_DATA_ATTR = true;
/* Decide if unknown protocols are okay */
var ALLOW_UNKNOWN_PROTOCOLS = false;
/* Output should be safe for common template engines.
* This means, DOMPurify removes data attributes, mustaches and ERB
*/
var SAFE_FOR_TEMPLATES = false;
/* Decide if document with ... should be returned */
var WHOLE_DOCUMENT = false;
/* Track whether config is already set on this instance of DOMPurify. */
var SET_CONFIG = false;
/* Decide if all elements (e.g. style, script) must be children of
* document.body. By default, browsers might move them to document.head */
var FORCE_BODY = false;
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
* string (or a TrustedHTML object if Trusted Types are supported).
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
*/
var RETURN_DOM = false;
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
* string (or a TrustedHTML object if Trusted Types are supported) */
var RETURN_DOM_FRAGMENT = false;
/* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
* `Node` is imported into the current `Document`. If this flag is not enabled the
* `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
* DOMPurify.
*
* This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
* might cause XSS from attacks hidden in closed shadowroots in case the browser
* supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
*/
var RETURN_DOM_IMPORT = true;
/* Try to return a Trusted Type object instead of a string, return a string in
* case Trusted Types are not supported */
var RETURN_TRUSTED_TYPE = false;
/* Output should be free from DOM clobbering attacks? */
var SANITIZE_DOM = true;
/* Keep element content when removing element? */
var KEEP_CONTENT = true;
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
* of importing it into a new Document and returning a sanitized copy */
var IN_PLACE = false;
/* Allow usage of profiles like html, svg and mathMl */
var USE_PROFILES = {};
/* Tags to ignore content of when KEEP_CONTENT is true */
var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
/* Tags that are safe for data: URIs */
var DATA_URI_TAGS = null;
var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
/* Attributes safe for values like "javascript:" */
var URI_SAFE_ATTRIBUTES = null;
var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
/* Document namespace */
var NAMESPACE = HTML_NAMESPACE;
/* Keep a reference to config to pass to hooks */
var CONFIG = null;
/* Ideally, do not touch anything below this line */
/* ______________________________________________ */
var formElement = document.createElement('form');
/**
* _parseConfig
*
* @param {Object} cfg optional config literal
*/
// eslint-disable-next-line complexity
var _parseConfig = function _parseConfig(cfg) {
if (CONFIG && CONFIG === cfg) {
return;
}
/* Shield configuration object from tampering */
if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
cfg = {};
}
/* Shield configuration object from prototype pollution */
cfg = clone(cfg);
/* Set configuration parameters */
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
IN_PLACE = cfg.IN_PLACE || false; // Default false
IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
NAMESPACE = cfg.NAMESPACE || NAMESPACE;
if (SAFE_FOR_TEMPLATES) {
ALLOW_DATA_ATTR = false;
}
if (RETURN_DOM_FRAGMENT) {
RETURN_DOM = true;
}
/* Parse profile info */
if (USE_PROFILES) {
ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
ALLOWED_ATTR = [];
if (USE_PROFILES.html === true) {
addToSet(ALLOWED_TAGS, html);
addToSet(ALLOWED_ATTR, html$1);
}
if (USE_PROFILES.svg === true) {
addToSet(ALLOWED_TAGS, svg);
addToSet(ALLOWED_ATTR, svg$1);
addToSet(ALLOWED_ATTR, xml);
}
if (USE_PROFILES.svgFilters === true) {
addToSet(ALLOWED_TAGS, svgFilters);
addToSet(ALLOWED_ATTR, svg$1);
addToSet(ALLOWED_ATTR, xml);
}
if (USE_PROFILES.mathMl === true) {
addToSet(ALLOWED_TAGS, mathMl);
addToSet(ALLOWED_ATTR, mathMl$1);
addToSet(ALLOWED_ATTR, xml);
}
}
/* Merge configuration parameters */
if (cfg.ADD_TAGS) {
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
ALLOWED_TAGS = clone(ALLOWED_TAGS);
}
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
}
if (cfg.ADD_ATTR) {
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
ALLOWED_ATTR = clone(ALLOWED_ATTR);
}
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
}
if (cfg.ADD_URI_SAFE_ATTR) {
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
}
/* Add #text in case KEEP_CONTENT is set to true */
if (KEEP_CONTENT) {
ALLOWED_TAGS['#text'] = true;
}
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
if (WHOLE_DOCUMENT) {
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
}
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
if (ALLOWED_TAGS.table) {
addToSet(ALLOWED_TAGS, ['tbody']);
delete FORBID_TAGS.tbody;
}
// Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
if (freeze) {
freeze(cfg);
}
CONFIG = cfg;
};
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
/* Keep track of all possible SVG and MathML tags
* so that we can perform the namespace checks
* correctly. */
var ALL_SVG_TAGS = addToSet({}, svg);
addToSet(ALL_SVG_TAGS, svgFilters);
addToSet(ALL_SVG_TAGS, svgDisallowed);
var ALL_MATHML_TAGS = addToSet({}, mathMl);
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
/**
*
*
* @param {Element} element a DOM element whose namespace is being checked
* @returns {boolean} Return false if the element has a
* namespace that a spec-compliant parser would never
* return. Return true otherwise.
*/
var _checkValidNamespace = function _checkValidNamespace(element) {
var parent = getParentNode(element);
// In JSDOM, if we're inside shadow DOM, then parentNode
// can be null. We just simulate parent in this case.
if (!parent || !parent.tagName) {
parent = {
namespaceURI: HTML_NAMESPACE,
tagName: 'template'
};
}
var tagName = stringToLowerCase(element.tagName);
var parentTagName = stringToLowerCase(parent.tagName);
if (element.namespaceURI === SVG_NAMESPACE) {
// The only way to switch from HTML namespace to SVG
// is via