// GLOBALS

// holds filters
// Used by various filtering function
ary_filters = new Array();

// regex_tmplt is used by makeDiv function.
// We put it here to compile only once.
var regex_tmplt = new RegExp('%!%([^%]+)%!%', 'g');

// ary_tmplt_product_div is used by makeDiv function.
// The first template, element 0, is used for product presentation.
// The secomd template, element 1, is used for product comparison.
// We put it here to compile only once.
// Uses the same templating system used by the generator **except**
// that the delimiter is %!%...%!% so that perl templating processor
// ignores it

var Href_View = '';
var ary_tmplt_product_div = new Array();

ary_tmplt_product_div[0] = 
    '<div id="prod_%!%Index%!%" class="prod">'
  + '<table class="prod" cellpadding="0px" cellspacing="0px">'
  + '<tr>'
  + '<td class="prod_img">'
  + '<a class="productlist" href="%!%Link%!%"'
  + 'onClick="%!%onClick%!%" '
  + 'title="%!%Title%!%"'
  + '><img class="prod_img" name="img_%!%Index%!%"'
  + 'src="%!%Image%!%" alt=""></a>'
  + '</td>'
  + '<td class="prod_desc">'
  + '<a class="standardlarge" href="%!%Link%!%" '
  + 'onClick="%!%onClick%!%" '
  + 'title="%!%Title%!%">%!%Title%!%</a>'
  + '<ul class="attr_list">'
  + '<li>Platform Type: %!%Platform Type%!%</li>'
  + '<li>Manufacturer: %!%Manufacturer%!%</li>'
  + '<li>Bus Type: %!%Bus Type%!%</li>'
  + '<li>Tuner Type: %!%Tuner Type%!%</li>'
  + '<li>Digital Cable TV (ClearQAM): %!%Digital Cable TV (ClearQAM)%!%</li>'
  + '<li>Free-to-Air DTV/HDTV: %!%Free-to-Air DTV/HDTV%!%</li>'
  + '<li>Free-to-Air Analog TV: %!%Free-to-Air Analog TV%!%</li>'
  + '<li>CableCARD&trade; Capable: %!%CableCARD&trade; Capable%!%</li>'
  + '<li>FM Radio Capable: %!%FM Radio Capable%!%</li>'
  + '<li>PVR Functionality: %!%PVR Functionality%!%</li>'
  + '</ul>'
  + '</td>'
  + '<td class="prod_buy">'
  + '<a class="standardlarge" href="%!%Link%!%" '
  + 'onClick="%!%onClick%!%" '
  + '>$%!%Price%!%</a><br><br>'
  + '<div class="storebutton"'
  + 'onMouseOver="this.className=\'storebutton hover\'"'
  + 'onMouseOut="this.className=\'storebutton\'">'
  + '<a href="%!%Link%!%" '
  + 'onClick="%!%onClick%!%"'
  + '>Go to store</a>'
  + '</div>'
  + '</td>'
  + '</tr>'
  + '</table>'
  + '</div>'
  ;
ary_tmplt_product_div[1] = 
    '<div id="prod_%!%Index%!%" class="prod">'
  + '<table class="prod" cellpadding="0px" cellspacing="0px">'
  + '<tr>'
  + '<td class="prod_img">'
  + '<a class="productlist" href="%!%Compare URL%!%"'
  + 'title="%!%Title%!%"'
  + '><img class="prod_img" name="img_%!%Index%!%"'
  + 'src="%!%Image%!%" alt=""></a>'
  + '</td>'
  + '<td class="prod_desc">'
  + '<a class="standardlarge" href="%!%Compare URL%!%"'
  + 'title="%!%Title%!%">%!%Title%!%</a>'
  + '<ul class="attr_list">'
  + '<li>Platform Type: %!%Platform Type%!%</li>'
  + '<li>Manufacturer: %!%Manufacturer%!%</li>'
  + '<li>Bus Type: %!%Bus Type%!%</li>'
  + '<li>Tuner Type: %!%Tuner Type%!%</li>'
  + '<li>Digital Cable TV (ClearQAM): %!%Digital Cable TV (ClearQAM)%!%</li>'
  + '<li>Free-to-Air DTV/HDTV: %!%Free-to-Air DTV/HDTV%!%</li>'
  + '<li>Free-to-Air Analog TV: %!%Free-to-Air Analog TV%!%</li>'
  + '<li>CableCARD&trade; Capable: %!%CableCARD&trade; Capable%!%</li>'
  + '<li>FM Radio Capable: %!%FM Radio Capable%!%</li>'
  + '<li>PVR Functionality: %!%PVR Functionality%!%</li>'
  + '</ul>'
  + '<a href="%!%Info URL%!%"'
  + '>Detailed Product Information</a>'
  + '</td>'
  + '<td class="prod_buy">'
  + '<a class="standardlarge" href="%!%Compare URL%!%"'
  + '>Compare Prices</a><br>'
  + 'Best price $%!%Best Price%!%<br>'
  + '%!%Num Retailers%!% retailers</br>'
  + '</td>'
  + '</tr>'
  + '</table>'
  + '</div>'
  ;
ary_tmplt_product_div[2] = 
    '<div id="prod_%!%Index%!%" class="prod">'
  + '<table class="prod" cellpadding="0px" cellspacing="0px">'
  + '<tr>'
  + '<td class="prod_img">'
  + '<table class="prod_img"><tr>'
  + '<td><img src="images/game_border_top_left.gif" alt=""></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><img src="images/game_border_top_right.gif" alt=""></td>'
  + '</tr>'
  + '<tr>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><a class="productlist" href="%!%Link%!%"'
  + 'title="%!%Title%!%" target="_blank"'
  + '><img class="prod_img" name="img_%!%Index%!%"'
  + 'src="%!%Image%!%" alt=""></a></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '</tr>'
  + '<tr>'
  + '<td><img src="images/game_border_bottom_left.gif" alt=""></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><img src="images/game_border_bottom_right.gif" alt=""></td>'
  + '</tr>'
  + '</table>'
  + '</td>'
  + '<td class="prod_desc">'
  + '<a class="standardlarge" href="%!%Link%!%" '
  + 'onClick="%!%onClick%!%" '
  + 'title="%!%Title%!%" target="_blank">%!%Title%!%</a>'
  + '<ul class="attr_list">'
  + '<li>Platform Type: %!%Platform Type%!%</li>'
  + '<li>Manufacturer: %!%Manufacturer%!%</li>'
  + '<li>Bus Type: %!%Bus Type%!%</li>'
  + '<li>Tuner Type: %!%Tuner Type%!%</li>'
  + '<li>Digital Cable TV (ClearQAM): %!%Digital Cable TV (ClearQAM)%!%</li>'
  + '<li>Free-to-Air DTV/HDTV: %!%Free-to-Air DTV/HDTV%!%</li>'
  + '<li>Free-to-Air Analog TV: %!%Free-to-Air Analog TV%!%</li>'
  + '<li>CableCARD&trade; Capable: %!%CableCARD&trade; Capable%!%</li>'
  + '<li>FM Radio Capable: %!%FM Radio Capable%!%</li>'
  + '<li>PVR Functionality: %!%PVR Functionality%!%</li>'
  + '</ul>'
  + '</td>'
  + '<td class="prod_buy">'
  + '<a class="standardlarge" href="%!%Link%!%" '
  + 'onClick="%!%onClick%!%" '
  + 'target="_blank">$%!%Price%!%</a><br><br>'
  + '<div class="storebutton"'
  + 'onMouseOver="this.className=\'storebutton hover\'"'
  + 'onMouseOut="this.className=\'storebutton\'">'
  + '<a href="%!%Link%!%" target="_blank" '
  + 'onClick="%!%onClick%!%" '
  + '>Go to store</a>'
  + '</div>'
  + '</td>'
  + '</tr>'
  + '</table>'
  + '</div>'
  ;
ary_tmplt_product_div[3] = 
    '<div id="prod_%!%Index%!%" class="prod">'
  + '<table class="prod" cellpadding="0px" cellspacing="0px">'
  + '<tr>'
  + '<td class="prod_img">'
  + '<table class="prod_img"><tr>'
  + '<td><img src="images/game_border_top_left.gif" alt=""></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><img src="images/game_border_top_right.gif" alt=""></td>'
  + '</tr>'
  + '<tr>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><a class="productlist" href="%!%Compare URL%!%"'
  + 'title="%!%Title%!%" target="_blank"'
  + '><img class="prod_img" name="img_%!%Index%!%"'
  + 'src="%!%Image%!%" alt=""></a></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '</tr>'
  + '<tr>'
  + '<td><img src="images/game_border_bottom_left.gif" alt=""></td>'
  + '<td><img src="images/transp.gif" alt=""></td>'
  + '<td><img src="images/game_border_bottom_right.gif" alt=""></td>'
  + '</tr>'
  + '</table>'
  + '</td>'
  + '<td class="prod_desc">'
  + '<a class="standardlarge" href="%!%Compare URL%!%"'
  + 'title="%!%Title%!%" target="_blank">%!%Title%!%</a>'
  + '<ul class="attr_list">'
  + '<li>Platform Type: %!%Platform Type%!%</li>'
  + '<li>Manufacturer: %!%Manufacturer%!%</li>'
  + '<li>Bus Type: %!%Bus Type%!%</li>'
  + '<li>Tuner Type: %!%Tuner Type%!%</li>'
  + '<li>Digital Cable TV (ClearQAM): %!%Digital Cable TV (ClearQAM)%!%</li>'
  + '<li>Free-to-Air DTV/HDTV: %!%Free-to-Air DTV/HDTV%!%</li>'
  + '<li>Free-to-Air Analog TV: %!%Free-to-Air Analog TV%!%</li>'
  + '<li>CableCARD&trade; Capable: %!%CableCARD&trade; Capable%!%</li>'
  + '<li>FM Radio Capable: %!%FM Radio Capable%!%</li>'
  + '<li>PVR Functionality: %!%PVR Functionality%!%</li>'
  + '</ul>'
  + '<a href="%!%Info URL%!%" target="_blank"'
  + '>Detailed Product Information</a>'
  + '</td>'
  + '<td class="prod_buy">'
  + '<a class="standardlarge" href="%!%Compare URL%!%"'
  + 'target="_blank">Compare Prices</a><br>'
  + 'Best price $%!%Best Price%!%<br>'
  + '%!%Num Retailers%!% retailers</br>'
  + '</td>'
  + '</tr>'
  + '</table>'
  + '</div>'
  ;

// we set this here to eliminate lookup time in makeDiv
var tmplt_product_div = ary_tmplt_product_div[1];

function initialize(){
    var objArgs           = getQArgs();
    var count_set_filters = 0;

    for( var idx_cat = 0; idx_cat < ary_cats.length; idx_cat++ ){
      // alert( 'Consider ' + key_q_arg + '!' );
      QARG:
      for( var key in objArgs ){
        var key_q_arg = unescape( key );
        // BEGIN process found matching key
        if( ary_cats[idx_cat]['name'] == key_q_arg ){
          var cmp_type  = ary_cats[idx_cat]['cmp'];
          var val_q_arg = objArgs[key_q_arg];
          // alert( 'Found ' + key_q_arg + ' val ' + val_q_arg );
          // BEGIN set range option
          if( cmp_type == 'range' ){
            var ary_minmax = val_q_arg.split(',');
            // require in form Best%20Price=100,200
            if( ary_minmax.length != 2 ){ continue QARG; }
            // alert( 'Processing range type min = ' + ary_minmax[0]
            //  + ' max = ' + ary_minmax[1]
            // );
            var id_range_bot = 'range_bot_' + idx_cat;
            var id_range_top = 'range_top_' + idx_cat;

            document.getElementById( id_range_bot ).value = ary_minmax[0];
            document.getElementById( id_range_top ).value = ary_minmax[1];
            setCatRange( idx_cat, 1 ); // 1 = no refresh filters, divs
            count_set_filters++;
            continue QARG;
          }
          // END set range option
          // BEGIN set explicit option
          else if( cmp_type == 'eq' || cmp_type == 'ge' || cmp_type == 'le' ){
            for( var idx_opt = ary_cats[idx_cat]['options'].length-1;
              idx_opt > -1;
              idx_opt--
            ){
              if( 
                ( cmp_type == 'eq'
                  && ary_cats[idx_cat]['options'][idx_opt]['name']
                  == val_q_arg 
                ) || (
                  cmp_type == 'ge'
                  && ary_cats[idx_cat]['options'][idx_opt]['value']
                  <= val_q_arg
                ) || (
                  cmp_type == 'le'
                  && ary_cats[idx_cat]['options'][idx_opt]['value']
                  >= val_q_arg
                )
              ){
                setCatOpt(idx_cat,idx_opt,1); // 1 = no refresh filters, divs
                count_set_filters++;
                continue QARG;
              }
            }
          }
          // END set explicit option
        }
        // END process found matching key
      }
      // alert( 'Searched throuh names \n' + str_debug );
    }
    if( count_set_filters > 0 ){ displayFilters(); }
    siftDivs();
}

function getQArgs(){
  var obj_args = new Object();
  var query    = location.hash.substring(1); // get query string
  // query_clean  = unescape(query);
  var ary_args = query.split('|||');

  QARG:
  for( var idx_arg = 0; idx_arg < ary_args.length; idx_arg++ ){
    var pos = ary_args[idx_arg].indexOf('=');
    var key = '';
    var val = '';
    if( pos == -1 ){
      key = unescape(ary_args[idx_arg]);
      val = true;
    }
    else {
      key = unescape(ary_args[idx_arg].substr(0,pos));
      val = unescape(ary_args[idx_arg].substr(pos+1));
    }
    if( key == '' ){ continue QARG };
    obj_args[key] = val;
  }
  return obj_args;
}

// BEGIN toggleCatView( idx_cat )
// For a category list of options, toggle visibility
function toggleCatView( idx_cat ){
  var id_img  = 'img_button_cat_' + idx_cat;
  var id_ul   = 'ul_cat_'         + idx_cat;
  var elemImg = document.getElementById( id_img );
  var elemUl  = document.getElementById( id_ul  );

  if( elemUl.style.display != 'none' ){
    elemUl.style.overflow = 'hidden';
    elemUl.style.display  = 'none';
    elemImg.src = 'images/expand.gif';
  }
  else {
    elemUl.style.overflow = 'hidden';
    elemUl.style.display  = 'block';
    elemImg.src = 'images/collapse.gif';
  }
}
// END toggleCatView( idx_cat )

// BEGIN siftDivs( num_page )
function siftDivs( num_page ){
  num_page = num_page || 0;
  var destForDivs = document.getElementById('products');
  destForDivs.innerHTML = '';

  var ary_cat_seen_opts = new Array();
  for( var idx_cat = 0;
    idx_cat < ary_cats.length;
    idx_cat++
  ){
    ary_cat_seen_opts[idx_cat] = new Array();
  }

  // BEGIN ATTR_SET
  var idx_matches = -1;
  ATTR_SET:
  for( var idx_attr_set = 0;
    idx_attr_set < ary_attrs.length;
    idx_attr_set++
  ){
    // BEGIN CAT:
    var ary_row_vals = ary_attrs[ idx_attr_set ];
    var count_pass = 0;
    var ary_cat_matches = new Array();
    CAT:
    for( var idx_cat = 0;
      idx_cat < ary_cats.length;
      idx_cat++
    ){
      var cmp_type = ary_cats[idx_cat]['cmp'];

      // BEGIN test if in range
      if( cmp_type == 'range' ){
        if(  ary_cats[idx_cat]['active'] == false
             || 
          ( ary_cats[idx_cat]['active'] == true
            && ary_row_vals[idx_cat] >= ary_cats[idx_cat]['bot']
            && ary_row_vals[idx_cat] <= ary_cats[idx_cat]['top']
          )
        ){
          // ary_cat_matches[idx_cat] = true;
          count_pass++;
          // BEGIN reset range if this is the first row
          if( idx_attr_set == 0 ){
            ary_cats[idx_cat]['pass_top'] = 0;
            ary_cats[idx_cat]['pass_bot'] = 10000;
          }
          // END reset range if this is the first row
        }
        continue CAT;
      }
      // END test if in range

      // BEGIN OPT:
      OPT:
      for( var idx_opt = ary_cats[idx_cat]['options'].length-1;
        idx_opt > -1;
        idx_opt--
      ){
        // BEGIN mark as matched if selected option matches attr
        if( ary_cats[idx_cat]['options'][idx_opt]['status'] == true ){
          if( cmp_type == 'eq' 
              && ary_row_vals[idx_cat] == idx_opt
          ){
            count_pass++;
            // store matching opt for this cat
            ary_cat_matches[idx_cat] = idx_opt;
            break OPT;
          }
          if( ary_cats[idx_cat]['cmp'] == 'ge' 
              && ary_row_vals[idx_cat]
              >= ary_cats[idx_cat]['options'][idx_opt]['value']
          ){
            count_pass++;
            // store matching opt for this cat
            ary_cat_matches[idx_cat] = idx_opt;
            break OPT;
          }
          if( ary_cats[idx_cat]['cmp'] == 'le' 
              && ary_row_vals[idx_cat]
              <= ary_cats[idx_cat]['options'][idx_opt]['value']
          ){
            count_pass++;
            // store matching opt for this cat
            ary_cat_matches[idx_cat] = idx_opt;
            break OPT;
          }
        }
        // END mark as matched if selected option matches attr
      }
      // END OPT
    }
    // END CAT
   
    // BEGIN do stuff if this product matches all selected options
    if( count_pass == ary_cats.length){
      // increment number of matching products
      idx_matches++;

      // BEGIN display product if on given page
      if(Math.floor(idx_matches/divs_per_page) == num_page) {
        destForDivs.innerHTML += makeDiv(idx_attr_set);
      }
      // END display product if on given page

      // BEGIN integrate to ary_cat_matches to ary_cat_seen_opts
      CAT_FOUND:
      for( var idx_cat = 0;
        idx_cat < ary_cats.length;
        idx_cat++
      ){
        if( ary_cats[idx_cat]['cmp'] == 'range' 
        ){
        // BEGIN remember maximum and minimum passing values
          if( ary_row_vals[idx_cat] > ary_cats[idx_cat]['pass_top'] ){
            ary_cats[idx_cat]['pass_top'] = ary_row_vals[idx_cat];
            // alert( 'top price now ' + ary_row_vals[idx_cat] );
          }
          if( ary_row_vals[idx_cat] < ary_cats[idx_cat]['pass_bot'] ){
            ary_cats[idx_cat]['pass_bot'] = ary_row_vals[idx_cat];
            // alert( 'bot price now ' + ary_row_vals[idx_cat] );
          }
        // END remember maximum and minimum passing values
          continue CAT_FOUND;
        }
        idx_opt = ary_cat_matches[idx_cat];
        ary_cat_seen_opts[idx_cat][idx_opt] = true;
      }
      // END integrate to ary_cat_matches to ary_cat_seen_opts
    }
    // END do stuff if this product matches all selected options
  }
  // END ATTR_SET

  /* ary_cat_seen_opts now looks like this:
     ary_cat_seen_opts[0][0] = true
     ary_cat_seen_opts[0][1] = true
     ...
     ary_cat_seen_opts[3][0] = false,
     ary_cat_seen_opts[3][1] = true, 
     ary_cat_seen_opts[3][2] = false,
     ...

     We process as follows:
       if the comparison type is 'eq' for the given category, we simply
        display the link if the seen opt value is true, or hide if false;

       if the comparison type is 'ge', we display the link if the
        link if the seen opt value is true, OR IF ANY HIGHER VALUE IS TRUE
        (e.g. if the category is 'HardDrive' and option that correlates
          to 'At least 50GB' is true, then 'At least 20GB' is also shown)

       if the comparison type is 'le', we display the link if the
        link if the seen opt value is true, OR IF ANY LOWER VALUE IS TRUE
        (e.g. if the category is 'Wattage' and option that correlates
          to 'At most 45W' is true, then 'At most 70W' is also shown)
   */

  // BEGIN hide or show options based on ary_cat_seen_opts content
  CAT_SEEN:
  for( var idx_cat = 0;
    idx_cat < ary_cats.length;
    idx_cat++
  ){
    var cmp_type = ary_cats[idx_cat]['cmp'];

    // BEGIN For Range, set values in range to min and max seen
    if( cmp_type == 'range' ){
      var id_range_top = 'range_top_' + idx_cat;
      var id_range_bot = 'range_bot_' + idx_cat;

      document.getElementById(id_range_top).value
        = ary_cats[idx_cat]['pass_top'];
      document.getElementById(id_range_bot).value
        = ary_cats[idx_cat]['pass_bot'];
      continue CAT_SEEN;
    } 
    // END For Range, set values in range to min and max seen

    var f_sticky = false;
    OPT_SEEN:
    for( var idx_opt = ary_cats[idx_cat]['options'].length-1;
      idx_opt > -1;
      idx_opt--
    ){
      var list_id = 'cat_opt_' + idx_cat + '_' + idx_opt;
      if( ary_cat_seen_opts[idx_cat][idx_opt] == true
        || ( f_sticky == true && cmp_type == 'ge' )
        || ( f_sticky == true && cmp_type == 'le' ) ){
        document.getElementById(list_id).style.display = 'list-item';
        f_sticky = true;
      }
      else {
        document.getElementById(list_id).style.display = 'none';
      }
    } // END OPT_SEEN
  } // END CAT_SEEN
  // END hide or show options based on ary_cat_seen_opts content

  // BEGIN show pagination
  if( idx_matches >= 0 ){
    document.getElementById('pagination_top').innerHTML
      = document.getElementById('pagination_bottom').innerHTML
      = paginate(num_page, idx_matches);
    document.getElementById('no_match').style.display='none';
    document.getElementById('no_prods').style.display='none';
  }
  else {
    document.getElementById('pagination_top').innerHTML
      = document.getElementById('pagination_bottom').innerHTML
      = '';
    document.getElementById('pagination_top').style.display
      = document.getElementById('pagination_bottom').style.display
      = 'none';
    if ( ary_attrs.length ) {
      document.getElementById('no_match').style.display='block';
      document.getElementById('no_prods').style.display='none';
    }
    else {
      document.getElementById('no_prods').style.display='block';
      document.getElementById('no_match').style.display='none';
    }
  }
  // END show pagination
}
// END siftDivs( num_page )


// BEGIN paginate( page, idx_matches)
// Creates html for the pagination <div>s
// which are above and below the product listing area.
function paginate( page, idx_matches ){
  var num_pages  = Math.ceil((idx_matches + 1) / divs_per_page)
    // msm 20070916 fixes bug where only one product shown
  num_pages      = num_pages > 0 ? num_pages : 1;
  var first_item = divs_per_page * page;
  var last_item  = divs_per_page * (page+1) - 1;

  // idx_matches is the index of matching products;
  //   add one to it to get the number of matching products.
  //   last_item is the index of the last product.
  if( last_item > idx_matches ){
      last_item = idx_matches
  }

  // BEGIN 'Showing X - Y of Z' part
  var html_paginate =
      '<table width="100%"><tr><td align="left">'
    + 'Showing <b>' + (first_item+1) + '</b> - <b>' + (last_item+1)
    + '</b> of '
    + '<b>' + ( idx_matches + 1 ) + '</b>'
    + '</td><td align="right">';
  // END 'Showing x - y of z' part

  // BEGIN 'Page 1 | <<' part
  if( page > 0 ){  // Page 1 | <<
    html_paginate += '<a href="javascript: siftDivs(); void(0);">'
      + 'Page 1</a>&nbsp; |&nbsp; '
      + '<a href="javascript: siftDivs('
      + ( page - 1)
      + '); void(0);">&laquo;</a>&nbsp; ';
  }
  else {
    html_paginate += 'Page 1&nbsp; |&nbsp; &laquo;&nbsp; ';
  }
  // END 'Page 1 | <<' part

  // BEGIN 'H I J K L' part
  for (var i = page-4>0?page-4:0 ; i < num_pages && i <= page+4; i++) {
      if( i == page ){
        html_paginate += '<b>' + (i+1) + '</b>&nbsp; ';
      } else {
        html_paginate += '<a href="javascript: siftDivs(' 
          + i + '); void(0);">'
          + (i+1) + '</a>&nbsp; '
          ;
      }
  }
  // END 'I J K L M' part

  // BEGIN ' >> | Page N' part
  if( page < num_pages-1 ){ // >> | Page N
    html_paginate +=
        '<a href="javascript: siftDivs(' + (page+1) + '); void(0);">'
      + '&raquo;</a>&nbsp; '
      + '|&nbsp; <a href="javascript: siftDivs(' + (num_pages-1) + ');'
      + 'void(0);">Page ' + num_pages + '</a>'
      ;
  } else {
    html_paginate += '&raquo;&nbsp; |&nbsp; Page ' + num_pages;
  }
  // END ' >> | Page N' part

  html_paginate += '</td></tr></table>';

  return html_paginate;
}
// END paginate()

// BEGIN makeDiv( idx_attr )
// SYNOPSIS: write the text for one single product <div>
//   See global ary_tmplt_product_div, above, for declaration
// INPUT:
//   idx_attr      = index to array of attributes for a given product
// OUPUT:
//   Returns html results of filled template
//
function makeDiv( idx_attr ){
    var divTemplateLookup = new Object();

    divTemplateLookup['Index'] = idx_attr;
    // BEGIN dynamic from generator
    divTemplateLookup['Image'] = ary_attrs[idx_attr][12];
    divTemplateLookup['Title'] = ary_attrs[idx_attr][13];
    divTemplateLookup['Link'] = ary_attrs[idx_attr][14];
    divTemplateLookup['Product ID'] = ary_attrs[idx_attr][15];
    divTemplateLookup['Compare URL'] = ary_attrs[idx_attr][16];
    divTemplateLookup['Info URL'] = ary_attrs[idx_attr][17];
    divTemplateLookup['Num Retailers'] = ary_attrs[idx_attr][18];

    // END dynamic from generator
    var prod_id_ary_loc = ary_attrs[idx_attr][(ary_attrs[idx_attr].length -1)];
    var my_price = parseFloat(ary_attrs[idx_attr][0]);
    var my_round = Math.round(parseFloat(ary_attrs[idx_attr][0]));
    var onclick_hbx_string = 'shop-us_' + prod_id_ary_loc + '_' + divTemplateLookup['Product ID'] + '_' + my_round;
    var prodShop_string =  "'WTB-US-EN', '" + prod_id_ary_loc + "', '" + divTemplateLookup["Product ID"] + "', '" + my_price + "', 'USD'";
    divTemplateLookup['onClick'] = "_hbLink('GoToStoreReferral');_hbLink('" + onclick_hbx_string + "');prodShopLink(" + prodShop_string + ");";

    for( var idx_cat = 0;
     idx_cat < ary_cats.length;
     idx_cat++
    ){
      var cmp_type = ary_cats[idx_cat]['cmp'];
      var name_cat = ary_cats[idx_cat]['name'];
      var val_attr = ary_attrs[idx_attr][idx_cat];

      // in the case of 'eq', val_attr is the idx_attr of the option name
      // e.g. 1 = 'Notebook'
      if( cmp_type == 'eq' ){
        divTemplateLookup[name_cat]
          = ary_cats[idx_cat]['options'][val_attr]['name']
          ;
      }
      // in the case of 'ge' or 'range', 
      // val_attr is the value of the attribute
      // e.g. 500 for 500GB hard drive
      else if( cmp_type == 'ge' ){
        if( val_attr < 1 ) {
          divTemplateLookup[name_cat] = 'none';
        }
        else {
          divTemplateLookup[name_cat]
            = val_attr + ary_cats[idx_cat]['opt_unit']
            ;
        }
      }
      else if( cmp_type == 'le' ){
        if( val_attr < 1 ) {
          divTemplateLookup[name_cat] = 'none';
        }
        else {
          divTemplateLookup[name_cat]
            = val_attr + ary_cats[idx_cat]['opt_unit']
            ;
        }
      }
      else {
        divTemplateLookup[name_cat] = val_attr;
      }
    }

    // these two lines are very similar to the templating regex
    // in the perl script.  See notes in the tmplt_product_div global 
    // declaration, above.

    var newDiv = tmplt_product_div.replace(
      regex_tmplt,
      function(match, name){ return divTemplateLookup[name];}
    );  // using an anonymous function is a bit like using /x in Perl

    return newDiv;
}
// END makeDiv()

// BEGIN setCatOpt
function setCatOpt( idx_cat, idx_selected, f_noRefresh ){
  for( idx_opt = 0;
    idx_opt < ary_cats[idx_cat]['options'].length;
    idx_opt++
  ){
    if( idx_opt == idx_selected ){
      ary_cats[idx_cat]['options'][idx_opt]['status'] = true;
    }
    else {
      ary_cats[idx_cat]['options'][idx_opt]['status'] = false;
    }
  }
  // debugStatus();
  document.getElementById('cat_' + idx_cat ).style.display='none';
  ary_filters[ary_filters.length] = [ idx_cat,idx_selected ];
  if( f_noRefresh != 1 ){
    displayFilters()
    siftDivs();
  };
}
// END setCatOpt

// BEGIN setCatRange
function setCatRange( idx_cat, f_noRefresh ){
  ary_cats[idx_cat]['active'] = true;
  var id_range_bot = 'range_bot_' + idx_cat;
  var id_range_top = 'range_top_' + idx_cat;

  var val_top = document.getElementById( id_range_top ).value
    || ary_cats[idx_cat]['pass_top']
    ;
  var val_bot = document.getElementById( id_range_bot ).value
    || ary_cats[idx_cat]['pass_bot']
    ;

  ary_cats[idx_cat]['bot']    = val_bot;
  ary_cats[idx_cat]['top']    = val_top;

  document.getElementById( id_range_top ).value = val_top;
  document.getElementById( id_range_bot ).value = val_bot;

  document.getElementById('cat_' + idx_cat ).style.display='none';
  ary_filters[ary_filters.length] = [ idx_cat, val_bot + ' - ' + val_top ];
  if( f_noRefresh != 1 ){
    displayFilters()
    siftDivs();
  };
}
// END setCatRange

// BEGIN displayFilters()
function displayFilters(){
  var ary_html_filters = new Array();
  var ary_q_args       = new Array();
  if( ary_filters.length == 0 ){
    document.getElementById('activefilters').style.display='none';
    Href_View = '';
    document.location.hash = Href_View;
    return;
  }

  FILTER:
  for( var idx_filter = 0;
    idx_filter < ary_filters.length;
    idx_filter++
  ){
    var idx_cat   = ary_filters[idx_filter][0];
    var idx_opt   = ary_filters[idx_filter][1];
    var cmp_type  = ary_cats[idx_cat]['cmp'];

    var name_cat  = ary_cats[idx_cat]['name'];
    var name_opt  = '';
    if( cmp_type == 'range' ){
      name_opt    = idx_opt;
      ary_q_args.push( escape( name_cat ) + '=' 
        + ary_cats[idx_cat]['bot'] + ',' + ary_cats[idx_cat]['top']
      );
    }
    else if( cmp_type == 'eq' ){
      name_opt  = ary_cats[idx_cat]['options'][idx_opt]['name'];
      ary_q_args.push( escape( name_cat ) + '='
        + escape( ary_cats[idx_cat]['options'][idx_opt]['name'] )
      );
    }
    else if( cmp_type == 'ge' || cmp_type == 'le' ){
      name_opt  = ary_cats[idx_cat]['options'][idx_opt]['name'];
      ary_q_args.push( escape( name_cat ) + '='
        + ary_cats[idx_cat]['options'][idx_opt]['value']
      );
    }
    
    else { continue FILTER } // skip unsupported filter types

    ary_html_filters.push(
        '<li>'
      + '<a href="javascript:rmFilter('
      + idx_filter + ')"'
      + '><img width="9" height="9" border="0" '
      + 'src="http://images.pricerunner.com/images/i/filterboxes/delete.gif"'
      + '><span class="af_header"> '
      + name_cat + '</span> <br>' + name_opt + '</a>'
      + '</li>'
    );
  }

  if( ary_filters.length > 1 ){
    ary_html_filters.push(
        '<li>'
      + '<a href="javascript:document.sifter.reset(); '
      + 'resetFilters(); siftDivs(); void(0);"'
      + '><img width="9" height="9" border="0" '
      + 'src="http://images.pricerunner.com/images/i/filterboxes/delete.gif"'
      + '><span class="af_header">'
      + ' Reset all filters</span></a>'
      + '</li>'
    ); 
  }
  document.getElementById('activefilters').innerHTML = 
      '<h4>Selected Filters</h4>'
    + '<ul class="selectedfilters">'
    + ary_html_filters.join('')
    + '</ul>'
    ;
  Href_View
    = '' + ary_q_args.join('|||')
    ;
  document.location.hash = Href_View;
  document.getElementById('activefilters').style.display='block';
  return;
}
// END displayFilters()

// BEGIN gotoView()
function gotoView(){
  displayFilters(); // updates href_bookmark;
}
// END gotoView()

// BEGIN rmFilter( idx_filter )
function rmFilter( idx_filter ){
    // BEGIN remove category from filter list
    var idx_cat       = ary_filters[idx_filter][0];
    var cmp_type      = ary_cats[idx_cat]['cmp'];
    ary_filters.splice(idx_filter,1);
    // END remove category from filter list

    // BEGIN handle range type options
    if( cmp_type == 'range' ){
      ary_cats[idx_cat]['active'] = false;
    }
    // END handle range type options

    // BEGIN otherwise set all category options to true
    else {
      for( var idx_opt = 0;
        idx_opt < ary_cats[idx_cat]['options'].length;
        idx_opt++
      ){
        ary_cats[idx_cat]['options'][idx_opt]['status'] = true;
      }
    }
    // END otherwise set all category options to true

    // BEGIN reset all if no filters remain
    if( ary_filters.length == 0 ){
      resetFilters();
      siftDivs();
    }
    document.getElementById('cat_' + idx_cat ).style.display='block';
    displayFilters();
    siftDivs();
}
// END rmFilter( idx_filter )

function resetFilters(){
  // BEGIN hide filters menu
  document.getElementById('activefilters').style.display='none';
  document.getElementById('activefilters').innerHTML = '';
  // END hide filters menu

  // BEGIN reset visibility of all attribute selectors
  CAT:
  for( var idx_cat = 0;
    idx_cat < ary_cats.length;
    idx_cat++
  ){
    var cmp_type      = ary_cats[idx_cat]['cmp'];
    // BEGIN handle range type options
    if( cmp_type == 'range' ){
      ary_cats[idx_cat]['active'] = false;
    }
    // END handle range type options
    // BEGIN reset all category options to true
    else {
      for( var idx_opt = 0;
        idx_opt < ary_cats[idx_cat]['options'].length;
        idx_opt++
      ){
        ary_cats[idx_cat]['options'][idx_opt]['status'] = true;
      }
    }
    // END reset all category options to true

    // redisplay the category div
    document.getElementById('cat_' + idx_cat).style.display='block';
  }
  // END reset visibility of all attribute selectors

  // clean out filter array
  ary_filters = [];

  // reset # magic in URL
  Href_View = '';
  document.location.hash = Href_View;
}

// BEGIN debugStatus
function debugStatus(){
  CAT:
  for( idx_cat = 0;
    idx_cat < ary_cats.length;
    idx_cat++
  ){
    var str_opt_status = '';
    var cmp_type      = ary_cats[idx_cat]['cmp'];

    if( cmp_type == 'range' ){
      alert( 'The name is :' + ary_cats[idx_cat]['name'] + ':\n'
        + 'cmp is :' + ary_cats[idx_cat]['cmp'] + ':\n'
        + 'top is :' + ary_cats[idx_cat]['top'] + ':\n'
        + 'bot is :' + ary_cats[idx_cat]['bot'] + ':\n'
        + 'active is :' + ary_cats[idx_cat]['active'] + ':\n'
      );
      continue CAT;
    }
    for( idx_opt = 0;
      idx_opt < ary_cats[idx_cat]['options'].length;
      idx_opt++
    ){ 
      str_opt_status
        += '  '
        +  ary_cats[idx_cat]['options'][idx_opt]['name']
        +  ':'
        +  ary_cats[idx_cat]['options'][idx_opt]['status']
        +  ':\n'
        ;
    }

    alert( 'The name is :' + ary_cats[idx_cat]['name'] + ':\n'
      + 'cmp is :' + ary_cats[idx_cat]['cmp'] + ':\n'
      + 'options and status are: \n'
      + str_opt_status
    );
  }
}
// END debugStatus

