| @@ -0,0 +1,44 @@ | |||
| export default class | |||
| CommonModule | |||
| { | |||
| static | |||
| isWide (chr) | |||
| { | |||
| return chr.match (/[^\x01-\x7f]/) !== null; | |||
| } | |||
| static | |||
| lenByFull (str) | |||
| { | |||
| return str.split ('').map (c => this.isWide (c) ? 1 : .5) | |||
| .reduce ((a, c) => a + c); | |||
| } | |||
| static | |||
| indexByFToC (str, index) | |||
| { | |||
| let i = 0; | |||
| let work = ''; | |||
| for (let c of str) | |||
| { | |||
| work += c; | |||
| if (this.lenByFull (work) > index) | |||
| break; | |||
| i += 1; | |||
| } | |||
| return i; | |||
| } | |||
| static | |||
| midByFull (str, start, length) | |||
| { | |||
| const trimmedLeft = str.slice (this.indexByFToC (str, start)); | |||
| return trimmedLeft.slice (0, this.indexByFToC (trimmedLeft, length)); | |||
| } | |||
| } | |||
| @@ -22,12 +22,18 @@ | |||
| </head> | |||
| <body> | |||
| <?php if (!($available)): ?> | |||
| <div class="alert alert-danger" role="alert"> | |||
| <strong>【警報】</strong>AIニジカが落ちてるぬ゛〜゛ん゛(泣) | |||
| </div> | |||
| <?php endif ?> | |||
| <div class="container my-5"> | |||
| <div class="accordion mb-5" id="accordion-filter"> | |||
| <div class="accordion-item"> | |||
| <h2 class="accordion-header"> | |||
| <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-filter" aria-controls="collapse-filter"> | |||
| 絞り込みてすと | |||
| 絞り込み | |||
| </button> | |||
| </h2> | |||
| @@ -124,9 +130,9 @@ | |||
| $length, | |||
| true) | |||
| as $record): ?> | |||
| <div class="mb-4"> | |||
| <div class="mb-4 message-block"> | |||
| <div> | |||
| <?= $record['date_time'] ?> | |||
| <span class="message-block-dt"><?= $record['date_time'] ?></span> | |||
| </div> | |||
| <div> | |||
| @@ -135,11 +141,11 @@ | |||
| </div> | |||
| <div style="color: blue"> | |||
| > <span style="font-style: italic"><?= $record['chat_message'] ?></span> | |||
| > <span class="message-block-chat" style="font-style: italic"><?= $record['chat_message'] ?></span> | |||
| </div> | |||
| <div> | |||
| <?= $record['answer'] ?> | |||
| <span class="message-block-answer"><?= $record['answer'] ?></span> | |||
| </div> | |||
| </div> | |||
| <?php endforeach ?> | |||
| @@ -7,6 +7,11 @@ const LOG_PATH = './log.txt'; | |||
| $log_data = []; | |||
| exec ("(ps -Af | grep -e '^miteruzo' | grep 'python3 main.py') && (ps -Af | grep -e '^miteruzo' | grep 'obs')", | |||
| $output, $exit_code); | |||
| $available = $exit_code === 0; | |||
| unset ($output, $exit_code); | |||
| $page = (int) ($_GET['p'] ?? 1); | |||
| $length = (int) ($_GET['max'] ?? 20); | |||
| $asc = ($_GET['asc'] ?? 0) != 0; | |||
| @@ -78,6 +78,15 @@ Script | |||
| $.cookie ('expand-filter', '0'); | |||
| }); | |||
| $ ('.message-block').on ('click', function () | |||
| { | |||
| const dt = $ (this).find ('.message-block-dt').text (); | |||
| const chat = $ (this).find ('.message-block-chat').text (); | |||
| const answer = $ (this).find ('.message-block-answer').text (); | |||
| window.open (`./talk.php?dt=${dt}&chat=${chat}&answer=${answer}`); | |||
| }); | |||
| if ($.cookie ('expand-filter') === '0') | |||
| { | |||
| $ ('#collapse-filter').removeClass ('show'); | |||
| @@ -0,0 +1,32 @@ | |||
| /* vim:set tabstop=2 softtabstop=2 expandtab :*/ | |||
| @font-face | |||
| { | |||
| font-family: 'Nikumaru'; | |||
| src: url(./assets/nikumaru.otf); | |||
| } | |||
| html, body | |||
| { | |||
| overflow: hidden; | |||
| } | |||
| body | |||
| { | |||
| background-color: black; | |||
| } | |||
| #canvas | |||
| { | |||
| width: 100%; | |||
| height: 100%; | |||
| padding: 0; | |||
| margin: 0; | |||
| overflow: unset; | |||
| border: none | |||
| !important; | |||
| outline: none | |||
| !important; | |||
| display: block; | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| <!-- vim:set tabstop=4 softtabstop=4 expandtab :--> | |||
| <!DOCTYPE html> | |||
| <html lang="ja"> | |||
| <head> | |||
| <meta charset="UTF-8" /> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <script src="//code.jquery.com/jquery-3.3.1.min.js"></script> | |||
| <link rel="stylesheet" href="./talk.css?<?= filemtime ('./talk.css') ?>" /> | |||
| <link rel="preconnect" href="./assets/nikumaru.otf" /> | |||
| <title>あほ</title> | |||
| <input type="hidden" id="dt" value="<?= $dt ?>" /> | |||
| <input type="hidden" id="chat" value="<?= $chat ?>" /> | |||
| <input type="hidden" id="answer" value="<?= $answer ?>" /> | |||
| </head> | |||
| <body> | |||
| <canvas id="canvas"></canvas> | |||
| <script src="./talk.js?<?= filemtime ('./talk.js') ?>" type="module"></script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,136 @@ | |||
| import CommonModule from './common_module.js'; | |||
| class | |||
| Talk | |||
| { | |||
| static | |||
| main () | |||
| { | |||
| const canvas = new Canvas; | |||
| window.onresize = () => canvas.resize (); | |||
| } | |||
| } | |||
| class | |||
| Canvas | |||
| { | |||
| constructor () | |||
| { | |||
| this.canvas = $ ('#canvas'); | |||
| this.ctx = this.canvas[0].getContext ('2d'); | |||
| (this.bg = new Image ()).src = './assets/bg.jpg'; | |||
| this.bg.onload = () => this.resize (); | |||
| (this.nizika = new Image ()).src = './assets/nizika.png'; | |||
| this.nizika.onload = () => this.resize (); | |||
| (this.talking = new Image ()).src = './assets/talking.png'; | |||
| this.talking.onload = () => this.resize (); | |||
| (new FontFace ('Nikumaru', 'url(./assets/nikumaru.otf)')).load ().then ( | |||
| () => this.resize ()); | |||
| } | |||
| resize () | |||
| { | |||
| this.canvas[0].width = $ (window).width (); | |||
| this.canvas[0].height = $ (window).height (); | |||
| this.redraw (); | |||
| } | |||
| redraw () | |||
| { | |||
| this.putBG (); | |||
| this.putText (0, 0, 15, $ ('#dt').val ()); | |||
| this.putImage (this.nizika, 370, 260, 1.1); | |||
| this.putImage (this.talking, 0, 0, 640 / 1024); | |||
| this.putText (75, 43.75, 20, | |||
| ('> ' + ((CommonModule.lenByFull ($ ('#chat').val ()) <= 21) | |||
| ? $ ('#chat').val () | |||
| : (CommonModule.midByFull ($ ('#chat').val (), 0, 19.5) | |||
| + '...'))), | |||
| undefined, undefined, | |||
| true); | |||
| this.putText (62.5, 93.75, 31.25, | |||
| ((CommonModule.lenByFull ($ ('#answer').val ()) <= 16) | |||
| ? $ ('#answer').val () | |||
| : CommonModule.midByFull ($ ('#answer').val (), 0, 16)), | |||
| 'Nikumaru', '#c00000'); | |||
| if (CommonModule.lenByFull ($ ('#answer').val ()) > 16) | |||
| { | |||
| this.putText (62.5, 125, 31.25, | |||
| ((CommonModule.lenByFull ($ ('#answer').val ()) <= 32) | |||
| ? CommonModule.midByFull ($ ('#answer').val (), 16, 16) | |||
| : (CommonModule.midByFull ($ ('#answer').val (), 16, 14.5) | |||
| + '...')), | |||
| 'Nikumaru', '#c00000'); | |||
| } | |||
| } | |||
| putBG () | |||
| { | |||
| const [x, y, zoom] = this.convertPosition (0, 0); | |||
| const width = this.bg.height * 4 / 3; | |||
| const height = this.bg.height; | |||
| this.ctx.drawImage (this.bg, | |||
| (852 - 640) / 2, 0, width, height, | |||
| x, y, width * zoom, height * zoom); | |||
| } | |||
| putImage (image, x, y, zoom = 1) | |||
| { | |||
| let zoom2 | |||
| [x, y, zoom2] = this.convertPosition (x, y); | |||
| zoom *= zoom2 | |||
| this.ctx.drawImage (image, | |||
| 0, 0, image.width, image.height, | |||
| x, y, image.width * zoom, image.height * zoom); | |||
| } | |||
| putText (x, y, size, text, style = 'sans-serif', colour = 'black', | |||
| italic = false) | |||
| { | |||
| let zoom; | |||
| [x, y, zoom] = this.convertPosition (x, y); | |||
| size *= zoom; | |||
| this.ctx.font = `${italic ? 'italic ' : ''}${Math.trunc (size)}px ${style}`; | |||
| this.ctx.fillStyle = colour; | |||
| this.ctx.textBaseline = 'top'; | |||
| this.ctx.fillText (text, x, y); | |||
| } | |||
| convertPosition (x, y) | |||
| { | |||
| const sizeX = this.canvas.width (); | |||
| const sizeY = this.canvas.height (); | |||
| const width = this.bg.height * 4 / 3; | |||
| const height = this.bg.height; | |||
| const vertical = sizeY / sizeX > height / width; | |||
| const zoom = vertical ? (sizeX / width) : (sizeY / height); | |||
| const baseX = vertical ? 0 : ((sizeX - width * zoom) / 2); | |||
| const baseY = vertical ? ((sizeY - height * zoom) / 2) : 0; | |||
| return [baseX + x * zoom, baseY + y * zoom, zoom]; | |||
| } | |||
| } | |||
| $ (() => Talk.main ()); | |||
| @@ -0,0 +1,11 @@ | |||
| <?php | |||
| if ($_SERVER['HTTP_HOST'] === 'nizika.monster') | |||
| header ('location: //nizika.tv/talk.php'); | |||
| $dt = htmlspecialchars ($_GET['dt']); | |||
| $chat = htmlspecialchars ($_GET['chat']); | |||
| $answer = htmlspecialchars ($_GET['answer']); | |||
| require_once './talk.frm.php'; | |||