最近の JS 遊び … 中国語判定、初期表示、マルコフ連鎖による文章生成、ギタータブ譜生成など

最近の Web フロントエンド、Node.js 関連の成果物を一気に紹介。

目次

Detect Chinese

文字列が「中国語かどうか」をゆるく判定するスクリプト。

過去に作りかけのコードを放置していたので、とりあえず npm パッケージ化してみた。

そしたらプルリクをいただいて、Unicode 抽出部分のバグ取りや TypeScript 定義などを追加いただいた。スバラシキ!

npm パッケージとしてグローバルインストールすれば CLI ツールとして、Node.js スクリプトから require() したりして、あとはブラウザ上でも動作するようにと、UMD 形式で作ってあって、型定義は type.d.ts で提供している。ブラウザ上で動作するデモは以下をドウゾ。

Convert Encoding New Line

テキストファイルの文字コードや改行コードを判定したり、変換したりする Node.js 製の CLI ツール。コチラも npm パッケージとして公開できるようにした。

CLI としてのオプション部分は Commander でパースしてサボった。

文字コードや改行コードは Bash 標準の file コマンドなんかでも確認できるし、nkf コマンドをインストールすればコマンドラインでの変換もできる。別段新しいツールではないのだが、自分が使いたいように使えるコマンドがなかったので、とりあえずデッチ上げた。

今回は encoding-japanese というパッケージのみ利用しているが、それ以外は利用していない、JavaScript コードのみで動いている。なので、もしかしたらやろうと思えばブラウザ上でファイルアップロードしたら変換後のファイルをダウンロードさせる、みたいなスクリプトにもできるかもしれない。でも今回は面倒臭いので CLI ツールとしてのみ動くようにして作っている。w

Watch Events

windowdocument、Element などが持っているイベントの一覧を見てみたくて作った。

それぞれのオブジェクトから、on から始まるプロパティ名を拾ってきて、それらを利用して addEventListener() を仕込んでいるだけ。click とか mouseenter とか wheel (マウスホイールイベント) とか、対象のイベントが発火したらそのイベント名を赤くハイライトしている。

そういえば DOMContentLoaded イベントはプロパティとしては存在していないのね。document.onDOMContentLoaded みたいなプロパティは存在しなかったようで、デモページの一覧に出てきていない。

Huge Initial Load

ページを初期表示する際に、重たい同期処理が行われたら、画面描画がどんな風になるのかを調べるため作った。いわゆる「JavaScript はシングルスレッドで動く」という、イベントループの話である。

僕は普段、head 要素内に script 要素を書くが、その場で処理を実行させることはなくて必ず document.addEventListener('DOMContentLoaded') の中で処理を開始するようにしている。つまり、HTML 全体の読み込みは終わって、画面初期表示が終わってから JavaScript コードが動くはずなのだが、重たい同期処理があると HTML 部分だけの初期表示が行われず、真っ白な画面がしばらく続き、同期処理の完了とともに初期表示がなされる、という挙動をしていた。

また、初期表示時に API をコールしてデータを取得する、というような非同期処理が発生する場合もあると思う。そのような非同期処理と、SPA フレームワークなどが大量の DOM 操作をするとか、JS 内で計算処理をするとかで、時間のかかる同期処理が重なる場合もあるだろう。そういう時に、どんな風に画面が初期表示されるのか、どういう書き方をしたらファーストビューが速く、全体的な処理完了までが速いのかを調べてみた。

このデモページでは、「for ループを大量に回す同期関数」と「setTimeout で待機してから resolve する非同期関数」の2つを用意してある。そしてそれらを直列で実行したり、並列実行してみたりしている。同期関数の方は Promise でラップしてみたり、setTimeout でラップしてみたりして、どういう変化があるか確認できるようにした。

デモページの中断で、そうしたパラメータを指定してページを開けるようにしている。いつものようにクエリ文字列でそうしたオプション指定ができるようにしてある。直列実行の順番とか、Promise.all() の配列に関数を列挙する順番とかも指定できるので、どういうコードの書き方をしたらどういう画面描画になるのか、というのを、開発者コンソールの出力やソースコードなども見ながら確認してみてほしい。

自環境で目視確認したところ、概ね次のような結果になった。

というワケで、「とりあえず HTML 部分だけは先に表示させてしまいたい」のであれば、重たい同期関数を setTimeout でラップすれば良いことが分かった。時間は 0 でも問題ない。

そして、そういう同期処理と非同期処理を並列実行させようとしても、シングルスレッドで動作する JavaScript の世界ではこれらを同時に・並列して動かすことはできないので、タイミング次第では結局非同期関数の処理がブロックされる。なので Promise.all() を使ったりして並列化しても意味はない。

それよりは、非同期関数を先に呼び出しておき、同期関数の開始タイミング (setTimeout の値) を 0 ではなく、もう少し大きな数値にして開始タイミングを遅らせてしまうことで、先に非同期関数の処理が全て終わって画面描画が終わり、それから同期関数の処理が開始する、という順番にしておいた方が、「何も表示されない真っ白な画面」が映る時間は短くできると思う。

XREA に MovableType・WordPress・MediaWiki を置いた

XREA サーバは昔から CMS をワンクリックでインストールできるツールが管理画面に用意してあったのだが、今回改めてそれをやってみた。試したのは neo.s58.xrea.com というスペースにて。

管理画面で MySQL データベースを作る。XREA Free の場合はデータベース名は選べず、アカウント名と同じデータベース名となる。

あとは管理画面で CMS をインストールする。「WordPress」「MovableType」「Xoops X」の3つがインストールできるのだが、Xoops X については PHP のワーニングが絶えず表示されていて使い物にならなさそうだったので断念した。インストール時にサブディレクトリ名を指定するので、WP と MT を両方インストールしたりもできる。

インストールが終わったら、それぞれのパスにアクセスすることで初期設定が始められる。WordPress は色々イジった経験があるけど、MovableType は超久々だった。管理画面からブログを複数個作れるようで面白いね。それぞれの UI が2000年代初頭の感じがあって懐かしい。w

MediaWiki については、XREA サーバに SSH 接続して、自分でインストールした。

# SSH 接続した直後は cd コマンドなどが制限されているので、その場合はさらに Bash を起動すれば回避できる
$ bash

$ cd ./public_html/
# MediaWiki の最新版をダウンロードする。64MB 程度だった
$ wget https://releases.wikimedia.org/mediawiki/1.38/mediawiki-1.38.2.zip
$ unzip ./mediawiki-1.38.2.zip
$ mv mediawiki-1.38.2 mediawiki

というワケで、/mediawiki にアクセスすると初期設定ウィザードが開く。各種設定を勧めていくと、LocalSettings.php というファイルがダウンロードできる。コレを ~/public_html/mediawiki/ 直下に配置 (scp コマンドでアップロードするなど) すれば初期設定完了。

あ、この際、「サイト設定」で設定されている PHP のバージョンが php70 などの古いバージョンだと、インストール要件を満たしていなくてうまく動かなかったりする。自分は php74 を選択して数分待ってリトライすることでうまく行った。

あとはアカウント名にハイフンが入っていると、各種アプリケーションが MySQL のデータベース名としてうまく認識できずにセットアップが失敗するので、その場合はアカウント名を変更するしかない。以下のヘルプページを参考に申請をすれば、アカウント名を変更してもらえる場合がある。

あと、MediaWiki の方はどういうワケか XREA の自動挿入広告が入らなかったので、自分で挿入した。見様見真似でテンプレートをイジって対処。

/* 次の2つの要素についてプロパティ値を変更して、画面上部に隙間を開けた */
#mw-head-base {
  margin-top: -5em;  /* 元の値は -1em */
}
#mw-head {
  top: 0;  /* 元の値は 4em */
}
<div id="mw-head-base" class="noprint"></div>
<!-- 元のコードは ↑ のようになっている。コレを以下のように変更した -->

<div id="mw-head-base" class="noprint">
  <div id="xrea-ad" style="position: absolute; top: 0; right: 0;">
    <!--nobanner-->
    <script type="text/javascript" src="https://cache1.value-domain.com/xa.j?site=【アカウント名】.s0.xrea.com"></script>
  </div>
</div>

XREA の広告コードは、管理画面 (コントロールパネル) の「契約情報」より取得できる。

コレで、MediaWiki の画面右上に XREA の広告を配置できて、XREA Free の規約も守れただろう。

作ってみたかっただけで、これらのブログに何か書く予定もないし、Wiki を書いていくつもりもあまりない。w

マルコフ連鎖 npm パッケージの比較

以前、Python と MeCab を使ってマルコフ連鎖による文章自動生成を試したことがあった。

その時は MeCab というライブラリを使って分かち書きをしていた。

日本語の場合、普段は分かち書きをしないのだが、マルコフ連鎖プログラムに文章を投げ込む時は分かち書きをしておくことでより自然な文章が生成できる。

そこで今回は、主に JS オンリーで分かち書きとマルコフ連鎖処理を実現できそうな npm パッケージをいくつか探して試してみた。

結果やソースコードなど一式は ↑ に置いた。

夏目漱石の「吾輩は猫である」をインプットテキストにしたのが良くなかったか、やはり分かち書きの精度は MeCab が一番高いかもしれない。以下にそれぞれのスクリプトを複数回実行して出力した文章を貼っておく。

「アハハハハハハハもうたいていても好いから、この講話を真面目になったものではない。多々良三平のような
「君も乱暴狼藉の練修に余念のない男である。第一からかうのは本当の人間の命と云う事に極めたかを逐一かい
「ええこれなざあ面白いんでしまったが、まだ主人夫婦の間、嫁姑の間に截然たるかたで行くつもりに違ないで
「しかしよくありますよ」と主人は例のごとく眉の根にちょっと逃げ出すかも知れぬことをいってすましてね。
「すべての安楽は困苦を通過せざるを得ない。誰も聞きます。始めはちょっとボール即ち攻城的砲術である。鼻
どこへ行ったり、酢に漬けて見なすったんだわ。
御嬢さまも、止せと云って出られっこないよ。
ああ奇麗に光っているはずだと気がついたと云わば吾輩猫などになる工夫はあるときは、この点に少しは淋しいだろうじゃないけども、淑徳婦人会の連発にて当分の間我慢して自己の災なるのみならずに上った女は罪な者でも利きましょう」とちゃらちゃら鳴らして見せるから安心したフロックコートを至急送れと云う句を細君に食っている小児ですらこのあき地、とか何とか威張っているものの実際はやはり実験室で珠を磨いてるのかい。
苦労と心配した覚はございませんでしたら小使でもよろしゅうございますので……」はてねと御相談なさるものですから能く聞き給え」と顔を洗いに風呂場へ行ったって食い物に不自由はした昨夜のごとく黄を含める淡灰色に漆のごときはまさしくその現象のあらわれる時代です、先生に御願があって来たら自分の顔を即席にこしらえて見るのです……つまり身振りがあまり見事に焼けてい給えと云ったって、先は商売だから恐縮しまさあ。
時に寒月はどうだい苦沙弥諸先生の御名論をやりますと云い放った。
はてな何でも同じ事を四五遍繰り返したのである。
書生がまた迎に来てくれるかと考え付いた。
そこを我慢してやって見た。
眼をねぶって運を天に任せていなかったから別段恐しいとも思わなかったから眼をねぶって運を天に任せていた。
のみならず顔の真中があまりに突起して無理やりに這って行く。
しかし今に主人はいつでも大きな奴が平生の領分は吃驚して仕事を提げている士官も申されたでしょう。
こんな大きな木板でまんまと首尾よくまた。
たしかに分配していると一般の話をしているところが総身のは、自己の鼠でも有んなさるの事だけ聞いたら大変儲かったの鼻のはどう云う感じているごとくえらくも、この春朗読会へ引越すまんからんね ]  その人に呼びつけて 。
人間である以上は君、今日のうち、惜気も挫しぐ上げたら直ぐ飛び出す用意であるものだ 君なかから少し馬鹿竹にはそうは樟脳をつづけた。
口髯をしないがある。

今回改めて思ったけど、「しゅうまい君」のような面白い文章を自動生成させるには、インプットとなる文章の量とバリエーションが必要だ。小説なんかを利用してもあまり面白い文章は出てこない。マルコフ連鎖処理よりも、投入するテキストをいかにクロールするか、という方が大事そうだ。w

Tab2Tab

自分はギターのタブ譜を書く時に、Guitar Pro とか Power Tab とかは使わず、プレーンテキストでメモしている。MIDI として再生する予定もなく、完全に個人のメモなので、プレーンテキストの方がソフトに依存しなくて気楽なのである。ただ、プレーンテキストでタブ譜を書くとなると、ハイフン記号などで位置を調整するのがダルい。

そこで、執筆する際は「Excel 方眼紙」で1マスごとにフレット数などを書き込んで行くことにし、それをプレーンテキストとして変換するためのツールを作ったという次第。

例によって UMD 形式で作ったので、Node.js CLI ツールとして使えたり、require() できたり、ブラウザ上でも動くようにしてある。完全にオレオレなパッケージ。w

Neo's Validator

JS で実装している時によくやるバリデーション系の関数をまとめておきたいなーと思って npm パッケージ化してみた。

TypeScript ベースで作っておいて、CommonJS (CJS・Node.js の require() で読み込める形式) と UMD (ブラウザの window 配下にプロパティが生える昔ながらの方式)、あと ESModules (ESM・<script type="module"> から import で読み込める方式) の3つの方式でビルドするようにした。

作ってる途中で飽きて放置してたのだが、とりあえずパッケージとして公開するところまでやった。多分このまま放置かな。w


以上。こんなモノを作って遊んでおりました。