|
|
|
|
'**************************************************
|
|
|
|
|
' FILE : BBCodeElement.vb
|
|
|
|
|
' AUTHOR : Paulo Santos
|
|
|
|
|
' CREATION : 4/29/2009 11:33:36 AM
|
|
|
|
|
' COPYRIGHT : Copyright <EFBFBD> 2009
|
|
|
|
|
' PJ on Development
|
|
|
|
|
' All Rights Reserved.
|
|
|
|
|
'
|
|
|
|
|
' Description:
|
|
|
|
|
' TODO: Add file description
|
|
|
|
|
'
|
|
|
|
|
' Change log:
|
|
|
|
|
' 0.1 4/29/2009 11:33:36 AM
|
|
|
|
|
' Paulo Santos
|
|
|
|
|
' Created.
|
|
|
|
|
'***************************************************
|
|
|
|
|
|
|
|
|
|
Imports System.Globalization
|
|
|
|
|
Imports System.Text.RegularExpressions
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Represents an BBCode element.
|
|
|
|
|
''' </summary>
|
|
|
|
|
Public Class BBCodeElement
|
|
|
|
|
Inherits BBCodeNode
|
|
|
|
|
|
|
|
|
|
Private __Name As String
|
|
|
|
|
Private __Nodes As BBCodeNodeCollection
|
|
|
|
|
Private __Attributes As BBCodeAttributeDictionary
|
|
|
|
|
Private __ReplacementFormat As String = String.Empty
|
|
|
|
|
Private __RequireClosingTag As TriState = TriState.UseDefault
|
|
|
|
|
Private __IsValueFormatted As Boolean
|
|
|
|
|
|
|
|
|
|
Private Shared ReadOnly __RxAttribute As New Regex("\{(?<name>[_a-zA-Z0-9].*?)\}")
|
|
|
|
|
|
|
|
|
|
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.
|
|
|
|
|
''' This is the default constructor for this class.</summary>
|
|
|
|
|
Friend Sub New()
|
|
|
|
|
End Sub
|
|
|
|
|
|
|
|
|
|
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.</summary>
|
|
|
|
|
''' <param name="name">The name of the element.</param>
|
|
|
|
|
Protected Sub New(ByVal name As String)
|
|
|
|
|
__Name = name.ToUpperInvariant()
|
|
|
|
|
End Sub
|
|
|
|
|
|
|
|
|
|
''' <summary>Initializes an instance of the <see cref="BBCodeElement" /> class.</summary>
|
|
|
|
|
''' <param name="parser">The parser used to create this element.</param>
|
|
|
|
|
Friend Sub New(ByVal parser As BBCodeParser)
|
|
|
|
|
MyBase.New(parser)
|
|
|
|
|
End Sub
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets the name of the element.
|
|
|
|
|
''' </summary>
|
|
|
|
|
Public ReadOnly Property Name() As String
|
|
|
|
|
Get
|
|
|
|
|
Return __Name
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets the replacement format for this element.
|
|
|
|
|
''' </summary>
|
|
|
|
|
''' <value>The replacement format.</value>
|
|
|
|
|
''' <remarks>
|
|
|
|
|
''' <para>In order to use the any parameter in the replacement format use the following syntax: {paramName}.</para>
|
|
|
|
|
''' <para/>
|
|
|
|
|
''' <para>The parameter names are case insensitive.</para>
|
|
|
|
|
''' <para/>
|
|
|
|
|
''' <para>There are two reserved parameter keywords for formatting: DEFAULT and VALUE.</para>
|
|
|
|
|
''' <para><c>DEFAULT</c> : The text following the first equal sign after the name of the BBCode element.</para>
|
|
|
|
|
''' <para><c>VALUE</c>: The HTML generated by the BBCode between the start and end element tag.</para>
|
|
|
|
|
''' <para><example>[url=http://tempuri.org]text[/url]</example></para>
|
|
|
|
|
''' <para>In the example above the parameter DEFAULT would have the value "http://tempuri.org", while VALUE would be "text".</para>
|
|
|
|
|
''' <para/>
|
|
|
|
|
''' <para>To replace [url=http://tempuri.org]text[/url] the with <a href="http://tempuri.org">text</a> the following ReplacementFormat should be used:</para>
|
|
|
|
|
''' <example>
|
|
|
|
|
''' <a href="{default|value}">{value|default}</a>
|
|
|
|
|
''' </example>
|
|
|
|
|
''' <para>The example above, will set the href attribute with either the default value or the text inside the [url] element. The pipe (|) implies in finding the first non-empty attribute.</para>
|
|
|
|
|
''' </remarks>
|
|
|
|
|
Public ReadOnly Property ReplacementFormat() As String
|
|
|
|
|
Get
|
|
|
|
|
If (String.IsNullOrEmpty(__ReplacementFormat)) Then
|
|
|
|
|
If (Me.Parser.Dictionary.ContainsKey(Me.Name)) Then
|
|
|
|
|
__ReplacementFormat = Me.Parser.Dictionary(Me.Name).ReplacementFormat
|
|
|
|
|
End If
|
|
|
|
|
End If
|
|
|
|
|
Return __ReplacementFormat
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets a value indicating if the element requires a closing tag.
|
|
|
|
|
''' </summary>
|
|
|
|
|
''' <value><c>True</c> if the element requires a closing tag; otherwise <c>False</c>.</value>
|
|
|
|
|
Public ReadOnly Property RequireClosingTag() As Boolean
|
|
|
|
|
Get
|
|
|
|
|
If (__RequireClosingTag = TriState.UseDefault) Then
|
|
|
|
|
If (Not String.IsNullOrEmpty(Me.Name) AndAlso Me.Parser.Dictionary.ContainsKey(Me.Name)) Then
|
|
|
|
|
__RequireClosingTag = If(Me.Parser.Dictionary(Me.Name).RequireClosingTag, TriState.True, TriState.False)
|
|
|
|
|
ElseIf (Not String.IsNullOrEmpty(Me.Name) AndAlso Me.Parser.ElementTypes.ContainsKey(Me.Name)) Then
|
|
|
|
|
__RequireClosingTag = If(Me.Parser.ElementTypes(Me.Name).RequireClosingTag, TriState.True, TriState.False)
|
|
|
|
|
End If
|
|
|
|
|
End If
|
|
|
|
|
Return (__RequireClosingTag = TriState.True)
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets the list of attributes.
|
|
|
|
|
''' </summary>
|
|
|
|
|
Public ReadOnly Property Attributes() As BBCodeAttributeDictionary
|
|
|
|
|
Get
|
|
|
|
|
If (__Attributes Is Nothing) Then
|
|
|
|
|
__Attributes = New BBCodeAttributeDictionary
|
|
|
|
|
End If
|
|
|
|
|
Return __Attributes
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets the list of sub nodes.
|
|
|
|
|
''' </summary>
|
|
|
|
|
Public ReadOnly Property Nodes() As BBCodeNodeCollection
|
|
|
|
|
Get
|
|
|
|
|
If (__Nodes Is Nothing) Then
|
|
|
|
|
__Nodes = New BBCodeNodeCollection(Me)
|
|
|
|
|
End If
|
|
|
|
|
Return __Nodes
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>Transforms this instance of <see cref="BBCodeElement" /> into its desired text representation.</summary>
|
|
|
|
|
''' <param name="formatter">An object that implements the <see cref="ITextFormatter" /> interface.</param>
|
|
|
|
|
''' <returns>The text formatted by the <see cref="ITextFormatter" />.</returns>
|
|
|
|
|
Public Overrides Function Format(ByVal formatter As ITextFormatter) As String
|
|
|
|
|
IsValueFormatted = False
|
|
|
|
|
Dim sb As New Text.StringBuilder(Me.ReplacementFormat)
|
|
|
|
|
Dim attribs = __RxAttribute.Matches(Me.ReplacementFormat)
|
|
|
|
|
For Each attrib As Match In attribs
|
|
|
|
|
sb.Replace(attrib.Value, GetAttribute(attrib.Groups("name").Value, formatter))
|
|
|
|
|
Next
|
|
|
|
|
If Not IsValueFormatted Then
|
|
|
|
|
sb.Append(GetAttribute("value", formatter))
|
|
|
|
|
End If
|
|
|
|
|
Return sb.ToString()
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
''' <summary>Gets or sets the inner </summary>
|
|
|
|
|
''' <value>The BBCode between the start and end tags.</value>
|
|
|
|
|
Public NotOverridable Overrides Property InnerBBCode() As String
|
|
|
|
|
Get
|
|
|
|
|
Dim sb As New System.Text.StringBuilder()
|
|
|
|
|
For Each n As BBCodeNode In Nodes
|
|
|
|
|
sb.Append(n.OuterBBCode)
|
|
|
|
|
Next
|
|
|
|
|
Return sb.ToString()
|
|
|
|
|
End Get
|
|
|
|
|
Set(ByVal value As String)
|
|
|
|
|
If Not RequireClosingTag Then
|
|
|
|
|
Throw New InvalidOperationException("The InnerBBCode property cannot be set for elements that does not require closing tags.")
|
|
|
|
|
End If
|
|
|
|
|
Me.Nodes.Clear()
|
|
|
|
|
Me.Nodes.AddRange(Me.Parser.Parse(value).Nodes)
|
|
|
|
|
End Set
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>Gets or sets the outer </summary>
|
|
|
|
|
''' <value>The BBCode of this instance of the <see cref="BBCodeNode" /> .</value>
|
|
|
|
|
Public NotOverridable Overrides ReadOnly Property OuterBBCode() As String
|
|
|
|
|
Get
|
|
|
|
|
Dim sb As New Text.StringBuilder()
|
|
|
|
|
sb.Append("[")
|
|
|
|
|
sb.Append(Me.Name)
|
|
|
|
|
If ((Me.Attributes.Count = 1) AndAlso Me.Attributes.ContainsKey("default")) Then
|
|
|
|
|
sb.AppendFormat(CultureInfo.InvariantCulture, "={0}", Quote(Me.Attributes("default")))
|
|
|
|
|
Else
|
|
|
|
|
For Each key In Me.Attributes.Keys
|
|
|
|
|
sb.AppendFormat(CultureInfo.InvariantCulture, " {0}={1}", key, Quote(Me.Attributes(key)))
|
|
|
|
|
Next
|
|
|
|
|
End If
|
|
|
|
|
If (Me.RequireClosingTag) Then
|
|
|
|
|
sb.Append("]")
|
|
|
|
|
sb.Append(Me.InnerBBCode)
|
|
|
|
|
sb.Append("[/")
|
|
|
|
|
sb.Append(Me.Name)
|
|
|
|
|
End If
|
|
|
|
|
sb.Append("]")
|
|
|
|
|
Return sb.ToString()
|
|
|
|
|
End Get
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>Gets or sets the plain text of the node.</summary>
|
|
|
|
|
''' <value>The plain text between the start and end tags.</value>
|
|
|
|
|
Public NotOverridable Overrides Property InnerText() As String
|
|
|
|
|
Get
|
|
|
|
|
Dim sb As New Text.StringBuilder()
|
|
|
|
|
For Each n In Me.Nodes
|
|
|
|
|
sb.Append(n.InnerText)
|
|
|
|
|
Next
|
|
|
|
|
Return sb.ToString()
|
|
|
|
|
End Get
|
|
|
|
|
Set(ByVal value As String)
|
|
|
|
|
If (Not RequireClosingTag) Then
|
|
|
|
|
Throw New InvalidOperationException("The InnerText property cannot be set for elements that does not require closing tags.")
|
|
|
|
|
End If
|
|
|
|
|
|
|
|
|
|
'*
|
|
|
|
|
'* Removes all nodex from the element
|
|
|
|
|
'*
|
|
|
|
|
Me.Nodes.Clear()
|
|
|
|
|
If (Not String.IsNullOrEmpty(value)) Then
|
|
|
|
|
'*
|
|
|
|
|
'* Only append a BBCodeText element if the value is not empty
|
|
|
|
|
'*
|
|
|
|
|
Me.Nodes.Add(New BBCodeText(value))
|
|
|
|
|
End If
|
|
|
|
|
End Set
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Sets the element name of this instance.
|
|
|
|
|
''' </summary>
|
|
|
|
|
''' <param name="name">The new element name of this instance.</param>
|
|
|
|
|
Friend Sub SetName(ByVal name As String)
|
|
|
|
|
__Name = name
|
|
|
|
|
End Sub
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
''' Gets a value indicating wheter or not the <see cref="BBCodeElement.Nodes"/> have been formatted.
|
|
|
|
|
''' </summary>
|
|
|
|
|
''' <returns><c>True</c> if the values have been formatted; otherwise <c>False</c>.</returns>
|
|
|
|
|
Private Property IsValueFormatted() As Boolean
|
|
|
|
|
Get
|
|
|
|
|
Return __IsValueFormatted
|
|
|
|
|
End Get
|
|
|
|
|
Set(ByVal value As Boolean)
|
|
|
|
|
__IsValueFormatted = value
|
|
|
|
|
End Set
|
|
|
|
|
End Property
|
|
|
|
|
|
|
|
|
|
Private Function GetAttribute(ByVal name As String, ByVal formatter As ITextFormatter) As String
|
|
|
|
|
Dim attribs = name.ToUpperInvariant().Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
|
For Each attrib In attribs
|
|
|
|
|
If (String.CompareOrdinal(attrib, "VALUE") = 0) Then
|
|
|
|
|
IsValueFormatted = True
|
|
|
|
|
Dim sb As New Text.StringBuilder()
|
|
|
|
|
For Each node In Me.Nodes
|
|
|
|
|
sb.Append(node.Format(formatter))
|
|
|
|
|
Next
|
|
|
|
|
Return sb.ToString()
|
|
|
|
|
ElseIf (Me.Attributes.ContainsKey(attrib)) Then
|
|
|
|
|
Return (Me.Attributes.Item(attrib))
|
|
|
|
|
End If
|
|
|
|
|
Next
|
|
|
|
|
Return String.Empty
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
End Class
|