Moved DocNet code to its own folder Added MarkdownDeep parser code plus tests Migrated DocNet code to use MarkdownDeep. Not complete yet (the h2 headers aren't collected yet).tags/0.9.0
| @@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | |||
| # Visual Studio 14 | |||
| VisualStudioVersion = 14.0.24720.0 | |||
| MinimumVisualStudioVersion = 10.0.40219.1 | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Docnet", "src\Docnet.csproj", "{48CA9947-AF13-459E-9D59-FC451B5C19D7}" | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Docnet", "src\DocNet\Docnet.csproj", "{48CA9947-AF13-459E-9D59-FC451B5C19D7}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownDeep", "src\MarkdownDeep\MarkdownDeep.csproj", "{1569ED47-C7C9-4261-B6F4-7445BD0F2C95}" | |||
| EndProject | |||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownDeepTests", "src\MarkdownDeepTests\MarkdownDeepTests.csproj", "{CD1F5BFF-0118-4994-86A2-92658A36CE1B}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| @@ -15,6 +19,14 @@ Global | |||
| {48CA9947-AF13-459E-9D59-FC451B5C19D7}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {48CA9947-AF13-459E-9D59-FC451B5C19D7}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {48CA9947-AF13-459E-9D59-FC451B5C19D7}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {1569ED47-C7C9-4261-B6F4-7445BD0F2C95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {1569ED47-C7C9-4261-B6F4-7445BD0F2C95}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {1569ED47-C7C9-4261-B6F4-7445BD0F2C95}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {1569ED47-C7C9-4261-B6F4-7445BD0F2C95}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {CD1F5BFF-0118-4994-86A2-92658A36CE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {CD1F5BFF-0118-4994-86A2-92658A36CE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {CD1F5BFF-0118-4994-86A2-92658A36CE1B}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {CD1F5BFF-0118-4994-86A2-92658A36CE1B}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| @@ -253,18 +253,22 @@ h1, h2, h3, h4, h5, h6 { | |||
| h1 { | |||
| font-size: 220%; | |||
| margin-bottom:25px; | |||
| } | |||
| h2 { | |||
| font-size: 190%; | |||
| margin-bottom:18px; | |||
| } | |||
| h3 { | |||
| font-size: 155%; | |||
| margin-bottom:15px; | |||
| } | |||
| h4 { | |||
| font-size: 130%; | |||
| margin-bottom: 13px; | |||
| } | |||
| h4.searchresulttitle { | |||
| @@ -273,10 +277,12 @@ h4.searchresulttitle { | |||
| h5 { | |||
| font-size: 115%; | |||
| margin-bottom: 10px; | |||
| } | |||
| h6 { | |||
| font-size: 105%; | |||
| margin-bottom: 7px; | |||
| } | |||
| hr { | |||
| @@ -67,6 +67,12 @@ | |||
| <None Include="App.config" /> | |||
| <None Include="packages.config" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\MarkdownDeep\MarkdownDeep.csproj"> | |||
| <Project>{1569ed47-c7c9-4261-b6f4-7445bd0f2c95}</Project> | |||
| <Name>MarkdownDeep</Name> | |||
| </ProjectReference> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| @@ -1163,18 +1163,6 @@ namespace Docnet | |||
| } | |||
| private string CreateAndManageAnchorHtmlIfRequired(int headerLevel, string headerText) | |||
| { | |||
| if(headerLevel != 2) | |||
| { | |||
| return string.Empty; | |||
| } | |||
| // create an anchor and collect it for ToC generation. | |||
| var anchorText = HttpUtility.HtmlEncode(headerText.Replace(" ", "").Replace("\\", "").Replace("/", "").Replace(".", "").Replace(":", "").Replace(";", "")); | |||
| this.CollectedH2AnchorNameTuples.Add(new Tuple<string, string>(anchorText, headerText)); | |||
| return string.Format("<a name=\"{0}\"></a>", anchorText); | |||
| } | |||
| private static Regex _horizontalRules = new Regex(@" | |||
| ^[ ]{0,3} # Leading space | |||
| @@ -1633,6 +1621,7 @@ namespace Docnet | |||
| } | |||
| #region MarkdownSharp extensions | |||
| private static Regex _githubCodeBlock = new Regex(@"(?<!\\)(`{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)", RegexOptions.Compiled); | |||
| private string DoGithubCodeBlocks(string text) | |||
| { | |||
| @@ -1741,7 +1730,19 @@ namespace Docnet | |||
| return string.Concat("\n\n<div class=\"alert alert-", alertType, "\"><span class=\"alert-title\"><i class=\"fa fa-", faIconName, "\"></i> ", title, "</span>", | |||
| text, "</div>"); | |||
| } | |||
| private string CreateAndManageAnchorHtmlIfRequired(int headerLevel, string headerText) | |||
| { | |||
| if(headerLevel != 2) | |||
| { | |||
| return string.Empty; | |||
| } | |||
| // create an anchor and collect it for ToC generation. | |||
| var anchorText = HttpUtility.HtmlEncode(headerText.Replace(" ", "").Replace("\\", "").Replace("/", "").Replace(".", "").Replace(":", "").Replace(";", "")); | |||
| this.CollectedH2AnchorNameTuples.Add(new Tuple<string, string>(anchorText, headerText)); | |||
| return string.Format("<a name=\"{0}\"></a>", anchorText); | |||
| } | |||
| #endregion | |||
| #region Encoding and Normalization | |||
| @@ -33,12 +33,17 @@ namespace Docnet | |||
| { | |||
| public static string ConvertMarkdownToHtml(string toConvert, List<Tuple<string, string>> createdAnchorCollector) | |||
| { | |||
| var parser = new Markdown(new MarkdownOptions() { EmptyElementSuffix = ">"}); | |||
| //var parser = new Markdown(new MarkdownOptions() { EmptyElementSuffix = ">"}); | |||
| //var toReturn = parser.Transform(toConvert); | |||
| //if(createdAnchorCollector != null) | |||
| //{ | |||
| // createdAnchorCollector.AddRange(parser.CollectedH2AnchorNameTuples); | |||
| //} | |||
| var parser = new MarkdownDeep.Markdown(); | |||
| parser.ExtraMode = true; | |||
| parser.GitHubCodeBlocks = true; | |||
| var toReturn = parser.Transform(toConvert); | |||
| if(createdAnchorCollector != null) | |||
| { | |||
| createdAnchorCollector.AddRange(parser.CollectedH2AnchorNameTuples); | |||
| } | |||
| return toReturn; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| class Abbreviation | |||
| { | |||
| public Abbreviation(string abbr, string title) | |||
| { | |||
| Abbr = abbr; | |||
| Title = title; | |||
| } | |||
| public string Abbr; | |||
| public string Title; | |||
| } | |||
| } | |||
| @@ -0,0 +1,500 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| // Some block types are only used during block parsing, some | |||
| // are only used during rendering and some are used during both | |||
| internal enum BlockType | |||
| { | |||
| Blank, // blank line (parse only) | |||
| h1, // headings (render and parse) | |||
| h2, | |||
| h3, | |||
| h4, | |||
| h5, | |||
| h6, | |||
| post_h1, // setext heading lines (parse only) | |||
| post_h2, | |||
| quote, // block quote (render and parse) | |||
| ol_li, // list item in an ordered list (render and parse) | |||
| ul_li, // list item in an unordered list (render and parse) | |||
| p, // paragraph (or plain line during parse) | |||
| indent, // an indented line (parse only) | |||
| hr, // horizontal rule (render and parse) | |||
| user_break, // user break | |||
| html, // html content (render and parse) | |||
| unsafe_html, // unsafe html that should be encoded | |||
| span, // an undecorated span of text (used for simple list items | |||
| // where content is not wrapped in paragraph tags | |||
| codeblock, // a code block (render only). If in github mode, `Data` contains the language name specified after the leading ```. | |||
| li, // a list item (render only) | |||
| ol, // ordered list (render only) | |||
| ul, // unordered list (render only) | |||
| HtmlTag, // Data=(HtmlTag), children = content | |||
| Composite, // Just a list of child blocks | |||
| table_spec, // A table row specifier eg: |---: | ---| `data` = TableSpec reference | |||
| dd, // definition (render and parse) `data` = bool true if blank line before | |||
| dt, // render only | |||
| dl, // render only | |||
| footnote, // footnote definition eg: [^id] `data` holds the footnote id | |||
| p_footnote, // paragraph with footnote return link append. Return link string is in `data`. | |||
| } | |||
| class Block | |||
| { | |||
| internal Block() | |||
| { | |||
| } | |||
| internal Block(BlockType type) | |||
| { | |||
| blockType = type; | |||
| } | |||
| public string Content | |||
| { | |||
| get | |||
| { | |||
| switch (blockType) | |||
| { | |||
| case BlockType.codeblock: | |||
| StringBuilder s = new StringBuilder(); | |||
| foreach (var line in children) | |||
| { | |||
| s.Append(line.Content); | |||
| s.Append('\n'); | |||
| } | |||
| return s.ToString(); | |||
| } | |||
| if (buf==null) | |||
| return null; | |||
| else | |||
| return contentStart == -1 ? buf : buf.Substring(contentStart, contentLen); | |||
| } | |||
| } | |||
| public int LineStart | |||
| { | |||
| get | |||
| { | |||
| return lineStart == 0 ? contentStart : lineStart; | |||
| } | |||
| } | |||
| internal void RenderChildren(Markdown m, StringBuilder b) | |||
| { | |||
| foreach (var block in children) | |||
| { | |||
| block.Render(m, b); | |||
| } | |||
| } | |||
| internal void RenderChildrenPlain(Markdown m, StringBuilder b) | |||
| { | |||
| foreach (var block in children) | |||
| { | |||
| block.RenderPlain(m, b); | |||
| } | |||
| } | |||
| internal string ResolveHeaderID(Markdown m) | |||
| { | |||
| // Already resolved? | |||
| if (this.data!=null && this.data is string) | |||
| return (string)this.data; | |||
| // Approach 1 - PHP Markdown Extra style header id | |||
| int end=contentEnd; | |||
| string id = Utils.StripHtmlID(buf, contentStart, ref end); | |||
| if (id != null) | |||
| { | |||
| contentEnd = end; | |||
| } | |||
| else | |||
| { | |||
| // Approach 2 - pandoc style header id | |||
| id = m.MakeUniqueHeaderID(buf, contentStart, contentLen); | |||
| } | |||
| this.data = id; | |||
| return id; | |||
| } | |||
| internal void Render(Markdown m, StringBuilder b) | |||
| { | |||
| switch (blockType) | |||
| { | |||
| case BlockType.Blank: | |||
| return; | |||
| case BlockType.p: | |||
| m.SpanFormatter.FormatParagraph(b, buf, contentStart, contentLen); | |||
| break; | |||
| case BlockType.span: | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append("\n"); | |||
| break; | |||
| case BlockType.h1: | |||
| case BlockType.h2: | |||
| case BlockType.h3: | |||
| case BlockType.h4: | |||
| case BlockType.h5: | |||
| case BlockType.h6: | |||
| if (m.ExtraMode && !m.SafeMode) | |||
| { | |||
| b.Append("<" + blockType.ToString()); | |||
| string id = ResolveHeaderID(m); | |||
| if (!String.IsNullOrEmpty(id)) | |||
| { | |||
| b.Append(" id=\""); | |||
| b.Append(id); | |||
| b.Append("\">"); | |||
| } | |||
| else | |||
| { | |||
| b.Append(">"); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| b.Append("<" + blockType.ToString() + ">"); | |||
| } | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append("</" + blockType.ToString() + ">\n"); | |||
| break; | |||
| case BlockType.hr: | |||
| b.Append("<hr />\n"); | |||
| return; | |||
| case BlockType.user_break: | |||
| return; | |||
| case BlockType.ol_li: | |||
| case BlockType.ul_li: | |||
| b.Append("<li>"); | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append("</li>\n"); | |||
| break; | |||
| case BlockType.dd: | |||
| b.Append("<dd>"); | |||
| if (children != null) | |||
| { | |||
| b.Append("\n"); | |||
| RenderChildren(m, b); | |||
| } | |||
| else | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append("</dd>\n"); | |||
| break; | |||
| case BlockType.dt: | |||
| { | |||
| if (children == null) | |||
| { | |||
| foreach (var l in Content.Split('\n')) | |||
| { | |||
| b.Append("<dt>"); | |||
| m.SpanFormatter.Format(b, l.Trim()); | |||
| b.Append("</dt>\n"); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| b.Append("<dt>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</dt>\n"); | |||
| } | |||
| break; | |||
| } | |||
| case BlockType.dl: | |||
| b.Append("<dl>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</dl>\n"); | |||
| return; | |||
| case BlockType.html: | |||
| b.Append(buf, contentStart, contentLen); | |||
| return; | |||
| case BlockType.unsafe_html: | |||
| m.HtmlEncode(b, buf, contentStart, contentLen); | |||
| return; | |||
| case BlockType.codeblock: | |||
| if(m.FormatCodeBlock == null) | |||
| { | |||
| var dataArgument = this.data as string ?? string.Empty; | |||
| if(m.GitHubCodeBlocks && !string.IsNullOrWhiteSpace(dataArgument)) | |||
| { | |||
| b.AppendFormat("<pre><code class=\"{0}\">", dataArgument); | |||
| } | |||
| else | |||
| { | |||
| b.Append("<pre><code>"); | |||
| } | |||
| foreach(var line in children) | |||
| { | |||
| m.HtmlEncodeAndConvertTabsToSpaces(b, line.buf, line.contentStart, line.contentLen); | |||
| b.Append("\n"); | |||
| } | |||
| b.Append("</code></pre>\n\n"); | |||
| } | |||
| else | |||
| { | |||
| var sb = new StringBuilder(); | |||
| foreach(var line in children) | |||
| { | |||
| m.HtmlEncodeAndConvertTabsToSpaces(sb, line.buf, line.contentStart, line.contentLen); | |||
| sb.Append("\n"); | |||
| } | |||
| b.Append(m.FormatCodeBlock(m, sb.ToString())); | |||
| } | |||
| return; | |||
| case BlockType.quote: | |||
| b.Append("<blockquote>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</blockquote>\n"); | |||
| return; | |||
| case BlockType.li: | |||
| b.Append("<li>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</li>\n"); | |||
| return; | |||
| case BlockType.ol: | |||
| b.Append("<ol>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</ol>\n"); | |||
| return; | |||
| case BlockType.ul: | |||
| b.Append("<ul>\n"); | |||
| RenderChildren(m, b); | |||
| b.Append("</ul>\n"); | |||
| return; | |||
| case BlockType.HtmlTag: | |||
| var tag = (HtmlTag)data; | |||
| // Prepare special tags | |||
| var name=tag.name.ToLowerInvariant(); | |||
| if (name == "a") | |||
| { | |||
| m.OnPrepareLink(tag); | |||
| } | |||
| else if (name == "img") | |||
| { | |||
| m.OnPrepareImage(tag, m.RenderingTitledImage); | |||
| } | |||
| tag.RenderOpening(b); | |||
| b.Append("\n"); | |||
| RenderChildren(m, b); | |||
| tag.RenderClosing(b); | |||
| b.Append("\n"); | |||
| return; | |||
| case BlockType.Composite: | |||
| case BlockType.footnote: | |||
| RenderChildren(m, b); | |||
| return; | |||
| case BlockType.table_spec: | |||
| ((TableSpec)data).Render(m, b); | |||
| break; | |||
| case BlockType.p_footnote: | |||
| b.Append("<p>"); | |||
| if (contentLen > 0) | |||
| { | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append(" "); | |||
| } | |||
| b.Append((string)data); | |||
| b.Append("</p>\n"); | |||
| break; | |||
| default: | |||
| b.Append("<" + blockType.ToString() + ">"); | |||
| m.SpanFormatter.Format(b, buf, contentStart, contentLen); | |||
| b.Append("</" + blockType.ToString() + ">\n"); | |||
| break; | |||
| } | |||
| } | |||
| internal void RenderPlain(Markdown m, StringBuilder b) | |||
| { | |||
| switch (blockType) | |||
| { | |||
| case BlockType.Blank: | |||
| return; | |||
| case BlockType.p: | |||
| case BlockType.span: | |||
| m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); | |||
| b.Append(" "); | |||
| break; | |||
| case BlockType.h1: | |||
| case BlockType.h2: | |||
| case BlockType.h3: | |||
| case BlockType.h4: | |||
| case BlockType.h5: | |||
| case BlockType.h6: | |||
| m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); | |||
| b.Append(" - "); | |||
| break; | |||
| case BlockType.ol_li: | |||
| case BlockType.ul_li: | |||
| b.Append("* "); | |||
| m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); | |||
| b.Append(" "); | |||
| break; | |||
| case BlockType.dd: | |||
| if (children != null) | |||
| { | |||
| b.Append("\n"); | |||
| RenderChildrenPlain(m, b); | |||
| } | |||
| else | |||
| m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); | |||
| break; | |||
| case BlockType.dt: | |||
| { | |||
| if (children == null) | |||
| { | |||
| foreach (var l in Content.Split('\n')) | |||
| { | |||
| var str = l.Trim(); | |||
| m.SpanFormatter.FormatPlain(b, str, 0, str.Length); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| RenderChildrenPlain(m, b); | |||
| } | |||
| break; | |||
| } | |||
| case BlockType.dl: | |||
| RenderChildrenPlain(m, b); | |||
| return; | |||
| case BlockType.codeblock: | |||
| foreach (var line in children) | |||
| { | |||
| b.Append(line.buf, line.contentStart, line.contentLen); | |||
| b.Append(" "); | |||
| } | |||
| return; | |||
| case BlockType.quote: | |||
| case BlockType.li: | |||
| case BlockType.ol: | |||
| case BlockType.ul: | |||
| case BlockType.HtmlTag: | |||
| RenderChildrenPlain(m, b); | |||
| return; | |||
| } | |||
| } | |||
| public void RevertToPlain() | |||
| { | |||
| blockType = BlockType.p; | |||
| contentStart = lineStart; | |||
| contentLen = lineLen; | |||
| } | |||
| public int contentEnd | |||
| { | |||
| get | |||
| { | |||
| return contentStart + contentLen; | |||
| } | |||
| set | |||
| { | |||
| contentLen = value - contentStart; | |||
| } | |||
| } | |||
| // Count the leading spaces on a block | |||
| // Used by list item evaluation to determine indent levels | |||
| // irrespective of indent line type. | |||
| public int leadingSpaces | |||
| { | |||
| get | |||
| { | |||
| int count = 0; | |||
| for (int i = lineStart; i < lineStart + lineLen; i++) | |||
| { | |||
| if (buf[i] == ' ') | |||
| { | |||
| count++; | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return count; | |||
| } | |||
| } | |||
| public override string ToString() | |||
| { | |||
| string c = Content; | |||
| return blockType.ToString() + " - " + (c==null ? "<null>" : c); | |||
| } | |||
| public Block CopyFrom(Block other) | |||
| { | |||
| blockType = other.blockType; | |||
| buf = other.buf; | |||
| contentStart = other.contentStart; | |||
| contentLen = other.contentLen; | |||
| lineStart = other.lineStart; | |||
| lineLen = other.lineLen; | |||
| return this; | |||
| } | |||
| internal BlockType blockType; | |||
| internal string buf; | |||
| internal int contentStart; | |||
| internal int contentLen; | |||
| internal int lineStart; | |||
| internal int lineLen; | |||
| internal object data; // content depends on block type | |||
| internal List<Block> children; | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| class FootnoteReference | |||
| { | |||
| public FootnoteReference(int index, string id) | |||
| { | |||
| this.index = index; | |||
| this.id = id; | |||
| } | |||
| public int index; | |||
| public string id; | |||
| } | |||
| } | |||
| @@ -0,0 +1,351 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| [Flags] | |||
| public enum HtmlTagFlags | |||
| { | |||
| Block = 0x0001, // Block tag | |||
| Inline = 0x0002, // Inline tag | |||
| NoClosing = 0x0004, // No closing tag (eg: <hr> and <!-- -->) | |||
| ContentAsSpan = 0x0008, // When markdown=1 treat content as span, not block | |||
| }; | |||
| public class HtmlTag | |||
| { | |||
| public HtmlTag(string name) | |||
| { | |||
| m_name = name; | |||
| } | |||
| // Get the tag name eg: "div" | |||
| public string name | |||
| { | |||
| get { return m_name; } | |||
| } | |||
| // Get a dictionary of attribute values (no decoding done) | |||
| public Dictionary<string, string> attributes | |||
| { | |||
| get { return m_attributes; } | |||
| } | |||
| // Is this tag closed eg; <br /> | |||
| public bool closed | |||
| { | |||
| get { return m_closed; } | |||
| set { m_closed = value; } | |||
| } | |||
| // Is this a closing tag eg: </div> | |||
| public bool closing | |||
| { | |||
| get { return m_closing; } | |||
| } | |||
| string m_name; | |||
| Dictionary<string, string> m_attributes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); | |||
| bool m_closed; | |||
| bool m_closing; | |||
| HtmlTagFlags m_flags = 0; | |||
| public HtmlTagFlags Flags | |||
| { | |||
| get | |||
| { | |||
| if (m_flags == 0) | |||
| { | |||
| if (!m_tag_flags.TryGetValue(name.ToLower(), out m_flags)) | |||
| { | |||
| m_flags |= HtmlTagFlags.Inline; | |||
| } | |||
| } | |||
| return m_flags; | |||
| } | |||
| } | |||
| static string[] m_allowed_tags = new string [] { | |||
| "b","blockquote","code","dd","dt","dl","del","em","h1","h2","h3","h4","h5","h6","i","kbd","li","ol","ul", | |||
| "p", "pre", "s", "sub", "sup", "strong", "strike", "img", "a" | |||
| }; | |||
| static Dictionary<string, string[]> m_allowed_attributes = new Dictionary<string, string[]>() { | |||
| { "a", new string[] { "href", "title", "class" } }, | |||
| { "img", new string[] { "src", "width", "height", "alt", "title", "class" } }, | |||
| }; | |||
| static Dictionary<string, HtmlTagFlags> m_tag_flags = new Dictionary<string, HtmlTagFlags>() { | |||
| { "p", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan }, | |||
| { "div", HtmlTagFlags.Block }, | |||
| { "h1", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan }, | |||
| { "h2", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, | |||
| { "h3", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, | |||
| { "h4", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, | |||
| { "h5", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, | |||
| { "h6", HtmlTagFlags.Block | HtmlTagFlags.ContentAsSpan}, | |||
| { "blockquote", HtmlTagFlags.Block }, | |||
| { "pre", HtmlTagFlags.Block }, | |||
| { "table", HtmlTagFlags.Block }, | |||
| { "dl", HtmlTagFlags.Block }, | |||
| { "ol", HtmlTagFlags.Block }, | |||
| { "ul", HtmlTagFlags.Block }, | |||
| { "form", HtmlTagFlags.Block }, | |||
| { "fieldset", HtmlTagFlags.Block }, | |||
| { "iframe", HtmlTagFlags.Block }, | |||
| { "script", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "noscript", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "math", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "ins", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "del", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "img", HtmlTagFlags.Block | HtmlTagFlags.Inline }, | |||
| { "li", HtmlTagFlags.ContentAsSpan}, | |||
| { "dd", HtmlTagFlags.ContentAsSpan}, | |||
| { "dt", HtmlTagFlags.ContentAsSpan}, | |||
| { "td", HtmlTagFlags.ContentAsSpan}, | |||
| { "th", HtmlTagFlags.ContentAsSpan}, | |||
| { "legend", HtmlTagFlags.ContentAsSpan}, | |||
| { "address", HtmlTagFlags.ContentAsSpan}, | |||
| { "hr", HtmlTagFlags.Block | HtmlTagFlags.NoClosing}, | |||
| { "!", HtmlTagFlags.Block | HtmlTagFlags.NoClosing}, | |||
| { "head", HtmlTagFlags.Block }, | |||
| }; | |||
| // Check if this tag is safe | |||
| public bool IsSafe() | |||
| { | |||
| string name_lower=m_name.ToLowerInvariant(); | |||
| // Check if tag is in whitelist | |||
| if (!Utils.IsInList(name_lower, m_allowed_tags)) | |||
| return false; | |||
| // Find allowed attributes | |||
| string[] allowed_attributes; | |||
| if (!m_allowed_attributes.TryGetValue(name_lower, out allowed_attributes)) | |||
| { | |||
| // No allowed attributes, check we don't have any | |||
| return m_attributes.Count == 0; | |||
| } | |||
| // Check all are allowed | |||
| foreach (var i in m_attributes) | |||
| { | |||
| if (!Utils.IsInList(i.Key.ToLowerInvariant(), allowed_attributes)) | |||
| return false; | |||
| } | |||
| // Check href attribute is ok | |||
| string href; | |||
| if (m_attributes.TryGetValue("href", out href)) | |||
| { | |||
| if (!Utils.IsSafeUrl(href)) | |||
| return false; | |||
| } | |||
| string src; | |||
| if (m_attributes.TryGetValue("src", out src)) | |||
| { | |||
| if (!Utils.IsSafeUrl(src)) | |||
| return false; | |||
| } | |||
| // Passed all white list checks, allow it | |||
| return true; | |||
| } | |||
| // Render opening tag (eg: <tag attr="value"> | |||
| public void RenderOpening(StringBuilder dest) | |||
| { | |||
| dest.Append("<"); | |||
| dest.Append(name); | |||
| foreach (var i in m_attributes) | |||
| { | |||
| dest.Append(" "); | |||
| dest.Append(i.Key); | |||
| dest.Append("=\""); | |||
| dest.Append(i.Value); | |||
| dest.Append("\""); | |||
| } | |||
| if (m_closed) | |||
| dest.Append(" />"); | |||
| else | |||
| dest.Append(">"); | |||
| } | |||
| // Render closing tag (eg: </tag>) | |||
| public void RenderClosing(StringBuilder dest) | |||
| { | |||
| dest.Append("</"); | |||
| dest.Append(name); | |||
| dest.Append(">"); | |||
| } | |||
| public static HtmlTag Parse(string str, ref int pos) | |||
| { | |||
| StringScanner sp = new StringScanner(str, pos); | |||
| var ret = Parse(sp); | |||
| if (ret!=null) | |||
| { | |||
| pos = sp.position; | |||
| return ret; | |||
| } | |||
| return null; | |||
| } | |||
| public static HtmlTag Parse(StringScanner p) | |||
| { | |||
| // Save position | |||
| int savepos = p.position; | |||
| // Parse it | |||
| var ret = ParseHelper(p); | |||
| if (ret!=null) | |||
| return ret; | |||
| // Rewind if failed | |||
| p.position = savepos; | |||
| return null; | |||
| } | |||
| private static HtmlTag ParseHelper(StringScanner p) | |||
| { | |||
| // Does it look like a tag? | |||
| if (p.current != '<') | |||
| return null; | |||
| // Skip '<' | |||
| p.SkipForward(1); | |||
| // Is it a comment? | |||
| if (p.SkipString("!--")) | |||
| { | |||
| p.Mark(); | |||
| if (p.Find("-->")) | |||
| { | |||
| var t = new HtmlTag("!"); | |||
| t.m_attributes.Add("content", p.Extract()); | |||
| t.m_closed = true; | |||
| p.SkipForward(3); | |||
| return t; | |||
| } | |||
| } | |||
| // Is it a closing tag eg: </div> | |||
| bool bClosing = p.SkipChar('/'); | |||
| // Get the tag name | |||
| string tagName=null; | |||
| if (!p.SkipIdentifier(ref tagName)) | |||
| return null; | |||
| // Probably a tag, create the HtmlTag object now | |||
| HtmlTag tag = new HtmlTag(tagName); | |||
| tag.m_closing = bClosing; | |||
| // If it's a closing tag, no attributes | |||
| if (bClosing) | |||
| { | |||
| if (p.current != '>') | |||
| return null; | |||
| p.SkipForward(1); | |||
| return tag; | |||
| } | |||
| while (!p.eof) | |||
| { | |||
| // Skip whitespace | |||
| p.SkipWhitespace(); | |||
| // Check for closed tag eg: <hr /> | |||
| if (p.SkipString("/>")) | |||
| { | |||
| tag.m_closed=true; | |||
| return tag; | |||
| } | |||
| // End of tag? | |||
| if (p.SkipChar('>')) | |||
| { | |||
| return tag; | |||
| } | |||
| // attribute name | |||
| string attributeName = null; | |||
| if (!p.SkipIdentifier(ref attributeName)) | |||
| return null; | |||
| // Skip whitespace | |||
| p.SkipWhitespace(); | |||
| // Skip equal sign | |||
| if (p.SkipChar('=')) | |||
| { | |||
| // Skip whitespace | |||
| p.SkipWhitespace(); | |||
| // Optional quotes | |||
| if (p.SkipChar('\"')) | |||
| { | |||
| // Scan the value | |||
| p.Mark(); | |||
| if (!p.Find('\"')) | |||
| return null; | |||
| // Store the value | |||
| tag.m_attributes.Add(attributeName, p.Extract()); | |||
| // Skip closing quote | |||
| p.SkipForward(1); | |||
| } | |||
| else | |||
| { | |||
| // Scan the value | |||
| p.Mark(); | |||
| while (!p.eof && !char.IsWhiteSpace(p.current) && p.current != '>' && p.current != '/') | |||
| p.SkipForward(1); | |||
| if (!p.eof) | |||
| { | |||
| // Store the value | |||
| tag.m_attributes.Add(attributeName, p.Extract()); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| tag.m_attributes.Add(attributeName, ""); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,344 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| public class LinkDefinition | |||
| { | |||
| public LinkDefinition(string id) | |||
| { | |||
| this.id= id; | |||
| } | |||
| public LinkDefinition(string id, string url) | |||
| { | |||
| this.id = id; | |||
| this.url = url; | |||
| } | |||
| public LinkDefinition(string id, string url, string title) | |||
| { | |||
| this.id = id; | |||
| this.url = url; | |||
| this.title = title; | |||
| } | |||
| public string id | |||
| { | |||
| get; | |||
| set; | |||
| } | |||
| public string url | |||
| { | |||
| get; | |||
| set; | |||
| } | |||
| public string title | |||
| { | |||
| get; | |||
| set; | |||
| } | |||
| internal void RenderLink(Markdown m, StringBuilder b, string link_text) | |||
| { | |||
| if (url.StartsWith("mailto:")) | |||
| { | |||
| b.Append("<a href=\""); | |||
| Utils.HtmlRandomize(b, url); | |||
| b.Append('\"'); | |||
| if (!String.IsNullOrEmpty(title)) | |||
| { | |||
| b.Append(" title=\""); | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(b, title); | |||
| b.Append('\"'); | |||
| } | |||
| b.Append('>'); | |||
| Utils.HtmlRandomize(b, link_text); | |||
| b.Append("</a>"); | |||
| } | |||
| else | |||
| { | |||
| HtmlTag tag = new HtmlTag("a"); | |||
| // encode url | |||
| StringBuilder sb = m.GetStringBuilder(); | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); | |||
| tag.attributes["href"] = sb.ToString(); | |||
| // encode title | |||
| if (!String.IsNullOrEmpty(title )) | |||
| { | |||
| sb.Length = 0; | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); | |||
| tag.attributes["title"] = sb.ToString(); | |||
| } | |||
| // Do user processing | |||
| m.OnPrepareLink(tag); | |||
| // Render the opening tag | |||
| tag.RenderOpening(b); | |||
| b.Append(link_text); // Link text already escaped by SpanFormatter | |||
| b.Append("</a>"); | |||
| } | |||
| } | |||
| internal void RenderImg(Markdown m, StringBuilder b, string alt_text) | |||
| { | |||
| HtmlTag tag = new HtmlTag("img"); | |||
| // encode url | |||
| StringBuilder sb = m.GetStringBuilder(); | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); | |||
| tag.attributes["src"] = sb.ToString(); | |||
| // encode alt text | |||
| if (!String.IsNullOrEmpty(alt_text)) | |||
| { | |||
| sb.Length = 0; | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(sb, alt_text); | |||
| tag.attributes["alt"] = sb.ToString(); | |||
| } | |||
| // encode title | |||
| if (!String.IsNullOrEmpty(title)) | |||
| { | |||
| sb.Length = 0; | |||
| Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); | |||
| tag.attributes["title"] = sb.ToString(); | |||
| } | |||
| tag.closed = true; | |||
| m.OnPrepareImage(tag, m.RenderingTitledImage); | |||
| tag.RenderOpening(b); | |||
| } | |||
| // Parse a link definition from a string (used by test cases) | |||
| internal static LinkDefinition ParseLinkDefinition(string str, bool ExtraMode) | |||
| { | |||
| StringScanner p = new StringScanner(str); | |||
| return ParseLinkDefinitionInternal(p, ExtraMode); | |||
| } | |||
| // Parse a link definition | |||
| internal static LinkDefinition ParseLinkDefinition(StringScanner p, bool ExtraMode) | |||
| { | |||
| int savepos=p.position; | |||
| var l = ParseLinkDefinitionInternal(p, ExtraMode); | |||
| if (l==null) | |||
| p.position = savepos; | |||
| return l; | |||
| } | |||
| internal static LinkDefinition ParseLinkDefinitionInternal(StringScanner p, bool ExtraMode) | |||
| { | |||
| // Skip leading white space | |||
| p.SkipWhitespace(); | |||
| // Must start with an opening square bracket | |||
| if (!p.SkipChar('[')) | |||
| return null; | |||
| // Extract the id | |||
| p.Mark(); | |||
| if (!p.Find(']')) | |||
| return null; | |||
| string id = p.Extract(); | |||
| if (id.Length == 0) | |||
| return null; | |||
| if (!p.SkipString("]:")) | |||
| return null; | |||
| // Parse the url and title | |||
| var link=ParseLinkTarget(p, id, ExtraMode); | |||
| // and trailing whitespace | |||
| p.SkipLinespace(); | |||
| // Trailing crap, not a valid link reference... | |||
| if (!p.eol) | |||
| return null; | |||
| return link; | |||
| } | |||
| // Parse just the link target | |||
| // For reference link definition, this is the bit after "[id]: thisbit" | |||
| // For inline link, this is the bit in the parens: [link text](thisbit) | |||
| internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode) | |||
| { | |||
| // Skip whitespace | |||
| p.SkipWhitespace(); | |||
| // End of string? | |||
| if (p.eol) | |||
| return null; | |||
| // Create the link definition | |||
| var r = new LinkDefinition(id); | |||
| // Is the url enclosed in angle brackets | |||
| if (p.SkipChar('<')) | |||
| { | |||
| // Extract the url | |||
| p.Mark(); | |||
| // Find end of the url | |||
| while (p.current != '>') | |||
| { | |||
| if (p.eof) | |||
| return null; | |||
| p.SkipEscapableChar(ExtraMode); | |||
| } | |||
| string url = p.Extract(); | |||
| if (!p.SkipChar('>')) | |||
| return null; | |||
| // Unescape it | |||
| r.url = Utils.UnescapeString(url.Trim(), ExtraMode); | |||
| // Skip whitespace | |||
| p.SkipWhitespace(); | |||
| } | |||
| else | |||
| { | |||
| // Find end of the url | |||
| p.Mark(); | |||
| int paren_depth = 1; | |||
| while (!p.eol) | |||
| { | |||
| char ch=p.current; | |||
| if (char.IsWhiteSpace(ch)) | |||
| break; | |||
| if (id == null) | |||
| { | |||
| if (ch == '(') | |||
| paren_depth++; | |||
| else if (ch == ')') | |||
| { | |||
| paren_depth--; | |||
| if (paren_depth==0) | |||
| break; | |||
| } | |||
| } | |||
| p.SkipEscapableChar(ExtraMode); | |||
| } | |||
| r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode); | |||
| } | |||
| p.SkipLinespace(); | |||
| // End of inline target | |||
| if (p.DoesMatch(')')) | |||
| return r; | |||
| bool bOnNewLine = p.eol; | |||
| int posLineEnd = p.position; | |||
| if (p.eol) | |||
| { | |||
| p.SkipEol(); | |||
| p.SkipLinespace(); | |||
| } | |||
| // Work out what the title is delimited with | |||
| char delim; | |||
| switch (p.current) | |||
| { | |||
| case '\'': | |||
| case '\"': | |||
| delim = p.current; | |||
| break; | |||
| case '(': | |||
| delim = ')'; | |||
| break; | |||
| default: | |||
| if (bOnNewLine) | |||
| { | |||
| p.position = posLineEnd; | |||
| return r; | |||
| } | |||
| else | |||
| return null; | |||
| } | |||
| // Skip the opening title delimiter | |||
| p.SkipForward(1); | |||
| // Find the end of the title | |||
| p.Mark(); | |||
| while (true) | |||
| { | |||
| if (p.eol) | |||
| return null; | |||
| if (p.current == delim) | |||
| { | |||
| if (delim != ')') | |||
| { | |||
| int savepos = p.position; | |||
| // Check for embedded quotes in title | |||
| // Skip the quote and any trailing whitespace | |||
| p.SkipForward(1); | |||
| p.SkipLinespace(); | |||
| // Next we expect either the end of the line for a link definition | |||
| // or the close bracket for an inline link | |||
| if ((id == null && p.current != ')') || | |||
| (id != null && !p.eol)) | |||
| { | |||
| continue; | |||
| } | |||
| p.position = savepos; | |||
| } | |||
| // End of title | |||
| break; | |||
| } | |||
| p.SkipEscapableChar(ExtraMode); | |||
| } | |||
| // Store the title | |||
| r.title = Utils.UnescapeString(p.Extract(), ExtraMode); | |||
| // Skip closing quote | |||
| p.SkipForward(1); | |||
| // Done! | |||
| return r; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| internal class LinkInfo | |||
| { | |||
| public LinkInfo(LinkDefinition def, string link_text) | |||
| { | |||
| this.def = def; | |||
| this.link_text = link_text; | |||
| } | |||
| public LinkDefinition def; | |||
| public string link_text; | |||
| } | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{1569ED47-C7C9-4261-B6F4-7445BD0F2C95}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>MarkdownDeep</RootNamespace> | |||
| <AssemblyName>MarkdownDeep</AssemblyName> | |||
| <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <FileUpgradeFlags> | |||
| </FileUpgradeFlags> | |||
| <UpgradeBackupLocation> | |||
| </UpgradeBackupLocation> | |||
| <OldToolsVersion>3.5</OldToolsVersion> | |||
| <PublishUrl>publish\</PublishUrl> | |||
| <Install>true</Install> | |||
| <InstallFrom>Disk</InstallFrom> | |||
| <UpdateEnabled>false</UpdateEnabled> | |||
| <UpdateMode>Foreground</UpdateMode> | |||
| <UpdateInterval>7</UpdateInterval> | |||
| <UpdateIntervalUnits>Days</UpdateIntervalUnits> | |||
| <UpdatePeriodically>false</UpdatePeriodically> | |||
| <UpdateRequired>false</UpdateRequired> | |||
| <MapFileExtensions>true</MapFileExtensions> | |||
| <ApplicationRevision>0</ApplicationRevision> | |||
| <ApplicationVersion>1.0.0.%2a</ApplicationVersion> | |||
| <IsWebBootstrapper>false</IsWebBootstrapper> | |||
| <UseApplicationTrust>false</UseApplicationTrust> | |||
| <BootstrapperEnabled>true</BootstrapperEnabled> | |||
| <TargetFrameworkProfile /> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> | |||
| <Prefer32Bit>false</Prefer32Bit> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> | |||
| <Prefer32Bit>false</Prefer32Bit> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="Accessibility" /> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Drawing" /> | |||
| <Reference Include="System.Web" /> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data.DataSetExtensions"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="Abbreviation.cs" /> | |||
| <Compile Include="BlockProcessor.cs" /> | |||
| <Compile Include="FootnoteReference.cs" /> | |||
| <Compile Include="HtmlTag.cs" /> | |||
| <Compile Include="LinkDefinition.cs" /> | |||
| <Compile Include="LinkInfo.cs" /> | |||
| <Compile Include="MardownDeep.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| <Compile Include="Block.cs" /> | |||
| <Compile Include="SpanFormatter.cs" /> | |||
| <Compile Include="StringScanner.cs" /> | |||
| <Compile Include="TableSpec.cs" /> | |||
| <Compile Include="Token.cs" /> | |||
| <Compile Include="Utils.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> | |||
| <Visible>False</Visible> | |||
| <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName> | |||
| <Install>false</Install> | |||
| </BootstrapperPackage> | |||
| <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> | |||
| <Visible>False</Visible> | |||
| <ProductName>.NET Framework 3.5 SP1</ProductName> | |||
| <Install>true</Install> | |||
| </BootstrapperPackage> | |||
| <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1"> | |||
| <Visible>False</Visible> | |||
| <ProductName>Windows Installer 3.1</ProductName> | |||
| <Install>true</Install> | |||
| </BootstrapperPackage> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <PropertyGroup> | |||
| <PostBuildEvent> | |||
| </PostBuildEvent> | |||
| </PropertyGroup> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| </Project> | |||
| @@ -0,0 +1,37 @@ | |||
| using System.Reflection; | |||
| using System.Runtime.CompilerServices; | |||
| using System.Runtime.InteropServices; | |||
| // General Information about an assembly is controlled through the following | |||
| // set of attributes. Change these attribute values to modify the information | |||
| // associated with an assembly. | |||
| [assembly: AssemblyTitle("MarkdownDeep")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Topten Software")] | |||
| [assembly: AssemblyProduct("MarkdownDeep")] | |||
| [assembly: AssemblyCopyright("Copyright © 2010-2016 Topten Software")] | |||
| [assembly: AssemblyTrademark("")] | |||
| [assembly: AssemblyCulture("")] | |||
| // Setting ComVisible to false makes the types in this assembly not visible | |||
| // to COM components. If you need to access a type in this assembly from | |||
| // COM, set the ComVisible attribute to true on that type. | |||
| [assembly: ComVisible(false)] | |||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||
| [assembly: Guid("dddc6d83-efc4-4387-90e2-eabe9f5998dd")] | |||
| // Version information for an assembly consists of the following four values: | |||
| // | |||
| // Major Version | |||
| // Minor Version | |||
| // Build Number | |||
| // Revision | |||
| // | |||
| // You can specify all the values or you can default the Build and Revision Numbers | |||
| // by using the '*' as shown below: | |||
| // [assembly: AssemblyVersion("1.0.*")] | |||
| [assembly: AssemblyVersion("1.6.0.0")] | |||
| [assembly: AssemblyFileVersion("1.6.0")] | |||
| [assembly: InternalsVisibleTo("MarkdownDeepTests")] | |||
| @@ -0,0 +1,541 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| /* | |||
| * StringScanner is a simple class to help scan through an input string. | |||
| * | |||
| * Maintains a current position with various operations to inspect the current | |||
| * character, skip forward, check for matches, skip whitespace etc... | |||
| */ | |||
| public class StringScanner | |||
| { | |||
| // Constructor | |||
| public StringScanner() | |||
| { | |||
| } | |||
| // Constructor | |||
| public StringScanner(string str) | |||
| { | |||
| Reset(str); | |||
| } | |||
| // Constructor | |||
| public StringScanner(string str, int pos) | |||
| { | |||
| Reset(str, pos); | |||
| } | |||
| // Constructor | |||
| public StringScanner(string str, int pos, int len) | |||
| { | |||
| Reset(str, pos, len); | |||
| } | |||
| // Reset | |||
| public void Reset(string str) | |||
| { | |||
| Reset(str, 0, str!=null ? str.Length : 0); | |||
| } | |||
| // Reset | |||
| public void Reset(string str, int pos) | |||
| { | |||
| Reset(str, pos, str!=null ? str.Length - pos : 0); | |||
| } | |||
| // Reset | |||
| public void Reset(string str, int pos, int len) | |||
| { | |||
| if (str == null) | |||
| str = ""; | |||
| if (len < 0) | |||
| len = 0; | |||
| if (pos < 0) | |||
| pos = 0; | |||
| if (pos > str.Length) | |||
| pos = str.Length; | |||
| this.str = str; | |||
| this.start = pos; | |||
| this.pos = pos; | |||
| this.end = pos + len; | |||
| if (end > str.Length) | |||
| end = str.Length; | |||
| } | |||
| // Get the entire input string | |||
| public string input | |||
| { | |||
| get | |||
| { | |||
| return str; | |||
| } | |||
| } | |||
| // Get the character at the current position | |||
| public char current | |||
| { | |||
| get | |||
| { | |||
| if (pos < start || pos >= end) | |||
| return '\0'; | |||
| else | |||
| return str[pos]; | |||
| } | |||
| } | |||
| // Get/set the current position | |||
| public int position | |||
| { | |||
| get | |||
| { | |||
| return pos; | |||
| } | |||
| set | |||
| { | |||
| pos = value; | |||
| } | |||
| } | |||
| // Get the remainder of the input | |||
| // (use this in a watch window while debugging :) | |||
| public string remainder | |||
| { | |||
| get | |||
| { | |||
| return Substring(position); | |||
| } | |||
| } | |||
| // Skip to the end of file | |||
| public void SkipToEof() | |||
| { | |||
| pos = end; | |||
| } | |||
| // Skip to the end of the current line | |||
| public void SkipToEol() | |||
| { | |||
| while (pos < end) | |||
| { | |||
| char ch=str[pos]; | |||
| if (ch=='\r' || ch=='\n') | |||
| break; | |||
| pos++; | |||
| } | |||
| } | |||
| // Skip if currently at a line end | |||
| public bool SkipEol() | |||
| { | |||
| if (pos < end) | |||
| { | |||
| char ch = str[pos]; | |||
| if (ch == '\r') | |||
| { | |||
| pos++; | |||
| if (pos < end && str[pos] == '\n') | |||
| pos++; | |||
| return true; | |||
| } | |||
| else if (ch == '\n') | |||
| { | |||
| pos++; | |||
| if (pos < end && str[pos] == '\r') | |||
| pos++; | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| // Skip to the next line | |||
| public void SkipToNextLine() | |||
| { | |||
| SkipToEol(); | |||
| SkipEol(); | |||
| } | |||
| // Get the character at offset from current position | |||
| // Or, \0 if out of range | |||
| public char CharAtOffset(int offset) | |||
| { | |||
| int index = pos + offset; | |||
| if (index < start) | |||
| return '\0'; | |||
| if (index >= end) | |||
| return '\0'; | |||
| return str[index]; | |||
| } | |||
| // Skip a number of characters | |||
| public void SkipForward(int characters) | |||
| { | |||
| pos += characters; | |||
| } | |||
| // Skip a character if present | |||
| public bool SkipChar(char ch) | |||
| { | |||
| if (current == ch) | |||
| { | |||
| SkipForward(1); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Skip a matching string | |||
| public bool SkipString(string str) | |||
| { | |||
| if (DoesMatch(str)) | |||
| { | |||
| SkipForward(str.Length); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Skip a matching string | |||
| public bool SkipStringI(string str) | |||
| { | |||
| if (DoesMatchI(str)) | |||
| { | |||
| SkipForward(str.Length); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Skip any whitespace | |||
| public bool SkipWhitespace() | |||
| { | |||
| if (!char.IsWhiteSpace(current)) | |||
| return false; | |||
| SkipForward(1); | |||
| while (char.IsWhiteSpace(current)) | |||
| SkipForward(1); | |||
| return true; | |||
| } | |||
| // Check if a character is space or tab | |||
| public static bool IsLineSpace(char ch) | |||
| { | |||
| return ch == ' ' || ch == '\t'; | |||
| } | |||
| // Skip spaces and tabs | |||
| public bool SkipLinespace() | |||
| { | |||
| if (!IsLineSpace(current)) | |||
| return false; | |||
| SkipForward(1); | |||
| while (IsLineSpace(current)) | |||
| SkipForward(1); | |||
| return true; | |||
| } | |||
| // Does current character match something | |||
| public bool DoesMatch(char ch) | |||
| { | |||
| return current == ch; | |||
| } | |||
| // Does character at offset match a character | |||
| public bool DoesMatch(int offset, char ch) | |||
| { | |||
| return CharAtOffset(offset) == ch; | |||
| } | |||
| // Does current character match any of a range of characters | |||
| public bool DoesMatchAny(char[] chars) | |||
| { | |||
| for (int i = 0; i < chars.Length; i++) | |||
| { | |||
| if (DoesMatch(chars[i])) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Does current character match any of a range of characters | |||
| public bool DoesMatchAny(int offset, char[] chars) | |||
| { | |||
| for (int i = 0; i < chars.Length; i++) | |||
| { | |||
| if (DoesMatch(offset, chars[i])) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Does current string position match a string | |||
| public bool DoesMatch(string str) | |||
| { | |||
| for (int i = 0; i < str.Length; i++) | |||
| { | |||
| if (str[i] != CharAtOffset(i)) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| // Does current string position match a string | |||
| public bool DoesMatchI(string str) | |||
| { | |||
| return string.Compare(str, Substring(position, str.Length), true) == 0; | |||
| } | |||
| // Extract a substring | |||
| public string Substring(int start) | |||
| { | |||
| return str.Substring(start, end-start); | |||
| } | |||
| // Extract a substring | |||
| public string Substring(int start, int len) | |||
| { | |||
| if (start + len > end) | |||
| len = end - start; | |||
| return str.Substring(start, len); | |||
| } | |||
| // Scan forward for a character | |||
| public bool Find(char ch) | |||
| { | |||
| if (pos >= end) | |||
| return false; | |||
| // Find it | |||
| int index = str.IndexOf(ch, pos); | |||
| if (index < 0 || index>=end) | |||
| return false; | |||
| // Store new position | |||
| pos = index; | |||
| return true; | |||
| } | |||
| // Find any of a range of characters | |||
| public bool FindAny(char[] chars) | |||
| { | |||
| if (pos >= end) | |||
| return false; | |||
| // Find it | |||
| int index = str.IndexOfAny(chars, pos); | |||
| if (index < 0 || index>=end) | |||
| return false; | |||
| // Store new position | |||
| pos = index; | |||
| return true; | |||
| } | |||
| // Forward scan for a string | |||
| public bool Find(string find) | |||
| { | |||
| if (pos >= end) | |||
| return false; | |||
| int index = str.IndexOf(find, pos); | |||
| if (index < 0 || index > end-find.Length) | |||
| return false; | |||
| pos = index; | |||
| return true; | |||
| } | |||
| // Forward scan for a string (case insensitive) | |||
| public bool FindI(string find) | |||
| { | |||
| if (pos >= end) | |||
| return false; | |||
| int index = str.IndexOf(find, pos, StringComparison.InvariantCultureIgnoreCase); | |||
| if (index < 0 || index >= end - find.Length) | |||
| return false; | |||
| pos = index; | |||
| return true; | |||
| } | |||
| // Are we at eof? | |||
| public bool eof | |||
| { | |||
| get | |||
| { | |||
| return pos >= end; | |||
| } | |||
| } | |||
| // Are we at eol? | |||
| public bool eol | |||
| { | |||
| get | |||
| { | |||
| return IsLineEnd(current); | |||
| } | |||
| } | |||
| // Are we at bof? | |||
| public bool bof | |||
| { | |||
| get | |||
| { | |||
| return pos == start; | |||
| } | |||
| } | |||
| // Mark current position | |||
| public void Mark() | |||
| { | |||
| mark = pos; | |||
| } | |||
| // Extract string from mark to current position | |||
| public string Extract() | |||
| { | |||
| if (mark >= pos) | |||
| return ""; | |||
| return str.Substring(mark, pos - mark); | |||
| } | |||
| // Skip an identifier | |||
| public bool SkipIdentifier(ref string identifier) | |||
| { | |||
| int savepos = position; | |||
| if (!Utils.ParseIdentifier(this.str, ref pos, ref identifier)) | |||
| return false; | |||
| if (pos >= end) | |||
| { | |||
| pos = savepos; | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| public bool SkipFootnoteID(out string id) | |||
| { | |||
| int savepos = position; | |||
| SkipLinespace(); | |||
| Mark(); | |||
| while (true) | |||
| { | |||
| char ch = current; | |||
| if (char.IsLetterOrDigit(ch) || ch == '-' || ch == '_' || ch == ':' || ch == '.' || ch == ' ') | |||
| SkipForward(1); | |||
| else | |||
| break; | |||
| } | |||
| if (position > mark) | |||
| { | |||
| id = Extract().Trim(); | |||
| if (!String.IsNullOrEmpty(id)) | |||
| { | |||
| SkipLinespace(); | |||
| return true; | |||
| } | |||
| } | |||
| position = savepos; | |||
| id = null; | |||
| return false; | |||
| } | |||
| // Skip a Html entity (eg: &) | |||
| public bool SkipHtmlEntity(ref string entity) | |||
| { | |||
| int savepos = position; | |||
| if (!Utils.SkipHtmlEntity(this.str, ref pos, ref entity)) | |||
| return false; | |||
| if (pos > end) | |||
| { | |||
| pos = savepos; | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| // Check if a character marks end of line | |||
| public static bool IsLineEnd(char ch) | |||
| { | |||
| return ch == '\r' || ch == '\n' || ch=='\0'; | |||
| } | |||
| bool IsUrlChar(char ch) | |||
| { | |||
| switch (ch) | |||
| { | |||
| case '+': | |||
| case '&': | |||
| case '@': | |||
| case '#': | |||
| case '/': | |||
| case '%': | |||
| case '?': | |||
| case '=': | |||
| case '~': | |||
| case '_': | |||
| case '|': | |||
| case '[': | |||
| case ']': | |||
| case '(': | |||
| case ')': | |||
| case '!': | |||
| case ':': | |||
| case ',': | |||
| case '.': | |||
| case ';': | |||
| return true; | |||
| default: | |||
| return Char.IsLetterOrDigit(ch); | |||
| } | |||
| } | |||
| // Attributes | |||
| string str; | |||
| int start; | |||
| int pos; | |||
| int end; | |||
| int mark; | |||
| } | |||
| } | |||
| @@ -0,0 +1,219 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| internal enum ColumnAlignment | |||
| { | |||
| NA, | |||
| Left, | |||
| Right, | |||
| Center, | |||
| } | |||
| internal class TableSpec | |||
| { | |||
| public TableSpec() | |||
| { | |||
| } | |||
| public bool LeadingBar; | |||
| public bool TrailingBar; | |||
| public List<ColumnAlignment> Columns=new List<ColumnAlignment>(); | |||
| public List<string> Headers; | |||
| public List<List<string>> Rows=new List<List<string>>(); | |||
| public List<string> ParseRow(StringScanner p) | |||
| { | |||
| p.SkipLinespace(); | |||
| if (p.eol) | |||
| return null; // Blank line ends the table | |||
| bool bAnyBars=LeadingBar; | |||
| if (LeadingBar && !p.SkipChar('|')) | |||
| { | |||
| return null; | |||
| } | |||
| // Create the row | |||
| List<string> row = new List<string>(); | |||
| // Parse all columns except the last | |||
| while (!p.eol) | |||
| { | |||
| // Find the next vertical bar | |||
| p.Mark(); | |||
| while (!p.eol && p.current != '|') | |||
| p.SkipEscapableChar(true); | |||
| row.Add(p.Extract().Trim()); | |||
| bAnyBars|=p.SkipChar('|'); | |||
| } | |||
| // Require at least one bar to continue the table | |||
| if (!bAnyBars) | |||
| return null; | |||
| // Add missing columns | |||
| while (row.Count < Columns.Count) | |||
| { | |||
| row.Add(" "); | |||
| } | |||
| p.SkipEol(); | |||
| return row; | |||
| } | |||
| internal void RenderRow(Markdown m, StringBuilder b, List<string> row, string type) | |||
| { | |||
| for (int i=0; i<row.Count; i++) | |||
| { | |||
| b.Append("\t<"); | |||
| b.Append(type); | |||
| if (i < Columns.Count) | |||
| { | |||
| switch (Columns[i]) | |||
| { | |||
| case ColumnAlignment.Left: | |||
| b.Append(" align=\"left\""); | |||
| break; | |||
| case ColumnAlignment.Right: | |||
| b.Append(" align=\"right\""); | |||
| break; | |||
| case ColumnAlignment.Center: | |||
| b.Append(" align=\"center\""); | |||
| break; | |||
| } | |||
| } | |||
| b.Append(">"); | |||
| m.SpanFormatter.Format(b, row[i]); | |||
| b.Append("</"); | |||
| b.Append(type); | |||
| b.Append(">\n"); | |||
| } | |||
| } | |||
| public void Render(Markdown m, StringBuilder b) | |||
| { | |||
| b.Append("<table>\n"); | |||
| if (Headers != null) | |||
| { | |||
| b.Append("<thead>\n<tr>\n"); | |||
| RenderRow(m, b, Headers, "th"); | |||
| b.Append("</tr>\n</thead>\n"); | |||
| } | |||
| b.Append("<tbody>\n"); | |||
| foreach (var row in Rows) | |||
| { | |||
| b.Append("<tr>\n"); | |||
| RenderRow(m, b, row, "td"); | |||
| b.Append("</tr>\n"); | |||
| } | |||
| b.Append("</tbody>\n"); | |||
| b.Append("</table>\n"); | |||
| } | |||
| public static TableSpec Parse(StringScanner p) | |||
| { | |||
| // Leading line space allowed | |||
| p.SkipLinespace(); | |||
| // Quick check for typical case | |||
| if (p.current != '|' && p.current != ':' && p.current != '-') | |||
| return null; | |||
| // Don't create the spec until it at least looks like one | |||
| TableSpec spec = null; | |||
| // Leading bar, looks like a table spec | |||
| if (p.SkipChar('|')) | |||
| { | |||
| spec=new TableSpec(); | |||
| spec.LeadingBar=true; | |||
| } | |||
| // Process all columns | |||
| while (true) | |||
| { | |||
| // Parse column spec | |||
| p.SkipLinespace(); | |||
| // Must have something in the spec | |||
| if (p.current == '|') | |||
| return null; | |||
| bool AlignLeft = p.SkipChar(':'); | |||
| while (p.current == '-') | |||
| p.SkipForward(1); | |||
| bool AlignRight = p.SkipChar(':'); | |||
| p.SkipLinespace(); | |||
| // Work out column alignment | |||
| ColumnAlignment col = ColumnAlignment.NA; | |||
| if (AlignLeft && AlignRight) | |||
| col = ColumnAlignment.Center; | |||
| else if (AlignLeft) | |||
| col = ColumnAlignment.Left; | |||
| else if (AlignRight) | |||
| col = ColumnAlignment.Right; | |||
| if (p.eol) | |||
| { | |||
| // Not a spec? | |||
| if (spec == null) | |||
| return null; | |||
| // Add the final spec? | |||
| spec.Columns.Add(col); | |||
| return spec; | |||
| } | |||
| // We expect a vertical bar | |||
| if (!p.SkipChar('|')) | |||
| return null; | |||
| // Create the table spec | |||
| if (spec==null) | |||
| spec=new TableSpec(); | |||
| // Add the column | |||
| spec.Columns.Add(col); | |||
| // Check for trailing vertical bar | |||
| p.SkipLinespace(); | |||
| if (p.eol) | |||
| { | |||
| spec.TrailingBar = true; | |||
| return spec; | |||
| } | |||
| // Next column | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,93 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace MarkdownDeep | |||
| { | |||
| /* | |||
| * Token is used to mark out various special parts of a string being | |||
| * formatted by SpanFormatter. | |||
| * | |||
| * Strings aren't actually stored in the token - just their offset | |||
| * and length in the input string. | |||
| * | |||
| * For performance, Token's are pooled and reused. | |||
| * See SpanFormatter.CreateToken() | |||
| */ | |||
| // TokenType - what sort of token? | |||
| internal enum TokenType | |||
| { | |||
| Text, // Plain text, should be htmlencoded | |||
| HtmlTag, // Valid html tag, write out directly but escape &s; | |||
| Html, // Valid html, write out directly | |||
| open_em, // <em> | |||
| close_em, // </em> | |||
| open_strong, // <strong> | |||
| close_strong, // </strong> | |||
| code_span, // <code></code> | |||
| br, // <br /> | |||
| link, // <a href>, data = LinkInfo | |||
| img, // <img>, data = LinkInfo | |||
| footnote, // Footnote reference | |||
| abbreviation, // An abbreviation, data is a reference to Abbrevation instance | |||
| // These are used during construction of <em> and <strong> tokens | |||
| opening_mark, // opening '*' or '_' | |||
| closing_mark, // closing '*' or '_' | |||
| internal_mark, // internal '*' or '_' | |||
| } | |||
| // Token | |||
| internal class Token | |||
| { | |||
| // Constructor | |||
| public Token(TokenType type, int startOffset, int length) | |||
| { | |||
| this.type = type; | |||
| this.startOffset = startOffset; | |||
| this.length = length; | |||
| } | |||
| // Constructor | |||
| public Token(TokenType type, object data) | |||
| { | |||
| this.type = type; | |||
| this.data = data; | |||
| } | |||
| public override string ToString() | |||
| { | |||
| if (true || data == null) | |||
| { | |||
| return string.Format("{0} - {1} - {2}", type.ToString(), startOffset, length); | |||
| } | |||
| else | |||
| { | |||
| return string.Format("{0} - {1} - {2} -> {3}", type.ToString(), startOffset, length, data.ToString()); | |||
| } | |||
| } | |||
| public TokenType type; | |||
| public int startOffset; | |||
| public int length; | |||
| public object data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,495 @@ | |||
| // | |||
| // MarkdownDeep - http://www.toptensoftware.com/markdowndeep | |||
| // Copyright (C) 2010-2011 Topten Software | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product except in | |||
| // compliance with the License. You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is | |||
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and limitations under the License. | |||
| // | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Text.RegularExpressions; | |||
| namespace MarkdownDeep | |||
| { | |||
| /* | |||
| * Various utility and extension methods | |||
| */ | |||
| static class Utils | |||
| { | |||
| // Extension method. Get the last item in a list (or null if empty) | |||
| public static T Last<T>(this List<T> list) where T:class | |||
| { | |||
| if (list.Count > 0) | |||
| return list[list.Count - 1]; | |||
| else | |||
| return null; | |||
| } | |||
| // Extension method. Get the first item in a list (or null if empty) | |||
| public static T First<T>(this List<T> list) where T : class | |||
| { | |||
| if (list.Count > 0) | |||
| return list[0]; | |||
| else | |||
| return null; | |||
| } | |||
| // Extension method. Use a list like a stack | |||
| public static void Push<T>(this List<T> list, T value) where T : class | |||
| { | |||
| list.Add(value); | |||
| } | |||
| // Extension method. Remove last item from a list | |||
| public static T Pop<T>(this List<T> list) where T : class | |||
| { | |||
| if (list.Count == 0) | |||
| return null; | |||
| else | |||
| { | |||
| T val = list[list.Count - 1]; | |||
| list.RemoveAt(list.Count - 1); | |||
| return val; | |||
| } | |||
| } | |||
| // Scan a string for a valid identifier. Identifier must start with alpha or underscore | |||
| // and can be followed by alpha, digit or underscore | |||
| // Updates `pos` to character after the identifier if matched | |||
| public static bool ParseIdentifier(string str, ref int pos, ref string identifer) | |||
| { | |||
| if (pos >= str.Length) | |||
| return false; | |||
| // Must start with a letter or underscore | |||
| if (!char.IsLetter(str[pos]) && str[pos] != '_') | |||
| { | |||
| return false; | |||
| } | |||
| // Find the end | |||
| int startpos = pos; | |||
| pos++; | |||
| while (pos < str.Length && (char.IsDigit(str[pos]) || char.IsLetter(str[pos]) || str[pos] == '_')) | |||
| pos++; | |||
| // Return it | |||
| identifer = str.Substring(startpos, pos - startpos); | |||
| return true; | |||
| } | |||
| // Skip over anything that looks like a valid html entity (eg: &, {, &#nnn) etc... | |||
| // Updates `pos` to character after the entity if matched | |||
| public static bool SkipHtmlEntity(string str, ref int pos, ref string entity) | |||
| { | |||
| if (str[pos] != '&') | |||
| return false; | |||
| int savepos = pos; | |||
| int len = str.Length; | |||
| int i = pos+1; | |||
| // Number entity? | |||
| bool bNumber=false; | |||
| bool bHex = false; | |||
| if (i < len && str[i] == '#') | |||
| { | |||
| bNumber = true; | |||
| i++; | |||
| // Hex identity? | |||
| if (i < len && (str[i] == 'x' || str[i] == 'X')) | |||
| { | |||
| bHex = true; | |||
| i++; | |||
| } | |||
| } | |||
| // Parse the content | |||
| int contentpos = i; | |||
| while (i < len) | |||
| { | |||
| char ch=str[i]; | |||
| if (bHex) | |||
| { | |||
| if (!(char.IsDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) | |||
| break; | |||
| } | |||
| else if (bNumber) | |||
| { | |||
| if (!char.IsDigit(ch)) | |||
| break; | |||
| } | |||
| else if (!char.IsLetterOrDigit(ch)) | |||
| break; | |||
| i++; | |||
| } | |||
| // Quit if ran out of string | |||
| if (i == len) | |||
| return false; | |||
| // Quit if nothing in the content | |||
| if (i == contentpos) | |||
| return false; | |||
| // Quit if didn't find a semicolon | |||
| if (str[i] != ';') | |||
| return false; | |||
| // Looks good... | |||
| pos = i + 1; | |||
| entity = str.Substring(savepos, pos - savepos); | |||
| return true; | |||
| } | |||
| // Randomize a string using html entities; | |||
| public static void HtmlRandomize(StringBuilder dest, string str) | |||
| { | |||
| // Deterministic random seed | |||
| int seed = 0; | |||
| foreach (char ch in str) | |||
| { | |||
| seed = unchecked(seed + ch); | |||
| } | |||
| Random r = new Random(seed); | |||
| // Randomize | |||
| foreach (char ch in str) | |||
| { | |||
| int x = r.Next() % 100; | |||
| if (x > 90 && ch != '@') | |||
| { | |||
| dest.Append(ch); | |||
| } | |||
| else if (x > 45) | |||
| { | |||
| dest.Append("&#"); | |||
| dest.Append(((int)ch).ToString()); | |||
| dest.Append(";"); | |||
| } | |||
| else | |||
| { | |||
| dest.Append("&#x"); | |||
| dest.Append(((int)ch).ToString("x")); | |||
| dest.Append(";"); | |||
| } | |||
| } | |||
| } | |||
| // Like HtmlEncode, but don't escape &'s that look like html entities | |||
| public static void SmartHtmlEncodeAmpsAndAngles(StringBuilder dest, string str) | |||
| { | |||
| if (str == null) | |||
| return; | |||
| for (int i=0; i<str.Length; i++) | |||
| { | |||
| switch (str[i]) | |||
| { | |||
| case '&': | |||
| int start = i; | |||
| string unused=null; | |||
| if (SkipHtmlEntity(str, ref i, ref unused)) | |||
| { | |||
| dest.Append(str, start, i - start); | |||
| i--; | |||
| } | |||
| else | |||
| { | |||
| dest.Append("&"); | |||
| } | |||
| break; | |||
| case '<': | |||
| dest.Append("<"); | |||
| break; | |||
| case '>': | |||
| dest.Append(">"); | |||
| break; | |||
| case '\"': | |||
| dest.Append("""); | |||
| break; | |||
| default: | |||
| dest.Append(str[i]); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| // Like HtmlEncode, but only escape &'s that don't look like html entities | |||
| public static void SmartHtmlEncodeAmps(StringBuilder dest, string str, int startOffset, int len) | |||
| { | |||
| int end = startOffset + len; | |||
| for (int i = startOffset; i < end; i++) | |||
| { | |||
| switch (str[i]) | |||
| { | |||
| case '&': | |||
| int start = i; | |||
| string unused = null; | |||
| if (SkipHtmlEntity(str, ref i, ref unused)) | |||
| { | |||
| dest.Append(str, start, i - start); | |||
| i--; | |||
| } | |||
| else | |||
| { | |||
| dest.Append("&"); | |||
| } | |||
| break; | |||
| default: | |||
| dest.Append(str[i]); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| // Check if a string is in an array of strings | |||
| public static bool IsInList(string str, string[] list) | |||
| { | |||
| foreach (var t in list) | |||
| { | |||
| if (string.Compare(t, str) == 0) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| // Check if a url is "safe" (we require urls start with valid protocol) | |||
| // Definitely don't allow "javascript:" or any of it's encodings. | |||
| public static bool IsSafeUrl(string url) | |||
| { | |||
| if (!url.StartsWith("http://") && !url.StartsWith("https://") && !url.StartsWith("ftp://")) | |||
| return false; | |||
| return true; | |||
| } | |||
| // Check if a character is escapable in markdown | |||
| public static bool IsEscapableChar(char ch, bool ExtraMode) | |||
| { | |||
| switch (ch) | |||
| { | |||
| case '\\': | |||
| case '`': | |||
| case '*': | |||
| case '_': | |||
| case '{': | |||
| case '}': | |||
| case '[': | |||
| case ']': | |||
| case '(': | |||
| case ')': | |||
| case '>': // Not in markdown documentation, but is in markdown.pl | |||
| case '#': | |||
| case '+': | |||
| case '-': | |||
| case '.': | |||
| case '!': | |||
| return true; | |||
| case ':': | |||
| case '|': | |||
| case '=': // Added for escaping Setext H1 | |||
| case '<': | |||
| return ExtraMode; | |||
| } | |||
| return false; | |||
| } | |||
| // Extension method. Skip an escapable character, or one normal character | |||
| public static void SkipEscapableChar(this StringScanner p, bool ExtraMode) | |||
| { | |||
| if (p.current == '\\' && IsEscapableChar(p.CharAtOffset(1), ExtraMode)) | |||
| { | |||
| p.SkipForward(2); | |||
| } | |||
| else | |||
| { | |||
| p.SkipForward(1); | |||
| } | |||
| } | |||
| // Remove the markdown escapes from a string | |||
| public static string UnescapeString(string str, bool ExtraMode) | |||
| { | |||
| if (str == null || str.IndexOf('\\')==-1) | |||
| return str; | |||
| var b = new StringBuilder(); | |||
| for (int i = 0; i < str.Length; i++) | |||
| { | |||
| if (str[i] == '\\' && i+1<str.Length && IsEscapableChar(str[i+1], ExtraMode)) | |||
| { | |||
| b.Append(str[i + 1]); | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| b.Append(str[i]); | |||
| } | |||
| } | |||
| return b.ToString(); | |||
| } | |||
| // Normalize the line ends in a string to just '\n' | |||
| // Handles all encodings - '\r\n' (windows), '\n\r' (mac), '\n' (unix) '\r' (something?) | |||
| static char[] lineends = new char[] { '\r', '\n' }; | |||
| public static string NormalizeLineEnds(string str) | |||
| { | |||
| if (str.IndexOfAny(lineends) < 0) | |||
| return str; | |||
| StringBuilder sb = new StringBuilder(); | |||
| StringScanner sp = new StringScanner(str); | |||
| while (!sp.eof) | |||
| { | |||
| if (sp.eol) | |||
| { | |||
| sb.Append('\n'); | |||
| sp.SkipEol(); | |||
| } | |||
| else | |||
| { | |||
| sb.Append(sp.current); | |||
| sp.SkipForward(1); | |||
| } | |||
| } | |||
| return sb.ToString(); | |||
| } | |||
| /* | |||
| * These two functions IsEmailAddress and IsWebAddress | |||
| * are intended as a quick and dirty way to tell if a | |||
| * <autolink> url is email, web address or neither. | |||
| * | |||
| * They are not intended as validating checks. | |||
| * | |||
| * (use of Regex for more correct test unnecessarily | |||
| * slowed down some test documents by up to 300%.) | |||
| */ | |||
| // Check if a string looks like an email address | |||
| public static bool IsEmailAddress(string str) | |||
| { | |||
| int posAt = str.IndexOf('@'); | |||
| if (posAt < 0) | |||
| return false; | |||
| int posLastDot = str.LastIndexOf('.'); | |||
| if (posLastDot < posAt) | |||
| return false; | |||
| return true; | |||
| } | |||
| // Check if a string looks like a url | |||
| public static bool IsWebAddress(string str) | |||
| { | |||
| return str.StartsWith("http://") || | |||
| str.StartsWith("https://") || | |||
| str.StartsWith("ftp://") || | |||
| str.StartsWith("file://"); | |||
| } | |||
| // Check if a string is a valid HTML ID identifier | |||
| internal static bool IsValidHtmlID(string str) | |||
| { | |||
| if (String.IsNullOrEmpty(str)) | |||
| return false; | |||
| // Must start with a letter | |||
| if (!Char.IsLetter(str[0])) | |||
| return false; | |||
| // Check the rest | |||
| for (int i = 0; i < str.Length; i++) | |||
| { | |||
| char ch = str[i]; | |||
| if (Char.IsLetterOrDigit(ch) || ch == '_' || ch == '-' || ch == ':' || ch == '.') | |||
| continue; | |||
| return false; | |||
| } | |||
| // OK | |||
| return true; | |||
| } | |||
| // Strip the trailing HTML ID from a header string | |||
| // ie: ## header text ## {#<idhere>} | |||
| // ^start ^out end ^end | |||
| // | |||
| // Returns null if no header id | |||
| public static string StripHtmlID(string str, int start, ref int end) | |||
| { | |||
| // Skip trailing whitespace | |||
| int pos = end - 1; | |||
| while (pos >= start && Char.IsWhiteSpace(str[pos])) | |||
| { | |||
| pos--; | |||
| } | |||
| // Skip closing '{' | |||
| if (pos < start || str[pos] != '}') | |||
| return null; | |||
| int endId = pos; | |||
| pos--; | |||
| // Find the opening '{' | |||
| while (pos >= start && str[pos] != '{') | |||
| pos--; | |||
| // Check for the # | |||
| if (pos < start || str[pos + 1] != '#') | |||
| return null; | |||
| // Extract and check the ID | |||
| int startId = pos + 2; | |||
| string strID = str.Substring(startId, endId - startId); | |||
| if (!IsValidHtmlID(strID)) | |||
| return null; | |||
| // Skip any preceeding whitespace | |||
| while (pos > start && Char.IsWhiteSpace(str[pos - 1])) | |||
| pos--; | |||
| // Done! | |||
| end = pos; | |||
| return strID; | |||
| } | |||
| public static bool IsUrlFullyQualified(string url) | |||
| { | |||
| return url.Contains("://") || url.StartsWith("mailto:"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class AutoHeaderIDTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| m = new Markdown(); | |||
| m.AutoHeadingIDs = true; | |||
| m.ExtraMode = true; | |||
| } | |||
| Markdown m; | |||
| /* Tests for pandoc style header ID generation */ | |||
| /* Tests are based on the examples in the pandoc documentation */ | |||
| [Test] | |||
| public void Simple() | |||
| { | |||
| Assert.AreEqual(@"header-identifiers-in-html", | |||
| m.MakeUniqueHeaderID(@"Header identifiers in HTML")); | |||
| } | |||
| [Test] | |||
| public void WithPunctuation() | |||
| { | |||
| Assert.AreEqual(@"dogs--in-my-house", | |||
| m.MakeUniqueHeaderID(@"Dogs?--in *my* house?")); | |||
| } | |||
| [Test] | |||
| public void WithLinks() | |||
| { | |||
| Assert.AreEqual(@"html-s5-rtf", | |||
| m.MakeUniqueHeaderID(@"[HTML](#html), [S5](#S5), [RTF](#rtf)")); | |||
| } | |||
| [Test] | |||
| public void WithLeadingNumbers() | |||
| { | |||
| Assert.AreEqual(@"applications", | |||
| m.MakeUniqueHeaderID(@"3. Applications")); | |||
| } | |||
| [Test] | |||
| public void RevertToSection() | |||
| { | |||
| Assert.AreEqual(@"section", | |||
| m.MakeUniqueHeaderID(@"!!!")); | |||
| } | |||
| [Test] | |||
| public void Duplicates() | |||
| { | |||
| Assert.AreEqual(@"heading", | |||
| m.MakeUniqueHeaderID(@"heading")); | |||
| Assert.AreEqual(@"heading-1", | |||
| m.MakeUniqueHeaderID(@"heading")); | |||
| Assert.AreEqual(@"heading-2", | |||
| m.MakeUniqueHeaderID(@"heading")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using MarkdownDeep; | |||
| using NUnit.Framework; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class AutoLinkTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| m = new Markdown(); | |||
| s = new SpanFormatter(m); | |||
| } | |||
| [Test] | |||
| public void http() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"http://url.com\">http://url.com</a> post", | |||
| s.Format("pre <http://url.com> post")); | |||
| } | |||
| [Test] | |||
| public void https() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"https://url.com\">https://url.com</a> post", | |||
| s.Format("pre <https://url.com> post")); | |||
| } | |||
| [Test] | |||
| public void ftp() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"ftp://url.com\">ftp://url.com</a> post", | |||
| s.Format("pre <ftp://url.com> post")); | |||
| } | |||
| Markdown m; | |||
| SpanFormatter s; | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class BlockLevelTests | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests() | |||
| { | |||
| return Utils.GetTests("blocktests"); | |||
| } | |||
| [Test, TestCaseSource("GetTests")] | |||
| public void Test(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,163 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class BlockProcessorTests | |||
| { | |||
| [SetUp] | |||
| public void Setup() | |||
| { | |||
| p = new BlockProcessor(new Markdown(), false); | |||
| } | |||
| [Test] | |||
| public void SingleLineParagraph() | |||
| { | |||
| var b = p.Process("paragraph"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.p, b[0].blockType); | |||
| Assert.AreEqual("paragraph", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void MultilineParagraph() | |||
| { | |||
| var b = p.Process("l1\nl2\n\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.p, b[0].blockType); | |||
| Assert.AreEqual("l1\nl2", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void SetExtH1() | |||
| { | |||
| var b = p.Process("heading\n===\n\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.h1, b[0].blockType); | |||
| Assert.AreEqual("heading", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void SetExtH2() | |||
| { | |||
| var b = p.Process("heading\n---\n\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.h2, b[0].blockType); | |||
| Assert.AreEqual("heading", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void SetExtHeadingInParagraph() | |||
| { | |||
| var b = p.Process("p1\nheading\n---\np2\n"); | |||
| Assert.AreEqual(3, b.Count); | |||
| Assert.AreEqual(BlockType.p, b[0].blockType); | |||
| Assert.AreEqual("p1", b[0].Content); | |||
| Assert.AreEqual(BlockType.h2, b[1].blockType); | |||
| Assert.AreEqual("heading", b[1].Content); | |||
| Assert.AreEqual(BlockType.p, b[2].blockType); | |||
| Assert.AreEqual("p2", b[2].Content); | |||
| } | |||
| [Test] | |||
| public void AtxHeaders() | |||
| { | |||
| var b = p.Process("#heading#\nparagraph\n"); | |||
| Assert.AreEqual(2, b.Count); | |||
| Assert.AreEqual(BlockType.h1, b[0].blockType); | |||
| Assert.AreEqual("heading", b[0].Content); | |||
| Assert.AreEqual(BlockType.p, b[1].blockType); | |||
| Assert.AreEqual("paragraph", b[1].Content); | |||
| } | |||
| [Test] | |||
| public void AtxHeadingInParagraph() | |||
| { | |||
| var b = p.Process("p1\n## heading ##\np2\n"); | |||
| Assert.AreEqual(3, b.Count); | |||
| Assert.AreEqual(BlockType.p, b[0].blockType); | |||
| Assert.AreEqual("p1", b[0].Content); | |||
| Assert.AreEqual(BlockType.h2, b[1].blockType); | |||
| Assert.AreEqual("heading", b[1].Content); | |||
| Assert.AreEqual(BlockType.p, b[2].blockType); | |||
| Assert.AreEqual("p2", b[2].Content); | |||
| } | |||
| [Test] | |||
| public void CodeBlock() | |||
| { | |||
| var b = p.Process("\tcode1\n\t\tcode2\n\tcode3\nparagraph"); | |||
| Assert.AreEqual(2, b.Count); | |||
| Block cb = b[0] as Block; | |||
| Assert.AreEqual("code1\n\tcode2\ncode3\n", cb.Content); | |||
| Assert.AreEqual(BlockType.p, b[1].blockType); | |||
| Assert.AreEqual("paragraph", b[1].Content); | |||
| } | |||
| [Test] | |||
| public void HtmlBlock() | |||
| { | |||
| var b = p.Process("<div>\n</div>\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.html, b[0].blockType); | |||
| Assert.AreEqual("<div>\n</div>\n", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void HtmlCommentBlock() | |||
| { | |||
| var b = p.Process("<!-- this is a\ncomments -->\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.html, b[0].blockType); | |||
| Assert.AreEqual("<!-- this is a\ncomments -->\n", b[0].Content); | |||
| } | |||
| [Test] | |||
| public void HorizontalRules() | |||
| { | |||
| var b = p.Process("---\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| b = p.Process("___\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| b = p.Process("***\n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| b = p.Process(" - - - \n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| b = p.Process(" _ _ _ \n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| b = p.Process(" * * * \n"); | |||
| Assert.AreEqual(1, b.Count); | |||
| Assert.AreEqual(BlockType.hr, b[0].blockType); | |||
| } | |||
| BlockProcessor p; | |||
| } | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class CodeSpanTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| f = new SpanFormatter(new Markdown()); | |||
| } | |||
| SpanFormatter f; | |||
| [Test] | |||
| public void SingleTick() | |||
| { | |||
| Assert.AreEqual("pre <code>code span</code> post", | |||
| f.Format("pre `code span` post")); | |||
| } | |||
| [Test] | |||
| public void SingleTickWithSpaces() | |||
| { | |||
| Assert.AreEqual("pre <code>code span</code> post", | |||
| f.Format("pre ` code span ` post")); | |||
| } | |||
| [Test] | |||
| public void MultiTick() | |||
| { | |||
| Assert.AreEqual("pre <code>code span</code> post", | |||
| f.Format("pre ````code span```` post")); | |||
| } | |||
| [Test] | |||
| public void MultiTickWithEmbeddedTicks() | |||
| { | |||
| Assert.AreEqual("pre <code>`code span`</code> post", | |||
| f.Format("pre ```` `code span` ```` post")); | |||
| } | |||
| [Test] | |||
| public void ContentEncoded() | |||
| { | |||
| Assert.AreEqual("pre <code><div></code> post", | |||
| f.Format("pre ```` <div> ```` post")); | |||
| Assert.AreEqual("pre <code>&amp;</code> post", | |||
| f.Format("pre ```` & ```` post")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,240 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class EmphasisTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| f = new SpanFormatter(new Markdown()); | |||
| } | |||
| SpanFormatter f; | |||
| [Test] | |||
| public void PlainText() | |||
| { | |||
| Assert.AreEqual("This is plain text", | |||
| f.Format("This is plain text")); | |||
| } | |||
| [Test] | |||
| public void em_simple() | |||
| { | |||
| Assert.AreEqual("This is <em>em</em> text", | |||
| f.Format("This is *em* text")); | |||
| Assert.AreEqual("This is <em>em</em> text", | |||
| f.Format("This is _em_ text")); | |||
| } | |||
| [Test] | |||
| public void strong_simple() | |||
| { | |||
| Assert.AreEqual("This is <strong>strong</strong> text", | |||
| f.Format("This is **strong** text")); | |||
| Assert.AreEqual("This is <strong>strong</strong> text", | |||
| f.Format("This is __strong__ text")); | |||
| } | |||
| [Test] | |||
| public void em_strong_lead_tail() | |||
| { | |||
| Assert.AreEqual("<strong>strong</strong>", | |||
| f.Format("__strong__")); | |||
| Assert.AreEqual("<strong>strong</strong>", | |||
| f.Format("**strong**")); | |||
| Assert.AreEqual("<em>em</em>", | |||
| f.Format("_em_")); | |||
| Assert.AreEqual("<em>em</em>", | |||
| f.Format("*em*")); | |||
| } | |||
| [Test] | |||
| public void strongem() | |||
| { | |||
| Assert.AreEqual("<strong><em>strongem</em></strong>", | |||
| f.Format("***strongem***")); | |||
| Assert.AreEqual("<strong><em>strongem</em></strong>", | |||
| f.Format("___strongem___")); | |||
| } | |||
| [Test] | |||
| public void no_strongem_if_spaces() | |||
| { | |||
| Assert.AreEqual("pre * notem *", | |||
| f.Format("pre * notem *")); | |||
| Assert.AreEqual("pre ** notstrong **", | |||
| f.Format("pre ** notstrong **")); | |||
| Assert.AreEqual("pre *Apples *Bananas *Oranges", | |||
| f.Format("pre *Apples *Bananas *Oranges")); | |||
| } | |||
| [Test] | |||
| public void em_in_word() | |||
| { | |||
| Assert.AreEqual("un<em>frigging</em>believable", | |||
| f.Format("un*frigging*believable")); | |||
| } | |||
| [Test] | |||
| public void strong_in_word() | |||
| { | |||
| Assert.AreEqual("un<strong>frigging</strong>believable", | |||
| f.Format("un**frigging**believable")); | |||
| } | |||
| [Test] | |||
| public void combined_1() | |||
| { | |||
| Assert.AreEqual("<strong><em>test test</em></strong>", | |||
| f.Format("***test test***")); | |||
| } | |||
| [Test] | |||
| public void combined_2() | |||
| { | |||
| Assert.AreEqual("<strong><em>test test</em></strong>", | |||
| f.Format("___test test___")); | |||
| } | |||
| [Test] | |||
| public void combined_3() | |||
| { | |||
| Assert.AreEqual("<em>test <strong>test</strong></em>", | |||
| f.Format("*test **test***")); | |||
| } | |||
| [Test] | |||
| public void combined_4() | |||
| { | |||
| Assert.AreEqual("<strong>test <em>test</em></strong>", | |||
| f.Format("**test *test***")); | |||
| } | |||
| [Test] | |||
| public void combined_5() | |||
| { | |||
| Assert.AreEqual("<strong><em>test</em> test</strong>", | |||
| f.Format("***test* test**")); | |||
| } | |||
| [Test] | |||
| public void combined_6() | |||
| { | |||
| Assert.AreEqual("<em><strong>test</strong> test</em>", | |||
| f.Format("***test** test*")); | |||
| } | |||
| [Test] | |||
| public void combined_7() | |||
| { | |||
| Assert.AreEqual("<strong><em>test</em> test</strong>", | |||
| f.Format("***test* test**")); | |||
| } | |||
| [Test] | |||
| public void combined_8() | |||
| { | |||
| Assert.AreEqual("<strong>test <em>test</em></strong>", | |||
| f.Format("**test *test***")); | |||
| } | |||
| [Test] | |||
| public void combined_9() | |||
| { | |||
| Assert.AreEqual("<em>test <strong>test</strong></em>", | |||
| f.Format("*test **test***")); | |||
| } | |||
| [Test] | |||
| public void combined_10() | |||
| { | |||
| Assert.AreEqual("<em>test <strong>test</strong></em>", | |||
| f.Format("_test __test___")); | |||
| } | |||
| [Test] | |||
| public void combined_11() | |||
| { | |||
| Assert.AreEqual("<strong>test <em>test</em></strong>", | |||
| f.Format("__test _test___")); | |||
| } | |||
| [Test] | |||
| public void combined_12() | |||
| { | |||
| Assert.AreEqual("<strong><em>test</em> test</strong>", | |||
| f.Format("___test_ test__")); | |||
| } | |||
| [Test] | |||
| public void combined_13() | |||
| { | |||
| Assert.AreEqual("<em><strong>test</strong> test</em>", | |||
| f.Format("___test__ test_")); | |||
| } | |||
| [Test] | |||
| public void combined_14() | |||
| { | |||
| Assert.AreEqual("<strong><em>test</em> test</strong>", | |||
| f.Format("___test_ test__")); | |||
| } | |||
| [Test] | |||
| public void combined_15() | |||
| { | |||
| Assert.AreEqual("<strong>test <em>test</em></strong>", | |||
| f.Format("__test _test___")); | |||
| } | |||
| [Test] | |||
| public void combined_16() | |||
| { | |||
| Assert.AreEqual("<em>test <strong>test</strong></em>", | |||
| f.Format("_test __test___")); | |||
| } | |||
| [Test] | |||
| public void combined_17() | |||
| { | |||
| var fExtra = new SpanFormatter(new Markdown() { ExtraMode = true }); | |||
| Assert.AreEqual("<strong>Bold</strong> <em>Italic</em>", | |||
| fExtra.Format("__Bold__ _Italic_")); | |||
| } | |||
| [Test] | |||
| public void combined_18() | |||
| { | |||
| var fExtra = new SpanFormatter(new Markdown() { ExtraMode = true }); | |||
| Assert.AreEqual("<em>Emphasis</em>, trailing", | |||
| fExtra.Format("_Emphasis_, trailing")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class EscapeCharacterTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| f = new SpanFormatter(new Markdown()); | |||
| } | |||
| SpanFormatter f; | |||
| [Test] | |||
| public void AllEscapeCharacters() | |||
| { | |||
| Assert.AreEqual(@"pre \ ` * _ { } [ ] ( ) # + - . ! post", | |||
| f.Format(@"pre \\ \` \* \_ \{ \} \[ \] \( \) \# \+ \- \. \! post")); | |||
| } | |||
| [Test] | |||
| public void SomeNonEscapableCharacters() | |||
| { | |||
| Assert.AreEqual( @"pre \q \% \? post", | |||
| f.Format(@"pre \q \% \? post")); | |||
| } | |||
| [Test] | |||
| public void BackslashWithTwoDashes() | |||
| { | |||
| Assert.AreEqual(@"backslash with \-- two dashes", | |||
| f.Format(@"backslash with \\-- two dashes")); | |||
| } | |||
| [Test] | |||
| public void BackslashWithGT() | |||
| { | |||
| Assert.AreEqual(@"backslash with \> greater", | |||
| f.Format(@"backslash with \\> greater")); | |||
| } | |||
| [Test] | |||
| public void EscapeNotALink() | |||
| { | |||
| Assert.AreEqual(@"\[test](not a link)", | |||
| f.Format(@"\\\[test](not a link)")); | |||
| } | |||
| [Test] | |||
| public void NoEmphasis() | |||
| { | |||
| Assert.AreEqual(@"\*no emphasis*", | |||
| f.Format(@"\\\*no emphasis*")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class ExtraModeTests | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests() | |||
| { | |||
| return Utils.GetTests("extramode"); | |||
| } | |||
| [Test, TestCaseSource("GetTests")] | |||
| public void Test(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class GithubModeTests | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests() | |||
| { | |||
| return Utils.GetTests("githubmode"); | |||
| } | |||
| [Test, TestCaseSource("GetTests")] | |||
| public void Test(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,144 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class HtmlTagTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| m_pos = 0; | |||
| } | |||
| [Test] | |||
| public void Unquoted() | |||
| { | |||
| string str = @"<div x=1 y=2>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, false); | |||
| Assert.AreEqual(tag.attributes.Count, 2); | |||
| Assert.AreEqual(tag.attributes["x"], "1"); | |||
| Assert.AreEqual(tag.attributes["y"], "2"); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void Quoted() | |||
| { | |||
| string str = @"<div x=""1"" y=""2"">"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, false); | |||
| Assert.AreEqual(tag.attributes.Count, 2); | |||
| Assert.AreEqual(tag.attributes["x"], "1"); | |||
| Assert.AreEqual(tag.attributes["y"], "2"); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void Empty() | |||
| { | |||
| string str = @"<div>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, false); | |||
| Assert.AreEqual(tag.attributes.Count, 0); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void Closed() | |||
| { | |||
| string str = @"<div/>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, true); | |||
| Assert.AreEqual(tag.attributes.Count, 0); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void ClosedWithAttribs() | |||
| { | |||
| string str = @"<div x=1 y=2/>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, true); | |||
| Assert.AreEqual(tag.attributes.Count, 2); | |||
| Assert.AreEqual(tag.attributes["x"], "1"); | |||
| Assert.AreEqual(tag.attributes["y"], "2"); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void Closing() | |||
| { | |||
| string str = @"</div>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "div"); | |||
| Assert.AreEqual(tag.closing, true); | |||
| Assert.AreEqual(tag.closed, false); | |||
| Assert.AreEqual(tag.attributes.Count, 0); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void Comment() | |||
| { | |||
| string str = @"<!-- comment -->"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "!"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, true); | |||
| Assert.AreEqual(tag.attributes.Count, 1); | |||
| Assert.AreEqual(tag.attributes["content"], " comment "); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| [Test] | |||
| public void NonValuedAttribute() | |||
| { | |||
| string str = @"<iframe y=""2"" allowfullscreen x=""1"" foo>"; | |||
| HtmlTag tag = HtmlTag.Parse(str, ref m_pos); | |||
| Assert.AreEqual(tag.name, "iframe"); | |||
| Assert.AreEqual(tag.closing, false); | |||
| Assert.AreEqual(tag.closed, false); | |||
| Assert.AreEqual(tag.attributes.Count, 4); | |||
| Assert.AreEqual(tag.attributes["allowfullscreen"], ""); | |||
| Assert.AreEqual(tag.attributes["foo"], ""); | |||
| Assert.AreEqual(tag.attributes["y"], "2"); | |||
| Assert.AreEqual(tag.attributes["x"], "1"); | |||
| Assert.AreEqual(m_pos, str.Length); | |||
| } | |||
| int m_pos; | |||
| } | |||
| } | |||
| @@ -0,0 +1,159 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using MarkdownDeep; | |||
| using NUnit.Framework; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class LinkAndImgTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| m = new Markdown(); | |||
| m.AddLinkDefinition(new LinkDefinition("link1", "url.com", "title")); | |||
| m.AddLinkDefinition(new LinkDefinition("link2", "url.com")); | |||
| m.AddLinkDefinition(new LinkDefinition("img1", "url.com/image.png", "title")); | |||
| m.AddLinkDefinition(new LinkDefinition("img2", "url.com/image.png")); | |||
| s = new SpanFormatter(m); | |||
| } | |||
| [Test] | |||
| public void ReferenceLinkWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\" title=\"title\">link text</a> post", | |||
| s.Format("pre [link text][link1] post")); | |||
| } | |||
| [Test] | |||
| public void ReferenceLinkIdsAreCaseInsensitive() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\" title=\"title\">link text</a> post", | |||
| s.Format("pre [link text][LINK1] post")); | |||
| } | |||
| [Test] | |||
| public void ImplicitReferenceLinkWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\">link2</a> post", | |||
| s.Format("pre [link2] post")); | |||
| Assert.AreEqual("pre <a href=\"url.com\">link2</a> post", | |||
| s.Format("pre [link2][] post")); | |||
| } | |||
| [Test] | |||
| public void ImplicitReferenceLinkWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\" title=\"title\">link1</a> post", | |||
| s.Format("pre [link1] post")); | |||
| Assert.AreEqual("pre <a href=\"url.com\" title=\"title\">link1</a> post", | |||
| s.Format("pre [link1][] post")); | |||
| } | |||
| [Test] | |||
| public void ReferenceLinkWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\">link text</a> post", | |||
| s.Format("pre [link text][link2] post")); | |||
| } | |||
| [Test] | |||
| public void MissingReferenceLink() | |||
| { | |||
| Assert.AreEqual("pre [link text][missing] post", | |||
| s.Format("pre [link text][missing] post")); | |||
| } | |||
| [Test] | |||
| public void InlineLinkWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\" title=\"title\">link text</a> post", | |||
| s.Format("pre [link text](url.com \"title\") post")); | |||
| } | |||
| [Test] | |||
| public void InlineLinkWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\">link text</a> post", | |||
| s.Format("pre [link text](url.com) post")); | |||
| } | |||
| [Test] | |||
| public void Boundaries() | |||
| { | |||
| Assert.AreEqual("<a href=\"url.com\">link text</a>", | |||
| s.Format("[link text](url.com)")); | |||
| Assert.AreEqual("<a href=\"url.com\" title=\"title\">link text</a>", | |||
| s.Format("[link text][link1]")); | |||
| } | |||
| [Test] | |||
| public void ReferenceImgWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"alt text\" title=\"title\" /> post", | |||
| s.Format("pre ![alt text][img1] post")); | |||
| } | |||
| [Test] | |||
| public void ImplicitReferenceImgWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"img2\" /> post", | |||
| s.Format("pre ![img2] post")); | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"img2\" /> post", | |||
| s.Format("pre ![img2][] post")); | |||
| } | |||
| [Test] | |||
| public void ImplicitReferenceImgWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"img1\" title=\"title\" /> post", | |||
| s.Format("pre ![img1] post")); | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"img1\" title=\"title\" /> post", | |||
| s.Format("pre ![img1][] post")); | |||
| } | |||
| [Test] | |||
| public void ReferenceImgWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"alt text\" /> post", | |||
| s.Format("pre ![alt text][img2] post")); | |||
| } | |||
| [Test] | |||
| public void MissingReferenceImg() | |||
| { | |||
| Assert.AreEqual("pre ![alt text][missing] post", | |||
| s.Format("pre ![alt text][missing] post")); | |||
| } | |||
| [Test] | |||
| public void InlineImgWithTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"alt text\" title=\"title\" /> post", | |||
| s.Format("pre  post")); | |||
| } | |||
| [Test] | |||
| public void InlineImgWithoutTitle() | |||
| { | |||
| Assert.AreEqual("pre <img src=\"url.com/image.png\" alt=\"alt text\" /> post", | |||
| s.Format("pre  post")); | |||
| } | |||
| [Test] | |||
| public void ImageLink() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"url.com\"><img src=\"url.com/image.png\" alt=\"alt text\" /></a> post", | |||
| s.Format("pre [](url.com) post")); | |||
| } | |||
| Markdown m; | |||
| SpanFormatter s; | |||
| } | |||
| } | |||
| @@ -0,0 +1,108 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class LinkDefinitionTests | |||
| { | |||
| [SetUp] | |||
| public void Setup() | |||
| { | |||
| r=null; | |||
| } | |||
| [Test] | |||
| public void NoTitle() | |||
| { | |||
| string str = "[id]: url.com"; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "url.com"); | |||
| Assert.AreEqual(r.title, null); | |||
| } | |||
| [Test] | |||
| public void DoubleQuoteTitle() | |||
| { | |||
| string str = "[id]: url.com \"my title\""; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "url.com"); | |||
| Assert.AreEqual(r.title, "my title"); | |||
| } | |||
| [Test] | |||
| public void SingleQuoteTitle() | |||
| { | |||
| string str = "[id]: url.com \'my title\'"; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "url.com"); | |||
| Assert.AreEqual(r.title, "my title"); | |||
| } | |||
| [Test] | |||
| public void ParenthesizedTitle() | |||
| { | |||
| string str = "[id]: url.com (my title)"; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "url.com"); | |||
| Assert.AreEqual(r.title, "my title"); | |||
| } | |||
| [Test] | |||
| public void AngleBracketedUrl() | |||
| { | |||
| string str = "[id]: <url.com> (my title)"; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "url.com"); | |||
| Assert.AreEqual(r.title, "my title"); | |||
| } | |||
| [Test] | |||
| public void MultiLine() | |||
| { | |||
| string str = "[id]:\n\t http://www.site.com \n\t (my title)"; | |||
| r = LinkDefinition.ParseLinkDefinition(str, false); | |||
| Assert.IsNotNull(r); | |||
| Assert.AreEqual(r.id, "id"); | |||
| Assert.AreEqual(r.url, "http://www.site.com"); | |||
| Assert.AreEqual(r.title, "my title"); | |||
| } | |||
| [Test] | |||
| public void Invalid() | |||
| { | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]:", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url> \"title", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url> \'title", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url> (title", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url> \"title\" crap", false)); | |||
| Assert.IsNull(LinkDefinition.ParseLinkDefinition("[id]: <url> crap", false)); | |||
| } | |||
| LinkDefinition r; | |||
| } | |||
| } | |||
| @@ -0,0 +1,473 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup> | |||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
| <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
| <ProductVersion>9.0.30729</ProductVersion> | |||
| <SchemaVersion>2.0</SchemaVersion> | |||
| <ProjectGuid>{CD1F5BFF-0118-4994-86A2-92658A36CE1B}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>MarkdownDeepTests</RootNamespace> | |||
| <AssemblyName>MarkdownDeepTests</AssemblyName> | |||
| <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
| <FileAlignment>512</FileAlignment> | |||
| <FileUpgradeFlags> | |||
| </FileUpgradeFlags> | |||
| <UpgradeBackupLocation> | |||
| </UpgradeBackupLocation> | |||
| <OldToolsVersion>3.5</OldToolsVersion> | |||
| <TargetFrameworkProfile /> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <DebugType>full</DebugType> | |||
| <Optimize>false</Optimize> | |||
| <OutputPath>bin\Debug\</OutputPath> | |||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> | |||
| <Prefer32Bit>false</Prefer32Bit> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
| <DebugType>pdbonly</DebugType> | |||
| <Optimize>true</Optimize> | |||
| <OutputPath>bin\Release\</OutputPath> | |||
| <DefineConstants>TRACE</DefineConstants> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <WarningLevel>4</WarningLevel> | |||
| <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> | |||
| <Prefer32Bit>false</Prefer32Bit> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="nunit.framework"> | |||
| <HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="nunit.mocks"> | |||
| <HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="pnunit.framework"> | |||
| <HintPath>..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll</HintPath> | |||
| </Reference> | |||
| <Reference Include="System" /> | |||
| <Reference Include="System.Core"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Web" /> | |||
| <Reference Include="System.Windows.Forms" /> | |||
| <Reference Include="System.Xml.Linq"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data.DataSetExtensions"> | |||
| <RequiredTargetFramework>3.5</RequiredTargetFramework> | |||
| </Reference> | |||
| <Reference Include="System.Data" /> | |||
| <Reference Include="System.Xml" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="BlockProcessorTests.cs" /> | |||
| <Compile Include="CodeSpanTests.cs" /> | |||
| <Compile Include="AutoLinkTests.cs" /> | |||
| <Compile Include="AutoHeaderIDTests.cs" /> | |||
| <Compile Include="GithubMode.cs" /> | |||
| <Compile Include="TableSpecTests.cs" /> | |||
| <Compile Include="ExtraMode.cs" /> | |||
| <Compile Include="SafeModeTests.cs" /> | |||
| <Compile Include="MoreTestFiles.cs" /> | |||
| <Compile Include="LinkAndImgTests.cs" /> | |||
| <Compile Include="SpanLevelTests.cs" /> | |||
| <Compile Include="EscapeCharacterTests.cs" /> | |||
| <Compile Include="LinkDefinitionTests.cs" /> | |||
| <Compile Include="SpecialCharacterTests.cs" /> | |||
| <Compile Include="HtmlTagTests.cs" /> | |||
| <Compile Include="BlockLevelTests.cs" /> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| <Compile Include="EmphasisTests.cs" /> | |||
| <Compile Include="StringScannerTests.cs" /> | |||
| <Compile Include="Utils.cs" /> | |||
| <Compile Include="XssAttackTests.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleParagraph.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleParagraph.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraph.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraph.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\MultipleParagraphs.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\MultipleParagraphs.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleOrderedList.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleOrderedList.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleUnorderedList.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SimpleUnorderedList.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\CodeBlocks.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\CodeBlocks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\QuoteBlocks.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\QuoteBlocks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\QuoteBlocksNested.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\QuoteBlocksNested.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlBlock.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlBlock.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\ParagraphBreaks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\ParagraphBreaks.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\AtxHeadings.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\AtxHeadings.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SetExtHeadings.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\SetExtHeadings.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedListItems.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedListItems.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraphWithListLikeLine.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraphWithListLikeLine.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\ComplexListItems.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\ComplexListItems.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraphInListItem.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HardWrappedParagraphInListItem.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ImplicitReferenceLinkWithTitle.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ImplicitReferenceLinkWithTitle.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ImplicitReferenceLinkWithoutTitle.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ImplicitReferenceLinkWithoutTitle.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ExplicitReferenceLinkWithTitle.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ExplicitReferenceLinkWithTitle.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ExplicitReferenceLinkWithoutTitle.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ExplicitReferenceLinkWithoutTitle.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\HtmlEncodeLinks.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\HtmlEncodeLinks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\InlineLinkWithTitle.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\InlineLinkWithTitle.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\xsstests\xss_attacks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\xsstests\non_attacks.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Amps_and_angle_encoding.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Auto_links.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Backslash_escapes.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Blockquotes_with_code_blocks.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Code_Blocks.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Code_Spans.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Hard_wrapped_paragraphs_with_list_like_lines.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Horizontal_rules.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Images.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_Advanced.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_comments.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_Simple.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_inline_style.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_reference_style.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_shortcut_references.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Literal_quotes_in_titles.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Markdown_Documentation_Basics.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Markdown_Documentation_Syntax.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Nested_blockquotes.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Ordered_and_unordered_lists.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Strong_and_em_together.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Tabs.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Tidyness.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\code-inside-list.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-cr.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-crlf.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-lf.html" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\markdown-readme.html" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\failure-to-escape-less-than.html" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\nested-divs.html" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\nested-emphasis.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Backslash escapes.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Code block in a list item.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Code Spans.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Email auto links.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Emphasis.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Headers.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Horizontal Rules.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML %28Simple%29.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML %28Span%29.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML comments.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Ins & del.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Links, inline style.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\MD5 Hashes.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Nesting.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Parens in URL.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\PHP-Specific Bugs.html" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Tight blocks.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Amps_and_angle_encoding.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Auto_links.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Backslash_escapes.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Blockquotes_with_code_blocks.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Code_Blocks.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Code_Spans.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Hard_wrapped_paragraphs_with_list_like_lines.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Horizontal_rules.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Images.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_Advanced.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_comments.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Inline_HTML_Simple.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_inline_style.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_reference_style.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Links_shortcut_references.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Literal_quotes_in_titles.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Markdown_Documentation_Basics.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Markdown_Documentation_Syntax.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Nested_blockquotes.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Ordered_and_unordered_lists.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Strong_and_em_together.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Tabs.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest11\Tidyness.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\code-inside-list.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-cr.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-crlf.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\line-endings-lf.text" /> | |||
| <EmbeddedResource Include="testfiles\mdtest01\markdown-readme.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\failure-to-escape-less-than.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\indented-code-in-list-item.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\nested-divs.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\nested-emphasis.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\unordered-list-and-horizontal-rules.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\unordered-list-followed-by-ordered-list.text" /> | |||
| <EmbeddedResource Include="testfiles\pandoc\unpredictable-sublists.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Backslash escapes.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Code block in a list item.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Code Spans.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Email auto links.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Emphasis.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Headers.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Horizontal Rules.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML %28Simple%29.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML %28Span%29.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Inline HTML comments.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Ins & del.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Links, inline style.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\MD5 Hashes.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Nesting.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Parens in URL.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\PHP-Specific Bugs.text" /> | |||
| <EmbeddedResource Include="testfiles\phpmarkdown\Tight blocks.text" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\InsTypes.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\InsTypes.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\LinkTitlesWithEmbeddedQuotes.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\LinkTitlesWithEmbeddedQuotes.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlComments.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlComments.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\EscapesInUrls.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\EscapesInUrls.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\NestedListItems.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\blocktests\NestedListItems.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ReferenceLinkWithIDOnNextLine.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\ReferenceLinkWithIDOnNextLine.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\FormattingInLinkText.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\FormattingInLinkText.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\pandoc\indented-code-in-list-item.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\pandoc\unordered-list-and-horizontal-rules.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\pandoc\unordered-list-followed-by-ordered-list.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\pandoc\unpredictable-sublists.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\Emphasis.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\spantests\Emphasis.txt" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="app.config"> | |||
| <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
| </None> | |||
| <None Include="packages.config" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <EmbeddedResource Include="testfiles\githubmode\FencedCodeBlocksAlt%28GitHubMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\githubmode\FencedCodeBlocksAlt%28GitHubMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue26%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue26%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue30%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue30%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\FencedCodeBlocksAlt%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\FencedCodeBlocksAlt%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue12%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Issue12%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\blocktests\PartiallyIndentedLists.html" /> | |||
| <EmbeddedResource Include="testfiles\blocktests\PartiallyIndentedLists.txt" /> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlAttributeWithoutValue.txt" /> | |||
| <EmbeddedResource Include="testfiles\blocktests\HtmlAttributeWithoutValue.html" /> | |||
| <EmbeddedResource Include="testfiles\spantests\LinkedImage.txt" /> | |||
| <EmbeddedResource Include="testfiles\spantests\LinkedImage.html" /> | |||
| <EmbeddedResource Include="testfiles\spantests\BackslashEscapes.txt" /> | |||
| <EmbeddedResource Include="testfiles\spantests\BackslashEscapes.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\BackslashEscapes%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\BackslashEscapes%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Emphasis%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Emphasis%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Abbreviations%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Abbreviations%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Footnotes%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Footnotes%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\DefinitionLists%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\DefinitionLists%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\TableFormatting%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\TableFormatting%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\TableAlignment%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\TableAlignment%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Tables%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\Tables%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\FencedCodeBlocks%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\FencedCodeBlocks%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\HeaderIDs%28ExtraMode%29%28AutoHeadingIDs%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\HeaderIDs%28ExtraMode%29%28AutoHeadingIDs%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\HeaderIDs%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\HeaderIDs%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-DeepNested%28ExtraMode%29%28MarkdownInHtml%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-DeepNested%28ExtraMode%29%28MarkdownInHtml%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-DeepNested%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-DeepNested%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-Nested%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml-Nested%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml%28ExtraMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\extramode\MarkdownInHtml%28ExtraMode%29.html" /> | |||
| <EmbeddedResource Include="testfiles\safemode\Basic%28SafeMode%29.txt" /> | |||
| <EmbeddedResource Include="testfiles\safemode\Basic%28SafeMode%29.html" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\MarkdownDeep\MarkdownDeep.csproj"> | |||
| <Project>{1569ed47-c7c9-4261-b6f4-7445bd0f2c95}</Project> | |||
| <Name>MarkdownDeep</Name> | |||
| </ProjectReference> | |||
| </ItemGroup> | |||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
| Other similar extension points exist, see Microsoft.Common.targets. | |||
| <Target Name="BeforeBuild"> | |||
| </Target> | |||
| <Target Name="AfterBuild"> | |||
| </Target> | |||
| --> | |||
| <PropertyGroup> | |||
| <PostBuildEvent> | |||
| </PostBuildEvent> | |||
| </PropertyGroup> | |||
| <PropertyGroup> | |||
| <PreBuildEvent> | |||
| </PreBuildEvent> | |||
| </PropertyGroup> | |||
| </Project> | |||
| @@ -0,0 +1,68 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class MoreTestFiles | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests_mdtest11() | |||
| { | |||
| return Utils.GetTests("mdtest11"); | |||
| } | |||
| [Test, TestCaseSource("GetTests_mdtest11")] | |||
| public void Test_mdtest11(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| public static IEnumerable<TestCaseData> GetTests_mdtest01() | |||
| { | |||
| return Utils.GetTests("mdtest01"); | |||
| } | |||
| [Test, TestCaseSource("GetTests_mdtest01")] | |||
| public void Test_mdtest01(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| /* | |||
| * Don't run the pandoc test's as they're basically a demonstration of things | |||
| * that are broken in markdown. | |||
| * | |||
| public static IEnumerable<TestCaseData> GetTests_pandoc() | |||
| { | |||
| return Utils.GetTests("pandoc"); | |||
| } | |||
| [Test, TestCaseSource("GetTests_pandoc")] | |||
| public void Test_pandoc(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| */ | |||
| public static IEnumerable<TestCaseData> GetTests_phpmarkdown() | |||
| { | |||
| return Utils.GetTests("phpmarkdown"); | |||
| } | |||
| [Test, TestCaseSource("GetTests_phpmarkdown")] | |||
| public void Test_phpmarkdown(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| using System.Reflection; | |||
| using System.Runtime.CompilerServices; | |||
| using System.Runtime.InteropServices; | |||
| using NUnit.Framework; | |||
| // General Information about an assembly is controlled through the following | |||
| // set of attributes. Change these attribute values to modify the information | |||
| // associated with an assembly. | |||
| [assembly: AssemblyTitle("MarkdownDeepTests")] | |||
| [assembly: AssemblyDescription("")] | |||
| [assembly: AssemblyConfiguration("")] | |||
| [assembly: AssemblyCompany("Topten Software")] | |||
| [assembly: AssemblyProduct("MarkdownDeepTests")] | |||
| [assembly: AssemblyCopyright("Copyright © 2010-2011 Topten Software")] | |||
| [assembly: AssemblyTrademark("")] | |||
| [assembly: AssemblyCulture("")] | |||
| // Setting ComVisible to false makes the types in this assembly not visible | |||
| // to COM components. If you need to access a type in this assembly from | |||
| // COM, set the ComVisible attribute to true on that type. | |||
| [assembly: ComVisible(false)] | |||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||
| [assembly: Guid("80e81372-b393-44fa-af74-fe39ceae854f")] | |||
| // Version information for an assembly consists of the following four values: | |||
| // | |||
| // Major Version | |||
| // Minor Version | |||
| // Build Number | |||
| // Revision | |||
| // | |||
| // You can specify all the values or you can default the Build and Revision Numbers | |||
| // by using the '*' as shown below: | |||
| // [assembly: AssemblyVersion("1.0.*")] | |||
| [assembly: AssemblyVersion("1.0.0.0")] | |||
| [assembly: AssemblyFileVersion("1.0.0.0")] | |||
| [assembly: RequiresSTA] | |||
| @@ -0,0 +1,26 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class SafeModeTests | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests() | |||
| { | |||
| return Utils.GetTests("safemode"); | |||
| } | |||
| [Test, TestCaseSource("GetTests")] | |||
| public void Test(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class SpanLevelTests | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests() | |||
| { | |||
| return Utils.GetTests("spantests"); | |||
| } | |||
| [Test, TestCaseSource("GetTests")] | |||
| public void Test(string resourceName) | |||
| { | |||
| Utils.RunResourceTest(resourceName); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class SpecialCharacterTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| f = new SpanFormatter(new Markdown()); | |||
| } | |||
| SpanFormatter f; | |||
| [Test] | |||
| public void SimpleTag() | |||
| { | |||
| Assert.AreEqual(f.Format("pre <a> post"), "pre <a> post"); | |||
| } | |||
| [Test] | |||
| public void TagWithAttributes() | |||
| { | |||
| Assert.AreEqual(f.Format("pre <a href=\"somewhere.html\" target=\"_blank\">link</a> post"), "pre <a href=\"somewhere.html\" target=\"_blank\">link</a> post"); | |||
| } | |||
| [Test] | |||
| public void NotATag() | |||
| { | |||
| Assert.AreEqual("pre a < b post", | |||
| f.Format("pre a < b post")); | |||
| } | |||
| [Test] | |||
| public void NotATag2() | |||
| { | |||
| Assert.AreEqual("pre a<b post", | |||
| f.Format("pre a<b post")); | |||
| } | |||
| [Test] | |||
| public void AmpersandsInUrls() | |||
| { | |||
| Assert.AreEqual("pre <a href=\"somewhere.html?arg1=a&arg2=b\" target=\"_blank\">link</a> post", | |||
| f.Format("pre <a href=\"somewhere.html?arg1=a&arg2=b\" target=\"_blank\">link</a> post")); | |||
| } | |||
| [Test] | |||
| public void AmpersandsInParagraphs() | |||
| { | |||
| Assert.AreEqual("pre this & that post", | |||
| f.Format("pre this & that post")); | |||
| } | |||
| [Test] | |||
| public void HtmlEntities() | |||
| { | |||
| Assert.AreEqual("pre & post", | |||
| f.Format("pre & post")); | |||
| Assert.AreEqual("pre { post", | |||
| f.Format("pre { post")); | |||
| Assert.AreEqual("pre Ư post", | |||
| f.Format("pre Ư post")); | |||
| } | |||
| [Test] | |||
| public void EscapeChars() | |||
| { | |||
| Assert.AreEqual(@"\ ` * _ { } [ ] ( ) # + - . ! >", | |||
| f.Format(@"\\ \` \* \_ \{ \} \[ \] \( \) \# \+ \- \. \! \>")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class StringScannerTests | |||
| { | |||
| [Test] | |||
| public void Tests() | |||
| { | |||
| var p = new StringScanner(); | |||
| p.Reset("This is a string with something [bracketed]"); | |||
| Assert.IsTrue(p.bof); | |||
| Assert.IsFalse(p.eof); | |||
| Assert.IsTrue(p.SkipString("This")); | |||
| Assert.IsFalse(p.bof); | |||
| Assert.IsFalse(p.eof); | |||
| Assert.IsFalse(p.SkipString("huh?")); | |||
| Assert.IsTrue(p.SkipLinespace()); | |||
| Assert.IsTrue(p.SkipChar('i')); | |||
| Assert.IsTrue(p.SkipChar('s')); | |||
| Assert.IsTrue(p.SkipWhitespace()); | |||
| Assert.IsTrue(p.DoesMatchAny(new char[] { 'r', 'a', 't'} )); | |||
| Assert.IsFalse(p.Find("Not here")); | |||
| Assert.IsFalse(p.Find("WITH")); | |||
| Assert.IsFalse(p.FindI("Not here")); | |||
| Assert.IsTrue(p.FindI("WITH")); | |||
| Assert.IsTrue(p.Find('[')); | |||
| p.SkipForward(1); | |||
| p.Mark(); | |||
| Assert.IsTrue(p.Find(']')); | |||
| Assert.AreEqual("bracketed", p.Extract()); | |||
| Assert.IsTrue(p.SkipChar(']')); | |||
| Assert.IsTrue(p.eof); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using NUnit.Framework; | |||
| using MarkdownDeep; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| class TableSpecTests | |||
| { | |||
| [SetUp] | |||
| public void SetUp() | |||
| { | |||
| } | |||
| TableSpec Parse(string str) | |||
| { | |||
| var s = new StringScanner(str); | |||
| return TableSpec.Parse(s); | |||
| } | |||
| [Test] | |||
| public void Simple() | |||
| { | |||
| var s = Parse("--|--"); | |||
| Assert.IsNotNull(s); | |||
| Assert.IsFalse(s.LeadingBar); | |||
| Assert.IsFalse(s.TrailingBar); | |||
| Assert.AreEqual(2, s.Columns.Count); | |||
| Assert.AreEqual(ColumnAlignment.NA, s.Columns[0]); | |||
| Assert.AreEqual(ColumnAlignment.NA, s.Columns[1]); | |||
| } | |||
| [Test] | |||
| public void Alignment() | |||
| { | |||
| var s = Parse("--|:--|--:|:--:"); | |||
| Assert.IsNotNull(s); | |||
| Assert.IsFalse(s.LeadingBar); | |||
| Assert.IsFalse(s.TrailingBar); | |||
| Assert.AreEqual(4, s.Columns.Count); | |||
| Assert.AreEqual(ColumnAlignment.NA, s.Columns[0]); | |||
| Assert.AreEqual(ColumnAlignment.Left, s.Columns[1]); | |||
| Assert.AreEqual(ColumnAlignment.Right, s.Columns[2]); | |||
| Assert.AreEqual(ColumnAlignment.Center, s.Columns[3]); | |||
| } | |||
| [Test] | |||
| public void LeadingTrailingBars() | |||
| { | |||
| var s = Parse("|--|:--|--:|:--:|"); | |||
| Assert.IsNotNull(s); | |||
| Assert.IsTrue(s.LeadingBar); | |||
| Assert.IsTrue(s.TrailingBar); | |||
| Assert.AreEqual(4, s.Columns.Count); | |||
| Assert.AreEqual(ColumnAlignment.NA, s.Columns[0]); | |||
| Assert.AreEqual(ColumnAlignment.Left, s.Columns[1]); | |||
| Assert.AreEqual(ColumnAlignment.Right, s.Columns[2]); | |||
| Assert.AreEqual(ColumnAlignment.Center, s.Columns[3]); | |||
| } | |||
| [Test] | |||
| public void Whitespace() | |||
| { | |||
| var s = Parse(" | -- | :-- | --: | :--: | "); | |||
| Assert.IsNotNull(s); | |||
| Assert.IsTrue(s.LeadingBar); | |||
| Assert.IsTrue(s.TrailingBar); | |||
| Assert.AreEqual(4, s.Columns.Count); | |||
| Assert.AreEqual(ColumnAlignment.NA, s.Columns[0]); | |||
| Assert.AreEqual(ColumnAlignment.Left, s.Columns[1]); | |||
| Assert.AreEqual(ColumnAlignment.Right, s.Columns[2]); | |||
| Assert.AreEqual(ColumnAlignment.Center, s.Columns[3]); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,145 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Reflection; | |||
| using NUnit.Framework; | |||
| using System.Windows.Forms; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| public static class Utils | |||
| { | |||
| public static IEnumerable<TestCaseData> GetTests(string foldername) | |||
| { | |||
| var names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); | |||
| return from name in Assembly.GetExecutingAssembly().GetManifestResourceNames() | |||
| where name.StartsWith("MarkdownDeepTests.testfiles." + foldername + ".") && (name.EndsWith(".txt") || name.EndsWith(".text")) | |||
| select new TestCaseData(name); | |||
| } | |||
| public static string LoadTextResource(string name) | |||
| { | |||
| // get a reference to the current assembly | |||
| var a = System.Reflection.Assembly.GetExecutingAssembly(); | |||
| System.IO.StreamReader r = new System.IO.StreamReader(a.GetManifestResourceStream(name)); | |||
| string str = r.ReadToEnd(); | |||
| r.Close(); | |||
| return str; | |||
| } | |||
| public static string strip_redundant_whitespace(string str) | |||
| { | |||
| StringBuilder sb = new StringBuilder(); | |||
| str = str.Replace("\r\n", "\n"); | |||
| int i = 0; | |||
| while (i < str.Length) | |||
| { | |||
| char ch = str[i]; | |||
| switch (ch) | |||
| { | |||
| case ' ': | |||
| case '\t': | |||
| case '\r': | |||
| case '\n': | |||
| // Store start of white space | |||
| i++; | |||
| // Find end of whitespace | |||
| while (i < str.Length) | |||
| { | |||
| ch = str[i]; | |||
| if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') | |||
| break; | |||
| i++; | |||
| } | |||
| // Replace with a single space | |||
| if (i < str.Length && str[i] != '<') | |||
| sb.Append(' '); | |||
| break; | |||
| case '>': | |||
| sb.Append("> "); | |||
| i++; | |||
| while (i < str.Length) | |||
| { | |||
| ch = str[i]; | |||
| if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') | |||
| break; | |||
| i++; | |||
| } | |||
| break; | |||
| case '<': | |||
| if (i+5<str.Length && str.Substring(i, 5) == "<pre>") | |||
| { | |||
| sb.Append(" "); | |||
| // Special handling for pre blocks | |||
| // Find end | |||
| int end = str.IndexOf("</pre>", i); | |||
| if (end < 0) | |||
| end=str.Length; | |||
| // Append the pre block | |||
| sb.Append(str, i, end - i); | |||
| sb.Append(" "); | |||
| // Jump to end | |||
| i = end; | |||
| } | |||
| else | |||
| { | |||
| sb.Append(" <"); | |||
| i++; | |||
| } | |||
| break; | |||
| default: | |||
| sb.Append(ch); | |||
| i++; | |||
| break; | |||
| } | |||
| } | |||
| return sb.ToString().Trim(); | |||
| } | |||
| public static void RunResourceTest(string resourceName) | |||
| { | |||
| string input = Utils.LoadTextResource(resourceName); | |||
| string expected = Utils.LoadTextResource(System.IO.Path.ChangeExtension(resourceName, "html")); | |||
| var md = new MarkdownDeep.Markdown(); | |||
| md.SafeMode = resourceName.IndexOf("(SafeMode)") >= 0; | |||
| md.ExtraMode = resourceName.IndexOf("(ExtraMode)") >= 0; | |||
| md.GitHubCodeBlocks = resourceName.IndexOf("(GitHubMode)") >= 0; | |||
| md.MarkdownInHtml = resourceName.IndexOf("(MarkdownInHtml)") >= 0; | |||
| md.AutoHeadingIDs = resourceName.IndexOf("(AutoHeadingIDs)") >= 0; | |||
| string actual = md.Transform(input); | |||
| string actual_clean = Utils.strip_redundant_whitespace(actual); | |||
| string expected_clean = Utils.strip_redundant_whitespace(expected); | |||
| string sep = new string('-', 30) + "\n"; | |||
| Console.WriteLine("Input:\n" + sep + input); | |||
| Console.WriteLine("Actual:\n" + sep + actual); | |||
| Console.WriteLine("Expected:\n" + sep + expected); | |||
| Assert.AreEqual(expected_clean, actual_clean); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,136 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using MarkdownDeep; | |||
| using NUnit.Framework; | |||
| using System.Reflection; | |||
| namespace MarkdownDeepTests | |||
| { | |||
| [TestFixture] | |||
| public class XssAttackTests | |||
| { | |||
| public bool IsTagReallySafe(HtmlTag tag) | |||
| { | |||
| switch (tag.name) | |||
| { | |||
| case "B": | |||
| case "UL": | |||
| case "LI": | |||
| case "I": | |||
| return tag.attributes.Count == 0; | |||
| case "A": | |||
| case "a": | |||
| return tag.closing && tag.attributes.Count == 0; | |||
| } | |||
| return false; | |||
| } | |||
| public static IEnumerable<TestCaseData> GetTestsFromFile(string filename) | |||
| { | |||
| var tests = Utils.LoadTextResource("MarkdownDeepTests.testfiles.xsstests." + filename); | |||
| // Split into lines | |||
| string[] lines = tests.Replace("\r\n", "\n").Split('\n'); | |||
| // Join bac | |||
| var strings = new List<string>(); | |||
| string str = null; | |||
| foreach (var l in lines) | |||
| { | |||
| // Ignore | |||
| if (l.StartsWith("////")) | |||
| continue; | |||
| // Terminator? | |||
| if (l == "====== UNTESTED ======") | |||
| { | |||
| str = null; | |||
| break; | |||
| } | |||
| // Blank line? | |||
| if (String.IsNullOrEmpty(l.Trim())) | |||
| { | |||
| if (str != null) | |||
| strings.Add(str); | |||
| str = null; | |||
| continue; | |||
| } | |||
| if (str == null) | |||
| str = l; | |||
| else | |||
| str = str + "\n" + l; | |||
| } | |||
| if (str != null) | |||
| strings.Add(str); | |||
| return from s in strings select new TestCaseData(s); | |||
| } | |||
| public static IEnumerable<TestCaseData> GetAttacks() | |||
| { | |||
| return GetTestsFromFile("xss_attacks.txt"); | |||
| } | |||
| [Test, TestCaseSource("GetAttacks")] | |||
| public void TestAttacksAreBlocked(string input) | |||
| { | |||
| StringScanner p = new StringScanner(input); | |||
| while (!p.eof) | |||
| { | |||
| HtmlTag tag=HtmlTag.Parse(p); | |||
| if (tag!=null) | |||
| { | |||
| if (tag.IsSafe()) | |||
| { | |||
| // There's a few tags that really are safe in the test data | |||
| Assert.IsTrue(IsTagReallySafe(tag)); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // Next character | |||
| p.SkipForward(1); | |||
| } | |||
| } | |||
| } | |||
| public static IEnumerable<TestCaseData> GetAllowed() | |||
| { | |||
| return GetTestsFromFile("non_attacks.txt"); | |||
| } | |||
| [Test, TestCaseSource("GetAllowed")] | |||
| public void TestNonAttacksAreAllowed(string input) | |||
| { | |||
| StringScanner p = new StringScanner(input); | |||
| while (!p.eof) | |||
| { | |||
| HtmlTag tag = HtmlTag.Parse(p); | |||
| if (tag != null) | |||
| { | |||
| Assert.IsTrue(tag.IsSafe()); | |||
| } | |||
| else | |||
| { | |||
| // Next character | |||
| p.SkipForward(1); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <configuration> | |||
| <configSections> | |||
| <sectionGroup name="NUnit"> | |||
| <section name="TestRunner" type="System.Configuration.NameValueSectionHandler" /> | |||
| </sectionGroup> | |||
| </configSections> | |||
| <NUnit> | |||
| <TestRunner> | |||
| <add key="ApartmentState" value="STA" /> | |||
| </TestRunner> | |||
| </NUnit> | |||
| </configuration> | |||
| @@ -0,0 +1,4 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <packages> | |||
| <package id="NUnit" version="2.5.10.11092" /> | |||
| </packages> | |||
| @@ -0,0 +1,38 @@ | |||
| <h1>heading 1</h1> | |||
| <p>para</p> | |||
| <h1>heading 1</h1> | |||
| <p>para</p> | |||
| <h2>heading 2</h2> | |||
| <p>para</p> | |||
| <h2>heading 2</h2> | |||
| <p>para</p> | |||
| <h6>heading 6</h6> | |||
| <p>para</p> | |||
| <h6>heading 6</h6> | |||
| <p>para</p> | |||
| <h6>heading 6</h6> | |||
| <p>para</p> | |||
| <h6>heading 6</h6> | |||
| <p>para</p> | |||
| <h2>heading 2</h2> | |||
| <hr /> | |||
| <p>para</p> | |||
| @@ -0,0 +1,39 @@ | |||
| # heading 1 | |||
| para | |||
| # heading 1 # | |||
| para | |||
| ## heading 2 | |||
| para | |||
| ## heading 2 ## | |||
| para | |||
| ###### heading 6 | |||
| para | |||
| ###### heading 6 ###### | |||
| para | |||
| ############ heading 6 | |||
| para | |||
| ############ heading 6 #################################### | |||
| para | |||
| ##heading 2## | |||
| ----- | |||
| para | |||
| @@ -0,0 +1,12 @@ | |||
| <p>before</p> | |||
| <pre><code>for (int i=0; i<10; i++) | |||
| { | |||
| print("%i\n", i); | |||
| } | |||
| </code></pre> | |||
| <p>sep</p> | |||
| <pre><code><div> | |||
| <p>Copyright &copy; 2010 | |||
| </div> | |||
| </code></pre> | |||
| <p>after</p> | |||
| @@ -0,0 +1,14 @@ | |||
| before | |||
| for (int i=0; i<10; i++) | |||
| { | |||
| print("%i\n", i); | |||
| } | |||
| sep | |||
| <div> | |||
| <p>Copyright © 2010 | |||
| </div> | |||
| after | |||
| @@ -0,0 +1,27 @@ | |||
| <p>before</p> | |||
| <ul> | |||
| <li> | |||
| <p>This is a list item</p> | |||
| <p>it contains some code</p> | |||
| <pre><code>for (int i=0; i<10; i++) | |||
| { | |||
| printf("Hello World\n"); | |||
| } | |||
| </code></pre> | |||
| <p>and a block quote:</p> | |||
| <blockquote> | |||
| <p>"I have a dream"</p> | |||
| </blockquote> | |||
| <p>and an embedded list</p> | |||
| <ul> | |||
| <li>Apples</li> | |||
| <li>Pears</li> | |||
| <li>Bananas</li> | |||
| </ul> | |||
| <p>and a hardwrapped | |||
| and left aligned paragraph</p> | |||
| </li> | |||
| </ul> | |||
| <p>after</p> | |||
| @@ -0,0 +1,25 @@ | |||
| before | |||
| * This is a list item | |||
| it contains some code | |||
| for (int i=0; i<10; i++) | |||
| { | |||
| printf("Hello World\n"); | |||
| } | |||
| and a block quote: | |||
| > "I have a dream" | |||
| and an embedded list | |||
| * Apples | |||
| * Pears | |||
| * Bananas | |||
| and a hardwrapped | |||
| and left aligned paragraph | |||
| after | |||
| @@ -0,0 +1,19 @@ | |||
| <ol> | |||
| <li>Apples | |||
| wrapped</li> | |||
| <li>Pears | |||
| wrapped</li> | |||
| <li>Bananas | |||
| wrapped</li> | |||
| </ol> | |||
| <p>sep</p> | |||
| <ol> | |||
| <li>Apples | |||
| wrapped</li> | |||
| <li>Pears | |||
| wrapped</li> | |||
| <li>Bananas | |||
| wrapped</li> | |||
| </ol> | |||
| @@ -0,0 +1,15 @@ | |||
| 1. Apples | |||
| wrapped | |||
| 2. Pears | |||
| wrapped | |||
| 3. Bananas | |||
| wrapped | |||
| sep | |||
| 1. Apples | |||
| wrapped | |||
| 2. Pears | |||
| wrapped | |||
| 3. Bananas | |||
| wrapped | |||
| @@ -0,0 +1,4 @@ | |||
| <p>p1 | |||
| p2 | |||
| p3 | |||
| </p> | |||
| @@ -0,0 +1,3 @@ | |||
| p1 | |||
| p2 | |||
| p3 | |||
| @@ -0,0 +1,10 @@ | |||
| <p>before</p> | |||
| <ul> | |||
| <li> | |||
| <p>This is a list item</p> | |||
| <p>with a hardwrapped | |||
| and left aligned paragraph</p> | |||
| </li> | |||
| </ul> | |||
| <p>after</p> | |||
| @@ -0,0 +1,8 @@ | |||
| before | |||
| * This is a list item | |||
| with a hardwrapped | |||
| and left aligned paragraph | |||
| after | |||
| @@ -0,0 +1,3 @@ | |||
| <p>This is a paragraph that was written in | |||
| 2010. The year it was written shouldn't | |||
| be treated like a list item</p> | |||
| @@ -0,0 +1,3 @@ | |||
| This is a paragraph that was written in | |||
| 2010. The year it was written shouldn't | |||
| be treated like a list item | |||
| @@ -0,0 +1 @@ | |||
| <iframe y="2" allowfullscreen x="1" foo /> | |||
| @@ -0,0 +1 @@ | |||
| <iframe y="2" allowfullscreen x="1" foo /> | |||
| @@ -0,0 +1,18 @@ | |||
| <p>before</p> | |||
| <div class="MyClass"> | |||
| <p>whatever</p> | |||
| </div> | |||
| <p>sep</p> | |||
| <div class="MyClass"> | |||
| <div> | |||
| <div> | |||
| nested | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <p>after</p> | |||
| @@ -0,0 +1,18 @@ | |||
| before | |||
| <div class="MyClass"> | |||
| <p>whatever</p> | |||
| </div> | |||
| sep | |||
| <div class="MyClass"> | |||
| <div> | |||
| <div> | |||
| nested | |||
| </div> | |||
| </div> | |||
| </div> | |||
| after | |||
| @@ -0,0 +1,10 @@ | |||
| <p>pre</p> | |||
| <!-- | |||
| Multiline | |||
| HTML | |||
| Comments | |||
| --> | |||
| <p>post</p> | |||
| @@ -0,0 +1,9 @@ | |||
| pre | |||
| <!-- | |||
| Multiline | |||
| HTML | |||
| Comments | |||
| --> | |||
| post | |||
| @@ -0,0 +1,8 @@ | |||
| <ins> | |||
| <p>Some text</p> | |||
| </ins> | |||
| <p><ins>And here it is inside a paragraph.</ins></p> | |||
| <p>And here it is <ins>in the middle of</ins> a paragraph.</p> | |||
| @@ -0,0 +1,7 @@ | |||
| <ins> | |||
| <p>Some text</p> | |||
| </ins> | |||
| <ins>And here it is inside a paragraph.</ins> | |||
| And here it is <ins>in the middle of</ins> a paragraph. | |||
| @@ -0,0 +1,2 @@ | |||
| <p>p1</p> | |||
| <p>p2</p> | |||
| @@ -0,0 +1,3 @@ | |||
| p1 | |||
| p2 | |||
| @@ -0,0 +1,6 @@ | |||
| <ul> | |||
| <li>Tab | |||
| <ul><li>Tab | |||
| <ul><li>Tab</li></ul></li></ul></li> | |||
| </ul> | |||
| @@ -0,0 +1,4 @@ | |||
| * Tab | |||
| * Tab | |||
| * Tab | |||
| @@ -0,0 +1,10 @@ | |||
| <p>this is a paragraph<br /> | |||
| with a contained break</p> | |||
| <h1>this is a heading</h1> | |||
| <p>followed by a paragraph</p> | |||
| <ul> | |||
| <li>an item<br /> | |||
| with a contained break</li> | |||
| </ul> | |||
| @@ -0,0 +1,8 @@ | |||
| this is a paragraph | |||
| with a contained break | |||
| # this is a heading | |||
| followed by a paragraph | |||
| * an item | |||
| with a contained break | |||
| @@ -0,0 +1,53 @@ | |||
| <p>pre</p> | |||
| <ol> | |||
| <li> | |||
| Outer 1 | |||
| <ul> | |||
| <li>Inner 1</li> | |||
| <li>Inner 2</li> | |||
| </ul> | |||
| </li> | |||
| <li>Outer 2</li> | |||
| </ol> | |||
| <p>between</p> | |||
| <ul> | |||
| <li> | |||
| Outer 1 | |||
| <ol> | |||
| <li>Inner 1</li> | |||
| <li>Inner 2</li> | |||
| </ol> | |||
| </li> | |||
| <li>Outer 2</li> | |||
| </ul> | |||
| <p>between</p> | |||
| <ul> | |||
| <li> | |||
| Outer 1 | |||
| <ul> | |||
| <li>Inner 1</li> | |||
| <li>Inner 2</li> | |||
| </ul> | |||
| </li> | |||
| <li>Outer 2</li> | |||
| </ul> | |||
| <p>between</p> | |||
| <ol> | |||
| <li> | |||
| Outer 1 | |||
| <ol> | |||
| <li>Inner 1</li> | |||
| <li>Inner 2</li> | |||
| </ol> | |||
| </li> | |||
| <li>Outer 2</li> | |||
| </ol> | |||
| <p>post</p> | |||
| @@ -0,0 +1,29 @@ | |||
| pre | |||
| 1. Outer 1 | |||
| - Inner 1 | |||
| - Inner 2 | |||
| 2. Outer 2 | |||
| between | |||
| * Outer 1 | |||
| 1. Inner 1 | |||
| 2. Inner 2 | |||
| * Outer 2 | |||
| between | |||
| * Outer 1 | |||
| * Inner 1 | |||
| * Inner 2 | |||
| * Outer 2 | |||
| between | |||
| 1. Outer 1 | |||
| 1. Inner 1 | |||
| 2. Inner 2 | |||
| 2. Outer 2 | |||
| post | |||
| @@ -0,0 +1,34 @@ | |||
| <p>before</p> | |||
| <blockquote> | |||
| <p>this is my quote | |||
| this is the second part</p> | |||
| </blockquote> | |||
| <p>after</p> | |||
| <p>before</p> | |||
| <blockquote> | |||
| <p>first line | |||
| hard wrapped</p> | |||
| <p>second line | |||
| hard wrapped</p> | |||
| </blockquote> | |||
| <p>after</p> | |||
| <p>before</p> | |||
| <blockquote> | |||
| <p>first line | |||
| hard wrapped indented</p> | |||
| <p>second line | |||
| hard wrapped indented</p> | |||
| </blockquote> | |||
| <p>after</p> | |||
| <p>before</p> | |||
| <blockquote> | |||
| <p>first line | |||
| hard wrapped</p> | |||
| <p>second line | |||
| hard wrapped</p> | |||
| </blockquote> | |||
| <p>after</p> | |||
| @@ -0,0 +1,36 @@ | |||
| before | |||
| > this is my quote | |||
| > this is the second part | |||
| after | |||
| before | |||
| > first line | |||
| hard wrapped | |||
| > second line | |||
| hard wrapped | |||
| after | |||
| before | |||
| > first line | |||
| hard wrapped indented | |||
| > second line | |||
| hard wrapped indented | |||
| after | |||
| before | |||
| >first line | |||
| hard wrapped | |||
| >second line | |||
| hard wrapped | |||
| after | |||
| @@ -0,0 +1,16 @@ | |||
| <p>before</p> | |||
| <blockquote> | |||
| <p>this is my quote | |||
| this is the second part</p> | |||
| <blockquote> | |||
| <p>Nested</p> | |||
| </blockquote> | |||
| <pre><code>this is code | |||
| </code></pre> | |||
| </blockquote> | |||
| <p>after</p> | |||
| @@ -0,0 +1,10 @@ | |||
| before | |||
| > this is my quote | |||
| > this is the second part | |||
| > | |||
| > > Nested | |||
| > | |||
| > this is code | |||
| after | |||
| @@ -0,0 +1,5 @@ | |||
| <p>Paragraph</p> | |||
| <h1>Header</h1> | |||
| <p>Paragraph</p> | |||
| @@ -0,0 +1 @@ | |||
| Paragraph Header ====== Paragraph | |||
| @@ -0,0 +1,6 @@ | |||
| <ol> | |||
| <li>Apples</li> | |||
| <li>Pears</li> | |||
| <li>Bananas</li> | |||
| </ol> | |||
| @@ -0,0 +1,3 @@ | |||
| 1. Apples | |||
| 2. Pears | |||
| 3. Bananas | |||
| @@ -0,0 +1 @@ | |||
| <p>paragraph</p> | |||
| @@ -0,0 +1 @@ | |||
| paragraph | |||
| @@ -0,0 +1,5 @@ | |||
| <ul> | |||
| <li>Apples</li> | |||
| <li>Pears</li> | |||
| <li>Bananas</li> | |||
| </ul> | |||
| @@ -0,0 +1,3 @@ | |||
| * Apples | |||
| * Pears | |||
| * Bananas | |||
| @@ -0,0 +1,7 @@ | |||
| <p><abbr title="World Wide Web">WWW</abbr> at start of doc should match</p> | |||
| <p>A simple case: <abbr title="World Wide Web">WWW</abbr></p> | |||
| <p>A longer version: <abbr title="World Wide Web of Wonder :)">WWWW</abbr> shouldn't match the short one</p> | |||
| <p>This shouldn't match: aWWW nor this: WWWa.</p> | |||
| <p>This should: (<abbr title="World Wide Web">WWW</abbr>) as should this <em><abbr title="World Wide Web">WWW</abbr></em>.</p> | |||
| <p>Multiword abbreviations like "<abbr title="Markdown made Better!">Markdown Deep</abbr>" should also work.</p> | |||