
window.onerror = null;

var ns6 = (!document.all && document.getElementById);
var ie4 = (document.all);
var ns4 = (document.layers);
var BV  = (parseFloat(navigator.appVersion.indexOf("MSIE")>0 ?
			navigator.appVersion.split(";")[1].substr(6)	 :
			navigator.appVersion));
var op  = (navigator.userAgent.indexOf('Opera')!=-1 && BV>=4);

var pageWidth  = 0;						// page dimension & visible offset
var pageHeight = 0;
var pageOffX   = 0;
var pageOffY   = 0;


// <---- Customizable part ----
var flakeImageDir   = 'http://robiu.ru/modules/mod_jssnow/';				// relative path for the snow flake images

var infoLayer  = false;				// set to false if you don't need f/s display

var flakes = 25;						// total number of snowflakes
var flake_speed_PperS =  30;			// flake speed in pixel/second
var storm_speed_PperS = 300;			// storm speed in pixel/second

var flake_TX          = 1.0;            // max. sec. of flake's constant X-movement on fluttering
var flake_XperY		  = 2.0;			// fluttering movement's max. vx/vy ratio

var storm_duration_S  = 10.0;			// storm duration in seconds - watch out: about 1-2 seconds for deceleration
var storm_lag_S       = 60.0;			// no-storm in seconds
var storm_YperX       = 1/3.0;			// storm's max. vy/vx ratio

var disappear_margin = 32;				// pixels from each border where objects disappear
// ---- Customizable part ---->


var refresh_FperS = 20;					// initial frames/second, recalculated.
var refresh 	  = 1000/refresh_FperS;	// ms/frame

var flake_speed 	= 0;				// flake speed in pixel/frame
var storm_speed 	= 0;				// storm speed in pixel/frame
var storm_YperX_current = storm_YperX;  // storm direction varies each time
var storm_v_sin     = 0;                // storm speed's sine
var storm_v_cos     = 1;                // storm speed's cosine
var storm_direction = 0;				// storm X-direction, -1/0=quiet/+1
var storm_id    	= 0;				// ID of storm timer

var storm_blowing	= 1;				// start with storm=ON

var flake    = new Array(flakes);
var flakeX   = new Array(flakes);
var flakeY   = new Array(flakes);
var flakeSX  = new Array(flakes);
var flakeVX  = new Array(flakes);
var flakeVY	 = new Array(flakes);
var flakeVIS = new Array(flakes);
var flakeDX  = 0;			// X-movement in pixel/frame, caused by storm
var flakeDY  = 0;			// Y-movement in pixel/frame, caused by storm



var timer_id    = 0;		// ID if timer proc.
var timer_sum   = refresh;	// Inital values for speed calculation
var timer_count = 1;		// --''--

var flake_visible = op;		// start with visble flakes for Opera, all others start invisible
var flake_id	  = 0;		// timer id of make_flake_visible


//-------------------------------------------------------------------------
// preload images
//-------------------------------------------------------------------------
var kFlakeImages = 7;
var flake_images = new Array(kFlakeImages);
for(i=0;i<kFlakeImages;i++) {
       flake_images[i] = new Image();
	flake_images[i].src = flakeImageDir+'snow'+i+'.gif';
}

//-------------------------------------------------------------------------
// calculates optimum framerate & corresponding speed
//-------------------------------------------------------------------------
function rebuild_speed_and_timer() {
	var old = refresh_FperS;
	refresh = Math.floor(timer_sum/timer_count*2)+10;	// ms/Frame + spare
	refresh_FperS = Math.floor(1000/refresh);			// frames/second

	flake_speed = flake_speed_PperS/refresh_FperS;		// pixel/second  --> pixel/frame
	storm_speed = storm_speed_PperS/refresh_FperS; 		// pixel/second  --> pixel/frame

	if (timer_id) window.clearInterval(timer_id);		// adapt timer
	timer_id = window.setInterval('move_snow()',refresh);

	// FRAMES PER SECOND DISPLAY: REMOVE IF NOT NEEDED
	if(infoLayer){
		if (old!=refresh_FperS) write_to_layer(infoLayer,refresh_FperS+'f/s');
	}

	// adapt speed - for smoothness
	if (old != refresh_FperS) {
		var ratio = old/refresh_FperS;
		
		for (i=0; i<flakes; i++) {
			flakeSX[i] *= ratio;
			flakeVX[i] *= ratio;
			flakeVY[i] *= ratio;
		}
	}

	timer_count /= 2;	// moving medium
	timer_sum   /= 2;
}



//-------------------------------------------------------------------------
// make flakes visible: while initalialisation phase flakes are invisble.
//						after make_flakes_visible, all new flakes start visible
//-------------------------------------------------------------------------
function make_flake_visible_proc() {
	window.clearInterval(flake_id);
	flake_visible = true;
}


//-------------------------------------------------------------------------
// storm proc - turn storm on/off
//-------------------------------------------------------------------------
function storm_proc() {
	// keep ourself from restarting while running
	window.clearInterval(storm_id);

	if (storm_blowing == 0) {
		// turn storm on
		storm_blowing = (Math.random()<0.5) ? -1 : 1 ;
		storm_YperX_current = Math.random()*storm_YperX;

		// prepare values by trigonometrical functions - too heavy for move_snow()
		var storm_theta = Math.atan(storm_YperX_current);
		storm_v_cos = Math.cos(storm_theta);
		storm_v_sin = Math.sin(storm_theta);
		storm_id = window.setInterval('storm_proc()',storm_duration_S*1000.0);

	} else {
		// turn storm off
		storm_blowing *= 0.7;
		if ((Math.abs(storm_blowing)<0.05) || (!flake_visible)) {
			storm_blowing = 0;
			storm_id = window.setInterval('storm_proc()',storm_lag_S*1000.0);
		} else {
			storm_id = window.setInterval('storm_proc()',500.0);
		}
	}

	// preapare movement vektor for snow, caused by storm
	flakeDX = storm_v_cos*storm_speed*storm_blowing;
	flakeDY = Math.abs(storm_v_sin*storm_speed*storm_blowing);

	}




//-------------------------------------------------------------------------
// create all layers & Objects
//-------------------------------------------------------------------------
function init_snow() {
	
	// create flake
	for (var i=0; i<flakes; i++) {
		flake[i]    = get_layer_by_name('flake'+i);
		flakeX[i]   = Math.random()*pageWidth;
		flakeY[i]   = Math.random()*pageHeight;
		flakeSX[i]  = 0;
		flakeVX[i]  = 0;
		flakeVIS[i] = flake_visible;
		flakeVY[i]  = 1;
	}
}



//-------------------------------------------------------------------------
// get the named layer
//-------------------------------------------------------------------------
function get_layer_by_name(id) {
	if (op) 	{	return document.getElementById(id);	}
	if (ns6)	{	return document.getElementById(id);	}
	if (ie4)	{	return document.all[id];			}
	if (ns4)	{	return document.layers[id];			}
}




//-------------------------------------------------------------------------
// move all objects
//-------------------------------------------------------------------------
function move_snow() {
	var beginn = new Date().getMilliseconds();
	move_snow();
	var ende = new Date().getMilliseconds();
	var diff = (beginn>ende?1000+ende-beginn:ende-beginn);
	timer_sum   += diff;
	timer_count ++;
	if (timer_count>10) {
		rebuild_speed_and_timer();
	}
}


//-------------------------------------------------------------------------
// snowflake's private movement
//-------------------------------------------------------------------------
function move_snow() {
	for (var i=0; i<flakes; i++) {
		// flake outside screen ?
		flakeX[i] += flakeVX[i] + flakeDX;
		flakeY[i] += flakeVY[i] + flakeDY;
		if (flakeY[i]>pageHeight-disappear_margin) {
			flakeX[i]  = Math.random()*pageWidth;
			flakeY[i]  = 0;
			flakeVY[i] = flake_speed+Math.random()*flake_speed;
			if (Math.random()<0.1) flakeVY[i] *= 2.0;
			if (flake_visible) flakeVIS[i] = true;
		}

		// left-right- movement
		flakeSX[i] --;
		if (flakeSX[i] <= 0) {
			flakeSX[i] = Math.random()*refresh_FperS*flake_TX;
			flakeVX[i] = (2.0*Math.random()-1.0)*flake_XperY*flake_speed;
		}

		if (flakeX[i]<-disappear_margin)
		    flakeX[i] += pageWidth;
		if (flakeX[i]>=(pageWidth-disappear_margin))
			flakeX[i] -= pageWidth;

		// move flake to new position
		move_to(flake[i],flakeX[i],flakeY[i],flakeVIS[i]);
	}
}



//-------------------------------------------------------------------------
// move a layer
//-------------------------------------------------------------------------
function move_to(obj, x, y, visible) {
	if (visible) {
		if (op) {
			obj.style.left		= x+"px";
			obj.style.top 		= y+"px";
			// FIX THIS: SHOW DOES NOT WORK OPERA
			obj.style.display 	= "block";
		} else if (ie4) {
			obj.style.pixelLeft = x;
			obj.style.pixelTop  = y;
			obj.style.visibility= "visible";
		} else if (ns4) {
			obj.left 			= x;
			obj.top				= y;
			obj.visibility 		= "show";
		} else if (ns6) {
			obj.style.left 		= x+"px";
			obj.style.top		= y+"px";
			obj.style.display 	= "block";
		}
	} else {
		if (ie4 || op) { obj.style.visibility = "hidden";}
		if (ns4) { obj.visibility 		= "hide";}
		if (ns6) { obj.style.display 	= "none";}
	}
}




//-------------------------------------------------------------------------
// fill a layer with new content
// --- parameter: layer-name, new content
//-------------------------------------------------------------------------
function write_to_layer(layer,txt) {
	if (op) {
		// FIX THIS: DOES NOT WORK FOR OPERA
		document.getElementById(layer).innerHTML = txt;
	} else if (ie4) {
		document.all[layer].innerHTML = txt;
	} else if (ns4) {
		document[layer].document.write(txt);
		document[layer].document.close();
	} else if (ns6) {
		var over = document.getElementById(layer);
		var range = document.createRange();
		range.setStartBefore(over);
		var domfrag = range.createContextualFragment(txt);
		while (over.hasChildNodes()) {
			over.removeChild(over.lastChild);
		}
		over.appendChild(domfrag);
	}
}





//-------------------------------------------------------------------------
// size of page
//-------------------------------------------------------------------------
function get_page_dimension() {
	if(op) {
		pageOffX   = 0;
		pageOffY   = 25;
		pageWidth  = innerWidth  + pageOffX;
		pageHeight = innerHeight + pageOffY;
	} else if(ns6) {
		pageOffX   = scrollX;
		pageOffY   = scrollY;
		pageWidth  = innerWidth  + pageOffX;
		pageHeight = innerHeight + pageOffY;
	} else if(ns4) {
		pageOffX   = window.pageXOffset;
		pageOffY   = window.pageYOffset;
		pageWidth  = innerWidth  + pageOffX;
		pageHeight = innerHeight + pageOffY;
	} else if(ie4) {
		pageOffX   = document.body.scrollLeft;
		pageOffY   = document.body.scrollTop;
		pageWidth  = document.body.clientWidth  + pageOffX;
		pageHeight = document.body.clientHeight + pageOffY;
	}
}


//-------------------------------------------------------------------------
// initialize all objects & timer
//-------------------------------------------------------------------------
function start() {
	var a = '';
	
	// each snowflake's private layer
	for (var i=0; i<flakes; i++) {
		a += '<div id="flake'+i+'" '
		  +  'style="position: absolute; '
		  +  'left:-1px; top:-1px; z-index:0;">'
		  +  '<img src="' +flake_images[i % kFlakeImages].src+ '"></div>\n';
	}


	// write private layers to destination layer - this works for opera,too
	document.write(a);

	// recalculate page dimension every second
	window.setInterval('get_page_dimension()',1000);
	get_page_dimension();

	// init all objects
	init_snow();

	// place snowflakes & trees
	rebuild_speed_and_timer(refresh);

	// start the animation
	timer_id = window.setInterval('move_snow()',refresh);
	storm_id = window.setInterval('storm_proc()',1800);					// init with visible storm
	flake_id = window.setInterval('make_flake_visible_proc()',2000);	// after the storm, let snowflakes fall :-)
}

start();

