Merge branch 'MentionsFeature'

main
Inga 🏳‍🌈 13 years ago
commit a24168632c
  1. 4
      FLocal.Common/BBCodes/AbstractLocalLink.cs
  2. 4
      FLocal.Common/BBCodes/B.cs
  3. 2
      FLocal.Common/BBCodes/Code.cs
  4. 4
      FLocal.Common/BBCodes/ECode.cs
  5. 4
      FLocal.Common/BBCodes/FUrl.cs
  6. 4
      FLocal.Common/BBCodes/Font.cs
  7. 4
      FLocal.Common/BBCodes/FontColor.cs
  8. 4
      FLocal.Common/BBCodes/FontSize.cs
  9. 4
      FLocal.Common/BBCodes/Google.cs
  10. 4
      FLocal.Common/BBCodes/I.cs
  11. 2
      FLocal.Common/BBCodes/Image.cs
  12. 4
      FLocal.Common/BBCodes/List.cs
  13. 2
      FLocal.Common/BBCodes/ListElem.cs
  14. 4
      FLocal.Common/BBCodes/Lurk.cs
  15. 2
      FLocal.Common/BBCodes/Math.cs
  16. 4
      FLocal.Common/BBCodes/Quote.cs
  17. 4
      FLocal.Common/BBCodes/QuoteSkipper.cs
  18. 4
      FLocal.Common/BBCodes/RuWiki.cs
  19. 4
      FLocal.Common/BBCodes/S.cs
  20. 4
      FLocal.Common/BBCodes/Spoiler.cs
  21. 2
      FLocal.Common/BBCodes/Tex.cs
  22. 4
      FLocal.Common/BBCodes/U.cs
  23. 2
      FLocal.Common/BBCodes/UploadImage.cs
  24. 4
      FLocal.Common/BBCodes/UploadLink.cs
  25. 4
      FLocal.Common/BBCodes/Url.cs
  26. 6
      FLocal.Common/BBCodes/User.cs
  27. 4
      FLocal.Common/BBCodes/Wiki.cs
  28. 7
      FLocal.Common/BBCodes/helpers/BBCode.cs
  29. 10
      FLocal.Common/BBCodes/helpers/IPostParsingContext.cs
  30. 21
      FLocal.Common/BBCodes/helpers/UserMentionProcessor.cs
  31. 6
      FLocal.Common/FLocal.Common.csproj
  32. 53
      FLocal.Common/UBBParser.cs
  33. 3
      FLocal.Common/URL/UrlManager.cs
  34. 6
      FLocal.Common/URL/users/user/Mentions.cs
  35. 1
      FLocal.Common/actions/ChangeSet.cs
  36. 69
      FLocal.Common/dataobjects/Mention.cs
  37. 46
      FLocal.Common/dataobjects/Post.cs
  38. 22
      FLocal.Common/dataobjects/Thread.cs
  39. 36
      FLocal.Common/dataobjects/User.cs
  40. 25
      FLocal.Common/helpers/DelegatePostParsingContext.cs
  41. 2
      FLocal.IISHandler/HandlersFactory.cs
  42. 4
      FLocal.IISHandler/handlers/response/UserRepliesHandler.cs
  43. 3
      FLocal.Patcher.Common/FLocal.Patcher.Common.csproj
  44. 91
      FLocal.Patcher.Common/Resources/Patch_00002_mentions.xml
  45. 10
      MySQLConnector/Connection.cs
  46. 4
      Resources/FLocal/static/css/modern/global.css
  47. 2
      Resources/FLocal/templates/Full/Boards.xslt
  48. 4
      Resources/FLocal/templates/Full/UserInfo.xslt
  49. 4
      Resources/FLocal/templates/Lite/UserInfo.xslt
  50. 2
      Resources/FLocal/templates/Modern/Boards.xslt
  51. 4
      Resources/FLocal/templates/Modern/elems/ForumHeader.xslt
  52. 4
      Resources/FLocal/templates/Modern/elems/UserHeader.xslt
  53. 244
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeConfiguration.vb
  54. 128
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeDocument.vb
  55. 436
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeElement.vb
  56. 136
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeElementFactory.vb
  57. 180
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeElementTypeDictionary.vb
  58. 28
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeHtmlFormatter.vb
  59. 136
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeNode.vb
  60. 130
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeNodeCollection.vb
  61. 598
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeParser.vb
  62. 102
      ThirdParty/PJonDevelopment.BBCode/Classes/BBCodeText.vb
  63. 14
      ThirdParty/PJonDevelopment.BBCode/Interfaces/ITextFormatter.vb
  64. 746
      ThirdParty/PJonDevelopment.BBCode/Modules/Utils.vb
  65. 8
      ThirdParty/PJonDevelopment.BBCode/README.TXT
  66. 8
      Web.Core/DB/IDBConnection.cs

@ -16,11 +16,11 @@ namespace FLocal.Common.BBCodes {
get; get;
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
var url = this.url; var url = this.url;
var name = this.Safe(url.title); var name = this.Safe(url.title);
if(this.Default != null) { if(this.Default != null) {
name = this.GetInnerHTML(formatter); name = this.GetInnerHTML(context, formatter);
} }
return string.Format("<a href=\"{0}\">{1}</a>", url.canonical, url.title); return string.Format("<a href=\"{0}\">{1}</a>", url.canonical, url.title);
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("b") { : base("b") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<b>" + this.GetInnerHTML(formatter) + "</b>"; return "<b>" + this.GetInnerHTML(context, formatter) + "</b>";
} }
} }

@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes {
: base("code") { : base("code") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<pre>" + System.Web.HttpUtility.HtmlEncode(this.InnerBBCode) + "</pre><br/>"; return "<pre>" + System.Web.HttpUtility.HtmlEncode(this.InnerBBCode) + "</pre><br/>";
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("ecode") { : base("ecode") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return this.GetInnerHTML(new BBCodeHtmlFormatter()); return this.GetInnerHTML(context, new BBCodeHtmlFormatter<BBCodes.IPostParsingContext>());
} }
} }

@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes {
: base("furl") { : base("furl") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string rawUrl = this.DefaultOrValue; string rawUrl = this.DefaultOrValue;
string title = null; string title = null;
if(rawUrl.ToLower() != this.InnerText.ToLower()) { if(rawUrl.ToLower() != this.InnerText.ToLower()) {
title = this.GetInnerHTML(formatter); title = this.GetInnerHTML(context, formatter);
} }
return UrlProcessor.ProcessLink(rawUrl, title, false); return UrlProcessor.ProcessLink(rawUrl, title, false);
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("font") { : base("font") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<font face=\"" + this.Default + "\">" + this.GetInnerHTML(formatter) + "</font>"; return "<font face=\"" + this.Default + "\">" + this.GetInnerHTML(context, formatter) + "</font>";
} }
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("color") { : base("color") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<font color=\"" + this.Default + "\">" + this.GetInnerHTML(formatter) + "</font>"; return "<font color=\"" + this.Default + "\">" + this.GetInnerHTML(context, formatter) + "</font>";
} }
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("size") { : base("size") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<font size=\"" + this.Default + "\">" + this.GetInnerHTML(formatter) + "</font>"; return "<font size=\"" + this.Default + "\">" + this.GetInnerHTML(context, formatter) + "</font>";
} }
} }

@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes {
: base("google") { : base("google") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<a href=\"http://lmgtfy.com/?q=" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">g:" + this.GetInnerHTML(formatter) + "</a>"; return "<a href=\"http://lmgtfy.com/?q=" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">g:" + this.GetInnerHTML(context, formatter) + "</a>";
} }
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("i") { : base("i") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<i>" + this.GetInnerHTML(formatter) + "</i>"; return "<i>" + this.GetInnerHTML(context, formatter) + "</i>";
} }
} }

@ -10,7 +10,7 @@ namespace FLocal.Common.BBCodes {
public Image() : base("image") { public Image() : base("image") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
var urlInfo = UrlProcessor.Process(this.InnerText); var urlInfo = UrlProcessor.Process(this.InnerText);
if (urlInfo.isLocal && urlInfo.relativeUrl.StartsWith("/user/upload/")) { if (urlInfo.isLocal && urlInfo.relativeUrl.StartsWith("/user/upload/")) {
return "<f:img><f:src>" + urlInfo.relativeUrl + "</f:src><f:alt>" + urlInfo.relativeUrl + "</f:alt></f:img>"; return "<f:img><f:src>" + urlInfo.relativeUrl + "</f:src><f:alt>" + urlInfo.relativeUrl + "</f:alt></f:img>";

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("list") { : base("list") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<ul>" + this.GetInnerHTML(formatter) + "</ul>"; return "<ul>" + this.GetInnerHTML(context, formatter) + "</ul>";
} }
} }

@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes {
: base("*") { : base("*") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
// return "<li>" + this.GetInnerHTML(formatter) + "</li>"; // return "<li>" + this.GetInnerHTML(formatter) + "</li>";
return "<li>"; return "<li>";
} }

@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes {
: base("lurk") { : base("lurk") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<a href=\"http://lurkmore.ru/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">l:" + this.GetInnerHTML(formatter) + "</a>"; return "<a href=\"http://lurkmore.ru/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">l:" + this.GetInnerHTML(context, formatter) + "</a>";
} }
} }

@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes {
: base("math") { : base("math") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string tex = "$$" + this.InnerBBCode + "$$"; string tex = "$$" + this.InnerBBCode + "$$";
var upload = helpers.TexProcessor.getCompiled(tex); var upload = helpers.TexProcessor.getCompiled(tex);
return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(tex) + "</f:alt></f:img>"; return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(tex) + "</f:alt></f:img>";

@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes {
: base("quote") { : base("quote") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string inner = this.GetInnerHTML(formatter).TrimHtml(); string inner = this.GetInnerHTML(context, formatter).TrimHtml();
if(inner == "") return ""; if(inner == "") return "";
string marker = this.Default; string marker = this.Default;
if(marker == null) marker = "Quote:"; if(marker == null) marker = "Quote:";

@ -10,7 +10,7 @@ namespace FLocal.Common.BBCodes {
public QuoteSkipper() : base("quoteskipper") { public QuoteSkipper() : base("quoteskipper") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
if(this.Name.ToLower() == "q" || this.Name.ToLower() == "quote") { if(this.Name.ToLower() == "q" || this.Name.ToLower() == "quote") {
return ""; return "";
} else if(this.Name.ToLower() == "code") { } else if(this.Name.ToLower() == "code") {
@ -36,7 +36,7 @@ namespace FLocal.Common.BBCodes {
} }
sb.Append("]"); sb.Append("]");
if(this.RequireClosingTag) { if(this.RequireClosingTag) {
sb.Append(this.GetInnerHTML(formatter)); sb.Append(this.GetInnerHTML(context, formatter));
sb.Append("[/"); sb.Append("[/");
sb.Append(name); sb.Append(name);
sb.Append("]"); sb.Append("]");

@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes {
: base("ruwiki") { : base("ruwiki") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<a href=\"http://ru.wikipedia.org/wiki/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">в:" + this.GetInnerHTML(formatter) + "</a>"; return "<a href=\"http://ru.wikipedia.org/wiki/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">в:" + this.GetInnerHTML(context, formatter) + "</a>";
} }
} }

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("s") { : base("s") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<s>" + this.GetInnerHTML(formatter) + "</s>"; return "<s>" + this.GetInnerHTML(context, formatter) + "</s>";
} }
} }

@ -11,10 +11,10 @@ namespace FLocal.Common.BBCodes {
: base("spoiler") { : base("spoiler") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string marker = this.Default; string marker = this.Default;
if(marker == null) marker = "Spoiler"; if(marker == null) marker = "Spoiler";
return "<blockquote spoiler><font opener class=\"small\" onClick=\"showSpoiler(this)\">" + marker + "</font><hr/><div inner name=\"inner\" style=\"display:none\">" + this.GetInnerHTML(formatter).Trim() + "</div><hr/></blockquote><br/>"; return "<blockquote spoiler><font opener class=\"small\" onClick=\"showSpoiler(this)\">" + marker + "</font><hr/><div inner name=\"inner\" style=\"display:none\">" + this.GetInnerHTML(context, formatter).Trim() + "</div><hr/></blockquote><br/>";
} }
} }

@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes {
: base("tex") { : base("tex") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string tex = this.InnerBBCode; string tex = this.InnerBBCode;
var upload = helpers.TexProcessor.getCompiled(tex); var upload = helpers.TexProcessor.getCompiled(tex);
return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(tex) + "</f:alt></f:img>"; return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(tex) + "</f:alt></f:img>";

@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("u") { : base("u") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<u>" + this.GetInnerHTML(formatter) + "</u>"; return "<u>" + this.GetInnerHTML(context, formatter) + "</u>";
} }
} }

@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes {
: base("uploadimage") { : base("uploadimage") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
var upload = dataobjects.Upload.LoadById(int.Parse(this.InnerText)); var upload = dataobjects.Upload.LoadById(int.Parse(this.InnerText));
var name = upload.filename; var name = upload.filename;
return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(upload.filename) + "</f:alt></f:img>"; return "<f:img><f:src>/Upload/Item/" + upload.id.ToString() + "/</f:src><f:alt>" + this.Safe(upload.filename) + "</f:alt></f:img>";

@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes {
: base("uploadlink") { : base("uploadlink") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
var upload = dataobjects.Upload.LoadById(int.Parse(this.DefaultOrValue)); var upload = dataobjects.Upload.LoadById(int.Parse(this.DefaultOrValue));
var name = this.Safe(upload.filename); var name = this.Safe(upload.filename);
if(this.Default != null) { if(this.Default != null) {
name = this.GetInnerHTML(formatter); name = this.GetInnerHTML(context, formatter);
} }
return "<a href=\"/Upload/Info/" + upload.id.ToString() + "/\">" + name + "</a>"; return "<a href=\"/Upload/Info/" + upload.id.ToString() + "/\">" + name + "</a>";
} }

@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes {
: base("url") { : base("url") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
string rawUrl = this.DefaultOrValue; string rawUrl = this.DefaultOrValue;
string title = null; string title = null;
if(rawUrl.ToLower() != this.InnerText.ToLower()) { if(rawUrl.ToLower() != this.InnerText.ToLower()) {
title = this.GetInnerHTML(formatter); title = this.GetInnerHTML(context, formatter);
} }
return UrlProcessor.ProcessLink(rawUrl, title, true); return UrlProcessor.ProcessLink(rawUrl, title, true);
} }

@ -11,10 +11,8 @@ namespace FLocal.Common.BBCodes {
: base("user") { : base("user") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
var user = dataobjects.User.LoadByName(this.DefaultOrValue); return UserMentionProcessor.ProcessUserMention(context, this.DefaultOrValue);
var url = new URL.users.user.Info(user.id.ToString(), null);
return String.Format("<a class=\"separate UG_{0}\" href=\"{1}\">{2}</a>", this.Safe(user.userGroup.name), url.canonical, this.Safe(user.name));
} }
} }

@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes {
: base("wiki") { : base("wiki") {
} }
public override string Format(ITextFormatter formatter) { public override string Format(IPostParsingContext context, ITextFormatter<IPostParsingContext> formatter) {
return "<a href=\"http://en.wikipedia.org/wiki/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">w:" + this.GetInnerHTML(formatter) + "</a>"; return "<a href=\"http://en.wikipedia.org/wiki/" + HttpUtility.UrlPathEncode(this.DefaultOrValue) + "\">w:" + this.GetInnerHTML(context, formatter) + "</a>";
} }
} }

@ -2,18 +2,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using FLocal.Common.helpers;
namespace FLocal.Common.BBCodes { namespace FLocal.Common.BBCodes {
abstract class BBCode : PJonDevelopment.BBCode.BBCodeElement { abstract class BBCode : PJonDevelopment.BBCode.BBCodeElement<IPostParsingContext> {
public BBCode(string name) public BBCode(string name)
: base(name) { : base(name) {
} }
protected string GetInnerHTML(PJonDevelopment.BBCode.ITextFormatter formatter) { protected string GetInnerHTML(IPostParsingContext context, PJonDevelopment.BBCode.ITextFormatter<IPostParsingContext> formatter) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
foreach (var node in this.Nodes) { foreach (var node in this.Nodes) {
builder.Append(node.Format(formatter)); builder.Append(node.Format(context, formatter));
} }
return builder.ToString(); return builder.ToString();
} }

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FLocal.Common.BBCodes {
public interface IPostParsingContext {
void OnUserMention(dataobjects.User user);
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FLocal.Common.BBCodes {
class UserMentionProcessor {
private static string Safe(string str) {
return System.Web.HttpUtility.HtmlEncode(str);
}
public static string ProcessUserMention(IPostParsingContext context, string username) {
var user = dataobjects.User.LoadByName(username);
context.OnUserMention(user);
var url = new URL.users.user.Info(user.id.ToString(), null);
return String.Format("<a class=\"separate userreference UG_{0}\" href=\"{1}\">{2}</a>", Safe(user.userGroup.name), url.canonical, Safe(user.name));
}
}
}

@ -68,6 +68,7 @@
<Compile Include="BBCodes\Code.cs" /> <Compile Include="BBCodes\Code.cs" />
<Compile Include="BBCodes\FUrl.cs" /> <Compile Include="BBCodes\FUrl.cs" />
<Compile Include="BBCodes\helpers\TexProcessor.cs" /> <Compile Include="BBCodes\helpers\TexProcessor.cs" />
<Compile Include="BBCodes\helpers\UserMentionProcessor.cs" />
<Compile Include="BBCodes\Image.cs" /> <Compile Include="BBCodes\Image.cs" />
<Compile Include="BBCodes\List.cs" /> <Compile Include="BBCodes\List.cs" />
<Compile Include="BBCodes\ListElem.cs" /> <Compile Include="BBCodes\ListElem.cs" />
@ -100,6 +101,7 @@
<Compile Include="dataobjects\AnonymousUserSettings.cs" /> <Compile Include="dataobjects\AnonymousUserSettings.cs" />
<Compile Include="dataobjects\LocalNetwork.cs" /> <Compile Include="dataobjects\LocalNetwork.cs" />
<Compile Include="dataobjects\Machichara.cs" /> <Compile Include="dataobjects\Machichara.cs" />
<Compile Include="dataobjects\Mention.cs" />
<Compile Include="dataobjects\Moderator.cs" /> <Compile Include="dataobjects\Moderator.cs" />
<Compile Include="dataobjects\ModernSkin.cs" /> <Compile Include="dataobjects\ModernSkin.cs" />
<Compile Include="dataobjects\PMConversation.cs" /> <Compile Include="dataobjects\PMConversation.cs" />
@ -122,6 +124,8 @@
<Compile Include="dataobjects\User.cs" /> <Compile Include="dataobjects\User.cs" />
<Compile Include="dataobjects\AccountSettings.cs" /> <Compile Include="dataobjects\AccountSettings.cs" />
<Compile Include="dataobjects\UserGroup.cs" /> <Compile Include="dataobjects\UserGroup.cs" />
<Compile Include="helpers\DelegatePostParsingContext.cs" />
<Compile Include="BBCodes\helpers\IPostParsingContext.cs" />
<Compile Include="IComplexSqlObjectTableSpec.cs" /> <Compile Include="IComplexSqlObjectTableSpec.cs" />
<Compile Include="IOutputParams.cs" /> <Compile Include="IOutputParams.cs" />
<Compile Include="ISqlObjectTableSpec.cs" /> <Compile Include="ISqlObjectTableSpec.cs" />
@ -179,7 +183,7 @@
<Compile Include="URL\users\user\Info.cs" /> <Compile Include="URL\users\user\Info.cs" />
<Compile Include="URL\users\user\PollsParticipated.cs" /> <Compile Include="URL\users\user\PollsParticipated.cs" />
<Compile Include="URL\users\user\Posts.cs" /> <Compile Include="URL\users\user\Posts.cs" />
<Compile Include="URL\users\user\Replies.cs" /> <Compile Include="URL\users\user\Mentions.cs" />
<Compile Include="URL\users\user\Threads.cs" /> <Compile Include="URL\users\user\Threads.cs" />
<Compile Include="UserContext.cs" /> <Compile Include="UserContext.cs" />
<Compile Include="UserSettingsGateway.cs" /> <Compile Include="UserSettingsGateway.cs" />

@ -7,26 +7,27 @@ using System.Text.RegularExpressions;
using PJonDevelopment.BBCode; using PJonDevelopment.BBCode;
using System.IO; using System.IO;
using Web.Core; using Web.Core;
using FLocal.Common.helpers;
namespace FLocal.Common { namespace FLocal.Common {
public static class UBBParser { public static class UBBParser {
private class BBParserGateway { private class BBParserGateway {
private class SimpleFormatter : ITextFormatter { private class SimpleFormatter : ITextFormatter<BBCodes.IPostParsingContext> {
public static readonly SimpleFormatter instance = new SimpleFormatter(); public static readonly SimpleFormatter instance = new SimpleFormatter();
private SimpleFormatter() { private SimpleFormatter() {
} }
public string Format(string source) { public string Format(BBCodes.IPostParsingContext context, string source) {
return source; return source;
} }
} }
private class TextFormatter : ITextFormatter { private class TextFormatter : ITextFormatter<BBCodes.IPostParsingContext> {
public static readonly TextFormatter instance = new TextFormatter(); public static readonly TextFormatter instance = new TextFormatter();
@ -68,6 +69,14 @@ namespace FLocal.Common {
return BBCodes.UrlProcessor.ProcessLink(url, null, true) + remainder; return BBCodes.UrlProcessor.ProcessLink(url, null, true) + remainder;
} }
private static readonly Regex USERS_MATCHER = new Regex("(?<start>^|\\W)@(?<username>\\w+)(?<end>\\W|$)", RegexOptions.Singleline | RegexOptions.Compiled);
private static string USERS_REPLACE(BBCodes.IPostParsingContext context, Match match) {
string start = match.Groups["start"].Value;
string username = match.Groups["username"].Value;
string end = match.Groups["end"].Value;
return start + BBCodes.UserMentionProcessor.ProcessUserMention(context, username) + end;
}
private static readonly Dictionary<Regex, MatchEvaluator> TYPOGRAPHICS = new Dictionary<Regex, MatchEvaluator> { private static readonly Dictionary<Regex, MatchEvaluator> TYPOGRAPHICS = new Dictionary<Regex, MatchEvaluator> {
{ new Regex("(?<=\\s)--?(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "–" }, { new Regex("(?<=\\s)--?(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "–" },
{ new Regex("(?<=\\s)---(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "—" }, { new Regex("(?<=\\s)---(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "—" },
@ -85,15 +94,16 @@ namespace FLocal.Common {
{ new Regex("&lt;-", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "←" }, { new Regex("&lt;-", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "←" },
}; };
private ITextFormatter inner; private ITextFormatter<BBCodes.IPostParsingContext> inner;
private TextFormatter() { private TextFormatter() {
this.inner = new BBCodeHtmlFormatter(); this.inner = new BBCodeHtmlFormatter<BBCodes.IPostParsingContext>();
} }
public string Format(string source) { public string Format(BBCodes.IPostParsingContext context, string source) {
string result = this.inner.Format(source).Replace("&nbsp;", " "); string result = this.inner.Format(context, source).Replace("&nbsp;", " ");
result = LINKS_MATCHER.Replace(result, LINKS_REPLACE); result = LINKS_MATCHER.Replace(result, LINKS_REPLACE);
result = USERS_MATCHER.Replace(result, match => USERS_REPLACE(context, match));
foreach(var smile in SMILEYS_DATA) { foreach(var smile in SMILEYS_DATA) {
result = smile.Key.Replace(result, smile.Value); result = smile.Key.Replace(result, smile.Value);
} }
@ -108,14 +118,14 @@ namespace FLocal.Common {
public static readonly BBParserGateway instance = new BBParserGateway(); public static readonly BBParserGateway instance = new BBParserGateway();
private BBCodeParser parser; private BBCodeParser<BBCodes.IPostParsingContext> parser;
private ITextFormatter formatter; private ITextFormatter<BBCodes.IPostParsingContext> formatter;
private BBCodeParser quotesParser; private BBCodeParser<BBCodes.IPostParsingContext> quotesParser;
private ITextFormatter simpleFormatter; private ITextFormatter<BBCodes.IPostParsingContext> simpleFormatter;
private BBParserGateway() { private BBParserGateway() {
this.parser = new BBCodeParser(); this.parser = new BBCodeParser<BBCodes.IPostParsingContext>();
this.parser.ElementTypes.Add("b", typeof(BBCodes.B), true); this.parser.ElementTypes.Add("b", typeof(BBCodes.B), true);
this.parser.ElementTypes.Add("code", typeof(BBCodes.Code), true); this.parser.ElementTypes.Add("code", typeof(BBCodes.Code), true);
this.parser.ElementTypes.Add("ecode", typeof(BBCodes.ECode), true); this.parser.ElementTypes.Add("ecode", typeof(BBCodes.ECode), true);
@ -146,29 +156,36 @@ namespace FLocal.Common {
this.parser.ElementTypes.Add("ruwiki", typeof(BBCodes.RuWiki), true); this.parser.ElementTypes.Add("ruwiki", typeof(BBCodes.RuWiki), true);
this.formatter = TextFormatter.instance; this.formatter = TextFormatter.instance;
this.quotesParser = new BBCodeParser(); this.quotesParser = new BBCodeParser<BBCodes.IPostParsingContext>();
foreach(var elementType in this.parser.ElementTypes) { foreach(var elementType in this.parser.ElementTypes) {
this.quotesParser.ElementTypes.Add(elementType.Key, typeof(BBCodes.QuoteSkipper), elementType.Value.RequireClosingTag); this.quotesParser.ElementTypes.Add(elementType.Key, typeof(BBCodes.QuoteSkipper), elementType.Value.RequireClosingTag);
} }
this.simpleFormatter = SimpleFormatter.instance; this.simpleFormatter = SimpleFormatter.instance;
} }
public string Parse(string input) { public string Parse(BBCodes.IPostParsingContext context, string input) {
string result = this.parser.Parse(input).Format(this.formatter); string result = this.parser.Parse(input).Format(context, this.formatter);
if(result.EndsWith("<br/>")) result = result.Substring(0, result.Length - 5); if(result.EndsWith("<br/>")) result = result.Substring(0, result.Length - 5);
return result; return result;
} }
public string ParseQuote(string input) { public string ParseQuote(string input) {
string result = this.quotesParser.Parse(input).Format(this.simpleFormatter); string result = this.quotesParser.Parse(input).Format(CreateStubContext(), this.simpleFormatter);
return result; return result;
} }
} }
private static BBCodes.IPostParsingContext CreateStubContext() {
return new DelegatePostParsingContext(user => {});
}
public static string UBBToIntermediate(BBCodes.IPostParsingContext context, string UBB) {
return BBParserGateway.instance.Parse(context, UBB);
}
public static string UBBToIntermediate(string UBB) { public static string UBBToIntermediate(string UBB) {
//return HttpUtility.HtmlEncode(UBB).Replace(Util.EOL, "<br/>" + Util.EOL); return UBBToIntermediate(CreateStubContext(), UBB);
return BBParserGateway.instance.Parse(UBB);
} }
public static string ShallerToUBB(string shaller) { public static string ShallerToUBB(string shaller) {

@ -305,7 +305,8 @@ namespace FLocal.Common.URL {
case "posts": case "posts":
return new users.user.Posts(requestParts[2], GetRemainder(requestParts, 4)); return new users.user.Posts(requestParts[2], GetRemainder(requestParts, 4));
case "replies": case "replies":
return new users.user.Replies(requestParts[2], GetRemainder(requestParts, 4)); case "mentions":
return new users.user.Mentions(requestParts[2], GetRemainder(requestParts, 4));
case "threads": case "threads":
return new users.user.Threads(requestParts[2], GetRemainder(requestParts, 4)); return new users.user.Threads(requestParts[2], GetRemainder(requestParts, 4));
default: default:

@ -4,14 +4,14 @@ using System.Linq;
using System.Text; using System.Text;
namespace FLocal.Common.URL.users.user { namespace FLocal.Common.URL.users.user {
public class Replies : Abstract { public class Mentions : Abstract {
public Replies(string userId, string remainder) : base(userId, remainder) { public Mentions(string userId, string remainder) : base(userId, remainder) {
} }
protected override string _canonical { protected override string _canonical {
get { get {
return "/Users/User/" + this.user.id + "/Replies/"; return "/Users/User/" + this.user.id + "/Mentions/";
} }
} }
} }

@ -41,6 +41,7 @@ namespace FLocal.Common.actions {
dataobjects.PunishmentLayerChange.TableSpec.TABLE, dataobjects.PunishmentLayerChange.TableSpec.TABLE,
dataobjects.Restriction.TableSpec.TABLE, dataobjects.Restriction.TableSpec.TABLE,
dataobjects.TexImage.TableSpec.TABLE, dataobjects.TexImage.TableSpec.TABLE,
dataobjects.Mention.TableSpec.TABLE,
dataobjects.Session.TableSpec.TABLE, dataobjects.Session.TableSpec.TABLE,
} }
); );

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Web.Core;
using Web.Core.DB;
using Web.Core.DB.conditions;
using FLocal.Common.actions;
namespace FLocal.Common.dataobjects {
public class Mention : SqlObject<Mention> {
public class TableSpec : ISqlObjectTableSpec {
public const string TABLE = "Mentions";
public const string FIELD_ID = "Id";
public const string FIELD_MENTIONEDUSERID = "MentionedUserId";
public const string FIELD_POSTID = "PostId";
public const string FIELD_DATE = "Date";
public static readonly TableSpec instance = new TableSpec();
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
public void refreshSqlObject(int id) { Refresh(id); }
}
protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
private int _mentionedUserId;
public int mentionedUserId {
get {
this.LoadIfNotLoaded();
return this._mentionedUserId;
}
}
public User mentionedUser {
get {
return User.LoadById(this.mentionedUserId);
}
}
private int _postId;
public int postId {
get {
this.LoadIfNotLoaded();
return this._postId;
}
}
public Post post {
get {
return Post.LoadById(this.postId);
}
}
private DateTime _date;
public DateTime date {
get {
this.LoadIfNotLoaded();
return this._date;
}
}
protected override void doFromHash(Dictionary<string, string> data) {
this._mentionedUserId = int.Parse(data[TableSpec.FIELD_MENTIONEDUSERID]);
this._postId = int.Parse(data[TableSpec.FIELD_POSTID]);
this._date = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_DATE]).Value;
}
}
}

@ -8,6 +8,7 @@ using Web.Core.DB;
using Web.Core.DB.conditions; using Web.Core.DB.conditions;
using FLocal.Common; using FLocal.Common;
using FLocal.Common.actions; using FLocal.Common.actions;
using FLocal.Common.helpers;
namespace FLocal.Common.dataobjects { namespace FLocal.Common.dataobjects {
public class Post : SqlObject<Post> { public class Post : SqlObject<Post> {
@ -330,12 +331,24 @@ namespace FLocal.Common.dataobjects {
actualLayer = this.layer; actualLayer = this.layer;
} }
lock(this.Edit_locker) { lock(this.Edit_locker) {
DateTime date = DateTime.Now;
HashSet<int> newMentionedUsersIds = new HashSet<int>();
if(parentPost != null && parentPost.poster.id != poster.id) {
newMentionedUsersIds.Add(parentPost.poster.id);
}
string newBodyIntermediate = UBBParser.UBBToIntermediate(
new DelegatePostParsingContext(mentionedUser => newMentionedUsersIds.Add(mentionedUser.id)),
newBody
);
List<AbstractChange> changes = new List<AbstractChange> { List<AbstractChange> changes = new List<AbstractChange> {
new InsertChange( new InsertChange(
Revision.TableSpec.instance, Revision.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> { new Dictionary<string, AbstractFieldValue> {
{ Revision.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) }, { Revision.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) },
{ Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) }, { Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(date.ToUTCString()) },
{ Revision.TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) }, { Revision.TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) },
{ Revision.TableSpec.FIELD_BODY, new ScalarFieldValue(newBody) }, { Revision.TableSpec.FIELD_BODY, new ScalarFieldValue(newBody) },
{ Revision.TableSpec.FIELD_NUMBER, new ScalarFieldValue((this.revision + 1).ToString()) }, { Revision.TableSpec.FIELD_NUMBER, new ScalarFieldValue((this.revision + 1).ToString()) },
@ -345,8 +358,8 @@ namespace FLocal.Common.dataobjects {
TableSpec.instance, TableSpec.instance,
new Dictionary<string, AbstractFieldValue> { new Dictionary<string, AbstractFieldValue> {
{ TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) }, { TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) },
{ TableSpec.FIELD_BODY, new ScalarFieldValue(UBBParser.UBBToIntermediate(newBody)) }, { TableSpec.FIELD_BODY, new ScalarFieldValue(newBodyIntermediate) },
{ TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) }, { TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(date.ToUTCString()) },
{ TableSpec.FIELD_REVISION, new IncrementFieldValue() }, { TableSpec.FIELD_REVISION, new IncrementFieldValue() },
{ TableSpec.FIELD_LAYERID, new ScalarFieldValue(actualLayer.id.ToString()) }, { TableSpec.FIELD_LAYERID, new ScalarFieldValue(actualLayer.id.ToString()) },
}, },
@ -364,10 +377,37 @@ namespace FLocal.Common.dataobjects {
) )
); );
} }
foreach(var mentionedUserId in newMentionedUsersIds.Except(this.mentionedUsersIds)) {
changes.Add(
new InsertChange(
Mention.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ Mention.TableSpec.FIELD_MENTIONEDUSERID, new ScalarFieldValue(mentionedUserId.ToString()) },
{ Mention.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) },
{ Mention.TableSpec.FIELD_DATE, new ScalarFieldValue(date.ToUTCString()) },
}
)
);
}
ChangeSetUtil.ApplyChanges(changes.ToArray()); ChangeSetUtil.ApplyChanges(changes.ToArray());
} }
} }
private IEnumerable<int> mentionedUsersIds {
get {
return from stringId in Config.instance.mainConnection.LoadIdsByConditions(
Mention.TableSpec.instance,
new ComparisonCondition(
Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_POSTID),
ComparisonType.EQUAL,
this.id.ToString()
),
Diapasone.unlimited,
Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_MENTIONEDUSERID)
) select int.Parse(stringId);
}
}
private IEnumerable<Post> subPosts { private IEnumerable<Post> subPosts {
get { get {
return Post.LoadByIds( return Post.LoadByIds(

@ -7,6 +7,7 @@ using Web.Core;
using Web.Core.DB; using Web.Core.DB;
using Web.Core.DB.conditions; using Web.Core.DB.conditions;
using FLocal.Common.actions; using FLocal.Common.actions;
using FLocal.Common.helpers;
namespace FLocal.Common.dataobjects { namespace FLocal.Common.dataobjects {
public class Thread : SqlObject<Thread> { public class Thread : SqlObject<Thread> {
@ -411,12 +412,19 @@ namespace FLocal.Common.dataobjects {
string parentPostId = null; string parentPostId = null;
if(parentPost != null) parentPostId = parentPost.id.ToString(); if(parentPost != null) parentPostId = parentPost.id.ToString();
bool isNewThread = (parentPost == null); bool isNewThread = (parentPost == null);
HashSet<int> mentionedUsersIds = new HashSet<int>();
if(parentPost != null && parentPost.poster.id != poster.id) {
mentionedUsersIds.Add(parentPost.poster.id);
}
string bodyIntermediate; string bodyIntermediate;
if(forcedPostId.HasValue) { if(forcedPostId.HasValue) {
//dirty hack //dirty hack
bodyIntermediate = body; bodyIntermediate = body;
} else { } else {
bodyIntermediate = UBBParser.UBBToIntermediate(body); bodyIntermediate = UBBParser.UBBToIntermediate(
new DelegatePostParsingContext(mentionedUser => mentionedUsersIds.Add(mentionedUser.id)),
body
);
} }
var postInsertData = new Dictionary<string,AbstractFieldValue> { var postInsertData = new Dictionary<string,AbstractFieldValue> {
{ Post.TableSpec.FIELD_THREADID, new ScalarFieldValue(threadId.ToString()) }, { Post.TableSpec.FIELD_THREADID, new ScalarFieldValue(threadId.ToString()) },
@ -484,6 +492,18 @@ namespace FLocal.Common.dataobjects {
changes.Add(threadUpdate); changes.Add(threadUpdate);
changes.Add(userUpdate); changes.Add(userUpdate);
foreach(var mentionedUserId in mentionedUsersIds) {
changes.Add(
new InsertChange(
Mention.TableSpec.instance,
new Dictionary<string, AbstractFieldValue> {
{ Mention.TableSpec.FIELD_MENTIONEDUSERID, new ScalarFieldValue(mentionedUserId.ToString()) },
{ Mention.TableSpec.FIELD_POSTID, new ReferenceFieldValue(postInsert) },
{ Mention.TableSpec.FIELD_DATE, new ScalarFieldValue(date.ToUTCString()) },
}
)
);
}
Dictionary<string, AbstractFieldValue> boardData = new Dictionary<string,AbstractFieldValue> { Dictionary<string, AbstractFieldValue> boardData = new Dictionary<string,AbstractFieldValue> {
{ Board.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() }, { Board.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() },

@ -253,38 +253,18 @@ namespace FLocal.Common.dataobjects {
); );
} }
public IEnumerable<Post> getReplies(Diapasone diapasone, bool isAscending) { public IEnumerable<Post> getMentions(Diapasone diapasone, bool isAscending) {
JoinSpec parent = new JoinSpec(
Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_PARENTPOSTID),
Post.TableSpec.instance,
"parent"
);
return Post.LoadByIds( return Post.LoadByIds(
from stringId in Config.instance.mainConnection.LoadIdsByConditions( from stringId in Config.instance.mainConnection.LoadIdsByConditions(
Post.TableSpec.instance, Mention.TableSpec.instance,
new ComplexCondition( new ComparisonCondition(
ConditionsJoinType.AND, Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_MENTIONEDUSERID),
new ComparisonCondition( ComparisonType.EQUAL,
parent.additionalTable.getColumnSpec(Post.TableSpec.FIELD_POSTERID), this.id.ToString()
ComparisonType.EQUAL,
this.id.ToString()
),
new ComparisonCondition(
Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_POSTERID),
ComparisonType.NOTEQUAL,
this.id.ToString()
)
), ),
diapasone, diapasone,
new JoinSpec[] { Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_POSTID),
parent new SortSpec(Mention.TableSpec.instance.getIdSpec(), isAscending)
},
new SortSpec[] {
new SortSpec(
Post.TableSpec.instance.getIdSpec(),
isAscending
),
}
) select int.Parse(stringId) ) select int.Parse(stringId)
); );
} }

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FLocal.Common.dataobjects;
namespace FLocal.Common.helpers {
class DelegatePostParsingContext : BBCodes.IPostParsingContext {
private readonly Action<User> onUserMention;
public DelegatePostParsingContext(Action<User> onUserMention) {
this.onUserMention = onUserMention;
}
#region IPostParsingContext Members
public void OnUserMention(User user) {
this.onUserMention(user);
}
#endregion
}
}

@ -51,7 +51,7 @@ namespace FLocal.IISHandler {
{ typeof(URL.users.user.Info), CreateHandler<URL.users.user.Info, handlers.response.UserInfoHandler> }, { typeof(URL.users.user.Info), CreateHandler<URL.users.user.Info, handlers.response.UserInfoHandler> },
{ typeof(URL.users.user.PollsParticipated), CreateHandler<URL.users.user.PollsParticipated, handlers.response.UserPollsParticipatedHandler> }, { typeof(URL.users.user.PollsParticipated), CreateHandler<URL.users.user.PollsParticipated, handlers.response.UserPollsParticipatedHandler> },
{ typeof(URL.users.user.Posts), CreateHandler<URL.users.user.Posts, handlers.response.UserPostsHandler> }, { typeof(URL.users.user.Posts), CreateHandler<URL.users.user.Posts, handlers.response.UserPostsHandler> },
{ typeof(URL.users.user.Replies), CreateHandler<URL.users.user.Replies, handlers.response.UserRepliesHandler> }, { typeof(URL.users.user.Mentions), CreateHandler<URL.users.user.Mentions, handlers.response.UserRepliesHandler> },
{ typeof(URL.users.user.Threads), CreateHandler<URL.users.user.Threads, handlers.response.UserThreadsHandler> }, { typeof(URL.users.user.Threads), CreateHandler<URL.users.user.Threads, handlers.response.UserThreadsHandler> },
}; };

@ -12,7 +12,7 @@ using Web.Core.DB.conditions;
namespace FLocal.IISHandler.handlers.response { namespace FLocal.IISHandler.handlers.response {
class UserRepliesHandler : AbstractUserGetHandler<FLocal.Common.URL.users.user.Replies> { class UserRepliesHandler : AbstractUserGetHandler<FLocal.Common.URL.users.user.Mentions> {
override protected string templateName { override protected string templateName {
get { get {
@ -22,7 +22,7 @@ namespace FLocal.IISHandler.handlers.response {
override protected IEnumerable<XElement> getUserSpecificData(WebContext context, User user) { override protected IEnumerable<XElement> getUserSpecificData(WebContext context, User user) {
PageOuter pageOuter = PageOuter.createFromUrl(this.url, context.userSettings.postsPerPage); PageOuter pageOuter = PageOuter.createFromUrl(this.url, context.userSettings.postsPerPage);
IEnumerable<Post> posts = user.getReplies(pageOuter, pageOuter.descendingDirection); IEnumerable<Post> posts = user.getMentions(pageOuter, pageOuter.descendingDirection);
return new XElement[] { return new XElement[] {
user.exportToXmlForViewing(context), user.exportToXmlForViewing(context),

@ -69,6 +69,9 @@
<EmbeddedResource Include="Resources\Patch_00000_cleaninstall.xml" /> <EmbeddedResource Include="Resources\Patch_00000_cleaninstall.xml" />
<EmbeddedResource Include="Resources\Patch_00001_threadsindexes.xml" /> <EmbeddedResource Include="Resources\Patch_00001_threadsindexes.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Patch_00002_mentions.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="..\..\Patcher\Resources\IPatch.xsd">
<version>
<number>2</number>
<author>mentions</author>
</version>
<looseCommandSet>
<persistentCommand>
<createTable>
<table>Mentions</table>
<primaryKey>
<column>Id</column>
<type>serial</type>
<isNotNull/>
</primaryKey>
<column>
<column>MentionedUserId</column>
<type>int</type>
<isNotNull/>
</column>
<column>
<column>PostId</column>
<type>integer</type>
<isNotNull/>
</column>
<column>
<column>Date</column>
<type>timestamp with time zone</type>
</column>
</createTable>
</persistentCommand>
<persistentCommand>
<createConstraint>
<table>Mentions</table>
<constraintName>Mentions_MentionedUserId_PostId_key</constraintName>
<unique>
<column>MentionedUserId</column>
<column>PostId</column>
</unique>
</createConstraint>
</persistentCommand>
<persistentCommand>
<createConstraint>
<table>Mentions</table>
<constraintName>Mentions_MentionedUserId_fkey</constraintName>
<foreignKey>
<column>MentionedUserId</column>
<referencedTable>Users</referencedTable>
<onUpdate>restrict</onUpdate>
<onDelete>restrict</onDelete>
</foreignKey>
</createConstraint>
</persistentCommand>
<persistentCommand>
<createConstraint>
<table>Mentions</table>
<constraintName>Mentions_PostId_fkey</constraintName>
<foreignKey>
<column>PostId</column>
<referencedTable>Posts</referencedTable>
<onUpdate>restrict</onUpdate>
<onDelete>restrict</onDelete>
</foreignKey>
</createConstraint>
</persistentCommand>
<command>
<sql>
<installSql>
<query>
INSERT INTO
"Mentions"("MentionedUserId", "PostId", "Date")
(
SELECT
parent."PosterId" "MentionedUserId",
post."Id" "PostId",
post."LastChangeDate" "Date"
FROM "Posts" post
JOIN "Posts" parent
ON post."ParentPostId" = parent."Id"
WHERE
post."PosterId" != parent."PosterId"
ORDER BY
post."Id" asc
)
</query>
</installSql>
<uninstallSql/>
</sql>
</command>
</looseCommandSet>
</patch>

@ -95,7 +95,7 @@ namespace MySQLConnector {
} }
} }
private List<string> _LoadIdsByConditions(DbCommand command, ITableSpec table, Web.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists) { private List<string> _LoadIdsByConditions(DbCommand command, ITableSpec table, Web.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, ColumnSpec idSpec, bool allowHugeLists) {
using(var logger = this.CreateCommandExecutionLogger()) { using(var logger = this.CreateCommandExecutionLogger()) {
command.CommandType = System.Data.CommandType.Text; command.CommandType = System.Data.CommandType.Text;
@ -164,7 +164,7 @@ namespace MySQLConnector {
if(diapasone.count >= 0) { if(diapasone.count >= 0) {
queryLimits = "LIMIT " + diapasone.count + " OFFSET " + diapasone.start; queryLimits = "LIMIT " + diapasone.count + " OFFSET " + diapasone.start;
} }
command.CommandText = logger.commandText = "SELECT " + table.getIdSpec().compile(this.traits) + " " + queryMain + " " + querySorts + " " + queryLimits; command.CommandText = logger.commandText = "SELECT " + idSpec.compile(this.traits) + " " + queryMain + " " + querySorts + " " + queryLimits;
List<string> result = new List<string>(); List<string> result = new List<string>();
using(DbDataReader reader = command.ExecuteReader()) { using(DbDataReader reader = command.ExecuteReader()) {
@ -177,10 +177,10 @@ namespace MySQLConnector {
} }
} }
public List<string> LoadIdsByConditions(ITableSpec table, Web.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists) { public List<string> LoadIdsByConditions(ITableSpec table, Web.Core.DB.conditions.AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, ColumnSpec idSpec, bool allowHugeLists) {
using(DbConnection connection = this.createConnection()) { using(DbConnection connection = this.createConnection()) {
using(DbCommand command = connection.CreateCommand()) { using(DbCommand command = connection.CreateCommand()) {
return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, allowHugeLists); return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, idSpec, allowHugeLists);
} }
} }
} }
@ -274,7 +274,7 @@ namespace MySQLConnector {
lock(transaction) { lock(transaction) {
using(DbCommand command = transaction.sqlconnection.CreateCommand()) { using(DbCommand command = transaction.sqlconnection.CreateCommand()) {
command.Transaction = transaction.sqltransaction; command.Transaction = transaction.sqltransaction;
return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, allowHugeLists); return this._LoadIdsByConditions(command, table, conditions, diapasone, joins, sorts, table.getIdSpec(), allowHugeLists);
} }
} }
} }

@ -284,3 +284,7 @@ a.external {
margin-left:0.5em; margin-left:0.5em;
margin-right:0.5em; margin-right:0.5em;
} }
.userreference:before {
content: "@";
}

@ -58,7 +58,7 @@
<br /> <br />
<a> <a>
<xsl:if test="session/user"> <xsl:if test="session/user">
<xsl:attribute name="href">/Users/User/<xsl:value-of select="session/user/id"/>/Replies/</xsl:attribute> <xsl:attribute name="href">/Users/User/<xsl:value-of select="session/user/id"/>/Mentions/</xsl:attribute>
</xsl:if> </xsl:if>
<xsl:text>Последние ответы на мои сообщения</xsl:text> <xsl:text>Последние ответы на мои сообщения</xsl:text>
</a> </a>

@ -192,8 +192,8 @@
</a> </a>
<xsl:text> | </xsl:text> <xsl:text> | </xsl:text>
<a> <a>
<xsl:attribute name="href">/Users/User/<xsl:value-of select="user/id"/>/Replies/</xsl:attribute> <xsl:attribute name="href">/Users/User/<xsl:value-of select="user/id"/>/Mentions/</xsl:attribute>
<xsl:text>Îòâåòû</xsl:text> <xsl:text>Îòêëèêè</xsl:text>
</a> </a>
<xsl:text> | </xsl:text> <xsl:text> | </xsl:text>
<a> <a>

@ -209,8 +209,8 @@
</a> </a>
<xsl:text> | </xsl:text> <xsl:text> | </xsl:text>
<a> <a>
<xsl:attribute name="href">/Users/User/<xsl:value-of select="user/id"/>/Replies/</xsl:attribute> <xsl:attribute name="href">/Users/User/<xsl:value-of select="user/id"/>/Mentions/</xsl:attribute>
<xsl:text>Îòâåòû</xsl:text> <xsl:text>Îòêëèêè</xsl:text>
</a> </a>
<xsl:text> | </xsl:text> <xsl:text> | </xsl:text>
<a> <a>

@ -53,7 +53,7 @@
<xsl:text>. </xsl:text> <xsl:text>. </xsl:text>
<a> <a>
<xsl:if test="session/user"> <xsl:if test="session/user">
<xsl:attribute name="href">/Users/User/<xsl:value-of select="session/user/id"/>/Replies/</xsl:attribute> <xsl:attribute name="href">/Users/User/<xsl:value-of select="session/user/id"/>/Mentions/</xsl:attribute>
</xsl:if> </xsl:if>
<xsl:call-template name="Messages_LastReplies"/> <xsl:call-template name="Messages_LastReplies"/>
</a> </a>

@ -17,8 +17,8 @@
<xsl:with-param name="text">Ñîîáùåíèÿ</xsl:with-param> <xsl:with-param name="text">Ñîîáùåíèÿ</xsl:with-param>
</xsl:call-template> </xsl:call-template>
<xsl:call-template name="headerLink"> <xsl:call-template name="headerLink">
<xsl:with-param name="url">/Users/User/<xsl:value-of select="session/user/id"/>/Replies/</xsl:with-param> <xsl:with-param name="url">/Users/User/<xsl:value-of select="session/user/id"/>/Mentions/</xsl:with-param>
<xsl:with-param name="text">Îòâåòû</xsl:with-param> <xsl:with-param name="text">Îòêëèêè</xsl:with-param>
<xsl:with-param name="isDisabled"> <xsl:with-param name="isDisabled">
<xsl:if test="not(session/sessionKey)">true</xsl:if> <xsl:if test="not(session/sessionKey)">true</xsl:if>
</xsl:with-param> </xsl:with-param>

@ -17,8 +17,8 @@
<xsl:with-param name="text">Ñîîáùåíèÿ</xsl:with-param> <xsl:with-param name="text">Ñîîáùåíèÿ</xsl:with-param>
</xsl:call-template> </xsl:call-template>
<xsl:call-template name="headerLink"> <xsl:call-template name="headerLink">
<xsl:with-param name="url"><xsl:value-of select="$baseLink"/>Replies/</xsl:with-param> <xsl:with-param name="url"><xsl:value-of select="$baseLink"/>Mentions/</xsl:with-param>
<xsl:with-param name="text">Îòâåòû</xsl:with-param> <xsl:with-param name="text">Îòêëèêè</xsl:with-param>
</xsl:call-template> </xsl:call-template>
<xsl:call-template name="headerLink"> <xsl:call-template name="headerLink">
<xsl:with-param name="url"><xsl:value-of select="$baseLink"/>PollsParticipated/</xsl:with-param> <xsl:with-param name="url"><xsl:value-of select="$baseLink"/>PollsParticipated/</xsl:with-param>

@ -22,127 +22,127 @@ Imports System.Xml.Serialization
''' </summary> ''' </summary>
<Serializable()> _ <Serializable()> _
<XmlRoot(ElementName:=STR_BBCodeConfigurationXmlElement, [Namespace]:=STR_BBCodeSchemaNamespace)> _ <XmlRoot(ElementName:=STR_BBCodeConfigurationXmlElement, [Namespace]:=STR_BBCodeSchemaNamespace)> _
Public NotInheritable Class BBCodeConfiguration Public NotInheritable Class BBCodeConfiguration(Of TContext As Class)
Implements IXmlSerializable Implements IXmlSerializable
Private Shared ReadOnly __CurrentVersion As New System.Version(1, 0) Private Shared ReadOnly __CurrentVersion As New System.Version(1, 0)
Private __Version As System.Version Private __Version As System.Version
Private __Dictionary As BBCodeElementDictionary Private __Dictionary As BBCodeElementDictionary
Private __ElementTypes As BBCodeElementTypeDictionary Private __ElementTypes As BBCodeElementTypeDictionary(Of TContext)
''' <summary>Initializes an instance of the <see cref="BBCodeConfiguration" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeConfiguration" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Friend Sub New() Friend Sub New()
__Version = New Version(1, 0) __Version = New Version(1, 0)
End Sub End Sub
''' <summary> ''' <summary>
''' Gets the version of the configuration file. ''' Gets the version of the configuration file.
''' </summary> ''' </summary>
Public Property Version() As System.Version Public Property Version() As System.Version
Get Get
Return __Version Return __Version
End Get End Get
Private Set(ByVal value As System.Version) Private Set(ByVal value As System.Version)
If (value > __CurrentVersion) Then If (value > __CurrentVersion) Then
Throw New ArgumentException("Unrecognized version.") Throw New ArgumentException("Unrecognized version.")
End If End If
__Version = value __Version = value
End Set End Set
End Property End Property
''' <summary> ''' <summary>
''' Gets the dictionary configuration. ''' Gets the dictionary configuration.
''' </summary> ''' </summary>
<XmlArrayItem()> _ <XmlArrayItem()> _
Public ReadOnly Property Dictionary() As BBCodeElementDictionary Public ReadOnly Property Dictionary() As BBCodeElementDictionary
Get Get
If (__Dictionary Is Nothing) Then If (__Dictionary Is Nothing) Then
__Dictionary = New BBCodeElementDictionary() __Dictionary = New BBCodeElementDictionary()
End If End If
Return __Dictionary Return __Dictionary
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets the factory configuration. ''' Gets the factory configuration.
''' </summary> ''' </summary>
<XmlArrayItem()> _ <XmlArrayItem()> _
Public ReadOnly Property ElementTypes() As BBCodeElementTypeDictionary Public ReadOnly Property ElementTypes() As BBCodeElementTypeDictionary(Of TContext)
Get Get
If (__ElementTypes Is Nothing) Then If (__ElementTypes Is Nothing) Then
__ElementTypes = New BBCodeElementTypeDictionary() __ElementTypes = New BBCodeElementTypeDictionary(Of TContext)()
End If End If
Return __ElementTypes Return __ElementTypes
End Get End Get
End Property End Property
''' <summary>This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute" /> to the class.</summary> ''' <summary>This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute" /> to the class.</summary>
''' <returns>An <see cref="T:System.Xml.Schema.XmlSchema" /> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)" /> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)" /> method.</returns> ''' <returns>An <see cref="T:System.Xml.Schema.XmlSchema" /> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)" /> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)" /> method.</returns>
Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
Return Nothing Return Nothing
End Function End Function
''' <summary>Generates an object from its XML representation.</summary> ''' <summary>Generates an object from its XML representation.</summary>
''' <param name="reader">The <see cref="T:System.Xml.XmlReader" /> stream from which the object is deserialized.</param> ''' <param name="reader">The <see cref="T:System.Xml.XmlReader" /> stream from which the object is deserialized.</param>
Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
'* '*
'* Check the name of the element '* Check the name of the element
'* '*
If (reader.NamespaceURI <> STR_BBCodeSchemaNamespace) OrElse (reader.LocalName <> STR_BBCodeConfigurationXmlElement) Then If (reader.NamespaceURI <> STR_BBCodeSchemaNamespace) OrElse (reader.LocalName <> STR_BBCodeConfigurationXmlElement) Then
Exit Sub Exit Sub
End If End If
'* '*
'* Gets the version of the configuration '* Gets the version of the configuration
'* '*
Dim versionString = reader.GetAttribute("version") Dim versionString = reader.GetAttribute("version")
If (Not String.IsNullOrEmpty(versionString)) Then If (Not String.IsNullOrEmpty(versionString)) Then
Me.Version = New System.Version(versionString) Me.Version = New System.Version(versionString)
End If End If
'* '*
'* Move to the first item '* Move to the first item
'* '*
reader.Read() reader.Read()
Dim dictionarySerializer = New XmlSerializer(GetType(BBCodeElementDictionary)) Dim dictionarySerializer = New XmlSerializer(GetType(BBCodeElementDictionary))
Dim typesSerializer = New XmlSerializer(GetType(BBCodeElementTypeDictionary)) Dim typesSerializer = New XmlSerializer(GetType(BBCodeElementTypeDictionary(Of TContext)))
__ElementTypes = Nothing __ElementTypes = Nothing
__Dictionary = Nothing __Dictionary = Nothing
If (reader.LocalName = STR_BBCodeElementTypesXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then If (reader.LocalName = STR_BBCodeElementTypesXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then
__ElementTypes = typesSerializer.Deserialize(reader) __ElementTypes = typesSerializer.Deserialize(reader)
End If End If
If (reader.LocalName = STR_BBCodeDictionaryXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then If (reader.LocalName = STR_BBCodeDictionaryXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then
__Dictionary = dictionarySerializer.Deserialize(reader) __Dictionary = dictionarySerializer.Deserialize(reader)
End If End If
If (__ElementTypes Is Nothing AndAlso reader.LocalName = STR_BBCodeElementTypesXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then If (__ElementTypes Is Nothing AndAlso reader.LocalName = STR_BBCodeElementTypesXmlElement AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Then
__ElementTypes = typesSerializer.Deserialize(reader) __ElementTypes = typesSerializer.Deserialize(reader)
End If End If
End Sub End Sub
''' <summary>Converts an object into its XML representation.</summary> ''' <summary>Converts an object into its XML representation.</summary>
''' <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> stream to which the object is serialized.</param> ''' <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> stream to which the object is serialized.</param>
Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
Dim dictionarySerializer = New XmlSerializer(GetType(BBCodeElementDictionary)) Dim dictionarySerializer = New XmlSerializer(GetType(BBCodeElementDictionary))
Dim typesSerializer = New XmlSerializer(GetType(BBCodeElementTypeDictionary)) Dim typesSerializer = New XmlSerializer(GetType(BBCodeElementTypeDictionary(Of TContext)))
If (ElementTypes.Count > 0) Then If (ElementTypes.Count > 0) Then
typesSerializer.Serialize(writer, ElementTypes) typesSerializer.Serialize(writer, ElementTypes)
End If End If
If (Dictionary.Count > 0) Then If (Dictionary.Count > 0) Then
dictionarySerializer.Serialize(writer, Dictionary) dictionarySerializer.Serialize(writer, Dictionary)
End If End If
End Sub End Sub
End Class End Class

@ -18,77 +18,77 @@
''' <summary> ''' <summary>
''' Represents a document writen in BBCode. ''' Represents a document writen in BBCode.
''' </summary> ''' </summary>
Public NotInheritable Class BBCodeDocument Public NotInheritable Class BBCodeDocument(Of TContext As Class)
Private __Text As String Private __Text As String
Private __Parser As BBCodeParser Private __Parser As BBCodeParser(Of TContext)
Private __Nodes As BBCodeNodeCollection Private __Nodes As BBCodeNodeCollection(Of TContext)
''' <summary>Initializes an instance of the <see cref="BBCodeDocument" /> class.</summary> ''' <summary>Initializes an instance of the <see cref="BBCodeDocument" /> class.</summary>
''' <param name="parser">The <see cref="BBCodeParser"/> that created this instance.</param> ''' <param name="parser">The <see cref="BBCodeParser"/> that created this instance.</param>
Friend Sub New(ByVal parser As BBCodeParser) Friend Sub New(ByVal parser As BBCodeParser(Of TContext))
__Parser = parser __Parser = parser
End Sub End Sub
''' <summary> ''' <summary>
''' Gets the <see cref="BBCodeParser"/> that is responsible for this <see cref="BBCodeDocument"/>. ''' Gets the <see cref="BBCodeParser"/> that is responsible for this <see cref="BBCodeDocument"/>.
''' </summary> ''' </summary>
Friend ReadOnly Property Parser() As BBCodeParser Friend ReadOnly Property Parser() As BBCodeParser(Of TContext)
Get Get
Return __Parser Return __Parser
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets or sets the BBCode of the document. ''' Gets or sets the BBCode of the document.
''' </summary> ''' </summary>
Public Property Text() As String Public Property Text() As String
Get Get
Return __Text Return __Text
End Get End Get
Set(ByVal value As String) Set(ByVal value As String)
__Text = value __Text = value
Nodes.Clear() Nodes.Clear()
Nodes.AddRange(Me.Parser.Parse(value).Nodes) Nodes.AddRange(Me.Parser.Parse(value).Nodes)
End Set End Set
End Property End Property
''' <summary> ''' <summary>
''' Gets the <see cref="BBCodeNodeCollection"/> generated ba the BBCode text. ''' Gets the <see cref="BBCodeNodeCollection"/> generated ba the BBCode text.
''' </summary> ''' </summary>
''' <value>A <see cref="BBCodeNodeCollection"/> that represents the parsed text of the document.</value> ''' <value>A <see cref="BBCodeNodeCollection"/> that represents the parsed text of the document.</value>
Public ReadOnly Property Nodes() As BBCodeNodeCollection Public ReadOnly Property Nodes() As BBCodeNodeCollection(Of TContext)
Get Get
If (__Nodes Is Nothing) Then If (__Nodes Is Nothing) Then
__Nodes = New BBCodeNodeCollection() __Nodes = New BBCodeNodeCollection(Of TContext)()
End If End If
Return __Nodes Return __Nodes
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Returns the formatted text. ''' Returns the formatted text.
''' </summary> ''' </summary>
''' <returns>The formatted text.</returns> ''' <returns>The formatted text.</returns>
Public Function Format() As String Public Function Format(ByVal context As TContext) As String
Return Format(New BBCodeHtmlFormatter()) Return Format(context, New BBCodeHtmlFormatter(Of TContext)())
End Function End Function
''' <summary> ''' <summary>
''' Returns the formatted text, using the specified <see cref="ITextFormatter"/>. ''' Returns the formatted text, using the specified <see cref="ITextFormatter"/>.
''' </summary> ''' </summary>
''' <param name="formatter">An object that implements the <see cref="ITextFormatter"/> interface.</param> ''' <param name="formatter">An object that implements the <see cref="ITextFormatter"/> interface.</param>
''' <returns>The formatted text.</returns> ''' <returns>The formatted text.</returns>
Public Function Format(ByVal formatter As ITextFormatter) As String Public Function Format(ByVal context As TContext, ByVal formatter As ITextFormatter(Of TContext)) As String
Dim sb As New Text.StringBuilder() Dim sb As New Text.StringBuilder()
For Each n In Nodes For Each n In Nodes
sb.Append(n.Format(formatter)) sb.Append(n.Format(context, formatter))
Next Next
Return sb.ToString() Return sb.ToString()
End Function End Function
Friend Sub SetText(ByVal text As String) Friend Sub SetText(ByVal text As String)
__Text = text __Text = text
End Sub End Sub
End Class End Class

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

@ -18,84 +18,84 @@
''' <summary> ''' <summary>
''' Creates instances of <see cref="BBCodeElement"/>. ''' Creates instances of <see cref="BBCodeElement"/>.
''' </summary> ''' </summary>
Friend NotInheritable Class BBCodeElementFactory Friend NotInheritable Class BBCodeElementFactory(Of TContext As Class)
Private __Parser As BBCodeParser Private __Parser As BBCodeParser(Of TContext)
''' <summary>Initializes an instance of the <see cref="BBCodeElementFactory" /> class.</summary> ''' <summary>Initializes an instance of the <see cref="BBCodeElementFactory" /> class.</summary>
''' <param name="parser">The <see cref="BBCodeParser"/> that will utilize the new instance of the <see cref="BBCodeElementFactory"/>.</param> ''' <param name="parser">The <see cref="BBCodeParser"/> that will utilize the new instance of the <see cref="BBCodeElementFactory"/>.</param>
Friend Sub New(ByVal parser As BBCodeParser) Friend Sub New(ByVal parser As BBCodeParser(Of TContext))
__Parser = parser __Parser = parser
End Sub End Sub
''' <summary> ''' <summary>
''' Gets the <see cref="BBCodeParser"/> that utilizes this instance of the <see cref="BBCodeElementFactory"/>. ''' Gets the <see cref="BBCodeParser"/> that utilizes this instance of the <see cref="BBCodeElementFactory"/>.
''' </summary> ''' </summary>
Public ReadOnly Property Parser() As BBCodeParser Public ReadOnly Property Parser() As BBCodeParser(Of TContext)
Get Get
Return __Parser Return __Parser
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Creates a new <see cref="BBCodeElement"/>. ''' Creates a new <see cref="BBCodeElement"/>.
''' </summary> ''' </summary>
''' <param name="name">The name of the element.</param> ''' <param name="name">The name of the element.</param>
''' <param name="attributes">The attributes of the element.</param> ''' <param name="attributes">The attributes of the element.</param>
''' <returns>A new <see cref="BBCodeElement"/>.</returns> ''' <returns>A new <see cref="BBCodeElement"/>.</returns>
Public Function CreateElement(ByVal name As String, ByVal attributes As BBCodeAttributeDictionary) As BBCodeElement Public Function CreateElement(ByVal name As String, ByVal attributes As BBCodeAttributeDictionary) As BBCodeElement(Of TContext)
Dim el As BBCodeElement Dim el As BBCodeElement(Of TContext)
'* '*
'* Check if we have a different type to create '* Check if we have a different type to create
'* '*
If (Parser.ElementTypes.ContainsKey(name.ToUpperInvariant())) Then If (Parser.ElementTypes.ContainsKey(name.ToUpperInvariant())) Then
'* '*
'* Gets the type of the element '* Gets the type of the element
'* '*
Dim type = Parser.ElementTypes(name.ToUpperInvariant()).Type Dim type = Parser.ElementTypes(name.ToUpperInvariant()).Type
'* '*
'* Check if the type IS BBCodeElement '* Check if the type IS BBCodeElement
'* '*
If (type.Equals(GetType(BBCodeElement))) Then If (type.Equals(GetType(BBCodeElement(Of TContext)))) Then
el = New BBCodeElement() el = New BBCodeElement(Of TContext)()
Else Else
'* '*
'* Gets the default constructor '* Gets the default constructor
'* '*
Dim ctor = type.GetConstructor(New Type() {}) Dim ctor = type.GetConstructor(New Type() {})
If (ctor Is Nothing) Then If (ctor Is Nothing) Then
Throw New InvalidOperationException("The type " & type.FullName & " does not have a default constructor.") Throw New InvalidOperationException("The type " & type.FullName & " does not have a default constructor.")
End If End If
'* '*
'* Creates the new element. '* Creates the new element.
'* '*
el = TryCast(ctor.Invoke(New Object() {}), BBCodeElement) el = TryCast(ctor.Invoke(New Object() {}), BBCodeElement(Of TContext))
If (el Is Nothing) Then If (el Is Nothing) Then
Throw New InvalidOperationException("The type " & type.FullName & " could not be assingned to BBCodeElement.") Throw New InvalidOperationException("The type " & type.FullName & " could not be assingned to BBCodeElement.")
End If End If
End If End If
Else Else
el = New BBCodeElement() el = New BBCodeElement(Of TContext)()
End If End If
'* '*
'* Set the element properties '* Set the element properties
'* '*
el.SetName(name) el.SetName(name)
el.SetParser(Parser) el.SetParser(Parser)
For Each attr In attributes For Each attr In attributes
el.Attributes.Add(attr.Key, attr.Value) el.Attributes.Add(attr.Key, attr.Value)
Next Next
'* '*
'* Returns the created element. '* Returns the created element.
'* '*
Return el Return el
End Function End Function
End Class End Class

@ -24,95 +24,95 @@ Imports System.Diagnostics.CodeAnalysis
''' </summary> ''' </summary>
<Serializable()> _ <Serializable()> _
<XmlRoot(ElementName:=STR_BBCodeElementTypesXmlElement, [Namespace]:=STR_BBCodeSchemaNamespace)> _ <XmlRoot(ElementName:=STR_BBCodeElementTypesXmlElement, [Namespace]:=STR_BBCodeSchemaNamespace)> _
Public NotInheritable Class BBCodeElementTypeDictionary Public NotInheritable Class BBCodeElementTypeDictionary(Of TContext As Class)
Inherits Dictionary(Of String, BBCodeElementTypeDefinition) Inherits Dictionary(Of String, BBCodeElementTypeDefinition)
Implements IXmlSerializable Implements IXmlSerializable
Private Const STR_ConfigurationItem As String = "element" Private Const STR_ConfigurationItem As String = "element"
''' <summary>Initializes an instance of the <see cref="BBCodeElementTypeDictionary" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeElementTypeDictionary" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Friend Sub New() Friend Sub New()
End Sub End Sub
''' <summary>Initializes a new instance of the <see cref="BBCodeElementDictionary" /> class with serialized data.</summary> ''' <summary>Initializes a new instance of the <see cref="BBCodeElementDictionary" /> class with serialized data.</summary>
''' <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo" /> object containing the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2" />.</param> ''' <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo" /> object containing the information required to serialize the <see cref="T:System.Collections.Generic.Dictionary`2" />.</param>
''' <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext" /> structure containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2" />.</param> ''' <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext" /> structure containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2" />.</param>
Private Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext) Private Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext)
MyBase.New(info, context) MyBase.New(info, context)
End Sub End Sub
''' <summary> ''' <summary>
''' Adds the specified key and value to the dictionary. ''' Adds the specified key and value to the dictionary.
''' </summary> ''' </summary>
''' <param name="tagName">The key of the element to add.</param> ''' <param name="tagName">The key of the element to add.</param>
''' <param name="value">The value of the element to add.</param> ''' <param name="value">The value of the element to add.</param>
Private Shadows Sub Add(ByVal tagName As String, ByVal value As BBCodeElementTypeDefinition) Private Shadows Sub Add(ByVal tagName As String, ByVal value As BBCodeElementTypeDefinition)
ValidateTagName(tagName) ValidateTagName(tagName)
ValidateBBCodeElementType(value.Type) ValidateBBCodeElementType(Of TContext)(value.Type)
MyBase.Add(tagName.ToUpperInvariant(), value) MyBase.Add(tagName.ToUpperInvariant(), value)
End Sub End Sub
''' <summary> ''' <summary>
''' Adds the specified key and value to the dictionary. ''' Adds the specified key and value to the dictionary.
''' </summary> ''' </summary>
''' <param name="tagName">The key of the element to add.</param> ''' <param name="tagName">The key of the element to add.</param>
''' <param name="value">The value of the element to add.</param> ''' <param name="value">The value of the element to add.</param>
Public Shadows Sub Add(ByVal tagName As String, ByVal value As Type, ByVal requireClosingTag As Boolean) Public Shadows Sub Add(ByVal tagName As String, ByVal value As Type, ByVal requireClosingTag As Boolean)
Add(tagName.ToUpperInvariant(), New BBCodeElementTypeDefinition() With {.TagName = tagName, .Type = value, .RequireClosingTag = requireClosingTag}) Add(tagName.ToUpperInvariant(), New BBCodeElementTypeDefinition() With {.TagName = tagName, .Type = value, .RequireClosingTag = requireClosingTag})
End Sub End Sub
''' <summary>This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute" /> to the class.</summary> ''' <summary>This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute" /> to the class.</summary>
''' <returns>An <see cref="T:System.Xml.Schema.XmlSchema" /> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)" /> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)" /> method.</returns> ''' <returns>An <see cref="T:System.Xml.Schema.XmlSchema" /> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)" /> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)" /> method.</returns>
Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
Return Nothing Return Nothing
End Function End Function
''' <summary>Generates an object from its XML representation.</summary> ''' <summary>Generates an object from its XML representation.</summary>
''' <param name="reader">The <see cref="T:System.Xml.XmlReader" /> stream from which the object is deserialized.</param> ''' <param name="reader">The <see cref="T:System.Xml.XmlReader" /> stream from which the object is deserialized.</param>
Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
'* '*
'* Check the name of the element '* Check the name of the element
'* '*
If (reader.NamespaceURI <> STR_BBCodeSchemaNamespace) OrElse (reader.LocalName <> STR_BBCodeElementTypesXmlElement) Then If (reader.NamespaceURI <> STR_BBCodeSchemaNamespace) OrElse (reader.LocalName <> STR_BBCodeElementTypesXmlElement) Then
Exit Sub Exit Sub
End If End If
'* '*
'* Reads the items '* Reads the items
'* '*
Do While (reader.Read() AndAlso reader.LocalName = STR_ConfigurationItem AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace) Do While (reader.Read() AndAlso reader.LocalName = STR_ConfigurationItem AndAlso reader.NamespaceURI = STR_BBCodeSchemaNamespace)
Dim definition As New BBCodeElementTypeDefinition() Dim definition As New BBCodeElementTypeDefinition()
With definition With definition
.TagName = reader.GetAttribute("name") .TagName = reader.GetAttribute("name")
Boolean.TryParse(reader.GetAttribute("requireClosingTag"), .RequireClosingTag) Boolean.TryParse(reader.GetAttribute("requireClosingTag"), .RequireClosingTag)
.Type = System.Type.GetType(reader.GetAttribute("type")) .Type = System.Type.GetType(reader.GetAttribute("type"))
End With End With
If (definition.Type.IsSubclassOf(GetType(BBCodeElement))) Then If (definition.Type.IsSubclassOf(GetType(BBCodeElement(Of TContext)))) Then
Me.Add(definition.TagName, definition) Me.Add(definition.TagName, definition)
End If End If
Loop Loop
If (reader.NodeType = Xml.XmlNodeType.EndElement) Then If (reader.NodeType = Xml.XmlNodeType.EndElement) Then
reader.Read() reader.Read()
End If End If
End Sub End Sub
''' <summary>Converts an object into its XML representation.</summary> ''' <summary>Converts an object into its XML representation.</summary>
''' <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> stream to which the object is serialized.</param> ''' <param name="writer">The <see cref="T:System.Xml.XmlWriter" /> stream to which the object is serialized.</param>
<SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", justification:="For human legibility purposes the tagname must be lower case in the configuration file.")> _ <SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", justification:="For human legibility purposes the tagname must be lower case in the configuration file.")> _
Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
For Each it In Me For Each it In Me
writer.WriteStartElement(STR_ConfigurationItem, STR_BBCodeSchemaNamespace) writer.WriteStartElement(STR_ConfigurationItem, STR_BBCodeSchemaNamespace)
writer.WriteAttributeString("name", it.Key.ToLower(CultureInfo.InvariantCulture)) writer.WriteAttributeString("name", it.Key.ToLower(CultureInfo.InvariantCulture))
writer.WriteAttributeString("requireClosingTag", it.Value.RequireClosingTag.ToString()) writer.WriteAttributeString("requireClosingTag", it.Value.RequireClosingTag.ToString())
writer.WriteAttributeString("type", it.Value.Type.AssemblyQualifiedName) writer.WriteAttributeString("type", it.Value.Type.AssemblyQualifiedName)
writer.WriteEndElement() writer.WriteEndElement()
Next Next
End Sub End Sub
End Class End Class

@ -18,20 +18,20 @@
''' <summary> ''' <summary>
''' Represnts an HTML generator for the <see cref="BBCodeParser"/>. ''' Represnts an HTML generator for the <see cref="BBCodeParser"/>.
''' </summary> ''' </summary>
Public NotInheritable Class BBCodeHtmlFormatter Public NotInheritable Class BBCodeHtmlFormatter(Of TContext As Class)
Implements ITextFormatter Implements ITextFormatter(Of TContext)
''' <summary> ''' <summary>
''' Generates the desired text from the specified source. ''' Generates the desired text from the specified source.
''' </summary> ''' </summary>
''' <param name="source">The source to generate the text.</param> ''' <param name="source">The source to generate the text.</param>
''' <returns>The text generated by the source.</returns> ''' <returns>The text generated by the source.</returns>
Public Function GenerateText(ByVal source As String) As String Implements ITextFormatter.Format Public Function GenerateText(ByVal context As TContext, ByVal source As String) As String Implements ITextFormatter(Of TContext).Format
Dim sb As New Text.StringBuilder(HtmlEncode(source)) Dim sb As New Text.StringBuilder(HtmlEncode(source))
sb.Replace(vbCrLf, vbLf) sb.Replace(vbCrLf, vbLf)
sb.Replace(vbCr, String.Empty) sb.Replace(vbCr, String.Empty)
sb.Replace(vbLf, "<br/>") sb.Replace(vbLf, "<br/>")
Return sb.ToString() Return sb.ToString()
End Function End Function
End Class End Class

@ -18,84 +18,84 @@
''' <summary> ''' <summary>
''' Represents the basic node of an BBCode document. ''' Represents the basic node of an BBCode document.
''' </summary> ''' </summary>
Public MustInherit Class BBCodeNode Public MustInherit Class BBCodeNode(Of TContext As Class)
Private __Parent As BBCodeNode Private __Parent As BBCodeNode(Of TContext)
Private __Parser As BBCodeParser Private __Parser As BBCodeParser(Of TContext)
''' <summary>Initializes an instance of the <see cref="BBCodeNode" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeNode" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Protected Sub New() Protected Sub New()
End Sub End Sub
''' <summary>Initializes an instance of the <see cref="BBCodeNode" /> class.</summary> ''' <summary>Initializes an instance of the <see cref="BBCodeNode" /> class.</summary>
''' <param name="parser">The parser used to create this element.</param> ''' <param name="parser">The parser used to create this element.</param>
Protected Sub New(ByVal parser As BBCodeParser) Protected Sub New(ByVal parser As BBCodeParser(Of TContext))
__Parser = parser __Parser = parser
End Sub End Sub
''' <summary> ''' <summary>
''' Gets the parent node. ''' Gets the parent node.
''' </summary> ''' </summary>
Public ReadOnly Property Parent() As BBCodeNode Public ReadOnly Property Parent() As BBCodeNode(Of TContext)
Get Get
Return __Parent Return __Parent
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets the <see cref="BBCodeParser"/> that create this instance of the <see cref="BBCodeNode"/>. ''' Gets the <see cref="BBCodeParser"/> that create this instance of the <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
Protected Friend ReadOnly Property Parser() As BBCodeParser Protected Friend ReadOnly Property Parser() As BBCodeParser(Of TContext)
Get Get
Return __Parser Return __Parser
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' When implemented in a derived class, transforms this instance of <see cref="BBCodeNode"/> into its desired text representation. ''' When implemented in a derived class, transforms this instance of <see cref="BBCodeNode"/> into its desired text representation.
''' </summary> ''' </summary>
''' <param name="formatter">An object that implements the <see cref="ITextFormatter"/> interface.</param> ''' <param name="formatter">An object that implements the <see cref="ITextFormatter"/> interface.</param>
''' <returns>The text formatted by the <see cref="ITextFormatter"/>.</returns> ''' <returns>The text formatted by the <see cref="ITextFormatter"/>.</returns>
Public MustOverride Function Format(ByVal formatter As ITextFormatter) As String Public MustOverride Function Format(ByVal context As TContext, ByVal formatter As ITextFormatter(Of TContext)) As String
''' <summary> ''' <summary>
''' When implemented in a derived class, gets or sets the inner BBCode. ''' When implemented in a derived class, gets or sets the inner BBCode.
''' </summary> ''' </summary>
''' <value>The BBCode between the start and end tags.</value> ''' <value>The BBCode between the start and end tags.</value>
Public MustOverride Property InnerBBCode() As String Public MustOverride Property InnerBBCode() As String
''' <summary> ''' <summary>
''' When implemented in a derived class, gets the outer BBCode. ''' When implemented in a derived class, gets the outer BBCode.
''' </summary> ''' </summary>
''' <value>The BBCode of this instance of the <see cref="BBCodeNode"/>.</value> ''' <value>The BBCode of this instance of the <see cref="BBCodeNode"/>.</value>
Public MustOverride ReadOnly Property OuterBBCode() As String Public MustOverride ReadOnly Property OuterBBCode() As String
''' <summary> ''' <summary>
''' When implemented in a derived class, gets or sets the plain text of the node. ''' When implemented in a derived class, gets or sets the plain text of the node.
''' </summary> ''' </summary>
''' <value>The plain text between the start and end tags.</value> ''' <value>The plain text between the start and end tags.</value>
Public MustOverride Property InnerText() As String Public MustOverride Property InnerText() As String
''' <summary> ''' <summary>
''' Sets the <see cref="BBCodeParser"/> of this instance. ''' Sets the <see cref="BBCodeParser"/> of this instance.
''' </summary> ''' </summary>
''' <param name="parser">The new parser of this instance.</param> ''' <param name="parser">The new parser of this instance.</param>
Friend Sub SetParser(ByVal parser As BBCodeParser) Friend Sub SetParser(ByVal parser As BBCodeParser(Of TContext))
__Parser = parser __Parser = parser
End Sub End Sub
''' <summary> ''' <summary>
''' The the parent node of this instance of the <see cref="BBCodeNode"/>. ''' The the parent node of this instance of the <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
''' <param name="parentNode">The parent node.</param> ''' <param name="parentNode">The parent node.</param>
Protected Friend Sub SetParent(ByVal parentNode As BBCodeNode) Protected Friend Sub SetParent(ByVal parentNode As BBCodeNode(Of TContext))
Dim element = TryCast(parentNode, BBCodeElement) Dim element = TryCast(parentNode, BBCodeElement(Of TContext))
If (element Is Nothing) OrElse (String.IsNullOrEmpty(element.Name)) Then If (element Is Nothing) OrElse (String.IsNullOrEmpty(element.Name)) Then
__Parent = Nothing __Parent = Nothing
Else Else
__Parent = element __Parent = element
End If End If
End Sub End Sub
End Class End Class

@ -18,78 +18,78 @@
''' <summary> ''' <summary>
''' Represents a collection of <see cref="BBCodeNode"/>. ''' Represents a collection of <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
Public Class BBCodeNodeCollection Public Class BBCodeNodeCollection(Of TContext As Class)
Inherits ObjectModel.Collection(Of BBCodeNode) Inherits ObjectModel.Collection(Of BBCodeNode(Of TContext))
Private __Owner As BBCodeNode Private __Owner As BBCodeNode(Of TContext)
''' <summary>Initializes an instance of the <see cref="BBCodeNodeCollection" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeNodeCollection" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Friend Sub New() Friend Sub New()
End Sub End Sub
''' <summary>Initializes an instance of the <see cref="BBCodeNodeCollection" /> class.</summary> ''' <summary>Initializes an instance of the <see cref="BBCodeNodeCollection" /> class.</summary>
''' <param name="owner">The collection owner.</param> ''' <param name="owner">The collection owner.</param>
''' <exception cref="ArgumentNullException">The argument <paramref name="owner" /> is <langword name="null" />.</exception> ''' <exception cref="ArgumentNullException">The argument <paramref name="owner" /> is <langword name="null" />.</exception>
Friend Sub New(ByVal owner As BBCodeNode) Friend Sub New(ByVal owner As BBCodeNode(Of TContext))
If (owner Is Nothing) Then If (owner Is Nothing) Then
Throw New ArgumentNullException("owner") Throw New ArgumentNullException("owner")
End If End If
__Owner = owner __Owner = owner
End Sub End Sub
''' <summary> ''' <summary>
''' Gets or sets the owner of the collection. ''' Gets or sets the owner of the collection.
''' </summary> ''' </summary>
Friend ReadOnly Property Owner() As BBCodeNode Friend ReadOnly Property Owner() As BBCodeNode(Of TContext)
Get Get
Return __Owner Return __Owner
End Get End Get
End Property End Property
''' <summary>Adds an object to the end of the <see cref="BBCodeNodeCollection" />.</summary> ''' <summary>Adds an object to the end of the <see cref="BBCodeNodeCollection" />.</summary>
''' <param name="node">The object to be added to the end of the <see cref="BBCodeNodeCollection" />.</param> ''' <param name="node">The object to be added to the end of the <see cref="BBCodeNodeCollection" />.</param>
''' <exception cref="ArgumentNullException">The argument <paramref name="node" /> is <langword name="null" />.</exception> ''' <exception cref="ArgumentNullException">The argument <paramref name="node" /> is <langword name="null" />.</exception>
Public Shadows Sub Add(ByVal node As BBCodeNode) Public Shadows Sub Add(ByVal node As BBCodeNode(Of TContext))
If (node Is Nothing) Then If (node Is Nothing) Then
Throw New ArgumentNullException("node") Throw New ArgumentNullException("node")
End If End If
node.SetParent(Me.Owner) node.SetParent(Me.Owner)
MyBase.Add(node) MyBase.Add(node)
End Sub End Sub
''' <summary>Adds the elements of the specified collection to the end of the <see cref="BBCodeNodeCollection" />.</summary> ''' <summary>Adds the elements of the specified collection to the end of the <see cref="BBCodeNodeCollection" />.</summary>
''' <param name="collection">The collection whose elements should be added to the end of the <see cref="BBCodeNodeCollection" />.</param> ''' <param name="collection">The collection whose elements should be added to the end of the <see cref="BBCodeNodeCollection" />.</param>
''' <exception cref="ArgumentNullException">The argument <paramref name="collection" /> is <langword name="null" />.</exception> ''' <exception cref="ArgumentNullException">The argument <paramref name="collection" /> is <langword name="null" />.</exception>
Public Shadows Sub AddRange(ByVal collection As IEnumerable(Of BBCodeNode)) Public Shadows Sub AddRange(ByVal collection As IEnumerable(Of BBCodeNode(Of TContext)))
If (collection Is Nothing) Then If (collection Is Nothing) Then
Throw New ArgumentNullException("collection") Throw New ArgumentNullException("collection")
End If End If
For Each n In collection For Each n In collection
Me.Add(n) Me.Add(n)
Next Next
End Sub End Sub
''' <summary> ''' <summary>
''' Inserts an element into the <see cref="BBCodeNodeCollection" /> at the specified index. ''' Inserts an element into the <see cref="BBCodeNodeCollection" /> at the specified index.
''' </summary> ''' </summary>
''' <param name="index">The zero-based index at which item should be inserted.</param> ''' <param name="index">The zero-based index at which item should be inserted.</param>
''' <param name="node">The object to insert.</param> ''' <param name="node">The object to insert.</param>
Public Shadows Sub Insert(ByVal index As Integer, ByVal node As BBCodeNode) Public Shadows Sub Insert(ByVal index As Integer, ByVal node As BBCodeNode(Of TContext))
node.SetParent(Me.Owner) node.SetParent(Me.Owner)
MyBase.Insert(index, node) MyBase.Insert(index, node)
End Sub End Sub
''' <summary> ''' <summary>
''' Inserts the elements of a collection into the <see cref="BBCodeNodeCollection" /> at the specified index. ''' Inserts the elements of a collection into the <see cref="BBCodeNodeCollection" /> at the specified index.
''' </summary> ''' </summary>
''' <param name="index">The zero-based index at which item should be inserted.</param> ''' <param name="index">The zero-based index at which item should be inserted.</param>
''' <param name="collection">The collection whose elements should be inserted into the <see cref="BBCodeNodeCollection" />.</param> ''' <param name="collection">The collection whose elements should be inserted into the <see cref="BBCodeNodeCollection" />.</param>
Public Shadows Sub InsertRange(ByVal index As Integer, ByVal collection As IEnumerable(Of BBCodeNode)) Public Shadows Sub InsertRange(ByVal index As Integer, ByVal collection As IEnumerable(Of BBCodeNode(Of TContext)))
For Each node In collection For Each node In collection
node.SetParent(Me.Owner) node.SetParent(Me.Owner)
Next Next
MyBase.Insert(index, collection) MyBase.Insert(index, collection)
End Sub End Sub
End Class End Class

@ -21,320 +21,320 @@ Imports System.Diagnostics.CodeAnalysis
''' <summary> ''' <summary>
''' The parser of ''' The parser of
''' </summary> ''' </summary>
Public NotInheritable Class BBCodeParser Public NotInheritable Class BBCodeParser(Of TContext As Class)
Private __Factory As BBCodeElementFactory Private __Factory As BBCodeElementFactory(Of TContext)
Private __Configuration As BBCodeConfiguration Private __Configuration As BBCodeConfiguration(Of TContext)
Private Shared ReadOnly __ConfigSerializer As New System.Xml.Serialization.XmlSerializer(GetType(BBCodeConfiguration)) Private Shared ReadOnly __ConfigSerializer As New System.Xml.Serialization.XmlSerializer(GetType(BBCodeConfiguration(Of TContext)))
Private Shared ReadOnly __Tokenizer As Tokenization.Tokenizer = PrepareTokenizer() Private Shared ReadOnly __Tokenizer As Tokenization.Tokenizer = PrepareTokenizer()
''' <summary>Initializes an instance of the <see cref="BBCodeParser" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeParser" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Public Sub New() Public Sub New()
End Sub End Sub
''' <summary> ''' <summary>
''' Gets the dictionary of elements to be replaced by the <see cref="BBCodeParser"/>. ''' Gets the dictionary of elements to be replaced by the <see cref="BBCodeParser"/>.
''' </summary> ''' </summary>
Public ReadOnly Property Dictionary() As BBCodeElementDictionary Public ReadOnly Property Dictionary() As BBCodeElementDictionary
Get Get
If (__Configuration Is Nothing) Then If (__Configuration Is Nothing) Then
__Configuration = New BBCodeConfiguration() __Configuration = New BBCodeConfiguration(Of TContext)()
End If End If
Return __Configuration.Dictionary Return __Configuration.Dictionary
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets the dictionary of types created by the parser. ''' Gets the dictionary of types created by the parser.
''' </summary> ''' </summary>
Public ReadOnly Property ElementTypes() As BBCodeElementTypeDictionary Public ReadOnly Property ElementTypes() As BBCodeElementTypeDictionary(Of TContext)
Get Get
If (__Configuration Is Nothing) Then If (__Configuration Is Nothing) Then
__Configuration = New BBCodeConfiguration() __Configuration = New BBCodeConfiguration(Of TContext)()
End If End If
Return __Configuration.ElementTypes Return __Configuration.ElementTypes
End Get End Get
End Property End Property
#Region " LoadConfiguration Methods " #Region " LoadConfiguration Methods "
''' <summary> ''' <summary>
''' Loads the configuration from the specified filename. ''' Loads the configuration from the specified filename.
''' </summary> ''' </summary>
''' <param name="fileName">The name of the file to read the dictionary from.</param> ''' <param name="fileName">The name of the file to read the dictionary from.</param>
Public Sub LoadConfiguration(ByVal fileName As String) Public Sub LoadConfiguration(ByVal fileName As String)
If (String.IsNullOrEmpty(fileName)) Then If (String.IsNullOrEmpty(fileName)) Then
Throw New ArgumentNullException("fileName") Throw New ArgumentNullException("fileName")
End If End If
Using fileStream As New IO.FileStream(fileName, IO.FileMode.Open) Using fileStream As New IO.FileStream(fileName, IO.FileMode.Open)
LoadConfiguration(fileStream) LoadConfiguration(fileStream)
End Using End Using
End Sub End Sub
''' <summary> ''' <summary>
''' Loads the configuration from the specified <see cref="IO.Stream"/>. ''' Loads the configuration from the specified <see cref="IO.Stream"/>.
''' </summary> ''' </summary>
''' <param name="stream">A <see cref="IO.Stream"/> to read the dictionary from.</param> ''' <param name="stream">A <see cref="IO.Stream"/> to read the dictionary from.</param>
Public Sub LoadConfiguration(ByVal stream As IO.Stream) Public Sub LoadConfiguration(ByVal stream As IO.Stream)
LoadConfiguration(New IO.StreamReader(stream, Text.Encoding.UTF8, True)) LoadConfiguration(New IO.StreamReader(stream, Text.Encoding.UTF8, True))
End Sub End Sub
''' <summary> ''' <summary>
''' Loads the configuration from the specified <see cref="IO.TextReader"/>. ''' Loads the configuration from the specified <see cref="IO.TextReader"/>.
''' </summary> ''' </summary>
''' <param name="reader">The <see cref="IO.TextReader"/> to read the dictionary from.</param> ''' <param name="reader">The <see cref="IO.TextReader"/> to read the dictionary from.</param>
Public Sub LoadConfiguration(ByVal reader As IO.TextReader) Public Sub LoadConfiguration(ByVal reader As IO.TextReader)
Dim dic = __ConfigSerializer.Deserialize(reader) Dim dic = __ConfigSerializer.Deserialize(reader)
If (dic IsNot Nothing) Then If (dic IsNot Nothing) Then
__Configuration = dic __Configuration = dic
End If End If
End Sub End Sub
#End Region #End Region
#Region " SaveConfiguration Methods " #Region " SaveConfiguration Methods "
''' <summary> ''' <summary>
''' Saves the conficuration to the specified file. ''' Saves the conficuration to the specified file.
''' </summary> ''' </summary>
''' <param name="fileName">The name of the file to save the dictionary.</param> ''' <param name="fileName">The name of the file to save the dictionary.</param>
Public Sub SaveConfiguration(ByVal fileName As String) Public Sub SaveConfiguration(ByVal fileName As String)
If (String.IsNullOrEmpty(fileName)) Then If (String.IsNullOrEmpty(fileName)) Then
Throw New ArgumentNullException("fileName") Throw New ArgumentNullException("fileName")
End If End If
Using fileStream As New IO.FileStream(fileName, IO.FileMode.Create) Using fileStream As New IO.FileStream(fileName, IO.FileMode.Create)
SaveConfiguration(fileStream) SaveConfiguration(fileStream)
End Using End Using
End Sub End Sub
''' <summary> ''' <summary>
''' Saves the conficuration to the specified <see cref="IO.Stream"/>. ''' Saves the conficuration to the specified <see cref="IO.Stream"/>.
''' </summary> ''' </summary>
''' <param name="stream">The <see cref="IO.Stream"/> to save the dictionary.</param> ''' <param name="stream">The <see cref="IO.Stream"/> to save the dictionary.</param>
Public Sub SaveConfiguration(ByVal stream As IO.Stream) Public Sub SaveConfiguration(ByVal stream As IO.Stream)
SaveConfiguration(New IO.StreamWriter(stream, Text.Encoding.UTF8)) SaveConfiguration(New IO.StreamWriter(stream, Text.Encoding.UTF8))
End Sub End Sub
''' <summary> ''' <summary>
''' Saves the conficuration to the specified <see cref="IO.TextWriter"/>. ''' Saves the conficuration to the specified <see cref="IO.TextWriter"/>.
''' </summary> ''' </summary>
''' <param name="writer">The <see cref="IO.TextWriter"/> to save the dictionary.</param> ''' <param name="writer">The <see cref="IO.TextWriter"/> to save the dictionary.</param>
Public Sub SaveConfiguration(ByVal writer As IO.TextWriter) Public Sub SaveConfiguration(ByVal writer As IO.TextWriter)
__ConfigSerializer.Serialize(writer, __Configuration) __ConfigSerializer.Serialize(writer, __Configuration)
End Sub End Sub
#End Region #End Region
#Region " Parse Methods " #Region " Parse Methods "
''' <summary> ''' <summary>
''' Parses the specified text, returning a collection of <see cref="BBCodeNode"/>. ''' Parses the specified text, returning a collection of <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
''' <param name="text">The text to be parsed.</param> ''' <param name="text">The text to be parsed.</param>
''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed text.</returns> ''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed text.</returns>
Public Function Parse(ByVal text As String) As BBCodeDocument Public Function Parse(ByVal text As String) As BBCodeDocument(Of TContext)
Using reader As New IO.StringReader(text) Using reader As New IO.StringReader(text)
Return Parse(reader) Return Parse(reader)
End Using End Using
End Function End Function
''' <summary> ''' <summary>
''' Parses the specified stream, returning a collection of <see cref="BBCodeNode"/>. ''' Parses the specified stream, returning a collection of <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
''' <param name="stream">The <see cref="IO.Stream"/> to be parsed.</param> ''' <param name="stream">The <see cref="IO.Stream"/> to be parsed.</param>
''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.Stream"/>.</returns> ''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.Stream"/>.</returns>
Public Function Parse(ByVal stream As IO.Stream) As BBCodeDocument Public Function Parse(ByVal stream As IO.Stream) As BBCodeDocument(Of TContext)
Return Parse(stream, Text.Encoding.UTF8) Return Parse(stream, Text.Encoding.UTF8)
End Function End Function
''' <summary> ''' <summary>
''' Parses the specified stream, returning a collection of <see cref="BBCodeNode"/>. ''' Parses the specified stream, returning a collection of <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
''' <param name="stream">The <see cref="IO.Stream"/> to be parsed.</param> ''' <param name="stream">The <see cref="IO.Stream"/> to be parsed.</param>
''' <param name="encoding">The encoding of the stream.</param> ''' <param name="encoding">The encoding of the stream.</param>
''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.Stream"/>.</returns> ''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.Stream"/>.</returns>
Public Function Parse(ByVal stream As IO.Stream, ByVal encoding As Text.Encoding) As BBCodeDocument Public Function Parse(ByVal stream As IO.Stream, ByVal encoding As Text.Encoding) As BBCodeDocument(Of TContext)
Return Parse(New IO.StreamReader(stream, encoding)) Return Parse(New IO.StreamReader(stream, encoding))
End Function End Function
''' <summary> ''' <summary>
''' Parses the specified <see cref="IO.TextReader"/>, returning a collection of <see cref="BBCodeNode"/>. ''' Parses the specified <see cref="IO.TextReader"/>, returning a collection of <see cref="BBCodeNode"/>.
''' </summary> ''' </summary>
''' <param name="reader">The <see cref="IO.TextReader"/> to be parsed.</param> ''' <param name="reader">The <see cref="IO.TextReader"/> to be parsed.</param>
''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.TextReader"/>.</returns> ''' <returns>A <see cref="BBCodeNodeCollection"/> containing the parsed <see cref="IO.TextReader"/>.</returns>
Public Function Parse(ByVal reader As IO.TextReader) As BBCodeDocument Public Function Parse(ByVal reader As IO.TextReader) As BBCodeDocument(Of TContext)
Dim doc = New BBCodeDocument(Me) Dim doc = New BBCodeDocument(Of TContext)(Me)
Dim rootElement As New BBCodeElement(Me) Dim rootElement As New BBCodeElement(Of TContext)(Me)
Dim currentElement As BBCodeElement = rootElement Dim currentElement As BBCodeElement(Of TContext) = rootElement
Dim tk As Tokenization.Token Dim tk As Tokenization.Token
Dim sb As New Text.StringBuilder() Dim sb As New Text.StringBuilder()
Dim sbText As New Text.StringBuilder() Dim sbText As New Text.StringBuilder()
Do While (reader.Peek() <> -1) Do While (reader.Peek() <> -1)
Dim line As String = reader.ReadLine() & vbCrLf Dim line As String = reader.ReadLine() & vbCrLf
sbText.AppendLine(line) sbText.AppendLine(line)
Do Do
'* '*
'* Get the next token '* Get the next token
'* '*
tk = Tokenizer.GetToken(line) tk = Tokenizer.GetToken(line)
If (tk Is Nothing) Then If (tk Is Nothing) Then
Exit Do Exit Do
End If End If
Dim tag = New BBCodeTag(tk.Value) Dim tag = New BBCodeTag(tk.Value)
ParseElement(rootElement, currentElement, tk, sb, tag) ParseElement(rootElement, currentElement, tk, sb, tag)
Loop Loop
Loop Loop
'* '*
'* Add the text node '* Add the text node
'* '*
If (sb.Length > 0) Then If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(sb.ToString())) currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
End If End If
'* '*
'* Add the nodes to the document '* Add the nodes to the document
'* '*
doc.Nodes.AddRange(rootElement.Nodes) doc.Nodes.AddRange(rootElement.Nodes)
'* '*
'* Sets the source text '* Sets the source text
'* '*
doc.SetText(sbText.ToString()) doc.SetText(sbText.ToString())
Return doc Return doc
End Function End Function
#End Region #End Region
''' <summary> ''' <summary>
''' Gets the <see cref="Tokenization.Tokenizer"/>. ''' Gets the <see cref="Tokenization.Tokenizer"/>.
''' </summary> ''' </summary>
Private Shared ReadOnly Property Tokenizer() As Tokenization.Tokenizer Private Shared ReadOnly Property Tokenizer() As Tokenization.Tokenizer
Get Get
Return __Tokenizer Return __Tokenizer
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' Gets the <see cref="BBCodeElementFactory"/>. ''' Gets the <see cref="BBCodeElementFactory"/>.
''' </summary> ''' </summary>
Private ReadOnly Property Factory() As BBCodeElementFactory Private ReadOnly Property Factory() As BBCodeElementFactory(Of TContext)
Get Get
If (__Factory Is Nothing) Then If (__Factory Is Nothing) Then
__Factory = New BBCodeElementFactory(Me) __Factory = New BBCodeElementFactory(Of TContext)(Me)
End If End If
Return __Factory Return __Factory
End Get End Get
End Property End Property
Private Sub ParseElement(ByVal rootElement As BBCodeElement, ByRef currentElement As BBCodeElement, ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag) Private Sub ParseElement(ByVal rootElement As BBCodeElement(Of TContext), ByRef currentElement As BBCodeElement(Of TContext), ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'* '*
'* Check the token Type '* Check the token Type
'* '*
Select Case token.RuleType Select Case token.RuleType
Case 0, -1 Case 0, -1
'* '*
'* Empty tag or char '* Empty tag or char
'* '*
sb.Append(token.Value) sb.Append(token.Value)
Case 1 Case 1
'* '*
'* Closing tag '* Closing tag
'* '*
ParseClosingTag(rootElement, currentElement, token, sb, tag) ParseClosingTag(rootElement, currentElement, token, sb, tag)
Case 2, 3, 4 Case 2, 3, 4
'* '*
'* Value Tag, Parametrized Tag, Generic Tag '* Value Tag, Parametrized Tag, Generic Tag
'* '*
ParseTag(currentElement, sb, tag) ParseTag(currentElement, sb, tag)
End Select End Select
End Sub End Sub
Private Sub ParseTag(ByRef currentElement As BBCodeElement, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag) Private Sub ParseTag(ByRef currentElement As BBCodeElement(Of TContext), ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'* '*
'* Add the text previous to the current element '* Add the text previous to the current element
'* '*
If (sb.Length > 0) Then If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(sb.ToString())) currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
sb.Remove(0, sb.Length) sb.Remove(0, sb.Length)
End If End If
'* '*
'* Add the new element to the list of nodes '* Add the new element to the list of nodes
'* '*
Dim el = Factory.CreateElement(tag.Name, tag.Paramters) Dim el = Factory.CreateElement(tag.Name, tag.Paramters)
currentElement.Nodes.Add(el) currentElement.Nodes.Add(el)
'* '*
'* Change the current element, if it requires an closing tag '* Change the current element, if it requires an closing tag
'* '*
If (el.RequireClosingTag) Then If (el.RequireClosingTag) Then
currentElement = el currentElement = el
End If End If
End Sub End Sub
Private Shared Sub ParseClosingTag(ByVal rootElement As BBCodeElement, ByRef currentElement As BBCodeElement, ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag) Private Shared Sub ParseClosingTag(ByVal rootElement As BBCodeElement(Of TContext), ByRef currentElement As BBCodeElement(Of TContext), ByVal token As Tokenization.Token, ByVal sb As Text.StringBuilder, ByVal tag As BBCodeTag)
'* '*
'* Check if the closing tag is closing a previously open tag '* Check if the closing tag is closing a previously open tag
'* '*
If currentElement.RequireClosingTag AndAlso (String.CompareOrdinal(currentElement.Name, tag.Name) = 0) Then If currentElement.RequireClosingTag AndAlso (String.CompareOrdinal(currentElement.Name, tag.Name) = 0) Then
'* '*
'* Add the inner text '* Add the inner text
'* '*
If (sb.Length > 0) Then If (sb.Length > 0) Then
currentElement.Nodes.Add(New BBCodeText(sb.ToString())) currentElement.Nodes.Add(New BBCodeText(Of TContext)(sb.ToString()))
sb.Remove(0, sb.Length) sb.Remove(0, sb.Length)
End If End If
'* '*
'* Move up a level '* Move up a level
'* '*
currentElement = If(currentElement.Parent, rootElement) currentElement = If(currentElement.Parent, rootElement)
Else Else
'* '*
'* Adds to the text '* Adds to the text
'* '*
sb.Append(token.Value) sb.Append(token.Value)
End If End If
End Sub End Sub
Private Shared Function PrepareTokenizer() As Tokenization.Tokenizer Private Shared Function PrepareTokenizer() As Tokenization.Tokenizer
Dim tk As New Tokenization.Tokenizer Dim tk As New Tokenization.Tokenizer
'* '*
'* Prepares the BBCode Grammar '* Prepares the BBCode Grammar
'* '*
With tk With tk
'* '*
'* Define the grammar macros '* Define the grammar macros
'* '*
AddTokenizerBaseMacros(tk) AddTokenizerBaseMacros(tk)
'* '*
'* Define the grammar rules '* Define the grammar rules
'* '*
.AddRule("EmptyTag", 0, "\[{w}\]") .AddRule("EmptyTag", 0, "\[{w}\]")
.AddRule("ClosingTag", 1, "\[/{name}\]") .AddRule("ClosingTag", 1, "\[/{name}\]")
.AddRule("ValueTag", 2, "\[{param}\]") .AddRule("ValueTag", 2, "\[{param}\]")
.AddRule("ParamsTag", 3, "\[{name}{params}\]") .AddRule("ParamsTag", 3, "\[{name}{params}\]")
.AddRule("Tag", 4, "\[[^ \t\r\n\f\]]+?\]") .AddRule("Tag", 4, "\[[^ \t\r\n\f\]]+?\]")
.AddRule("Char", -1, ".") .AddRule("Char", -1, ".")
End With End With
Return tk Return tk
End Function End Function
End Class End Class

@ -18,64 +18,64 @@
''' <summary> ''' <summary>
''' Represents a simple text in the BBCode. ''' Represents a simple text in the BBCode.
''' </summary> ''' </summary>
Public NotInheritable Class BBCodeText Public NotInheritable Class BBCodeText(Of TContext As Class)
Inherits BBCodeNode Inherits BBCodeNode(Of TContext)
Private __InnerText As String Private __InnerText As String
''' <summary>Initializes an instance of the <see cref="BBCodeText" /> class. ''' <summary>Initializes an instance of the <see cref="BBCodeText" /> class.
''' This is the default constructor for this class.</summary> ''' This is the default constructor for this class.</summary>
Friend Sub New() Friend Sub New()
End Sub End Sub
''' <summary>Initializes an instance of the <see cref="BBCodeText" /> class.</summary> ''' <summary>Initializes an instance of the <see cref="BBCodeText" /> class.</summary>
''' <param name="text">The text of the <see cref="BBCodeText"/>.</param> ''' <param name="text">The text of the <see cref="BBCodeText"/>.</param>
Friend Sub New(ByVal text As String) Friend Sub New(ByVal text As String)
Me.InnerText = text Me.InnerText = text
End Sub End Sub
''' <summary>Transforms this instance of <see cref="BBCodeText" /> into its desired text representation.</summary> ''' <summary>Transforms this instance of <see cref="BBCodeText" /> into its desired text representation.</summary>
''' <param name="formatter">An object that implements the <see cref="ITextFormatter" /> interface.</param> ''' <param name="formatter">An object that implements the <see cref="ITextFormatter" /> interface.</param>
''' <returns>The text formatted by the <see cref="ITextFormatter" />.</returns> ''' <returns>The text formatted by the <see cref="ITextFormatter" />.</returns>
Public Overrides Function Format(ByVal formatter As ITextFormatter) As String Public Overrides Function Format(ByVal context As TContext, ByVal formatter As ITextFormatter(Of TContext)) As String
Return formatter.Format(__InnerText) Return formatter.Format(context, __InnerText)
End Function End Function
''' <summary>Gets or sets the inner BBCode.</summary> ''' <summary>Gets or sets the inner BBCode.</summary>
''' <value>The BBCode between the start and end tags.</value> ''' <value>The BBCode between the start and end tags.</value>
Public Overrides Property InnerBBCode() As String Public Overrides Property InnerBBCode() As String
Get Get
Return Me.InnerText Return Me.InnerText
End Get End Get
Set(ByVal value As String) Set(ByVal value As String)
Me.InnerText = value Me.InnerText = value
End Set End Set
End Property End Property
''' <summary>Gets or sets the plain text of the node.</summary> ''' <summary>Gets or sets the plain text of the node.</summary>
''' <value>The plain text between the start and end tags.</value> ''' <value>The plain text between the start and end tags.</value>
Public Overrides Property InnerText() As String Public Overrides Property InnerText() As String
Get Get
Return __InnerText Return __InnerText
End Get End Get
Set(ByVal value As String) Set(ByVal value As String)
__InnerText = value __InnerText = value
End Set End Set
End Property End Property
''' <summary>Gets the outer BBCode.</summary> ''' <summary>Gets the outer BBCode.</summary>
''' <value>The BBCode of this instance of the <see cref="BBCodeNode" /> .</value> ''' <value>The BBCode of this instance of the <see cref="BBCodeNode" /> .</value>
Public Overrides ReadOnly Property OuterBBCode() As String Public Overrides ReadOnly Property OuterBBCode() As String
Get Get
Return Me.InnerText Return Me.InnerText
End Get End Get
End Property End Property
''' <summary>Returns a <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.</summary> ''' <summary>Returns a <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.</summary>
''' <returns>A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.</returns> ''' <returns>A <see cref="T:System.String" /> that represents the current <see cref="T:System.Object" />.</returns>
''' <filterpriority>2</filterpriority> ''' <filterpriority>2</filterpriority>
Public Overrides Function ToString() As String Public Overrides Function ToString() As String
Return Me.InnerText Return Me.InnerText
End Function End Function
End Class End Class

@ -18,13 +18,13 @@
''' <summary> ''' <summary>
''' Defines a generic text formatter. ''' Defines a generic text formatter.
''' </summary> ''' </summary>
Public Interface ITextFormatter Public Interface ITextFormatter(Of TContext As Class)
''' <summary> ''' <summary>
''' Formats the specified text. ''' Formats the specified text.
''' </summary> ''' </summary>
''' <param name="source">The text to be formatted.</param> ''' <param name="source">The text to be formatted.</param>
''' <returns>The formatted text.</returns> ''' <returns>The formatted text.</returns>
Function Format(ByVal source As String) As String Function Format(ByVal context As TContext, ByVal source As String) As String
End Interface End Interface

@ -20,392 +20,392 @@ Imports System.Diagnostics.CodeAnalysis
Friend Module Utils Friend Module Utils
Public Const STR_BBCodeSchemaNamespace As String = "" '= "http://pjondevelopment.50webs.com/schema/bbcode" Public Const STR_BBCodeSchemaNamespace As String = "" '= "http://pjondevelopment.50webs.com/schema/bbcode"
Public Const STR_BBCodeDictionaryXmlElement As String = "Dictionary" Public Const STR_BBCodeDictionaryXmlElement As String = "Dictionary"
Public Const STR_BBCodeElementTypesXmlElement As String = "ElementTypes" Public Const STR_BBCodeElementTypesXmlElement As String = "ElementTypes"
Public Const STR_BBCodeConfigurationXmlElement As String = "Configuration" Public Const STR_BBCodeConfigurationXmlElement As String = "Configuration"
''' <summary> ''' <summary>
''' Returns the specified text between quotes. ''' Returns the specified text between quotes.
''' </summary> ''' </summary>
''' <param name="text">The text to be placed between quotes.</param> ''' <param name="text">The text to be placed between quotes.</param>
''' <returns>The text between quotes.</returns> ''' <returns>The text between quotes.</returns>
Public Function Quote(ByVal text As String) As String Public Function Quote(ByVal text As String) As String
Return "'" & text.Replace("'", "''") & "'" Return "'" & text.Replace("'", "''") & "'"
End Function End Function
''' <summary> ''' <summary>
''' Unquotes the specified text. ''' Unquotes the specified text.
''' </summary> ''' </summary>
''' <param name="text">The text to be unquoted.</param> ''' <param name="text">The text to be unquoted.</param>
''' <returns>The unquoted text.</returns> ''' <returns>The unquoted text.</returns>
Public Function UnQuote(ByVal text As String) As String Public Function UnQuote(ByVal text As String) As String
Dim rx As New Text.RegularExpressions.Regex("^(?<quote>'(?<text>(?:''|[^'])*)')|(?<doubleQuote>""(?<text>(?:""""|[^""])*)"")$") Dim rx As New Text.RegularExpressions.Regex("^(?<quote>'(?<text>(?:''|[^'])*)')|(?<doubleQuote>""(?<text>(?:""""|[^""])*)"")$")
Dim m = rx.Match(text) Dim m = rx.Match(text)
If (Not m.Success) Then If (Not m.Success) Then
Return text Return text
End If End If
If (m.Groups("quote").Success) Then If (m.Groups("quote").Success) Then
Return m.Groups("text").Value.Replace("''", "'") Return m.Groups("text").Value.Replace("''", "'")
End If End If
Return m.Groups("text").Value.Replace("""""", """") Return m.Groups("text").Value.Replace("""""", """")
End Function End Function
''' <summary> ''' <summary>
''' Validates the specified tag name. ''' Validates the specified tag name.
''' </summary> ''' </summary>
''' <param name="tagName">The tagname to be validated.</param> ''' <param name="tagName">The tagname to be validated.</param>
Public Sub ValidateTagName(ByVal tagName As String) Public Sub ValidateTagName(ByVal tagName As String)
If (String.IsNullOrEmpty(tagName)) Then If (String.IsNullOrEmpty(tagName)) Then
Throw New ArgumentNullException("tagName") Throw New ArgumentNullException("tagName")
End If End If
If (tagName.IndexOf("=", StringComparison.Ordinal) <> -1) OrElse (tagName.IndexOf("[", StringComparison.Ordinal) <> -1) OrElse (tagName.IndexOf("]", StringComparison.Ordinal) <> -1) Then If (tagName.IndexOf("=", StringComparison.Ordinal) <> -1) OrElse (tagName.IndexOf("[", StringComparison.Ordinal) <> -1) OrElse (tagName.IndexOf("]", StringComparison.Ordinal) <> -1) Then
Throw New ArgumentException("Invalid tag name. The tag name cannot contain '=' '[' or ']'.", "tagName") Throw New ArgumentException("Invalid tag name. The tag name cannot contain '=' '[' or ']'.", "tagName")
End If End If
End Sub End Sub
''' <summary> ''' <summary>
''' Validates the specified type to ensure that it is a subclass of <see cref="BBCodeElement"/>. ''' Validates the specified type to ensure that it is a subclass of <see cref="BBCodeElement"/>.
''' </summary> ''' </summary>
''' <param name="value">The <see cref="Type"/> to be validated.</param> ''' <param name="value">The <see cref="Type"/> to be validated.</param>
Public Sub ValidateBBCodeElementType(ByVal value As Type) Public Sub ValidateBBCodeElementType(Of TContext As Class)(ByVal value As Type)
'* '*
'* Validate is nothing '* Validate is nothing
'* '*
If (value Is Nothing) Then If (value Is Nothing) Then
Throw New ArgumentNullException("value") Throw New ArgumentNullException("value")
End If End If
'* '*
'* Validates the BBCodeElement itself '* Validates the BBCodeElement itself
'* '*
Dim bbcodeType = GetType(BBCodeElement) Dim bbcodeType = GetType(BBCodeElement(Of TContext))
If (value.Equals(bbcodeType)) Then If (value.Equals(bbcodeType)) Then
Exit Sub Exit Sub
End If End If
'* '*
'* Validate subclass '* Validate subclass
'* '*
If Not bbcodeType.IsAssignableFrom(value) Then If Not bbcodeType.IsAssignableFrom(value) Then
Throw New InvalidOperationException("The type " & value.FullName & " must be a assingable to BBCodeElement.") Throw New InvalidOperationException("The type " & value.FullName & " must be a assingable to BBCodeElement.")
End If End If
'* '*
'* Validate default constructor '* Validate default constructor
'* '*
If (value.GetConstructor(New Type() {}) Is Nothing) Then If (value.GetConstructor(New Type() {}) Is Nothing) Then
Throw New InvalidOperationException("The type " & value.FullName & " does not provide a public default constructor.") Throw New InvalidOperationException("The type " & value.FullName & " does not provide a public default constructor.")
End If End If
End Sub End Sub
''' <summary> ''' <summary>
''' Encodes the specified text as HTML. ''' Encodes the specified text as HTML.
''' </summary> ''' </summary>
''' <param name="text">The text to be encoded.</param> ''' <param name="text">The text to be encoded.</param>
''' <returns>The encoded HTML.</returns> ''' <returns>The encoded HTML.</returns>
<SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification:="This methos is simple a list of substuition character by its HTML representation.")> _ <SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification:="This methos is simple a list of substuition character by its HTML representation.")> _
Public Function HtmlEncode(ByVal text As String) As String Public Function HtmlEncode(ByVal text As String) As String
Dim sb As New Text.StringBuilder(text) Dim sb As New Text.StringBuilder(text)
sb.Replace(ChrW(&H26), "&amp;") sb.Replace(ChrW(&H26), "&amp;")
sb.Replace(ChrW(&H22), "&quot;") sb.Replace(ChrW(&H22), "&quot;")
sb.Replace(ChrW(&H27), "&apos;") sb.Replace(ChrW(&H27), "&apos;")
sb.Replace(ChrW(&H3C), "&lt;") sb.Replace(ChrW(&H3C), "&lt;")
sb.Replace(ChrW(&H3E), "&gt;") sb.Replace(ChrW(&H3E), "&gt;")
sb.Replace(ChrW(&HA0), "&nbsp;") sb.Replace(ChrW(&HA0), "&nbsp;")
sb.Replace(ChrW(&HA1), "&iexcl;") sb.Replace(ChrW(&HA1), "&iexcl;")
sb.Replace(ChrW(&HA2), "&cent;") sb.Replace(ChrW(&HA2), "&cent;")
sb.Replace(ChrW(&HA3), "&pound;") sb.Replace(ChrW(&HA3), "&pound;")
sb.Replace(ChrW(&HA4), "&curren;") sb.Replace(ChrW(&HA4), "&curren;")
sb.Replace(ChrW(&HA5), "&yen;") sb.Replace(ChrW(&HA5), "&yen;")
sb.Replace(ChrW(&HA6), "&brvbar;") sb.Replace(ChrW(&HA6), "&brvbar;")
sb.Replace(ChrW(&HA7), "&sect;") sb.Replace(ChrW(&HA7), "&sect;")
sb.Replace(ChrW(&HA8), "&uml;") sb.Replace(ChrW(&HA8), "&uml;")
sb.Replace(ChrW(&HA9), "&copy;") sb.Replace(ChrW(&HA9), "&copy;")
sb.Replace(ChrW(&HAA), "&ordf;") sb.Replace(ChrW(&HAA), "&ordf;")
sb.Replace(ChrW(&HAB), "&laquo;") sb.Replace(ChrW(&HAB), "&laquo;")
sb.Replace(ChrW(&HAC), "&not;") sb.Replace(ChrW(&HAC), "&not;")
sb.Replace(ChrW(&HAD), "&shy;") sb.Replace(ChrW(&HAD), "&shy;")
sb.Replace(ChrW(&HAE), "&reg;") sb.Replace(ChrW(&HAE), "&reg;")
sb.Replace(ChrW(&HAF), "&macr;") sb.Replace(ChrW(&HAF), "&macr;")
sb.Replace(ChrW(&HB0), "&deg;") sb.Replace(ChrW(&HB0), "&deg;")
sb.Replace(ChrW(&HB1), "&plusmn;") sb.Replace(ChrW(&HB1), "&plusmn;")
sb.Replace(ChrW(&HB2), "&sup2;") sb.Replace(ChrW(&HB2), "&sup2;")
sb.Replace(ChrW(&HB3), "&sup3;") sb.Replace(ChrW(&HB3), "&sup3;")
sb.Replace(ChrW(&HB4), "&acute;") sb.Replace(ChrW(&HB4), "&acute;")
sb.Replace(ChrW(&HB5), "&micro;") sb.Replace(ChrW(&HB5), "&micro;")
sb.Replace(ChrW(&HB6), "&para;") sb.Replace(ChrW(&HB6), "&para;")
sb.Replace(ChrW(&HB7), "&middot;") sb.Replace(ChrW(&HB7), "&middot;")
sb.Replace(ChrW(&HB8), "&cedil;") sb.Replace(ChrW(&HB8), "&cedil;")
sb.Replace(ChrW(&HB9), "&sup1;") sb.Replace(ChrW(&HB9), "&sup1;")
sb.Replace(ChrW(&HBA), "&ordm;") sb.Replace(ChrW(&HBA), "&ordm;")
sb.Replace(ChrW(&HBB), "&raquo;") sb.Replace(ChrW(&HBB), "&raquo;")
sb.Replace(ChrW(&HBC), "&frac14;") sb.Replace(ChrW(&HBC), "&frac14;")
sb.Replace(ChrW(&HBD), "&frac12;") sb.Replace(ChrW(&HBD), "&frac12;")
sb.Replace(ChrW(&HBE), "&frac34;") sb.Replace(ChrW(&HBE), "&frac34;")
sb.Replace(ChrW(&HBF), "&iquest;") sb.Replace(ChrW(&HBF), "&iquest;")
sb.Replace(ChrW(&HC0), "&Agrave;") sb.Replace(ChrW(&HC0), "&Agrave;")
sb.Replace(ChrW(&HC1), "&Aacute;") sb.Replace(ChrW(&HC1), "&Aacute;")
sb.Replace(ChrW(&HC2), "&Acirc;") sb.Replace(ChrW(&HC2), "&Acirc;")
sb.Replace(ChrW(&HC3), "&Atilde;") sb.Replace(ChrW(&HC3), "&Atilde;")
sb.Replace(ChrW(&HC4), "&Auml;") sb.Replace(ChrW(&HC4), "&Auml;")
sb.Replace(ChrW(&HC5), "&Aring;") sb.Replace(ChrW(&HC5), "&Aring;")
sb.Replace(ChrW(&HC6), "&AElig;") sb.Replace(ChrW(&HC6), "&AElig;")
sb.Replace(ChrW(&HC7), "&Ccedil;") sb.Replace(ChrW(&HC7), "&Ccedil;")
sb.Replace(ChrW(&HC8), "&Egrave;") sb.Replace(ChrW(&HC8), "&Egrave;")
sb.Replace(ChrW(&HC9), "&Eacute;") sb.Replace(ChrW(&HC9), "&Eacute;")
sb.Replace(ChrW(&HCA), "&Ecirc;") sb.Replace(ChrW(&HCA), "&Ecirc;")
sb.Replace(ChrW(&HCB), "&Euml;") sb.Replace(ChrW(&HCB), "&Euml;")
sb.Replace(ChrW(&HCC), "&Igrave;") sb.Replace(ChrW(&HCC), "&Igrave;")
sb.Replace(ChrW(&HCD), "&Iacute;") sb.Replace(ChrW(&HCD), "&Iacute;")
sb.Replace(ChrW(&HCE), "&Icirc;") sb.Replace(ChrW(&HCE), "&Icirc;")
sb.Replace(ChrW(&HCF), "&Iuml;") sb.Replace(ChrW(&HCF), "&Iuml;")
sb.Replace(ChrW(&HD0), "&ETH;") sb.Replace(ChrW(&HD0), "&ETH;")
sb.Replace(ChrW(&HD1), "&Ntilde;") sb.Replace(ChrW(&HD1), "&Ntilde;")
sb.Replace(ChrW(&HD2), "&Ograve;") sb.Replace(ChrW(&HD2), "&Ograve;")
sb.Replace(ChrW(&HD3), "&Oacute;") sb.Replace(ChrW(&HD3), "&Oacute;")
sb.Replace(ChrW(&HD4), "&Ocirc;") sb.Replace(ChrW(&HD4), "&Ocirc;")
sb.Replace(ChrW(&HD5), "&Otilde;") sb.Replace(ChrW(&HD5), "&Otilde;")
sb.Replace(ChrW(&HD6), "&Ouml;") sb.Replace(ChrW(&HD6), "&Ouml;")
sb.Replace(ChrW(&HD7), "&times;") sb.Replace(ChrW(&HD7), "&times;")
sb.Replace(ChrW(&HD8), "&Oslash;") sb.Replace(ChrW(&HD8), "&Oslash;")
sb.Replace(ChrW(&HD9), "&Ugrave;") sb.Replace(ChrW(&HD9), "&Ugrave;")
sb.Replace(ChrW(&HDA), "&Uacute;") sb.Replace(ChrW(&HDA), "&Uacute;")
sb.Replace(ChrW(&HDB), "&Ucirc;") sb.Replace(ChrW(&HDB), "&Ucirc;")
sb.Replace(ChrW(&HDC), "&Uuml;") sb.Replace(ChrW(&HDC), "&Uuml;")
sb.Replace(ChrW(&HDD), "&Yacute;") sb.Replace(ChrW(&HDD), "&Yacute;")
sb.Replace(ChrW(&HDE), "&THORN;") sb.Replace(ChrW(&HDE), "&THORN;")
sb.Replace(ChrW(&HDF), "&szlig;") sb.Replace(ChrW(&HDF), "&szlig;")
sb.Replace(ChrW(&HE0), "&agrave;") sb.Replace(ChrW(&HE0), "&agrave;")
sb.Replace(ChrW(&HE1), "&aacute;") sb.Replace(ChrW(&HE1), "&aacute;")
sb.Replace(ChrW(&HE2), "&acirc;") sb.Replace(ChrW(&HE2), "&acirc;")
sb.Replace(ChrW(&HE3), "&atilde;") sb.Replace(ChrW(&HE3), "&atilde;")
sb.Replace(ChrW(&HE4), "&auml;") sb.Replace(ChrW(&HE4), "&auml;")
sb.Replace(ChrW(&HE5), "&aring;") sb.Replace(ChrW(&HE5), "&aring;")
sb.Replace(ChrW(&HE6), "&aelig;") sb.Replace(ChrW(&HE6), "&aelig;")
sb.Replace(ChrW(&HE7), "&ccedil;") sb.Replace(ChrW(&HE7), "&ccedil;")
sb.Replace(ChrW(&HE8), "&egrave;") sb.Replace(ChrW(&HE8), "&egrave;")
sb.Replace(ChrW(&HE9), "&eacute;") sb.Replace(ChrW(&HE9), "&eacute;")
sb.Replace(ChrW(&HEA), "&ecirc;") sb.Replace(ChrW(&HEA), "&ecirc;")
sb.Replace(ChrW(&HEB), "&euml;") sb.Replace(ChrW(&HEB), "&euml;")
sb.Replace(ChrW(&HEC), "&igrave;") sb.Replace(ChrW(&HEC), "&igrave;")
sb.Replace(ChrW(&HED), "&iacute;") sb.Replace(ChrW(&HED), "&iacute;")
sb.Replace(ChrW(&HEE), "&icirc;") sb.Replace(ChrW(&HEE), "&icirc;")
sb.Replace(ChrW(&HEF), "&iuml;") sb.Replace(ChrW(&HEF), "&iuml;")
sb.Replace(ChrW(&HF0), "&eth;") sb.Replace(ChrW(&HF0), "&eth;")
sb.Replace(ChrW(&HF1), "&ntilde;") sb.Replace(ChrW(&HF1), "&ntilde;")
sb.Replace(ChrW(&HF2), "&ograve;") sb.Replace(ChrW(&HF2), "&ograve;")
sb.Replace(ChrW(&HF3), "&oacute;") sb.Replace(ChrW(&HF3), "&oacute;")
sb.Replace(ChrW(&HF4), "&ocirc;") sb.Replace(ChrW(&HF4), "&ocirc;")
sb.Replace(ChrW(&HF5), "&otilde;") sb.Replace(ChrW(&HF5), "&otilde;")
sb.Replace(ChrW(&HF6), "&ouml;") sb.Replace(ChrW(&HF6), "&ouml;")
sb.Replace(ChrW(&HF7), "&divide;") sb.Replace(ChrW(&HF7), "&divide;")
sb.Replace(ChrW(&HF8), "&oslash;") sb.Replace(ChrW(&HF8), "&oslash;")
sb.Replace(ChrW(&HF9), "&ugrave;") sb.Replace(ChrW(&HF9), "&ugrave;")
sb.Replace(ChrW(&HFA), "&uacute;") sb.Replace(ChrW(&HFA), "&uacute;")
sb.Replace(ChrW(&HFB), "&ucirc;") sb.Replace(ChrW(&HFB), "&ucirc;")
sb.Replace(ChrW(&HFC), "&uuml;") sb.Replace(ChrW(&HFC), "&uuml;")
sb.Replace(ChrW(&HFD), "&yacute;") sb.Replace(ChrW(&HFD), "&yacute;")
sb.Replace(ChrW(&HFE), "&thorn;") sb.Replace(ChrW(&HFE), "&thorn;")
sb.Replace(ChrW(&HFF), "&yuml;") sb.Replace(ChrW(&HFF), "&yuml;")
sb.Replace(ChrW(&H152), "&OElig;") sb.Replace(ChrW(&H152), "&OElig;")
sb.Replace(ChrW(&H153), "&oelig;") sb.Replace(ChrW(&H153), "&oelig;")
sb.Replace(ChrW(&H160), "&Scaron;") sb.Replace(ChrW(&H160), "&Scaron;")
sb.Replace(ChrW(&H161), "&scaron;") sb.Replace(ChrW(&H161), "&scaron;")
sb.Replace(ChrW(&H178), "&Yuml;") sb.Replace(ChrW(&H178), "&Yuml;")
sb.Replace(ChrW(&H192), "&fnof;") sb.Replace(ChrW(&H192), "&fnof;")
sb.Replace(ChrW(&H2C6), "&circ;") sb.Replace(ChrW(&H2C6), "&circ;")
sb.Replace(ChrW(&H2DC), "&tilde;") sb.Replace(ChrW(&H2DC), "&tilde;")
sb.Replace(ChrW(&H391), "&Alpha;") sb.Replace(ChrW(&H391), "&Alpha;")
sb.Replace(ChrW(&H392), "&Beta;") sb.Replace(ChrW(&H392), "&Beta;")
sb.Replace(ChrW(&H393), "&Gamma;") sb.Replace(ChrW(&H393), "&Gamma;")
sb.Replace(ChrW(&H394), "&Delta;") sb.Replace(ChrW(&H394), "&Delta;")
sb.Replace(ChrW(&H395), "&Epsilon;") sb.Replace(ChrW(&H395), "&Epsilon;")
sb.Replace(ChrW(&H396), "&Zeta;") sb.Replace(ChrW(&H396), "&Zeta;")
sb.Replace(ChrW(&H397), "&Eta;") sb.Replace(ChrW(&H397), "&Eta;")
sb.Replace(ChrW(&H398), "&Theta;") sb.Replace(ChrW(&H398), "&Theta;")
sb.Replace(ChrW(&H399), "&Iota;") sb.Replace(ChrW(&H399), "&Iota;")
sb.Replace(ChrW(&H39A), "&Kappa;") sb.Replace(ChrW(&H39A), "&Kappa;")
sb.Replace(ChrW(&H39B), "&Lambda;") sb.Replace(ChrW(&H39B), "&Lambda;")
sb.Replace(ChrW(&H39C), "&Mu;") sb.Replace(ChrW(&H39C), "&Mu;")
sb.Replace(ChrW(&H39D), "&Nu;") sb.Replace(ChrW(&H39D), "&Nu;")
sb.Replace(ChrW(&H39E), "&Xi;") sb.Replace(ChrW(&H39E), "&Xi;")
sb.Replace(ChrW(&H39F), "&Omicron;") sb.Replace(ChrW(&H39F), "&Omicron;")
sb.Replace(ChrW(&H3A0), "&Pi;") sb.Replace(ChrW(&H3A0), "&Pi;")
sb.Replace(ChrW(&H3A1), "&Rho;") sb.Replace(ChrW(&H3A1), "&Rho;")
sb.Replace(ChrW(&H3A3), "&Sigma;") sb.Replace(ChrW(&H3A3), "&Sigma;")
sb.Replace(ChrW(&H3A4), "&Tau;") sb.Replace(ChrW(&H3A4), "&Tau;")
sb.Replace(ChrW(&H3A5), "&Upsilon;") sb.Replace(ChrW(&H3A5), "&Upsilon;")
sb.Replace(ChrW(&H3A6), "&Phi;") sb.Replace(ChrW(&H3A6), "&Phi;")
sb.Replace(ChrW(&H3A7), "&Chi;") sb.Replace(ChrW(&H3A7), "&Chi;")
sb.Replace(ChrW(&H3A8), "&Psi;") sb.Replace(ChrW(&H3A8), "&Psi;")
sb.Replace(ChrW(&H3A9), "&Omega;") sb.Replace(ChrW(&H3A9), "&Omega;")
sb.Replace(ChrW(&H3B1), "&alpha;") sb.Replace(ChrW(&H3B1), "&alpha;")
sb.Replace(ChrW(&H3B2), "&beta;") sb.Replace(ChrW(&H3B2), "&beta;")
sb.Replace(ChrW(&H3B3), "&gamma;") sb.Replace(ChrW(&H3B3), "&gamma;")
sb.Replace(ChrW(&H3B4), "&delta;") sb.Replace(ChrW(&H3B4), "&delta;")
sb.Replace(ChrW(&H3B5), "&epsilon;") sb.Replace(ChrW(&H3B5), "&epsilon;")
sb.Replace(ChrW(&H3B6), "&zeta;") sb.Replace(ChrW(&H3B6), "&zeta;")
sb.Replace(ChrW(&H3B7), "&eta;") sb.Replace(ChrW(&H3B7), "&eta;")
sb.Replace(ChrW(&H3B8), "&theta;") sb.Replace(ChrW(&H3B8), "&theta;")
sb.Replace(ChrW(&H3B9), "&iota;") sb.Replace(ChrW(&H3B9), "&iota;")
sb.Replace(ChrW(&H3BA), "&kappa;") sb.Replace(ChrW(&H3BA), "&kappa;")
sb.Replace(ChrW(&H3BB), "&lambda;") sb.Replace(ChrW(&H3BB), "&lambda;")
sb.Replace(ChrW(&H3BC), "&mu;") sb.Replace(ChrW(&H3BC), "&mu;")
sb.Replace(ChrW(&H3BD), "&nu;") sb.Replace(ChrW(&H3BD), "&nu;")
sb.Replace(ChrW(&H3BE), "&xi;") sb.Replace(ChrW(&H3BE), "&xi;")
sb.Replace(ChrW(&H3BF), "&omicron;") sb.Replace(ChrW(&H3BF), "&omicron;")
sb.Replace(ChrW(&H3C0), "&pi;") sb.Replace(ChrW(&H3C0), "&pi;")
sb.Replace(ChrW(&H3C1), "&rho;") sb.Replace(ChrW(&H3C1), "&rho;")
sb.Replace(ChrW(&H3C2), "&sigmaf;") sb.Replace(ChrW(&H3C2), "&sigmaf;")
sb.Replace(ChrW(&H3C3), "&sigma;") sb.Replace(ChrW(&H3C3), "&sigma;")
sb.Replace(ChrW(&H3C4), "&tau;") sb.Replace(ChrW(&H3C4), "&tau;")
sb.Replace(ChrW(&H3C5), "&upsilon;") sb.Replace(ChrW(&H3C5), "&upsilon;")
sb.Replace(ChrW(&H3C6), "&phi;") sb.Replace(ChrW(&H3C6), "&phi;")
sb.Replace(ChrW(&H3C7), "&chi;") sb.Replace(ChrW(&H3C7), "&chi;")
sb.Replace(ChrW(&H3C8), "&psi;") sb.Replace(ChrW(&H3C8), "&psi;")
sb.Replace(ChrW(&H3C9), "&omega;") sb.Replace(ChrW(&H3C9), "&omega;")
sb.Replace(ChrW(&H3D1), "&thetasym;") sb.Replace(ChrW(&H3D1), "&thetasym;")
sb.Replace(ChrW(&H3D2), "&upsih;") sb.Replace(ChrW(&H3D2), "&upsih;")
sb.Replace(ChrW(&H3D6), "&piv;") sb.Replace(ChrW(&H3D6), "&piv;")
sb.Replace(ChrW(&H2002), "&ensp;") sb.Replace(ChrW(&H2002), "&ensp;")
sb.Replace(ChrW(&H2003), "&emsp;") sb.Replace(ChrW(&H2003), "&emsp;")
sb.Replace(ChrW(&H2009), "&thinsp;") sb.Replace(ChrW(&H2009), "&thinsp;")
sb.Replace(ChrW(&H200C), "&zwnj;") sb.Replace(ChrW(&H200C), "&zwnj;")
sb.Replace(ChrW(&H200D), "&zwj;") sb.Replace(ChrW(&H200D), "&zwj;")
sb.Replace(ChrW(&H200E), "&lrm;") sb.Replace(ChrW(&H200E), "&lrm;")
sb.Replace(ChrW(&H200F), "&rlm;") sb.Replace(ChrW(&H200F), "&rlm;")
sb.Replace(ChrW(&H2013), "&ndash;") sb.Replace(ChrW(&H2013), "&ndash;")
sb.Replace(ChrW(&H2014), "&mdash;") sb.Replace(ChrW(&H2014), "&mdash;")
sb.Replace(ChrW(&H2018), "&lsquo;") sb.Replace(ChrW(&H2018), "&lsquo;")
sb.Replace(ChrW(&H2019), "&rsquo;") sb.Replace(ChrW(&H2019), "&rsquo;")
sb.Replace(ChrW(&H201A), "&sbquo;") sb.Replace(ChrW(&H201A), "&sbquo;")
sb.Replace(ChrW(&H201C), "&ldquo;") sb.Replace(ChrW(&H201C), "&ldquo;")
sb.Replace(ChrW(&H201D), "&rdquo;") sb.Replace(ChrW(&H201D), "&rdquo;")
sb.Replace(ChrW(&H201E), "&bdquo;") sb.Replace(ChrW(&H201E), "&bdquo;")
sb.Replace(ChrW(&H2020), "&dagger;") sb.Replace(ChrW(&H2020), "&dagger;")
sb.Replace(ChrW(&H2021), "&Dagger;") sb.Replace(ChrW(&H2021), "&Dagger;")
sb.Replace(ChrW(&H2022), "&bull;") sb.Replace(ChrW(&H2022), "&bull;")
sb.Replace(ChrW(&H2026), "&hellip;") sb.Replace(ChrW(&H2026), "&hellip;")
sb.Replace(ChrW(&H2030), "&permil;") sb.Replace(ChrW(&H2030), "&permil;")
sb.Replace(ChrW(&H2032), "&prime;") sb.Replace(ChrW(&H2032), "&prime;")
sb.Replace(ChrW(&H2033), "&Prime;") sb.Replace(ChrW(&H2033), "&Prime;")
sb.Replace(ChrW(&H2039), "&lsaquo;") sb.Replace(ChrW(&H2039), "&lsaquo;")
sb.Replace(ChrW(&H203A), "&rsaquo;") sb.Replace(ChrW(&H203A), "&rsaquo;")
sb.Replace(ChrW(&H203E), "&oline;") sb.Replace(ChrW(&H203E), "&oline;")
sb.Replace(ChrW(&H2044), "&frasl;") sb.Replace(ChrW(&H2044), "&frasl;")
sb.Replace(ChrW(&H20AC), "&euro;") sb.Replace(ChrW(&H20AC), "&euro;")
sb.Replace(ChrW(&H2111), "&image;") sb.Replace(ChrW(&H2111), "&image;")
sb.Replace(ChrW(&H2118), "&weierp;") sb.Replace(ChrW(&H2118), "&weierp;")
sb.Replace(ChrW(&H211C), "&real;") sb.Replace(ChrW(&H211C), "&real;")
sb.Replace(ChrW(&H2122), "&trade;") sb.Replace(ChrW(&H2122), "&trade;")
sb.Replace(ChrW(&H2135), "&alefsym;") sb.Replace(ChrW(&H2135), "&alefsym;")
sb.Replace(ChrW(&H2190), "&larr;") sb.Replace(ChrW(&H2190), "&larr;")
sb.Replace(ChrW(&H2191), "&uarr;") sb.Replace(ChrW(&H2191), "&uarr;")
sb.Replace(ChrW(&H2192), "&rarr;") sb.Replace(ChrW(&H2192), "&rarr;")
sb.Replace(ChrW(&H2193), "&darr;") sb.Replace(ChrW(&H2193), "&darr;")
sb.Replace(ChrW(&H2194), "&harr;") sb.Replace(ChrW(&H2194), "&harr;")
sb.Replace(ChrW(&H21B5), "&crarr;") sb.Replace(ChrW(&H21B5), "&crarr;")
sb.Replace(ChrW(&H21D0), "&lArr;") sb.Replace(ChrW(&H21D0), "&lArr;")
sb.Replace(ChrW(&H21D1), "&uArr;") sb.Replace(ChrW(&H21D1), "&uArr;")
sb.Replace(ChrW(&H21D2), "&rArr;") sb.Replace(ChrW(&H21D2), "&rArr;")
sb.Replace(ChrW(&H21D3), "&dArr;") sb.Replace(ChrW(&H21D3), "&dArr;")
sb.Replace(ChrW(&H21D4), "&hArr;") sb.Replace(ChrW(&H21D4), "&hArr;")
sb.Replace(ChrW(&H2200), "&forall;") sb.Replace(ChrW(&H2200), "&forall;")
sb.Replace(ChrW(&H2202), "&part;") sb.Replace(ChrW(&H2202), "&part;")
sb.Replace(ChrW(&H2203), "&exist;") sb.Replace(ChrW(&H2203), "&exist;")
sb.Replace(ChrW(&H2205), "&empty;") sb.Replace(ChrW(&H2205), "&empty;")
sb.Replace(ChrW(&H2207), "&nabla;") sb.Replace(ChrW(&H2207), "&nabla;")
sb.Replace(ChrW(&H2208), "&isin;") sb.Replace(ChrW(&H2208), "&isin;")
sb.Replace(ChrW(&H2209), "&notin;") sb.Replace(ChrW(&H2209), "&notin;")
sb.Replace(ChrW(&H220B), "&ni;") sb.Replace(ChrW(&H220B), "&ni;")
sb.Replace(ChrW(&H220F), "&prod;") sb.Replace(ChrW(&H220F), "&prod;")
sb.Replace(ChrW(&H2211), "&sum;") sb.Replace(ChrW(&H2211), "&sum;")
sb.Replace(ChrW(&H2212), "&minus;") sb.Replace(ChrW(&H2212), "&minus;")
sb.Replace(ChrW(&H2217), "&lowast;") sb.Replace(ChrW(&H2217), "&lowast;")
sb.Replace(ChrW(&H221A), "&radic;") sb.Replace(ChrW(&H221A), "&radic;")
sb.Replace(ChrW(&H221D), "&prop;") sb.Replace(ChrW(&H221D), "&prop;")
sb.Replace(ChrW(&H221E), "&infin;") sb.Replace(ChrW(&H221E), "&infin;")
sb.Replace(ChrW(&H2220), "&ang;") sb.Replace(ChrW(&H2220), "&ang;")
sb.Replace(ChrW(&H2227), "&and;") sb.Replace(ChrW(&H2227), "&and;")
sb.Replace(ChrW(&H2228), "&or;") sb.Replace(ChrW(&H2228), "&or;")
sb.Replace(ChrW(&H2229), "&cap;") sb.Replace(ChrW(&H2229), "&cap;")
sb.Replace(ChrW(&H222A), "&cup;") sb.Replace(ChrW(&H222A), "&cup;")
sb.Replace(ChrW(&H222B), "&int;") sb.Replace(ChrW(&H222B), "&int;")
sb.Replace(ChrW(&H2234), "&there4;") sb.Replace(ChrW(&H2234), "&there4;")
sb.Replace(ChrW(&H223C), "&sim;") sb.Replace(ChrW(&H223C), "&sim;")
sb.Replace(ChrW(&H2245), "&cong;") sb.Replace(ChrW(&H2245), "&cong;")
sb.Replace(ChrW(&H2248), "&asymp;") sb.Replace(ChrW(&H2248), "&asymp;")
sb.Replace(ChrW(&H2260), "&ne;") sb.Replace(ChrW(&H2260), "&ne;")
sb.Replace(ChrW(&H2261), "&equiv;") sb.Replace(ChrW(&H2261), "&equiv;")
sb.Replace(ChrW(&H2264), "&le;") sb.Replace(ChrW(&H2264), "&le;")
sb.Replace(ChrW(&H2265), "&ge;") sb.Replace(ChrW(&H2265), "&ge;")
sb.Replace(ChrW(&H2282), "&sub;") sb.Replace(ChrW(&H2282), "&sub;")
sb.Replace(ChrW(&H2283), "&sup;") sb.Replace(ChrW(&H2283), "&sup;")
sb.Replace(ChrW(&H2284), "&nsub;") sb.Replace(ChrW(&H2284), "&nsub;")
sb.Replace(ChrW(&H2286), "&sube;") sb.Replace(ChrW(&H2286), "&sube;")
sb.Replace(ChrW(&H2287), "&supe;") sb.Replace(ChrW(&H2287), "&supe;")
sb.Replace(ChrW(&H2295), "&oplus;") sb.Replace(ChrW(&H2295), "&oplus;")
sb.Replace(ChrW(&H2297), "&otimes;") sb.Replace(ChrW(&H2297), "&otimes;")
sb.Replace(ChrW(&H22A5), "&perp;") sb.Replace(ChrW(&H22A5), "&perp;")
sb.Replace(ChrW(&H22C5), "&sdot;") sb.Replace(ChrW(&H22C5), "&sdot;")
sb.Replace(ChrW(&H2308), "&lceil;") sb.Replace(ChrW(&H2308), "&lceil;")
sb.Replace(ChrW(&H2309), "&rceil;") sb.Replace(ChrW(&H2309), "&rceil;")
sb.Replace(ChrW(&H230A), "&lfloor;") sb.Replace(ChrW(&H230A), "&lfloor;")
sb.Replace(ChrW(&H230B), "&rfloor;") sb.Replace(ChrW(&H230B), "&rfloor;")
sb.Replace(ChrW(&H2329), "&lang;") sb.Replace(ChrW(&H2329), "&lang;")
sb.Replace(ChrW(&H232A), "&rang;") sb.Replace(ChrW(&H232A), "&rang;")
sb.Replace(ChrW(&H25CA), "&loz;") sb.Replace(ChrW(&H25CA), "&loz;")
sb.Replace(ChrW(&H2660), "&spades;") sb.Replace(ChrW(&H2660), "&spades;")
sb.Replace(ChrW(&H2663), "&clubs;") sb.Replace(ChrW(&H2663), "&clubs;")
sb.Replace(ChrW(&H2665), "&hearts;") sb.Replace(ChrW(&H2665), "&hearts;")
sb.Replace(ChrW(&H2666), "&diams;") sb.Replace(ChrW(&H2666), "&diams;")
sb.Replace(" ", " &nbsp;") sb.Replace(" ", " &nbsp;")
Return sb.ToString() Return sb.ToString()
End Function End Function
Sub AddTokenizerBaseMacros(ByVal tokenizer As Tokenization.Tokenizer) Sub AddTokenizerBaseMacros(ByVal tokenizer As Tokenization.Tokenizer)
'* '*
'* Prepares the BBCode Grammar '* Prepares the BBCode Grammar
'* '*
With tokenizer With tokenizer
.Options = RegexOptions.Singleline Or RegexOptions.IgnoreCase .Options = RegexOptions.Singleline Or RegexOptions.IgnoreCase
'* '*
'* Define the grammar macros '* Define the grammar macros
'* '*
.AddMacro("h", "[0-9a-f]") .AddMacro("h", "[0-9a-f]")
.AddMacro("nl", "\n|\r\n|\r|\f") .AddMacro("nl", "\n|\r\n|\r|\f")
.AddMacro("space", "[ \t\r\n\f]+") .AddMacro("space", "[ \t\r\n\f]+")
.AddMacro("s", "[ \t\r\n\f]*") .AddMacro("s", "[ \t\r\n\f]*")
.AddMacro("w", "{s}?") .AddMacro("w", "{s}?")
.AddMacro("nonascii", "[\u0080-\uffff]") .AddMacro("nonascii", "[\u0080-\uffff]")
.AddMacro("unicode", "\\{h}{1,6}(\r\n|[ \t\r\n\f])?") .AddMacro("unicode", "\\{h}{1,6}(\r\n|[ \t\r\n\f])?")
.AddMacro("escape", "{unicode}|\\[^\r\n\f0-9a-f]") .AddMacro("escape", "{unicode}|\\[^\r\n\f0-9a-f]")
.AddMacro("nmstart", "[_a-z]|{nonascii}|{escape}") .AddMacro("nmstart", "[_a-z]|{nonascii}|{escape}")
.AddMacro("nmchar", "[_a-z0-9-]|{nonascii}|{escape}") .AddMacro("nmchar", "[_a-z0-9-]|{nonascii}|{escape}")
.AddMacro("string1", "\""([^\n\r\f\\""]|\\{nl}|{escape})*\""") .AddMacro("string1", "\""([^\n\r\f\\""]|\\{nl}|{escape})*\""")
.AddMacro("string2", "\'([^\n\r\f\\']|\\{nl}|{escape})*\'") .AddMacro("string2", "\'([^\n\r\f\\']|\\{nl}|{escape})*\'")
.AddMacro("invalid1", "\""([^\n\r\f\\""]|\\{nl}|{escape})*") .AddMacro("invalid1", "\""([^\n\r\f\\""]|\\{nl}|{escape})*")
.AddMacro("invalid2", "\'([^\n\r\f\\']|\\{nl}|{escape})*") .AddMacro("invalid2", "\'([^\n\r\f\\']|\\{nl}|{escape})*")
.AddMacro("name", "{nmchar}+") .AddMacro("name", "{nmchar}+")
.AddMacro("num", "[0-9]+|[0-9]*\.[0-9]+") .AddMacro("num", "[0-9]+|[0-9]*\.[0-9]+")
.AddMacro("string", "{string1}|{string2}") .AddMacro("string", "{string1}|{string2}")
.AddMacro("invalid", "{invalid1}|{invalid2}") .AddMacro("invalid", "{invalid1}|{invalid2}")
.AddMacro("url", "(ftp|https?://)" & _ .AddMacro("url", "(ftp|https?://)" & _
"(([0-9a-z_!~*'().&=+$%-]+):([0-9a-z_!~*'().&=+$%-]+@))?" & _ "(([0-9a-z_!~*'().&=+$%-]+):([0-9a-z_!~*'().&=+$%-]+@))?" & _
"(([0-9]{1,3}\.){3}[0-9]{1,3}|([0-9a-z_!~*'()-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6})" & _ "(([0-9]{1,3}\.){3}[0-9]{1,3}|([0-9a-z_!~*'()-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6})" & _
"(:[0-9]{1,4})?" & _ "(:[0-9]{1,4})?" & _
"((/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?|(/?))") "((/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?|(/?))")
.AddMacro("mail", "(?:mailto:)?[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}") .AddMacro("mail", "(?:mailto:)?[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}")
.AddMacro("value", "{string}|{mail}|{url}|{num}|{name}") .AddMacro("value", "{string}|{mail}|{url}|{num}|{name}")
.AddMacro("param", "{name}{w}={w}{value}") .AddMacro("param", "{name}{w}={w}{value}")
.AddMacro("params", "({space}({param}|{name}))+") .AddMacro("params", "({space}({param}|{name}))+")
End With End With
End Sub End Sub
End Module End Module

@ -3,3 +3,11 @@ Code taken from <http://pjondevelopment.50webs.com/articles/bbcodeParser.html> a
svn r133, git 71e2a7fd3675ddb5f57f9d4c9aecec204b984e6b svn r133, git 71e2a7fd3675ddb5f57f9d4c9aecec204b984e6b
<https://github.com/inga-lovinde/FLocal/commit/71e2a7fd3675ddb5f57f9d4c9aecec204b984e6b> <https://github.com/inga-lovinde/FLocal/commit/71e2a7fd3675ddb5f57f9d4c9aecec204b984e6b>
Implemented RequireClosingTag flag for custom-defined elements Implemented RequireClosingTag flag for custom-defined elements
git eeae827bf12d943cbc55852c76b773b5554ebb4d
<https://github.com/inga-lovinde/FLocal/commit/eeae827bf12d943cbc55852c76b773b5554ebb4d>
PJonDevelopment refactored to use generic context
git f93fee93ee468b3a950704595988d6d2277d43ad
<https://github.com/inga-lovinde/FLocal/commit/f93fee93ee468b3a950704595988d6d2277d43ad>
PJonDevelopment.BBCode.ITextFormatter refactored to use generic context

@ -9,7 +9,7 @@ namespace Web.Core.DB {
List<Dictionary<string, string>> LoadByIds(ITableSpec table, List<string> ids); List<Dictionary<string, string>> LoadByIds(ITableSpec table, List<string> ids);
List<string> LoadIdsByConditions(ITableSpec table, AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, bool allowHugeLists); List<string> LoadIdsByConditions(ITableSpec table, AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts, ColumnSpec idSpec, bool allowHugeLists);
long GetCountByConditions(ITableSpec table, AbstractCondition conditions, params JoinSpec[] joins); long GetCountByConditions(ITableSpec table, AbstractCondition conditions, params JoinSpec[] joins);
@ -34,13 +34,17 @@ namespace Web.Core.DB {
public static class IDBConnectionExtensions { public static class IDBConnectionExtensions {
public static List<string> LoadIdsByConditions(this IDBConnection connection, ITableSpec table, AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts) { public static List<string> LoadIdsByConditions(this IDBConnection connection, ITableSpec table, AbstractCondition conditions, Diapasone diapasone, JoinSpec[] joins, SortSpec[] sorts) {
return connection.LoadIdsByConditions(table, conditions, diapasone, joins, sorts, false); return connection.LoadIdsByConditions(table, conditions, diapasone, joins, sorts, table.getIdSpec(), false);
} }
public static List<string> LoadIdsByConditions(this IDBConnection connection, ITableSpec table, AbstractCondition conditions, Diapasone diapasone, params JoinSpec[] joins) { public static List<string> LoadIdsByConditions(this IDBConnection connection, ITableSpec table, AbstractCondition conditions, Diapasone diapasone, params JoinSpec[] joins) {
return connection.LoadIdsByConditions(table, conditions, diapasone, joins, new SortSpec[] { new SortSpec(table.getIdSpec(), true) }); return connection.LoadIdsByConditions(table, conditions, diapasone, joins, new SortSpec[] { new SortSpec(table.getIdSpec(), true) });
} }
public static List<string> LoadIdsByConditions(this IDBConnection connection, ITableSpec table, AbstractCondition conditions, Diapasone diapasone, ColumnSpec idSpec, params SortSpec[] sorts) {
return connection.LoadIdsByConditions(table, conditions, diapasone, new JoinSpec[0], sorts, idSpec, false);
}
public static Transaction beginTransaction(this IDBConnection connection) { public static Transaction beginTransaction(this IDBConnection connection) {
return connection.beginTransaction(System.Data.IsolationLevel.ReadCommitted); return connection.beginTransaction(System.Data.IsolationLevel.ReadCommitted);
} }

Loading…
Cancel
Save