JavaScript: 縮めろ

主にhtmlやjsを圧縮する企画です。超高速に展開できる優れ物。圧縮率も悪くありません。
圧縮手法はLZを採用。binary tree match finderで速度、optimal parsingで圧縮力を稼ぎます。
`[\0-\x7f]`以外の文字列は特殊変換するため、性能が悪くなります。
設計図が薄汚いのは仕様です。気にしてはいけません。
```js
/*
A2LZe(T,ws,ES6,IE,fix,type)
@T :[\0-\x7f]からなる文字列. それ以外は特殊変換される
@ws :最長一致.範囲は0～127. 大きくすると辞書が小さくなる
@ES6 :ECMAScriptのversionが5か6+かの真偽
@IE :InternetExplorer対応かどうかの真偽
@fix :[\x80-\uffff]の変換法.
	0はUnicode番号に応じて桁数調整するが復号器が複雑.
	1だと4桁固定で復号器が単純
@type: 復号法. 圧縮率が変わる
	0:for loop
	1:再帰(巨大文字列苦手)
	2:for loop & substr未使用
	3:for loop & longest match>255. 巨大文字列向き
*/
function A2LZe(T,ws,ES6,IE,fix,type){
	var e=(fix?/\1/:/[\1-\3]/).test(T),f=/[\x80-\uffff]/.test(T)&&[],pf=fix?["\x0100","\x010","\1"]:["\1","\2","\3"];
	e&&f?T=T.replace(fix?/\1/g:/[\1-\3]/g,function(a){return a+a}):e=0;
	T=(f?T.replace(/[\x80-\uffff]/g,function(a,b){
		a=a.charCodeAt();b=a.toString(16);
		return(a<256?f[0]=pf[0]:a<4096?f[1]=pf[1]:f[2]=pf[2])+b
	}):T).split("");
	var MIN=3,H=[],N=[],O=[],a=0,b=65308,c,d,h,i,k=0,l=32655,m,n,o=0,p,r,s,v,w,x,z=T.length;
	if(!z)return"";
	for(ws+=2;ws>b/--l>>0;);ws=l;
	var Max=b/ws|0,M2=Max+1,M3=Max,ex=ES6?{92:1,96:1}:{10:1,13:1,34:+/"/.test(T),39:+/'/.test(T),92:1};
	for(type>2&&(Max+=0xd7f1,M2=Max+1);Max>>++k;);
	// Pass 1: Find all matches
	for(x=(1<<k)-1;a<z;a++){
		v=0,m=z-a,p=o;
		if(m>2){
			r=a%ws,l=r+ws,b=c=0,w=a<ws?-1:a-ws,s=H[h=T[a]+T[a+1]+T[a+2]];
			m>M2&&(m=M2);
			for(H[h]=a;s>w;){
				i=b<c?b:c;
				if(T[s+i]===T[a+i]){
					for(;i++<m&&T[s+i]===T[a+i];);
					if(i>v){
						if(s+i<a||type>1)O[++o]=~s+a<<k|i;
						else{
							for(n=m,h=s,v=a;h>w&&T[h-1]===T[--v]&&n--;)--h;
							for(v=0;T[h+v]===T[a+v]&&h+v<a&&v<m;)++v;
							if(v<a-s)O[++o]=~s+a<<k|a-s;
							else if(v>2)O[++o]=~h+a<<k|v
						}v=i;
						if(i>m){N[r]=N[s%=ws];N[l]=N[s+ws];m=0;break}}
				}
				if(T[s+i]<T[a+i])N[r]=s,s=N[r=s%ws+ws],c=i;
				else N[l]=s,s=N[l=s%ws],b=i}
			if(m)N[l]=N[r]=-1}
		O[p]=o++-p}
	// Pass 2: Build the shortest path
	a=p=N[0]=N.length=H.length=0;
	for(v=M3+MIN-1;a<z;h>=N[a]||(N[a]=h,H[a]=c)){
		if(n=O[p++])for(c=N[a],h=MIN-1;n--;){
			l=O[p++];d=l>>>k;l&=x;r=d*M3;
			b=c+2;r-=MIN;d<<=k;
			if(type<3)for(;h<l;s>=N[a+h]||(N[a+h]=s,H[a+h]=d+h))m=++h+r,s=m<1923?b:m<55296||57343<m?b+1:b+4;
			else for(;h<l;s>=N[a+h]||(N[a+h]=s,H[a+h]=d+h))m=++h<v?h+r:v+r,s=m<1923?b:m<55296||57343<m?b+1:b+4,h<v||(s++,s+=ex[h-v+14]||0)
		}
		h=N[a]+1,c=T[a++]
	}
	for(o=p=O.length=0;c=N[p++]=H[a],a-=c.big?1:c&x;);
	// Pass 3: Output the codes
	H=String.fromCharCode;
	if(type<3)for(;l=N[--p];)
		O[o++]=l.big?l:H((l>>>k)*M3+(l&x)+125);
	else for(M2=M3+MIN-1;l=N[--p];)
		if(l.big)O[o++]=l;
		else O[o++]=H((l>>>k)*M3+(l&=x,l<M2?l:M2)+125),l<M2||(O[o++]=H(l-M2+14));

	// build decoder
	for(b=c=d=h=i=0;o;)s=O[--o],b+=s==="\n"||s==="\r",c+=s==='"',d+=s==="'",h+=s==="$"&&O[o+1]==="{",i+=s==="`";
	O=O.join("").replace(/\\/g,'\\\\').replace(/\0/g,'\\0').replace(/[\ud800-\udfff]/g,function(a){return"\\u"+a.charCodeAt().toString(16)}); // avoid '\' '\0' and surrogate pair

	if(ES6&&b+c+d>h+i)r="`",O=O.replace(/\$\{/g,"\\${").replace(/`/g,'\\`');
	else O=O.replace(/\n/g,'\\n').replace(/\r/g,'\\r').replace(RegExp(r=d>c?'"':"'","g"),"\\"+r).replace(/[\u2028\u2029]/g,function(a){return"\\u"+a.charCodeAt().toString(16)}); // LineTerminator can't be included

	if(f){
		if(fix)f=["\1([\\da-f]{4})"];
		else f[a=0]&&(f[a++]=f[0]+"([\\da-f]{2})"),f[1]&&(f[a++]=f[1]+"([\\da-f]{3})"),f[2]&&(f[a++]=f[2]+"([\\da-f]{4})"),f.length=a;
		s="String.fromCharCode('0x'+"+(a>1?"a.slice(1)":"b")+")",b=ES6?"(a,b)=>"+s:"function(a,b){return "+s+"}";
	}
	f=f?".replace(/"+f.join("|").replace(a>1?/[()]/g:"","")+"/g,"+b+")":"";
	e=e?".replace(/("+(fix?"\1":"[\1-\3]")+"){2}/g,"+(ES6?"(a,b)=>b":"function(a,b){return b}")+")"+f:f;
	if(!type){
		// for loop. it can decode big text
		a="{for(;c=i.charCodeAt(++p);)o+=c<128?i"+(IE?".charAt(p)":"[p]")+":o.substr(~((c-=128)/"+Max+")"+(IE?"+o.length":"")+",c%"+Max+"+3);return o}"+(ES6?")":"");
		return(ES6?"((i,o,p,c)=>":"function(i,o,p,c)")+a+"("+r+O+r+","+r+r+",-1)"+e
	}
	if(type<2){
		// recursive loop. best for small text
		a="(c=i.charCodeAt(p))?f(i,o+(c<128?i"+(IE?".charAt(p)":"[p]")+":o.substr(~((c-=128)/"+Max+")"+(IE?"+o.length":"")+",c%"+Max+"+3)),++p):o";
		return ES6?"(f=(i,o,p,c)=>"+a+")("+r+O+r+","+r+r+",0)"+e:"function f(i,o,p,c){return"+a+"}("+r+O+r+","+r+r+",0)"+e
	}
	// for loop & longest match>255. best for big text
	a="{for(;c=i[h](++p);)if(c<128)o[n++]=i"+(IE?".charAt(p)":"[p]")+";else for(a=~((c-=128)/"+M3+")+n,c%="+M3+(type<3?"":","+--M3+">c||(c+=i[h](++p)-14)")+";--c+4;)o[n++]=o[a++];return o.join('')}"+(ES6?")":"");
	return(ES6?"((i,o,p,n,h,c,a)=>":"function(i,o,p,n,h,c,a)")+a+"("+r+O+r+",[],-1,0,'charCodeAt')"+e
}
```
出力文字列は即時関数に囲まれているので、`eval()`や`document.write()`に放り込んだり、変数に代入する等して下さい。ついでに言うとUTF8形式です。以下codepenによる実演。
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="ExzbYZr" data-pen-title="A2LZ" data-user="xezz" style="height:300px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;border:2px solid;margin:1em 0;padding:1em"> <span>See the Pen <a href="https://codepen.io/xezz/pen/ExzbYZr">A2LZ</a> by xezz (<a href="https://codepen.io/xezz">@xezz</a>) on <a href="https://codepen.io">CodePen</a>.</span></p><script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 関数の説明的解説
* `T`は入力文字列。圧縮されます
* `ws`は最長一致。範囲は0～127。大きくすると辞書が小さくなる(ws=0だと32640、ws=127だと506)
* `ES6`はECMAScriptのversionが5か6+かの真偽。trueの方が復号器が小さい
* `IE`はInternet Explorer対応かどうかの真偽。具体的には`substr`の仕様対策
* `fix`は`[\x80-\uffff]`の変換法。0はUnicode番号に応じて桁数調整するが復号器が複雑。1だと4桁固定で復号器が単純
* `type`は復号法

	* 0: for loop
	* 1: 再帰。巨大文字列は復号できない
	* 2: for loop。0より圧縮しやすいが、復号器は大きくなる
	* 3: for loop。`ws`の値に関係なく最長一致無制限になる。巨大文字列に向いている