ModernUO/Projects/Server/Utilities/Html.cs
2026-03-05 19:36:54 -08:00

283 lines
9.4 KiB
C#

/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Html.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Text;
namespace Server;
public enum TextAlignment : byte
{
Left = 0,
Center = 1,
Right = 2
}
public static class Html
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this string input) => Center(input.AsSpan());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this ReadOnlySpan<char> input) => $"<CENTER>{input}</CENTER>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(ref RawInterpolatedStringHandler input)
{
var str = input.Text.Center();
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this string input, int color) => Center(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this string input, ReadOnlySpan<char> color) => Center(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this ReadOnlySpan<char> input, int color) =>
$"<CENTER><BASEFONT COLOR=#{color:X6}>{input}</BASEFONT></CENTER>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(this ReadOnlySpan<char> input, ReadOnlySpan<char> color) =>
$"<CENTER><BASEFONT COLOR={color}>{input}</BASEFONT></CENTER>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(ref RawInterpolatedStringHandler input, int color)
{
var str = input.Text.Center(color);
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Center(ref RawInterpolatedStringHandler input, ReadOnlySpan<char> color)
{
var str = input.Text.Center(color);
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(this string input, int color) => Color(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(this string input, ReadOnlySpan<char> color) => Color(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(this ReadOnlySpan<char> input, int color) => $"<BASEFONT COLOR=#{color:X6}>{input}</CENTER>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(this ReadOnlySpan<char> input, ReadOnlySpan<char> color) =>
$"<BASEFONT COLOR={color}>{input}</CENTER>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(ref RawInterpolatedStringHandler input, int color)
{
var str = input.Text.Color(color);
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Color(ref RawInterpolatedStringHandler input, ReadOnlySpan<char> color)
{
var str = input.Text.Color(color);
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this string input) => Right(input.AsSpan());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this ReadOnlySpan<char> input) => $"<RIGHT>{input}</RIGHT>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(ref RawInterpolatedStringHandler input)
{
var str = input.Text.Right();
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this string input, int color) => Right(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this string input, ReadOnlySpan<char> color) => Right(input.AsSpan(), color);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this ReadOnlySpan<char> input, int color) =>
$"<RIGHT><BASEFONT COLOR=#{color:X6}>{input}</BASEFONT></RIGHT>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(this ReadOnlySpan<char> input, ReadOnlySpan<char> color) =>
$"<RIGHT><BASEFONT COLOR={color}>{input}</BASEFONT></RIGHT>";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(ref RawInterpolatedStringHandler input, int color)
{
var str = input.Text.Right(color);
input.Clear();
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string Right(ref RawInterpolatedStringHandler input, ReadOnlySpan<char> color)
{
var str = input.Text.Right(color);
input.Clear();
return str;
}
private static readonly SearchValues<char> _htmlSearchValues = SearchValues.Create('<', '>', '&', '"', '\'');
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string EscapeHtml(this string input)
{
if (string.IsNullOrEmpty(input))
{
return input ?? "";
}
return EscapeHtml(input.AsSpan());
}
public static string EscapeHtml(this ReadOnlySpan<char> input)
{
if (input.IsEmpty)
{
return string.Empty;
}
var indexOfAny = input.IndexOfAny(_htmlSearchValues);
if (indexOfAny < 0)
{
return input.ToString();
}
using var builder = ValueStringBuilder.Create(input.Length * 2);
var lastIndex = 0;
while (indexOfAny >= 0)
{
if (indexOfAny > lastIndex)
{
builder.Append(input[lastIndex..indexOfAny]);
}
var c = input[indexOfAny];
var replacement = c switch
{
'&' => "&amp;",
'<' => "&lt;",
'>' => "&gt;",
'"' => "&quot;",
'\'' => "&#39;"
};
builder.Append(replacement);
lastIndex = indexOfAny + 1;
indexOfAny = input[lastIndex..].IndexOfAny(_htmlSearchValues);
if (indexOfAny < 0)
{
break;
}
indexOfAny += lastIndex;
}
if (lastIndex < input.Length)
{
builder.Append(input[lastIndex..]);
}
var result = builder.ToString();
builder.Dispose();
return result;
}
public static string Build(
ReadOnlySpan<char> text, ReadOnlySpan<char> color = default, int size = -1, byte fontStyle = 0,
TextAlignment align = TextAlignment.Left
)
{
var arr = STArrayPool<char>.Shared.Rent(BuildCharCount(text, color));
var bytesWritten = Build(text, arr.AsSpan(), color, size, fontStyle, align);
var result = arr.AsSpan(0, bytesWritten).ToString();
STArrayPool<char>.Shared.Return(arr);
return result;
}
public static int BuildCharCount(ReadOnlySpan<char> text, ReadOnlySpan<char> color) => 61 + text.Length + color.Length;
public static int Build(
ReadOnlySpan<char> text, Span<char> dest, ReadOnlySpan<char> color = default, int size = -1, byte fontStyle = 0,
TextAlignment align = TextAlignment.Left
)
{
using var builder = new ValueStringBuilder(dest);
if (align == TextAlignment.Right)
{
builder.Append("<RIGHT>");
}
else if (align == TextAlignment.Center)
{
builder.Append("<CENTER>");
}
if (color.Length > 0 || size > -1 || fontStyle > 0)
{
builder.Append("<BASEFONT");
if (color.Length > 0)
{
builder.Append($" COLOR={color}");
}
if (size > -1)
{
builder.Append($" SIZE={size}");
}
if (fontStyle > 0)
{
builder.Append($" STYLE={fontStyle}");
}
builder.Append($">{text}</BASEFONT>");
}
else
{
builder.Append(text);
}
if (align == TextAlignment.Right)
{
builder.Append("</RIGHT>");
}
else if (align == TextAlignment.Center)
{
builder.Append("</CENTER>");
}
return builder.Length;
}
}