diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITagHelper.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITagHelper.cs
index badb7227e0..1b716db0a1 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITagHelper.cs
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITagHelper.cs
@@ -1,12 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
+using System.Threading.Tasks;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
+ ///
+ /// Contract used to filter matching HTML elements.
+ ///
public interface ITagHelper
{
- // TODO: Will be implemented in https://github.com/aspnet/razor/issues/154
+ ///
+ /// Asynchronously executes the with the given and
+ /// .
+ ///
+ /// Contains information associated with the current HTML tag.
+ /// A stateful HTML element used to generate an HTML tag.
+ /// A that on completion updates the .
+ Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelper.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelper.cs
new file mode 100644
index 0000000000..91e5415f4c
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelper.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// Class used to filter matching HTML elements.
+ ///
+ public abstract class TagHelper : ITagHelper
+ {
+ ///
+ /// Synchronously executes the with the given and
+ /// .
+ ///
+ /// Contains information associated with the current HTML tag.
+ /// A stateful HTML element used to generate an HTML tag.
+ public virtual void Process(TagHelperContext context, TagHelperOutput output)
+ {
+ }
+
+ ///
+ /// Asynchronously executes the with the given and
+ /// .
+ ///
+ /// Contains information associated with the current HTML tag.
+ /// A stateful HTML element used to generate an HTML tag.
+ /// A that on completion updates the .
+ /// By default this calls into ..
+ #pragma warning disable 1998
+ public virtual async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
+ {
+ Process(context, output);
+ }
+ #pragma warning restore 1998
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs
new file mode 100644
index 0000000000..fc91494b38
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+
+namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// Contains information related to the execution of s.
+ ///
+ public class TagHelperContext
+ {
+ ///
+ /// Instantiates a new .
+ ///
+ /// Every attribute associated with the current HTML element.
+ public TagHelperContext([NotNull] IDictionary allAttributes)
+ {
+ AllAttributes = allAttributes;
+ }
+
+ ///
+ /// Every attribute associated with the current HTML element.
+ ///
+ public IDictionary AllAttributes { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs
new file mode 100644
index 0000000000..8a04c913be
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs
@@ -0,0 +1,159 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Text;
+
+namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// Class used to represent the output of an .
+ ///
+ public class TagHelperOutput
+ {
+ private string _content;
+ private string _tagName;
+
+ // Internal for testing
+ internal TagHelperOutput(string tagName)
+ {
+ TagName = tagName;
+ Attributes = new Dictionary(StringComparer.Ordinal);
+ }
+
+ // Internal for testing
+ internal TagHelperOutput(string tagName, [NotNull] IDictionary attributes)
+ : this(tagName, attributes, string.Empty)
+ {
+ }
+
+ ///
+ /// Instantiates a new instance of .
+ ///
+ /// The HTML element's tag name.
+ /// The HTML attributes.
+ /// The HTML element's content.
+ public TagHelperOutput(string tagName,
+ [NotNull] IDictionary attributes,
+ string content)
+ {
+ TagName = tagName;
+ Content = content;
+ Attributes = new Dictionary(attributes, StringComparer.Ordinal);
+ }
+
+ ///
+ /// The HTML element's tag name.
+ ///
+ ///
+ /// A whitespace value results in no start or end tag being rendered.
+ ///
+ public string TagName
+ {
+ get
+ {
+ return _tagName;
+ }
+ set
+ {
+ _tagName = value ?? string.Empty;
+ }
+ }
+
+ ///
+ /// The HTML element's content.
+ ///
+ public string Content
+ {
+ get
+ {
+ return _content;
+ }
+ set
+ {
+ _content = value ?? string.Empty;
+ }
+ }
+
+ ///
+ /// Indicates whether or not the tag is self closing.
+ ///
+ public bool SelfClosing { get; set; }
+
+ ///
+ /// The HTML element's attributes.
+ ///
+ public IDictionary Attributes { get; private set; }
+
+ ///
+ /// Generates the 's start tag.
+ ///
+ /// string.Empty if is string.Empty or whitespace. Otherwise, the
+ /// representation of the 's start tag.
+ public string GenerateStartTag()
+ {
+ // Only render a start tag if the tag name is not whitespace
+ if (string.IsNullOrWhiteSpace(TagName))
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder();
+
+ sb.Append('<')
+ .Append(TagName);
+
+ foreach (var attribute in Attributes)
+ {
+ var value = WebUtility.HtmlEncode(attribute.Value);
+ sb.Append(' ')
+ .Append(attribute.Key)
+ .Append("=\"")
+ .Append(value)
+ .Append('"');
+ }
+
+ if (SelfClosing)
+ {
+ sb.Append(" /");
+ }
+
+ sb.Append('>');
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Generates the 's body.
+ ///
+ /// string.Empty if is true. otherwise.
+ ///
+ public string GenerateContent()
+ {
+ if (SelfClosing)
+ {
+ return string.Empty;
+ }
+
+ return Content;
+ }
+
+ ///
+ /// Generates the 's end tag.
+ ///
+ /// string.Empty if is string.Empty or whitespace. Otherwise, the
+ /// representation of the 's end tag.
+ public string GenerateEndTag()
+ {
+ if (SelfClosing || string.IsNullOrWhiteSpace(TagName))
+ {
+ return string.Empty;
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0}>", TagName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs
new file mode 100644
index 0000000000..a6aa597529
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// A class used to run s.
+ ///
+ public class TagHelperRunner
+ {
+ ///
+ /// Calls the method on s.
+ ///
+ /// Contains information associated with running s.
+ /// Resulting from processing all of the
+ /// 's s.
+ public async Task RunAsync([NotNull] TagHelpersExecutionContext context)
+ {
+ return await RunAsyncCore(context, string.Empty);
+ }
+
+ ///
+ /// Calls the method on s.
+ ///
+ /// Contains information associated with running s.
+ /// Contains the buffered content of the current HTML tag.
+ /// Resulting from processing all of the
+ /// 's s.
+ public async Task RunAsync([NotNull] TagHelpersExecutionContext context,
+ [NotNull] TextWriter bufferedBody)
+ {
+ return await RunAsyncCore(context, bufferedBody.ToString());
+ }
+
+ private async Task RunAsyncCore(TagHelpersExecutionContext executionContext, string outputContent)
+ {
+ var tagHelperContext = new TagHelperContext(executionContext.AllAttributes);
+ var tagHelperOutput = new TagHelperOutput(executionContext.TagName,
+ executionContext.HTMLAttributes,
+ outputContent);
+
+ foreach (var tagHelper in executionContext.TagHelpers)
+ {
+ await tagHelper.ProcessAsync(tagHelperContext, tagHelperOutput);
+ }
+
+ return tagHelperOutput;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelpersExecutionContext.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelpersExecutionContext.cs
new file mode 100644
index 0000000000..dd58d2c723
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelpersExecutionContext.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// Class used to store information about a 's execution lifetime.
+ ///
+ public class TagHelpersExecutionContext
+ {
+ private readonly List _tagHelpers;
+
+ ///
+ /// Instantiates a new .
+ ///
+ /// The HTML tag name in the Razor source.
+ public TagHelpersExecutionContext([NotNull] string tagName)
+ {
+ AllAttributes = new Dictionary(StringComparer.Ordinal);
+ HTMLAttributes = new Dictionary(StringComparer.Ordinal);
+ _tagHelpers = new List();
+ TagName = tagName;
+ }
+
+ ///
+ /// HTML attributes.
+ ///
+ public IDictionary HTMLAttributes { get; private set; }
+
+ ///
+ /// bound attributes and HTML attributes.
+ ///
+ public IDictionary AllAttributes { get; private set; }
+
+ ///
+ /// s that should be run.
+ ///
+ public IEnumerable TagHelpers
+ {
+ get
+ {
+ return _tagHelpers;
+ }
+ }
+
+ ///
+ /// The HTML tag name in the Razor source.
+ ///
+ public string TagName { get; private set; }
+
+ ///
+ /// The s' output.
+ ///
+ public TagHelperOutput Output { get; set; }
+
+ ///
+ /// Tracks the given .
+ ///
+ /// The tag helper to track.
+ public void Add([NotNull] ITagHelper tagHelper)
+ {
+ _tagHelpers.Add(tagHelper);
+ }
+
+ ///
+ /// Tracks the HTML attribute in and .
+ ///
+ /// The HTML attribute name.
+ /// The HTML attribute value.
+ public void AddHtmlAttribute([NotNull] string name, string value)
+ {
+ HTMLAttributes.Add(name, value);
+ AllAttributes.Add(name, value);
+ }
+
+ ///
+ /// Tracks the bound attribute in .
+ ///
+ /// The bound attribute name.
+ /// The attribute value.
+ public void AddTagHelperAttribute([NotNull] string name, object value)
+ {
+ AllAttributes.Add(name, value);
+ }
+ }
+}
\ No newline at end of file