/*
OpenCollective - http://www.netbrick.net/ -- Version 0.73
Copyright (c) 2004
by Tyler Jensen ( tylerj@netbrick.net ) of NetBrick Inc. ( http://www.netbrick.net )
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Web;
using OpenCollective.Data;
namespace OpenCollective.Engine
{
/// <summary>
/// The wiki parser is the presentation engine that converts plain text entries to presentable HTML.
/// </summary>
public class Parser
{
// no public constructor, methods only
public Parser() {}
// Regex to extract topics from a line
// or the whole text like topic_text or topic_
public static string LinkRxForm = @"(\^\[\[|\+\[\[|\[\[)(.)*?(\]\])";
//Now use the string to create the Regex
public static Regex LinkRx = new Regex(LinkRxForm, RegexOptions.Compiled);
public static Regex LinkRxAll = new Regex(LinkRxForm, RegexOptions.Compiled|RegexOptions.Singleline);
public string ConvertWikiTextToHTML(string appPath, string wikiName, string topic, string wikitext)
{
StringBuilder result = new StringBuilder(7);
ArrayList Segments = this.SegmentWikiText(wikitext);
for(int i = 0; i < Segments.Count; i++)
{
WikiTextBlock block = (WikiTextBlock)Segments[i];
switch(block.BlockType)
{
case WikiTextBlockType.List:
result.Append(FormatListWikiText(appPath, wikiName, topic, block.WikiText));
break;
case WikiTextBlockType.Code:
result.Append(FormatCodeWikiText(appPath, wikiName, topic, block.WikiText));
break;
case WikiTextBlockType.Table:
result.Append(FormatTableWikiText(appPath, wikiName, topic, block.WikiText));
break;
default:
result.Append(FormatPlainWikiText(appPath, wikiName, topic, block.WikiText));
break;
}
}
return result.ToString();
}
public string ConvertWikiTextToHTMLNoLinks(string appPath, string wikiName, string topic, string wikitext)
{
bool hideLinks = true;
StringBuilder result = new StringBuilder(7);
ArrayList Segments = this.SegmentWikiText(wikitext);
for(int i = 0; i < Segments.Count; i++)
{
WikiTextBlock block = (WikiTextBlock)Segments[i];
switch(block.BlockType)
{
case WikiTextBlockType.List:
result.Append(FormatListWikiText(appPath, wikiName, topic, block.WikiText, hideLinks));
break;
case WikiTextBlockType.Code:
result.Append(FormatCodeWikiText(appPath, wikiName, topic, block.WikiText));
break;
case WikiTextBlockType.Table:
result.Append(FormatTableWikiText(appPath, wikiName, topic, block.WikiText, hideLinks));
break;
default:
result.Append(FormatPlainWikiText(appPath, wikiName, topic, block.WikiText, hideLinks));
break;
}
}
return result.ToString();
}
private string FormatListWikiText(string appPath, string wikiName, string topic, string wikitext)
{
bool hideLinks = false;
return FormatListWikiText(appPath, wikiName, topic, wikitext, hideLinks);
}
private string FormatListWikiText(string appPath, string wikiName, string topic, string wikitext, bool hideLinks)
{
wikitext = wikitext.Replace("\r", "");
string [] Lines = wikitext.Split (new char[] {'\n'});
StringBuilder result = new StringBuilder(7);
Stack listStack = new Stack(3); //a stack for ol and ul lists
bool isFirstLine = true;
foreach(string wikiline in Lines)
{
if(wikiline != "") //only parse lines with text or return character - this eliminates any trailing empty lines
{
string theLine = wikiline; //wikiline.Replace("\r", ""); //drop the return character
//this.HTMLEncodeSimple(ref theLine); //convert all & < > characters //TODO - consider allowing HTML
this.FormatStandardWikiLine(appPath, wikiName, ref theLine, topic, hideLinks);
//handle lists Stack listStack, bool inList, (indent level can be taken from listStack.Count
string listLine = "";
//check for the list line format @"(^\s[\s]+)(1|\*)(\s)"
Match m = Regex.Match(theLine, @"(^\s[\s]+)(\#|\*)(\s)", RegexOptions.Compiled);
if (m.Length > 0)
listLine = m.ToString();
int indentLevel = listStack.Count;
int indentSpaces = listLine.Length - 2;
bool isBullet = true;
if(listLine != "")
isBullet = listLine.Substring(listLine.Length - 2, 1) == "*";
double levelsToChange = 0.0;
double rem = 0.0;
if(isFirstLine)
{
rem = indentSpaces % 2;
if (rem > 0.0)
levelsToChange = (indentSpaces + 1) / 2;
else
levelsToChange = indentSpaces / 2;
theLine = "<li>" + theLine.Remove(0, listLine.Length) + "</li>\n";
for (int x = 0; x < levelsToChange; x++) //note: we need at least one header
{
if (isBullet)
{
theLine = "<ul>" + theLine;
listStack.Push("</ul>");
}
else
{
switch (x)
{
case 1:
theLine = "<ol type=\"A\">" + theLine;
break;
case 2:
theLine = "<ol type=\"a\">" + theLine;
break;
default:
theLine = "<ol>" + theLine;
break;
}
listStack.Push("</ol>");
}
}
theLine += "\n";
isFirstLine = false; //toggle back now that the first line is done
}
else
{
//this is a subsequent list line - get it's indent level and if different that current indentLevel, adjust
if ((indentLevel * 2) == indentSpaces)
{
//this line is on the same indent level, so just add <li> and </li> tags - removing the list indicator of course
theLine = "<li>" + theLine.Remove(0, listLine.Length) + "</li>\n";
}
else
{
//indent level changed so we either have to add a <ul> or <ol> and push the close tags
//or we have to pop close tags to close the number of levels the line dropped and then add <li> and </li> tags
if ((indentLevel * 2) > indentSpaces)
{
//going back in, so pop some close tags off after applying the <li> tag - ? is how many to pop off
rem = ((indentLevel * 2) - indentSpaces) % 2;
if (rem > 0.0)
levelsToChange = ((indentLevel * 2) - indentSpaces) / 2;
else
levelsToChange = ((indentLevel * 2) - indentSpaces + 1) / 2;
theLine = "<li>" + theLine.Remove(0, listLine.Length) + "</li>\n";
for (int x = 0; x < levelsToChange; x++)
{
try
{
theLine = listStack.Pop().ToString() + theLine;
}
catch
{
//do nothing - means we popped 'em all off
}
}
theLine += "\n";
}
else
{
//indenting even more, so add the <ul> or <ol> tag, push the close tag and add the <li> tag
rem = (indentSpaces - (indentLevel * 2)) % 2;
if (rem > 0.0)
levelsToChange = (indentSpaces - (indentLevel * 2) + 1) / 2;
else
levelsToChange = (indentSpaces - (indentLevel * 2)) / 2;
theLine = "<li>" + theLine.Remove(0, listLine.Length) + "</li>\n";
for (int x = 0; x < levelsToChange; x++)
{
if (isBullet)
{
theLine = "<ul>" + theLine;
listStack.Push("</ul>");
}
else
{
switch (indentLevel + x)
{
case 1:
theLine = "<ol type=\"A\">" + theLine;
break;
case 2:
theLine = "<ol type=\"a\">" + theLine;
break;
default:
theLine = "<ol>" + theLine;
break;
}
listStack.Push("</ol>");
}
}
theLine += "\n";
}
}
}
result.Append(theLine);
}
}
//append remaining </##> closing tages
while(listStack.Count > 0)
{
result.Append(listStack.Pop().ToString());
}
return result.ToString();
}
private string FormatTableWikiText(string appPath, string wikiName, string topic, string wikitext)
{
bool hideLinks = false;
return FormatTableWikiText(appPath, wikiName, topic, wikitext, hideLinks);
}
private string FormatTableWikiText(string appPath, string wikiName, string topic, string wikitext, bool hideLinks)
{
/*
++++(W360,H100%,B1)
@@
Here is a the first table data row. With column header style. FormatStandardWikiLine
@@
Here is the second column with header style. FormatStandardWikiLine
~~~~
||
Here is the first column data FormatStandardWikiLine
||(W60,H90)
Here is the second column data with width=60 and height=90 FormatStandardWikiLine
~~~~
##
Here is a the first table data row. With column footer style. FormatStandardWikiLine
##
Here is the second column with footer style. FormatStandardWikiLine
++++
++++(W360,H100%,B1)\n@@ \nHere is a the first table data row. With column header style.\n@@ \nHere is the second column with header style.\n~~~~\n||\nHere is the first column data\n||(W60,H90) \nHere is the second column data with width=60 and height=90\n~~~~\n##\nHere is a the first table data row. With column footer style.\n##\nHere is the second column with footer style. \n++++\n
*/
string head = Regex.Replace(wikitext.Substring(0, wikitext.IndexOf("\n")), @"(.*?)(\()(.+?)(\))", "$3", RegexOptions.Compiled);
string[] tboptions = head.Split(',');
string tbwidth = "";
string tbheight = "";
//string tbborder = "";
string tbalign = "";
for(int i = 0; i < tboptions.Length; i++)
{
if(tboptions[i].ToUpper().StartsWith("W"))
tbwidth = " width=\"" + tboptions[i].Substring(1, tboptions[i].Length - 1) + "\"";
else if(tboptions[i].ToUpper().StartsWith("H"))
tbheight = " height=\"" + tboptions[i].Substring(1, tboptions[i].Length - 1) + "\"";
else if(tboptions[i].ToUpper().StartsWith("C"))
tbalign = " align=\"" + tboptions[i].Substring(0, tboptions[i].Length) + "\"";
else if(tboptions[i].ToUpper().StartsWith("L"))
tbalign = " align=\"" + tboptions[i].Substring(0, tboptions[i].Length) + "\"";
else if(tboptions[i].ToUpper().StartsWith("R"))
tbalign = " align=\"" + tboptions[i].Substring(0, tboptions[i].Length) + "\"";
//else if(tboptions[i].ToUpper().StartsWith("B"))
// tbborder = " border=\"" + tboptions[i].Substring(1, tboptions[i].Length - 1) + "\"";
}
wikitext = wikitext.Remove(0, wikitext.IndexOf("\n") + 1);
wikitext = wikitext.Remove(wikitext.Length - 7, 7); //get rid of ++++\n
//string[] rows = wikitext.Split(new char[] {'~','~','~','~','\n'});
MatchCollection rows = Regex.Matches(wikitext, @"(@@|\|\||##)(.+?)(?=\n~~+.*?\n|$)", RegexOptions.Compiled|RegexOptions.Singleline); //(?:(\n~~~~\n|$))", RegexOptions.Compiled);
if(rows.Count == 0)
return "<div><br><b>@@@@@@@@@ TABLE FORMATTING ERROR - REMOVE TRAILING SPACES FROM ROW SEPARATORS ====</b><br></div>";
else
{
// @@ \nHere is a the first table data row. With column header style.\n@@ \nHere is the second column with header style.\n
// ||\nHere is the first column data\n||(W60,H90) \nHere is the second column data with width=60 and height=90\n
// ##\nHere is a the first table data row. With column footer style.\n##\nHere is the second column with footer style.
StringBuilder result = new StringBuilder(7);
result.Append("<table border=\"0\" class=\"ctable\" " + tbalign + tbwidth + tbheight + " cellspacing=\"0\" cellpadding=\"0\">\n");
foreach(Match row in rows)
{
string rowtext = row.ToString();
result.Append("<tr>");
MatchCollection columns = Regex.Matches(rowtext, @"(@@|\|\||##)(.*?)(?=$|@@|\|\||##)", RegexOptions.Compiled|RegexOptions.Singleline);
foreach(Match col in columns)
{
string coltext = col.ToString();
if(coltext.EndsWith("\n")) coltext = coltext.Substring(0, coltext.Length - 1);
if(coltext.StartsWith("@@") || coltext.StartsWith("||") || coltext.StartsWith("##"))
{
string[] colItems = coltext.Split('\n');
string rhead = colItems[0];
string tdclass = "cellhead";
if(rhead.StartsWith("||"))
tdclass = "celldata";
else if(rhead.StartsWith("##"))
tdclass = "cellfoot";
string rheadOptions = Regex.Replace(rhead, @"(.*?)(\()(.+?)(\))", "$3", RegexOptions.Compiled);
string[] rowOptions = rheadOptions.Split(',');
string tdwidth = "";
string tdheight = "";
string tdalign = "";
for(int n = 0; n < rowOptions.Length; n++)
{
if(rowOptions[n].ToUpper().StartsWith("W"))
tdwidth = " width=\"" + rowOptions[n].Substring(1, rowOptions[n].Length - 1) + "\"";
else if(rowOptions[n].ToUpper().StartsWith("H"))
tdheight = " height=\"" + rowOptions[n].Substring(1, rowOptions[n].Length - 1) + "\"";
else if(rowOptions[n].ToUpper().StartsWith("L"))
tdalign = " align=\"" + rowOptions[n].Substring(0, rowOptions[n].Length) + "\"";
else if(rowOptions[n].ToUpper().StartsWith("C"))
tdalign = " align=\"" + rowOptions[n].Substring(0, rowOptions[n].Length) + "\"";
else if(rowOptions[n].ToUpper().StartsWith("R"))
tdalign = " align=\"" + rowOptions[n].Substring(0, rowOptions[n].Length) + "\"";
}
result.Append("<td valign=\"top\" class=\"" + tdclass + "\"" + tdwidth + tdheight + tdalign + ">");
for(int y = 1; y < colItems.Length; y++)
{
string line = colItems[y];
this.FormatStandardWikiLine(appPath, wikiName, ref line, topic, hideLinks);
result.Append(line + "\n");
}
result.Append("</td>\n");
}
else
return "<div><br><b>@@@@@@@@@ TABLE FORMATTING ERROR - EACH COLUMN MUST HAVE A STYLE: @@, ||, or ##</b><br></div>";
}
//close up row
result.Append("</tr>\n");
}
result.Append("</table>\n");
return result.ToString();
}
}
private static string FormatCodeWikiText(string appPath, string wikiName, string topic, string wikitext)
{
//TODO - color code it for HTML and C# and Javascript
//Parser.HTMLEncodeSimple(ref wikitext);
return "\n<pre>" + wikitext + "</pre>\n"; //TODO - use style/class courier/courier new
}
private string FormatPlainWikiText(string appPath, string wikiName, string topic, string wikitext)
{
bool hideLinks = false;
return FormatPlainWikiText(appPath, wikiName, topic, wikitext, hideLinks);
}
private string FormatPlainWikiText(string appPath, string wikiName, string topic, string wikitext, bool hideLinks)
{
wikitext = wikitext.Replace("\r", "");
string [] Lines = wikitext.Split (new char[] {'\n'});
StringBuilder result = new StringBuilder(7);
foreach(string wikiline in Lines)
{
if(wikiline != "") //only parse lines with text or return character - this eliminates any trailing empty lines
{
string theLine = wikiline; //.Replace("\r", ""); //drop the return character
//HTMLEncodeSimple (ref theLine); //convert all & < > characters //TODO - consider allowing HTML
if (theLine.Trim() == "")
theLine = theLine = "<div> </div>\n"; //add div as a blank line //theLine.Trim(); //
else
{
this.FormatStandardWikiLine(appPath, wikiName, ref theLine, topic, hideLinks);
//last act - if it does not end with blockquote, surround with div tags
if (theLine.EndsWith("</blockquote>"))
theLine += "\n";
else
theLine = "<div>" + theLine + "</div>\n";
}
result.Append(theLine);
}
}
return result.ToString();
}
public void FormatStandardWikiLine(string appPath, string wikiName, ref string theLine, string topic, bool hideLinks)
{
//format line - if starts with ; - then apply no formatting
if(theLine.StartsWith(";"))
theLine = theLine.Substring(1, theLine.Length - 1); //just remove the comment char
else
{
if(!hideLinks)
FormatWikiLinks(appPath, wikiName, topic, ref theLine);
FormatHorizontalRule(ref theLine);
FormatBoldItalic(ref theLine);
FormatBoldItalicUnderlineWord(ref theLine);
FormatHeading(ref theLine); //now includes numbering
FormatIndent(ref theLine);
}
}
#region Preset Format Directives
//instance members for preset format directives
private bool fdDoNumbers = true;
private void ProcessPresetFormatDirectives(string preLine)
{
//preLine = "{{nonumbers}} or {{mydirective}{nonumbers}}
if(preLine.IndexOf("{nonumbers}") > 0)
{
this.fdDoNumbers = false;
}
}
#endregion
public ArrayList SegmentWikiText(string wikitext)
{
wikitext = wikitext.Replace("\r", "");
ArrayList segList = new ArrayList();
StringBuilder blocktext = new StringBuilder();
WikiTextBlockType blockType = WikiTextBlockType.Plain; //start with the assumption of plain text
string[] Lines = wikitext.Split(new char[] {'\n'});
//Process Preset Format Directives
if(Lines.Length > 0)
{
for( int i = 0; i < Lines.Length; i++)
{
string rline = Lines[i].Trim();
if(Regex.IsMatch(rline, @"^\{\{(.*?)\}\}$"))
{
ProcessPresetFormatDirectives(rline.ToLower());
Lines[i] = ""; //remove directives
}
}
}
//number header lines if more than 3 - loop to count, then loop to add numbers if greater than 3
if(this.fdDoNumbers)
{
int hdCount = 0;
for( int i = 0; i < Lines.Length; i++)
{
if(Regex.IsMatch(Lines[i], "^\\={2}(.*?)\\={2}$"))
hdCount++;
}
if(hdCount > Config.HeaderThreshold)
{
//do count
int h1 = 0;
int h2 = 0;
int h3 = 0;
int curLevel = 1;
for( int i = 0; i < Lines.Length; i++)
{
if(Regex.IsMatch(Lines[i], "^\\={4}(.*?)\\={4}$"))
{
h3++;
curLevel = 3;
Lines[i] = AddLineNumber(Lines[i], h1, h2, h3);
}
else if(Regex.IsMatch(Lines[i], "^\\={3}(.*?)\\={3}$"))
{
if(curLevel == 3) h3 = 0;
h2++;
curLevel = 2;
Lines[i] = AddLineNumber(Lines[i], h1, h2, h3);
}
else if(Regex.IsMatch(Lines[i], "^\\={2}(.*?)\\={2}$"))
{
if(curLevel == 2 || curLevel == 3)
{
h2 = 0;
h3 = 0;
}
h1++;
curLevel = 1;
Lines[i] = AddLineNumber(Lines[i], h1, h2, h3);
}
}
}
}
/*
++++(W360,H100%,B1)
@@
Here is a the first table data row. With column header style.
@@
Here is the second column with header style.
====
##
Here is the first column data
## (W60,H90)
Here is the second column data with width=60 and height=90
====
##
Here is the first column data
##
Here is the second column data
====
##
Here is the first column data
##
Here is the second column data
====
##
Here is a the first table data row. With column footer style.
##
Here is the second column with footer style.
++++
*/
// parse each line of text individually
foreach(string wline in Lines)
{
string wikiline = wline; //.Replace("\r", "");
if(wikiline.StartsWith("::code::"))
{
if (blockType == WikiTextBlockType.Plain || blockType == WikiTextBlockType.List || blockType == WikiTextBlockType.Table)
{
//begins a new list type block, so we must end the previous block if it has anything in it
if(blocktext.Length > 0)
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder();
}
blockType = WikiTextBlockType.Code; // toggle flag for code mode - starts the preformatted block
}
else
{
//this is the end of the code block, so add it to the return value
if(blocktext.Length > 0)
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder(3); //reset to new string builder object
}
blockType = WikiTextBlockType.Plain; //toggle back to plain
}
}
else if(wikiline.StartsWith("++++")) //table block
{
if (blockType == WikiTextBlockType.Plain || blockType == WikiTextBlockType.List || blockType == WikiTextBlockType.Code)
{
//begins a new list type block, so we must end the previous block if it has anything in it
if(blocktext.Length > 0)
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder();
}
blockType = WikiTextBlockType.Table; // toggle flag for table - starts the table block
//include first line in block
wikiline += "\n";
blocktext.Append(wikiline); //add current line to existing block
}
else
{
//this is the end of the code block, so add it to the return value
if(blocktext.Length > 0)
{
//include last line in block
wikiline += "\n";
blocktext.Append(wikiline); //add current line to existing block
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder(3); //reset to new string builder object
}
blockType = WikiTextBlockType.Plain; //toggle back to plain
}
}
else
{
//not a ::code:: toggle line, so check for block switch and append the line to the current block string builder
//match list line format:
//line begins with two or more spaces and then a * or # and then a space
Match m = Regex.Match(wikiline, @"(^\s[\s]+)(\#|\*)(\s)", RegexOptions.Compiled);
bool isListLine = m.Length > 0; //handle lists or plain block
if(blockType == WikiTextBlockType.List)
{
if(!isListLine)
{
if(blocktext.Length > 0)
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder(3); //reset to new string builder object
}
blockType = WikiTextBlockType.Plain;; //now we have just ended our list
}
}
else
{
if(isListLine)
{
//this is the first list line, so close the current block and switch to list mode
if(blocktext.Length > 0)
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
blocktext = new StringBuilder(3); //reset to new string builder object
}
blockType = WikiTextBlockType.List; //this is the first line in a list - start list and add to listQ and indentLevel, etc.
}
}
wikiline += "\n";
blocktext.Append(wikiline); //add current line to existing block
}
}
if(blocktext.Length > 0) //handle last block
{
WikiTextBlock block = new WikiTextBlock(blocktext.ToString(), blockType);
segList.Add(block);
}
return segList;
}
private static string AddLineNumber(string line, int h1, int h2, int h3)
{
//0.0.0 = 0
//1.0.0 = 1
//1.1.0 = 1.1
//0.2.2 = 0.2.2
string num = h1.ToString();
if(h2 > 0 || (h2 == 0 && h3 > 0)) num += "." + h2.ToString();
if(h3 > 0) num += "." + h3.ToString();
num += " "; //add trailing space
if(line.StartsWith("===="))
line = line.Insert(4, num);
else if(line.StartsWith("==="))
line = line.Insert(3, num);
else if(line.StartsWith("=="))
line = line.Insert(2, num);
return line;
}
private static void FormatHorizontalRule(ref string line)
{
line = Regex.Replace(line, "^-{4,}", "<hr>", RegexOptions.Compiled);
}
private static void FormatBoldItalic(ref string line)
{
// '''text''' = bold, ''text'' = italic
line = Regex.Replace(line, "'{3}(.*?)'{3}", "<strong>$1</strong>", RegexOptions.Compiled);
line = Regex.Replace(line, "'{2}(.*?)'{2}", "<em>$1</em>", RegexOptions.Compiled);
}
private static void FormatBoldItalicUnderlineWord(ref string line)
{
// bold = *word*
line = Regex.Replace(line, "(^|\\s)\\*(\\S*)\\*(\\s|$|\\.|,|:|;|\\?|\\!)", "$1<strong>$2</strong>$3", RegexOptions.Compiled);
// italic = /word/
line = Regex.Replace(line, "(^|\\s)/(\\S*)/(\\s|$|\\.|,|:|;|\\?|\\!)", "$1<em>$2</em>$3", RegexOptions.Compiled);
// underline = _word_
line = Regex.Replace(line, "(^|\\s)_(\\S*)_(\\s|$|\\.|,|:|;|\\?|\\!)", "$1<u>$2</u>$3", RegexOptions.Compiled);
}
private static void FormatHeading(ref string line)
{
// Format <H1>, <H2> amd <H3> headings - word or phrase surrounded by ==, ===, or ====
// Heading 3
line = Regex.Replace(line, "^\\={4}(.*?)\\={4}$", "<div class=\"hd3\">$1</div>", RegexOptions.Compiled);
// Heading 2
line = Regex.Replace(line, "^\\={3}(.*?)\\={3}$", "<div class=\"hd2\">$1</div>", RegexOptions.Compiled);
// Heading 1
line = Regex.Replace(line, "^\\={2}(.*?)\\={2}$", "<div class=\"hd1\">$1</div>", RegexOptions.Compiled);
}
private static void FormatIndent(ref string theLine)
{
//surround with div class=ind1, ind2, or ind3
if (theLine.StartsWith(": "))
theLine = "<div class=\"ind1\">" + theLine.Substring(2, theLine.Length - 2) + "</div>";
else if (theLine.StartsWith(":: "))
theLine = "<div class=\"ind2\">" + theLine.Substring(3, theLine.Length - 3) + "</div>";
else if (theLine.StartsWith("::: "))
theLine = "<div class=\"ind3\">" + theLine.Substring(4, theLine.Length - 4) + "</div>";
}
public string ConvertWikiLinksToHTML(string appPath, string wikiName, string wikiTopic, string wikiText)
{
FormatWikiLinks(appPath, wikiName, wikiTopic, ref wikiText);
return wikiText;
}
private static void FormatWikiLinks(string appPath, string wikiName, string topic, ref string line)
{
// format new links style with images, files, topics, http, ftp, mail
MatchCollection wikiTopics = Parser.LinkRx.Matches(line);
foreach (Match m in wikiTopics)
{
string mtxt = m.ToString();
WikiTopicLink link = new WikiTopicLink(wikiName, mtxt);
string preImg = "";
if(link.IsParent)
preImg = "<img src=\"" + appPath + "/images/parent.gif\" border=\"0\" alt=\"This is a parent link.\" align=\"absmiddle\">";
else if(link.IsChild)
preImg = "<img src=\"" + appPath + "/images/child.gif\" border=\"0\" alt=\"This is a child link.\" align=\"absmiddle\">";
string repLink = "";
switch(link.Protocol)
{
case "file":
repLink = "<a href=\"" + appPath + "/getfile.aspx?w=" + HttpUtility.HtmlEncode(link.WikiName)
+ "&f=" + HttpUtility.HtmlEncode(link.Topic) + "\" class=\"bodylink\">" + HttpUtility.HtmlEncode(link.LinkText) + "</a>";
break;
case "image":
repLink = BuildImageHtml(appPath, link);
break;
case "imagelink":
repLink = BuildImageLink(preImg, appPath, link);
break;
case "http":
case "https":
case "ftp":
case "mailto":
repLink = "<a href=\"" + HttpUtility.HtmlEncode(link.Topic) + "\" target=\"_blank\" class=\"bodylink\">"
+ HttpUtility.HtmlEncode(link.LinkText) + "</a>";
break;
case "":
case "talk":
default:
if(link.Topic.ToLower() == topic.ToLower() && link.WikiName.ToLower() == wikiName.ToLower())
repLink = "<b>" + link.LinkText + "</b>";
else
{
WikiKey key = new WikiKey(link.WikiName, link.Topic);
string lkclass = "nolink";
if(TopicManager.TopicIndex[key] != null)
lkclass = "bodylink";
string talkVal = "";
if(link.Protocol == "talk") talkVal = "?talk";
repLink = "<a href=\"" + appPath + "/wiki/" + link.WikiName + "/" + link.Topic + ".wiki" + talkVal
+ "\" class=\"" + lkclass + "\">" + preImg + link.LinkText + "</a>";
}
break;
}
line = line.Replace(mtxt, repLink);
}
}
private static string BuildImageHtml(string appPath, WikiTopicLink link)
{
//all options come into account here
//link.File
//link.FileWiki
//link.Align
//link.Caption
//link.Frame ----
//link.LinkText
//link.IsChild
//link.IsParent
//link.Thumb -----
//link.Width
//link.WikiName
//<div style="float:right;"><img src="images/logo.gif" border="0"></div>
string divclass = "imgmid";
if(link.Align == "right") divclass = "imgright";
if(link.Align == "left") divclass = "imgleft";
if(link.Frame) divclass += "fr"; //imgmidfr, imgrightfr, imgleftfr
string divtag = "<div class=\"" + divclass + "\">";
string width = "";
if(link.Thumb) width = " width=\"100px\"";
if(link.Width.Length > 0) width = " width=\"" + link.Width + "\"";
string repLink = "<img src=\"" + appPath + "/getimage.aspx?w=" + HttpUtility.HtmlEncode(link.WikiName)
+ "&f=" + HttpUtility.HtmlEncode(link.Topic) + "\" border=\"0\"" + width + ">";
if(link.Thumb)
repLink = "<a href=\"" + appPath + "/getimage.aspx?w=" + HttpUtility.HtmlEncode(link.WikiName)
+ "&f=" + HttpUtility.HtmlEncode(link.File) + "\" target=\"_blank\" border=\"0\">" + repLink + "</a>";
if(link.Frame && link.Caption.Length > 0)
repLink += "<br><div class=\"caption\">" + link.Caption + "</div>";
repLink = divtag + repLink + "</div>";
return repLink;
}
private static string BuildImageLink(string preImg, string appPath, WikiTopicLink link)
{
//all options come into account here
string divclass = "imgmid";
if(link.Align == "right") divclass = "imgright";
if(link.Align == "left") divclass = "imgleft";
if(link.Frame) divclass += "fr"; //imgmidfr, imgrightfr, imgleftfr
string divtag = "<div class=\"" + divclass + "\">";
string width = "";
if(link.Thumb) width = " width=\"100px\"";
if(link.Width.Length > 0) width = " width=\"" + link.Width + "\"";
string repLink = "<img src=\"" + appPath + "/getimage.aspx?w=" + HttpUtility.HtmlEncode(link.FileWiki)
+ "&f=" + HttpUtility.HtmlEncode(link.File) + "\" border=\"0\"" + width + ">";
WikiKey key = new WikiKey(link.WikiName, link.Topic);
string lkclass = "nolink";
if(TopicManager.TopicIndex[key] != null)
lkclass = "bodylink";
//make link to topic - check for outside link - target=_blank
if( link.IsOutsideLink )
repLink = "<a href=\"" + link.Topic + "\" class=\"bodylink\" target=\"_blank\">" + repLink;
else
repLink = "<a href=\"" + appPath + "/wiki/" + link.WikiName + "/" + link.Topic + ".wiki\" class=\"" + lkclass + "\">" + repLink;
if(link.Frame)
repLink += "<br><div class=\"caption\">" + preImg + link.LinkText + "</div>";
repLink += "</a>"; //finish link to include caption if included
repLink = divtag + repLink + "</div>";
return repLink;
}
}
}