private string Parse(string input, User viewer, Primitive postOwner, bool appendP, string id, string styleClass, BbcodeParseMode mode, bool fullInternalUrls) { #if DEBUG timer.Start(); parseCount++; #endif if (string.IsNullOrEmpty(input)) { return string.Empty; } if (mode == BbcodeParseMode.Normal) { // Convert all URLs that aren't BB Coded into BB Code input = ParseUrls(input); } StringBuilder debugLog = new StringBuilder(); BbcodeOptions options = BbcodeOptions.ShowImages | BbcodeOptions.ShowFlash | BbcodeOptions.ShowVideo | BbcodeOptions.ShowAudio; if (viewer != null) { options = viewer.UserInfo.GetUserBbcodeOptions; } if (fullInternalUrls) { options = options | BbcodeOptions.FullInternalUris; } long start = DateTime.Now.Ticks; Stack tags = new Stack(); bool inTag = false; bool startAttr = false; bool endTag = false; bool inQuote = false; int inList = 0; bool parseList = false; bool inCode = false; string Tag = string.Empty; string attr = string.Empty; int startIndex = 0; int strLength = input.Length; int end = strLength; int quoteDepth = 0; int shareStart = 0; int shareDepth = 0; StringBuilder newOutput = new StringBuilder(); int lastEndIndex = 0; if (mode == BbcodeParseMode.Normal && appendP) { newOutput.Append("<p"); if (!string.IsNullOrEmpty(id)) { newOutput.Append(" id=\""); newOutput.Append(id); newOutput.Append("\""); } if (!string.IsNullOrEmpty(styleClass)) { newOutput.Append(" class=\""); newOutput.Append(styleClass); newOutput.Append("\""); } newOutput.Append(">"); } List<BbcodeTaglet> taglets = new List<BbcodeTaglet>(); int indexOffset = 0; int i = 0; while (i < strLength) { char current = input[i]; if (!inQuote && inList > 0) { if (current.Equals('[') && tags.Count > 0 && (i + 7) <= input.Length) { if (input.Substring(i, 3) == "[*]" || input.Substring(i, 7) == "[/list]") { if (((BbcodeTag)tags.Peek()).Tag.Equals("*")) { endTag = true; Tag = "*"; parseList = true; } } } } if (current.Equals(']') && !inQuote || (inList > 0 && parseList)) { parseList = false; startAttr = false; if (!endTag && !inCode) { if (Tag.Length > 0) { tags.Push(new BbcodeTag(Tag, attr, startIndex, indexOffset)); if (Tag.Equals("list")) { if (ValidList((BbcodeTag)tags.Peek())) { if (EndTag(input, i, "list")) inList++; } else { tags.Pop(); } } if (Tag.Equals("code")) { inCode = true; } if (Tag.Equals("quote")) { quoteDepth++; } if (Tag.Equals("share")) { quoteDepth++; shareDepth++; } } } if (endTag) { if (Tag.Length > 0) { if (tags.Count > 0) { bool startTagExists = false; foreach (BbcodeTag bt in tags) { if (bt.Tag.Equals(Tag)) startTagExists = true; } if (startTagExists) { while (!((BbcodeTag)tags.Peek()).Tag.Equals(Tag)) { // TODO: style tags.Pop(); } } if (((BbcodeTag)tags.Peek()).Tag.Equals(Tag)) { BbcodeTag tempTag = (BbcodeTag)tags.Pop(); int tagLength = Tag.Length; int startTagLength = 2 + tagLength + tempTag.Attributes.Length; int endTagLength = 3 + tagLength; int startReplaceLength = 2 + tagLength; int endReplaceLength = 3 + tagLength; int endOffset = 0; bool abortParse = false; string insertStart = string.Empty; string insertEnd = string.Empty; bool listItem = false; bool trimStart = false; /* * A couple of special cases */ if (Tag.Equals("code")) { inCode = false; } if (Tag.Equals("list")) { inList--; } if (Tag.Equals("*")) { listItem = true; endOffset = 0; endTagLength = 0; } if (Tag.Equals("quote")) { quoteDepth--; } if (Tag.Equals("share")) { quoteDepth--; } bool handled = false; int tempIndex = tempTag.indexStart + tempTag.StartLength; string contents = string.Empty; int tempLength = i - tempIndex - tempTag.EndLength + 1; if (tempLength > 0) { contents = input.Substring(tempIndex, tempLength); } BbcodeEventArgs eventArgs = new BbcodeEventArgs(core, contents, tempTag, options, postOwner, (inList > 0), quoteDepth, shareDepth, mode, ref insertStart, ref insertEnd, ref handled, ref abortParse); BbcodeHooks(eventArgs); insertStart = eventArgs.PrefixText; insertEnd = eventArgs.SuffixText; handled = eventArgs.Handled; abortParse = eventArgs.ParseAborted; if (eventArgs.NoContents) { startTagLength += eventArgs.Contents.Length; } if (!handled) { abortParse = true; } startReplaceLength = insertStart.Length; endReplaceLength = insertEnd.Length; /* We force trimming the share to the first shared to behave properly */ if (mode == BbcodeParseMode.Tldr && Tag.Equals("share") && quoteDepth == 1) { trimStart = true; /* Do not delete the tag as we are in Tldr mode, but do remove everything before it */ taglets.Add(new BbcodeTaglet(trimStart, tempTag.indexStart, 0, insertStart)); } if (!abortParse) { /* two pass method */ taglets.Add(new BbcodeTaglet(trimStart, tempTag.indexStart, startTagLength, insertStart)); taglets.Add(new BbcodeTaglet(startIndex - endOffset, endTagLength, insertEnd, listItem)); } } } } } inTag = false; endTag = false; inQuote = false; Tag = string.Empty; attr = string.Empty; } else { if (inTag) { if (current.Equals('&') && (i + 6) <= strLength && input.Substring(i, 6).Equals(""")) { inQuote = !inQuote; } if (i == startIndex + 1 && current.Equals('/')) { endTag = true; } else { if (current.Equals(' ') || current.Equals('=')) { if (Tag.Length == 0) { inTag = false; endTag = false; inQuote = false; Tag = string.Empty; attr = string.Empty; } else { startAttr = true; } } if (startAttr) { attr += current.ToString(); } else { if ((current >= 'a' && current <= 'z') || (current >= 'A' && current <= 'Z') || (current >= '0' && current <= '9') || current == '*') { Tag += current.ToString().ToLower(); } else { inTag = false; endTag = false; inQuote = false; Tag = string.Empty; attr = string.Empty; } } } } } if (current.Equals('[') && !inQuote) { startIndex = i; inTag = true; endTag = false; inQuote = false; startAttr = false; // fixed parsing error Tag = string.Empty; attr = string.Empty; } i++; if (!inTag) { int nextIndex = input.IndexOf('[', i); if (nextIndex > 0) { i = startIndex = nextIndex; } } } /* second pass */ /* unpack the list into the input stream */ taglets.Sort(); for (int t = 0; t < taglets.Count; t++) { BbcodeTaglet taglet = taglets[t]; if ((!taglet.TrimStart) && taglet.StartIndex - lastEndIndex > 0) { newOutput.Append(input.Substring(lastEndIndex, taglet.StartIndex - lastEndIndex)); } newOutput.Append(taglet.RenderText); lastEndIndex = taglet.StartIndex + taglet.Length; } if (input.Length > lastEndIndex) { newOutput.Append(input.Substring(lastEndIndex, input.Length - lastEndIndex)); } if (mode == BbcodeParseMode.Tldr) { if (shareStart > 0) { newOutput.Remove(0, shareStart); } } if (mode == BbcodeParseMode.Normal && appendP) { newOutput.Append("</p>"); } input = newOutput.ToString(); double time = ((double)(DateTime.Now.Ticks - start)) / 10000000; if (mode == BbcodeParseMode.Normal) { input = input.Replace("\r\n", "\n"); input = input.Replace("\n", "<br />"); //input = input.Replace("<br /><br />", "</p><p>"); // Maybe? Need parser to break span tags at new lines input = input.Replace("<br /><br /><a ", "</p><p><a "); // But if the next tag is an anchor then it's a pretty decent chance that there is no formatting input = input.Replace("<p></p>", string.Empty); input = input.Replace("<p><br /><br />", "<p>"); input = input.Replace("<p><br />", "<p>"); input = input.Replace("<br /></p>", "</p>"); input = input.Replace("<br /><li>", "<li>"); input = input.Replace("<br /></ul>", "</ul>"); input = input.Replace("<br /></ol>", "</ol>"); input = input.Replace("<blockquote></blockquote>", "<blockquote> </blockquote>"); //input = Regex.Replace(input, @"\<p\>(\s+)\<\/p\>", string.Empty, RegexOptions.Compiled); input = input.Replace("<p></p>", string.Empty); input = input.Replace("<p> </p>", string.Empty); input = input.Replace("<p>\n</p>", string.Empty); input = input.Replace("<p>\r\n</p>", string.Empty); input = ParseEmoticons(core, input); } #if DEBUG timer.Stop(); #endif return input; }
public BbcodeEventArgs(Core core, string contents, BbcodeTag tag, BbcodeOptions options, User postOwner, bool inList, int quoteDepth, int shareDepth, BbcodeParseMode mode, ref string prefixText, ref string suffixText, ref bool handled, ref bool abortParse) { this.core = core; this.tag = tag; this.options = options; this.attributes = tag.GetAttributes(); this.contents = contents; this.prefixText = prefixText; this.suffixText = suffixText; this.inList = inList; this.quoteDepth = quoteDepth; this.shareDepth = shareDepth; this.mode = mode; this.handled = handled; this.abortParse = abortParse; this.owner = postOwner; }
private string Parse(string input, User viewer, Primitive postOwner, bool appendP, string id, string styleClass, BbcodeParseMode mode) { return Parse(input, viewer, postOwner, appendP, id, styleClass, mode, false); }