// Element.Rotate - Will rotate content for you
// Written By: Eric Anderson <eric@afaik.us>
// License: Public Domain, do what you want

Element.Rotate = Class.create();
Object.extend(Element.Rotate, {
	RANDOM:   '_random',
	SEQUENCE: '_sequence'
});
Object.extend(Element.Rotate.prototype, {
	initialize: function(element, elements, options) {
		this.element = $(element);
		this._initElement();
		default_duration = 2;
		this.options = {
			frequency:         5,
			transitionType:    Element.Rotate.RANDOM,
			hideEffect:        Effect.Fade,
			hideEffectOptions: {duration: default_duration},
			showEffect:        Effect.Appear,
			showEffectOptions: {duration: default_duration}
		};
		Object.extend(this.options, options);
		userFinish = this.options.hideEffectOptions.afterFinish ||
			function(){};
		this.options.hideEffectOptions.afterFinish = function(effect) {
			userFinish();
			Element.remove(effect.element);
		};
		if( !elements ) {
			alert('No elements specified');
			return;
		} else {
			this.elements = elements;
		}
		this.elements = this.elements.collect(this._imageize.bind(this));
		this.elements.each(function(e) {e.preloadImgs()});
		this.start();
	},
	start: function() {
		this.timer = setInterval(this.transition.bind(this),
			this.options.frequency * 1000);
	},
	stop: function() {
		if( this.timer )
			clearInterval(this.timer);
		this.timer = null;
	},
	transition: function() {
		// Cover work area with clone
		clone = this.element.firstChild.cloneNode(true);
		this.element.appendChild(clone);
		Position.absolutize(clone);
		Position.clone(this.element.firstChild, clone);

		// Put next element in DOM tree
		nextElementMethod = this.options.transitionType+'NextElement';
		this.current = this[nextElementMethod](this.current);
		Element.remove(this.element.firstChild);
		new Insertion.Top(this.element, this.current);

		// Init effects to transition one to the other
		this.options.showEffect(this.element.firstChild, this.options.showEffectOptions);
		this.options.hideEffect(clone, this.options.hideEffectOptions);
	},
	_initElement: function() {
		e = this.element;
		this.element = document.createElement('div');
		e.parentNode.insertBefore(this.element, e);
		this.element.appendChild(e);
	},
	_each: function(iterator) {
		this.elements._each(iterator);
	},
	_randomNextElement: function(current) {
		while((next = this.elements.random()) == current) {}
		return next;
	},
	_sequenceNextElement: function(current) {
		current = this.elements.indexOf(current);
		if( current == -1 )
			return this.elements[0];
		return current == this.elements.length ? 0 : (current+1);
	},
	_imageize: function(content) {
		if( content.match(/\.(?:jpg|png|gif)$/) ) {
			return '<img src="'+content+'" />';
		}
		return content;
	}
});
Object.extend(Element.Rotate.prototype, Enumerable);

Array.prototype.random = function() {
	return this[Math.ceil(Math.random()*this.length)-1];
}

Prototype.ImageTag = '<img.*?src\=\"([^\"]+)\"[^\>]*\>';
Object.extend(String, {
	_imgCache: [],
	unloadImgCache: function() {
		String._imgCache.clear();
	}
});
Object.extend(String.prototype, {
	// Mostely borrowed from Prototype.js String.prototype.extractScripts
	extractImgs: function() {
		var matchAll = new RegExp(Prototype.ImageTag, 'img');
		var matchOne = new RegExp(Prototype.ImageTag, 'im');
		return (this.match(matchAll) || []).map(function(imageTag) {
			return (imageTag.match(matchOne) || ['', ''])[1];
		});
	},
	preloadImgs: function() {
		this.extractImgs().each(function(src) {
			img = new Image();
			img.src = src;
			String._imgCache.push(img);
		});
	}
});

Event.observe(window, 'unload', String.unloadImgCache, false);

