NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Selext
// @namespace http://nirelbaz.com/
// @version 1.12
// @description Do something useful after text selection
// @author Nir Elbaz
// @require https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser-polyfill.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js
// @include *
// @grant GM_addStyle
// @grant GM_setClipboard
// @exclude https://openuserjs.org/*
// @exclude https://translate.google.com/*
// @exclude http://feditor.io/*
// @exclude https://github.com/*
// ==/UserScript==
/* jshint ignore:start */
var inline_src = (<><![CDATA[
/* jshint ignore:end */
/* jshint esnext:true */
let selext,
blnMouseMove = false,
arrMenu = [
{
command: 'search',
class: 'ion-search',
title: 'Search it on Google',
action: function () {
window.open('https://www.google.co.il/#q=' + selext.text);
}
},
{
command: 'email',
class: 'ion-email',
title: 'Email it to a friend',
action: function () {
document.location.href = 'mailto:?body=' + selext.text;
}
},
{
command: 'twitter',
class: 'ion-social-twitter',
title: 'Share it on Twitter',
action: function () {
window.open('https://twitter.com/intent/tweet?text=' + selext.text);
}
},
{
command: 'copy',
class: 'ion-clipboard',
title: 'Copy it to your clipboard',
action: function () {
GM_setClipboard(selext.text);
}
},
{
command: 'speak',
class: 'ion-speakerphone',
title: 'Speak it load and clear',
action: function () {
let ssu = new SpeechSynthesisUtterance();
ssu.text = selext.text;
ssu.lang = document.getElementsByTagName('html')[0].getAttribute('lang') || 'en-US';
ssu.volume = 1;
speechSynthesis.speak(ssu);
}
},
{
command: 'translate',
class: 'ion-earth',
title: 'Translate it to browser\'s language',
action: function () {
window.open('https://translate.google.com/#auto/' + navigator.language + '/' + selext.text);
}
},
{
command: 'dismiss',
class: 'ion-power',
title: 'Dismiss Selext for current website during current session',
action: function () {
sessionStorage.setItem('selext-dismiss', '1');
document.removeEventListener('mouseup', onDocumentMouseup);
}
}
];
class Selext {
constructor (arrMenu) {
this.element = document.createElement('div');
this.element.id = 'selext-menu';
this.text = '';
this.draw(arrMenu);
this.element.style.display = 'block';
// Listen to transition-end event on menu:
this.element.addEventListener('click', (e) => {
if (e.target.dataset && e.target.dataset.action) {
arrMenu.filter(function (item) { return item.command === e.target.dataset.command; })[0].action();
}
});
// Listen to transition-end event on menu:
this.element.addEventListener('transitionend', () => {
if (this.element.style.opacity === '0') {
this.element.style.zIndex = -1;
}
});
}
draw (arrMenu) {
let arrMenuMarkup = arrMenu.map(item => `<li class="${item.class}" data-command="${item.command}" data-action="${eval(item.action)};" title="${item.title}"></li>`);
let elmLink = document.createElement('LINK');
let strStyle = `
#selext-menu {
font-family: arial;
position: absolute;
display: none;
top: -500;
-webkit-filter: drop-shadow(4px 4px 4px rgba(0,0,0,0.5));
filter: drop-shadow(4px 4px 4px rgba(0,0,0,0.5));
left: -500;
z-index: -1;
opacity: 0;
width: 150px;
-webkit-user-select: none;
user-select: none;
transition: opacity 500ms ease-out;
}
#selext-menu header {
background-color: #333;
border-radius: 5px 5px 0 0;
color: #fff;
font-size: 12px;
font-weight: bold;
height: auto;
padding: 2px;
text-transform: uppercase;
}
#selext-menu ul {
list-style: none;
display: flex;
flex-flow: wrap;
margin: 0;
padding: 0;
text-align: center;
}
#selext-menu li {
color: #ddd;
cursor: pointer;
flex: 1 1 33%;
font-size: 25px;
line-height: 40px;
transition: color 300ms ease-out, font-size 300ms ease-out;
}
#selext-menu li:hover {
color: #fff;
font-size: 30px;
}
#selext-menu li:nth-child(1) {
background-color: #F44336;
}
#selext-menu li:nth-child(2) {
background-color: #8BC34A;
}
#selext-menu li:nth-child(3) {
background-color: #03A9F4;
}
#selext-menu li:nth-child(4) {
background-color: #FF9800;
}
#selext-menu li:nth-child(5) {
background-color: #9C27B0;
}
#selext-menu li:nth-child(6) {
background-color: #009688;
}
#selext-menu li:last-child {
background-color: #616161;
border-radius: 0 0 5px 5px;
color: #FF7777;
flex: 1 1 100%;
font-size: 15px;
line-height: 25px;
}
`;
// Style:
GM_addStyle(strStyle);
elmLink.setAttribute('rel', 'stylesheet');
elmLink.setAttribute('href', 'https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css');
document.head.appendChild(elmLink);
// Menu:
this.element.innerHTML = '<header> Selext</header><ul>' + arrMenuMarkup.join('') + '</ul>';
document.body.appendChild(this.element);
}
show (strText, intLeft, intTop) {
this.text = strText;
this.element.style.left = intLeft + 10 + 'px';
this.element.style.top = intTop + 10 + 'px';
this.element.style.opacity = 1;
this.element.style.zIndex = 9999;
}
hide () {
this.element.style.opacity = 0;
}
}
/**
* Listens to mouse up events and detect text selection
* @param {object} e Mouse event
*/
function onDocumentMouseup (e) {
if (blnMouseMove) {
let strText = window.getSelection().toString();
if (strText !== '') {
selext.show(strText, e.pageX, e.pageY);
}
}
}
/**
* Listens to mousemove events on document to show Selext menu right after
* @param {object} e Event
*/
function onDocumentMousemove (e) {
if (e.which === 1) {
blnMouseMove = true;
}
}
/**
* Listens to click events on document to hide Selext menu
* @param {object} e Event
*/
function onDocumentClick (e) {
if (blnMouseMove === true) {
blnMouseMove = false;
return;
}
selext.hide();
}
// Create menu only if user hasn't dismissed Selext:
if (sessionStorage.hasOwnProperty('selext-dismiss') === false) {
selext = new Selext(arrMenu);
// Listen to mouseup event on document:
document.addEventListener('mouseup', onDocumentMouseup);
// Listen to mousemove event on document:
document.addEventListener('mousemove', onDocumentMousemove);
// Listen to click event on document:
document.addEventListener('click', onDocumentClick);
}
/* jshint ignore:start */
]]></>).toString();
var c = babel.transform(inline_src);
eval(c.code);
/* jshint ignore:end */