private void GenerateSearchPage() { var activePath = new NavigatedPath(); activePath.Push(this.Pages); var searchSimpleElement = new SimpleNavigationElement() { Name = "Search", Value = "Docnet_search.htm", IsIndexElement = false, ParentContainer = this.Pages }; searchSimpleElement.ContentProducerFunc = e => @" <h1 id=""search"">Search Results</h1> <p> <form id=""content_search"" action=""docnet_search.htm""> <span role= ""status"" aria-live=""polite"" class=""ui-helper-hidden-accessible""></span> <input name=""q"" id=""search-query"" type=""text"" class=""search_input search-query ui-autocomplete-input"" placeholder=""Search the Docs"" autocomplete=""off"" autofocus/> </form> </p> <div id=""search-results""> <p>Sorry, page not found.</p> </div>" ; searchSimpleElement.ExtraScriptProducerFunc = e => @" <script>var base_url = '.';</script> <script data-main=""js/search.js"" src=""js/require.js""></script>" ; searchSimpleElement.GenerateOutput(this, activePath); activePath.Pop(); }
/// <summary> /// The actual implementation of GenerateToCFragment. This is factored out to be able to re-use the fragment HTML product logic for the root index element /// which would otherwise be skipped as it's an index element. /// </summary> /// <param name="navigatedPath">The navigated path.</param> /// <param name="relativePathToRoot">The relative path to root.</param> /// <returns></returns> public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot) { // we can't navigate deeper from here. If we are the element being navigated to, we are the current and will have to emit any additional relative URLs too. bool isCurrent = navigatedPath.Contains(this); var fragments = new List <string>(); var liClass = "tocentry"; var aClass = string.Empty; if (isCurrent) { liClass = "tocentry current"; aClass = "current"; } fragments.Add(string.Format("<li{0}><a{1} href=\"{2}{3}\">{4}</a>", string.IsNullOrWhiteSpace(liClass) ? string.Empty : string.Format(" class=\"{0}\"", liClass), string.IsNullOrWhiteSpace(aClass) ? string.Empty : string.Format(" class=\"{0}\"", aClass), relativePathToRoot, HttpUtility.UrlPathEncode(this.TargetURL), this.Name)); if (isCurrent && _relativeH2LinksOnPage.Any()) { // generate relative links fragments.Add(string.Format("<ul class=\"{0}\">", this.ParentContainer.IsRoot ? "currentrelativeroot" : "currentrelative")); foreach (var p in _relativeH2LinksOnPage) { fragments.Add(string.Format("<li class=\"tocentry\"><a href=\"#{0}\">{1}</a></li>", p.Item1, p.Item2)); } fragments.Add("</ul>"); } else { fragments.Add("</li>"); } return(string.Join(Environment.NewLine, fragments.ToArray())); }
public void Fill(string markDownFromFile, string targetURL, string title, NavigatedPath tocLocaton) { this.Location = HttpUtility.UrlPathEncode(targetURL); this.Title = title; this.BreadCrumbs = tocLocaton.CreateBreadCrumbsText(string.Empty).Replace("\"", "").Replace("'", ""); RetrieveWords(markDownFromFile); }
/// <summary> /// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu. /// </summary> /// <param name="navigatedPath">The navigated path to the current element, which doesn't necessarily have to be this element.</param> /// <param name="relativePathToRoot">The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path.</param> /// <returns></returns> public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot) { var fragments = new List <string>(); if (!this.IsRoot) { fragments.Add("<li class=\"tocentry\">"); } if (navigatedPath.Contains(this)) { // we're expanded. If we're not root and on the top of the navigated path stack, our index page is the page we're currently generating the ToC for, so // we have to mark the entry as 'current' if (navigatedPath.Peek() == this && !this.IsRoot) { fragments.Add("<ul class=\"current\">"); } else { fragments.Add("<ul>"); } // first render the level header, which is the index element, if present or a label. The root always has an __index element otherwise we'd have stopped at load. var elementStartTag = "<li><span class=\"navigationgroup\"><i class=\"fa fa-caret-down\"></i> "; var indexElement = this.IndexElement; if (indexElement == null) { fragments.Add(string.Format("{0}{1}</span></li>", elementStartTag, this.Name)); } else { if (this.IsRoot) { fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot)); } else { fragments.Add(string.Format("{0}<a href=\"{1}{2}\">{3}</a></span></li>", elementStartTag, relativePathToRoot, HttpUtility.UrlPathEncode(indexElement.TargetURL), this.Name)); } } // then the elements in the container. Index elements are skipped here. foreach (var element in this.Value) { fragments.Add(element.GenerateToCFragment(navigatedPath, relativePathToRoot)); } fragments.Add("</ul>"); } else { // just a link fragments.Add(string.Format("<span class=\"navigationgroup\"><i class=\"fa fa-caret-right\"></i> <a href=\"{0}{1}\">{2}</a></span>", relativePathToRoot, HttpUtility.UrlPathEncode(this.TargetURL), this.Name)); } if (!this.IsRoot) { fragments.Add("</li>"); } return(string.Join(Environment.NewLine, fragments.ToArray())); }
/// <summary> /// Generates the output for this navigation element /// </summary> /// <param name="activeConfig">The active configuration to use for the output.</param> /// <param name="activePath">The active path navigated through the ToC to reach this element.</param> public override void GenerateOutput(Config activeConfig, NavigatedPath activePath) { activePath.Push(this); foreach (var element in this.Value) { element.GenerateOutput(activeConfig, activePath); } activePath.Pop(); }
/// <summary> /// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu. /// </summary> /// <param name="navigatedPath">The navigated path to the current element, which doesn't necessarily have to be this element.</param> /// <param name="relativePathToRoot">The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path.</param> /// <param name="navigationContext">The navigation context.</param> /// <returns></returns> public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { // index elements are rendered in the parent container. if (this.IsIndexElement) { return(string.Empty); } return(PerformGenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext)); }
/// <summary> /// Generates the output for this navigation element /// </summary> /// <param name="activeConfig">The active configuration to use for the output.</param> /// <param name="activePath">The active path navigated through the ToC to reach this element.</param> public override void GenerateOutput(Config activeConfig, NavigatedPath activePath) { activePath.Push(this); int i = 0; while (i < this.Value.Count) { var element = this.Value[i]; element.GenerateOutput(activeConfig, activePath); i++; } activePath.Pop(); }
/// <summary> /// The actual implementation of GenerateToCFragment. This is factored out to be able to re-use the fragment HTML product logic for the root index element /// which would otherwise be skipped as it's an index element. /// </summary> /// <param name="navigatedPath">The navigated path.</param> /// <param name="relativePathToRoot">The relative path to root.</param> /// <param name="navigationContext">The navigation context.</param> /// <returns></returns> public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { // we can't navigate deeper from here. If we are the element being navigated to, we are the current and will have to emit any additional relative URLs too. bool isCurrent = navigatedPath.Contains(this); var fragments = new List <string>(); var liClass = "tocentry"; var aClass = string.Empty; if (isCurrent) { liClass = "tocentry current"; aClass = "current"; } fragments.Add(string.Format("<li{0}><a{1} href=\"{2}{3}\">{4}</a>", string.IsNullOrWhiteSpace(liClass) ? string.Empty : string.Format(" class=\"{0}\"", liClass), string.IsNullOrWhiteSpace(aClass) ? string.Empty : string.Format(" class=\"{0}\"", aClass), relativePathToRoot, this.GetFinalTargetUrl(navigationContext), this.Name)); if (isCurrent && _relativeLinksOnPage.Count > 0) { bool renderHeadings = _relativeLinksOnPage.SelectMany(x => x.Children).Any(x => x.Level > 1); if (!renderHeadings) { // check if there are headings of a higher level than 1 present. If so, continue and render as usual renderHeadings = _relativeLinksOnPage.Any(x => x.Level > 1); } if (renderHeadings) { // generate relative links fragments.Add("<ul class=\"currentrelative\">"); foreach (var heading in _relativeLinksOnPage) { var content = GenerateToCFragmentForHeading(heading, navigationContext); if (!string.IsNullOrWhiteSpace(content)) { fragments.Add(content); } } fragments.Add("</ul>"); } } else { fragments.Add("</li>"); } return(string.Join(Environment.NewLine, fragments.ToArray())); }
internal void Generate404Page(NavigationContext navigationContext) { var simpleNavigationElement = new NotFoundNavigationElement { ParentContainer = Pages }; var navigatedPath = new NavigatedPath(); navigatedPath.Push(this.Pages); simpleNavigationElement.GenerateOutput(this, navigatedPath, navigationContext); navigatedPath.Pop(); }
/// <summary> /// Collects the search index entries. These are created from simple navigation elements found in this container, which aren't index element. /// </summary> /// <param name="collectedEntries">The collected entries.</param> /// <param name="activePath">The active path currently navigated.</param> /// <param name="navigationContext">The navigation context.</param> public override void CollectSearchIndexEntries(List <SearchIndexEntry> collectedEntries, NavigatedPath activePath, NavigationContext navigationContext) { activePath.Push(this); // simply convert ourselves into an entry if we're not an index if (!this.IsIndexElement) { var toAdd = new SearchIndexEntry(); toAdd.Fill(this.MarkdownFromFile, this.GetTargetURL(navigationContext), this.Name, activePath); collectedEntries.Add(toAdd); } activePath.Pop(); }
/// <summary> /// Collects the search index entries. These are created from simple navigation elements found in this container, which aren't index element. /// </summary> /// <param name="collectedEntries">The collected entries.</param> /// <param name="activePath">The active path currently navigated.</param> public override void CollectSearchIndexEntries(List <SearchIndexEntry> collectedEntries, NavigatedPath activePath) { activePath.Push(this); foreach (var element in this.Value) { element.CollectSearchIndexEntries(collectedEntries, activePath); } activePath.Pop(); }
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { // Skip return(string.Empty); }
/// <summary> /// Collects the search index entries. These are created from simple navigation elements found in this container, which aren't index element. /// </summary> /// <param name="collectedEntries">The collected entries.</param> /// <param name="activePath">The active path currently navigated.</param> /// <param name="navigationContext">The navigation context.</param> public abstract void CollectSearchIndexEntries(List <SearchIndexEntry> collectedEntries, NavigatedPath activePath, NavigationContext navigationContext);
/// <summary> /// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu. /// </summary> /// <param name="navigatedPath">The navigated path to the current element, which doesn't necessarily have to be this element.</param> /// <param name="relativePathToRoot">The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path.</param> /// <param name="navigationContext">The navigation context.</param> /// <returns></returns> public abstract string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext);
/// <summary> /// Generates the output for this navigation element /// </summary> /// <param name="activeConfig">The active configuration to use for the output.</param> /// <param name="activePath">The active path navigated through the ToC to reach this element.</param> /// <param name="navigationContext">The navigation context.</param> public abstract void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext);
public override void CollectSearchIndexEntries(List <SearchIndexEntry> collectedEntries, NavigatedPath activePath, NavigationContext navigationContext) { // Skip }
/// <summary> /// Generates the output for this navigation element /// </summary> /// <param name="activeConfig">The active configuration to use for the output.</param> /// <param name="activePath">The active path navigated through the ToC to reach this element.</param> /// <param name="navigationContext">The navigation context.</param> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="System.IO.FileNotFoundException"></exception> public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext) { // if we're the __index element, we're not pushing ourselves on the path, as we're representing the container we're in, which is already on the path. if (!this.IsIndexElement) { activePath.Push(this); } _relativeLinksOnPage.Clear(); var sourceFile = Utils.MakeAbsolutePath(activeConfig.Source, this.Value); var destinationFile = Utils.MakeAbsolutePath(activeConfig.Destination, this.GetTargetURL(navigationContext)); var sb = new StringBuilder(activeConfig.PageTemplateContents.Length + 2048); var content = string.Empty; this.MarkdownFromFile = string.Empty; var relativePathToRoot = Utils.MakeRelativePathForUri(Path.GetDirectoryName(destinationFile), activeConfig.Destination); if (File.Exists(sourceFile)) { this.MarkdownFromFile = File.ReadAllText(sourceFile, Encoding.UTF8); // Check if the content contains @@include tag content = Utils.IncludeProcessor(this.MarkdownFromFile, Utils.MakeAbsolutePath(activeConfig.Source, activeConfig.IncludeFolder)); content = Utils.ConvertMarkdownToHtml(content, Path.GetDirectoryName(destinationFile), activeConfig.Destination, sourceFile, _relativeLinksOnPage, activeConfig.ConvertLocalLinks); } else { // if we're not the index element, the file is missing and potentially it's an error in the config page. // Otherwise we can simply assume we are a missing index page and we'll generate default markdown so the user has something to look at. if (this.IsIndexElement) { // replace with default markdown snippet. This is the name of our container and links to the elements in that container as we are the index page that's not // specified / existend. var defaultMarkdown = new StringBuilder(); defaultMarkdown.AppendFormat("# {0}{1}{1}", this.ParentContainer.Name, Environment.NewLine); defaultMarkdown.AppendFormat("Please select one of the topics in this section:{0}{0}", Environment.NewLine); foreach (var sibling in this.ParentContainer.Value) { if (sibling == this) { continue; } defaultMarkdown.AppendFormat("* [{0}]({1}{2}){3}", sibling.Name, relativePathToRoot, sibling.GetFinalTargetUrl(navigationContext), Environment.NewLine); } defaultMarkdown.Append(Environment.NewLine); content = Utils.ConvertMarkdownToHtml(defaultMarkdown.ToString(), Path.GetDirectoryName(destinationFile), activeConfig.Destination, string.Empty, _relativeLinksOnPage, activeConfig.ConvertLocalLinks); } else { // target not found. See if there's a content producer func to produce html for us. If not, we can only conclude an error in the config file. if (this.ContentProducerFunc == null) { throw new FileNotFoundException(string.Format("The specified markdown file '{0}' couldn't be found. Aborting", sourceFile)); } content = this.ContentProducerFunc(this, activeConfig, navigationContext); } } sb.Append(activeConfig.PageTemplateContents); sb.Replace("{{Name}}", activeConfig.Name); sb.Replace("{{Footer}}", activeConfig.Footer); sb.Replace("{{TopicTitle}}", this.Name); sb.Replace("{{Path}}", relativePathToRoot); sb.Replace("{{RelativeSourceFileName}}", Utils.MakeRelativePathForUri(activeConfig.Destination, sourceFile).TrimEnd('/')); sb.Replace("{{RelativeTargetFileName}}", Utils.MakeRelativePathForUri(activeConfig.Destination, destinationFile).TrimEnd('/')); sb.Replace("{{Breadcrumbs}}", activePath.CreateBreadCrumbsHTML(relativePathToRoot, navigationContext)); sb.Replace("{{ToC}}", activePath.CreateToCHTML(relativePathToRoot, navigationContext)); sb.Replace("{{ExtraScript}}", (this.ExtraScriptProducerFunc == null) ? string.Empty : this.ExtraScriptProducerFunc(this, activeConfig, navigationContext)); // the last action has to be replacing the content marker, so markers in the content which we have in the template as well aren't replaced sb.Replace("{{Content}}", content); Utils.CreateFoldersIfRequired(destinationFile); File.WriteAllText(destinationFile, sb.ToString()); if (!this.IsIndexElement) { activePath.Pop(); } }
/// <summary> /// Generates the output for this navigation element /// </summary> /// <param name="activeConfig">The active configuration to use for the output.</param> /// <param name="activePath">The active path navigated through the ToC to reach this element.</param> /// <returns>true if everything went well, false otherwise</returns> public abstract void GenerateOutput(Config activeConfig, NavigatedPath activePath);