Link to home
Start Free TrialLog in
Avatar of numberkruncher
numberkruncherFlag for United Kingdom of Great Britain and Northern Ireland

asked on

JavaScript cleanup pasted content during onpaste of DIV

How do I get the text which is about to be pasted from the clipboard during the onpaste event which occurs when the user pastes text into the contentEditable DIV?

This needs to be a cross-browser solution.

The onpaste event is not fired in all browsers by default, but I found a website which describes how to get the onpaste event to fire across all major browsers (http://www.actsascommunity.com/snippets/6).
<html>
<head>
	<title>Demo</title>
	<script type="text/javascript" src="prototype.js"></script>
	<script type="text/javascript">
		// Following three methods taken from: http://www.actsascommunity.com/snippets/6
		// Verified: 5:25 PM 26/10/2008
		function checkForPaste(event) {
			var e = event.element();
			if ((e.previousValue && e.value.length > e.previousValue.length + 1) || (!e.previousValue && e.value.length > 1)) { 
				if (e.onpaste)
					e.onpaste(e)
				else if (e.readAttribute("onpaste"))
					eval(e.readAttribute("onpaste"));
			}
			e.previousValue = e.value;
		}
		function firefoxOnPaste() {
			$$('div').each(function(e) { 
				if (e.contentEditable)
					if (e.onpaste || e.readAttribute("onpaste"))
						Event.observe(e,'input',checkForPaste);
			});
		}
		if (Prototype.Browser.Gecko) {
			document.observe('dom:loaded', firefoxOnPaste);
		}
 
		function editor_onPaste(event) {
			// This method is fired to handle the editor paste event.
			// Method is fired in IE, FF, Chrome, and Safari upon pasting into the editable DIV.
 
			//// Alert to verify that event is being fired.
			//alert('pasted');
 
			// QUESTION: How do I get the text which is about to be pasted from
			// the clipboard? This needs to be cross-browser solution.
//			var pasteText = ???
 
			// I actually have the following two points working already.
 
			// 1. Here is where I will process and cleanup the pasted text.
			// 2. And here is where I will insert it into the DIV at the caret position.
		}
	</script>
</head>
<body>
	<div style="min-height: 300px; border: solid 1px gray; padding: 6px" contentEditable="true" onpaste="editor_onPaste(event || window.event)">
	</div>
</body>
</html>

Open in new window

Avatar of sh0e
sh0e

So, what this code does is check if the length of the text has changed more than 1 character.
All you have to do is use e.value.substring(e.previousValue.length);
I also want to mention that most of the methods, including this one, are outdated since onpaste is implemented in FF3, and oninput no longer fires when you paste.  Also, FF3 doesn't give you access to clipboard data.
Avatar of numberkruncher

ASKER

The user might not necessarily be entering text at the end of the DIV element. So instead of getting a sub-string from the end of the input field, I would need to get it using the selection range instead. How reliable would you suggest that this approach would be?

That's interesting, I didn't realise that second point.

I will have to read up on the new FF3 events on the Mozilla documentation website. Is there an event which fires no matter how the DIV content is changed? Like 'onchange' at a guess?

I am basically trying to constrain the types of content that can be contained within the editable field. It would be fantastic if it were possible to create a single solution which also covered dragged and dropped content....this might be asking too much of JavaScript I fear.

Obviously I am not 100% reliant on valid data being transmitted by the client because the server does validate upon receipt. At the moment I am just concerned about maintaining an accurate representation for the user.
ASKER CERTIFIED SOLUTION
Avatar of sh0e
sh0e

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
In what situations is the above tricked?

Yes I agree, I think the selectionStart and selectionEnd approach may be the way to go.

I just had a random thought, I seem to remember that Flash can copy to the clipboard. If it is possible, would you recommend this approach? The only problem I see is overriding the "Paste" command from the Edit and Context Menus of each web browser.
I forget exactly, but what I do remember is that one character pastes don't behave correctly, and that copying and pasting over selections doesn't always behave correctly.  I think it's if they're the same size?

The Flash idea is interesting.  I don't see why it wouldn't work.  If I get time I'll surely give it a go (have to go and reinstall Flash).  Tell me how you progress if you try it.

Actually, if you don't mind, would you be willing to make a Flash that handles clipboard reading/writing and provides a JavaScript API?  Then I can implement it quickly.  Post it here:
https://www.experts-exchange.com/questions/23849403/Copy-Paste-Manipulation-Adobe-Flash-Clipboard.html
I hate to be the bringer of bad news, but due to a recent exploit, clipboard manipulation will require user interaction in a Flash 10 update.  Bummer.
Yeah sure sh0e, I will give that a go today. I am away for most of the day, but I will do it this evening and upload it to your site.

That might be a problem, I was going to have the flash control hidden, I hope that the user interaction is by way of a HTML confirm style window as opposed to an internal flash message box.
Unfortunately,

http://www.adobe.com/devnet/flashplayer/articles/fplayer10_security_changes_02.html#head31
"What do I need to do?

Any existing content that sets data on the system Clipboard using the System.setClipboard() method outside of an event triggered by user interaction will need to be updated. Setting the Clipboard will now have to be invoked through a button, keyboard shortcut, or some other event initiated by the user. "
For me, a popup message box wouldn't be too bad. When I was trying to implement the flash file I came across a bigger problem.

Flash provides 'System.setClipboard' but for security reasons there is no way to access the clipboard. I am going to take a look into a Java applet to see if it is possible there; otherwise I will just have to go with the less perfect HTML solution. If I do manage to get it working in the form of a Java applet, I will post the source here.

Quoted from: http://www.cgdou.net/flash/as3reference/flash/system/System.html
-------------------------------------
Note: Because of security concerns, it is not possible to read the contents of the system Clipboard. In other words, there is no corresponding System.getClipboard() method.
Thanks for your input, it has been fantastic!
With the help of n_sachin1 on this website, I have created a Java applet which exposes a JavaScript interface to get the contents of the clipboard.

I have attached a ZIP file of the JCreator applet project.

The only problem with this solution is that you get an "Untrusted Certificate" warning message each time you load the applet. If I can work out a way to get rid of this, then there will be no user interruptions when accessing the clipboard contents.
Sorry, EE refused to add the ZIP file.

I have listed the files in the code snippet box below:

htmlEditor.java
htmlEditor.xml (ANT Script)
htmlEditor.html (HTML test page)
htmlEditor.java
===============
package numberkruncher;
 
import java.applet.*;
import java.io.*;
import javax.swing.event.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.text.html.*;
import javax.swing.text.*;
import java.net.*;
import java.awt.datatransfer.*;
 
public class htmlEditor extends JApplet {
	public htmlEditor() {
	}
 
	public void init() {
	}
	
	public String doSomething2() {
		return "Hello World!";
	}
 
	public String doSomething() {
		String result = "";
			
	    result = (String)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
			public Object run() {
				String result = "";
				
				Clipboard clipboard = getToolkit().getSystemClipboard();
				Transferable clipboardContent = clipboard.getContents(this);
				if (clipboardContent != null && clipboardContent.isDataFlavorSupported(DataFlavor.stringFlavor))
				{
					try
					{
						result = (String)clipboardContent.getTransferData(DataFlavor.stringFlavor);
					}
					catch(Exception e)
					{
					}
				}
				return result;
			}
	    });
		
		return result;
	}
}
 
 
htmlEditor.xml (ANT Script)
===========================
<?xml version="1.0" encoding="UTF-8"?>
 
<project basedir="." default="all" name="numberkruncher">
	
	<property file="${basedir}/build.properties"/>
	<property name="bin" value="${basedir}/../bin" />
	<property name="src" value="${basedir}/../src" />
	<property name="dist" value="${basedir}/../dist" />
	<property name="app.jar" value="numberkruncher.jar" />
	
	<path id="project.class.path">
		<pathelement location="${bin}" />
	</path>
	
	<target name="clean">
		<delete file="${dist}/${app.jar}" />
		<delete failonerror="false" includeemptydirs="true">
			<fileset dir="${bin}" />
		</delete>
	</target>
	
	<target name="init">
		<mkdir dir="${bin}" />
		<mkdir dir="${dist}" />
	</target>
 
	<target  name="compile" depends="init">
		<javac classpathref="project.class.path" destdir="${bin}">
			<src path="${src}" />
		</javac>
	</target>
	
	<target name="dist"  depends=" compile">
		<jar compress="true" destfile="${dist}/${app.jar}">
			<fileset dir="${bin}">
				<include name="**/*.class" />
			</fileset>
			<manifest>
				<attribute name="Main-Class" value="htmlEditor" />
			</manifest>
		</jar>
		<copy todir="${dist}">
			<fileset dir="${src}">
				<include name="**/*.html" />
			</fileset>
		</copy>
	</target>
	
	<target name="keytoolexec" unless="keystore.exists">
		<mkdir dir="${basedir}/Keystore"/>
		<exec dir="${basedir}" executable="keytool" failonerror="true">
			<arg value="-genkey "/>
			<arg value="-alias" />
			<arg value="numberkruncher"/>
			<arg value="-keystore" />
			<arg value="${basedir}/Keystore/Kruncher.jks"/>	
			<arg value="-keypass" />
			<arg value="n_sachin1"/>
			<arg value="-dname" />
			<arg value="CN=Test CA, OU=NK, O=NK, L=Bangalore, S=KA, C=IN"/>	
			<arg value="-storepass" />
			<arg value="n_sachin1"/>
			<arg value="-validity" />
			<arg value="365"/>
			
		</exec> 
	</target>
	
	<target name="testKeyStore">
		<available property="keystore.exists" file="${basedir}/Keystore/Kruncher.jks" />
	</target>
 
	  <target name="signjar" depends="dist, testKeyStore, keytoolexec">
	  	
	    <echo message="------ Signing JAR files"/>
	     <signjar jar="${dist}/${app.jar}" storepass="n_sachin1" alias="numberkruncher"
	           keystore="${basedir}/Keystore/Kruncher.jks" keypass="n_sachin1" />
	  </target>
 
	
	<target name="all" depends="clean, dist, signjar"/>
</project>
 
 
htmlEditor.html (HTML test page)
================================
<html>
	<head>
		<title>Test Page</title>
		<script type="text/javascript">
			function test() {
				var myApplet = document.getElementById('myApplet');
				if (myApplet)
					alert(myApplet.doSomething());
				else
					alert('Invalid applet!');	
			}
		</script>
	</head>
	<body>
		<center style="border: solid 1px black">
			<applet
				id		= "myApplet"
				code	= "numberkruncher.htmlEditor.class"
				width	= "500"
				height	= "300"
				archive	= "dist/numberkruncher.jar"
				>
			</applet>
		</center>
		<br />
		<input type="button" value="Do Something!" onclick="test()" />
	</body>
</html>

Open in new window