
// Written by Dmitry Zhemchuzhin

// ***************** Globals ************************************************

var isIE = (navigator.appVersion.indexOf("MSIE")!=-1);

//--------------------------------------------------------------------------
// -- separates text in the object into letters and returns a collection of them
// as <SPAN> elements

function separateLetters(obj) {
	if (isIE)
	{
		var text = obj.innerText;
		var newHTML = "";
		for (var i=0; i<text.length; i++) {
			newHTML += "<span>" + text.charAt(i) + "</span>";
		}
		obj.innerHTML = newHTML;
		return obj.children;
	}
	else
	{
		var text_node = obj.removeChild(obj.firstChild);
		var text = text_node.data;
		var node;
		var result = new Array();
		for (var i=0; i<text.length; i++) {
			node = document.createElement("SPAN");
			node.appendChild(document.createTextNode(text.charAt(i)));
			obj.appendChild(node);
			result[i] = node;
		}
		return result;
	}
}

function GetElementById(element_id)
{
	if (isIE) return document.all.item(element_id,0);
	else return document.getElementById(element_id);
}

// ************ Object Animation *******************************************

var Animation_Objects = new Array();	// this is a collection of all Animation objects

//--------------------------------------------------------------------------
// -- object constructor

// !!! IMPORTANT !!! This constructor should never be called directly, use
//                   Animation_createNewObject(obj) instead.

// constructor accepts an object which should be animated;
// this object MUST have the following interface:
// *** Property ".len" retrieves the number of elements in the object
// *** Method ".setFrameElement(index, value)" should use "value" to modify in some way the
//     element[index] of the object. The object should expect the "value" to be between 0 and 1.

function Animation(obj) {
	
	// fields
	this.frequency = 10;	// frames per second
	this.wave_length = 10;	// number of elements affected simultaneously
	this.speed = 15;	// speed of movement (elements per second)
	this.loop = true;	// repeat the animation sequence if true
	this.delay = 5;		// delay before the sequence is repeated;

	this.obj = obj;		// stores an object to be animated
	this.glob_index;	// stores index in a global collection
	this.phase = 0;		// parameter
	this.intervalID;	// stores ID for cancelling interval
	this.running = false;	// indicates if this object is running

	// methods
	this.globalEquiv = Animation_globalEquiv;
	this.start = Animation_start;
	this.stop = Animation_stop;
	this.frame = Animation_frame;
	this.arg = Animation_arg;
	this.wave = Animation_wave;
}

//--------------------------------------------------------------------------
// -- object "pseudo-constructor". Should be used instead of a normal constructor.
// This is a global function and not a member of Animation object.

function Animation_createNewObject(obj) {
	var i = Animation_Objects.length;
	var newAnim = new Animation(obj);
	newAnim.glob_index = i;
	Animation_Objects[i] = newAnim;
	return newAnim;
}

//--------------------------------------------------------------------------
// -- returns a name of the global equivalent of this object

function Animation_globalEquiv() {
	return "Animation_Objects[" + this.glob_index + "]";
}

//--------------------------------------------------------------------------
// -- starts animation; initial delay in seconds can be supplied

function Animation_start(initial_delay) {
	if (this.running) { 
		alert("Error: This Animation object is already running!");
		return;
	}
	if (typeof(initial_delay) != "undefined" && initial_delay>0) {
		window.setTimeout(this.globalEquiv() + ".start()", initial_delay*1000);
		return;
	}
	this.running = true;
	this.phase = 0;
	this.intervalID = window.setInterval(this.globalEquiv() + ".frame()", 1000/this.frequency);
}

//--------------------------------------------------------------------------
// -- stops animation

function Animation_stop() {
	if (!this.running) {
		alert("Error: This Animation object is not running!");
		return;
	}
	window.clearInterval(this.intervalID);
	this.running = false;
	if (this.loop) window.setTimeout(this.globalEquiv() + ".start()", this.delay*1000);
}

//--------------------------------------------------------------------------
// -- draws current frame based on parameter value, 
//    icrements parameter and checks if it's time to stop the sequence

function Animation_frame() {
	var t = this.phase/this.frequency;
	var index_start = Math.max(0, Math.round ( t * this.speed - this.wave_length - .5 ));
	var index_end = Math.min(this.obj.len, Math.round ( t * this.speed - .5 ));
	for (var index=index_start; index<index_end; index++) {
		var value = this.wave(this.arg(index));
		this.obj.setFrameElement(index, value);
	}
	if (this.phase/this.frequency > (this.obj.len+this.wave_length-1)/this.speed) this.stop();
	this.phase++;
}

//--------------------------------------------------------------------------
// -- calculates an argument

function Animation_arg(index) {
	return this.phase/this.frequency - index/this.speed;
}

//--------------------------------------------------------------------------
// -- calculates a "wave" function

function Animation_wave(t) {
	var wl = this.wave_length/(this.speed);	// this is a wave length expressed in seconds
	if (t<0 || t>=wl) return 0;
	else {
//		return 4*t*(1-t/wl)/wl;		// this is parabola function implementation

		if (t<wl/2) return 2*t/wl;	// this is line function implementation
		else return 2*(1-t/wl);
	}
}

// ************ Object ColorChanger ****************************************

// This object supports animation interface as specified in object Animation

//--------------------------------------------------------------------------
// -- object constructor

function ColorChanger(elements) {

	// fields
	this.color0 = 0x0;		// default color0 is black
	this.color1 = 0xFFFF00;		// default color1 is yellow
	this.elements = elements;
	this.len = elements.length;	// property len is a part of animation interface

	// methods
	this.setColor = ColorChanger_setColor;
	this.setBgColor = ColorChanger_setBgColor;
	this.setFrameElement = ColorChanger_setFrameElement;	// this method is a part of animation interface
}

//--------------------------------------------------------------------------
// -- sets the foreground color of the specified element

function ColorChanger_setColor(index, r, g, b) {
	this.elements[index].style.color = "rgb("+ r + "," + g + "," + b + ")";
}

//--------------------------------------------------------------------------
// -- sets the background color of the specified element

function ColorChanger_setBgColor(index, r, g, b) {
	this.elements[index].style.backgroundColor = "rgb("+ r + "," + g + "," + b + ")";
}

//--------------------------------------------------------------------------
// -- this method is a part of animation interface,
// it accepts element's index and a value between 0 and 1 and sets the element's color

function ColorChanger_setFrameElement(index, value) {
	var color0 = this.color0; var color1 = this.color1;
	var color0_red_channel = (color0 & 0xFF0000)/0x10000;
	var color1_red_channel = (color1 & 0xFF0000)/0x10000;
	var red_channel = color0_red_channel + Math.round ((color1_red_channel - color0_red_channel ) * value - .5);
	var color0_green_channel = (color0 & 0xFF00)/0x100;
	var color1_green_channel = (color1 & 0xFF00)/0x100;
	var green_channel = color0_green_channel + Math.round ((color1_green_channel - color0_green_channel ) * value - .5);
	var color0_blue_channel = color0 & 0xFF;
	var color1_blue_channel = color1 & 0xFF;
	var blue_channel = color0_blue_channel + Math.round ((color1_blue_channel - color0_blue_channel ) * value - .5);
	//var rgb = red_channel * 0x10000 + green_channel * 0x100 + blue_channel;
	this.setColor(index, red_channel, green_channel, blue_channel);
}

//**************************************************************************

