User:Isaacl/script/copy-comment-link-to-clipboard.js

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(){

	let fShowCopyCommentLinkTriggers = false;

    function copyLinkToClipboard(event)
    {
    	let link = event.target.dataset.link2clipboardCommentLink;
    	navigator.clipboard.writeText(link).then( () => {
    		showLinkCopiedNotification();
    	});
    }

    function htmlEncode(text)
    {
    	return text.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
    }

	function addCopyCommentLinkTrigger(commentIdElem, label, description)
	{
		let labelText = '';
		if (label != null)
			labelText = label;
		let descriptionText = '';
		if (description != null)
		{
		   descriptionText = description.concat(' ');
		}
		descriptionText = descriptionText.concat('link');

		let id = commentIdElem.getAttribute("id");
		let linkElem = document.createElement("span");
		linkElem.dataset.link2clipboardTrigger = '';
		linkElem.style.textDecorationLine = 'underline';
		linkElem.style.textDecorationStyle = 'dotted';

		let link = mw.config.get('wgPageName');
		link = link.replaceAll('_', ' ');
		link = link.concat('#', id.replaceAll('_', ' '));
		linkElem.dataset.link2clipboardCommentLink = link;

		let triggerText = "";
		triggerText = triggerText.concat("<", labelText, "/> ");
		linkElem.appendChild(document.createTextNode(triggerText));

        let popupContent = '<p style="font-size:0.875em;">'.concat(htmlEncode(link), '</p>');
        let popupWidget = new OO.ui.PopupWidget({
        	$content: $(popupContent),
        	$floatableContainer: $(linkElem),
        	classes: [ 'link2clipboardPopup' ],
        	head:true,
        	padded:true
        });
        $(document.body).append(popupWidget.$element);

		let titleText = "";
		titleText = titleText.concat('Copy ', descriptionText, ' to clipboard');
		linkElem.title = titleText;
		linkElem.addEventListener('click', function(event) {
			popupWidget.toggle(true);
			copyLinkToClipboard(event);
		});

        let parent = commentIdElem.parentNode;
        let insertBeforeElem = commentIdElem;
        if (parent.tagName == 'A' && parent.href != "")
        {
        	insertBeforeElem = parent;
	        parent = parent.parentNode;
        }
		parent.insertBefore(linkElem, insertBeforeElem);
	}
	
	function showCommentLinks()
	{
		let commentStartSpans = document.querySelectorAll("span[data-mw-comment-start]");
		for (let commentStartElem of commentStartSpans)
		{
			if (commentStartElem.hasAttribute("id"))
			{
				addCopyCommentLinkTrigger(commentStartElem);
			}
		}
		let headlineIdSpans = document.getElementsByClassName('mw-headline');
		for (let headlineIdElem of headlineIdSpans)
		{
			if (headlineIdElem.hasAttribute("id"))
			{
				addCopyCommentLinkTrigger(headlineIdElem, "h", "heading");
			}
		}
	}
	function hideCommentLinks()
	{
		let linkElems = document.querySelectorAll("span[data-link2clipboard-trigger]");
		for (let linkElem of linkElems)
		{
			linkElem.remove();
		}
		let popupElems = document.querySelectorAll('.link2clipboardPopup');
		for (let popupElem of popupElems)
		{
			popupElem.remove();
		}
	}

	function showLinkCopiedNotification()
	{
    	mw.loader.using(['mediawiki.notification']).then( () => {
    		mw.notification.notify("Copied link to clipboard.");
    	});

	}

    function showEnabledNotification()
    {
    	mw.loader.using(['mediawiki.notification']).then( () => {
			mw.notification.notify("Enabled copying links to clipboard.");
		});
    }

    function showDisabledNotification()
    {
    	mw.loader.using(['mediawiki.notification']).then( () => {
			mw.notification.notify("Disabled copying links to clipboard.");
		});
    }

    function clickEventListener(event)
    {
    	event.preventDefault();
    	event.stopPropagation();
    	hideCommentLinks();
    	if (fShowCopyCommentLinkTriggers)
    	{
    		showDisabledNotification();
    	}
    	else
    	{
			showCommentLinks();
    		showEnabledNotification();
    	}
		fShowCopyCommentLinkTriggers = !fShowCopyCommentLinkTriggers;
		return false;
    }

	let portletItemDropDownMenuConfig = [
		{ portletName: 'p-personal', id: 'link2clipboard-PortletItem-personal' }, 
		{ portletName: 'p-personal-sticky-header', id: 'link2clipboard-PortletItem-personal-sticky-header' }, 
	];
	let portletItemSidebarConfig = [
		{ portletName: 'p-tb', id: 'link2clipboard-PortletItem-tb' }, 
	];

	let skinsWithDropDownMenu = [ 'vector2022', 'timeless', 'minerva' ];
	let portletItemConfig = portletItemSidebarConfig;

    // TODO: mw.config.skin is returning undefined, and not the skin name
	//console.log("Using skin: ", mw.config.skin);
	//if (skinsWithDropDownMenu.includes(mw.config.skin))
	//	portletItemConfig = portletItemDropDownMenuConfig;

	let portletItemText = "Toggle link2clipboard";

	function addPortletLinkEventHandler(item, options)
	{
		for (let portletItemInfo of portletItemConfig)
		{
	    	if (options.id == portletItemInfo.id)
	    	{
	    		mw.loader.using('oojs-ui-core').done( function() {
					item.addEventListener('click', clickEventListener);
	    		});
	    	}
		}
	}

	function initializePortletItem()
	{
		mw.hook('util.addPortletLink').add(addPortletLinkEventHandler);
		mw.loader.using( [ 'mediawiki.util' ] ).then( function () {
			for (let portletItemInfo of portletItemConfig)
			{
				let portletItem = document.getElementById(portletItemInfo.id);
				if (portletItem == null)
				{
					mw.util.addPortletLink(portletItemInfo.portletName, '#', portletItemText, portletItemInfo.id);
				}
			}
		} );
		return;
	}

    initializePortletItem();
})();