/// <summary> /// Removes up to a number of newlines (and whitespace between them) that are found after the specified /// <paramref name="tagInstance"/>. /// </summary> /// <remarks> /// If <paramref name="numberOfNewlines"/> is higher than the actual number of newlines after the /// <paramref name="tagInstance"/>, only the actual number of newlines will be removed. /// </remarks> /// <param name="tagInstance">The tag instance to remove whitespace from after</param> /// <param name="numberOfNewlines">The number of newlines to remove</param> public void RemoveWhitespaceAfterTag(ITagInstance tagInstance, int numberOfNewlines) { if (numberOfNewlines <= 0) { throw new ArgumentOutOfRangeException("numberOfNewlines", "numberOfNewlines must be greater than 0."); } if (tagInstance.CharRange.StartAt >= _RenderString.Length) { return; } int startAt = tagInstance.CharRange.StartAt + tagInstance.CharRange.Length; Match match = _AfterTagWhitespaceRegex.Match(GetRenderString(), startAt); if (match.Success == false) { return; } CaptureCollection captures = match.Groups["nl"].Captures; if (captures.Count <= numberOfNewlines) { RenderNonTagRange(match.Index, match.Length, String.Empty, false); } else { int length = captures.Cast <Capture>().Take(numberOfNewlines).Sum(c => c.Length); RenderNonTagRange(startAt, length, String.Empty, false); } }
/// <summary> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { int size = (int)tagInstance.Attributes["Size"]; SizeTagDefinition sizeTagDef = (SizeTagDefinition)tagInstance.ParentDefinition; return(size > sizeTagDef._MaxSize || size < sizeTagDef._MinSize); }
/// <summary> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { if (tagInstance.Attributes.ContainsKey("Hex")) { return(false); } return(_ColourNames.Contains(((string)tagInstance.Attributes["Name"]).ToLower()) == false); }
/// <summary> /// Returns an <see cref="IEnumerable{T}"/> of the tags following the specified /// <paramref name="tagInstance"/> in the ordered sequence of tags defined by <see cref="IBbStringContext.Tags"/>. /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/> to return the tags after</param> /// <returns> /// The <see cref="ITagInstance"/>s after (and not including) <paramref name="tagInstance"/> /// from the <see cref="IBbStringContext.Tags"/> list. /// </returns> public IEnumerable <ITagInstance> GetTagsAfter(ITagInstance tagInstance) { int index = _Tags.BinarySearch(tagInstance, TagInstanceComparer.Instance); if (index < 0) { throw new ArgumentException("tagInstance must be in the Tags enumeration", "tagInstance"); } return(EnumerateFromIndex(_Tags, index + 1)); }
/// <summary> /// Wraps text between <paramref name="afterTag"/> and <paramref name="untilTag"/> in paragraph /// XHTML tags /// </summary> /// <param name="afterTag">The tag after which the text follows</param> /// <param name="untilTag">The tag before which the text lies</param> /// <param name="context">The <see cref="RenderContext"/></param> private void WrapTextBetweenTagsInPTags(ITagInstance afterTag, ITagInstance untilTag, IRenderContext context) { Match whitespaceMatch = _WhitespaceRegex.Match(context.GetRenderString(afterTag, untilTag)); if (whitespaceMatch.Success) { return; //No need to wrap whitespace in a P tag } context.RenderNonTagRange(afterTag.CharRange.StartAt + afterTag.CharRange.Length, 0, TEXT_WRAP_OPEN_TAG, true); context.RenderNonTagRange(untilTag.CharRange.StartAt, 0, TEXT_WRAP_CLOSE_TAG, true); }
/// <summary> /// Checks if the current tag's character range overlaps the previous tag's character range /// </summary> /// <remarks> /// Checking backwards for every tag previous the current is unnecessary because each previous /// tag is vetoed if it overlaps with the tag before it. /// </remarks> /// <param name="context">The <see cref="ValidationContext"/></param> /// <param name="tagIndex">The index of the current tag in <see cref="ValidationContext.Tags"/></param> /// <returns>True if it does overlap, false otherwise</returns> private bool CheckForPreviousTagOverlap(ValidationContext context, int tagIndex) { ITagInstance tagInstance = context.Tags[tagIndex]; if (tagIndex == 0) { return(false); } ITagInstance previousTagInstance = context.Tags[tagIndex - 1]; int endIndex = previousTagInstance.CharRange.StartAt + previousTagInstance.CharRange.Length - 1; return(tagInstance.CharRange.StartAt <= endIndex); }
/// <summary> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { FlashTagDefinition flashTagDef = tagInstance.ParentDefinition as FlashTagDefinition; if (flashTagDef == null || tagInstance is IOpenTagInstance == false) { throw new IllegalStateException("MustHaveValidSizesVetoRule did not receive an img open tag"); } int width = (int)tagInstance.Attributes["Width"]; int height = (int)tagInstance.Attributes["Height"]; return((width < flashTagDef._MinWidth || width > flashTagDef._MaxWidth) || (height < flashTagDef._MinHeight || height > flashTagDef._MaxHeight)); }
/// <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> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { ImageTagDefinition imgTagDef = tagInstance.ParentDefinition as ImageTagDefinition; if (imgTagDef == null || tagInstance is IOpenTagInstance == false) { throw new IllegalStateException("MustHaveValidSizesVetoRule did not receive an img open tag"); } if (tagInstance.Attributes.ContainsKey("Width") == false) { return(false); //No sizes specified } int width = (int)tagInstance.Attributes["Width"]; int height = (int)tagInstance.Attributes["Height"]; return((width < imgTagDef._MinWidth || width > imgTagDef._MaxWidth) || (height < imgTagDef._MinHeight || height > imgTagDef._MaxHeight)); }
/// <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> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { return(tagInstance.ParentDefinition is T); }
/// <summary> /// Replaces the range of characters defined by the <paramref name="tagInstance"/> with the specified /// <paramref name="replacementText"/>. /// </summary> /// <remarks> /// If the <paramref name="replacementText"/> is longer or shorter than the range of characters defined by /// the <paramref name="tagInstance"/>, the <see cref="IBbStringContext.Tags"/> and /// <see cref="IRenderContext.HtmlEscapedRanges"/> are automatically shifted to reflect the changes. /// </remarks> /// <param name="tagInstance">The <see cref="ITagInstance"/> whose character range will be replaced</param> /// <param name="replacementText">The replacement text</param> /// <param name="registerAsHtmlEscaped"> /// Whether or not to register the rendered character range as an escaped character range (see /// <see cref="IRenderContext.HtmlEscapedRanges"/>). /// </param> public void RenderTag(ITagInstance tagInstance, string replacementText, bool registerAsHtmlEscaped) { Render(tagInstance.CharRange.StartAt, tagInstance.CharRange.Length, replacementText, registerAsHtmlEscaped); tagInstance.Rendered = true; }
/// <summary> /// Gets the subset of the render string that is between the specified <see cref="ITagInstance"/>s as it /// looks at this point in time. This means i may be in a partially rendering (or not rendered at all) form. /// </summary> /// <param name="afterTag">The first <see cref="ITagInstance"/></param> /// <param name="untilTag">The second <see cref="ITagInstance"/></param> /// <returns>The subset of the render string</returns> public string GetRenderString(ITagInstance afterTag, ITagInstance untilTag) { int startIndex = afterTag.CharRange.StartAt + afterTag.CharRange.Length; return(GetRenderString(startIndex, untilTag.CharRange.StartAt - startIndex)); }
/// <summary> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { return(context.OpenTagStack.Any() && context.OpenTagStack.Peek().RendersToInlineElement); }
public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { return(context.OpenTagStack.Any() == false || context.OpenTagStack.Peek().ParentDefinition != tagInstance.ParentDefinition); }
/// <summary> /// Makes an assessment as to whether <paramref name="tagInstance"/> is a valid tag /// </summary> /// <param name="tagInstance">The <see cref="ITagInstance"/></param> /// <param name="context">The <see cref="ValidationContext"/></param> /// <returns>True if the tag is not valid should be vetoed, false otherwise</returns> public bool CheckForVeto(ITagInstance tagInstance, ValidationContext context) { return(_AllowableTagDefinitionTypes.Any(t => t == tagInstance.ParentDefinition.GetType()) == false); }