Greasy Fork is available in English.

Blabbermouth - generate timestamps and add link to the fb comments area

Generates missing timestamps or converts the existing ones in relative format, and adds link to the fb comments area

// ==UserScript==
// @name        Blabbermouth - generate timestamps and add link to the fb comments area
// @namespace   darkred
// @version     2.0
// @date        2022.5.14
// @description Generates missing timestamps or converts the existing ones in relative format, and adds link to the fb comments area
// @author      darkred
// @license     MIT
// @include     /^(https?:)?\/\/(www\.)?blabbermouth\.net\/(news|reviews)?/
// @exclude     /^(https?:)?\/\/(www\.)?blabbermouth\.net\/reviews(\/page|$)/
// @include     https://www.facebook.com/plugins/feedback.php*
// @grant       none
// @require     https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js
// @supportURL  https://github.com/darkred/Userscripts/issues
// @icon        https://drnizx3otcofi.cloudfront.net/b64e6010-2ac1-4e95-b545-1a2994bbbd04/img/favicon/favicon-32x32.png
// ==/UserScript==


/* global moment */
/* eslint-disable no-console */

'use strict';


// Customize the strings in the locale to display "1 minute ago" instead of "a minute ago" (https://github.com/moment/moment/issues/3764#issuecomment-279928245)
moment.updateLocale('en', {
	relativeTime: {
		future: 'in %s',
		past: '%s ago',
		s: 'seconds',
		m: '1 minute',
		mm: '%d minutes',
		h: '1 hour',
		hh: '%d hours',
		d: '1 day',
		dd: '%d days',
		M: '1 month',
		MM: '%d months',
		y: '1 year',
		yy: '%d years'
	}
});

function convertToLocalTimezone(timestamp) {
	// (the timestamp is in ISO 8601 format and its trailing Z means that it's in UTC )
	// 2020-03-05T15:40:38.000Z    old
	// 2022-05-11T12:16:05+00:00   new
	let initialTimestamp = timestamp;
	if (moment(initialTimestamp, moment.ISO_8601, true).isValid()) {
		// let convertedToLocalTimezone = moment(initialTimestamp.replace('Z','')  + '-05:00', 'YYYY-MM-DDTHH:mm:ssZ');		// the server's timezone is GMT-5
		let convertedToLocalTimezone = moment(initialTimestamp + '-03:47', 'YYYY-MM-DDTHH:mm:ss+Z');		// the server's timezone is GMT-4 plus 13 min, in order to sync with the relevant post timestamps in both Facebook and Twitter(TW is 1 min later than FB) blabbbermouth pages
		publishedTimeLTZ = convertedToLocalTimezone.fromNow();
		let format = 'YYYY-MM-DD HH:mm:ss';
		publishedTimeLTZtitle = convertedToLocalTimezone.format(format);
	}
}

function recalc(existingTimestampElement, format, notitle) {
	setInterval(function() {
		if (existingTimestampElement && moment(existingTimestampElement.title, format, true).isValid()) {
			existingTimestampElement.textContent = moment(existingTimestampElement.title).fromNow();
		} else if (notitle === true) {
			existingTimestampElement.innerText = moment(existingTimestampElement.innerText.trim()).fromNow();
		}
	}, 1 * 60 * 1000);		// repeat every 1 minute
}


function onClick(){
	document.querySelector('iframe[title="fb:comments Facebook Social Plugin"]').scrollIntoView();
}


// 1. IF ON NEWS PAGES LISTINGS (convertTolocal + apply to pagination)
if (
	window.location.href.endsWith('blabbermouth.net/') ||
	window.location.href.endsWith('blabbermouth.net/news') ||
	window.location.href.includes('blabbermouth.net/news/page/')
) {

	let options = {
		root: null,
		rootMargin: '0px',
		threshold: 0
	};

	let callback = (entries) => {
		entries.forEach(entry => {

			if (!entry.target.classList.contains('done')){


				if (entry.isIntersecting && !entry.target.classList.contains('in-viewport') ) {
					entry.target.classList.add('in-viewport');

					const xhr = new XMLHttpRequest();
					const url = entry.target.parentElement.parentElement.firstElementChild.firstElementChild.href;
					xhr.open('GET', url, true);	// XMLHttpRequest.open(method, url, async)
					xhr.onload = function () {

						let container = document.implementation.createHTMLDocument().documentElement;
						container.innerHTML = xhr.responseText;

						let publishedTimestamp = container.querySelector('meta[property="article:published_time"]').content;

						convertToLocalTimezone(publishedTimestamp);

						entry.target.textContent = publishedTimeLTZ;
						entry.target.title = publishedTimeLTZtitle;

						entry.target.classList.add('done');

						recalc(entry.target, 'YYYY-MM-DD HH:mm:ss');
					};
					xhr.send();
				}
			}
		});
	};

	let observer = new IntersectionObserver(callback, options);

	let allTimestamps = document.querySelectorAll('.news-single span.date');
	allTimestamps.forEach((element) => {
		observer.observe(element);
	});


	// ----------------------------------------
	// Watch for pagination events (when new '.article' children are added inside the '.infinite_scroll' element)
	const targetNode2 = document.querySelector('.infinite-scroll-component');
	const config2 = { attributes: false, childList: true, subtree: false };

	const callback2 = function(mutationsList) {
		for(const mutation of mutationsList) {
			if (mutation.type === 'childList') {
				let allTimestamps = document.querySelectorAll('.news-single span.date');
				allTimestamps.forEach((element) => {
					observer.observe(element);
				});
			}
		}
	};

	const observer2 = new MutationObserver(callback2);
	observer2.observe(targetNode2, config2);
	// ----------------------------------------


	// /blabbermouth\.net/\(reviews|news)/i.test(window.location.href)

// 2+3. ELSE IF ON CD/DVD REVIEWS OR NEWS PAGES ((convertTolocal + generate timestamp)
} else if ( /blabbermouth\.net\/(reviews|news)/i.test(window.location.href) &&
		!window.location.href.includes('/page/') ) {

	//--- Double-check that this iframe is on the expected domain:
	if (/blabbermouth\.net/i.test(location.host)) {
		console.log('Userscript is in the MAIN page.');

		// 2019-10-17T15:32:18.000Z


		let publishedTimestamp;
		if (document.querySelector('meta[property="article:published_time"]') !== null) {
			publishedTimestamp = document.querySelector('meta[property="article:published_time"]').content;
		}
		console.log('publishedTimestamp: ' + publishedTimestamp);



		if (window.location.href.includes('blabbermouth.net/news/')) {

			var publishedTimeLTZ, publishedTimeLTZtitle;

			convertToLocalTimezone(publishedTimestamp);

			let existingTimestampElement = document.querySelector('div > h1+span.date');

			existingTimestampElement.textContent = publishedTimeLTZ;
			existingTimestampElement.title = publishedTimeLTZtitle;

			recalc(existingTimestampElement, 'YYYY-MM-DD HH:mm:ss');

		}








		if (!window.location.href.includes('blabbermouth.net/news/page/')){

			convertToLocalTimezone(publishedTimestamp);

			var commentcount = '0';

			var datePart = !window.location.href.includes('blabbermouth.net/news/') ? `<span class="date">${publishedTimeLTZ}</span>` : '';

			var HTML = `
${datePart}
<div>
<span class="date">
<a href="javascript:void(0)" id="commentCount">${commentcount} Comments</a>
</span>
</div>
`;

			const refSelector = '.reviews-single-article > div > .reviews-rate-comments,div > h1+span.date';

			if (document.querySelector(refSelector) !== null) {
				document.querySelector(refSelector).insertAdjacentHTML('afterend', HTML);
				document.querySelector(refSelector).title = publishedTimeLTZtitle;

				document.getElementById('commentCount').addEventListener('click', onClick, false);

				let newDateTimeElement = document.querySelector('span.date');
				recalc(newDateTimeElement, 'YYYY-MM-DD HH:mm:ss');
			}

		}

		// Wait for messages [commentcount] (from iframe)
		window.addEventListener('message', function addFbCounter(e) {
			// something from an unknown domain, or doesn't contain the string "Comment" let's ignore it
			console.log('Received message: ' + e.data);
			if (e.origin !== 'https://www.facebook.com' || e.data.indexOf(' Comment') === -1) {
				return;
			}
			console.log('Received message: ' + e.data);
			// document.querySelector('#commentCount').innerText = e.data.replace(/ Comments?/i,'');
			document.querySelector('#commentCount').innerText = e.data;
			window.removeEventListener('message', addFbCounter);
		},
		false
		);
		console.log('Waiting for Message 1, from iframe...');
	}


// ELSE IF IT'S ON THE FACEBOOK COMMENTS IFRAME  (send fb comment count to main page)
}

if (window.location.href.includes('facebook.com')) {

	console.log('Userscript is in the FRAMED page.');

	const selector = '._50f7';
	// Send commentcount to MAIN page
	window.parent.postMessage(
		document.querySelector(selector).innerText,
		'https://blabbermouth.net/reviews/'
	);
}