diff --git a/package-lock.json b/package-lock.json
index 473b1b6..5b3af32 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "showdown",
- "version": "2.0.0",
+ "version": "3.0.0-alpha",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "showdown",
- "version": "2.0.0",
+ "version": "3.0.0-alpha",
"license": "MIT",
"dependencies": {
"commander": "^9.0.0",
diff --git a/src/subParsers/makehtml/image.js b/src/subParsers/makehtml/image.js
index 0ed6128..a42eb33 100644
--- a/src/subParsers/makehtml/image.js
+++ b/src/subParsers/makehtml/image.js
@@ -140,9 +140,9 @@ showdown.subParser('makehtml.image', function (text, options, globals) {
startEvent = globals.converter.dispatch(startEvent);
text = startEvent.output;
- let inlineRegExp = /!\[([^\]]*?)][ \t]*\([ \t]?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\5)?[ \t]?\)/g,
+ let inlineRegExp = /!\[([^\]]*?)][ \t]*\([ \t]?(\S+?(?:\(\S*?\)\S*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\5)?[ \t]?\)/g,
crazyRegExp = /!\[([^\]]*?)][ \t]*\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\5)?[ \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,
+ base64RegExp = /!\[([^\]]*?)][ \t]*\([ \t]?(data:.+?\/.+?;base64,[A-Za-z\d+/=\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;
diff --git a/src/subParsers/makehtml/link.js b/src/subParsers/makehtml/link.js
index 2b87930..b5e8700 100644
--- a/src/subParsers/makehtml/link.js
+++ b/src/subParsers/makehtml/link.js
@@ -25,11 +25,19 @@ showdown.subParser('makehtml.link', function (text, options, globals) {
* @param {string|null} [url]
* @param {string|null} [title]
* @param {boolean} [emptyCase]
+ * @returns {string}
*/
function writeAnchorTag (subEvtName, pattern, wholeMatch, text, linkId, url, title, emptyCase) {
- let otp = '';
- let target = null;
+ let matches = {
+ _wholeMatch: wholeMatch,
+ _linkId: linkId,
+ _url: url,
+ _title: title,
+ text: text
+ },
+ otp,
+ attributes = {};
title = title || null;
url = url || null;
@@ -55,35 +63,85 @@ showdown.subParser('makehtml.link', function (text, options, globals) {
}
}
+ url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
+ attributes.href = url;
if (title && showdown.helper.isString(title)) {
title = title
.replace(/"/g, '"')
.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
+ attributes.title = title;
}
// optionLinksInNewWindow only applies
// to external links. Hash links (#) open in same page
if (options.openLinksInNewWindow && !/^#/.test(url)) {
- // escaped _
- target = ' rel="noopener noreferrer" target="¨E95Eblank"';
+ attributes.rel = 'noopener noreferrer';
+ attributes.target = '¨E95Eblank'; // escaped _
+
}
- // Text can be a markdown element, so we run through the appropriate parsers
- text = showdown.subParser('makehtml.codeSpan')(text, options, globals);
- text = showdown.subParser('makehtml.emoji')(text, options, globals);
- text = showdown.subParser('makehtml.underline')(text, options, globals);
- text = showdown.subParser('makehtml.emphasisAndStrong')(text, options, globals);
- text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
- text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
- text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
+ let captureStartEvent = new showdown.Event('makehtml.link.' + subEvtName + '.onCapture', wholeMatch);
+ captureStartEvent
+ .setOutput(null)
+ ._setGlobals(globals)
+ ._setOptions(options)
+ .setRegexp(pattern)
+ .setMatches(matches)
+ .setAttributes(attributes);
+ captureStartEvent = globals.converter.dispatch(captureStartEvent);
+ // if something was passed as output, it takes precedence
+ // and will be used as output
+ if (captureStartEvent.output && captureStartEvent.output !== '') {
+ otp = captureStartEvent.output;
+ } else {
+ attributes = captureStartEvent.attributes;
+ text = captureStartEvent.matches.text || '';
+ // Text can be a markdown element, so we run through the appropriate parsers
+ text = showdown.subParser('makehtml.codeSpan')(text, options, globals);
+ text = showdown.subParser('makehtml.emoji')(text, options, globals);
+ text = showdown.subParser('makehtml.underline')(text, options, globals);
+ text = showdown.subParser('makehtml.emphasisAndStrong')(text, options, globals);
+ text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
+ text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
+ text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
+ otp = '' + text + '';
+ }
-
- return otp;
+ let beforeHashEvent = new showdown.Event('makehtml.link.' + subEvtName + '.onHash', otp);
+ beforeHashEvent
+ .setOutput(otp)
+ ._setGlobals(globals)
+ ._setOptions(options);
+ beforeHashEvent = globals.converter.dispatch(beforeHashEvent);
+ otp = beforeHashEvent.output;
+ return showdown.subParser('makehtml.hashHTMLSpans')(otp, options, globals);
}
+ /**
+ * @param {string} mail
+ * @returns {{mail: string, url: string}}
+ */
+ function parseMail (mail) {
+ let url = 'mailto:';
+ mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
+ if (options.encodeEmails) {
+ url = showdown.helper.encodeEmailAddress(url + mail);
+ mail = showdown.helper.encodeEmailAddress(mail);
+ } else {
+ url = url + mail;
+ }
+ return {
+ mail: mail,
+ url: url
+ };
+ }
+
+ //
+ // Parser starts here
+ //
let startEvent = new showdown.Event('makehtml.link.onStart', text);
startEvent
.setOutput(text)
@@ -92,10 +150,172 @@ showdown.subParser('makehtml.link', function (text, options, globals) {
startEvent = globals.converter.dispatch(startEvent);
text = startEvent.output;
- let referenceRegex = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
-
// 1. Handle reference-style links: [link text] [id]
- text = text.replace(referenceRegex, function (wholeMatch, altText, linkId) {
- return writeAnchorTag ('reference', referenceRegex, wholeMatch, altText, linkId, '');
+ let referenceRegex = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]/g;
+ text = text.replace(referenceRegex, function (wholeMatch, text, linkId) {
+ // bail if we find 2 newlines somewhere
+ if (/\n\n/.test(wholeMatch)) {
+ return wholeMatch;
+ }
+ return writeAnchorTag ('reference', referenceRegex, wholeMatch, text, linkId);
});
+
+ // 2. Handle inline-style links: [link text](url "optional title")
+ // 2.1. Look for empty cases: []() and [empty]() and []("title")
+ let inlineEmptyRegex = /\[(.*?)]\( ?>? ?(["'](.*)["'])?\)/g;
+ text = text.replace(inlineEmptyRegex, function (wholeMatch, text, m1, title) {
+ return writeAnchorTag ('inline', inlineEmptyRegex, wholeMatch, text, null, null, title, true);
+ });
+
+ // 2.2. Look for cases with crazy urls like ./image/cat1).png
+ // the url mus be enclosed in <>
+ let inlineCrazyRegex = /\[((?:\[[^\]]*]|[^\[\]])*)]\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\4))?[ \t]?\)/g;
+ text = text.replace(inlineCrazyRegex, function (wholeMatch, text, url, m1, m2, title) {
+ return writeAnchorTag ('inline', inlineCrazyRegex, wholeMatch, text, null, url, title);
+ });
+
+ // 2.3. inline links with no title or titles wrapped in ' or ":
+ // [text](url.com) || [text]() || [text](url.com "title") || [text]( "title")
+ let inlineNormalRegex1 = /\[([\S ]*?)]\s?\( *([^\s'"]*?(?:\(\S*?\)\S*?)?)>?\s*(?:(['"])(.*?)\3)? *\)/g;
+ text = text.replace(inlineNormalRegex1, function (wholeMatch, text, url, m1, title) {
+ return writeAnchorTag ('inline', inlineNormalRegex1, wholeMatch, text, null, url, title);
+ });
+
+ // 2.4. inline links with titles wrapped in (): [foo](bar.com (title))
+ let inlineNormalRegex2 = /\[([\S ]*?)]\s?\( *([^\s'"]*?(?:\(\S*?\)\S*?)?)>?\s+\((.*?)\) *\)/g;
+ text = text.replace(inlineNormalRegex2, function (wholeMatch, text, url, title) {
+ return writeAnchorTag ('inline', inlineNormalRegex2, wholeMatch, text, null, url, title);
+ });
+
+
+ // 3. Handle reference-style shortcuts: [link text]
+ // These must come last in case there's a [link text][1] or [link text](/foo)
+ let referenceShortcutRegex = /\[([^\[\]]+)]/g;
+ text = text.replace(referenceShortcutRegex, function (wholeMatch, text) {
+ return writeAnchorTag ('reference', referenceShortcutRegex, wholeMatch, text);
+ });
+
+ // 4. Handle angle brackets links -> ``
+ // Must come after links, because you can use < and > delimiters in inline links like [this]().
+
+ // 4.1. Handle links first
+ let angleBracketsLinksRegex = /<(((?:https?|ftp):\/\/|www\.)[^'">\s]+)>/gi;
+ text = text.replace(angleBracketsLinksRegex, function (wholeMatch, url, urlStart) {
+ let text = url;
+ url = (urlStart === 'www.') ? 'http://' + url : url;
+ return writeAnchorTag ('angleBrackets', angleBracketsLinksRegex, wholeMatch, text, null, url);
+ });
+
+ // 4.2. Then mail adresses
+ let angleBracketsMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z\d]+(\.[-a-z\d]+)*\.[a-z]+)>/gi;
+ text = text.replace(angleBracketsMailRegex, function (wholeMatch, mail) {
+ const m = parseMail(mail);
+ return writeAnchorTag ('angleBrackets', angleBracketsMailRegex, wholeMatch, m.mail, null, m.url);
+ });
+
+ // 5. Handle GithubMentions (if option is enabled)
+ if (options.ghMentions) {
+ let ghMentionsRegex = /(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gi;
+ text = text.replace(ghMentionsRegex, function (wholeMatch, st, escape, mentions, username) {
+ // bail if the mentions was escaped
+ if (escape === '\\') {
+ return st + mentions;
+ }
+ // check if options.ghMentionsLink is a string
+ // TODO Validation should be done at initialization not at runtime
+ if (!showdown.helper.isString(options.ghMentionsLink)) {
+ throw new Error('ghMentionsLink option must be a string');
+ }
+ let url = options.ghMentionsLink.replace(/\{u}/g, username);
+ return st + writeAnchorTag ('reference', ghMentionsRegex, wholeMatch, mentions, null, url);
+ });
+ }
+
+ // 6 and 7 have to come here to prevent naked links to catch html
+ // 6. Handle tags
+ text = text.replace(/]*>[\s\S]*<\/a>/g, function (wholeMatch) {
+ return showdown.helper._hashHTMLSpan(wholeMatch, globals);
+ });
+
+ // 7. Handle tags
+ text = text.replace(/]*\/?>/g, function (wholeMatch) {
+ return showdown.helper._hashHTMLSpan(wholeMatch, globals);
+ });
+
+ // 8. Handle naked links (if option is enabled)
+ if (options.simplifiedAutoLink) {
+ // 8.1. Check for naked URLs
+ // we also include leading markdown magic chars [_*~] for cases like __https://www.google.com/foobar__
+ let nakedUrlRegex = /([_*~]*?)(((?:https?|ftp):\/\/|www\.)[^\s<>"'`´.-][^\s<>"'`´]*?\.[a-z\d.]+[^\s<>"']*)\1/gi;
+ text = text.replace(nakedUrlRegex, function (wholeMatch, leadingMDChars, url, urlPrefix) {
+ // we now will start traversing the url from the front to back, looking for punctuation chars [_*~,;:.!?\)\]]
+ const len = url.length;
+ let suffix = '';
+
+ for (let i = len - 1; i >= 0; --i) {
+ let char = url.charAt(i);
+ if (/[_*~,;:.!?]/.test(char)) {
+ // it's a punctuation char so we remove it from the url
+ url = url.slice(0, -1);
+ // and prepend it to the suffix
+ suffix = char + suffix;
+ } else if (/[)\]]/.test(char)) {
+ // it's a parenthesis so we need to check for "balance" (kinda)
+ let opPar, clPar;
+ if (/\)/.test(char)) {
+ // it's a curved parenthesis
+ opPar = url.match(/\(/g) || [];
+ clPar = url.match(/\)/g);
+ } else {
+ // it's a squared parenthesis
+ opPar = url.match(/\[/g) || [];
+ clPar = url.match(/]/g);
+ }
+ if (opPar.length < clPar.length) {
+ // there are more closing Parenthesis than opening so chop it!!!!!
+ url = url.slice(0, -1);
+ // and prepend it to the suffix
+ suffix = char + suffix;
+ } else {
+ // it's (kinda) balanced so our work is done
+ break;
+ }
+ } else {
+ // it's not a punctuation or a parenthesis so our work is done
+ break;
+ }
+ }
+
+ // we copy the treated url to the text variable
+ let txt = url;
+ // finally, if it's a www shortcut, we prepend http
+ url = (urlPrefix === 'www.') ? 'http://' + url : url;
+
+ // url part is done so let's take care of text now
+ // we need to escape the text (because of links such as www.example.com/foo__bar__baz)
+ txt = txt.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
+
+ // and return the link tag, with the leadingMDChars and suffix. The leadingMDChars are added at the end too because
+ // we consumed those characters in the regexp
+ return leadingMDChars +
+ writeAnchorTag ('autoLink', nakedUrlRegex, wholeMatch, txt, null, url) +
+ suffix +
+ leadingMDChars;
+ });
+
+ // 8.2. Now check for naked mail
+ let nakedMailRegex = /(^|\s)(?:mailto:)?([A-Za-z\d!#$%&'*+-/=?^_`{|}~.]+@[-a-z\d]+(\.[-a-z\d]+)*\.[a-z]+)(?=$|\s)/gmi;
+ text = text.replace(nakedMailRegex, function (wholeMatch, leadingChar, mail) {
+ const m = parseMail(mail);
+ return leadingChar + writeAnchorTag ('autoLink', nakedMailRegex, wholeMatch, m.mail, null, m.url);
+ });
+ }
+
+ let afterEvent = new showdown.Event('makehtml.link.onEnd', text);
+ afterEvent
+ .setOutput(text)
+ ._setGlobals(globals)
+ ._setOptions(options);
+ afterEvent = globals.converter.dispatch(afterEvent);
+ return afterEvent.output;
});
diff --git a/src/subParsers/makehtml/links.old.js b/src/subParsers/makehtml/links.old.js
deleted file mode 100644
index 70c2a07..0000000
--- a/src/subParsers/makehtml/links.old.js
+++ /dev/null
@@ -1,429 +0,0 @@
-////
-// makehtml/links.js
-// Copyright (c) 2018 ShowdownJS
-//
-// Transforms MD links into `` html anchors
-//
-// A link contains link text (the visible text), a link destination (the URI that is the link destination), and
-// optionally a link title. There are two basic kinds of links in Markdown.
-// In inline links the destination and title are given immediately after the link text.
-// In reference links the destination and title are defined elsewhere in the document.
-//
-// ***Author:***
-// - Estevão Soares dos Santos (Tivie)
-////
-
-(function () {
- /**
- * Helper function: Wrapper function to pass as second replace parameter
- *
- * @param {RegExp} rgx
- * @param {string} evtRootName
- * @param {{}} options
- * @param {{}} globals
- * @param {boolean} emptyCase
- * @returns {Function}
- */
- function replaceAnchorTagReference (rgx, evtRootName, options, globals, emptyCase) {
- emptyCase = !!emptyCase;
- return function (wholeMatch, text, id, url, m5, m6, title) {
- // bail we we find 2 newlines somewhere
- if (/\n\n/.test(wholeMatch)) {
- return wholeMatch;
- }
-
- var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
- return writeAnchorTag(evt, options, globals, emptyCase);
- };
- }
-
- function replaceAnchorTagBaseUrl (rgx, evtRootName, options, globals, emptyCase) {
- return function (wholeMatch, text, id, url, m5, m6, title) {
- url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url);
-
- var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
- return writeAnchorTag(evt, options, globals, emptyCase);
- };
- }
-
- /**
- * TODO Normalize this
- * Helper function: Create a capture event
- * @param {RegExp} rgx
- * @param {String} evtName Event name
- * @param {String} wholeMatch
- * @param {String} text
- * @param {String} id
- * @param {String} url
- * @param {String} title
- * @param {{}} options
- * @param {{}} globals
- * @returns {showdown.Event|*}
- */
- function createEvent (rgx, evtName, wholeMatch, text, id, url, title, options, globals) {
- return globals.converter._dispatch(evtName, wholeMatch, options, globals, {
- regexp: rgx,
- matches: {
- wholeMatch: wholeMatch,
- text: text,
- id: id,
- url: url,
- title: title
- }
- });
- }
-
- /**
- * Helper Function: Normalize and write an anchor tag based on passed parameters
- * @param evt
- * @param options
- * @param globals
- * @param {boolean} emptyCase
- * @returns {string}
- */
- function writeAnchorTag (evt, options, globals, emptyCase) {
-
- var wholeMatch = evt.matches.wholeMatch;
- var text = evt.matches.text;
- var id = evt.matches.id;
- var url = evt.matches.url;
- var title = evt.matches.title;
- var target = '';
-
- if (!title) {
- title = '';
- }
- id = (id) ? id.toLowerCase() : '';
-
- if (emptyCase) {
- url = '';
- } else if (!url) {
- if (!id) {
- // lower-case and turn embedded newlines into spaces
- id = text.toLowerCase().replace(/ ?\n/g, ' ');
- }
- url = '#' + id;
-
- if (!showdown.helper.isUndefined(globals.gUrls[id])) {
- url = globals.gUrls[id];
- if (!showdown.helper.isUndefined(globals.gTitles[id])) {
- title = globals.gTitles[id];
- }
- } else {
- return wholeMatch;
- }
- }
- //url = showdown.helper.escapeCharacters(url, '*_:~', false); // replaced line to improve performance
- url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
-
- if (title !== '' && title !== null) {
- title = title.replace(/"/g, '"');
- //title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
- title = title.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
- title = ' title="' + title + '"';
- }
-
- // optionLinksInNewWindow only applies
- // to external links. Hash links (#) open in same page
- if (options.openLinksInNewWindow && !/^#/.test(url)) {
- // escaped _
- target = ' rel="noopener noreferrer" target="¨E95Eblank"';
- }
-
- // Text can be a markdown element, so we run through the appropriate parsers
- text = showdown.subParser('makehtml.codeSpan')(text, options, globals);
- text = showdown.subParser('makehtml.emoji')(text, options, globals);
- text = showdown.subParser('makehtml.underline')(text, options, globals);
- text = showdown.subParser('makehtml.emphasisAndStrong')(text, options, globals);
- text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
- text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
- text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
-
- //evt = createEvent(rgx, evtRootName + '.captureEnd', wholeMatch, text, id, url, title, options, globals);
-
- var result = '' + text + '';
-
- //evt = createEvent(rgx, evtRootName + '.beforeHash', wholeMatch, text, id, url, title, options, globals);
-
- result = showdown.subParser('makehtml.hashHTMLSpans')(result, options, globals);
-
- return result;
- }
-
- var evtRootName = 'makehtml.links';
-
- /**
- * Turn Markdown link shortcuts into XHTML tags.
- */
- showdown.subParser('makehtml.links', function (text, options, globals) {
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- // 1. Handle reference-style links: [link text] [id]
- text = showdown.subParser('makehtml.links.reference')(text, options, globals);
-
- // 2. Handle inline-style links: [link text](url "optional title")
- text = showdown.subParser('makehtml.links.inline')(text, options, globals);
-
- // 3. Handle reference-style shortcuts: [link text]
- // These must come last in case there's a [link text][1] or [link text](/foo)
- text = showdown.subParser('makehtml.links.referenceShortcut')(text, options, globals);
-
- // 4. Handle angle brackets links -> ``
- // Must come after links, because you can use < and > delimiters in inline links like [this]().
- text = showdown.subParser('makehtml.links.angleBrackets')(text, options, globals);
-
- // 5. Handle GithubMentions (if option is enabled)
- text = showdown.subParser('makehtml.links.ghMentions')(text, options, globals);
-
- // 6. Handle tags and img tags
- text = text.replace(/]*>[\s\S]*<\/a>/g, function (wholeMatch) {
- return showdown.helper._hashHTMLSpan(wholeMatch, globals);
- });
-
- text = text.replace(/]*\/?>/g, function (wholeMatch) {
- return showdown.helper._hashHTMLSpan(wholeMatch, globals);
- });
-
- // 7. Handle naked links (if option is enabled)
- text = showdown.subParser('makehtml.links.naked')(text, options, globals);
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
- return text;
- });
-
- /**
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.inline', function (text, options, globals) {
- var evtRootName = evtRootName + '.inline';
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- // 1. Look for empty cases: []() and [empty]() and []("title")
- var rgxEmpty = /\[(.*?)]()()()()\( ?>? ?(?:["'](.*)["'])?\)/g;
- text = text.replace(rgxEmpty, replaceAnchorTagBaseUrl(rgxEmpty, evtRootName, options, globals, true));
-
- // 2. Look for cases with crazy urls like ./image/cat1).png
- var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
- text = text.replace(rgxCrazy, replaceAnchorTagBaseUrl(rgxCrazy, evtRootName, options, globals));
-
- // 3. inline links with no title or titles wrapped in ' or ":
- // [text](url.com) || [text]() || [text](url.com "title") || [text]( "title")
- //var rgx2 = /\[[ ]*[\s]?[ ]*([^\n\[\]]*?)[ ]*[\s]?[ ]*] ?()\([ ]*[\s]?[ ]*([^\s'"]*)>?(?:[ ]*[\n]?[ ]*()(['"])(.*?)\5)?[ ]*[\s]?[ ]*\)/; // this regex is too slow!!!
- var rgx2 = /\[([\S ]*?)]\s?()\( *([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s*(?:()(['"])(.*?)\5)? *\)/g;
- text = text.replace(rgx2, replaceAnchorTagBaseUrl(rgx2, evtRootName, options, globals));
-
- // 4. inline links with titles wrapped in (): [foo](bar.com (title))
- var rgx3 = /\[([\S ]*?)]\s?()\( *([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s+()()\((.*?)\) *\)/g;
- text = text.replace(rgx3, replaceAnchorTagBaseUrl(rgx3, evtRootName, options, globals));
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
-
- return text;
- });
-
- /**
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.reference', function (text, options, globals) {
- var evtRootName = evtRootName + '.reference';
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
- text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
-
- return text;
- });
-
- /**
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.referenceShortcut', function (text, options, globals) {
- var evtRootName = evtRootName + '.referenceShortcut';
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- var rgx = /\[([^\[\]]+)]()()()()()/g;
- text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals));
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
-
- return text;
- });
-
- /**
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.ghMentions', function (text, options, globals) {
- var evtRootName = evtRootName + 'ghMentions';
-
- if (!options.ghMentions) {
- return text;
- }
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- var rgx = /(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gi;
-
- text = text.replace(rgx, function (wholeMatch, st, escape, mentions, username) {
- // bail if the mentions was escaped
- if (escape === '\\') {
- return st + mentions;
- }
-
- // check if options.ghMentionsLink is a string
- // TODO Validation should be done at initialization not at runtime
- if (!showdown.helper.isString(options.ghMentionsLink)) {
- throw new Error('ghMentionsLink option must be a string');
- }
- var url = options.ghMentionsLink.replace(/{u}/g, username);
- var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, mentions, null, url, null, options, globals);
- // captureEnd Event is triggered inside writeAnchorTag function
- return st + writeAnchorTag(evt, options, globals);
- });
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
-
- return text;
- });
-
- /**
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.angleBrackets', function (text, options, globals) {
- var evtRootName = 'makehtml.links.angleBrackets';
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- // 1. Parse links first
- var urlRgx = /<(((?:https?|ftp):\/\/|www\.)[^'">\s]+)>/gi;
- text = text.replace(urlRgx, function (wholeMatch, url, urlStart) {
- var text = url;
- url = (urlStart === 'www.') ? 'http://' + url : url;
- var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
- return writeAnchorTag(evt, options, globals);
- });
-
- // 2. Then Mail Addresses
- var mailRgx = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
- text = text.replace(mailRgx, function (wholeMatch, mail) {
- var url = 'mailto:';
- mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
- if (options.encodeEmails) {
- url = showdown.helper.encodeEmailAddress(url + mail);
- mail = showdown.helper.encodeEmailAddress(mail);
- } else {
- url = url + mail;
- }
- var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
- return writeAnchorTag(evt, options, globals);
- });
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
- return text;
- });
-
- /**
- * TODO MAKE THIS WORK (IT'S NOT ACTIVATED)
- * TODO WRITE THIS DOCUMENTATION
- */
- showdown.subParser('makehtml.links.naked', function (text, options, globals) {
- if (!options.simplifiedAutoLink) {
- return text;
- }
-
- var evtRootName = 'makehtml.links.naked';
-
- text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
-
- // 2. Now we check for
- // we also include leading markdown magic chars [_*~] for cases like __https://www.google.com/foobar__
- var urlRgx = /([_*~]*?)(((?:https?|ftp):\/\/|www\.)[^\s<>"'`´.-][^\s<>"'`´]*?\.[a-z\d.]+[^\s<>"']*)\1/gi;
- text = text.replace(urlRgx, function (wholeMatch, leadingMDChars, url, urlPrefix) {
-
- // we now will start traversing the url from the front to back, looking for punctuation chars [_*~,;:.!?\)\]]
- var len = url.length;
- var suffix = '';
- for (var i = len - 1; i >= 0; --i) {
- var char = url.charAt(i);
-
- if (/[_*~,;:.!?]/.test(char)) {
- // it's a punctuation char
- // we remove it from the url
- url = url.slice(0, -1);
- // and prepend it to the suffix
- suffix = char + suffix;
- } else if (/\)/.test(char)) {
- var opPar = url.match(/\(/g) || [];
- var clPar = url.match(/\)/g);
-
- // it's a curved parenthesis so we need to check for "balance" (kinda)
- if (opPar.length < clPar.length) {
- // there are more closing Parenthesis than opening so chop it!!!!!
- url = url.slice(0, -1);
- // and prepend it to the suffix
- suffix = char + suffix;
- } else {
- // it's (kinda) balanced so our work is done
- break;
- }
- } else if (/]/.test(char)) {
- var opPar2 = url.match(/\[/g) || [];
- var clPar2 = url.match(/\]/g);
- // it's a squared parenthesis so we need to check for "balance" (kinda)
- if (opPar2.length < clPar2.length) {
- // there are more closing Parenthesis than opening so chop it!!!!!
- url = url.slice(0, -1);
- // and prepend it to the suffix
- suffix = char + suffix;
- } else {
- // it's (kinda) balanced so our work is done
- break;
- }
- } else {
- // it's not a punctuation or a parenthesis so our work is done
- break;
- }
- }
-
- // we copy the treated url to the text variable
- var text = url;
- // finally, if it's a www shortcut, we prepend http
- url = (urlPrefix === 'www.') ? 'http://' + url : url;
-
- // url part is done so let's take care of text now
- // we need to escape the text (because of links such as www.example.com/foo__bar__baz)
- text = text.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
-
- // finally we dispatch the event
- var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
-
- // and return the link tag, with the leadingMDChars and suffix. The leadingMDChars are added at the end too because
- // we consumed those characters in the regexp
- return leadingMDChars + writeAnchorTag(evt, options, globals) + suffix + leadingMDChars;
- });
-
- // 2. Then mails
- var mailRgx = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi;
- text = text.replace(mailRgx, function (wholeMatch, leadingChar, mail) {
- var url = 'mailto:';
- mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
- if (options.encodeEmails) {
- url = showdown.helper.encodeEmailAddress(url + mail);
- mail = showdown.helper.encodeEmailAddress(mail);
- } else {
- url = url + mail;
- }
- var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
- return leadingChar + writeAnchorTag(evt, options, globals);
- });
-
-
- text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
- return text;
- });
-})();
diff --git a/src/subParsers/makehtml/spanGamut.js b/src/subParsers/makehtml/spanGamut.js
index e16662e..794a115 100644
--- a/src/subParsers/makehtml/spanGamut.js
+++ b/src/subParsers/makehtml/spanGamut.js
@@ -20,7 +20,7 @@ showdown.subParser('makehtml.spanGamut', function (text, options, globals) {
// Process link and image tags. Images must come first,
// because ![foo][f] looks like a link.
text = showdown.subParser('makehtml.image')(text, options, globals);
- text = showdown.subParser('makehtml.links')(text, options, globals);
+ text = showdown.subParser('makehtml.link')(text, options, globals);
text = showdown.subParser('makehtml.emoji')(text, options, globals);
text = showdown.subParser('makehtml.underline')(text, options, globals);
diff --git a/test/functional/makehtml/testsuite.features.js b/test/functional/makehtml/testsuite.features.js
index add5f0b..5e8b18a 100644
--- a/test/functional/makehtml/testsuite.features.js
+++ b/test/functional/makehtml/testsuite.features.js
@@ -1,7 +1,7 @@
/**
* Created by Estevao on 08-06-2015.
*/
-var bootstrap = require('./makehtml.bootstrap.js'),
+const bootstrap = require('./makehtml.bootstrap.js'),
showdown = bootstrap.showdown,
assertion = bootstrap.assertion,
testsuite = bootstrap.getTestSuite('test/functional/makehtml/cases/features/'),
@@ -29,8 +29,8 @@ describe('makeHtml() features testsuite', function () {
'use strict';
describe('issues', function () {
- for (var i = 0; i < testsuite.length; ++i) {
- var converter;
+ for (let i = 0; i < testsuite.length; ++i) {
+ let converter;
if (testsuite[i].name === '#143.support-image-dimensions') {
converter = new showdown.Converter({parseImgDimensions: true});
} else if (testsuite[i].name === '#69.header-level-start') {
@@ -114,9 +114,9 @@ describe('makeHtml() features testsuite', function () {
/** test Table Syntax Support **/
describe('table support', function () {
- var converter,
+ let converter,
suite = tableSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'basic-with-header-ids') {
converter = new showdown.Converter({tables: true, tablesHeaderId: true});
} else if (suite[i].name === '#179.parse-md-in-table-ths') {
@@ -130,9 +130,9 @@ describe('makeHtml() features testsuite', function () {
/** test simplifiedAutoLink Support **/
describe('simplifiedAutoLink support in', function () {
- var converter,
+ let converter,
suite = simplifiedAutoLinkSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'emphasis-and-strikethrough') {
converter = new showdown.Converter({simplifiedAutoLink: true, strikethrough: true});
} else if (suite[i].name === 'disallow-underscores') {
@@ -148,9 +148,9 @@ describe('makeHtml() features testsuite', function () {
/** test openLinksInNewWindow support **/
describe('openLinksInNewWindow support in', function () {
- var converter,
+ let converter,
suite = openLinksInNewWindowSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'simplifiedAutoLink') {
converter = new showdown.Converter({openLinksInNewWindow: true, simplifiedAutoLink: true});
} else {
@@ -162,9 +162,9 @@ describe('makeHtml() features testsuite', function () {
/** test disableForced4SpacesIndentedSublists support **/
describe('disableForced4SpacesIndentedSublists support in', function () {
- var converter,
+ let converter,
suite = disableForced4SpacesIndentedSublistsSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({disableForced4SpacesIndentedSublists: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -172,9 +172,9 @@ describe('makeHtml() features testsuite', function () {
/** test rawHeaderId support **/
describe('rawHeaderId support', function () {
- var converter,
+ let converter,
suite = rawHeaderIdSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'with-prefixHeaderId') {
converter = new showdown.Converter({rawHeaderId: true, prefixHeaderId: '/prefix/'});
} else {
@@ -186,9 +186,9 @@ describe('makeHtml() features testsuite', function () {
/** test rawPrefixHeaderId support **/
describe('rawPrefixHeaderId support', function () {
- var converter,
+ let converter,
suite = rawPrefixHeaderIdSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({rawPrefixHeaderId: true, prefixHeaderId: '/prefix/'});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -196,7 +196,7 @@ describe('makeHtml() features testsuite', function () {
/** test emojis support **/
describe('emojis support', function () {
- var converter,
+ let converter,
suite = emojisSuite,
imgSrcRegexp = /]+src=("https?:\/\/[^"]+"|'https?:\/\/[^']+')/g;
@@ -222,7 +222,7 @@ describe('makeHtml() features testsuite', function () {
};
}
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'simplifiedautolinks') {
converter = new showdown.Converter({emoji: true, simplifiedAutoLink: true});
} else {
@@ -231,7 +231,7 @@ describe('makeHtml() features testsuite', function () {
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
- var imgUrl = imgSrcRegexp.exec(suite[i].expected);
+ let imgUrl = imgSrcRegexp.exec(suite[i].expected);
if (imgUrl) {
it('should use a working emoji URL', testImageUrlExists(imgUrl[1]));
}
@@ -240,9 +240,9 @@ describe('makeHtml() features testsuite', function () {
/** test underline support **/
describe('underline support', function () {
- var converter,
+ let converter,
suite = underlineSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'simplifiedautolinks') {
converter = new showdown.Converter({underline: true, simplifiedAutoLink: true});
} else {
@@ -254,9 +254,9 @@ describe('makeHtml() features testsuite', function () {
/** test ellipsis option **/
describe('ellipsis option', function () {
- var converter,
+ let converter,
suite = ellipsisSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({ellipsis: false});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -264,9 +264,9 @@ describe('makeHtml() features testsuite', function () {
/** test literalMidWordUnderscores option **/
describe('literalMidWordUnderscores option', function () {
- var converter,
+ let converter,
suite = literalMidWordUnderscoresSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({literalMidWordUnderscores: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -275,9 +275,9 @@ describe('makeHtml() features testsuite', function () {
/** test literalMidWordAsterisks option **/
/*
describe('literalMidWordAsterisks option', function () {
- var converter,
+ let converter,
suite = literalMidWordAsterisksSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({literalMidWordAsterisks: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -286,9 +286,9 @@ describe('makeHtml() features testsuite', function () {
/** test completeHTMLDocument option **/
describe('completeHTMLDocument option', function () {
- var converter,
+ let converter,
suite = completeHTMLOutputSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({completeHTMLDocument: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
@@ -297,10 +297,10 @@ describe('makeHtml() features testsuite', function () {
/** test metadata option **/
describe('metadata option', function () {
- var converter,
+ let converter,
suite = metadataSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
if (suite[i].name === 'embeded-in-output' ||
suite[i].name === 'embeded-two-consecutive-metadata-blocks' ||
suite[i].name === 'embeded-two-consecutive-metadata-blocks-different-symbols') {
@@ -316,10 +316,10 @@ describe('makeHtml() features testsuite', function () {
/** test metadata option **/
describe('splitAdjacentBlockquotes option', function () {
- var converter,
+ let converter,
suite = splitAdjacentBlockquotesSuite;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({splitAdjacentBlockquotes: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
@@ -327,10 +327,10 @@ describe('makeHtml() features testsuite', function () {
/** test moreStyling option **/
describe('moreStyling option', function () {
- var converter,
+ let converter,
suite = moreStyling;
- for (var i = 0; i < suite.length; ++i) {
+ for (let i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({moreStyling: true, tasklists: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
diff --git a/test/unit/events.js b/test/unit/events.js
index 31b1186..812c04d 100644
--- a/test/unit/events.js
+++ b/test/unit/events.js
@@ -4,14 +4,12 @@
describe('showdown.Event', function () {
'use strict';
- let eventList = {
-
- };
+ //let eventList = {};
describe('event listeners', function () {
it('should listen to triggered event', function () {
- })
+ });
});
});