Jump to content

User:Nardog/Consecudiff.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Nardog (talk | contribs) at 12:05, 25 January 2022 (stability fix). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
(function consecudiff() {
	mw.loader.addStyleTag('.consecudiff::before{content:" ["} .consecudiff::after{content:"]"} .consecudiff-top::before{content:" ⟨"} .consecudiff-top::after{content:"⟩"}');
	class Consecudiff {
		constructor(list) {
			this.isHist = list.id === 'pagehistory';
			this.isContribs = !this.isHist &&
				list.classList.contains('mw-contributions-list');
			this.isEnhanced = !this.isHist && !this.isContribs &&
				!!list.querySelector('.mw-enhanced-rc');
			let lis = this.isHist || this.isContribs
				? list.children
				: list.querySelectorAll('.mw-changeslist-edit:not(table)[data-mw-revid]');
			if (lis.length < 2) return;
			lis = [...lis];
			this.diffSelector = this.isHist
				? '.mw-history-histlinks > span:last-child > a'
				: '.mw-changeslist-diff';
			this.threshold = this.isContribs ? window.consecudiffContribsThreshold || 120
				: this.isHist ? window.consecudiffHistThreshold || 720
				: window.consecudiffThreshold || 720;
			this.strictMode = !this.isHist && !this.isContribs &&
				!!window.consecudiffDetectInterruptions;
			this.permaSelector = this.isEnhanced && '.mw-enhanced-rc-time > a' ||
				(this.isHist || this.isContribs) && 'a.mw-changeslist-date';
			this.topClass = this.isContribs
				? 'mw-contributions-current'
				: 'mw-changeslist-last';
			let dependencies = ['mediawiki.util'];
			if ((this.isHist || this.isContribs) &&
				mw.config.get('wgUserLanguage') !== 'en'
			) {
				dependencies.push('mediawiki.language.months');
			}
			mw.loader.using(dependencies, () => {
				if (this.isHist) {
					this.processLis(lis);
					return;
				}
				let lisByTitle = this.groupByTitle(lis);
				Object.entries(lisByTitle).forEach(([title, subLis]) => {
					this.processLis(subLis, title);
				});
			});
		}
		groupByTitle(lis) {
			let selector = this.isContribs
				? '.mw-contributions-title'
				: '.mw-changeslist-title';
			let lisByTitle = {};
			lis.forEach(li => {
				let title = (this.isEnhanced ? li.closest('table') : li)
					.querySelector(selector);
				if (!title) return;
				title = title.textContent;
				if (!lisByTitle.hasOwnProperty(title)) lisByTitle[title] = [];
				lisByTitle[title].push(li);
			});
			return lisByTitle;
		}
		processLis(lis, title) {
			if (lis.length < 2) return;
			let data = lis.map(li => {
				let obj = {};
				let userLink = li.querySelector('.mw-userlink');
				if (userLink) obj.user = userLink.textContent;
				let date;
				if (this.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) obj.date = date / 60000;
				if (this.strictMode) {
					let diff = li.querySelector(this.diffSelector);
					if (diff) {
						obj.nextRev = mw.util.getParamValue('oldid', diff.search);
					}
				}
				return obj;
			});
			let lastSplitAt = 0;
			lis.forEach((li, i) => {
				if (!i) return;
				let cur = data[i], prev = data[i - 1];
				if (cur.user !== prev.user ||
					prev.date - cur.date > this.threshold ||
					prev.nextRev && prev.nextRev !== li.dataset.mwRevid
				) {
					this.addLinks(lis.slice(lastSplitAt, i), title);
					lastSplitAt = i;
				}
			});
			this.addLinks(lis.slice(lastSplitAt), title);
		}
		addLinks(lis, title) {
			let count = lis.length;
			if (count < 2) return;
			let start = lis.findIndex(li => (
				li.querySelector(this.permaSelector || this.diffSelector)
			));
			if (start === -1 || count - start < 2) return;
			let end, lastDiff;
			for (let i = count - 1; i > start; i--) {
				if (!this.isContribs) {
					lastDiff = lis[i].querySelector(this.diffSelector);
					if (lastDiff) {
						end = i + 1;
						break;
					}
				}
				if (this.permaSelector
					? lis[i].querySelector(this.permaSelector)
					: lis[i].classList.contains('mw-changeslist-src-mw-new')
				) {
					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 (this.isContribs && !lis[end - 1].querySelector('.newpage')) {
					params.direction = 'prev';
				}
			}
			let url = mw.util.getUrl(title, params);
			let classes = 'consecudiff';
			if (!this.isHist && lis[start].classList.contains(this.topClass)) {
				classes += ' consecudiff-top';
			}
			lis.slice(start, end).forEach((li, i) => {
				$('<span>').addClass(classes).append(
					$('<a>')
						.attr('href', url)
						.text(this.convertNumber(count - i + '/' + count))
				).appendTo(this.isEnhanced ? li.lastElementChild : li);
			});
		}
		convertNumber(num) {
			try {
				return mw.language.convertNumber(num);
			} catch (e) {
				return num;
			}
		}
		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;
			if (!this.digitRes) {
				this.digitRes = Object.entries(mw.language.getDigitTransformTable())
					.map(([k, v]) => [k, new RegExp(mw.util.escapeRegExp(v), 'g')]);
			}
			this.digitRes.forEach(([k, v]) => {
				s = s.replace(v, k);
			});
			let h, m;
			s = s.replace(/(\d\d?)[.:h](\d\d?)(?::\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 temp;
				[mo, temp] = this.getDate(s);
				mo = parseInt(mo);
				if (mo > 0 && mo < 13) {
					mo = mo - 1;
					s = temp;
				} else {
					mo = -1;
				}
			} 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];
		}
	}
	mw.hook('wikipage.content').add($content => {
		let $lists = $content.filter('.mw-changeslist');
		if (!$lists.length) {
			$lists = $content.find('#pagehistory, .mw-contributions-list, .mw-changeslist');
		}
		$lists.each(function () {
			new Consecudiff(this);
		});
	});
}());