/*

jquery.hyperspace.js v0.0
 
Gregory LeRoy
 
This is my first open source code that I will soon be releasing to the public free for all to do whatever they can do with it.
 
It is the classic [graphics programming 101] "starfield" with hypertext stars (without the svg, canvas element, or any flashy stuff)
 
I have to document, reference, refactor, optimize, and correct quite a few things before my first 0 public release.

What about an option for 3-d glasses!  Should I wait or just release it now????

bitwize@acm.org

2009-12-27

*/
;(function($) {

	Sprite = function(e,x,y,z,a,t) {
		this.e = e;
		this.i = null;
		this.t = t || [e.html()];
		this.images = [];
		this.x = x || 0;
		this.y = y || 0;
		this.z = z || 0;
		this.a = a || 0;
		this.n = 0;
		this.zindex = 0;
		this.init();
	};
	
	Sprite.prototype = {	
		init : function() {
			this.initImages();
		},
		initImages : function() {
			this.images = [];
			this.i = this.e.find('img');
			if (this.i.length) {
				var $this = this;
				$.each(this.i, function(i, val) {
					$val = $(val);
					$this.images[i] = {image:val, width:val.width, height:val.height};			
					$val.onload = function(i, val) {
						$this.images[i].width  = val.width;
						$this.images[i].height = val.height;
					}(i, val);
				});
			}
		},
		resizeImages : function(size) {
			if (this.i.length) {
				size = size / 100;
				var $this = this;
				$.each(this.i, function(i, val) {
					img = $this.images[i];
					if (img.width == 0 || img.height == 0) {
						img.width  = val.width; 
						img.height = val.height; 
					}
					w = img.width  * size;
					h = img.height * size;
					$(val).css({'width':w,'height':h});
				});
			}
		},
		next : function() {
			this.n = this.n+1;
			var html = this.t[this.n % this.t.length];
			this.e.html(html);
			this.initImages();
		}
	};
	
	$.hyperSpace = function(space, hypertext, options) {		
		this.options = $.extend({
			depth        : 255,
			animSpeed    : 50,
			zoomLimit    : 20,
			zoomFactor   : 0.135,
			waveX	     : false,
			waveY        : false
  		}, options || {});
		this.count  	 = 0;
		this.space 		 = $(space);
		this.hypertext   = hypertext || [];
		this.width  	 = this.space.width();
		this.height 	 = this.space.height();
		this.xLimit 	 = this.width/2;
		this.yLimit 	 = this.height/2;
		this.zLimit 	 = this.options.depth;
		this.animSpeed 	 = this.options.animSpeed;
		this.zoomLimit 	 = this.options.zoomLimit;
		this.zoomFactor  = this.options.zoomFactor;
		this.waveX 		 = this.options.waveX;
		this.waveY 		 = this.options.waveY;
		this.wave 		 = {degree:0, dir:.1, ampl:20, freq:100};
		this.started 	 = false;
		this.sprites 	 = [];
		this.init();
	};
	
	$.hyperSpace.prototype = {
		render : function(id, html) { 
			return '<div id="'+id+'" class="sprite">'+html+'</div>'; 
		},
		
		init : function() {
			this.sprites = [];
			this.space.empty();
			this.space.css('position','relative');
			var n = this.hypertext.length;
			for(var i=0; i<n; i++) {		
				this.add(this.hypertext[i]);
			}
		},
		resize : function(w,h) {
			this.width = w;
			this.height = h;
			this.xLimit = w/2;
			this.yLimit = h/2;
		},
		start : function() {
			this.started = true;
			this.intervalId = window.setInterval(function(objRef){
				return function(){
					objRef.step.call(objRef)
				}
			}(this), this.animSpeed);
		},
		
		stop : function() {
			this.started = false;
			window.clearInterval(this.intervalId);
		},
		toggle : function() {
			if (this.started) {
				this.stop();
			}
			else {
				this.start();	
			}
		},
		reset : function() {
			if (this.started) {
				this.stop();
			}
			this.init();
		},
		
		add : function(hypertext) {
			var items = $.isArray(hypertext)? hypertext : $.makeArray(hypertext);			
			var i = this.sprites.length;
			var html = items[0]; 
			var id = 'sprite'+i;
			var rendered = this.render(id, html);
			var elem  = $(rendered);
   			$(this.space).append(elem);
			this.setVisible(elem,false);
			var sprite = new Sprite(elem, this.newX(), this.newY(), this.newZ(), this.newA(), items);
			this.sprites.push(sprite);
			this.count++;
		},
		
		step : function() {
			var n = this.sprites.length;
			for(var i=0; i<n; i++){
				var sprite = this.sprites[i];
			
				if (Math.abs(sprite.x) > this.xLimit) {
					this.recycle(sprite);
				}
				else {
					sprite.x += (sprite.x * sprite.a) / 200;	
					if(this.waveX) sprite.x += this.wave.ampl * Math.sin(this.wave.degree);
				}
				if (Math.abs(sprite.y) > this.yLimit) {
					this.recycle(sprite);
				}
				else {
					sprite.y += (sprite.y * sprite.a) / 200;	
					if(this.waveY) sprite.y += this.wave.ampl * Math.sin(this.wave.degree);
				}
				sprite.z = (sprite.z < this.zoomLimit) ? sprite.z + this.zoomFactor : sprite.z;
				sprite.zindex = sprite.zindex + 1;
				this.draw(sprite);
			}
			if(this.waveX || this.waveY){
				if(this.wave.degree > 90 || this.wave.degree < 0) this.wave.dir *= -.1;
				this.wave.degree += this.wave.dir;
			}
		},
		recycle : function(sprite) {
			sprite.x = this.newX();
			sprite.y = this.newY();
			sprite.z = this.newZ();
			sprite.a = this.newA();
			sprite.zindex = 0;
			sprite.next();
		},
		draw : function(sprite) {
			var e = sprite.e;
			var w = e.width();
			var h = e.height();
			var cy = h / 2;
			var cx = w / 2;
			var x = sprite.x + this.xLimit;
			var y = sprite.y + this.yLimit;
			this.setStyles(e, sprite.z * 100, x, y, sprite.zindex);
			sprite.resizeImages(sprite.z * 100);
		},
		setStyles : function(elem, size, left, top, zindex) {
			size = parseInt(size);
			top = Math.floor(top || 0);
			left = Math.floor(left || 0);
			zindex = zindex || 0;
			elem.css({'position':'absolute','visibility':'visible','font-size': size + '%','left':left+'px', 'top':top+'px', 'z-index':zindex});
		},

		setVisible : function(elem, visible) {
			elem.css('visibility', (visible ? 'visible' : 'hidden'));
		},
		setAbsolute : function(elem) {
			elem.css('position', 'absolute');
		},
		setFontSize : function(elem, size) {
			size = parseInt(size);
			elem.css({'position':'absolute','visibility':'visible','font-size': size + '%'});
		},
		setPosition : function(elem, left, top, zindex) {
			top = Math.floor(top || 0);
			left = Math.floor(left || 0);
			zorder = zorder || 0;
			elem.css({'left':left+'px', 'top':top+'px', 'z-index':zindex});
		},
		rand : function(min, max) {
			return Math.floor(Math.random() * (max-min) + min);
		},
		newX : function() {
			return this.rand(-this.xLimit, this.xLimit);
		},
		newY : function() {
			return this.rand(-this.yLimit, this.yLimit);	
		},
		newZ : function() {
			return .1;	
		},
		newA : function() {
			return this.rand(5, 25);
		}
	};
	
})(jQuery);
