It's much much shorter and I think it's much easier to extend it now. Now it's also much easier to write another renderer that creates i.e. a XDocument/XElement.
public class MarkupBuilder : DynamicObject, IEnumerable<object>
{
private readonly List<IMarkupBuilderExtension> _extensions = new List<UserQuery.IMarkupBuilderExtension>();
private MarkupBuilder(MarkupBuilderSettingsMarkupBuilder settingsmarkupBuilder, string tag)
{
Settings_extensions = newmarkupBuilder._extensions;
MarkupBuilderSettings(settings ?? MarkupSchema = new MarkupBuilderSettingsMarkupSchema()markupBuilder.MarkupSchema);
Tag = tag;
Attributes = new List<MarkupAttribute>();
Content = new List<object>();
}
public MarkupBuilder(MarkupBuilderSettingsMarkupSchema settingsmarkupSchema = null)
:{
this MarkupSchema = new MarkupSchema(settings,markupSchema string.Empty?? new MarkupSchema());
{ Attributes = new List<MarkupAttribute>();
Content = new List<object>();
}
public MarkupBuilderSettingsIEnumerable<IMarkupBuilderExtension> SettingsExtensions => _extensions.AsReadOnly();
public MarkupSchema MarkupSchema { get; }
public string Tag { get; protected set; }
public List<MarkupAttribute> Attributes { get; private set; }
public List<object> Content { get; private set; }
public MarkupBuilder Parent { get; private set; }
private int Depth
{
get
{
var depth = 0;
var parent = Parent;
while (parent != null)
{
depth++;
parent = parent.Parent;
}
return depth;
}
}
public MarkupBuilder Register<T>() where T : IMarkupBuilderExtension, new()
{
_extensions.Add(new T());
return this;
}
// supports object initializer
public void Add(object content)
{
if (content != null)
{
Content.Add(content);
var htmlElement = content as MarkupBuilder;
if (htmlElement != null)
{
htmlElement.Parent = this;
}
}
}
public MarkupBuilder AddRange(params object[] content)
{
foreach (var item in content)
{
Add(item);
}
return this;
}
public IEnumerator<object> GetEnumerator()
{
return Content.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
// --- DynamicObject members
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach (var extension in Settings.Extensions)
{
if (extension.TryGetMember(this, binder, out result))
{
return true;
}
}
result = new MarkupBuilder(Settingsthis, binder.Name);
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
foreach (var extension in Settings.Extensions)
{
if (extension.TryInvokeMember(this, binder, args, out result))
{
return true;
}
}
if (SettingsMarkupSchema.AllowedTagsTags.Any() && !SettingsMarkupSchema.AllowedTagsTags.ContainsContainsKey(binder.Name))
{
result = null;
throw new NotSupportedException($"Method '{binder.Name}' is returnnot false;supported.");
}
result = new MarkupBuilder(Settingsthis, binder.Name).AddRange(args);
return true;
}
}
It's supported byIt can be customized with the MarkupBuilderSettingsMarkupSchema
a very simple schema that defines which tags/attributes are allowed and how to format the markup later:
public class MarkupBuilderSettingsMarkupSchema
{
public MarkupBuilderSettingsMarkupSchema() {
Tags = new Dictionary<string, MarkupFormattingOptions>();
GlobalAttributes = new HashSet<string>();
IndentWidth = 4;
}
publicinternal MarkupBuilderSettingsMarkupSchema(MarkupBuilderSettingsMarkupSchema settingsother)
{
AllowedTagsTags = settings.AllowedTags ?? new List<string>Dictionary<string, MarkupFormattingOptions>(other.Tags);
AllowerAttributesGlobalAttributes = settings.AllowerAttributes ?? new List<string>HashSet<string>(other.GlobalAttributes);
ExtensionsIndentWidth = settingsother.ExtensionsIndentWidth;
?? new List<IMarkupBuilderExtension>(); }
public Dictionary<string, MarkupFormattingOptions> Tags { get; set; }
public IReadOnlyCollection<string>HashSet<string> AllowedTagsGlobalAttributes { get; set; }
public IReadOnlyCollection<string>int AllowerAttributesIndentWidth { get; set; }
// Creates a Html schema.
public IReadOnlyCollection<IMarkupBuilderExtension>static ExtensionsMarkupSchema Html => new MarkupSchema
{ get; set; Tags =
{
["body"] = MarkupFormattingOptions.PlaceClosingTagOnNewLine,
["br"] = MarkupFormattingOptions.IsVoid,
["span"] = MarkupFormattingOptions.None,
["p"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h1"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h2"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h3"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h4"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h5"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h6"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["ul"] = MarkupFormattingOptions.PlaceBothTagsOnNewLine,
["ol"] = MarkupFormattingOptions.PlaceBothTagsOnNewLine,
["li"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
// ...
},
GlobalAttributes = { "style" }
};
public bool TagHasFormattingOptions(string tagName, MarkupFormattingOptions options)
{
var tagFormattingOptions = MarkupFormattingOptions.None;
return Tags.TryGetValue(tagName, out tagFormattingOptions) ? tagFormattingOptions.HasFlag(options) : false;
}
}
public class MarkupRenderer
{
public static string RenderMarkup(object value, IMarkupFormatting formatting)
{
var element = value as MarkupBuilder;
if (element == null)
{
return value == null ? string.Empty : (string)value;
}
var content = element.Content.Aggregate(
new StringBuilder(),
(builder, next) => builder.Append(MarkupRenderer.RenderMarkup(next, formatting))).ToString();
var isEmpty = string.IsNullOrEmpty(content);
var html = new StringBuilder();
if (element.Parent != null && formatting[element.Tag].HasFlag(MarkupFormattingOptions.PlaceOpeningTagOnNewLine))
{
html.AppendLine().Append(IndentString(formatting.IndentWidth));
}
html.Append(CreateOpeningElement(element, formatting));
// if (IsVoid)
// {
// return html.ToString();
// }
if (!isEmpty)
{
html.Append(content);
}
if (!isEmpty && formatting[element.Tag].HasFlag(MarkupFormattingOptions.PlaceClosingTagOnNewLine))
{
html.AppendLine();
if (element.Parent != null) { html.Append(IndentString(formatting.IndentWidth)); }
}
html.Append(CreateClosingElement(element, formatting));
return html.ToString();
}
private static string IndentString(int indentWidth)
{
return new string(' ', indentWidth);
}
private static string CreateOpeningElement(MarkupBuilder element, IMarkupFormatting formatting)
{
var attributes = CreateAttributesString(element, formatting);
var html = new StringBuilder()
.Append("<").Append(element.Tag)
.Append(string.IsNullOrEmpty(attributes) ? string.Empty : " ")
.Append(attributes)
//.Append(IsVoid ? "/" : string.Empty)
.Append(">")
.ToString();
return html;
}
private static string CreateAttributesString(MarkupBuilder element, IMarkupFormatting formatting)
{
return string.Join(" ", element.Attributes);
}
private static string CreateClosingElement(MarkupBuilder element, IMarkupFormatting formatting)
{
return formatting[element.Tag].HasFlag(MarkupFormattingOptions.IsVoid)
? string.Empty
: new StringBuilder()
.Append("</")
.Append(element.Tag)
.Append(">")
.ToString();
}
}
The rendering can be customized with the IMarkupFormatting
interface:
public interface IMarkupFormatting
{
MarkupFormattingOptions this[string tag] { get; }
int IndentWidth { get; }
}
For html it looks like this:
public class HtmlFormattingMarkupRenderer
{
: IMarkupFormatting public static string RenderMarkup(MarkupBuilder builder)
{
public HtmlFormatting return RenderMarkup(builder, builder.MarkupSchema);
}
private static string RenderMarkup(object value, MarkupSchema markupSchema)
{
Optionsvar markupBuilder = newvalue Dictionary<string,as MarkupFormattingOptions>MarkupBuilder;
if (markupBuilder == null)
{
["body"]return =value MarkupFormattingOptions== null ? string.PlaceClosingTagOnNewLine,Empty : (string)value;
}
["br"] var content = MarkupFormattingOptionsmarkupBuilder.IsVoidContent.Aggregate(
new StringBuilder(),
["span"] = MarkupFormattingOptions (sb, next) => sb.NoneAppend(MarkupRenderer.RenderMarkup(next, markupSchema))).ToString();
var isEmpty = string.IsNullOrEmpty(content);
["p"] var html = new StringBuilder();
if (markupBuilder.Parent != null && markupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.PlaceOpeningTagOnNewLine,))
{
["h1"] = MarkupFormattingOptions html.PlaceOpeningTagOnNewLine,AppendLine().Append(IndentString(markupSchema.IndentWidth));
}
html.Append(CreateOpeningElement(markupBuilder));
IndentWidth// = 3; if (IsVoid)
// {
// return html.ToString();
// }
public MarkupFormattingOptions this[string tag] if (!isEmpty)
{
get html.Append(content);
}
if (!isEmpty && markupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.PlaceClosingTagOnNewLine))
{
varhtml.AppendLine();
options if (markupBuilder.Parent != MarkupFormattingOptionsnull) { html.None;Append(IndentString(markupSchema.IndentWidth)); }
}
html.Append(CreateClosingElement(markupBuilder));
return Optionshtml.TryGetValueToString(tag);
}
private static string IndentString(int indentWidth)
{
return new string(' ', outindentWidth);
options }
private static string CreateOpeningElement(MarkupBuilder markupBuilder) ? options : MarkupFormattingOptions{
var attributes = CreateAttributesString(markupBuilder);
var html = new StringBuilder()
.None;Append("<").Append(markupBuilder.Tag)
} .Append(string.IsNullOrEmpty(attributes) ? string.Empty : " ")
set .Append(attributes)
{ //.Append(IsVoid ? "/" : string.Empty)
Options[tag].Append(">")
= value; .ToString();
}return html;
}
publicprivate intstatic IndentWidthstring CreateAttributesString(MarkupBuilder markupBuilder)
{ get; set; return string.Join(" ", markupBuilder.Attributes);
}
publicprivate Dictionary<string,static MarkupFormattingOptions>string OptionsCreateClosingElement(MarkupBuilder markupBuilder)
{ get; set; return markupBuilder.MarkupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.IsVoid)
? string.Empty
: new StringBuilder()
.Append("</")
.Append(markupBuilder.Tag)
.Append(">")
.ToString();
}
}
dynamic html = new MarkupBuilder(new MarkupBuilderSettings
{
// if none are specified then all are allowed
AllowedTags = new[] { "body", "p", "br", "span" },
AllowerAttributes = new[] { "style" },
Extensions = new IMarkupBuilderExtension[]
{MarkupBuilder(MarkupSchema.Html)
new cssExtension.Register<cssExtension>(),
new attrExtension.Register<attrExtension>()
}
});
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
var reslt = MarkupRenderer.RenderMarkup(body as MarkupBuilder, new HtmlFormatting().Dump();
I think now it should be possible to create a different renderer for xml or even XDocument/XElement.
It's much much shorter and I think it's much easier to extend it now.
public class MarkupBuilder : DynamicObject, IEnumerable<object>
{
private MarkupBuilder(MarkupBuilderSettings settings, string tag)
{
Settings = new MarkupBuilderSettings(settings ?? new MarkupBuilderSettings());
Tag = tag;
Attributes = new List<MarkupAttribute>();
Content = new List<object>();
}
public MarkupBuilder(MarkupBuilderSettings settings = null)
: this(settings, string.Empty)
{
}
public MarkupBuilderSettings Settings { get; }
public string Tag { get; protected set; }
public List<MarkupAttribute> Attributes { get; private set; }
public List<object> Content { get; private set; }
public MarkupBuilder Parent { get; set; }
private int Depth
{
get
{
var depth = 0;
var parent = Parent;
while (parent != null)
{
depth++;
parent = parent.Parent;
}
return depth;
}
}
// supports object initializer
public void Add(object content)
{
if (content != null)
{
Content.Add(content);
var htmlElement = content as MarkupBuilder;
if (htmlElement != null)
{
htmlElement.Parent = this;
}
}
}
public MarkupBuilder AddRange(params object[] content)
{
foreach (var item in content)
{
Add(item);
}
return this;
}
public IEnumerator<object> GetEnumerator()
{
return Content.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
// --- DynamicObject members
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach (var extension in Settings.Extensions)
{
if (extension.TryGetMember(this, binder, out result))
{
return true;
}
}
result = new MarkupBuilder(Settings, binder.Name);
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
foreach (var extension in Settings.Extensions)
{
if (extension.TryInvokeMember(this, binder, args, out result))
{
return true;
}
}
if (Settings.AllowedTags.Any() && !Settings.AllowedTags.Contains(binder.Name))
{
result = null;
return false;
}
result = new MarkupBuilder(Settings, binder.Name).AddRange(args);
return true;
}
}
It's supported by MarkupBuilderSettings
:
public class MarkupBuilderSettings
{
public MarkupBuilderSettings() { }
public MarkupBuilderSettings(MarkupBuilderSettings settings)
{
AllowedTags = settings.AllowedTags ?? new List<string>();
AllowerAttributes = settings.AllowerAttributes ?? new List<string>();
Extensions = settings.Extensions ?? new List<IMarkupBuilderExtension>();
}
public IReadOnlyCollection<string> AllowedTags { get; set; }
public IReadOnlyCollection<string> AllowerAttributes { get; set; }
public IReadOnlyCollection<IMarkupBuilderExtension> Extensions { get; set; }
}
public class MarkupRenderer
{
public static string RenderMarkup(object value, IMarkupFormatting formatting)
{
var element = value as MarkupBuilder;
if (element == null)
{
return value == null ? string.Empty : (string)value;
}
var content = element.Content.Aggregate(
new StringBuilder(),
(builder, next) => builder.Append(MarkupRenderer.RenderMarkup(next, formatting))).ToString();
var isEmpty = string.IsNullOrEmpty(content);
var html = new StringBuilder();
if (element.Parent != null && formatting[element.Tag].HasFlag(MarkupFormattingOptions.PlaceOpeningTagOnNewLine))
{
html.AppendLine().Append(IndentString(formatting.IndentWidth));
}
html.Append(CreateOpeningElement(element, formatting));
// if (IsVoid)
// {
// return html.ToString();
// }
if (!isEmpty)
{
html.Append(content);
}
if (!isEmpty && formatting[element.Tag].HasFlag(MarkupFormattingOptions.PlaceClosingTagOnNewLine))
{
html.AppendLine();
if (element.Parent != null) { html.Append(IndentString(formatting.IndentWidth)); }
}
html.Append(CreateClosingElement(element, formatting));
return html.ToString();
}
private static string IndentString(int indentWidth)
{
return new string(' ', indentWidth);
}
private static string CreateOpeningElement(MarkupBuilder element, IMarkupFormatting formatting)
{
var attributes = CreateAttributesString(element, formatting);
var html = new StringBuilder()
.Append("<").Append(element.Tag)
.Append(string.IsNullOrEmpty(attributes) ? string.Empty : " ")
.Append(attributes)
//.Append(IsVoid ? "/" : string.Empty)
.Append(">")
.ToString();
return html;
}
private static string CreateAttributesString(MarkupBuilder element, IMarkupFormatting formatting)
{
return string.Join(" ", element.Attributes);
}
private static string CreateClosingElement(MarkupBuilder element, IMarkupFormatting formatting)
{
return formatting[element.Tag].HasFlag(MarkupFormattingOptions.IsVoid)
? string.Empty
: new StringBuilder()
.Append("</")
.Append(element.Tag)
.Append(">")
.ToString();
}
}
The rendering can be customized with the IMarkupFormatting
interface:
public interface IMarkupFormatting
{
MarkupFormattingOptions this[string tag] { get; }
int IndentWidth { get; }
}
For html it looks like this:
public class HtmlFormatting : IMarkupFormatting
{
public HtmlFormatting()
{
Options = new Dictionary<string, MarkupFormattingOptions>
{
["body"] = MarkupFormattingOptions.PlaceClosingTagOnNewLine,
["br"] = MarkupFormattingOptions.IsVoid,
["span"] = MarkupFormattingOptions.None,
["p"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h1"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
};
IndentWidth = 3;
}
public MarkupFormattingOptions this[string tag]
{
get
{
var options = MarkupFormattingOptions.None;
return Options.TryGetValue(tag, out options) ? options : MarkupFormattingOptions.None;
}
set
{
Options[tag] = value;
}
}
public int IndentWidth { get; set; }
public Dictionary<string, MarkupFormattingOptions> Options { get; set; }
}
dynamic html = new MarkupBuilder(new MarkupBuilderSettings
{
// if none are specified then all are allowed
AllowedTags = new[] { "body", "p", "br", "span" },
AllowerAttributes = new[] { "style" },
Extensions = new IMarkupBuilderExtension[]
{
new cssExtension(),
new attrExtension()
}
});
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
var reslt = MarkupRenderer.RenderMarkup(body as MarkupBuilder, new HtmlFormatting());
I think now it should be possible to create a different renderer for xml or even XDocument/XElement.
It's much much shorter and I think it's much easier to extend it now. Now it's also much easier to write another renderer that creates i.e. a XDocument/XElement.
public class MarkupBuilder : DynamicObject, IEnumerable<object>
{
private readonly List<IMarkupBuilderExtension> _extensions = new List<UserQuery.IMarkupBuilderExtension>();
private MarkupBuilder(MarkupBuilder markupBuilder, string tag)
{
_extensions = markupBuilder._extensions;
MarkupSchema = new MarkupSchema(markupBuilder.MarkupSchema);
Tag = tag;
Attributes = new List<MarkupAttribute>();
Content = new List<object>();
}
public MarkupBuilder(MarkupSchema markupSchema = null)
{
MarkupSchema = new MarkupSchema(markupSchema ?? new MarkupSchema());
Attributes = new List<MarkupAttribute>();
Content = new List<object>();
}
public IEnumerable<IMarkupBuilderExtension> Extensions => _extensions.AsReadOnly();
public MarkupSchema MarkupSchema { get; }
public string Tag { get; }
public List<MarkupAttribute> Attributes { get; }
public List<object> Content { get; }
public MarkupBuilder Parent { get; private set; }
private int Depth
{
get
{
var depth = 0;
var parent = Parent;
while (parent != null)
{
depth++;
parent = parent.Parent;
}
return depth;
}
}
public MarkupBuilder Register<T>() where T : IMarkupBuilderExtension, new()
{
_extensions.Add(new T());
return this;
}
// supports object initializer
public void Add(object content)
{
if (content != null)
{
Content.Add(content);
var htmlElement = content as MarkupBuilder;
if (htmlElement != null)
{
htmlElement.Parent = this;
}
}
}
public MarkupBuilder AddRange(params object[] content)
{
foreach (var item in content)
{
Add(item);
}
return this;
}
public IEnumerator<object> GetEnumerator()
{
return Content.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
// --- DynamicObject members
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach (var extension in Extensions)
{
if (extension.TryGetMember(this, binder, out result))
{
return true;
}
}
result = new MarkupBuilder(this, binder.Name);
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
foreach (var extension in Extensions)
{
if (extension.TryInvokeMember(this, binder, args, out result))
{
return true;
}
}
if (MarkupSchema.Tags.Any() && !MarkupSchema.Tags.ContainsKey(binder.Name))
{
throw new NotSupportedException($"Method '{binder.Name}' is not supported.");
}
result = new MarkupBuilder(this, binder.Name).AddRange(args);
return true;
}
}
It can be customized with the MarkupSchema
a very simple schema that defines which tags/attributes are allowed and how to format the markup later:
public class MarkupSchema
{
public MarkupSchema() {
Tags = new Dictionary<string, MarkupFormattingOptions>();
GlobalAttributes = new HashSet<string>();
IndentWidth = 4;
}
internal MarkupSchema(MarkupSchema other)
{
Tags = new Dictionary<string, MarkupFormattingOptions>(other.Tags);
GlobalAttributes = new HashSet<string>(other.GlobalAttributes);
IndentWidth = other.IndentWidth;
}
public Dictionary<string, MarkupFormattingOptions> Tags { get; set; }
public HashSet<string> GlobalAttributes { get; set; }
public int IndentWidth { get; set; }
// Creates a Html schema.
public static MarkupSchema Html => new MarkupSchema
{ Tags =
{
["body"] = MarkupFormattingOptions.PlaceClosingTagOnNewLine,
["br"] = MarkupFormattingOptions.IsVoid,
["span"] = MarkupFormattingOptions.None,
["p"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h1"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h2"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h3"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h4"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h5"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["h6"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
["ul"] = MarkupFormattingOptions.PlaceBothTagsOnNewLine,
["ol"] = MarkupFormattingOptions.PlaceBothTagsOnNewLine,
["li"] = MarkupFormattingOptions.PlaceOpeningTagOnNewLine,
// ...
},
GlobalAttributes = { "style" }
};
public bool TagHasFormattingOptions(string tagName, MarkupFormattingOptions options)
{
var tagFormattingOptions = MarkupFormattingOptions.None;
return Tags.TryGetValue(tagName, out tagFormattingOptions) ? tagFormattingOptions.HasFlag(options) : false;
}
}
public class MarkupRenderer
{
public static string RenderMarkup(MarkupBuilder builder)
{
return RenderMarkup(builder, builder.MarkupSchema);
}
private static string RenderMarkup(object value, MarkupSchema markupSchema)
{
var markupBuilder = value as MarkupBuilder;
if (markupBuilder == null)
{
return value == null ? string.Empty : (string)value;
}
var content = markupBuilder.Content.Aggregate(
new StringBuilder(),
(sb, next) => sb.Append(MarkupRenderer.RenderMarkup(next, markupSchema))).ToString();
var isEmpty = string.IsNullOrEmpty(content);
var html = new StringBuilder();
if (markupBuilder.Parent != null && markupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.PlaceOpeningTagOnNewLine))
{
html.AppendLine().Append(IndentString(markupSchema.IndentWidth));
}
html.Append(CreateOpeningElement(markupBuilder));
// if (IsVoid)
// {
// return html.ToString();
// }
if (!isEmpty)
{
html.Append(content);
}
if (!isEmpty && markupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.PlaceClosingTagOnNewLine))
{
html.AppendLine();
if (markupBuilder.Parent != null) { html.Append(IndentString(markupSchema.IndentWidth)); }
}
html.Append(CreateClosingElement(markupBuilder));
return html.ToString();
}
private static string IndentString(int indentWidth)
{
return new string(' ', indentWidth);
}
private static string CreateOpeningElement(MarkupBuilder markupBuilder) {
var attributes = CreateAttributesString(markupBuilder);
var html = new StringBuilder()
.Append("<").Append(markupBuilder.Tag)
.Append(string.IsNullOrEmpty(attributes) ? string.Empty : " ")
.Append(attributes)
//.Append(IsVoid ? "/" : string.Empty)
.Append(">")
.ToString();
return html;
}
private static string CreateAttributesString(MarkupBuilder markupBuilder)
{ return string.Join(" ", markupBuilder.Attributes);
}
private static string CreateClosingElement(MarkupBuilder markupBuilder)
{ return markupBuilder.MarkupSchema.TagHasFormattingOptions(markupBuilder.Tag, MarkupFormattingOptions.IsVoid)
? string.Empty
: new StringBuilder()
.Append("</")
.Append(markupBuilder.Tag)
.Append(">")
.ToString();
}
}
dynamic html =
new MarkupBuilder(MarkupSchema.Html)
.Register<cssExtension>()
.Register<attrExtension>();
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
MarkupRenderer.RenderMarkup(body as MarkupBuilder).Dump();
dynamic html = new MarkupBuilder(new MarkupBuilderSettings
{
// if none are specified then all are allowed
AllowedTags = new[] { "body", "p", "br", "span" },
AllowerAttributes = new[] { "style" },
Extensions = new IMarkupBuilderExtension[]
{
new cssExtension(),
new attrExtension()
}
});
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
var reslt = MarkupRenderer.RenderMarkup(body as MarkupBuilder, new HtmlFormatting());
I think now it should be possible to create a different renderer for xml or even XDocument/XElement.
dynamic html = new MarkupBuilder(new MarkupBuilderSettings
{
// if none are specified then all are allowed
AllowedTags = new[] { "body", "p", "br", "span" },
AllowerAttributes = new[] { "style" },
Extensions = new IMarkupBuilderExtension[]
{
new cssExtension(),
new attrExtension()
}
});
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
var reslt = MarkupRenderer.RenderMarkup(body as MarkupBuilder, new HtmlFormatting());
dynamic html = new MarkupBuilder(new MarkupBuilderSettings
{
// if none are specified then all are allowed
AllowedTags = new[] { "body", "p", "br", "span" },
AllowerAttributes = new[] { "style" },
Extensions = new IMarkupBuilderExtension[]
{
new cssExtension(),
new attrExtension()
}
});
var body = html.body
(
html.p("foo"),
html.p(
"bar",
html.span("quux").css("blah"),
html.br,
"baz"
)
);
var reslt = MarkupRenderer.RenderMarkup(body as MarkupBuilder, new HtmlFormatting());
I think now it should be possible to create a different renderer for xml or even XDocument/XElement.