script.js
author Eugen Sawin <sawine@me73.com>
Fri, 06 Jan 2012 17:41:08 +0100
changeset 88 9c66e0a69a07
parent 87 c19fea1cd73f
child 89 728dc42f2eb3
permissions -rw-r--r--
Fixed failed init of game of life.
     1 var QUOTES_NUMBER = 39;
     2 var SERVER = "http://" + window.location.hostname + ":" + window.location.port;
     3 var simulation;
     4 var gameoflife;
     5 
     6 $(document).ready(function() {   
     7     simulation = new Simulation(document.getElementById("sim"), 
     8                                 670, 50, 6, green, background);  
     9     var image = new Image();
    10     image.onload = init_logo;
    11     image.src = "images/logobase.png";
    12     $("#logo").click(handle_click);   
    13     $("#logo").hover(function() {
    14         $(this).css("cursor", "pointer");        
    15     }, function() {
    16         $(this).css("cursor", "auto");
    17     });
    18     $("#sim").mousemove(function(event) { simulation.mouse_moved(mouse_pos(event, "#sim")); });
    19     var onEachFrame;
    20     if (window.webkitRequestAnimationFrame) {
    21         onEachFrame = function(cb) {
    22             var _cb = function() { cb(); webkitRequestAnimationFrame(_cb); }
    23             _cb();
    24         };
    25     } else if (window.mozRequestAnimationFrame) {
    26         onEachFrame = function(cb) {
    27             var _cb = function() { cb(); mozRequestAnimationFrame(_cb); }
    28             _cb();
    29         };
    30     } else {
    31         onEachFrame = function(cb) {
    32             setInterval(cb, 1000 / 30);
    33         }
    34     }    
    35     window.onEachFrame = onEachFrame;   
    36     window.onEachFrame(update);  
    37     var pos = document.location.href.lastIndexOf("/") + 1; 
    38     var page = document.location.href.substr(pos);
    39     if (page == "mandelbrot.html") {
    40         draw(0, 0, 0, 0, 0);
    41     } else if (page == "" || page == "index.html") {
    42         init_gameoflife("ME73");
    43     } else if (page == "gameoflife.html") {
    44         init_gameoflife("");
    45     }
    46 });
    47 
    48 $(document).keypress(function(event) {
    49     if (event.which == 13) {      
    50 	event.preventDefault();
    51         update_logo();	    
    52     }
    53 });
    54 
    55 var menu_width = 670;
    56 var menu_height = 100;
    57 var menu_splits = new Array(180, 350, 505);
    58 
    59 var click_events = new Array();
    60 click_events["logo"] = new Array();
    61 click_events["logo"][0] = new Object();
    62 click_events["logo"][0]["min_x"] = 0;
    63 click_events["logo"][0]["max_x"] = menu_splits[0];
    64 click_events["logo"][0]["min_y"] = 0;
    65 click_events["logo"][0]["max_y"] = menu_height;
    66 click_events["logo"][0]["func"] = switch_page;
    67 click_events["logo"][0]["args"] = new Array("personalwork.html");
    68 click_events["logo"][1] = new Object();
    69 click_events["logo"][1]["min_x"] = menu_splits[0] + 1;
    70 click_events["logo"][1]["max_x"] = menu_splits[1];
    71 click_events["logo"][1]["min_y"] = 0;
    72 click_events["logo"][1]["max_y"] = menu_height;
    73 click_events["logo"][1]["func"] = switch_page;
    74 click_events["logo"][1]["args"] = new Array("howiwork.html");
    75 click_events["logo"][2] = new Object();
    76 click_events["logo"][2]["min_x"] = menu_splits[1] + 1;
    77 click_events["logo"][2]["max_x"] = menu_splits[2];
    78 click_events["logo"][2]["min_y"] = 0;
    79 click_events["logo"][2]["max_y"] = menu_height;
    80 click_events["logo"][2]["func"] = switch_page;
    81 click_events["logo"][2]["args"] = new Array("books.html");
    82 click_events["logo"][3] = new Object();
    83 click_events["logo"][3]["min_x"] = menu_splits[2] + 1;
    84 click_events["logo"][3]["max_x"] = menu_width - 1;
    85 click_events["logo"][3]["min_y"] = 0;
    86 click_events["logo"][3]["max_y"] = menu_height;
    87 click_events["logo"][3]["func"] = switch_page;
    88 click_events["logo"][3]["args"] = new Array("links.html", "linksend.html");
    89 
    90 function mouse_pos(event, id) { 
    91     var offset = $(id).offset();   
    92     return new Array(event.pageX - offset.left, 
    93                      event.pageY - offset.top);
    94 }
    95 
    96 function handle_click(event) {
    97     var xy = mouse_pos(event, "#logo");
    98     var x = xy[0];
    99     var y = xy[1];
   100     for (var i in click_events[event.target.id]) {
   101         var ces = click_events[event.target.id][i];
   102         if (ces && x >= ces["min_x"] && x <= ces["max_x"]
   103             && y >= ces["min_y"] && y <= ces["max_y"]) {    
   104             ces["func"](event.target.id, ces);
   105             break;
   106         }
   107     }
   108 }
   109 
   110 function switch_page(id, params) {
   111     var page = params["args"][0];
   112     $("body").load(page);
   113     document.location.href = page;    
   114 }
   115 
   116 function colour_area(image, min_x, max_x, min_y, max_y, colour) {   
   117     var pixels = image.data;    
   118     for (var y = min_y; y <= max_y; y += 1) {
   119         for (var x = min_x; x <= max_x; x += 1) {
   120             var pix_colour = getPixel(image, x, y);
   121             if (pix_colour.a > 0) {
   122                 colour.a = pix_colour.a;
   123                 setPixel(image, x, y, colour);
   124             }
   125         }
   126     }    
   127 }
   128 
   129 function init_logo(event) {
   130     var canvas = document.getElementById("logo");
   131     var context = canvas.getContext("2d");    
   132     canvas.width = menu_width;
   133     canvas.height = menu_height;
   134     context.drawImage(event.target, 0, 0);
   135     var image = context.getImageData(0, 0, menu_width, menu_height);    
   136     for (var i in click_events["logo"]) {
   137         var p = click_events["logo"][i]; 
   138         var min_x = p["min_x"];
   139         var max_x = p["max_x"];
   140         var min_y = p["min_y"];
   141         var max_y = p["max_y"];
   142         var pos = document.location.href.lastIndexOf("/") + 1; 
   143         var page = document.location.href.substr(pos);        
   144         for (var j in p["args"]) {
   145             if (page == p["args"][j]) {         
   146                 colour_area(image, min_x, max_x, 0, 60, black);
   147                 colour_area(image, min_x, max_x, 60, max_y, white);
   148                 break;
   149             }
   150             else {
   151                 colour_area(image, min_x, max_x, 0, 60, white);
   152                 colour_area(image, min_x, max_x, 60, max_y,  orange);  
   153             }
   154         }       
   155     } 
   156     context.putImageData(image, 0, 0);      
   157     // context.fillStyle = green.str();
   158     // console.log((found["max_x"] - found["min_x"]) / 2 + found["min_x"] - 2);
   159     // context.fillRect((found["max_x"]-found["min_x"])/2+found["min_x"]-1, 95, 2, 5);  
   160     draw_menu_lines();   
   161 }
   162 
   163 function draw_menu_lines() {
   164     var canvas = document.getElementById("logo");
   165     var context = canvas.getContext("2d");  
   166     var image = context.getImageData(0, 0, menu_width, menu_height);
   167     var pixels = image.data;
   168     context.lineCap = "round";
   169     context.lineWidth = 1;
   170     var colour1a = new Colour(0, 0, 0, 0.1);
   171     context.beginPath();   
   172     context.moveTo(0, menu_height - 1);
   173     context.lineTo(menu_width - 1, menu_height - 1);   
   174     context.closePath();  
   175     context.strokeStyle = colour1a.str();
   176     context.stroke(); 
   177     var colour1e = new Colour(0, 0, 0, 0.05);  
   178     context.beginPath();   
   179     context.moveTo(70, menu_height - 1);
   180     context.lineTo(menu_width - 71, menu_height - 1);   
   181     context.closePath();  
   182     context.strokeStyle = colour1e.str();
   183     context.stroke();  
   184     var colour1b = new Colour(0, 0, 0, 0.05);  
   185     context.beginPath();   
   186     context.moveTo(50, menu_height - 1);
   187     context.lineTo(menu_width - 51, menu_height - 1);   
   188     context.closePath();  
   189     context.strokeStyle = colour1b.str();
   190     context.stroke();
   191     var colour1c = new Colour(0, 0, 0, 0.05);  
   192     context.beginPath();   
   193     context.moveTo(25, menu_height - 1);
   194     context.lineTo(menu_width - 26, menu_height - 1);   
   195     context.closePath();  
   196     context.strokeStyle = colour1c.str();
   197     context.stroke();
   198     var colour1d = new Colour(0, 0, 0, 0.05);  
   199     context.beginPath();   
   200     context.moveTo(10, menu_height - 1);
   201     context.lineTo(menu_width - 11, menu_height - 1);   
   202     context.closePath();  
   203     context.strokeStyle = colour1d.str();
   204     context.stroke();  
   205     
   206     var colour2 = new Colour(0, 0, 0, 0.2);
   207     context.beginPath();  
   208     context.moveTo(menu_splits[0], 0); 
   209     context.lineTo(menu_splits[0], menu_height - 1);
   210     context.moveTo(menu_splits[1], 0);
   211     context.lineTo(menu_splits[1], menu_height - 1);
   212     context.moveTo(menu_splits[2], 0);
   213     context.lineTo(menu_splits[2], menu_height - 1);
   214     context.closePath();
   215     context.strokeStyle = colour2.str();
   216     context.stroke();
   217 
   218     context.clearRect(menu_splits[0] - 1, menu_height - 1, 2, 2);
   219     context.clearRect(menu_splits[1] - 1, menu_height - 1, 2, 2);
   220     context.clearRect(menu_splits[2] - 1, menu_height - 1, 2, 2);
   221 
   222     context.clearRect(menu_splits[0] - 1, menu_height - 2, 1, 1);
   223     context.clearRect(menu_splits[1] - 1, menu_height - 2, 1, 1);
   224     context.clearRect(menu_splits[2] - 1, menu_height - 2, 1, 1);
   225 }
   226 
   227 function clearMenuLines() {
   228     var canvas = document.getElementById("logo");
   229     var context = canvas.getContext("2d");  
   230     var image = context.getImageData(0, 0, menu_width, menu_height);
   231     var pixels = image.data;
   232     context.clearRect(0, menu_height - 2, menu_width - 1, 2);
   233     context.clearRect(menu_splits[0] - 1, 0, 2, menu_height - 1);
   234     context.clearRect(menu_splits[1] - 1, 0, 2, menu_height - 1);
   235     context.clearRect(menu_splits[2] - 1, 0, 2, menu_height - 1);
   236 }
   237 
   238 function draw_sim_lines() {
   239     var w = 670;
   240     var h = 50;
   241     var canvas = document.getElementById("sim");
   242     var context = canvas.getContext("2d");  
   243     var image = context.getImageData(0, 0, w, h);
   244     var pixels = image.data;
   245     context.lineCap = "round";
   246     context.lineWidth = 1;
   247     var colour1a = new Colour(0, 0, 0, 0.1);
   248     context.beginPath();   
   249     context.moveTo(0, h - 1);
   250     context.lineTo(w - 1, h - 1);   
   251     context.closePath();  
   252     context.strokeStyle = colour1a.str();
   253     context.stroke(); 
   254     var colour1e = new Colour(0, 0, 0, 0.05);  
   255     context.beginPath();   
   256     context.moveTo(70, h - 1);
   257     context.lineTo(w - 71, h - 1);   
   258     context.closePath();  
   259     context.strokeStyle = colour1e.str();
   260     context.stroke();  
   261     var colour1b = new Colour(0, 0, 0, 0.05);  
   262     context.beginPath();   
   263     context.moveTo(50, h - 1);
   264     context.lineTo(w - 51, h - 1);   
   265     context.closePath();  
   266     context.strokeStyle = colour1b.str();
   267     context.stroke();
   268     var colour1c = new Colour(0, 0, 0, 0.05);  
   269     context.beginPath();   
   270     context.moveTo(25, h - 1);
   271     context.lineTo(w - 26, h - 1);   
   272     context.closePath();  
   273     context.strokeStyle = colour1c.str();
   274     context.stroke();
   275     var colour1d = new Colour(0, 0, 0, 0.05);  
   276     context.beginPath();   
   277     context.moveTo(10, h - 1);
   278     context.lineTo(w - 11, h - 1);   
   279     context.closePath();  
   280     context.strokeStyle = colour1d.str();
   281     context.stroke();   
   282 }
   283 
   284 function update_logo() {
   285     var canvas = document.getElementById("logo");
   286     var context = canvas.getContext("2d");  
   287     var image = context.getImageData(0, 0, menu_width, menu_height);
   288     var pixels = image.data;
   289     var white = new Colour(255, 255, 255);
   290     for (var y = 0; y < menu_height; y += 1) {
   291         for (var x = 0; x < menu_width; x += 1) {
   292             var pix_colour = getPixel(image, x, y);
   293             if (pix_colour.a > 0) {
   294                 setPixel(image, x, y, new Colour(90, 215, 21, pix_colour.a));
   295             }
   296         }
   297     }   
   298     context.putImageData(image, 0, 0);
   299 }
   300 
   301 var char_map = new Array();
   302 char_map["A"] = new Array(new Array(0, 4), new Array(1, 3),
   303                           new Array(2, 2), new Array(3, 1),
   304                           new Array(4, 0), new Array(5, 1),
   305                           new Array(6, 2), new Array(7, 3),
   306                           new Array(8, 4), new Array(2, 3),
   307                           new Array(3, 3), new Array(4, 3),
   308                           new Array(5, 3), new Array(6, 3));
   309 char_map["M"] = new Array(new Array(0, 4), new Array(0, 3),
   310                           new Array(0, 2), new Array(0, 1),
   311                           new Array(0, 0), new Array(0, 5),
   312                           new Array(5, 4), new Array(5, 3),
   313                           new Array(5, 2), new Array(5, 1),
   314                           new Array(5, 0), new Array(5, 5),
   315                           new Array(1, 1), new Array(2, 2), 
   316                           new Array(4, 2), new Array(5, 1),
   317                           new Array(3, 3));
   318 char_map["E"] = new Array(new Array(0, 4), new Array(0, 3),
   319                           new Array(0, 2), new Array(0, 1),
   320                           new Array(0, 0), new Array(0, 5),
   321                           new Array(1, 0), new Array(2, 0),
   322                           new Array(3, 0),
   323                           new Array(1, 3), new Array(2, 3),                       
   324                           new Array(1, 5), new Array(2, 5),
   325                           new Array(3, 5))
   326 char_map["7"] = new Array(new Array(0, 0), new Array(1, 0),
   327                           new Array(2, 0),
   328                           new Array(0, 5), new Array(0, 4),
   329                           new Array(1, 3), new Array(2, 2));
   330 char_map["3"] = new Array(new Array(2, 4), new Array(2, 3),
   331                           new Array(2, 2), new Array(2, 1),
   332                           new Array(0, 0), new Array(0, 5),
   333                           new Array(1, 0), new Array(2, 0),                         
   334                           new Array(1, 3), new Array(2, 3),                          
   335                           new Array(1, 5), new Array(2, 5))
   336 
   337 function init_gameoflife(text) {
   338     var canvas = document.getElementById("gameoflife");
   339     var cell_size = 16;
   340     if (canvas) {
   341         gameoflife = new Simulation(canvas, 670, 670, cell_size,
   342                                     green, black);
   343         $("#gameoflife").mousemove(function(event) { 
   344             gameoflife.mouse_moved(mouse_pos(event, "#gameoflife")); 
   345         });       
   346         var offset = new Array(13, 16);
   347         for (var c in text) {            
   348             var w = 0;
   349             for (p in char_map[text[c]]) {
   350                 var x = char_map[text[c]][p][0];
   351                 var y = char_map[text[c]][p][1];
   352                 w = Math.max(w, x);
   353                 var pos = new Array((x + offset[0]) * cell_size, 
   354                                     (y + offset[1]) * cell_size);
   355                 gameoflife.activate_cell(pos);
   356             }            
   357             offset[0] += w + 2;
   358         }       
   359     }    
   360 }
   361 
   362 function Colour(r, g, b, a) {
   363     this.r = r;
   364     this.g = g;
   365     this.b = b;
   366     this.a = a == undefined ? 255 : a;
   367 }
   368 Colour.prototype.equals = function(rhs) {
   369     for (p in this) {
   370         if (typeof(rhs[p]) == undefined
   371            || this[p] != rhs[p]) {           
   372             return false;
   373         }
   374     }
   375     return true;
   376 }
   377 Colour.prototype.str = function() {
   378     return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")";
   379 }
   380 
   381 var white = new Colour(255, 255, 255);
   382 var black = new Colour(0, 0, 0);
   383 var orange = new Colour(201, 87, 35);
   384 var green = new Colour(90, 215, 21);
   385 var background = new Colour(34, 34, 34);
   386 
   387 function setPixel(image, x, y, colour) {
   388     var index = 4 * (x + y * image.width);
   389     image.data[index] = colour.r;
   390     image.data[index + 1] = colour.g;
   391     image.data[index + 2] = colour.b;
   392     image.data[index + 3] = colour.a;
   393 }
   394 
   395 function getPixel(image, x, y) {   
   396     var index = 4 * (x + y * image.width);
   397     var r = image.data[index];
   398     var g = image.data[index + 1];
   399     var b = image.data[index + 2];
   400     var a = image.data[index + 3];
   401     return new Colour(r, g, b, a);
   402 }
   403 
   404 function load_random_quote() {
   405     var file = "/quotes/quote" + Math.floor(Math.random() * QUOTES_NUMBER + 1) + ".html";
   406     $.ajax({url: SERVER + file,
   407             success: write_quote});
   408 }
   409 
   410 function write_quote(data, status, xhr) {   
   411     $("#random_quote").html(data);
   412 }
   413 
   414 function load_footer()
   415 {
   416 	var file = "/footer.html";
   417 	var currentFile = self.location.hostname + self.location.pathname;		
   418 	var request = http_request_object();
   419 	var url  = "http://" + self.location.hostname + file;	
   420 	request.open("GET", url, false);
   421 	request.setRequestHeader("User-Agent", navigator.userAgent);
   422 	request.send(null)
   423 	// if (oRequest.status == 200) alert(oRequest.responseText);
   424 	// else alert("Error executing XMLHttpRequest call!");	
   425 	//document.write(url);
   426 	//document.write(request.responseText);
   427     document.getElementById('footer').innerHTML = request.responseText;
   428 }
   429 
   430 
   431 
   432 // Mandelbrot functions
   433 function Complex(real, imag) 
   434 {
   435 	this.real = real;
   436 	this.imag = imag;
   437 }
   438 
   439 var MIN_C = new Complex(-2.2, -1.4);
   440 var MAX_C = new Complex(1.0, 1.4);
   441 var min_c = MIN_C;
   442 var max_c = MAX_C;
   443 var MIN_ITER = 100;
   444 var max_iter = MIN_ITER;
   445 var zoom = 1.0;
   446 var resolution = 3;
   447 var bailout = 4.0;
   448 
   449 function Result(z, iter) 
   450 {
   451 	this.z = z;
   452 	this.iter = iter;
   453 }
   454 
   455 function complex_quad(c)
   456 {
   457 	return new Complex(Math.pow(c.real, 2) - Math.pow(c.imag, 2), 
   458 		2.0 * c.real * c.imag);
   459 }
   460 
   461 function complex_quad_value(c)
   462 {
   463 	return Math.pow(c.real, 2) + Math.pow(c.imag, 2);
   464 }
   465 
   466 function complex_add(c1, c2)
   467 {
   468 	return new Complex(c1.real + c2.real, c1.imag + c2.imag);
   469 }
   470 
   471 function complex_equal(c1, c2)
   472 {	
   473 	return (c1.real == c2.real) && (c1.imag == c2.imag);
   474 }
   475 
   476 function iterate(z, c) 
   477 {
   478 	z_quad = complex_quad(z);
   479 	return new Complex(z_quad.real + c.real, z_quad.imag + c.imag);
   480 }
   481 
   482 function test(c, max_iter) 
   483 {
   484 	var iter = 0;
   485 	var z = new Complex(0.0, 0.0);
   486 	var last_z = new Complex(-1.0, 0.0);
   487 	var quad_z = complex_quad_value(z);
   488 	
   489 	while (iter < max_iter
   490 		&& !complex_equal(z, last_z)
   491 		&& quad_z <= bailout)
   492 	{
   493 		last_z = z;
   494 		z = iterate(z, c);
   495 		quad_z = complex_quad_value(z);
   496 		iter++;
   497 	}
   498 	return new Result(quad_z, iter);
   499 }
   500 
   501 function draw(diter, dx, dy, dz, dres)
   502 {  
   503 	var canvas = document.getElementById('mandelbrot'); 
   504 	
   505 	if (canvas.getContext)
   506 	{  
   507 		zoom += dz;
   508 		var ctx = canvas.getContext('2d');
   509 
   510 		if (dres != 0)
   511 		{		
   512 			resolution = Math.max(1, resolution + dres);
   513 		}
   514 
   515 		if (diter != 0)
   516 		{
   517 			max_iter = Math.max(MIN_ITER, max_iter + diter);
   518 		}
   519 		
   520 		var red = "rgb(255, 0, 0)";
   521 		var white = "rgb(255, 255, 255)";
   522 		var width = canvas.width;
   523 		var height = canvas.height;
   524 		var dim = Math.max(width, height);
   525 		var dim_ratio = Math.round(width / height);	
   526 		var diff_c = new Complex(max_c.real - min_c.real,
   527 			max_c.imag - min_c.imag);
   528 		dx_min = diff_c.real / 100 * (dx + dz);
   529 		dx_max = diff_c.real / 100 * (dx - dz);
   530 
   531 		dy_min = diff_c.imag / 100 * (dy + dz);
   532 		dy_max = diff_c.imag / 100 * (dy - dz);
   533 
   534 		var min_inc = new Complex(dx_min * dim_ratio / 2.0, dy_min);
   535 		var max_inc = new Complex(dx_max * dim_ratio / 2.0, dy_max);
   536 		min_c = complex_add(min_c, min_inc);
   537 		max_c = complex_add(max_c, max_inc);
   538 		diff_c = new Complex(max_c.real - min_c.real,
   539 			max_c.imag - min_c.imag);
   540 		
   541 		for (var y = 0; y < height; y += resolution) 
   542 		{
   543 			for (var x = 0; x < width; x += resolution) 
   544 			{    			
   545 				var c = new Complex(min_c.real + diff_c.real / dim * x, 
   546 					min_c.imag + diff_c.imag / dim * y);  	
   547 			 	var result = test(c, max_iter);	
   548 		 		var r = Math.min(255, Math.pow(Math.max(0, 
   549 		 			(result.iter - max_iter / 20.0)), 2));
   550 				var g = Math.min(255, Math.pow(Math.max(0, 
   551 					(result.iter - max_iter / 25.0)), 2));			
   552 		 		var b = Math.min(255, Math.pow(Math.max(0, 
   553 					(result.iter - max_iter / 20.0)), 2));
   554 		 		var colour = "rgb(" + r + "," + g + "," + b + ")";
   555 		 		ctx.fillStyle = colour; 
   556 				ctx.fillRect(x, y, resolution, resolution);			
   557 			}
   558   		}
   559 	}  
   560 } 
   561 
   562 function getEventOffsetX(evt)
   563 {
   564 	if (evt.offsetX != null)
   565 		return evt.offsetX;
   566  
   567     var obj = evt.target || evt.srcElement;
   568    	setPageTopLeft(obj);
   569     return (evt.clientX - obj.pageLeft);
   570 }
   571 
   572 function getEventOffsetY(evt)
   573 {
   574 	if (evt.offsetY != null)
   575 		return evt.offsetY;
   576  
   577     var obj = evt.target || evt.srcElement;
   578    	setPageTopLeft(obj);
   579     return (evt.clientY - obj.pageTop);
   580 }
   581  
   582 function setPageTopLeft( o )
   583 {
   584     var top = 0,
   585     left = 0,
   586     obj = o;
   587  
   588     while (o.offsetParent)
   589      {
   590          left += o.offsetLeft ;
   591          top += o.offsetTop ;
   592          o = o.offsetParent ;
   593     };
   594  
   595     obj.pageTop = top;
   596     obj.pageLeft = left; 
   597 }
   598  
   599 function draw2(evt) {
   600     var iter = 0;
   601     var res = 0;
   602     var x = (getEventOffsetX(evt) - 335) / 167.5;
   603     var y = (getEventOffsetY(evt) - 140) / 70;
   604     var z = 0;
   605     draw(iter, x, y, z, res);
   606 }
   607 
   608 function update() {
   609     simulation.update();
   610     simulation.draw();
   611     if (gameoflife) {
   612         gameoflife.update();
   613         gameoflife.draw();
   614     }
   615 }
   616 
   617 function Grid(width, height, cell_size) {
   618     this.width = parseInt(width / cell_size + 0.5);
   619     this.height = parseInt(height / cell_size + 0.5);
   620     this.canvas_width = width;
   621     this.canvas_height = height;
   622     this.cell_size = cell_size;  
   623     this.cells = new Array();
   624     for (var y = 0; y < this.height; ++y) { 
   625         this.cells[y] = new Array();
   626         for (var x = 0; x < this.width; ++x) {
   627             this.cells[y][x] = new Cell(x, y, 0, this);
   628         }
   629     }
   630 }
   631 Grid.prototype.cell = function(x, y) {   
   632     return this.cells[y][x];
   633 }
   634 Grid.prototype.pick_cell = function(x, y) {
   635     var lx = parseInt(x / this.cell_size - 0.5);
   636     var ly = parseInt(y / this.cell_size - 0.5);
   637     return this.cells[ly][lx];
   638 }
   639 
   640 function Cell(x, y, value, grid) {
   641     this.x = x;
   642     this.y = y;  
   643     this.canvas_x = x * grid.cell_size;
   644     this.canvas_y = y * grid.cell_size;
   645     this.value = value;
   646     this.grid = grid;
   647     this._neighbours = new Array();
   648     this.last_mod = 0;
   649 }
   650 Cell.prototype.hash = function() {
   651     return this.x + ", " + this.y;
   652 }
   653 Cell.prototype.set_value = function(value) {
   654     this.value = value;
   655     this.last_mod = $.now();
   656 }
   657 Cell.prototype.neighbours = function() {
   658     if (this._neighbours.length) {
   659         return this._neighbours;
   660     }
   661     if (this.x > 0) {
   662         this._neighbours.push(this.grid.cell(this.x-1, this.y));
   663         if (this.y > 0) {
   664             this._neighbours.push(this.grid.cell(this.x-1, this.y-1));
   665             this._neighbours.push(this.grid.cell(this.x, this.y-1));
   666         }
   667         if (this.y + 1 < this.grid.height) {
   668             this._neighbours.push(this.grid.cell(this.x-1, this.y+1));
   669         }
   670     }   
   671     if (this.x + 1 < this.grid.width) {
   672         this._neighbours.push(this.grid.cell(this.x+1, this.y));
   673         if (this.y + 1 < this.grid.height) {
   674             this._neighbours.push(this.grid.cell(this.x+1, this.y+1));
   675             this._neighbours.push(this.grid.cell(this.x, this.y+1));
   676         }
   677         if (this.y > 0) {
   678             this._neighbours.push(this.grid.cell(this.x+1, this.y-1));
   679         }
   680     }
   681     return this._neighbours;
   682 }
   683 Cell.prototype.density = function() {
   684     var d = 0;
   685     var neighbours = this.neighbours();
   686     for (var n in neighbours) {
   687         d += neighbours[n].value;
   688     }
   689     return d;
   690 }
   691 
   692 function Simulation(canvas, w, h, cell_size, life_colour, dead_colour) {
   693     this.last_update = $.now() + 3000;
   694     this.last_draw = 0;
   695     this.last_mouse_moved = 0;
   696     this.redraw = new Array();   
   697     this.canvas = canvas;
   698     this.canvas.width = w;
   699     this.canvas.height = h;
   700     this.context = canvas.getContext("2d");    
   701     this.pos_queue = new Array();
   702     this.cell_queue = new Array();  
   703     this.cell_size = cell_size;
   704     this.life_colour = life_colour;
   705     this.dead_colour = dead_colour;
   706     this.grid = new Grid(this.canvas.width, this.canvas.height, cell_size);
   707     draw_sim_lines();
   708 }
   709 Simulation.prototype.draw = function() {
   710     var now = $.now();
   711     if (this.redraw.length) {
   712         var cell = this.redraw[0];
   713         this.redraw.shift();
   714         this.context.fillStyle = cell.value == 1 ? 
   715             this.life_colour.str() : this.dead_colour.str();
   716         this.context.fillRect(cell.canvas_x, cell.canvas_y, 
   717                               this.grid.cell_size, this.grid.cell_size);
   718         this.last_draw = $.now();
   719     }
   720 }
   721 Simulation.prototype.update = function() {
   722     var now = $.now();
   723     while (this.pos_queue.length) {
   724         var pos = this.pos_queue.pop();       
   725         if (pos[0] < this.canvas.width
   726             && pos[1] < this.canvas.height) {
   727             var cell = this.grid.pick_cell(pos[0], pos[1]);
   728             cell.set_value(1);
   729             this.cell_queue.push(cell);
   730             this.cell_queue.concat(cell.neighbours()); 
   731             this.redraw.unshift(cell);          
   732         }
   733     }
   734     if (this.last_update + 1000 > now) {
   735         return;
   736     }
   737     var changed = new Array();
   738     var next_cell_queue = new Array();
   739     while (this.cell_queue.length) {
   740         var cell = this.cell_queue.pop();       
   741         if (changed[cell.hash()]) {           
   742             continue;
   743         }
   744         var d = cell.density();
   745         if (d == 3 && cell.value == 0) {           
   746             changed[cell.hash()] = cell;            
   747             next_cell_queue = next_cell_queue.concat(cell.neighbours());               
   748         } else if (cell.value == 1) {
   749             if (d < 2 || d > 3) {              
   750                 changed[cell.hash()] = cell;                
   751                 next_cell_queue = next_cell_queue.concat(cell.neighbours());               
   752             }
   753         }       
   754     }
   755     for (var pos in changed) {
   756         var cell = changed[pos];      
   757         cell.set_value(1 - cell.value);
   758         this.redraw.push(cell);
   759     }
   760     this.cell_queue = next_cell_queue;
   761     this.last_update = now;  
   762 }
   763 Simulation.prototype.activate_cell = function(pos) {
   764     this.pos_queue.push(pos);   
   765 }
   766 Simulation.prototype.mouse_moved = function(pos) {
   767     var now = $.now();    
   768     if (this.last_mouse_moved + 50 < now) {       
   769         this.pos_queue.push(pos);      
   770         this.last_mouse_moved = now;       
   771     }
   772 }
   773 
   774