User:Nardog/Consecudiff.js

(mw.config.get('wgNamespaceNumber') || mw.config.get('wgAction') !== 'view') && (function consecudiff {	mw.loader.addStyleTag('.consecudiff::before{content:" ["} .consecudiff::after{content:"]"} .consecudiff-top::before{content:" ⟨"} .consecudiff-top::after{content:"⟩"}');	let isHist = mw.config.get('wgAction') === 'history';	class Consecudiff {		constructor(lis, isContribs) {			this.isContribs = isContribs;			this.isEnhanced = !isHist && !isContribs &&				lis[0].classList.contains('mw-enhanced-rc');			this.threshold = isContribs ? window.consecudiffContribsThreshold || 120				: isHist ? window.consecudiffHistThreshold || 720				: window.consecudiffThreshold || 720;			this.strictMode = !isContribs &&				!!window.consecudiffDetectInterruptions;			this.diffSelector = isHist				? 'a.mw-history-histlinks-previous'				: '.mw-changeslist-diff';			this.permaSelector = this.isEnhanced && '.mw-enhanced-rc-time > a' ||				(isHist || isContribs) && 'a.mw-changeslist-date';			this.hybridSelector = this.diffSelector; if (this.permaSelector) { this.hybridSelector += ', ' + this.permaSelector; }			this.topClass = isContribs ? 'mw-contributions-current' : 'mw-changeslist-last'; let dependencies = ['mediawiki.util']; if ((isHist || isContribs) && mw.config.get('wgUserLanguage') !== 'en') { dependencies.push('mediawiki.language.months'); }			mw.loader.using(dependencies, => {				let chunks;				if (isHist) {					chunks = this.chunkByUser(lis);				} else {					chunks = [];					this.groupByTitle(lis).forEach(group => { chunks.push(...this.chunkByUser(group)); });				}				let subchunks = [];				chunks.forEach(chunk => { subchunks.push(...this.divideByDate(chunk)); });				let linkPairs = [];				subchunks.forEach(subchunk => { linkPairs.push(...this.makeLinks(subchunk)); });				linkPairs.forEach(([$span, parent]) => { $span.appendTo(parent); });			});		}		groupByTitle(lis) { let selector = this.isContribs ? '.mw-contributions-title' : '.mw-changeslist-title'; let lisByTitle = {}; lis.forEach(li => {				let link = (this.isEnhanced ? li.closest('table') : li)					.querySelector(selector);				if (!link) return;				let title = link.textContent;				if (!lisByTitle.hasOwnProperty(title)) lisByTitle[title] = [];				lisByTitle[title].push(li);			}); return Object.values(lisByTitle).filter(group => group.length > 1); }		chunkByUser(lis) { if (this.isSingleContribs) return [lis]; let chunks = [], lastSplitAt = 0, prevUser; this.isSingleContribs = lis.some((li, i) => {				let link = li.querySelector('.mw-userlink');				if (!link) return this.isContribs;				let user = link.textContent;				if (i && user !== prevUser) {					chunks.push(lis.slice(lastSplitAt, i));					lastSplitAt = i;				}				prevUser = user;			}); if (this.isSingleContribs) return [lis]; chunks.push(lis.slice(lastSplitAt)); return chunks.filter(chunk => chunk.length > 1); }		divideByDate(lis) { let chunks = [], lastSplitAt = 0, prevDate; lis.forEach((li, i) => {				let date;				if (isHist || this.isContribs) {					date = this.parseDate( li.querySelector('.mw-changeslist-date').textContent );				} else {					date = Date.parse( li.dataset.mwTs.replace(/(....)(..)(..)(..)(..)(..)/, '$1-$2-$3T$4:$5:$6Z') );				}				if (date) date = date / 60000;				if (i && prevDate - date > this.threshold) {					chunks.push(lis.slice(lastSplitAt, i));					lastSplitAt = i;				}				prevDate = date;				if (!this.strictMode || lastSplitAt === i) return;				let prevDiff = lis[i - 1].querySelector(this.diffSelector);				if (prevDiff) {					let prevNext = mw.util.getParamValue('oldid', prevDiff.search);					if (prevNext !== li.dataset.mwRevid) {						chunks.push(lis.slice(lastSplitAt, i));						lastSplitAt = i;					}				}			}); chunks.push(lis.slice(lastSplitAt)); return chunks.filter(chunk => chunk.length > 1); }		makeLinks(lis) { let count = lis.length; let firstPerma; let start = lis.findIndex(li => ( firstPerma = li.querySelector(this.hybridSelector) ));			if (start === -1 || count - start < 2) return []; let end, lastDiff; for (let i = count - 1; i > start; i--) { if (!isHist && !this.isContribs) { lastDiff = lis[i].querySelector(this.diffSelector); if (lastDiff ||						lis[i].classList.contains('mw-changeslist-src-mw-new')					) { end = i + 1; break; }				}				if (this.permaSelector && lis[i].querySelector(this.permaSelector)) { end = i + 1; break; }			}			if (!end) return []; count = end - start; let params = { diff: lis[start].dataset.mwRevid }; if (lastDiff) { params.oldid = mw.util.getParamValue('oldid', lastDiff.search); } else { params.oldid = lis[end - 1].dataset.mwRevid; if (isHist && lis[end - 1].querySelector(this.diffSelector) ||					this.isContribs && !lis[end - 1].querySelector('.newpage')				) { params.direction = 'prev'; }			}			let title = !isHist && mw.util.getParamValue('title', firstPerma.search); let url = mw.util.getUrl(title, params); let classes = 'consecudiff'; if (!isHist && lis[start].classList.contains(this.topClass)) { classes += ' consecudiff-top'; }			return lis.slice(start, end).map((li, i) => [				$(' ').addClass(classes).append( $('') .attr('href', url) .text(this.convertNumber(count - i + '/' + count)) ),				this.isEnhanced					? li.tagName === 'TR'						? li.lastElementChild						: li.querySelector('.mw-changeslist-line-inner')					: li			]); }		parseDate(s) { let date = Date.parse(s); if (date) return date; if (s.includes(',')) date = Date.parse(s.replace(',', '')); if (date) return date; if (mw.loader.getState('mediawiki.language.months') !== 'ready') return; s = s.replace(/\D/g, c => {				let n = mw.language.convertNumber(c, true);				return Number.isNaN(n) ? c : n;			}); let h, m;			s = s.replace(/(\d\d?)[.:h](\d\d?)/, ($0, $1, $2) => {				h = $1;				m = $2;				return ' ';			}); if (!h) return; let y, dateFirst; s = s.replace(/^(.*?)(\d{4})(?!\d)/, ($0, $1, $2) => {				y = $2;				dateFirst = /\d/.test($1);				return $1 + ' ';			}); if (!y) return; let mo, d;			if (dateFirst) { [d, s] = this.getDate(s); if (!d) return; [mo, s] = this.getMonth(s); if (mo === -1) return; } else { [mo, s] = this.getMonth(s); if (mo === -1) return; [d, s] = this.getDate(s); if (!d) return; }			return new Date(y, mo, d, h, m).getTime; }		getMonth(s) { if (!this.months) { this.months = mw.language.months.abbrev .concat(mw.language.months.names, mw.language.months.genitive) .reverse; }			let mo = this.months.findIndex(mn => {				let temp = s.replace(mn, ' ');				if (temp !== s) {					s = temp;					return true;				}			}); if (mo === -1) { let [numeric, temp] = this.getDate(s); numeric = parseInt(numeric); if (numeric > 0 && numeric < 13) { mo = numeric - 1; s = temp; }			} else { mo = 11 - mo % 12; }			return [mo, s]; }		getDate(s) { let d;			s = s.replace(/(^|\D)(\d\d?)(?!\d)/, ($0, $1, $2) => {				d = $2;				return $1 + ' ';			}); return [d, s]; }		convertNumber(num) { try { return mw.language.convertNumber(num); } catch (e) { return num; }		}	}	mw.hook('wikipage.content').add($content => {		$content.find('.mw-pager-body').each(function { let lis = this.querySelectorAll(':scope > .mw-contributions-list > li'); if (lis.length > 1) new Consecudiff([...lis], !isHist); });		if (isHist) return;		let $lists = $content.filter('.mw-changeslist');		if (!$lists.length) $lists = $content.find('.mw-changeslist');		$lists.each(function { let lis = this.querySelectorAll('.mw-changeslist-edit:not(.mw-changeslist-src-mw-categorize)[data-mw-revid]'); if (lis.length > 1) new Consecudiff([...lis]); });	}); });