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

LVL 13
numberkruncherAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sh0eCommented:
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);
0
sh0eCommented:
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.
0
numberkruncherAuthor Commented:
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.
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

sh0eCommented:
I remember doing something like this already for another question.  The solution ended up being using selections.  A little quirky and not entirely reliable, but it works.  And besides, none of the implementations of fake onpaste are reliable.  The one above is actually really easily tricked.

Anyhow, you use selectionStart and selectionEnd.  Then check how much has been pasted and grab the substring.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
numberkruncherAuthor Commented:
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.
0
sh0eCommented:
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:
http://www.experts-exchange.com/Software/Photos_Graphics/Web_Graphics/Macromedia_Flash/ActionScript/Q_23849403.html
0
sh0eCommented:
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.
0
numberkruncherAuthor Commented:
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.
0
sh0eCommented:
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. "
0
numberkruncherAuthor Commented:
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.
0
numberkruncherAuthor Commented:
Thanks for your input, it has been fantastic!
0
numberkruncherAuthor Commented:
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.
0
numberkruncherAuthor Commented:
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

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
HTML

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.