Jump to content

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

From Wikipedia, the free encyclopedia
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, linkType)
	{
		let labelText = '';
		if (label != null)
			labelText = label;
		let descriptionText = '';
		if (description != null)
		{
		   descriptionText = description.concat(' ');
		}
		descriptionText = descriptionText.concat('link');

		if (linkType == null || linkType != 'original')
		{
			linkType = 'permalink';
		}

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

		let permalink = 'Special:GoToComment';
		let link = mw.config.get('wgPageName');
		link = link.replaceAll('_', ' ');
		let idLinkText = id.replaceAll('_', ' ');
		link = link.concat('#', idLinkText);
		permalink = permalink.concat('/', idLinkText);
		if (linkType == 'permalink')
		{
			linkElem.dataset.link2clipboardCommentLink = permalink;
		}
		else
		{
			linkElem.dataset.link2clipboardCommentLink = link;
		}

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

        let permalinkHTML = htmlEncode(permalink);
        let originalLinkHTML = htmlEncode(link);
        let popupContent = '<p>';
        if (linkType == 'permalink')
        {
        	popupContent = popupContent.concat(permalinkHTML,
        	  '</p><p><small>original page link:</small> ',
              originalLinkHTML, '</p>');
        }
        else
        {
        	popupContent = popupContent.concat(originalLinkHTML, '</p>');
        }
        let popupWidget = new OO.ui.PopupWidget({
        	$content: $(popupContent),
        	$floatableContainer: $(linkElem),
        	classes: [ 'link2clipboardPopup' ],
        	head:true,
        	padded:true
        });
        OO.ui.getTeleportTarget().append(popupWidget.$element[0]);

		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);
			}
		}
		// Legacy structure for headlines: span element inside a heading element
		let headlineIdSpans = document.getElementsByClassName('mw-headline');
		for (let headlineIdElem of headlineIdSpans)
		{
			if (headlineIdElem.hasAttribute("id"))
			{
				addCopyCommentLinkTrigger(headlineIdElem, "h", "heading", "original");
			}
		}
		// Current structure for headlines: div element wrapping heading element.
		let headingNameList = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ];
		for (let headingName of headingNameList)
		{
			let selector = "div.mw-heading ".concat(headingName.concat("[id]"));
			let headingElements = document.querySelectorAll(selector);
			for (let headingElem of headingElements)
			{
				addCopyCommentLinkTrigger(headingElem, "h", "heading", "original");
			}
		}
	}
	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();
})();