Exemplo n.º 1
0
        /// <summary>
        /// Specializes block writing for anchor injection.
        /// For each formatted header we generate an anchor based on the context name, the header content and eventually add an integer suffix in order to prevent conflicts.
        /// </summary>
        /// <param name="block">The block to process.</param>
        /// <param name="isOpening">Define whether the block is opening.</param>
        /// <param name="isClosing">Defines whether the block is closing.</param>
        /// <param name="ignoreChildNodes">return whether the processing ignored child nodes.</param>
        protected override void WriteBlock(Block block, bool isOpening, bool isClosing, out bool ignoreChildNodes)
        {
            // Process block content
            if (null != block.StringContent)
            {
                // Read current block content
                string content = block.StringContent.TakeFromStart(block.StringContent.Length);

                // Detect snippet reference
                if (content.StartsWith(snippetReferencePrefix))
                {
                    // Fetch matching snippet
                    Extension.Model.Snippet snippet = snippetDictionary[Guid.Parse(content.Substring(snippetReferencePrefix.Length))];

                    // Render and write plain text snippet
                    PlainTextSnippet plainTextSnippet = snippet as PlainTextSnippet;
                    if (null != plainTextSnippet)
                    {
                        block.StringContent.Replace(plainTextSnippet.Text, 0, plainTextSnippet.Text.Length);
                    }

                    // Render and write node snippet
                    NodeSnippet nodeSnippet = snippet as NodeSnippet;
                    if (null != nodeSnippet)
                    {
                        // Render node as html
                        string renderedNode = Render(nodeSnippet.Node);

                        // Write rendering
                        block.StringContent.Replace(renderedNode, 0, renderedNode.Length);
                    }
                }
            }

            // Filter opening header
            if (isOpening && null != block && block.Tag == BlockTag.AtxHeading)
            {
                // Apply section title base
                block.Heading = new HeadingData(block.Heading.Level + this.sectionTitleBase);

                // Retrieve header content
                string headerContent;
                if (null != block.InlineContent && null != block.InlineContent.LiteralContent)
                {
                    // Read the whole content
                    Inline inline = block.InlineContent;
                    StringBuilder stringBuilder = new StringBuilder();
                    do
                    {
                        stringBuilder.Append(inline.LiteralContent);
                        inline = inline.NextSibling;
                    } while (null != inline);
                    headerContent = stringBuilder.ToString();
                }
                else
                {
                    headerContent = "unknown";
                }

                // Compute the anchor value
                string sectionId = headerContent.ToLower();
                sectionId = invalidTitleChars.Replace(string.Format("{0}-{1}", this.ContextName, sectionId), "-");

                // Detect anchor conflict
                if (sectionConflict.ContainsKey(sectionId))
                {
                    // Append the index
                    sectionId = string.Format("{0}-{1}", sectionId, ++sectionConflict[sectionId]);
                }

                // Flush the writer to move the stream position used during page break creation
                this.writer.Flush();

                // Add a new page break
                this.pageBreak.Add(new PageBreakInfo(sectionId, Math.Max(0, (int)block.Heading.Level), headerContent, this.writer.BaseStream.Position));

                // Initialize section conflict
                sectionConflict[sectionId] = 1;
            }

            // Read all paragraph inline strings in order to make table detectable
            List<string> tableParts = new List<string>();
            List<string[]> splittedTableParts = new List<string[]>();
            Match[] headerDelimiterMatches = null;
            if (isOpening && null != block && block.Tag == BlockTag.Paragraph)
            {
                Inline inline = block.InlineContent;
                while (null != inline)
                {
                    if (inline.Tag == InlineTag.String)
                    {
                        // Read and split line on '|'
                        tableParts.Add(inline.LiteralContent);
                        string line = inline.LiteralContent.Trim();
                        string[] lineParts = line.Split(new char[] { '|' }, StringSplitOptions.None).Select(x => x.Trim()).ToArray();

                        // At the third line of the same block, ensure we're processing a table before to continue table parsing
                        if (splittedTableParts.Count == 2)
                        {
                            // If the second line cannot be a header delimiter, break the table parsing
                            if (splittedTableParts[1].Any(x => string.IsNullOrWhiteSpace(x)) || (splittedTableParts[1].Length < 2 && !tableParts[1].Contains('|')))
                            {
                                break;
                            }

                            // Matches the second line as header delimiter and abort table parsing if the match fail
                            headerDelimiterMatches = splittedTableParts[1].Select(x => dashDelimiter.Match(x)).ToArray();
                            if (!headerDelimiterMatches.All(x => x.Success))
                            {
                                break;
                            }
                        }

                        // Define parts boundaries
                        int startPos = 0;
                        int numbber = lineParts.Length;

                        // Ignore the first part if the line starts with '|'
                        if ('|' == line.First())
                        {
                            ++startPos;
                            --numbber;
                        }

                        // Ignore the last part if the line ends with '|'
                        if ('|' == line.Last())
                        {
                            --numbber;
                        }

                        // Add the line to splitted parts
                        splittedTableParts.Add(lineParts.Skip(startPos).Take(numbber).ToArray());
                    }
                    inline = inline.NextSibling;
                }
            }

            // Process table rendering
            if (null != headerDelimiterMatches && splittedTableParts.Count > 2)
            {
                // Remove the delimiter
                splittedTableParts.RemoveAt(1);

                // Render table
                this.Write(@"<table class=""table"">");
                for (int i = 0; i < splittedTableParts.Count; ++i)
                {
                    // Render rows
                    this.Write("<tr>");

                    // Render cells
                    for (int j = 0; j < splittedTableParts[i].Length; ++j)
                    {
                        // Determine text alignment
                        string style = "text-left";
                        if (headerDelimiterMatches.Length > j && 0 < i)
                        {
                            Match match = headerDelimiterMatches[j];
                            if (":" == match.Groups[1].Value && ":" == match.Groups[2].Value)
                            {
                                style = "text-center";
                            }
                            else if ("" == match.Groups[1].Value && ":" == match.Groups[2].Value)
                            {
                                style = "text-right";
                            }
                        }

                        // Generate markup
                        string tag = 0 == i ? "th" : "td";
                        this.Write(string.Format(@"<{0} class=""{1}"">", tag, style));
                        this.Write(splittedTableParts[i][j]);
                        this.Write(string.Format("</{0}>", tag));
                    }

                    this.Write("</tr>");
                }
                this.Write("</table>");

                // Report rendering finished
                ignoreChildNodes = true;
                return;
            }

            // Trigger parent rendering for the default html rendering
            base.WriteBlock(block, isOpening, isClosing, out ignoreChildNodes);
        }