mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
fix(links): a number of issues with links subparser
This is a major refactor of the links subparser, previously known as anchors subparser. Closes #355, #534 BREAKING CHANGE: `excludeTrailingPunctuationFromURLs` option was removed. This is now the default behavior
This commit is contained in:
parent
798bbe6751
commit
d3ebff7ef0
|
@ -14,7 +14,7 @@
|
|||
"quotmark": "single",
|
||||
"undef": false,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"onevar": true,
|
||||
|
|
12
README.md
12
README.md
|
@ -259,16 +259,8 @@ var defaultOptions = showdown.getDefaultOptions();
|
|||
<p>some text <a href="www.google.com">www.google.com</a>
|
||||
```
|
||||
|
||||
* **excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls.
|
||||
Punctuation excluded: `. ! ? ( )`. Only applies if **simplifiedAutoLink** option is set to `true`.
|
||||
|
||||
```md
|
||||
check this link www.google.com!
|
||||
```
|
||||
will be parsed as
|
||||
```html
|
||||
<p>check this link <a href="www.google.com">www.google.com</a>!</p>
|
||||
```
|
||||
* ~~**excludeTrailingPunctuationFromURLs**: (boolean) [default false] This option excludes trailing punctuation from autolinking urls.
|
||||
Punctuation excluded: `. ! ? ( )`. Only applies if **simplifiedAutoLink** option is set to `true`.~~
|
||||
|
||||
* **literalMidWordUnderscores**: (boolean) [default false] Turning this on will stop showdown from interpreting
|
||||
underscores in the middle of words as `<em>` and `<strong>` and instead treat them as literal underscores.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
This option is weird, hard to maintain and really... makes little sense.
|
||||
|
||||
- [ ] **excludeTrailingPunctuationFromURLs** (removal)
|
||||
- [X] **excludeTrailingPunctuationFromURLs** (removal)
|
||||
|
||||
This option will be removed and will be the default behavior from now on.
|
||||
|
||||
|
@ -89,16 +89,15 @@
|
|||
HTML and OTP extensions will be dropped in favor of Listener Extensions. We might even give them a new name
|
||||
|
||||
## Subparsers
|
||||
- [ ] **Anchors**: Revamp the anchors subparser so it calls strikethrough, bold, italic and underline directly
|
||||
- [ ] **autoLinks**: Fix some lingering bugs and issues with autolinks
|
||||
|
||||
- [X] **Anchors**: Revamp the anchors subparser so it calls strikethrough, bold, italic and underline directly
|
||||
- [X] **autoLinks**: Fix some lingering bugs and issues with autolinks
|
||||
|
||||
## Priority Bugs
|
||||
- [ ] **#355**: *simplifiedAutoLink URLs inside parenthesis followed by another character are not parsed correctly*
|
||||
- [X] **#355**: *simplifiedAutoLink URLs inside parenthesis followed by another character are not parsed correctly*
|
||||
- [X] **#534**: *Multiple parentheses () in url link is not parsed correctly*
|
||||
- [ ] **#367**: *sublists rendering with 2 spaces* - related to disableForced4SpacesIndentedSublists option...
|
||||
- [ ] **#537**: *master branch doesn't work in a web worker*
|
||||
|
||||
|
||||
## CLI
|
||||
- [ ] Refactor the CLI
|
||||
- [ ] **#381**: *Support for src and dst directories in showdown cli*
|
||||
|
@ -114,6 +113,7 @@
|
|||
- [ ] **#486**: *A backslash at the end of the line is a hard line break*
|
||||
- [ ] **#548**: *anchors and images of subParser are errors when they are specific strings*
|
||||
- [ ] **#549**: *Strange parsing issue with `<pre><code>`*
|
||||
- [ ] Rethink the global variable
|
||||
|
||||
## NEW Features
|
||||
|
||||
|
|
BIN
dist/showdown.js
vendored
BIN
dist/showdown.js
vendored
Binary file not shown.
BIN
dist/showdown.js.map
vendored
BIN
dist/showdown.js.map
vendored
Binary file not shown.
BIN
dist/showdown.min.js
vendored
BIN
dist/showdown.min.js
vendored
Binary file not shown.
BIN
dist/showdown.min.js.map
vendored
BIN
dist/showdown.min.js.map
vendored
Binary file not shown.
|
@ -396,15 +396,26 @@ showdown.helper.unescapeHTMLEntities = function (txt) {
|
|||
.replace(/&/g, '&');
|
||||
};
|
||||
|
||||
showdown.helper._hashHTMLSpan = function (html, globals) {
|
||||
return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
|
||||
};
|
||||
|
||||
/**
|
||||
* Showdown's Event Object
|
||||
* @param {string} name Name of the event
|
||||
* @param {string} text Text
|
||||
* @param {{}} params optional. params of the event
|
||||
* @constructor
|
||||
*/
|
||||
showdown.helper.Event = function (name, text, params) {
|
||||
'use strict';
|
||||
|
||||
var regexp = params.regexp || null;
|
||||
var matches = params.matches || {};
|
||||
var options = params.options || {};
|
||||
var converter = params.converter || null;
|
||||
var globals = params.globals || {};
|
||||
|
||||
/**
|
||||
* Get the name of the event
|
||||
* @returns {string}
|
||||
|
@ -417,12 +428,6 @@ showdown.helper.Event = function (name, text, params) {
|
|||
return name;
|
||||
};
|
||||
|
||||
var regexp = params.regexp || null;
|
||||
var matches = params.matches || {};
|
||||
var options = params.options || {};
|
||||
var converter = params.converter || null;
|
||||
var globals = params.globals || {};
|
||||
|
||||
this._stopExecution = false;
|
||||
|
||||
this.parsedText = params.parsedText || null;
|
||||
|
@ -430,30 +435,39 @@ showdown.helper.Event = function (name, text, params) {
|
|||
this.getRegexp = function () {
|
||||
return regexp;
|
||||
};
|
||||
|
||||
this.getOptions = function () {
|
||||
return options;
|
||||
};
|
||||
|
||||
this.getConverter = function () {
|
||||
return converter;
|
||||
};
|
||||
|
||||
this.getGlobals = function () {
|
||||
return globals;
|
||||
};
|
||||
|
||||
this.getCapturedText = function () {
|
||||
return text;
|
||||
};
|
||||
|
||||
this.getText = function () {
|
||||
return text;
|
||||
};
|
||||
|
||||
this.setText = function (newText) {
|
||||
text = newText;
|
||||
};
|
||||
|
||||
this.getMatches = function () {
|
||||
return matches;
|
||||
};
|
||||
|
||||
this.setMatches = function (newMatches) {
|
||||
matches = newMatches;
|
||||
};
|
||||
|
||||
this.preventDefault = function (bool) {
|
||||
this._stopExecution = !bool;
|
||||
};
|
||||
|
@ -485,7 +499,8 @@ if (typeof(console) === 'undefined') {
|
|||
* We declare some common regexes to improve performance
|
||||
*/
|
||||
showdown.helper.regexes = {
|
||||
asteriskDashAndColon: /([*_:~])/g
|
||||
asteriskDashTildeAndColon: /([*_:~])/g,
|
||||
asteriskDashAndTilde: /([*_~])/g
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,11 +51,6 @@ function getDefaultOpts (simple) {
|
|||
describe: 'Turn on/off GFM autolink style',
|
||||
type: 'boolean'
|
||||
},
|
||||
excludeTrailingPunctuationFromURLs: {
|
||||
defaultValue: false,
|
||||
describe: 'Excludes trailing punctuation from links generated with autoLinking',
|
||||
type: 'boolean'
|
||||
},
|
||||
literalMidWordUnderscores: {
|
||||
defaultValue: false,
|
||||
describe: 'Parse midword underscores as literal underscores',
|
||||
|
|
|
@ -11,7 +11,6 @@ var showdown = {},
|
|||
github: {
|
||||
omitExtraWLInCodeBlocks: true,
|
||||
simplifiedAutoLink: true,
|
||||
excludeTrailingPunctuationFromURLs: true,
|
||||
literalMidWordUnderscores: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
|
@ -35,7 +34,6 @@ var showdown = {},
|
|||
omitExtraWLInCodeBlocks: true,
|
||||
parseImgDimensions: true,
|
||||
simplifiedAutoLink: true,
|
||||
excludeTrailingPunctuationFromURLs: true,
|
||||
literalMidWordUnderscores: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
// url allowed chars [a-z\d_.~:/?#[]@!$&'()*+,;=-]
|
||||
|
||||
var simpleURLRegex = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,
|
||||
simpleURLRegex2 = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,
|
||||
delimUrlRegex = /()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,
|
||||
simpleMailRegex = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi,
|
||||
delimMailRegex = /<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
|
||||
|
||||
replaceLink = function (options) {
|
||||
'use strict';
|
||||
return function (wm, leadingMagicChars, link, m2, m3, trailingPunctuation, trailingMagicChars) {
|
||||
link = link.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
var lnkTxt = link,
|
||||
append = '',
|
||||
target = '',
|
||||
lmc = leadingMagicChars || '',
|
||||
tmc = trailingMagicChars || '';
|
||||
if (/^www\./i.test(link)) {
|
||||
link = link.replace(/^www\./i, 'http://www.');
|
||||
}
|
||||
if (options.excludeTrailingPunctuationFromURLs && trailingPunctuation) {
|
||||
append = trailingPunctuation;
|
||||
}
|
||||
if (options.openLinksInNewWindow) {
|
||||
target = ' target="¨E95Eblank"';
|
||||
}
|
||||
return lmc + '<a href="' + link + '"' + target + '>' + lnkTxt + '</a>' + append + tmc;
|
||||
};
|
||||
},
|
||||
|
||||
replaceMail = function (options, globals) {
|
||||
'use strict';
|
||||
return function (wholeMatch, b, mail) {
|
||||
var href = 'mailto:';
|
||||
b = b || '';
|
||||
mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
|
||||
if (options.encodeEmails) {
|
||||
href = showdown.helper.encodeEmailAddress(href + mail);
|
||||
mail = showdown.helper.encodeEmailAddress(mail);
|
||||
} else {
|
||||
href = href + mail;
|
||||
}
|
||||
return b + '<a href="' + href + '">' + mail + '</a>';
|
||||
};
|
||||
};
|
||||
|
||||
showdown.subParser('makehtml.autoLinks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('makehtml.autoLinks.before', text, options, globals).getText();
|
||||
|
||||
text = text.replace(delimUrlRegex, replaceLink(options));
|
||||
text = text.replace(delimMailRegex, replaceMail(options, globals));
|
||||
|
||||
text = globals.converter._dispatch('makehtml.autoLinks.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
showdown.subParser('makehtml.simplifiedAutoLinks', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
if (!options.simplifiedAutoLink) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('makehtml.simplifiedAutoLinks.before', text, options, globals).getText();
|
||||
|
||||
if (options.excludeTrailingPunctuationFromURLs) {
|
||||
text = text.replace(simpleURLRegex2, replaceLink(options));
|
||||
} else {
|
||||
text = text.replace(simpleURLRegex, replaceLink(options));
|
||||
}
|
||||
text = text.replace(simpleMailRegex, replaceMail(options, globals));
|
||||
|
||||
text = globals.converter._dispatch('makehtml.simplifiedAutoLinks.after', text, options, globals).getText();
|
||||
|
||||
return text;
|
||||
});
|
|
@ -5,32 +5,26 @@ showdown.subParser('makehtml.hashHTMLSpans', function (text, options, globals) {
|
|||
'use strict';
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLSpans.before', text, options, globals).getText();
|
||||
|
||||
function hashHTMLSpan (html) {
|
||||
return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
|
||||
}
|
||||
|
||||
// Hash Self Closing tags
|
||||
text = text.replace(/<[^>]+?\/>/gi, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash tags without properties
|
||||
text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash tags with properties
|
||||
text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
// Hash self closing tags without />
|
||||
text = text.replace(/<[^>]+?>/gi, function (wm) {
|
||||
return hashHTMLSpan(wm);
|
||||
return showdown.helper._hashHTMLSpan(wm, globals);
|
||||
});
|
||||
|
||||
/*showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');*/
|
||||
|
||||
text = globals.converter._dispatch('makehtml.hashHTMLSpans.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
|
|
|
@ -56,16 +56,16 @@ showdown.subParser('makehtml.images', function (text, options, globals) {
|
|||
altText = altText
|
||||
.replace(/"/g, '"')
|
||||
//altText = showdown.helper.escapeCharacters(altText, '*_', false);
|
||||
.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
//url = showdown.helper.escapeCharacters(url, '*_', false);
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
var result = '<img src="' + url + '" alt="' + altText + '"';
|
||||
|
||||
if (title && showdown.helper.isString(title)) {
|
||||
title = title
|
||||
.replace(/"/g, '"')
|
||||
//title = showdown.helper.escapeCharacters(title, '*_', false);
|
||||
.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
|
||||
result += ' title="' + title + '"';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,124 +1,419 @@
|
|||
/**
|
||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||
*/
|
||||
showdown.subParser('makehtml.links', function (text, options, globals) {
|
||||
'use strict';
|
||||
////
|
||||
// makehtml/links.js
|
||||
// Copyright (c) 2018 ShowdownJS
|
||||
//
|
||||
// Transforms MD links into `<a>` 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) <https://github.com/tivie>
|
||||
////
|
||||
|
||||
text = globals.converter._dispatch('makehtml.links.before', text, options, globals).getText();
|
||||
|
||||
var writeAnchorTag = function (rgx) {
|
||||
|
||||
return function (wholeMatch1, linkText1, linkId1, url1, m5, m6, title1) {
|
||||
var evt = globals.converter._dispatch('makehtml.links.capturestart', wholeMatch1, options, globals, {
|
||||
regexp: rgx,
|
||||
matches: {
|
||||
wholeMatch: wholeMatch1,
|
||||
linkText: linkText1,
|
||||
linkId: linkId1,
|
||||
url: url1,
|
||||
title: title1
|
||||
}
|
||||
});
|
||||
|
||||
var wholeMatch = evt.getMatches().wholeMatch;
|
||||
var linkText = evt.getMatches().linkText;
|
||||
var linkId = evt.getMatches().linkId;
|
||||
var url = evt.getMatches().url;
|
||||
var title = evt.getMatches().title;
|
||||
|
||||
if (showdown.helper.isUndefined(title)) {
|
||||
title = '';
|
||||
}
|
||||
linkId = linkId.toLowerCase();
|
||||
|
||||
// Special case for explicit empty url
|
||||
if (wholeMatch.search(/\(<?\s*>? ?(['"].*['"])?\)$/m) > -1) {
|
||||
url = '';
|
||||
} else if (!url) {
|
||||
if (!linkId) {
|
||||
// lower-case and turn embedded newlines into spaces
|
||||
linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
|
||||
}
|
||||
url = '#' + linkId;
|
||||
|
||||
if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
|
||||
url = globals.gUrls[linkId];
|
||||
if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
|
||||
title = globals.gTitles[linkId];
|
||||
}
|
||||
} else {
|
||||
return wholeMatch;
|
||||
}
|
||||
(function () {
|
||||
/**
|
||||
* Helper function: Wrapper function to pass as second replace parameter
|
||||
*
|
||||
* @param {RegExp} rgx
|
||||
* @param {string} evtRootName
|
||||
* @param {{}} options
|
||||
* @param {{}} globals
|
||||
* @returns {Function}
|
||||
*/
|
||||
function replaceAnchorTag (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;
|
||||
}
|
||||
|
||||
//url = showdown.helper.escapeCharacters(url, '*_:~', false); // replaced line to improve performance
|
||||
url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
|
||||
var result = '<a href="' + url + '"';
|
||||
|
||||
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.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
result += ' title="' + title + '"';
|
||||
}
|
||||
|
||||
// optionLinksInNewWindow only applies
|
||||
// to external links. Hash links (#) open in same page
|
||||
if (options.openLinksInNewWindow && !/^#/.test(url)) {
|
||||
// escaped _
|
||||
result += ' target="¨E95Eblank"';
|
||||
}
|
||||
|
||||
result += '>' + linkText + '</a>';
|
||||
|
||||
return result;
|
||||
var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
|
||||
return writeAnchorTag(evt, options, globals, emptyCase);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
var referenceRegex = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
|
||||
// First, handle reference-style links: [link text] [id]
|
||||
text = text.replace(referenceRegex, writeAnchorTag(referenceRegex));
|
||||
/**
|
||||
* 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.helper.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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Next, inline-style links: [link text](url "optional title")
|
||||
// cases with crazy urls like ./image/cat1).png
|
||||
var inlineRegexCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
|
||||
text = text.replace(inlineRegexCrazy, writeAnchorTag(inlineRegexCrazy));
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// normal cases
|
||||
var inlineRegex = /\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
|
||||
text = text.replace(inlineRegex, writeAnchorTag(inlineRegex));
|
||||
var wholeMatch = evt.getMatches().wholeMatch;
|
||||
var text = evt.getMatches().text;
|
||||
var id = evt.getMatches().id;
|
||||
var url = evt.getMatches().url;
|
||||
var title = evt.getMatches().title;
|
||||
var target = '';
|
||||
|
||||
// handle reference-style shortcuts: [link text]
|
||||
// These must come last in case you've also got [link test][1]
|
||||
// or [link test](/foo)
|
||||
var referenceShortcutRegex = /\[([^\[\]]+)]()()()()()/g;
|
||||
text = text.replace(referenceShortcutRegex, writeAnchorTag(referenceShortcutRegex));
|
||||
if (!title) {
|
||||
title = '';
|
||||
}
|
||||
id = (id) ? id.toLowerCase() : '';
|
||||
|
||||
// Lastly handle GithubMentions if option is enabled
|
||||
if (options.ghMentions) {
|
||||
text = text.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gmi, function (wm, st, escape, mentions, username) {
|
||||
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 = ' target="¨E95Eblank"';
|
||||
}
|
||||
|
||||
// Text can be a markdown element, so we run through the appropriate parsers
|
||||
text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.emoji')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.underline')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.italicsAndBold')(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 = '<a href="' + url + '"' + title + target + '>' + text + '</a>';
|
||||
|
||||
//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 <a> 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 -> `<http://example.com/>`
|
||||
// Must come after links, because you can use < and > delimiters in inline links like [this](<url>).
|
||||
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 <a> tags and img tags
|
||||
text = text.replace(/<a\s[^>]*>[\s\S]*<\/a>/g, function (wholeMatch) {
|
||||
return showdown.helper._hashHTMLSpan(wholeMatch, globals);
|
||||
});
|
||||
|
||||
text = text.replace(/<img\s[^>]*\/?>/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, replaceAnchorTag(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, replaceAnchorTag(rgxCrazy, evtRootName, options, globals));
|
||||
|
||||
// 3. inline links with no title or titles wrapped in ' or ":
|
||||
// [text](url.com) || [text](<url.com>) || [text](url.com "title") || [text](<url.com> "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, replaceAnchorTag(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, replaceAnchorTag(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, replaceAnchorTag(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, replaceAnchorTag(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
|
||||
// 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 lnk = options.ghMentionsLink.replace(/\{u}/g, username),
|
||||
target = '';
|
||||
if (options.openLinksInNewWindow) {
|
||||
target = ' target="¨E95Eblank"';
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// lnk = showdown.helper.escapeCharacters(lnk, '*_', false); // replaced line to improve performance
|
||||
lnk = lnk.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
|
||||
// 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;
|
||||
|
||||
return st + '<a href="' + lnk + '"' + target + '>' + mentions + '</a>';
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
|
||||
text = globals.converter._dispatch('makehtml.links.after', text, options, globals).getText();
|
||||
return text;
|
||||
});
|
||||
// 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;
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
showdown.subParser('makehtml.spanGamut', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
text = globals.converter._dispatch('smakehtml.panGamut.before', text, options, globals).getText();
|
||||
text = globals.converter._dispatch('makehtml.span.before', text, options, globals).getText();
|
||||
|
||||
text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.encodeBackslashEscapes')(text, options, globals);
|
||||
|
@ -13,13 +14,13 @@ 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.images')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.links')(text, options, globals);
|
||||
|
||||
// Make links out of things like `<http://example.com/>`
|
||||
// Must come after links, because you can use < and >
|
||||
// delimiters in inline links like [this](<url>).
|
||||
text = showdown.subParser('makehtml.autoLinks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.simplifiedAutoLinks')(text, options, globals);
|
||||
text = globals.converter._dispatch('smakehtml.links.before', text, options, globals).getText();
|
||||
text = showdown.subParser('makehtml.links')(text, options, globals);
|
||||
text = globals.converter._dispatch('smakehtml.links.after', text, options, globals).getText();
|
||||
|
||||
//text = showdown.subParser('makehtml.autoLinks')(text, options, globals);
|
||||
//text = showdown.subParser('makehtml.simplifiedAutoLinks')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.emoji')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.underline')(text, options, globals);
|
||||
text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals);
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
showdown.subParser('makehtml.strikethrough', function (text, options, globals) {
|
||||
'use strict';
|
||||
|
||||
function parseInside (txt) {
|
||||
if (options.simplifiedAutoLink) {
|
||||
txt = showdown.subParser('makehtml.simplifiedAutoLinks')(txt, options, globals);
|
||||
}
|
||||
return '<del>' + txt + '</del>';
|
||||
}
|
||||
|
||||
if (options.strikethrough) {
|
||||
text = globals.converter._dispatch('makehtml.strikethrough.before', text, options, globals).getText();
|
||||
text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return parseInside(txt); });
|
||||
text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return '<del>' + txt + '</del>'; });
|
||||
text = globals.converter._dispatch('makehtml.strikethrough.after', text, options, globals).getText();
|
||||
}
|
||||
|
||||
|
|
4994
test/functional/makehtml/cases/commonmark.testsuite.json
Normal file
4994
test/functional/makehtml/cases/commonmark.testsuite.json
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -2,5 +2,4 @@
|
|||
<p>www.foobar</p>
|
||||
<p><a href="http://www.foobar.com">www.foobar.com</a></p>
|
||||
<p><a href="http://foobar.com">http://foobar.com</a></p>
|
||||
<p><a href="https://www.foobar.com/baz?bazinga=nhecos;">https://www.foobar.com/baz?bazinga=nhecos;</a></p>
|
||||
<p><a href="http://www.google.com/">http://www.google.com</a></p>
|
||||
<p><a href="https://www.foobar.com/baz?bazinga=nhecos">https://www.foobar.com/baz?bazinga=nhecos</a>;</p>
|
||||
|
|
|
@ -7,5 +7,3 @@ www.foobar.com
|
|||
http://foobar.com
|
||||
|
||||
https://www.foobar.com/baz?bazinga=nhecos;
|
||||
|
||||
<a href="http://www.google.com/">http://www.google.com</a>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<p>(<a href="https://www.google.com">https://www.google.com</a>)!</p>
|
|
@ -0,0 +1 @@
|
|||
(https://www.google.com)!
|
|
@ -0,0 +1 @@
|
|||
<p><code>http://www.gmail.com</code></p>
|
|
@ -0,0 +1 @@
|
|||
`http://www.gmail.com`
|
|
@ -28,9 +28,9 @@
|
|||
<p><a href="http://j.mp">http://j.mp</a></p>
|
||||
<p><a href="ftp://foo.bar/baz">ftp://foo.bar/baz</a></p>
|
||||
<p><a href="http://foo.bar/?q=Test%20URL-encoded%20stuff">http://foo.bar/?q=Test%20URL-encoded%20stuff</a></p>
|
||||
<p><a href="http://مثال.إختبار">http://مثال.إختبار</a></p>
|
||||
<p><a href="http://例子.测试">http://例子.测试</a></p>
|
||||
<p><a href="http://उदाहरण.परीक्षा">http://उदाहरण.परीक्षा</a></p>
|
||||
<!-- http://مثال.إختبار -->
|
||||
<!-- http://例子.测试 -->
|
||||
<!-- http://उदाहरण.परीक्षा -->
|
||||
<p><a href="http://1337.net">http://1337.net</a></p>
|
||||
<p><a href="http://a.b-c.de">http://a.b-c.de</a></p>
|
||||
<p><a href="http://223.255.255.254">http://223.255.255.254</a></p>
|
||||
|
@ -41,6 +41,8 @@
|
|||
<!-- SHOULD PARTIALLY PASS -->
|
||||
<p><a href="http://foo.bar/foo(bar)baz">http://foo.bar/foo(bar)baz</a> quux</p>
|
||||
<p><a href="http://foo.bar?q=Spaces">http://foo.bar?q=Spaces</a> should be encoded</p>
|
||||
<p>http://.<a href="http://www.foo.bar/">www.foo.bar/</a></p>
|
||||
<p>http://.<a href="http://www.foo.bar./">www.foo.bar./</a></p>
|
||||
<!-- THESE ARE INVALID IPS BUT WE WILL LET THEM PASS -->
|
||||
<p><a href="http://10.1.1.1">http://10.1.1.1</a></p>
|
||||
<p><a href="http://10.1.1.254">http://10.1.1.254</a></p>
|
||||
|
@ -73,7 +75,4 @@
|
|||
<p>:// should fail</p>
|
||||
<p>http://-error-.invalid/</p>
|
||||
<p>http://-a.b.co</p>
|
||||
<p>http://a.b-.co</p>
|
||||
<p>http://3628126748</p>
|
||||
<p>http://.www.foo.bar/</p>
|
||||
<p>http://.www.foo.bar./</p>
|
||||
|
|
|
@ -58,11 +58,11 @@ ftp://foo.bar/baz
|
|||
|
||||
http://foo.bar/?q=Test%20URL-encoded%20stuff
|
||||
|
||||
http://مثال.إختبار
|
||||
<!-- http://مثال.إختبار -->
|
||||
|
||||
http://例子.测试
|
||||
<!-- http://例子.测试 -->
|
||||
|
||||
http://उदाहरण.परीक्षा
|
||||
<!-- http://उदाहरण.परीक्षा -->
|
||||
|
||||
http://1337.net
|
||||
|
||||
|
@ -72,7 +72,6 @@ http://223.255.255.254
|
|||
|
||||
https://foo_bar.example.com/
|
||||
|
||||
|
||||
<!-- WEIRD BUT SHOULD ALSO PASS -->
|
||||
|
||||
http://www.foo.bar./
|
||||
|
@ -85,6 +84,10 @@ http://foo.bar/foo(bar)baz quux
|
|||
|
||||
http://foo.bar?q=Spaces should be encoded
|
||||
|
||||
http://.www.foo.bar/
|
||||
|
||||
http://.www.foo.bar./
|
||||
|
||||
<!-- THESE ARE INVALID IPS BUT WE WILL LET THEM PASS -->
|
||||
http://10.1.1.1
|
||||
|
||||
|
@ -149,10 +152,4 @@ http://-error-.invalid/
|
|||
|
||||
http://-a.b.co
|
||||
|
||||
http://a.b-.co
|
||||
|
||||
http://3628126748
|
||||
|
||||
http://.www.foo.bar/
|
||||
|
||||
http://.www.foo.bar./
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<p><a href="http://www.google.com">http://www.google.com</a>!</p>
|
||||
<p><a href="http://www.google.com">http://www.google.com</a>!!!!!!!!!!!!!!!!</p>
|
||||
<p><a href="http://www.google.com/!!!!!!!!!!!!!!!!foobar">http://www.google.com/!!!!!!!!!!!!!!!!foobar</a></p>
|
||||
<p><a href="http://www.google.com/">http://www.google.com/</a>!!!!!!!!!!!!!!!! foobar</p>
|
||||
<p>(<a href="http://www.google.com/">http://www.google.com/</a>?!.)</p>
|
||||
<p><a href="http://www.google.com/?!.(">http://www.google.com/?!.(</a></p>
|
||||
<p><a href="http://www.google.com/a()">http://www.google.com/a()</a></p>
|
||||
<p><a href="http://www.google.com/a?!.()">http://www.google.com/a?!.()</a></p>
|
|
@ -0,0 +1,15 @@
|
|||
http://www.google.com!
|
||||
|
||||
http://www.google.com!!!!!!!!!!!!!!!!
|
||||
|
||||
http://www.google.com/!!!!!!!!!!!!!!!!foobar
|
||||
|
||||
http://www.google.com/!!!!!!!!!!!!!!!! foobar
|
||||
|
||||
(http://www.google.com/?!.)
|
||||
|
||||
http://www.google.com/?!.(
|
||||
|
||||
http://www.google.com/a()
|
||||
|
||||
http://www.google.com/a?!.()
|
|
@ -0,0 +1,2 @@
|
|||
<p>(<a href="https://www.google.com">https://www.google.com</a>)</p>
|
||||
<p>(<a href="https://www.google.com">https://www.google.com</a>!?;)</p>
|
|
@ -0,0 +1,5 @@
|
|||
(https://www.google.com)
|
||||
|
||||
(https://www.google.com!?;)
|
||||
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
var foo_bar_baz_bar_foo = "foo_bar_";
|
||||
</script>
|
||||
<p><a href="http://myurl.com/foo_bar_baz_bar_foo">foo_bar_baz foo_bar_baz_bar_foo <em>foo_bar baz_bar</em> baz_foo</a></p>
|
||||
<p><a href="http://myurl.com/foo_bar_baz_bar_foo" title="foo_bar_baz foo_bar_baz_bar_foo _foo_bar baz_bar_ baz_foo">foo_bar_baz foo_bar_baz_bar_foo <em>foo_bar baz_bar</em> baz_foo</a></p>
|
||||
<p><a href="http://myurl.com/foo_bar_baz_bar_foo" title="foo_bar_baz foo_bar_baz_bar_foo _foo_bar baz_bar_ baz_foo">foo_bar_baz foo_bar_baz_bar_foo _foo_bar baz_bar_ baz_foo</a></p>
|
||||
<p><img src="http://myurl.com/foo_bar_baz_bar_foo" alt="foo_bar_baz foo_bar_baz_bar_foo _foo_bar baz_bar_ baz_foo"></p>
|
||||
<h2 id="foo_bar_bazfoo_bar_baz_bar_foo_foo_barbaz_bar_baz_foo">foo_bar_baz foo_bar_baz_bar_foo <em>foo_bar baz_bar</em> baz_foo</h2>
|
||||
<h3 id="foo_bar_bazfoo_bar_baz_bar_foo_foo_barbaz_bar_baz_foo-1">foo_bar_baz foo_bar_baz_bar_foo <em>foo_bar baz_bar</em> baz_foo</h3>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<p>This is a <a href="https://en.wikipedia.org/wiki/Textile_(markup_language)" title="Textile">link</a>.</p>
|
||||
<p>This is another <a href="https://en.wikipedia.org/wiki/Textile_(markup_language)" title="Textile">link</a>.</p>
|
||||
<p><a href="./image/cat1).png" title="title">link</a></p>
|
||||
<p>This is another <a href="https://en.wikipedia.org/wiki/Textile_(markup_language)" title="Textile">link2</a>.</p>
|
||||
<p><a href="./image/cat1).png" title="title">link3</a></p>
|
||||
<p><a href="https://en.wikipedia.org/wiki/Textile_(markup_language)" title="Textile">link4</a>(this should work)</p>
|
||||
|
|
|
@ -2,6 +2,8 @@ This is a [link][].
|
|||
|
||||
[link]: https://en.wikipedia.org/wiki/Textile_(markup_language) "Textile"
|
||||
|
||||
This is another [link](https://en.wikipedia.org/wiki/Textile_(markup_language) "Textile").
|
||||
This is another [link2](https://en.wikipedia.org/wiki/Textile_(markup_language) "Textile").
|
||||
|
||||
[link](<./image/cat1).png> "title")
|
||||
[link3](<./image/cat1).png> "title")
|
||||
|
||||
[link4](https://en.wikipedia.org/wiki/Textile_(markup_language) "Textile")(this should work)
|
||||
|
|
|
@ -12,3 +12,8 @@
|
|||
<p><a href="">empty</a></p>
|
||||
<p><img src="" alt="empty" title="title" /></p>
|
||||
<p><a href="" title="title">empty</a></p>
|
||||
<p><a href="">empty</a></p>
|
||||
<p><a href="" title="title">empty</a></p>
|
||||
<p><a href=""></a></p>
|
||||
<p><a href=""></a></p>
|
||||
<p><a href="" title="title"></a></p>
|
||||
|
|
|
@ -25,3 +25,13 @@
|
|||
![empty](< > "title")
|
||||
|
||||
[empty](< > "title")
|
||||
|
||||
[empty]()
|
||||
|
||||
[empty]("title")
|
||||
|
||||
[]()
|
||||
|
||||
[](<>)
|
||||
|
||||
[]("title")
|
||||
|
|
|
@ -4,4 +4,4 @@ This is a [link](https://en.wikipedia.org/wiki/Textile_(markup) (some other text
|
|||
|
||||
This is a [link](https://en.wikipedia.org/wiki/Textile_(markup_language)) (some other text)
|
||||
|
||||
This is a [link](https://en.wikipedia.org/wiki/Textile_(markup_language)/foo) (some other text)
|
||||
This is a [link](https://en.wikipedia.org/wiki/Textile_(markup_language)/foo) (some other text)
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
.map(map(dir));
|
||||
}
|
||||
|
||||
function getJsonTestSuite (file) {
|
||||
var json = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
return mapJson(json, file);
|
||||
}
|
||||
|
||||
function filter () {
|
||||
return function (file) {
|
||||
var ext = file.slice(-3);
|
||||
|
@ -25,7 +30,8 @@
|
|||
|
||||
function map (dir) {
|
||||
return function (file) {
|
||||
var name = file.replace('.md', ''),
|
||||
var oFile = 'file://' + process.cwd().replace(/\\/g, '/') + dir + file,
|
||||
name = file.replace('.md', ''),
|
||||
htmlPath = dir + name + '.html',
|
||||
html = fs.readFileSync(htmlPath, 'utf8'),
|
||||
mdPath = dir + name + '.md',
|
||||
|
@ -34,18 +40,40 @@
|
|||
return {
|
||||
name: name,
|
||||
input: md,
|
||||
expected: html
|
||||
expected: html,
|
||||
file: oFile
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function mapJson (jsonArray, file) {
|
||||
var tcObj = {};
|
||||
for (var i = 0; i < jsonArray.length; ++i) {
|
||||
var section = jsonArray[i].section;
|
||||
var name = jsonArray[i].section + '_' + jsonArray[i].example;
|
||||
var md = jsonArray[i].markdown;
|
||||
var html = jsonArray[i].html;
|
||||
if (!tcObj.hasOwnProperty(section)) {
|
||||
tcObj[section] = [];
|
||||
}
|
||||
tcObj[section].push({
|
||||
name: name,
|
||||
input: md,
|
||||
expected: html,
|
||||
file: process.cwd().replace(/\\/g, '/') + file
|
||||
});
|
||||
}
|
||||
return tcObj;
|
||||
}
|
||||
|
||||
|
||||
function assertion (testCase, converter) {
|
||||
return function () {
|
||||
testCase.actual = converter.makeHtml(testCase.input);
|
||||
testCase = normalize(testCase);
|
||||
|
||||
// Compare
|
||||
testCase.actual.should.equal(testCase.expected);
|
||||
testCase.actual.should.equal(testCase.expected, testCase.file);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -81,6 +109,7 @@
|
|||
|
||||
module.exports = {
|
||||
getTestSuite: getTestSuite,
|
||||
getJsonTestSuite: getJsonTestSuite,
|
||||
assertion: assertion,
|
||||
normalize: normalize,
|
||||
showdown: require('../../../.build/showdown.js')
|
||||
|
|
25
test/functional/makehtml/testsuite.commonmark.js
Normal file
25
test/functional/makehtml/testsuite.commonmark.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Created by Estevao on 08-06-2015.
|
||||
*/
|
||||
|
||||
// jshint ignore: start
|
||||
/*
|
||||
var bootstrap = require('./makehtml.bootstrap.js'),
|
||||
converter = new bootstrap.showdown.Converter(),
|
||||
assertion = bootstrap.assertion,
|
||||
testsuite = bootstrap.getJsonTestSuite('test/functional/makehtml/cases/commonmark.testsuite.json');
|
||||
|
||||
describe('makeHtml() commonmark testsuite', function () {
|
||||
'use strict';
|
||||
|
||||
for (var section in testsuite) {
|
||||
if (testsuite.hasOwnProperty(section)) {
|
||||
describe(section, function () {
|
||||
for (var i = 0; i < testsuite[section].length; ++i) {
|
||||
it(testsuite[section][i].name, assertion(testsuite[section][i], converter));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
|
@ -62,7 +62,7 @@ describe('makeHtml() features testsuite', function () {
|
|||
} else if (testsuite[i].name === 'simpleLineBreaks-handle-html-pre') {
|
||||
converter = new showdown.Converter({simpleLineBreaks: true});
|
||||
} else if (testsuite[i].name === 'excludeTrailingPunctuationFromURLs-option') {
|
||||
converter = new showdown.Converter({simplifiedAutoLink: true, excludeTrailingPunctuationFromURLs: true});
|
||||
converter = new showdown.Converter({simplifiedAutoLink: true});
|
||||
} else if (testsuite[i].name === 'requireSpaceBeforeHeadingText') {
|
||||
converter = new showdown.Converter({requireSpaceBeforeHeadingText: true});
|
||||
} else if (testsuite[i].name === '#320.github-compatible-generated-header-id') {
|
||||
|
@ -88,11 +88,13 @@ describe('makeHtml() features testsuite', function () {
|
|||
} else if (testsuite[i].name === 'customizedHeaderId-simple') {
|
||||
converter = new showdown.Converter({customizedHeaderId: true});
|
||||
} else if (testsuite[i].name === '#378.simplifiedAutoLinks-with-excludeTrailingPunctuationFromURLs') {
|
||||
converter = new showdown.Converter({simplifiedAutoLink: true, excludeTrailingPunctuationFromURLs: true});
|
||||
converter = new showdown.Converter({simplifiedAutoLink: true});
|
||||
} else if (testsuite[i].name === '#374.escape-html-tags') {
|
||||
converter = new showdown.Converter({backslashEscapesHTMLTags: true});
|
||||
} else if (testsuite[i].name === '#379.openLinksInNewWindow-breaks-em-markdup') {
|
||||
converter = new showdown.Converter({openLinksInNewWindow: true});
|
||||
} else if (testsuite[i].name === '#355.simplifiedAutoLink-URLs-inside-parenthesis-followed-by-another-character-are-not-parsed-correctly') {
|
||||
converter = new showdown.Converter({simplifiedAutoLink: true});
|
||||
} else {
|
||||
converter = new showdown.Converter();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user