﻿
if (!blweb) {
  var blweb = {};
}

blweb.renderResultsPage = function(rows, options) {

  var opt = options || {};
  opt.target = opt.target || "";
  opt.hiddenfields = opt.hiddenfields || blweb.HiddenFields;
  opt.rowclass1 = opt.rowclass1 || "result_row_1";
  opt.rowclass2 = opt.rowclass2 || "result_row_2";
  opt.colfnmap = opt.colfnmap || {};
  opt.defaultcolfn = opt.defaultcolfn || function(key, val, row) { return val; };
  opt.columns = options.columns || [];
  
  var firstrow = rows[0];
  var table = '<table id="' + opt.target + '-table"><tr>';
  for (var keyidx in opt.columns) {
    var key = opt.columns[keyidx];
    if (!opt.hiddenfields[key]) {
      table = table + "<th>" + key + "</th>";
    }
  }
  table = table + "</tr></table>";
  $("#" + opt.target).html(table);        
  for (var rowidx in rows) {
    var row = rows[rowidx];
    var tr = '<tr class="' + (rowidx % 2 == 0 ? opt.rowclass1 : opt.rowclass2) + '">';
    for (var keyidx in opt.columns) {
      var key = opt.columns[keyidx];
      if (!opt.hiddenfields[key]) {
        if (opt.colfnmap[key]) {
          tr = tr + "<td>" + opt.colfnmap[key](key, row[key], row) + "</td>";
        } else {
          tr = tr + "<td>" + opt.defaultcolfn(key, row[key], row) + "</td>";
        }
      }            
    }
    tr = tr + "</tr>";
    $("#" + opt.target + "-table").append(tr);
  }    
}
    
blweb.getResultsPage = function(urlroot, columns, pageid, target, options) {

  var opt = options || {};
      
  opt.renderfn = opt.renderfn || blweb.renderResultsPage;
  opt.ItemsPerPage = opt.ItemsPerPage || blweb.ItemsPerPage;
  opt.HiddenFields = opt.HiddenFields || blweb.HiddenFields;
  opt.target = target;
  opt.pageid = pageid;
  opt.columns = columns;
  opt.url = url;

  var url = "%s?columns=%s&ItemsPerPage=%s&Page=%s".sprintf(urlroot, columns.join(','), opt.ItemsPerPage, pageid);  
  $.get(url, null, function(data, status) {        
    eval("var results = " + data);
    if (results.status == 'exception') {
      alert(results.exception)
    } else {
      var rows = results.rows
      opt.renderfn(rows, opt);    
    }
  });          
}

blweb.prepareResultsMap = function(divid) {          
  if (GBrowserIsCompatible()) {
    map = new GMap2(document.getElementById(divid));
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.addControl(new GScaleControl());
    map.setCenter(new GLatLng(-22, 116), 4);
    map.enableContinuousZoom();
    map.enableScrollWheelZoom();
    
    blweb.cancelled = false;

    GMap2.prototype.addOverlays = function(overlays, step) {
        step = step || 50;  // Default to 10 if not provided
        var _addMarkers = function(map, locations, i) {
          var stop = Math.min(i+step, locations.length); 
          var percent = Math.round(((i / overlays.length) * 100));              
          $('#pointsprogress').text('Drawing points (' + percent + '% complete)');
          for (; i < stop; i++) {                
            map.addOverlay(locations[i]);
          }
          if (blweb.cancelled) {
            $('#statusbar').text("Cancelled! %d points of %d drawn".sprintf(i, overlays.length));
          } else {
            if (i < locations.length) {
              setTimeout(function(){_addMarkers(map, locations, i);},0);
            } else {
              $('#statusbar').html("");
            }
          }
        }
        var html = '<div><span id="pointsprogress" />&nbsp;<input value="Cancel" type="button" onclick="blweb.cancelled = true" /></div>';
        $('#statusbar').html(html);            
        _addMarkers(this,overlays,0);
    };
    
    $(window).unload(function() {
      GUnload();
    });
    // Now get the points...
    $.get("SpecimenResultsPage.ashx?Columns=" + blweb.MapPointColumns, null, function(data, status) {
      eval("var results = " + data);
      var records = results.rows;
      var bounds = new GLatLngBounds();
      var markers = [];
      for (var idx in records) {
        var record = records[idx];
        var point = new GLatLng(record.Latitude, record.Longitude);
        bounds.extend(point);
        var title = "%s [%s] (%0.4f,%0.4f) count: %d".sprintf(record.ScientificName, record.Locality, record.Latitude, record.Longitude, record.IndividualCount);
        var marker = new GMarker(point,{title:title});
        markers.push(marker);            
      }
      map.addOverlays(markers);
      map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
    });
  }       

}

blweb.sha1 = function(msg) {
    // constants [§4.2.1]
    var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];


    // PREPROCESSING 
 
    msg += String.fromCharCode(0x80); // add trailing '1' bit to string [§5.1.1]

    // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
    var l = Math.ceil(msg.length/4) + 2;  // long enough to contain msg plus 2-word length
    var N = Math.ceil(l/16);              // in N 16-int blocks
    var M = new Array(N);
    for (var i=0; i<N; i++) {
        M[i] = new Array(16);
        for (var j=0; j<16; j++) {  // encode 4 chars per integer, big-endian encoding
            M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) | 
                      (msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
        }
    }
    // add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1]
    // note: most significant word would be ((len-1)*8 >>> 32, but since JS converts
    // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
    M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14])
    M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;

    // set initial hash value [§5.3.1]
    var H0 = 0x67452301;
    var H1 = 0xefcdab89;
    var H2 = 0x98badcfe;
    var H3 = 0x10325476;
    var H4 = 0xc3d2e1f0;

    // HASH COMPUTATION [§6.1.2]

    var W = new Array(80); var a, b, c, d, e;
    for (var i=0; i<N; i++) {

        // 1 - prepare message schedule 'W'
        for (var t=0;  t<16; t++) W[t] = M[i][t];
        for (var t=16; t<80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);

        // 2 - initialise five working variables a, b, c, d, e with previous hash value
        a = H0; b = H1; c = H2; d = H3; e = H4;

        // 3 - main loop
        for (var t=0; t<80; t++) {
            var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
            var T = (ROTL(a,5) + f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
            e = d;
            d = c;
            c = ROTL(b, 30);
            b = a;
            a = T;
        }

        // 4 - compute the new intermediate hash value
        H0 = (H0+a) & 0xffffffff;  // note 'addition modulo 2^32'
        H1 = (H1+b) & 0xffffffff; 
        H2 = (H2+c) & 0xffffffff; 
        H3 = (H3+d) & 0xffffffff; 
        H4 = (H4+e) & 0xffffffff;
    }

    return H0.toHexStr() + H1.toHexStr() + H2.toHexStr() + H3.toHexStr() + H4.toHexStr();
}

//
// function 'f' [§4.1.1]
//
blweb.f = function(s, x, y, z) 
{
    switch (s) {
    case 0: return (x & y) ^ (~x & z);           // Ch()
    case 1: return x ^ y ^ z;                    // Parity()
    case 2: return (x & y) ^ (x & z) ^ (y & z);  // Maj()
    case 3: return x ^ y ^ z;                    // Parity()
    }
}

//
// rotate left (circular left shift) value x by n positions [§3.2.5]
//
blweb.ROTL = function(x, n)
{
    return (x<<n) | (x>>>(32-n));
}

//
// extend Number class with a tailored hex-string method 
//   (note toString(16) is implementation-dependant, and 
//   in IE returns signed numbers when used on full words)
//
Number.prototype.toHexStr = function()
{
    var s="", v;
    for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); }
    return s;
}

/* Copyright (c) 2005 Scott S. McCoy
 * This was originally a non-object oriented interface

/* Function printf(format_string,arguments...)
 * Javascript emulation of the C printf function (modifiers and argument types 
 *    "p" and "n" are not supported due to language restrictions)
 *
 * Copyright 2003 K&L Productions. All rights reserved
 * http://www.klproductions.com 
 *
 * Terms of use: This function can be used free of charge IF this header is not
 *               modified and remains with the function code.
 * 
 * Legal: Use this code at your own risk. K&L Productions assumes NO resposibility
 *        for anything.
 ********************************************************************************/

String.prototype.sprintf = function () {
  var fstring = this.toString();

  var pad = function(str,ch,len) { var ps='';
      for(var i=0; i<Math.abs(len); i++) ps+=ch;
      return len>0?str+ps:ps+str;
  }
  var processFlags = function(flags,width,rs,arg) { 
      var pn = function(flags,arg,rs) {
          if(arg>=0) { 
              if(flags.indexOf(' ')>=0) rs = ' ' + rs;
              else if(flags.indexOf('+')>=0) rs = '+' + rs;
          }
          else
              rs = '-' + rs;
          return rs;
      }
      var iWidth = parseInt(width,10);
      if(width.charAt(0) == '0') {
          var ec=0;
          if(flags.indexOf(' ')>=0 || flags.indexOf('+')>=0) ec++;
          if(rs.length<(iWidth-ec)) rs = pad(rs,'0',rs.length-(iWidth-ec));
          return pn(flags,arg,rs);
      }
      rs = pn(flags,arg,rs);
      if(rs.length<iWidth) {
          if(flags.indexOf('-')<0) rs = pad(rs,' ',rs.length-iWidth);
          else rs = pad(rs,' ',iWidth - rs.length);
      }    
      return rs;
  }
  var converters = new Array();
  converters['c'] = function(flags,width,precision,arg) { 
      if (typeof(arg) == 'number') return String.fromCharCode(arg);
      if (typeof(arg) == 'string') return arg.charAt(0);
      return '';
  }
  converters['d'] = function(flags,width,precision,arg) { 
      return converters['i'](flags,width,precision,arg); 
  }
  converters['u'] = function(flags,width,precision,arg) { 
      return converters['i'](flags,width,precision,Math.abs(arg)); 
  }
  converters['i'] =  function(flags,width,precision,arg) {
      var iPrecision=parseInt(precision);
      var rs = ((Math.abs(arg)).toString().split('.'))[0];
      if(rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
      return processFlags(flags,width,rs,arg); 
  }
  converters['E'] = function(flags,width,precision,arg) {
      return (converters['e'](flags,width,precision,arg)).toUpperCase();
  }
  converters['e'] = function(flags,width,precision,arg) {
      iPrecision = parseInt(precision);
      if(isNaN(iPrecision)) iPrecision = 6;
      rs = (Math.abs(arg)).toExponential(iPrecision);
      if(rs.indexOf('.')<0 && flags.indexOf('#')>=0) rs = rs.replace(/^(.*)(e.*)$/,'$1.$2');
      return processFlags(flags,width,rs,arg);        
  }
  converters['f'] = function(flags,width,precision,arg) { 
      iPrecision = parseInt(precision);
      if(isNaN(iPrecision)) iPrecision = 6;
      rs = (Math.abs(arg)).toFixed(iPrecision);
      if(rs.indexOf('.')<0 && flags.indexOf('#')>=0) rs = rs + '.';
      return processFlags(flags,width,rs,arg);
  }
  converters['G'] = function(flags,width,precision,arg) { 
      return (converters['g'](flags,width,precision,arg)).toUpperCase();
  }
  converters['g'] = function(flags,width,precision,arg) {
      iPrecision = parseInt(precision);
      absArg = Math.abs(arg);
      rse = absArg.toExponential();
      rsf = absArg.toFixed(6);
      if(!isNaN(iPrecision)) { 
          rsep = absArg.toExponential(iPrecision);
          rse = rsep.length < rse.length ? rsep : rse;
          rsfp = absArg.toFixed(iPrecision);
          rsf = rsfp.length < rsf.length ? rsfp : rsf;
      }
      if(rse.indexOf('.')<0 && flags.indexOf('#')>=0) rse = rse.replace(/^(.*)(e.*)$/,'$1.$2');
      if(rsf.indexOf('.')<0 && flags.indexOf('#')>=0) rsf = rsf + '.';
      rs = rse.length<rsf.length ? rse : rsf;
      return processFlags(flags,width,rs,arg);        
  }  
  converters['o'] = function(flags,width,precision,arg) { 
      var iPrecision=parseInt(precision);
      var rs = Math.round(Math.abs(arg)).toString(8);
      if(rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
      if(flags.indexOf('#')>=0) rs='0'+rs;
      return processFlags(flags,width,rs,arg); 
  }
  converters['X'] = function(flags,width,precision,arg) { 
      return (converters['x'](flags,width,precision,arg)).toUpperCase();
  }
  converters['x'] = function(flags,width,precision,arg) { 
      var iPrecision=parseInt(precision);
      arg = Math.abs(arg);
      var rs = Math.round(arg).toString(16);
      if(rs.length<iPrecision) rs=pad(rs,' ',iPrecision - rs.length);
      if(flags.indexOf('#')>=0) rs='0x'+rs;
      return processFlags(flags,width,rs,arg); 
  }
  converters['s'] = function(flags,width,precision,arg) { 
      var iPrecision=parseInt(precision);
      var rs = arg;
      if(rs.length > iPrecision) rs = rs.substring(0,iPrecision);
      return processFlags(flags,width,rs,0);
  }

  farr = fstring.split('%');
  retstr = farr[0];
  fpRE = /^([-+ #]*)(\d*)\.?(\d*)([cdieEfFgGosuxX])(.*)$/;
  for(var i = 1; i<farr.length; i++) { 
      fps=fpRE.exec(farr[i]);
      if(!fps) continue;
      if(arguments[i-1]!=null) {
          retstr+=converters[fps[4]](fps[1],fps[2],fps[3],arguments[i-1]);
      }
      retstr += fps[5];
  }
  return retstr;
}

