Source code:
var wgPageName = mw.config.get('wgPageName');
var wgCurRevisionId = mw.config.get('wgCurRevisionId');
// var wgAction = mw.config.get('wgAction');
var wgAction = new URLSearchParams(location.href).get("action"); // detect fake actions
function HistoryGraph() {
if(wgCurRevisionId == 0) return;
var hist = document.getElementById("ca-history");
if(!hist) return;
mw.util.addPortletLink('p-cactions', `/w/index.php?title=${wgPageName}&action=graph`, 'Graph',
'ca-graph', "View graph of page history", null, "#ca-move");
if(wgAction != "graph") return;
var contentDiv = document.getElementById("mw-content-text");
contentDiv.innerHTML = '';
var fakeTitle = wgPageName.replace(/_/g, " ") + ": History graph";
document.getElementById("firstHeading").innerHTML = fakeTitle;
document.title = fakeTitle;
var ym = new Date().toJSON().match(/^(\d+)\-(\d+)/);
var yyyy = parseInt(ym[1]), mm = parseInt(ym[2]), d1 = "01T00:00:00Z";
var f = function(d) { return d.toString().padStart(2,'0'); }
var url_base_dbg = '/w/api.php?action=query&prop=revisions&rvlimit=1&rvprop=size|timestamp|ids';
var url_base = '/w/api.php?action=query&prop=revisions&rvlimit=1&rvprop=size&format=json';
var f1 = function(r1) { return r1.json(); };
var sz = [];
var doneLoading = function() {
var divs = contentDiv.getElementsByTagName("div");
divs[divs.length-1].innerHTML += " (No more found!)";
sz.push(0);
drawGraph();
}
var loop = function() {
var time = [f(yyyy), f(mm), d1].join('-');
var url_i = `&titles=${wgPageName}&rvstart=${time}`;
var url = url_base + url_i;
var dbg_url = url_base_dbg + url_i;
contentDiv.innerHTML += `<div><a href="${dbg_url}">Fetching last edit before ${time}...</a></div>`;
var f2 = function(r2) {
var jtxt = JSON.stringify(r2.query);
var ms = jtxt.match(/"size"\:(\d+)/);
if(!ms) { doneLoading(); return; }
sz.push(parseInt(ms[1]));
mm--;
if(mm==0) { mm = 12; yyyy--; }
loop();
};
fetch(url).then(f1).then(f2);
};
loop();
var drawGraph = function() {
var height = 480, color = "red", stroke_width = 2;
var q = "JFMAMJJASOND";
sz.reverse();
var max = Math.max.apply(null, sz);
var dl = Math.pow(10, Math.floor(Math.log10(max)-0.5));
var dx = 20;
var dy = height/max;
var lmax = Math.floor(max/dl)*dl;
var width = dx * (sz.length + 1);
var pts = [];
var vpath=[], vtext = [], ytext = [];
for(var i = 0; i < sz.length; i++) {
pts.push(`${(i * dx)}, ${height - (sz[i] * dy)}`);
var x = i * dx;
var h = (mm==1)? (height+40) : height;
vpath.push(`<path d="M${x},0 L${x},${h}" />`);
vtext.push(`<text x="${x}" y="${height+20}">${q[mm-1]}</text>`);
if(i == 0 || mm == 1)
ytext.push(`<text x="${x}" y="${height+40}">${yyyy}</text>`);
mm++;
if(mm > 12) { mm = 1; yyyy++; }
}
var hpath = [], htext = [];
for(var i = 0; i <= lmax; i += dl) {
var y = height - (i * dy);
hpath.push(`<path d="M0,${y} L${width},${y}" />`);
htext.push(`<text x="40" y="${y}">${i}</text>`);
}
height += 60;
var bkg = `<rect x="0" y="0" width="${width}" height="${height}" fill="white" />`;
var path = `<path d="M${pts.join(' L')}" stroke="${color}" stroke-width="${stroke_width}" fill="none" />`;
var g1 = `<g stroke="gray" stroke-width="1" fill="none">${hpath.join('')}${vpath.join('')}</g>`;
var g2 = `<g font-family="monospace" font-size="12px" text-align="right" color="black">${htext.join('')}${vtext.join('')}${ytext.join('')}</g>`;
var svg = `<svg width="${width}" height="${height}">${bkg}${g1}${path}${g2}</svg>`;
contentDiv.innerHTML = svg;
var blobUrl = URL.createObjectURL(new Blob([svg], {type:"image/svg+xml;charset=utf-8"}));
contentDiv.innerHTML += `<a href="${blobUrl}" download="HistoryGraph-${wgPageName}.svg"><button>Download</button></a>`;
}
}
HistoryGraph();
For background, details, and caveats of this script, please read:
- Development: Wikipedia:Village pump (technical)/Archive 176#Page file size graph
- Actual usage: Talk:Donald Trump/Archive 106#File size graph
- Example output: File:Mandruss-Trump-graph1.png