MathJaxをデバッグしてJavaScriptの理解を深める

提供:yonewiki
2022年11月26日 (土) 15:22時点におけるYo-net (トーク | 投稿記録)による版 (→‎t.defaultReady)

VScodeで簡易WebServerの元でデバッグに戻る。


概要

 JavaScriptで書かれたMathJaxライブラリは、TeXのような組版処理をサービスを提供しているCDN(Content Delivery Network:コンテンツ配信サービス)あるいは自前のサーバーに配置するかするようにして構築するものです。主に数式の組版をするために提供されているサービスです。


 このような組版処理はいくらやってもらってもいいという感じで、楽譜とかも組版出来たらいいのにと夢みたいなことをおもっていたのですが、一部には実際に楽譜が組めるようになっているサービスもあるようですが、なっとくいかない仕上がりです。納得しろやオマエ。って思うでしょ。そうなんすよ。作れないくせになっとくしてないんすよ。もっとやりたいことは簡単な楽譜処理なんすよ。そういうのはおいといて、数式の組版を実現したMathJaxはスゴイと思います。それで、これを理解したら自分にも役に立つJavaScriptが作れるようになるんじゃないかと思ったりしました。JavaScript初心者ですよ。


 MathJaxに使われている技術を理解してく記事です。そして理解するための工夫や解析によってわかったMathJaxの考え方をここで書いていきます。自分が使ったことのあるのはMathJaxの中の/es5/tex-mml-chtml.jsで、これをちょろっと除くとまぁエグイ。一行目で終わってるプログラム。さすが、一行の長さがエグイ。インデントしてみたら5万行にもわたるプログラムが1行になってる。改行なしのプログラムあるんだなぁ。


 まずは、インデント処理を自動でやってくれるサービスと出会うことから始めました。


いろいろなプログラムを自動整形するサービス

 MathJaxを理解する作業をはじめて、いきなりありがたいサービスにまた出会う。このサービスのプログラムもすごいね。完璧すぎる自動整形インデント処理をしてくれます。そのJavaScriptの自動整形サイトが以下です。


  • Online Javascript Beautifier - BeautifyConverter.com
https://www.beautifyconverter.com/javascript-beautifier.php


 他にもBeautifier Minifier/CSV/Excel/TSV/HTML/JSON/SQL/XML/YAML/Validators/CSS/Gen/Unit/Escape/IMG/PDF/IP/Color/Othersという整形処理ができる。自分で作ったプログラムが画期的である場合にはこのような自動整形処理を他人に任せるのは知的財産の流出の危険があるので、気を付けてください。信頼できるサイトもあるかもしれませんが、自分は知りません。サービス自体はありがたい。Microsoftがこういう機能をVisualStudioCodeにつけていたら楽だったのにな。あるっちゃあるんすよ。Shift+Alt+Fらしい。あまり自動整形されないっす。


 とはいいつつも自動整形が必要なのはここまでヒドイと思ったプログラムだけですけどね。自分でプログラム作成して整形したものが最高に美しい。余計なことをするのがMicrosoftのエディタって感じですけど、Microsoftの考え方と共鳴しながらのコーディングをして、美しさを求めるのが自分流。MathJaxを自動整形して見えてくるものはある。最初の方に関数が連想配列でつめこまれて1000から3000くらいの関数が配列化されている。しかも番号だけで管理している。手ごわい。いみわからん。まったくいみわからん。変数名も1文字が多い。意味わからん。そんなことできるの?ってぐらいスゴイ。なんかそういう風にプログラムを意味わからんくする自動ツールがあるのかもしれん。でも自動整形したら少しましになった。あー、ありましたねminifyとかってサービスこれのことかな。意味のあるプログラム変数名を圧縮してしまう技。こういうのを使ってるのかもしれない。でもソースで配布してるのおかしくね。MathJaxの中の人。ちがうのか。謎のままおいておこう。

 

デバッグ環境を整える

 基本的にはVScodeで簡易WebServerの元でデバッグで記述した方法によってVSCodeでデバッグします。ソースのダウンロードにはGitコマンドっていうのを使いたくて、Git for Windowsみたいなのをインストールしました。Gitのインストーラーがややこしい。Ver2.38.1ですが、CommandPromptでGitコマンドが使えりゃいいかなと思ったりするだけなんですけど、ややこしい。基本デフォルト設定でインストールするだけかなと思ったんですけどところどころ触ってやらないとCommandPromptで使えるような状態にはならないらしい。インストーラはhttps://gitforwindows.org/からデカデカとあるDownloadから入手。手を加えたところを太字にするとして、おおまかな流れは


 実行→アプリがデバイスに変更[はい]→ライセンス確認[I Agree]→インストール先[Next]→フォルダ作成確認[はい]→ インストールオプション[Next](右クリックメニューにGitShell起動とGitBash起動を追加/大きいファイルサポート/*.gitファイルをテキストエディタで編集/*.shファイルをGitBashで起動)→スタートメニューのグループ名(GITのまま)[Next]→コミットメッセージで使うエディタは何[Use Vim(the ubiquitous text editor) as Git's default editor]-[Next]→Git initで作られるブランチ名(master (Let Git Decide:Gitに決定させよう))[Next]→パスの設定[Git from the command line and also from 3rd-party software(path追加Gitインストール先\Git\cmd)]-[Next]→OpenSSHのインストール[Use bundled OpenSSH]-[Next]→HTTPSリモートリポジトリ接続[Use the OpenSSL library]-[Next]→改行コード自動変換[Checkout Windows-style,commit Unix-style line endings(コミット処理でなんかうまくいかないときはこの設定が邪魔していることも覚えておかないといけませんよ。あまり使わない機能なので大丈夫だと思いますけど)]-[Next]→Git Bashターミナルを選択[Use MinTTY]-[Next]→Git pullの設定[Default(git pull --ff)]-[Next]→Credentioal設定[Git CredentialManager]-[Next]→オプション[Enable file system caching のみ ON]-[Next]→試験運用オプション[check無し]-[Install]


 あらゆるプロジェクトにおいて、GitHubでソースコードを管理する波は来ています。確実に。Gitコマンドつかえるようになったほうが良いと思います。GitHubの一人勝ちだな。一般向けに最初に納得いくものを作った人たち偉い。会社には会社の中のコード管理があると思います。Visual Source Safeなつかしい。そんなのつかっているひといるのかな。ソース管理とかしたことないからわからないことばかりだけど、必要だよね。


 例えばC:¥JavaScriptフォルダを作って、そこにソースをダウンロードする場合。


cd C:¥JavaScript
git clone git://github.com/mathjax/MathJax.git MathJax


 これだけでC:¥JavaScript¥MathJaxが出来て、その中にes5フォルダが出来上がります。es5とはEcmaScriptというJavaScriptのもう一つの規格名がありまして、これにしたがっているのがes5です。何がどう違うかは自分は完全には理解していません。これに対応している関数や配列なんかには __esModule= true という値が取得できるようになっていたりします。高度なJavaScriptではこの値を制御するみたいな記事もあります。自分はまだ理解していません。


 いろいろな種類の動作を確認するサンプルもダウンロードしましょう。MathJaxの多くの機能を網羅するようなSampleってのはありがたいものです。以下のアドレスから取得できます。



 同じフォルダ(例C:\JavaScript\MathJax)にDownloadしましょう。結果的にC:\JavaScript\MathJax\MathJax-Example\MathJax-base\index.htmlのようなものにアクセスできるようになります。このindex.htmlには2次方程式の解の公式がサンプル表示されるようになっています。Mathjaxのプログラムの参照部分が ../MathJax.js になっているので、 ../../es5/tex-mml-chtml.js のように書き換える必要があります。全部のファイルで書き換えないとだめなのでちょっと辛いですけど、やってみましょう。ファイルによっては違うプログラムを参照した方がいいようなものもあります。これで関連記事のようなVSCodeを設定をするとデバッグができるようになります。

 


軽く追いかけてみる

序盤その1

 自動整形で変換したtex-mml-chtml.jsの1行目の (function(){ にブレークポイントを設定して、プライマリサイドバーの上から4つめ実行とデバッグを選択して、launch.jsonを作ったら、Open index.html の横にある 蛍光色黄緑 ▷ のボタンを押します。ブラウザが起動して、VisualStudioCodeのtex-mml-chtml.jsの1行目で止まります。


 最初の行に"use strict";とあるので、このプログラムでは、強制的に構文チェックをするモードになり、変数定義を正式に実施していないとエラーになるモードになります。


 次に __webpack_modules__ 代入処理で配列が定義されます。 {351 : … とあるので、351が最初に定義される関数みたいですね。何に使うものなのか、この番号からはあまり想像つきません。ものすごい数の配列になってそう。__webpack_modules__はいわば、関数群ですね。


 次に __webpack_module_cache__ = {}; という上記関数群のためのcacheと思われる変数への初期化がされます。プログラムが進むとここにいろいろ設定されていくのでしょう。よく使うやつとかがこんなかにはいるのかな。__webpack_modules__はものスゴイ数の関数がありますので。


 次に function __webpack_require__(t){ … } があるので、tという変数の引数を受け取る__webpack_require__関数が定義されています。短いです。


	function __webpack_require__(t) {
		var e = __webpack_module_cache__[t];
		if(void 0 !== e) return e.exports;
		var r = __webpack_module_cache__[t] = {
			exports: {}
		};
		return __webpack_modules__[t].call(r.exports, r, r.exports, __webpack_require__), r.exports
	}


 ペロッと貼っておきました。cacheの中に設定されている連想配列キー変数 t の配列値を e に取得して何もなければ t の配列値にexports : {} という連想配列を代入して、その値を更に r で覚えさせておくみたいだね。そして、最初に登場にした巨大な関数群を格納している配列 t を実行しています。 返却する値として .call は連想配列値を実行ですね。連想配列にプログラムを格納しておくという技なんだな。なるほど。その引数は、たった今、{exports:{}}とした r の exports キーの値 {} と r 全体 と また同じ、r の exports キーの値 {} と この関数全体の値 __webpack_require__ も引数にしていて4つが引数になっています。この関数群の中のひとつの実行結果が返り値の一つ目、そしてもうひとつ何回も登場するr の exports キーの値 {} を返却するみたい。


 次に__webpack_require__に.gというメンバ変数をつくって、これに何やら設定する関数が実行されます。


	__webpack_require__.g = function() {
		if("object" == typeof globalThis) return globalThis;
		try {
			return this || new Function("return this")()
		} catch(t) {
			if("object" == typeof window) return window
		}
	}();


 ここは単純だな。globalThisという値を __webpack_require__.g に代入する処理なんだけど、ちゃんとobjectとして存在しているかを確認して代入している慎重さがある。globalThisというオブジェクトが無い場合は例外処理付の次の処理を実施します。tryはエラーが発生する可能性がある処理で、エラーが発生したらcatchの処理をするという感じです。返却される可能性のある値は、this || new Function("return this")() エラーになった場合は、window ですね。とにかく取得できる環境変数は取るみたいな処理ですね。catch(t)のtはエラー情報格納変数です。eを使う人も多い印象だけど、tでもいいんだね。


 勉強になるのは this || new Function("return this")() っていう部分が無駄に感じるけど、Debugしてみると単純なthisはundefinedで、new Function("return this")()だとwindowオブジェクトがかえってきたりして、意味が違うんだなって思ったことです。そして、この場合の || の使い方がむずい。なんじゃコレ。論理和演算だからどっちかがtrueになったらtrueが返却され、両方がfalseならfalseがかえるのだろう。それも動かないときはwindowオブジェクトで扱う。


 まだわからないのは

  • new Function("return this")()の後ろのカッコの有り無しの違い
  • 関数構文の中のthisとfunction(){}の中のthisの違い。

 また今度だな。

 

序盤その2

 次にvar __webpack_exports__ = {}; で __webpack_exports__ の連想配列変数を初期化。


 次はちょっと特殊 !function(){}という関数の実行処理です。戻り値のBool値が反転しそうだ。論理否定付きの関数。こんなこともできるんだな。なるほどなるほど。function xxx = {} や var xxx = { zzz : function(x,x,x) = {} }なら関数定義だけfunction(){ }なら実行する内容になるんだね。定義だけか関数だよといいながらも即実行。


 次の部分からは __webpack_require__(t 【←数値】 )で5つの値 t, e, r, n, o を定義。なんの処理の関数を結び付けるのやら、みてもわからんだろうな。9515, 3282, 235, 265, 2388 暗号だな。さっきしらべたとおり __webpack_require__(t) を呼ぶと、__webpack_modules__[t].call(xx,xx,xx,xx)が実行される。__webpack_modules_cache__[t]には{ exports:{ } }が作成されている状態です。


 他にも、undefinedで変数が沢山認識される。


 _, _e, _n, _r, _t, _$, $r, $t, a, A, ae, Ae, an, ar, Ar, at, At, b, B, Be, bn, br, Br, bt, Bt, c, C, ce, Ce, cn, cr, Cr, ct, Ct, d, D, de, De, dn, dr, Dr, dt, Dt, e, E, ee, Ee, en, er, Er, et, Et, f, F, fe, Fe, fn, fr, Fr, ft, Ft, g, G, ge, Ge, gn, gr, Gr, gt, Gt, h, H, he, He, hn, hr, Hr, ht, Ht, i, I, ie, Ie, ir, Ir, it, It, j, J, je, Je, jr, Jr, jt, Jt, k, K, ke, Ke, kr, Kr, kt, Kt, l, L, le, Le, ln, lr, Lr, lt, Lt, m, M, me, Me, mn, mr, Mr, mt, Mt, n, N, ne, Ne, nn, nr, Nr, nt, Nt, o, O, oe, Oe, on, or, Or, ot, Ot, p, P, pe, Pe, pn, pr, Pr, pt, Pt, q, Q, qe, Qe, qr, Qr, qt, Qt, r, R, re, Re, rn, rr, Rr, rt, Rt, s, S, se, Se, sn, Sn, sr, Sr, st, St, t, T, te, Te, this, tn tr, Tr, tt, Tt, u, U, ue, Ue, un, ur, Ur, ut, Ut, v, V, ve, Ve, vn, vr, Vr, vt Vt, w, W, we, We, wr, Wr, wt, Wt, x, X, xe, Xe, xr, Xr, xt, Xt, y, Y, ye, Ye, yn, yr, Yr, yt, Yt, z, Z, ze, Ze, zr, Zr, zt, Zt


 上記ぐらいが定義される。アルファベット全部の小文字(26)、大文字(26)と、アルファベット小文字or大文字に続いて[e,r,t]((26x2)x3=156) アルファベット小文字に続いて n (52)。n だけ大文字との組み合わせ変数が無い。意味深。全部使うのかな?この変数たち。


 うち、Sn と i は使われていた。

Sn:Sn(t, e) {
  (null == e || e > t.length) && (e = t.length);
  for(var r = 0, n = new Array(e); r < e; r++)
    n[r] = t[r];
  return n
}
i:i(t, e) {
  (null == e || e > t.length) && (e = t.length);
  for(var r = 0, n = new Array(e); r < e; r++) 
    n[r] = t[r];
  return n
}

 だけ、定義済みでした。まったく同じ関数。


 この関数が呼ばれるときの t は、  t:(30) ['[tex]/action', '[tex]/ams', '[tex]/amscd', '[tex]/bbox', '[tex]/boldsymbol', '[tex]/braket', '[tex]/bussproofs', '[tex]/cancel', '[tex]/centernot', '[tex]/color', '[tex]/colortbl', '[tex]/configmacros', '[tex]/enclose', '[tex]/extpfeil', '[tex]/html', '[tex]/mathtools', '[tex]/mhchem', '[tex]/newcommand', '[tex]/noerrors', '[tex]/noundefined', '[tex]/physics', '[tex]/require', '[tex]/setoptions', '[tex]/tagformat', '[tex]/textcomp', '[tex]/textmacros', '[tex]/unicode', '[tex]/verb', '[tex]/cases', '[tex]/empheq']

で、e は undefined だから e は t の配列の要素数(30)になります。n は 要素数30の配列になって、r が 0 から 29 まで変動して、n[r] = r[r]; なので配列のコピーですね。コピーした n を返します。


 何やってるか、想像がつきそうなのが2個3個あればいいな。

 

__webpack_modules__[9515].call(r.exports, r, r.exports, __webpack_require__)

			9515: function(t, e, r) {
				var n = this && this.__values || function(t) {
					var e = "function" == typeof Symbol && Symbol.iterator,
						r = e && t[e],
						n = 0;
					if(r) return r.call(t);
					if(t && "number" == typeof t.length) return {
						next: function() {
							return t && n >= t.length && (t = void 0), {
								value: t && t[n++],
								done: !t
							}
						}
					};
					throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
				};
				Object.defineProperty(e, "__esModule", {
					value: !0
				}), e.MathJax = e.combineWithMathJax = e.combineDefaults = e.combineConfig = e.isObject = void 0;
				var o = r(3282);

				function i(t) {
					return "object" == typeof t && null !== t
				}

				function s(t, e) {
					var r, o;
					try {
						for(var a = n(Object.keys(e)), l = a.next(); !l.done; l = a.next()) {
							var c = l.value;
							"__esModule" !== c && (!i(t[c]) || !i(e[c]) || e[c] instanceof Promise ? null !== e[c] && void 0 !== e[c] && (t[c] = e[c]) : s(t[c], e[c]))
						}
					} catch(t) {
						r = {
							error: t
						}
					} finally {
						try {
							l && !l.done && (o = a.return) && o.call(a)
						} finally {
							if(r) throw r.error
						}
					}
					return t
				}
				e.isObject = i, e.combineConfig = s, e.combineDefaults = function t(e, r, o) {
					var s, a;
					e[r] || (e[r] = {}), e = e[r];
					try {
						for(var l = n(Object.keys(o)), c = l.next(); !c.done; c = l.next()) {
							var u = c.value;
							i(e[u]) && i(o[u]) ? t(e, u, o[u]) : null == e[u] && null != o[u] && (e[u] = o[u])
						}
					} catch(t) {
						s = {
							error: t
						}
					} finally {
						try {
							c && !c.done && (a = l.return) && a.call(l)
						} finally {
							if(s) throw s.error
						}
					}
					return e
				}, e.combineWithMathJax = function(t) {
					return s(e.MathJax, t)
				}, void 0 === r.g.MathJax && (r.g.MathJax = {}), r.g.MathJax.version || (r.g.MathJax = {
					version: o.VERSION,
					_: {},
					config: r.g.MathJax
				}), e.MathJax = r.g.MathJax
			},


 なかなかわけわからん。

var n = this && this.__values || function(t){}

 グローバルオブジェクトだからthisはWindowオブジェクトなんだけど、strictモードなので、. の前が省略されているので、undefined だね。だから、値だけを取り出す、this.__values も undefined。n は function(t) があるから true かな。論理演算している意味が謎だわ。何がしたいんだろう。ま、あとあと何回か呼び出されるんだろうと思って、ブレークポイントを設定したら、謎のコードが33回呼ばれる。おいかけきれるかこれ? t には何かのキーワードみたいなのが入って呼ばれるね。呼び出されるときの t の値を記録してみるか。


 追記※1 : やっぱり論理演算にはJavaScript特有の動作がありました。論理和の場合は、左から0 や null や undefinedのような値が続いて、一番最初に左側に現れた値をそのまま返すんだそうな。なので、最初の値がオブジェクトや関数や変数ならばそのまま、その値が返るそうです。論理積の場合は左側に 1 や true がある場合は最初のオブジェクト値や関数や変数値がかえるそうなのです。


エグイ仕様だな。もう比較する必要ないね。みたいなサバサバ系の処理だ。これは専門家じゃないとむずいな。JavaScript末恐ろしい。自分なんかは知らないことだらけなんじゃね。と、思えてきた。深い!JavaScript。勉強しがいがある! 追記※1ここまで。


 1回目 (10) ['paths', 'source', 'dependencies', 'provides', 'load', 'ready', 'failed', 'require', 'pathFilters', 'versionWarnings']
 2回目 ( 1) ['loader']
 3回目 ( 9) ['input', 'output', 'handler', 'adaptor', 'document', 'elements', 'typeset', 'ready', 'pageReady']
 4回目 ( 2) ['startup', 'options']
 5回目 ( 2) ['startup', 'options']
 6回目 (1) ['_']
 7回目 (1) ['components']
 8回目 (37) ['a11y/semantic-enrich', 'a11y/complexity', 'a11y/explorer', '[mml]/mml3', '[tex]/all-packages', '[tex]/action', '[tex]/autoload', '[tex]/ams', '[tex]/amscd', '[tex]/bbox', '[tex]/boldsymbol', '[tex]/braket', '[tex]/bussproofs', '[tex]/cancel', '[tex]/centernot', '[tex]/color', '[tex]/colorv2', '[tex]/colortbl', '[tex]/configmacros', '[tex]/enclose', '[tex]/extpfeil', '[tex]/html', '[tex]/mathtools', '[tex]/mhchem', '[tex]/newcommand', '[tex]/noerrors', '[tex]/noundefined', '[tex]/physics', '[tex]/require', '[tex]/setoptions', '[tex]/tagformat', '[tex]/textcomp', '[tex]/textmacros', '[tex]/unicode', '[tex]/verb', '[tex]/cases', '[tex]/empheq']
 9回目 (3) ['tex', 'mml', 'sre']
10回目 (4) ['startup', 'input/tex', 'input/tex-full', '[tex]/all-packages']
11回目 (4) ['[tex]/amsCd', '[tex]/colorV2', '[tex]/configMacros', '[tex]/tagFormat']
12回目 (1) ['_']
13回目 (6) ['adaptors', 'components', 'core', 'handlers', 'mathjax', 'util']
14回目 (1) ['global']
15回目 (1) ['_']
16回目 (1) ['input']
17回目 (1) ['_']
18回目 (1) ['input']
19回目 (2) ['mathml_ts', 'mathml']
20回目 (1) ['_']
21回目 (1) ['output']
22回目 (1) ['checkReady']
23回目 (1) ['_']
24回目 (1) ['output']
25回目 (2) ['chtml', 'common']
26回目 (1) ['fonts']
27回目 (1) ['fonts']
28回目 (1) ['fontURL']
29回目 (1) ['font']
30回目 (1) ['_']
31回目 (1) ['ui']
32回目 (1) ['_']
33回目 (1) ['a11y']


 呼ばれるのは、この9515をコールしたときではなく、後で、違う方法で実行されるように呼ばれるようだ。そのときに、このような引数が渡されて実行される。でも、デバッガによると n には、この関数全体が格納される。おそらく n によってこの関数が実行されるんだろう。論理演算ってそういう解釈があるんだなとは疑問に思いつつも納得しようと思う。調べないとな。33回も呼ばれるんだから、後で、こんなのあったなぁってなるんだろう。先に進もう。


 次に実行されるのはObject.defineProperty(e, "__esModule", { value: !0 })で、Object.defineProperty()はオブジェクトの定義をする命令で、第一引数のオブジェクトに、第二引数のメンバ変数に第三引数を値として設定している。つまり、e.__esModule = {value : !0}としている。なんで!0なんだろう。!0 って true のことだと思うので、e.__esModule = {value : true}って感じだと思う。 __esModule は、EcmaScriptに準拠したモジュールかを設定しているとか説明があった。変数 e の扱いを決定するものだな。でも、e.__esModule = true じゃなくて{value : true} なんだな。


 続けて、xxx = void 0;を設定という形式で

e.MathJax = undefined;
e.combineWithMathJax = undefined;
e.combineDefaults = undefined;
e.combineConfig = undefined;
e.isObject = undefined;

 としている。e は、どうやら環境変数って意味っぽいな Environments の e だな。MathJax は、このプログラムの核となる設定でcombineは結合とか混合って意味でwithMathJaxはMathJaxと結合するもの。Defaults規定値で結合するもの。Configは設定で結合するもの、isObjectはオブジェクトかどうか。って感じかな。ひとつ感じるのは、まだ何も始まってすらいないという感じだな。これだけおおがかりなことやっていながら、設定するのは、undefined だもんな。


 次は var o = r(3282);が実行されます。rは関数が格納されていて、__webpack_require__です。いやはや、この__webpack_require__(9515)を通じて、__webpack_modules__[9515].call()がされて今にいたっているのに、それが終わるのを待たずして、関数の内で、また新しく__webpack_require__(3282)を実行するようなものですね。確か、この__webpack_require__(9515)の後も同じように__webpack_require__(3282)が実行される流れになっていたような。ここでも実行しようとするんだね。3282は関数の内容が少ない。 o = { VERSION: "3.2.2", __esModule : true } になる。でも、次に呼ばれる__webpack_require__(3282)の結果は e 配列に追加格納されるような。さっき環境変数の e にいくつかundefined値が設定されたものもあったな。そして o にも同じような値を持たせるんだな。今、結構1文字の変数埋まってきてるな。環境変数っぽい e 。今しがた論理演算で込みなのに取得された n 。そして、o 。それから__webpack_require__が格納された r 。9515のcall内での t は r.export r という引数で{ Exports : {} }に対応する。eは r.Exportsの{}に対応しているがeには値が設定されてExports値の中身が増えていくはず。呼ばれる前は t は配列番号の9515を保持するモノとして使われていた。


この後、さらに2つの1文字変数に関数が格納されます。 i 短い関数function i(t) { return "object" == typeof t && null !== t }、そして s には割かし長めの関数がfunction s(t, e) { }


 そして今しがた獲得したiとsの関数をちょっと前に登場した環境変数に格納する。グローバル系の処理が多いね。これって、なんか違うjava Scriptを同時に動かしているsiteなら、変数が重複して動かなくなりそうなもんだけど大丈夫なのかな。


e.isObject = i; 
e.combineConfig = s;


 に格納している。確かにe.isObject(t)は動かすと、t がオブジェクトなのかを識別して、オブジェクトなら1と返却するし、違うなら0になるね。combineConfig は長いよね。簡単にながめてみると。


 関数 s は for文で 論理演算の元で格納された関数 n に引数 o を与えながら全ての o の要素分だけ繰り返す。o は Version と __esModuleだけだし、たいした繰り返し処理ではなさそうだ。それよか、この for 文がエグイ記述だな。

var a = n(Object.keys(e)), l = a.next(); !l.done; l = a.next()

 ということなんだけど、初期値がカンマ区切りで2個設定されている l = a.next()は次の値なのかな。繰り返し条件の !l.done だから、l.done が 0 の間は、!l.done が 1 なので、繰り返す。l.done が 1 なら終了で、増分は l= a.next で次の配列値が l に入るみたいな感じだな。 ややこしい。e の配列には今は以下の通りになっていて、順番に実行されるのかな。

e.MathJax = undefined;
e.combineWithMathJax = undefined;
e.combineDefaults = undefined;
e.combineConfig = s;
e.isObject = i;

 こんな感じの環境変数のキーを一個づつ a に取得 l には次の値。繰り返したい要素 2つの値を同時に処理できるというのは斬新だけどやりたい処理ってのは c には 次のキー値 l.value の値が入りつつ、論理演算で、__esModuleという文字列値でないときにtrueである且つ、e[c] instanceof Promise ? null !== e[c] && void 0 !== e[c] && (t[c] = e[c]) : s(t[c], e[c])?instanceof Promise?ムズッ。Promiseってのは非同期・同期処理についてキャストできたかというのを判定する処理らしい。なんだよ非同期とか同期とか。関数によっては時間がかかるものとかは、順番に処理されることなく、次々と時間がかからない処理を進めていく。非同期。それを順番に処理するのは、同期処理。と言っていると思います。Promiseは処理をするタイミングを調整するものです。そういった関数であるかどうかを判定しているらしい。いづれにしても何がしたいのかわからない。論理演算だけをして、特に代入することもないという。わからん。e[c] instanceof Promiseの結果が true なら null !== e[c] && void 0 !== e[c] && (t[c] = e[c]) で false ならs(t[c], e[c])で、再帰処理がされる。いやそもそも i(t[c]) とか i(e[c]) とか t[c] とか e[c] とか なんだろうね。i(…) は引数がオブジェクトかどうかを判定する関数でした。cは、 MathJax/combineWithMathJax/combineDefaults/combineConfig/isObject/…esModule/で終了条件にesModuleというキーが使われている予感。他のものの場合は、それがオブジェクトなのかを判断するi(e[c])。t[c]。おいておくか。ちょろっとここにブレークポイントを設定してどういうものが実行されるかみてみてみましょう。


実行されるときには t の内容は以下のような値になっているようです。

{version: '3.2.2', _: {}, config: {paths: {mathjax: 'http://localhost:5500/es5'}, source: {}, dependencies: {}, provides: { }, load: Array(0), …}}


 eは{loader: {versions: Map(0), ready: ƒ, load: ƒ, preLoad: ƒ, defaultReady: ƒ, …}}で、a = n(Object.key(e)) の a はn('loader')になります。nはちょっと前にあった関数が入っていて、


function(t /* ← 'loader' */) {
  var e = "function" == typeof Symbol && Symbol.iterator,
  	r = e && t[e],/* e : Symbol(Symbol.iterator) && 'loader'[Symbol(Symbol.iterator)] → r : ƒ values() */
  	n = 0;
  if(r) return r.call(t); /* r = function( t /='loader'=/ ){return t /='loader'=/ } → return 'loader' */
  if(t && "number" == typeof t.length) return {
  	next: function() {
  		return t && n >= t.length && (t = void 0), {
  			value: t && t[n++],
  			done: !t
  		}
  	}
  };
  throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
}

でした。この t が'loader'ってことですね。 l は { value: "loader", done: false, } になります。for文の中身の

var a = n(Object.keys(e)), l = a.next(); !l.done; l = a.next()

 この l です。a は {} です。Array Iterator と表現されます。このfor文の使い方は難しい。理解できない。lの中身がそうなる理由が見えてこない。初期値は長い関数によって帰ってくる値で n('loader') の結果が {} で、l = a.next() なんだけど、{ value: "loader", done: false, }っつうね。終了条件の l.done がどこからきたかはわかっていないけど、最初は false なので 1回は処理されるというね。で、おわるとl = a.next() が実行されて次の処理になる。

 c = l.Value は最初は 'loader'

t[c] = undefined
e[c] = {versions: Map(0), ready: ƒ, load: ƒ, preLoad: ƒ, defaultReady: ƒ, …} これは e['loader']の値です。
e[c] instanceof Promise : false

 falseなので、再帰部分が呼ばれる。s(t[c], e[c])。ふむ。いずれにしても、ここでは論理式が書かれているだけで、処理はなにもされなくて、これがエラーにならないかどうかを確かめているだけなのだろう。何もなければ、t そのものが返却される。うまくいかないとエラーになる。


 c はこの後 'loader' → 'versions' → 'ready' → 'load' → 'preLoad' → 'defaultReady' → 'getRoot' → 'pathFilter' → 'item' → '0' → 'item' → … になる。


 こんな、調子だとわからないことだらけだな!


 なんしか、e.combineDefaults = function t(e, r, o) { … } もおなじような試みの関数が設定される。

e.combineDefaults = t; また t が使われた。
e.combineConfig = s;
e.isObject = i;

 次に、e.combineWithMathJax = function(t) { return s(e.MathJax, t) }が設定される。あ!!あー!これ今作ったばっかりの関数 s が呼ばれてる!でも、実行されるのはもっと後だな。でも引数は e.MathJax, t だ。s(t, e)だからな。でも、t が e.MathJax で、 e が t か。また t が引数。このときの t は {'loader' : {versions: Map(0), ready: ƒ, load: ƒ, preLoad: ƒ, defaultReady: ƒ, this : {} }}みたいな値です。


void 0 === r.g.MathJax && (r.g.MathJax = {}),
r.g.MathJax.version || (
  r.g.MathJax = {
    version: o.VERSION,
    _: {},
    config: r.g.MathJax
  }
), 
e.MathJax = r.g.MathJax


 あーそうか。呼び出される前に、var r =__webpack_module_cache__[t] = { exports: {} };処理やってるから2回目は、少なくともexportsの空の値に e 返す。っていう処理になるんだな。この後 3282 。で呼ばれても、同じ処理をするわけではないんだな。

 

 

__webpack_modules__[3282].call(r.exports, r, r.exports, __webpack_require__)

			3282: function(t, e) {
				Object.defineProperty(e, "__esModule", {
					value: !0
				}), e.VERSION = void 0, e.VERSION = "3.2.2"
			},

 9515の中のoに設定したのと同じ要領で、e に設定するのかと思いきや、知らぬ間に __webpack_module_cache__ に{ 3282 : Exports : { version : '3.2.2', __esModule : true }, 9515 : {isObject: ƒ, combineConfig: ƒ, combineDefaults: ƒ, combineWithMathJax: ƒ, MathJax: {…}, …} }ってのが出来上がってた。だから、3282は __webpack_module__[3282]は呼ばれず、__webpack_module_cache__[3282]のreturn で e.Exports = { version : '3.2.2', __esModule : true } が返却される。つまり実行済みだからもう一度やり直しにはならない。__webpack_module_cache__ は優秀な仕組み。9515でいろいろやっている内に設定されたのは自分の知能不足。__webpack_modules__のcallは、最初の一つ目の引数は呼ばれた関数のthisになるオブジェクトを指定している。その後に最大3つの引数が設定されていて、t = r, e = r.Exports, n = __webpack_require__ として処理されることは覚えておいてよいね。r に何が設定されているかで、処理が決まるといってもいい。そして、どこで r や r.Exports が変化するかを抑えないと駄目だ。このあたりがこのプログラムを理解するうえで重要そうだ。

 

__webpack_modules__[235].call(r.exports, r, r.exports, __webpack_require__)

235: function(t, e, r) {
	var n, o, i = this && this.__values || function(t) {
		var e = "function" == typeof Symbol && Symbol.iterator,
			r = e && t[e],
			n = 0;
		if(r) return r.call(t);
		if(t && "number" == typeof t.length) return {
			next: function() {
				return t && n >= t.length && (t = void 0), {
					value: t && t[n++],
					done: !t
				}
			}
		};
		throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
	};
	Object.defineProperty(e, "__esModule", {
		value: !0
	}), e.CONFIG = e.MathJax = e.Loader = e.PathFilters = e.PackageError = e.Package = void 0;
	var s = r(9515),
		a = r(265),
		l = r(265);
	Object.defineProperty(e, "Package", {
		enumerable: !0,
		get: function() {
			return l.Package
		}
	}), Object.defineProperty(e, "PackageError", {
		enumerable: !0,
		get: function() {
			return l.PackageError
		}
	});
	var c, u = r(7525);
	if(
		e.PathFilters = {
			source: function(t) {
				return e.CONFIG.source.hasOwnProperty(t.name) && (t.name = e.CONFIG.source[t.name]), !0
			},
			normalize: function(t) {
				var e = t.name;
				return e.match(/^(?:[a-z]+:\/)?\/|[a-z]:\\|\[/i) || (t.name = "[mathjax]/" + e.replace(/^\.\//, "")), t.addExtension && !e.match(/\.[^\/]+$/) && (t.name += ".js"), !0
			},
			prefix: function(t) {
			  for(var r;
				  (r = t.name.match(/^\[([^\]]*)\]/)) && 
					e.CONFIG.paths.hasOwnProperty(r[1]);
				) 
				  t.name = e.CONFIG.paths[r[1]] + t.name.substr(r[0].length);
				return !0
		  }
		}, 
		function(t) {
			var r = s.MathJax.version;
			  t.versions = new Map, 
				t.ready = function() {
				for(var t, e, r = [], n = 0; n < arguments.length; n++) r[n] = arguments[n];
				0 === r.length && (r = Array.from(a.Package.packages.keys()));
				var o = [];
				try {
					for(var s = i(r), l = s.next(); !l.done; l = s.next()) {
						var c = l.value,
							u = a.Package.packages.get(c) || new a.Package(c, !0);
						o.push(u.promise)
					}
				} catch(e) {
					t = {
						error: e
					}
				} finally {
					try {
						l && !l.done && (e = s.return) && e.call(s)
					} finally {
						if(t) throw t.error
					}
				}
				return Promise.all(o)
			}, 
			t.load = function() {
				for(var r, n, o = [], s = 0; s < arguments.length; s++)
				  o[s] = arguments[s];
				if(0 === o.length)
				  return Promise.resolve();
				var l = [],
				c = function(r) {
					var n = a.Package.packages.get(r);
					n || 
					(n = new a.Package(r)).provides(e.CONFIG.provides[r]), 
						n.checkNoLoad(), 
						l.push(n.promise.then(
						  (
								function() {
							    e.CONFIG.versionWarnings && 
								  n.isLoaded && 
								  !t.versions.has(a.Package.resolvePath(r)) && 
								  console.warn("No version information available for component ".concat(r))
						    }
						  )
						)
					)
				};
				try {
					for(var u = i(o), p = u.next(); !p.done; p = u.next()) {
						var h = p.value;
						c(h)
					}
				} 
				catch(t) {
					r = {	error: t }
				} 
				finally {
					try {
						p && !p.done && (n = u.return) && n.call(u)
					} 
					finally {
						if(r) 
						  throw r.error
					}
				}
				return a.Package.loadAll(), Promise.all(l)
			}, 
			t.preLoad = function() {
				for(var t, r, n = [], o = 0; o < arguments.length; o++) 
				  n[o] = arguments[o];
				  try {
					  for(var s = i(n), l = s.next(); !l.done; l = s.next()) {
						  var c = l.value,
							  u = a.Package.packages.get(c);
						    u || (u = new a.Package(c, !0)).provides(e.CONFIG.provides[c]), u.loaded()
					  }
				  } 
				  catch(e) {
					  t = { error: e }
				  }
					finally {
					  try {
						  l && !l.done && (r = s.return) && r.call(s)
					  } 
					  finally {
						  if(t) 
							  throw t.error
					}
				}
			}, 
			t.defaultReady = function() {
				void 0 !== e.MathJax.startup && e.MathJax.config.startup.ready()
			}, 
			t.getRoot = function() {
				var t = "//../../es5";
				if("undefined" != typeof document) {
					var e = document.currentScript || document.getElementById("MathJax-script");
					e && (t = e.src.replace(/\/[^\/]*$/, ""))
				}
				return t
			}, 
			t.checkVersion = function(n, o, i) {
				return t.versions.set(a.Package.resolvePath(n), r),
				  !(!e.CONFIG.versionWarnings || o === r) && (console.warn("Component ".concat(n, " uses ").concat(o, " of MathJax; version in use is ").concat(r)), !0)
			},
			t.pathFilters = new u.FunctionList, 
			t.pathFilters.add(e.PathFilters.source,     0), 
			t.pathFilters.add(e.PathFilters.normalize, 10), 
			t.pathFilters.add(e.PathFilters.prefix,    20)
		}
		(c = e.Loader || (e.Loader = {})), 
		  e.MathJax = s.MathJax,
			void 0 === e.MathJax.loader /*だいぶ前のif文の比較演算子の対象。カンマ構文の最後*/
	) 
	{   /* e.MathJax.loader が undefined のときの処理 */
		(	0, s.combineDefaults )( /* s.combineDefaults メンバ関数の引数開始 */
          e.MathJax.config, 
          "loader", 
		  {
			  paths: {
				  mathjax: c.getRoot()
			  },
			  source: {},
			  dependencies: {},
			  provides: {},
			  load: [],
			  ready: c.defaultReady.bind(c),
			  failed: function(t) {
			  	return console.log("MathJax(" . concat(t.package || "?", "): ") . concat(t.message))
			  },
			  require: null,
			  pathFilters: [],
			  versionWarnings: !0
		  }
		), 
		(0, s.combineWithMathJax)( /* combineWithMathJax メンバ関数の引数開始 */
			{	
				loader: c	
			}
		); //長かったカンマ構文終わり。
		try {
			for(var p = i(e.MathJax.config.loader.pathFilters), h = p.next(); !h.done; h = p.next()) {
				var f = h.value;
				Array.isArray(f) ? c.pathFilters.add(f[0], f[1]) : c.pathFilters.add(f)
			}
		} 
		catch(t) {
			n = { error: t }
		} 
		finally {
			try {
				h && !h.done && (o = p.return) && o.call(p)
			} 
			finally {
				if(n) 
				  throw n.error
			}
		}
	}
	e.CONFIG = e.MathJax.config.loader
},

 ここでも9515とよく似た処理がされるね。t = r.Exports ⇔ {exports: {…}, undefined: undefined}, e = r ⇔ {}, r = r.Exports ⇔ ƒ __webpack_require__(t) { … } の3つの引数が使われる。引数の対応すら違っているな、__webpack_modules__[t].call(r.exports, r, r.exports, __webpack_require__)


 ん?あ、callの処理を勘違いしているんだな。ちょっと調べてみる。おー。第一引数は呼び出した関数のthisとなるものらしい。nullとundefined(void 0)を設定すると、グローバルのthisになってWindowオブジェクトが返ってくるんだそうな。今回の場合はr.exportsがthisってことだな。なので、t = r ⇔ {exports: {…}, undefined: undefined}, e = r.exports ⇔ {}, r = __webpack_require__ ⇔ ƒ __webpack_require__(t) { … } ってことだ。おーちょっと解決。勘違い恐るべし。


 t は this。e は Environments。r は result という意味かな? r.Exports を 格納する result。


 以下のような値が設定されます。

e.CONFIG = undefined;
e.MathJax = undefined;
e.Loader = undefined;
e.PathFilters = undefined;
e.PackageError = undefined; 
e.Package = undefined;  


 そして、

var s = r(9515);{isObject: ƒ, combineConfig: ƒ, combineDefaults: ƒ, combineWithMathJax: ƒ, MathJax: {version: '3.2.2', _: {…}, config: {…}}}
var a = r(265); {PackageError: ƒ, Package: ƒ, __esModule: true}
var l = r(265); {PackageError: ƒ, Package: ƒ, __esModule: true}

 の結果をs, a, l にそれぞれ格納。

 r() で その時点で Exports されている値が取得できるんだね。

 Object.defineProperty で e.Packageとe.PackageErrorに値と関数を設定。

 
{
  enumerable : true
  get        : function() { return l.Package (e.PackageErrorはl.PackageError) }
}


 そして

var u = r(7525); {FunctionList: ƒ, __esModule: true}

 の結果を u に格納。


 次に無駄に長い if 文。カンマ区切り構文が続く、評価されるのは一番最後(右側)の値。e.PathFilters={source:function(t){},normalize:function(t){},prefix:function(t){}},…まだ続く。


 まずはキー source からで e.CONFIG.source.hasOwnProperty(t.name) で t.name を作り、 t.name = e.CONFIG.source[t.name]を設定し、常に!0つまりtrueを返却するキー名sourceの値に結び付いた関数。

 normalize は t.nameがパターンマッチで、/^(?:[a-z]+:\/)?\/|[a-z]:\\|\[/i つまり、先頭が(?:[a-z]+:\/)で始まるかを判定。例えばc:abcd:\やc:abcd:abcd:abcd:\みたいに:で始まって、aからzのアルファベットの連続があったあとに来て:\で閉じられるものもしくは、先頭からじゃなくても、aからzの1文字:\もしくは [ があるかを判定。i オプションなので大文字と小文字の区別をしないというマッチングになります。おそらくドライブ文字のようなものにマッチングしようとしていると推測されます。"[mathjax]/" + e.replace(/^\.\//, "") は、t.name の値として [mathjax]/を先頭に付け足して、最初の./を取っ払ったものに置換しています。末尾が ./**** に一致しなければ、.jsを付け足す。という処理をするようです。先に ./ があったら取っ払っていますので、拡張子が既につけられていないことを確認しているものですね。一行でやりたいことをまとめれるって素敵ですね。自分だったらもっと何段階かにわけないと駄目だな。で、見てみたんですけど、e は startup のとき t は { name: '[mathjax]/startup.js', original: 'startup', addExtension: true} となり、startup を [mathjax]/startup.js に置換・格納する処理になっています。


 prefixはfor文の終了条件でパターンマッチングし [] の中が先頭から ] 以外の文字で最長マッチングした値を r[1] に取得。ようするに[]の中の文字列を r へ格納して、マッチングの結果 r[1] という名前のメンバがある限り繰り返し処理。r[0]には[xxxx]のように最長マッチングが取得できて、要するに r[0] は [] 付き、r[1] は [] 無しの文字列が取得できる。[] 無しの文字列をプログラム中では prefix プレフィックス:接頭辞と呼んでいるようで、その終わる部分の長さ以降からの文字を e.config.path['mathjax']の値、つまり http://localhosts:5500/es5 の後ろに startup.js 付け足す処理をします。


 ふむー。具体的に何をするんだろうな。先頭から追っていくと、何に使われるかわからない関数の準備ばっかりになるんだろうか。すさまじいコード量だ。作った人すげー。日々、痛感させられる。自分は何かここまで頑張りとおしたことはあるだろうかと、過去への振り返りさえ発生する。見習いたいものだ。


 とはいいつつも次の関数定義もざっと見ておく。


 次の関数は割かし眺めだ。しかもやっぱりわけがわからない。こういうときは動かしてみてどうなるか見てみる。ひとついえるのは動かしてみてとはいうものの、この関数が呼ばれる保証はない。ただ、まだ前半なのプログラムの根幹をなす関数を定義している気がする。


 動かすと t の値が {versions: Map(0) {size: 0}, ready: ƒ, load: ƒ, preLoad: ƒ, defaultReady: ƒ} こうなった。Version Map0とは?配列の便利なタイプがMapというものらしく、.set('xxx',{yyy:xxx,zzz:xxx}) や .get('xxx') や .has('xxx') といったメソッドや.size といったものがあります。Version番号くらいしか設定されないところに Map というのが不可解なくらいですね。あとは、動作した後に中々、多くの関数が定義されたなという印象。あとはカンマ構文は、セミコロンにできるだけ焼き直した方がデバッグしやすいけど、うかつに分解すると不具合が発生する元にもなるという諸刃の剣。でもちょっと分解しないとデバッグしておいかけにくい。自分みたいな素人が改造して使わないようにデバッグしにくいようにしてるんだろうなぁ。


 さっそく、このあたりの定義されるだけの関数の中のプログラムにブレークポイントを設定してみました。一番最初に呼ばれたのは、t.getRoot の部分です。ふむ。なにげに気になっていた部分 //../../es5/とハードコーディングされているパスがあるが、MathJaxはどこにあるhtmlから呼ばれるかわからない構造のはずだし、このtex-mml-chtml.jsファイル自体からのパスだとしてもそんなところにはes5はない。おー。すぐに t は上書きされますね。 document.getElementById("MathJax-script") = null で e = document.currentScript で e.src は http://localhost:5500/es5/tex-mml-chtml.js になりました。そうすると。これの e.src.replace(/\/[^\/]*$/, "") は http://localhost:5500/es5/ です。/[/以外]末尾 で末尾の / から以外っていみで、後ろのファイル名が取り除かれた名前になります。t.getRoot に http://localhost:5500/es5/ がセットされます。 おー。やるなMathJax。


 次に呼ばれたのは、t.preload です。o < Argument.length の間、繰り返します。Argument は 大きさ9の要素になっています。2つの連想配列があります。キーはcalleeとSymbol(Symbol.iterator)です。Arguments(9) ['loader', 'startup', 'core', 'input/tex', 'input/mml', 'output/chtml', 'output/chtml/fonts/tex.js', 'ui/menu', 'a11y/assistive-mml', callee: <accessor>, Symbol(Symbol.iterator): ƒ] で、繰り返し処理によってn[0]~n[8]の9個が Argument からコピーされます。

 e.CONFIG = 
{
 paths           : {mathjax: 'http://localhost:5500/es5'},
 failed          : {   ƒ (t) {
                           \treturn console.log("MathJax(" . concat(t.package || "?", "): ") . concat(t.message))
                       }
                   },
 source          : {}, 
 dependencies    : {}, 
 provides        : {}, 
 load            : Array(0), 
 versionWarnings : true
}


 やっぱり、for文に.next()が使われるパターンの深い理解が必要だな。かなりの頻度で使われる。ふむhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Generator/next によると、next()を使うと、value 実際に使いたい次々に取り出す配列値 と done 終了フラグ を返すそうな。終わったら true だそうです。

for(var arraydata = arrayobject, countobject = arraydata.next(); !countobject.done; countobject = arraydata.next()){ ; var contentvalue= countobject.value; }

 for文はこの形式で構成されます。初期値の処理が二つになります。配列や連想配列構造のオブジェクトarrayobjectをarraydataに受け取り、この値に対してarraydata.next()を呼び出すたびに次の配列値へと移動しますが、最初の値の取り出しでもarraydata.next()が必要なので、実行して、実際に使う変数値として、countobject に保持させます。これがfor文の初期動作です。終了条件は、実際に使う値の countobject のメンバ関数 done(ダン) を監視すればよいことになります。最後の値を処理したら、true になります。なので、終了条件は !countobject.done となります。そして、次の値を実際に使用する変数 countobject に代入しつつも、arraydata.next() を呼び出せばいいです。for文の中では実際に使う値を、contentvalue = countobject.valueのように取り出すことをするでしょう。実際、mathjax でも、実にオーソドックスに配列の値を使ったfor文を処理しています。一方で、もうちょっと変わった連想配列の繰り返し処理中で特殊なfor文(for of:キー値のみ取り出し/for in:値の取り出し)は使っていないようです。for in だと関数内で追加で定義宣言(これをよくプロトタイプ関数と言っています。)した関数が配列の中の要素として使われるので、値が関数でないかを確認しなくてはいけなくなったりします。複雑なプログラムでは実は意外と使いにくいfor in。for of で配列値を指定した場合はキーではなく、配列値が使えます。for(var val of/in】 Array){…}


 以上の事を踏まえると、t.ready と t.preLoad と t.defaultReady と t.getRoot と t.checkVersion と t.pathFileters関数が作られて、これがどこから何回呼ばれるのかを確認することで、大部分を把握できて、if(void 0 === e.mathJax.loader) が成立したとき、つまり e.MathJax.loader が undefined のときに s.combineDefaults と s.combineWithMathJax に複雑な引数が渡される処理を実施して、最後に、 i(e.MathJax.config.loader.pathFilters) の結果得られる要素全てについての処理を行う処理が実施されるので、この動きを見極めればよいことになる。


t.ready

 このメンバ関数はsampleのような単純なhtmlからだと呼ばれない関数になっていた。なので、内容をおいかけるのはあとあとです。


 

t.Load

 5万1000行目付近の r.CONFIG.load で、このt.Load は呼ばれます。呼ばれた時の argument 値は

{
  "0": "input/tex-base",
  "1": "[tex]/newcommand",
  "2": "[tex]/action",
  "3": "output/chtml",
}

 となっていました。argument を一旦、 o[0] ~ o[3] にコピーします。

 そして、o の要素数が 0 なら、return Promise.resolve(); を返して終了ですが、そのようなことはないので、もう少し処理が進みます。l = []; で空の配列と c = function(r){…}で関数を定義します。o 要素の値を h に取り込んで for文で繰り返し処理します。先ほど定義した c(h) がすぐに使われます。関数 c の中で r は h で o の要素値です。その r をつかった n = a.Package.pacakages.get(r); で n は

input/tex-base
 ⇔ n= { name : input/tex-base, isLoaded : true, isLoading : false, hasFailed : false, 
         noLoad : true, dependencyCount : 0, 
         dependencies (1)
           0 : t { 
             isLoaded: true, 
             isLoading: false, 
             hasFailed: false, 
             dependencies: Array(1), 
             dependents: Array(7), 
             …
           }
         },
         dependents : Array(7), 
         canLoad : function() { /* noLoad true ⇒ return false */
           return 
             0 === this.dependencyCount && 
             !this.noLoad && 
             !this.isLoading && 
             !this.hasFailed
         },
         promise : Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined},
         provided : [],
         reject : function(){},
         resolve : function(){},
    }
[tex]/newcommand
 ⇔ n = { name : [tex]/newcommand, isLoaded : true, isLoading : false, hasFailed : false, 
         noLoad : true, dependencyCount : 0, 
         …
       }
[tex]/action
 ⇔ n = { name : [tex]/action, isLoaded : true, isLoading : false, hasFailed : false, 
         noLoad : true, dependencyCount : 0, 
         …
       }
output/chtml
 ⇔ n = { name : output/chtml, isLoaded : true, isLoading : false, hasFailed : false, 
         noLoad : true, dependencyCount : 0, 
         …
       }
}

となります。


 関数 c では、こうして作られた4つのa.Package.pacakages.nameについて l.push という操作で Promise がスタックされるところが重要な処理になってるみたい。Promise が 具体的に何なのかを明確に理解していないのでわからないですが、大事なポイントなので後で整理するとしよう。


 そして、finally で さきほど使った next() による for 文のときにつかった 一時的に配列をスキャンしてカウントしていく p.done がちゃんと true になったか確認して !p.done が false ならちゃんと処理したものとしてエラーを出さずに最後の return 処理をします。 ここで出てくるのが a.Package.loadAll() ですが、このloadALLはMathJaxで作られた関数です。以下のような内容。

function() {
  var t, e;
  try {
  	for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) {
  		var o = n.value;
  		o.canLoad && o.load()
  	}
  } catch(e) {
  	t = {
  		error: e
  	}
  } finally {
  	try {
  		n && !n.done && (e = r.return) && e.call(r)
  	} finally {
  		if(t) throw t.error
  	}
  }
}

 this.packages.values() は、 以下のような値になっていました。

[[Entries]]
{
 0 : {"loader" => t},
 1 : {"core" => t},
 2 : {"startup" => t},
 3 : {"input/tex" => t},
 4 : {"input/tex-base" => t},
 5 : {"[tex]/ams" => t},
 6 : {"[tex]/newcommand" => t},
 7 : {"[tex]/noundefined" => t},
 8 : {"[tex]/require" => t},
 9 : {"[tex]/autoload" => t},
10 : {"[tex]/configmacros" => t},
11 : {"input/mml" => t},
12 : {"output/chtml" => t},
13 : {"output/chtml/fonts/tex.js" => t},
14 : {"ui/menu" => t},
15 : {"a11y/assistive-mml" => t},
16 : {"[tex]/action" => t},
}

 this.packages.values() の 要素 を n とすると、 n.value.name として得られるのが上記の値です。


 この値って何だろうって考えたとき、 最初の

  "0": "input/tex-base",
  "1": "[tex]/newcommand",
  "2": "[tex]/action",
  "3": "output/chtml",

 から増えているので、chtml形式の出力のときに使われる、ファイル群なのではないかと思いました。なのでそういうファイルがes5ディレクトリの中にあるか確認をしてみました。すると

core.js ある!
loader.js ある!
startup.js ある!
a11y/assistive-mml.js ある!
input/mml.js ある!
input/tex.js ある!
input/tex-base.js ある!
output/chtml.js ある!
output/chtml/fonts/tex.js ある!
ui/menu.js ある!

 で[tex]というのが input/tex/extensions ならば

input/tex/extensions/action.js ある!
input/tex/extensions/ams ある!
input/tex/extensions/autoload ある!
input/tex/extensions/configmacros ある!
input/tex/extensions/newcommand ある!
input/tex/extensions/noundefined ある!
input/tex/extensions/require ある!

 という関係なので、ファイル名を差してるんだろうね。てことはこのクソ長い5万行のプログラムに加えて、17の外部ファイルが、読み込まれる可能性がありそう。ということなので、すべてのファイルを自動整形して、1行目にブレークポイントを設定しておく必要がありそう。1行目が呼ばれるかどうかはわかないのだけど、とりあえずってことです。


 そして、o.canLoad は 以下のような関数で

{
  return 
    0 === this.dependencyCount && 
    !this.noLoad && 
    !this.isLoading && 
    !this.hasFailed
}

 this.noLoad が true なので retrun は false になります。!this.noLoad ではじめて 0 になるので、この値が return の対象になります。先にも登場しましたが、念のため記述しておくと、JAVA Script 論理演算子と関数の代入式の併用に詳しい説明があります。

o.canLoad && o.load()

も同じ理屈で o.canLoad までが処理対象になります。ちなみに o.load は以下のような関数です。

function() {
  if(!this.isLoaded && !this.isLoading && !this.noLoad) {
  	this.isLoading = !0;
  	var e = t.resolvePath(this.name);
  	l.CONFIG.require ? this.loadCustom(e) : this.loadScript(e)
  }
}


 今回は処理されない部分なので、こちらとしても深追いしません。走ったとしても noLoad のところで false なので if文の中の命令は処理されません。


 んで、やっぱり、ちゃんとfor文の処理がされたかをfinalyの処理で確かめてますね。これは定石のように何回も登場する例外処理です。

 

t.preLoad

 r.Loader.preLoad("loader"), r.Loader.load.apply(r.Loader, function(t) {…} 後ろから探した方が早いくらいのとこにあるこの部分で実行されます。


 最初のfor文の中で使われる argument 引数は、以下のようなものです。

{
  "0": "loader",
  "1": "startup",
  "2": "core",
  "3": "input/tex",
  "4": "input/mml",
  "5": "output/chtml",
  "6": "output/chtml/fonts/tex.js",
  "7": "ui/menu",
  "8": "a11y/assistive-mml",
}

 これはファイル群です。html 側で定義した Load ファイルは、'input/tex-base', '[tex]/newcommand', '[tex]/action', 'output/chtml'でしたが、それとは違う Load ファイルとなります。この 9 つの引数を n[o] の配列にコピー。この 9 つのファイル名について、for文で


 u = a.Package.packages.get(c);


 a.Package.packages に結び付いた ファイル名の Key に対応する値を取得し u に格納します。 u に何も結び付いていなければ、次の行の処理をします。基本的には、最初の呼び出しでは何も結び付いていないので、次の処理をするのが目的だと思います。u = new a.Package(c, !0) を実行すると void 0 === r && (r = !1), this.isLoaded = !1, this.isLoading = !1, this.hasFailed = !1, this.dependents = [], this.dependencies = [], this.dependencyCount = 0, this.provided = [], this.name = e, this.noLoad = r, t.packages.set(e, this), this.promise = this.makePromise(this.makeDependencies()) という設定がなされて、name が ファイル名で、t(⇔a.Packages).pakeages.set(ファイル名)で、それ以外のメンバ変数が各種設定がされたような a.Package.Packeges.get(ファイル名) が構築されます。 因みに promise は非同期処理の順序処理に関する定義をするものです。これらのファイルの処理に関する非同期を制御する予定があるんだなと思っとけばいいかな。その制御をする makePromise メンバ関数と makeDependencies メンバ関数があります。


makePromise関数はこんな感じ。

function(t) {
  var e = this,
  r = new Promise(
    (
      function(t, r) {
        e.resolve = t, e.reject = r
      }
    )
  ),
  n = l.CONFIG[this.name] || {};
  return n.ready && 
         (
           r = r.then(
             (
               function(t) {
  	             return n.ready(e.name)
               }
             )
           )
         ), 
         t.length && 
         (
           t.push(r),
           r = Promise.all(t).then(
             (
               function(t) {
  	             return t.join(", ")
               }
             )
           )
         ), 
         n.failed && 
         r.catch(
           (
             function(t) {
  	           return n.failed(new c(t, e.name))
             }
           )
         ), 
         r /*カンマ演算子の最右値 return 対象*/
}

 引数の最初 t は 次の処理を指定していて、第二引数が非同期処理が失敗した時の値のようですね。全部の処理が終わった時も t が次の処理になるようになっているように見えます。全部の処理が終わったかをみる場合の t は文字列っぽいですね。全部の名前文字列を , 区切りで結合して保持する処理があったりしています。l.CONFIG[ファイル名]の .ready というメンバ変数に呼び出し元オブジェクトで関数を入れておけば、それが、このPromiseを宣言した時点で実行されそうにも見えます。r は Promise の 結果であるオブジェクトが持たれていて、 PromiseStatus : Pending or Resolved or Rejected と PromiseResult undefined or 成功時の関数・変数 t のもしくは 失敗時の変数 r の呼び出しが保持されます。非同期処理中の成功・失敗はデバッグ処理でもたついたときにも発生するので、あまり長いことブレークしすぎないような工夫をしながら、デバッグする必要もありそうです。見る時は一か所でじっくり見て、次に進むときは再起動して次のポイントを見るとかね。


 最初の呼び出しはファイル名 r = [tex]/action でした。


 (n = new a.Package(r)).provides(e.CONFIG.provides[r])


 Packageを定義しましたが、.provides では e.CONFIG.provides[r] が undefined なので、特に何もしませんでした。ふむ。


 次にn.CheckNoLoad()が実行されます。以下のような関数です。


function() {
  var t, e;
  if(this.noLoad) {
  	this.noLoad = !1;
  	try {
  		for(var r = i(this.dependencies), n = r.next(); !n.done; n = r.next()) {
  			n.value.checkNoLoad()
  		}
  	} catch(e) {
  		t = {
  			error: e
  		}
  	} finally {
  		try {
  			n && !n.done && (e = r.return) && e.call(r)
  		} finally {
  			if(t) throw t.error
  		}
  	}
  }
}


 NoLoad は最初 true でしたが、反転します。そして、最初のファイル名についてのi(this.dependencies)の結果についても個別にCheckNoLoadを再帰して呼び出します。


 i は下に示すような関数で ファイル名のオブジェクト this.dependencies {0: {isLoaded: true, isLoading: false, hasFailed: false, dependents: Array(7), dependencies: Array(1), …} } はこんな感じです。ずっとループする this.dependencies[0].dependencies[0].… とループする構造です。

function(t) {
var e = "function" == typeof Symbol && Symbol.iterator,
	r = e && t[e],
	n = 0;
if(r) return r.call(t);
if(t && "number" == typeof t.length) return {
	next: function() {
		return t && n >= t.length && (t = void 0), {
			value: t && t[n++],
			done: !t
		}
	}
};
throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
}

 "function" == typeof Symbol は true なので、e = Symbol.iterator で、  r = e と同じ結果により if(r)が true で r.call(t) が return 対象で、{isLoaded: true, isLoading: false, hasFailed: false, dependents: Array(7), dependencies: Array(1), …} が 返却されます。noLoad はないので n.valuse.CheckNoLoad() は 最初のif文でfalseなので、なにも帰さないです。再帰から脱出です。


 そして l.push(n.Promise.then(…)) によって非同期処理の完了後処理がスタックされていきます。


 o = ['input/tex-base', '[tex]/newcommand', '[tex]/action', 'output/chtml']で、繰り返して処理をします。


最後にreturn a.Package.loadAll(), Promise.all(l)で LoadAll()は以下のような関数を実行します。

function() {
  var t, e;
  try {
  	for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) {
  		var o = n.value;
  		o.canLoad && o.load()
  	}
  } catch(e) {
  	t = {
  		error: e
  	}
  } finally {
  	try {
  		n && !n.done && (e = r.return) && e.call(r)
  	} finally {
  		if(t) throw t.error
  	}
  }
}

 各ファイル名のオブジェクトについて、o.canload が true なら load() を実行していく感じですね。ファイルをロードするのにものすごく非同期処理の順序を気にしている感じがします。確実に全部読み込んでから実行するためなのでしょう。エラーは絶対に出さないという強い意志を感じる凄みがあります。

 

t.defaultReady

 void 0 !== e.MathJax.startup && e.MathJax.config.startup.ready() の一行の処理で、e.MathJax.startup は定義されていれば、void 0 !== e.MathJax.startup が true で、定義されていたら、e.MathJax.config.startup.ready() を実行するというのが全て。呼ばれるのは、最後から4行目あたりのreturn r.CONFIG.ready()で呼ばれる。


 e.MathJax.config.startup.ready() は、以下のような関数。

{
  f(), 
  d(), 
  t.pagePromise.then(
    (
      function() {
  	    return e.CONFIG.pageReady()
      }
    )
  ).then(
    (
      function() {
  	    return t.promiseResolve()
      }
    )
  ).catch(
    (
      function(e) {
  	    return t.promiseReject(e)
      }
    )
  )
}

 f() は、

a = new e.MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor,
l = e.MathJax._.mathjax.mathjax, 
t.input = v(), 
t.output = _(), 
t.adaptor = S(), 
t.handler && l.handlers.unregister(t.handler), 
t.handler = M(), 
t.handler && (l.handlers.register(t.handler), 
t.document = O())

 一行だからってなめてたけど、めっちゃ処理されるじゃん。深い!a = { NodeHandlers : Map(5){size: 5, inferredMrow => ƒ (t, e), annotation => ƒ (t, e), TeXAtom => ƒ (t, e), text => ƒ (t, e), XML => ƒ (t, e)}

 e.MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor は、こんな関数


e.MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor = function r() {
  return null !== t && t.apply(this, arguments) || this
}


この時の t は


t = function e(e){
  return 
    void 0 === e && (e = null), 
    e || (e = new i.MmlFactory), 
    t.call(this, e) || this
}


 で、e は undefined で、返却される値は t.call(this, e) || this で、e が いったん null値になった後、e = new i.MmlFactoryfunction e() { return null !== t && t.apply(this, arguments) || this } がその内容になっています。


e {
  defaultKind: 'unknown',
  nodeMap: Map(42) {size: 42, 
    math => function e() {
              return null !== t && t.apply(this, arguments) || this
            }
    mi => ƒ e(), 
    mn => ƒ e(), 
    mo => ƒ e(), 
    mtext => ƒ e(), 
    mspace
ms => ƒ e(),
mrow => ƒ e(),
inferredMrow => ƒ e(),
mfrac => ƒ e(),
msqrt => ƒ e(),
mroot => ƒ e(),
mstyle => ƒ e(),
merror => ƒ e(),
mpadded => ƒ e(),
mphantom => ƒ e(),
mfenced => ƒ e(),
menclose => ƒ e(),
maction => ƒ e(),
msub => ƒ e(),
msup => ƒ e(),
musubsup => ƒ e(),
munder => ƒ e(),
mover => ƒ e(),
munderover => ƒ e(),
mmultiscripts => ƒ e(),
mprescript => ƒ e(),
none => ƒ e(),
mtable => ƒ e(),
mlabeledtr => ƒ e(),
mtr => ƒ e(),
mtd => ƒ e(),
maligngtoup => ƒ e(),
malignmark => ƒ e(),
mglyph => ƒ e(),
semantics => ƒ e(),
annotation => ƒ e(),
annotation-xml => ƒ e(),
TexAtom => ƒ e(),
MathChoice => ƒ e(),
text => ƒ e(),
XML  => ƒ e(),
}
  node: {
    annotation     : ƒ {
                         for(var t = [], e = 0; e < arguments.length; e++) 
                           t[e] = arguments[e];
                         return new(
                           i.bind.apply(
                             i,
                             o(
                               [void 0, r], 
                               n(t), 
                               !1
                             )
                           )
                         )
                       },
    annotation-xml : ƒ,
    inferredMrow   : ƒ,
    maction        : ƒ,
    maligngroup    : ƒ,
    malignmark     : ƒ,
    math           : ƒ,
    MathChoice     : ƒ,
    menclose       : ƒ,
    merror         : ƒ,
    mfenced        : ƒ,
    mfrac          : ƒ,
    mglyph         : ƒ,
    mi             : ƒ,
    mlabeledtr     : ƒ,
    mmultiscripts  : ƒ,
    mn             : ƒ,
    mo             : ƒ,
    mover          : ƒ,
    mpadded        : ƒ,
    mphantom       : ƒ,
    mprescripts    : ƒ,
    mroot          : ƒ,
    mrow           : ƒ,
    ms             : ƒ,
    mspace         : ƒ,
    msqrt          : ƒ,
    mstyle         : ƒ,
    msub           : ƒ,
    msubsup        : ƒ,
    msup           : ƒ,
    mtable         : ƒ,
    mtd            : ƒ,
    mtext          : ƒ,
    mtr            : ƒ,
    munder         : ƒ,
    munderover     : ƒ,
    mnone          : ƒ,
    semantics      : ƒ,
    TeXAtom        : ƒ,
    text           : ƒ,
    XLM            : ƒ,

  math: ƒ, mi: ƒ, mn: ƒ, mo: ƒ, mtext: ƒ, …}
}

となってから、t.call(this, e) || this が 呼び出される。


t は、以下のような関数です。


ƒ t(e) {
  var r, o;
  this.nodeHandlers = new Map;
  try {
    for(var i = n(e.getKinds()), s = i.next(); !s.done; s = i.next()) {
      var a = s.value,
        l = this[t.methodName(a)];
      l && this.nodeHandlers.set(a, l)
    }
  } catch(t) {
    r = {
      error: t
    }
  } finally {
    try {
      s && !s.done && (o = i.return) && o.call(i)
    } finally {
      if(r) throw r.error
    }
  }
}


 s.value は Mathから始まる値で a に代入される。"visit" + (t.charAt(0).toUpperCase() + t.substr(1)).replace(/[^a-z0-9_]/gi, "_") + "Node" が返されて、t.methodName(a) は visitMathNodeが返ってくる。そのような名前の配列がthisに無いので undefined になることが多い。Mathの部分が一文字が大文字になったものと残りの文字をくっつける。その結果でも先頭文字が数字だったり小文字だったり_だったりしたら_に置き換える。という処理をする。ふむ。this.nodeHandlers.set で元の a の値と 変換された結果得られる this.['visitXxxxxNode'] が undefined だろうと 何かあったろうと ついにして set に設定する。残りの命令は例によって、for文が最後まで処理できたかを確認している。そうやってできたオブジェクト t を返却として準備する。


 そして、l = {version: '3.2.2', handlers: e, document: ƒ (t, r) {return e.mathjax.handlers.document(t, r)}, handleRetriesFor: ƒ (t) {return new Promise((function e(r, n) {try {r(t())} catch(t) {t.retry && t.retry instanceof Promise ? t.retry.then((function() {return e(r, n)})).catch((function(t) {return n(t)})) : t.restart && t.restart.isCallback ? MathJax.Callback.After((function() {return e(r, n)}), t.restart) : n(t)}}))}, retryAfter: ƒ (t) {var e = new Error("MathJax retry");throw e.retry = t, }, asyncLoad :ƒ (t) {return MathJax.loader.load(t)}, handlers : { item: [] }} 次に t.input の関数 v()は…とその前に…


 まだf() の最初の2行しか追えてない。残り7行あるし、d()も、その次も残ってる。ナゲー。preLoad やばい。

t.input = v(), 
t.output = _(), 
t.adaptor = S(), 
t.handler && l.handlers.unregister(t.handler), 
t.handler = M(), 
t.handler && (l.handlers.register(t.handler), 
t.document = O())

 困ったね。

 それで v()なんだけども

function v() {
  var r, n, i = [];
  try {
  	for(var s = o(e.CONFIG.input), a = s.next(); !a.done; a = s.next()) {
  		var l = a.value,
  			c = t.constructors[l];
  		if(!c) throw Error('Input Jax "' + l + '" is not defined (has it been loaded?)');
  		i.push(new c(e.MathJax.config[l]))
  	}
  } catch(t) {
  	r = {
  		error: t
  	}
  } finally {
  	try {
  		a && !a.done && (n = s.return) && n.call(s)
  	} finally {
  		if(r) throw r.error
  	}
  }
  return i
}


 e.CONFIG.input = ['tex', 'mml'] で、t.constructors[l] は、以下のような関数です。


t.constructors[l] = function(r) {
  void 0 === r && (r = {});
  var n = this,
  o = s(
    (
      0, 
      c.separateOptions
    )
    (
      r, 
      e.OPTIONS, 
      u.FindTeX.OPTIONS
    ),
    3
  ),
  i = o[0],
  a = o[1],
  l = o[2];

  (
    n = t.call(this, a) || 
        this
  ).findTeX = n.options.FindTeX || 
              new u.FindTeX(l);

  var h = n.options.packages,
  f = n.configuration = e.configure(h),
  d = n._parseOptions = new m.default(f, [n.options, y.TagsFactory.OPTIONS]);
  
  return 
    (
      0, 
      c.userOptions
    )
    (
      d.options,
      i
    ), 
    f.config(n), 
    e.tags(d, f), 
    n.postFilters.add(p.default.cleanSubSup, -6), 
    n.postFilters.add(p.default.setInherited, -5), 
    n.postFilters.add(p.default.moveLimits, -4),
    n.postFilters.add(p.default.cleanStretchy, -3), 
    n.postFilters.add(p.default.cleanAttributes, -2), 
    n.postFilters.add(p.default.combineRelations, -1), 
}

 なげー。プログラムだらけだな。これを c として、引数に e.MathJax.config[l] を迎え、 i.push(new c(e.MathJax.config['tex'])) → i.push(new c(e.MathJax.config['mml']))のように順番に i にプッシュされてスタックを作る。e.MathJax.config['tex']には inline のキーワード が格納されている。よく使われるのは $$ , $$とか \( , \) とかですね。inlineMath packages とか身近なキーワードうれしい。この命令は html で 設定した config の tex や mml を取得する部分なんだろうなって思う。だけど上のプログラムの引数 r がそうなんだけど inline とか packages がまだないね。淋しい。あー packages あるわ。n.option.packages ね。ってことは 今回は mml は定義してないから何もしない感じだな。


 また関数が呼ばれる s は以下のようなモノ。

s = function(t, e) {
  var r = "function" == typeof Symbol && t[Symbol.iterator];
  if(!r) return t;
  var n, o, i = r.call(t),
  	s = [];
  try {
  	for(;
  		(void 0 === e || e-- > 0) && !(n = i.next()).done;) s.push(n.value)
  } catch(t) {
  	o = {
  		error: t
  	}
  } finally {
  	try {
  		n && !n.done && (r = i.return) && r.call(i)
  	} finally {
  		if(o) throw o.error
  	}
  }
  return s
}

 s の呼び出しの第一引数が複雑な表記になっている。第二は 3 だけ。短っ。


第一引数の手前のカッコの一番右がカンマ構文では有効な値で、 c.separateOptions は関数で、その後ろのカッコが引数になっていると思われる。ここでもまたカンマ構文があるがここは引数なので、カンマ左の一つ目が採用される。なので自分で設定した r が使われる。 r が設定されていなかったら2つめ、3つめが使われるんだっけか。この表記の意味がわかんねぇ。調べよっか。


これが u.FindTeX.OPTIONS の値。 デフォルトの tex の option 値 っぽいのが設定されている。おー知ってるのまた出てきた。

{
  inlineMath: [
    ["\\(", "\\)",],
  ],
  displayMath: [
    ["$$",  "$$", ],
    ["\\[", "\\]",],
  ],
  processEscapes: true,
  processEnvironments: true,
  processRefs: true,
}


 以下が、c.separateOptions関数。


function(t) {
  for(var e, n, o, i, s = [], a = 1; a < arguments.length; a++) s[a - 1] = arguments[a];
  var l = [];
  try {
  	for(var c = r(s), u = c.next(); !u.done; u = c.next()) {
  		var p = u.value,
  			h = {},
  			f = {};
  		try {
  			for(var d = (o = void 0, r(Object.keys(t || {}))), m = d.next(); !m.done; m = d.next()) {
  				var y = m.value;
  				(void 0 === p[y] ? f : h)[y] = t[y]
  			}
  		} catch(t) {
  			o = {
  				error: t
  			}
  		} finally {
  			try {
  				m && !m.done && (i = d.return) && i.call(d)
  			} finally {
  				if(o) throw o.error
  			}
  		}
  		l.push(h), t = f
  	}
  } catch(t) {
  	e = {
  		error: t
  	}
  } finally {
  	try {
  		u && !u.done && (n = c.return) && n.call(c)
  	} finally {
  		if(e) throw e.error
  	}
  }
  return l.unshift(t), l
}


 あーなるほど。なるほど、動かしてみると引数が3つあった意味が分かった。全部使われるんだね。関数名の横の引数名をあたえるところが一つしかなくても、変数に格納されないだけで argument という特別な変数には、全部の引数が配列に格納されてました。こういう方法もあるんだな。t はユーザが設定した引数。 s[0] に第二引数、s[1] に第三引数が格納される感じで使う。なぜこうやってねじるようなことをするのかはわからない。


 それで、最初にユーザ指定しなかった s の配列変数から先に処理をします。グローバルの r 関数 に引数 s[0] s[1]の順で処理した値を初期値にします。 最初はこんな値を u が保持します。

{
  FindTeX: null,
  packages: ["base",],
  digits: { /^(?:[0-9]+(?:\\{,\\}[0-9]{3})*(?:\\.[0-9]*)?|\\.[0-9]+)/ },
  maxBuffer: 5120,
  formatError: function(t, e) {
    return t.formatError(e)
  },
}

Object.keys(t || {}) = ['inlineMath', 'packages'] はユーザが指定したオプションです。このkeyと u の値のキーと一致するものがあるかないかを2項演算子で比較して あれば ? の後ろの f[y] = t[y] なければ h[y] = t[y] となります。h[y] には一致しない属性が集まります。f[y] には一致した属性があつまります。 全てを処理したら、l.push(h) で新しい属性を lにプッシュします。, 重複したキーの値は t = f で t に格納します。 最後に関数はスタックした l から t を 取り除いて、l を 返却します。


lが設定されれば、s の関数では {} {} {} のような結果が o の配列を構成し、 r に config tex の設定が格納されます。3つの配列の値をそれぞれ、i a l に格納します。その後 h f d に config値が取り込まれます。


 d() は

function d() {
  var e, r;
  t.input && t.output && m();
  var n = t.output ? t.output.name.toLowerCase() : "";
  try {
  	for(var i = o(t.input), s = i.next(); !s.done; s = i.next()) {
  		var a = s.value,
  			l = a.name.toLowerCase();
  		g(l, a), b(l, a), t.output && y(l, n, a)
  	}
  } catch(t) {
  	e = {
  		error: t
  	}
  } finally {
  	try {
  		s && !s.done && (r = i.return) && r.call(i)
  	} finally {
  		if(e) throw e.error
  	}
  }
}

 

t.getRoot

 

t.checkVersion

 

t.pathFileters

 

s.combineDefaults

 

s.combineWithMathJax

 

i(e.MathJax.config.loader.pathFilters)

   

__webpack_modules__[265].call(r.exports, r, r.exports, __webpack_require__)

			265: function(t, e, r) {
				var n, o = this && this.__extends || (n = function(t, e) {
						return n = Object.setPrototypeOf || {
							__proto__: []
						}
						instanceof Array && function(t, e) {
							t.__proto__ = e
						} || function(t, e) {
							for(var r in e) Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r])
						}, n(t, e)
					}, function(t, e) {
						if("function" != typeof e && null !== e) throw new TypeError("Class extends value " + String(e) + " is not a constructor or null");

						function r() {
							this.constructor = t
						}
						n(t, e), t.prototype = null === e ? Object.create(e) : (r.prototype = e.prototype, new r)
					}),
					i = this && this.__values || function(t) {
						var e = "function" == typeof Symbol && Symbol.iterator,
							r = e && t[e],
							n = 0;
						if(r) return r.call(t);
						if(t && "number" == typeof t.length) return {
							next: function() {
								return t && n >= t.length && (t = void 0), {
									value: t && t[n++],
									done: !t
								}
							}
						};
						throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
					},
					s = this && this.__read || function(t, e) {
						var r = "function" == typeof Symbol && t[Symbol.iterator];
						if(!r) return t;
						var n, o, i = r.call(t),
							s = [];
						try {
							for(;
								(void 0 === e || e-- > 0) && !(n = i.next()).done;) s.push(n.value)
						} catch(t) {
							o = {
								error: t
							}
						} finally {
							try {
								n && !n.done && (r = i.return) && r.call(i)
							} finally {
								if(o) throw o.error
							}
						}
						return s
					},
					a = this && this.__spreadArray || function(t, e, r) {
						if(r || 2 === arguments.length)
							for(var n, o = 0, i = e.length; o < i; o++) !n && o in e || (n || (n = Array.prototype.slice.call(e, 0, o)), n[o] = e[o]);
						return t.concat(n || Array.prototype.slice.call(e))
					};
				Object.defineProperty(e, "__esModule", {
					value: !0
				}), e.Package = e.PackageError = void 0;
				var l = r(235),
					c = function(t) {
						function e(e, r) {
							var n = t.call(this, e) || this;
							return n.package = r, n
						}
						return o(e, t), e
					}(Error);
				e.PackageError = c;
				var u = function() {
					function t(e, r) {
						void 0 === r && (r = !1), this.isLoaded = !1, this.isLoading = !1, this.hasFailed = !1, this.dependents = [], this.dependencies = [], this.dependencyCount = 0, this.provided = [], this.name = e, this.noLoad = r, t.packages.set(e, this), this.promise = this.makePromise(this.makeDependencies())
					}
					return Object.defineProperty(t.prototype, "canLoad", {
						get: function() {
							return 0 === this.dependencyCount && !this.noLoad && !this.isLoading && !this.hasFailed
						},
						enumerable: !1,
						configurable: !0
					}), t.resolvePath = function(t, e) {
						void 0 === e && (e = !0);
						var r = {
							name: t,
							original: t,
							addExtension: e
						};
						return l.Loader.pathFilters.execute(r), r.name
					}, t.loadAll = function() {
						var t, e;
						try {
							for(var r = i(this.packages.values()), n = r.next(); !n.done; n = r.next()) {
								var o = n.value;
								o.canLoad && o.load()
							}
						} catch(e) {
							t = {
								error: e
							}
						} finally {
							try {
								n && !n.done && (e = r.return) && e.call(r)
							} finally {
								if(t) throw t.error
							}
						}
					}, t.prototype.makeDependencies = function() {
						var e, r, n = [],
							o = t.packages,
							c = this.noLoad,
							u = this.name,
							p = [];
						l.CONFIG.dependencies.hasOwnProperty(u) ? p.push.apply(p, a([], s(l.CONFIG.dependencies[u]), !1)) : "core" !== u && p.push("core");
						try {
							for(var h = i(p), f = h.next(); !f.done; f = h.next()) {
								var d = f.value,
									m = o.get(d) || new t(d, c);
								this.dependencies.indexOf(m) < 0 && (m.addDependent(this, c), this.dependencies.push(m), m.isLoaded || (this.dependencyCount++, n.push(m.promise)))
							}
						} catch(t) {
							e = {
								error: t
							}
						} finally {
							try {
								f && !f.done && (r = h.return) && r.call(h)
							} finally {
								if(e) throw e.error
							}
						}
						return n
					}, t.prototype.makePromise = function(t) {
						var e = this,
							r = new Promise((function(t, r) {
								e.resolve = t, e.reject = r
							})),
							n = l.CONFIG[this.name] || {};
						return n.ready && (r = r.then((function(t) {
							return n.ready(e.name)
						}))), t.length && (t.push(r), r = Promise.all(t).then((function(t) {
							return t.join(", ")
						}))), n.failed && r.catch((function(t) {
							return n.failed(new c(t, e.name))
						})), r
					}, t.prototype.load = function() {
						if(!this.isLoaded && !this.isLoading && !this.noLoad) {
							this.isLoading = !0;
							var e = t.resolvePath(this.name);
							l.CONFIG.require ? this.loadCustom(e) : this.loadScript(e)
						}
					}, t.prototype.loadCustom = function(t) {
						var e = this;
						try {
							var r = l.CONFIG.require(t);
							r instanceof Promise ? r.then((function() {
								return e.checkLoad()
							})).catch((function(r) {
								return e.failed("Can't load \"" + t + '"\n' + r.message.trim())
							})) : this.checkLoad()
						} catch(t) {
							this.failed(t.message)
						}
					}, t.prototype.loadScript = function(t) {
						var e = this,
							r = document.createElement("script");
						r.src = t, r.charset = "UTF-8", r.onload = function(t) {
							return e.checkLoad()
						}, r.onerror = function(r) {
							return e.failed("Can't load \"" + t + '"')
						}, document.head.appendChild(r)
					}, t.prototype.loaded = function() {
						var t, e, r, n;
						this.isLoaded = !0, this.isLoading = !1;
						try {
							for(var o = i(this.dependents), s = o.next(); !s.done; s = o.next()) {
								s.value.requirementSatisfied()
							}
						} catch(e) {
							t = {
								error: e
							}
						} finally {
							try {
								s && !s.done && (e = o.return) && e.call(o)
							} finally {
								if(t) throw t.error
							}
						}
						try {
							for(var a = i(this.provided), l = a.next(); !l.done; l = a.next()) {
								l.value.loaded()
							}
						} catch(t) {
							r = {
								error: t
							}
						} finally {
							try {
								l && !l.done && (n = a.return) && n.call(a)
							} finally {
								if(r) throw r.error
							}
						}
						this.resolve(this.name)
					}, t.prototype.failed = function(t) {
						this.hasFailed = !0, this.isLoading = !1, this.reject(new c(t, this.name))
					}, t.prototype.checkLoad = function() {
						var t = this;
						((l.CONFIG[this.name] || {}).checkReady || function() {
							return Promise.resolve()
						})().then((function() {
							return t.loaded()
						})).catch((function(e) {
							return t.failed(e)
						}))
					}, t.prototype.requirementSatisfied = function() {
						this.dependencyCount && (this.dependencyCount--, this.canLoad && this.load())
					}, t.prototype.provides = function(e) {
						var r, n;
						void 0 === e && (e = []);
						try {
							for(var o = i(e), s = o.next(); !s.done; s = o.next()) {
								var a = s.value,
									c = t.packages.get(a);
								c || (l.CONFIG.dependencies[a] || (l.CONFIG.dependencies[a] = []), l.CONFIG.dependencies[a].push(a), (c = new t(a, !0)).isLoading = !0), this.provided.push(c)
							}
						} catch(t) {
							r = {
								error: t
							}
						} finally {
							try {
								s && !s.done && (n = o.return) && n.call(o)
							} finally {
								if(r) throw r.error
							}
						}
					}, t.prototype.addDependent = function(t, e) {
						this.dependents.push(t), e || this.checkNoLoad()
					}, t.prototype.checkNoLoad = function() {
						var t, e;
						if(this.noLoad) {
							this.noLoad = !1;
							try {
								for(var r = i(this.dependencies), n = r.next(); !n.done; n = r.next()) {
									n.value.checkNoLoad()
								}
							} catch(e) {
								t = {
									error: e
								}
							} finally {
								try {
									n && !n.done && (e = r.return) && e.call(r)
								} finally {
									if(t) throw t.error
								}
							}
						}
					}, t.packages = new Map, t
				}();
				e.Package = u
			},
			2388: function(t, e, r) {
				var n = this && this.__assign || function() {
						return n = Object.assign || function(t) {
							for(var e, r = 1, n = arguments.length; r < n; r++)
								for(var o in e = arguments[r]) Object.prototype.hasOwnProperty.call(e, o) && (t[o] = e[o]);
							return t
						}, n.apply(this, arguments)
					},
					o = this && this.__values || function(t) {
						var e = "function" == typeof Symbol && Symbol.iterator,
							r = e && t[e],
							n = 0;
						if(r) return r.call(t);
						if(t && "number" == typeof t.length) return {
							next: function() {
								return t && n >= t.length && (t = void 0), {
									value: t && t[n++],
									done: !t
								}
							}
						};
						throw new TypeError(e ? "Object is not iterable." : "Symbol.iterator is not defined.")
					},
					i = this && this.__read || function(t, e) {
						var r = "function" == typeof Symbol && t[Symbol.iterator];
						if(!r) return t;
						var n, o, i = r.call(t),
							s = [];
						try {
							for(;
								(void 0 === e || e-- > 0) && !(n = i.next()).done;) s.push(n.value)
						} catch(t) {
							o = {
								error: t
							}
						} finally {
							try {
								n && !n.done && (r = i.return) && r.call(i)
							} finally {
								if(o) throw o.error
							}
						}
						return s
					},
					s = this && this.__spreadArray || function(t, e, r) {
						if(r || 2 === arguments.length)
							for(var n, o = 0, i = e.length; o < i; o++) !n && o in e || (n || (n = Array.prototype.slice.call(e, 0, o)), n[o] = e[o]);
						return t.concat(n || Array.prototype.slice.call(e))
					};
				Object.defineProperty(e, "__esModule", {
					value: !0
				}), e.CONFIG = e.MathJax = e.Startup = void 0;
				var a, l = r(9515),
					c = r(8666),
					u = r(7233);
				! function(t) {
					var a, l, u = new c.PrioritizedList;

					function h(e) {
						return a.visitTree(e, t.document)
					}

					function f() {
						a = new e.MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor, l = e.MathJax._.mathjax.mathjax, t.input = v(), t.output = _(), t.adaptor = S(), t.handler && l.handlers.unregister(t.handler), t.handler = M(), t.handler && (l.handlers.register(t.handler), t.document = O())
					}

					function d() {
						var e, r;
						t.input && t.output && m();
						var n = t.output ? t.output.name.toLowerCase() : "";
						try {
							for(var i = o(t.input), s = i.next(); !s.done; s = i.next()) {
								var a = s.value,
									l = a.name.toLowerCase();
								g(l, a), b(l, a), t.output && y(l, n, a)
							}
						} catch(t) {
							e = {
								error: t
							}
						} finally {
							try {
								s && !s.done && (r = i.return) && r.call(i)
							} finally {
								if(e) throw e.error
							}
						}
					}

					function m() {
						e.MathJax.typeset = function(e) {
							void 0 === e && (e = null), t.document.options.elements = e, t.document.reset(), t.document.render()
						}, e.MathJax.typesetPromise = function(e) {
							return void 0 === e && (e = null), t.document.options.elements = e, t.document.reset(), l.handleRetriesFor((function() {
								t.document.render()
							}))
						}, e.MathJax.typesetClear = function(e) {
							void 0 === e && (e = null), e ? t.document.clearMathItemsWithin(e) : t.document.clear()
						}
					}

					function y(r, n, o) {
						var i = r + "2" + n;
						e.MathJax[i] = function(e, r) {
							return void 0 === r && (r = {}), r.format = o.name, t.document.convert(e, r)
						}, e.MathJax[i + "Promise"] = function(e, r) {
							return void 0 === r && (r = {}), r.format = o.name, l.handleRetriesFor((function() {
								return t.document.convert(e, r)
							}))
						}, e.MathJax[n + "Stylesheet"] = function() {
							return t.output.styleSheet(t.document)
						}, "getMetricsFor" in t.output && (e.MathJax.getMetricsFor = function(e, r) {
							return t.output.getMetricsFor(e, r)
						})
					}

					function g(r, n) {
						var o = e.MathJax._.core.MathItem.STATE;
						e.MathJax[r + "2mml"] = function(e, r) {
							return void 0 === r && (r = {}), r.end = o.CONVERT, r.format = n.name, h(t.document.convert(e, r))
						}, e.MathJax[r + "2mmlPromise"] = function(e, r) {
							return void 0 === r && (r = {}), r.end = o.CONVERT, r.format = n.name, l.handleRetriesFor((function() {
								return h(t.document.convert(e, r))
							}))
						}
					}

					function b(t, r) {
						e.MathJax[t + "Reset"] = function() {
							for(var t = [], e = 0; e < arguments.length; e++) t[e] = arguments[e];
							return r.reset.apply(r, s([], i(t), !1))
						}
					}

					function v() {
						var r, n, i = [];
						try {
							for(var s = o(e.CONFIG.input), a = s.next(); !a.done; a = s.next()) {
								var l = a.value,
									c = t.constructors[l];
								if(!c) throw Error('Input Jax "' + l + '" is not defined (has it been loaded?)');
								i.push(new c(e.MathJax.config[l]))
							}
						} catch(t) {
							r = {
								error: t
							}
						} finally {
							try {
								a && !a.done && (n = s.return) && n.call(s)
							} finally {
								if(r) throw r.error
							}
						}
						return i
					}

					function _() {
						var r = e.CONFIG.output;
						if(!r) return null;
						var n = t.constructors[r];
						if(!n) throw Error('Output Jax "' + r + '" is not defined (has it been loaded?)');
						return new n(e.MathJax.config[r])
					}

					function S() {
						var r = e.CONFIG.adaptor;
						if(!r || "none" === r) return null;
						var n = t.constructors[r];
						if(!n) throw Error('DOMAdaptor "' + r + '" is not defined (has it been loaded?)');
						return n(e.MathJax.config[r])
					}

					function M() {
						var r, n, i = e.CONFIG.handler;
						if(!i || "none" === i || !t.adaptor) return null;
						var s = t.constructors[i];
						if(!s) throw Error('Handler "' + i + '" is not defined (has it been loaded?)');
						var a = new s(t.adaptor, 5);
						try {
							for(var l = o(u), c = l.next(); !c.done; c = l.next()) {
								a = c.value.item(a)
							}
						} catch(t) {
							r = {
								error: t
							}
						} finally {
							try {
								c && !c.done && (n = l.return) && n.call(l)
							} finally {
								if(r) throw r.error
							}
						}
						return a
					}

					function O(r) {
						return void 0 === r && (r = null), l.document(r || e.CONFIG.document, n(n({}, e.MathJax.config.options), {
							InputJax: t.input,
							OutputJax: t.output
						}))
					}
					t.constructors = {}, t.input = [], t.output = null, t.handler = null, t.adaptor = null, t.elements = null, t.document = null, t.promise = new Promise((function(e, r) {
						t.promiseResolve = e, t.promiseReject = r
					})), t.pagePromise = new Promise((function(t, e) {
						var n = r.g.document;
						if(n && n.readyState && "complete" !== n.readyState && "interactive" !== n.readyState) {
							var o = function() {
								return t()
							};
							n.defaultView.addEventListener("load", o, !0), n.defaultView.addEventListener("DOMContentLoaded", o, !0)
						} else t()
					})), t.toMML = h, t.registerConstructor = function(e, r) {
						t.constructors[e] = r
					}, t.useHandler = function(t, r) {
						void 0 === r && (r = !1), e.CONFIG.handler && !r || (e.CONFIG.handler = t)
					}, t.useAdaptor = function(t, r) {
						void 0 === r && (r = !1), e.CONFIG.adaptor && !r || (e.CONFIG.adaptor = t)
					}, t.useInput = function(t, r) {
						void 0 === r && (r = !1), p && !r || e.CONFIG.input.push(t)
					}, t.useOutput = function(t, r) {
						void 0 === r && (r = !1), e.CONFIG.output && !r || (e.CONFIG.output = t)
					}, t.extendHandler = function(t, e) {
						void 0 === e && (e = 10), u.add(t, e)
					}, t.defaultReady = function() {
						f(), d(), t.pagePromise.then((function() {
							return e.CONFIG.pageReady()
						})).then((function() {
							return t.promiseResolve()
						})).catch((function(e) {
							return t.promiseReject(e)
						}))
					}, t.defaultPageReady = function() {
						return e.CONFIG.typeset && e.MathJax.typesetPromise ? e.MathJax.typesetPromise(e.CONFIG.elements) : Promise.resolve()
					}, t.getComponents = f, t.makeMethods = d, t.makeTypesetMethods = m, t.makeOutputMethods = y, t.makeMmlMethods = g, t.makeResetMethod = b, t.getInputJax = v, t.getOutputJax = _, t.getAdaptor = S, t.getHandler = M, t.getDocument = O
				}(a = e.Startup || (e.Startup = {})), e.MathJax = l.MathJax, void 0 === e.MathJax._.startup && ((0, l.combineDefaults)(e.MathJax.config, "startup", {
					input: [],
					output: "",
					handler: null,
					adaptor: null,
					document: "undefined" == typeof document ? "" : document,
					elements: null,
					typeset: !0,
					ready: a.defaultReady.bind(a),
					pageReady: a.defaultPageReady.bind(a)
				}), (0, l.combineWithMathJax)({
					startup: a,
					options: {}
				}), e.MathJax.config.startup.invalidOption && (u.OPTIONS.invalidOption = e.MathJax.config.startup.invalidOption), e.MathJax.config.startup.optionError && (u.OPTIONS.optionError = e.MathJax.config.startup.optionError)), e.CONFIG = e.MathJax.config.startup;
				var p = 0 !== e.CONFIG.input.length
			},

 

関連情報

 MathJax

 

VScodeで簡易WebServerの元でデバッグに戻る。