1// console.log shortcut 2window.d = console.log 3// States 4var states = [{ 5 "name": "Alabama", 6 "abbreviation": "AL" 7}, { 8 "name": "Alaska", 9 "abbreviation": "AK" 10}, { 11 "name": "American Samoa", 12 "abbreviation": "AS" 13}, { 14 "name": "Arizona", 15 "abbreviation": "AZ" 16}, { 17 "name": "Arkansas", 18 "abbreviation": "AR" 19}, { 20 "name": "California", 21 "abbreviation": "CA" 22}, { 23 "name": "Colorado", 24 "abbreviation": "CO" 25}, { 26 "name": "Connecticut", 27 "abbreviation": "CT" 28}, { 29 "name": "Delaware", 30 "abbreviation": "DE" 31}, { 32 "name": "District Of Columbia", 33 "abbreviation": "DC" 34}, { 35 "name": "Federated States Of Micronesia", 36 "abbreviation": "FM" 37}, { 38 "name": "Florida", 39 "abbreviation": "FL" 40}, { 41 "name": "Georgia", 42 "abbreviation": "GA" 43}, { 44 "name": "Guam", 45 "abbreviation": "GU" 46}, { 47 "name": "Hawaii", 48 "abbreviation": "HI" 49}, { 50 "name": "Idaho", 51 "abbreviation": "ID" 52}, { 53 "name": "Illinois", 54 "abbreviation": "IL" 55}, { 56 "name": "Indiana", 57 "abbreviation": "IN" 58}, { 59 "name": "Iowa", 60 "abbreviation": "IA" 61}, { 62 "name": "Kansas", 63 "abbreviation": "KS" 64}, { 65 "name": "Kentucky", 66 "abbreviation": "KY" 67}, { 68 "name": "Louisiana", 69 "abbreviation": "LA" 70}, { 71 "name": "Maine", 72 "abbreviation": "ME" 73}, { 74 "name": "Marshall Islands", 75 "abbreviation": "MH" 76}, { 77 "name": "Maryland", 78 "abbreviation": "MD" 79}, { 80 "name": "Massachusetts", 81 "abbreviation": "MA" 82}, { 83 "name": "Michigan", 84 "abbreviation": "MI" 85}, { 86 "name": "Minnesota", 87 "abbreviation": "MN" 88}, { 89 "name": "Mississippi", 90 "abbreviation": "MS" 91}, { 92 "name": "Missouri", 93 "abbreviation": "MO" 94}, { 95 "name": "Montana", 96 "abbreviation": "MT" 97}, { 98 "name": "Nebraska", 99 "abbreviation": "NE" 100}, { 101 "name": "Nevada", 102 "abbreviation": "NV" 103}, { 104 "name": "New Hampshire", 105 "abbreviation": "NH" 106}, { 107 "name": "New Jersey", 108 "abbreviation": "NJ" 109}, { 110 "name": "New Mexico", 111 "abbreviation": "NM" 112}, { 113 "name": "New York", 114 "abbreviation": "NY" 115}, { 116 "name": "North Carolina", 117 "abbreviation": "NC" 118}, { 119 "name": "North Dakota", 120 "abbreviation": "ND" 121}, { 122 "name": "Northern Mariana Islands", 123 "abbreviation": "MP" 124}, { 125 "name": "Ohio", 126 "abbreviation": "OH" 127}, { 128 "name": "Oklahoma", 129 "abbreviation": "OK" 130}, { 131 "name": "Oregon", 132 "abbreviation": "OR" 133}, { 134 "name": "Palau", 135 "abbreviation": "PW" 136}, { 137 "name": "Pennsylvania", 138 "abbreviation": "PA" 139}, { 140 "name": "Puerto Rico", 141 "abbreviation": "PR" 142}, { 143 "name": "Rhode Island", 144 "abbreviation": "RI" 145}, { 146 "name": "South Carolina", 147 "abbreviation": "SC" 148}, { 149 "name": "South Dakota", 150 "abbreviation": "SD" 151}, { 152 "name": "Tennessee", 153 "abbreviation": "TN" 154}, { 155 "name": "Texas", 156 "abbreviation": "TX" 157}, { 158 "name": "Utah", 159 "abbreviation": "UT" 160}, { 161 "name": "Vermont", 162 "abbreviation": "VT" 163}, { 164 "name": "Virgin Islands", 165 "abbreviation": "VI" 166}, { 167 "name": "Virginia", 168 "abbreviation": "VA" 169}, { 170 "name": "Washington", 171 "abbreviation": "WA" 172}, { 173 "name": "West Virginia", 174 "abbreviation": "WV" 175}, { 176 "name": "Wisconsin", 177 "abbreviation": "WI" 178}, { 179 "name": "Wyoming", 180 "abbreviation": "WY" 181}] 182 183// DOM selectors 184// There correspond to html in the map/charts shortcode 185var DOM = { 186 mapPath: '#us-map path, #us-map circle', // The state, used for hover function 187 mapText: '#us-map text tspan', 188 189 paintMap: '#paint-map', // Container wrapping the map svg 190 chartContainer: '#chart-container .chart', // Container for chart, not-tab version 191 chart: '.chart', // Where the charts are drawn (releative to tabPane) 192 infoBox: '#info-box', // The hover box 193 tabs: '[data-toggle=tab]', // The <a> tag to trigger the tab. 194 tabPane: '.tab-pane', // Container for each tab 195 toggleAll: '.toggle-all-rankings' // Toggle all button 196} 197 198// These are used to mark the active tab. These could be changed to suit your preference 199// It's built for tabs to be hidden by default & show by adding a class on click 200var CLASSES = { 201 activeTabPane: 'active show', // Class used to show a tab 202 activeTabNav: 'active' // Class used to mark nav as active 203} 204 205// Shows Full Number 206// var formatNumber = function (up) { 207// // return up.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') 208// return (Math.round(up)); 209// } 210var prefix = '$'; 211// Give numbers like 4.1k 212var formatNumber = function abbreviateNumber(value) { 213 let newValue = value; 214 const suffixes = ["", "K", "M", "B", "T"]; 215 let suffixNum = 0; 216 while (newValue >= 1000) { 217 newValue /= 1000; 218 suffixNum++; 219 } 220 221 newValue = newValue.toPrecision(3); 222 223 newValue += suffixes[suffixNum]; 224 return prefix+newValue; 225} 226 227// Chart Labels 228 229var CHART = { 230 rankingLabel: 'Ranking', 231 showLegend: false, 232 colorStop1: '#006c93', 233 colorStop2: '#0487b6', 234 colorStop3: '#0298cf', 235 colorStop4: '#04a4df', 236 colorStop5: '#01aae8', 237 hoverColor: '#01445c', 238 datalabelsColor: '#000', 239 aspectRatio: 3, // ratio of width/height when showing 10 items. Auto-calculated when changed. 240 241 animationTime: 750, 242 formatter: formatNumber // function to format numbers 243} 244var FANCYBOX = { 245 width: 70 // percent (number only) 246} 247// UI object 248// returns event handler functions 249var ui = (function ($) { 250 var $ = jQuery.noConflict(); 251 var activeState = null; 252 253 // Smart tooltip positioning v1.2 - positions opposite to mouse to avoid obstruction 254 var positionTooltipSmart = function(mouseEvent, $infoBox) { 255 var $container = $(DOM.paintMap); 256 if (!$container.length) return; 257 258 var containerRect = $container[0].getBoundingClientRect(); 259 var mouseX = mouseEvent.clientX - containerRect.left; 260 var mouseY = mouseEvent.clientY - containerRect.top; 261 262 var tooltipWidth = $infoBox.outerWidth(); 263 var tooltipHeight = $infoBox.outerHeight(); 264
265 // Determine which half of container mouse is in 266 var isLeftHalf = mouseX < (containerRect.width / 2); 267 var isTopHalf = mouseY < (containerRect.height / 2); 268 269 var css = {}; 270 271 // Horizontal positioning - opposite side of mouse 272 if (isLeftHalf) { 273 css.right = '0'; 274 css.left = 'auto'; 275 } else { 276 css.left = '0'; 277 css.right = 'auto'; 278 } 279 280 // Vertical positioning - opposite side of mouse 281 if (isTopHalf) { 282 css.bottom = '0'; 283 css.top = 'auto'; 284 } else { 285 css.top = '0'; 286 css.bottom = 'auto'; 287 } 288 289 $infoBox.css(css); 290 } 291 var updateChartjs = function (key, showAll = false) { 292 if (!window.mapData) return; 293 // d('drawing chart'); 294 var tabdata = mapData[key].data; 295 var sel = `#${key} ${DOM.chart}`; 296 297 var chart = $(DOM.chartContainer).data('chart'); 298 299 if (!chart) { 300 d("-- can't find chart"); 301 return; 302 } 303 304 // var labels = tabdata.map(item => item.state + ' (' + item.rank + ')'); // Shows the rank in (( if needed)) 305 var labels = tabdata.map(item => item.state); 306 var series = tabdata.map(item => parseInt(item.value, 10)); 307 308 var l = showAll ? tabdata.length : 10; 309 chart.data.labels = labels.splice(0, l); 310 chart.data.datasets[0].data = series.splice(0, l); 311 // chart.options.aspectRatio = aspectRatio; 312 // d(chart.options, chart.data); 313 d("UPDATE CHART"); 314 315 chart.update(); 316 $(DOM.chartContainer).data('key', key); 317 } 318 // Function to draw the chart 319 var drawChartjs = (key, showAll = false, sel = null) => { 320 if (!window.mapData) return; 321 // d('drawing chart'); 322 // d('key', key, mapData[key], mapData); 323 if (!mapData[key]) { 324 d('-- no file for tab'); 325 return; 326 } 327 var tabdata = mapData[key].data; 328 prefix = mapData[key].prefix; 329 330 // d('tabdata:',tabdata); 331 if (!tabdata) return; 332 // d('sel', sel); 333 if (!sel) sel = `#${key} ${DOM.chart}`; 334 // d('sel', sel); 335 if ($(sel).length == 0) { 336 d('-- tab not found', key); 337 return; 338 } 339 if (showAll && !sel) $(sel).addClass('all'); 340 else $(sel).removeClass('all'); 341 $(sel).find('canvas').remove(); 342 $(sel).append(`<canvas></canvas>`) 343 // var labels = tabdata.map(item => item.state + ' (' + item.rank + ')'); // Shows rank 344 var labels = tabdata.map(item => item.state); 345 var series = tabdata.map(item => parseInt(item.value, 10)); 346 347 var l = showAll ? tabdata.length : 10; 348 // var l = tabdata.length; 349 var ctx = $(sel + ' canvas')[0].getContext('2d'); 350 // d(series.splice(0, 10)); 351 // Change default options for ALL charts 352 353 var baseAspectRation = CHART.aspectRatio * 10 / l; 354 355 if (window.innerWidth > 640) { 356 var aspectRatio = baseAspectRation; 357 } else { 358 var aspectRatio = 1; 359 } 360 361 // d('aspect:', aspectRatio) 362 Chart.defaults.font.size = 15; 363 Chart.defaults.color = 'black'; 364 var gradient = ctx.createLinearGradient(0, 0, 0, 1000); 365 gradient.addColorStop(0, CHART.colorStop1); 366 gradient.addColorStop(.25, CHART.colorStop2); 367 gradient.addColorStop(.5, CHART.colorStop3); 368 gradient.addColorStop(.75, CHART.colorStop4); 369 gradient.addColorStop(1, CHART.colorStop5); 370 var chart = new Chart(ctx, { 371 372 // The type of chart we want to create 373 type: 'bar', 374 plugins: [ChartDataLabels], 375 376 // The data for our dataset 377 data: { 378 labels: labels.splice(0, l), 379 380 datasets: [{ 381 label: CHART.rankingLabel, 382 backgroundColor: gradient, 383 hoverBackgroundColor: CHART.hoverColor, 384 borderColor: gradient, 385 barThickness: 10, 386 data: series.splice(0, l), 387 388 }] 389 }, 390 391 // Configuration options go here (Chart.js v4 syntax) 392 options: { 393 indexAxis: 'y', // horizontal bars (replaces v2's 'horizontalBar' type) 394 onResize: function (chart, size) { 395 chart.options.aspectRatio = window.innerWidth > 640 ? baseAspectRation : 1; 396 }, 397 responsive: true, 398 maintainAspectRatio: true, 399 aspectRatio, 400 animation: { 401 duration: CHART.animationTime, 402 easing: 'easeOutQuad' 403 }, 404 layout: { 405 padding: { 406 left: 0, 407 right: 50, 408 top: 0, 409 bottom: 0 410 } 411 }, 412 413 scales: { 414 x: { 415 display: false //this will remove all the x-axis grid lines 416 }, 417 y: { 418 position: 'left', 419 afterFit: function (scaleInstance) { 420 scaleInstance.width = 150; // sets the width for labels 421 } 422 } 423 }, 424 425 plugins: { 426 legend: { 427 display: CHART.showLegend 428 }, 429 tooltip: { 430 enabled: false 431 }, 432 datalabels: { 433 color: CHART.datalabelsColor, 434 align: 'end', 435 anchor: 'end', 436 clamp: true, 437 formatter: CHART.formatter, 438 // color: '#36A2EB' 439 440 } 441 }, 442 } 443 }); 444 $(sel).data('chart', chart); 445 $(sel).data('key', key); 446 // $(sel).data('height', $(sel).height()); 447 } 448 449 // Hover over map 450 var mapHover = function (e) { 451 if (!window.mapData) return; 452 var abbr = $(e.target).attr('id') || ''; 453 454 // Use text instead, if it is 2 characters long (ie. a state code) 455 if (abbr.length > 2) abbr = $(e.target).text().trim(); 456 457 if (!abbr) return; 458 if (abbr.length > 2) return; 459 460 var state = states.find(s => s.abbreviation == abbr); 461 if (!state || !mapData.map_data || !mapData.map_data.data) return; 462 var data = mapData.map_data.data.find(datum => datum.state.trim().toLowerCase() == state.name.trim().toLowerCase()); 463 // if (abbr == 'DC' && !data) data = mapData.map_data.data[49]; 464 // d('state', state, data); 465 // var info = $(e.target).data('info'); 466 // d("STATE ABBR", abbr, data); 467 if (!data) return; 468 469 if (activeState === abbr && $(DOM.infoBox).is(':visible')) { 470 return; 471 } 472 activeState = abbr; 473 474 var details = '';
475 Object.entries(data).forEach(([key, item]) => { 476 // d('key:', key, 'item:', item); 477 var k = key.replace(' ', '_').toLowerCase(); 478 // var prefix = mapData[k] ? mapData[k].prefix : ''; 479 // if( prefix == false ) prefix = ''; 480 var label = key == 'state' ? 'State' : key; 481 if (item) details += `<li><strong>${label}:</strong> <span>${item || ''}</span></li>`; 482 }) 483 // d('details:',details); 484 var html = ` 485 <div class="state-info"> 486 <h4>${data.state}</h4> 487 <ul>${details}</ul> 488 </div> `; 489 var $infoBox = $(DOM.infoBox); 490 491 $infoBox.html(html); 492 $infoBox.css('display', 'block'); 493 494 window.requestAnimationFrame(function() { 495 positionTooltipSmart(e, $infoBox); 496 }); 497 } 498 499 // Leave map 500 var mapMouseleave = function () { 501 $(DOM.infoBox).css('display', 'none'); 502 activeState = null; 503 } 504 505 // Handler for the "View All" button 506 var showAllFancybox = function (e) { 507 e.preventDefault(); 508 509 var key = $(DOM.chartContainer).data('key'); 510 511 // d('open fancybox!'); 512 var width = FANCYBOX.width / 100 * window.innerWidth; 513 d('width', width); 514 $.fancybox.open('<div id="fancybox-chart" data-width="' + width + 'px"><div class="chart"></div></div>', { 515 width: '100%', 516 afterShow: function () { 517 d('draw chart in fancybox', key); 518 drawChartjs(key, true, '#fancybox-chart'); 519 } 520 }); 521 } 522 523 // Handler for changing tabs 524 var changeTab = function (e) { 525 e.preventDefault(); 526 $(DOM.tabPane).removeClass(CLASSES.activeTabPane); 527 $(DOM.tabs).parent().removeClass(CLASSES.activeTabNav); 528 var key = $(e.target).attr('aria-controls'); 529 var target = '#' + key; 530 // d('target', target); 531 // var key = target.replace('#', ''); 532 $(e.target).parent().addClass(CLASSES.activeTabNav); 533 $(target).addClass(CLASSES.activeTabPane); 534 ui.drawChartjs(key, false); 535 return false; 536 } 537 538 var changeChart = function (e) { 539 e.preventDefault(); 540 var chart = $(e.target).attr('aria-controls').replace('#', ''); 541 updateChartjs(chart, false, DOM.chartContainer); 542 d('add nav active, fancybox'); 543 $(DOM.tabs).parent().removeClass(CLASSES.activeTabNav); 544 $(e.target).parent().addClass(CLASSES.activeTabNav); 545 } 546 var showAllRankings = function (e) { 547 e.preventDefault(); 548 const $tab = $(e.target).closest(DOM.tabPane); 549 const key = $tab.attr('id'); 550 const all = $(e.target).data('all') || false; 551 const text = all ? $(e.target).data('more') : $(e.target).data('less'); 552 553 $(e.target).text(text) 554 $(e.target).data('all', !all); 555 // d('tabpane', $tab[0], all) 556 all ? $tab.removeClass('all') : $tab.addClass('all'); 557 drawChartjs(key, !all); 558 559 } 560 return { 561 mapHover, 562 mapMouseleave, 563 showAllRankings, 564 showAllFancybox, 565 changeTab, 566 changeChart, 567 drawChartjs, 568 updateChartjs 569 } 570 571})(jQuery); 572 573// Controller provides functions that can be called to assign event handlers to the DOM 574var controller = (function (ui, $) { 575 // Set preferred jQuery variable 576 var initMap = function () { 577 $(document).on('mouseenter', DOM.mapPath, ui.mapHover); 578 $(document).on('mouseenter', DOM.mapText, ui.mapHover); 579 $(document).on('mouseleave', DOM.mapPath, ui.mapMouseleave); 580 $(document).on('mouseleave', DOM.mapText, ui.mapMouseleave); 581 } 582 583 var initTabs = function () { 584 d('container:', $(DOM.chartContainer).length); 585 if ($(DOM.chartContainer).length) { 586 d('change chart') 587 $(document).on('click', DOM.tabs, ui.changeChart); 588 } 589 else { 590 $(DOM.tabs).on('click', ui.changeTab); 591 } 592 } 593 594 var initChartToggle = function () { 595 if ($(DOM.chartContainer).length) $(document).on('click', DOM.toggleAll, ui.showAllFancybox); 596 else $(document).on('click', DOM.toggleAll, ui.showAllRankings); 597 } 598 599 var initChartjs = function () { 600 // This is being set 601 if (!window.mapData) { 602 d('NO MAP DATA!'); 603 return 604 } 605 var drawn = false; 606 Object.entries(mapData).forEach(([key, tabdata]) => { 607 if (key == 'map_data') return; 608 if ($(DOM.chartContainer).length && drawn) return false;
609 610 var sel = $(DOM.chartContainer).length ? DOM.chartContainer : null; 611 if (mapData[key]) ui.drawChartjs(key, false, sel); 612 drawn = true; 613 }) 614 } 615 // Return an object for later us 616 return { 617 initMap, 618 initTabs, 619 initChartToggle, 620 initChartjs 621 } 622})(ui, jQuery); 623 624// Mobile State Slider v1.1 â initializes Blaze Slider + Select2 for [paint-map-mobile] 625var mobileSlider = (function ($) { 626 var sliderInstance = null; 627 var currentSlide = 0; 628 var slideStates = []; // array of state names in slide order 629 630 var init = function () { 631 var $el = $('.paint-mobile-slider'); 632 if (!$el.length || typeof BlazeSlider === 'undefined') return; 633 634 // Build index of state names from slides 635 $el.find('.paint-mobile-slide').each(function (i) { 636 slideStates.push($(this).data('state')); 637 }); 638 639 sliderInstance = new BlazeSlider($el[0], { 640 all: { 641 slidesToShow: 1, 642 slidesToScroll: 1, 643 loop: true, 644 draggable: true, 645 slideGap: '0px', 646 enablePagination: false, 647 transitionDuration: 300, 648 transitionTimingFunction: 'ease' 649 } 650 }); 651 652 // Track current slide and sync Select2 653 sliderInstance.onSlide(function (pageIndex, firstSlideIndex) { 654 currentSlide = firstSlideIndex; 655 // Sync Select2 dropdown with current slide 656 var $select = $('.paint-mobile-search__select'); 657 if ($select.length) { 658 $select.val(String(firstSlideIndex)).trigger('change.select2'); 659 } 660 }); 661 662 // Init Select2 on the state dropdown 663 var $select = $('.paint-mobile-search__select'); 664 if ($select.length && $.fn.select2) { 665 $select.select2({ 666 placeholder: 'Search States...', 667 allowClear: true, 668 width: '100%', 669 minimumResultsForSearch: 0 670 }); 671 672 // Jump to slide when a state is selected 673 $select.on('select2:select', function (e) { 674 var targetIndex = parseInt(e.params.data.id, 10); 675 if (!isNaN(targetIndex)) goToSlide(targetIndex); 676 }); 677 678 // If user clears, optionally do nothing (stay on current slide) 679 $select.on('select2:clear', function () { 680 // no-op â keep current slide 681 }); 682 } 683 }; 684 685 var goToSlide = function (targetIndex) { 686 if (!sliderInstance || targetIndex === currentSlide) return; 687 var total = slideStates.length; 688 var diff = targetIndex - currentSlide; 689 // Account for loop â pick shorter path 690 if (Math.abs(diff) > total / 2) { 691 if (diff > 0) diff -= total; 692 else diff += total; 693 } 694 if (diff > 0) sliderInstance.next(diff); 695 else if (diff < 0) sliderInstance.prev(Math.abs(diff)); 696 }; 697 698 return { init: init }; 699})(jQuery); 700 701// Finally, we trigger the functions here. 702jQuery(document).ready(function ($) { 703 controller.initMap(); 704 controller.initTabs(); 705 controller.initChartToggle(); 706 controller.initChartjs(); 707 mobileSlider.init(); 708})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.