
var screenWidth = 800.0;

var selectedTab = 0;

// Keys are state names, values are state abbreviations:
var stateNames = {"PNW": "PNW", "Alaska": "AK", "British Columbia": "BC", "Idaho": "ID", "Montana": "MT", "Oregon": "OR", "Washington": "WA", "Yukon Territory": "YK"};

// Keys are state abbreviations, values are state names:
var stateCodes = {"PNW": "PNW", "AK": "Alaska", "BC": "British Columbia", "ID": "Idaho", "MT": "Montana", "OR": "Oregon", "WA": "Washington", "YK": "Yukon Territory"};

var gridSize = 0.25; // In degrees longitude/latitude.

// latRefs map degrees latitude to pixels on map image, ordered from south to north.
// lngRefs map degrees longitude to pixels on map image, ordered from west to east.
// Each county entry is an array withh pixel dimensions and position (not scaled): width, height, left, top
var mapConfig = {
  ak: {
    width: 1600,
    height: 1226,
    displayWidth: 600,
    latRefs: [[52.090895, 1162],[71.26972, 20]],
    lngRefs: [[-177.512148, 293],[-130.943850, 1560]]
},
  bc: {
    width: 1600,
    height: 1336,
    displayWidth: 600,
    latRefs: [[49.131408, 1220],[56.735873, 428]],
    lngRefs: [[-131.721954, 470],[-122.098618, 1081]]
  },
  id: {
    width: 1200,
    height: 1900,
    displayWidth: 400,
    latRefs: [[43.396753, 1519],[46.51638, 713]],
    lngRefs: [[-116.589575, 146],[-113.021636, 800]],
    ada: [104, 181, 157, 1412],
    adams: [146, 218, 92, 1038],
    bannock: [164, 197, 848, 1609],
    bearlake: [112, 151, 1053, 1717],
    benewah: [137, 111, 60, 470],
    bingham: [267, 197, 799, 1458],
    blaine: [370, 348, 436, 1365],
    boise: [243, 197, 201, 1276],
    bonner: [188, 269, 60, 78],
    bonneville: [275, 158, 890, 1458],
    boundary: [185, 144, 62, 35],
    butte: [202, 246, 661, 1304],
    camas: [135, 170, 419, 1401],
    canyon: [108, 148, 63, 1398],
    caribou: [206, 156, 959, 1610],
    cassia: [240, 176, 565, 1695],
    clark: [258, 157, 803, 1220],
    clearwater: [341, 181, 168, 600],
    custer: [366, 330, 380, 1143],
    elmore: [230, 335, 214, 1344],
    franklin: [116, 112, 964, 1757],
    fremont: [209, 223, 956, 1176],
    gem: [107, 181, 121, 1237],
    gooding: [96, 140, 419, 1566],
    idaho: [457, 414, 107, 670],
    jefferson: [203, 116, 857, 1349],
    jerome: [131, 93, 505, 1652],
    kootenai: [138, 176, 60, 313],
    latah: [137, 162, 60, 547],
    lemhi: [373, 384, 468, 926],
    lewis: [130, 130, 120, 723],
    lincoln: [167, 114, 509, 1566],
    madison: [111, 83, 989, 1382],
    minidoka: [101, 174, 630, 1566],
    nezperce: [133, 209, 57, 680],
    oneida: [176, 130, 801, 1739],
    owyhee: [371, 424, 63, 1446],
    payette: [101, 97, 73, 1324],
    power: [162, 201, 757, 1588],
    shoshone: [251, 312, 191, 293],
    teton: [70, 121, 1095, 1369],
    twinfalls: [183, 234, 427, 1636],
    valley: [279, 274, 212, 1057],
    washington: [174, 182, 25, 1149],
  },
  or: {
    width: 1600,
    height: 1182,
    displayWidth: 800,
    latRefs: [[43.11693, 863],[44.976275, 370]],
    lngRefs: [[-122.745395, 384],[-118.503513, 1194]],
    baker: [334, 228, 1189, 340],
    benton: [147, 124, 176, 437],
    clackamas: [238, 163, 357, 236],
    clatsop: [155, 144, 115, 14],
    columbia: [122, 135, 262, 39],
    coos: [170, 250, 36, 750],
    crook: [284, 237, 693, 479],
    curry: [181, 254, 18, 901],
    deschutes: [406, 215, 525, 523],
    douglas: [447, 332, 85, 642],
    gilliam: [175, 207, 783, 144],
    grant: [283, 284, 967, 362],
    harney: [341, 542, 916, 615],
    hoodriver: [100, 134, 537, 165],
    jackson: [188, 263, 288, 891],
    jefferson: [286, 124, 552, 408],
    josephine: [163, 199, 133, 957],
    klamath: [275, 429, 468, 728],
    lake: [387, 429, 647, 729],
    lane: [478, 232, 96, 551],
    lincoln: [121, 214, 104, 349],
    linn: [282, 165, 286, 417],
    malheur: [262, 646, 1242, 510],
    marion: [281, 166, 298, 287],
    morrow: [171, 259, 904, 111],
    multnomah: [212, 86, 345, 166],
    polk: [139, 102, 193, 342],
    sherman: [129, 182, 710, 164],
    tillamook: [152, 206, 124, 150],
    umatilla: [286, 280, 1013, 90],
    union: [281, 251, 1153, 128],
    wallowa: [296, 257, 1291, 89],
    wasco: [274, 246, 560, 171],
    washington: [149, 133, 240, 150],
    wheeler: [167, 210, 812, 343],
    yamhill: [183, 104, 181, 245],
  },
  mt: {
    width: 1600,
    height: 908,
    displayWidth: 800,
    latRefs: [[46.188803, 561],[47.661977, 283]],
    lngRefs: [[-113.021207, 418],[-107.441311, 1137]],
    beaverhead: [321, 283, 299, 607],
    bighorn: [319, 196, 971, 585],
    blaine: [174, 251, 861, 23],
    broadwater: [99, 174, 574, 602],
    carbon: [220, 116, 830, 664],
    carter: [133, 212, 1442, 568],
    cascade: [186, 168, 541, 275],
    chouteau: [246, 175, 623, 157],
    custer: [193, 204, 1294, 433],
    daniels: [157, 90, 1299, 23],
    dawson: [147, 181, 1395, 257],
    deerlodge: [121, 96, 356, 543],
    fallon: [116, 155, 1459, 465],
    fergus: [249, 215, 777, 254],
    flathead: [278, 275, 159, 24],
    gallatin: [131, 317, 577, 557],
    garfield: [245, 217, 1067, 223],
    glacier: [245, 139, 283, 23],
    goldenvalley: [118, 136, 849, 453],
    granite: [112, 170, 307, 438],
    hill: [171, 173, 707, 23],
    jefferson: [124, 156, 472, 488],
    judithbasin: [139, 143, 703, 328],
    lake: [134, 179, 212, 206],
    lewisandclark: [220, 290, 398, 229],
    liberty: [91, 173, 623, 23],
    lincoln: [188, 218, 26, 23],
    madison: [178, 213, 457, 620],
    mccone: [164, 194, 1263, 199],
    meagher: [180, 173, 594, 391],
    mineral: [173, 150, 67, 315],
    missoula: [195, 187, 190, 293],
    musselshell: [164, 121, 931, 452],
    park: [165, 225, 670, 557],
    petroleum: [119, 164, 967, 294],
    phillips: [225, 304, 947, 23],
    pondera: [250, 101, 378, 124],
    powderriver: [172, 150, 1283, 631],
    powell: [156, 255, 358, 294],
    prairie: [196, 125, 1307, 372],
    ravalli: [137, 226, 218, 470],
    richland: [158, 156, 1417, 188],
    roosevelt: [237, 114, 1338, 108],
    rosebud: [230, 315, 1078, 434],
    sanders: [243, 222, 27, 166],
    sheridan: [134, 123, 1440, 23],
    silverbow: [111, 105, 412, 560],
    stillwater: [159, 182, 796, 569],
    sweetgrass: [117, 196, 767, 552],
    teton: [209, 125, 419, 191],
    toole: [124, 156, 522, 23],
    treasure: [106, 117, 1097, 504],
    valley: [212, 263, 1137, 23],
    wheatland: [120, 103, 768, 454],
    wibaux: [77, 148, 1498, 331],
    yellowstone: [192, 192, 942, 504],
  },
  wa: {
    width: 1600,
    height: 1034,
    displayWidth: 800,
    latRefs: [[46.626983, 712],[48.268426, 232]],
    lngRefs: [[-122.463613, 487],[-118.562651, 1261]],
    adams: [284, 157, 1099, 528],
    asotin: [115, 129, 1473, 767],
    benton: [189, 257, 1000, 683],
    chelan: [264, 384, 741, 148],
    clallam: [393, 190, 12, 165],
    clark: [113, 150, 421, 874],
    columbia: [131, 183, 1322, 712],
    cowlitz: [198, 155, 336, 779],
    douglas: [274, 276, 911, 267],
    ferry: [156, 353, 1198, 12],
    franklin: [251, 160, 1083, 678],
    garfield: [133, 206, 1398, 689],
    grant: [216, 395, 966, 321],
    graysharbor: [266, 221, 87, 447],
    island: [111, 172, 406, 189],
    jefferson: [424, 254, 41, 203],
    king: [296, 207, 471, 376],
    kitsap: [124, 169, 373, 322],
    kittitas: [308, 253, 685, 431],
    klickitat: [350, 129, 655, 877],
    lewis: [404, 123, 305, 662],
    lincoln: [235, 209, 1176, 323],
    mason: [144, 158, 280, 427],
    okanogan: [408, 320, 801, 12],
    pacific: [166, 165, 147, 663],
    pendoreille: [122, 290, 1444, 12],
    pierce: [297, 200, 410, 487],
    sanjuan: [116, 162, 327, 66],
    skagit: [415, 113, 428, 116],
    skamania: [150, 241, 528, 780],
    snohomish: [307, 159, 491, 223],
    spokane: [160, 236, 1405, 296],
    stevens: [195, 363, 1292, 12],
    thurston: [188, 129, 339, 548],
    wahkiakum: [107, 74, 235, 781],
    wallawalla: [212, 178, 1166, 717],
    whatcom: [523, 118, 323, 11],
    whitman: [243, 248, 1323, 528],
    yakima: [333, 305, 672, 578]
  },
  yk: {
    width: 1200,
    height: 1624,
    displayWidth: 600,
    latRefs: [[60.0, 1578],[69.646818, 47]],
    lngRefs: [[-141.004580, 47],[-123.801363, 1196]]
  },
  pnw: {
    width: 1600,
    height: 1118,
    displayWidth: 800,
    latRefs: [[41.998458, 1088],[44.675168, 792],[47.594471, 454],[50.776418, 65]],
    lngRefs: [[-128.42348, 29],[-122.236463, 526],[-116.049787, 1024],[-111.055814, 1425]],
    idada: [46, 79, 985, 889],
    idadams: [63, 96, 958, 724],
    idbannock: [71, 86, 1289, 975],
    idbearlake: [49, 66, 1378, 1022],
    idbenewah: [60, 49, 943, 475],
    idbingham: [117, 86, 1267, 909],
    idblaine: [162, 153, 1108, 868],
    idboise: [106, 85, 1005, 830],
    idbonner: [82, 118, 943, 303],
    idbonneville: [121, 69, 1307, 909],
    idboundary: [81, 64, 943, 284],
    idbutte: [89, 108, 1206, 841],
    idcamas: [59, 74, 1100, 884],
    idcanyon: [47, 65, 944, 882],
    idcaribou: [91, 69, 1337, 975],
    idcassia: [105, 77, 1165, 1013],
    idclark: [114, 69, 1268, 804],
    idclearwater: [150, 79, 990, 532],
    idcuster: [161, 143, 1083, 771],
    idelmore: [100, 145, 1011, 860],
    idfranklin: [51, 48, 1339, 1041],
    idfremont: [91, 98, 1336, 785],
    idgem: [47, 79, 970, 813],
    idgooding: [42, 61, 1100, 956],
    ididaho: [200, 182, 964, 563],
    idjefferson: [89, 50, 1292, 861],
    idjerome: [58, 40, 1138, 944],
    idkootenai: [60, 77, 943, 406],
    idlatah: [60, 71, 943, 509],
    idlemhi: [164, 169, 1122, 675],
    idlewis: [56, 57, 970, 586],
    idlincoln: [73, 50, 1140, 956],
    idmadison: [49, 35, 1350, 876],
    idminidoka: [44, 76, 1193, 956],
    idnezperce: [58, 91, 942, 568],
    idoneida: [77, 57, 1268, 1032],
    idowyhee: [163, 186, 944, 903],
    idpayette: [44, 43, 949, 850],
    idpower: [72, 88, 1248, 965],
    idshoshone: [106, 137, 1000, 398],
    idteton: [30, 52, 1397, 870],
    idtwinfalls: [80, 102, 1104, 987],
    idvalley: [123, 120, 1009, 733],
    idwashington: [76, 79, 927, 773],
    mtbeaverhead: [200, 177, 1193, 648],
    mtblaine: [55, 157, 1545, 284],
    mtbroadwater: [61, 106, 1365, 550],
    mtcarbon: [75, 72, 1525, 685],
    mtcascade: [115, 105, 1345, 441],
    mtchouteau: [153, 108, 1396, 369],
    mtdeerlodge: [75, 60, 1229, 610],
    mtfergus: [108, 133, 1492, 429],
    mtflathead: [173, 171, 1106, 284],
    mtgallatin: [81, 197, 1367, 618],
    mtglacier: [153, 87, 1183, 284],
    mtgoldenvalley: [63, 85, 1537, 553],
    mtgranite: [70, 105, 1198, 544],
    mthill: [106, 108, 1449, 284],
    mtjefferson: [76, 96, 1302, 575],
    mtjudithbasin: [86, 89, 1446, 475],
    mtlake: [83, 111, 1139, 399],
    mtlewisandclark: [137, 182, 1255, 412],
    mtliberty: [56, 108, 1396, 284],
    mtlincoln: [116, 136, 1023, 284],
    mtmadison: [111, 133, 1292, 657],
    mtmeagher: [111, 107, 1378, 514],
    mtmineral: [106, 93, 1050, 467],
    mtmissoula: [122, 116, 1124, 453],
    mtpark: [103, 140, 1425, 618],
    mtpondera: [157, 63, 1241, 347],
    mtpowell: [97, 159, 1230, 453],
    mtravalli: [84, 140, 1143, 564],
    mtsanders: [152, 138, 1023, 374],
    mtsilverbow: [68, 65, 1265, 620],
    mtstillwater: [95, 113, 1505, 625],
    mtsweetgrass: [72, 122, 1486, 615],
    mtteton: [130, 78, 1268, 389],
    mttoole: [76, 97, 1333, 285],
    mtwheatland: [75, 64, 1486, 554],
    orbaker: [139, 95, 825, 746],
    orbenton: [62, 51, 398, 787],
    orclackamas: [100, 68, 474, 702],
    orclatsop: [64, 60, 373, 609],
    orcolumbia: [52, 56, 434, 619],
    orcoos: [71, 104, 339, 911],
    orcrook: [119, 99, 616, 804],
    orcurry: [75, 106, 332, 983],
    ordeschutes: [171, 90, 545, 823],
    ordouglas: [187, 139, 360, 873],
    orgilliam: [65, 86, 663, 664],
    orgrant: [118, 119, 732, 755],
    orharney: [143, 227, 710, 862],
    orhoodriver: [42, 56, 550, 672],
    orjackson: [79, 110, 445, 978],
    orjefferson: [119, 51, 557, 775],
    orjosephine: [68, 83, 380, 1006],
    orklamath: [115, 179, 521, 910],
    orlake: [163, 179, 596, 910],
    orlane: [200, 97, 365, 835],
    orlincoln: [50, 89, 368, 750],
    orlinn: [118, 68, 445, 779],
    ormalheur: [110, 272, 847, 817],
    ormarion: [118, 69, 449, 724],
    ormorrow: [72, 108, 704, 650],
    ormultnomah: [88, 35, 470, 673],
    orpolk: [58, 42, 405, 747],
    orsherman: [54, 76, 623, 672],
    ortillamook: [63, 87, 377, 665],
    orumatilla: [119, 118, 751, 640],
    orunion: [118, 105, 810, 657],
    orwallowa: [124, 108, 868, 640],
    orwasco: [113, 102, 560, 675],
    orwashington: [61, 55, 426, 666],
    orwheeler: [70, 88, 666, 747],
    oryamhill: [77, 43, 400, 706],
    waadams: [116, 65, 756, 493],
    waasotin: [46, 52, 908, 591],
    wabenton: [77, 103, 715, 557],
    wachelan: [107, 155, 611, 341],
    waclallam: [160, 77, 314, 347],
    waclark: [47, 60, 480, 635],
    wacolumbia: [54, 71, 846, 572],
    wacowlitz: [82, 60, 446, 596],
    wadouglas: [112, 112, 679, 388],
    waferry: [64, 144, 796, 284],
    wafranklin: [99, 65, 752, 555],
    wagarfield: [53, 84, 877, 559],
    wagrant: [88, 160, 702, 410],
    wagraysharbor: [108, 90, 345, 461],
    waisland: [45, 70, 475, 356],
    wajefferson: [172, 103, 327, 362],
    waking: [119, 84, 501, 432],
    wakitsap: [49, 69, 463, 410],
    wakittitas: [125, 103, 588, 454],
    waklickitat: [143, 52, 575, 636],
    walewis: [163, 50, 434, 548],
    walincoln: [96, 85, 787, 411],
    wamason: [59, 65, 423, 452],
    waokanogan: [165, 131, 636, 284],
    wapacific: [68, 68, 370, 548],
    wapendoreille: [51, 118, 895, 284],
    wapierce: [121, 81, 476, 477],
    wasanjuan: [47, 64, 443, 307],
    waskagit: [168, 46, 483, 326],
    waskamania: [61, 97, 524, 596],
    wasnohomish: [125, 65, 509, 370],
    waspokane: [65, 96, 880, 400],
    wastevens: [79, 148, 835, 284],
    wathurston: [74, 52, 448, 502],
    wawahkiakum: [44, 30, 405, 596],
    wawallawalla: [86, 72, 783, 571],
    wawhatcom: [212, 48, 441, 284],
    wawhitman: [98, 100, 847, 494],
    wayakima: [135, 124, 583, 514]
  }
};


$(document).ready(function()
{
  // Set map tab:
  var s = 0;
  if(selectedMap != '')
  {
    for(var t = 0; t < mapTabs.length; t++) if(mapTabs[t] == selectedMap) { s = t+1; break; }
  }
  if(s > 0) showTab(s);
  else showTab(1);
  
  // If all specimens are from a single state/province, then expand its list:
  if(mapTabs.length == 2)
  {
    var state = stateCodes[mapTabs[1]];
    toggleStateList(1, state);
  }
});

function showTab(t)
{
  if(t < 1) return;
  
  if(selectedTab > 0) $('#tab'+selectedTab).removeClass('tabsel');
  
  selectedTab = t;
  $('#tab'+t).addClass('tabsel');
  
  showMap(t);
};

function showMap(t)
{
  var code = mapTabs[t-1];  // Abbreviation for state name or "PNW"
  if(typeof code == 'undefined' || code == '') return;
  
  var state = stateCodes[code];  // Full name of state or "PNW"
  if(typeof state == 'undefined' || state == '') return;
  
  var codeLower = code.toLowerCase();
  
  var config = mapConfig[codeLower];
  if(typeof config == 'undefined') return;
  
  var scale = config.width / config.displayWidth;
  
  var mapWidth = config.displayWidth;
  var mapHeight = Math.round(config.height / scale);
  
  var dotSize = 28;
  if(code == "PNW") dotSize = 20;
  
  var squareType = "thin";
  if(code == "PNW") squareType = "thick";
  
  // Pixel size of grid square for displaying specimens whose locality has been hidden (not adjusted for scale):
  var cLat = config.latRefs.length-1;
  var cLng = config.lngRefs.length-1;
  var squareWidth = Math.abs(Math.round((config.lngRefs[cLng][1] - config.lngRefs[0][1]) / (config.lngRefs[cLng][0] - config.lngRefs[0][0]) * gridSize));
  var squareHeight = Math.abs(Math.round((config.latRefs[cLat][1] - config.latRefs[0][1]) / (config.latRefs[cLat][0] - config.latRefs[0][0]) * gridSize));
  //var squareWidth = Math.abs(Math.round((config.ref2X - config.ref1X) / (config.ref2Long - config.ref1Long) * gridSize));
  //var squareHeight = Math.abs(Math.round((config.ref2Y - config.ref1Y) / (config.ref2Lat - config.ref1Lat) * gridSize));
  
  // Base map:
  var html = "<img src='../../maps/"+codeLower+".jpg' width='"+mapWidth+"px' height='"+mapHeight+"px' />\r\n";
  
  var selCounties = [];
  
  // County overlays:
  for(var i = 0; i < states.length; i++)
  {
    var includeCounty = false;
    if(code != "PNW" && states[i] == state) includeCounty = true;
    else if(code == "PNW" && (states[i] == "Washington" || states[i] == "Oregon" || states[i] == "Idaho" || states[i] == "Montana" || states[i] == "British Columbia")) includeCounty = true;
    
    if(includeCounty)
    {
      if(counties[i] != '')
      {
        var countyCode = counties[i].toLowerCase().replace("/", "").replace(/ /g, "");
        var stateCode = stateNames[states[i]].toLowerCase();
        if(code == "PNW") countyCode = stateCode + countyCode;
        
        var pos = config[countyCode];
        if(pos && !selCounties[countyCode])
        {
          selCounties[countyCode] = true;
          var width = Math.round(pos[0]/scale);
          var height = Math.round(pos[1]/scale);
          var left = Math.round(pos[2]/scale);
          var top = Math.round(pos[3]/scale);
          html += "<img src='../../maps/"+codeLower+"counties/"+countyCode+".png' width='"+width+"px' height='"+height+"px' style='position: absolute; left: "+left+"px; top: "+top+"px;' />\r\n";
        }
      }
    }
  }
  
  // Specimen markers:
  var dot = '';
  var dotHTML = "";
  var selHTML = "";
  for(var i = 0; i < states.length; i++)
  {
    var includeSpecimen = false;
    if(code != "PNW" && states[i] == state) includeSpecimen = true;
    else if(code == "PNW" && (states[i] == "Washington" || states[i] == "Oregon" || states[i] == "Idaho" || states[i] == "Montana" || states[i] == "British Columbia")) includeSpecimen = true;
    
    if(includeSpecimen)
    {
      var latLng = coords[i];
      if(latLng != null)
      {
        // Convert lat/lng to pixel position on map (not scaled):
        var pix = latLngToPixel(latLng, config);
        var left = pix[0];
        var top = pix[1];
        //var left = Math.round(config.ref1X + (latLng[1] - config.ref1Long) / (config.ref2Long - config.ref1Long) * (config.ref2X - config.ref1X));
        //var top = Math.round(config.ref1Y + (latLng[0] - config.ref1Lat) / (config.ref2Lat - config.ref1Lat) * (config.ref2Y - config.ref1Y));
        
        //left = Math.floor(left / scale);
        //top = Math.floor(top / scale);
        
        // Adjust pixel position to upper left corner of marker, and apply scale:
        if(listed[i] == 'Y') 
        {
          left = Math.floor(left / scale);
          top = Math.floor((top - squareHeight) / scale);
          
          var markerWidth = Math.round(squareWidth/scale);
          var markerHeight = Math.round(squareHeight/scale);
        }
        else
        {
          left = Math.floor((left - dotSize/2) / scale);
          top = Math.floor((top - dotSize/2) / scale);
          
          var markerWidth = Math.round(dotSize/scale);
          var markerHeight = Math.round(dotSize/scale);
        }
        
        if(left >= 0 && left <= mapWidth && top >= 0 && top <= mapHeight)
        {
          var selected = false;
          if(providers[i] == selectedHerbarium || selectedHerbarium == '') selected = true;
          
          var dotColor = 'orange';
          if(selected) dotColor = 'green';
          if(latLng[2] == 'N') dotColor = 'gray';
          if(latLng[3] != null)
          {
            if(latLng[3] > 10000) dotColor = 'gray';
          }
          
          if(listed[i] == 'Y') dot = "<img src='../../maps/square-"+dotColor+"-"+squareType+".png' width='"+markerWidth+"px' height='"+markerHeight+"px' style='position: absolute; left: "+left+"px; top: "+top+"px;' />\r\n";
          else dot = "<img src='../../maps/dot-"+dotSize+"-"+dotColor+".png' width='"+markerWidth+"px' height='"+markerHeight+"px' style='position: absolute; left: "+left+"px; top: "+top+"px;' />\r\n";
          
          if(selected) selHTML += dot;
          else dotHTML += dot;
        }
      }
    }
  }
  html += dotHTML + selHTML;
  
  $('#map').empty().append(html, config);
  
  if(code == selectedMap && mapScrollPos > 0)
  {
    var w = $(window).width();
    var s = mapScrollPos;
    if(s + w > mapWidth) s = mapWidth - w;
    $('#map').scrollLeft(s);
  }
  else $('#map').scrollLeft(0);
};

function latLngToPixel(latLng, config)
{
  // Determine which reference points to use, based on which coordinates the point falls between:
  //
  var latRefCount = config.latRefs.length;
  var lngRefCount = config.lngRefs.length;
  var latRef1 = config.latRefs[0];  // Default to using the first and last (farthest apart) points.
  var latRef2 = config.latRefs[latRefCount - 1];
  var lngRef1 = config.lngRefs[0];
  var lngRef2 = config.lngRefs[lngRefCount - 1];
  if(latRefCount > 2)
  {
    for(l = 0; l < latRefCount - 1; l++)
    {
      if(config.latRefs[l][0] < latLng[0] && config.latRefs[l][0] > latRef1[0]) latRef1 = config.latRefs[l];
    }
    for(l = latRefCount - 1; l > 0; l--)
    {
      if(config.latRefs[l][0] > latLng[0] && config.latRefs[l][0] < latRef2[0]) latRef2 = config.latRefs[l];
    }
  }
  if(lngRefCount > 2)
  {
    for(l = 0; l < lngRefCount - 1; l++)
    {
      if(config.lngRefs[l][0] < latLng[1] && config.lngRefs[l][0] > lngRef1[0]) lngRef1 = config.lngRefs[l];
    }
    for(l = lngRefCount - 1; l > 0; l--)
    {
      if(config.lngRefs[l][0] > latLng[1] && config.lngRefs[l][0] < lngRef2[0]) lngRef2 = config.lngRefs[l];
    }
  }
    
  var left = Math.floor(lngRef1[1] + (latLng[1] - lngRef1[0]) / (lngRef2[0] - lngRef1[0]) * (lngRef2[1] - lngRef1[1]));
  var top  = Math.floor(latRef1[1] + (latLng[0] - latRef1[0]) / (latRef2[0] - latRef1[0]) * (latRef2[1] - latRef1[1]));
  
  return [left, top];
};

function toggleStateList(id, state)
{
  if($('#stateheader'+id).hasClass('list1ArrowDown'))
  {
    $('#stateheader'+id).removeClass('list1ArrowDown').addClass('list1ArrowRight');
    $('#statelist'+id).empty().removeClass('subListVisible').addClass('subList');
  }
  else
  {
    $('#stateheader'+id).removeClass('list1ArrowRight').addClass('list1ArrowDown');
    $('#statelist'+id).empty().removeClass('subList').addClass('subListVisible');
    
    var countyID = 0;
    var countyCount = 0;
    var countySpecimenCount = 0;
    var countyHerbaria = "";
    var lastCounty = "";
    var html = "";
    for(var i = 0; i < states.length; i++)
    {
      if(states[i] == state)
      {
        var county = counties[i];
        if(county == '') county = 'No County';
        
        if(county == lastCounty)
        {
          countySpecimenCount++;
          if(countyHerbaria.indexOf(providers[i] + ", ") == -1) countyHerbaria = countyHerbaria + providers[i] + ", ";
        }
        else
        {
          if(countySpecimenCount > 0) html += "<div id='countyheader"+id+"c"+countyID+"' class='list2ArrowRight' onclick='toggleCountyList(\""+id+"c"+countyID+"\", \""+state+"\", \""+lastCounty+"\");'>"+lastCounty+" ("+countySpecimenCount+")</div>\r\n<div id='countylist"+id+"c"+countyID+"' class='subList'></div>\r\n";
          
          countyCount++;
          countyID++;
          countySpecimenCount = 1;
          countyHerbaria = "";
        }
        
        lastCounty = county;
      }
    }
    if(countySpecimenCount > 0)  // Catch the last (or only) county:
    {
      html += "<div id='countyheader"+id+"c"+countyID+"' class='list2ArrowRight' onclick='toggleCountyList(\""+id+"c"+countyID+"\", \""+state+"\", \""+lastCounty+"\");'>"+lastCounty+" ("+countySpecimenCount+")</div>\r\n<div id='countylist"+id+"c"+countyID+"' class='subList'></div>\r\n";
    }
    
    $('#statelist'+id).append(html);
    
    // If all specimens are from a single county, then expand its list:
    if(countyCount == 1) toggleCountyList(id+'c1', state, lastCounty);
  }
};

function toggleCountyList(id, state, county)
{
  if($('#countyheader'+id).hasClass('list2ArrowDown'))
  {
    $('#countyheader'+id).removeClass('list2ArrowDown').addClass('list2ArrowRight');
    $('#countylist'+id).empty().removeClass('subListVisible').addClass('subList');
  }
  else
  {
    $('#countyheader'+id).removeClass('list2ArrowRight').addClass('list2ArrowDown');
    $('#countylist'+id).empty().removeClass('subList').addClass('subListVisible');
    
    var html = "";
    
    for(var i = 0; i < states.length; i++)
    {
      if(states[i] == state)
      {
        var c = counties[i];
        if(c == '') c = 'No County';
        
        if(c == county)
        {
          // Insert a link to zoomable map view for this specimen:
          var mapLink = "";
          if(coords[i] != null && listed[i] != "Y")
          {
            mapLink = "<a href='http://www.pnwherbaria.org/m/datasets/specimenmap.php?Lat="+coords[i][0]+"&Lng="+coords[i][1]+"' target='SpecimenMap'><img src='../../graphics/map_48.png' class='mapLink' /></a>";
            if(coords[i][2] == 'N') mapLink += "<img src='../../graphics/warning.png' class='invalidcoordIcon' alt='Caution: the coordinates for this specimen appear to be incorrect.' />";
          }
          
          html += "<div class='specimenLabel'>"+mapLink+labels[i]+"</div>";
        }
      }
    }
    
    $('#countylist'+id).append(html);
  }
};

function toggleImageList(id, provider)
{
  if($('#imageheader'+id).hasClass('list1ArrowDown'))
  {
    $('#imageheader'+id).removeClass('list1ArrowDown').addClass('list1ArrowRight');
    $('#imagelist'+id).empty().removeClass('subListVisible').addClass('subList');
  }
  else
  {
    $('#imageheader'+id).removeClass('list1ArrowRight').addClass('list1ArrowDown');
    $('#imagelist'+id).empty().removeClass('subList').addClass('subListVisible');
    
    var imageCount = images.length;
    var providerImageCount = 0;
    var pageSize = 50;
    var pageStart = 1;
    var pageEnd = pageStart+pageSize-1;
    
    var html = "";
    for(var i = 0; i < imageCount; i++)  // This loops through all images from all providers.
    {
      if(images[i] != null && providers[i] == provider)
      {
        providerImageCount++;
        
        if(providerImageCount >= pageStart && providerImageCount <= pageEnd)
        {
          var thumbnailURL = images[i][0];
          var imageURL = images[i][1];
          
          if(thumbnailURL != '') html += "<a href='"+imageURL+"' target='SpecimenImage'><img id='thumbnail_"+id+"_"+i+"' class='thumbnail' src='"+thumbnailURL+"' /></a>\r\n";
        }
      }
    }
    html += "<div id='showMoreButtonSpacer"+id+"' style='clear: both; height: 16px;'></div>";
    
    if(providerImageCount > pageSize)
    {
      html += "<div id='showMoreButton"+id+"' class='showMoreButton' onclick='showMoreImages(\""+id+"\", \""+provider+"\");'>Show more images</div>";
    }
    html += "<div style='clear: both; height: 16px; border-bottom: 1px solid #98A685;'></div>";
    
    $('#imagelist'+id).append(html);
  }
};

function showMoreImages(id, provider)
{
  var imageCount = images.length;
  var providerImageCount = 0;
  var pageSize = 50;
  
  var pageStart = $("img[id^='thumbnail_"+id+"']").length+1;
  var pageEnd = pageStart+pageSize-1;
  
  var html = "";
  for(var i = 0; i < imageCount; i++)  // This loops through all images from all providers.
  {
    if(images[i] != null && providers[i] == provider)
    {
      providerImageCount++;
      
      if(providerImageCount >= pageStart && providerImageCount <= pageEnd)
      {
        var thumbnailURL = images[i][0];
        var imageURL = images[i][1];
        
        if(thumbnailURL != '') html += "<a href='"+imageURL+"' target='SpecimenImage'><img id='thumbnail_"+id+"_"+i+"' class='thumbnail' src='"+thumbnailURL+"' /></a>\r\n";
      }
    }
  }
  $('#showMoreButtonSpacer'+id).before(html);
  
  if(pageEnd >= providerImageCount) $('#showMoreButton'+id).css({'display': 'none'});
};
