Supporting the system clipboard in your Web Applications: Part Two
I had a rather long post on the pain of dealing with the system clipboard in Web applications now that Flash went and fixed some security hole :/
The solution in place at that time worked well in Safari, with the on[cut|copy|paste]
and unfortunately not great with Firefox due to one crucial big of data being omitted.
Then Tom Robinson and a couple of others made the great point that I could get ALMOST everything I wanted with just the hidden textaraea trick. Instead of tying the trick to the on[cut|copy|paste]
events, I can just manually grab the Cmd/Ctrl-C X V commands. The only downside to this is that if the user goes to the Edit menu and chooses something, it won’t work. Annoying, but that’s the world of the hacky Web sometimes.
I added in this new tactic, and copy and paste works OK for Firefox in the latest version of Bespin in code (not deployed to bespin.mozilla.com at the time of this writing):
dojo.declare("bespin.util.clipboard.HiddenWorld", null, { install: function() { // * Configure the hidden copynpaster element var copynpaster = dojo.create("textarea", { tabIndex: '-1', autocomplete: 'off', id: 'copynpaster', style: "position: absolute; z-index: -400; top: -100px; left: -100px; width: 0; height: 0; border: none;" }, dojo.body()); var grabAndGo = function(text) { copynpaster.value = text; focusSelectAndGo(); }; var focusSelectAndGo = function() { copynpaster.focus(); copynpaster.select(); setTimeout(function() { dojo.byId('canvas').focus(); }, 0); }; this.keyDown = dojo.connect(document, "keydown", function(e) { if ((bespin.util.isMac() && e.metaKey) || e.ctrlKey) { // Copy if (e.keyCode == 67 /*c*/) { // place the selection into the textarea var selectionText = _editor.getSelectionAsText(); if (selectionText && selectionText != '') { grabAndGo(selectionText); } // Cut } else if (e.keyCode == 88 /*x*/) { // place the selection into the textarea var selectionObject = _editor.getSelection(); if (selectionObject) { var selectionText = _editor.model.getChunk(selectionObject); if (selectionText && selectionText != '') { grabAndGo(selectionText); _editor.ui.actions.deleteSelection(selectionObject); } } // Paste } else if (e.keyCode == 86 /*v*/) { focusSelectAndGo(); setTimeout(function() { // wait just a TOUCH to make sure that it is selected var args = bespin.editor.utils.buildArgs(); args.chunk = copynpaster.value; if (args.chunk) _editor.ui.actions.insertChunk(args); }, 1); } } }); }, uninstall: function() { dojo.disconnect(this.keyDown); } });
Because of the issues, I took out the UI buttons for cut/copy/paste, and am in fact wondering if the editor needs that row at all. I wonder if we can consolidate the header to one line, giving us more vertical space. A code editor for developers is not like Google Docs for average Joe users, so having the visual cues probably doesn’t matter in the same way for items like copy.
There are a few subtle annoyances such as running an action like killLine (Ctrl-K) which cuts the selection but has to do so in a non-work with the clipboard way.
End result: getting there, but still need to work on making this generally viable for any application on the Open Web Platform. What do you think?
UPDATE: Used the copynpaster
variable throughout, and added a setTimeout around the paste operation as I found some people needed to hit the paste key twice as the focus()/select() was taking a little too long.
March 4th, 2009 at 9:58 am
> visual queues
cues?
March 4th, 2009 at 11:04 am
Oh yes, consolidating the two headers to one line sounds great.
March 4th, 2009 at 11:07 am
RichB,
Thanks :) Fixed
March 5th, 2009 at 1:00 am
Thanks for sharing, I needed some clipboard filtering solution for my in place editor, and this fits perfectly. As i understand, it should work on all browsers, not just firefox?
March 5th, 2009 at 1:21 am
I find typing one handed and copy/pates one handed is easier with the mouse.
Click and highlight the text I want, right click copy, go to my next window, click to place cursor then click on the paste button. I dislike right click to paste because the cursor usually moves as I right click.
Food for thought. I do have two hands but sometimes I am coding and holding a sleeping baby at the same time.
monk.e.boy
March 5th, 2009 at 4:49 am
Sorry, this is a bit OT
Concerning the Mozilla Labs event on Tuesday March 10, 2009, I came to the one the last time, maybe a bit late (around 7pm) but I could not find the floor where the meeting was. I guess it was probably the last floor of the Waterstone, but as I did not see anybody with a T-shirt mozilla, I left…
Would it be possible to have a sign or something to indicate where is the mozilla lab meeting?
thanks in advance,
March 5th, 2009 at 5:02 am
Hey Dion,
Excellent! It even compensates for us Mac users who have to switch back and forth from windows VMs! Ctrl-C/X/V works on all platforms according to your code. :-)
You sure got comfy with dojo fast! You make it look so easy!
One suggestion: dojo.publish/subscribe might be a simple way to pass data to/from the editor. (How’s that _editor var getting into the scope here, anyways?)
Oh, #2: instead of using dojo.byId(’copynpaster’) to look up the hidden textarea every time, you could use the reference you already have as var copynpaster = …
Great work (and beautiful code: I wish more peeps could take the time to write good code like this.)
– John
March 5th, 2009 at 1:12 pm
Hi Dion,
Being left-handed, I never use Cmd/Ctrl-C X V. I would have to take my hand off the mouse to do that. I use CTRL-Insert (copy) and SHIFT-Insert (paste).
This works in almost every Windows app, except for those by Adobe for some reason. It would be nice if it could work here too.
March 5th, 2009 at 9:32 pm
@James,
Ah, good point. I will have to test to see if CTRL-Insert/SHIFT-Insert fire the on* events for WebKit. On Firefox, if they cause a copy/paste then I should be able to just wire them up and be good to go.
Thanks! Filed this as a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=481801
March 5th, 2009 at 9:42 pm
@unscriptable,
Great points. We do use pub/sub as a way to event into the right scope. _editor is a cheat for this example. I *love* pub/sub (and will do a post on our usage of that shortly).
I changed the code to use the copynpaster too, great idea. Thanks!
March 5th, 2009 at 9:46 pm
@Ben,
Ah yes, I *really* don’t want this to happen to you again (and anyone else). Also, just in case, I will be the large chap with glasses, and Ben is the slightly taller and smaller chap.
Some bad photos: http://www.flickr.com/search/?w=all&q=Ben+Galbraith+Dion+Almaer&m=text
Looking forward to meeting you there!
March 6th, 2009 at 11:18 am
One thing that we ran into with the hidden textarea approach is placement of the textarea element. When the textarea gets focus it will try to scroll to that position on the page (-100px in this case). Not a problem for Bespin since the browser scroll bar is not used I guess :) but can be annoying in a general case.
March 6th, 2009 at 3:04 pm
@Dave,
If you notice, Dion is using tabIndex=-1. This prevents the text area from getting focus. If for some reason you can’t use this, then you could just use right: 101% to position the text area. This won’t scroll the page if it gets focus.
– John
March 11th, 2009 at 11:21 pm
IMO, a better way of doing this is listening to onpaste/oncut/oncopy, instead of listening to onkeydown of ctrl+v/ctrl+x/ctrl+c
keyboard shortcut will trigger onpaste/oncut/oncopy (right after onkeydown), so for keyboard events, it works the same, while this also works if user choose copy/paste/cut from the brower Edit menu or from the native browser context menu (if the native context menu is used there)
March 20th, 2009 at 4:51 am
@liucougar: In Firefox, the onpaste/oncut/oncopy events fire too late, or too weird, for this to work. Key combo listeners will do the trick, but the odd fact is that paste combo Shift+Insert is mangled: The textarea receives the focus alright, but nothing gets pasted into it.
May 17th, 2009 at 12:45 pm
Having read both articles and looked at the code –
1)
Are you able to have a ‘copy’ button/icon and a ‘paste’ button/icon that lets you copy/paste from an external editor into the browser?
If so, how? Can you give a more generic (non-Dojo) description?
2)
If not, why is Ctrl-XCV not working – isn’t this supported by all browsers? (IE. I just copied and pasted this post from Notepad++)
August 2nd, 2009 at 3:23 am
Awesome articles, very helpful. I also used the textarea technique in my web app to get at the system clipboard via keystrokes – but i never thought of using tiny floats… thanks!
I have also written a similar article at http://brooknovak.wordpress.com/2009/07/28/accessing-the-system-clipboard-with-javascript/ which explores some other approaches, check it out
Cheers