Difference between notices #2

まず diff を取る対象はマークアップレベルのテキストにした。ただし a 要素の href 属性以外は全て削ぎ落とされる。

また diff ライブラリとしては jsdifflib を利用するようにした。行単位の比較だし、diff 本体とプレゼンテーションのためのコードが分離しているのも都合がいいし、10KB ちょいとコンパクト。

というわけで注意書きの変更を検知すると、window.alert() でその旨を通知したのち自動的に注意書きパネルが開き、以下のように変更点が表示される:

多分初見で意味が分かると思うけど、赤背景で打ち消し線が引いてあるのが変更後に削除された行、緑背景が追加された行だ。

Difference between notices

ふたばの画像掲示板では送信フォームの下部に様々な注意書きを表示しているが、赤福プラスはロードした際にその注意書きの内容を覚えて保持しており、n キーを押すとパネルに注意書きが表示される。

さて、リロードした際にリロード前後の注意書きの内容を比較して、差異があったらそれを通知したい。できれば加えて、何が増えたのか減ったのかをグラフィカルに表示したい。つまり、diff の結果を表示したい。ところが、この diff のアルゴリズムというのは簡単なように見えて恐ろしく難しくて、それだけで1本論文が書けちゃうほどのものだったりする。それを真面目に実装するのも大変だし、既存のライブラリを組み込むにも、下手すると赤福プラス本体よりデカいとかありうる。

さらに、diff 自体の難しさと並行して、diff の対象をマークアップの段階にするか、人が読んで意味を取れる文章の段階にするかという問題もある。前者にすると、html マークアップ内の属性の変化も検知できるが、それがユーザが欲してる情報かどうかはやや疑わしい。例えば Chrome に ScriptSafe というエクステンションを入れると副作用としてありとあらゆる要素に ss-なんちゃらという属性を付けて回るが、この変化も検知してしまう。しかしこれはユーザにとってはどうでもいい情報だ。それを通知されても困る。

一方で後者、つまり各要素の内容だけをフィルタした結果にすると、例えば a 要素の href 属性と言った、割と重要な情報も見逃してしまう恐れがあるのである。どちらに振っても良い結果にならない。

どうするか。

From crying 16yo to grieving 18yo #2

以前 xubuntu を 16.04 から 18.04 にアップグレードして以来、LightDM のログインスクリーンでパスワード打ち込んでもログインできず、オンスクリーンキーボードを経由せざるを得ない現象が発生してとても不便。

/var/log/lightdm/lightdm.log を見ていたら、起動時に読み込まれる設定ファイルの中に /usr/share/lightdm/lightdm.conf.d/50-xubuntu-numlock.conf というものがあった。はーなるほど numlock がかかってるのね。

ちなみにこの設定ファイルは xubuntu-numlockx を呼び出しているだけなのだが、このファイルは /etc/X11/Xsession.d/55numlockx への薄いラッパでしかない。 55numlockx はシェルスクリプトで、/etc/default/numlockx に記述されている設定に従って X が起動する際の numlock キーの状態を変更するようだ。設定が auto の場合は実行マシンがラップトップかどうかとか、接続されているデバイスを見てよしなにしてくれる。

ということで、/etc/default/numlockx における設定を NUMLOCK=auto から NUMLOCK=off に変更。

不具合なってた理由はつまり、55numlockx 内で USB キーボードが接続されているかを検知し、接続されていたら有無を言わさず numlock をオンにするようになっているからだ。うちのキーボードは USB 接続だが、テンキーがないタイプなのであった。そのケースを想定していない。

 * * *

ところでこの PC は設定で10分放置するとディスプレイを消灯するようにしているのだが、実際にはそれが無視される状態が続いていた。関係あるのかないのかよく分からないのだが、上記の変更を施したらなぜかその設定も反映されるようになってしまった。なんで…?

一応記録のために書いておくと上記の変更の他に /etc/lightdm/lightdm.conf.d/10-xubuntu.conf も以下のような感じで変更してある:

--- 10-xubuntu.conf.old
+++ 10-xubuntu.conf
@@ -1,2 +1,2 @@
-[SeatDefaults]
+[Seat:*]
user-session=xubuntu

でもいずれにしても、電源周りには関係なさそうで、謎。まあ、いっか。

Kenya Television Network #3

というわけで私的な環境におけるキーバインディングを整理したのだが、実は赤福プラス自身も textarea においていくつか Emacs ライクなショートカットを定義している。

でもそもそもこれ、いるんだろうか。textarea にショートカットがたくさん用意されていないと死んでしまうような輩は、そもそも自分でシステムレベルで有効な環境を構築している気がする。

まああっても害になるものではないし、残しておくかな。とりあえず設定にそういったショートカットを有効にするかどうかの項目だけ追加しておくことにしよう。

ところで javascript から textarea 内のキャレット位置を変更するには、selectionStart/selectionEnd をいじる他に、Selection#modify() がとても有用なのだが…どうもこれ、Firefox だと全然動かないみたい。63.0 on Linux、63.0.1 on Windows10 の両方で動かない。リファレンスを自分とこで用意しておきながら動かないってどういうことなんだろう。

Kenya Television Network #2

よく調べてみたら、Chromeに残ってしまうEmacsぽいキーバインドはcVimのデフォルトの動作だった。なーんだ、そりゃ確かに自分で定義した覚えがないわけだ。記憶力ヤバくなかった。

しかしそうすると、テキスト入力系ウィジェットへの追加のキーバインディングが複数の箇所にまたがってるのがなんだか居心地が悪い。gtk のレベルで定義したほうがグローバルに使えるし、そっちに統一しようかな。

cVim が定義するバインディングは以下の通り。

<C-i> move cursor to the beginning of the line
<C-e> move cursor to the end of the line
<C-u> delete to the beginning of the line
<C-o> delete to the end of the line
<C-y> delete back one word
<C-p> delete forward one word
<C-h> move cursor back one word
<C-l> move cursor forward one word
<C-f> move cursor forward one letter
<C-b> move cursor back one letter
<C-j> move cursor forward one line
<C-k> move cursor back one line

この内多用しているのは <C-i>、<C-e>、<C-u>、<C-f>、<C-b>、<C-j>、<C-k> くらい。これらを gtk のバインディングに移植すればいい。そして、cvimrc 側では [cci]iunmapAll[/cci] して、テキスト入力系ウィジェットへのバインディングをすべて削除する。

さて次に gtk 側で、自前のバインディングを定義する。

$ cd ~
$ mkdir .themes
$ cd .themes
$ cp -r /usr/share/themes/Emacs .
$ mv Emacs MyBindings

てな感じでホームに Emacs の定義をコピーし、必要な箇所をいじる。ところで gtk と言っても 2.0 系と 3.0 系があり、アプリケーションがどちらのバージョンのライブラリを参照しているかは傍目にはよく分からない(新しめのアプリケーションはまあ 3.0 系と考えていいんだろうけど)。Chrome の場合は

  • /usr/bin/google-chrome (/etc/alternatives/google-chrome へのシンボリックリンク)
  • → /etc/alternatives/google-chrome (/usr/bin/google-chrome-stable へのシンボリックリンク)
  • → /usr/bin/google-chrome-stable (/opt/google/chrome/google-chrome へのシンボリックリンク)
  • → /opt/google/chrome/google-chrome (シェルスクリプトであり、/opt/google/chrome/chrome を exec する)

つまり最終的に実行される実行形式は /opt/google/chrome/chrome なので、これを ldd にかける:

$ ldd /opt/google/chrome/chrome | grep gtk
libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f99fa5cc000)

chrome は gtk-3.0 の方のバインディングを参照することが確認できた。ということでまずは MyBindings/gtk-3.0/gtk-keys.css をいじろう。このファイルは例えば

@binding-set gtk-emacs-text-entry
{
bind "b" { "move-cursor" (logical-positions, -1, 0) };
bind "b" { "move-cursor" (logical-positions, -1, 1) };
bind "f" { "move-cursor" (logical-positions, 1, 0) };
bind "f" { "move-cursor" (logical-positions, 1, 1) };

bind "b" { "move-cursor" (words, -1, 0) };
bind "b" { "move-cursor" (words, -1, 1) };
bind "f" { "move-cursor" (words, 1, 0) };
bind "f" { "move-cursor" (words, 1, 1) };

bind "a" { "move-cursor" (paragraph-ends, -1, 0) };
bind "a" { "move-cursor" (paragraph-ends, -1, 1) };
bind "e" { "move-cursor" (paragraph-ends, 1, 0) };
bind "e" { "move-cursor" (paragraph-ends, 1, 1) };

bind "w" { "cut-clipboard" () };
bind "y" { "paste-clipboard" () };

bind "d" { "delete-from-cursor" (chars, 1) };
bind "d" { "delete-from-cursor" (word-ends, 1) };
bind "k" { "delete-from-cursor" (paragraph-ends, 1) };
bind "backslash" { "delete-from-cursor" (whitespace, 1) };

bind "space" { "delete-from-cursor" (whitespace, 1)
"insert-at-cursor" (" ") };
bind "KP_Space" { "delete-from-cursor" (whitespace, 1)
"insert-at-cursor" (" ") };
/*
* Some non-Emacs keybindings people are attached to
*/
bind "u" { "move-cursor" (paragraph-ends, -1, 0)
"delete-from-cursor" (paragraph-ends, 1) };

bind "h" { "delete-from-cursor" (chars, -1) };
bind "w" { "delete-from-cursor" (word-ends, -1) };
}

entry {
-gtk-key-bindings: gtk-emacs-text-entry;
}

という感じになっていて、@binding-set で様々なバインディングを定義し、entry { -gtk-key-bindings: [name] } で割り当てる。binding-set に与える名前はそのスコープがよくわからないが、まあユニークなものにしておいたほうがいいんじゃないかな? 一方 entry (GtkEntry) は 1行入力のウィジェットのこと、textview (GtkTextView) は複数入力のウィジェットのことだ。それぞれに対して呼び出せる move-cursor や delete-from-cursor などの定義は以下を参照:

さて cVim のバインディングを持ってくる際、いくつか衝突する箇所がある。

  • cVim での ^U はカーソルから前方へ、行頭までを削除する。gtk ではカーソル行が位置する物理行全体を削除する。どちらを採るべきか?
  • cVim ではカーソルを行頭へ移動するのは ^I だが、gtk では Emacs にそのまま倣って ^A である。おそらく cVim は ^A = 全選択というジェネリックな UI に妥協して ^I に移動させたんだと思う。どちらを採るべきか? ちなみに gtk の Emacs バインディングでは全選択は ^/ で行える

そんなこんなをアレコレしたら、同じような変更を gtk-2.0/gtkrc にも施したら設定エディタの Gtk -> keyThemeName に新しく作ったテーマ名を与える。または、端末から
$ xfconf-query -c xsettings -p /Gtk/KeyThemeName -s MyBindings
な感じ。xfce ではないデスクトップ環境の場合は
$ gsettings set org.gnome.desktop.interface gtk-key-theme MyBindings
でいいと思うけどよく知らない。

Kenya Television Network

GTK の環境に対して定義できる設定のうち、[cci]gtk-key-theme-name[/cci] というものがあって、主にテキスト入力系のウィジェットのキーバインディングを変更することができる。で、これを [cci]Emacs[/cci] にすると、どのアプリケーションでもまあだいたいそんな感じの振る舞いをしてくれる。

これらのバインディングは [cci]/usr/share/themes/Emacs/[/cci] 以下の gtk2.0 と gtk3.0 用のそれぞれのファイルで定義される。[cci]gtk-key-theme-name[/cci] 自体はどこで定義するのかと言うと……実は複雑すぎてよく分からないのだが、Xfce であれば設定マネージャから設定エディタを呼び出して xsettings チャネルの Gtk -> KeyThemeName プロパティの値を Emacs にすればいい。

ただ、実に不思議なことがある。Chrome の各種のテキスト入力系ウィジェットも当然この影響を受けるのだけど、KeyThemeName プロパティを空に戻してもどういうわけか Emacs バインディングが残ったままになる。他のアプリケーションからは Emacs バインディングはスパッと消え失せるのだけど。Chrome 自身が独自にどこかにキャッシュしているの?

それと、うちの環境だと ^P ^N の代わりに ^K ^J でキャレットが上下方向に移動する。多分これ、自分でそうなるように定義したと思うのだけど(/usr/share/themes/Emacs/ 以下のファイルにはそういう定義はないので)、ホームのどこでそれを定義したのか全然覚えてない。定義したのかどうかも覚えてない。記憶力の危険が危ない。

Daikuuji

たまに虹裏では複数の「」が1文字ずつレスをして大空寺あゆ(・▽・)萌えっ!と叫んだり叫ばなかったりする。

で、完成すると(あるいは完成しなくても)適当なタイミングで「○○完成してるの初めて見た」的な締めになるわけだが。この時の○○が、せいぜい10文字程度ならその場で打ち込むのだけど、もっと長い何十にも続いたレス群の場合に困るのである。いちいち1文字ずつコピペで持ってくるのも面倒だし、さりとて打ち込み直すとタイポをしてしまう可能性がある。

というわけで

このように対象になる箇所を大胆に選択した後、出てきたメニューのうち「大!空!寺!」を選ぶ。すると、日付など必要のない部分はスキップし、コメント部分だけを抜き出した上で連結し、コメント欄に出力する。

収集されるテキストは、コメントを格納する div の直接の子供であるテキストノードのみである。エレメントノードは無視される。引用された行も span に囲まれているので、収集の対象にはならない。コメント部分が複数行だった場合は改行は削除される。

Let the box be light

画像をクリックするとデフォルトで lightbox 的な振る舞いをするのだが、これにはいくつか積み残しになっている点があった。

まず、画像を原寸表示させた際。このモードではマウスのドラッグにより画像のスクロールが可能なのだが、ドラッグしたままポインタをブラウザ外に持っていき、そこでボタンを離したりすると状態管理の不整合が起きてドラッグしてないのに画像がポインタについて来るみたいなことになってしまう。

これはつまりポインタのキャプチャをしていないからなのだが、何故していないのかと言うと、正確には覚えていないけど多分 Presto Opera にその機能がなかったんだと思う。これを直したい。

次に、全体表示から原寸表示モードに移行した際に拡大の原点がつねに画像の中心なのが不便。たとえば漫画なんかを1枚絵にした画像の場合に、原寸表示にしたあと上端へスクロールさせなければならないのが煩わしい。これを解決したい。

最後に、スマホで撮った写真など、本来90度回転して表示されるべきものが、そうなっていない場合がある。これに対応するため、画像の回転機能が欲しい。

というわけで、上記を解決するために書き直した。

回転にも transition をかけたのでやたらスムーズにくるくる回るのが面白い。

2番めの問題を解決するために、従来は画像をクリックすると lightbox を抜ける動作をしていたのだが、新しいバージョンではクリックしたポイントを原点に拡大縮小するようになった。lightbox を抜けるには画像以外の部分をクリックする必要がある。これはちょっと優しくない仕様変更かもしれない。

それと、画像のドラッグ時にいわゆる bounce-back 機能をつけたのだけど。これをつけると Apple に訴えられるんでしたっけ? ヤバイのかな。

* * *

とかなんとかやってたらまたふたばが全滅してるし…。

Is it ok with such a deciding!?

お名前や、題名や、メール欄や、コメントの内容が規定の長さを超えていたら警告する処理が長年動いてなかったのを修正。それぞれの要素の内容がそれぞれ設定されている最大の長さを超えていた時、警告を点滅表示する。

この警告はなんかよく分からないのだが、10年以上前 Opera 版赤福プラスを作る際、いもげにスレを立てて警告メッセージとして何がいいか「」に伺った時に出てきたものだ。当初は欲張って3桁ゾロ目のレスを採ると言ったのだが、さすがに1/100の確率なのでなかなか出ない。そういうわけでしびれを切らして次のゾロ目にしますと書き込んだ直後のゾロ目のレスが「そんな決め方でいいのか!?」だった。確か。