アニヲタWiki(仮)のメニューにランダムリンクを追加するユーザースクリプト あるいは過去のランダムリンク機能について

[アニヲタWiki(仮)]は雑多なWikiで、暇つぶしの読み物に使える。
本家？Wikipediaより砕けた文体でパルプっぽく読み流せる。

少し時間が空いたときなどに[中国史](https://w.atwiki.jp/aniwotawiki/tag/%E4%B8%AD%E5%9B%BD%E5%8F%B2)、[映画](https://w.atwiki.jp/aniwotawiki/tag/%E6%98%A0%E7%94%BB)、[mtg](https://w.atwiki.jp/aniwotawiki/tag/MtG)、[SCP](https://w.atwiki.jp/aniwotawiki/tag/SCP%20Foundation)、[秀逸な項目](https://w.atwiki.jp/aniwotawiki/tag/%E7%A7%80%E9%80%B8%E3%81%AA%E9%A0%85%E7%9B%AE)とかを読んだりする。
アンサイクロペディアよりは読みやすさ含めまだ[臨](https://zhongdanhai.hatenablog.com/entry/2019/11/12/011122) [終](https://anond.hatelabo.jp/20200511004349)していないと思われる。

で、私の回遊を促す時間泥棒の役割として過去にWikiのメニューにはランダムリンクが存在していた。

ランダムリンクは三つ存在しており、それぞれ

* [バシルーラ](https://wikiwiki.jp/dqdic3rd/%E3%80%90%E3%83%90%E3%82%B7%E3%83%AB%E3%83%BC%E3%83%A9%E3%80%91)(ドラクエの敵を吹き飛ばす呪文)
* [ゆびをふる](https://w.atwiki.jp/aniwotawiki/pages/35899.html)(ポケモンのランダム技)
* [車田飛び](https://dic.pixiv.net/a/%E8%BB%8A%E7%94%B0%E9%A3%9B%E3%81%B3)(聖闘士星矢作者の表現技法の俗称)

という名前で、飛ばすのかランダムなのか飛ばされるのか微妙に覚えづらい三つの候補が毎回表示されていた。

いまでは読み込まれるためのページが各々残っているだけである。

[ランダムリンク](https://w.atwiki.jp/aniwotawiki/pages/10.html)
[ランダムリンク2](https://w.atwiki.jp/aniwotawiki/pages/8.html)
[ランダムリンク3](https://w.atwiki.jp/aniwotawiki/pages/5.html)

当時の私は
項目名が出ないのは開くまでのワクワクがあるし、訪問済みはリンク色が変わり三つあれば大抵どれかは非訪問だろうから捗るなー
などと思ったりしていた。

それがあるときからリンクが無くなり、ぼんやりとした回遊は「最近作成されたページ」か「最近更新されたページ」経由になり残念に思っていた。

具体的に無くなった日付は[2019/08/13](https://w.atwiki.jp/aniwotawiki/?cmd=backup&action=diff&pageid=2&num=4)でありもう結構立ちますね。
最初は調整中かな？と思ったものの復活の兆しが見えず、Wikiだけみている、コミュニティに詳しくない私は傍観するしかないのでした。

その後もたまーにランダムリンクについてググったりしていたのですが、
日がたち、たまたま削除理由を知ることができた。

[アニヲタWiki(仮)管理用総合相談所 提案所2 416レス目](https://jbbs.shitaraba.net/bbs/read.cgi/otaku/17534/1525774878/416)
> 416： 名無しさん ：2019/08/07(水) 13:29:36 HOST:p344048-ipngn200408sizuokaden.shizuoka.ocn.ne.jp
    左メニューにあるランダムリンクは削除した方がいいのではないでしょうか。
>
>    あれはソースを見ると分かる通り、候補のURLを列挙してその中からランダムにリンク先を選ぶ方式ですが、
    2500個までしか対応できないためか、3つのリンクで合わせても7500項目に候補が限定されています。
    更新もされていませんし、40000項目以上も記事もある現在では意義は薄いでしょう。
>
>    それでも無害なら放置しても良いのですが、左メニューに記載されているため
    各記事を読み込むたびに列挙してある7500個のURLも毎回読み込まれることになり、
    閲覧者やサーバーの通信量や負荷という面で実害はあるように思います。
>
417： 名無しさん ：2019/08/07(水) 18:39:07 HOST:p76ed56f8.kngwnt01.ap.so-net.ne.jp
    >>416
    あれそういう仕組みだったんですか……だったら削除に賛成
>
418： 名無しさん ：2019/08/07(水) 18:41:28 HOST:216.net119083066.t-com.ne.jp
    >>416
    そういうふうな作りだとしたら相当重たくなりますね。
    コレは管理者様に連絡をしたほうが良いかもしれません。
>
419： 名無しさん ：2019/08/07(水) 22:07:58 HOST:sp49-97-94-113.msc.spmode.ne.jp
    >>416
    仮に残しておくとするとランダムリンク専用のページを作ってそこから飛ばす感じになるのかね
    どのみち中途半端に残しておく位なら消してしまった方が良いとは思う

これを見つけたときはオイオイオイ、と思ったものである。

ひとつに、天下のWikiがランダム機能・記法すらなく手作業作成・保守でやらなければいけないという点。
もうひとつに、**各記事(ランダムリンクを作るために)を読み込むたびに列挙してある7500個のURLも毎回読み込まれる**というとんでもない~~クソ~~コードである点。
さらに(サーバー側のみかクライアント側もか不明だが)**各記事を読み込むたびに列挙してある7500個のURLも毎回読み込まれる**にもかかわらず快適に閲覧できている事実という点。(サーバーの通信量という単語からクライアント側処理の可能性が高い？)

困惑や驚愕などいろいろな情動を刺激された提案は反対意見もとくになく、上記のとおり13日に管理人によりリンクが削除された。

# atwikiにランダムリンク記法はあるのか

先に「天下のWikiがランダム機能・記法すらなく」などと思ってしまいましたが、atwiki[^`]自体がとんでもないコードを使っていてアニヲタwikiはその被害者の可能性があるかもしれません。

とにもかくにもささっと調べられそうなWiki機能から調査してみます。

[atwikiの機能一覧](https://w.atwiki.jp/guide/pages/264.html)から[ランダムリンク機能が三つあることがわかります](https://w.atwiki.jp/guide/pages/931.html#id_fcc967af)

aタグのテキストを指定する都合上、
`#random3()`
ではなく
`&random(ランダムにリンク表示)`
か
`#random2(wikiの編集例,＠ウィキの基本操作,新着プラグイン,更新情報,改善情報,text=お楽しみリンク)`
が当時の仕様に近しいことが実現できそうです。
>候補のURLを列挙して

とあるので`#random2`でしょうか。

ただし、この記事だけではリンク上限が2500であることや各ページを読み込む情報は得られませんでした。

# 7500個のURLも毎回読み込まれるランダムリンクコード

では実際にどのような記述がなされていたのか、コードをのぞいてみます。

[ランダムリンクのソースより](https://w.atwiki.jp/aniwotawiki/?cmd=backup&action=source&pageid=10&num=0)
>```js
#js(){{
var txt = new Array();
txt[0]="http://www49.atwiki.jp/aniwotawiki/pages/15.html";
txt[1]="http://www49.atwiki.jp/aniwotawiki/pages/18.html";
txt[2]="http://www49.atwiki.jp/aniwotawiki/pages/19.html";
txt[3]="http://www49.atwiki.jp/aniwotawiki/pages/20.html";
txt[4]="http://www49.atwiki.jp/aniwotawiki/pages/21.html";
txt[5]="http://www49.atwiki.jp/aniwotawiki/pages/22.html";
txt[6]="http://www49.atwiki.jp/aniwotawiki/pages/23.html";
txt[7]="http://www49.atwiki.jp/aniwotawiki/pages/24.html";
txt[8]=	"http://www49.atwiki.jp/aniwotawiki/pages/25.html";
txt[9]=	"http://www49.atwiki.jp/aniwotawiki/pages/26.html";
txt[10]=	"http://www49.atwiki.jp/aniwotawiki/pages/27.html";
txt[11]=	"http://www49.atwiki.jp/aniwotawiki/pages/28.html";
txt[12]=	"http://www49.atwiki.jp/aniwotawiki/pages/29.html";
txt[13]=	"http://www49.atwiki.jp/aniwotawiki/pages/30.html";
txt[14]=	"http://www49.atwiki.jp/aniwotawiki/pages/31.html";
txt[15]=	"http://www49.atwiki.jp/aniwotawiki/pages/32.html";
~~~中略~~~
txt[2500]=	"http://www49.atwiki.jp/aniwotawiki/pages/2517.html";
>
mmax = 2500; 
txtno = Math.floor(Math.random() * mmax);
document.write("<a href='"+txt[txtno]+"'> ▷ バシルーラ</a>");
>
>
}}
```

…なるほど！
URLを列挙した配列からひとつをランダムに取得するJavaScriptですね。
たしかにこれでは新記事を追加するごとに手動でURLを追加しないといけませんね。
上限が2500というのはwiki側の制約でしょうか？
MDNでは
>length プロパティの値は正符号を持つ整数で、2 の 32 乗 (232) 未満の値です。

として
`new Array(4294967296)`のコードが記載されています。
[Array.length - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/length)

# とんでもない誤解

ま、配列の最大長などはそれは些細なこととして…
各リンクはstring型であり、wikiの仕様としてもJSの文字列にURLがあったとしてそのページを読み込むようなことはないでしょう。

ここまで書いてすなわち、もし見ている方がいたら何に驚いていたのか伝わらない書き方だったのですが、

>各記事を読み込むたびに列挙してある7500個のURLも毎回読み込まれる

という表現は提案者は、あるいは私以外のみなさんが

7500個のURL = 文字列 = たくさんの文字列
読み込まれる = メニューとして取得される内容に含まれる

つまりメニューを含めたHTMLページの文字数・バイト数が単純に増加することを指しての通信量や負荷の懸念を述べていたと推測するのですが、
私は
7500個のURLを読み込む = 各URL **先**をGETなりで**参照している** つまり各URL先との**通信が発生している**と盛大な勘違いをかましていました。

「URLを読み込む」という表現をそれそのままと受け取らず、勝手に拡大解釈してURL先にまで思いをはせてしまったことや、
負荷の実害、相当重たくなるという表現に悪い方面にわざと考えてしまったこと、
実際のコードがURLをフルで記載しているとは思わなかったりで7500という数字をデータ量感覚であまり重く受け取らなかったことがあるのかなと思います。

はずかしー！
ああはずかしい…

# どれぐらい重くなるのか

というわけで7500個のURL文字列がどれぐらい重いのか見てみることにしました。どんぶり勘定で。

まず[ランダムリンク](https://w.atwiki.jp/aniwotawiki/pages/10.html)のソースを見ると、ページで書いたJSプラグインの内容がminifyなどもされずに`script`タグとして挿入されていることがわかると思います。

```html
<!--@@@@@-->
<script type="text/javascript">
var txt = new Array();
txt[0]="http://www49.atwiki.jp/aniwotawiki/pages/15.html";
txt[1]="http://www49.atwiki.jp/aniwotawiki/pages/18.html";

```
このページがサイズで約216KBらしいです。
次に現状の[左メニューのページ](https://w.atwiki.jp/aniwotawiki/pages/2.html)が約85KB。
左メニューと本体に同一のメニューが出るので、ヘッダーなどを無視してこの半分が大雑把に左メニューのサイズとして約43KB。
216 - 43 = 173KBがざっくりいちランダムリンクに消費されるサイズ？
いやもう少し単純に問題の`script`タグのバイト数が168KBぐらいだったの170KBでいいでしょう。

それが三つだったので510KBが毎回左メニューに挿入されていたと思われます。…あってるのかな？wikiのinclude記法の詳細を知らないので適当ですけれど。

[魚拓無いかしらん](http://web.archive.org/web/*/https://w.atwiki.jp/aniwotawiki/)と見てみましたが削除されて以降のものしかなかったですね。削除されて軽くなったから取得されるようになったのかしらん。


510KB…結構すごいですね…
[jQueryが約90KBなので](https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js)およそ5.5個分ぐらい読み込まれているといえるでしょうか。
メニューもそのぐらいなので1/6にシェイプアップできたとも。
こうしてサイズであらわすと消されるのもさもあらんという気がします。

ただし単純なサイズのみでサーバーの通信量や負荷を語るのは避けるのですが。

# コードが悪いよコードが

いろいろ改善できると思いますが少なくともサイズ増大の一番の原因はURLを毎回完全に近い形で記載していることですね。
アドレスバーのコピペを貼り付けるならこれが一番楽かもしれませんが、

ごく簡単に

```js
var txt = new Array();
txt[0]="15";
txt[1]="18";
txt[2]="19";
~~中略~~
txt[2499]=	"2516";
txt[2500]=	"2517";

mmax = 2500; 
txtno = Math.floor(Math.random() * mmax);
document.write("<a href='http://www49.atwiki.jp/aniwotawiki/pages/"+txt[txtno]+".html'> ▷ バシルーラ</a>");
```
とするだけで約45KB、三つで135KB…思ったよりありますね…いや、まだまだ重いですが、2/3以上を削減できるわけで。

がっつり書き直すなら
ところどころに含めたくない歯抜けidがあったとしてもrangeを組み合わせればスッキリ書けそうですね。
[range関数はないですけど](https://qiita.com/RyutaKojima/items/168632d4980e65a285f3)

```js
const range = (start, end) => [...Array((end - start) + 1)].map((_, i) => start + i);
const t = [].concat([15], range(18, 20), range(102, 2000), range(2100, 2517))
document.write("<a href='http://www49.atwiki.jp/aniwotawiki/pages/"+t[ Math.floor(Math.random() * t.length)]+".html'> ▷ バシルーラ</a>");
```

こんな感じ？URLのドメインを削ったりしてもいいですけれどこれで1KBにも満たないのでもともとから見て誤差も誤差ですね。
別ページに挿入されるなら`const`はマズいかしらん？`var`のままでいいかな。

このコードが書かれたのが2013年で当時の相談所も閉鎖してしまっているので経緯は謎ですが、実際のところ歯抜けも1-14,16-17を除いてほぼないです。
なので4万記事中ごくたまーに入るメニューやテンプレート用のid一桁番、最近作られ始めたコメントログなどを許容できるなら、最大の値だけをたまーにメンテしてやって

ついでに現在でも機能しますが[atwikiのURLが`www49`から`w`に変わったので](https://atwiki.jp/news/543)そこもhttps化もあわせて

```js
var max = 40000 //最新記事のid
var rl = () => "https://ｗ.atwiki.jp/aniwotawiki/pages/" + (Math.floor(Math.random() * max) + 1) + ".html"
document.write("<a href='" + rl() + "'> ▷ バシルーラ</a>");
//document.write("<a href='" + rl() + "'> ▷ ゆびをふる</a>");
//document.write("<a href='" + rl() + "'> ▷ 車田飛び</a>");
```

ひとつのページにするなら縦並びのためにdivで囲って

```js
document.write("<div><a href='" + rl() + "'> ▷ バシルーラ</a></div>" +
"<div><a href='" + rl() + "'> ▷ ゆびをふる</a></div>" +
"<div><a href='" + rl() + "'> ▷ 車田飛び</a></div>");
```


...かな？


# ユーザースクリプトコード

記事を書きだしてすっかり主従が逆転してしまいましたが、そんなランダムリンクを自分で復活させようという話…でした。

```js
// ==UserScript==
// @name         Aniwota add random link
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  メニューにランダムリンクを追加する
// @author       khsk
// @match        https://w.atwiki.jp/aniwotawiki/*
// @grant        none
// ==/UserScript==

(function() {
  'use strict';

  const menu = document.querySelector('#menubar, #atwiki-menupage') // PCとモバイルでメニュー名が違う
  if (!menu) { return }

  const latest = document.querySelector('.plugin_list_read_only a')
  const latestNumber = latest.href.match(/(\d+)\.html/)[1]
  const a = document.createElement('a')
  a.textContent = 'ランダムリンク'
  a.href = 'https://w.atwiki.jp/aniwotawiki/pages/' + (Math.floor(Math.random() * latestNumber) + 1) + '.html'
  menu.insertBefore(a, menu.firstChild)

})();
```

最大の問題は記事の増加に対してどうメンテナンスフリーにするかでしたが、新規ページはidの単純増加っぽいこと。
そして最新記事は同メニュー内にプラグインから取得されていることからその最新記事のリンクからidを引っ張ってくることで常に最新を含めたランダム値を取るようにしました。

時計や閲覧数の場所にも項目数があって、現在はidに対して項目数の差が900ほどあるので、900/44000がハズレページの割合でしょうか2%ぐらい。

削除されたページは
>[指定されたページ番号は存在しません。削除されたか新しく作り直された可能性があります。](https://w.atwiki.jp/aniwotawiki/pages/44839.html)

となるので飛びなおしてねって感じですね。

***

んーメニューの表示待ちを入れれば本体でもいけるのかな
wikiにはjQueryがあるのでばっさり読み込み待ちして

`<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>`

```js
$(function() {
  var max = document.querySelector('.plugin_list_read_only a').href.match(/(\d+)\.html/)[1];
  var rl = () => "https://ｗ.atwiki.jp/aniwotawiki/pages/" + (Math.floor(Math.random() * max) + 1) + ".html";
  document.write("<div><a href='" + rl() + "'> ▷ バシルーラ</a></div>" +
  "<div><a href='" + rl() + "'> ▷ ゆびをふる</a></div>" +
  "<div><a href='" + rl() + "'> ▷ 車田飛び</a></div>");
});
```

でも動くのかな？


[アニヲタWiki(仮)]:https://w.atwiki.jp/aniwotawiki
[^`]: Qiitaのユーザーメンションになる場合があるので`@wiki`ではなく、atwiki or アットウィキと~~面倒なので~~書きます。