RU
apps/caramail-chat / src / Chat / Smileys.php · commit 6eebcdb
<?phpnamespace App\Chat;/** * The smileys of the 2002 applet (net.spray.chat.a.g::for), found in a segment of text: * * - composed faces: eyes + nose + mouth, ":-)", ";o)", "8-D"... (the nose is required: ":)" stays text); a digit *   after it colours the face (":-)2" pink), then "#c" / "#d" a left hand (thumb up / finger), and one more *   "c" / "d" a right hand (":-)#cd"); * - "#00".."#14": drawn pictures (bomb, heart...); #02 #04 #13 #14 take a colour digit ("#135"); * - "@00".."@59": the pictures of smiley.gif (3 rows of 20 cells of 32 x 20, assets/smileys/smiley.png), the ones *   of the applet's "Smileys" panel. */final class Smileys{    public const EYES = ':;8\'<|[B>%]}{O(';    public const NOSES = '*!&-#=>oO$<';    public const MOUTHS = ')(D|OoXxpP/\\*!{}#[$';    public const DEFAULT_COLOR = 10;    /** The pictures of smiley.gif: 20 a row, 3 rows. */    public const IMAGES = 60;    public const BIG_COUNT = 15;    /**     * Cuts a text into pieces: strings, and smileys as arrays.     *     * @return list<string|array{kind: string, eyes?: int, nose?: int, mouth?: int, hands?: int, color?: int, n?: int, source: string}>     */    public static function parse(string $text, int $images = self::IMAGES): array    {        $out = [];        $plain = '';        $len = strlen($text);        $i = 0;        while ($i < $len) {            $smiley = self::at($text, $i, $len, $images);            if ($smiley === null) {                $plain .= $text[$i];                $i++;                continue;            }            if ($plain !== '') {                $out[] = $plain;                $plain = '';            }            $out[] = $smiley;            $i += strlen($smiley['source']);        }        if ($plain !== '') {            $out[] = $plain;        }        return $out;    }    /** @return array{kind: string, eyes?: int, nose?: int, mouth?: int, hands?: int, color?: int, n?: int, source: string}|null */    private static function at(string $s, int $i, int $len, int $images): ?array    {        if ($len - $i < 3) {            return null;        }        $e = strpos(self::EYES, $s[$i]);        if ($e !== false) {            $n = strpos(self::NOSES, $s[$i + 1]);            $m = strpos(self::MOUTHS, $s[$i + 2]);            if ($n === false || $m === false) {                return null;            }            $extra = 0;            $color = self::DEFAULT_COLOR;            if ($len - $i >= 4 && ctype_digit($s[$i + 3])) {                $color = (int)$s[$i + 3];                $extra = 1;            }            $hands = 0;            if ($len - $i >= 5 + $extra && $s[$i + 3 + $extra] === '#' && in_array($s[$i + 4 + $extra], ['c', 'd'], true)) {                $hands |= $s[$i + 4 + $extra] === 'd' ? 1 : 2;                $extra += 2;                if ($len - $i >= 4 + $extra && in_array($s[$i + 3 + $extra], ['c', 'd'], true)) {                    $hands |= $s[$i + 3 + $extra] === 'd' ? 4 : 8;                    $extra += 1;                }            }            return ['kind' => 'face', 'eyes' => $e, 'nose' => $n, 'mouth' => $m, 'color' => $color, 'hands' => $hands,                'source' => substr($s, $i, 3 + $extra)];        }        $c = $s[$i];        if (($c !== '#' && $c !== '@') || !ctype_digit(substr($s, $i + 1, 2))) {            return null;        }        $nn = (int)substr($s, $i + 1, 2);        if ($c === '#' && $nn < self::BIG_COUNT) {            $color = in_array($nn, [13, 14], true) ? 1 : self::DEFAULT_COLOR;            $length = 3;            if (in_array($nn, [2, 4, 13, 14], true) && $len - $i >= 4 && ctype_digit($s[$i + 3])) {                $color = (int)$s[$i + 3];                $length = 4;            }            return ['kind' => 'big', 'n' => $nn, 'color' => $color, 'source' => substr($s, $i, $length)];        }        if ($c === '@' && $nn < $images) {            return ['kind' => 'image', 'n' => $nn, 'source' => substr($s, $i, 3)];        }        return null;    }}