1(function($) { 2 var _options; 3 var _self; 4 5 var _boundingBoxes; 6 var _zoneCentroids = {}; 7 var _selectedRegionKey; 8 var _selectedPolygon; 9 var _mapper; 10 var _mapZones = {}; 11 var _transitions = {}; 12 13 var _currentHoverRegion; 14 var _hoverRegions = {}; 15 var _hoverPolygons = []; 16 var _map; 17 var _loader; 18 var _loaderGif; 19 var _maskPng; 20 var _needsLoader = 0; 21 22 var GoogleMapsMapper = function(el, mouseClickHandler, mouseMoveHandler, mapOptions) { 23 var gmaps = google.maps; 24 ; 25 26 // Create the maps instance 27 _map = new gmaps.Map(el, $.extend({ 28 mapTypeId: gmaps.MapTypeId.ROADMAP, 29 center: new gmaps.LatLng(mapOptions.centerLat, mapOptions.centerLng) 30 }, mapOptions)); 31 gmaps.event.addListener(_map, 'click', mouseClickHandler); 32 if (mouseMoveHandler) { 33 gmaps.event.addListener(_map, 'mousemove', mouseMoveHandler); 34 } 35 36 var addPolygon = function(coords, stroke, fill, clickHandler, mouseMoveHandler) { 37 var mapPolygon = new gmaps.Polygon({ 38 paths: coords, 39 strokeColor: stroke.color, 40 strokeOpacity: stroke.opacity, 41 strokeWeight: stroke.width, 42 fillColor: fill.color, 43 fillOpacity: fill.opacity 44 }); 45 mapPolygon.setMap(_map); 46 47 gmaps.event.addListener(mapPolygon, 'click', clickHandler); 48 49 if (mouseMoveHandler) { 50 gmaps.event.addListener(mapPolygon, 'mousemove', mouseMoveHandler); 51 } 52 53 return mapPolygon; 54 }; 55 56 var createPoint = function(lat, lng) { 57 return new gmaps.LatLng(lat, lng); 58 }; 59 60 var hideInfoWindow = function() { 61 if (_map.lastInfoWindow) { 62 _map.lastInfoWindow.close(); 63 } 64 }; 65 66 var removePolygon = function(mapPolygon) { 67 mapPolygon.setMap(null); 68 }; 69 70 var showInfoWindow = function(pos, content, callback) { 71 var infowindow = new gmaps.InfoWindow({ 72 content: '<div id="timezone_picker_infowindow" class="timezone-picker-infowindow">' + 73 content + 74 '</div>' 75 }); 76 77 gmaps.event.addListener(infowindow, 'domready', function() { 78 // HACK: Put rounded corners on the infowindow 79 $('#timezone_picker_infowindow').parent().parent().parent().prev().css('border-radius', 80 '5px'); 81 82 if (callback) { 83 callback.apply($('#timezone_picker_infowindow')); 84 } 85 }); 86 infowindow.setPosition(pos); 87 infowindow.open(_map); 88 89 _map.lastInfoWindow = infowindow; 90 }; 91 92 return { 93 addPolygon: addPolygon, 94 createPoint: createPoint, 95 hideInfoWindow: hideInfoWindow, 96 removePolygon: removePolygon, 97 showInfoWindow: showInfoWindow 98 }; 99 }; 100 101 var OpenLayersMapper = function(el, mouseClickHandler, mouseMoveHandler, mapOptions) { 102 var infoWindow; 103 104 // Create the maps instance 105 var map = new OpenLayers.Map(OpenLayers.Util.extend({ 106 div: el, 107 projection: "EPSG:900913", 108 displayProjection: "EPSG:4326", 109 numZoomLevels: 18, 110 controls: [ 111 new OpenLayers.Control.Attribution(), 112 new OpenLayers.Control.DragPan(), 113 new OpenLayers.Control.Navigation({ 114 mouseWheelOptions: { 115 cumulative: false, 116 maxDelta: 6, 117 interval: 50 118 }, 119 zoomWheelEnabled: true 120 }), 121 new OpenLayers.Control.Zoom(), 122 new OpenLayers.Control.ZoomBox() 123 ] 124 }, mapOptions)); 125 126 var wms = new OpenLayers.Layer.WMS('OpenLayers WMS', 127 'http://vmap0.tiles.osgeo.org/wms/vmap0', { 128 layers: 'basic' 129 }); 130 map.addLayer(wms); 131 132 var vectors = new OpenLayers.Layer.Vector("vector"); 133 map.addLayer(vectors); 134 135 OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, { 136 defaultHandlerOptions: { 137 'single': true, 138 'double': false, 139 'pixelTolerance': 0, 140 'stopSingle': false, 141 'stopDouble': false 142 }, 143 144 initialize: function() { 145 this.handlerOptions = OpenLayers.Util.extend( 146 {}, this.defaultHandlerOptions 147 ); 148 OpenLayers.Control.prototype.initialize.apply( 149 this, arguments 150 ); 151 this.handler = new OpenLayers.Handler.Click( 152 this, { 153 click: this.trigger 154 }, this.handlerOptions 155 ); 156 }, 157 158 trigger: function(e) {
159 var position = map.getLonLatFromViewPortPx(e.xy); 160 position.transform( 161 map.getProjectionObject(), 162 new OpenLayers.Projection("EPSG:4326") 163 ); 164 mapClickHandler({ 165 latLng: { 166 lat: function() { 167 return position.lat; 168 }, 169 lng: function() { 170 return position.lon; 171 } 172 } 173 }); 174 } 175 }); 176 var click = new OpenLayers.Control.Click(); 177 map.addControl(click); 178 click.activate(); 179 180 if (mouseMoveHandler) { 181 map.events.register("mousemove", map, function(e) { 182 var position = map.getLonLatFromViewPortPx(e.xy); 183 position.transform( 184 map.getProjectionObject(), 185 new OpenLayers.Projection("EPSG:4326") 186 ); 187 mouseMoveHandler({ 188 latLng: { 189 lat: function() { 190 return position.lat; 191 }, 192 lng: function() { 193 return position.lon; 194 } 195 } 196 }); 197 }); 198 } 199 200 var onPolygonSelect = function(feature) { 201 if (feature.clickHandler) { 202 var position = map.getLonLatFromPixel(new OpenLayers.Pixel( 203 polygonSelect.handlers.feature.evt.layerX, 204 polygonSelect.handlers.feature.evt.layerY 205 )); 206 position.transform( 207 map.getProjectionObject(), 208 new OpenLayers.Projection("EPSG:4326") 209 ); 210 feature.clickHandler({ 211 latLng: { 212 lat: function() { 213 return position.lat; 214 }, 215 lng: function() { 216 return position.lon; 217 } 218 } 219 }); 220 } 221 }; 222 223 var onPolygonHighlight = function(e) { 224 if (e.feature.hoverHandler) { 225 e.feature.hoverHandler(); 226 } 227 }; 228 229 var polygonHover = new OpenLayers.Control.SelectFeature(vectors, { 230 hover: true, 231 highlightOnly: true, 232 renderIntent: "temporary", 233 eventListeners: { 234 beforefeaturehighlighted:onPolygonHighlight 235 } 236 }); 237 238 var polygonSelect = new OpenLayers.Control.SelectFeature(vectors, { 239 onSelect: onPolygonSelect 240 }); 241 242 map.addControl(polygonHover); 243 map.addControl(polygonSelect); 244 polygonHover.activate(); 245 polygonSelect.activate(); 246 247 map.setCenter(new OpenLayers.LonLat(0, 0), mapOptions.zoom); 248 249 var addPolygon = function(coords, stroke, fill, clickHandler, mouseMoveHandler) { 250 for (var i = 0; i < coords.length; i++) { 251 coords[i].transform( 252 new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984 253 map.getProjectionObject() // to Spherical Mercator Projection 254 ); 255 } 256 257 var style = { 258 strokeColor: stroke.color, 259 strokeOpacity: stroke.opacity, 260 strokeWidth: stroke.width, 261 fillColor: fill.color, 262 fillOpacity: fill.opacity 263 }; 264 var linearRing = new OpenLayers.Geometry.LinearRing(coords); 265 var feature = new OpenLayers.Feature.Vector( 266 new OpenLayers.Geometry.Polygon(linearRing), null, style 267 ); 268 269 vectors.addFeatures([ feature ]); 270 271 // NOTE: Stuff our click/mousemove handlers on the object for use in onPolygonSelect 272 feature.clickHandler = clickHandler; 273 feature.hoverHandler = mouseMoveHandler; 274 275 return feature; 276 }; 277 278 var createPoint = function(lat, lng) { 279 return new OpenLayers.Geometry.Point(lng, lat); 280 }; 281 282 var hideInfoWindow = function() { 283 if (infoWindow) { 284 map.removePopup(infoWindow); 285 infoWindow.destroy(); 286 infoWindow = null; 287 } 288 }; 289 290 var removePolygon = function(mapPolygon) { 291 vectors.removeFeatures([ mapPolygon ]); 292 }; 293 294 var showInfoWindow = function(pos, content, callback) { 295 if (infoWindow) { 296 hideInfoWindow(infoWindow); 297 } 298 299 pos = new OpenLayers.LonLat(pos.x, pos.y); 300 pos.transform( 301 new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984 302 map.getProjectionObject() // to Spherical Mercator Projection 303 ); 304 305 infoWindow = new OpenLayers.Popup.FramedCloud('timezone_picker_infowindow', 306 pos, 307 new OpenLayers.Size(100,100), 308 content, 309 null, true, null); 310 map.addPopup(infoWindow); 311 312 // HACK: callback for popup using a set timeout 313 if (callback) { 314 setTimeout(function() { 315 callback.apply($('#timezone_picker_infowindow')); 316 }, 100); 317 } 318 }; 319 320 return { 321 addPolygon: addPolygon, 322 createPoint: createPoint, 323 hideInfoWindow: hideInfoWindow, 324 removePolygon: removePolygon, 325 showInfoWindow: showInfoWindow 326 }; 327 }; 328 329 // Forward declarations to satisfy jshint 330 var hideLoader, hitTestAndConvert, selectPolygonZone, 331 showInfoWindow, slugifyName; 332 333 var clearHover = function() { 334 $.each(_hoverPolygons, function(i, p) { 335 _mapper.removePolygon(p); 336 }); 337 338 _hoverPolygons = []; 339 }; 340 341 var clearZones = function() { 342 $.each(_mapZones, function(i, zone) { 343 $.each(zone, function(j, polygon) { 344 _mapper.removePolygon(polygon); 345 }); 346 }); 347 348 _mapZones = {}; 349 }; 350 351 var drawZone = function(name, lat, lng, callback) { 352 if (_mapZones[name]) { 353 return; 354 } 355 356 $.get(_options.jsonRootUrl + 'polygons/' + name + '.json', function(data) { 357 _needsLoader--; 358 if (_needsLoader === 0 && _loader) { 359 hideLoader(); 360 } 361 362 if (callback) { 363 callback(); 364 } 365 366 data = typeof data === 'string' ? JSON.parse(data) : data; 367 368 _mapZones[name] = []; 369 $.extend(_transitions, data.transitions); 370 371 var result = hitTestAndConvert(data.polygons, lat, lng); 372 373 if (result.inZone) { 374 _selectedRegionKey = name; 375 $.each(result.allPolygons, function(i, polygonInfo) { 376 var mapPolygon = _mapper.addPolygon(polygonInfo.coords, {
377 color: '#ff0000', 378 opacity: 0.7, 379 width: 1 380 }, { 381 color: '#ffcccc', 382 opacity: 0.5 383 }, function() { 384 selectPolygonZone(polygonInfo.polygon); 385 }, clearHover); 386 387 _mapZones[name].push(mapPolygon); 388 }); 389 390 selectPolygonZone(result.selectedPolygon); 391 } 392 }).error(function() { 393 console.warn(arguments); 394 }); 395 }; 396 397 var getCurrentTransition = function(transitions) { 398 if (transitions.length === 1) { 399 return transitions[0]; 400 } 401 402 var now = _options.date.getTime() / 1000; 403 var selected = null; 404 $.each(transitions, function(i, transition) { 405 if (transition[0] < now && i < transitions.length - 1 && 406 transitions[i + 1][0] > now) { 407 selected = transition; 408 } 409 }); 410 411 // If we couldn't find a matching transition, just use the first one 412 // NOTE: This will sometimes be wrong for events in the past 413 if (!selected) { 414 selected = transitions[0]; 415 } 416 417 return selected; 418 }; 419 420 var hideInfoWindow = function() { 421 _mapper.hideInfoWindow(); 422 }; 423 424 hideLoader = function() { 425 _loader.remove(); 426 _loader = null; 427 }; 428 429 hitTestAndConvert = function(polygons, lat, lng) { 430 var allPolygons = []; 431 var inZone = false; 432 var selectedPolygon; 433 $.each(polygons, function(i, polygon) { 434 // Ray casting counter for hit testing. 435 var rayTest = 0; 436 var lastPoint = polygon.points.slice(-2); 437 438 var coords = []; 439 var j = 0; 440 for (j = 0; j < polygon.points.length; j += 2) { 441 var point = polygon.points.slice(j, j + 2); 442 443 coords.push(_mapper.createPoint(point[0], point[1])); 444 445 // Ray casting test 446 if ((lastPoint[0] <= lat && point[0] >= lat) || 447 (lastPoint[0] > lat && point[0] < lat)) { 448 var slope = (point[1] - lastPoint[1]) / (point[0] - lastPoint[0]); 449 var testPoint = slope * (lat - lastPoint[0]) + lastPoint[1]; 450 if (testPoint < lng) { 451 rayTest++; 452 } 453 } 454 455 lastPoint = point; 456 } 457 458 allPolygons.push({ 459 polygon: polygon, 460 coords: coords 461 }); 462 463 // If the count is odd, we are in the polygon 464 var odd = (rayTest % 2 === 1); 465 inZone = inZone || odd; 466 if (odd) { 467 selectedPolygon = polygon; 468 } 469 }); 470 471 return { 472 allPolygons: allPolygons, 473 inZone: inZone, 474 selectedPolygon: selectedPolygon 475 }; 476 }; 477 478 var mapClickHandler = function(e) { 479 if (_needsLoader > 0) { 480 return; 481 } 482 483 hideInfoWindow(); 484 485 var lat = e.latLng.lat(); 486 var lng = e.latLng.lng(); 487 488 var candidates = []; 489 $.each(_boundingBoxes, function(i, v) { 490 var bb = v.boundingBox; 491 if (lat > bb.ymin && lat < bb.ymax && 492 lng > bb.xmin && 493 lng < bb.xmax) { 494 candidates.push(slugifyName(v.name)); 495 } 496 }); 497 498 _needsLoader = candidates.length; 499 setTimeout(function() { 500 if (_needsLoader > 0) { 501 showLoader(); 502 } 503 }, 500); 504 505 clearZones(); 506 $.each(candidates, function(i, v) { 507 drawZone(v, lat, lng, function() { 508 $.each(_hoverPolygons, function(i, p) { 509 _mapper.removePolygon(p); 510 }); 511 _hoverPolygons = []; 512 _currentHoverRegion = null; 513 }); 514 }); 515 }; 516 517 var mouseMoveHandler = function(e) { 518 var lat = e.latLng.lat(); 519 var lng = e.latLng.lng(); 520 521 $.each(_boundingBoxes, function(i, v) { 522 var bb = v.boundingBox; 523 if (lat > bb.ymin && lat < bb.ymax && 524 lng > bb.xmin && 525 lng < bb.xmax) { 526 var hoverRegion = _hoverRegions[v.name]; 527 if (!hoverRegion) { 528 return; 529 } 530 531 var result = hitTestAndConvert(hoverRegion.hoverRegion, lat, lng); 532 var slugName = slugifyName(v.name); 533 if (result.inZone && slugName !== _currentHoverRegion && 534 slugName !== _selectedRegionKey) { 535 clearHover(); 536 _currentHoverRegion = slugName; 537 538 $.each(result.allPolygons, function(i, polygonInfo) { 539 var mapPolygon = _mapper.addPolygon(polygonInfo.coords, {
540 color: '#444444', 541 opacity: 0.7, 542 width: 1 543 }, { 544 color: '#888888', 545 opacity: 0.5 546 }, mapClickHandler, null); 547 548 _hoverPolygons.push(mapPolygon); 549 }); 550 551 if (_options.onHover) { 552 var transition = getCurrentTransition(hoverRegion.transitions); 553 _options.onHover(transition[1], transition[2]); 554 } 555 } 556 } 557 }); 558 }; 559 560 selectPolygonZone = function(polygon) { 561 _selectedPolygon = polygon; 562 563 var transition = getCurrentTransition( 564 _transitions[polygon.name]); 565 566 var olsonName = polygon.name; 567 var utcOffset = transition[1]; 568 var tzName = transition[2]; 569 570 if (_options.onSelected) { 571 _options.onSelected(olsonName, utcOffset, tzName); 572 } 573 else { 574 var pad = function(d) { 575 if (d < 10) { 576 return '0' + d; 577 } 578 return d.toString(); 579 }; 580 581 var now = new Date(); 582 var adjusted = new Date(); 583 adjusted.setTime(adjusted.getTime() + 584 (adjusted.getTimezoneOffset() + utcOffset) * 60 * 1000); 585 586 showInfoWindow('<h2>' + 587 olsonName.split('/').slice(-1)[0].replace('_', ' ') + ' ' + 588 '(' + tzName + ')</h2>' + 589 '<div class="metadata">' + 590 '<div>Current Time: ' + 591 pad(adjusted.getHours()) + ':' + 592 pad(adjusted.getMinutes()) + ':' + 593 pad(adjusted.getSeconds()) + '</div>' + 594 '<div>Your Time: ' + 595 pad(now.getHours()) + ':' + 596 pad(now.getMinutes()) + ':' + 597 pad(now.getSeconds()) + '</div>' + 598 '<div>UTC Offset (in hours): ' + 599 (utcOffset / 60) + '</div>' + 600 '</div>'); 601 } 602 }; 603 604 showInfoWindow = function(content, callback) { 605 // Hack to get the centroid of the largest polygon - we just check 606 // which has the most edges 607 var centroid; 608 var maxPoints = 0; 609 if (_selectedPolygon.points.length > maxPoints) { 610 centroid = _selectedPolygon.centroid; 611 maxPoints = _selectedPolygon.points.length; 612 } 613 614 hideInfoWindow(); 615 616 _mapper.showInfoWindow(_mapper.createPoint(centroid[1], centroid[0]), content, 617 callback); 618 }; 619 620 var showLoader = function() { 621 _loader = $('<div style="background: url(' + _maskPng + 622 ');z-index:10000;position: absolute;top:0;left:0;">' + 623 '<img style="position:absolute;' + 624 'top:50%; left:50%;margin-top:-8px;margin-left:-8px" ' + 625 'src="' + 626 _loaderGif + 627 '" /></div>'); 628 _loader.height(_self.height()).width(_self.width()); 629 _self.append(_loader); 630 }; 631 632 slugifyName = function(name) { 633 return name.toLowerCase().replace(/[^a-z0-9]/g, '-'); 634 }; 635 636 var methods = { 637 init: function(options) { 638 _self = this; 639 640 // Populate the options and set defaults 641 _options = options || {}; 642 _options.initialZoom = _options.initialZoom || 2; 643 _options.initialLat = _options.initialLat || 0; 644 _options.initialLng = _options.initialLng || 0; 645 _options.strokeColor = _options.strokeColor || '#ff0000'; 646 _options.strokeWeight = _options.strokeWeight || 2; 647 _options.strokeOpacity = _options.strokeOpacity || 0.7; 648 _options.fillColor = _options.fillColor || '#ffcccc'; 649 _options.fillOpacity = _options.fillOpacity || 0.5; 650 _options.jsonRootUrl = _options.jsonRootUrl || 'tz_json/'; 651 _options.date = _options.date || new Date(); 652 653 _options.mapOptions = $.extend({ 654 zoom: _options.initialZoom, 655 centerLat: _options.initialLat, 656 centerLng: _options.initialLng 657 }, _options.mapOptions); 658 659 if (typeof _options.hoverRegions === 'undefined') { 660 _options.hoverRegions = true; 661 } 662 663 if (_options.useOpenLayers) { 664 _mapper = new OpenLayersMapper(_self.get(0), 665 mapClickHandler, 666 _options.hoverRegions ? mouseMoveHandler : null, 667 _options.mapOptions); 668 } 669 else { 670 _mapper = new GoogleMapsMapper(_self.get(0), 671 mapClickHandler, 672 _options.hoverRegions ? mouseMoveHandler : null, 673 _options.mapOptions); 674 } 675 676 // Load the necessary data files 677 var loadCount = _options.hoverRegions ? 2 : 1; 678 var checkLoading = function() { 679 loadCount--; 680 if (loadCount === 0) { 681 hideLoader(); 682 683 if (_options.onReady) { 684 _options.onReady(); 685 } 686 } 687 }; 688 689 showLoader(); 690 $.get(_options.jsonRootUrl + 'bounding_boxes.json', function(data) { 691 _boundingBoxes = typeof data === 'string' ? JSON.parse(data) : data; 692 $.each(_boundingBoxes, function(i, bb) { 693 $.extend(_zoneCentroids, bb.zoneCentroids); 694 }); 695 checkLoading(); 696 }); 697 698 if (_options.hoverRegions) { 699 $.get(_options.jsonRootUrl + 'hover_regions.json', function(data) { 700 var hoverData = typeof data === 'string' ? JSON.parse(data) : data; 701 $.each(hoverData, function(i, v) { 702 _hoverRegions[v.name] = v; 703 }); 704 checkLoading(); 705 }); 706 } 707 }, 708 setDate: function(date) { 709 hideInfoWindow(); 710 _options.date = date; 711 }, 712 hideInfoWindow: hideInfoWindow, 713 showInfoWindow: function(content, callback) { 714 showInfoWindow(content, callback); 715 }, 716 selectZone: function(olsonName) { 717 var centroid = _zoneCentroids[olsonName]; 718 719 if (centroid) { 720 _map.setCenter({lat: centroid[1], lng: centroid[0]}); 721 mapClickHandler({ 722 latLng: { 723 lat: function() { 724 return centroid[1]; 725 }, 726 lng: function() { 727 return centroid[0]; 728 } 729 } 730 }); 731 } 732 } 733 }; 734 735 $.fn.timezonePicker = function(method) { 736 737 if (methods[method]) { 738 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 739 } 740 else if (typeof method === 'object' || !method) { 741 return methods.init.apply(this, arguments); 742 } 743 else { 744 $.error('Method ' + method + ' does not exist on jQuery.timezonePicker.'); 745 } 746 }; 747 748 _loaderGif = "data:image/gif;base64,R0lGODlhEAAQAPIAAKqqqv///729vejo6P///93d3dPT083NzSH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="; 749 _maskPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwS
749FlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDgA6CHKQBUUAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGDwAQAAUQBNbrgEdAAAAABJRU5ErkJggg=="; 750})(jQuery);
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.