2 <title>Finite Automata
</title>
4 <b>Your browser does not support JavaScript or JavaScript is disabled.
</b>
17 var stage
= new Array(); /* status of stage - all details stored here */
18 var store
= new Array(); /* status of input */
20 var bit_x
= null; /* location of current bit in grid */
21 var bit_y
= null; /* see above - 0,0 is top left of grid */
23 var stage_rows
= 8; /* number of rows on stage */
24 var stage_cols
= 9; /* number of columns on stage */
25 var store_bit_count
= 10; /* number of bits stored in machine */
27 var input_count
= 10; /* number of input bits */
29 var grid_status
= 1; /* turn grid lines on and off */
30 var grid_size
= 50; /* size in pixels of grid lines - both X and Y */
31 var cont_selected
= null; /* current tool selected */
32 var cont_direction
= null; /* direction of current tool */
34 var canvas
; /* object id of canvas tag */
35 var canvas_input
; /* object id of input tag */
36 var canvas_control
; /* object id of control tag */
38 var control_panel
= new panel("del", GREEN
, '1', '1', '1');
40 var ctx
; /* context of canvas */
41 var input
; /* context of input canvas */
42 var control
; /* context of control canvas */
44 function panel(selected
, bitadd_col
, branch_rb_dir
, branch_yg_dir
, bus_dir
){
45 this.selected
= selected
; /* current machine part selected */
46 this.bitadd_col
= bitadd_col
; /* color of bitadd in control panel */
47 this.branch_rb_dir
= branch_rb_dir
; /* direction of red blue branch in control panel */
48 this.branch_yg_dir
= branch_yg_dir
;
49 this.bus_dir
= bus_dir
;
52 /* each square on grid has an associated block of data tied to it */
53 function grid(type
, bit
, dir
, col
){
54 this.type
= type
; /* type of machine part */
55 this.bit
= bit
; /* data in this block - if any */
56 this.dir
= dir
; /* direction this item is turned */
57 this.col
= col
; /* color of machine part (if any) */
60 /* this is used to display all bits on all active items */
61 function test_types(){
62 var i
= 0, j
= 0, k
= 1, l
= 2, col
= 1;
63 for(i
= 0; i
< stage_rows
; i
++) {
64 for(j
= 0; j
< stage_rows
; j
++){
67 case 1: stage
[i
][j
].type
= "branch"; break;
68 case 2: stage
[i
][j
].type
= "bus"; break;
69 case 3: stage
[i
][j
].type
= "input"; break;
70 case 4: stage
[i
][j
].type
= "output"; break;
72 stage
[i
][j
].type
= "bitadd";
73 if(col
== 5) { col
= 1; }
75 case 1: stage
[i
][j
].col
= RED
; break;
76 case 2: stage
[i
][j
].col
= GREEN
; break;
77 case 3: stage
[i
][j
].col
= YELLOW
; break;
78 case 4: stage
[i
][j
].col
= BLUE
; break;
86 case 1: stage
[i
][j
].bit
= RED
; break;
87 case 2: stage
[i
][j
].bit
= GREEN
; break;
88 case 3: stage
[i
][j
].bit
= YELLOW
; break;
89 case 4: stage
[i
][j
].bit
= BLUE
; break;
96 /* create all output data structures and draw inital values on screen */
97 function init_stage(){
100 /* create blank grid data structure */
101 for(x
= 0; x
< stage_cols
; x
++) {
102 stage
[x
] = new Array();
103 for(y
= 0; y
< stage_rows
; y
++) {
104 stage
[x
][y
] = new grid('', '', '', '');
107 stage
[5][0].type
= "input";
108 stage
[5][8].type
= "output";
112 /* moves automata to next position */
113 function next_move(){
114 // if input is empty - get next bit
118 // determine what type of machine part we are on
119 switch(stage
[bit_x
][bit_y
].type
){
136 alert("Unknown entity.");
141 function move_getbit(){
142 stage
[5][0].bit
= store
.shift();
145 bit_x
= 5; bit_y
= 0;
148 function move_bitadd(){
149 // TODO: temove this switch statement
150 store
.push(stage
[bit_x
][bit_y
].col
);
151 stage
[bit_x
][bit_y
+1].bit
= stage
[bit_x
][bit_y
].bit
;
152 stage
[bit_x
][bit_y
].bit
= "";
153 draw_tile(bit_x
,bit_y
);
154 draw_tile(bit_x
,bit_y
+1);
159 // determine which direction to move and move there
160 function move_branch(){
161 if(stage
[bit_x
][bit_y
].bit
== BLUE
){
162 stage
[bit_x
+1][bit_y
].bit
= stage
[bit_x
][bit_y
].bit
;
163 stage
[bit_x
][bit_y
].bit
= "";
164 draw_tile(bit_x
,bit_y
);
165 draw_tile(bit_x
+1,bit_y
);
167 } else if (stage
[bit_x
][bit_y
].bit
== RED
){
168 stage
[bit_x
-1][bit_y
].bit
= stage
[bit_x
][bit_y
].bit
;
169 stage
[bit_x
][bit_y
].bit
= "";
170 draw_tile(bit_x
,bit_y
);
171 draw_tile(bit_x
-1,bit_y
);
174 stage
[bit_x
][bit_y
+1].bit
= stage
[bit_x
][bit_y
].bit
;
175 stage
[bit_x
][bit_y
].bit
= "";
176 draw_tile(bit_x
,bit_y
);
177 draw_tile(bit_x
,bit_y
+1);
184 // 1= DOWN; 2= LEFT; 3 = UP; 4 = LEFT
185 switch(stage
[bit_x
][bit_y
].dir
){
199 alert("Unknown bus direction.");
201 stage
[bit_x
+x
][bit_y
+y
].bit
= stage
[bit_x
][bit_y
].bit
;
202 stage
[bit_x
][bit_y
].bit
= "";
203 draw_tile(bit_x
,bit_y
);
204 bit_y
+= y
; bit_x
+= x
;
205 draw_tile(bit_x
,bit_y
);
208 function move_input(){
210 // look for entity next to input.
211 // walk around clockwise until one is found
212 if(stage
[bit_x
][bit_y
+1].type
){
213 stage
[bit_x
][bit_y
+1].bit
= stage
[bit_x
][bit_y
].bit
;
214 stage
[bit_x
][bit_y
].bit
= "";
215 draw_tile(bit_x
,bit_y
);
216 draw_tile(bit_x
,bit_y
+1);
218 } else { alert("Cannot continue: No connection to input."); }
221 // if we are on an output, remove bit and move to an input
222 function move_output(){
223 stage
[bit_x
][bit_y
].bit
= "";
224 draw_tile(bit_x
,bit_y
);
225 bit_x
= null; bit_y
= null;
228 /* set initial values for input */
229 function init_input(){
233 store
.push(GREEN
); /* add to end */
234 //store.unshift("red"); /* add to front */
235 //store.pop(); /* remove from end */
236 //store.shift(); /* remove from front */
239 /* draw faint gridlines on stage - used as a guide for the user */
240 function draw_grid(){
241 var x
, y
; /* current x and y position */
242 var offset
= 10; /* x and y maximum offset (far bottom or side of the window) */
243 ctx
.strokeStyle
= "#ccc";
245 /* draw vertical lines */
246 for(x
= grid_size
, y
= 0, offset
= window
.innerWidth
; x
< window
.innerWidth
; x
= x
+ grid_size
){
249 ctx
.lineTo(x
,y
+offset
);
253 /* draw horizontal lines */
254 for(x
= 0, y
= grid_size
, offset
= window
.innerWidth
; y
< window
.innerWidth
; y
= y
+ grid_size
){
257 ctx
.lineTo(x
+offset
,y
);
264 move through each grid in stage and draw contents.
265 this function can be used to refresh the screen at any time.
267 function draw_stage(){
269 /* loop through all grids on stage, drawing contents */
270 for(x
=0; x
< stage_cols
; x
++){
271 for(y
= 0; y
< stage_rows
; y
++){
277 /* delete item from stage */
278 function stage_delete(){
282 /* add current item to stage at clicked location */
283 function stage_add(){
287 /* select this item as next item to be placed */
288 function stage_select(){
293 function init_form(){
295 /* initalize canvas element for use */
296 canvas
= document
.getElementById("stage");
297 ctx
= canvas
.getContext("2d");
299 canvas_input
= document
.getElementById("input");
300 input
= canvas_input
.getContext("2d");
302 canvas_control
= document
.getElementById("control");
303 control
= canvas_control
.getContext("2d");
305 /* get width and height of window and set stage (canvas) with it. */
306 canvas
.height
= window
.innerHeight
-125;
307 canvas
.width
= window
.innerWidth
- 45;
308 if(grid_status
){draw_grid(); }
316 /* returns coordinates of canvas in pixels */
317 function cnvs_get_coordinates(canvas
, e
){
318 var x_offset
= canvas
.offsetLeft
;
319 var y_offset
= canvas
.offsetTop
;
320 if(canvas
== 'undefined'){ alert("Canvas parameter is undefined"); }
321 x_offset
= e
.clientX
- x_offset
;
322 y_offset
= e
.clientY
- y_offset
;
323 //document.getElementById("xycoordinates").innerHTML="Coordinates: (" + x_offset + "," + y_offset + ")";
324 return [x_offset
,y_offset
];
327 /* canvas has been clicked find out which grid and make correct change to square if needed. */
328 function cont_clicked(e
){
329 var coords
= cnvs_get_coordinates(canvas_control
, e
);
330 var x_pos
= Math
.floor(coords
[0] / grid_size
);
331 var y_pos
= Math
.floor(coords
[1] / grid_size
);
332 //alert(x_pos + "," + y_pos);
335 if(x_pos
>= 12 && x_pos
<= 14){
336 control_panel
.selected
= "bitadd";
337 if(x_pos
== 13 && y_pos
== 2){ control_panel
.bitadd_col
= GREEN
; }
338 if(x_pos
== 12 && y_pos
== 1){ control_panel
.bitadd_col
= BLUE
; }
339 if(x_pos
== 13 && y_pos
== 0){ control_panel
.bitadd_col
= YELLOW
; }
340 if(x_pos
== 14 && y_pos
== 1){ control_panel
.bitadd_col
= RED
; }
343 // branch (red and blue)
344 if(x_pos
>= 6 && x_pos
<= 8){
345 control_panel
.selected
= "branch_rb";
346 if(x_pos
== 7 && y_pos
== 2){ control_panel
.branch_rb_dir
= 1; }
347 if(x_pos
== 6 && y_pos
== 1){ control_panel
.branch_rb_dir
= 2; }
348 if(x_pos
== 7 && y_pos
== 0){ control_panel
.branch_rb_dir
= 3; }
349 if(x_pos
== 8 && y_pos
== 1){ control_panel
.branch_rb_dir
= 4; }
352 // branch (yellow and greeen)
353 if(x_pos
>= 9 && x_pos
<= 11){
354 control_panel
.selected
= "branch_yg";
355 if(x_pos
== 10 && y_pos
== 2){ control_panel
.branch_yg_dir
= 1; }
356 if(x_pos
== 9 && y_pos
== 1){ control_panel
.branch_yg_dir
= 2; }
357 if(x_pos
== 10 && y_pos
== 0){ control_panel
.branch_yg_dir
= 3; }
358 if(x_pos
== 11 && y_pos
== 1){ control_panel
.branch_yg_dir
= 4; }
362 if(x_pos
>= 3 && x_pos
<= 5){
363 control_panel
.selected
= "bus";
364 if(x_pos
== 4 && y_pos
== 2){ control_panel
.bus_dir
= 1; }
365 if(x_pos
== 3 && y_pos
== 1){ control_panel
.bus_dir
= 2; }
366 if(x_pos
== 4 && y_pos
== 0){ control_panel
.bus_dir
= 3; }
367 if(x_pos
== 5 && y_pos
== 1){ control_panel
.bus_dir
= 4; }
371 if(x_pos
>= 0 && x_pos
<= 2){
372 control_panel
.selected
= "delete";
378 /* move through tape and draw bits */
379 function draw_tape(){
380 var i
= 0; var x
= 50;
381 input
.fillStyle
= "#f00";
382 input
.clearRect(0,0,579,100);
383 while(i
< store
.length
){
385 input
.fillStyle
= store
[i
];
386 input
.arc(x
,25,20,0,TWO_PI
,0);
389 input
.strokeStyle
= "#000";
392 input
.arc(x
,25,20,0,TWO_PI
,0);
400 function draw_control(){
401 //control.scale(.75,.75);
402 control
.fillStyle
= "#fff";
403 control
.fillRect(0,0,779,220)
408 control
.translate(grid_size
, grid_size
);
413 control
.translate(grid_size
*4, grid_size
);
414 draw_bus(control
, control_panel
.bus_dir
);
417 control
.translate(grid_size
, 0); /* right */
418 draw_bit(control
, GRAY
);
422 control
.translate(-grid_size
, 0); /* left */
423 draw_bit(control
, GRAY
);
427 control
.translate(0, grid_size
); /* bottom */
428 draw_bit(control
, BLACK
);
432 control
.translate(0, -grid_size
); /* top */
433 draw_bit(control
, GRAY
);
436 /* branch icon red and blue */
439 control
.translate(grid_size
*7, grid_size
);
440 draw_branch(control
, "branch_rb", control_panel
.branch_rb_dir
);
443 control
.translate(grid_size
, 0); /* right */
444 if(control_panel
.branch_rb_dir
== 4) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
448 control
.translate(-grid_size
, 0); /* left */
449 if(control_panel
.branch_rb_dir
== 2) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
453 control
.translate(0, grid_size
); /* bottom */
454 if(control_panel
.branch_rb_dir
== 1) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
458 control
.translate(0, -grid_size
); /* top */
459 if(control_panel
.branch_rb_dir
== 3) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
462 /* branch icon green and yellow */
465 control
.translate(grid_size
*3, 0);
466 draw_branch(control
, "branch_yg", control_panel
.branch_yg_dir
);
469 control
.translate(grid_size
, 0); /* right */
470 if(control_panel
.branch_yg_dir
== 4) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
474 control
.translate(-grid_size
, 0); /* left */
475 if(control_panel
.branch_yg_dir
== 2) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
479 control
.translate(0, grid_size
); /* bottom */
480 if(control_panel
.branch_yg_dir
== 1) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
484 control
.translate(0, -grid_size
); /* top */
485 if(control_panel
.branch_yg_dir
== 3) { draw_bit(control
, BLACK
); } else { draw_bit(control
, GRAY
); }
490 control
.translate(grid_size
*3, 0);
491 draw_bitadd(control
, control_panel
.bitadd_col
);
494 control
.translate(grid_size
, 0); /* right */
495 draw_bit(control
, RED
);
499 control
.translate(-grid_size
, 0); /* left */
500 draw_bit(control
, BLUE
);
504 control
.translate(0, grid_size
); /* bottom */
505 draw_bit(control
, GREEN
);
509 control
.translate(0, -grid_size
); /* top */
510 draw_bit(control
, YELLOW
);
513 /* highlight currenly selected tool */
514 control
.setTransform(1, 0, 0, 1, 0, 0);
515 switch(control_panel
.selected
){
517 draw_control_highlight(grid_size
*12,0,grid_size
*3,grid_size
*3);
520 draw_control_highlight(grid_size
*6,0,grid_size
*3,grid_size
*3);
523 draw_control_highlight(grid_size
*9,0,grid_size
*3,grid_size
*3);
526 draw_control_highlight(grid_size
*3,0,grid_size
*3,grid_size
*3);
529 draw_control_highlight(0,0,grid_size
*3,grid_size
*3);
534 function draw_control_highlight(x_start
, y_start
, x_stop
, y_stop
){
535 control
.lineWidth
= 5;
536 control
.strokeStyle
= "#000";
537 control
.strokeRect(x_start
,y_start
,x_stop
,y_stop
);
541 /* (re)draws any map tile on grid */
542 function draw_tile(x
,y
){
544 ctx
.translate(grid_size
* x
, grid_size
* y
);
545 switch (stage
[x
][y
].type
){
547 draw_bitadd(ctx
, stage
[x
][y
].col
);
550 draw_branch(ctx
, stage
[x
][y
].type
, stage
[x
][y
].dir
);
553 draw_branch(ctx
, stage
[x
][y
].type
, stage
[x
][y
].dir
);
556 draw_bus(ctx
, stage
[x
][y
].dir
);
564 default: clear_square(ctx
);
566 if(stage
[x
][y
].bit
){ draw_bit(ctx
, stage
[x
][y
].bit
); }
570 function draw_trash(canvas
){
571 canvas
.strokeStyle
= "#000";
572 canvas
.lineCap
= "round";
573 canvas
.lineWidth
= 12;
575 canvas
.moveTo(10,10);
576 canvas
.lineTo(grid_size
-10,grid_size
-10);
579 canvas
.moveTo(10, grid_size
-10);
580 canvas
.lineTo(grid_size
-10, 10);
583 canvas
.strokeStyle
= "#f00";
584 canvas
.lineWidth
= 8;
586 canvas
.moveTo(10,10);
587 canvas
.lineTo(grid_size
-10,grid_size
-10);
590 canvas
.moveTo(10, grid_size
-10);
591 canvas
.lineTo(grid_size
-10, 10);
595 /* draws small bit of correct color on grid */
596 function draw_bit(canvas
, color
){
597 canvas
.fillStyle
= "#f00";
599 canvas
.fillStyle
= color
;
600 canvas
.arc(25,25,10,0,TWO_PI
,0);
603 canvas
.strokeStyle
= "#000";
604 canvas
.lineWidth
= 2;
606 canvas
.arc(25,25,10,0,TWO_PI
,0);
610 /* draw gray square with black outline */
611 function draw_input(canvas
){
612 canvas
.lineWidth
= 1;
613 canvas
.strokeStyle
= "#000";
614 canvas
.strokeRect(0,0,grid_size
,grid_size
);
615 canvas
.fillStyle
= "#aaa";
616 canvas
.fillRect(0,0,grid_size
,grid_size
);
619 function drawSpirograph(ctx
,R
,r
,O
){
627 var x2
= (R
+r
)*Math
.cos(i
*Math
.PI
/72) - (r+O)*Math.cos(((R+r)/r
)*(i
*Math
.PI
/72))
628 var y2
= (R
+r
)*Math
.sin(i
*Math
.PI
/72) - (r+O)*Math.sin(((R+r)/r
)*(i
*Math
.PI
/72))
633 } while (x2
!= R
-O
&& y2
!= 0 );
637 function draw_output(canvas
){
638 canvas
.fillStyle
= "#fff";
639 canvas
.fillRect(2,2,grid_size
-2,grid_size
-2); /* clear grid */
641 canvas
.translate(grid_size
/2,grid_size/2);
642 canvas
.strokeStyle
= "#d80";
643 canvas
.lineWidth
= 2;
644 drawSpirograph(canvas
,9,2,7);
645 canvas
.translate(-(grid_size
/2),-(grid_size/2));
648 /* a bus moves bits from one location to another */
649 function draw_bus(canvas
, dir
){
651 clear_square(canvas
);
652 canvas
.fillStyle
= "#fff";
653 canvas
.fillRect(2,2,grid_size
-2,grid_size
-2); /* clear grid */
655 canvas
.lineWidth
= 2;
656 canvas
.fillStyle
= "#aaa";
657 canvas
.strokeStyle
= "#000";
661 canvas
.translate(grid_size
,0);
665 canvas
.translate(grid_size
,grid_size
);
669 canvas
.translate(0,grid_size
);
670 canvas
.rotate(-PI
/2);
675 if(i
== 1) { canvas
.save(); canvas
.translate(0, grid_size
/2); }
678 canvas
.lineTo(grid_size
/2,grid_size/2);
679 canvas
.lineTo(grid_size
,0);
680 canvas
.lineTo(grid_size
/2,grid_size/4);
686 canvas
.lineTo(grid_size
/2,grid_size/2);
687 canvas
.lineTo(grid_size
,0);
688 canvas
.lineTo(grid_size
/2,grid_size/4);
691 if(i
== 1) { canvas
.restore(); }
696 /* tiles branch movement of each bit */
697 function draw_branch(canvas
, type
, dir
){
698 var color_1
, color_2
;
699 if(type
== "branch_rb"){
702 } else if(type
== "branch_yg"){
706 alert("Incorrect color sent to draw_branch().");
712 canvas
.translate(grid_size
,0);
716 canvas
.translate(grid_size
,grid_size
);
720 canvas
.translate(0,grid_size
);
721 canvas
.rotate(-PI
/2);
726 canvas
.lineWidth
= 1;
727 canvas
.fillStyle
= color_1
;
730 canvas
.lineTo(grid_size
/2,grid_size/2);
731 canvas
.lineTo(0,grid_size
);
736 canvas
.fillStyle
= "#000";
739 canvas
.lineTo(grid_size
/2,grid_size/2);
740 canvas
.lineTo(grid_size
,0);
745 canvas
.fillStyle
= color_2
;
747 canvas
.moveTo(grid_size
,0);
748 canvas
.lineTo(grid_size
/2,grid_size/2);
749 canvas
.lineTo(grid_size
,grid_size
);
754 canvas
.fillStyle
= "#aaa";
756 canvas
.moveTo(0,grid_size
);
757 canvas
.lineTo(grid_size
/2,grid_size/2);
758 canvas
.lineTo(grid_size
,grid_size
);
765 function draw_bitadd(canvas
, color
){
767 clear_square(canvas
);
770 canvas
.strokeStyle
= color
;
771 canvas
.lineWidth
= 10;
773 canvas
.strokeStyle
= "#000";
774 canvas
.lineWidth
= 15;
777 canvas
.moveTo(grid_size
/2,0);
778 canvas
.lineTo(grid_size
/2,grid_size
);
783 canvas
.moveTo(0, grid_size
/2);
784 canvas
.lineTo(grid_size
,grid_size
/2);
789 canvas
.strokeStyle
= "#000";
790 canvas
.lineWidth
= 1;
793 canvas
.lineTo(0,grid_size
);
794 canvas
.lineTo(grid_size
,grid_size
);
795 canvas
.lineTo(grid_size
,0);
801 /* clear this square by setting area to white */
802 function clear_square(canvas
){
803 canvas
.fillStyle
= "#fff";
804 canvas
.fillRect(1,1,grid_size
-2,grid_size
-2);
807 /* canvas has been clicked find out which grid and make correct change to square if needed. */
808 function cnvs_clicked(e
){
809 var coords
= cnvs_get_coordinates(canvas
, e
);
810 var x_pos
= Math
.floor(coords
[0] / grid_size
);
811 var y_pos
= Math
.floor(coords
[1] / grid_size
);
812 if(stage
[x_pos
][y_pos
].type
== "input" || stage
[x_pos
][y_pos
].type
== "output") { return; }
814 stage
[x_pos
][y_pos
].type
= control_panel
.selected
;
815 switch(control_panel
.selected
){
817 stage
[x_pos
][y_pos
].dir
= 1;
818 stage
[x_pos
][y_pos
].col
= control_panel
.bitadd_col
;
821 stage
[x_pos
][y_pos
].dir
= control_panel
.branch_rb_dir
;
824 stage
[x_pos
][y_pos
].dir
= control_panel
.branch_yg_dir
;
827 stage
[x_pos
][y_pos
].dir
= control_panel
.bus_dir
;
830 stage
[x_pos
][y_pos
].type
= "";
833 draw_tile(x_pos
,y_pos
);
838 <style type=
"text/css">
842 <body onLoad=
"init_form();">
843 <center><h2>Finite Automata
</h2></center>
844 <div id=
"topsection">
847 <button onClick='reset();'
><img src=
"http://opentextbook.info/icons/32x32/resultset_first.png" title=
"Restart" alt=
"Restart"></button>
849 <button onClick='step_back();'
><img src=
"http://opentextbook.info/icons/32x32/resultset_previous.png" title=
"Step Back" alt=
"Step Back"></button>
851 <button onClick='next_move();'
><img src=
"http://opentextbook.info/icons/32x32/resultset_next.png" title=
"Next Step" alt=
"Next Step"></button>
853 <button onClick='run();'
><img src=
"http://opentextbook.info/icons/32x32/resultset_last.png" title=
"Run" alt=
"Run"></button>
857 <button onClick='halt();'><img src="http://opentextbook.info/icons/32x32/cancel.png" title="Halt Execution" alt="Halt Execution"></button>
860 <button disabled><img src="http://opentextbook.info/icons/32x32/disk.png" title="Save Code" alt="Save Code"></button>
862 <button onClick='display_docs();'><img src="http://opentextbook.info/icons/32x32/book_open.png" title="Open Documentation" alt="Open Documentation"></button>
868 <!--<div id="xycoordinates">Coordinates:</div>-->
869 <canvas id=
"input" width=
"579" height=
"120"></canvas><br>
870 <canvas id=
"control" width=
"779" height=
"220" onClick=
"cont_clicked(event);"></canvas>
871 <canvas id=
"stage" width=
"579" height=
"770" onclick=
"cnvs_clicked(event);">
872 Your browser does not support HTML5 Canvas.