/// <summary>
        /// Causes the <see cref="ITagDefinition"/> to render all of its <see cref="ITagInstance"/>s into XHTML.
        /// </summary>
        /// <remarks>
        /// This method is always called after <see cref="ITagDefinition.RemoveWhitespace"/> is called on any
        /// <see cref="ITagDefinition"/>. The order in which this method is called across the different
        /// <see cref="ITagDefinition"/>s is undefined.
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IRenderContext"/> contains all the <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the rendering of <see cref="ITagInstance"/>s.
        /// </param>
        public override void Render(IRenderContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <IOpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                string url = context.GetRenderString(openTag, closeTag);

                //If the url isn't a real URL, don't create a tag and skip it instead
                Match urlMatch = RegularExpressions.Url.Match(url);
                if (urlMatch.Success == false || urlMatch.Index != 0 || urlMatch.Length != url.Length)
                {
                    continue;
                }

                string tag;
                int    width  = (int)openTag.Attributes["Width"];
                int    height = (int)openTag.Attributes["Height"];
                tag = String.Format(REPLACEMENT_TAG_FORMAT_STRING, url, width, height);

                context.RenderTag(openTag, tag, true);

                //Remove the containing text and the end tag as the open tag is the only tag now
                int indexAfterOpenTag = openTag.CharRange.StartAt + openTag.CharRange.Length;
                context.RenderNonTagRange(indexAfterOpenTag, closeTag.CharRange.StartAt - indexAfterOpenTag, String.Empty, false);
                context.RenderTag(closeTag, String.Empty, false);
            }

            context.RegisterRenderCacheability(true);
        }
        /// <summary>
        /// Causes the <see cref="ITagDefinition"/> to render all of its <see cref="ITagInstance"/>s into XHTML.
        /// </summary>
        /// <remarks>
        /// This method is always called after <see cref="ITagDefinition.RemoveWhitespace"/> is called on any
        /// <see cref="ITagDefinition"/>. The order in which this method is called across the different
        /// <see cref="ITagDefinition"/>s is undefined.
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IRenderContext"/> contains all the <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the rendering of <see cref="ITagInstance"/>s.
        /// </param>
        public override void Render(IRenderContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <IOpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                string text = context.GetRenderString(openTag, closeTag);
                string href = openTag.Attributes.ContainsKey("URL") ? (string)openTag.Attributes["URL"] : text;

                //If the href isn't a real URL, don't create a tag and skip it instead
                Match urlMatch = RegularExpressions.Url.Match(href);
                if (urlMatch.Success == false || urlMatch.Index != 0 || urlMatch.Length != href.Length)
                {
                    continue;
                }

                string tag = String.Format(REPLACEMENT_OPEN_TAG_FORMAT_STRING, WebUtility.HtmlEncode(href));
                context.RenderTag(openTag, tag, true);

                context.RenderTag(closeTag, REPLACEMENT_CLOSE_TAG, true);
            }

            context.RegisterRenderCacheability(true);
        }
Пример #3
0
        /// <summary>
        /// Causes the <see cref="ITagDefinition"/> to remove any whitespace it sees fit that is around its
        /// identified <see cref="ITagInstance"/>s. An <see cref="ITagDefinition"/> does not need to remove
        /// any whitespace, if it doesn't want to.
        /// </summary>
        /// <remarks>
        /// This method is always called before <see cref="ITagDefinition.Render"/> is called on any <see cref="ITagDefinition"/>.
        /// The order in which this method is called across the different <see cref="ITagDefinition"/>s is undefined.
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IWhitespaceRemovalContext"/> contains all <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the removal of whitespace.
        /// </param>
        public void RemoveWhitespace(IWhitespaceRemovalContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <OpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                IList <ListItemTagInstance> listItemTags = context.GetTagsAfter(openTag)
                                                           .TakeWhile(t => t != closeTag)
                                                           .WhereStackDepthIs(0)
                                                           .OfType <ListItemTagInstance>()
                                                           .ToList();

                //Don't remove whitespace if there's only whitespace in between the open and close tags
                //because the tag won't be rendered
                if (listItemTags.Any() == false &&
                    _WhitespaceRegex.Match(context.GetRenderString(openTag, closeTag)).Success)
                {
                    continue;
                }

                context.RemoveWhitespaceBeforeTag(openTag, 1);
                context.RemoveWhitespaceAfterTag(openTag, 1);
                context.RemoveWhitespaceBeforeTag(openTag.CloseTag, 1);
                context.RemoveWhitespaceAfterTag(openTag.CloseTag, 2);
            }
        }
Пример #4
0
        /// <summary>
        /// Causes the <see cref="ITagDefinition"/> to render all of its <see cref="ITagInstance"/>s into XHTML.
        /// </summary>
        /// <remarks>
        /// This method is always called after <see cref="ITagDefinition.RemoveWhitespace"/> is called on any
        /// <see cref="ITagDefinition"/>. The order in which this method is called across the different
        /// <see cref="ITagDefinition"/>s is undefined.
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IRenderContext"/> contains all the <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the rendering of <see cref="ITagInstance"/>s.
        /// </param>
        public void Render(IRenderContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <OpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                IList <ListItemTagInstance> listItemTags = context.GetTagsAfter(openTag)
                                                           .TakeWhile(t => t != closeTag)
                                                           .WhereStackDepthIs(0)
                                                           .OfType <ListItemTagInstance>()
                                                           .ToList();

                string replacementOpenTag;
                string replacementCloseTag;
                ChooseReplacementTags(openTag, out replacementOpenTag, out replacementCloseTag);

                if (listItemTags.Any() == false)
                {
                    //Render only if there's not only whitespace in between the open and close tags
                    if (_WhitespaceRegex.Match(context.GetRenderString(openTag, closeTag)).Success == false)
                    {
                        context.RenderTag(openTag, replacementOpenTag + REPLACEMENT_LIST_ITEM_OPEN_TAG, true);
                        context.RenderTag(closeTag, REPLACEMENT_LIST_ITEM_CLOSE_TAG + replacementCloseTag, true);
                    }
                }
                else
                {
                    //If there's content between the open tag and the first list item tag, wrap that in a li tag too
                    ListItemTagInstance firstListItemTag = listItemTags.First();
                    if (_WhitespaceRegex.Match(context.GetRenderString(openTag, firstListItemTag)).Success)
                    {
                        context.RenderTag(openTag, replacementOpenTag, true);
                        context.RenderNonTagRange(openTag.CharRange.StartAt + openTag.CharRange.Length, firstListItemTag.CharRange.StartAt - (openTag.CharRange.StartAt + openTag.CharRange.Length), String.Empty, false);
                        context.RenderTag(firstListItemTag, REPLACEMENT_LIST_ITEM_OPEN_TAG, true);
                    }
                    else
                    {
                        context.RenderTag(openTag, replacementOpenTag + REPLACEMENT_LIST_ITEM_OPEN_TAG, true);
                        context.RenderTag(firstListItemTag, REPLACEMENT_LIST_ITEM_CLOSE_TAG + REPLACEMENT_LIST_ITEM_OPEN_TAG, true);
                    }

                    foreach (ListItemTagInstance listItemTagInstance in listItemTags.Skip(1))
                    {
                        context.RenderTag(listItemTagInstance, REPLACEMENT_LIST_ITEM_CLOSE_TAG + REPLACEMENT_LIST_ITEM_OPEN_TAG, true);
                    }

                    context.RenderTag(closeTag, REPLACEMENT_LIST_ITEM_CLOSE_TAG + replacementCloseTag, true);
                }
            }

            context.RegisterRenderCacheability(true);
        }
Пример #5
0
        /// <summary>
        /// Causes the <see cref="ITagDefinition"/> to remove any whitespace it sees fit that is around its
        /// identified <see cref="ITagInstance"/>s. An <see cref="ITagDefinition"/> does not need to remove any
        /// whitespace, if it doesn't want to.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method is always called before <see cref="ITagDefinition.Render"/> is called on any
        /// <see cref="ITagDefinition"/>. The order in which this method is called across the different
        /// <see cref="ITagDefinition"/>s is undefined.
        /// </para>
        /// <para>
        /// This method does nothing in its default implementation. Inheritors should override this method
        /// if they want to perform whitespace removal.
        /// </para>
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IWhitespaceRemovalContext"/> contains all <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the removal of whitespace.
        /// </param>
        public override void RemoveWhitespace(IWhitespaceRemovalContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <IOpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                context.RemoveWhitespaceBeforeTag(openTag, 1);
                context.RemoveWhitespaceAfterTag(openTag, 1);
                context.RemoveWhitespaceBeforeTag(closeTag, 1);
                context.RemoveWhitespaceAfterTag(closeTag, 2);
            }
        }
Пример #6
0
        /// <summary>
        /// Causes the <see cref="ITagDefinition"/> to render all of its <see cref="ITagInstance"/>s into XHTML.
        /// </summary>
        /// <remarks>
        /// This method is always called after <see cref="ITagDefinition.RemoveWhitespace"/> is called on any
        /// <see cref="ITagDefinition"/>. The order in which this method is called across the different
        /// <see cref="ITagDefinition"/>s is undefined.
        /// </remarks>
        /// <param name="context">
        /// The <see cref="IRenderContext"/> contains all the <see cref="ITagInstance"/>s, the input text,
        /// and methods that allow for the rendering of <see cref="ITagInstance"/>s.
        /// </param>
        public override void Render(IRenderContext context)
        {
            IEnumerable <IOpenTagInstance> tagInstances = context.Tags.Where(t => t.ParentDefinition == this)
                                                          .OfType <IOpenTagInstance>();

            foreach (IOpenTagInstance openTag in tagInstances)
            {
                ICloseTagInstance closeTag = openTag.CloseTag;

                string replacementOpenTag = REPLACEMENT_OPEN_TAG;
                if (openTag.Attributes.ContainsKey("Username"))
                {
                    replacementOpenTag += String.Format(AUTHOR_TAG_FORMAT_STRING, WebUtility.HtmlEncode((string)openTag.Attributes["Username"]));
                }
                context.RenderTag(openTag, replacementOpenTag, true);

                //Wrap all content that is not inside an block element tag in P tags
                ITagInstance afterTag = openTag;
                while (afterTag != null)
                {
                    //Find the next block element tag nested inside openTag
                    IOpenTagInstance untilTag = context.GetTagsAfter(afterTag)
                                                .TakeWhile(t => t != closeTag)                                                   //Take until the close tag
                                                .WhereStackDepthIs(0)                                                            //Only tags immediately nested inside openTag
                                                .Where(t => t.RendersToInlineElement == false)
                                                .OfType <IOpenTagInstance>()
                                                .FirstOrDefault();

                    if (untilTag != null)
                    {
                        WrapTextBetweenTagsInPTags(afterTag, untilTag, context);
                        afterTag = untilTag.CloseTag;
                    }
                    else
                    {
                        WrapTextBetweenTagsInPTags(afterTag, closeTag, context);
                        afterTag = null;
                    }
                }

                context.RenderTag(closeTag, REPLACEMENT_CLOSE_TAG, true);
            }

            context.RegisterRenderCacheability(true);
        }
Пример #7
0
        /// <summary>
        /// Validates the BBCode tags and ensures they respect the rules they define. For example, some tags
        /// disallow other tags inside them. This is where those rules are enforced and errant tags
        /// (<see cref="ITagInstance"/>s) are removed.
        /// </summary>
        /// <param name="context">The <see cref="ValidationContext"/></param>
        private void ValidateAndRepairSyntax(ValidationContext context)
        {
            for (int i = 0; i < context.Tags.Count; i++)
            {
                ITagInstance tagInstance = context.Tags[i];
                bool         vetoed      = CheckForPreviousTagOverlap(context, i);

                if (vetoed == false && tagInstance is IOpenTagInstance)
                {
                    IOpenTagInstance openTagInstance = (IOpenTagInstance)tagInstance;

                    //If any other tag vetoes this one, OR this tag vetoes itself
                    vetoed = context.OpenTagStack.Any(t => t.CheckForVetoAgainstAnotherTag(openTagInstance, context)) ||
                             openTagInstance.CheckForSelfVeto(context);

                    if (vetoed == false)
                    {
                        context.OpenTagStack.Push(openTagInstance);
                    }
                }

                if (vetoed == false && tagInstance is ICloseTagInstance)
                {
                    //If we find a close tag without any open tag, OR
                    //if the current innermost tag (top of the stack) is not the open tag for this close tag
                    vetoed = context.OpenTagStack.Any() == false ||
                             context.OpenTagStack.Peek().ParentDefinition != tagInstance.ParentDefinition;

                    if (vetoed == false)
                    {
                        IOpenTagInstance openTagInstance = context.OpenTagStack.Pop();
                        openTagInstance.CloseTag = (ICloseTagInstance)tagInstance;
                    }
                }

                if (vetoed)
                {
                    context.Tags.RemoveAt(i);
                    i--;
                }
            }

            //Close any tags that were left open
            while (context.OpenTagStack.Any())
            {
                IOpenTagInstance openTagInstance = context.OpenTagStack.Peek();

                string            tagText;
                ICloseTagInstance closeTag = openTagInstance.ParentDefinition.MakeCloseTagFor(openTagInstance, context.InputString.Length, out tagText);
                if (closeTag.CheckIfValidClose(context))
                {
                    context.InputString.Append(tagText);
                    context.Tags.Add(closeTag);
                    openTagInstance.CloseTag = closeTag;
                }
                else
                {
                    context.Tags.Remove(openTagInstance);
                }

                context.OpenTagStack.Pop();
            }
        }