nire0510 / Selext

// ==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>&nbsp;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 */