', '');
}
return html;
}
function setLang(code){
if(LANGS.indexOf(code) < 0) code = 'ja';
LANG = code;
try {
document.documentElement.setAttribute('lang', LANG_ATTR[code]);
document.documentElement.setAttribute('data-lang', code);
} catch(e){}
if(window.ORBIX && window.ORBIX.setSearchLang) window.ORBIX.setSearchLang(code);
}
function prefix(code){ return code === 'ja' ? '' : code + '/'; }
var frame = document.getElementById('orbix-frame');
var loading = document.getElementById('orbix-loading');
var zline = document.getElementById('orbix-zapline');
var zflash = document.getElementById('orbix-zapflash');
var pendingSub = '';
var first = true;
var busy = false;
var current = null; // route currently shown; zap only fires when leaving the top
var REDUCED = false;
try { REDUCED = matchMedia('(prefers-reduced-motion: reduce)').matches; } catch(e){}
// プチュン: squash the current page into a bright line, collapse to a dot, flash.
/* ============================================================
遷移にかける時間
------------------------------------------------------------
v46 で、ひと続きの遷移をそれぞれ 0.5 秒だけ長くした。
足す先を選んでいる。指を離してから次のページが「読めるように
なるまで」を伸ばすと、それは演出ではなく、ただの待ちになる。
だから伸ばすのは主に「出てくる側」で、消える側はほとんど
元のまま。プチュンの歯切れは、この短さが作っている。
プチュン(トップから出るとき) 380 + 520 = 900ms
→ 480 + 920 = 1400ms
ふんわり(それ以外) 170 + 460 = 630ms
→ 220 + 910 = 1130ms
============================================================ */
var T = {
zapSquash: 260, /* 潰れて一本の線になるまで */
zapDot: 220, /* 線が点になって消えるまで */
zapLine: 540, /* 走る光の線(装飾。潰れ+点に合わせる) */
zapFlash: 430, /* 弾けるひかり(装飾) */
zapFlashDelay: 115,
bloom: 920, /* 新しいページが中心から開くまで */
softOut: 220, /* ふんわり:前のページが引くまで */
softIn: 910 /* ふんわり:新しいページが出てくるまで */
};
function zapOut(el){
var keep = [];
var squash = el.animate(
[{transform:'scale(1,1)',filter:'brightness(1)',opacity:1},
{transform:'scale(1.04,0.02)',filter:'brightness(2.1) saturate(1.4)',opacity:1}],
{duration:T.zapSquash,easing:'cubic-bezier(.7,0,.85,.4)',fill:'forwards'});
keep.push(squash);
zline.animate(
[{opacity:0,transform:'translateY(-50%) scaleX(.1)'},
{opacity:1,transform:'translateY(-50%) scaleX(1)',offset:.45},
{opacity:1,transform:'translateY(-50%) scaleX(1)',offset:.62},
{opacity:0,transform:'translateY(-50%) scaleX(0)'}],
{duration:T.zapLine,easing:'cubic-bezier(.22,.9,.28,1)'});
return squash.finished.then(function(){
var dot = el.animate(
[{transform:'scale(1.04,0.02)',filter:'brightness(2.1)',opacity:1},
{transform:'scale(0,0.02)',filter:'brightness(3)',opacity:0}],
{duration:T.zapDot,easing:'cubic-bezier(.6,0,.9,.4)',fill:'forwards'});
keep.push(dot);
zflash.animate([{opacity:0},{opacity:1,offset:.35},{opacity:0}],
{duration:T.zapFlash,easing:'ease-out',delay:T.zapFlashDelay});
return dot.finished.then(function(){ return keep; });
});
}
// 開花: the new page blooms out from a central point.
function bloomIn(el){
return el.animate(
[{clipPath:'circle(0% at 50% 50%)',transform:'scale(.94)',filter:'brightness(1.7)',opacity:1},
{clipPath:'circle(75% at 50% 50%)',transform:'scale(1)',filter:'brightness(1)',opacity:1}],
{duration:T.bloom,easing:'cubic-bezier(.22,.9,.28,1)'}).finished;
}
function resolve(r){
r = (r || '').replace(/^#/, '');
var sub = '';
var hi = r.indexOf('#');
if(hi >= 0){ sub = r.slice(hi); r = r.slice(0, hi); }
/* 先頭の en/ ko/ zh/ は言語の指定。#en/media#reports のように書ける。 */
var lang = 'ja';
var m = /^(en|ko|zh)\/(.*)$/.exec(r);
if(m){ lang = m[1]; r = m[2]; }
if(ALIASES.hasOwnProperty(r)) r = ALIASES[r];
if(!known(r)) r = DEFAULT;
return { route: r, sub: sub, lang: lang };
}
function applySub(){
if(!pendingSub) return;
var sub = pendingSub;
pendingSub = '';
// set the embedded page's own hash so its SPA panels / anchor scrolling react
try{ frame.contentWindow.location.hash = sub; }catch(e){}
// 一覧を JS で描くページでは、読み込んだ直後の位置が当てにならない。
// 描画が落ち着いて位置が動かなくなるまで、数回ねらい直す。
/* scrollIntoView の直後に測ると必ず 0 になるので、
「落ち着いたか」では判定できない。決めた回数だけ、間を置いてねらい直す。
画面外のカードが描かれて高さが変わっても、最後には合う。 */
var tries = 0;
(function aim(){
try{
var el = frame.contentDocument.querySelector(sub);
if(el) el.scrollIntoView({block:'start'});
}catch(_){}
if(++tries < 7) setTimeout(aim, 130);
})();
}
/* ふんわり:いったん静かに引いて、新しい面が浮かび上がる。
トップから出るときの「プチュン」とは別の、移動中の所作。 */
function softSwap(html){
var out;
try{
out = frame.animate([{opacity:1, filter:'blur(0px)'},
{opacity:0, filter:'blur(3px)'}],
{duration:T.softOut, easing:'cubic-bezier(.4,0,.9,.5)', fill:'forwards'});
}catch(e){ out = null; }
var go = function(){
frame.onload = function(){
try{ if(out) out.cancel(); }catch(e){}
applySub();
if(loading) loading.classList.add('hidden');
try{
frame.animate([{opacity:0, transform:'translateY(10px) scale(.994)', filter:'blur(4px)'},
{opacity:1, transform:'none', filter:'blur(0px)'}],
{duration:T.softIn, easing:'cubic-bezier(.22,.9,.28,1)'});
}catch(e){}
busy = false; drain();
};
frame.srcdoc = html;
};
if(out && out.finished) out.finished.then(go, go); else setTimeout(go, 0);
}
function swap(html){
frame.onload = function(){ applySub(); if(loading) loading.classList.add('hidden'); };
frame.srcdoc = html;
}
/* 押されたが、まだ届いていないページ。届いたら開く。 */
var waitingFor = null, waitTimer = null, waitBound = false;
function waitAndLoad(raw){
waitingFor = raw;
if(loading) loading.classList.remove('hidden');
if(waitTimer) return;
waitTimer = setInterval(function(){
if(waitingFor === null){ clearInterval(waitTimer); waitTimer = null; return; }
var r = resolve(waitingFor);
if(ready(document.getElementById('op-' + r.route))){
clearInterval(waitTimer); waitTimer = null;
var raw2 = waitingFor; waitingFor = null;
load(raw2);
}
}, 60);
/* 読み込みが終わってもまだ無いなら、それは本当に存在しないページ */
if(!waitBound){
waitBound = true;
document.addEventListener('DOMContentLoaded', function(){
setTimeout(function(){
if(waitingFor === null) return;
var raw3 = waitingFor; waitingFor = null;
if(waitTimer){ clearInterval(waitTimer); waitTimer = null; }
load(raw3);
}, 0);
});
}
}
/* 遷移中にもう一度押されたぶん。捨てずに覚えておき、終わったら最後の1件を開く。
捨てていたころは、URL だけ先に変わって中身が追いつかなかった。 */
var queued = null;
function drain(){
if(queued === null) return;
var q = queued; queued = null;
load(q);
}
function load(raw){
var res = resolve(raw);
/* 名簿には載っているのに、まだ中身が届いていない */
if(!pageHtml(res.route)){ waitAndLoad(raw); return; }
// プチュン fires ONLY for transitions that leave the top page (index-stage).
var fromTop = (current === DEFAULT);
pendingSub = res.sub;
if(!cat(res.lang)){ waitAndLoad(raw); return; } /* 対訳表がまだ届いていない */
var langChanged = (res.lang !== LANG);
setLang(res.lang);
var html = localize(pageHtml(res.route), res.lang);
// 外側のタブタイトルを、表示中のページのものに合わせる。
// 全ページ同じ題名だと、タブを並べたときにどれがどれか分からなくなる。
try{
var tm = /