// Image Protect, JavaScript.
// For The NEUMES Project website.
// Copyright 2004-2006 by Louis W. G. Barton

// Do not change the line saying 'copyright notice'
/* Filename: imageprotect.js
 * Copyright Notice:
 * Copyright 2005-2006, Louis W. G. Barton.
 * All rights reserved.
 */

// Version: 5 January 2006
/*
 * Abstract
 * ========
 * This script should be included in the <head> section of all HTML documents of the
 *  NEUMES website.
 * The primary purpose is to intercept attempts by users to save images to local disk and
 *  display a copyright notice instead. Image saving mechanisms vary between platforms and
 *  browsers, such as right-click on an image (Windows and Linux), image drag to the desktop
 *  (Mac), image drag to Word (IE), or holding down the Ctrl button while clicking (Mac).
 *  This functionality of this script needs to be supplemented on each page by the meta-tags,
	<meta name="ROBOTS" content="NOIMAGECLICK"> (prevents use of links directly to images)
	<meta name="ROBOTS" content="NOIMAGEINDEX">
	<meta HTTP-EQUIV="imagetoolbar" CONTENT="no">
 * Obviously, the interception does not work if the user has JavaScript turned off; a workaround
 *  is to allow access to sensitive pages only via JavaScript. A further deterent is to stop
 *  local caching of images by:
	<META HTTP-EQUIV="Pragma" CONTENT="no-cache"> (this, however, slows image access)
 *  and/or,
	<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
 * This script does not work in Opera 5- and Omniweb; therefore, if possible, detect these two
 *  browsers, and disable caching (use JS document.write() of META tag in the HEAD).
 *
 * Since it can be relied upon that, this script is in every page of the website, additional
 *  functionality can be added here without having to insert it in pages individually.
 * Additional functionality:
 * 1/ Check whether the page is being framed inside a non-NEUMES Project webpage,
 *    in which case the page gets replaced immediately by a violation warning page.
 *
 * WARNING: The event handlers registered here for onMouseDown, onMouseUp, and onMouseMove might
 *  conflict with event handlers registered on particular pages of the website; be aware of
 *  potential conflicts, and adjust sourcecode accordingly. (The NEUMEs website makes frequent
 *  use of onMouseOver, onMouseOut, and onClick for hyperlink control.) A solution to conflicts
 *  might be for other scripts defined AFTER this one to add code to a handler by this pattern:
	function addLoadEvent(func) {
		var oldonload=window.onload;
		if (typeof window.onload != 'function') {
			window.onload=func;
		} else {
			window.onload=function() {
				oldonload();
				func();
			}
		}
	}  //end, addLoadEvent()
	...
	addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
 *
 * Limitations
 * ===========
 * Does not work in Opera 5- and Omniweb. Does not work in Opera 7.20 because this browser does
 *  not detect right-clicks at all. Detecting whether the user calls up the 'special' menu is
 *  impossible in Internet Explorer on Mac. When the image is also a link, this script is much
 *  less reliable. If the image is inside a link, this script does not work in Netscape 4.
 *
 * Usage
 * =====
 * In the <head> section of an HTML document, write:
	<SCRIPT LANGUAGE="text/javascript"
	SRC="http://purl.oclc.org/SCRIBE/NEUMES/scripts/imageprotect.js"></SCRIPT>
 *
 * Implementation Notes
 * ====================
 * Cases to consider:
 * 1/ User drags image to desktop or another app
 *   1.a/ MAC, any browser other than NS4
 *   1.b/ MAC, NS4 (events cannot be registered on images)
 *   1.c/ IE, any ver
 * 2/ User clicks on image
 * In JavaScript, names of variables and functions in a script are particular to the document
 *  in which the script is defined. Therefore, each document containing this script will have
 *  its own copies of these names, and no confusion should arise.
 * In NS4, event handlers can only be registered to layers. viz., a DIV or a similar HTML
 *   element that has position: absolute in the style sheet.
 * Three event models:
 * 1) Netscape Navigator 4 (NN4);
 * 2) Internet Explorer 4 and later for Macintosh and Windows (IE4+); and
 * 3) W3C DOM, as implemented in Netscape Navigator 6.
 * There are five binding techniques implemented in various browser versions.
 * TO DO:
 * if not top.getFramesState() or top.getFramesState()!='no' then reopen this page in frames.
 *
 * Semantic Designs Obfuscator Notes:
 *  String literals should get encrypted.
 *  Callback fns must be added to reserved-names list.
 *  RegExp for copyright notice (at bottom of file) is added to Comment Filter in the GUI
 *  (blanks and punctuation escaped).
 *
 * <BODY oncontextmenu="return false" onselectstart="return false" ondragstart="return false">
 * oncontextmenu: supported by IE5+ only: The right mouse button (or its equivalent keyboard
 *  sequence) has been activated for an element.
 * onselectstart: supported by IE4+ only. Content selection has begun.
 * ondragstart: supported by IE4+ only. Notifies the source element when a drag operation begins.
 */


/*	**************************
	***  GLOBAL VARIABLES  ***
	**************************
*/

var img_prot_initialized=false;  // whether mousedown events have been registered for images
var img_prot_drag=false;  // sense drag: set true when OnMouseDown; clear false when OnMouseUp
var img_prot_ckLoadedTimer=null;  // timer for periodic check of whether document has loaded

var img_prot_msg1='Please do not copy images or other intellectual property from this website.' +
 '\n\nAll content of this website is protected by one or more of ' +
 'the following copyrights, or by other copyrights on particular pages.\n' +
 'Copyright 2005-2006, The University of Oxford.\n' +
 'Copyright 2003-2005 by Louis W. G. Barton.\n';
var img_prot_msg2='Copyright 2002-2003 by The President and Fellows of Harvard College;\n' +
 'contains software or other intellectual property licensed from Louis W. G. Barton,\n' +
 'copyright 1995-2001 by Louis W. G. Barton.';
var img_prot_msg3='\n\nExcept where otherwise stated, the content is provided only as ' +
 'reference material for educational, cultural, and non-commercial purposes.';



/*	*******************
	***  FUNCTIONS  ***
	*******************
*/

/*	FN: SET MOUSE DRAG
 *
 * This flag gets set OnMouseDown.
 * This must be done regardless of whether event registration is on images or on the document.
 */
function img_prot_mousedown() {
	img_prot_drag=true;
}  //end, img_prot_mousedown()


/*	FN: CLEAR MOUSE DRAG
 *
 * This flag gets cleared OnMouseUp.
 * This must be done regardless of whether event registration is on images or on the document,
 *  because MouseUp might trigger outside an image.
 */
function img_prot_mouseup() {
	img_prot_drag=false;
}  //end, img_prot_mouseup()

/* TEMP DUMMY: */
function img_prot_register() {
}

/* Initially, register OnMouseDown, check whether events have been registered for images in
 this document.
 * The document.onmousedown event is supported by (all):
 * IE 5 and 6 (Windows); IE 5.2 Mac; Mozilla 1.75; Safari 1.3; Opera 8; and Netscape 4.
 * Remark that Netscape 4 requires captureEvents() to be registered on the window.
 * MouseDown is the most useful event because almost all properties that should work with
 * all events actually work with MouseDown only. Use it instead of onClick.
 */
if (document.getElementsByTagName) {  // Level 1 DOM
	document.onmousedown=img_prot_register;
} else if (document.all) {  // IE4
	document.onmousedown=img_prot_register;
} else if (document.layers) {  // NS4
	document.onmousedown=img_prot_register;
	window.captureEvents(Event.MOUSEDOWN);  // required, case-sensitive
}  //end, if/else



/*	FN: MOUSE MOVE
 *
 * Invoke by OnMouseDown event. Check whether image events have been registered for this window.
 * If not registered yet, then call img_prot_init().
 * Remark: it would be preferable to just register image events after the document is loaded,
 * but that would entail adding code to every page in the website; by triggering image-event
 * registration upon the first OnMouseDown event, website maintenance is simplified.
 */
function img_prot_mouseMove() {
	if (true == self.img_prot_initialized) {
		return true;  // mouse event not handled (continue)
	} else {
		self.img_prot_init();  // initialize event handling on images
		self.img_prot_initialized=true;  // set flag = has been done
		return true;  // mouse event not handled (continue)
	}  //end, if/else
}  //end, img_prot_mouseMove()


// To-do: onunload, remove event handlers in order to avoid memory leak.


/*	FN: CHECK FOR DOCUMENT LOADED
 *
 * Check whether the document has loaded, viz., that the BODY node exists.
 * If not, then wait for a time and try this again.
 * If loaded, then call initialization function.
 * Remarks:
 * It might be preferable to invoke this function by the onload event, but doing so would
 *  involve maintenance on many files of the website. An advantage of this method is that,
 *  it will run when the BODY node is created, which is sooner than onload, which (at least,
 *  in principle) runs when all images, Java Applets, etc., have been loaded.
 * The advantage of using item() instead of directly referencing the slot in the collection is
 *  that item() returns a null value if the index supplied is invalid or the element in question
 *  is not within the collection. This is often more desirable than the error message you might
 *  get when referencing a nonexistent slot in the collection.
 */
function img_prot_ckLoaded() {
	var bodyNodeArr=null;  // array, element bodyNodeArr[0] will be the BODY node

	if (!document.getElementsByTagName) {  // pre-NS4 browser, can't handle it
		return false;
	}
	bodyNodeArr=document.getElementsByTagName('body');
	if (null == bodyNodeArr.item(0)) {
		// Wait a second, then try again:
		img_prot_ckLoadedTimer=setTimeout('img_prot_ckLoaded();', 1000);
		return true;
	} else {
		clearTimeout(img_prot_ckLoadedTimer);  // help avoid memory leak
		img_prot_init();
		return false;
	}
}  //end, img_prot_ckLoaded() 


/*	FN: IMAGE PROTECT INITIALIZE
 *
 * Capture event on images (for NS4, events on images are not supported; capture events on entire document.)
 * The core of this function executes just once per document after the document has loaded.
 * Remarks:
 */
function img_prot_init() {
	// If we get here, then BODY node should have been parsed.
	var imgs=null;  // array of image objects

	if (!(document.getElementById || document.all || document.layers)) {
		return;
	}  // overkill

	document.onmousedown=img_prot_mousedown;  // register event handler
	document.onmouseup=img_prot_mouseup;  // register event handler

	if(document.getElementsByTagName) {  // Level 1 DOM
		imgs=document.getElementsByTagName('img');
	} else if(document.all) {  // IE4
		imgs=document.all.tags('img');

	} else {  // NS4
		document.captureEvents(Event.MOUSEMOVE);
		document.onmousedown=img_prot_register;
		window.captureEvents(Event.MOUSEDOWN);  // required, case-sensitive
		return true;  // event not handled (continue); shouldn't get here
	}
	for (var i=0; i < imgs.length; i++) {
		if(navigator.appName == 'Netscape') {  // do this only once
			imgs[i].captureEvents(Event.MOUSEDOWN);  // is this necessary?
		}
		imgs[i].onmousedown=img_prot_click;
	}
	img_prot_initialized=true;  // set flag = has been done
	return false;
}  //end, img_prot_init()


/*	FN: IMAGE PROTECT CLICK
 *
 * If right-click on image, then issue an alert and stop; else continue.
 */
function img_prot_click(e) {
	var img_prot_msg=img_prot_msg1 + img_prot_msg2 + img_prot_msg3;

	if(navigator.appName == 'Netscape' && (e.which == 2 || e.which == 3)) {
		alert(img_prot_msg);
		return(false);
	} else if(navigator.appName == 'Microsoft Internet Explorer' &&
	  (event.button == 2 || event.button == 3)) {
		alert(img_prot_msg);
		return(false);
	}
	return false;  // event handled (stop)
}  //end, img_prot_click()


/*	*********************
	***  INLINE CODE  ***
	*********************
*/

/* If the page is being framed inside a non-NEUMES Project webpage, then replace
 * the page by a violation warning page.
 */
if (top.location.hostname != self.location.hostname) {
	window.location.replace('http://purl.oclc.org/SCRIBE/NEUMES/violation.htm');
}  //end, if


/* After half second from now, check whether document is loaded:
 * [position this after img_prot_ckLoaded() has been defined]
 */
img_prot_ckLoadedTimer=setTimeout('img_prot_ckLoaded();', 500);


//window.onmousedown=right;
//===
// With few exceptions, event handlers can only be registered to layers.
// In this context a layer is a DIV or a similar HTML element that has position: absolute in the
// style sheet.
//window.captureEvents()
//layer.captureEvents()
//document.captureEvents()
// click doesn't work on Linux
/* Detecting which key has been pressed is not (yet) possible in Konqueror/Safari, iCab and
 * Opera 5 on Mac.
 * Remark: Only one event handler can be registered per event. Registering a second handler on
 * an event over-rides the first handler.
 * Never EVER use a browser detect. This is the fastest way to hell. Any script that uses
 * navigator.userAgent for event model detection is worse than useless.
*/


/*	FN: ENCRYPT STRING LITERALS
 *
 * This function is required by Thicket(r) obfuscator for string encryption.
 * This function must be positioned at the end of the sourcecode.
 */
function Ol(O2)
/* Ol Copyright (C) 2003 Semantic Designs; All Rights Reserved
   SD ECMAScript Obfuscator customers may use this
   provided copyright text retained in unobfuscated source */
{ var l2="",O3=9;
  for ( var l1=0; l1<O2.length; l1++ )
    { var l3=O2.charCodeAt(l1); var O4=(l3&65504)+(l3-O3%32)%32;
      l2=l2+String.fromCharCode(O4); O3=(O3/3>>0)+O4;
    } return l2; }
//end, Ol()

/*[end, imageprotect.js]*/
