/*
    Raul Parolari, October 1, 2007: library to animate graphic elements.

    This is a library for rails architecture demos
*/

/* new section added in lib_rails_arch_common */
var drawing = null;
var debug   = 0;
var conn_debug = 0;

/* 
    Configurable data (set in main cgi file)
    - Canvas:                CANVAS_WIDTH, CANVAS_FULL_HEIGHT
    - Coordinates AC::Base : X_FIRST_CLASS_BOX, Y_CLASS_BOX  
*/

var NO_ARROW      = 0;   // false

// These are values to allow indenting of segments (for pleasant effects)
var LEN_SEG_IN_BOX_INST = 10; // indent segment (leaving Inst box) inside box 
var LEN_SEG_IN_BOX      = 20; // indent segment (leaving a box) inside the box 

var SEG_OFFSET_TOP      = 10; /* when connecting to the Top of a box, connect
                                 a bit under the box */
var INDENT_VERT_CONN    = 10;  // cnec vertic 2 boxes, line a bit outside them
var INDENT2_VERT_CONN   = 20;  // cnec vertic 2 boxes, line further outside

// Independent Constants for Positioning
var BOX_WIDTH_ENLARG = 10;   // needed to allow decent titles (10px: just 1 char!)
var BOX_WIDTH    = 100 + BOX_WIDTH_ENLARG;
var BOX_HEIGHT   = 140;   // was 160

// Proxy
var PROXY_WIDTH  = BOX_WIDTH;
var PROXY_HEIGHT = 60;
var MOD_WIDTH    = 110;
var MOD_HEIGHT   = 60;

/* empty horiz space between boxes: we enlarged by 10px the boxes but we don't
   want to enlarge the whole drawing; so we end up reducing distance between
   boxes by the 10px
*/
var EMPTY_HORIZ  =  EMPTY_HORIZ_WISHED - BOX_WIDTH_ENLARG;   

var GAP_PROXY_TO_MOD = BOX_HEIGHT - PROXY_HEIGHT - MOD_HEIGHT;

// Module's distance relative to its class
var X_DELTA_MOD_FROM_CLASS = BOX_WIDTH + EMPTY_HORIZ;     
var X_DELTA_MOD_FROM_MOD   = BOX_WIDTH + EMPTY_HORIZ;
// Y of module relative to proxy same as relative to class
var Y_DELTA_MOD_FROM_PROXY = PROXY_HEIGHT + GAP_PROXY_TO_MOD;
var Y_DELTA_PROXY_FROM_MOD = - Y_DELTA_MOD_FROM_PROXY;
var Y_DELTA_MOD_FROM_CLASS = Y_DELTA_MOD_FROM_PROXY;


// Now the tough part: ANONYMOUS modules relative to left edge classes
var X_ANON_PROXY_TO_CLASS = 0;   // was: BOX_WIDTH + EMPTY_HORIZ;
var X_ANON_MOD_TO_CLASS   = 0;
                                                    
var Y_DELTA_ANON_MOD_FROM_CLASS = - (MOD_HEIGHT + 40);

var Y_DELTA_ANON_PROXY_FROM_CLASS = Y_DELTA_ANON_MOD_FROM_CLASS -
                                    Y_DELTA_MOD_FROM_PROXY;

   // distance of ActView::Base from ActCntr::Base
var X_DELTA_VIEW_FROM_CONTR = PROXY_WIDTH + EMPTY_HORIZ + PROXY_WIDTH  + EMPTY_HORIZ;
var Y_DELTA_VIEW_FROM_CONTR = Y_DELTA_ANON_PROXY_FROM_CLASS;

var X_FIRST_META_BOX = X_FIRST_CLASS_BOX;
var Y_META_BOX       = Y_CLASS_BOX + BOX_HEIGHT + EMPTY_VERT_CLASS_META;

// Distance between 2 Controllers
X_DIST_CONTROLLERS = 2 * (PROXY_WIDTH + EMPTY_HORIZ);

var X_USER_CONTROLLER_BOX = X_FIRST_CLASS_BOX - (2 * X_DIST_CONTROLLERS);
var Y_USER_CONTROLLER_BOX = Y_CLASS_BOX;
//var Y_MAX_TUTOR           = Y_USER_CONTROLLER_BOX -20; // not finished

/********* Instance View, Instance Controller, Anonymous View:  NOT USED  ***********/

/* Anon View */
W_ANON_VIEW_CLASS = 90;
// H (height) as per normal classes


/* Instances */	
X_INST_VIEW  =  30;
Y_INST_VIEW  = 190;
W_INST  =  80;
H_INST  = 100;

X_INST_CNTR  = 150;
Y_INST_CNTR  = 360;
/* Instance Contr */
X_INST_CONTROLLER  = X_INST_VIEW;
Y_INST_CONTROLLER  = Y_USER_CONTROLLER_BOX + 20;


/********* END of Instance View, Instance Control, AnonView: NOT USED  ***********/


/* CSS defines classes like: .cl_super { left: 20px, top: 40px }  defining 
   eg start positions of pointers

  Javascript cannot retrieve attributes of Css classes (although it can insted
  dynamically read the 'style.top, style.left.." of an ELEMENT!).
  Thus, hardcode these values:  
*/
var HALF_H_PTR = 10;    //  __       _ 
                        //    |____  _ horiz arrow drawn at 1/2 height Pointr Box
                        //  __|  
   
/*** class instances ***/
var Y_KLASS_INST      = 20 + HALF_H_PTR;  /* was 30px */
var Y_VIEW_TO_CNTRL = 60 + HALF_H_PTR;
var Y_CNTRL_TO_VIEW = 60 + HALF_H_PTR;

/*** end of class instances ***/

var Y_SUPER       = 15 + HALF_H_PTR;   /* was 20 + */
var Y_KLASS       = 40 + HALF_H_PTR; 
//var Y_IV_TBL    = 36 + HALF_H_PTR; 
var Y_MHM         = 60 + HALF_H_PTR; 
var Y_METHODS     = 90 + HALF_H_PTR; 

var Y_TO_METH_FROM_MINI_PROXY = 50;  // in the mini proxy, conn.out_to_methods
var Y_METHODS_MINI_MODULE = 20; // + HALF_H_PTR
// End CSS derived values


/****************** Definitions for Drawing Speed ******************/

/* Definitions to draw fast or slow
   Pixels Per Frame: PPF 
   Important: Try to position Boxes at Y multiple of PPF (both for slow or fast),
   as we determine the number of frames to move an element dividing the distance
   by PPF! And the  number of frames MUST be an INTEGER (read below story).

   We were bitten by the "Module Proxy Box" fantastic problem: it ended 
   up a few pixels misaligned with Class Boxes (bringing us to insanity):
   - Proxy box appears descending from the top of the diagram; distance was 90
     pixels and PPF_SLOW was 5, and 90/5.0 = 18.0 (perfect). But PPF_FAST=20, and
     90/20.0 = 4.5, which, rounded to 4 introduced a significant error. 

   - So, the algorithm to compute the movement put it slightly higher. The
     solution was either to bring the box from the left (like all other boxes,
	 where the distances were always multiple of 20) OR change the vertical
	 distance to 100 pixels. 
   - We adopted the second solution, that meant increasing setting the height of
     the tutorial boxes to 90... 
     (because the visible height is = css_height + 2 * padding).
*/
	
var PPF_SLOW = 10;
var PPF_FAST = 10; 
var PIX_PER_FRAME  = PPF_SLOW;  

// Milliseconds Per Frame: MPF (we preferred not to go under 20msec)
var MPF_SLOW = 50;  
var MPF_FAST = 20; 
var MSEC_PER_FRAME = MPF_SLOW;  
// End of definitions to draw fast or slow

function draw_fast() {
   PIX_PER_FRAME  = PPF_FAST;
   MSEC_PER_FRAME = MPF_FAST;
}
function draw_slow() {
   PIX_PER_FRAME  = PPF_SLOW;
   MSEC_PER_FRAME = MPF_SLOW;
}

function get_pix_per_frame() {
   return PIX_PER_FRAME;
}
/****************** End of Definitions for Drawing Speed ******************/


/****************** Definitions for CONNECTION TYPES ******************/

// Based on these types, we retrieve the right dscr properties for endpoints
var REF_TO_INST      = 0;
var INST_TO_CLASS    = 1;

// there are several connections 'super to super': they get same value
// this is still valid (I think!), as we kept 'super' at same level in all boxes
var CLASS_TO_CLASS   = 2;
var META_TO_META     = CLASS_TO_CLASS;   // same: through super pointer
var PROXY_TO_CLASS   = CLASS_TO_CLASS;
var CLASS_TO_PROXY   = CLASS_TO_CLASS;
var PROXY_TO_PROXY   = CLASS_TO_CLASS;

// vertical connections
var CLASS_TO_META    = 3;  // klass ptr to meta box

var PROXY_TO_MODULE  = 4;  // this one is 'logical': comprised of physical next 2 
var PROXY_TO_MODULE_IV_TBL  = 5;
var PROXY_TO_MODULE_METHODS = 6;
var MINI_PROXY_TO_MODULE = 7;
var CLASS_TO_MHM_MOD = 8;  // class to master helper module

var OUT1_TO_INC1 =  9;  // mongrel -> cgi,    cgi -> mongrel


var VIEW_TO_CNTRL_INST = 12;  // instance view to instance controller
var CNTRL_TO_VIEW_INST = 13;  // instance view to instance controller

/******************  END Definitions for CONNECTIONS ******************/ 


/*** Connection Anon Child View via Virtual Points 

function connect_AnonCVp_to_AVBasep_via_VP() {
	
   var info = 'connect_AnonCVp_to_AVBasep_via_VP: ';
  
   var dscr_ucp = hsh_ds['UserControllerp'];
   var 
   var vp_left = { x: }
}

    END Connection Anon Child View via Virtual Points ****/


/******** 'Generic boxes' : Mongrel, CGI and Routing ********/
var TYPE_MONGREL    = 1;
var TYPE_CGI        = 2;
var TYPE_DISPATCHER = 3;
var TYPE_ROUTING    = 4;

// all these participants have their positions set relative to mongrel coords
// and the 'gaps
var TOTAL_HEIGHT_COLUMN = 120;

X_MONGREL = 10;
Y_MONGREL = 10;
W_MONGREL = 110;
H_MONGREL = 130;
var COORD_MONGREL = { x : X_MONGREL, y : Y_MONGREL, w : W_MONGREL, h : H_MONGREL }

var X_GAP_MONGREL_CGI  = 30;

// var Y_GAP_MONGREL_DISP = 20;
// var Y_GAP_CGI_ROUTING  = 20;

var W_CGI  = 90;
var H_CGI  = 40; 
var Y_CGI  = Y_MONGREL + (H_MONGREL - H_CGI);

// X_INST_CONTROLLER  = X_INST_VIEW;
// Y_INST_CONTROLLER  = Y_USER_CONTROLLER_BOX + 60;

var X_DISPATCHER = COORD_MONGREL.x;
var Y_DISPATCHER = Y_META_BOX;
var W_DISPATCHER = COORD_MONGREL.w;
var H_DISPATCHER = BOX_HEIGHT ; // var HEIGHT_DISPATCHER = TOTAL_HEIGHT_COLUMN  - COORD_MONGREL.h - Y_GAP_MONGREL_DISP;

var X_GAP_DISP_ROUTING = X_GAP_MONGREL_CGI;

var X_ROUTING  = X_DISPATCHER + W_DISPATCHER + 40;
var Y_ROUTING  = Y_DISPATCHER;
var H_ROUTING  = 60;
var W_ROUTING  = 80;

function get_mongrel_coord() {
   return COORD_MONGREL;
}

function get_genericBox_coord(type) {
	
   var coord_mongrel = COORD_MONGREL;
   var coord = {};

   switch (type) {
	
      case TYPE_MONGREL:
	     coord = COORD_MONGREL;
      break;

      case TYPE_DISPATCHER:
	     coord = {
		    x: X_DISPATCHER, //coord_mongrel.x
		    y: Y_DISPATCHER, // coord_mongrel.y + coord_mongrel.h + Y_GAP_MONGREL_DISP,
		    w: coord_mongrel.w,
		    h: H_DISPATCHER
	     }
	  break;
	
	
      case TYPE_CGI:
	     coord = {
		    x: coord_mongrel.x + coord_mongrel.w + X_GAP_MONGREL_CGI,
		    y: Y_CGI,
		    w: W_CGI,
		    h: H_CGI
	     }
	  break;
	
      case TYPE_ROUTING:
	     coord = {  // in relation to CGI
			x: X_ROUTING,   // coord_mongrel.x + coord_mongrel.w + X_GAP_DISP_ROUTING,
			y: Y_ROUTING,   // coord_mongrel.y + HEIGHT_CGI + Y_GAP_CGI_ROUTING, 
			w: W_ROUTING,
			h: H_ROUTING
		 }
	  break;
   }
	return coord;
} // get_genericBox_coord


function update_HttpServer_with_request_response(dscr) {
   //alert('update_mongrel_with_cgi_data=' + dscr.elem.innerHTML);

   dscr.elem.innerHTML += 
		      	          '<p style="top: 35px; left: 25px">request</p>'  +
			              '<p style="top: 50px; left: 25px">response</p>';  		
}

function update_dispatcher_with_controller_data(dscr) {

   dscr.elem.innerHTML += '<p style="top: 15px; left: 10px">controller</p>';  		
}

function update_mongrel_with_cgi_data(dscr) {

   dscr.elem.innerHTML += '<p style="top: 102px; left: 40px">cgi</p>';  		
}

function update_mongrel_with_rails_handler(dscr) {

   dscr.elem.innerHTML += 
    '<div style="position: absolute; top: 90px; left: 10px; width: 90px; height: 35px; background: aqua">' +
        '<p class="cl_title_rails_handler">RailsHandlr</p>' +
    '</div>';
}

function update_mhm_with_helper_method(dscr) {
	
	dscr.elem.innerHTML += 

	  '<p style="top: 10px; font: 14px">def user<br />' +
	     '&nbsp;cntr.send(user)<br />' +
	  'end</p>';
}

function get_genericBox_content(name, type) {
   var info = 'get_genericBox_content' + ', name=' + name + ', type=' + type;

   var title   = '';
   var content = '';

   switch (type) {
     case TYPE_MONGREL:
	    title = name;

        content = '<p class="cl_title_mongrel">' + title + '</p>';
        content += 
    '<div style="position: absolute; top: 25px; left: 10px; width: 90px; height: 45px; background: aqua">' +
                '<p class="cl_title_http_server">HttpServer</p>' +
            '</div>';

/*
	    content = '<p class="cl_title_mongrel">' + title + '</p>' +	
    '<div style="position: absolute; top: 25px; left: 5px; width: 90px; height: 45px; background: aqua">' +
                '<p class="cl_title_http_server">HttpServer</p>' +
            '</div>' +

    '<div style="position: absolute; top: 90px; left: 5px; width: 90px; height: 35px; background: aqua">' +
                '<p class="cl_title_rails_handler">RailsHandlr</p>' +
            '</div>';
*/
            // position: relative; top: 45px			
/*
'<p style="top: 20px; left: 10px; width: 80px; height: 40px; background: aqua; text-align: center">' +
'HttpServer</p>'
*/
        break;

     case TYPE_DISPATCHER:
	    title = name;
	    content = '<p class="cl_title_dispatcher">' + title + '</p>';
        break;

     case TYPE_CGI:
	    title = 'CGIWrapper';
	    content = '<p class="cl_title_cgi">' + title + '</p>';
	    break;
	
     case TYPE_ROUTING:
	    title = name;
	    content = '<p class="cl_title_routes">' + title + '</p>';
        break;

     default:
        alert(info + ', unknown type!');
     break;
   }

   return [title, content];
} // get_mongrel_content


/*** BEGIN define connections for generic boxes ***/

// all values are relative to top of the box 

// mongrel -> cgi
var Y_MONGREL_OUT1 = 110;
var Y_MONGREL_INC1 = Y_MONGREL_OUT1 + 10;

// cgi to mongrel
var Y_SMALLBOX_INC1  = Y_MONGREL_OUT1 - (H_MONGREL - H_CGI);
var Y_SMALLBOX_OUT1  = Y_MONGREL_INC1 - (H_MONGREL - H_CGI);

// dispatcher -> routing; 
var Y_DISP_OUT1 = 20;
var Y_DISP_INC1 = Y_DISP_OUT1 + 10;

//var Y_????_OUT2 = 85;
//var Y_DISP_INC2 = 95;

function define_genericBox_connections(name, type, dscr) {
   var info = 'define_genericBox_connections' + ', name=' + name + ', type=' + type;

   var x_inbox = 0; 

   switch (type) {
      case TYPE_MONGREL:
	     dscr.conn.out1 = function() {
		    var style = dscr.elem.style;
            var x     = parseInt(style.left) + parseInt(style.width);
            var y     = parseInt(style.top)  + Y_MONGREL_OUT1;
            return [x, y, x_inbox];
         }

         dscr.conn.inc1 = function() {
			 var style = dscr.elem.style;
	         var x     = parseInt(style.left) + parseInt(style.width);
	         var y     = parseInt(style.top)  + Y_MONGREL_INC1;
	         return [x, y, x_inbox];	 
         }

      break;

      case TYPE_DISPATCHER:
	     dscr.conn.out1 = function() {
		    var style = dscr.elem.style;
            var x     = parseInt(style.left) + parseInt(style.width);
            var y     = parseInt(style.top)  + Y_DISP_OUT1;
            return [x, y, x_inbox];
         }

         dscr.conn.inc1 = function() {
			 var style = dscr.elem.style;
	         var x     = parseInt(style.left) + parseInt(style.width);
	         var y     = parseInt(style.top)  + Y_DISP_INC1;
	         return [x, y, x_inbox];	 
         }
      break;
        
      case TYPE_CGI:
      case TYPE_ROUTING:
	     dscr.conn.out1 = function() {
		    var style = dscr.elem.style;
            var x     = parseInt(style.left);
            var y     = parseInt(style.top)  + Y_SMALLBOX_OUT1;
            return [x, y, x_inbox];
         }

         dscr.conn.inc1 = function() {
			 var style = dscr.elem.style;
	         var x     = parseInt(style.left);
	         var y     = parseInt(style.top)  + Y_SMALLBOX_INC1;
	         return [x, y, x_inbox];	 
         }
	  break;

      default:
         alert(info + ', unknown type!');
      break;
   } // end of switch

   return dscr;
} // define_genericBox_connections

/******* END define connections for generic boxes ******/
/********* End of Mongrel, Cgi, Routing *********/


/**********  Section Manage content of boxes (eg. m_h_m) ***********/

// Methods shown in box for module ClassMethods 

function get_classBox_content(className, type, methods) {

  var title = className;

  var class_vs_inst = type == TYPE_CLASS? "inst" : 'class';


  var content = '<p class="cl_title">' + title + '</p>'  +
	        '<p class="cl_super">super</p>'  +
            '<p class="cl_klass">klass</p>';
            
  if (methods.length !=0) { 
     content += '<p class="cl_meth_list">' + class_vs_inst + ' methods:</p>'; 
 
     // complete content with mehods 
     for (var i=0; i < methods.length; i++) { 
        cl_meth  = 'cl_meth' + i;  // see css defs cl_meth0, cl_meth1 ..
        content += '<p class="' + cl_meth + '">-' + methods[i] + '</p>';
     }	
  }
  return [title, content];
} // get_classBox_content     
							  
                              
function update_class_with_mhm(dscr, notify) {
   var info = 'update_class_with_mhm=' + dscr.elem.innerHTML;

   var mhm_label = '<p class="cl_mhm">m_help_mod</p>';   

   // if (className.match(/View.*Base/)) { mhm_label = ''; }
  
   dscr.elem.innerHTML += mhm_label;  	

   if (notify) notify();	
}

function update_class_with_inherited_w_h(dscr, notify) {
   var info = 'update_class_inherited_w_h=' + dscr.elem.innerHTML;

   // '<p class="cl_inherited_w_h">inherited_<br>&nbsp;&nbsp;with_helper</p>';

   var method_str = '<p class="cl_inherited_w_h"> -inherited_w_h</p>';

   // add method to the end of innerHTML
   dscr.elem.innerHTML += method_str; 	

   if (notify) notify();	
} // update_class_with_inherited_w_h

/**********  End Section dynamic update content of  boxes  ***********/


/* Dynamic positioning: boxes must be built sequentially, from left to right
   array of 2 types of boxes: class and meta_class/

   The idea was to lay out boxes sequentially and to have an administrator to
   keep track of them; boxes were laid in 2 rows, one for Class and the other
   for MetaClass (vertically aligned per Class). Later, we thought to interpose
   new boxes (ruby modules) between existing ones (increasing their distance),
   and this became more complex to maintain (in other words: a mess).

   In this app, we just use this mechanism for Classes, that are regularly
   disposed; for other boxes, we use ad-hoc allocation.
*/
var adm_coord_boxes = {
   info: 'adm_coord_boxes',

   matrix_box_names : [  [ ], [ ]  ],

   // offsets of next box 
   x_next_box :  [ X_FIRST_CLASS_BOX,  X_FIRST_META_BOX ],
   y_next_box :  [ Y_CLASS_BOX, Y_META_BOX ],  // values fixed (in each row)

   // number of next box (-1 when none): 0 for 
   // TYPE_CLASS = 0; TYPE_META  = 1; TYPE_BOTH  = 2;

   seq_box_nr : [ -1,  -1],   // is it useful?

   go_to_initial_state: function() {
      this.matrix_box_names = [ [], [] ];
      this.x_next_box =  [ X_FIRST_CLASS_BOX,  X_FIRST_META_BOX ];
      this.y_next_box =  [ Y_CLASS_BOX, Y_META_BOX ];
      this.seq_box_nr =  [ -1,  -1];
   },
   
   gen_next_coord : function(name, type) {

      var info = this.info + '.gen_next_coord: ';
	  //alert(info + 'name=' + name + ', type=' + type);
	
      if ((arguments.length != 2) || (type >= this.seq_box_nr.length)) {
            alert('next_box.coord_class: type? ' + type);
            return;
      }
      this.matrix_box_names[type].push(name);

      next = {};
      if (name.match(/C.*Base/)) {
	     next.x = (type == TYPE_CLASS)?  X_FIRST_CLASS_BOX : X_FIRST_META_BOX;
	     next.y = (type == TYPE_CLASS)?  Y_CLASS_BOX : Y_META_BOX;
      }

      else if (name.match(/View.*Base/)) {
	     
	     next.x = X_FIRST_CLASS_BOX + X_DELTA_VIEW_FROM_CONTR; 
	  
	     next.y = (type == TYPE_CLASS)?  Y_CLASS_BOX + Y_DELTA_VIEW_FROM_CONTR:       
	                                     Y_META_BOX  + Y_DELTA_VIEW_FROM_CONTR;	  
      }

      else if (name.match(/Appl/)) {
	     
	     next.x = X_FIRST_CLASS_BOX - X_DIST_CONTROLLERS; 
	  
	     next.y = (type == TYPE_CLASS)?  Y_CLASS_BOX : Y_META_BOX;	  
      }

      else if (name.match(/User/)) {
	     
	     next.x = X_FIRST_CLASS_BOX - 2 * X_DIST_CONTROLLERS; 
	  
	     next.y = (type == TYPE_CLASS)?  Y_CLASS_BOX : Y_META_BOX;	  
      }

      else if (name.match(/Anon/)) {  // the Anon class with superclass View
	     
	     next.x = X_FIRST_CLASS_BOX - 2 * X_DIST_CONTROLLERS - (BOX_WIDTH + EMPTY_HORIZ); 
	  
	     next.y = (type == TYPE_CLASS)?  Y_CLASS_BOX + Y_DELTA_VIEW_FROM_CONTR:       
	                                     Y_META_BOX  + Y_DELTA_VIEW_FROM_CONTR;	  
      }

      else {
	     alert(info + "have no instructions for this box");
	     return false;
      }

      //alert(info + this.get_curr_box_name(type));
      return next;
   },

   get_curr_box_name: function(type) {
      var info = this.info + '.get_curr_box_name: ';

      if (! (type < this.matrix_box_names.length) ) {
         alert (info + 'type invalid= ' + type);
         return null;
      }
      var arr_ln = this.matrix_box_names[type].length;

      //alert(info + this.matrix_box_names[type][arr_ln-1]);
      return this.matrix_box_names[type][arr_ln-1];
   },

   get_prev_box_name: function(type) {
      var info = this.info + '.get_prev_box_name: ';

      // get previous box nr
      if (! (type < seq_box_nr.length) ) {
         alert (info + 'type invalid= ' + type);
         return null;
      }
      var prev_box_nr =  this.seq_box_nr[type] -1;

      // get previous box name
      if (! (prev_box_nr >= arr_names.length) ) {
         alert(info + 'invalid prev_box_nr= '  + prev_box_nr);
         return null;
      }
      return matrix_box_names[type][prev_box_nr];
   }, 

   end: null
} // adm_coord_boxes 


/******
function issue_connect_below_box(boxName, conn_type, 
                                src_name, dst_name, cond_trigger) {

  if (arguments.length =! 5) { 
     alert('issue_connect: expects 5 params, got ' + arguments.length);
     return false;
  }

  var info = 'issue_connect: src_name=' + src_name +  
                          ', dst_name=' + dst_name + ': ';

  var dscr_src = hsh_ds[src_name];
  var dscr_dst = hsh_ds[dst_name];

  if (! (dscr_src && dscr_dst) ) {
     var result = dscr_src? 'only src' :
                  dscr_dst? 'only dst' : 'neither src nor dst'; 
     alert(info + 'found hsh_ds[] entries in ' + result);
     return false;
  } 
  else if (conn_debug) { 
     var arr   = dscr_src.conn.to_inst(); 
     var style = dscr_src.elem.style;
     alert(info + 'style.left=' + style.left + ', ' + 
              '(x+w)arr[0]=' + arr[0] + ', arr[1]=' + arr[1]);
  }

  return connect_src_to_dst(conn_type, dscr_src, dscr_dst, src_name, dst_name,
                            cond_trigger);
} // issue_connect_below_box()
*******/

function issue_connect(conn_type, src_name, dst_name, notify) {
   
   var info = 'issue_connect: src_name=' + src_name +  
              ', dst_name=' + dst_name + ': ';

   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }

   var dscr_src = hsh_ds[src_name];
   var dscr_dst = hsh_ds[dst_name];

   if (! (dscr_src && dscr_dst) ) {
      var result = dscr_src? 'only src' :
                   dscr_dst? 'only dst' : 'neither src nor dst'; 
      alert(info + 'found hsh_ds[] entries in ' + result);
      return false;
   } 
   else if (conn_debug) { 
      var arr   = dscr_src.conn.to_inst(); 
      var style = dscr_src.elem.style;
      alert(info + 'style.left=' + style.left + ', ' + 
               '(x+w)arr[0]=' + arr[0] + ', arr[1]=' + arr[1]);
   }

   return connect_src_to_dst(conn_type, dscr_src, dscr_dst, src_name, dst_name,
                             notify);
} // issue_connect()


/****************************      DRAWING      *****************************/
   
/*   
    each object returns a descriptor hash, that is added to a global hash
    of descriptors hsh_ds, using as key the name of the object.
*/

// Global Variables   
// Constants 
TYPE_CLASS = 0;
TYPE_META  = 1;
TYPE_BOTH  = 2;

// descriptors
var hsh_ds = { };     // the { } is necessary here: creates the hash object!

var INTERVAL = 50;
 // End Global Variables

var MSEC_IN_SEC = 1000;

var pause = {
   // normal pause duration is 1 second
   second   : MSEC_IN_SEC,
   
   set_fast : function() {
      this.second = 0.04 * MSEC_IN_SEC;   // superfast
    //  this.second =  0.2 * MSEC_IN_SEC; // normal
   },

   set_normal : function() {
      this.second = MSEC_IN_SEC;
   },
   
   end : null
};


/***** section that was before the only one in lib_rails_arch_common ****/


function draw_class(className, type, methods, notify, options) {
   var info="draw_class: " + className + ', ' + type;

   if (!options) options = { };

  /* if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }
  */

   var dscr  = build_class_dscr(className, type, methods, options);
   dscr.elem = drawing.box(dscr);

   if (notify) notify(); // notify (if exists) starts a timer; we continue

   return dscr;
} // draw_class


function build_generic_dscr(name, type, options) {

   var info     = 'build_generic_dscr: name= ' + name;
   /*
   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }
   */

   if (!options) options = { };

   var box_label = options.label ? options.label : name;
 
   // this is the Css class giving color to the box
   var cssClass = 'box' + name;
   //alert(cssClass);
   
   var arr_title_cont = get_genericBox_content(name, type);
   var title   = arr_title_cont[0];
   var content = arr_title_cont[1];

   var coord = get_genericBox_coord(type);

   var dscr = { 
                  x: coord.x,  y: coord.y,  
                  w: coord.w,  h: coord.h, 
                  name:       box_label,   // 'ruby class' : eg; A, A'
                  title:      title,
                  id:         'id_' + name,
                  cssClass:   cssClass,
                  content:    content,
                  conn   :    { },
                  elem:       null
               };

   define_genericBox_connections(name, type, dscr);
   return dscr;
} // build_generic_dscr


function draw_generic_box(name, type, notify, options) {
   var info="draw_generic_box: ";

   /*
   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }
   */

   if (!options) options = { };

   var dscr  = build_generic_dscr(name, type, options);
   dscr.elem = drawing.box(dscr);

   if (notify) notify(); // notify (if exists) starts a timer; we continue

   return dscr;
} // draw_generic_box


function build_mini_proxy_dscr(proxyName, x0, y0) {
	
	var info  = 'build_mini_proxy_dscr: proxyName= ' + proxyName + ' : ';
	
	var title = 'Proxy ';      // 'Proxy M'? but it's a Proxy 'Class'..  

	// for future: examine styles embedded rather than in Css file
	// this would allow the top for example to be derived from Y_SUPER
	// '<p  class="cl_super" style="left: 6px; top: 16px; ">super</p>'
	
    // fixed part of content
	var content = '<p  class="cl_title">' + title + '</p>'  +
	              '<p  class="cl_super">super</p>' +
	              '<p  class="mini_proxy_meth">' + 'methods</p>';
	
   var dscr =  {
                  x       :  x0,           
                  y       :  y0, 
                  w       :  PROXY_WIDTH,   
                  h       :  PROXY_HEIGHT,
                  name    :  proxyName,                 
                  title   :  title,
                  id      :  'id_proxy_' + proxyName,  // id_proxy_M
                  cssClass:  'boxProxy',               // boxProxy
                  content :  content,
                  conn    :  { },   // connections
                  elem    :  null
               };
	
   // Arriving Connections:
   // - from previous class, arrives at left edge, at 'super' level 
   dscr.conn.inc_from_super =  function() {
                              var x_inbox = 0;  
                              var style = dscr.elem.style;
                              var x     = parseInt(style.left);
                              var y     = parseInt(style.top) + Y_SUPER;
                              if (conn_debug) alert(info + ', inc_from_super: x=' 
                                 + x + ', left= ' + style.left + ', y=' + y);

                              return [x, y, x_inbox];
                           }

   // Connection to next class/proxy, super level
   dscr.conn.out_to_super =  
               function() {
                  // coordinates right edge of the box for super
                  var style = dscr.elem.style;
                  var x     = parseInt(style.left) + parseInt(style.width);
                  var y     = parseInt(style.top)  + Y_SUPER;
                  var x_inbox   = LEN_SEG_IN_BOX;
                  //var conn_debug = 1;
                  if (conn_debug) alert(info + ', out_to_super: x(start)=' + x +  
                                    ', left= ' + style.left + ', y=' + y);
                  return [x, y, x_inbox];
               }   

   // conn to module, at method level 
   dscr.conn.out_to_methods =  function() {
               var style = dscr.elem.style;
               // ensure vertical connection, x null
               var x        = parseInt(style.left) + parseInt(style.width); 
               var x_inbox  = LEN_SEG_IN_BOX;
               var y        = parseInt(style.top) + Y_TO_METH_FROM_MINI_PROXY;

               if (conn_debug) alert(info + ', out_to_methods: x=' 
                  + x + ', left= ' + style.left + ', y=' + y);

               return [x, y, x_inbox];
   }

   return dscr;
  
} // build_mini_proxy_dscr


function _draw_mini_proxy(proxy_name, x0, y0, notify) {
	
   var info="draw_mini_proxy: " + proxy_name;
   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }

   var dscr  = build_mini_proxy_dscr(proxy_name, x0, y0);
   dscr.elem = drawing.box(dscr);

   if (notify) notify(); // notify at most starts a timer; we continue

   return dscr;
} // _draw_mini_proxy


function draw_proxy_in_place_for_module(dscr_mod, report) {
   var x_mod    = parseInt(dscr_mod.elem.style.left);
   var y_mod    = parseInt(dscr_mod.elem.style.top);

   // now draw: convention: proxy name = its module name + 'p'
   var proxy_name = dscr_mod.name + 'p';  
   var x_proxy    = x_mod;
   var y_proxy    = y_mod + Y_DELTA_PROXY_FROM_MOD;

   var dscr = _draw_mini_proxy(proxy_name, x_proxy, y_proxy, report);
   return dscr;
} // draw_proxy_in_place_for_module


function set_coord_of_inst_of_class(dscr) {
	
   /*
      do not reassign the hash, as in:  dscr = { x: 40, y: 40, ..}, as it
	  allocates a new object, different from caller's (which remains the same); 
	  instead assign the properties. 
   */

   if (dscr.name == 'view') {
	  dscr.x = X_INST_VIEW;   dscr.y = Y_INST_VIEW;  
	  dscr.w = W_INST;    dscr.h = H_INST;
   }
   else {
	  dscr.x = X_INST_CNTR;  dscr.y = Y_INST_CNTR;  
	  dscr.w = W_INST;   dscr.h = H_INST;
   }
 
} // get_coord_inst_of_class


function draw_inst_of_class(inst_name, notify) {
   var info     = "draw_inst_of_class: inst_name=" + inst_name;

   var cssString = convert_name_to_cssString(inst_name);   // 'view' -> 'view'

   var ref_name = inst_name == 'view' ? 'controller' : 'r.template';

   var dscr = { name:  inst_name, 
	            title: inst_name,
	            id:       'id_' + cssString, // 'id_inst_V'
                cssClass: 'cl_' + cssString, // 'cl_inst_V'
 
	            content:  '<p  class="cl_title_inst">' + inst_name + '</p>' +
	                      '<p  class="cl_klass_inst"> klass</p>' +
	                      '<p  class="cl_view_cntrl_inst">' + ref_name + '</p>' ,  

	            // Connections:    
	            elem:     null        // last line wout comma
	          };  // dscr  
	
   set_coord_of_inst_of_class(dscr);  
	
   dscr.elem = drawing.box(dscr);
   dscr.conn = { };

   dscr.conn.out_to_class =         // connection to class from instance starts here
      function() {      
         var style   = dscr.elem.style;
         var x       = parseInt(style.left) + parseInt(style.width);
         var x_inbox = LEN_SEG_IN_BOX_INST; 
      
         var y = parseInt(style.top)  + Y_KLASS_INST;
         //alert(info + 'x= ' + x + ', w=' + style.width + ', x_inbox=' + x_inbox);
         return [x, y, x_inbox];
   }

   if (inst_name == 'view') {
	  // departing connection view->controller: 
      dscr.conn.out_to_cntrl =         
         function() {
            var style   = dscr.elem.style;
            var x       = parseInt(style.left) + parseInt(style.width); // right edge
            var x_inbox = LEN_SEG_IN_BOX_INST; 
         
            var y = parseInt(style.top)  + Y_VIEW_TO_CNTRL; 
            //alert(info + 'x= ' + x + ', w=' + style.width + ', x_inbox=' + x_inbox);
            return [x, y, x_inbox];
      };

      dscr.conn.inc_from_cntrl =  
         // incoming connection controller->view: at the lower side of box, in center     
         function() {
            var style   = dscr.elem.style;
            var x       = parseInt(style.left) + parseInt(style.width)/2;  // middle
            var x_inbox = 0; 
         
            var y = parseInt(style.top)  + dscr.h; // bottom
            //alert(info + 'x= ' + x + ', w=' + style.width + ', x_inbox=' + x_inbox);
            return [x, y, x_inbox];
      };
   }
   else if (inst_name == 'controller') {
	  dscr.conn.out_to_view =         
         function() {
            var style   = dscr.elem.style;
            var x       = parseInt(style.left); // left edge
            var x_inbox = 0; 

            var y = parseInt(style.top)  + Y_CNTRL_TO_VIEW;
            //alert(info + 'x= ' + x + ', w=' + style.width + ', x_inbox=' + x_inbox);
            return [x, y, x_inbox];
      };

      dscr.conn.inc_from_view =  // incoming connection view->controller: left edge      
         function() {
            var style   = dscr.elem.style;
            var x       = parseInt(style.left);  // left edge
            var x_inbox = 0; 
         
            var y = parseInt(style.top)  + SEG_OFFSET_TOP; // was Y_VIEW_CNTRL_INST;
            //alert(info + 'x= ' + x + ', w=' + style.width + ', x_inbox=' + x_inbox);
            return [x, y, x_inbox];
      };
   }
   else (alert(info + ": inst_name has unexpected value"));
	            
   if (notify) notify();  

   return dscr;
} // draw_inst_of_class
	

	
function build_mini_module_dscr(modName, x0, y0, methods) {
	
   var info="build_mini_module_dscr: " + modName;
   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }

   var title = convert_modName_to_title(modName);  // remove %xyz from title
   // fixed part of content
   var content = '<p class="cl_title">' + title + '</p>';
   
   if (methods.length !=0) {
	 /* content += '<p class="mini_mod_meth">methods:</p>'; */

     // complete content with mehods 
     for (var i=0; i < methods.length; i++) { 
       cl_meth  = 'mini_mod_meth' + i;  // see css defs cl_meth0, cl_meth1 ..
       content += '<p  class="' + cl_meth + '">-' + methods[i] + '</p>';
     }
     //alert(content);
   }

   var dscr =  {
                  x       :  x0,           
                  y       :  y0, 
                  w       :  MOD_WIDTH,   
                  h       :  MOD_HEIGHT,
                  name    :  modName,                  // M
                  title   :  'Module '    + modName,  // Module M
                  id      :  'id_module_' + modName,  // id_module_M
                  cssClass:  'boxModule',              // cl_module
                  content :  content,
                  conn    :  { },    // connections
                  elem    :  null
               };

   // Connections
   var conn_debug = 0;

   // Arriving Connections: 

   // conn from klass ptr in Class, arrives to top of meta class, right edge

   var x_inbox = 0; // let any case override it

   dscr.conn.inc_from_proxy  =  function() {
                           var style = dscr.elem.style;
                           // ensure vertical connection, leaving x null
                           // parseInt(style.left) + parseInt(style.width);
                           var x     = null; 
                           var y     = parseInt(style.top) + Y_METHODS_MINI_MODULE;
                           if (conn_debug) alert(info + ', inc_from_proxy: x=' 
                                + x + ', left= ' + style.left + ', y=' + y);
                           return [x, y, x_inbox];
                        }

   dscr.conn.inc_from_inst  =  function() {
                           var style = dscr.elem.style;
                           var x     = parseInt(style.left);
                           var y     = parseInt(style.top)  + SEG_OFFSET_TOP;
                           return [x, y, x_inbox];
                        }

   dscr.conn.inc_from_mhm_ptr  =  function() {
                           var style = dscr.elem.style;
                           var x  = parseInt(style.left); 
                           var y  = parseInt(style.top) + SEG_OFFSET_TOP;
                           if (conn_debug) alert(info + ', inc_from_mhm_ptr: x=' 
                                + x + ', left= ' + style.left + ', y=' + y);
                           return [x, y, x_inbox];
                        }

   return dscr;
} // build_mini_module_dscr


function draw_mini_module(modName, methods, x0, y0, notify) {
	
   var info="draw_mini_module: " + modName;
   if (! lib_check_args(info, arguments.length, arguments.callee.length)) {
      return false;
   }

   var dscr  = build_mini_module_dscr(modName, x0, y0, methods);
   dscr.elem = drawing.box(dscr);
   /*
	   dscr.elem.style.clip = "rect(0px 0px 0px 0px)";
	   var height = dscr.elem.style.height;
	   //width = dscr.elem.style.width;
	   numFrames = 22; msec_per_frame = 20; width_incr = 5;
	   //if (modName.match(/Base/)) alert(width);
	   animateCSS(dscr.elem, numFrames, 100, //MSEC_PER_FRAME, 
	                { 
	                   clip: function(el, frame, time) { 
	                      var curr_clip = "rect(-10px " + width_incr*frame 
										  +"px " + height + " auto)";
	                      return curr_clip;
	                   }
	                },
	                function() {  // whendone
	                   if (notify) notify();    
	                }
	             );

	   if (notify) notify();  // notify at most starts a timer; we can continue
   */

   if (notify) notify(); // notify (if exists) starts a timer; we continue

   return dscr; 
} // draw_mini_module


function build_proxy_dscr(proxyName, x0, y0) {

   var info  = 'build_proxy_dscr: proxyName= ' + proxyName + ' : ';

   var title = 'Proxy ';      // 'Proxy M'? but it's a Proxy 'Class'..  
   // fixed part of content
   var content = '<p  class="cl_title">' + title + '</p>'  +
               '<p  class="cl_super">super</p>'  +
               '<p  class="cl_iv_tbl">iv_tbl</p>'  +
               '<p  class="cl_klass">klass</p>'  +
               '<p  class="cl_meth">' + 'methods</p>';

   var dscr =  {
                  x       :  x0,           
                  y       :  y0, 
                  w       :  PROXY_WIDTH,   
                  h       :  PROXY_HEIGHT,
                  name    :  proxyName,                 
                  title   :  title,
                  id      :  'id_proxy_' + proxyName,  // id_proxy_M
                  cssClass:  'boxProxy',               // boxProxy
                  content :  content,
                  conn    :  { },   // connections
                  elem    :  null
               };

   // Arriving Connections:
   // - from previous class, arrives at left edge, at 'super' level 
   dscr.conn.inc_from_super =  function() {
                              var x_inbox = 0;  
                              var style = dscr.elem.style;
                              var x     = parseInt(style.left);
                              var y     = parseInt(style.top) + Y_SUPER;
                              if (conn_debug) alert(info + ', inc_from_super: x=' 
                                 + x + ', left= ' + style.left + ', y=' + y);

                              return [x, y, x_inbox];
                           }
   
   //var conn_debug = 0;
   // Departing connections:
   // Connection to next class, super level
   dscr.conn.out_to_super =  
               function() {
                  // coordinates right edge of the box for super
                  var style = dscr.elem.style;
                  var x     = parseInt(style.left) + parseInt(style.width);
                  var y     = parseInt(style.top)  + Y_SUPER;
                  var x_inbox   = LEN_SEG_IN_BOX;
                  //var conn_debug = 1;
                  if (conn_debug) alert(info + ', out_to_super: x(start)=' + x +  
                                    ', left= ' + style.left + ', y=' + y);
                  return [x, y, x_inbox];
               }

   // conn to module, at iv_tbl level 
   dscr.conn.out_to_iv_tbl =  function() {
               var style = dscr.elem.style;
               var x        = parseInt(style.left) + parseInt(style.width); 
               var x_inbox  = LEN_SEG_IN_BOX;
               var y        = parseInt(style.top) + Y_IV_TBL;

               if (conn_debug) alert(info + ', out_to_iv_tbl: x=' 
                  + x + ', left= ' + style.left + ', y=' + y);
               var indent = INDENT2_VERT_CONN;
               return [x, y, x_inbox, indent];
   }

   // conn to module, at method level 
   dscr.conn.out_to_methods =  function() {
               var style = dscr.elem.style;
               // ensure vertical connection, x null
               var x        = parseInt(style.left) + parseInt(style.width); 
               var x_inbox  = LEN_SEG_IN_BOX - 15;
               var y        = parseInt(style.top) + Y_METHODS;

               if (conn_debug) alert(info + ', out_to_methods: x=' 
                  + x + ', left= ' + style.left + ', y=' + y);

               return [x, y, x_inbox];
   }

   return dscr;
} //  build_proxy_dscr

