Index: third_party/pkg/markdown/lib/src/inline_parser.dart |
diff --git a/third_party/pkg/markdown/lib/src/inline_parser.dart b/third_party/pkg/markdown/lib/src/inline_parser.dart |
index 392ca69b1904df56e9e9dfa0b18806a6b5424679..6772c8416d95a16ca556b12ea2fc271223f56c4e 100644 |
--- a/third_party/pkg/markdown/lib/src/inline_parser.dart |
+++ b/third_party/pkg/markdown/lib/src/inline_parser.dart |
@@ -29,6 +29,7 @@ class InlineParser { |
new AutolinkSyntax(), |
new LinkSyntax(), |
+ new ImageLinkSyntax(), |
// "*" surrounded by spaces is left alone. |
new TextSyntax(r' \* '), |
// "_" surrounded by spaces is left alone. |
@@ -83,8 +84,11 @@ class InlineParser { |
} else { |
syntaxes = defaultSyntaxes; |
} |
- // Custom link resolver goes after the generic text syntax. |
- syntaxes.insert(1, new LinkSyntax(linkResolver: document.linkResolver)); |
+ // Custom link resolvers goes after the generic text syntax. |
+ syntaxes.insertAll(1, [ |
+ new LinkSyntax(linkResolver: document.linkResolver), |
+ new ImageLinkSyntax(linkResolver: document.linkResolver) |
+ ]); |
} |
List<Node> parse() { |
@@ -214,8 +218,8 @@ class AutolinkSyntax extends InlineSyntax { |
bool onMatch(InlineParser parser, Match match) { |
final url = match[1]; |
- final anchor = new Element.text('a', escapeHtml(url)); |
- anchor.attributes['href'] = url; |
+ final anchor = new Element.text('a', escapeHtml(url)) |
+ ..attributes['href'] = url; |
parser.addNode(anchor); |
return true; |
@@ -267,76 +271,105 @@ class LinkSyntax extends TagSyntax { |
// 4: Contains the title, if present, for an inline link. |
} |
- LinkSyntax({this.linkResolver}) |
- : super(r'\[', end: linkPattern); |
- |
- bool onMatchEnd(InlineParser parser, Match match, TagState state) { |
- var url; |
- var title; |
+ LinkSyntax({this.linkResolver, String pattern: r'\['}) |
+ : super(pattern, end: linkPattern); |
+ Node createNode(InlineParser parser, Match match, TagState state) { |
// If we didn't match refLink or inlineLink, then it means there was |
// nothing after the first square bracket, so it isn't a normal markdown |
// link at all. Instead, we allow users of the library to specify a special |
// resolver function ([linkResolver]) that may choose to handle |
// this. Otherwise, it's just treated as plain text. |
- if ((match[1] == null) || (match[1] == '')) { |
- if (linkResolver == null) return false; |
+ if (isNullOrEmpty(match[1])) { |
+ if (linkResolver == null) return null; |
// Only allow implicit links if the content is just text. |
// TODO(rnystrom): Do we want to relax this? |
- if (state.children.any((child) => child is! Text)) return false; |
+ if (state.children.any((child) => child is! Text)) return null; |
// If there are multiple children, but they are all text, send the |
// combined text to linkResolver. |
var textToResolve = state.children.fold('', |
(oldVal, child) => oldVal + child.text); |
+ |
// See if we have a resolver that will generate a link for us. |
- final node = linkResolver(textToResolve); |
- if (node == null) return false; |
+ return linkResolver(textToResolve); |
+ } else { |
+ Link link = getLink(parser, match, state); |
+ if (link == null) return null; |
- parser.addNode(node); |
- return true; |
+ final Element node = new Element('a', state.children) |
+ ..attributes["href"] = escapeHtml(link.url) |
+ ..attributes['title'] = escapeHtml(link.title); |
+ |
+ cleanMap(node.attributes); |
+ return node; |
} |
+ } |
+ Link getLink(InlineParser parser, Match match, TagState state) { |
if ((match[3] != null) && (match[3] != '')) { |
// Inline link like [foo](url). |
- url = match[3]; |
- title = match[4]; |
+ var url = match[3]; |
+ var title = match[4]; |
// For whatever reason, markdown allows angle-bracketed URLs here. |
if (url.startsWith('<') && url.endsWith('>')) { |
url = url.substring(1, url.length - 1); |
} |
+ |
+ return new Link(null, url, title); |
} else { |
+ var id; |
// Reference link like [foo] [bar]. |
- var id = match[2]; |
- if (id == '') { |
+ if (match[2] == '') |
// The id is empty ("[]") so infer it from the contents. |
id = parser.source.substring(state.startPos + 1, parser.pos); |
- } |
+ else |
+ id = match[2]; |
// References are case-insensitive. |
id = id.toLowerCase(); |
+ return parser.document.refLinks[id]; |
+ } |
+ } |
+ |
+ bool onMatchEnd(InlineParser parser, Match match, TagState state) { |
+ Node node = createNode(parser, match, state); |
+ if (node == null) return false; |
+ parser.addNode(node); |
+ return true; |
+ } |
+} |
- // Look up the link. |
- final link = parser.document.refLinks[id]; |
- // If it's an unknown link just emit plaintext. |
- if (link == null) return false; |
+/// Matches images like `![alternate text](url "optional title")` and |
+/// `![alternate text][url reference]`. |
+class ImageLinkSyntax extends LinkSyntax { |
+ Resolver linkResolver; |
+ ImageLinkSyntax({this.linkResolver}) |
+ : super(pattern: r'!\['); |
- url = link.url; |
- title = link.title; |
- } |
+ Node createNode(InlineParser parser, Match match, TagState state) { |
+ Node node = super.createNode(parser, match, state); |
+ if (node == null) return null; |
- final anchor = new Element('a', state.children); |
- anchor.attributes['href'] = escapeHtml(url); |
- if ((title != null) && (title != '')) { |
- anchor.attributes['title'] = escapeHtml(title); |
- } |
+ final Element imageElement = new Element.withTag("img") |
+ ..attributes["src"] = node.attributes["href"] |
+ ..attributes["title"] = node.attributes["title"] |
+ ..attributes["alt"] = node.children |
+ .map((e) => isNullOrEmpty(e) || e is! Text ? '' : e.text) |
+ .join(' '); |
- parser.addNode(anchor); |
- return true; |
+ cleanMap(imageElement.attributes); |
+ |
+ node.children |
+ ..clear() |
+ ..add(imageElement); |
+ |
+ return node; |
} |
} |
+ |
/// Matches backtick-enclosed inline code blocks. |
class CodeSyntax extends InlineSyntax { |
CodeSyntax(String pattern) |