📄 formatter.cs
字号:
using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.SessionState;
using System.Diagnostics;
using ScrewTurn.Wiki.PluginFramework;
namespace ScrewTurn.Wiki {
/// <summary>
/// Performs all the text formatting and parsing operations.
/// </summary>
public static class Formatter {
private static Regex noWiki = new Regex(@"\<nowiki\>(.|\n|\r)+?\<\/nowiki\>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex link = new Regex(@"(\[\[.+?\]\])|(\[.+?\])", RegexOptions.Compiled);
private static Regex redirection = new Regex(@"^\ *\>\>\>\ *.+\ *$", RegexOptions.Compiled | RegexOptions.Multiline);
private static Regex h1 = new Regex(@"^==.+?==", RegexOptions.Compiled | RegexOptions.Multiline);
private static Regex h2 = new Regex(@"^===.+?===", RegexOptions.Compiled | RegexOptions.Multiline);
private static Regex h3 = new Regex(@"^====.+?====", RegexOptions.Compiled | RegexOptions.Multiline);
private static Regex h4 = new Regex(@"^=====.+?=====", RegexOptions.Compiled | RegexOptions.Multiline);
private static Regex bold = new Regex(@"'''.+?'''", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex italic = new Regex(@"''.+?''", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex boldItalic = new Regex(@"'''''.+?'''''", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex underlined = new Regex(@"__.+?__", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex striked = new Regex(@"(?<!\<\!)(\-\-(?!\>).+?\-\-)(?!\>)", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex code = new Regex(@"\{\{.+?\}\}", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex pre = new Regex(@"\{\{\{\{.+?\}\}\}\}", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex box = new Regex(@"\(\(\(.+?\)\)\)", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex specialTag = new Regex(@"\{(wikititle|wikiversion|mainurl|up|rsspage|themepath|clear|br|top|searchbox|pagecount|cloud)\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex list = new Regex(@"(?<=(\n|^))((\*|\#)+(\ )?.+?\n)+", RegexOptions.Compiled);
private static Regex toc = new Regex(@"\{toc\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex transclusion = new Regex(@"\{T(\:|\|).+}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex hr = new Regex(@"(?<=(\n|^))(\ )*----(\ )*\n", RegexOptions.Compiled);
private static Regex snippet = new Regex(@"\{S(\:|\|).+?}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex table = new Regex(@"\{\|(\ [^\n]*)?\n.+?\|\}", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex indent = new Regex(@"(?<=(\n|^))\:+(\ )?.+?\n", RegexOptions.Compiled);
private static Regex esc = new Regex(@"\<esc\>(.|\n|\r)*?\<\/esc\>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex sign = new Regex(@"Ё\(.+?\)Ё", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static Regex fullCode = new Regex(@"@@.+?@@", RegexOptions.Compiled | RegexOptions.Singleline);
private static Regex username = new Regex(@"\{username\}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static string tocTitlePlaceHolder = "%%%%TocTitlePlaceHolder%%%%";
private static string editSectionPlaceHolder = "%%%%EditSectionPlaceHolder%%%%"; // This string is also used in History.aspx.cs
private static string upReplacement = "GetFile.aspx?File=";
public static string Format(string raw, PageInfo current) {
PageInfo[] lp;
return Format(raw, current, out lp);
}
/// <summary>
/// Formats WikiMarkup, converting it into XHTML.
/// </summary>
/// <param name="raw">The raw WikiMarkup text.</param>
/// <param name="pages">The Pages.</param>
/// <param name="current">The current Page (can be null).</param>
/// <returns>The formatted text.</returns>
public static string Format(string raw, PageInfo current, out PageInfo[] linkedPages) {
linkedPages = new PageInfo[0];
List<PageInfo> lp = new List<PageInfo>();
StringBuilder sb = new StringBuilder(raw);
Match match;
string tmp, a, n, url, title, bigUrl;
StringBuilder dummy; // Used for temporary string manipulation inside formatting cycles
bool done = false;
List<int> noWikiBegin = new List<int>(), noWikiEnd = new List<int>();
int end = 0;
List<HPosition> hPos = new List<HPosition>();
sb.Replace("\r", "");
if(!sb.ToString().EndsWith("\n")) sb.Append("\n"); // Very important to make Regular Expressions work!
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
if(current != null) {
// Check redirection
match = redirection.Match(sb.ToString());
if(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
string destination = match.Value.Trim().Substring(4).Trim();
while(destination.StartsWith("[") && destination.EndsWith("]")) {
destination = destination.Substring(1, destination.Length - 2);
}
while(sb[match.Index] == '\n' && match.Index < sb.Length - 1) sb.Remove(match.Index, 1);
PageInfo dest = Pages.Instance.FindPage(destination);
if(dest != null) {
Redirections.Instance.AddRedirection(current, dest);
}
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
}
}
// Before Producing HTML
match = fullCode.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
string content = match.Value.Substring(2, match.Length - 4);
dummy = new StringBuilder();
dummy.Append("<pre>");
dummy.Append(EscapeWikiMarkup(content));
dummy.Append("</pre>");
sb.Insert(match.Index, dummy.ToString());
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = fullCode.Match(sb.ToString(), end);
}
// No more needed (Striked Regex modified)
// Temporarily "escape" comments
//sb.Replace("<!--", "($_^)");
//sb.Replace("-->", "(^_$)");
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
// Before Producing HTML
match = esc.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
sb.Insert(match.Index, match.Value.Substring(5, match.Length - 11).Replace("<", "<").Replace(">", ">"));
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = esc.Match(sb.ToString(), end);
}
// Snippets were here
// Moved to solve problems with lists and tables
match = table.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
sb.Insert(match.Index, BuildTable(match.Value));
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = table.Match(sb.ToString(), end);
}
match = indent.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
sb.Insert(match.Index, BuildIndent(match.Value) + "\n");
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = indent.Match(sb.ToString(), end);
}
match = specialTag.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
switch(match.Value.Substring(1, match.Value.Length - 2).ToUpper()) {
case "WIKITITLE":
sb.Insert(match.Index, Settings.WikiTitle);
break;
case "WIKIVERSION":
sb.Insert(match.Index, Settings.WikiVersion);
break;
case "MAINURL":
sb.Insert(match.Index, Settings.MainUrl);
break;
case "UP":
sb.Insert(match.Index, upReplacement);
break;
case "RSSPAGE":
if(current != null) {
sb.Insert(match.Index, @"<a href=""RSS.aspx?Page=" + Tools.UrlEncode(current.Name) + @""" title=""" + Exchanger.ResourceExchanger.GetResource("RssForThisPage") + @"""><img src=""" + Settings.ThemePath + @"Images/RSS.png"" alt=""RSS"" /></a>");
}
break;
case "THEMEPATH":
sb.Insert(match.Index, Settings.ThemePath);
break;
case "CLEAR":
sb.Insert(match.Index, @"<div style=""clear: both;""></div>");
break;
case "BR":
sb.Insert(match.Index, "<br />");
break;
case "TOP":
sb.Insert(match.Index, @"<a href=""#PageTop"">" + Exchanger.ResourceExchanger.GetResource("Top") + "</a>");
break;
case "SEARCHBOX":
sb.Insert(match.Index, @"<nowiki><input type=""text"" id=""TxtSearchBox"" onkeydown=""javascript:var keycode; if(window.event) keycode = event.keyCode; else keycode = event.which; if(keycode == 10 || keycode == 13) { document.location = 'Search.aspx?Query=' + encodeURI(document.getElementById('TxtSearchBox').value); return false; }"" /></nowiki>");
break;
case "PAGECOUNT":
sb.Insert(match.Index, Pages.Instance.AllPages.Count.ToString());
break;
case "CLOUD":
sb.Insert(match.Index, BuildCloud());
break;
}
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = specialTag.Match(sb.ToString(), end);
}
match = list.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
int d = 0;
try {
sb.Insert(match.Index, GenerateList(match.Value.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries), 0, 0, ref d) + "\n");
}
catch {
sb.Insert(match.Index, @"<b style=""color: #FF0000;"">FORMATTER ERROR (Malformed List)</b>");
}
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = list.Match(sb.ToString(), end);
}
match = hr.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
sb.Insert(match.Index, @"<h1 style=""border-bottom: solid 1px #999999;""> </h1>" + "\n");
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = hr.Match(sb.ToString(), end);
}
// Replace \n with BR was here
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
// Transclusion (intra-Wiki)
match = transclusion.Match(sb.ToString());
while(match.Success) {
if(!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
sb.Remove(match.Index, match.Length);
PageInfo info = Pages.Instance.FindPage(match.Value.Substring(3, match.Value.Length - 4));
if(info != null && info != current) { // Avoid circular transclusion!
dummy = new StringBuilder();
dummy.Append(@"<div class=""transcludedpage"">");
// The current PageInfo is null to disable section editing and similar features
dummy.Append(FormattingPipeline.FormatWithPhase1And2(Content.GetPageContent(info, true).Content, null));
dummy.Append("</div>");
sb.Insert(match.Index, dummy.ToString());
}
}
ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
match = transclusion.Match(sb.ToString(), end);
}
List<string> attachments = new List<string>();
// Links and images
match = link.Match(sb.ToString());
while(match.Success) {
if(IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end)) {
match = link.Match(sb.ToString(), end);
continue;
}
if(match.Value.Equals("[]") || match.Value.Equals("[[]]")) continue; // Prevents formatting emtpy links
done = false;
if(match.Value.StartsWith("[[")) tmp = match.Value.Substring(2, match.Length - 4).Trim();
else tmp = match.Value.Substring(1, match.Length - 2).Trim();
sb.Remove(match.Index, match.Length);
a = "";
n = "";
if(tmp.IndexOf("|") != -1) {
// There are some fields
string[] fields = tmp.Split('|');
if(fields.Length == 2) {
// Link with title
a = fields[0];
n = fields[1];
}
else {
StringBuilder img = new StringBuilder();
// Image
if(fields[0].ToLower().Equals("imageleft") || fields[0].ToLower().Equals("imageright") || fields[0].ToLower().Equals("imageauto")) {
string c = "";
switch(fields[0].ToLower()) {
case "imageleft":
c = "imageleft";
break;
case "imageright":
c = "imageright";
break;
case "imageauto":
c = "imageauto";
break;
default:
c = "image";
break;
}
title = fields[1];
url = fields[2];
if(fields.Length == 4) bigUrl = fields[3];
else bigUrl = "";
url = EscapeUrl(url);
bigUrl = EscapeUrl(bigUrl);
if(c.Equals("imageauto")) {
img.Append(@"<table class=""imageauto"" cellpadding=""0"" cellspacing=""0"" align=""center""><tr><td>");
}
else {
img.Append(@"<div class=""");
img.Append(c);
img.Append(@""">");
}
if(bigUrl.Length > 0) {
dummy = new StringBuilder();
dummy.Append(@"<img class=""image"" src=""");
dummy.Append(url);
dummy.Append(@""" alt=""");
dummy.Append(Exchanger.ResourceExchanger.GetResource("Image"));
dummy.Append(@""" />");
img.Append(BuildLink(bigUrl, dummy.ToString(), true, lp));
}
else {
img.Append(@"<img class=""image"" src=""");
img.Append(url);
img.Append(@""" alt=""");
img.Append(Exchanger.ResourceExchanger.GetResource("Image"));
img.Append(@""" />");
}
if(title.Length > 0) {
img.Append(@"<p class=""imagedescription"">");
img.Append(title);
img.Append("</p>");
}
if(c.Equals("imageauto")) {
img.Append("</td></tr></table>");
}
else {
img.Append("</div>");
}
sb.Insert(match.Index, img);
}
else if(fields[0].ToLower().Equals("image")) {
title = fields[1];
url = fields[2];
if(fields.Length == 4) bigUrl = fields[3];
else bigUrl = "";
url = EscapeUrl(url);
bigUrl = EscapeUrl(bigUrl);
if(bigUrl.Length > 0) {
dummy = new StringBuilder();
dummy.Append(@"<img src=""");
dummy.Append(url);
dummy.Append(@""" alt=""");
dummy.Append(Exchanger.ResourceExchanger.GetResource("Image"));
dummy.Append(@""" />");
img.Append(BuildLink(bigUrl, dummy.ToString(), true, lp));
}
else {
img.Append(@"<img src=""");
img.Append(url);
img.Append(@""" alt=""");
img.Append(Exchanger.ResourceExchanger.GetResource("Image"));
img.Append(@""" />");
}
sb.Insert(match.Index, img.ToString());
}
else {
sb.Insert(match.Index, @"<b style=""color: #FF0000;"">FORMATTER ERROR (Malformed Image Tag)</b>");
}
done = true;
}
}
else if(tmp.ToLower().StartsWith("attachment:")) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -