You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

LinkDefinition.cs 7.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //
  2. // MarkdownDeep - http://www.toptensoftware.com/markdowndeep
  3. // Copyright (C) 2010-2011 Topten Software
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in
  6. // compliance with the License. You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed under the License is
  11. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and limitations under the License.
  13. //
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.Text;
  18. namespace MarkdownDeep
  19. {
  20. public class LinkDefinition
  21. {
  22. public LinkDefinition(string id)
  23. {
  24. this.id= id;
  25. }
  26. public LinkDefinition(string id, string url)
  27. {
  28. this.id = id;
  29. this.url = url;
  30. }
  31. public LinkDefinition(string id, string url, string title)
  32. {
  33. this.id = id;
  34. this.url = url;
  35. this.title = title;
  36. }
  37. public string id
  38. {
  39. get;
  40. set;
  41. }
  42. public string url
  43. {
  44. get;
  45. set;
  46. }
  47. public string title
  48. {
  49. get;
  50. set;
  51. }
  52. internal void RenderLink(Markdown m, StringBuilder b, string link_text)
  53. {
  54. if (url.StartsWith("mailto:"))
  55. {
  56. b.Append("<a href=\"");
  57. Utils.HtmlRandomize(b, url);
  58. b.Append('\"');
  59. if (!String.IsNullOrEmpty(title))
  60. {
  61. b.Append(" title=\"");
  62. Utils.SmartHtmlEncodeAmpsAndAngles(b, title);
  63. b.Append('\"');
  64. }
  65. b.Append('>');
  66. Utils.HtmlRandomize(b, link_text);
  67. b.Append("</a>");
  68. }
  69. else
  70. {
  71. HtmlTag tag = new HtmlTag("a");
  72. // encode url
  73. StringBuilder sb = m.GetStringBuilder();
  74. Utils.SmartHtmlEncodeAmpsAndAngles(sb, url);
  75. tag.attributes["href"] = sb.ToString();
  76. // encode title
  77. if (!String.IsNullOrEmpty(title ))
  78. {
  79. sb.Length = 0;
  80. Utils.SmartHtmlEncodeAmpsAndAngles(sb, title);
  81. tag.attributes["title"] = sb.ToString();
  82. }
  83. // Do user processing
  84. m.OnPrepareLink(tag);
  85. // Render the opening tag
  86. tag.RenderOpening(b);
  87. b.Append(link_text); // Link text already escaped by SpanFormatter
  88. b.Append("</a>");
  89. }
  90. }
  91. internal void RenderImg(Markdown m, StringBuilder b, string alt_text)
  92. {
  93. HtmlTag tag = new HtmlTag("img");
  94. // encode url
  95. StringBuilder sb = m.GetStringBuilder();
  96. Utils.SmartHtmlEncodeAmpsAndAngles(sb, url);
  97. tag.attributes["src"] = sb.ToString();
  98. // encode alt text
  99. if (!String.IsNullOrEmpty(alt_text))
  100. {
  101. sb.Length = 0;
  102. Utils.SmartHtmlEncodeAmpsAndAngles(sb, alt_text);
  103. tag.attributes["alt"] = sb.ToString();
  104. }
  105. // encode title
  106. if (!String.IsNullOrEmpty(title))
  107. {
  108. sb.Length = 0;
  109. Utils.SmartHtmlEncodeAmpsAndAngles(sb, title);
  110. tag.attributes["title"] = sb.ToString();
  111. }
  112. tag.closed = true;
  113. m.OnPrepareImage(tag, m.RenderingTitledImage);
  114. tag.RenderOpening(b);
  115. }
  116. // Parse a link definition from a string (used by test cases)
  117. internal static LinkDefinition ParseLinkDefinition(string str, bool ExtraMode)
  118. {
  119. StringScanner p = new StringScanner(str);
  120. return ParseLinkDefinitionInternal(p, ExtraMode);
  121. }
  122. // Parse a link definition
  123. internal static LinkDefinition ParseLinkDefinition(StringScanner p, bool ExtraMode)
  124. {
  125. int savepos=p.Position;
  126. var l = ParseLinkDefinitionInternal(p, ExtraMode);
  127. if (l==null)
  128. p.Position = savepos;
  129. return l;
  130. }
  131. internal static LinkDefinition ParseLinkDefinitionInternal(StringScanner p, bool ExtraMode)
  132. {
  133. // Skip leading white space
  134. p.SkipWhitespace();
  135. // Must start with an opening square bracket
  136. if (!p.SkipChar('['))
  137. return null;
  138. // Extract the id
  139. p.Mark();
  140. if (!p.Find(']'))
  141. return null;
  142. string id = p.Extract();
  143. if (id.Length == 0)
  144. return null;
  145. if (!p.SkipString("]:"))
  146. return null;
  147. // Parse the url and title
  148. var link=ParseLinkTarget(p, id, ExtraMode);
  149. // and trailing whitespace
  150. p.SkipLinespace();
  151. // Trailing crap, not a valid link reference...
  152. if (!p.Eol)
  153. return null;
  154. return link;
  155. }
  156. // Parse just the link target
  157. // For reference link definition, this is the bit after "[id]: thisbit"
  158. // For inline link, this is the bit in the parens: [link text](thisbit)
  159. internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode)
  160. {
  161. // Skip whitespace
  162. p.SkipWhitespace();
  163. // End of string?
  164. if (p.Eol)
  165. return null;
  166. // Create the link definition
  167. var r = new LinkDefinition(id);
  168. // Is the url enclosed in angle brackets
  169. if (p.SkipChar('<'))
  170. {
  171. // Extract the url
  172. p.Mark();
  173. // Find end of the url
  174. while (p.Current != '>')
  175. {
  176. if (p.Eof)
  177. return null;
  178. p.SkipEscapableChar(ExtraMode);
  179. }
  180. string url = p.Extract();
  181. if (!p.SkipChar('>'))
  182. return null;
  183. // Unescape it
  184. r.url = Utils.UnescapeString(url.Trim(), ExtraMode);
  185. // Skip whitespace
  186. p.SkipWhitespace();
  187. }
  188. else
  189. {
  190. // Find end of the url
  191. p.Mark();
  192. int paren_depth = 1;
  193. while (!p.Eol)
  194. {
  195. char ch=p.Current;
  196. if (char.IsWhiteSpace(ch))
  197. break;
  198. if (id == null)
  199. {
  200. if (ch == '(')
  201. paren_depth++;
  202. else if (ch == ')')
  203. {
  204. paren_depth--;
  205. if (paren_depth==0)
  206. break;
  207. }
  208. }
  209. p.SkipEscapableChar(ExtraMode);
  210. }
  211. r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode);
  212. }
  213. p.SkipLinespace();
  214. // End of inline target
  215. if (p.DoesMatch(')'))
  216. return r;
  217. bool bOnNewLine = p.Eol;
  218. int posLineEnd = p.Position;
  219. if (p.Eol)
  220. {
  221. p.SkipEol();
  222. p.SkipLinespace();
  223. }
  224. // Work out what the title is delimited with
  225. char delim;
  226. switch (p.Current)
  227. {
  228. case '\'':
  229. case '\"':
  230. delim = p.Current;
  231. break;
  232. case '(':
  233. delim = ')';
  234. break;
  235. default:
  236. if (bOnNewLine)
  237. {
  238. p.Position = posLineEnd;
  239. return r;
  240. }
  241. else
  242. return null;
  243. }
  244. // Skip the opening title delimiter
  245. p.SkipForward(1);
  246. // Find the end of the title
  247. p.Mark();
  248. while (true)
  249. {
  250. if (p.Eol)
  251. return null;
  252. if (p.Current == delim)
  253. {
  254. if (delim != ')')
  255. {
  256. int savepos = p.Position;
  257. // Check for embedded quotes in title
  258. // Skip the quote and any trailing whitespace
  259. p.SkipForward(1);
  260. p.SkipLinespace();
  261. // Next we expect either the end of the line for a link definition
  262. // or the close bracket for an inline link
  263. if ((id == null && p.Current != ')') ||
  264. (id != null && !p.Eol))
  265. {
  266. continue;
  267. }
  268. p.Position = savepos;
  269. }
  270. // End of title
  271. break;
  272. }
  273. p.SkipEscapableChar(ExtraMode);
  274. }
  275. // Store the title
  276. r.title = Utils.UnescapeString(p.Extract(), ExtraMode);
  277. // Skip closing quote
  278. p.SkipForward(1);
  279. // Done!
  280. return r;
  281. }
  282. }
  283. }

No Description