Sauvegarde / Export New Favorites

// ==UserScript==
// @name         Export New Favorites
// @namespace    https://openuserjs.org/users/Sauvegarde
// @version      0.2
// @author       Sauvegarde
// @description  Copy new favorites in the clipboard so it can be pasted in a Google sheet.
// @match        https://aryion.com/g4/messagepage.php
// @grant        none
// @iconURL      https://www.google.com/s2/favicons?domain=aryion.com
// @updateURL    https://openuserjs.org/meta/Sauvegarde/Export_New_Favorites.meta.js
// @downloadURL  https://openuserjs.org/install/Sauvegarde/Export_New_Favorites.user.js
// @copyright    2021, Sauvegarde (https://openuserjs.org/users/Sauvegarde)
// @license		 MIT
// ==/UserScript==

/* jshint esversion: 6 */
'use strict';

main();

function main() {
	const favCtn = document.querySelector("#favorite-messages");
	const favs = favCtn.querySelectorAll(".g-box-contents li");
	const exportBuffer = [];
	const sep = '\t'
	for(const fav of favs) {
		const ulink = fav.querySelector("a.user-link");
		const title = fav.querySelector("a.title");
		const pdate = fav.querySelector("span.pretty-date");
		const rdate = parsePrettyDate(pdate.title);
		const textBuffer = [hyperlink(ulink), hyperlink(title), frenchDate(rdate)];
		exportBuffer.push(textBuffer.join(sep));
	}
	const rightActionCtn = favCtn.querySelector(".g-box-righttitle");
	const exportLink = document.createElement("a");
	exportLink.innerHTML = "Export New Favorites";
	exportLink.className = "confirm-swap";
	exportLink.style.marginRight = "12px";
	exportLink.addEventListener("click", () => writeToClipboard(exportBuffer.join('\n')));
	rightActionCtn.insertBefore(exportLink, rightActionCtn.firstChild);
}

/** Attempt to copy the text (ctrl+c) into the system clipboard. */
function writeToClipboard(str) {
	navigator.clipboard.writeText(str)
		.then(() => {
		alert("The list of favorites has been successfully written to clipboard.");
	})
		.catch(err => {
		alert("Error in writing to clipboard: ", err);
	});
}

function frenchDate(d) {
	const zero = n => n < 10 ? "0" + n : n;
	return `${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()} ${zero(d.getHours())}:${zero(d.getMinutes())}`;
}

function hyperlink(a) {
	return `=HYPERLINK("${a.href}"; "${a.innerHTML}")`;
}

/** Parse a pretty date ("Apr 17th, 2021 08:17 AM") to a Date object. */
function parsePrettyDate(str) {
	function iMonth(str) {
		const months = {
			"jan": 0, "january": 0,
			"feb": 1, "february": 1,
			"mar": 2, "march": 2,
			"apr": 3, "april": 3,
			"may": 4,
			"jun": 5, "june": 5,
			"jul": 6, "july": 6,
			"aug": 7, "august": 7,
			"sep": 8, "september": 8,
			"oct": 9, "october": 9,
			"nov": 10, "november": 10,
			"dec": 11, "december": 11
		};
		return months[str.toLowerCase()];
	}
	function h24(hours12, meridiem) {
		hours12 = +hours12;
		meridiem = meridiem.toLowerCase();
		if(meridiem === "pm" && hours12 < 12) {
			return hours12 + 12;
		} else if(meridiem === "am" && hours12 === 12) {
			return 0;
		} else {
			return hours12;
		}
	}
	const t = /^(\w+) (\d+).*?(\d+) (\d+):(\d+) (\w+)$/.exec(str);
	const MMM = t[1];
	const dd = t[2];
	const yyyy = t[3];
	const hh12 = t[4];
	const mm = t[5];
	const mi = t[6];
	return new Date(yyyy, iMonth(MMM), dd, h24(hh12, mi), mm, 0);
}