/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.clipboard;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import org.waveprotocol.wave.client.common.util.JsoView;
import org.waveprotocol.wave.client.common.util.QuirksConstants;
import org.waveprotocol.wave.client.common.util.UserAgent;
import org.waveprotocol.wave.client.editor.selection.html.NativeSelectionUtil;
import org.waveprotocol.wave.model.document.util.Point;
/**
* Provides an offscreen, HTML paste buffer for various browsers.
*
*/
public class PasteBufferImpl {
/**
* The actual element that contains the paste.
*/
protected Element element = null;
private static final boolean SHOW_DEBUG_PASTEBUFFER = false;
/**
* Factory constructor, creates and attaches the buffer to the DOM.
*
* @return Browser specific implementation of a paste buffer.
*/
static PasteBufferImpl create() {
PasteBufferImpl pasteBuffer;
if (UserAgent.isSafari()) {
pasteBuffer = new PasteBufferImplSafari();
} else if (UserAgent.isFirefox() && !QuirksConstants.SANITIZES_PASTED_CONTENT) {
// Older versions of firefox doesn't sanitize pasted content and requires the
// paste buffer to be an iframe to prevent XSS.
pasteBuffer = new PasteBufferImplFirefox();
} else {
pasteBuffer = new PasteBufferImpl();
}
pasteBuffer.setupDom();
return pasteBuffer;
}
/**
* Empty protected constructor. Use static create for instantiation.
*/
protected PasteBufferImpl() {
}
/**
* Clears and sets the content of the paste element.
*
* @param node The DOM to append.
*/
void setContent(Node node) {
element.setInnerHTML("");
element.appendChild(node);
}
/**
* Use this to get the root level container.
*
* @return The offscreen element.
*/
public Element getContainer() {
return element;
}
/**
* Use this to get the paste contents (from innerHTML).
*
* @return The pasted contents.
*/
public Element getPasteContainer() {
return element;
}
/**
* Prepare the buffer to accept a paste event by setting the selection and
* focus to the container.
*/
public void prepareForPaste() {
element.setInnerHTML("");
element.appendChild(Document.get().createTextNode(""));
NativeSelectionUtil.setCaret(Point.inText(element.getFirstChild(), 0));
}
protected void positionPasteBuffer(Element element) {
if (SHOW_DEBUG_PASTEBUFFER) {
element.getStyle().setPosition(Position.ABSOLUTE);
element.getStyle().setHeight(150, Unit.PX);
element.getStyle().setLeft(1000, Unit.PX);
element.getStyle().setTop(10, Unit.PX);
} else {
element.getStyle().setPosition(Position.ABSOLUTE);
element.getStyle().setTop(-100, Unit.PX); // arbitrary numbers
element.getStyle().setHeight(50, Unit.PX);
}
}
/**
* Sets up the PasteBuffer DOM.
*
* Implementations should call positionPasteBuffer
*/
protected void setupDom() {
element = Document.get().createDivElement();
// For some reason setting display to none prevents this trick from working
// instead, we move it away from view, so it's still "visible"
// NOTE(user): We setwhitespace pre-wrap prevents the browser from
// collapsing sequences of whitespace. This is important to ensure that the
// spaces after a start tag, or before an end tag are preserved through copy/paste.
// Also, we can't use DomHelper.setContentEditable as setting -webkit-user-modify
// to read-write-plaintext-only will force the pasted content to plain text and
// kill all formatting and semantic paste.
// This trick doesn't work in Firefox, because the pre-wrap attribute is not
// preserved through copy, to fix this in Firefox, we'll need to manually
// replace spaces with
element.setAttribute("contentEditable", "true");
JsoView.as(element.getStyle()).setString("white-space", "pre-wrap");
// DomHelper.setContentEditable(element, true, false);
element.getStyle().setOverflow(Overflow.HIDDEN);
positionPasteBuffer(element);
Document.get().getBody().appendChild(element);
}
}