              /* Javascript library by Raul Parolari, Febr 2007 */

var app_debug = 0;

function jslib_activate_debug() {
   lib_activate_debug();
   app_debug = 1;
}

/* This is the library for application specific code */


/*
    sel_menus is a hash of hashes of arrays of hashes; the reason is:
    - outer hash is keyed on the main subject (Css, Javascript, Ajax)
    - 1st inner hash is keyed on 'options' vs 'optgroups'
    - the array is the array of options or optgroups
    - the last hash (exists for optgroups) is keyed on 'label' (subdirectory)
      vs 'options' (equiv to file names)
*/
var sel_menu =     
  {
    Javascript: { options: [  ], 
                  optgrps: [ { label: "Pattern Match" ,
                               options: [ "Methods" ]
                             },
                           /*
                             { label: "Pseudo Classes",
                               options: [ "Line Equation" ]
                             },
                             { label: "Cookies",
                               options: [ "Cookie Class" ]
                             },
                            */
                             { label: "Security",
                               options: [ "Origin Policy" ]
                             }
 
                           ]
                },

    Css : { // options: [ .. ],  
            optgrps: [  { label: "Formatting",      // becomes subdir 
                          options: [ "Basic Properties", 
                                     "Vertical Surprises"]  // becomes filename (lcased)
                        },
                        { label: "Float", 
                          options: [ "Basics", "Nested" ]
                        },
                        { label: "Tables", 
                          options: [ "Border Spacing", "Table Formats" ]
                        }
                     ]
          },

     Ruby  : { // options: [ .. ],
               // optgrps: [ { } ]
             },

     Rails : { 
             },
  
     end: null   // to avoid trailing comma curse when modifying
    };

var qt = "'";
var dbq = '"';

/*
   This function is called upon page loading; 
   0) finds the select element for left_navigation (div enclosing the select)
   1) picks up the document.URL and converts old style URI paths (eg, user 
      with old bookmarks) like Topic/cgi-bin/xyz.cgi to Topic/xyz
   2) splits URL in 3 components (for cgi, they are 2, Css/Jscript have 3)
      at most (the second or third can be empty)
   3) calls find_option_to_select() to find option with matching URI with
      those components and sets the 'selected' attribute of that option's
      element to true
*/
function config_select_navig() {
   var info = 'config_select_navig: ';

   var divNav_el = document.getElementById("left_navigation");
   if (! divNav_el) { return; }

   var sel_el = divNav_el.getElementsByTagName('select')[0];
   if (! sel_el) { alert("no select box"); return }

   //alert('location=' + window.location + ', document.URL=' + document.URL)

   // first exclude query (everyting, not greedy, until a ?)from path
   var pat = /^(.*?)[?].*$/;
   var url = document.URL.replace(pat, "$1");
   
   /* if this is a cgi script, and user calls it with true name, convert it 
      to the (non-cgi) name listed in the select box, ie:
      http:://raul/Ruby/cgi-bin/demo.cgi --> http:://raul/Ruby//Ruby/demo

      pattern:      everything_1 until /cgi-bin/ + everything_2 until .cgi
      replacement:  1/2
   */
   var pattern = /(.*?)[/]cgi-bin[/](.*?).cgi/;
   url = url.replace(pattern, "$1/$2");            
                    /* In Ruby, we would need: string.sub(pattern, ""\1/\2")
                       (as #{$1} and #{$2} would be substituted before the pattern
					   match starts). In Javascript, there is no interpolation
					   in single or double quoted strings, so the $1 and $2 are
					   left intact until the replace is executed! We can do the
					   the same with: str.sub(pattern) {|match| block })
					
					     pattern = %r|(.*?)[/]cgi-bin[/](.*?).cgi| 
					     str="http:://raul/Ruby/cgi-bin/demo.cgi"
					     str.sub!(pattern) { |m| "#{$1}/#{$2}" } 
					                # =>"http:://raul/Ruby/demo"
                     */
   //alert(url);
   // now extract components
   var arr = get_url_components(url);
   if (! arr) return false; // url wout dirs in path: we should not be here

   var maindir = arr[0];        // eg: "Ruby" 
   var subdir  = arr[1];        // eg: "cgi-bin"  or "env"  (new Urls)
   var fname   = arr[2];        // eg: "env.cgi"

   // finds option containing the path (set its selected attr to true, which
   // will allow to find the selected index with sel_el.selectedIndex) 
   find_option_to_select(sel_el, maindir, subdir, fname);
   if (app_debug) alert(info + "maindir= " + maindir + ", subdir=" + subdir + ", fname=" + fname);

   // sets handler for the select elem, which will set the window Url as per
   // user selection
   sel_el.onchange = select_onchange_handler;
   
} /* config_select_navig() */

/*
   this function changes document's URL as per user selection  
*/
function select_onchange_handler() {
   var sel_el = this;
   var selIdx = sel_el.selectedIndex;
  
   var option = sel_el.options[selIdx];
   if (app_debug) {
      alert(   "select_onchange_handler(): index= " + selIdx 
             + ", text= "  + option.text  
             + ", value= " + option.value); 
   }

   // document.URL is read-only! use window.location 
   window.location.href = option.value;
} // select_onchange_handler() 


/*
    Central routine of the file: builds the select element for the main
    directory (extracted from the Url of current document): eg: Css
*/
function build_select_box(maindir) {

   // STEP 0: verify that maindir exists in sel_menu configuration
   if (!sel_menu[maindir]) {
      alert("central sel_menu wout entry for= " + maindir); 
      return null;
   }

   // STEP 1: make select element: sel_el
   var mk_sel = dom_maker("select");
   var sel_el = mk_sel( { name: maindir} ); 

   /* 
      STEP 1.1: define variables to use for invokations of constructor
      'new Option', instead of writing silly arguments 'true/false'
   */
   var defaultSelected = true;   // for top element, then switch to false
   var selected        = false;

   // STEP 2: add the top level option (eg: "Css") 
   var opt_txt  = maindir;
   var opt_val = "/" + maindir + "/index.html";
   var option = new Option(opt_txt,  opt_val, defaultSelected, selected);
   // set class to make this entry slightly more prominent
   option.className = "main_title";
   sel_el.add(option, null);

   /* if options (or optgrps) array is missing, we set it to empty array [] so 
       that 'length' will be defined (= 0). This allows to invoke loops wout 
       if then elses and silly indentations of code.
   */
   var options = sel_menu[maindir].options || [];  
   var optgrps = sel_menu[maindir].optgrps || []; 

   if (!options.length && !optgrps.length) { 
       // possible: means that maindir theme exists but is empty
       if (app_debug) { 
          alert("build_select_box: no config for" + qt+maindir+qt + "!");
       }
       return sel_el;
   }

   defaultSelected = false;
   // STEP 3: now the top options (for files in main directory, if any)
   // Also: if given url does not contain subdir, we search selectedIndex 
   //       
   for (var i=0; i < options.length; i++) {
      var opt_txt = options[i];                     // eg: "Topic X"
      var opt_val = cvt_to_filename(opt_txt);       // "topic_x"
      opt_val     = "/" + maindir + "/" + opt_val;  // "Css/topic.x"
      // add option 
      var option = new Option(opt_txt, opt_val, defaultSelected, selected);
      sel_el.add(option, null);
   }

   // STEP 4: now the optgrps
   var path_main = '/' + maindir + '/';   // subdir not known yet
   for (var i=0; i < optgrps.length; i++) {

      var optgrp   = optgrps[i];
      // add subdir to path (stringify subdir:  "Aa Bb" -> "Aa_Bb"
      path_subdir = path_main + cvt_to_dirname(optgrp.label) + '/';  

      var options  = optgrp.options;
      if (!options)  continue; // opt group planned but not configured 

      // add this optgrp to the select box 
      var mk_optgrp   = dom_maker("optgroup");
      var optgrp_el   = mk_optgrp({ name: optgrp.label}, optgrp.label);
      sel_el.add(optgrp_el, null);   // sel_el.appendChild(optgrp_el)
     
      // add the sub_options to the select box (note: index 'i' in use!)
      for (var k=0; k < options.length; k++) {
         var opt_txt = options[k];
         var opt_val = cvt_to_filename(opt_txt);
         opt_val     = path_subdir + opt_val + ".html";
         // add option  
         var option = new Option(opt_txt, opt_val, defaultSelected, selected);
         sel_el.add(option, null);
      }
   }

   // Note: options.length counts all options, but not optgrps (they are fake)
   //       Thus nrlines in menu = options.length  + optgrps.length
   var nrlines = sel_el.options.length + optgrps.length; 
   nrlines    += Math.round(optgrps.length * 0.3); // css pad for optgrp: 0.3em 
   sel_el.size = 1; //nrlines;

   return sel_el;
} // build_select_box()


/*
    Based on Url, find option to be selected in the select element:
    Example of parameters:   maindir   subdir   fname
                              Css      Float    x.html
                              Ruby     cgi-bin  env     
    - Builds the path from params given
    - Loops on select option looking for the option that contains path
    - If it finds the option, sets its 'selected' attr to true
*/
function find_option_to_select(sel_el, maindir, subdir, fname) {

   var path = '/' + maindir  + '/' ;
   path    += subdir? subdir + '/' : "";
   path    += fname;
   //alert('find_option_to_select=' + path);

   var index = -1;
   for (var i=0; i < sel_el.options.length; i++) {
      var option_val = sel_el.options[i].value;
      //alert("i=" + i + ": " + option_val);

      //old: if (option_val == path) {

      // if given path contains the select option, select it 
      // example: /Rails/xyz/param1/param2/  contains /Rails/xyz
      // reason:  a select option url cannot be subset of another select
      //          (the extra parameters are PathInfo)
      if (path.substring(0, option_val.length) == option_val) {
         index = i;
         break;
      }
   }

   if (index == -1) {
      alert("find_option: no select option for path= " + path +
            ", nr_options in select box= " + sel_el.options.length);
      index = 0;
   }
   sel_el.options[index].selected = true;
} // find_option_to_select()



             /***********  Application Utililtes ************/

/* Converts text strings (eg from left navigation select) to file names
   String is lowercased, and spaces are converted to '_'
   Example: 'Table_Formats' becomes 'table_formats'
*/
function cvt_to_filename(str) {

   var pattern = /\s+/;
   return str.toLowerCase().replace(pattern, '_');
} // end cvt_to_filename()


/* 
   Similar to cvt_to_filename, but no lowercase: just cvt spaces -> '_'
*/
function cvt_to_dirname(str) {

   var pattern = /\s+/g;
   return str.replace(pattern, '_');
} // end cvt_to_dirname()


/*
   This function extracts from Url 1 or 2 directories, + a file name. 
   Returns an array with 3 values:    maindir  subdir  fname
                                                [0]   [1]     [2]    
   http://raul/Css                           -> Css   null    null
   http://raul/Css/x.html                    -> Css   null    x
   http://raul/Css/Tables/table_formats.html -> Css   Tables  table_formats
   http://raul/Css/Xyz/                      -> Css   Xyz,    index
*/
function get_url_components(url) {
   if (app_debug) alert('get_url_components(url)=' + url);
   //       http://www.site.com  +   /dir1/    + dir2 ?    + .(cgi|html))?
   var pattern = /http:[/][/][^/]+([/][^/]+[/])([^/]+[/])?(.*)/;

   var arr = url.match(pattern);
   if (!arr) { 
      alert("split_url(): Url= " + url + " not parsable");
      return null;
   }

   var maindir = arr[1]; 
   var subdir  = arr[2]; 
   var fname   = arr[3];
   if (!maindir) return null; // Url wout dirs (type 'site/index.html')

   // remove slashes from directories (and don't leave subdir undefined) 
   // ( tried pattern: /[/](\w+)[/]/  bad; works only if 2 slashes present!)
   var pattern = /[/]/g;
   maindir     = maindir.replace(pattern, "");
   subdir      = subdir ? subdir.replace(pattern, "") : "";

   // if there is no final html file, set it to index.html 
   //fname = fname ? fname : "index.html";
   if (! fname) {
      // assign fname to index only if subdir not present
      if (! subdir) fname = "index.html";
   }

   // strip '.html' from file for comparison with options
   //pattern  = /^(\w+)[.]html$/;
   //fname    = fname.replace(pattern, "$1");
   //alert("got fname: " + fname);
   return [maindir, subdir, fname];
    
} // end get_url_components()



