private OutlineNode GenerateOutlineTree(HtmlNode sectioningContentNode, OutlineNode parentOutlineNode)
        {
            OutlineNode outlineNode = null;

            foreach (HtmlNode childNode in sectioningContentNode.ChildNodes)
            {
                if (childNode.Name == "header")
                {
                    HtmlNode headingElement = childNode.SelectSingleNode("*");
                    int      level          = headingElement.Name[1] - 48; // http://www.asciitable.com/
                    outlineNode = new OutlineNode
                    {
                        Content = headingElement.InnerText,
                        Level   = level,
                        Href    = "#" + sectioningContentNode.Id
                    };
                    parentOutlineNode.Children.Add(outlineNode);
                }

                // Intentionally skips sub trees since they do not contribute to the document's outline. Sub trees are children of
                // sectioning content roots other than section and article.
                if (childNode.Name == "section" && outlineNode?.Level < 3) // Don't include h4s, h5s and h6s
                {
                    GenerateOutlineTree(childNode, outlineNode);
                }
            }

            return(null);
        }
Exemple #2
0
        private void GenerateOutlineNodes(HtmlNode ulElement, OutlineNode outlineNode, HtmlDocument outlineHtmlDoc)
        {
            foreach (OutlineNode childOutlineNode in outlineNode.Children)
            {
                HtmlNode liElement = outlineHtmlDoc.CreateElement("li");
                HtmlNode aElement  = outlineHtmlDoc.CreateElement("a");
                aElement.SetAttributeValue("href", childOutlineNode.Href);
                HtmlNode spanElement = outlineHtmlDoc.CreateElement("span");
                spanElement.InnerHtml = childOutlineNode.Content;
                aElement.AppendChild(spanElement);
                liElement.AppendChild(aElement);
                ulElement.AppendChild(liElement);

                // Scrollabe track
                if (outlineNode.Level == 1)
                {
                    HtmlNode indicatorTrackElement = outlineHtmlDoc.CreateElement("div");
                    indicatorTrackElement.SetAttributeValue("class", "outline-scrollable-track");
                    liElement.AppendChild(indicatorTrackElement);
                }

                if (childOutlineNode.Children.Count > 0 && childOutlineNode.Level < 3) // Don't include h4s, h5s and h6s
                {
                    HtmlNode childULElement = outlineHtmlDoc.CreateElement("ul");
                    childULElement.SetAttributeValue("class", $"level-{childOutlineNode.Level + 1}");
                    liElement.AppendChild(childULElement);

                    GenerateOutlineNodes(childULElement, childOutlineNode, outlineHtmlDoc);
                }
            }
        }
Exemple #3
0
        private OutlineNode GenerateOutlineTree(HtmlNode htmlNode, OutlineNode outlineNode)
        {
            HtmlNodeCollection children = htmlNode.ChildNodes;

            foreach (HtmlNode childHtmlNode in children)
            {
                // Intentionally skips sub trees since they do not contribute to the document's outline. Sub trees are children of
                // sectioning content roots, like blockquote. If child is a blockquote, we never iterate through its children, so sub trees are ignored.
                if (childHtmlNode.Name == "section")
                {
                    // We don't know the heading tag, could be h1|h2|h3|h4|h5|h6. Xpath 1 sucks so bad.
                    HtmlNode    headingElement   = childHtmlNode.SelectSingleNode("header/*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6]");
                    int         level            = headingElement.Name[1] - 48; // http://www.asciitable.com/
                    OutlineNode childOutlineNode = new OutlineNode
                    {
                        Content = headingElement.InnerText,
                        Level   = level,
                        Href    = "#" + childHtmlNode.Id
                    };
                    outlineNode.Children.Add(childOutlineNode);

                    GenerateOutlineTree(childHtmlNode, childOutlineNode);
                }
            }


            return(null);
        }
        private void GenerateOutlineNodes(HtmlNode ulElement, OutlineNode outlineNodes, HtmlDocument outlineHtmlDoc)
        {
            bool firstChild = true;

            foreach (OutlineNode childOutlineNode in outlineNodes.Children)
            {
                HtmlNode liElement = outlineHtmlDoc.CreateElement("li");
                liElement.SetAttributeValue("class", $"outline__node outline__node_level_{childOutlineNode.Level}");
                HtmlNode aElement = outlineHtmlDoc.CreateElement("a");
                if (childOutlineNode.Level == 2 && firstChild)
                {
                    aElement.SetAttributeValue("class", "outline__link outline__link_has-bar outline__link_indented");
                }
                else if (childOutlineNode.Level == 1)
                {
                    aElement.SetAttributeValue("class", "outline__link outline__link_title");
                }
                else
                {
                    aElement.SetAttributeValue("class", "outline__link outline__link_indented");
                }
                aElement.SetAttributeValue("href", childOutlineNode.Href);
                HtmlNode spanElement = outlineHtmlDoc.CreateElement("span");
                spanElement.SetAttributeValue("class", "outline__text");
                spanElement.InnerHtml = childOutlineNode.Content;
                aElement.AppendChild(spanElement);
                liElement.AppendChild(aElement);
                ulElement.AppendChild(liElement);
                ulElement.SetAttributeValue("style", $"--level: {childOutlineNode.Level}");

                if (childOutlineNode.Level == 1)
                {
                    HtmlNode backToTopIconSvgNode = outlineHtmlDoc.CreateElement("svg");
                    backToTopIconSvgNode.SetAttributeValue("class", "outline__back-to-top-icon");
                    HtmlNode backToTopIconUseNode = outlineHtmlDoc.CreateElement("use");
                    backToTopIconUseNode.SetAttributeValue("xlink:href", "#material-design-arrow-upward");
                    backToTopIconSvgNode.AppendChild(backToTopIconUseNode);
                    aElement.AppendChild(backToTopIconSvgNode);
                }

                if (childOutlineNode.Children.Count > 0)
                {
                    HtmlNode childULElement = outlineHtmlDoc.CreateElement("ul");
                    childULElement.SetAttributeValue("class", "outline__list");
                    liElement.AppendChild(childULElement);

                    GenerateOutlineNodes(childULElement, childOutlineNode, outlineHtmlDoc);
                }

                firstChild = false;
            }
        }
        // If article menu is enabled, generates outline and inserts it
        public Manifest Process(Manifest manifest, string outputFolder)
        {
            if (outputFolder == null)
            {
                throw new ArgumentNullException("Base directory cannot be null");
            }

            foreach (ManifestItem manifestItem in manifest.Files)
            {
                if (manifestItem.DocumentType != "Conceptual")
                {
                    continue;
                }

                manifestItem.Metadata.TryGetValue("mimo_disableArticleMenu", out object disableArticleMenu);
                if (disableArticleMenu as bool? == true)
                {
                    continue;
                }

                // Get document Node
                HtmlNode documentNode = manifestItem.
                                        GetHtmlOutputDoc(outputFolder).
                                        DocumentNode;

                // Get main article node
                HtmlNode mainArticleNode = documentNode.SelectSingleNode($"//article[@class='{UtilsConstants.MainArticleClasses}']");

                // Generate outline tree for article
                OutlineNode rootOutlineNode = new OutlineNode();
                GenerateOutlineTree(mainArticleNode, rootOutlineNode);

                // Render outline tree
                var      outlineHtmlDoc = new HtmlDocument();
                HtmlNode rootULElement  = outlineHtmlDoc.CreateElement("ul");
                rootULElement.SetAttributeValue("class", "outline__list outline__list_root");
                GenerateOutlineNodes(rootULElement, rootOutlineNode, outlineHtmlDoc);

                // Insert title
                HtmlNode articleMenuContentElement = documentNode.SelectSingleNode("//*[@class='article-menu__content dropdown__body']");

                // Scrollable indicators
                HtmlNode level2ULElement = rootULElement.SelectSingleNode("li/ul");
                if (level2ULElement != null) // If there is no level2ULElement, there's no scrolling
                {
                    // Wrap level 2 ul element in a div together with indicators, insert wrapper as child of level 2 ul element's parent
                    level2ULElement.SetAttributeValue("class", "outline__list scrollable-indicators__scrollable outline__scrollable");
                    level2ULElement.Remove();
                    HtmlNode scrollableIndicatorsNode = outlineHtmlDoc.CreateElement("div");
                    scrollableIndicatorsNode.SetAttributeValue("class", "outline__scrollable-indicators scrollable-indicators scrollable-indicators_axis_vertical");
                    HtmlNode startIndicatorElement = outlineHtmlDoc.CreateElement("div");
                    startIndicatorElement.SetAttributeValue("class", "scrollable-indicators__indicator scrollable-indicators__indicator_start");
                    HtmlNode endIndicatorElement = outlineHtmlDoc.CreateElement("div");
                    endIndicatorElement.SetAttributeValue("class", "scrollable-indicators__indicator scrollable-indicators__indicator_end");
                    scrollableIndicatorsNode.AppendChild(level2ULElement);
                    scrollableIndicatorsNode.AppendChild(startIndicatorElement);
                    scrollableIndicatorsNode.AppendChild(endIndicatorElement);
                    HtmlNode level1LIElement = rootULElement.SelectSingleNode("li");
                    level1LIElement.AppendChild(scrollableIndicatorsNode);
                }

                // Insert outline tree
                HtmlNode outlineNode = outlineHtmlDoc.CreateElement("div");
                outlineNode.SetAttributeValue("class", "article-menu__outline outline");
                outlineNode.AppendChild(rootULElement);
                articleMenuContentElement.AppendChild(outlineNode);

                // Save
                string relPath = manifestItem.GetHtmlOutputRelPath();
                File.WriteAllText(Path.Combine(outputFolder, relPath), documentNode.OuterHtml);
            }

            return(manifest);
        }
Exemple #6
0
        // If article menu is enabled, generates outline and inserts it
        public Manifest Process(Manifest manifest, string outputFolder)
        {
            if (outputFolder == null)
            {
                throw new ArgumentNullException("Base directory cannot be null");
            }

            foreach (ManifestItem manifestItem in manifest.Files)
            {
                if (manifestItem.DocumentType != "Conceptual")
                {
                    continue;
                }

                manifestItem.Metadata.TryGetValue("mimo_disableArticleMenu", out object disableArticleMenu);
                if (disableArticleMenu as bool? == true)
                {
                    continue;
                }

                // Get document Node
                HtmlNode documentNode = manifestItem.
                                        GetHtmlOutputDoc(outputFolder).
                                        DocumentNode;

                // Get article node
                HtmlNode articleNode = documentNode.SelectSingleNode("//article[@class='main-article']");

                // Generate outline tree for article
                OutlineNode rootOutlineNode = new OutlineNode
                {
                    Content = articleNode.SelectSingleNode("h1").InnerText,
                    Level   = 1,
                    Href    = "#"
                };
                GenerateOutlineTree(articleNode.SelectSingleNode("div[@class='content']"), rootOutlineNode); // content div is the direct parent level 2 sections

                // Render outline tree
                var      outlineHtmlDoc = new HtmlDocument();
                HtmlNode rootULElement  = outlineHtmlDoc.CreateElement("ul");
                rootULElement.SetAttributeValue("class", "level-2");
                GenerateOutlineNodes(rootULElement, rootOutlineNode, outlineHtmlDoc);

                // Insert title
                HtmlNode outlineElement   = documentNode.SelectSingleNode("//*[@id='outline']");
                HtmlNode titleSpanElement = outlineHtmlDoc.CreateElement("span");
                titleSpanElement.InnerHtml = rootOutlineNode.Content;
                HtmlNode titleAElement = outlineHtmlDoc.CreateElement("a");
                titleAElement.SetAttributeValue("href", "#");
                titleAElement.SetAttributeValue("class", "level-1");
                titleAElement.AppendChild(titleSpanElement);
                outlineElement.PrependChild(titleAElement);

                // Insert outline tree
                HtmlNode outlineScrollableElement = outlineElement.SelectSingleNode("*[@id='outline-scrollable']");
                outlineScrollableElement.AppendChild(rootULElement);

                // Save
                string relPath = manifestItem.GetHtmlOutputRelPath();
                File.WriteAllText(Path.Combine(outputFolder, relPath), documentNode.OuterHtml);
            }

            return(manifest);
        }