Skip to content

Instantly share code, notes, and snippets.

@julik
Created August 19, 2013 10:18
Show Gist options
  • Save julik/6267710 to your computer and use it in GitHub Desktop.
Save julik/6267710 to your computer and use it in GitHub Desktop.
Copypaste implementation
// Detect Control/Shift/Alt key status
var CTRL = false;
var SHIFT = false;
var ALT = false;
var CHAR_CODE = -1;
var menuItemDiv = null;
var curMouseX, curMouseY;
function debug(str) {
document.gs.q.value = new Date().getTime() + " " + str;
//document.getElementById("file-menu").innerHTML = new Date().getTime() + " " + str;
}
/**
* Handler when any key is pressed
*/
function keyDownHandler(e) {
var x = '';
if (document.all) {
var evnt = window.event;
x = evnt.keyCode;
}
else {
x = e.keyCode;
}
detectKeys(x, true);
}
/**
* Handler when any key is released
*/
function keyUpHandler(e) {
var x = '';
if (document.all) {
var evnt = window.event;
x = evnt.keyCode;
}
else {
x = e.keyCode;
}
detectKeys(x, false);
}
/**
* Handler for mouseup event
*/
// Sometimes, mouseup event fires twice (e.g. in Google Doc).
// I don't know why, so I use the following timer trick to eat the second event.
var mouseUpWaiting = false;
function mouseUpHandler(e) {
getPref("cwf_floating_button_enabled", function(pref) {
if (pref == "true") {
if (mouseUpWaiting) {
return;
}
mouseUpWaiting = true;
window.setTimeout(function() {mouseUpDelayedHandler(e);}, 10);
}
});
getPref("cwf_auto_copy_enabled", function(pref) {
if (pref == "true") {
copyUnformattedTextFromSelection();
}
});
}
function mouseUpDelayedHandler(e) {
mouseUpWaiting = false;
// Only handle left mouse key
if (e.which == 1) {
// When user clicks on the floating menu, hide the menu.
// The text has been copied in the mousedown event handler.
if (isMenuItemVisible() && isWithInMenuItem(e.pageX, e.pageY)) {
hideMenuItem();
}
// When user finishes selecting some new content, we wait for a bit,
// and then show the floating menu
else {
window.setTimeout(function() {doMouseUp(e);}, 50);
}
}
}
var savedSelectedText = null;
function doMouseUp(e) {
var selection = getSelectedText();
// When user selects something new, show the floating menu.
if (!(selection == null || selection == "") && savedSelectedText != selection) {
showMenuItem();
}
savedSelectedText = selection;
}
var mouseDownWaiting = false;
function mouseDownHandler(e) {
// When user is clicking on the menu item, copy the current selection
if (isWithInMenuItem(e.pageX, e.pageY)) {
copyUnformattedTextFromSelection();
return;
}
// When user starts selecting something
else {
if (mouseDownWaiting) {
return;
}
mouseDownWaiting = true;
window.setTimeout(function() {mouseDownDelayedHandler(e);}, 10);
}
}
function mouseDownDelayedHandler(e) {
mouseDownWaiting = false;
// Do nothing when menuItemDiv is not visible
if (!isMenuItemVisible()) {
return;
}
hideMenuItem();
}
/**
* Show the floating menu at current mouse position.
*/
function showMenuItem() {
getPref("cwf_button_type", function(pref) {
if (pref == "1") {
menuItemDiv.innerHTML = "<img src='" + chrome.extension.getURL('icon16.png') + "'>";
menuItemDiv.style.height = "17px";
menuItemDiv.title = "Copy unformatted text";
}
else if (pref == "2") {
menuItemDiv.innerHTML = "Copy unformatted text";
menuItemDiv.style.height = "15px";
menuItemDiv.title = "";
}
else if (pref == "3") {
menuItemDiv.innerHTML = "<div style='float:left'><img src='" + chrome.extension.getURL('icon16.png') + "'></div>"
+ "<div style='float:left'>&nbsp;</div><div style='display:table-cell; vertical-align:middle'>Copy unformatted text</div>";
menuItemDiv.style.height = "16px";
menuItemDiv.title = "";
}
menuItemDiv.style.opacity = 0;
menuItemDiv.style.display = "block";
menuItemDiv.style.left = (curMouseX + 2) + "px";
menuItemDiv.style.top = (curMouseY + 2) + "px";
animateFadeIn(new Date().getTime(), menuItemDiv, 100);
});
}
/**
* Hide floating menu.
*/
function hideMenuItem() {
menuItemDiv.style.display = "none";
}
/**
* Check whether the floating menu is visible.
*/
function isMenuItemVisible() {
return menuItemDiv.style.display == "block";
}
/**
* Check whether a point is inside the floating menu.
*/
function isWithInMenuItem(x, y) {
return x >= menuItemDiv.offsetLeft
&& x <= menuItemDiv.offsetLeft + menuItemDiv.offsetWidth
&& y >= menuItemDiv.offsetTop
&& y <= menuItemDiv.offsetTop + menuItemDiv.offsetHeight;
}
/**
* Animate fading in.
*/
function animateFadeIn(lastTick, element, duration) {
var curTick = new Date().getTime();
var elapsedTicks = curTick - lastTick;
if(duration <= elapsedTicks) {
element.style.opacity = '1';
return;
}
element.style.opacity = elapsedTicks/duration;
window.setTimeout(function() {
animateFadeIn(lastTick, element, duration);
}, 10);
}
/**
* Key detection. Called at every key up/down.
*/
function detectKeys(KeyCode, IsKeyDown) {
hideMenuItem();
// Shift key is pressed/released
if (KeyCode == '16') {
SHIFT = IsKeyDown;
}
// Control key is pressed/released
else if (KeyCode == '17') {
CTRL = IsKeyDown;
}
// Alt key is pressed/released
else if (KeyCode == '18') {
ALT = IsKeyDown;
}
// Regular keys
else {
if(IsKeyDown) {
CHAR_CODE = KeyCode;
}
else {
CHAR_CODE = -1;
}
}
// Test whether user presses the specified shortcut key
getPref("cwf_modifier_index", function(pref) {
var modifierMatched = false;
var lowerCaseKey = false;
// Ctrl + Shift
if (pref == "0") {
modifierMatched = CTRL && SHIFT && !ALT;
}
// Ctrl + Alt
else if (pref == "1") {
modifierMatched = CTRL && !SHIFT && ALT;
lowerCaseKey = true;
}
// Alt + Shift
else if (pref == "2") {
modifierMatched = !CTRL && SHIFT && ALT;
}
// Ctrl + Alt + Shift
else if (pref == "3") {
modifierMatched = CTRL && SHIFT && ALT;
}
// Ctrl
else if (pref == "4") {
modifierMatched = CTRL && !SHIFT && !ALT;
lowerCaseKey = true;
}
getPref("cwf_key", function(pref2) {
var c = String.fromCharCode(CHAR_CODE);
if (lowerCaseKey) {
c = c.toUpperCase();
}
// debug(c+","+ pref2 +"," + modifierMatched);
if (c == pref2 + "" && modifierMatched) {
copyUnformattedTextFromSelection();
}
});
});
}
/**
* Get selected text from user selection
*/
function getSelectedText() {
// Maybe user selects the text in the main document?
var selectedText = window.getSelection();
// Also try to see whether user selects something in any of the embedded iframes.
// This is very tricky. The following procedure works for Googld Doc selection,
// but not Gmail selection...
var iframes = document.getElementsByTagName("iframe");
var i = 0;
for (i = 0; i < iframes.length; i++) {
if (iframes[i].contentDocument == null) {
continue;
}
try {
var iframeSelectedText = iframes[i].contentDocument.getSelection();
if (iframeSelectedText != null && iframeSelectedText != "") {
selectedText = iframeSelectedText;
}
}
catch(err) {
}
}
if (selectedText == null) {
return null;
}
else {
return selectedText.toString();
}
}
/**
* Send the selected text to background.html, where the copy operation happens
*/
function copyUnformattedTextFromSelection() {
var selection = getSelectedText();
if (selection == null || selection == "") {
return;
}
chrome.extension.sendRequest({selectedText: "" + selection}, function(response) {
});
}
function getPref(key, func) {
chrome.extension.sendRequest({k: key}, function(response) {
func(response.pref);
});
}
/**
* Initialization function. Will be called when the script is first time loaded.
*/
function init() {
// Add key listeners to main documents.
// Those key listeners detect keyboard shortcut.
document.addEventListener('keydown', keyDownHandler);
document.addEventListener('keyup', keyUpHandler);
// Add key listeners to all the documents of iframes.
var iframes = document.getElementsByTagName("iframe");
var i = 0;
for (i = 0; i < iframes.length; i++) {
if (iframes[i].contentDocument != null) {
iframes[i].contentDocument.addEventListener('keydown', keyDownHandler);
iframes[i].contentDocument.addEventListener('keyup', keyUpHandler);
}
}
// Add mouse up/down listener to the main document.
// Every event (no matter where it initiates) will eventually bubble up to the main document.
document.addEventListener('mouseup', mouseUpHandler);
document.addEventListener('mousedown', mouseDownHandler);
// Add mousemove listener to the main document. It keeps updating the current mouse position
document.addEventListener('mousemove', function(e) {
curMouseX = e.clientX + document.body.scrollLeft;
curMouseY = e.clientY + document.body.scrollTop;
});
// Initialize the floating menu div
menuItemDiv = document.createElement('div');
menuItemDiv.setAttribute('id', 'menu_item');
menuItemDiv.setAttribute('class', 'jjMenuItemDiv');
menuItemDiv.innerHTML = "Copy without formatting";
menuItemDiv.addEventListener('mouseover', function(event) {
menuItemDiv.style.background = "rgba(255, 204, 136, 0.9) !important";
});
menuItemDiv.addEventListener('mouseout', function(event) {
menuItemDiv.style.background = "rgba(255, 255, 255, 0.9) !important";
});
document.body.appendChild(menuItemDiv);
}
init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment