/// <summary> /// Chooses the replacement open and close tags to use for rendering based off whether we are /// rendering an ordered or unordered list. /// </summary> /// <param name="openTag">The <see cref="IOpenTagInstance"/></param> /// <param name="replacementOpenTag">Returns the replacement open tag to use</param> /// <param name="replacementCloseTag">Returns the replacement close tag to use</param> private void ChooseReplacementTags(IOpenTagInstance openTag, out string replacementOpenTag, out string replacementCloseTag) { if ((bool)openTag.Attributes["Ordered"]) { replacementOpenTag = REPLACEMENT_OLIST_OPEN_TAG; replacementCloseTag = REPLACEMENT_OLIST_CLOSE_TAG; } else { replacementOpenTag = REPLACEMENT_UOLIST_OPEN_TAG; replacementCloseTag = REPLACEMENT_UOLIST_CLOSE_TAG; } }
/// <summary> /// Replacement function that writes the rendered open tag for colour BBcode open tags /// </summary> /// <param name="openTagInstance">The <see cref="ITagInstance"/></param> /// <returns>The rendered XHTML tag</returns> private static string ReplacementOpenTagFactoryFunction(IOpenTagInstance openTagInstance) { string colour; if (openTagInstance.Attributes.ContainsKey("Hex")) { colour = (string)openTagInstance.Attributes["Hex"]; } else { colour = (string)openTagInstance.Attributes["Name"]; } return(String.Format(REPLACEMENT_OPEN_TAG_FORMAT_STRING, colour)); }
/// <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); }
/// <summary> /// Asks this <see cref="IOpenTagInstance"/> whether the specified <paramref name="tagInstance"/>is allowed /// to be inside it (ie before this open tag's close tag occurs). /// </summary> /// <param name="tagInstance"> /// The <see cref="IOpenTagInstance"/> which is trying to reside before the this tag's /// <see cref="ICloseTagInstance"/>. /// </param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns> /// True if this tag objects to the specified <paramref name="tagInstance"/> (which causes the /// <paramref name="tagInstance"/> to be removed and therefore ignored during rendering), /// false if it is okay for the <paramref name="tagInstance"/> to be there. /// </returns> public bool CheckForVetoAgainstAnotherTag(IOpenTagInstance tagInstance, ValidationContext context) { return(_OpenTagVetoRulesSet.OtherTagVetoRules.Any(r => r.CheckForVeto(tagInstance, context))); }
/// <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(); } }
/// <summary> /// Replacement function that writes the rendered open tag for size BBcode open tags /// </summary> /// <param name="openTagInstance">The <see cref="IOpenTagInstance"/></param> /// <returns>The rendered XHTML tag</returns> private static string ReplacementOpenTagFactoryFunction(IOpenTagInstance openTagInstance) { int size = (int)openTagInstance.Attributes["Size"]; return(String.Format(REPLACEMENT_OPEN_TAG_FORMAT_STRING, size)); }
/// <summary> /// Makes a closing tag instance for the specified <see cref="IOpenTagInstance"/>. Used to create closing /// tags for unclosed tag pairs during tag validation. /// </summary> /// <param name="openTagInstance">The <see cref="IOpenTagInstance"/> that needs a closing tag</param> /// <param name="firstCharIndex">The index of where the first character of the close tag must be</param> /// <param name="tagText"> /// Returns the text of the tag, which will be added to the input string by the validator /// </param> /// <returns>The <see cref="ICloseTagInstance"/></returns> public virtual ICloseTagInstance MakeCloseTagFor(IOpenTagInstance openTagInstance, int firstCharIndex, out string tagText) { tagText = _CloseTag; return(new CloseTagInstance(new CharRange(firstCharIndex, _CloseTag.Length), this, _RendersToInlineElement, _CloseTagVetoRules, Enumerable.Empty <KeyValuePair <string, object> >())); }
/// <summary> /// Makes a closing tag instance for the specified <see cref="IOpenTagInstance"/>. Used to create closing /// tags for unclosed tag pairs during tag validation. /// </summary> /// <param name="openTagInstance">The <see cref="IOpenTagInstance"/> that needs a closing tag</param> /// <param name="firstCharIndex">The index of where the first character of the close tag must be</param> /// <param name="tagText"> /// Returns the text of the tag, which will be added to the input string by the validator /// </param> /// <returns>The <see cref="ICloseTagInstance"/></returns> public ICloseTagInstance MakeCloseTagFor(IOpenTagInstance openTagInstance, int firstCharIndex, out string tagText) { tagText = CloseTag; return(new CloseTagInstance(new CharRange(firstCharIndex, CloseTag.Length), this, false, Enumerable.Empty <IVetoRule>(), Enumerable.Empty <KeyValuePair <string, object> >())); }