/** @type{boolean} */
var use_opacity = !glow.env.ie || glow.env.ie >= 9;

/**
 * @constructor
 */

/** @const */ var activate_radius_squared = 19600; // 140^2

function stone_track(stone) {
	var s = new StoneTrack(stone);
	glow.events.addListener(stone, 'mousemove', s.mousemove, s);
	glow.events.addListener(stone, 'mouseleave', s.mouseleave, s);
}

function StoneTrack(stone) {
	this.over = false;
	this.radius_squared = activate_radius_squared;
	this.stone = stone;
}

StoneTrack.prototype.mousemove = function(e) {
	var ne = e.nativeEvent;

	var undefined;
	e.offsetX = ne.offsetX !== undefined ? ne.offsetX : ne.layerX;
	e.offsetY = ne.offsetY !== undefined ? ne.offsetY : ne.layerY;
	
	var dst_x = e.offsetX - this.stone.offsetWidth / 2;
	var dst_y = e.offsetY - this.stone.offsetHeight / 2;
	var distance_squared = dst_x * dst_x + dst_y * dst_y;
	
	var over = distance_squared < this.radius_squared;
	if(over || this.over) {
		this.over = over;
		glow.events.fire(e.attachedTo, over ? "stone_mousemove" : "stone_mouseleave", e);
	}
}

StoneTrack.prototype.mouseleave = function(e) {
	if(this.over) {
		this.over = false;
		glow.events.fire(e.attachedTo, "stone_mouseleave", e);
	}
}

/**
 * @constructor
 */

var AnimationQueue = function() {
	this.queue_ = [];
	
	/** @type {?number} */
	this.interval_ = null;
}

AnimationQueue.prototype.add = function(animation) {
	this.queue_.push(animation);
	if(!this.interval_) {
		var self = this;
		this.interval_ = window.setInterval(function() { self.processQueue() }, 20);
	}
}

AnimationQueue.prototype.processQueue = function() {
	var time = new Date();
	for(var i = 0; i < this.queue_.length; ++i) {
		this.queue_[i].go(time);
	}
	for(var i = 0, j = 0; i < this.queue_.length; ++i) {
		if(this.queue_[i] && this.queue_[i].running())
			this.queue_[j++] = this.queue_[i];
		else
			this.queue_[i].cleanUp();
	}
	this.queue_.length = j;
	
	if(j == 0) {
		window.clearInterval(this.interval_);
		this.interval_ = null;
	}
}

var animation_queue = new AnimationQueue();

function pageWidth() {
	var width = window.innerWidth != null? window.innerWidth :
		document.documentElement && document.documentElement.clientWidth ?
		document.documentElement.clientWidth : document.body != null ?
		document.body.clientWidth : 0;

	content = glow.dom.get(".content");	
	return Math.max(width, content.length ? content.position().left + content.width() : 0);
}

function scrolling_text(selector, src, width) {
	var scrolling_text = glow.dom.get(selector);
	scrolling_text.children().destroy();
	scrolling_text.each(
		function() {
			var el = this;
			setTimeout(function() {
				el.style.backgroundImage = 'url(' + src + ')';
				el.style.backgroundRepeat = 'repeat-x';
				el.style.top = parseInt(el.offsetTop, 10) + 90 + 'px';
				el.style.width = Math.max(600, pageWidth() - el.offsetLeft) + 'px';
				el.style.height = '100px';
		
				animation_queue.add(
					new HorizontalScroller(el, width, width * 5, 0)
				);
			}, 0);
		}
	);
}

/**
 * @constructor
 */

var HorizontalScroller = function(element, width, duration, y) {
	this.element = element;
	this.width = width;
	this.duration = duration;
	this.start = new Date();
	this.y = y;
}

HorizontalScroller.prototype.go = function(time) {
	var pos = (this.start - time) / this.duration % 1;
	this.element.style.backgroundPosition =
		(pos * this.width >> 0) +'px ' + this.y + 'px';
};

HorizontalScroller.prototype.running = function() {
	return true;
};

HorizontalScroller.prototype.cleanUp = function() {
};


/* Hover */

/** @const */ var max_radius = 250;
/** @const */ var accel = 2 / 1000;

function hover_image(selector, img_file)
{
	if(!use_opacity) img_file = img_file.replace(/\.png$/, '.gif');

	glow.dom.get(selector).each(
		function() {
			new HoverImage(this, img_file);
		}
	);
}

/** @constructor */
function HoverImage(element, file) {
	this.stone = element;

	var self = this;
	var image = new Image();
	image.onload = function() { self.image_loaded(); }
	image.src = file;
	this.image = image;
}

HoverImage.prototype.image_loaded = function() {
	delete this.image.onload;
	this.image = glow.dom.get(this.image).css('position', 'absolute').hide().appendTo(document.body);
	stone_track(this.stone);
	glow.events.addListener(this.stone, 'stone_mousemove', this.start_popup, this );
}

HoverImage.prototype.start_popup = function() {
	if(this.tracking || this.running_) return;

	var stone_pos = glow.dom.get(this.stone).offset();

	this.last = new Date();
	this.pos_x = stone_pos.left + this.stone.offsetWidth / 2;
	this.pos_y = stone_pos.top + this.stone.offsetHeight / 2;
	this.speed_x = 0;
	this.speed_y = 0;
	this.opacity = 0;
	// Save original position, in order to fade out as it moves away.
	// Should possibly recalculate if the page reflows?
	this.target_stone_x = this.pos_x;
	this.target_stone_y = this.pos_y;

	this.move_popup();
	this.image.show();

	animation_queue.add(this);
	this.tracking = glow.events.addListener(
		document, 'mousemove', this.mousemove, this);
	this.running_ = true;
}

HoverImage.prototype.running = function() {
	return this.running_;
}

HoverImage.prototype.cleanUp = function() {
	glow.events.removeListener(this.tracking);
	this.image.hide();
	delete this.tracking;
}

HoverImage.prototype.mousemove = function(e) {
	this.dst_x = e.pageX;
	this.dst_y = e.pageY;
}

HoverImage.prototype.go = function(new_time) {
	var time = new_time - this.last;
	
	var new_x = this.move(this.pos_x, this.speed_x, this.dst_x, time, accel);
	var new_y = this.move(this.pos_y, this.speed_y, this.dst_y, time, accel);
	this.pos_x = new_x.pos;
	this.speed_x = new_x.speed;
	this.pos_y = new_y.pos;
	this.speed_y = new_y.speed;
	
	var dst_x = this.pos_x - this.target_stone_x;
	var dst_y = this.pos_y - this.target_stone_y;
	var change_opacity = (1 - (dst_x * dst_x + dst_y * dst_y) / (max_radius * max_radius)) - this.opacity;
	this.opacity += change_opacity < 0 ? Math.max(change_opacity, -time * 0.01) : Math.min(change_opacity, + time * 0.01);
	
	if(this.opacity <= 0.01) {
		this.running_ = false;
	}
	else
		this.move_popup();

	this.last = new_time;
}

HoverImage.prototype.move_popup = function(centre) {
	this.image
		.css('left', (this.pos_x - 100) + 'px')
		.css('top', (this.pos_y - 100) + 'px')
		.css('opacity', this.opacity);
}

HoverImage.prototype.move = function(pos, speed, dst, time, accel) {
	var distance = dst - pos;
	if(!distance && !speed) return { pos: dst, speed: 0 };
	
	var negative = distance < 0;
	if(negative) {
		speed = -speed;
		distance = -distance;
	}
	
	// Accelerate
	
	if(speed < 0 || distance > speed * speed / 2 / accel) {
		var max_speed = Math.sqrt(accel * distance + speed * speed / 2);
		var new_speed = speed + time * accel;
		if(new_speed <= max_speed) {
			return {
				pos: negative ?
					pos - (speed + new_speed) / 2 * time :
					pos + (speed + new_speed) / 2 * time,
				speed: negative ? -new_speed : new_speed
			};
		}
		else {
			var t1 = (max_speed - speed) / accel;
			pos = negative ?
				pos - (max_speed + speed) / 2 * t1 :
				pos + (max_speed + speed) / 2 * t1;
			speed = max_speed;
			time -= t1;
		}
	}
	
	// Decelerate
	
	var new_speed = speed - time * accel;
	if(new_speed >= 0) {
		return {
			pos: negative ?
				pos - (speed + new_speed) / 2 * time :
				pos + (speed + new_speed) / 2 * time,
			speed: negative ? -new_speed : new_speed
		};
	}
	else {
		var t1 = speed / accel;
		return {
			pos: negative ?
				pos - speed / 2 * t1 :
				pos + speed / 2 * t1,
			speed: 0
		};
		//time -= t1;
	}
};

/* Bubbles */

/** @const */ var max_bubbles = 10;
/** @const */ var initial_speed = 30;
/** @const */ var upward_acceleration = -75;
/** @const */ var duration = 2000;
/** @const */ var full_size = 50;
/** @const */ var pause = duration / max_bubbles;

function hover_bubbles(selector, img_file)
{
	//if(!use_opacity) img_file = img_file.replace(/\.png$/, '.gif');

	glow.dom.get(selector).each(
		function() {
			new HoverBubbles(this, img_file);
		}
	);
}

/** @constructor */
function HoverBubbles(element, file) {
	this.stone = element;
	
	var self = this;
	var image = new Image();
	image.onload = function() { self.image_loaded(); }
	image.src = file;
	this.image = image;
}

HoverBubbles.prototype.image_loaded = function() {
	this.bubbles_object = null;
	stone_track(this.stone);
	glow.events.addListener(this.stone, 'stone_mousemove', this.start_bubbles, this );
}

HoverBubbles.prototype.start_bubbles = function(e) {
	if(!this.bubbles_object) {
		this.bubbles_object = new BubbleAnimation(this);
		animation_queue.add(this.bubbles_object);
	}
	
	this.bubbles_object.mousemove(this.image.src, e.pageX, e.pageY);
}

HoverBubbles.prototype.bubbleAnimationFinished = function() {
	this.bubbles_object = null;
}

/**
 * @constructor
 */

function BubbleAnimation(parent) {
	this.parent = parent;
	this.bubbles = [];
	this.last_bubble = 0;
	this.bubbles_last = new Date();
}

BubbleAnimation.prototype.mousemove = function(image_src, x, y) {
	if(this.bubbles.length >= max_bubbles) {
		return;
	}

	var time = new Date();

	if(time - this.last_bubble > pause) {
		var dir = Math.random() * 2 * Math.PI;

		var element =
			glow.dom.create('<img src="'+image_src+'" class="bubble">')						
				.css('left', x +'px')
				.css('top', y +'px')
				.css('width', 0)
				.css('height', 0)
				.css('position', 'absolute')
				.appendTo(document.body);

		this.bubbles.push({
			element : element,
			x : x,
			y : y,
			dx : initial_speed * Math.sin(dir),
			dy : initial_speed * Math.cos(dir),
			start : time
		});
		this.last_bubble = time;
	}
}

BubbleAnimation.prototype.go = function(time) {
	var interval = time - this.bubbles_last;
	this.bubbles_last = time;

	var del = 0, i = 0, bubble;
	while(bubble = this.bubbles[i++]) {
		var pos = (time - bubble.start) / duration;
		var size = full_size * pos * (2 - pos);
		if(pos >= 1) {
			bubble.element.destroy();
			del = i;
		}
		else {
			bubble.x += bubble.dx * interval / 1000;
			bubble.y += bubble.dy * interval / 1000;
			bubble.dx = bubble.dx * 0.99;
			bubble.dy = bubble.dy + upward_acceleration * interval / 1000;
			bubble.element
				.css('left', (bubble.x - size / 2 + Math.sin(pos * 15) * 6) + 'px')
				.css('top', (bubble.y - size / 2) + 'px')
				.css('width', size + 'px')
				.css('height', size + 'px');
			if(use_opacity) bubble.element.css('opacity', pos < 0.75 ? 1 : (1 - pos) * 4);
		}
	}
	
	this.bubbles.splice(0, del);
};

BubbleAnimation.prototype.running = function() {
	return this.bubbles.length != 0;
};

BubbleAnimation.prototype.cleanUp = function() {
	this.parent.bubbleAnimationFinished();
};

/* Numbers */

/** @const */ var number_max = 10;
/** @const */ var number_initial_speed = 50;
/** @const */ var number_duration = 2000;
/** @const */ var number_pause = number_duration / number_max;
/** @const */ var number_sizes = [ "20px", "20px", "50px", "50px", "100px"];

function hover_numbers(selector)
{
	glow.dom.get(selector).each(
		function() {
			new HoverNumbers(this);
		}
	);
}

/** @constructor */
function HoverNumbers(element) {
	this.stone = element;
	this.numbers_object = null;
	stone_track(element);
	glow.events.addListener(element, 'stone_mousemove', this.start_numbers, this);
}

HoverNumbers.prototype.start_numbers = function(e) {
	if(!this.numbers_object) {
		this.numbers_object = new NumberAnimation(this);
		animation_queue.add(this.numbers_object);
	}
	
	this.numbers_object.mousemove(this.stone, e.offsetX, e.offsetY);
}

HoverNumbers.prototype.numbersAnimationFinished = function() {
	this.numbers_object = null;
}

/**
 * @constructor
 */

function NumberAnimation(parent) {
	this.parent = parent;
	this.numbers = [];
	this.last_number = 0;
	this.numbers_last = new Date();
}

NumberAnimation.prototype.mousemove = function(stone, x, y) {
	if(this.numbers.length >= number_max) {
		return;
	}

	var time = new Date();

	if(time - this.last_number > number_pause) {
		var dir = Math.random() * 2 * Math.PI;
		var number = (Math.random() * 10) >> 0;
		var element = 
			glow.dom.create('<div class="number">' +number + '</div>')
				.css('position', 'absolute')
				.css('visibility', 'hidden')
				.css('font-size', number_sizes[Math.floor(Math.random() * number_sizes.length)])
				.appendTo(stone);
		this.numbers.push({
			element : element,
			x : x,
			y : y,
			dx : number_initial_speed * Math.sin(dir),
			dy : number_initial_speed * Math.cos(dir),
			halfWidth: element[0].offsetWidth / 2,
			halfHeight: element[0].offsetHeight / 2,
			start : time
		});
		this.last_number = time;
	}
}

NumberAnimation.prototype.go = function(time) {
	var interval = time - this.numbers_last;
	this.numbers_last = time;

	var del = 0, i = 0, number;
	while(number = this.numbers[i++]) {
		var pos = (time - number.start) / number_duration;
		if(pos >= 1) {
			number.element.destroy();
			del = i;
		}
		else {
			number.x += number.dx * interval / 1000;
			number.y += number.dy * interval / 1000;
			number.dx = number.dx * 0.99;
			number.dy = number.dy * 0.99;
			number.element
				.css('left', (number.x - number.halfWidth) + 'px')
				.css('top', (number.y  - number.halfHeight) + 'px')
				.css('visibility', 'visible');
			if(use_opacity) number.element.css('opacity', pos < 0.75 ? 1 : (1 - pos) * 4);
		}
	}
	
	this.numbers.splice(0, del);
}

NumberAnimation.prototype.running = function() {
	return this.numbers.length != 0;
}

NumberAnimation.prototype.cleanUp = function() {
	this.parent.numbersAnimationFinished();
}

/* Words */

/** @const */ var max_words = 3;
/** @const */ var words_pause = 800;
/** @const */ var words_duration = 2000;

function hover_words(selector, image_files)
{
	glow.dom.get(selector).each(
		function() {
			new HoverWords(this, image_files);
		}
	);
}

/** @constructor */

function HoverWords(element, images) {
	this.stone = element;
	this.loaded = false;
	this.ready = [];
	this.displayed = [];
	this.used = [];
	this.last_word = 0;

	for(var i = 0; i < images.length; ++i) this.load(images[i]);
}

HoverWords.prototype.load = function(image_src) {
	var image_obj = new Image();
	var image = {};
	
	var self = this;
	image_obj.onload = function() { self.image_loaded(image); }
	image_obj.src = image_src;
	image.obj = glow.dom.get(image_obj);
}

HoverWords.prototype.image_loaded = function(image) {
	this.ready.push(image);
	image.width = image.obj[0].width;
	image.height = image.obj[0].height;
	image.obj
		.css('width', image.width / 2)
		.css('height', image.height / 2);

	if(!this.loaded) {
		stone_track(this.stone);
		glow.events.addListener(this.stone, 'stone_mousemove', this.detect_mousemove, this);
		glow.events.addListener(this.stone, 'stone_mouseleave', this.detect_mouseleave, this);
		this.loaded = true;
	}
}

HoverWords.prototype.detect_mousemove = function(e) {
	this.over_ = true;
	if(!this.running_) {
		this.running_ = true;
		animation_queue.add(this);
	}
}

HoverWords.prototype.detect_mouseleave = function(e) {
	this.over_ = false;
}


HoverWords.prototype.go = function(time) {
	if(this.over_ && this.displayed.length < max_words && this.ready.length && time - this.last_word > words_pause) {
	    var stone_x = this.stone.offsetWidth / 2;
	    var stone_y = this.stone.offsetHeight / 2;
		var pos_x = 140 + Math.random() * 20;
		var pos_y = -80 + Math.random() * 160;
		var dst_x = -160 + Math.random() * 20;
		var dst_y = pos_y - 10 + Math.random() * 20;
		if(Math.random() >= 0.5) { pos_x = -pos_x; dst_x = -dst_x; }
		
	    var ready_word = Math.floor(Math.random() * this.ready.length);
	    var image = this.ready[ready_word];

		var size = Math.random() * 0.4 + 0.1;
		var width = image.width * size;
		var height = image.height * size;

		image.start_x = stone_x + pos_x - width / 2;
		image.start_y = stone_y + pos_y - height / 2;
		image.dir_x = dst_x - pos_x;
		image.dir_y = dst_y - pos_y;
		image.time = time;

	    image.obj
	        .css('position', 'absolute')
	        .css('left', image.start_x + "px")
	        .css('top', image.start_y + "px")
	        .css('z-index', 1)
			.css('width', width + "px")
			.css('height', height + "px")
			.css('visibility', 'hidden')
	        .appendTo(this.stone);

	    this.ready.splice(ready_word, 1);
		this.displayed.unshift(image);
		this.last_word = time;
	}

	for(var i = 0, anim; anim = this.displayed[i]; ++i) {
        var pos = (time - anim.time) / words_duration;
        if(pos > 1) break;
        var x = anim.start_x + pos * anim.dir_x;
        var y = anim.start_y + pos * anim.dir_y;
        var opacity = pos * (1 - pos) * 4;
        if(use_opacity) anim.obj.css('opacity', opacity);
        anim.obj
            .css('left', x + "px")
            .css('top', y + "px")
            .css('visibility', 'visible');
    }
    
    var cut = i;
    for(;anim = this.displayed[i]; ++i) {
        anim.obj.remove();
        this.used.unshift(anim);
    }
    
    this.displayed.length = cut;

    if(this.used.length > 3) {
        for(var i = 3; i < this.used.length; ++i) {
            this.ready.push(this.used[i]);
        }
        this.used.length = 3;
    }
}

HoverWords.prototype.running = function() {
    return this.displayed.length > 0;
}

HoverWords.prototype.cleanUp = function() {
    this.running_ = false;
}

/* Lines */

/** @const */ var lines_max = 10;
/** @const */ var lines_pause = 400;
/** @const */ var lines_duration = 2000;

function hover_lines(selector)
{
	glow.dom.get(selector).each(
		function() {
			new HoverLines(this);
		}
	);
}

/** @constructor */

function HoverLines(element) {
	this.stone = element;
	this.displayed = [];
	this.last = 0;

	// Detect transform support:
	
	var attrs = ['transform', 'WebkitTransform', 'MozTransform', 'OTransform'];
	this.transform = null;
	for(var i = 0, attr; attr = attrs[i]; ++i) {
		if(typeof(element.style[attr]) != 'undefined') this.transform = attr;
	}

	stone_track(this.stone);
	glow.events.addListener(this.stone, 'stone_mousemove', this.detect_mousemove, this);
	glow.events.addListener(this.stone, 'stone_mouseleave', this.detect_mouseleave, this);
}

HoverLines.prototype.detect_mousemove = function(e) {
	this.over_ = true;
	if(!this.running_) {
		this.running_ = true;
		animation_queue.add(this);
	}
}

HoverLines.prototype.detect_mouseleave = function(e) {
	this.over_ = false;
}

HoverLines.prototype.go = function(time) {
	if(this.over_ && this.displayed.length < lines_max && time - this.last > lines_pause) {
    	var horizontal = this.transform ? true : Math.random() >= 0.5;
    	var dimension1 = horizontal ? this.stone.offsetWidth : this.stone.offsetHeight;
    	var dimension2 = horizontal ? this.stone.offsetHeight : this.stone.offsetWidth;

    	var r = Math.random() * 0.75 + 0.25;
    	var length = Math.floor(dimension1 * r);
    	var thickness = Math.ceil((1 - r) * 10);

		var line = {
			pos_attr: horizontal ? 'top' : 'left',
			pos_start: Math.random() * dimension2,
			pos_end: Math.random() * dimension2,
    		rotate_start: Math.random() * 40 - 20,
    		rotate_end: Math.random() * 40 - 20,
    		time: time
    	}
    	
    	line.obj = glow.dom.create('<div/>')
	        .css('position', 'absolute')
	        .css(horizontal ? 'left' : 'top', dimension1 * (Math.random() * (1-r)) + 'px')
	        .css(line.pos_attr, line.pos_start + 'px')
	        .css(horizontal ? 'width' : 'height', length + 'px')
	        .css(horizontal ? 'height' : 'width', thickness + 'px')
	        .css('background', '#fff')
	        .css('z-index', 100);
	        
		if(this.transform)
			line.obj
				.css(this.transform, 'rotate(' + line.rotate_start + 'deg)')
				.css(this.transform + 'OriginX', '50%')
				.css(this.transform + 'OriginY', '50%');
	        
	    line.obj.appendTo(this.stone);

		this.displayed.unshift(line);
		this.last = time;
	}

	for(var i = 0, anim; anim = this.displayed[i]; ++i) {
        var pos = (time - anim.time) / words_duration;
        if(pos > 1) break;
        var x = anim.pos_start + pos * (anim.pos_end - anim.pos_start);
        var rotate = anim.rotate_start + pos * (anim.rotate_end - anim.rotate_start);
        var opacity = pos * (1 - pos) * 4;
        anim.obj.css(anim.pos_attr, x + "px");

        if(use_opacity)
            anim.obj.css('opacity', opacity);

		if(this.transform)
			anim.obj.css(this.transform, 'rotate(' + rotate + 'deg)');
    }
    
    var cut = i;
    for(;anim = this.displayed[i]; ++i) {
        anim.obj.remove(); // TODO: remove or delete?
    }
    
    this.displayed.length = cut;
}

HoverLines.prototype.running = function() {
    return this.displayed.length > 0;
}

HoverLines.prototype.cleanUp = function() {
    this.running_ = false;
}

/* Characters */

/** @const */ var character_initial_speed = 100;
/** @const */ var character_duration = 2000;
/** @const */ var character_pause = 700;

function hover_characters(selector, images)
{
	glow.dom.get(selector).each(
		function() {
			new HoverCharacters(this, images);
		}
	);
}

/** @constructor */
function HoverCharacters(element, images) {
	this.stone = element;
	this.loaded = false;
	this.ready = [];
	this.characters = [];
	this.last_character = 0;
	
	for(var i = 0; i < images.length; ++i) this.load(images[i]);
}

HoverCharacters.prototype.load = function(image_src) {
	var image_obj = new Image();
	var image = {};
	
	var self = this;
	image_obj.onload = function() { self.image_loaded(image); }
	image_obj.src = image_src;
	image.obj = glow.dom.get(image_obj);
}

HoverCharacters.prototype.image_loaded = function(image) {
	this.ready.push(image);
	image.width = image.obj[0].width;
	image.height = image.obj[0].height;

	if(!this.loaded) {
		stone_track(this.stone);
		glow.events.addListener(this.stone, 'stone_mousemove', this.start_characters, this);
		this.loaded = true;
		animation_queue.add(this); // TODO: better handling?
	}
}

HoverCharacters.prototype.charactersAnimationFinished = function() {
}

HoverCharacters.prototype.start_characters = function(e) {
	if(!this.ready.length) {
		return;
	}

	var time = new Date();

	if(time - this.last_character > character_pause) {
	    var ready_word = Math.floor(Math.random() * this.ready.length);
	    var image = this.ready[ready_word];

		var dir = (Math.random() / 2 + 0.75) * Math.PI;

		image.obj
			.css('position', 'absolute')
			.css('visibility', 'hidden')
			.css('z-index', 100)
			.appendTo(this.stone);
			
		image.x = e.offsetX;
		image.y = e.offsetY;
		image.dx = character_initial_speed * Math.sin(dir);
		image.dy = character_initial_speed * Math.cos(dir);
		image.start = time;

	    this.ready.splice(ready_word, 1);
		this.characters.push(image);
		this.last_character = time;
	}
}

HoverCharacters.prototype.go = function(time) {
	var interval = time - this.characters_last;
	this.characters_last = time;
	
	var del = 0, i = 0, character;
	while(character = this.characters[i++]) {
		var pos = (time - character.start) / character_duration;
		if(pos >= 1) {
			del = i;
		}
		else {
			character.x += character.dx * interval / 1000;
			character.y += character.dy * interval / 1000;
			character.dx = character.dx * 0.99;
			character.dy = character.dy * 0.99;
			var width = character.width * pos / 2;
			var height = character.height * pos / 2;
			character.obj
				.css('left', (character.x - width / 2) + 'px')
				.css('top', (character.y  - height / 2) + 'px')
				.css('width', width)
				.css('height', height)
				.css('visibility', 'visible');
			if(use_opacity) character.obj.css('opacity', pos < 0.75 ? 1 : (1 - pos) * 4);
		}
	}

	for(var i = 0; i < del; ++i) {
		this.characters[i].obj.remove();
		this.ready.push(this.characters[i]);
	}
	this.characters.splice(0, del);
}

HoverCharacters.prototype.running = function() {
	return true; //this.characters.length != 0;
}

HoverCharacters.prototype.cleanUp = function() {
}

/* Thoughts */

/** @const */ var thought_initial_speed = 100;
/** @const */ var thought_duration = 2000;
/** @const */ var thought_pause = 700;

function hover_thoughts(selector, images)
{
	glow.dom.get(selector).each(
		function() {
			new HoverThoughts(this, images);
		}
	);
}

/** @constructor */
function HoverThoughts(element, images) {
	this.stone = element;
	this.loaded = false;
	this.images = [];
	this.thoughts = [];
	this.last_thought = 0;
	this.count = images.length;
	
	for(var i = 0; i < images.length; ++i) this.load(images[i]);
}

HoverThoughts.prototype.load = function(image_src) {
	var image_obj = new Image();
	var image = {};
	
	var self = this;
	image_obj.onload = function() { self.image_loaded(image); }
	image_obj.src = image_src;
	image.obj = image_obj;
	this.images.push(image);
}

HoverThoughts.prototype.image_loaded = function(image) {
	image.width = image.obj.width;
	image.height = image.obj.height;
	--this.count;

	if(!this.count) {
		stone_track(this.stone);
		glow.events.addListener(this.stone, 'stone_mousemove', this.start_thoughts, this);
		this.loaded = true;
		animation_queue.add(this); // TODO: better handling?
	}
}

HoverThoughts.prototype.thoughtsAnimationFinished = function() {
}

HoverThoughts.prototype.start_thoughts = function(e) {
	var time = new Date();

	if(time - this.last_thought > thought_pause) {
		var details = [
			{ size: 0.0587, x: 0, y: 0 },
			{ size: 0.1268, x: 26, y: -11 },
			{ size: 0.4014, x: 58, y: -65 }
		]
	
		for(var i = 0; i < this.images.length; ++i) {
		    var image = this.images[i];

			var element =
				glow.dom.create('<img src="' + image.obj.src + '">')
					.css('position', 'absolute')
					.css('visibility', 'hidden')
					.css('z-index', 100)
					.appendTo(this.stone);

			this.thoughts.push({
				obj: element,
				x: e.offsetX + details[i].x,
				y: e.offsetY + details[i].y,
				dx: 20 - i * 20,
				dy: -30 - i * 40,
				width1: 0,
				height1: 0,
				width2: details[i].size * image.width - 0,
				height2: details[i].size * image.height - 0,
				start: time,
				i : i
			});
	
			this.last_thought = time;
		}
	}
}

HoverThoughts.prototype.go = function(time) {
	var interval = time - this.thoughts_last;
	this.thoughts_last = time;
	
	var del = 0;
	for(var i = 0, thought; thought = this.thoughts[i]; ++i) {
		var pos = (time - thought.start) / thought_duration;
		if(pos >= 1) {
			del = i;
		}
		else {
			var pos2 = pos * 6 - (thought.i * 0.9);
			var width = 0, height = 0;
			
			if(pos2 < 0) {
				width = 0; height = 0;
			}
			else if(pos2 < 1) {
				width = thought.width1 + pos2 * thought.width2;
				height = thought.height1 + pos2 * thought.height2;
			}
			else {
				width = thought.width1 + thought.width2;
				height = thought.height1 + thought.height2;
			}

			thought.obj
				.css('left', (thought.x + pos * thought.dx - width / 2) + 'px')
				.css('top', (thought.y + pos * thought.dy  - height / 2) + 'px')
				.css('width', width)
				.css('height', height)
				.css('visibility', 'visible');
			
			if(use_opacity && pos > 0.75) {
				thought.obj.css('opacity', (1 - pos) * 4);
			}
		}
	}

	for(var i = 0; i < del; ++i) {
		this.thoughts[i].obj.remove();
	}
	this.thoughts.splice(0, del);
}

HoverThoughts.prototype.running = function() {
	return true; //this.thoughts.length != 0;
}

HoverThoughts.prototype.cleanUp = function() {
}
