46    public const ESCAPE = 
"\xc2\xa7"; 
 
   47    public const EOL = 
"\n";
 
   49    public const BLACK = TextFormat::ESCAPE . 
"0";
 
   50    public const DARK_BLUE = TextFormat::ESCAPE . 
"1";
 
   51    public const DARK_GREEN = TextFormat::ESCAPE . 
"2";
 
   52    public const DARK_AQUA = TextFormat::ESCAPE . 
"3";
 
   53    public const DARK_RED = TextFormat::ESCAPE . 
"4";
 
   54    public const DARK_PURPLE = TextFormat::ESCAPE . 
"5";
 
   55    public const GOLD = TextFormat::ESCAPE . 
"6";
 
   56    public const GRAY = TextFormat::ESCAPE . 
"7";
 
   57    public const DARK_GRAY = TextFormat::ESCAPE . 
"8";
 
   58    public const BLUE = TextFormat::ESCAPE . 
"9";
 
   59    public const GREEN = TextFormat::ESCAPE . 
"a";
 
   60    public const AQUA = TextFormat::ESCAPE . 
"b";
 
   61    public const RED = TextFormat::ESCAPE . 
"c";
 
   62    public const LIGHT_PURPLE = TextFormat::ESCAPE . 
"d";
 
   63    public const YELLOW = TextFormat::ESCAPE . 
"e";
 
   64    public const WHITE = TextFormat::ESCAPE . 
"f";
 
   65    public const MINECOIN_GOLD = TextFormat::ESCAPE . 
"g";
 
   66    public const MATERIAL_QUARTZ = TextFormat::ESCAPE . 
"h";
 
   67    public const MATERIAL_IRON = TextFormat::ESCAPE . 
"i";
 
   68    public const MATERIAL_NETHERITE = TextFormat::ESCAPE . 
"j";
 
   69    public const MATERIAL_REDSTONE = TextFormat::ESCAPE . 
"m";
 
   70    public const MATERIAL_COPPER = TextFormat::ESCAPE . 
"n";
 
   71    public const MATERIAL_GOLD = TextFormat::ESCAPE . 
"p";
 
   72    public const MATERIAL_EMERALD = TextFormat::ESCAPE . 
"q";
 
   73    public const MATERIAL_DIAMOND = TextFormat::ESCAPE . 
"s";
 
   74    public const MATERIAL_LAPIS = TextFormat::ESCAPE . 
"t";
 
   75    public const MATERIAL_AMETHYST = TextFormat::ESCAPE . 
"u";
 
   76    public const MATERIAL_RESIN = TextFormat::ESCAPE . 
"v";
 
   78    public const COLORS = [
 
   79        self::BLACK => self::BLACK,
 
   80        self::DARK_BLUE => self::DARK_BLUE,
 
   81        self::DARK_GREEN => self::DARK_GREEN,
 
   82        self::DARK_AQUA => self::DARK_AQUA,
 
   83        self::DARK_RED => self::DARK_RED,
 
   84        self::DARK_PURPLE => self::DARK_PURPLE,
 
   85        self::GOLD => self::GOLD,
 
   86        self::GRAY => self::GRAY,
 
   87        self::DARK_GRAY => self::DARK_GRAY,
 
   88        self::BLUE => self::BLUE,
 
   89        self::GREEN => self::GREEN,
 
   90        self::AQUA => self::AQUA,
 
   91        self::RED => self::RED,
 
   92        self::LIGHT_PURPLE => self::LIGHT_PURPLE,
 
   93        self::YELLOW => self::YELLOW,
 
   94        self::WHITE => self::WHITE,
 
   95        self::MINECOIN_GOLD => self::MINECOIN_GOLD,
 
   96        self::MATERIAL_QUARTZ => self::MATERIAL_QUARTZ,
 
   97        self::MATERIAL_IRON => self::MATERIAL_IRON,
 
   98        self::MATERIAL_NETHERITE => self::MATERIAL_NETHERITE,
 
   99        self::MATERIAL_REDSTONE => self::MATERIAL_REDSTONE,
 
  100        self::MATERIAL_COPPER => self::MATERIAL_COPPER,
 
  101        self::MATERIAL_GOLD => self::MATERIAL_GOLD,
 
  102        self::MATERIAL_EMERALD => self::MATERIAL_EMERALD,
 
  103        self::MATERIAL_DIAMOND => self::MATERIAL_DIAMOND,
 
  104        self::MATERIAL_LAPIS => self::MATERIAL_LAPIS,
 
  105        self::MATERIAL_AMETHYST => self::MATERIAL_AMETHYST,
 
  106        self::MATERIAL_RESIN => self::MATERIAL_RESIN,
 
  109    public const OBFUSCATED = TextFormat::ESCAPE . 
"k";
 
  110    public const BOLD = TextFormat::ESCAPE . 
"l";
 
  115    public const ITALIC = TextFormat::ESCAPE . 
"o";
 
  117    public const FORMATS = [
 
  118        self::OBFUSCATED => self::OBFUSCATED,
 
  119        self::BOLD => self::BOLD,
 
  120        self::ITALIC => self::ITALIC,
 
  123    public const RESET = TextFormat::ESCAPE . 
"r";
 
  125    private static function makePcreError() : \InvalidArgumentException{
 
  126        $errorCode = preg_last_error();
 
  128            PREG_INTERNAL_ERROR => 
"Internal error",
 
  129            PREG_BACKTRACK_LIMIT_ERROR => 
"Backtrack limit reached",
 
  130            PREG_RECURSION_LIMIT_ERROR => 
"Recursion limit reached",
 
  131            PREG_BAD_UTF8_ERROR => 
"Malformed UTF-8",
 
  132            PREG_BAD_UTF8_OFFSET_ERROR => 
"Bad UTF-8 offset",
 
  133            PREG_JIT_STACKLIMIT_ERROR => 
"PCRE JIT stack limit reached" 
  134        ][$errorCode] ?? 
"Unknown (code $errorCode)";
 
  135        throw new \InvalidArgumentException(
"PCRE error: $message");
 
  141    private static function preg_replace(
string $pattern, 
string $replacement, 
string $string) : string{
 
  142        $result = preg_replace($pattern, $replacement, $string);
 
  143        if($result === 
null){
 
  144            throw self::makePcreError();
 
  154    public static function tokenize(
string $string) : array{
 
  155        $result = preg_split(
"/(" . 
TextFormat::ESCAPE . 
"[0-9a-v])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
 
  156        if($result === 
false) 
throw self::makePcreError();
 
 
  165    public static function clean(
string $string, 
bool $removeFormat = 
true) : string{
 
  166        $string = mb_scrub($string, 
'UTF-8');
 
  167        $string = self::preg_replace(
"/[\x{E000}-\x{F8FF}]/u", 
"", $string); 
 
  169            $string = str_replace(TextFormat::ESCAPE, 
"", self::preg_replace(
"/" . TextFormat::ESCAPE . 
"[0-9a-v]/u", 
"", $string));
 
  171        return str_replace(
"\x1b", 
"", self::preg_replace(
"/\x1b[\\(\\][[0-9;\\[\\(]+[Bm]/u", 
"", $string));
 
 
  179    public static function colorize(
string $string, 
string $placeholder = 
"&") : string{
 
  180        return self::preg_replace(
'/' . preg_quote($placeholder, 
"/") . 
'([0-9a-v])/u', 
TextFormat::ESCAPE . 
'$1', $string);
 
 
  196    public static function addBase(
string $baseFormat, 
string $string) : string{
 
  197        $baseFormatParts = self::tokenize($baseFormat);
 
  198        foreach($baseFormatParts as $part){
 
  199            if(!isset(self::FORMATS[$part]) && !isset(self::COLORS[$part])){
 
  200                throw new \InvalidArgumentException(
"Unexpected base format token \"$part\", expected only color and format tokens");
 
  203        $baseFormat = self::RESET . $baseFormat;
 
  205        return $baseFormat . str_replace(TextFormat::RESET, $baseFormat, $string);
 
 
  225    public static function toHTML(
string $string) : string{
 
  228        foreach(self::tokenize($string) as $token){
 
  229            $formatString = match($token){
 
  230                TextFormat::BLACK => 
"color:#000",
 
  231                TextFormat::DARK_BLUE => 
"color:#00A",
 
  232                TextFormat::DARK_GREEN => 
"color:#0A0",
 
  233                TextFormat::DARK_AQUA => 
"color:#0AA",
 
  234                TextFormat::DARK_RED => 
"color:#A00",
 
  235                TextFormat::DARK_PURPLE => 
"color:#A0A",
 
  236                TextFormat::GOLD => 
"color:#FA0",
 
  237                TextFormat::GRAY => 
"color:#AAA",
 
  238                TextFormat::DARK_GRAY => 
"color:#555",
 
  239                TextFormat::BLUE => 
"color:#55F",
 
  240                TextFormat::GREEN => 
"color:#5F5",
 
  241                TextFormat::AQUA => 
"color:#5FF",
 
  242                TextFormat::RED => 
"color:#F55",
 
  243                TextFormat::LIGHT_PURPLE => 
"color:#F5F",
 
  244                TextFormat::YELLOW => 
"color:#FF5",
 
  245                TextFormat::WHITE => 
"color:#FFF",
 
  246                TextFormat::MINECOIN_GOLD => 
"color:#dd0",
 
  247                TextFormat::MATERIAL_QUARTZ => 
"color:#e2d3d1",
 
  248                TextFormat::MATERIAL_IRON => 
"color:#cec9c9",
 
  249                TextFormat::MATERIAL_NETHERITE => 
"color:#44393a",
 
  250                TextFormat::MATERIAL_REDSTONE => 
"color:#961506",
 
  251                TextFormat::MATERIAL_COPPER => 
"color:#b4684d",
 
  252                TextFormat::MATERIAL_GOLD => 
"color:#deb02c",
 
  253                TextFormat::MATERIAL_EMERALD => 
"color:#119f36",
 
  254                TextFormat::MATERIAL_DIAMOND => 
"color:#2cb9a8",
 
  255                TextFormat::MATERIAL_LAPIS => 
"color:#20487a",
 
  256                TextFormat::MATERIAL_AMETHYST => 
"color:#9a5cc5",
 
  257                TextFormat::MATERIAL_RESIN => 
"color:#fc7812",
 
  258                TextFormat::BOLD => 
"font-weight:bold",
 
  259                TextFormat::ITALIC => 
"font-style:italic",
 
  262            if($formatString !== 
null){
 
  263                $newString .= 
"<span style=\"$formatString\">";
 
  265            }elseif($token === TextFormat::RESET){
 
  266                $newString .= str_repeat(
"</span>", $tokens);
 
  269                $newString .= $token;
 
  273        $newString .= str_repeat(
"</span>", $tokens);