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