Merge branch 'master' of github.com:ONLYOFFICE/CommunityServer-AspNetCore

This commit is contained in:
Alexey Safronov 2019-08-19 16:20:05 +03:00
commit 6b998cd1f1
62 changed files with 3012 additions and 40 deletions

View File

@ -38,7 +38,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Data.Reassigns", "commo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Notify", "common\services\ASC.Notify\ASC.Notify.csproj", "{8484A675-1C93-4D87-8FF2-7530A5711208}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.Studio.Notify", "common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj", "{E3567AB9-0926-444D-A0D0-A369D5890EAA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Studio.Notify", "common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj", "{E3567AB9-0926-444D-A0D0-A369D5890EAA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.Textile", "common\ASC.Textile\ASC.Textile.csproj", "{C8F410B4-B83B-47B9-9ECD-07590A8750A7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -114,6 +116,10 @@ Global
{E3567AB9-0926-444D-A0D0-A369D5890EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3567AB9-0926-444D-A0D0-A369D5890EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3567AB9-0926-444D-A0D0-A369D5890EAA}.Release|Any CPU.Build.0 = Release|Any CPU
{C8F410B4-B83B-47B9-9ECD-07590A8750A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8F410B4-B83B-47B9-9ECD-07590A8750A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8F410B4-B83B-47B9-9ECD-07590A8750A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8F410B4-B83B-47B9-9ECD-07590A8750A7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current">
<PropertyGroup>
<ProductVersion>9.0.30729</ProductVersion>
<TargetFramework>netstandard2.1</TargetFramework>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<AssemblyTitle>ASC.Textile</AssemblyTitle>
<Company>Ascensio System SIA</Company>
<Product>ASC.Textile</Product>
<Copyright>(c) Ascensio System SIA. All rights reserved</Copyright>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
namespace Textile
{
public class BlockModifier
{
protected BlockModifier()
{
}
public virtual string ModifyLine(string line)
{
return line;
}
public virtual string Conclude(string line)
{
return line;
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Textile
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class BlockModifierAttribute : Attribute
{
public BlockModifierAttribute()
{
}
}
}

View File

@ -0,0 +1,157 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class BlockAttributesParser
{
public static StyleReader Styler { get; set; }
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
static public string ParseBlockAttributes(string input)
{
return ParseBlockAttributes(input, "");
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <param name="element"></param>
/// <returns></returns>
static public string ParseBlockAttributes(string input, string element)
{
var style = string.Empty;
var cssClass = string.Empty;
var lang = string.Empty;
var colspan = string.Empty;
var rowspan = string.Empty;
var id = string.Empty;
var atts = string.Empty;
if (Styler != null)
{
style = GetStyle(element, style);
}
if (input.Length == 0)
return (style.Length > 0 ? " style=\"" + style + "\"" : "");
Match m;
var matched = input;
if (element == "td")
{
// column span
m = Regex.Match(matched, @"\\(\d+)");
if (m.Success)
colspan = m.Groups[1].Value;
// row span
m = Regex.Match(matched, @"/(\d+)");
if (m.Success)
rowspan = m.Groups[1].Value;
// vertical align
m = Regex.Match(matched, @"(" + Globals.VerticalAlignPattern + @")");
if (m.Success)
style += "vertical-align:" + Globals.VerticalAlign[m.Captures[0].Value] + ";";
}
// First, match custom styles
m = Regex.Match(matched, @"\{([^}]*)\}");
if (m.Success)
{
style += m.Groups[1].Value + ";";
matched = matched.Replace(m.ToString(), "");
}
// Then match the language
m = Regex.Match(matched, @"\[([^()]+)\]");
if (m.Success)
{
lang = m.Groups[1].Value;
matched = matched.Replace(m.ToString(), "");
}
// Match classes and IDs after that
m = Regex.Match(matched, @"\(([^()]+)\)");
if (m.Success)
{
cssClass = m.Groups[1].Value;
matched = matched.Replace(m.ToString(), "");
// Separate the public class and the ID
m = Regex.Match(cssClass, @"^(.*)#(.*)$");
if (m.Success)
{
cssClass = m.Groups[1].Value;
id = m.Groups[2].Value;
}
if (Styler != null && !string.IsNullOrEmpty(cssClass))
{
style = GetStyle("." + cssClass, style);
}
}
// Get the padding on the left
m = Regex.Match(matched, @"([(]+)");
if (m.Success)
{
style += "padding-left:" + m.Groups[1].Length + "em;";
matched = matched.Replace(m.ToString(), "");
}
// Get the padding on the right
m = Regex.Match(matched, @"([)]+)");
if (m.Success)
{
style += "padding-right:" + m.Groups[1].Length + "em;";
matched = matched.Replace(m.ToString(), "");
}
// Get the text alignment
m = Regex.Match(matched, "(" + Globals.HorizontalAlignPattern + ")");
if (m.Success)
style += "text-align:" + Globals.HorizontalAlign[m.Groups[1].Value] + ";";
return (
(style.Length > 0 ? " style=\"" + style + "\"" : "") +
(cssClass.Length > 0 ? " class=\"" + cssClass + "\"" : "") +
(lang.Length > 0 ? " lang=\"" + lang + "\"" : "") +
(id.Length > 0 ? " id=\"" + id + "\"" : "") +
(colspan.Length > 0 ? " colspan=\"" + colspan + "\"" : "") +
(rowspan.Length > 0 ? " rowspan=\"" + rowspan + "\"" : "")
);
}
private static string GetStyle(string element, string style)
{
var styled = Styler.GetStyle(element);
if (!string.IsNullOrEmpty(styled))
{
style += styled;
}
return style;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class BoldPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\*\*", "b");
}
}
}

View File

@ -0,0 +1,19 @@
using System.Text.RegularExpressions;
namespace Textile.Blocks
{
public class CapitalsBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
var me = new MatchEvaluator(CapitalsFormatMatchEvaluator);
line = Regex.Replace(line, @"(?<=^|\s|" + Globals.PunctuationPattern + @")(?<caps>[A-Z][A-Z0-9]+)(?=$|\s|" + Globals.PunctuationPattern + @")", me);
return line;
}
static private string CapitalsFormatMatchEvaluator(Match m)
{
return @"<span class=""caps"">" + m.Groups["caps"].Value + @"</span>";
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class CitePhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\?\?", "cite");
}
}
}

View File

@ -0,0 +1,61 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class CodeBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
// Replace "@...@" zones with "<code>" tags.
var me = new MatchEvaluator(CodeFormatMatchEvaluator);
line = Regex.Replace(line,
@"(?<before>^|([\s\([{]))" + // before
"@" +
@"(\|(?<lang>\w+)\|)?" + // lang
"(?<code>[^@]+)" + // code
"@" +
@"(?<after>$|([\]}])|(?=" + Globals.PunctuationPattern + @"{1,2}|\s|$))", // after
me);
// Encode the contents of the "<code>" tags so that we don't
// generate formatting out of it.
line = NoTextileEncoder.EncodeNoTextileZones(line,
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
@"(?=</code>)");
return line;
}
public override string Conclude(string line)
{
// Recode everything except "<" and ">";
line = NoTextileEncoder.DecodeNoTextileZones(line,
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
@"(?=</code>)",
new string[] { "<", ">" });
return line;
}
static public string CodeFormatMatchEvaluator(Match m)
{
var res = m.Groups["before"].Value + "<code";
if (m.Groups["lang"].Length > 0)
res += " language=\"" + m.Groups["lang"].Value + "\"";
res += ">" + m.Groups["code"].Value + "</code>" + m.Groups["after"].Value;
return res;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class DeletedPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\-", "del");
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class EmphasisPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"_", "em");
}
}
}

View File

@ -0,0 +1,27 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class FootNoteReferenceBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
return Regex.Replace(line, @"\b\[([0-9]+)\](\W)", "<sup><a href=\"#fn$1\">$1</a></sup>$2");
}
}
}

View File

@ -0,0 +1,92 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class GlyphBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
line = Regex.Replace(line, "\"\\z", "\" ");
// fix: hackish
string[,] glyphs = {
{ @"([^\s[{(>_*])?\'(?(1)|(\s|s\b|" + Globals.PunctuationPattern + @"))", "$1&#8217;$2" }, // single closing
{ @"\'", "&#8216;" }, // single opening
{ @"([^\s[{(>_*])?""(?(1)|(\s|" + Globals.PunctuationPattern + @"))", "$1&#8221;$2" }, // double closing
{ @"""", "&#8220;" }, // double opening
{ @"\b( )?\.{3}", "$1&#8230;" }, // ellipsis
{ @"\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])", "<acronym title=\"$2\">$1</acronym>" }, // 3+ uppercase acronym
{ @"(\s)?--(\s)?", "$1&#8212;$2" }, // em dash
{ @"\s-\s", " &#8211; " }, // en dash
{ @"(\d+)( )?x( )?(\d+)", "$1$2&#215;$3$4" }, // dimension sign
{ @"\b ?[([](TM|tm)[])]", "&#8482;" }, // trademark
{ @"\b ?[([](R|r)[])]", "&#174;" }, // registered
{ @"\b ?[([](C|c)[])]", "&#169;" } // copyright
};
var output = "";
if (!Regex.IsMatch(line, "<.*>"))
{
// If no HTML, do a simple search & replace.
for (var i = 0; i < glyphs.GetLength(0); ++i)
{
line = Regex.Replace(line, glyphs[i, 0], glyphs[i, 1]);
}
output = line;
}
else
{
var splits = Regex.Split(line, "(<.*?>)");
var offtags = "code|pre|notextile";
var codepre = false;
foreach (var split in splits)
{
var modifiedSplit = split;
if (modifiedSplit.Length == 0)
continue;
if (Regex.IsMatch(modifiedSplit, @"<(" + offtags + ")>"))
codepre = true;
if (Regex.IsMatch(modifiedSplit, @"<\/(" + offtags + ")>"))
codepre = false;
if (!Regex.IsMatch(modifiedSplit, "<.*>") && !codepre)
{
for (var i = 0; i < glyphs.GetLength(0); ++i)
{
modifiedSplit = Regex.Replace(modifiedSplit, glyphs[i, 0], glyphs[i, 1]);
}
}
// do htmlspecial if between <code>
if (codepre == true)
{
//TODO: htmlspecialchars(line)
//line = Regex.Replace(line, @"&lt;(\/?" + offtags + ")&gt;", "<$1>");
//line = line.Replace("&amp;#", "&#");
}
output += modifiedSplit;
}
}
return output;
}
}
}

View File

@ -0,0 +1,62 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class HyperLinkBlockModifier : BlockModifier
{
private readonly string m_rel = string.Empty;
public override string ModifyLine(string line)
{
line = Regex.Replace(line,
@"(?<pre>[\s[{(]|" + Globals.PunctuationPattern + @")?" + // $pre
"\"" + // start
Globals.BlockModifiersPattern + // attributes
"(?<text>[\\w\\W]+?)" + // text
@"\s?" +
@"(?:\((?<title>[^)]+)\)(?=""))?" + // title
"\":" +
string.Format(@"""(?<url>\S+[^""]+)""",Regex.Escape(@"a-zA-Z:/.-{}?&_%#+=@")) + // url
@"(?<slash>\/)?" + // slash
@"(?<post>[^\w\/;]*)" + // post
@"(?=\s|$)",
new MatchEvaluator(HyperLinksFormatMatchEvaluator));
return line;
}
private string HyperLinksFormatMatchEvaluator(Match m)
{
//TODO: check the URL
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "a");
if (m.Groups["title"].Length > 0)
atts += " title=\"" + m.Groups["title"].Value + "\"";
var linkText = m.Groups["text"].Value.Trim(' ');
var str = m.Groups["pre"].Value + "<a ";
if (!string.IsNullOrEmpty(m_rel))
str += "ref=\"" + m_rel + "\" ";
str += "href=\"" +
m.Groups["url"].Value + m.Groups["slash"].Value + "\"" +
atts +
">" + linkText + "</a>" + m.Groups["post"].Value;
return str;
}
}
}

View File

@ -0,0 +1,73 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public class ImageBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
line = Regex.Replace(line,
@"\!" + // opening !
@"(?<algn>\<|\=|\>)?" + // optional alignment atts
Globals.BlockModifiersPattern + // optional style, public class atts
@"(?:\. )?" + // optional dot-space
@"(?<url>[^\s(!]+)" + // presume this is the src
@"\s?" + // optional space
@"(?:\((?<title>([^\)]+))\))?" +// optional title
@"\!" + // closing
@"(?::(?<href>(\S+)))?" + // optional href
@"(?=\s|\.|,|;|\)|\||$)", // lookahead: space or simple punctuation or end of string
new MatchEvaluator(ImageFormatMatchEvaluator)
);
return line;
}
static string ImageFormatMatchEvaluator(Match m)
{
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "img");
if (m.Groups["algn"].Length > 0)
atts += " align=\"" + Globals.ImageAlign[m.Groups["algn"].Value] + "\"";
if (m.Groups["title"].Length > 0)
{
atts += " title=\"" + m.Groups["title"].Value + "\"";
atts += " alt=\"" + m.Groups["title"].Value + "\"";
}
else
{
atts += " alt=\"\"";
}
// Get Image Size?
var res = "<img src=\"" + m.Groups["url"].Value + "\"" + atts + " />";
if (m.Groups["href"].Length > 0)
{
var href = m.Groups["href"].Value;
var end = string.Empty;
var endMatch = Regex.Match(href, @"(.*)(?<end>\.|,|;|\))$");
if (m.Success && !string.IsNullOrEmpty(endMatch.Groups["end"].Value))
{
href = href[0..^1];
end = endMatch.Groups["end"].Value;
}
res = "<a href=\"" + Globals.EncodeHTMLLink(href) + "\">" + res + "</a>" + end;
}
return res;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class InsertedPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\+", "ins");
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class ItalicPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"__", "i");
}
}
}

View File

@ -0,0 +1,35 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
#endregion
namespace Textile.Blocks
{
public class NoTextileBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
line = NoTextileEncoder.EncodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
line = NoTextileEncoder.EncodeNoTextileZones(line, @"==", @"==");
return line;
}
public override string Conclude(string line)
{
line = NoTextileEncoder.DecodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
line = NoTextileEncoder.DecodeNoTextileZones(line, @"==", @"==");
return line;
}
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.Text.RegularExpressions;
namespace Textile.Blocks
{
public static class NoTextileEncoder
{
private static readonly string[,] TextileModifiers = {
{ "\"", "&#34;" },
{ "%", "&#37;" },
{ "*", "&#42;" },
{ "+", "&#43;" },
{ "-", "&#45;" },
{ "<", "&lt;" }, // or "&#60;"
{ "=", "&#61;" },
{ ">", "&gt;" }, // or "&#62;"
{ "?", "&#63;" },
{ "^", "&#94;" },
{ "_", "&#95;" },
{ "~", "&#126;" },
{ "@", "&#64;" },
{ "'", "&#39;" },
{ "|", "&#124;" },
{ "!", "&#33;" },
{ "(", "&#40;" },
{ ")", "&#41;" },
{ ".", "&#46;" },
{ "x", "&#120;" }
};
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
{
return EncodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
}
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
{
string evaluator(Match m)
{
var toEncode = m.Groups["notex"].Value;
if (toEncode == string.Empty)
{
return string.Empty;
}
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
{
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
{
toEncode = toEncode.Replace(TextileModifiers[i, 0], TextileModifiers[i, 1]);
}
}
return patternPrefix + toEncode + patternSuffix;
}
tmp = Regex.Replace(tmp, string.Format("({0}(?<notex>.+?){1})*", patternPrefix, patternSuffix), new MatchEvaluator(evaluator));
return tmp;
}
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
{
return DecodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
}
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
{
string evaluator(Match m)
{
var toEncode = m.Groups["notex"].Value;
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
{
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
{
toEncode = toEncode.Replace(TextileModifiers[i, 1], TextileModifiers[i, 0]);
}
}
return toEncode;
}
tmp = Regex.Replace(tmp, string.Format("({0}(?<notex>.+?){1})*", patternPrefix, patternSuffix), new MatchEvaluator(evaluator));
return tmp;
}
}
}

View File

@ -0,0 +1,96 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.Blocks
{
public abstract class PhraseBlockModifier : BlockModifier
{
protected PhraseBlockModifier()
{
}
protected string PhraseModifierFormat(string input, string modifier, string tag)
{
// All phrase modifiers are one character, or a double character. Sometimes,
// there's an additional escape character for the regex ('\').
var compressedModifier = modifier;
if (modifier.Length == 4)
{
compressedModifier = modifier.Substring(0, 2);
}
else if (modifier.Length == 2)
{
if (modifier[0] != '\\')
compressedModifier = modifier.Substring(0, 1);
//else: compressedModifier = modifier;
}
//else: compressedModifier = modifier;
// We try to remove the Textile tag used for the formatting from
// the punctuation pattern, so that we match the end of the formatted
// zone correctly.
var punctuationPattern = Globals.PunctuationPattern.Replace(compressedModifier, "");
// Now we can do the replacement.
var pmme = new PhraseModifierMatchEvaluator(tag);
var res = Regex.Replace(input,
@"(?<=\s|" + punctuationPattern + @"|[{\(\[]|^)" +
modifier +
Globals.BlockModifiersPattern +
@"(:(?<cite>(\S+)))?" +
@"(?<content>[^" + compressedModifier + "]*)" +
@"(?<end>" + punctuationPattern + @"*)" +
modifier +
@"(?=[\]\)}]|" + punctuationPattern + @"+|\s|$)",
new MatchEvaluator(pmme.MatchEvaluator)
);
return res;
}
private class PhraseModifierMatchEvaluator
{
readonly string m_tag;
public PhraseModifierMatchEvaluator(string tag)
{
m_tag = tag;
}
public string MatchEvaluator(Match m)
{
if (m.Groups["content"].Length == 0)
{
// It's possible that the "atts" match groups eats the contents
// when the user didn't want to give block attributes, but the content
// happens to match the syntax. For example: "*(blah)*".
if (m.Groups["atts"].Length == 0)
return m.ToString();
return "<" + m_tag + ">" + m.Groups["atts"].Value + m.Groups["end"].Value + "</" + m_tag + ">";
}
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, m_tag);
if (m.Groups["cite"].Length > 0)
atts += " cite=\"" + m.Groups["cite"] + "\"";
var res = "<" + m_tag + atts + ">" +
m.Groups["content"].Value + m.Groups["end"].Value +
"</" + m_tag + ">";
return res;
}
}
}
}

View File

@ -0,0 +1,41 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
#endregion
namespace Textile.Blocks
{
public class PreBlockModifier : BlockModifier
{
public override string ModifyLine(string line)
{
// Encode the contents of the "<pre>" tags so that we don't
// generate formatting out of it.
line = NoTextileEncoder.EncodeNoTextileZones(line,
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
@"(?=</pre>)");
return line;
}
public override string Conclude(string line)
{
// Recode everything.
line = NoTextileEncoder.DecodeNoTextileZones(line,
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
@"(?=</pre>)",
new string[] { "<", ">" });
return line;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class SpanPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"%", "span");
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class StrongPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\*", "strong");
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class SubScriptPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"~", "sub");
}
}
}

View File

@ -0,0 +1,10 @@
namespace Textile.Blocks
{
public class SuperScriptPhraseBlockModifier : PhraseBlockModifier
{
public override string ModifyLine(string line)
{
return PhraseModifierFormat(line, @"\^", "sup");
}
}
}

View File

@ -0,0 +1,141 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System;
using System.Text.RegularExpressions;
#endregion
namespace Textile
{
/// <summary>
/// Base class for formatter states.
/// </summary>
/// A formatter state describes the current situation
/// of the text being currently processed. A state can
/// write HTML code when entered, exited, and can modify
/// each line of text it receives.
public abstract class FormatterState
{
/// <summary>
/// The formatter this state belongs to.
/// </summary>
public TextileFormatter Formatter { get; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="f">The parent formatter.</param>
public FormatterState(TextileFormatter formatter)
{
Formatter = formatter;
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <param name="m"></param>
/// <returns></returns>
public abstract string Consume(string input, Match m);
/// <summary>
/// Method called when the state is entered.
/// </summary>
public abstract void Enter();
/// <summary>
/// Method called when the state is exited.
/// </summary>
public abstract void Exit();
/// <summary>
/// Method called when a line of text should be written
/// to the web form.
/// </summary>
/// <param name="input">The line of text.</param>
public abstract void FormatLine(string input);
/// <summary>
/// Returns whether this state can last for more than one line.
/// </summary>
/// <returns>A boolean value stating whether this state is only for one line.</returns>
/// This method should return true only if this state is genuinely
/// multi-line. For example, a header text is only one line long. You can
/// have several consecutive lines of header texts, but they are not the same
/// header - just several headers one after the other.
/// Bulleted and numbered lists are good examples of multi-line states.
//abstract public bool IsOneLineOnly();
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public abstract bool ShouldExit(string input);
/// <summary>
///
/// </summary>
/// <param name="actualTag"></param>
/// <param name="alignNfo"></param>
/// <param name="attNfo"></param>
/// <returns></returns>
public virtual bool ShouldNestState(FormatterState other)
{
return false;
}
/// <summary>
/// Returns whether block formatting (quick phrase modifiers, etc.) should be
/// applied to this line.
/// </summary>
/// <param name="input">The line of text</param>
/// <returns>Whether the line should be formatted for blocks</returns>
public virtual bool ShouldFormatBlocks(string input)
{
return true;
}
/// <summary>
/// Returns whether the current state accepts being superceded by another one
/// we would possibly find by parsing the input line of text.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public virtual bool ShouldParseForNewFormatterState(string input)
{
return true;
}
/// <summary>
/// Gets the formatting state we should fallback to if we don't find anything
/// relevant in a line of text.
/// </summary>
public virtual Type FallbackFormattingState
{
get
{
return typeof(States.ParagraphFormatterState);
}
}
protected FormatterState CurrentFormatterState
{
get { return this.Formatter.CurrentState; }
}
protected void ChangeFormatterState(FormatterState formatterState)
{
this.Formatter.ChangeState(formatterState);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
namespace Textile
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class FormatterStateAttribute : Attribute
{
public string Pattern { get; }
public FormatterStateAttribute(string pattern)
{
Pattern = pattern;
}
public static FormatterStateAttribute Get(Type type)
{
var atts = type.GetCustomAttributes(typeof(FormatterStateAttribute), false);
if (atts.Length == 0)
return null;
return (FormatterStateAttribute)atts[0];
}
}
}

View File

@ -0,0 +1,107 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Collections.Generic;
#endregion
namespace Textile
{
/// <summary>
/// A utility class for global things used by the TextileFormatter.
/// </summary>
class Globals
{
#region Global Regex Patterns
public const string HorizontalAlignPattern = @"(?:[()]*(\<(?!>)|(?<!<)\>|\<\>|=)[()]*)";
public const string VerticalAlignPattern = @"[\-^~]";
public const string CssClassPattern = @"(?:\([^)]+\))";
public const string LanguagePattern = @"(?:\[[^]]+\])";
public const string CssStylePattern = @"(?:\{[^}]+\})";
public const string ColumnSpanPattern = @"(?:\\\d+)";
public const string RowSpanPattern = @"(?:/\d+)";
public const string AlignPattern = "(?<align>" + HorizontalAlignPattern + "?" + VerticalAlignPattern + "?|" + VerticalAlignPattern + "?" + HorizontalAlignPattern + "?)";
public const string SpanPattern = @"(?<span>" + ColumnSpanPattern + "?" + RowSpanPattern + "?|" + RowSpanPattern + "?" + ColumnSpanPattern + "?)";
public const string BlockModifiersPattern = @"(?<atts>" + CssClassPattern + "?" + CssStylePattern + "?" + LanguagePattern + "?|" +
CssStylePattern + "?" + LanguagePattern + "?" + CssClassPattern + "?|" +
LanguagePattern + "?" + CssStylePattern + "?" + CssClassPattern + "?)";
public const string PunctuationPattern = @"[\!""#\$%&'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{}~]";
public const string HtmlAttributesPattern = @"(\s+\w+=((""[^""]+"")|('[^']+')))*";
#endregion
private static Dictionary<string, string> m_imageAlign;
/// <summary>
/// Image alignment tags, mapped to their HTML meanings.
/// </summary>
public static Dictionary<string, string> ImageAlign
{
get { return Globals.m_imageAlign; }
set { Globals.m_imageAlign = value; }
}
private static Dictionary<string, string> m_horizontalAlign;
/// <summary>
/// Horizontal text alignment tags, mapped to their HTML meanings.
/// </summary>
public static Dictionary<string, string> HorizontalAlign
{
get { return Globals.m_horizontalAlign; }
set { Globals.m_horizontalAlign = value; }
}
private static Dictionary<string, string> m_verticalAlign;
/// <summary>
/// Vertical text alignment tags, mapped to their HTML meanings.
/// </summary>
public static Dictionary<string, string> VerticalAlign
{
get { return Globals.m_verticalAlign; }
set { Globals.m_verticalAlign = value; }
}
static Globals()
{
m_imageAlign = new Dictionary<string, string>
{
["<"] = "left",
["="] = "center",
[">"] = "right"
};
m_horizontalAlign = new Dictionary<string, string>
{
["<"] = "left",
["="] = "center",
[">"] = "right",
["<>"] = "justify"
};
m_verticalAlign = new Dictionary<string, string>
{
["^"] = "top",
["-"] = "middle",
["~"] = "bottom"
};
}
public static string EncodeHTMLLink(string url)
{
url = url.Replace("&amp;", "&#38;");
url = System.Text.RegularExpressions.Regex.Replace(url, "&(?=[^#])", "&#38;");
return url;
}
}
}

View File

@ -0,0 +1,53 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
#endregion
namespace Textile
{
/// <summary>
/// Interface through which the HTML formatted text
/// will be sent.
/// </summary>
/// Clients of the TextileFormatter class will have to provide
/// an outputter that implements this interface. Most of the
/// time, it'll be the WebForm itself.
public interface IOutputter
{
/// <summary>
/// Method called just before the formatted text
/// is sent to the outputter.
/// </summary>
void Begin();
/// <summary>
/// Metohd called whenever the TextileFormatter wants to
/// print some text.
/// </summary>
/// <param name="text">The formatted HTML text.</param>
void Write(string text);
/// <summary>
/// Metohd called whenever the TextileFormatter wants to
/// print some text. This should automatically print an
/// additionnal end of line character.
/// </summary>
/// <param name="line">The formatted HTML text.</param>
void WriteLine(string line);
/// <summary>
/// Method called at the end of the formatting.
/// </summary>
void End();
}
}

View File

@ -0,0 +1,57 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System;
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bq" + SimpleBlockFormatterState.PatternEnd)]
public class BlockQuoteFormatterState : SimpleBlockFormatterState
{
public BlockQuoteFormatterState(TextileFormatter f)
: base(f)
{
}
public override void Enter()
{
Formatter.Output.Write("<blockquote" + FormattedStylesAndAlignment("blockquote") + "><p>");
}
public override void Exit()
{
Formatter.Output.WriteLine("</p></blockquote>");
}
public override void FormatLine(string input)
{
Formatter.Output.Write(input);
}
public override bool ShouldExit(string input)
{
if (Regex.IsMatch(input, @"^\s*$"))
return true;
Formatter.Output.WriteLine("<br />");
return false;
}
public override Type FallbackFormattingState
{
get { return null; }
}
}
}

View File

@ -0,0 +1,80 @@
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*<code" + Globals.HtmlAttributesPattern + ">")]
public class CodeFormatterState : FormatterState
{
bool m_shouldExitNextTime = false;
bool m_shouldFixHtmlEntities = false;
public CodeFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
if (!Regex.IsMatch(input, "</code>"))
{
this.Formatter.ChangeState(this);
}
else
{
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
}
return input;
}
public override bool ShouldNestState(FormatterState other)
{
return true;
}
public override void Enter()
{
m_shouldFixHtmlEntities = false;
}
public override void Exit()
{
}
public override void FormatLine(string input)
{
if (m_shouldFixHtmlEntities)
input = FixEntities(input);
Formatter.Output.WriteLine(input);
m_shouldFixHtmlEntities = true;
}
public override bool ShouldExit(string input)
{
if (m_shouldExitNextTime)
return true;
m_shouldExitNextTime = Regex.IsMatch(input, @"</code>");
m_shouldFixHtmlEntities = !m_shouldExitNextTime;
return false;
}
public override bool ShouldFormatBlocks(string input)
{
return false;
}
public override bool ShouldParseForNewFormatterState(string input)
{
return false;
}
private string FixEntities(string text)
{
// de-entify any remaining angle brackets or ampersands
text = text.Replace("&", "&amp;");
text = text.Replace(">", "&gt;");
text = text.Replace("<", "&lt;");
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
return text;
}
}
}

View File

@ -0,0 +1,65 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System;
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"fn[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
public class FootNoteFormatterState : SimpleBlockFormatterState
{
int m_noteID = 0;
public FootNoteFormatterState(TextileFormatter f)
: base(f)
{
}
public override void Enter()
{
Formatter.Output.Write(
string.Format("<p id=\"fn{0}\"{1}><sup>{2}</sup> ",
m_noteID,
FormattedStylesAndAlignment("p"),
m_noteID));
}
public override void Exit()
{
Formatter.Output.WriteLine("</p>");
}
public override void FormatLine(string input)
{
Formatter.Output.Write(input);
}
public override bool ShouldExit(string input)
{
return true;
}
protected override void OnContextAcquired()
{
var m = Regex.Match(Tag, @"^fn(?<id>[0-9]+)");
m_noteID = int.Parse(m.Groups["id"].Value);
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
}
}

View File

@ -0,0 +1,109 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"pad[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
public class PaddingFormatterState : SimpleBlockFormatterState
{
public PaddingFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
public int HeaderLevel { get; private set; } = 0;
public override void Enter()
{
for (var i = 0; i < HeaderLevel; i++)
{
Formatter.Output.Write(string.Format("<br {0}/>", FormattedStylesAndAlignment("br")));
}
}
public override void Exit()
{
}
protected override void OnContextAcquired()
{
var m = Regex.Match(Tag, @"^pad(?<lvl>[0-9]+)");
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
}
public override void FormatLine(string input)
{
Formatter.Output.Write(input);
}
public override bool ShouldExit(string intput)
{
return true;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
}
/// <summary>
/// Formatting state for headers and titles.
/// </summary>
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"h[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
public class HeaderFormatterState : SimpleBlockFormatterState
{
public int HeaderLevel { get; private set; } = 0;
public HeaderFormatterState(TextileFormatter f)
: base(f)
{
}
public override void Enter()
{
Formatter.Output.Write(string.Format("<h{0}{1}>", HeaderLevel, FormattedStylesAndAlignment(string.Format("h{0}", HeaderLevel))));
}
public override void Exit()
{
Formatter.Output.WriteLine(string.Format("</h{0}>", HeaderLevel.ToString()));
}
protected override void OnContextAcquired()
{
var m = Regex.Match(Tag, @"^h(?<lvl>[0-9]+)");
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
}
public override void FormatLine(string input)
{
Formatter.Output.Write(input);
}
public override bool ShouldExit(string intput)
{
return true;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
}
}

View File

@ -0,0 +1,141 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
/// <summary>
/// Base formatting state for all lists.
/// </summary>
abstract public class ListFormatterState : FormatterState
{
internal const string PatternBegin = @"^\s*(?<tag>";
internal const string PatternEnd = @")" + Globals.BlockModifiersPattern + @"(?:\s+)? (?<content>.*)$";
private bool m_firstItem = true;
private bool m_firstItemLine = true;
private string m_tag;
private string m_attsInfo;
private string m_alignInfo;
protected int NestingDepth
{
get { return m_tag.Length; }
}
public ListFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
public override string Consume(string input, Match m)
{
m_tag = m.Groups["tag"].Value;
m_alignInfo = m.Groups["align"].Value;
m_attsInfo = m.Groups["atts"].Value;
input = m.Groups["content"].Value;
this.Formatter.ChangeState(this);
return input;
}
public sealed override void Enter()
{
m_firstItem = true;
m_firstItemLine = true;
WriteIndent();
}
public sealed override void Exit()
{
Formatter.Output.WriteLine("</li>");
WriteOutdent();
}
public sealed override void FormatLine(string input)
{
if (m_firstItemLine)
{
if (!m_firstItem)
Formatter.Output.WriteLine("</li>");
Formatter.Output.Write(string.Format("<li {0}>",FormattedStylesAndAlignment("li")));
m_firstItemLine = false;
}
else
{
Formatter.Output.WriteLine("<br />");
}
Formatter.Output.Write(input);
m_firstItem = false;
}
public sealed override bool ShouldNestState(FormatterState other)
{
var listState = (ListFormatterState)other;
return (listState.NestingDepth > NestingDepth);
}
public sealed override bool ShouldExit(string input)
{
// If we have an empty line, we can exit.
if (string.IsNullOrEmpty(input))
return true;
// We exit this list if the next
// list item is of the same type but less
// deep as us, or of the other type of
// list and as deep or less.
if (NestingDepth > 1)
{
if (IsMatchForMe(input, 1, NestingDepth - 1))
return true;
}
if (IsMatchForOthers(input, 1, NestingDepth))
return true;
// As it seems we're going to continue taking
// care of this line, we take the opportunity
// to check whether it's the same list item as
// previously (no "**" or "##" tags), or if it's
// a new list item.
if (IsMatchForMe(input, NestingDepth, NestingDepth))
m_firstItemLine = true;
return false;
}
public sealed override bool ShouldParseForNewFormatterState(string input)
{
// We don't let anyone but ourselves mess with our stuff.
if (IsMatchForMe(input, 1, 100))
return true;
if (IsMatchForOthers(input, 1, 100))
return true;
return false;
}
protected abstract void WriteIndent();
protected abstract void WriteOutdent();
protected abstract bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth);
protected abstract bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth);
protected string FormattedStylesAndAlignment(string element)
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo,element);
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*<notextile>\s*$")]
public class NoTextileFormatterState : FormatterState
{
bool m_shouldExitNextTime = false;
public NoTextileFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
this.Formatter.ChangeState(this);
return string.Empty;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override void Enter()
{
}
public override void Exit()
{
}
public override void FormatLine(string input)
{
if (!m_shouldExitNextTime)
Formatter.Output.WriteLine(input);
}
public override bool ShouldExit(string input)
{
if (m_shouldExitNextTime)
return true;
m_shouldExitNextTime = Regex.IsMatch(input, @"^\s*</notextile>\s*$");
return false;
}
public override bool ShouldFormatBlocks(string input)
{
return false;
}
public override bool ShouldParseForNewFormatterState(string input)
{
return false;
}
public override Type FallbackFormattingState
{
get
{
return null;
}
}
}
}

View File

@ -0,0 +1,51 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
/// <summary>
/// Formatting state for a numbered list.
/// </summary>
[FormatterState(ListFormatterState.PatternBegin + @"#+" + ListFormatterState.PatternEnd)]
public class OrderedListFormatterState : ListFormatterState
{
public OrderedListFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
protected override void WriteIndent()
{
Formatter.Output.WriteLine("<ol" + FormattedStylesAndAlignment("ol") + ">");
}
protected override void WriteOutdent()
{
Formatter.Output.WriteLine("</ol>");
}
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
{
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})#" + Globals.BlockModifiersPattern + @"\s");
}
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
{
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})\*" + Globals.BlockModifiersPattern + @"\s");
}
}
}

View File

@ -0,0 +1,58 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
/// <summary>
/// Formatting state for a standard text (i.e. just paragraphs).
/// </summary>
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"p" + SimpleBlockFormatterState.PatternEnd)]
public class ParagraphFormatterState : SimpleBlockFormatterState
{
public ParagraphFormatterState(TextileFormatter f)
: base(f)
{
}
public override void Enter()
{
Formatter.Output.Write("<p" + FormattedStylesAndAlignment("p") + ">");
}
public override void Exit()
{
Formatter.Output.WriteLine("</p>");
}
public override void FormatLine(string input)
{
Formatter.Output.Write(input);
}
public override bool ShouldExit(string input)
{
if (Regex.IsMatch(input, @"^\s*$"))
return true;
Formatter.Output.WriteLine("<br />");
return false;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
}
}

View File

@ -0,0 +1,42 @@
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*<(h[0-9]|p|pre|blockquote)" + Globals.HtmlAttributesPattern + ">")]
public class PassthroughFormatterState : FormatterState
{
public PassthroughFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
this.Formatter.ChangeState(this);
return input;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override void Enter()
{
}
public override void Exit()
{
}
public override void FormatLine(string input)
{
Formatter.Output.WriteLine(input);
}
public override bool ShouldExit(string input)
{
return true;
}
}
}

View File

@ -0,0 +1,61 @@
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bc" + SimpleBlockFormatterState.PatternEnd)]
public class PreCodeFormatterState : SimpleBlockFormatterState
{
public PreCodeFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
public override void Enter()
{
Formatter.Output.Write("<pre><code>");
}
public override void Exit()
{
Formatter.Output.WriteLine("</code></pre>");
}
public override void FormatLine(string input)
{
Formatter.Output.WriteLine(FixEntities(input));
}
public override bool ShouldExit(string input)
{
if (Regex.IsMatch(input, @"^\s*$"))
return true;
Formatter.Output.WriteLine("<br />");
return false;
}
public override bool ShouldFormatBlocks(string input)
{
return false;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override bool ShouldParseForNewFormatterState(string input)
{
return false;
}
private string FixEntities(string text)
{
// de-entify any remaining angle brackets or ampersands
text = text.Replace("&", "&amp;");
text = text.Replace(">", "&gt;");
text = text.Replace("<", "&lt;");
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
return text;
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*<pre" + Globals.HtmlAttributesPattern + ">")]
public class PreFormatterState : FormatterState
{
bool m_shouldExitNextTime = false;
int m_fakeNestingDepth = 0;
public PreFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
if (!Regex.IsMatch(input, "</pre>"))
{
this.Formatter.ChangeState(this);
}
else
{
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
}
return input;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override void Enter()
{
}
public override void Exit()
{
}
public override void FormatLine(string input)
{
if (Regex.IsMatch(input, "<pre>"))
m_fakeNestingDepth++;
Formatter.Output.WriteLine(input);
}
public override bool ShouldExit(string input)
{
if (m_shouldExitNextTime)
return true;
if (Regex.IsMatch(input, @"</pre>"))
m_fakeNestingDepth--;
if (m_fakeNestingDepth <= 0)
m_shouldExitNextTime = true;
return false;
}
public override bool ShouldFormatBlocks(string input)
{
return false;
}
public override bool ShouldParseForNewFormatterState(string input)
{
// Only allow a child formatting state for <code> tag.
return Regex.IsMatch(input, @"^\s*<code");
}
public override Type FallbackFormattingState
{
get { return null; }
}
}
}

View File

@ -0,0 +1,66 @@
using System.Text.RegularExpressions;
namespace Textile.States
{
public abstract class SimpleBlockFormatterState : FormatterState
{
internal const string PatternBegin = @"^\s*(?<tag>";
internal const string PatternEnd = @")" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.(?:\s+)?(?<content>.*)$";
public string Tag { get; private set; } = null;
public string AlignInfo { get; private set; } = null;
public string AttInfo { get; private set; } = null;
protected SimpleBlockFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
public override string Consume(string input, Match m)
{
Tag = m.Groups["tag"].Value;
AlignInfo = m.Groups["align"].Value;
AttInfo = m.Groups["atts"].Value;
input = m.Groups["content"].Value;
OnContextAcquired();
this.Formatter.ChangeState(this);
return input;
}
public override bool ShouldNestState(FormatterState other)
{
var blockFormatterState = (SimpleBlockFormatterState)other;
return (blockFormatterState.Tag != Tag ||
blockFormatterState.AlignInfo != AlignInfo ||
blockFormatterState.AttInfo != AttInfo);
}
protected virtual void OnContextAcquired()
{
}
/// <summary>
///
/// </summary>
/// <returns></returns>
protected string FormattedAlignment()
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo);
}
protected string FormattedStyles(string element)
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(AttInfo, element);
}
protected string FormattedStylesAndAlignment(string element)
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo + AttInfo, element);
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Text.RegularExpressions;
namespace Textile.States
{
public class TableCellParser
{
readonly string m_lineFragment;
public TableCellParser(string input)
{
m_lineFragment = input;
}
public string GetLineFragmentFormatting()
{
var htmlTag = "td";
var m = Regex.Match(m_lineFragment,
@"^((?<head>_?)" +
Globals.SpanPattern +
Globals.AlignPattern +
Globals.BlockModifiersPattern +
@"(?<dot>\.)\s?)?" +
@"(?<content>.*)"
);
if (!m.Success)
throw new Exception("Couldn't parse table cell.");
if (m.Groups["head"].Value == "_")
htmlTag = "th";
//string opts = BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value, "td") +
// BlockAttributesParser.ParseBlockAttributes(m.Groups["align"].Value, "td") +
// BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "td");
var opts = Blocks.BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value + m.Groups["align"].Value + m.Groups["atts"].Value, "td");
var res = "<" + htmlTag + opts + ">";
// It may be possible the user actually intended to have a dot at the beginning of
// this cell's text, without any formatting (header tag or options).
if (string.IsNullOrEmpty(opts) && htmlTag == "td" && !string.IsNullOrEmpty(m.Groups["dot"].Value))
res += ".";
res += m.Groups["content"].Value;
res += "</" + htmlTag + ">";
return res;
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*(?<tag>table)" +
Globals.SpanPattern +
Globals.AlignPattern +
Globals.BlockModifiersPattern +
@"\.\s*$")]
public class TableFormatterState : FormatterState
{
private string m_attsInfo;
private string m_alignInfo;
public TableFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
m_alignInfo = m.Groups["align"].Value;
m_attsInfo = m.Groups["atts"].Value;
//TODO: check the state (it could already be a table!)
this.Formatter.ChangeState(this);
return string.Empty;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override void Enter()
{
Formatter.Output.WriteLine("<table" + FormattedStylesAndAlignment() + ">");
}
public override void Exit()
{
Formatter.Output.WriteLine("</table>");
}
public override void FormatLine(string input)
{
if (input.Length > 0)
throw new Exception("The TableFormatter state is not supposed to format any lines!");
}
public override bool ShouldExit(string input)
{
var m = Regex.Match(input,
@"^\s*" + Globals.AlignPattern + Globals.BlockModifiersPattern +
@"(\.\s?)?(?<tag>\|)" +
@"(?<content>.*)(?=\|)"
);
return( m.Success == false );
}
protected string FormattedStylesAndAlignment()
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo);
}
}
}

View File

@ -0,0 +1,76 @@
using System.Text.RegularExpressions;
namespace Textile.States
{
[FormatterState(@"^\s*(" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.\s?)?" +
@"\|(?<content>.*)\|\s*$")]
public class TableRowFormatterState : FormatterState
{
private string m_attsInfo;
private string m_alignInfo;
public TableRowFormatterState(TextileFormatter f)
: base(f)
{
}
public override string Consume(string input, Match m)
{
m_alignInfo = m.Groups["align"].Value;
m_attsInfo = m.Groups["atts"].Value;
input = string.Format("|{0}|", m.Groups["content"].Value);
if (!(this.Formatter.CurrentState is TableFormatterState))
{
var s = new TableFormatterState(this.Formatter);
this.Formatter.ChangeState(s);
}
this.Formatter.ChangeState(this);
return input;
}
public override bool ShouldNestState(FormatterState other)
{
return false;
}
public override void Enter()
{
Formatter.Output.WriteLine("<tr" + FormattedStylesAndAlignment() + ">");
}
public override void Exit()
{
Formatter.Output.WriteLine("</tr>");
}
public override void FormatLine(string input)
{
// can get: Align & Classes
var formattedLine = "";
var cellsInput = input.Split('|');
for (var i = 1; i < cellsInput.Length - 1; i++)
{
var cellInput = cellsInput[i];
var tcp = new TableCellParser(cellInput);
formattedLine += tcp.GetLineFragmentFormatting();
}
Formatter.Output.WriteLine(formattedLine);
}
public override bool ShouldExit(string input)
{
return true;
}
protected string FormattedStylesAndAlignment()
{
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo);
}
}
}

View File

@ -0,0 +1,51 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile.States
{
/// <summary>
/// Formatting state for a bulleted list.
/// </summary>
[FormatterState(ListFormatterState.PatternBegin + @"\*+" + ListFormatterState.PatternEnd)]
public class UnorderedListFormatterState : ListFormatterState
{
public UnorderedListFormatterState(TextileFormatter formatter)
: base(formatter)
{
}
protected override void WriteIndent()
{
Formatter.Output.WriteLine("<ul" + FormattedStylesAndAlignment("ul") + ">");
}
protected override void WriteOutdent()
{
Formatter.Output.WriteLine("</ul>");
}
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
{
return Regex.IsMatch(input, @"^\s*[\*]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
}
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
{
return Regex.IsMatch(input, @"^\s*[#]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
}
}
}

View File

@ -0,0 +1,56 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text;
#endregion
namespace Textile
{
public class StringBuilderTextileFormatter : IOutputter
{
StringBuilder m_stringBuilder = null;
public StringBuilderTextileFormatter()
{
}
public string GetFormattedText()
{
return m_stringBuilder.ToString();
}
#region IOutputter Members
public void Begin()
{
m_stringBuilder = new StringBuilder();
}
public void End()
{
}
public void Write(string text)
{
m_stringBuilder.Append(text);
}
public void WriteLine(string line)
{
m_stringBuilder.AppendLine(line);
}
#endregion
}
}

View File

@ -0,0 +1,30 @@
using System.Text.RegularExpressions;
namespace Textile
{
public class StyleReader
{
private readonly Regex _styleParser = new Regex(@"(?<selector>[^\{]+)(?<style>[^\}]+)");
private readonly Regex _minimizer = new Regex(@";\s+");
private readonly System.Collections.Specialized.StringDictionary _tagStyler = new System.Collections.Specialized.StringDictionary();
public StyleReader(string styles)
{
//Read it
var matches = _styleParser.Matches(styles.Replace(System.Environment.NewLine, ""));
foreach (Match match in matches)
{
if (match.Success)
{
_tagStyler.Add(match.Groups["selector"].Value.Trim('{', '}', ' '), _minimizer.Replace(match.Groups["style"].Value.Trim('{', '}', ' '),";"));
}
}
}
public string GetStyle(string tag)
{
return _tagStyler[tag];
}
}
}

View File

@ -0,0 +1,175 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using Textile.Blocks;
using Textile.States;
#endregion
namespace Textile
{
/// <summary>
/// Class for formatting Textile input into HTML.
/// </summary>
/// This class takes raw Textile text and sends the
/// formatted, ready to display HTML string to the
/// outputter defined in the constructor of the
/// class.
public partial class TextileFormatter
{
static TextileFormatter()
{
RegisterFormatterState(typeof(HeaderFormatterState));
RegisterFormatterState(typeof(PaddingFormatterState));
RegisterFormatterState(typeof(BlockQuoteFormatterState));
RegisterFormatterState(typeof(ParagraphFormatterState));
RegisterFormatterState(typeof(FootNoteFormatterState));
RegisterFormatterState(typeof(OrderedListFormatterState));
RegisterFormatterState(typeof(UnorderedListFormatterState));
RegisterFormatterState(typeof(TableFormatterState));
RegisterFormatterState(typeof(TableRowFormatterState));
RegisterFormatterState(typeof(CodeFormatterState));
RegisterFormatterState(typeof(PreFormatterState));
RegisterFormatterState(typeof(PreCodeFormatterState));
RegisterFormatterState(typeof(NoTextileFormatterState));
RegisterBlockModifier(new NoTextileBlockModifier());
RegisterBlockModifier(new CodeBlockModifier());
RegisterBlockModifier(new PreBlockModifier());
RegisterBlockModifier(new HyperLinkBlockModifier());
RegisterBlockModifier(new ImageBlockModifier());
RegisterBlockModifier(new GlyphBlockModifier());
RegisterBlockModifier(new EmphasisPhraseBlockModifier());
RegisterBlockModifier(new StrongPhraseBlockModifier());
RegisterBlockModifier(new ItalicPhraseBlockModifier());
RegisterBlockModifier(new BoldPhraseBlockModifier());
RegisterBlockModifier(new CitePhraseBlockModifier());
RegisterBlockModifier(new DeletedPhraseBlockModifier());
RegisterBlockModifier(new InsertedPhraseBlockModifier());
RegisterBlockModifier(new SuperScriptPhraseBlockModifier());
RegisterBlockModifier(new SubScriptPhraseBlockModifier());
RegisterBlockModifier(new SpanPhraseBlockModifier());
RegisterBlockModifier(new FootNoteReferenceBlockModifier());
//TODO: capitals block modifier
}
/// <summary>
/// Public constructor, where the formatter is hooked up
/// to an outputter.
/// </summary>
/// <param name="output">The outputter to be used.</param>
public TextileFormatter(IOutputter output)
{
Output = output;
}
#region Properties for Output
/// <summary>
/// The ouputter to which the formatted text
/// is sent to.
/// </summary>
public IOutputter Output { get; } = null;
/// <summary>
/// The offset for the header tags.
/// </summary>
/// When the formatted text is inserted into another page
/// there might be a need to offset all headers (h1 becomes
/// h4, for instance). The header offset allows this.
public int HeaderOffset { get; set; } = 0;
#endregion
#region Properties for Conversion
public bool FormatImages
{
get { return IsBlockModifierEnabled(typeof(ImageBlockModifier)); }
set { SwitchBlockModifier(typeof(ImageBlockModifier), value); }
}
public bool FormatLinks
{
get { return IsBlockModifierEnabled(typeof(HyperLinkBlockModifier)); }
set { SwitchBlockModifier(typeof(HyperLinkBlockModifier), value); }
}
public bool FormatLists
{
get { return IsBlockModifierEnabled(typeof(OrderedListFormatterState)); }
set
{
SwitchBlockModifier(typeof(OrderedListFormatterState), value);
SwitchBlockModifier(typeof(UnorderedListFormatterState), value);
}
}
public bool FormatFootNotes
{
get { return IsBlockModifierEnabled(typeof(FootNoteReferenceBlockModifier)); }
set
{
SwitchBlockModifier(typeof(FootNoteReferenceBlockModifier), value);
SwitchFormatterState(typeof(FootNoteFormatterState), value);
}
}
public bool FormatTables
{
get { return IsFormatterStateEnabled(typeof(TableFormatterState)); }
set
{
SwitchFormatterState(typeof(TableFormatterState), value);
SwitchFormatterState(typeof(TableRowFormatterState), value);
}
}
/// <summary>
/// Attribute to add to all links.
/// </summary>
public string Rel { get; set; } = string.Empty;
#endregion
#region Utility Methods
/// <summary>
/// Utility method for quickly formatting a text without having
/// to create a TextileFormatter with an IOutputter.
/// </summary>
/// <param name="input">The string to format</param>
/// <returns>The formatted version of the string</returns>
public static string FormatString(string input)
{
var s = new StringBuilderTextileFormatter();
var f = new TextileFormatter(s);
f.Format(input);
return s.GetFormattedText();
}
/// <summary>
/// Utility method for formatting a text with a given outputter.
/// </summary>
/// <param name="input">The string to format</param>
/// <param name="outputter">The IOutputter to use</param>
public static void FormatString(string input, IOutputter outputter)
{
var f = new TextileFormatter(outputter);
f.Format(input);
}
#endregion
}
}

View File

@ -0,0 +1,55 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
#endregion
namespace Textile
{
public partial class TextileFormatter
{
#region Block Modifiers Registration
private static readonly List<BlockModifier> s_blockModifiers = new List<BlockModifier>();
private static readonly List<Type> s_blockModifiersTypes = new List<Type>();
public static void RegisterBlockModifier(BlockModifier blockModifer)
{
s_blockModifiers.Add(blockModifer);
s_blockModifiersTypes.Add(blockModifer.GetType());
}
#endregion
#region Block Modifiers Management
private readonly List<Type> m_disabledBlockModifiers = new List<Type>();
public bool IsBlockModifierEnabled(Type type)
{
return !m_disabledBlockModifiers.Contains(type);
}
public void SwitchBlockModifier(Type type, bool onOff)
{
if (onOff)
m_disabledBlockModifiers.Remove(type);
else if (!m_disabledBlockModifiers.Contains(type))
m_disabledBlockModifiers.Add(type);
}
#endregion
}
}

View File

@ -0,0 +1,122 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System.Text.RegularExpressions;
#endregion
namespace Textile
{
public partial class TextileFormatter
{
private readonly Regex VelocityArguments =
new Regex("nostyle(?<arg>.*?)/nostyle", RegexOptions.IgnoreCase | RegexOptions.Singleline);
private string argMatchReplace(Match match)
{
return match.Result("${arg}");
}
#region Formatting Methods
/// <summary>
/// Formats the given text.
/// </summary>
/// <param name="input">The text to format.</param>
public void Format(string input)
{
Output.Begin();
// Clean the text...
var str = PrepareInputForFormatting(input);
// ...and format each line.
foreach (var line in str.Split('\n'))
{
var tmp = line;
// Let's see if the current state(s) is(are) finished...
while (CurrentState != null && CurrentState.ShouldExit(tmp))
PopState();
if (!Regex.IsMatch(tmp, @"^\s*$"))
{
// Figure out the new state for this text line, if possible.
if (CurrentState == null || CurrentState.ShouldParseForNewFormatterState(tmp))
{
tmp = HandleFormattingState(tmp);
}
// else, the current state doesn't want to be superceded by
// a new one. We'll leave him be.
// Modify the line with our block modifiers.
if (CurrentState == null || CurrentState.ShouldFormatBlocks(tmp))
{
foreach (var blockModifier in s_blockModifiers)
{
//TODO: if not disabled...
tmp = blockModifier.ModifyLine(tmp);
}
for (var i = s_blockModifiers.Count - 1; i >= 0; i--)
{
var blockModifier = s_blockModifiers[i];
tmp = blockModifier.Conclude(tmp);
}
}
tmp = VelocityArguments.Replace(tmp, argMatchReplace);
// Format the current line.
CurrentState.FormatLine(tmp);
}
}
// We're done. There might be a few states still on
// the stack (for example if the text ends with a nested
// list), so we must pop them all so that they have
// their "Exit" method called correctly.
while (m_stackOfStates.Count > 0)
PopState();
Output.End();
}
#endregion
#region Preparation Methods
/// <summary>
/// Cleans up a text before formatting.
/// </summary>
/// <param name="input">The text to clean up.</param>
/// <returns>The clean text.</returns>
/// This method cleans stuff like line endings, so that
/// we don't have to bother with it while formatting.
private string PrepareInputForFormatting(string input)
{
input = CleanWhiteSpace(input);
return input;
}
private string CleanWhiteSpace(string text)
{
text = text.Replace("\r\n", "\n");
text = text.Replace("\t", "");
text = Regex.Replace(text, @"\n{3,}", "\n\n");
text = Regex.Replace(text, @"\n *\n", "\n\n");
text = Regex.Replace(text, "\"$", "\" ");
return text;
}
#endregion
}
}

View File

@ -0,0 +1,160 @@
#region License Statement
// Copyright (c) L.A.B.Soft. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// You must not remove this notice, or any other, from this software.
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
#endregion
namespace Textile
{
public partial class TextileFormatter
{
#region State Registration
private static readonly List<Type> s_registeredStates = new List<Type>();
private static readonly List<FormatterStateAttribute> s_registeredStatesAttributes = new List<FormatterStateAttribute>();
public static void RegisterFormatterState(Type formatterStateType)
{
if (!formatterStateType.IsSubclassOf(typeof(FormatterState)))
throw new ArgumentException("The formatter state must be a sub-public class of FormatterStateBase.");
if (formatterStateType.GetConstructor(new Type[] { typeof(TextileFormatter) }) == null)
throw new ArgumentException("The formatter state must have a constructor that takes a TextileFormatter reference.");
var att = FormatterStateAttribute.Get(formatterStateType);
if (att == null)
throw new ArgumentException("The formatter state must have the FormatterStateAttribute.");
s_registeredStates.Add(formatterStateType);
s_registeredStatesAttributes.Add(att);
}
#endregion
#region State Management
private readonly List<Type> m_disabledFormatterStates = new List<Type>();
private readonly Stack<FormatterState> m_stackOfStates = new Stack<FormatterState>();
private bool IsFormatterStateEnabled(Type type)
{
return !m_disabledFormatterStates.Contains(type);
}
private void SwitchFormatterState(Type type, bool onOff)
{
if (onOff)
m_disabledFormatterStates.Remove(type);
else if (!m_disabledFormatterStates.Contains(type))
m_disabledFormatterStates.Add(type);
}
/// <summary>
/// Pushes a new state on the stack.
/// </summary>
/// <param name="s">The state to push.</param>
/// The state will be entered automatically.
private void PushState(FormatterState s)
{
m_stackOfStates.Push(s);
s.Enter();
}
/// <summary>
/// Removes the last state from the stack.
/// </summary>
/// The state will be exited automatically.
private void PopState()
{
m_stackOfStates.Peek().Exit();
m_stackOfStates.Pop();
}
/// <summary>
/// The current state, if any.
/// </summary>
internal FormatterState CurrentState
{
get
{
if (m_stackOfStates.Count > 0)
return m_stackOfStates.Peek();
else
return null;
}
}
internal void ChangeState(FormatterState formatterState)
{
if (CurrentState != null && CurrentState.GetType() == formatterState.GetType())
{
if (!CurrentState.ShouldNestState(formatterState))
return;
}
PushState(formatterState);
}
#endregion
#region State Handling
/// <summary>
/// Parses the string and updates the state accordingly.
/// </summary>
/// <param name="input">The text to process.</param>
/// <returns>The text, ready for formatting.</returns>
/// This method modifies the text because it removes some
/// syntax stuff. Maybe the states themselves should handle
/// their own syntax and remove it?
private string HandleFormattingState(string input)
{
for (var i = 0; i < s_registeredStates.Count; i++)
{
var type = s_registeredStates[i];
if (IsFormatterStateEnabled(type))
{
var att = s_registeredStatesAttributes[i];
var m = Regex.Match(input, att.Pattern);
if (m.Success)
{
var formatterState = (FormatterState)Activator.CreateInstance(type, this);
return formatterState.Consume(input, m);
}
}
}
// Default, when no block is specified, we ask the current state, or
// use the paragraph state.
if (CurrentState != null)
{
if (CurrentState.FallbackFormattingState != null)
{
var formatterState = (FormatterState)Activator.CreateInstance(CurrentState.FallbackFormattingState, this);
ChangeState(formatterState);
}
// else, the current state doesn't want to be superceded by
// a new one. We'll leave him be.
}
else
{
ChangeState(new States.ParagraphFormatterState(this));
}
return input;
}
#endregion
}
}

View File

@ -7,5 +7,16 @@
"LastName": "Last Name",
"Email": "Email",
"Comments": "Comments",
"ContactInformation": "Contact Information"
"ContactInformation": "Contact Information",
"UserType": "Type",
"Sex": "Sex",
"Birthdate": "Date of Birth",
"Location": "Location",
"Language": "Language",
"Subscriptions": "Subscriptions",
"SocialProfiles": "Social Profiles",
"PendingTitle": "Pending",
"EditUserDialogTitle": "Edit Profile",
"LoadingProcessing": "Loading...",
"LoadingDescription": "Please wait..."
}

View File

@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next';
const getSortData = () => {
return [
{ key: "firstname", label: "Name" },
{ key: "lastname", label: "Surname" }
{ key: "firstname", label: "By first name" },
{ key: "lastname", label: "By last name" }
];
};

View File

@ -3,6 +3,7 @@ import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { PageLayout, RequestLoader } from "asc-web-components";
import { withTranslation } from 'react-i18next';
import {
ArticleHeaderContent,
ArticleBodyContent,
@ -95,6 +96,7 @@ class Home extends React.Component {
isHeaderChecked,
selected
} = this.state;
const t = this.props.t;
return (
<>
<RequestLoader
@ -102,7 +104,7 @@ class Home extends React.Component {
zIndex={256}
loaderSize={16}
loaderColor={"#999"}
label={"Loading... Please wait..."}
label={`${t('Resource:LoadingProcessing')} ${t('Resource:LoadingDescription')}`}
fontSize={12}
fontColor={"#999"}
/>
@ -156,4 +158,4 @@ function mapStateToProps(state) {
export default connect(
mapStateToProps,
{ setSelected }
)(withRouter(Home));
)(withRouter(withTranslation()(Home)));

View File

@ -1,5 +1,6 @@
import React, { useCallback } from "react";
import { withRouter } from "react-router";
import { useTranslation } from 'react-i18next';
import {
Text,
Avatar,
@ -142,6 +143,7 @@ const createContacts = contacts => {
};
const SectionBodyContent = props => {
const { t, i18n } = useTranslation();
const { profile, history, settings, isAdmin, viewer } = props;
//console.log(props);
const contacts = profile.contacts && getUserContacts(profile.contacts);
@ -189,19 +191,19 @@ const SectionBodyContent = props => {
<Button
style={editButtonWrapper}
size="big"
label="Edit profile"
label={t("Resource:EditUserDialogTitle")}
onClick={onEditProfileClick}
/>
)}
</div>
<div style={infoWrapper}>
<div style={titlesWrapper}>
<Text.Body style={restMargins} color="#A3A9AE" title="Type">
Type:
<Text.Body style={restMargins} color="#A3A9AE" title={t('Resource:UserType')}>
{t('Resource:UserType')}:
</Text.Body>
{profile.email && (
<Text.Body style={restMargins} color="#A3A9AE" title="E-mail">
E-mail:
<Text.Body style={restMargins} color="#A3A9AE" title={t('Resource:Email')}>
{t('Resource:Email')}:
</Text.Body>
)}
{profile.department && (
@ -220,8 +222,8 @@ const SectionBodyContent = props => {
</Text.Body>
)}
{profile.sex && (
<Text.Body style={restMargins} color="#A3A9AE" title="Sex">
Sex:
<Text.Body style={restMargins} color="#A3A9AE" title={t('Resource:Sex')}>
{t('Resource:Sex')}:
</Text.Body>
)}
{profile.workFrom && (
@ -237,19 +239,19 @@ const SectionBodyContent = props => {
<Text.Body
style={restMargins}
color="#A3A9AE"
title="Date of birth"
title={t('Resource:Birthdate')}
>
Date of birth:
{t('Resource:Birthdate')}:
</Text.Body>
)}
{profile.location && (
<Text.Body style={restMargins} color="#A3A9AE" title="Location">
Location:
<Text.Body style={restMargins} color="#A3A9AE" title={t('Resource:Location')}>
{t('Resource:Location')}:
</Text.Body>
)}
{isSelf && (
<Text.Body style={restMargins} color="#A3A9AE" title="Language">
Language:
<Text.Body style={restMargins} color="#A3A9AE" title={t('Resource:Language')}>
{t('Resource:Language')}:
</Text.Body>
)}
{/*{isSelf && <Text.Body style={marginTop24} color='#A3A9AE' title='Affiliate status'>Affiliate status:</Text.Body>}*/}
@ -267,7 +269,7 @@ const SectionBodyContent = props => {
>
{profile.email}
</Link>
{profile.activationStatus === 2 && " (Pending)"}
{profile.activationStatus === 2 && ` (${t("Resource:PendingTitle")})`}
</Text.Body>
<Text.Body as="div" style={restMargins}>{formatedDepartments}</Text.Body>
<Text.Body style={restMargins}>{profile.title}</Text.Body>
@ -300,7 +302,7 @@ const SectionBodyContent = props => {
{isSelf && (
<div style={selfToggleWrapper}>
<ToggleContent
label="Subscriptions"
label={t('Resource:Subscriptions')}
style={notesWrapper}
isOpen={true}
>
@ -317,7 +319,7 @@ const SectionBodyContent = props => {
)}
{profile.notes && (
<div style={notesToggleWrapper}>
<ToggleContent label="Comment" style={notesWrapper} isOpen={true}>
<ToggleContent label={t('Resource:Comments')} style={notesWrapper} isOpen={true}>
<Text.Body as="span">{profile.notes}</Text.Body>
</ToggleContent>
</div>
@ -325,7 +327,7 @@ const SectionBodyContent = props => {
{profile.contacts && (
<div style={contactsToggleWrapper}>
<ToggleContent
label="Contact information"
label={t('Resource:ContactInformation')}
style={notesWrapper}
isOpen={true}
>
@ -336,7 +338,7 @@ const SectionBodyContent = props => {
{profile.contacts && (
<div style={contactsToggleWrapper}>
<ToggleContent
label="Social profiles"
label={t('Resource:SocialProfiles')}
style={notesWrapper}
isOpen={true}
>

View File

@ -0,0 +1,9 @@
{
"LoadingProcessing": "Loading...",
"LoginButton": "Sign In",
"Password": "Password",
"RegistrationEmailWatermark": "Your registration email",
"Profile": "Profile",
"LogoutButton": "Sign Out",
"AboutCompanyTitle": "About this program"
}

View File

@ -1,6 +1,3 @@
{
"Profile": "Profile",
"LogoutButton": "Sign Out",
"AboutCompanyTitle": "About this program",
"ResourceNotFound": "Sorry, the resource cannot be found."
}

View File

@ -0,0 +1,5 @@
{
"Profile": "Профиль",
"LogoutButton": "Выйти",
"AboutCompanyTitle": "О программе"
}

View File

@ -1,6 +0,0 @@
{
"Profile": "Профиль",
"LogoutButton": "Выйти",
"AboutCompanyTitle": "О программе",
"ResourceNotFound": "Извините, страница не найдена."
}

View File

@ -35,13 +35,13 @@ class StudioLayout extends React.Component {
const currentUserActions = [
{
key: 'ProfileBtn', label: t("Profile"), onClick: this.onProfileClick
key: 'ProfileBtn', label: t("Resource:Profile"), onClick: this.onProfileClick
},
{
key: 'AboutBtn', label: t("AboutCompanyTitle"), onClick: this.onAboutClick
key: 'AboutBtn', label: t("Resource:AboutCompanyTitle"), onClick: this.onAboutClick
},
{
key: 'LogoutBtn', label: t("LogoutButton"), onClick: this.onLogoutClick
key: 'LogoutBtn', label: t("Resource:LogoutButton"), onClick: this.onLogoutClick
},
];

View File

@ -6,6 +6,7 @@ import { Button, TextInput, PageLayout } from 'asc-web-components';
import { connect } from 'react-redux';
import { login } from '../../../store/auth/actions';
import styled from 'styled-components';
import { useTranslation } from 'react-i18next';
const FormContainer = styled(Container)`
margin-top: 70px;
@ -34,6 +35,7 @@ const FormContainer = styled(Container)`
const mdOptions = { size: 6, offset: 3 };
const Form = props => {
const { t, i18n } = useTranslation();
const [identifier, setIdentifier] = useState('');
const [identifierValid, setIdentifierValid] = useState(true);
const [password, setPassword] = useState('');
@ -115,7 +117,7 @@ const Form = props => {
name="login"
hasError={!identifierValid}
value={identifier}
placeholder="You registration email"
placeholder={t('Resource:RegistrationEmailWatermark')}
size='huge'
scale={true}
isAutoFocussed={true}
@ -138,7 +140,7 @@ const Form = props => {
type="password"
hasError={!passwordValid}
value={password}
placeholder="Password"
placeholder={t('Resource:Password')}
size='huge'
scale={true}
tabIndex={2}
@ -158,7 +160,7 @@ const Form = props => {
<Button
primary
size='big'
label={isLoading ? "Loading..." : "Sign In"}
label={isLoading ? t('Resource:LoadingProcessing') : t('Resource:LoginButton')}
tabIndex={3}
isDisabled={isLoading}
isLoading={isLoading}

View File

@ -15,9 +15,10 @@ i18n
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
lng: "en",
lng: "ru",
fallbackLng: 'en',
debug: true,
ns: ['translation', 'Resource'],
interpolation: {
escapeValue: false, // not needed for react as it escapes by default