Created
August 19, 2013 10:18
-
-
Save julik/6267710 to your computer and use it in GitHub Desktop.
Copypaste implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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'> </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