1// Copyright © 2005,2006,2009,2018,2026 by Jef Poskanzer <[email protected]>. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions 6// are met: 7// 1. Redistributions of source code must retain the above copyright 8// notice, this list of conditions and the following disclaimer. 9// 2. Redistributions in binary form must reproduce the above copyright 10// notice, this list of conditions and the following disclaimer in the 11// documentation and/or other materials provided with the distribution. 12// 13// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23// SUCH DAMAGE. 24// 25// For commentary on this license please see http://acme.com/license.html 26 27'use strict'; 28 29 30// Globals. 31 32var xmlsrcs = null; 33var xi; 34 35var headerElement = document.getElementById( 'header' ); 36var imageElement = document.getElementById( 'image' ); 37var titleElement = document.getElementById( 'title' ); 38var descriptionElement = document.getElementById( 'description' ); 39var groupsElement = document.getElementById( 'groups' ); 40 41var footerElement = document.getElementById( 'footer' ); 42var plainHtmlElement = document.getElementById( 'plainHtml' ); 43 44var listElement = document.getElementById( 'list' ); 45 46var mapElement = document.getElementById( 'map' ); 47 48var searchElement = document.getElementById( 'search' ); 49var searchFieldsElement = document.getElementById( 'searchFields' ); 50var searchPatternElement = document.getElementById( 'searchPattern' ); 51 52var selectsElement = document.getElementById( 'selects' ); 53var selectsTbodyElement = document.getElementById( 'selectsTbody' ); 54var potentialAutoSelects = []; 55potentialAutoSelects['Neighborhood'] = []; 56var selects = []; 57var selectsElements = []; 58 59var calendarElement = document.getElementById( 'calendar' ); 60var datePickerElement = document.getElementById( 'datePicker' ); 61var datePickerEarliestElement = document.getElementById( 'datePickerEarliest' ); 62var datePickerLatestElement = document.getElementById( 'datePickerLatest' ); 63var datePickerSelectElement = document.getElementById( 'datePickerSelect' ); 64var datePickerEarliestContainerElement = document.getElementById( 'datePickerEarliestContainer' ); 65var datePickerLatestContainerElement = document.getElementById( 'datePickerLatestContainer' ); 66var pubDatesExist; 67 68var map = null; 69var channels = null; 70var points = null; 71var markers = null; 72 73var icons; 74var iconIdx; 75 76var iconSizeHelper = []; 77 78 79function Setup() 80 { 81 acme.maps.Initialize(); 82 83 // ESC closes any popups. 84 acme.keynav.KeyDown( acme.keynav.escKeyCode, PopUpCloseAll ); 85 86 // Collect the xmlsrc parameters We don't use acme.utils.GetParameters 87 // because we want to allow multiple parameters with the same name. 88 var str = acme.utils.GetHash(); 89 if ( ! str ) 90 str = acme.utils.GetQuery(); 91 var namevals = str.split( '&' ); 92 xmlsrcs = []; 93 for ( var i = 0; i < namevals.length; ++i ) 94 { 95 var nameval = namevals[i].split( '=' ); 96 if ( nameval.length == 2 && nameval[0] == 'xmlsrc' ) 97 xmlsrcs.push( decodeURIComponent( nameval[1].replace( /\+/g, ' ' )) ); 98 } 99 100 // If there were no xmlsrcs, go to the about page. 101 if ( xmlsrcs.length == 0 ) 102 { 103 location.replace( location.protocol + '//' + location.host + '/GeoRSS/about.html' ); 104 return; 105 } 106 107 // Fix protocols. To avoid problems with mixing http/https protocols, 108 // it's common to leave the protocol off of subsidiary resource URLs. 109 // Browsers know to fill in the same protocol as the parent page. 110 // However this doesn't happen automatically for the xmlsrc parameters. 111 // So, we make it happen. 112 for ( var i in xmlsrcs ) 113 if ( xmlsrcs[i].charAt( 0 ) == '/' ) 114 xmlsrcs[i] = location.protocol + xmlsrcs[i]; 115 116 // Layout the panels. 117 Layout(); 118 window.onresize = Layout; 119 120 // Create the map. 121 map = acme.maps.CreateMap( 'map' ); 122 123 icons = [ acme.maps.blueIcon, acme.maps.redIcon, acme.maps.greenIcon, acme.maps.yellowIcon, acme.maps.purpleIcon, acme.maps.brownIcon, acme.maps.orangeIcon, acme.maps.whiteIcon, acme.maps.blackIcon ]; 124 125 iconSizeHelper['http://www.dbaclick.com/images/train8.gif'] = [ 28, 30 ]; 126 127 AddControlElement( map, searchElement, 'topright' ); 128 searchPatternElement.value = '' 129 searchTimer = setInterval( SearchCheck, 100 ); 130 131 AddControlElement( map, calendarElement, 'topright' ); 132 133 // Start fetching the XML files. 134 iconIdx = 0; 135 channels = []; 136 xi = 0; 137 FetchNextXmlSrc(); 138 } 139 140 141function Layout() 142 { 143 var wh = acme.utils.GetPageWH(); 144 var bodyWidth = wh.w; 145 var bodyHeight = wh.h; 146 listElement.style.bottom = footerElement.offsetHeight + 'px'; 147 mapElement.style.top = headerElement.offsetHeight + 'px'; 148 mapElement.style.left = 0; 149 if ( bodyWidth < bodyHeight ) 150 { 151 // Narrow. 152 listElement.style.left = 0; 153 listElement.style.right = 0; 154 listElement.style.height = '30vw'; 155 mapElement.style.right = 0; 156 mapElement.style.bottom = ( footerElement.offsetHeight + listElement.offsetHeight ) + 'px'; 157 } 158 else 159 { 160 // Not narrow. 161 listElement.style.top = headerElement.offsetHeight + 'px'; 162 listElement.style.right = 0; 163 listElement.style.width = '20vw'; 164 mapElement.style.right = listElement.offsetWidth + 'px'; 165 mapElement.style.bottom = footerElement.offsetHeight + 'px'; 166 } 167 if ( map ) 168 map.invalidateSize(); 169 } 170 171 172function FetchNextXmlSrc() 173 { 174 if ( xi >= xmlsrcs.length ) 175 { 176 // Done. 177 LoadAllJS( channels ); 178 Layout(); 179 Controls(); 180 return; 181 } 182 183 acme.utils.HttpGet( 'geocode_proxy.cgi?url=' + encodeURIComponent( xmlsrcs[xi] ), RequestChecker ); 184 } 185 186 187function RequestChecker( request ) 188 { 189 if ( request.responseXML == null ) 190 alert( 'XML parse failed!' ); 191 else 192 { 193 TraverseDocument( request.responseXML.documentElement, xmlsrcs[xi] ); 194 Display(); 195 } 196 197 // Start the next fetch. 198 ++xi; 199 FetchNextXmlSrc(); 200 } 201 202 203function Controls() 204 { 205 var i, j; 206 207 // Add any potential auto-selects that were used. 208 for ( i in potentialAutoSelects ) 209 if ( potentialAutoSelects[i].length > 0 ) 210 { 211 var options = potentialAutoSelects[i].sort(); 212 for ( j in options ) 213 options[j] = { text: options[j], value: '"' + i + ' ' + options[j] + '"' }; 214 selects.push( { name: i, options: options } ); 215 } 216 217 // Add any selects. 218 if ( selects.length > 0 ) 219 { 220 for ( i in selects ) 221 { 222 var select = selects[i]; 223 var tr = acme.utils.AppendElement( selectsTbodyElement, 'tr', {} ); 224 var td1 = acme.utils.AppendElement( tr, 'td', {} ); 225 acme.utils.AppendText( td1, select.name ); 226 var td2 = acme.utils.AppendElement( tr, 'td', {} ); 227 var selectElement = acme.utils.AppendElement( td2, 'select', { name: select.name, onchange: SearchFieldsChanged } ); 228 acme.utils.AppendElement( selectElement, 'option', { innerHTML: '' } ); 229 for ( j in select.options ) 230 { 231 var option = select.options[j]; 232 acme.utils.AppendElement( selectElement, 'option', { value: option.value, innerHTML: option.text } ); 233 } 234 selectsElements.push( selectElement ); 235 } 236 } 237 else 238 acme.utils.HideElement( selectsElement ); 239 240 pubDatesExist = false;
241 for ( i = 0; i < channels.length; ++i ) 242 for ( j = 0; j < channels[i].items.length; ++j ) 243 if ( channels[i].items[j].pubDate ) 244 pubDatesExist = true; 245 if ( pubDatesExist ) 246 acme.utils.ShowElement( calendarElement ); 247 } 248 249 250// The following few dozen routines traverse the XML parse tree. There's 251// a separate routine for each type of element. The code does get a 252// little spread out but the advantage is you hit each node precisely 253// once, no duplication of effort possible. 254 255 256function TraverseDocument( element, xmlsrc ) 257 { 258 var child = element.firstChild; 259 while ( child ) 260 { 261 if ( child.nodeName == 'channel' ) 262 TraverseChannel( child, xmlsrc ); 263 child = child.nextSibling; 264 } 265 } 266 267 268function TraverseChannel( element, xmlsrc ) 269 { 270 var channel = {}; 271 channel.xmlsrc = xmlsrc; 272 channel.groups = []; 273 channel.groupIdxLookup = [] 274 channel.items = []; 275 276 var child = element.firstChild; 277 while ( child ) 278 { 279 if ( child.nodeName == 'link' ) 280 TraverseChannelLink( child, channel ); 281 else if ( child.nodeName == 'title' ) 282 TraverseChannelTitle( child, channel ); 283 else if ( child.nodeName == 'description' ) 284 TraverseChannelDescription( child, channel ); 285 else if ( child.nodeName == 'managingEditor' ) 286 TraverseChannelManagingEditor( child, channel ); 287 else if ( child.nodeName == 'webMaster' ) 288 TraverseChannelWebMaster( child, channel ); 289 else if ( child.nodeName == 'image' ) 290 TraverseChannelImage( child, channel ); 291 else if ( child.nodeName == 'geo:lat' || child.nodeName == 'lat' ) 292 TraverseChannelGeoLat( child, channel ); 293 else if ( child.nodeName == 'geo:long' || child.nodeName == 'geo:lon' || child.nodeName == 'geo:lng' || child.nodeName == 'long' || child.nodeName == 'lon' || child.nodeName == 'lng' ) 294 TraverseChannelGeoLong( child, channel ); 295 else if ( child.nodeName == 'georss:point' || child.nodeName == 'point' ) 296 TraverseChannelGeorssPoint( child, channel ); 297 else if ( child.nodeName == 'ymaps:ZoomLevel' || child.nodeName == 'ZoomLevel' ) 298 TraverseChannelYmapsZoomLevel( child, channel ); 299 else if ( child.nodeName == 'ymaps:Groups' || child.nodeName == 'Groups' ) 300 TraverseYmapsGroups( child, channel ); 301// else if ( child.nodeName == 'js' ) 302// TraverseChannelJS( child, channel ); 303 else if ( child.nodeName == 'acme:select' ) 304 TraverseAcmeSelect( child, channel ); 305 else if ( child.nodeName == 'item' ) 306 TraverseItem( child, channel ); 307 child = child.nextSibling; 308 } 309 310 if ( channel.yZoom != null ) 311 channel.gZoom = 17 - Math.round( channel.yZoom * 3 / 2 ); 312 313 // Save the channel. 314 channels.push( channel ); 315 } 316 317 318function TraverseChannelLink( element, channel ) 319 { 320 channel.link = acme.utils.GetXmlText( element ); 321 } 322 323 324function TraverseChannelTitle( element, channel ) 325 { 326 channel.title = acme.utils.GetXmlText( element ); 327 } 328 329 330function TraverseChannelDescription( element, channel ) 331 { 332 channel.description = acme.utils.GetXmlText( element ); 333 } 334 335 336function TraverseChannelManagingEditor( element, channel ) 337 { 338 channel.managingEditor = acme.utils.GetXmlText( element ); 339 } 340 341 342function TraverseChannelWebMaster( element, channel ) 343 { 344 channel.webMaster = acme.utils.GetXmlText( element ); 345 } 346 347 348function TraverseChannelImage( element, channel ) 349 { 350 var child = element.firstChild; 351 while ( child ) 352 { 353 if ( child.nodeName == 'url' ) 354 TraverseChannelImageUrl( child, channel ); 355 else if ( child.nodeName == 'title' ) 356 TraverseChannelImageTitle( child, channel ); 357 else if ( child.nodeName == 'description' ) 358 TraverseChannelImageDescription( child, channel ); 359 else if ( child.nodeName == 'link' ) 360 TraverseChannelImageLink( child, channel ); 361 else if ( child.nodeName == 'width' ) 362 TraverseChannelImageWidth( child, channel ); 363 else if ( child.nodeName == 'height' ) 364 TraverseChannelImageHeight( child, channel ); 365 child = child.nextSibling; 366 } 367 } 368 369 370function TraverseChannelImageUrl( element, channel ) 371 { 372 channel.imageUrl = acme.utils.GetXmlText( element ); 373 } 374 375 376function TraverseChannelImageTitle( element, channel ) 377 { 378 channel.imageTitle = acme.utils.GetXmlText( element ); 379 } 380 381 382function TraverseChannelImageDescription( element, channel ) 383 { 384 channel.imageDescription = acme.utils.GetXmlText( element ); 385 } 386 387 388function TraverseChannelImageLink( element, channel ) 389 { 390 channel.imageLink = acme.utils.GetXmlText( element ); 391 } 392 393 394function TraverseChannelImageWidth( element, channel ) 395 { 396 channel.imageWidth = parseInt( acme.utils.GetXmlText( element ) ); 397 } 398 399 400function TraverseChannelImageHeight( element, channel ) 401 { 402 channel.imageHeight = parseInt( acme.utils.GetXmlText( element ) ); 403 } 404 405 406function TraverseChannelGeoLat( element, channel ) 407 { 408 channel.lat = parseFloat( acme.utils.GetXmlText( element ) ); 409 } 410 411 412function TraverseChannelGeoLong( element, channel ) 413 { 414 channel.lng = parseFloat( acme.utils.GetXmlText( element ) ); 415 } 416 417 418function TraverseChannelGeoPoint( element, channel ) 419 { 420 var vals = acme.utils.GetXmlText( element ).split( ' ' ); 421 if ( vals.length == 2 ) 422 { 423 channel.lat = parseFloat( vals[0] ); 424 channel.lng = parseFloat( vals[1] ); 425 } 426 } 427 428 429 430function TraverseChannelYmapsZoomLevel( element, channel ) 431 {
432 channel.yZoom = parseInt( acme.utils.GetXmlText( element ) ); 433 } 434 435 436function TraverseYmapsGroups( element, channel ) 437 { 438 var child = element.firstChild; 439 while ( child ) 440 { 441 if ( child.nodeName == 'group' || child.nodeName == 'Group' ) 442 TraverseGroup( child, channel ); 443 child = child.nextSibling; 444 } 445 } 446 447 448function TraverseGroup( element, channel ) 449 { 450 var group = {}; 451 452 var child = element.firstChild; 453 while ( child ) 454 { 455 if ( child.nodeName == 'title' || child.nodeName == 'Title' ) 456 TraverseGroupTitle( child, group ); 457 else if ( child.nodeName == 'id' || child.nodeName == 'Id' ) 458 TraverseGroupId( child, group ); 459 else if ( child.nodeName == 'ymaps:BaseIcon' || child.nodeName == 'BaseIcon' ) 460 TraverseGroupBaseIcon( child, group ); 461 else if ( child.nodeName == 'ymaps:HoverIcon' || child.nodeName == 'HoverIcon' ) 462 TraverseGroupHoverIcon( child, group ); 463 else if ( child.nodeName == 'ymaps:PopupIcon' || child.nodeName == 'PopupIcon' ) 464 TraverseGroupPopupIcon( child, group ); 465 child = child.nextSibling; 466 } 467 468 if ( group.id ) 469 channel.groupIdxLookup[group.id] = channel.groups.length 470 if ( group.icon == null ) 471 { 472 group.icon = icons[iconIdx]; 473 iconIdx = ( iconIdx + 1 ) % icons.length; 474 } 475 476 channel.groups.push( group ); 477 } 478 479 480function TraverseGroupTitle( element, group ) 481 { 482 group.title = acme.utils.GetXmlText( element ); 483 } 484 485 486function TraverseGroupId( element, group ) 487 { 488 group.id = acme.utils.GetXmlText( element ); 489 } 490 491 492function TraverseGroupBaseIcon( element, group ) 493 { 494 group.baseIcon = acme.utils.GetXmlText( element ); 495 496 group.icon = L.icon( 497 { 498 iconUrl: group.baseIcon 499 } ); 500 LookupIconSize( group.icon ); 501 } 502 503 504function TraverseGroupHoverIcon( element, group ) 505 { 506 group.hoverIcon = acme.utils.GetXmlText( element ); 507 // Nothing done with this yet. 508 } 509 510 511function TraverseGroupPopupIcon( element, group ) 512 { 513 group.popupIcon = acme.utils.GetXmlText( element ); 514 // Nothing done with this yet. 515 } 516 517 518function TraverseChannelJS( element, channel ) 519 { 520 if ( channel.js == null ) 521 channel.js = [] 522 channel.js.push( acme.utils.GetXmlText( element ) ); 523 } 524 525 526function TraverseAcmeSelect( element, channel ) 527 { 528 var select = { options: [] } 529 530 select.name = element.getAttribute( 'name' ); 531 532 var child = element.firstChild; 533 while ( child ) 534 { 535 if ( child.nodeName == 'acme:option' ) 536 TraverseAcmeSelectOption( child, select ); 537 child = child.nextSibling; 538 } 539 540 selects.push( select ); 541 } 542 543 544function TraverseAcmeSelectOption( element, select ) 545 { 546 var text = acme.utils.GetXmlText( element ); 547 if ( text ) 548 text = DeFang( text ); 549 var value = element.getAttribute( 'value' ); 550 if ( value ) 551 value = DeFang( value ); 552 else 553 value = '"' + text + '"'; 554 select.options.push ( { text: text, value: value } ); 555 } 556 557 558function TraverseItem( element, channel ) 559 { 560 var item = {}; 561 item.groupIdx = -1; 562 item.extraLinks = []; 563 item.extraImages = []; 564 565 var child = element.firstChild; 566 while ( child ) 567 { 568 if ( child.nodeName == 'title' ) 569 TraverseItemTitle( child, item ); 570 else if ( child.nodeName == 'pubDate' ) 571 TraverseItemPubDate( child, item ); 572 else if ( child.nodeName == 'link' ) 573 TraverseItemLink( child, item ); 574 else if ( child.nodeName == 'description' ) 575 TraverseItemDescription( child, item ); 576 else if ( child.nodeName == 'author' ) 577 TraverseItemAuthor( child, item ); 578 else if ( child.nodeName == 'geo:lat' || child.nodeName == 'lat' ) 579 TraverseItemGeoLat( child, item ); 580 else if ( child.nodeName == 'geo:long' || child.nodeName == 'geo:lon' || child.nodeName == 'geo:lng' || child.nodeName == 'long' || child.nodeName == 'lon' || child.nodeName == 'lng' ) 581 TraverseItemGeoLong( child, item ); 582 else if ( child.nodeName == 'georss:point' || child.nodeName == 'point' ) 583 TraverseItemGeorssPoint( child, item ); 584 else if ( child.nodeName == 'ymaps:Address' || child.nodeName == 'Address' ) 585 TraverseItemYmapsAddress( child, item ); 586 else if ( child.nodeName == 'acme:neighborhood' || child.nodeName == 'neighborhood' ) 587 TraverseItemAcmeNeighborhood( child, item ); 588 else if ( child.nodeName == 'ymaps:CityState' || child.nodeName == 'CityState' ) 589 TraverseItemYmapsCityState( child, item ); 590 else if ( child.nodeName == 'ymaps:Zip' || child.nodeName == 'Zip' ) 591 TraverseItemYmapsZip( child, item ); 592 else if ( child.nodeName == 'ymaps:Country' || child.nodeName == 'Country' ) 593 TraverseItemYmapsCountry( child, item ); 594 else if ( child.nodeName == 'ymaps:PhoneNumber' || child.nodeName == 'PhoneNumber' ) 595 TraverseItemYmapsPhoneNumber( child, item ); 596 else if ( child.nodeName == 'ymaps:GroupId' || child.nodeName == 'GroupId' ) 597 TraverseItemYmapsGroupId( child, item ); 598 else if ( child.nodeName == 'ymaps:BaseIcon' || child.nodeName == 'BaseIcon' ) 599 TraverseItemYmapsBaseIcon( child, item ); 600 else if ( child.nodeName == 'ymaps:HoverIcon' || child.nodeName == 'HoverIcon' ) 601 TraverseItemYmapsHoverIcon( child, item ); 602 else if ( child.nodeName == 'ymaps:PopupIcon' || child.nodeName == 'PopupIcon' ) 603 TraverseItemYmapsPopupIcon( child, item ); 604 else if ( child.nodeName == 'ymaps:ExtraLink' || child.nodeName == '
604ExtraLink' ) 605 TraverseItemYmapsExtraLink( child, item ); 606 else if ( child.nodeName == 'ymaps:ExtraImage' || child.nodeName == 'ExtraImage' ) 607 TraverseItemYmapsExtraImage( child, item ); 608 else if ( child.nodeName == 'ymaps:ItemUrl' || child.nodeName == 'ItemUrl' ) 609 TraverseItemYmapsItemUrl( child, item ); 610 child = child.nextSibling; 611 } 612 613 if ( item.groupId != null ) 614 item.groupIdx = channel.groupIdxLookup[item.groupId]; 615 616 channel.items.push( item ); 617 } 618 619 620function TraverseItemTitle( element, item ) 621 { 622 item.title = acme.utils.GetXmlText( element ); 623 } 624 625 626function TraverseItemPubDate( element, item ) 627 { 628 item.pubDateStr = acme.utils.GetXmlText( element ); 629 item.pubDate = new Date( item.pubDateStr ); 630 } 631 632 633function TraverseItemLink( element, item ) 634 { 635 item.link = acme.utils.GetXmlText( element ); 636 } 637 638 639function TraverseItemDescription( element, item ) 640 { 641 item.description = acme.utils.GetXmlText( element ); 642 } 643 644 645function TraverseItemAuthor( element, item ) 646 { 647 item.author = acme.utils.GetXmlText( element ); 648 } 649 650 651function TraverseItemGeoLat( element, item ) 652 { 653 item.lat = parseFloat( acme.utils.GetXmlText( element ) ); 654 } 655 656 657function TraverseItemGeoLong( element, item ) 658 { 659 item.lng = parseFloat( acme.utils.GetXmlText( element ) ); 660 } 661 662 663function TraverseItemGeorssPoint( element, item ) 664 { 665 var vals = acme.utils.GetXmlText( element ).split( ' ' ); 666 if ( vals.length == 2 ) 667 { 668 item.lat = parseFloat( vals[0] ); 669 item.lng = parseFloat( vals[1] ); 670 } 671 } 672 673 674function TraverseItemYmapsAddress( element, item ) 675 { 676 item.address = acme.utils.GetXmlText( element ); 677 } 678 679 680function TraverseItemAcmeNeighborhood( element, item ) 681 { 682 item.neighborhood = acme.utils.GetXmlText( element ); 683 var options = potentialAutoSelects['Neighborhood']; 684 if ( ! options.includes( item.neighborhood ) ) 685 options.push( item.neighborhood ); 686 } 687 688 689function TraverseItemYmapsCityState( element, item ) 690 { 691 item.cityState = acme.utils.GetXmlText( element ); 692 } 693 694 695function TraverseItemYmapsZip( element, item ) 696 { 697 item.zip = acme.utils.GetXmlText( element ); 698 } 699 700 701function TraverseItemYmapsCountry( element, item ) 702 { 703 item.country = acme.utils.GetXmlText( element ); 704 } 705 706 707function TraverseItemYmapsPhoneNumber( element, item ) 708 { 709 item.phoneNumber = acme.utils.GetXmlText( element ); 710 } 711 712 713function TraverseItemYmapsGroupId( element, item ) 714 { 715 item.groupId = acme.utils.GetXmlText( element ); 716 if ( item.groupIdx == null ) 717 item.groupIdx = -1; 718 } 719 720 721function TraverseItemYmapsBaseIcon( element, item ) 722 { 723 item.baseIcon = acme.utils.GetXmlText( element ); 724 725 item.icon = L.icon( 726 { 727 iconUrl: item.baseIcon 728 } ); 729 LookupIconSize( item.icon ); 730 } 731 732 733function TraverseItemYmapsHoverIcon( element, item ) 734 { 735 item.hoverIcon = acme.utils.GetXmlText( element ); 736 // Nothing done with this yet. 737 } 738 739 740function TraverseItemYmapsPopupIcon( element, item ) 741 { 742 item.popupIcon = acme.utils.GetXmlText( element ); 743 // Nothing done with this yet. 744 } 745 746 747function TraverseItemYmapsExtraLink( element, item ) 748 { 749 var extraLink = {}; 750 extraLink.text = acme.utils.GetXmlText( element ); 751 extraLink.href = element.getAttribute( 'href' ); 752 item.extraLinks.push( extraLink ); 753 } 754 755 756function TraverseItemYmapsExtraImage( element, item ) 757 { 758 var extraImage = {}; 759 760 var child = element.firstChild; 761 while ( child ) 762 { 763 if ( child.nodeName == 'url' ) 764 TraverseExtraImageUrl( child, extraImage ); 765 else if ( child.nodeName == 'title' ) 766 TraverseExtraImageTitle( child, extraImage ); 767 else if ( child.nodeName == 'link' ) 768 TraverseExtraImageLink( child, extraImage ); 769 else if ( child.nodeName == 'width' ) 770 TraverseExtraImageWidth( child, extraImage ); 771 else if ( child.nodeName == 'height' ) 772 TraverseExtraImageHeight( child, extraImage ); 773 child = child.nextSibling; 774 } 775 776 item.extraImages.push( extraImage ); 777 } 778 779 780function TraverseExtraImageUrl( element, extraImage ) 781 { 782 extraImage.url = acme.utils.GetXmlText( element ); 783 } 784 785 786function TraverseExtraImageTitle( element, extraImage ) 787 { 788 extraImage.title = acme.utils.GetXmlText( element ); 789 } 790 791 792function TraverseExtraImageLink( element, extraImage ) 793 { 794 extraImage.link = acme.utils.GetXmlText( element ); 795 } 796 797 798function TraverseExtraImageWidth( element, extraImage ) 799 { 800 extraImage.imageWidth = parseInt( acme.utils.GetXmlText( element ) ); 801 } 802 803 804function TraverseExtraImageHeight( element, extraImage ) 805 { 806 extraImage.imageHeight = parseInt( acme.utils.GetXmlText( element ) ); 807 } 808 809 810function TraverseItemYmapsItemUrl( element, item ) 811 { 812 item.itemUrl = acme.utils.GetXmlText( element ); 813 } 814 815 816// - - - - - - - - - - End of XML traversal. - - - - - - - - - 817 818 819function GetGroupIcon( channel, groupIdx ) 820 { 821 if ( groupIdx == -1 || channel.groups[groupIdx] == null ) 822 { 823 if ( channel.defaultIcon == null ) 824 { 825 channel.defaultIcon = icons[iconIdx]; 826 iconIdx = ( iconIdx + 1 ) % icons.length; 827 } 828 return channel.defaultIcon; 829 } 830 var groupIcon = channel.groups[groupIdx].icon; 831 if ( groupIcon == null ) 832 return icons[0]; // shouldn't happen 833 return groupIcon; 834 } 835 836 837function GetItemIcon( channel, item ) 838 { 839 if ( item.icon ) 840 return item.icon; 841 return GetGroupIcon( channel, item.groupIdx ); 842 } 843 844 845function GetGroupIconUrl( channel, groupIdx ) 846 { 847 var groupIcon = GetGroupIcon( channel, groupIdx ); 848 var groupUrl = groupIcon.url; 849 return groupUrl; 850 } 851 852 853function GetItemIconUrl( channel, item ) 854 { 855 var itemIcon = GetItemIcon( channel, item ); 856 var itemUrl = itemIcon.url; 857 return itemUrl; 858 } 859 860 861function GetGroupSmallUrl( channel, groupIdx ) 862 { 863 var groupIcon = GetGroupIcon( channel, groupIdx ); 864 var groupSmallUrl = groupIcon.options.smallUrl; 865 if ( ! groupSmallUrl ) 866 groupSmallUrl = groupIcon.options.iconUrl; 867 return groupSmallUrl; 868 } 869 870 871function GetItemSmallUrl( channel, item ) 872 { 873 var itemIcon = GetItemIcon( channel, item ); 874 var itemSmallUrl = itemIcon.options.smallUrl; 875 if ( ! itemSmallUrl ) 876 itemSmallUrl = itemIcon.options.iconUrl; 877 return itemSmallUrl; 878 } 879 880 881function LoadAllJS( channels ) 882 { 883 for ( var i = 0; i < channels.length; ++i ) 884 { 885 var channel = channels[i]; 886 var js = channel.js; 887 if ( js != null ) 888 LoadJS( js, channel.xmlsrc ) 889 } 890 } 891 892function LoadJS( js, xmlsrc ) 893 { 894 for ( var i = 0; i < js.length; ++i ) 895 { 896 var jurl = js[i]; 897 898 // Check that the JS URL has the same origin as the XML URL. 899 try 900 { 901 if ( ! SameOrigin( jurl, xmlsrc ) ) 902 { 903 alert( 'JS URL has a different origin!' ); 904 return; 905 } 906 } 907 catch ( e ) 908 { 909 alert( 'JS URL parse error!' ); 910 return; 911 } 912 alert( 'ok to load ' + jurl ); //!!! 913 } 914 915 //!!! 916 } 917 918function SameOrigin( urlstr1, urlstr2 ) 919 { 920 var url1 = new URL( urlstr1 ); 921 var url2 = new URL( urlstr2 ); 922 return url1.origin == url2.origin; 923 // This check is too strict, really. But that's beter than too lenient. 924 } 925 926 927// Display the current state.
928function Display() 929 { 930 var i, j, channel, item, same, html, address, title; 931 var imageHtml = ''; 932 var titleHtml = ''; 933 var descriptionHtml = ''; 934 var groupsHtml = ''; 935 var listHtml = ''; 936 var plainHtmlHtml = ''; 937 938 // Get rid of any existing points and markers. 939 if ( markers !== null ) 940 for ( i = 0; i < markers.length; ++i ) 941 markers[i].remove(); 942 points = []; 943 markers = []; 944 945 for ( i = 0; i < channels.length; ++i ) 946 { 947 channel = channels[i]; 948 949 // Image. 950 if ( channel.imageUrl || channel.imageDescription ) 951 { 952 same = false; 953 for ( j = 0; j < i; ++j ) 954 if ( channel.imageUrl == channels[j].imageUrl && channel.imageLink == channels[j].imageLink && channel.imageTitle == channels[j].imageTitle && channel.imageDescription == channels[j].imageDescription ) 955 { 956 same = true; 957 break; 958 } 959 if ( ! same ) 960 { 961 html = '<table border="0">'; 962 if ( channel.imageUrl ) 963 { 964 html += '<tr><td>' 965 if ( channel.imageLink ) 966 html += '<a target="_top" href="' + encodeURI( channel.imageLink ) + '">'; 967 html += '<img '; 968 if ( channel.imageTitle ) 969 html += 'alt="' + channel.imageTitle + '" '; 970 if ( channel.imageWidth != null ) 971 html += 'width="' + channel.imageWidth + '" '; 972 if ( channel.imageHeight != null ) 973 html += 'height="' + channel.imageHeight + '" '; 974 html += 'src="' + channel.imageUrl + '" />'; 975 if ( channel.imageLink ) 976 html += '</a>'; 977 html += '</td></tr>' 978 } 979 if ( channel.imageDescription ) 980 { 981 html += '<tr><td>' 982 if ( channel.imageLink ) 983 html += '<a target="_top" href="' + channel.imageLink + '">'; 984 html += channel.imageDescription; 985 if ( channel.imageLink ) 986 html += '</a>'; 987 html += '</td></tr>' 988 } 989 html += '</table>' 990 imageHtml += html; 991 } 992 } 993 if ( imageHtml == '' ) 994 imageHtml = '<img src="//acme.com/resources/images/atom_ani.gif" />'; 995 996 // Title. 997 if ( channel.title ) 998 { 999 same = false; 1000 for ( j = 0; j < i; ++j ) 1001 if ( channel.title == channels[j].title && channel.link == channels[j].link ) 1002 { 1003 same = true; 1004 break; 1005 } 1006 if ( ! same ) 1007 { 1008 html = ''; 1009 if ( channel.link ) 1010 html += '<a href="' + channel.link + '">'; 1011 html += channel.title; 1012 if ( channel.link ) 1013 html += '</a>'; 1014 html += '<br />'; 1015 titleHtml += html; 1016 } 1017 } 1018 1019 // Description. 1020 if ( channel.description ) 1021 { 1022 same = false; 1023 for ( j = 0; j < i; ++j ) 1024 if ( channel.description == channels[j].description ) 1025 { 1026 same = true; 1027 break; 1028 } 1029 if ( ! same ) 1030 descriptionHtml += channel.description + '<br />'; 1031 } 1032 1033 // Groups. 1034 if ( channel.groups.length > 0 ) 1035 { 1036 html = '<table border="1" cellpadding="5"><tbody><tr>'; 1037 var perRow = 0; 1038 for ( j = 0; j < channel.groups.length; ++j ) 1039 { 1040 if ( perRow >= 5 ) 1041 { 1042 html += '</tr><tr>'; 1043 perRow = 0; 1044 } 1045 html += '<td><img src="' + GetGroupSmallUrl( channel, j ) + '" />' + channel.groups[j].title + '</td>'; 1046 ++perRow; 1047 } 1048 html += '</tr></tbody></table><br />'; 1049 groupsHtml += html; 1050 } 1051 1052 // Items: markers and list. 1053 for ( j = 0; j < channel.items.length; ++j ) 1054 { 1055 item = channel.items[j]; 1056 1057 if ( searchFieldsShowing ) 1058 { 1059 // Text search and selects check. 1060 var searchPattern = searchPatternElement.value; 1061 var searchText = ''; 1062 if ( item.title ) searchText += item.title + ' '; 1063 if ( item.description ) searchText += item.description + ' '; 1064 if ( item.address ) searchText += item.address + ' '; 1065 if ( item.neighborhood ) searchText += '"Neighborhood ' + item.neighborhood + '" '; 1066 if ( item.cityState ) searchText += item.cityState + ' '; 1067 if ( selects.length > 0 ) 1068 { 1069 for ( var k in selectsElements ) 1070 { 1071 var value = selectsElements[k].value; 1072 if ( value ) 1073 searchPattern += value + ' '; 1074 } 1075 } 1076 if ( searchPattern && searchText ) 1077 if ( ! GooGrep( searchPattern, searchText ) ) 1078 continue; // don't show this item 1079 } 1080 1081 if ( datePickerShowing ) 1082 { 1083 // Date check. Add a day of slop on each end for timezones. 1084 if ( pubDatesExist && item.pubDate && 1085 datePickerEarliest && datePickerLatest && 1086 ( item.pubDate.getTime() < datePickerEarliest.getTime() - dayMsecs || 1087 item.pubDate.getTime() > datePickerLatest.getTime() + dayMsecs ) ) 1088 continue; // don't show this item 1089 } 1090 1091 if ( item.lat != null && item.lng != null ) 1092 { 1093 var point = L.latLng( item.lat, item.lng ); 1094 var marker = new L.marker( point, { icon: GetItemIcon( channel, item ), title: acme.utils.EntityToIso8859( item.title ), alt: item.title } ).addTo( map ); 1095 marker.channelIdx = i; 1096 marker.itemIdx = j; 1097 item.markerIdx = markers.length;
1098 marker.bindPopup( acme.utils.Partial( PopUpHTML, item.markerIdx ) ); 1099 points.push( point ); 1100 markers.push( marker ); 1101 } 1102 1103 if ( item.title ) 1104 { 1105 html = ''; 1106 if ( item.markerIdx != null ) 1107 { 1108 var link = '<a href="javascript:PopUpOpen( ' + item.markerIdx + ' )">'; 1109 html += link + '<img border="0" src="' + GetItemSmallUrl( channel, item ) + '" /></a> ' + link + item.title + '</a>'; 1110 } 1111 else if ( item.link ) 1112 html += '<a target="_top" href="' + item.link + '">' + item.title + '</a>'; 1113 else 1114 html += item.title; 1115 html += '<br />'; 1116 listHtml += html; 1117 } 1118 } 1119 1120 if ( channel.title ) 1121 title = channel.title; 1122 else 1123 title = xmlsrcs[xi]; 1124 html = '<a target="_top" href="html.cgi?xmlsrc=' + encodeURIComponent( xmlsrcs[xi] ) + '">' + title + ' as plain HTML</a>'; 1125 plainHtmlHtml += html; 1126 } 1127 1128 if ( channels.length == 1 && channels[0].lat != null && channels[0].lng != null && channels[0].gZoom != null ) 1129 map.setView( L.latLng( channels[0].lat, channels[0].lng ), channels[0].gZoom ); 1130 else if ( points.length > 0 ) 1131 map.fitBounds( L.latLngBounds( points ), { animate: false } ); 1132 1133 imageElement.innerHTML = imageHtml; 1134 titleElement.innerHTML = titleHtml; 1135 descriptionElement.innerHTML = descriptionHtml; 1136 groupsElement.innerHTML = groupsHtml; 1137 listElement.innerHTML = listHtml; 1138 plainHtmlElement.innerHTML = plainHtmlHtml; 1139 } 1140 1141 1142// Text search stuff. 1143 1144var searchFieldsShowing = false; 1145var searchTimer; 1146var searchPrevPattern = ''; 1147 1148function SearchFieldsToggle() 1149 { 1150 searchFieldsShowing = ! searchFieldsShowing; 1151 if ( searchFieldsShowing ) 1152 { 1153 acme.utils.ShowElement( searchFieldsElement ); 1154 searchPatternElement.focus(); 1155 } 1156 else 1157 { 1158 acme.utils.HideElement( searchFieldsElement ); 1159 } 1160 SearchFieldsChanged(); 1161 } 1162 1163 1164function SearchCheck() 1165 { 1166 if ( searchPatternElement.value != searchPrevPattern ) 1167 { 1168 searchPrevPattern = searchPatternElement.value; 1169 SearchFieldsChanged(); 1170 } 1171 } 1172 1173 1174function GooGrep( pattern, text ) 1175 { 1176 // Split pattern up into an array of terms. 1177 var terms = []; 1178 var i = 0, j, prefix, postfix; 1179 while ( i < pattern.length ) 1180 { 1181 // Skip spaces. 1182 if ( pattern.charAt( i ) == ' ' ) 1183 { 1184 ++i; 1185 continue; 1186 } 1187 // Find the end of this term. 1188 var c = pattern.charAt( i ); 1189 if ( c == '"' || c == "'" ) 1190 { 1191 // Quoted string. 1192 ++i; 1193 j = i + 1; 1194 while ( j < pattern.length && pattern.charAt( j ) != c ) 1195 ++j; 1196 // Quoted strings must match at word boundaries. For reasons. 1197 prefix = '(?:^|[^a-z0-9_&;])'; 1198 postfix = '(?:[^a-z0-9_&;]|$)'; 1199 //!!! Adding &; to the non-words character class is kind of hackish. 1200 } 1201 else 1202 { 1203 // Not quoted. 1204 j = i + 1; 1205 while ( j < pattern.length && pattern.charAt( j ) != ' ' ) 1206 ++j; 1207 prefix = postfix = ''; 1208 } 1209 // Turn it into a regular expression and save it. 1210 var str = pattern.slice( i, j ); 1211 if ( RegExp.escape ) 1212 str = RegExp.escape( str ); 1213 var restr = prefix + str + postfix; 1214 terms.push( new RegExp( restr, 'i' ) ); 1215 // And go on to the next one. 1216 i = j + 1; 1217 } 1218 1219 // Now do the matches. The text must match all the terms. 1220 for ( i in terms ) 1221 { 1222 if ( ! terms[i].test( text ) ) 1223 return false; 1224 } 1225 return true; 1226 } 1227 1228 1229function DeFang( str ) 1230 { 1231 return str.replaceAll( '<', '<' ).replaceAll( '>', '>' ); 1232 } 1233 1234 1235function SearchFieldsChanged() 1236 { 1237 Display(); 1238 } 1239 1240 1241// Date picker stuff. 1242 1243var datePickerShowing = false; 1244var datePickerFirstTime = true; 1245var datePickerEarliest = null; 1246var datePickerLatest = null; 1247var datePickerEarliestCalendarOpen; 1248var datePickerLatestCalendarOpen; 1249 1250function DatePickerToggle() 1251 { 1252 datePickerShowing = ! datePickerShowing; 1253 if ( datePickerShowing ) 1254 { 1255 acme.utils.ShowElement( datePickerElement ); 1256 datePickerEarliestCalendarOpen = false; 1257 datePickerLatestCalendarOpen = false; 1258 } 1259 else 1260 { 1261 acme.utils.HideElement( datePickerElement ); 1262 acme.utils.HideElement( datePickerEarliestContainer ); 1263 acme.utils.HideElement( datePickerLatestContainer ); 1264 } 1265 DatePickerChanged(); 1266 } 1267 1268 1269var secondMsecs = 1000; 1270var minuteMsecs = secondMsecs * 60; 1271var hourMsecs = minuteMsecs * 60; 1272var dayMsecs = hourMsecs * 24; 1273var weekMsecs = dayMsecs * 7; 1274var monthMsecs = dayMsecs * 31; 1275var yearMsecs = dayMsecs * 365; 1276 1277function DatePickerChanged() 1278 { 1279 if ( datePickerFirstTime ) 1280 { 1281 datePickerSelectElement.value = 'all'; 1282 datePickerFirstTime = false;
1283 } 1284 var now = new Date(); 1285 switch ( datePickerSelectElement.value ) 1286 { 1287 case 'day': 1288 datePickerLatest = now; 1289 datePickerEarliest = new Date( now.getTime() - dayMsecs ); 1290 break; 1291 case 'week': 1292 datePickerLatest = now; 1293 datePickerEarliest = new Date( now.getTime() - weekMsecs ); 1294 break; 1295 case 'month': 1296 datePickerLatest = now; 1297 datePickerEarliest = new Date( now.getTime() - monthMsecs ); 1298 break; 1299 case 'year': 1300 datePickerLatest = now; 1301 datePickerEarliest = new Date( now.getTime() - yearMsecs ); 1302 break; 1303 case 'all': 1304 var datePickerEarliestTime = 100000000000000; 1305 var datePickerLatestTime = 0; 1306 for ( var i = 0; i < channels.length; ++i ) 1307 { 1308 var channel = channels[i]; 1309 for ( var j = 0; j < channel.items.length; ++j ) 1310 { 1311 var item = channel.items[j]; 1312 var pubDate = item.pubDate; 1313 if ( pubDate ) 1314 { 1315 var pubDateTime = pubDate.getTime(); 1316 if ( pubDateTime < datePickerEarliestTime ) 1317 datePickerEarliestTime = pubDateTime; 1318 if ( pubDateTime > datePickerLatestTime ) 1319 datePickerLatestTime = pubDateTime; 1320 } 1321 } 1322 } 1323 datePickerEarliest = new Date( datePickerEarliestTime ); 1324 datePickerLatest = new Date( datePickerLatestTime ); 1325 break; 1326 case 'custom': 1327 // Leave the values alone. 1328 break; 1329 } 1330 datePickerEarliestElement.textContent = acme.utils.Strftime( '%d%b%Y', datePickerEarliest ); 1331 datePickerLatestElement.textContent = acme.utils.Strftime( '%d%b%Y', datePickerLatest ); 1332 Display(); 1333 } 1334 1335 1336function DatePickerCalendarToggle( which ) 1337 { 1338 var container; 1339 var date; 1340 switch ( which ) 1341 { 1342 case 'earliest': 1343 if ( datePickerEarliestCalendarOpen ) 1344 { 1345 acme.utils.HideElement( datePickerEarliestContainer ); 1346 datePickerEarliestCalendarOpen = false; 1347 return; 1348 } 1349 datePickerEarliestCalendarOpen = true; 1350 datePickerLatestCalendarOpen = false; 1351 container = datePickerEarliestContainer; 1352 date = datePickerEarliest; 1353 break; 1354 case 'latest': 1355 if ( datePickerLatestCalendarOpen ) 1356 { 1357 acme.utils.HideElement( datePickerLatestContainer ); 1358 datePickerLatestCalendarOpen = false; 1359 return; 1360 } 1361 datePickerLatestCalendarOpen = true; 1362 datePickerEarliestCalendarOpen = false; 1363 container = datePickerLatestContainer; 1364 date = datePickerLatest; 1365 break; 1366 } 1367 acme.utils.HideElement( datePickerEarliestContainer ); 1368 acme.utils.HideElement( datePickerLatestContainer ); 1369 acme.utils.ClearElement( container ); 1370 acme.calendar.MakeCalendarDMY( container, date.getDate(), date.getMonth() + 1, date.getFullYear(), acme.utils.Partial( DatePickerCallback, which, container ) ); 1371 acme.utils.ShowElement( container ); 1372 } 1373 1374 1375function DatePickerCallback( which, container, day, month, year ) 1376 { 1377 datePickerSelectElement.value = 'custom'; 1378 var date = new Date( year, month - 1, day, 12, 0, 0, 0 ); 1379 switch ( which ) 1380 { 1381 case 'earliest': 1382 datePickerEarliest = date; 1383 break; 1384 case 'latest': 1385 datePickerLatest = date; 1386 break; 1387 } 1388 DatePickerChanged(); 1389 acme.utils.HideElement( container ); 1390 datePickerEarliestCalendarOpen = false; 1391 datePickerLatestCalendarOpen = false; 1392 } 1393 1394 1395// Open the info box for the specified marker. 1396function PopUpOpen( m ) 1397 { 1398 markers[m].openPopup(); 1399 } 1400 1401 1402// Close any open info boxes. 1403function PopUpCloseAll() 1404 { 1405 if ( searchFieldsShowing ) 1406 SearchFieldsToggle(); 1407 if ( datePickerShowing ) 1408 DatePickerToggle(); 1409 for ( var m in markers ) 1410 markers[m].closePopup(); 1411 } 1412 1413 1414// Generate the popup HTML for the specified marker. 1415function PopUpHTML( m ) 1416 { 1417 var i; 1418 1419 var marker = markers[m]; 1420 if ( marker.channelIdx == null || marker.itemIdx == null ) 1421 return; 1422 var item = channels[marker.channelIdx].items[marker.itemIdx]; 1423 var html = ''; 1424 if ( item.link ) 1425 html += '<a target="_top" href="' + item.link + '">'; 1426 if ( item.title ) 1427 html += '<b>' + item.title + '</b>'; 1428 if ( item.link ) 1429 html += '</a>'; 1430 html += '<br />'; 1431 if ( item.pubDate ) 1432 { 1433 html += acme.utils.Strftime( '%d%b%Y', item.pubDate ); 1434 html += '<br />'; 1435 } 1436 if ( item.address ) 1437 html += item.address + '<br />'; 1438 if ( item.cityState || item.zip || item.country ) 1439 { 1440 if ( item.cityState ) 1441 html += item.cityState + ' '; 1442 if ( item.zip ) 1443 html += item.zip + ' '; 1444 if ( item.country ) 1445 html += item.country + ' '; 1446 html += '<br />'; 1447 } 1448 if ( item.phoneNumber ) 1449 html += item.phoneNumber + '<br />'; 1450 if ( item.description ) 1451 html += '<p>' + item.description + '</p>'; 1452 if ( item.author ) 1453 html += '<p>
1453<a target="_top" href="mailto:' + item.author + '">' + item.author + '</a></p>'; 1454 for ( i = 0; i < item.extraLinks.length; ++i ) 1455 html += '<a target="_top" href="' + item.extraLinks[i].href + '">' + item.extraLinks[i].text + '</a><br />'; 1456 for ( i = 0; i < item.extraImages.length; ++i ) 1457 { 1458 if ( item.extraImages[i].link ) 1459 html += '<a target="_top" href="' + item.extraImages[i].link + '">'; 1460 if ( item.extraImages[i].url ) 1461 { 1462 html += '<img '; 1463 if ( item.extraImages[i].title ) 1464 html += 'alt="' + item.extraImages[i].title + '" '; 1465 if ( item.extraImages[i].imageWidth != null ) 1466 html += 'width="' + item.extraImages[i].imageWidth + '" '; 1467 if ( item.extraImages[i].imageHeight != null ) 1468 html += 'height="' + item.extraImages[i].imageHeight + '" '; 1469 html += 'src="' + item.extraImages[i].url + '" style="max-width: 300px; max-height: 300px;" />'; 1470 } 1471 if ( item.extraImages[i].link ) 1472 html += '</a>'; 1473 } 1474 if ( item.itemUrl ) 1475 html += '<p><iframe src="' + item.itemUrl + '"></iframe></p>'; 1476 return html; 1477 } 1478 1479 1480function LookupIconSize( icon ) 1481 { 1482 var size = iconSizeHelper[icon.options.iconUrl]; 1483 if ( size ) 1484 icon.iconSize = size; 1485 } 1486 1487 1488function AddControlElement( aMap, anElement, aPosition ) 1489 { 1490 var aControlClass = L.Control.extend( { 1491 onAdd: function ( anotherMap ) { 1492 return anElement; 1493 } 1494 } ); 1495 var options = {}; 1496 if ( aPosition ) 1497 options.position = aPosition; 1498 var aControl = new aControlClass( options ); 1499 aControl.addTo( aMap ); 1500 return aControl; 1501 }
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.