kba / One Click Copy Links to clipboard

// ==UserScript==
// @name         One Click Copy Links to clipboard
// @namespace    http://github.com/kba
// @version      0.2
// @description  Adds buttons to text links and videos to copy the linked URI to clipboard in one click.
// @author       kba
// @match        */*

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_log

// @require      https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/master/gm_config.js
// @require      https://code.jquery.com/jquery-2.2.0.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.8/clipboard.min.js

// ==/UserScript==
/* jshint -W097, esnext:true */
/* globals GM_log, GM_setValue, GM_getValue, GM_registerMenuCommand, GM_config, Clipboard, $ */
'use strict';

// Setup configuration
GM_config.init({
	'id': 'one-click-copy',
	'title': 'Configure OneClickCopy',
	'fields': {

		'auto_video': {
			'label': 'Add Copy Button to all videos?',
			'type': 'checkbox',
			'default': false,
		},

		'timeout': {
			'label': 'Delay in ms before adding button to video',
			'type': 'number',
			'default': 1500,
		},

		'auto_text': {
			'label': 'Add Copy Button to all Text links?',
			'type': 'checkbox',
			'default': false,
		},

		"include_links": {
			'label': 'Selectors to find text links. First matching selector is used.',
			'type': 'textarea',
			'rows': 5,
			'width': 30,
			'default': [
				'#content a[href]',
				'a[href]'
			].join("\n"),
		},


		"skip_parents": {
			'label': 'Skip text links that are children of parents matching these selectors',
			'type': 'textarea',
			'rows': 10,
			'width': 30,
			'default': [
				'header',
				'#header',
				'footer',
				'#footer',
				'#tags',
				'*[id*=tags]',
				'*[class*=tags]',
				'#categories',
				'*[id*=categories]',
				'*[class*=categories]',
				'#navigation',
				'.nav',
				'*[role="navigation"]',
			].join("\n"),
		},

	},

});

// Helper to add inline CSS rules
function addCSSRules(rules) {
	var style   = document.createElement("style");
	style.innerHTML = rules.replace(/;/g, '!important ;');
	document.head.appendChild(style);
}

// Add CSS
$('head').append($('<style>').append(`

	.gm-link-tools {
		position: relative;
		display: inline-block;
		line-height: 1;
		padding: 0 2px;
		cursor: copy;
		font-size: 150%;
		opacity: 1;
		vertical-align: middle;
		background: gray;
		color: white;
		height: initial;
		width: initial;
		z-index: 1001;
	}

	.gm-link-tools > span {
		display: block;
		overflow:hidden;
		max-height:100%;
	}

	.gm-link-tools.copied {
		color: #6f5;
	}

`));

// The rules that determine which links are skipped
var skip_parents = GM_config.get('skip_parents').split(/\n/g);
var excludeMatchers = {
	'numeric': function($link) { return /^\d+$/.test($link.html()); },
	'image': function($link) { return $("img", $link).length; },
	'parent': function($link) { 
		for (var i = 0 ; i < skip_parents.length ; i++) {
			if ($link.parents(skip_parents[i]).length > 0) {
				return skip_parents[i];
			}
		}
	},
};

// Create the button
function createCopyLink(href) {
	return $(`
		<a data-clipboard-text="${href}" title="Copy to Clipboard" class="gm-link-tools">
			<span>∀</span>
		</a>
	`);
}

// Add clipboard functionality to copy button and add after element
function appendClipboardLink($el, $copyLink) {
	var clipboard = new Clipboard($copyLink[0]);
	clipboard.on('success', function(e) {
		$copyLink.addClass("copied");
		$copyLink.html('<span>✔</span>');
		return false;
	});
	clipboard.on('error', function(e) {
		$copyLink.addClass("failed");
		$copyLink.html('<span>X</span>');
	});
	$("<span>")
		.on('click tap mousedown touchstart', function(evt) {
			evt.stopPropagation();
			return false;
		})
		.append($copyLink)
		.insertAfter($el);
}

//
// Text Links
//

function clippifyLink(){
	/* jshint validthis:true */
	var $link = $(this);
	for (var rule in excludeMatchers) {
		var result = excludeMatchers[rule]($link);
		if (result) {
			GM_log(`SKIP ${rule}/${result}: '${$link.text()}'`);
			return;
		}
	}
	var href = $link[0].href;
	var $copyLink = createCopyLink(href);
	appendClipboardLink($link, $copyLink);
}

function clippifyLinks(){
	var links;
	var includeLinks = GM_config.get('include_links').split(/\n/g);
	for (var i = 0 ; i < includeLinks.length ; i++) {
		links = $(includeLinks[i]);
		if (links.length > 0) {
			break;
		}
	}
	$(links).each(clippifyLink);
}

//
// Videos
//

function clippifyVideo() {
	/* jshint validthis:true */
	var $video = $(this);
	var src = $video[0].currentSrc;
	if (! src) return;
	var $copyLink = createCopyLink(src);
	appendClipboardLink($video, $copyLink);
}

function clippifyVideos() {
	$("video").each(clippifyVideo);
}

//
// Setup menu commands
//
GM_registerMenuCommand('Configure One-Click-Copy', GM_config.open.bind(GM_config), 'c');
GM_registerMenuCommand("Text Links: Add Copy Button", clippifyLinks, "l");
GM_registerMenuCommand("Videos: Add Copy Button", clippifyVideos, "v");

//
// Automate adding buttons
//
if (GM_config.get('auto_video')) {
	setTimeout(function() {
		$("video").one('progress', clippifyVideo);
	}, GM_config.get('timeout'));
}
if (GM_config.get('auto_text')) {
	setTimeout(function() {
		clippifyLinks();
	}, GM_config.get('timeout'));
}






// vim: set noet ts=4 sw=4 :