PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/BaconographyWP8Core/Converters/MarkdownConverter.cs

https://github.com/hippiehunter/Baconography
C# | 548 lines | 479 code | 64 blank | 5 comment | 91 complexity | 47ee90f22f2dd736da762945fae9b090 MD5 | raw file
  1. using BaconographyPortable.ViewModel;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Data;
  9. using System.Windows.Controls;
  10. using System.Windows.Documents;
  11. using System.Windows.Markup;
  12. using System.IO;
  13. using BaconographyWP8Core.Common;
  14. using BaconographyPortable.Services;
  15. using SnuDomWP8;
  16. using System.Windows.Media;
  17. using BaconographyWP8.Common;
  18. using BaconographyWP8Core.View.Markdown;
  19. using GalaSoft.MvvmLight.Command;
  20. using BaconographyPortable.Common;
  21. namespace BaconographyWP8.Converters
  22. {
  23. public class MarkdownConverter : IValueConverter
  24. {
  25. unsafe public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  26. {
  27. if (value is MarkdownData)
  28. {
  29. var markdownData = value as MarkdownData;
  30. try
  31. {
  32. var categoryVisitor = new SnuDomCategoryVisitor();
  33. ((IDomObject)markdownData.MarkdownDom).Accept(categoryVisitor);
  34. switch (categoryVisitor.Category)
  35. {
  36. case MarkdownCategory.PlainText:
  37. {
  38. var visitor = new SnuDomPlainTextVisitor();
  39. ((IDomObject)markdownData.MarkdownDom).Accept(visitor);
  40. return MakePlain(visitor.Result);
  41. }
  42. case MarkdownCategory.Formatted:
  43. case MarkdownCategory.Full:
  44. {
  45. var visitor = new SnuDomFullUIVisitor(Styles.Resources["PhoneForegroundBrush"] as Brush);
  46. ((IDomObject)markdownData.MarkdownDom).Accept(visitor);
  47. if (visitor.ResultGroup != null)
  48. return visitor.ResultGroup;
  49. else
  50. return visitor.Result;
  51. }
  52. default:
  53. return new TextBlock { Text = "" };
  54. }
  55. }
  56. catch(Exception ex)
  57. {
  58. return MakePlain(ex.ToString());
  59. }
  60. }
  61. else
  62. return new TextBlock { Text = "" };
  63. }
  64. private object MakePlain(string value)
  65. {
  66. return new TextBlock { Text = value as string, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(4, 6, 4, 6) };
  67. }
  68. public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  69. {
  70. throw new NotImplementedException();
  71. }
  72. }
  73. class SnuDomFullUIVisitor : IDomVisitor
  74. {
  75. public SnuDomFullUIVisitor(Brush forgroundBrush)
  76. {
  77. _forgroundBrush = forgroundBrush;
  78. }
  79. Brush _forgroundBrush;
  80. private int _textLengthInCurrent = 0;
  81. public RichTextBox Result = new RichTextBox { TextWrapping = TextWrapping.Wrap, Margin = new Thickness(-6, 6, 4, 6) };
  82. public StackPanel ResultGroup = null;
  83. System.Windows.Documents.Paragraph _currentParagraph;
  84. private void MaybeSplitForParagraph()
  85. {
  86. if (_textLengthInCurrent > 1000)
  87. {
  88. if (ResultGroup == null)
  89. {
  90. ResultGroup = new StackPanel { Orientation = Orientation.Vertical};
  91. ResultGroup.Children.Add(Result);
  92. Result.Margin = new Thickness(-6);
  93. }
  94. ResultGroup.Children.Add(Result = new RichTextBox { TextWrapping = TextWrapping.Wrap, Margin = new Thickness(-6, 6, 4, 6) });
  95. _textLengthInCurrent = 0;
  96. }
  97. if (_currentParagraph != null)
  98. {
  99. _currentParagraph.Inlines.Add(new System.Windows.Documents.LineBreak());
  100. }
  101. _currentParagraph = new System.Windows.Documents.Paragraph { TextAlignment = TextAlignment.Left };
  102. Result.Blocks.Add(_currentParagraph);
  103. }
  104. private void DirectlyPlaceUIContent(UIElement element)
  105. {
  106. if (ResultGroup == null)
  107. {
  108. ResultGroup = new StackPanel { Orientation = Orientation.Vertical, Margin = new Thickness(0)};
  109. if (Result.Blocks.Count == 0)
  110. {
  111. //nothing here yet so lets just ignore the current result and move on
  112. }
  113. else
  114. {
  115. ResultGroup.Children.Add(Result);
  116. }
  117. }
  118. else if(ResultGroup.Children.Last() is RichTextBox && ((RichTextBox)ResultGroup.Children.Last()).Blocks.Count == 0)
  119. {
  120. ResultGroup.Children.Remove(ResultGroup.Children.Last());
  121. }
  122. ResultGroup.Children.Add(element);
  123. ResultGroup.Children.Add(Result = new RichTextBox { TextWrapping = TextWrapping.Wrap, Margin = new Thickness(-6, 6, 4, 6) });
  124. _textLengthInCurrent = 0;
  125. }
  126. public void Visit(Text text)
  127. {
  128. var madeRun = new Run { Text = text.Contents };
  129. _textLengthInCurrent += text.Contents.Length;
  130. if (text.Italic)
  131. madeRun.FontStyle = FontStyles.Italic;
  132. if (text.Bold)
  133. madeRun.FontWeight = FontWeights.Bold;
  134. if (text.HeaderSize != 0)
  135. {
  136. switch (text.HeaderSize)
  137. {
  138. case 1:
  139. madeRun.FontSize = 24;
  140. break;
  141. case 2:
  142. madeRun.FontSize = 24;
  143. madeRun.FontWeight = FontWeights.Bold;
  144. madeRun.Foreground = _forgroundBrush;
  145. break;
  146. case 3:
  147. case 4:
  148. case 5:
  149. case 6:
  150. madeRun.FontSize = 28;
  151. madeRun.FontWeight = FontWeights.Bold;
  152. break;
  153. }
  154. MaybeSplitForParagraph();
  155. _currentParagraph.Inlines.Add(madeRun);
  156. if (text.HeaderSize == 1)
  157. {
  158. var inlineContainer = new System.Windows.Documents.InlineUIContainer();
  159. inlineContainer.Child = new Border
  160. {
  161. Margin = new Thickness(0, 5, 0, 5),
  162. Height = 1,
  163. VerticalAlignment = System.Windows.VerticalAlignment.Top,
  164. BorderBrush = _forgroundBrush,
  165. BorderThickness = new Thickness(1),
  166. MinWidth = 1800
  167. };
  168. _currentParagraph.Inlines.Add(inlineContainer);
  169. }
  170. else
  171. _currentParagraph.Inlines.Add(new System.Windows.Documents.LineBreak());
  172. }
  173. else
  174. {
  175. if (_currentParagraph == null)
  176. {
  177. _currentParagraph = new System.Windows.Documents.Paragraph { TextAlignment = TextAlignment.Left };
  178. Result.Blocks.Add(_currentParagraph);
  179. }
  180. _currentParagraph.Inlines.Add(madeRun);
  181. }
  182. }
  183. public void Visit(SnuDomWP8.Paragraph paragraph)
  184. {
  185. MaybeSplitForParagraph();
  186. foreach (var elem in paragraph)
  187. {
  188. elem.Accept(this);
  189. }
  190. }
  191. public void Visit(HorizontalRule horizontalRule)
  192. {
  193. var inlineContainer = new System.Windows.Documents.InlineUIContainer();
  194. inlineContainer.Child = new Border
  195. {
  196. Margin = new Thickness(0, 5, 0, 5),
  197. Height = 2,
  198. VerticalAlignment = System.Windows.VerticalAlignment.Top,
  199. BorderBrush = _forgroundBrush,
  200. BorderThickness = new Thickness(2),
  201. MinWidth = 1800
  202. };
  203. MaybeSplitForParagraph();
  204. _currentParagraph.Inlines.Add(inlineContainer);
  205. }
  206. public void Visit(SnuDomWP8.LineBreak lineBreak)
  207. {
  208. _currentParagraph.Inlines.Add(new System.Windows.Documents.LineBreak());
  209. }
  210. public void Visit(Link link)
  211. {
  212. if(link.Display.Count() == 0 &&
  213. (link.Url.StartsWith("#") || link.Url.StartsWith("/#") ||
  214. link.Url.StartsWith("//#") || (link.Url.StartsWith("/") && link.Url.Count(ch => ch == '/') == 1)))
  215. {
  216. return;
  217. }
  218. Inline inlineContainer = null;
  219. SnuDomCategoryVisitor categoryVisitor = new SnuDomCategoryVisitor();
  220. if (link.Display != null)
  221. {
  222. foreach (var item in link.Display)
  223. {
  224. item.Accept(categoryVisitor);
  225. }
  226. }
  227. if (categoryVisitor.Category == MarkdownCategory.PlainText)
  228. {
  229. var plainTextVisitor = new SnuDomPlainTextVisitor();
  230. if (link.Display != null && link.Display.FirstOrDefault() != null)
  231. {
  232. foreach (var item in link.Display)
  233. item.Accept(plainTextVisitor);
  234. }
  235. else
  236. plainTextVisitor.Result = link.Url;
  237. inlineContainer = new Hyperlink { Command = new RelayCommand<string>(UtilityCommandImpl.GotoLinkImpl), CommandParameter = link.Url };
  238. ((Hyperlink)inlineContainer).Inlines.Add(plainTextVisitor.Result);
  239. //inlineContainer.Child = new MarkdownButton(link.Url, plainTextVisitor.Result);
  240. }
  241. else
  242. {
  243. inlineContainer = new Hyperlink { Command = new RelayCommand<string>(UtilityCommandImpl.GotoLinkImpl), CommandParameter = link.Url };
  244. var text = link.Display.FirstOrDefault() as Text;
  245. if (text != null)
  246. {
  247. if (text.Italic)
  248. inlineContainer.FontStyle = FontStyles.Italic;
  249. if (text.Bold)
  250. inlineContainer.FontWeight = FontWeights.Bold;
  251. if (text.HeaderSize != 0)
  252. {
  253. switch (text.HeaderSize)
  254. {
  255. case 1:
  256. inlineContainer.FontSize = 24;
  257. break;
  258. case 2:
  259. inlineContainer.FontSize = 24;
  260. inlineContainer.FontWeight = FontWeights.Bold;
  261. inlineContainer.Foreground = _forgroundBrush;
  262. break;
  263. case 3:
  264. case 4:
  265. case 5:
  266. case 6:
  267. inlineContainer.FontSize = 28;
  268. inlineContainer.FontWeight = FontWeights.Bold;
  269. break;
  270. }
  271. }
  272. var plainTextVisitor = new SnuDomPlainTextVisitor();
  273. if (link.Display != null && link.Display.FirstOrDefault() != null)
  274. {
  275. foreach (var item in link.Display)
  276. item.Accept(plainTextVisitor);
  277. }
  278. else
  279. plainTextVisitor.Result = link.Url;
  280. ((Hyperlink)inlineContainer).Inlines.Add(plainTextVisitor.Result);
  281. }
  282. else
  283. {
  284. inlineContainer = new System.Windows.Documents.InlineUIContainer();
  285. var fullUIVisitor = new SnuDomFullUIVisitor(_forgroundBrush);
  286. //cant be null in this category
  287. foreach (var item in link.Display)
  288. item.Accept(fullUIVisitor);
  289. ((InlineUIContainer)inlineContainer).Child = new RichMarkdownButton(link.Url, fullUIVisitor.Result);
  290. }
  291. }
  292. if (_currentParagraph == null)
  293. {
  294. MaybeSplitForParagraph();
  295. }
  296. _currentParagraph.Inlines.Add(inlineContainer);
  297. }
  298. public void Visit(Code code)
  299. {
  300. var plainTextVisitor = new SnuDomPlainTextVisitor();
  301. foreach (var item in code)
  302. item.Accept(plainTextVisitor);
  303. var madeRun = new Run { Text = plainTextVisitor.Result };
  304. if (_currentParagraph == null || code.IsBlock)
  305. {
  306. MaybeSplitForParagraph();
  307. }
  308. _currentParagraph.Inlines.Add(madeRun);
  309. }
  310. public void Visit(Quote code)
  311. {
  312. SnuDomCategoryVisitor categoryVisitor = new SnuDomCategoryVisitor();
  313. UIElement result = null;
  314. foreach (var item in code)
  315. {
  316. item.Accept(categoryVisitor);
  317. }
  318. if (categoryVisitor.Category == MarkdownCategory.PlainText && code.Count() == 1)
  319. {
  320. var plainTextVisitor = new SnuDomPlainTextVisitor();
  321. foreach (var item in code)
  322. item.Accept(plainTextVisitor);
  323. result = new MarkdownQuote(plainTextVisitor.Result);
  324. }
  325. else
  326. {
  327. var fullUIVisitor = new SnuDomFullUIVisitor(_forgroundBrush);
  328. //cant be null in this category
  329. foreach (var item in code)
  330. item.Accept(fullUIVisitor);
  331. if (fullUIVisitor.ResultGroup != null)
  332. {
  333. result = new MarkdownQuote(fullUIVisitor.ResultGroup);
  334. }
  335. else
  336. {
  337. result = new MarkdownQuote(fullUIVisitor.Result);
  338. }
  339. }
  340. DirectlyPlaceUIContent(result);
  341. }
  342. private void FlatenVisitParagraph(IDomVisitor visitor, SnuDomWP8.Paragraph paragraph)
  343. {
  344. foreach (var item in paragraph)
  345. {
  346. if (item is SnuDomWP8.Paragraph)
  347. {
  348. FlatenVisitParagraph(visitor, item as SnuDomWP8.Paragraph);
  349. }
  350. else
  351. item.Accept(visitor);
  352. }
  353. }
  354. private IEnumerable<UIElement> BuildChildUIList(IEnumerable<IDomObject> objects)
  355. {
  356. List<UIElement> results = new List<UIElement>();
  357. foreach (var item in objects)
  358. {
  359. SnuDomCategoryVisitor categoryVisitor = new SnuDomCategoryVisitor();
  360. if (item is TableColumn)
  361. {
  362. foreach (var contents in ((TableColumn)item).Contents)
  363. {
  364. contents.Accept(categoryVisitor);
  365. }
  366. }
  367. else
  368. {
  369. item.Accept(categoryVisitor);
  370. }
  371. var column = item as TableColumn;
  372. IDomObject columnFirstContent = null;
  373. if (categoryVisitor.Category == MarkdownCategory.PlainText)
  374. {
  375. var plainTextVisitor = new SnuDomPlainTextVisitor();
  376. //this might be a pp
  377. if (column != null)
  378. {
  379. foreach (var contents in column.Contents)
  380. {
  381. contents.Accept(plainTextVisitor);
  382. }
  383. }
  384. else if(item is SnuDomWP8.Paragraph)
  385. {
  386. item.Accept(plainTextVisitor);
  387. }
  388. results.Add(new TextBlock { TextWrapping = System.Windows.TextWrapping.Wrap, Text = plainTextVisitor.Result, Margin = new Thickness(4, 6, 4, 6) });
  389. }
  390. else if (column != null && ((TableColumn)item).Contents.Count() == 1 && (columnFirstContent = ((TableColumn)item).Contents.FirstOrDefault()) != null &&
  391. (columnFirstContent is Text))
  392. {
  393. if (columnFirstContent is Link)
  394. {
  395. var plainTextVisitor = new SnuDomPlainTextVisitor();
  396. var lnk = columnFirstContent as Link;
  397. var firstContent = lnk.Display.FirstOrDefault();
  398. if(firstContent != null)
  399. firstContent.Accept(plainTextVisitor);
  400. results.Add(new MarkdownButton(lnk.Url, plainTextVisitor.Result));
  401. }
  402. else
  403. {
  404. results.Add(new TextBlock { TextWrapping = System.Windows.TextWrapping.Wrap, Text = ((Text)columnFirstContent).Contents, Margin = new Thickness(4, 6, 4, 6) });
  405. }
  406. }
  407. else
  408. {
  409. var fullUIVisitor = new SnuDomFullUIVisitor(_forgroundBrush);
  410. if (column != null)
  411. {
  412. foreach (var contents in column.Contents)
  413. {
  414. contents.Accept(fullUIVisitor);
  415. }
  416. }
  417. else if (item is SnuDomWP8.Paragraph)
  418. {
  419. FlatenVisitParagraph(fullUIVisitor, item as SnuDomWP8.Paragraph);
  420. }
  421. if (fullUIVisitor.ResultGroup != null)
  422. results.Add(fullUIVisitor.ResultGroup);
  423. else
  424. results.Add(fullUIVisitor.Result);
  425. }
  426. if (column != null)
  427. {
  428. switch (column.Alignment)
  429. {
  430. case ColumnAlignment.Center:
  431. results.Last().SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
  432. break;
  433. case ColumnAlignment.Left:
  434. results.Last().SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Left);
  435. break;
  436. case ColumnAlignment.Right:
  437. results.Last().SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Right);
  438. break;
  439. }
  440. results.Last().SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Top);
  441. }
  442. }
  443. return results;
  444. }
  445. public void Visit(OrderedList orderedList)
  446. {
  447. var uiElements = BuildChildUIList(orderedList);
  448. DirectlyPlaceUIContent(new MarkdownList(true, uiElements));
  449. }
  450. public void Visit(UnorderedList unorderedList)
  451. {
  452. var uiElements = BuildChildUIList(unorderedList);
  453. DirectlyPlaceUIContent(new MarkdownList(false, uiElements));
  454. }
  455. public void Visit(Table table)
  456. {
  457. var headerUIElements = BuildChildUIList(table.Headers);
  458. List<IEnumerable<UIElement>> tableBody = new List<IEnumerable<UIElement>>();
  459. foreach (var row in table.Rows)
  460. {
  461. tableBody.Add(BuildChildUIList(row.Columns));
  462. }
  463. DirectlyPlaceUIContent(new MarkdownTable(headerUIElements, tableBody));
  464. }
  465. public void Visit(Document document)
  466. {
  467. foreach (var elem in document)
  468. {
  469. elem.Accept(this);
  470. }
  471. }
  472. public void Visit(TableRow tableRow)
  473. {
  474. throw new NotImplementedException();
  475. }
  476. public void Visit(TableColumn tableColumn)
  477. {
  478. foreach (var elem in tableColumn.Contents)
  479. {
  480. elem.Accept(this);
  481. }
  482. }
  483. }
  484. }