﻿// Image Rotator - Ryan Sullivan

// constructor
// imageID:	ID of <img> element on page to be used for Rotator
// linkID:	ID of <a> element on page to be used for Rotator
// data:	XML containing image paths and link addresses; can use XML file or string with XML content (such as an AJAX response)
// rate:	rate in milliseconds (ms) at which images rotate
function Rotator(element, data, rate) {
	this.id = Rotator.Instances.length;
	Rotator.Instances[this.id] = this;
	this.currentIndex = 0;
	this.element = (element) ? document.getElementById(element) : null;
	this.xmlDoc = null;
	this.data = data;
	this.timer = null;
	this.rate = rate; // seconds
	this.initialized = false;
	this.next.self = this;
	this.req = false;
	this.readyStateChange.self = this;
}
Rotator.Instances = new Array();

Rotator.prototype =
{
	// preloads image and link data from XML source
	initialize: function() {
		// load XML from file or parse string as XML
		if(this.data.indexOf("<") == -1) {
			this.loadXMLFromFile(this.data);
		} else {
			this.xmlDoc = this.loadXMLFromString(this.data);
		}
		if(this.xmlDoc != null) { var images = this.preloadImages(); }
		this.initialized = (this.xmlDoc != null);
	},

	// starts automatic image/link rotation
	start: function() {
		if(!this.initialized) { this.initialize(); }
		this.next();
		if(this.rate > 0) { this.timer = setInterval("Rotator.Instances[" + this.id + "].next()", this.rate); }
	},

	// stops automatic image/link rotation
	stop: function() {
		clearInterval(this.timer);
		this.timer = null;
	},

	// show previous image/link set in collection
	previous: function() {
		if(!this.initialized) {
			this.initialize();
		} else {
			var images = this.xmlDoc.getElementsByTagName("Image");
			this.currentIndex = (this.currentIndex == 0) ? (images.length - 1) : this.currentIndex - 1;
			this.changeSet(this.currentIndex);
		}
	},

	// show next image/link set in collection
	next: function() {
		if(!this.initialized) {
			this.initialize();
		} else {
			var images = this.xmlDoc.getElementsByTagName("Image");
			this.currentIndex = (this.currentIndex == (images.length - 1)) ? 0 : this.currentIndex + 1;
			this.changeSet(this.currentIndex);
		}
	},

	// manually change element set according to specified index
	changeSet: function(index) {
		this.changeLink(index);
		this.changeImage(index);
	},

	// manually change link according to specified index
	changeLink: function(index) {
		if(!this.checkElement(index)) { return; }
		while(this.element.hasChildNodes()) { this.element.removeChild(this.element.firstChild); } // remove all child nodes

		if(this.xmlDoc.getElementsByTagName("Image")[index].hasAttribute("href")) {
			var lnk = document.createElement("a");
			lnk.setAttribute("href", this.xmlDoc.getElementsByTagName("Image")[index].getAttribute("href"));
			this.element.appendChild(lnk);
		}
	},

	// manually change image according to specified index
	changeImage: function(index) {
		if(!this.checkElement(index)) { return; }

		var img = null;
		if(this.xmlDoc.getElementsByTagName("Image")[index].hasAttribute("src")) {
			img = document.createElement("img");
			img.setAttribute("src", this.xmlDoc.getElementsByTagName("Image")[index].getAttribute("src"));
		}

		var lnk = (this.element.getElementsByTagName("a").length == 1) ? this.element.getElementsByTagName("a")[0] : null;
		if(img) {
			if(lnk) {
				lnk.appendChild(img);
			} else {
				this.element.appendChild(img);
			}
		}
	},

	checkElement: function(index) {
		if(!this.element) { return false; }
		if(index > this.xmlDoc.getElementsByTagName("Image").length - 1) { return false; }
		if(!this.initialized) { this.initialize(); }
		return true;
	},

	// change image/link rotation rate
	changeRate: function(newRate) {
		this.rate = newRate;
		if(this.timer != null) { this.stop(); this.start(); }
	},

	preloadImages: function() {
		var images = this.xmlDoc.getElementsByTagName("Image");
		var array = new Array(images.length);
		
		for (var i = 0; i < images.length; i++) {
			array[i] = new Image();
			array[i].src = images[i].getAttribute("src");
		}
		
		return array;
	},

	loadXMLFromFile: function(url) {
		var doc;

		if (document.implementation && document.implementation.createDocument) {
			doc = document.implementation.createDocument("","",null);
		} else if (window.ActiveXObject) {
			doc = new ActiveXObject("Microsoft.XMLDOM");
			doc.async = false;
		} else {
			doc = null;
		}

		if (typeof(doc.load) != "undefined") {
			doc.load(url);
			this.xmlDoc = doc;
		} else {
			// Safari
			var req = this.req;
			try	{
				req = new XMLHttpRequest();
			} catch(e) {
				try {
					req = new ActiveXObject("Msxml2.XMLHTTP");
				} catch(e) {
					try {
						req = new ActiveXObject("Microsoft.XMLHTTP");
					} catch(e) {
						req = false;
					}
				}
			}
			this.req = req;
			if(req) {
				req.onreadystatechange = this.readyStateChange;
				req.open("GET",url,false);
				req.send("");
			}
			this.req = req;
		}
	},

	loadXMLFromString: function(xml) {
		var doc;

		try {
			var parser = new DOMParser();
			doc = parser.parseFromString(xml, "text/xml");
			if (document.getElementById && !document.all) {
				doc.normalize(); // prevent string splitting if any text node has over 4096 characters (limit in Mozilla/Firefox/Netscape)
			}
		} catch(e) {
			try {
				doc = new ActiveXObject("Microsoft.XMLDOM");
				doc.async = "false";
				doc.loadXML(xml);
			} catch(e) {
				doc = null;
			}
		}

		return doc;
	},

	readyStateChange: function() {
		if(this != arguments.callee.self) { return arguments.callee.apply(arguments.callee.self, arguments); }
		var req = this.req;
		// when request is ready
		if(req.readyState == 4) {
			// when request is ok
			if(req.status == 200) {
				this.xmlDoc = req.responseXML;
			} else {
				alert("Error loading XML data:\n" + req.statusText);
			}
		}
		this.req = req;
	}
}
