Using only standard javascript techniques it is not possible to copy text from the browser to the users clipboard. The only cross-platform method for web applications utilizes a Flash movie. Therefore a transparent Flash movie floats above a div element. A javascript on the web page can set the text to copy to the Flash movie by Adobes ExternalInterface API. Flash is able to copy the text to the clipboard after the user clicks an the transparent movie.
A widely used framework which bundles this functionality is ZeroClipboard [1] . The ZeroClipboard.js communicates with the provided Flash movie and creates, repositions and removes the corresponding DOM elements on the web page.
For component based web applications like vaadin it would be nice to have the complete javascript and Flash magic bundled into a ready-to-use widget. This blog post is about integrating ZeroClipboard into a vaadin widget. The widget is available in the vaadin addon directory [2] , the sources can be found on github [3] .
As a foundation for the new widget the com.vaadin.terminal.gwt.client.ui.VButton is used. It already provides everything I would expect from a Button. Only the initialization and communication with the ZeroClipboard lib and the communication with the server side vaadin component must be added. The new widget should then provide the following:
- the button should behave correctly in any position on the page
- add/remove buttons during runtime
- even in popups the button must be applicable (e.g using the Vaadin PopupButton addon [4] )
- multiple buttons on the page
- provide a listener to register for successful copy actions
The server side API is kept simple:
- construction like a usual Button
- #setClipboardText(String t)
- #addListener(ClipboardListener l)
The client side is responsible for the ZeroClipboard handling:
- JSNI calls for instance creation, repositioning, callback registration
- to provide accurate repositioning:
- show the Flash movie on mouseover
- hide the Flash movie an mouseout
- remove the DOM element from the page if the button is removed from the page
The ZeroClipboard client will be created in the #updateFromUIDL method since all javascript objects are available now. A flag limits instantiation to the first call:
1if (firstLoad) { 2 this.buttonId = ID_PREFIX + paintableId; // create a unique Id for the DOM Element 3 Element root = getElement(); 4 DOM.setElementAttribute(root, "id", buttonId); // add the id. this allows multiple clipboard buttons on the page 5 glueCopy(GWT.getModuleBaseURL(), root, buttonId); // glue the js and flash to the component: 6 7 firstLoad = false; 8} 9 10// set the clipboard text to the flash movie 11setClipboardText(clipboardText, buttonId);
The JSNI call [5] in #glueCopy connects the DOM element of the button with the new DOM element of the ZeroClipboard client. The client element is directly added to the page body. The placing of the client element inside the Vaadin generated DOM element might leed to simpler object and event handling but leads to missplacement of the whole widget when positioned in a popup. In this case Vaadin gets confused during size calculation of the button.
1private native void glueCopy(String baseUrl, Element buttonElement, String buttonId)
2 /*-{
3 var path = baseUrl + 'ZeroClipboard10.swf';
4 $wnd.ZeroClipboard.setMoviePath(path);
5
6 var clip = $wnd.ZeroClipboard.newClient(); // new instance
7 clip.zIndex = 20001; // float just over a vaadin popup
8 clip.glue(buttonElement); // magic
9 clip.hide(); // initially hide the flash movie
10
11 var widget = this;
12
13 // notify me when the copy action is done:
14 var completeListener = function copyComplete(client, text) {
15 widget.@de.codecentric.vaadin.gwt.client.VCopy2ClipboardButton::onComplete()();
16 }
17 clip.addEventListener('complete', completeListener);
18
19 // notify me when the mouse leaves the flash movie:
20 var mouseoutListener = function mouseoutL() {
21 widget.@de.codecentric.vaadin.gwt.client.VCopy2ClipboardButton::onMouseout()();
22 }
23 clip.addEventListener('mouseout', mouseoutListener);
24
25 // store the object on the button
26 $doc.getElementById(buttonId).clip = clip;
27 }-*/;
Unfortunately Firefox behaves different on JSNI calls from Chrome and IE.
Instead of
1var clip = new $wnd.ZeroClipboard.Client();
the construction of the client must be handled in a separate method newClient() inside ZeroClipboard.js. Even the listeners must be assigned to separate variables before registration. They will call back methods an the widget: The listener for the event “complete” will be called for a successful copy to clipboard action. The listener for “mouseout” lets the widget know the mouse left the Flash movie. It will then be hidden outside the viewport of the web page. The widget itself reacts to the “mouseover” event to show the Flash movie above the button.
Basic usage
Using the Copy2Clipboard button is straight forward:
1// Create a new instance
2Copy2ClipboardButton button = new Copy2ClipboardButton("Copy");
3// Set the text to copy
4button.setClipboardText("copy this to the clipboard");
5
6// The listener will trigger on successful copy action
7button.addListener(new ClipboardListener() {
8
9 @Override
10 public void copiedToClipboard(ClipboardEvent event) {
11 mainWindow.showNotification("Copied to the clipboard");
12 }
13});
Copy2ClipboardButton in a popup
If you want to use the Copy2ClipboardButton inside a popup you have to deal with two things:
Since the server side button will not be destroyed but only set to invisible the #onUnload method must decide whether to destroy or keep the ZeroClipboard DOM objects. You can tell the Copy2ClipboardButton during instantiation to behave the latter way using the retainClipElement flag.
In addition the popup will be closed when clicking on the flash movie because the event triggered outside of the popup. In order to reopen the popup instantly you have to register a ClipboardListener:
1Copy2ClipboardButton copyButton = new Copy2ClipboardButton("copy", true); // set retainClipElement to true
2final PopupButton popup = new PopupButton("show popup");
3popup.addComponent(copyButton);
4
5copyButton.addListener(new ClipboardListener() {
6
7 @Override
8 public void copiedToClipboard(ClipboardEvent event) {
9 popup.setPopupVisible(true);
10 }
11});
References
[1] ZeroClipboard on github
[2] copy2clipboard in the vaadin addon directory
[3] copy2clipboard on github
[4] Vaadin PopupButton addon
[5] JSNI Basics at google
More articles
fromHenning Treu
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Henning Treu
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.