Image Protection

It does not work in Opera 5- and Omniweb.

The script does not work in Opera 7.20 because this browser cannot detect right clicks at all.

Detecting whether the user calls up the special menu is impossible in Explorer on Mac

If the image is inside a link the script does not work in Netscape 4.

When right-clicking on the link, Opera 7 gives the alert ages after the context menu has disappeared.

See this script in real live action at the photo pages of the World Press Photo site I coded.

On this page I explain how you can partially protect your images from being copied. The most common solution is to disable the right click with which users copy an image. However, it can also be done by dragging the image to your desktop (on Mac this is the only way). This script disables the dragging also.

First of all, please note that this is in no way a true protection of your images. It will only stop newbies, more advanced surfers may turn of JavaScript and copy the image anyway. Besides, when the image is also a link the script becomes much less reliable.
Nonetheless it serves as a polite reminder of the copyright to the users of your site and as a token to the holder of the copyright that you are taking some steps to protect his intellectual property.

I know this script is not perfect, there are plenty of ways to copy the image despite the script. However, it's the best I can do. Please do not mail me about the script not working in certain conditions. I know. It can't be helped.

Copying an image

There are two ways of copying an image:

  1. By selecting 'Save Image' or something like that from the special menu.
  2. By dragging the image to your desktop.

The special menu is called by pressing the right mouse button (Windows and Linux) or holding down the Ctrl button while clicking (Mac). Detecting a right click is simple, but unfortunately it turned out that when you call up the special menu in Explorer and Opera on Mac, it detects no events at all. Therefore detecting the special menu in these browsers is impossible and the image can be copied in that way.

As to dragging the image, this works in Explorer and in any Mac browser and this script defeats it.

Problems when the image is a link

Things get murkier when the image is also a link. First of all, Netscape 4 simply doesn't register any more events on the image, so the script won't work. Secondly, we'll have to take great care in the drag part of the script. After all, if a user clicks on the link he may move the mouse just a tiny little bit while doing it. This shouldn't be counted as an attempt to copy the image, the browser should load the next page. So I decided that if the the image is a link and the user moves the mouse less than 10 pixels he's trying to click the link. Only when he moves the mouse more we count it as a copying attempt.

As to Explorer on Windows, although you can drag the image to your desktop and this is difficult to prevent, the action nets you only a shortcut to the page the link leads to, not a copy of the actual image.

Example

Try copying the images below. The right image is inside a link.

The script

var specialcase = ((navigator.userAgent.indexOf('Mac') != -1) || document.all)
var flag = 0;
var msg = 'This image is protected by copyright.\nWe request you not to copy it.';
var x,y,x1,y1,copyAttempt;

function init()
{
	if (!(document.getElementById || document.all || document.layers)) return;
	if (specialcase && document.layers)
	{
		document.captureEvents(Event.MOUSEMOVE);
		document.onmousemove = special;
	}
	for (i=0;i<document.images.length;i++)
	{
		document.images[i].onmousedown = checkIt;
		document.images[i].onmouseup = function() {return false};
		if (specialcase)
		{
			document.images[i].onmousemove = special;
			document.images[i].onclick = clearIt;
		}
	}
}

function checkIt(e)
{
	copyAttempt = 0;
	if (window.Event)
	{
		x = e.screenX;
		y = e.screenY;
		theButt = (e.which == 3);
	}
	else
	{
		x = window.event.clientX;
		y = window.event.clientY;
		theButt = (window.event.button == 2);
	}
	if (theButt)
	{
		copyAttempt = 1;
		flag = 0;
		alert(msg);
		return false; // NN4 only
	}
	if (specialcase) flag = 1;
	return false;
}

function special(e)
{
	theObj = '';
	if (window.Event)
	{
		x1 = e.screenX;
		y1 = e.screenY;
		if (e.target.parentNode) theObj = e.target.parentNode.tagName;
	}
	else
	{
		x1 = window.event.clientX;
		y1 = window.event.clientY;
		theObj = window.event.srcElement.parentElement.tagName;
	}
	var isLink = (theObj == 'A');
	if (flag && (!isLink || ((Math.abs(x-x1) > 10) || (Math.abs(y-y1) > 10))))
	{
		copyAttempt = 1;
		flag = 0;
		alert(msg);
		return false;
	}
}

function clearIt()
{
	flag = 0;
	if (copyAttempt)
	{
		copyAttempt = 0;
		return false;
	}
}

and call function init() onLoad:

<BODY etc. onLoad="init()">

Explanation

We work our magic by detecting certain mouse events and then deciding if they're meant to copy an image:

  1. If the user selects the special menu, this is a copying attempt and we should produce our warning.
  2. If the user can drag-copy and he presses any mouse button and then starts moving the mouse, this is a copying attempt and we should produce our warning.
  3. If the image is also a link, however, we should give the user some leeway for moving his mouse a tiny little bit while clicking the link.

First of all we determine if the user has a browser that allows image dragging (Mac or Explorer, specialcase becomes true) and we set the special variable flag to 0 (I'll explain below).

var specialcase = ((navigator.userAgent.indexOf('Mac') != -1) || document.all)
var flag = 0;
var x,y,x1,y1,copyAttempt;

Then comes the message for the alert:

var msg = 'This image is protected by copyright.\nWe request you not to copy it.';

function init()

onLoad the function init() is executed which initializes the event handlers. First of all we check if the browser can handle the script. If it can't we stop the script, it would only produce errors.

function init()
{
	if (!(document.getElementById || document.all || document.layers)) return;

Now we define a great number of event handlers. First of all the specialcase browsers that support document.layers ( = Netscape 4 on Mac) should get a mousemove event for the whole page (I'd rather have defined it for the images only, but that's impossible in Netscape 4).

	if (specialcase && document.layers)
	{
		document.captureEvents(Event.MOUSEMOVE);
		document.onmousemove = special;
	}

From now on Netscape 4 on Mac will send all mouseMove events in the entire document to the function special().

Then we go through all the images in the document and attach a mouseDown event to them. This event fires the primary right-click disabling script checkIt().

	for (i=0;i<document.images.length;i++)
	{
		document.images[i].onmousedown = checkIt;

onMouseUp we return false. This is for disabling the special menu in Netscape 6 on Windows, where it comes up onMouseUp, not Down. Returning false doesn't hurt the other browsers.

		document.images[i].onmouseup = function() {return false};

If the browser allows dragging, we give the images function special() for mouseMove. onClick we should call clearIt(), that cancels the link after a copying attempt.

		if (specialcase)
		{
			document.images[i].onmousemove = special;
			document.images[i].onclick = clearIt;
		}
	}
}

The scripts have now been initialized.

onMouseDown: checkIt()

checkIt() handles all mouseDown events on images. onMouseDown two checks should be made:

  1. Does the user try to call up the special menu? This is a copying attempt.
  2. If the users uses a specialcase browser, set a flag because the mouseDown event might be followed by a mouseMove event, which constitutes a copying attempt.

For Netscape's sake we hand the event to the function in the traditional variable e.

function checkIt(e)
{

Then we need to find out two things:

  1. What is the mouse position? This is important when the image is also a link, later on we'll need to compare mouse positions to see if the user tries to copy the image or merely tries to follow the link.
  2. Which button is pressed?

Not surprisingly, the code for this is browser specific. First Netscape:

	if (window.Event)
	{
		x = e.screenX;
		y = e.screenY;
		theButt = (e.which == 3);
	}

x and y now hold the coordinates of the mouse, while theButt is true if the user presses the right button (if the which property of the event is 3). Surprisingly which is also 3 when you press Ctrl in Netscape 6 on Mac, so this copying attempt is also defeated. (As to Netscape 4 on Mac, it doesn't show the special menu either, although I have no idea why it doesn't. I haven't consciously programmed it into the script.)

Then for Explorer:

	else
	{
		x = window.event.clientX;
		y = window.event.clientY;
		theButt = (window.event.button == 2);
	}

The same result, though with different code. As said before, it is impossible to detect the calling of the special menu on Mac because if you call it suddenly Explorer doesn't register any more events.

Now we see if the special menu is called:

	if (theButt)
	{

If it is, we need to do several things. First of all this consitutes a copy attempt, so we set the variable copyAttempt to 1.

		copyAttempt = 1;

Secondly, flag becomes 0. The script doesn't need to check any other events any more, this is already a copying attempt.

		flag = 0;

We give the user an alert with our message.

Very weird: The alert is required in Explorer on Windows for making this script work. No alert, no right-click disabling. (Why? I have no idea)

		alert(msg);

and finally we return false. This is to prevent the special menu to pop up in Netscape 4 on Windows and Linux.

		return false; // NN4 only
	}

Finally, if this is a specialcase browser we set flag to 1, which means that the mouseMove function goes into a state of alert, watching for a copy attempt. After that we return false (now for the sake of Netscape 4 on Mac).

	if (specialcase) flag = 1;
	return false;
}

onMouseMove: special()

onMouseMove the function special() is executed. It gathers data about the mouseMove, then decides if the moving constitues a copying attempt.

First of all we pass the event to it and we create a new variable theObj which we'll need later on.

function special(e)
{
	theObj = '';

Now we once again need to find out the mouse position. In addition, we want to know what the parent element of the image is. If it's a link, we'll need to allow a bit of mouseMoving.
We can only find the parentNode of the link in Netscape 6, but since Netscape 4 won't do anything when the image is inside a link, I don't mind.

	if (window.Event)
	{
		x1 = e.screenX;
		y1 = e.screenY;
		if (e.target.parentNode) theObj = e.target.parentNode.tagName;
	}

Same for Explorer, here the parent element of the image is found through a different bit of code.

	else
	{
		x1 = window.event.clientX;
		y1 = window.event.clientY;
		theObj = window.event.srcElement.parentElement.tagName;
	}

Then we see if the image is in a link. If it is, the tagName of the parentElement is A. Variable isLink now is true when the parent element is a link.

	var isLink = (theObj == 'A');

Then, finally, we come to the bit that tries to see if the moving of the mouse is a copying attempt. First of all, we see if the variable flag is set to 1. This happens only when there already has been a mouseDown event on the image.

	if (flag

If flag is set AND the image is NOT inside a link, any mouseMove constitues a copying event.

	&& (!isLink

The mouseMove also consitutes a copying attempt if the mouse has moved more than 10 pixels either horizontally or vertically.

	|| ((Math.abs(x-x1) > 10) || (Math.abs(y-y1) > 10))))

So the total if statement becomes:

	if (flag && (!isLink || ((Math.abs(x-x1) > 10) || (Math.abs(y-y1) > 10))))
	{

If this statement is true, the user tries to copy the image and we need to do the same as before:

		copyAttempt = 1;
		flag = 0;
		alert(msg);
		return false;
	}
}

onClick: clearIt()

Finally we need to disable links if the user has just tried to copy a link image.

function clearIt()
{

First we set flag to 0, mouseMove doesn't need to be on alert any more.

	flag = 0;

If we've just defeated a copy attempt

	if (copyAttempt)
	{

we set copyAttempt to 0 once again (mission succeeded)

		copyAttempt = 0;

and we return false. This stops the browser from following the link, if there is any. This would be too confusing right after the warning alert.

		return false;
	}
}

That's it. You've protected your images as much as is possible.