User:ATDT/global.js
Appearance
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
$(function () {
const parsereport = mw.config.get('wgPageParseReport');
const origin = parsereport.cachereport.origin;
const walltime = parsereport.limitreport.walltime * 1000;
const li = document.createElement('li');
li.innerText = 'Parsed by ' + origin + ' in ' + walltime + 'ms.';
document.getElementById('footer-info').appendChild(li);
});
/**
* Perf menu by Krinkle (revision 11 Feb 2021).
*/
var domReady = new Promise(function (resolve) { document.readyState === 'complete' ? setTimeout(resolve) : document.onreadystatechange = setTimeout.bind(null, resolve); });
domReady.then(function () {
var prevPortlet;
var perfMenu;
var nt;
var ppr;
var ppr14;
var pprInt14;
var nowInt14;
var browserResp = 'unknown';
var ttfb = null;
var respParts = [];
var cdnResp = 'unknown';
var beParts = [];
var beResp = 0;
var pcParts = [];
var pcResp = 0;
function addItem(texts) {
if (perfMenu) {
var item = document.createElement('li');
item.className = 'perf-textonly';
if (Array.isArray(texts)) {
texts.forEach(function (text, i) {
if (text === null) {
return;
}
if (i !== 0) {
item.append(document.createElement('br'));
}
item.append(text);
});
} else {
item.textContent = texts;
}
perfMenu.append(item);
}
}
function conf(key) {
return window.mw && window.mw.config && window.mw.config.get(key);
}
function tfmt(ms) {
// use milliseconds upto 9000 ms, then use seconds
return ms > 9000 ? (ms / 1000).toFixed(3) + ' s' : Math.round(ms).toLocaleString() + ' ms';
}
function percent(total, sub) {
var ratio = (sub / total) * 100;
return Math.floor(ratio) + '%';
}
// Vector skin
prevPortlet = document.querySelector('#p-variants.vector-menu');
if (prevPortlet) {
prevPortlet.insertAdjacentHTML('afterend', ''
+ '<style>#p-perf li.perf-textonly {'
+ ' padding: 0.42em 0.625em;'
+ ' line-height: 1.4;'
+ ' font-size: 0.8125em;'
+ ' white-space: nowrap;'
+ '}</style>'
+ '<nav id="p-perf" class="mw-portlet vector-menu vector-menu-dropdown" aria-labelledby="p-perf-label" role="navigation">'
+ '<input type="checkbox" class="vector-menu-checkbox" aria-labelledby="p-perf-label"><h3 id="p-perf-label"><span>⏱</span></h3>'
+ '<div class="vector-menu-content"><ul class="vector-menu-content-list"></ul></div></nav>'
);
}
// Monobook skin
prevPortlet = document.querySelector('#p-tb.portlet');
if (prevPortlet) {
prevPortlet.insertAdjacentHTML('afterend', ''
+ '<div role="navigation" class="portlet" id="p-perf" aria-labelledby="p-perf-label">'
+ '<h3 id="p-perf-label" dir="ltr" lang="en">⏱ Performance</h3>'
+ '<div class="pBody"><ul dir="ltr" lang="en"></ul></div></div>'
);
}
// Timeless skin
prevPortlet = document.querySelector('#p-pagemisc.mw-portlet');
if (prevPortlet) {
prevPortlet.insertAdjacentHTML('afterend', ''
+ '<style>#p-perf ul { color: #555; line-height: 1; font-size: 85%; }</style>'
+ '<div role="navigation" class="mw-portlet" id="p-perf" aria-labelledby="p-perf-label">'
+ '<h3 id="p-perf-label" dir="ltr" lang="en">⏱ Performance</h3>'
+ '<div class="mw-portlet-body"><ul dir="ltr" lang="en"></ul></div></div>'
);
}
// Minerva skin
prevPortlet = document.querySelector('footer.minerva-footer');
if (prevPortlet) {
prevPortlet.insertAdjacentHTML('beforeend', ''
// Use hlist for font style, but use separate lines
+ '<style>#p-perf {'
+ 'margin-top: 1rem;'
+ 'overflow: visible;'
+ '}'
+ '#p-perf li {'
+ 'display: list-item;'
+ 'list-style: circle outside;'
+ 'margin-left: 0.5rem;'
+ '}</style>'
+ '<div id="p-perf" class="post-content footer-content">'
+ '<h2>⏱ Performance</h2>'
+ '<ul class="hlist"></ul></div>');
}
perfMenu = document.querySelector('#p-perf ul');
try {
// Navigation Timing API
nt = performance.getEntriesByType('navigation')[0];
ttfb = nt.responseStart;
// Resource Timing API
if (nt.transferSize === 0) {
browserResp = 'local cache (no network)';
} else if (nt.transferSize > 0 && nt.encodedBodySize > 0 && nt.transferSize < nt.encodedBodySize ) {
browserResp = 'local cache (after HTTP 304)';
} else {
browserResp = 'fresh HTTP 200';
}
respParts.push('Response time: ' + tfmt(ttfb));
// Server Timing API
if (nt.serverTiming[0].name === 'cache') {
// One of "hit", "hit-front", "miss" or "pass"
cdnResp = nt.serverTiming[0].description;
}
// MediaWiki-specific: config on all HTML responses
beResp = (cdnResp.includes('hit') || browserResp.includes('cache')) ? 0 : conf('wgBackendResponseTime');
if (beResp) {
beParts.push('MediaWiki backend: ' + conf('wgHostname'));
} else {
beParts.push('MediaWiki backend: (cache hit)');
beParts.push('(cached) host: ' + conf('wgHostname'));
// Only show dedicated entry here if cached (and thus not shown in respParts)
beParts.push('(cached) duration: ' + tfmt(conf('wgBackendResponseTime')));
}
respParts.push(
'➙ ' + percent(ttfb, ttfb - beResp) + ' 🌐 Internet connection: ' + tfmt(ttfb - beResp),
Object.assign(document.createElement('small'), { textContent: ' time between browser and CDN' })
);
if (beResp) {
respParts.push('➙ ' + percent(ttfb, beResp) + ' 🌻 MediaWiki backend: ' + tfmt(beResp));
}
// MediaWiki-specific: on when action=view, on a page that exists, is local, and has wikitext content.
ppr = conf('wgPageParseReport');
if (ppr.cachereport && ppr.limitreport) {
ppr14 = ppr.cachereport.timestamp;
// This is the timestamp after parsing is done when it is about to saved.
// Therefore, below we don't need to account for parse time itself.
pprInt14 = Number(ppr14);
// "2020-10-18T23:50:34.799Z" -> 20201018235034
nowInt14 = Number(new Date(performance.timeOrigin).toISOString().replace(/([-T:]|\..*$)/g, ''));
// Assume cache reuse, unless same host and under 5 seconds ago.
pcResp = (ppr.cachereport.origin === conf('wgHostname') && (nowInt14 - pprInt14) < 5) ? (ppr.limitreport.walltime * 1000) : 0;
if (pcResp) {
respParts.push('➙➙ ' + percent(ttfb, pcResp) + ' 🧮 MediaWiki parser: ' + tfmt(pcResp));
respParts.push('➙➙ ' + percent(ttfb, beResp - pcResp) + ' 🖼 MediaWiki skin: ' + tfmt(beResp - pcResp));
pcParts.push('MediaWiki parser: miss (freshly parsed)');
} else {
respParts.push('➙➙ ' + percent(ttfb, pcResp) + ' 🧮 MediaWiki parser: ' + tfmt(pcResp));
respParts.push('➙➙ ' + percent(ttfb, beResp - pcResp) + ' 🖼 MediaWiki skin: ' + tfmt(beResp - pcResp));
pcParts.push('MediaWiki parser: (cache hit)');
pcParts.push('(cached) host: ' + ppr.cachereport.origin);
// Only show dedicated entry here if cached (and thus not shown in respParts)
pcParts.push('(cached) duration: ' + tfmt(ppr.limitreport.walltime * 1000));
// Only show if cached, otherwise uninteresting
pcParts.push('(cached) timestamp: ' + ppr14.slice(0, 4) + '-' + ppr14.slice(4, 6) + '-' + ppr14.slice(6, 8) +
' ' + ppr14.slice(8, 10) + ':' + ppr14.slice(10, 12) + ':' + ppr14.slice(12, 14) + ' (UTC)'
);
}
}
} catch (e) {
// Ignored
}
addItem('Response: ' + browserResp);
addItem(respParts);
addItem('CDN response: ' + cdnResp);
addItem(beParts);
addItem(pcParts);
});