/* Gas-tax clock

   Look for spans of class="zF21".  Text within these spans may be one of:

                                    (Aliases)
      US_GAS_TAX_PER_GALLON         UGTG
      US_GAS_TAX_TOTAL              UGT
      OPEC_GAS_TAX_PER_GALLON       OGTG
      OPEC_GAS_TAX_TOTAL            OGT

   Clock data are substituted for each span's contents.


*/

if ( window.onload ) {

   var previousOnload = window.onload;

   window.onload = function() {
      previousOnload();

      // Don't start more than one even if script sourced more than once on a
      // page.

      if ( ! window.instZFGasTax ) {
         instZFGasTax = new zFGasTax();
      }
   }
} else {
   window.onload = function() {
      if ( ! window.instZFGasTax ) {
         instZFGasTax = new zFGasTax();
      }
   }
}

function zFGasTax() {

   // Name of this gizmo (for usage data logging via function usage_data_log())
   // and URL of log script.

   this.gizName = "G21gastax";
   this.usage_data_script = "http://zfacts.com/giz/udat/rec.php";

   // Clock data definitions.

   this.dataItems = [ "US_GAS_TAX_PER_GALLON", 
                      "US_GAS_TAX_TOTAL",
                      "OPEC_GAS_TAX_PER_GALLON",
                      "OPEC_GAS_TAX_TOTAL",
                      "UGTG",
                      "UGT",
                      "OGTG",
                      "OGT"
                    ];
   this.n_dataItems = this.dataItems.length;

   // Required link alternatives (any is OK).

   this.acceptableLink = [ "http://zfacts.com/p/990.html" ];

   // Required words in link text (all must be present).

   this.requiredLinkWord = [ "gas",
                             "tax"
                           ];

   // Default units -- dollars per gallon, gallons.

   this.unitCvt = 1;

   // Default decimal places.

   this.dec = 2;

   // Default (if not set by user).

   if ( window.zF21cps == undefined ) {
      zF21cps = 5;
   }

   // Data.

   // http://www.api.org/policy/tax/stateexcise/upload/March_2007_gasoline_and_diesel_summary_pages.pdf

   this.USGasTaxPerGallon = 0.458;

   // See http://zfacts.com/p/992.html

   this.OPECGasTaxPerGallon = 1.88;

   // http://www.eia.doe.gov/emeu/mer/pdf/pages/sec3_15.pdf

   this.USGasolineGallonsPerDay = 9019000 * 42.0;

   // Validate - check that specified link to zfacts provided.  Check that
   // link words include what's required.

   this.validateLink();

   // Initialize styles: add stylesheet for gizmo if user hasn't entered
   // class rules.

   this.initStyle();

   // Initialize HTML: if any spans with gizmo class exist then don't need
   // to add default HTML (backwards compatible); otherwise, replace anchors
   // referencing zFacts with default HTML. Also, any divs with gizmo class get
   // replaced with default HTML in any case.

   this.initHTML();

   // Initialize - find spans, create arrays of location and type.

   this.initSpans();

   // Initialize data - time, rate parameters.

   this.initData();

   // Initialize static "counters."

   this.initCounters();

   // Record usage.

//   this.usage_data_log();

   // Begin updating.

   this.updateCounters();
}


zFGasTax.prototype.validateLink = function() {

   // Find all <a ...> tags with class zF21.  Save those linking to zFacts in 
   // array.

   this.zFactsLinks = [];
   var links = document.getElementsByTagName( "a" );
   var n_links = links.length;
   var n_alt_links = this.acceptableLink.length;
   var link_ok_f = false;
   var n_linkWords = this.requiredLinkWord.length;
   var neededWords = [];
   for ( var i=0; i<n_links; i++ ) {

      // Loop over alternate acceptable link refs.

      for ( var j=0; j<n_alt_links; j++ ) {
         var linkElement = links[i];
         if ( linkElement.href.toLowerCase() == this.acceptableLink[j] ) {
            if ( ! link_ok_f ) {

               // Check that required text is there for at least one link.  Need
               // all required words.

               neededWords = [];
               var linkText = linkElement.innerHTML.toLowerCase();
               for ( var j=0; j<n_linkWords; j++ ) {
                  if ( linkText.indexOf( this.requiredLinkWord[j] ) == -1 ) {
                     neededWords.push( this.requiredLinkWord[j] );
                  }
               }
               link_ok_f = neededWords.length == 0;
            }
            this.zFactsLinks.push( linkElement );
            break;
         }
      }
   }
   if ( ! link_ok_f ) {
      if ( neededWords.length == 0 ) {

         // Required link not present.

         alert( "Need link like '" + this.acceptableLink[0] + "'" );
      } else {

         // Link was there, but didn't get required words.

         neededWords = neededWords.join( ", " );
         alert( "Link text must include: " + neededWords );
      }
   }
}

zFGasTax.prototype.initStyle = function() {

   // Create stylesheet for gizmo and set rules if user hasn't done so.

   var styleNode = document.createElement("style");
   this.styleNode = styleNode;

   styleNode.type  = "text/css";
   styleNode.rel   = "stylesheet";
   styleNode.media = "screen";
   styleNode.title = "zF21";

   // Must be in head for Safari.

   document.getElementsByTagName("head")[0].appendChild(styleNode);

   // Find our stylesheet.

   var styleSheet = this.getStylesheet( "zF21" );
   styleSheet.disabled = false;

   // Set rules if not set by user.
 
   var zF21 = this.getCSSRule(".zF21");
   if ( ! zF21 ) {
      zF21 = this.addCSSRule( styleSheet, ".zF21" );
      zF21.style.backgroundColor = "black";
      zF21.style.color           = "white";
      zF21.style.fontFamily      = "arial, sans-serif";
      zF21.style.fontSize        = "9pt";
      zF21.style.fontWeight      = "bold";
   }
}


zFGasTax.prototype.defaultHTML =
  '<table class="zF21">'
 +   '<tr>'
 +      '<td>'
 +         '<a href="http://zfacts.com/p/990.html"'
 +            'class="zF21"'
 +            'style="text-decoration:none;">'
 +            'U.S. gas tax</a>'
 +      '</td>'
 +      '<td align="right">'
 +         '<span class="zF21" style="display: none;" units="cents/gallon" '
 +               'dec="1">'
 +            'US_GAS_TAX_PER_GALLON'
 +         '</span>'
 +      '</td>'
 +      '<td colspan="2">'
 +         '&cent;/gal'
 +      '</td>'
 +   '</tr>'
 +   '<tr>'
 +      '<td colspan="2" align="right">'
 +         '$<span class="zF21" style="display: none;" units="dollars" dec="2">'
 +            'US_GAS_TAX_TOTAL'
 +         '</span>'
 +      '</td>'
 +   '</tr>'
 +   '<tr>'
 +      '<td>'
 +         '<a href="http://zfacts.com/p/990.html"'
 +            'class="zF21"'
 +            'style="text-decoration:none;">'
 +            'OPEC&nbsp;gas&nbsp;tax</a>'
 +      '</td>'
 +      '<td align="right">'
 +         '<span class="zF21" style="display: none;" units="cents/gallon" '
 +               'dec="1">'
 +            'OPEC_GAS_TAX_PER_GALLON'
 +         '</span>'
 +      '</td>'
 +      '<td colspan="2">'
 +         '&cent;/gal'
 +      '</td>'
 +   '</tr>'
 +   '<tr>'
 +      '<td colspan="2" align="right">'
 +         '$<span class="zF21" style="display: none;" units="dollars" dec="2">'
 +            'OPEC_GAS_TAX_TOTAL'
 +         '</span>'
 +      '</td>'
 +   '</tr>'
 +'</table>';


zFGasTax.prototype.initHTML = function() {

   // Initialize HTML: if any spans with gizmo class exist then don't need
   // to add default HTML (backwards compatible); otherwise, replace anchors
   // referencing zFacts with default HTML. Also, any divs with gizmo class get
   // replaced with default HTML in any case.

   this.span = this.getTagsByNameAndClass( "span", "zF21" );
   if ( this.span.length == 0 ) {

      // None exists.  Replace anchors referencing zFacts and having class
      // "zF21" with default HTML.

      var zF21Anchors = [];
      for ( var i=0; i<this.zFactsLinks.length; i++ ) {
         var anchor = this.zFactsLinks[i];
         if ( anchor.className == "zF21" ) {
            zF21Anchors.push( anchor );
         }
      }
      this.nodesToHTML( zF21Anchors );
   }

   // Find all divs with gizmo class; replace with HTML.

   var divs = this.getTagsByNameAndClass( "div", "zF21" );
   this.nodesToHTML( divs );
}


zFGasTax.prototype.initSpans = function() {

   // Find spans.

   this.span = this.getTagsByNameAndClass( "span", "zF21" );

   // See which each is.

   this.n_spans = this.span.length;
   this.spanItem    = [];
   this.spanUnitCvt = [];
   this.spanDec     = [];
   for ( var i=0; i<this.n_spans; i++ ) {
      this.spanItem[i]    = -1;

      // Compare span's inner HTML with list of allowed items.

      for ( var i_item=0; i_item<this.n_dataItems; i_item++ ) {
         var text = this.span[i].innerHTML;
         if ( text.indexOf( this.dataItems[i_item] ) != -1 ) {

            // OK.  Also, set visible.

            this.spanItem[i] = i_item;
            this.span[i].style.display = "inline";
            break;
         }
      }

      // See if span has attributes -- units, dec.

      var units = this.span[i].getAttribute( "units" );
      if ( units ) {

         // Original calculations in dollars and dollars/gallon.
         // Options: cents or cents/gallon.

         units = units.toLowerCase();
         var unitCvt;
         switch( units ) {
            case "cents" :
            case "cents/gallon" :
               unitCvt = 100;
               break;

            default :
               unitCvt = this.unitCvt;
               break;
         }
         this.spanUnitCvt[i] = unitCvt;
      } else {

         // Default (set above).

         this.spanUnitCvt[i] = this.unitCvt;
      }

      // Decimal points displayed.

      var dec = this.span[i].getAttribute( "dec" );
      if ( dec ) {
         if ( ! isNaN( dec ) ) {
            this.spanDec[i] = parseInt( dec );
         }
      } else {

         // Default (set above).

         this.spanDec[i] = this.dec;
      }
   }
}


zFGasTax.prototype.initData = function() {

   this.clockStart = new Date() * 0.001;
   var USGasolineGallonsPerSecond =this.USGasolineGallonsPerDay / (24.0*3600.0);
   this.USGasTaxDollarsPerSecond 
                        = USGasolineGallonsPerSecond * this.USGasTaxPerGallon;
   this.OPECGasTaxDollarsPerSecond 
                        = USGasolineGallonsPerSecond * this.OPECGasTaxPerGallon;

   // Timeout interval - milliseconds.

   this.timeoutMillisec = 1000 / zF21cps;
}


zFGasTax.prototype.initCounters = function() {

   // Initialize static "counters."

   // Loop over counters.

   for ( var i=0; i<this.n_spans; i++ ) {
      switch( this.spanItem[i] ) {
         case 0:
         case 4:

            // US State and Federal gas taxes, $/gallon.
            // Convert, format, and put in span.

            this.updateCounter( this.span[i], this.USGasTaxPerGallon, 
                                this.spanDec[i],
                                this.spanUnitCvt[i] );
            break;

         case 2:
         case 6:

            // "OPEC gas tax".

            this.updateCounter( this.span[i], this.OPECGasTaxPerGallon,
                                this.spanDec[i],
                                this.spanUnitCvt[i] );
            break;

      }
   }
}


zFGasTax.prototype.updateCounters = function() {

   // Calculations.  Time in seconds.

   var tnow = new Date();
   var t = tnow.getTime()*0.001 - this.clockStart;

   // Loop over counters.

   for ( var i=0; i<this.n_spans; i++ ) {
      switch( this.spanItem[i] ) {
         case 1:
         case 5:

            // Federal and State dollar total (since page opened).

            var dollars = this.USGasTaxDollarsPerSecond * t;

            // Convert, format, and put in span.

            this.updateCounter( this.span[i], dollars, this.spanDec[i],
                                this.spanUnitCvt[i] );
            break;

         case 3:
         case 7:

            // "OPEC gas tax" dollar total.

            var dollars = this.OPECGasTaxDollarsPerSecond * t;
            this.updateCounter( this.span[i], dollars, this.spanDec[i],
                                this.spanUnitCvt[i] );
            break;

      }
   }

   // Repeat at interval.

   setTimeout( "instZFGasTax.updateCounters()", this.timeoutMillisec );
}


zFGasTax.prototype.updateCounter 
                             = function( span, value, decimalPlaces, unitCvt ) {

   // Unit conversion.

   value *= unitCvt;

   // Format - commas, decimals.

   var formattedValue = this.formatNumber( value, decimalPlaces, ",", ".", 
                                           "", "", "-", "");
   span.innerHTML = formattedValue;
}


zFGasTax.prototype.usage_data_log = function() {

   // src script file that records usage.  Send name of this gizmo, etc.

   var script = document.createElement( "script" );
   script.src = this.usage_data_script + "?gizName="  + this.gizName
                                       + "&domain="   + document.domain
                                       + "&location=" + document.location;
   document.body.appendChild( script );
}


zFGasTax.prototype.toMonthYear = function( yearMonthDay ) {
   yearMonthDay = yearMonthDay.split( "-" );
   var month = parseInt( yearMonthDay[1] ) - 1;

   return this.monthString[month] + " " + yearMonthDay[0];

}


zFGasTax.prototype.nodesToHTML = function( nodes ) {
   var n_nodes = nodes.length;
   for ( var i=0; i<n_nodes; i++ ) {
      var node_i = nodes[i];
      var nodeParent = node_i.parentNode;

      zF21Div = document.createElement( "div" );
      nodeParent.insertBefore( zF21Div, node_i );
      zF21Div.innerHTML = this.defaultHTML;
      nodeParent.removeChild( node_i );
   }
}


zFGasTax.prototype.getInnerHTML = function(node) {
    var ihtml = node.innerHTML;

    // Converts special characters inside of href to hex.

    var ihtmlHref = ihtml.match(/href\s*=\s*"[^"]*(%7B|%7D)[^"]*"/g) || [];
    for (var i=0; i< ihtmlHref.length; i++) {
      var a = ihtmlHref[i];
      var b = a.replace(/%7B/g, '{');
      b = b.replace(/%7D/g, '}');
      ihtml = ihtml.replace(a, b);
    }
    return ihtml;
};


zFGasTax.prototype.getTagsByNameAndClass = function( tagName, className ) {
   var tags = [];
   var allTags = document.getElementsByTagName( tagName );
   var n_tags = allTags.length;
   for ( var i=0; i<n_tags; i++ ) {
      if ( allTags[i].className == className ) {
         tags.push( allTags[i] );
      }
   }

   return tags;
}


zFGasTax.prototype.getStylesheet = function( title ) {
   var styleSheet;
   var n_styleSheets = document.styleSheets.length;
   for ( var i_sheet=0; i_sheet<n_styleSheets; i_sheet++ ) {
      var styleSheet_i = document.styleSheets[i_sheet];
      try {
         if ( styleSheet_i.title.toLowerCase() == title.toLowerCase() ) {
            styleSheet = styleSheet_i;
         }
      } catch(e) {}
   }

   return styleSheet;
}


zFGasTax.prototype.getCSSRule = function( ruleName ) {
   var cssRule;
   var cssRules;
   for ( var i_sheet=0; i_sheet<document.styleSheets.length; i_sheet++ ) {
      var styleSheet_i = document.styleSheets[i_sheet];

      // Avoid linked sheets -- if not this site, may run afoul of security
      // restriction.

      try {
         if ( ! cssRules ) {
            if ( document.styleSheets[i_sheet].cssRules ) {
               cssRules = "cssRules";
            } else {
               cssRules = "rules";   // IE.
            }
         }
         for ( var i_rule=0; i_rule<styleSheet_i[cssRules].length; i_rule++ ) {
            var rule_i = styleSheet_i[cssRules][i_rule];
            if ( rule_i.selectorText.toLowerCase() == ruleName.toLowerCase() ) {
               cssRule = rule_i;
            }
         }
      } catch(e) {}
   }

   return cssRule;
}


zFGasTax.prototype.addCSSRule = function ( styleSheet, ruleName ) {
   if ( styleSheet.addRule ) {
      styleSheet.addRule(ruleName);   // IE
   } else {
      styleSheet.insertRule(ruleName+" { }", 0);
   }
   return this.getCSSRule( ruleName );
}


// number formatting function
// copyright Stephen Chapman 24th March 2006, 10th February 2007
// permission to use this function is granted provided
// that this copyright notice is retained intact

/* num   - number to format
 * dec   - number of decimal places
 * thou  - thousands separator
 * pnt   - decimal pt - period or comma
 * curr1 - $, etc., before number
 * curr2 - USD, etc. after number
 * n1    - before number, if negative - e.g., '-' or '('
 * n2    - after number, if negative - e.g., '' or ')'
 */

zFGasTax.prototype.formatNumber 
                           = function (num,dec,thou,pnt,curr1,curr2,n1,n2) {
   if ( dec == 0 ) {
      pnt = '';
   }
   var x = Math.round(num * Math.pow(10,dec));
   if (x >= 0) 
      n1=n2='';
   var y = (''+Math.abs(x)).split('');
   var z = y.length - dec; 
   if (z<0) z--; 
   for(var i = z; i < 0; i++) 
      y.unshift('0');
   y.splice(z, 0, pnt); 
   if(y[0] == pnt) 
      y.unshift('0'); 
   while (z > 3) {
      z-=3; 
      y.splice(z,0,thou);
   }
   var r = curr1+n1+y.join('')+n2+curr2;
   return r;
}
