Browse Source

Merge branch 'Geertvanhorrik-pr/path-specification'

pull/70/head
Frans Bouma 8 years ago
parent
commit
6f88f4b72e
12 changed files with 317 additions and 117 deletions
  1. +21
    -2
      src/DocNet/Config.cs
  2. +2
    -0
      src/DocNet/Docnet.csproj
  3. +5
    -2
      src/DocNet/Engine.cs
  4. +13
    -5
      src/DocNet/INavigationElement.cs
  5. +35
    -0
      src/DocNet/INavigationElementExtensions.cs
  6. +8
    -6
      src/DocNet/NavigatedPath.cs
  7. +14
    -5
      src/DocNet/NavigationElement.cs
  8. +119
    -55
      src/DocNet/NavigationLevel.cs
  9. +11
    -0
      src/DocNet/PathSpecification.cs
  10. +2
    -2
      src/DocNet/Properties/AssemblyInfo.cs
  11. +43
    -22
      src/DocNet/SimpleNavigationElement.cs
  12. +44
    -18
      src/MarkdownDeep/MardownDeep.cs

+ 21
- 2
src/DocNet/Config.cs View File

@@ -131,7 +131,7 @@ namespace Docnet
private void GenerateSearchDataIndex()
{
var collectedSearchEntries = new List<SearchIndexEntry>();
this.Pages.CollectSearchIndexEntries(collectedSearchEntries, new NavigatedPath());
this.Pages.CollectSearchIndexEntries(collectedSearchEntries, new NavigatedPath(), this.PathSpecification);
JObject searchIndex = new JObject(new JProperty("docs",
new JArray(
collectedSearchEntries.Select(e=>new JObject(
@@ -164,7 +164,7 @@ namespace Docnet
searchSimpleElement.ExtraScriptProducerFunc = e=> @"
<script>var base_url = '.';</script>
<script data-main=""js/search.js"" src=""js/require.js""></script>";
searchSimpleElement.GenerateOutput(this, activePath);
searchSimpleElement.GenerateOutput(this, activePath, this.PathSpecification);
activePath.Pop();
}

@@ -256,6 +256,25 @@ namespace Docnet
get { return _templateContents ?? string.Empty; }
}

public PathSpecification PathSpecification
{
get
{
var pathSpecification = PathSpecification.Full;

var pathSpecificationAsString = (string)_configData.PathSpecification;
if (!string.IsNullOrWhiteSpace(pathSpecificationAsString))
{
if (!Enum.TryParse(pathSpecificationAsString, true, out pathSpecification))
{
pathSpecification = PathSpecification.Full;
}
}

return pathSpecification;
}
}

public NavigationLevel Pages
{
get


+ 2
- 0
src/DocNet/Docnet.csproj View File

@@ -60,10 +60,12 @@
<Compile Include="NavigatedPath.cs" />
<Compile Include="NavigationElement.cs" />
<Compile Include="NavigationLevel.cs" />
<Compile Include="PathSpecification.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SearchIndexEntry.cs" />
<Compile Include="SimpleNavigationElement.cs" />
<Compile Include="INavigationElementExtensions.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>


+ 5
- 2
src/DocNet/Engine.cs View File

@@ -70,11 +70,14 @@ namespace Docnet
Console.WriteLine("Errors occurred, can't continue!");
return null;
}
if(config.Pages.IndexElement == null)

var indexElement = config.Pages.GetIndexElement(config.PathSpecification);
if(indexElement == null)
{
Console.WriteLine("[ERROR] Root __index not found. The root navigationlevel is required to have an __index element");
return null;
}

return config;
}

@@ -95,7 +98,7 @@ namespace Docnet
Console.WriteLine("Copying source folders to copy.");
_loadedConfig.CopySourceFoldersToCopy();
Console.WriteLine("Generating pages in '{0}'", _loadedConfig.Destination);
_loadedConfig.Pages.GenerateOutput(_loadedConfig, new NavigatedPath());
_loadedConfig.Pages.GenerateOutput(_loadedConfig, new NavigatedPath(), _loadedConfig.PathSpecification);
Console.WriteLine("Generating search index");
_loadedConfig.GenerateSearchData();
Console.WriteLine("Done!");


+ 13
- 5
src/DocNet/INavigationElement.cs View File

@@ -35,31 +35,39 @@ namespace Docnet
/// </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>
void GenerateOutput(Config activeConfig, NavigatedPath activePath);
/// <param name="pathSpecification">The path specification.</param>
void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification);

/// <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="pathSpecification">The path specification.</param>
/// <returns></returns>
string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot);
string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification);
/// <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>
void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath);
/// <param name="pathSpecification">The path specification.</param>
void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification);

/// <summary>
/// Gets the target URL with respect to the <see cref="PathSpecification"/>.
/// </summary>
/// <param name="pathSpecification">The path specification.</param>
/// <returns></returns>
string GetTargetURL(PathSpecification pathSpecification);

/// <summary>
/// Gets a value indicating whether this element is the __index element
/// </summary>
bool IsIndexElement { get; set; }
bool IsAutoGenerated { get; set; }
string Name { get; set; }
object Value { get; set; }
string TargetURL { get; }
NavigationLevel ParentContainer { get; set; }
}
}

+ 35
- 0
src/DocNet/INavigationElementExtensions.cs View File

@@ -0,0 +1,35 @@
using System;
using System.Web;

namespace Docnet
{
public static class INavigationElementExtensions
{
private const string IndexHtmFileName = "index.htm";

/// <summary>
/// Gets the final URL by encoding the path and by removing the filename if it equals <c>index.htm</c>.
/// </summary>
/// <param name="navigationElement">The navigation element.</param>
/// <param name="pathSpecification">The path specification.</param>
/// <returns></returns>
public static string GetFinalTargetUrl(this INavigationElement navigationElement, PathSpecification pathSpecification)
{
var targetUrl = navigationElement.GetTargetURL(pathSpecification);
var link = HttpUtility.UrlPathEncode(targetUrl);

// Disabled for now as discussed in #65 (https://github.com/FransBouma/DocNet/pull/65), but
// is required for #44
//if (pathSpecification == PathSpecification.RelativeAsFolder)
//{
// if (link.Length > IndexHtmFileName.Length &&
// link.EndsWith(IndexHtmFileName, StringComparison.InvariantCultureIgnoreCase))
// {
// link = link.Substring(0, link.Length - IndexHtmFileName.Length);
// }
//}

return link;
}
}
}

+ 8
- 6
src/DocNet/NavigatedPath.cs View File

@@ -35,17 +35,18 @@ namespace Docnet
public class NavigatedPath : Stack<INavigationElement>
{
/// <summary>
/// Creates the bread crumbs HTML of the elements in this path, delimited by '/' characters.
/// Creates the bread crumbs HTML of the elements in this path, delimited by '/' characters.
/// </summary>
/// <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="pathSpecification">The path specification.</param>
/// <returns></returns>
public string CreateBreadCrumbsHTML(string relativePathToRoot)
public string CreateBreadCrumbsHTML(string relativePathToRoot, PathSpecification pathSpecification)
{
var fragments = new List<string>();
// we enumerate a stack, which enumerates from top to bottom, so we have to reverse things first.
foreach(var element in this.Reverse())
{
var targetURL = element.TargetURL;
var targetURL = element.GetTargetURL(pathSpecification);
if(string.IsNullOrWhiteSpace(targetURL))
{
fragments.Add(string.Format("<li>{0}</li>", element.Name));
@@ -73,11 +74,12 @@ namespace Docnet

/// <summary>
/// Creates the ToC HTML for the element reached by the elements in this path. All containers in this path are expanded, all elements inside these containers which
/// aren't, are not expanded.
/// aren't, are not expanded.
/// </summary>
/// <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="pathSpecification">The path specification.</param>
/// <returns></returns>
public string CreateToCHTML(string relativePathToRoot)
public string CreateToCHTML(string relativePathToRoot, PathSpecification pathSpecification)
{
// the root container is the bottom element of this path. We use that container to build the root and navigate any node open along the navigated path.
var rootContainer = this.Reverse().FirstOrDefault() as NavigationLevel;
@@ -86,7 +88,7 @@ namespace Docnet
// no root container, no TOC
return string.Empty;
}
return rootContainer.GenerateToCFragment(this, relativePathToRoot);
return rootContainer.GenerateToCFragment(this, relativePathToRoot, pathSpecification);
}
}
}

+ 14
- 5
src/DocNet/NavigationElement.cs View File

@@ -36,30 +36,39 @@ namespace Docnet
/// </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);
/// <param name="pathSpecification">The path specification.</param>
public abstract void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification);
/// <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="pathSpecification">The path specification.</param>
/// <returns></returns>
public abstract string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot);
public abstract string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification);
/// <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 abstract void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath);
/// <param name="pathSpecification">The path specification.</param>
public abstract void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification);

/// <summary>
/// Gets the target URL with respect to the <see cref="PathSpecification"/>.
/// </summary>
/// <param name="pathSpecification">The path specification.</param>
/// <returns></returns>
public abstract string GetTargetURL(PathSpecification pathSpecification);

#region Properties
public abstract string TargetURL { get; }
/// <summary>
/// Gets / sets a value indicating whether this element is the __index element
/// </summary>
public abstract bool IsIndexElement { get; set; }

public bool IsAutoGenerated { get; set; }

public string Name { get; set; }
/// <summary>
/// Gets or sets the value of this element, which can either be a string or a NavigationLevel


+ 119
- 55
src/DocNet/NavigationLevel.cs View File

@@ -71,26 +71,27 @@ namespace Docnet
{
path = Path.Combine(_rootDirectory, path);
}

toAdd = CreateGeneratedLevel(path);
toAdd.Name = nameToUse;
}
else
{
toAdd = new SimpleNavigationElement
{
Name = nameToUse,
Value = childValue,
IsIndexElement = isIndexElement
};
{
Name = nameToUse,
Value = childValue,
IsIndexElement = isIndexElement
};
}
}
else
{
var subLevel = new NavigationLevel(_rootDirectory)
{
Name = child.Key,
IsRoot = false
};
{
Name = child.Key,
IsRoot = false
};
subLevel.Load((JObject)child.Value);
toAdd = subLevel;
}
@@ -105,12 +106,13 @@ namespace Docnet
/// </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)
/// <param name="pathSpecification">The path specification.</param>
public override void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification)
{
activePath.Push(this);
foreach (var element in this.Value)
{
element.CollectSearchIndexEntries(collectedEntries, activePath);
element.CollectSearchIndexEntries(collectedEntries, activePath, pathSpecification);
}
activePath.Pop();
}
@@ -121,14 +123,15 @@ namespace Docnet
/// </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)
/// <param name="pathSpecification">The path specification.</param>
public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification)
{
activePath.Push(this);
int i = 0;
while (i < this.Value.Count)
{
var element = this.Value[i];
element.GenerateOutput(activeConfig, activePath);
element.GenerateOutput(activeConfig, activePath, pathSpecification);
i++;
}
activePath.Pop();
@@ -140,8 +143,9 @@ namespace Docnet
/// </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="pathSpecification">The path specification.</param>
/// <returns></returns>
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot)
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification)
{
var fragments = new List<string>();
if (!this.IsRoot)
@@ -163,7 +167,7 @@ namespace Docnet

// 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;
var indexElement = this.GetIndexElement(pathSpecification);
if (indexElement == null)
{
fragments.Add(string.Format("{0}{1}</span></li>", elementStartTag, this.Name));
@@ -172,18 +176,18 @@ namespace Docnet
{
if (this.IsRoot)
{
fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot));
fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification));
}
else
{
fragments.Add(string.Format("{0}<a href=\"{1}{2}\">{3}</a></span></li>", elementStartTag, relativePathToRoot, HttpUtility.UrlPathEncode(indexElement.TargetURL),
this.Name));
fragments.Add(string.Format("{0}<a href=\"{1}{2}\">{3}</a></span></li>",
elementStartTag, relativePathToRoot, indexElement.GetFinalTargetUrl(pathSpecification), 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(element.GenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification));
}
fragments.Add("</ul>");
}
@@ -191,7 +195,7 @@ namespace Docnet
{
// 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));
relativePathToRoot, this.GetFinalTargetUrl(pathSpecification), this.Name));
}
if (!this.IsRoot)
{
@@ -204,9 +208,10 @@ namespace Docnet
private NavigationLevel CreateGeneratedLevel(string path)
{
var root = new NavigationLevel(_rootDirectory)
{
ParentContainer = this
};
{
ParentContainer = this,
IsAutoGenerated = true
};

foreach (var mdFile in Directory.GetFiles(path, "*.md", SearchOption.TopDirectoryOnly))
{
@@ -217,11 +222,12 @@ namespace Docnet
}

var item = new SimpleNavigationElement
{
Name = name,
Value = Utils.MakeRelativePath(mdFile, _rootDirectory),
ParentContainer = root
};
{
Name = name,
Value = Path.Combine(Utils.MakeRelativePath(_rootDirectory, path), Path.GetFileName(mdFile)),
ParentContainer = root,
IsAutoGenerated = true
};

root.Value.Add(item);
}
@@ -234,6 +240,7 @@ namespace Docnet

root.Value.Add(subDirectoryNavigationElement);
}

return root;
}

@@ -270,49 +277,106 @@ namespace Docnet
return title;
}


#region Properties
public override string TargetURL
public override string GetTargetURL(PathSpecification pathSpecification)
{
get
var defaultElement = this.GetIndexElement(pathSpecification);
if (defaultElement == null)
{
var defaultElement = this.IndexElement;
if (defaultElement == null)
{
return string.Empty;
}
return defaultElement.TargetURL ?? string.Empty;
return string.Empty;
}
}

return defaultElement.GetTargetURL(pathSpecification) ?? string.Empty;
}

public SimpleNavigationElement IndexElement
public SimpleNavigationElement GetIndexElement(PathSpecification pathSpecification)
{
get
var toReturn = this.Value.FirstOrDefault(e => e.IsIndexElement) as SimpleNavigationElement;
if (toReturn == null)
{
var toReturn = this.Value.FirstOrDefault(e => e.IsIndexElement) as SimpleNavigationElement;
if (toReturn == null)
// no index element, add an artificial one.
var path = string.Empty;

// Don't check parents when using relative paths since we need to walk the tree manually
if (pathSpecification == PathSpecification.Full)
{
// no index element, add an artificial one.
var path = string.Empty;
if (this.ParentContainer != null)
{
path = Path.GetDirectoryName(this.ParentContainer.TargetURL);
path = Path.GetDirectoryName(this.ParentContainer.GetTargetURL(pathSpecification));
}
var nameToUse = this.Name.Replace(".", "").Replace('/', '_').Replace("\\", "_").Replace(":", "").Replace(" ", "");
if (string.IsNullOrWhiteSpace(nameToUse))
{
return null;
}
toReturn = new SimpleNavigationElement() { ParentContainer = this, Value = string.Format("{0}{1}.md", path, nameToUse), Name = this.Name, IsIndexElement = true };
this.Value.Add(toReturn);
}

return toReturn;
var nameToUse = this.Name.Replace(".", "").Replace('/', '_').Replace("\\", "_").Replace(":", "").Replace(" ", "");
if (string.IsNullOrWhiteSpace(nameToUse))
{
return null;
}

var value = string.Format("{0}{1}.md", path, nameToUse);

switch (pathSpecification)
{
case PathSpecification.Full:
// Default is correct
break;

case PathSpecification.Relative:
case PathSpecification.RelativeAsFolder:
if (!IsRoot)
{
string preferredPath = null;

// We're making a big assumption here, but we can get the first page and assume it's
// in the right folder.

// Find first (simple) child and use 1 level up. A SimpleNavigationElement mostly represents a folder
// thus is excellent to be used as a folder name
var firstSimpleChildPage = (SimpleNavigationElement) this.Value.FirstOrDefault(x => x is SimpleNavigationElement && !ReferenceEquals(this, x));
if (firstSimpleChildPage != null)
{
preferredPath = Path.GetDirectoryName(firstSimpleChildPage.Value);
}
else
{
// This is representing an empty folder. Search for first child navigation that has real childs,
// then retrieve the path by going levels up.
var firstChildNavigationLevel = (NavigationLevel)this.Value.FirstOrDefault(x => x is NavigationLevel && ((NavigationLevel)x).Value.Any() && !ReferenceEquals(this, x));
if (firstChildNavigationLevel != null)
{
var targetUrl = firstChildNavigationLevel.Value.First().GetTargetURL(pathSpecification);

// 3 times since we need 2 parents up
preferredPath = Path.GetDirectoryName(targetUrl);
preferredPath = Path.GetDirectoryName(preferredPath);
preferredPath = Path.GetDirectoryName(preferredPath);
}
}

if (!string.IsNullOrWhiteSpace(preferredPath))
{
value = Path.Combine(preferredPath, "index.md");
}
}
break;

default:
throw new ArgumentOutOfRangeException(nameof(pathSpecification), pathSpecification, null);
}

toReturn = new SimpleNavigationElement
{
ParentContainer = this,
Value = value,
Name = this.Name,
IsIndexElement = true
};

this.Value.Add(toReturn);
}
}

return toReturn;
}

#region Properties
/// <summary>
/// Gets / sets a value indicating whether this element is the __index element
/// </summary>


+ 11
- 0
src/DocNet/PathSpecification.cs View File

@@ -0,0 +1,11 @@
namespace Docnet
{
public enum PathSpecification
{
Full,

Relative,

RelativeAsFolder
}
}

+ 2
- 2
src/DocNet/Properties/AssemblyInfo.cs View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 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("0.15.0.0")]
[assembly: AssemblyFileVersion("0.15.0")]
[assembly: AssemblyVersion("0.16.0.0")]
[assembly: AssemblyFileVersion("0.16.0")]

+ 43
- 22
src/DocNet/SimpleNavigationElement.cs View File

@@ -49,7 +49,9 @@ namespace Docnet
/// </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)
/// <param name="pathSpecification">The path specification.</param>
/// <exception cref="System.IO.FileNotFoundException"></exception>
public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification)
{
// 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)
@@ -58,7 +60,7 @@ namespace Docnet
}
_relativeH2LinksOnPage.Clear();
var sourceFile = Utils.MakeAbsolutePath(activeConfig.Source, this.Value);
var destinationFile = Utils.MakeAbsolutePath(activeConfig.Destination, this.TargetURL);
var destinationFile = Utils.MakeAbsolutePath(activeConfig.Destination, this.GetTargetURL(pathSpecification));
var sb = new StringBuilder(activeConfig.PageTemplateContents.Length + 2048);
var content = string.Empty;
this.MarkdownFromFile = string.Empty;
@@ -87,7 +89,8 @@ namespace Docnet
{
continue;
}
defaultMarkdown.AppendFormat("* [{0}]({1}{2}){3}", sibling.Name, relativePathToRoot, HttpUtility.UrlPathEncode(sibling.TargetURL), Environment.NewLine);
defaultMarkdown.AppendFormat("* [{0}]({1}{2}){3}", sibling.Name, relativePathToRoot,
sibling.GetFinalTargetUrl(pathSpecification), Environment.NewLine);
}
defaultMarkdown.Append(Environment.NewLine);
content = Utils.ConvertMarkdownToHtml(defaultMarkdown.ToString(), Path.GetDirectoryName(destinationFile), activeConfig.Destination, string.Empty, _relativeH2LinksOnPage, activeConfig.ConvertLocalLinks);
@@ -109,8 +112,8 @@ namespace Docnet
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));
sb.Replace("{{ToC}}", activePath.CreateToCHTML(relativePathToRoot));
sb.Replace("{{Breadcrumbs}}", activePath.CreateBreadCrumbsHTML(relativePathToRoot, pathSpecification));
sb.Replace("{{ToC}}", activePath.CreateToCHTML(relativePathToRoot, pathSpecification));
sb.Replace("{{ExtraScript}}", (this.ExtraScriptProducerFunc == null) ? string.Empty : this.ExtraScriptProducerFunc(this));

// 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
@@ -129,14 +132,15 @@ namespace Docnet
/// </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)
/// <param name="pathSpecification">The path specification.</param>
public override void CollectSearchIndexEntries(List<SearchIndexEntry> collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification)
{
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.TargetURL, this.Name, activePath);
toAdd.Fill(this.MarkdownFromFile, this.GetTargetURL(pathSpecification), this.Name, activePath);
collectedEntries.Add(toAdd);
}
activePath.Pop();
@@ -148,8 +152,9 @@ namespace Docnet
/// </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="pathSpecification">The path specification.</param>
/// <returns></returns>
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot)
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification)
{
// index elements are rendered in the parent container.
if(this.IsIndexElement)
@@ -157,7 +162,7 @@ namespace Docnet
return string.Empty;
}

return PerformGenerateToCFragment(navigatedPath, relativePathToRoot);
return PerformGenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification);
}


@@ -167,8 +172,9 @@ namespace Docnet
/// </summary>
/// <param name="navigatedPath">The navigated path.</param>
/// <param name="relativePathToRoot">The relative path to root.</param>
/// <param name="pathSpecification">The path specification.</param>
/// <returns></returns>
public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot)
public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification)
{
// 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);
@@ -184,7 +190,7 @@ namespace Docnet
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.GetFinalTargetUrl(pathSpecification),
this.Name));
if(isCurrent && _relativeH2LinksOnPage.Any())
{
@@ -203,26 +209,41 @@ namespace Docnet
return string.Join(Environment.NewLine, fragments.ToArray());
}


#region Properties
public override string TargetURL
/// <summary>
/// Gets the target URL with respect to the <see cref="T:Docnet.PathSpecification" />.
/// </summary>
/// <param name="pathSpecification">The path specification.</param>
/// <returns></returns>
/// <exception cref="System.NotImplementedException"></exception>
public override string GetTargetURL(PathSpecification pathSpecification)
{
get
if (_targetURLForHTML == null)
{
if(_targetURLForHTML==null)
_targetURLForHTML = (this.Value ?? string.Empty);

var toReplace = ".md";
var replacement = ".htm";

if (pathSpecification == PathSpecification.RelativeAsFolder)
{
_targetURLForHTML = (this.Value ?? string.Empty);
if(_targetURLForHTML.ToLowerInvariant().EndsWith(".md"))
if (!IsIndexElement && !_targetURLForHTML.EndsWith("index.md", StringComparison.InvariantCultureIgnoreCase))
{
_targetURLForHTML = _targetURLForHTML.Substring(0, _targetURLForHTML.Length-3) + ".htm";
replacement = "/index.htm";
}
_targetURLForHTML = _targetURLForHTML.Replace("\\", "/");
}
return _targetURLForHTML;

if (_targetURLForHTML.EndsWith(toReplace, StringComparison.InvariantCultureIgnoreCase))
{
_targetURLForHTML = _targetURLForHTML.Substring(0, _targetURLForHTML.Length - toReplace.Length) + replacement;
}

_targetURLForHTML = _targetURLForHTML.Replace("\\", "/");
}
}

return _targetURLForHTML;
}

#region Properties
/// <summary>
/// Gets / sets a value indicating whether this element is the __index element
/// </summary>


+ 44
- 18
src/MarkdownDeep/MardownDeep.cs View File

@@ -14,6 +14,7 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

@@ -279,8 +280,10 @@ namespace MarkdownDeep
}

// Override to supply the size of an image
public virtual bool OnGetImageSize(string url, bool TitledImage, out int width, out int height)
public virtual bool OnGetImageSize(string url, bool TitledImage, out int width, out int height, out string finalUrl)
{
finalUrl = url;

if (GetImageSizeFunc != null)
{
var info = new ImageInfo() { url = url, titled_image=TitledImage };
@@ -314,30 +317,51 @@ namespace MarkdownDeep
url=url.Substring(1);
}

str=str + "\\" + url.Replace("/", "\\");
var success = false;

// Because PathSpecification.RelativeAsFolder creates an additional layer of directories,
// this trial & error code was implemented to ensure that images could be found
var count = 0;
while (count < 2)
{
//Create an image object from the uploaded file
try
{
var fileName = str + "\\";
var currentUrl = url;

//
for (int i = 0; i < count; i++)
{
currentUrl = "../" + currentUrl;
}

//Create an image object from the uploaded file
try
{
var img = System.Drawing.Image.FromFile(str);
width=img.Width;
height=img.Height;
fileName += currentUrl.Replace("/", "\\");

if (File.Exists(fileName))
{
var img = System.Drawing.Image.FromFile(fileName);
width = img.Width;
height = img.Height;
finalUrl = currentUrl;

if (MaxImageWidth != 0 && width > MaxImageWidth)
{
height = (int)((double)height * (double)MaxImageWidth / (double)width);
width = MaxImageWidth;
}

if (MaxImageWidth != 0 && width>MaxImageWidth)
success = true;
break;
}
}
catch (Exception)
{
height=(int)((double)height * (double)MaxImageWidth / (double)width);
width=MaxImageWidth;
}

return true;
}
catch (Exception)
{
return false;
count++;
}

return success;
}

@@ -386,9 +410,11 @@ namespace MarkdownDeep
}

// Try to determine width and height
var url = tag.attributes["src"];
int width, height;
if (OnGetImageSize(tag.attributes["src"], TitledImage, out width, out height))
if (OnGetImageSize(url, TitledImage, out width, out height, out url))
{
tag.attributes["src"] = url;
tag.attributes["width"] = width.ToString();
tag.attributes["height"] = height.ToString();
}


Loading…
Cancel
Save