1 2var TRIO = TRIO || {}; 3 4TRIO.LocationSlider = { 5 element: null, 6 7 create: function(elem) { 8 this.index = 0; 9 this.element = jQuery(elem); 10 11 if (this.element.length > 0) { 12 this.map = jQuery('#locationMap'); 13 this.scroller = this.element.find('.laboratory-finder__scroller'); 14 this.scrollUpHandle = this.element.find('a.laboratory-finder__scroll---up'); 15 this.scrollDownHandle = this.element.find('a.laboratory-finder__scroll---down'); 16 17 this.init(); 18 } 19 }, 20 21 init: function() { 22 var that = this; 23 24 this.scrollUpHandle.on('click', function() { 25 that.slide('up'); 26 }); 27 28 this.scrollDownHandle.on('click', function() { 29 that.slide('down'); 30 }); 31 32 this.setMapMarkers(); 33 34 this.updateButtons(); 35 }, 36 37 setMapMarkers: function() { 38 var that = this; 39 40 this.scroller.find('.laboratory-finder__location-list-item').each(function(n) { 41 var location = jQuery(this); 42 var coords = location.data('coords').split(','); 43 var isActive = (n == 0) ? ' laboratory-finder__marker--active' : ''; 44 var labelActive = (n == 0) ? ' style="display: block;"' : ' style="display: none;"'; 45 var marker = jQuery('<span class="laboratory-finder__marker" style="left: ' + coords[0] + '%; top: ' + coords[1] + '%;"><span class="laboratory-finder__pointer' + isActive + '"></span><span class="laboratory-finder__label ' + ((isActive) ? 'laboratory-finder__label--opened' : '') + '"' + labelActive + '>' + location.data('title') + '</span></span>'); 46 47 marker.on('click', function() { 48 that.scrollToLocation(n); 49 }); 50 51 marker.on('mouseenter', function() { 52 if (marker.find('.laboratory-finder__label--opened').length === 0) { 53 $('.laboratory-finder__label--opened').removeClass('laboratory-finder__label--opened').fadeOut(100); 54 that.showLabel(marker); 55 } 56 }); 57 58 that.map.append(marker); 59 }); 60 }, 61 62 showLabel: function(marker) { 63 marker.find('.laboratory-finder__label').addClass('laboratory-finder__label--opened').fadeIn(100); 64 }, 65 66 slide: function(direction) { 67 var slideTo, 68 that = this, 69 maxIndex = this.scroller.find(">.laboratory-finder__location-list-item").length - 1; 70 71 switch(direction) { 72 case 'up': 73 if (this.index > 0) { 74 this.index--; 75 } 76 break; 77 case 'down': 78 if (this.index < maxIndex) { 79 this.index++; 80 } 81 break; 82 } 83 84 slideTo = -this.index * 480; 85 this.scroller.stop(true, true).animate({top: slideTo}, function() { 86 that.update(); 87 }); 88 }, 89 90 scrollToLocation: function(index) { 91 // Only scroll if new index 92 if (index === this.index) { 93 return; 94 } 95 96 this.index = index; 97 this.scroller.stop(true, true).animate({top: -(index * 480)}); 98 this.update(); 99 }, 100 101 updateButtons: function() { 102 var numElements = $('.laboratory-finder__location-list-item').length; 103 104 if (this.index === 0) { 105 this.scrollUpHandle.addClass('laboratory-finder__scroll---disabled'); 106 } else if (this.index > 0 && this.index < numElements - 1) { 107 this.scrollUpHandle.add(this.scrollDownHandle).removeClass('laboratory-finder__scroll---disabled'); 108 } else { 109 this.scrollDownHandle.addClass('laboratory-finder__scroll---disabled'); 110 } 111 }, 112 113 update: function() { 114 this.map.find('.laboratory-finder__label').hide(); 115 this.map.find('.laboratory-finder__pointer').removeClass('laboratory-finder__marker--active'); 116 jQuery(this.map.find('.laboratory-finder__pointer')[this.index]).addClass('laboratory-finder__marker--active'); 117 jQuery(this.map.find('.laboratory-finder__marker')[this.index]).find('.laboratory-finder__label').fadeIn(); 118 119 this.updateButtons(); 120 } 121}; 122 123jQuery(function($) { 124 TRIO.LocationSlider.create('#location_slider'); 125}); 126 127// "Alle Srechstunden" hat ein reload? Soll das so? 128 129var TRIO = TRIO || {}; 130 131TRIO.ConsultationFinder = { 132 element: null, 133 134 create: function (elem) { 135 this.index = 0; 136 this.element = jQuery(elem); 137 138 if (this.element.length > 0) { 139 this.map = jQuery('#locationMap'); 140 this.scroller = this.element.find('.consultation-hour-finder__scroller'); 141 this.scrollUpHandle = this.element.find('a.consultation-hour-finder__scroll---up'); 142 this.scrollDownHandle = this.element.find('a.consultation-hour-finder__scroll---down'); 143 144 this.init(); 145 } 146 147 me = this; 148 }, 149 slideCardHeight: 480, 150 detectSlideHeight: function() { 151 var me = this; 152 jQuery('.consultation-hour-finder__location-content').each(function(i, el) { 153 var height = jQuery(el).outerHeight() 154 if (me.slideCardHeight < height) { 155 me.slideCardHeight = height + 20; // 20 is some extra space 156 } 157 }); 158 jQuery('#consultation-hour_finder') 159 .css('height', this.slideCardHeight + 'px'); 160 jQuery('.consultation-hour-finder__location-list-item, .consultation-hour-finder__location-list') 161 .css('height', this.slideCardHeight + 'px'); 162 }, 163 164 init: function () { 165 var that = this; 166 167 this.detectSlideHeight(); 168 169 this.scrollUpHandle.on('click', function () { 170 that.slide('up'); 171 }); 172 173 this.scrollDownHandle.on('click', function () { 174 that.slide('down'); 175 }); 176 177 this.setMapMarkers(); 178 179 this.updateButtons(); 180 }, 181 slide: function (direction) { 182 183 var slideTo, 184 that = this, 185 maxIndex = this.scroller.find(">.consultation-hour-finder__location-list-item").length - 1; 186 187 switch (direction) { 188 case 'up': 189 if (that.index > 0) { 190 that.index--; 191 that.currentSlide--; 192 } 193 break; 194 case 'down': 195 if (that.index < maxIndex) { 196 that.index++; 197 that.currentSlide++; 198 } 199 break; 200 } 201 202 slideTo = -that.index * this.slideCardHeight; 203 this.scroller.stop(true, true).animate({top: slideTo}, function () { 204 var elementId;
205 that.update(); 206 $('.consultation-hour-finder__location-list-item').not('.consultation-hour-finder__location-list-item--hidden').each(function (i) { 207 if (i === that.index) { 208 elementId = $(this).attr('id'); 209 } 210 }); 211 212 $(TRIO.ConsultationMap.markers).each(function () { 213 if (this.id === elementId) { 214 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker.png'); 215 TRIO.ConsultationMap.map.setCenter(this.position); 216 TRIO.ConsultationMap.map.setZoom(12); 217 } else { 218 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png'); 219 } 220 }); 221 }); 222 }, 223 224 updateButtons: function () { 225 var numElements = $('.consultation-hour-finder__location-list-item').not('.consultation-hour-finder__location-list-item--hidden').length; 226 227 if (this.index <= 0) { 228 this.scrollUpHandle.addClass('consultation-hour-finder__scroll---disabled'); 229 this.scrollDownHandle.removeClass('consultation-hour-finder__scroll---disabled'); 230 } 231 232 if (this.index >= numElements - 1) { 233 this.scrollUpHandle.removeClass('consultation-hour-finder__scroll---disabled'); 234 this.scrollDownHandle.addClass('consultation-hour-finder__scroll---disabled'); 235 } 236 237 if (this.index > 0 && this.index < numElements - 1) { 238 this.scrollUpHandle.removeClass('consultation-hour-finder__scroll---disabled'); 239 this.scrollDownHandle.removeClass('consultation-hour-finder__scroll---disabled'); 240 } 241 242 if (this.index <= 0 && this.index >= numElements - 1) { 243 this.scrollUpHandle.addClass('consultation-hour-finder__scroll---disabled'); 244 this.scrollDownHandle.addClass('consultation-hour-finder__scroll---disabled'); 245 } 246 }, 247 248 update: function () { 249 this.updateButtons(); 250 }, 251 252 slideTo: function (id) { 253 var that = this; 254 var ix = 0; 255 var slideTo = 0; 256 257 $('.consultation-hour-finder__location-list-item').not('.consultation-hour-finder__location-list-item--hidden').each(function (i) { 258 if (id === $(this).attr('id')) { 259 slideTo = -ix * that.slideCardHeight; 260 that.index = ix; 261 } 262 ix++; 263 }); 264 265 this.scroller.stop(true, true).animate({top: slideTo}, function () { 266 that.updateButtons(); 267 }); 268 269 }, 270 271 setMapMarkers: function () { 272 var that = this; 273 274 this.scroller.find('.consultation-hour-finder__location-list-item').each(function (n) { 275 /*var location = jQuery(this); 276 var coords = location.data('coords').split(','); 277 var isActive = (n == 0) ? ' consultation-hour-finder__marker--active' : ''; 278 var labelActive = (n == 0) ? ' style="display: block;"' : ' style="display: none;"'; 279 var marker = jQuery('<span class="consultation-hour-finder__marker" style="left: ' + coords[0] + '%; top: ' + coords[1] + '%;"><span class="laboratory-finder__pointer' + isActive + '"></span><span class="laboratory-finder__label ' + ((isActive) ? 'laboratory-finder__label--opened' : '') + '"' + labelActive + '>' + location.data('title') + '</span></span>'); 280 281 marker.on('click', function() { 282 that.scrollToLocation(n); 283 }); 284 285 marker.on('mouseenter', function() { 286 if (marker.find('.consultation-hour-finder__label--opened').length === 0) { 287 $('.consultation-hour-finder__label--opened').removeClass('consultation-hour-finder__label--opened').fadeOut(100); 288 that.showLabel(marker); 289 } 290 }); 291 292 that.map.append(marker);*/ 293 }); 294 }, 295 296 setFirstSlideAsActive: function (setInactive) { 297 var elementId; 298 299 var firstIteration = false; 300 $('.consultation-hour-finder__location-list-item').not('.consultation-hour-finder__location-list-item--hidden').each(function (i) { 301 302 if (!firstIteration) { 303 elementId = $(this).attr('id'); 304 firstIteration = true; 305 } 306 }); 307 308 if (setInactive) { 309 $(TRIO.ConsultationMap.markers).each(function () { 310 this.setIcon('/typo3conf/ext/address_map/Resources/Public/
310Icons/marker_gray.png'); 311 }); 312 return; 313 } 314 315 $(TRIO.ConsultationMap.markers).each(function () { 316 if (this.id === elementId) { 317 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker.png'); 318 } else { 319 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png'); 320 } 321 }); 322 }, 323}; 324 325var MY_MAPTYPE_ID = 'map_style'; 326 327TRIO.ConsultationMap = { 328 element: null, 329 addresses: null, 330 zoomDisabled: null, 331 center: null, 332 activemarker: null, 333 address: null, 334 settings: null, 335 styles: null, 336 map: null, 337 layer: null, 338 geocoder: null, 339 last: null, 340 url: null, 341 markers: [], 342 343 filter: function (category) { 344 var that = this; 345 var latlngbounds = new google.maps.LatLngBounds(); 346 var activeMarkerCount = 0; 347 var activeMarker = null; 348 349 // Watch for boundaries and force min zoom level 350 google.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { 351 this.setZoom(Math.min(12, this.getZoom())); 352 }); 353 354 if (category === undefined) { 355 this.addresses.each(function () { 356 var $markers = $(that.markers); 357 $markers.each(function () { 358 latlngbounds.extend(this.position); 359 activeMarkerCount++; 360 activeMarker = null; 361 this.setVisible(true); 362 }); 363 364 activeMarker = $markers.first(); 365 $(this).removeClass('consultation-hour-finder__location-list-item--hidden'); 366 }); 367 } else { 368 this.addresses.each(function () { 369 var id = $(this).attr('id'); 370 var categories = $(this).data('category'); 371 var cats = []; 372 if(typeof categories == 'string' && categories.includes(",")) { 373 cats = categories.split(","); 374 } 375 376 if (category == $(this).data('category') || cats.includes("" + category +"")) { 377 $(this).removeClass('consultation-hour-finder__location-list-item--hidden'); 378 $(that.markers).each(function () { 379 if (this.id == id) { 380 latlngbounds.extend(this.position); 381 activeMarker = this; 382 activeMarkerCount++; 383 this.setVisible(true); 384 } 385 }); 386 } else { 387 $(this).addClass('consultation-hour-finder__location-list-item--hidden'); 388 $(that.markers).each(function () { 389 if (this.id == id) { 390 this.setVisible(false); 391 } 392 }); 393 } 394 }); 395 } 396 397 if (activeMarkerCount > 1) { 398 this.map.fitBounds(latlngbounds); 399 } else if (activeMarkerCount == 1) { 400 this.map.setZoom(12); 401 this.map.panTo(activeMarker.position); 402 } 403 TRIO.ConsultationFinder.index = 0; 404 TRIO.ConsultationFinder.update(); 405 $('.consultation-hour-finder__scroller').css('top', '0px'); 406 407 if (category === undefined) { 408 TRIO.ConsultationFinder.setFirstSlideAsActive(true); 409 } else { 410 TRIO.ConsultationFinder.setFirstSlideAsActive(); 411 } 412 413 if (!Limbach.isMedium()) { 414 $('html, body').animate({scrollTop: $('.consultation-hour-finder__location-slider').offset().top}); 415 } 416 417 }, 418 419 create: function (elem) { 420 var consultationMap = this; 421 this.element = jQuery(elem); 422 this.zoomLevel = this.element.data('zoomlevel'); 423 this.addresses = this.element.find('.consultation-hour-finder__location-list-item'); 424 this.zoomDisabled = (this.element.data('zoom') == 0) ? true : false; 425 this.center = [50.51168, 8.920441]; 426 this.activemarker = []; 427 428 var filterButtons = $('.consultation-hour-finder__filter-button'); 429 var activeButtonClass = 'consultation-hour-finder__filter-button--active'; 430 431 /** 432 * Show all labs on selected filter button 433 */ 434 filterButtons.on('click', function () { 435 filterButtons.removeClass(activeButtonClass); 436 $(this).addClass(activeButtonClass); 437 consultationMap.filter($(this).data('uid')); 438 }); 439 440 if (this.element.length > 0) { 441 this.settings = { 442 zoom: TRIO.ConsultationMap.zoomLevel, 443 scrollwheel: false, 444 center: new google.maps.LatLng(TRIO.ConsultationMap.center[0], TRIO.ConsultationMap.center[1]), 445 mapTypeId: google.maps.MapTypeId.ROADMAP, 446 mapTypeControlOptions: { 447 mapTypeIds: [google.maps.MapTypeId.ROADMAP, MY_MAPTYPE_ID] 448 }, 449 streetViewControl: false 450 }; 451 452 this.styles = [ 453 { 454 stylers: [ 455 {hue: "#dddddd"}, 456 {saturation: -100} 457 ] 458 }, { 459 "elementType": "labels.text.fill", 460 "stylers": [ 461 {"color": "#991355"}, 462 {"visibility": "on"} 463 ] 464 }, { 465 featureType: "administrative.country", 466 elementType: "labels", 467 stylers: [ 468 {visibility: "off"} 469 ] 470 }, { 471 featureType: "road.highway", 472 stylers: [ 473 {lightness: 50} 474 ] 475 }, { 476 featureType: "poi", 477 stylers: [ 478 {visibility: "off"} 479 ] 480 } 481 ]; 482 483 this.init(); 484 } 485 }, 486 487 init: function () { 488 this.map = new google.maps.Map(document.getElementById('locationMap'), this.settings); 489 this.setMarkerByGeocoder(); 490 }, 491 492 update: function () { 493 this.geocoder = null; 494 495 for (var i = 0; i < this.activemarker.length; i++) { 496 this.activemarker[i].setMap(null); 497 } 498 499 this.activemarker = []; 500 this.addresses = null; 501 this.addresses = this.element.find('.consultation-hour-finder__location-list-item'); 502 this.setMarkerByGeocoder(); 503 }, 504 505 setMarkerByGeocoder: function () { 506 var that = this; 507 var marker; 508 var latlng_pos = []; 509 510 this.geocoder = new google.maps.Geocoder(); 511 if (this.addresses.length > 0) { 512 this.addresses.each(function (i) { 513 var address = jQuery(this); 514 515 if (address.data('geodata').length > 0) { 516 var geodata = address.data('geodata').split(','); 517 518 that.markers.push(marker = new google.maps.Marker({ 519 position: new google.maps.LatLng(geodata[0], geodata[1]), 520 map: TRIO.ConsultationMap.map, 521 title: address.data('title'), 522 id: address.attr('id'), 523 icon: '/typo3conf/ext/address_map/Resources/Public/
523Icons/marker_gray.png' 524 })); 525 } else { 526 /*that.geocoder.geocode({'address': address.data('address')}, function(results, status) { 527 if (status == google.maps.GeocoderStatus.OK) { 528 var location = results[0].geometry.location; 529 marker = new google.maps.Marker({ 530 position: location, 531 map: that.map, 532 title: address.data('title'), 533 link: address.data('link'), 534 icon: '/typo3conf/ext/address_map/Resources/Public/Icons/marker.png' 535 }); 536 } else { 537 alert('Geocode was not successful for the following reason: ' + status); 538 } 539 });*/ 540 } 541 542 that.activemarker[i] = marker; 543 latlng_pos[i] = marker.position; 544 545 google.maps.event.addListener(marker, 'click', function () { 546 var id = $(this).attr('id'); 547 $(that.markers).each(function () { 548 if (this.id == id) { 549 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker.png'); 550 TRIO.ConsultationFinder.slideTo(id); 551 that.map.setCenter(this.position); 552 that.map.setZoom(12); 553 554 //var center = new google.maps.LatLng(lat, lng); 555 //that.map.panTo(center); 556 } else { 557 this.setIcon('/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png'); 558 } 559 }); 560 //TODO: scroll to element with id this.id 561 }); 562 }); 563 564 if (this.addresses.length == 1) { 565 this.map.setZoom(12); 566 this.map.panTo(marker.position); 567 } 568 569 if (this.addresses.length > 1) { 570 var latlngbounds = new google.maps.LatLngBounds(); 571 572 for (var j = 0; j < latlng_pos.length; j++) { 573 latlngbounds.extend(latlng_pos[j]); 574 } 575 576 this.map.fitBounds(latlngbounds); 577 } 578 } 579 580 this.map.setOptions({ 581 styles: this.styles 582 }); 583 584 var mcStyles = [ 585 { 586 textColor: 'white', 587 url: '/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png', 588 height: 47, 589 width: 34, 590 anchorText: [-8, 0], 591 anchorIcon: [47, 34/2], 592 textSize: 15 593 }, 594 { 595 textColor: 'white', 596 url: '/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png', 597 height: 47, 598 width: 34, 599 anchorText: [-8, 0], 600 anchorIcon: [47, 34/2], 601 textSize: 15 602 }, 603 { 604 textColor: 'white', 605 url: '/typo3conf/ext/address_map/Resources/Public/Icons/marker_gray.png', 606 height: 47, 607 width: 34, 608 anchorText: [-8, 0], 609 anchorIcon: [47, 34/2], 610 textSize: 15 611 } 612 ]; 613 614 var mcOptions = { 615 gridSize: 50, 616 styles: mcStyles, 617 averageCenter: true, 618 minimumClusterSize: 2, 619 ignoreHidden: true 620 }; 621 this.markerClusterer = new MarkerClusterer(this.map, this.markers, mcOptions); 622 } 623}; 624 625jQuery(document).ready(function () { 626 TRIO.ConsultationFinder.create('#consultation-hour_finder'); 627 TRIO.ConsultationMap.create('#consultation-hour_finder'); 628}); 629 630/** 631 * @name MarkerClustererPlus for Google Maps V3 632 * @version 2.1.1 [November 4, 2013] 633 * @author Gary Little 634 * @fileoverview 635 * The library creates and manages per-zoom-level clusters for large amounts of markers. 636 * <p> 637 * This is an enhanced V3 implementation of the 638 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 639 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the 640 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/" 641 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little. 642 * <p>
643 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It 644 * adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>, 645 * and <code>calculator</code> properties as well as support for four more events. It also allows 646 * greater control over the styling of the text that appears on the cluster marker. The 647 * documentation has been significantly improved and the overall code has been simplified and 648 * polished. Very large numbers of markers can now be managed without causing Javascript timeout 649 * errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been 650 * deprecated. The new name is <code>click</code>, so please change your application code now. 651 */ 652 653/** 654 * Licensed under the Apache License, Version 2.0 (the "License"); 655 * you may not use this file except in compliance with the License. 656 * You may obtain a copy of the License at 657 * 658 * http://www.apache.org/licenses/LICENSE-2.0 659 * 660 * Unless required by applicable law or agreed to in writing, software 661 * distributed under the License is distributed on an "AS IS" BASIS, 662 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 663 * See the License for the specific language governing permissions and 664 * limitations under the License. 665 */ 666 667 668/** 669 * @name ClusterIconStyle 670 * @class This class represents the object for values in the <code>styles</code> array passed 671 * to the {@link MarkerClusterer} constructor. The element in this array that is used to 672 * style the cluster icon is determined by calling the <code>calculator</code> function. 673 * 674 * @property {string} url The URL of the cluster icon image file. Required. 675 * @property {number} height The display height (in pixels) of the cluster icon. Required. 676 * @property {number} width The display width (in pixels) of the cluster icon. Required. 677 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to 678 * where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code> 679 * where <code>yoffset</code> increases as you go down from center and <code>xoffset</code> 680 * increases to the right of center. The default is <code>[0, 0]</code>. 681 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the 682 * spot on the cluster icon that is to be aligned with the cluster position. The format is 683 * <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and 684 * <code>xoffset</code> increases to the right of the top-left corner of the icon. The default 685 * anchor position is the center of the cluster icon. 686 * @property {string} [textColor="black"] The color of the label text shown on the 687 * cluster icon. 688 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the 689 * cluster icon. 690 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code> 691 * property for the label text shown on the cluster icon. 692 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code> 693 * property for the label text shown on the cluster icon. 694 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code> 695 * property for the label text shown on the cluster icon. 696 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code> 697 * property for the label text shown on the cluster icon. 698 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image 699 * within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code> 700 * (the same format as for the CSS <code>background-position</code> property). You must set 701 * this property appropriately when the image defined by <code>url</code> represents a sprite 702 * containing multiple images. Note that the position <i>must</i> be specified in px units. 703 */ 704/** 705 * @name ClusterIconInfo
706 * @class This class is an object containing general information about a cluster icon. This is 707 * the object that a <code>calculator</code> function returns. 708 * 709 * @property {string} text The text of the label to be shown on the cluster icon. 710 * @property {number} index The index plus 1 of the element in the <code>styles</code> 711 * array to be used to style the cluster icon. 712 * @property {string} title The tooltip to display when the mouse moves over the cluster icon. 713 * If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the 714 * value of the <code>title</code> property passed to the MarkerClusterer. 715 */ 716/** 717 * A cluster icon. 718 * 719 * @constructor 720 * @extends google.maps.OverlayView 721 * @param {Cluster} cluster The cluster with which the icon is to be associated. 722 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons 723 * to use for various cluster sizes. 724 * @private 725 */ 726function ClusterIcon(cluster, styles) { 727 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 728 729 this.cluster_ = cluster; 730 this.className_ = cluster.getMarkerClusterer().getClusterClass(); 731 this.styles_ = styles; 732 this.center_ = null; 733 this.div_ = null; 734 this.sums_ = null; 735 this.visible_ = false; 736 737 this.setMap(cluster.getMap()); // Note: this causes onAdd to be called 738} 739 740 741/** 742 * Adds the icon to the DOM. 743 */ 744ClusterIcon.prototype.onAdd = function () { 745 var cClusterIcon = this; 746 var cMouseDownInCluster; 747 var cDraggingMapByCluster; 748 749 this.div_ = document.createElement("div"); 750 this.div_.className = this.className_; 751 if (this.visible_) { 752 this.show(); 753 } 754 755 this.getPanes().overlayMouseTarget.appendChild(this.div_); 756 757 // Fix for Issue 157 758 this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () { 759 cDraggingMapByCluster = cMouseDownInCluster; 760 }); 761 762 google.maps.event.addDomListener(this.div_, "mousedown", function () { 763 cMouseDownInCluster = true; 764 cDraggingMapByCluster = false; 765 }); 766 767 google.maps.event.addDomListener(this.div_, "click", function (e) { 768 cMouseDownInCluster = false; 769 if (!cDraggingMapByCluster) { 770 var theBounds; 771 var mz; 772 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 773 /** 774 * This event is fired when a cluster marker is clicked. 775 * @name MarkerClusterer#click 776 * @param {Cluster} c The cluster that was clicked. 777 * @event 778 */ 779 google.maps.event.trigger(mc, "click", cClusterIcon.cluster_); 780 google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name 781 782 // The default click handler follows. Disable it by setting 783 // the zoomOnClick property to false. 784 if (mc.getZoomOnClick()) { 785 // Zoom into the cluster. 786 mz = mc.getMaxZoom(); 787 theBounds = cClusterIcon.cluster_.getBounds(); 788 mc.getMap().fitBounds(theBounds); 789 // There is a fix for Issue 170 here: 790 setTimeout(function () { 791 mc.getMap().fitBounds(theBounds); 792 // Don't zoom beyond the max zoom level 793 if (mz !== null && (mc.getMap().getZoom() > mz)) { 794 mc.getMap().setZoom(mz + 1); 795 } 796 }, 100); 797 } 798 799 // Prevent event propagation to the map: 800 e.cancelBubble = true; 801 if (e.stopPropagation) { 802 e.stopPropagation(); 803 } 804 } 805 }); 806 807 google.maps.event.addDomListener(this.div_, "mouseover", function () { 808 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 809 /** 810 * This event is fired when the mouse moves over a cluster marker. 811 * @name MarkerClusterer#mouseover 812 * @param {Cluster} c The cluster that the mouse moved over. 813 * @event 814 */ 815 google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_); 816 }); 817 818 google.maps.event.addDomListener(this.div_, "mouseout", function () { 819 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 820 /** 821 * This event is fired when the mouse moves out of a cluster marker. 822 * @name MarkerClusterer#mouseout 823 * @param {Cluster} c The cluster that the mouse moved out of. 824 * @event 825 */ 826 google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_); 827 }); 828}; 829 830 831/** 832 * Removes the icon from the DOM. 833 */ 834ClusterIcon.prototype.onRemove = function () { 835 if (this.div_ && this.div_.parentNode) { 836 this.hide(); 837 google.maps.event.removeListener(this.boundsChangedListener_); 838 google.maps.event.clearInstanceListeners(this.div_); 839 this.div_.parentNode.removeChild(this.div_); 840 this.div_ = null; 841 } 842}; 843 844 845/** 846 * Draws the icon. 847 */ 848ClusterIcon.prototype.draw = function () { 849 if (this.visible_) { 850 var pos = this.getPosFromLatLng_(this.center_); 851 this.div_.style.top = pos.y + "px"; 852 this.div_.style.left = pos.x + "px"; 853 } 854}; 855 856 857/** 858 * Hides the icon. 859 */ 860ClusterIcon.prototype.hide = function () { 861 if (this.div_) { 862 this.div_.style.display = "none"; 863 } 864 this.visible_ = false;
vendor: 7,053 bytes, lines 865-1101
865}; 866 867 868/** 869 * Positions and shows the icon. 870 */ 871ClusterIcon.prototype.show = function () { 872 if (this.div_) { 873 var img = ""; 874 // NOTE: values must be specified in px units 875 var bp = this.backgroundPosition_.split(" "); 876 var spriteH = parseInt(bp[0].trim(), 10); 877 var spriteV = parseInt(bp[1].trim(), 10); 878 var pos = this.getPosFromLatLng_(this.center_); 879 this.div_.style.cssText = this.createCss(pos); 880 img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; "; 881 if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) { 882 img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " + 883 ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);"; 884 } 885 else { 886 img += "width: " + this.width_ + "px;" + "height: " + this.height_ + "px;"; 887 } 888 img += "'>"; 889 this.div_.innerHTML = img + "<div style='" + 890 "position: absolute;" + 891 "top: " + this.anchorText_[0] + "px;" + 892 "left: " + this.anchorText_[1] + "px;" + 893 "color: " + this.textColor_ + ";" + 894 "font-size: " + this.textSize_ + "px;" + 895 "font-family: " + this.fontFamily_ + ";" + 896 "font-weight: " + this.fontWeight_ + ";" + 897 "font-style: " + this.fontStyle_ + ";" + 898 "text-decoration: " + this.textDecoration_ + ";" + 899 "text-align: center;" + 900 "width: " + this.width_ + "px;" + 901 "line-height:" + this.height_ + "px;" + 902 "'>" + (this.cluster_.hideLabel_ ? ' ' : this.sums_.text) + "</div>"; 903 if (typeof this.sums_.title === "undefined" || this.sums_.title === "") { 904 this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); 905 } else { 906 this.div_.title = this.sums_.title; 907 } 908 this.div_.style.display = ""; 909 } 910 this.visible_ = true; 911}; 912 913 914/** 915 * Sets the icon styles to the appropriate element in the styles array. 916 * 917 * @param {ClusterIconInfo} sums The icon label text and styles index. 918 */ 919ClusterIcon.prototype.useStyle = function (sums) { 920 this.sums_ = sums; 921 var index = Math.max(0, sums.index - 1); 922 index = Math.min(this.styles_.length - 1, index); 923 var style = this.styles_[index]; 924 this.url_ = style.url; 925 this.height_ = style.height; 926 this.width_ = style.width; 927 this.anchorText_ = style.anchorText || [0, 0]; 928 this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)]; 929 this.textColor_ = style.textColor || "black"; 930 this.textSize_ = style.textSize || 11; 931 this.textDecoration_ = style.textDecoration || "none"; 932 this.fontWeight_ = style.fontWeight || "bold"; 933 this.fontStyle_ = style.fontStyle || "normal"; 934 this.fontFamily_ = style.fontFamily || "Arial,sans-serif"; 935 this.backgroundPosition_ = style.backgroundPosition || "0 0"; 936}; 937 938 939/** 940 * Sets the position at which to center the icon. 941 * 942 * @param {google.maps.LatLng} center The latlng to set as the center. 943 */ 944ClusterIcon.prototype.setCenter = function (center) { 945 this.center_ = center; 946}; 947 948 949/** 950 * Creates the cssText style parameter based on the position of the icon. 951 * 952 * @param {google.maps.Point} pos The position of the icon. 953 * @return {string} The CSS style text. 954 */ 955ClusterIcon.prototype.createCss = function (pos) { 956 var style = []; 957 style.push("cursor: pointer;"); 958 style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;"); 959 style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;"); 960 return style.join(""); 961}; 962 963 964/** 965 * Returns the position at which to place the DIV depending on the latlng. 966 * 967 * @param {google.maps.LatLng} latlng The position in latlng. 968 * @return {google.maps.Point} The position in pixels. 969 */ 970ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) { 971 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 972 pos.x -= this.anchorIcon_[1]; 973 pos.y -= this.anchorIcon_[0]; 974 pos.x = parseInt(pos.x, 10); 975 pos.y = parseInt(pos.y, 10); 976 return pos; 977}; 978 979 980/** 981 * Creates a single cluster that manages a group of proximate markers. 982 * Used internally, do not call this constructor directly. 983 * @constructor 984 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this 985 * cluster is associated. 986 */ 987function Cluster(mc) { 988 this.markerClusterer_ = mc; 989 this.map_ = mc.getMap(); 990 this.gridSize_ = mc.getGridSize(); 991 this.minClusterSize_ = mc.getMinimumClusterSize(); 992 this.averageCenter_ = mc.getAverageCenter(); 993 this.hideLabel_ = mc.getHideLabel(); 994 this.markers_ = []; 995 this.center_ = null; 996 this.bounds_ = null; 997 this.clusterIcon_ = new ClusterIcon(this, mc.getStyles()); 998} 999 1000 1001/** 1002 * Returns the number of markers managed by the cluster. You can call this from 1003 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 1004 * for the <code>MarkerClusterer</code> object. 1005 * 1006 * @return {number} The number of markers in the cluster. 1007 */ 1008Cluster.prototype.getSize = function () { 1009 return this.markers_.length; 1010}; 1011 1012 1013/** 1014 * Returns the array of markers managed by the cluster. You can call this from 1015 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 1016 * for the <code>MarkerClusterer</code> object. 1017 * 1018 * @return {Array} The array of markers in the cluster. 1019 */ 1020Cluster.prototype.getMarkers = function () { 1021 return this.markers_; 1022}; 1023 1024 1025/** 1026 * Returns the center of the cluster. You can call this from 1027 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 1028 * for the <code>MarkerClusterer</code> object. 1029 * 1030 * @return {google.maps.LatLng} The center of the cluster. 1031 */ 1032Cluster.prototype.getCenter = function () { 1033 return this.center_; 1034}; 1035 1036 1037/** 1038 * Returns the map with which the cluster is associated. 1039 * 1040 * @return {google.maps.Map} The map. 1041 * @ignore 1042 */ 1043Cluster.prototype.getMap = function () { 1044 return this.map_; 1045}; 1046 1047 1048/** 1049 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated. 1050 * 1051 * @return {MarkerClusterer} The associated marker clusterer. 1052 * @ignore 1053 */ 1054Cluster.prototype.getMarkerClusterer = function () { 1055 return this.markerClusterer_; 1056}; 1057 1058 1059/** 1060 * Returns the bounds of the cluster. 1061 * 1062 * @return {google.maps.LatLngBounds} the cluster bounds. 1063 * @ignore 1064 */ 1065Cluster.prototype.getBounds = function () { 1066 var i; 1067 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 1068 var markers = this.getMarkers(); 1069 for (i = 0; i < markers.length; i++) { 1070 bounds.extend(markers[i].getPosition()); 1071 } 1072 return bounds; 1073}; 1074 1075 1076/** 1077 * Removes the cluster from the map. 1078 * 1079 * @ignore 1080 */ 1081Cluster.prototype.remove = function () { 1082 this.clusterIcon_.setMap(null); 1083 this.markers_ = []; 1084 delete this.markers_; 1085}; 1086 1087 1088/** 1089 * Adds a marker to the cluster. 1090 * 1091 * @param {google.maps.Marker} marker The marker to be added. 1092 * @return {boolean} True if the marker was added. 1093 * @ignore 1094 */ 1095Cluster.prototype.addMarker = function (marker) { 1096 var i; 1097 var mCount; 1098 var mz; 1099 1100 if (this.isMarkerAlreadyAdded_(marker)) { 1101 return false;
1102 } 1103 1104 if (!this.center_) { 1105 this.center_ = marker.getPosition(); 1106 this.calculateBounds_(); 1107 } else { 1108 if (this.averageCenter_) { 1109 var l = this.markers_.length + 1; 1110 var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; 1111 var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; 1112 this.center_ = new google.maps.LatLng(lat, lng); 1113 this.calculateBounds_(); 1114 } 1115 } 1116 1117 marker.isAdded = true; 1118 this.markers_.push(marker); 1119 1120 mCount = this.markers_.length; 1121 mz = this.markerClusterer_.getMaxZoom(); 1122 if (mz !== null && this.map_.getZoom() > mz) { 1123 // Zoomed in past max zoom, so show the marker. 1124 if (marker.getMap() !== this.map_) { 1125 marker.setMap(this.map_); 1126 } 1127 } else if (mCount < this.minClusterSize_) { 1128 // Min cluster size not reached so show the marker. 1129 if (marker.getMap() !== this.map_) { 1130 marker.setMap(this.map_); 1131 } 1132 } else if (mCount === this.minClusterSize_) { 1133 // Hide the markers that were showing. 1134 for (i = 0; i < mCount; i++) { 1135 this.markers_[i].setMap(null); 1136 } 1137 } else { 1138 marker.setMap(null); 1139 } 1140 1141 return true; 1142}; 1143 1144 1145/** 1146 * Determines if a marker lies within the cluster's bounds. 1147 * 1148 * @param {google.maps.Marker} marker The marker to check. 1149 * @return {boolean} True if the marker lies in the bounds. 1150 * @ignore 1151 */ 1152Cluster.prototype.isMarkerInClusterBounds = function (marker) { 1153 return this.bounds_.contains(marker.getPosition()); 1154}; 1155 1156 1157/**
1158 * Calculates the extended bounds of the cluster with the grid. 1159 */ 1160Cluster.prototype.calculateBounds_ = function () { 1161 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 1162 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 1163}; 1164 1165 1166/** 1167 * Updates the cluster icon. 1168 */ 1169Cluster.prototype.updateIcon_ = function () { 1170 var mCount = this.markers_.length; 1171 var mz = this.markerClusterer_.getMaxZoom(); 1172 1173 if (mz !== null && this.map_.getZoom() > mz) { 1174 this.clusterIcon_.hide(); 1175 return; 1176 } 1177 1178 if (mCount < this.minClusterSize_) { 1179 // Min cluster size not yet reached. 1180 this.clusterIcon_.hide(); 1181 return; 1182 } 1183 1184 var numStyles = this.markerClusterer_.getStyles().length; 1185 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 1186 this.clusterIcon_.setCenter(this.center_); 1187 this.clusterIcon_.useStyle(sums); 1188 this.clusterIcon_.show(); 1189}; 1190 1191 1192/** 1193 * Determines if a marker has already been added to the cluster. 1194 * 1195 * @param {google.maps.Marker} marker The marker to check. 1196 * @return {boolean} True if the marker has already been added. 1197 */ 1198Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) { 1199 for (var i = 0, n = this.markers_.length; i < n; i++) { 1200 if (marker === this.markers_[i]) { 1201 return true; 1202 } 1203 } 1204 return false; 1205}; 1206 1207 1208/** 1209 * @name MarkerClustererOptions 1210 * @class This class represents the optional parameter passed to 1211 * the {@link MarkerClusterer} constructor. 1212 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square. 1213 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or 1214 * <code>null</code> if clustering is to be enabled at all zoom levels. 1215 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is 1216 * clicked. You may want to set this to <code>false</code> if you have installed a handler 1217 * for the <code>click</code> event and it deals with zooming on its own. 1218 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be 1219 * the average position of all markers in the cluster. If set to <code>false</code>, the 1220 * cluster marker is positioned at the location of the first marker added to the cluster. 1221 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster 1222 * before the markers are hidden and a cluster marker appears. 1223 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You 1224 * may want to set this to <code>true</code> to ensure that hidden markers are not included 1225 * in the marker count that appears on a cluster marker (this count is the value of the 1226 * <code>text</code> property of the result returned by the default <code>calculator</code>). 1227 * If set to <code>true</code> and you change the visibility of a marker being clustered, be 1228 * sure to also call <code>MarkerClusterer.repaint()</code>. 1229 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster 1230 * marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a 1231 * different tooltip for each cluster marker.) 1232 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine 1233 * the text to be displayed on a cluster marker and the index indicating which style to use 1234 * for the cluster marker. The input parameters for the function are (1) the array of markers 1235 * represented by a cluster marker and (2) the number of cluster icon styles. It returns a 1236 * {@link ClusterIconInfo} object. The default <code>calculator</code> returns a 1237 * <code>text</code> property which is the number of markers in the cluster and an 1238 * <code>index</code> property which is one higher than the lowest integer such that 1239 * <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles 1240 * array, whichever is less. The <code>styles</code> array element used has an index of 1241 * <code>index</code> minus 1. For example, the default <code>calculator</code> returns a 1242 * <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code> 1243 * for a cluster icon representing 125 markers so the element used in the <code>styles</code> 1244 * array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code> 1245 * property that contains the text of the tooltip to be used for the cluster marker. If 1246 * <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code> 1247 * property for the MarkerClusterer. 1248 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles 1249 * for the cluster markers. Use this class to define CSS styles that are not set up by the code 1250 * that processes the <code>styles</code> array.
1251 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles 1252 * of the cluster markers to be used. The element to be used to style a given cluster marker 1253 * is determined by the function defined by the <code>calculator</code> property. 1254 * The default is an array of {@link ClusterIconStyle} elements whose properties are derived 1255 * from the values for <code>imagePath</code>, <code>imageExtension</code>, and 1256 * <code>imageSizes</code>. 1257 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that 1258 * have sizes that are some multiple (typically double) of their actual display size. Icons such 1259 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays. 1260 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons. 1261 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the 1262 * number of markers to be processed in a single batch when using a browser other than 1263 * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead). 1264 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is 1265 * being used, markers are processed in several batches with a small delay inserted between 1266 * each batch in an attempt to avoid Javascript timeout errors. Set this property to the 1267 * number of markers to be processed in a single batch; select as high a number as you can 1268 * without causing a timeout error in the browser. This number might need to be as low as 100 1269 * if 15,000 markers are being managed, for example. 1270 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH] 1271 * The full URL of the root name of the group of image files to use for cluster icons. 1272 * The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code> 1273 * where n is the image file number (1, 2, etc.). 1274 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION] 1275 * The extension name for the cluster icon image files (e.g., <code>"png"</code> or 1276 * <code>"jpg"</code>). 1277 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES] 1278 * An array of numbers containing the widths of the group of 1279 * <code>imagePath</code>n.<code>imageExtension</code> image files. 1280 * (The images are assumed to be square.) 1281 */ 1282/** 1283 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}. 1284 * @constructor 1285 * @extends google.maps.OverlayView 1286 * @param {google.maps.Map} map The Google map to attach to. 1287 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster. 1288 * @param {MarkerClustererOptions} [opt_options] The optional parameters. 1289 */ 1290function MarkerClusterer(map, opt_markers, opt_options) { 1291 // MarkerClusterer implements google.maps.OverlayView interface. We use the 1292 // extend function to extend MarkerClusterer with google.maps.OverlayView 1293 // because it might not always be available when the code is defined so we 1294 // look for it at the last possible moment. If it doesn't exist now then 1295 // there is no point going ahead :) 1296 this.extend(MarkerClusterer, google.maps.OverlayView); 1297 1298 opt_markers = opt_markers || []; 1299 opt_options = opt_options || {}; 1300 1301 this.markers_ = []; 1302 this.clusters_ = []; 1303 this.listeners_ = []; 1304 this.activeMap_ = null; 1305 this.ready_ = false; 1306 1307 this.gridSize_ = opt_options.gridSize || 60; 1308 this.minClusterSize_ = opt_options.minimumClusterSize || 2; 1309 this.maxZoom_ = opt_options.maxZoom || null; 1310 this.styles_ = opt_options.styles || []; 1311 this.title_ = opt_options.title || ""; 1312 this.zoomOnClick_ = true; 1313 if (opt_options.zoomOnClick !== undefined) { 1314 this.zoomOnClick_ = opt_options.zoomOnClick; 1315 } 1316 this.averageCenter_ = false; 1317 if (opt_options.averageCenter !== undefined) { 1318 this.averageCenter_ = opt_options.averageCenter; 1319 } 1320 this.ignoreHidden_ = false; 1321 if (opt_options.ignoreHidden !== undefined) { 1322 this.ignoreHidden_ = opt_options.ignoreHidden; 1323 } 1324 this.enableRetinaIcons_ = false; 1325 if (opt_options.enableRetinaIcons !== undefined) { 1326 this.enableRetinaIcons_ = opt_options.enableRetinaIcons; 1327 } 1328 this.hideLabel_ = false;
vendor: 11,610 bytes, lines 1329-1796
1329 if (opt_options.hideLabel !== undefined) { 1330 this.hideLabel_ = opt_options.hideLabel; 1331 } 1332 this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH; 1333 this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION; 1334 this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES; 1335 this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR; 1336 this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE; 1337 this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE; 1338 this.clusterClass_ = opt_options.clusterClass || "cluster"; 1339 1340 if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) { 1341 // Try to avoid IE timeout when processing a huge number of markers: 1342 this.batchSize_ = this.batchSizeIE_; 1343 } 1344 1345 this.setupStyles_(); 1346 1347 this.addMarkers(opt_markers, true); 1348 this.setMap(map); // Note: this causes onAdd to be called 1349} 1350 1351 1352/** 1353 * Implementation of the onAdd interface method. 1354 * @ignore 1355 */ 1356MarkerClusterer.prototype.onAdd = function () { 1357 var cMarkerClusterer = this; 1358 1359 this.activeMap_ = this.getMap(); 1360 this.ready_ = true; 1361 1362 this.repaint(); 1363 1364 // Add the map event listeners 1365 this.listeners_ = [ 1366 google.maps.event.addListener(this.getMap(), "zoom_changed", function () { 1367 cMarkerClusterer.resetViewport_(false); 1368 // Workaround for this Google bug: when map is at level 0 and "-" of 1369 // zoom slider is clicked, a "zoom_changed" event is fired even though 1370 // the map doesn't zoom out any further. In this situation, no "idle" 1371 // event is triggered so the cluster markers that have been removed 1372 // do not get redrawn. Same goes for a zoom in at maxZoom. 1373 if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) { 1374 google.maps.event.trigger(this, "idle"); 1375 } 1376 }), 1377 google.maps.event.addListener(this.getMap(), "idle", function () { 1378 cMarkerClusterer.redraw_(); 1379 }) 1380 ]; 1381}; 1382 1383 1384/** 1385 * Implementation of the onRemove interface method. 1386 * Removes map event listeners and all cluster icons from the DOM. 1387 * All managed markers are also put back on the map. 1388 * @ignore 1389 */ 1390MarkerClusterer.prototype.onRemove = function () { 1391 var i; 1392 1393 // Put all the managed markers back on the map: 1394 for (i = 0; i < this.markers_.length; i++) { 1395 if (this.markers_[i].getMap() !== this.activeMap_) { 1396 this.markers_[i].setMap(this.activeMap_); 1397 } 1398 } 1399 1400 // Remove all clusters: 1401 for (i = 0; i < this.clusters_.length; i++) { 1402 this.clusters_[i].remove(); 1403 } 1404 this.clusters_ = []; 1405 1406 // Remove map event listeners: 1407 for (i = 0; i < this.listeners_.length; i++) { 1408 google.maps.event.removeListener(this.listeners_[i]); 1409 } 1410 this.listeners_ = []; 1411 1412 this.activeMap_ = null; 1413 this.ready_ = false; 1414}; 1415 1416 1417/** 1418 * Implementation of the draw interface method. 1419 * @ignore 1420 */ 1421MarkerClusterer.prototype.draw = function () {}; 1422 1423 1424/** 1425 * Sets up the styles object. 1426 */ 1427MarkerClusterer.prototype.setupStyles_ = function () { 1428 var i, size; 1429 if (this.styles_.length > 0) { 1430 return; 1431 } 1432 1433 for (i = 0; i < this.imageSizes_.length; i++) { 1434 size = this.imageSizes_[i]; 1435 this.styles_.push({ 1436 url: this.imagePath_ + (i + 1) + "." + this.imageExtension_, 1437 height: size, 1438 width: size 1439 }); 1440 } 1441}; 1442 1443 1444/** 1445 * Fits the map to the bounds of the markers managed by the clusterer. 1446 */ 1447MarkerClusterer.prototype.fitMapToMarkers = function () { 1448 var i; 1449 var markers = this.getMarkers(); 1450 var bounds = new google.maps.LatLngBounds(); 1451 for (i = 0; i < markers.length; i++) { 1452 bounds.extend(markers[i].getPosition()); 1453 } 1454 1455 this.getMap().fitBounds(bounds); 1456}; 1457 1458 1459/** 1460 * Returns the value of the <code>gridSize</code> property. 1461 * 1462 * @return {number} The grid size. 1463 */ 1464MarkerClusterer.prototype.getGridSize = function () { 1465 return this.gridSize_; 1466}; 1467 1468 1469/** 1470 * Sets the value of the <code>gridSize</code> property. 1471 * 1472 * @param {number} gridSize The grid size. 1473 */ 1474MarkerClusterer.prototype.setGridSize = function (gridSize) { 1475 this.gridSize_ = gridSize; 1476}; 1477 1478 1479/** 1480 * Returns the value of the <code>minimumClusterSize</code> property. 1481 * 1482 * @return {number} The minimum cluster size. 1483 */ 1484MarkerClusterer.prototype.getMinimumClusterSize = function () { 1485 return this.minClusterSize_; 1486}; 1487 1488/** 1489 * Sets the value of the <code>minimumClusterSize</code> property. 1490 * 1491 * @param {number} minimumClusterSize The minimum cluster size. 1492 */ 1493MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) { 1494 this.minClusterSize_ = minimumClusterSize; 1495}; 1496 1497 1498/** 1499 * Returns the value of the <code>maxZoom</code> property. 1500 * 1501 * @return {number} The maximum zoom level. 1502 */ 1503MarkerClusterer.prototype.getMaxZoom = function () { 1504 return this.maxZoom_; 1505}; 1506 1507 1508/** 1509 * Sets the value of the <code>maxZoom</code> property. 1510 * 1511 * @param {number} maxZoom The maximum zoom level. 1512 */ 1513MarkerClusterer.prototype.setMaxZoom = function (maxZoom) { 1514 this.maxZoom_ = maxZoom; 1515}; 1516 1517 1518/** 1519 * Returns the value of the <code>styles</code> property. 1520 * 1521 * @return {Array} The array of styles defining the cluster markers to be used. 1522 */ 1523MarkerClusterer.prototype.getStyles = function () { 1524 return this.styles_; 1525}; 1526 1527 1528/** 1529 * Sets the value of the <code>styles</code> property. 1530 * 1531 * @param {Array.<ClusterIconStyle>} styles The array of styles to use. 1532 */ 1533MarkerClusterer.prototype.setStyles = function (styles) { 1534 this.styles_ = styles; 1535}; 1536 1537 1538/** 1539 * Returns the value of the <code>title</code> property. 1540 * 1541 * @return {string} The content of the title text. 1542 */ 1543MarkerClusterer.prototype.getTitle = function () { 1544 return this.title_; 1545}; 1546 1547 1548/** 1549 * Sets the value of the <code>title</code> property. 1550 * 1551 * @param {string} title The value of the title property. 1552 */ 1553MarkerClusterer.prototype.setTitle = function (title) { 1554 this.title_ = title; 1555}; 1556 1557 1558/** 1559 * Returns the value of the <code>zoomOnClick</code> property. 1560 * 1561 * @return {boolean} True if zoomOnClick property is set. 1562 */ 1563MarkerClusterer.prototype.getZoomOnClick = function () { 1564 return this.zoomOnClick_; 1565}; 1566 1567 1568/** 1569 * Sets the value of the <code>zoomOnClick</code> property. 1570 * 1571 * @param {boolean} zoomOnClick The value of the zoomOnClick property. 1572 */ 1573MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) { 1574 this.zoomOnClick_ = zoomOnClick; 1575}; 1576 1577 1578/** 1579 * Returns the value of the <code>averageCenter</code> property. 1580 * 1581 * @return {boolean} True if averageCenter property is set. 1582 */ 1583MarkerClusterer.prototype.getAverageCenter = function () { 1584 return this.averageCenter_; 1585}; 1586 1587 1588/** 1589 * Sets the value of the <code>averageCenter</code> property. 1590 * 1591 * @param {boolean} averageCenter The value of the averageCenter property. 1592 */ 1593MarkerClusterer.prototype.setAverageCenter = function (averageCenter) { 1594 this.averageCenter_ = averageCenter; 1595}; 1596 1597 1598/** 1599 * Returns the value of the <code>ignoreHidden</code> property. 1600 * 1601 * @return {boolean} True if ignoreHidden property is set. 1602 */ 1603MarkerClusterer.prototype.getIgnoreHidden = function () { 1604 return this.ignoreHidden_; 1605}; 1606 1607 1608/** 1609 * Sets the value of the <code>ignoreHidden</code> property. 1610 * 1611 * @param {boolean} ignoreHidden The value of the ignoreHidden property. 1612 */ 1613MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) { 1614 this.ignoreHidden_ = ignoreHidden; 1615}; 1616 1617 1618/** 1619 * Returns the value of the <code>enableRetinaIcons</code> property. 1620 * 1621 * @return {boolean} True if enableRetinaIcons property is set. 1622 */ 1623MarkerClusterer.prototype.getEnableRetinaIcons = function () { 1624 return this.enableRetinaIcons_; 1625}; 1626 1627 1628/** 1629 * Sets the value of the <code>enableRetinaIcons</code> property. 1630 * 1631 * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property. 1632 */ 1633MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) { 1634 this.enableRetinaIcons_ = enableRetinaIcons; 1635}; 1636 1637 1638/** 1639 * Returns the value of the <code>imageExtension</code> property. 1640 * 1641 * @return {string} The value of the imageExtension property. 1642 */ 1643MarkerClusterer.prototype.getImageExtension = function () { 1644 return this.imageExtension_; 1645}; 1646 1647 1648/** 1649 * Sets the value of the <code>imageExtension</code> property. 1650 * 1651 * @param {string} imageExtension The value of the imageExtension property. 1652 */ 1653MarkerClusterer.prototype.setImageExtension = function (imageExtension) { 1654 this.imageExtension_ = imageExtension; 1655}; 1656 1657 1658/** 1659 * Returns the value of the <code>imagePath</code> property. 1660 * 1661 * @return {string} The value of the imagePath property. 1662 */ 1663MarkerClusterer.prototype.getImagePath = function () { 1664 return this.imagePath_; 1665}; 1666 1667 1668/** 1669 * Sets the value of the <code>imagePath</code> property. 1670 * 1671 * @param {string} imagePath The value of the imagePath property. 1672 */ 1673MarkerClusterer.prototype.setImagePath = function (imagePath) { 1674 this.imagePath_ = imagePath; 1675}; 1676 1677 1678/** 1679 * Returns the value of the <code>imageSizes</code> property. 1680 * 1681 * @return {Array} The value of the imageSizes property. 1682 */ 1683MarkerClusterer.prototype.getImageSizes = function () { 1684 return this.imageSizes_; 1685}; 1686 1687 1688/** 1689 * Sets the value of the <code>imageSizes</code> property. 1690 * 1691 * @param {Array} imageSizes The value of the imageSizes property. 1692 */ 1693MarkerClusterer.prototype.setImageSizes = function (imageSizes) { 1694 this.imageSizes_ = imageSizes; 1695}; 1696 1697 1698/** 1699 * Returns the value of the <code>calculator</code> property. 1700 * 1701 * @return {function} the value of the calculator property. 1702 */ 1703MarkerClusterer.prototype.getCalculator = function () { 1704 return this.calculator_; 1705}; 1706 1707 1708/** 1709 * Sets the value of the <code>calculator</code> property. 1710 * 1711 * @param {function(Array.<google.maps.Marker>, number)} calculator The value 1712 * of the calculator property. 1713 */ 1714MarkerClusterer.prototype.setCalculator = function (calculator) { 1715 this.calculator_ = calculator; 1716}; 1717 1718/** 1719 * Sets the value of the <code>hideLabel</code> property. 1720 * 1721 * @param {boolean} printable The value of the hideLabel property. 1722 */ 1723MarkerClusterer.prototype.setHideLabel = function (hideLabel) { 1724 this.hideLabel_ = hideLabel; 1725}; 1726 1727/** 1728 * Returns the value of the <code>hideLabel</code> property. 1729 * 1730 * @return {boolean} the value of the hideLabel property. 1731 */ 1732MarkerClusterer.prototype.getHideLabel = function () { 1733 return this.hideLabel_; 1734}; 1735 1736/** 1737 * Returns the value of the <code>batchSizeIE</code> property. 1738 * 1739 * @return {number} the value of the batchSizeIE property. 1740 */ 1741MarkerClusterer.prototype.getBatchSizeIE = function () { 1742 return this.batchSizeIE_; 1743}; 1744 1745 1746/** 1747 * Sets the value of the <code>batchSizeIE</code> property. 1748 * 1749 * @param {number} batchSizeIE The value of the batchSizeIE property. 1750 */ 1751MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) { 1752 this.batchSizeIE_ = batchSizeIE; 1753}; 1754 1755 1756/** 1757 * Returns the value of the <code>clusterClass</code> property. 1758 * 1759 * @return {string} the value of the clusterClass property. 1760 */ 1761MarkerClusterer.prototype.getClusterClass = function () { 1762 return this.clusterClass_; 1763}; 1764 1765 1766/** 1767 * Sets the value of the <code>clusterClass</code> property. 1768 * 1769 * @param {string} clusterClass The value of the clusterClass property. 1770 */ 1771MarkerClusterer.prototype.setClusterClass = function (clusterClass) { 1772 this.clusterClass_ = clusterClass; 1773}; 1774 1775 1776/** 1777 * Returns the array of markers managed by the clusterer. 1778 * 1779 * @return {Array} The array of markers managed by the clusterer. 1780 */ 1781MarkerClusterer.prototype.getMarkers = function () { 1782 return this.markers_; 1783}; 1784 1785 1786/** 1787 * Returns the number of markers managed by the clusterer. 1788 * 1789 * @return {number} The number of markers. 1790 */ 1791MarkerClusterer.prototype.getTotalMarkers = function () { 1792 return this.markers_.length; 1793}; 1794 1795 1796/**
1797 * Returns the current array of clusters formed by the clusterer. 1798 * 1799 * @return {Array} The array of clusters formed by the clusterer. 1800 */ 1801MarkerClusterer.prototype.getClusters = function () { 1802 return this.clusters_; 1803}; 1804 1805 1806/** 1807 * Returns the number of clusters formed by the clusterer. 1808 * 1809 * @return {number} The number of clusters formed by the clusterer. 1810 */ 1811MarkerClusterer.prototype.getTotalClusters = function () { 1812 return this.clusters_.length; 1813}; 1814 1815 1816/** 1817 * Adds a marker to the clusterer. The clusters are redrawn unless 1818 * <code>opt_nodraw</code> is set to <code>true</code>. 1819 * 1820 * @param {google.maps.Marker} marker The marker to add. 1821 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1822 */ 1823MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) { 1824 this.pushMarkerTo_(marker); 1825 if (!opt_nodraw) { 1826 this.redraw_(); 1827 } 1828}; 1829 1830 1831/** 1832 * Adds an array of markers to the clusterer. The clusters are redrawn unless 1833 * <code>opt_nodraw</code> is set to <code>true</code>. 1834 * 1835 * @param {Array.<google.maps.Marker>} markers The markers to add. 1836 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1837 */ 1838MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) { 1839 var key; 1840 for (key in markers) { 1841 if (markers.hasOwnProperty(key)) { 1842 this.pushMarkerTo_(markers[key]); 1843 } 1844 } 1845 if (!opt_nodraw) { 1846 this.redraw_(); 1847 } 1848}; 1849 1850 1851/** 1852 * Pushes a marker to the clusterer. 1853 * 1854 * @param {google.maps.Marker} marker The marker to add. 1855 */ 1856MarkerClusterer.prototype.pushMarkerTo_ = function (marker) { 1857 // If the marker is draggable add a listener so we can update the clusters on the dragend: 1858 if (marker.getDraggable()) { 1859 var cMarkerClusterer = this; 1860 google.maps.event.addListener(marker, "dragend", function () { 1861 if (cMarkerClusterer.ready_) { 1862 this.isAdded = false; 1863 cMarkerClusterer.repaint(); 1864 } 1865 }); 1866 } 1867 marker.isAdded = false; 1868 this.markers_.push(marker); 1869}; 1870 1871 1872/** 1873 * Removes a marker from the cluster and map. The clusters are redrawn unless 1874 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the 1875 * marker was removed from the clusterer. 1876 * 1877 * @param {google.maps.Marker} marker The marker to remove. 1878 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1879 * @param {boolean} [opt_noMapRemove] Set to <code>true</code> to prevent removal from map but still removing from cluster management 1880 * @return {boolean} True if the marker was removed from the clusterer. 1881 */ 1882MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw, opt_noMapRemove) { 1883 var removeFromMap = true && !opt_noMapRemove; 1884 var removed = this.removeMarker_(marker,removeFromMap); 1885 1886 if (!opt_nodraw && removed) { 1887 this.repaint(); 1888 } 1889 1890 return removed; 1891}; 1892 1893 1894/** 1895 * Removes an array of markers from the cluster and map. The clusters are redrawn unless 1896 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers 1897 * were removed from the clusterer. 1898 * 1899 * @param {Array.<google.maps.Marker>} markers The markers to remove. 1900 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1901 * @param {boolean} [opt_noMapRemove] Set to <code>true</code> to prevent removal from map but still removing from cluster management 1902 * @return {boolean} True if markers were removed from the clusterer. 1903 */ 1904MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw, opt_noMapRemove) { 1905 var i, r; 1906 var removed = false; 1907 var removeFromMap = true && !opt_noMapRemove; 1908 1909 for (i = 0; i < markers.length; i++) { 1910 r = this.removeMarker_(markers[i],removeFromMap); 1911 removed = removed || r; 1912 } 1913 1914 if (!opt_nodraw && removed) { 1915 this.repaint(); 1916 } 1917 1918 return removed; 1919}; 1920 1921 1922/** 1923 * Removes a marker and returns true if removed, false if not. 1924 * 1925 * @param {google.maps.Marker} marker The marker to remove 1926 * @param {boolean} removeFromMap set to <code>true</code> to explicitly remove from map as well as cluster manangement 1927 * @return {boolean} Whether the marker was removed or not 1928 */ 1929MarkerClusterer.prototype.removeMarker_ = function (marker,removeFromMap) { 1930 var i; 1931 var index = -1; 1932 if (this.markers_.indexOf) { 1933 index = this.markers_.indexOf(marker); 1934 } else { 1935 for (i = 0; i < this.markers_.length; i++) { 1936 if (marker === this.markers_[i]) { 1937 index = i; 1938 break; 1939 } 1940 } 1941 } 1942 1943 if (index === -1) { 1944 // Marker is not in our list of markers, so do nothing: 1945 return false;
1946 } 1947 1948 if (removeFromMap){ 1949 marker.setMap(null); 1950 } 1951 1952 this.markers_.splice(index, 1); // Remove the marker from the list of managed markers 1953 return true; 1954}; 1955 1956 1957/** 1958 * Removes all clusters and markers from the map and also removes all markers 1959 * managed by the clusterer. 1960 */ 1961MarkerClusterer.prototype.clearMarkers = function () { 1962 this.resetViewport_(true); 1963 this.markers_ = []; 1964}; 1965 1966 1967/** 1968 * Recalculates and redraws all the marker clusters from scratch. 1969 * Call this after changing any properties. 1970 */ 1971MarkerClusterer.prototype.repaint = function () { 1972 var oldClusters = this.clusters_.slice(); 1973 this.clusters_ = []; 1974 this.resetViewport_(false); 1975 this.redraw_(); 1976 1977 // Remove the old clusters. 1978 // Do it in a timeout to prevent blinking effect. 1979 setTimeout(function () { 1980 var i; 1981 for (i = 0; i < oldClusters.length; i++) { 1982 oldClusters[i].remove(); 1983 } 1984 }, 0); 1985}; 1986 1987 1988/** 1989 * Returns the current bounds extended by the grid size. 1990 * 1991 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 1992 * @return {google.maps.LatLngBounds}
vendor: 4,297 bytes, lines 1992-2140
1992 The extended bounds. 1993 * @ignore 1994 */ 1995MarkerClusterer.prototype.getExtendedBounds = function (bounds) { 1996 var projection = this.getProjection(); 1997 1998 // Turn the bounds into latlng. 1999 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 2000 bounds.getNorthEast().lng()); 2001 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 2002 bounds.getSouthWest().lng()); 2003 2004 // Convert the points to pixels and the extend out by the grid size. 2005 var trPix = projection.fromLatLngToDivPixel(tr); 2006 trPix.x += this.gridSize_; 2007 trPix.y -= this.gridSize_; 2008 2009 var blPix = projection.fromLatLngToDivPixel(bl); 2010 blPix.x -= this.gridSize_; 2011 blPix.y += this.gridSize_; 2012 2013 // Convert the pixel points back to LatLng 2014 var ne = projection.fromDivPixelToLatLng(trPix); 2015 var sw = projection.fromDivPixelToLatLng(blPix); 2016 2017 // Extend the bounds to contain the new bounds. 2018 bounds.extend(ne); 2019 bounds.extend(sw); 2020 2021 return bounds; 2022}; 2023 2024 2025/** 2026 * Redraws all the clusters. 2027 */ 2028MarkerClusterer.prototype.redraw_ = function () { 2029 this.createClusters_(0); 2030}; 2031 2032 2033/** 2034 * Removes all clusters from the map. The markers are also removed from the map 2035 * if <code>opt_hide</code> is set to <code>true</code>. 2036 * 2037 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers 2038 * from the map. 2039 */ 2040MarkerClusterer.prototype.resetViewport_ = function (opt_hide) { 2041 var i, marker; 2042 // Remove all the clusters 2043 for (i = 0; i < this.clusters_.length; i++) { 2044 this.clusters_[i].remove(); 2045 } 2046 this.clusters_ = []; 2047 2048 // Reset the markers to not be added and to be removed from the map. 2049 for (i = 0; i < this.markers_.length; i++) { 2050 marker = this.markers_[i]; 2051 marker.isAdded = false; 2052 if (opt_hide) { 2053 marker.setMap(null); 2054 } 2055 } 2056}; 2057 2058 2059/** 2060 * Calculates the distance between two latlng locations in km. 2061 * 2062 * @param {google.maps.LatLng} p1 The first lat lng point. 2063 * @param {google.maps.LatLng} p2 The second lat lng point. 2064 * @return {number} The distance between the two points in km. 2065 * @see http://www.movable-type.co.uk/scripts/latlong.html 2066*/ 2067MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) { 2068 var R = 6371; // Radius of the Earth in km 2069 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 2070 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 2071 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 2072 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 2073 Math.sin(dLon / 2) * Math.sin(dLon / 2); 2074 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 2075 var d = R * c; 2076 return d; 2077}; 2078 2079 2080/** 2081 * Determines if a marker is contained in a bounds. 2082 * 2083 * @param {google.maps.Marker} marker The marker to check. 2084 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 2085 * @return {boolean} True if the marker is in the bounds. 2086 */ 2087MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) { 2088 return bounds.contains(marker.getPosition()); 2089}; 2090 2091 2092/** 2093 * Adds a marker to a cluster, or creates a new cluster. 2094 * 2095 * @param {google.maps.Marker} marker The marker to add. 2096 */ 2097MarkerClusterer.prototype.addToClosestCluster_ = function (marker) { 2098 var i, d, cluster, center; 2099 var distance = 40000; // Some large number 2100 var clusterToAddTo = null; 2101 for (i = 0; i < this.clusters_.length; i++) { 2102 cluster = this.clusters_[i]; 2103 center = cluster.getCenter(); 2104 if (center) { 2105 d = this.distanceBetweenPoints_(center, marker.getPosition()); 2106 if (d < distance) { 2107 distance = d; 2108 clusterToAddTo = cluster; 2109 } 2110 } 2111 } 2112 2113 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 2114 clusterToAddTo.addMarker(marker); 2115 } else { 2116 cluster = new Cluster(this); 2117 cluster.addMarker(marker); 2118 this.clusters_.push(cluster); 2119 } 2120}; 2121 2122 2123/** 2124 * Creates the clusters. This is done in batches to avoid timeout errors 2125 * in some browsers when there is a huge number of markers. 2126 * 2127 * @param {number} iFirst The index of the first marker in the batch of 2128 * markers to be added to clusters. 2129 */ 2130MarkerClusterer.prototype.createClusters_ = function (iFirst) { 2131 var i, marker; 2132 var mapBounds; 2133 var cMarkerClusterer = this; 2134 if (!this.ready_) { 2135 return; 2136 } 2137 2138 // Cancel previous batch processing if we're working on the first batch: 2139 if (iFirst === 0) { 2140 /**
2141 * This event is fired when the <code>MarkerClusterer</code> begins 2142 * clustering markers. 2143 * @name MarkerClusterer#clusteringbegin 2144 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 2145 * @event 2146 */ 2147 google.maps.event.trigger(this, "clusteringbegin", this); 2148 2149 if (typeof this.timerRefStatic !== "undefined") { 2150 clearTimeout(this.timerRefStatic); 2151 delete this.timerRefStatic; 2152 } 2153 } 2154 2155 // Get our current map view bounds. 2156 // Create a new bounds object so we don't affect the map. 2157 // 2158 // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug: 2159 if (this.getMap().getZoom() > 3) { 2160 mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(), 2161 this.getMap().getBounds().getNorthEast()); 2162 } else { 2163 mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625)); 2164 } 2165 var bounds = this.getExtendedBounds(mapBounds); 2166 2167 var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length); 2168 2169 for (i = iFirst; i < iLast; i++) { 2170 marker = this.markers_[i]; 2171 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 2172 if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) { 2173 this.addToClosestCluster_(marker); 2174 } 2175 } 2176 } 2177 2178 if (iLast < this.markers_.length) { 2179 this.timerRefStatic = setTimeout(function () { 2180 cMarkerClusterer.createClusters_(iLast); 2181 }, 0); 2182 } else { 2183 delete this.timerRefStatic; 2184 2185 /** 2186 * This event is fired when the <code>MarkerClusterer</code> stops 2187 * clustering markers. 2188 * @name MarkerClusterer#clusteringend 2189 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 2190 * @event 2191 */ 2192 google.maps.event.trigger(this, "clusteringend", this); 2193 2194 for (i = 0; i < this.clusters_.length; i++) { 2195 this.clusters_[i].updateIcon_(); 2196 } 2197 } 2198}; 2199 2200 2201/** 2202 * Extends an object's prototype by another's. 2203 * 2204 * @param {Object} obj1 The object to be extended. 2205 * @param {Object} obj2 The object to extend with. 2206 * @return {Object} The new extended object. 2207 * @ignore 2208 */ 2209MarkerClusterer.prototype.extend = function (obj1, obj2) { 2210 return (function (object) { 2211 var property; 2212 for (property in object.prototype) { 2213 this.prototype[property] = object.prototype[property]; 2214 } 2215 return this; 2216 }).apply(obj1, [obj2]); 2217}; 2218 2219 2220/** 2221 * The default function for determining the label text and style 2222 * for a cluster icon. 2223 * 2224 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster. 2225 * @param {number} numStyles The number of marker styles available. 2226 * @return {ClusterIconInfo} The information resource for the cluster. 2227 * @constant 2228 * @ignore 2229 */ 2230MarkerClusterer.CALCULATOR = function (markers, numStyles) { 2231 var index = 0; 2232 var title = ""; 2233 var count = markers.length.toString(); 2234 2235 var dv = count; 2236 while (dv !== 0) { 2237 dv = parseInt(dv / 10, 10); 2238 index++; 2239 } 2240 2241 index = Math.min(index, numStyles); 2242 return { 2243 text: count, 2244 index: index, 2245 title: title 2246 }; 2247}; 2248 2249 2250/** 2251 * The number of markers to process in one batch. 2252 * 2253 * @type {number} 2254 * @constant 2255 */ 2256MarkerClusterer.BATCH_SIZE = 2000; 2257 2258 2259/** 2260 * The number of markers to process in one batch (IE only). 2261 * 2262 * @type {number} 2263 * @constant 2264 */ 2265MarkerClusterer.BATCH_SIZE_IE = 500; 2266 2267 2268/** 2269 * The default root name for the marker cluster images. 2270 * 2271 * @type {string} 2272 * @constant 2273 */ 2274MarkerClusterer.IMAGE_PATH = "//cdn.rawgit.com/mahnunchik/markerclustererplus/master/images/m"; 2275 2276 2277/** 2278 * The default extension name for the marker cluster images. 2279 * 2280 * @type {string} 2281 * @constant 2282 */ 2283MarkerClusterer.IMAGE_EXTENSION = "png"; 2284 2285 2286/** 2287 * The default array of sizes for the marker cluster images. 2288 * 2289 * @type {Array.<number>} 2290 * @constant 2291 */ 2292MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90]; 2293 2294if (typeof String.prototype.trim !== 'function') { 2295 /** 2296 * IE hack since trim() doesn't exist in all browsers 2297 * @return {string} The string with removed whitespace 2298 */ 2299 String.prototype.trim = function() { 2300 return this.replace(/^\s+|\s+$/g, ''); 2301 }; 2302} 2303 2304 2305var Limbach = { 2306 initDatepicker: function() { 2307 var fields = $('.datepicker-input'); 2308 if ($(fields).length > 0) { 2309 $('.datepicker-input').flatpickr({ 2310 dateFormat: 'd.m.Y', 2311 locale: 'de' 2312 }); 2313 } 2314 }, 2315 2316 isExtraSmall: function () { 2317 return $(window).width() >= '480'; 2318 }, 2319 2320 isSmall: function () { 2321 return $(window).width() >= '768'; 2322 }, 2323 2324 isMedium: function () { 2325 return $(window).width() >= '992'; 2326 }, 2327 2328 isLarge: function () { 2329 return $(window).width() >= '1200'; 2330 }, 2331 2332 initSliderTeaser: function () { 2333 if (this.isMedium() === true) { 2334 var $sliderTeaser = $('.teaser__summary-about-box'), 2335 sliderTeaserHeight = $sliderTeaser.outerHeight(), 2336 requiredHeight = 0, 2337 minHeight = 200; 2338 2339 $sliderTeaser.mouseenter(function () { 2340 requiredHeight = $(this).find('.teaser__summary-about-content').outerHeight() + sliderTeaserHeight + 35; 2341 $(this).stop().animate({ 2342 minHeight: (requiredHeight > minHeight) ? requiredHeight : minHeight + 'px' 2343 }, 200); 2344 }).mouseleave(function () { 2345 $(this).stop().animate({ 2346 minHeight: '0' 2347 }, 400); 2348 }); 2349 } 2350 }, 2351
2352 initContactVitae: function () { 2353 var $vitaeButton = $('.contact__vitae-button'); 2354 2355 $vitaeButton.on('touch click', function () { 2356 var $this = $(this); 2357 var $btnCaption = $this.find('.contact__vitae-button-caption'); 2358 var $btnCaptionText = $btnCaption.text(); 2359 $this.nextAll('.contact__vitae').stop().slideToggle(); 2360 $this.find('.contact__vitae-button-caption').text($btnCaptionText === '+' ? '-' : '+') 2361 }); 2362 }, 2363 2364 initAccordion: function () { 2365 var $accordionBox = $('.accordion__title'); 2366 2367 2368 2369 $accordionBox.click(function (e) { 2370 2371 e.preventDefault(); 2372 e.stopPropagation() 2373 if( $(this).prop("tagName") == 'p' || $(this).prop("tagName") == 'P' ) { 2374 $(this).next('.accordion__content').slideToggle('fast'); 2375 } else { 2376 $(this).parent().find('.accordion__content').first().stop().slideToggle('fast'); 2377 } 2378 2379 $(this).toggleClass('active'); 2380 }); 2381 }, 2382 2383 initMainNaviSearchButton: function () { 2384 var $searchButton = $('.meta-navigation__search-button'); 2385 2386 $searchButton.click(function () { 2387 $('.meta-navigation__search-input').fadeToggle().focus(); 2388 }); 2389 }, 2390 2391 initThrottledResize: function () { 2392 this.initTeaserEqualHeights(); 2393 this.initMobileMenu(); 2394 this.initDownloadSlider(); 2395 this.initMobileHeader(); 2396 this.initEqualHeights(); 2397 this.initSearchFilter(); 2398 this.initSliderCaptionPosition(); 2399 }, 2400 2401 initTeaserEqualHeights: function () { 2402 var $teaserElements = $('div[class^="teaser__main--"],div[class^="teaser__main-image"]').not('div[class^="teaser__main--content"], div[class^="teaser__main--content"] > div[class^="teaser__main"], div[class^=teaser__main--square]'); 2403 $teaserElements.css('min-height', '0'); 2404 var maxHeight = 0; 2405 2406 function getMaxHeight() { 2407 $.each($teaserElements, function () { 2408 var height = $(this).outerHeight(true); 2409 2410 if (height > maxHeight) { 2411 maxHeight = height; 2412 } 2413 }); 2414 } 2415 2416 getMaxHeight(); 2417 2418 $.each($teaserElements, function () { 2419 if(typeof $(this).closest('.container').data('context') === 'undefined') { 2420 $(this).css('min-height', maxHeight); 2421 } 2422 }); 2423 2424 //$teaserElements.css('min-height', maxHeight); 2425 }, 2426 2427 initEqualHeights: function () { 2428 var $autoHeightElements = $('div[data-autoheight^="true"]'); 2429 var maxHeight = 0; 2430 $autoHeightElements.css('minHeight', maxHeight); 2431 $autoHeightElements.height('auto'); 2432 2433 $.each($autoHeightElements, function (key, val) { 2434 if ($(this).outerHeight() > maxHeight) { 2435 maxHeight = $(this).outerHeight(); 2436 } 2437 2438 }); 2439 2440 //$autoHeightElements.height(maxHeight); 2441 $autoHeightElements.css('minHeight', maxHeight); 2442 2443 }, 2444 2445 initContact: function () { 2446 if (this.isMedium === true) { 2447 var $contactBox = $('.contact__box'), 2448 $contactContent = $('.contact__content'); 2449 2450 $.each($contactBox, function () { 2451 $(this).find('.contact__content').height($(this).innerHeight()); 2452 $(this).find('.contact__image-content').height($(this).innerHeight()); 2453 }); 2454 } 2455 }, 2456 2457 initCalculatorDescription: function () { 2458 var $showDescriptionBtn = $('.calculator__description-show'); 2459 2460 $showDescriptionBtn.click(function (evt) { 2461 evt.preventDefault(); 2462 var $currentText = $(this).text(); 2463 $(this).text($currentText === 'Beschreibung anzeigen' ? 'Beschreibung ausblenden' : 'Beschreibung anzeigen'); 2464 $(this).next('.calculator__description').stop().slideToggle(); 2465 }); 2466 }, 2467 2468 initCalculatorShowResult: function () { 2469 var $calculateBtn = $('.calculator__element > input[type="button"]'); 2470 2471 $calculateBtn.click(function (evt) { 2472 $(this).parents('.calculator__box').next('.calculator__result').stop().slideDown(); 2473 if (typeof _paq != 'undefined') { 2474 _paq.push(['trackEvent', 'Calculator', 'Calculate', document.title]); 2475 } 2476 }); 2477 }, 2478
2479 initCalculatorSubmit: function () { 2480 $('.calculator__input').keypress(function (evt) { 2481 if ((evt.which && evt.which == 13) || (evt.keyCode && evt.keyCode == 13)) { 2482 $('input[type=button]').click(); 2483 return false; 2484 } else { 2485 return true; 2486 } 2487 }); 2488 }, 2489 2490 initPayType: function () { 2491 var $elv = $('#elv'), 2492 $elvForm = $('#elv-form'), 2493 $billing = $('#billing'), 2494 $billingForm = $('#billing-form'); 2495 2496 $elv.click(function () { 2497 $billingForm.stop().slideUp(); 2498 $elvForm.stop().slideDown(); 2499 }); 2500 2501 $billing.click(function () { 2502 $elvForm.stop().slideUp(); 2503 $billingForm.stop().slideDown(); 2504 }); 2505 }, 2506 2507 initOverlay: function () { 2508 var that = this; 2509 2510 $('a[class^=main-navigation__link]').mouseover(function () { 2511 if (that.isMedium()) { 2512 var submenuSelector = '.main-navigation__sub-menu'; 2513 2514 if ($(this).next().is(submenuSelector)) { 2515 $('.content__overlay').stop().fadeIn(300); 2516 2517 // Check if we need to flip the menu 2518 var $submenu = $(this).next(); 2519 if ($submenu.offset().left + $submenu.outerWidth(true) > $(window).innerWidth()) { 2520 $submenu.addClass('main-navigation__sub-menu--flipped'); 2521 } 2522 } else { 2523 // Mouseover from one menu with submenu to menu without submenu should hide overlay 2524 $('.content__overlay').stop().fadeOut(250); 2525 } 2526 } 2527 2528 }); 2529 2530 $('.main-navigation__list').mouseleave(function () { 2531 if (that.isMedium()) { 2532 $('.content__overlay').stop().fadeOut(250); 2533 2534 // Reset the flipped 2535 $('.main-navigation__link + .main-navigation__sub-menu').removeClass('main-navigation__sub-menu--flipped'); 2536 } 2537 }); 2538 }, 2539 2540 initFooterOverlay: function () { 2541 var that = this; 2542 $('.footer-navigation__link').mouseover(function () { 2543 if (that.isMedium()) { 2544 var submenuSelectors = 'ul.level2Block'; 2545 if ($(this).next().is(submenuSelectors)) { 2546 $('.content__overlay').stop().fadeIn(300); 2547 } else { 2548 // Mouseover from one menu with submenu to menu without submenu should hide overlay 2549 $('.content__overlay').stop().fadeOut(250); 2550 } 2551 } 2552 }); 2553 2554 $('.level2Block').mouseover(function () { 2555 if (that.isMedium()) { 2556 $('.content__overlay').stop().fadeIn(300); 2557 } 2558 }); 2559 2560 $('.footer-navigation__wrapper').mouseleave(function () { 2561 if (that.isMedium()) { 2562 $('.content__overlay').stop().fadeOut(250); 2563 } 2564 }); 2565 }, 2566 2567 initializedMobileMenu: false, 2568 metaMobileMenu: function() { 2569 if (!this.isMedium()) { 2570 var menu = $('#meta-navigation .meta-first'); 2571 $(menu).find('.has-sub').each(function(_i, el) { 2572 if ($(el).find('.cloned-first').length == 0) { 2573 var clone = $(this).children('a').clone(); 2574 $(clone).find('i').remove(); 2575 $(this).children('ul').prepend(clone); 2576 $(clone).wrap('<li class="cloned-first"></li>'); 2577 } 2578 }); 2579 2580 $(menu).on('click', '.has-sub a', function(ev) { 2581 if ($(this).parent().hasClass('has-sub')) { 2582 ev.preventDefault(); 2583 if ($(this).parent().hasClass('is-open')) { 2584 $(this).parent().removeClass('is-open'); 2585 $(this).parent().children('ul').slideUp(); 2586 } else { 2587 $(this).parent().addClass('is-open'); 2588 $(this).parent().children('ul').slideDown(); 2589 } 2590 } 2591 }); 2592 } 2593 }, 2594 initMobileMenu: function () { 2595 var that = this; 2596 2597 if (!this.isMedium()) { 2598 // This should only be bound once 2599 if (!this.initializedMobileMenu) { 2600 that.metaMobileMenu(); 2601 2602 // footer menu 2603 $('.footer-navigation__trigger').on('click touch', function (evt) { 2604 evt.preventDefault(); 2605 $(this).next().stop().slideToggle(); 2606 }); 2607 2608 2609 // main menu 2610 $('.main-navigation__trigger').on('click touch', function (evt) { 2611 evt.preventDefault(); 2612 if (!that.isMedium()) { 2613 $('#navigation').stop(true, true).slideToggle(); 2614 $(this).toggleClass('isOpen'); 2615 } 2616 }); 2617 $('.main-navigation__sub-item-caption--haschild, [class^=main-navigation__link]').on('click touch', function (evt) { 2618 if (!that.isMedium()) { 2619 var $el = $(this), 2620 $next = $el.next(); 2621 2622 if ($next.length > 0 && $next.hasClass('main-navigation__sub-menu') || $next.hasClass('main-navigation__sub-item')) { 2623 evt.preventDefault(); 2624 } 2625 2626 $(this).next().stop(true, true).slideToggle(); 2627 } 2628 }); 2629 2630 $('[class^=main-navigation__link]').each(function () { 2631 var $el = $(this), 2632 $next = $el.next(), 2633 hasSubmenu = $next.length > 0 && $next.hasClass('main-navigation__sub-menu'); 2634 2635 if (hasSubmenu) { 2636 // Find first submenu item and clone it 2637 var $menu = $next.find('.main-navigation__sub-item-list'); 2638 2639 $menu.prepend('<li class="main-navigation__sub-item-head main-navigation__sub-item--only-mobile"><a href="' + $el.attr('href') + '" class="main-navigation__sub-item-caption">Ãbersicht ' + $el.text() + '</a></li>'); 2640 2641 $menu.find('.main-navigation__sub-item-head > a').each(function () { 2642 var $item = $(this), 2643 $next = $item.next(), 2644 hasSubMenuItem = $next.length > 0 && $next.hasClass('main-navigation__sub-item'); 2645 2646 if (hasSubMenuItem) { 2647 $next.prepend('<li class="main-navigation__sub-item-element main-navigation__sub-item--only-mobile"><a class="main-navigation__sub-item-link" href="' + $item.attr('href') + '">
2647Ãbersicht ' + $item.text() + '</a></li>'); 2648 } 2649 }); 2650 } 2651 }); 2652 2653 this.initializedMobileMenu = true; 2654 } 2655 } else { 2656 $('.main-navigation__items').show(); 2657 } 2658 }, 2659 2660 initTriggerFirstLink: function () { 2661 $("[data-trigger-first-link]").click(function (e) { 2662 // Prevent bubbling 2663 if (!$(e.target).is('a')) { 2664 $(this).find('a').eq(0)[0].click(); 2665 } 2666 }); 2667 }, 2668 2669 initDownloadSlider: function () { 2670 var $downloadSlider = $('.download__slider'); 2671 if ($downloadSlider.length) { 2672 $downloadSlider.slick({ 2673 centerMode: true, 2674 centerPadding: '120px', 2675 slidesToShow: 3, 2676 swipeToSlide: true, 2677 responsive: [ 2678 { 2679 breakpoint: 768, 2680 settings: { 2681 arrows: false, 2682 centerMode: true, 2683 centerPadding: '40px', 2684 slidesToShow: 2 2685 } 2686 }, 2687 { 2688 breakpoint: 480, 2689 settings: { 2690 arrows: false, 2691 centerMode: true, 2692 centerPadding: '40px', 2693 slidesToShow: 1 2694 } 2695 } 2696 ] 2697 }); 2698 } 2699 }, 2700 2701 initToTop: function () { 2702 $(window).scroll(function () { 2703 if ($(this).scrollTop() > 100) { 2704 $('#back-top').stop().fadeIn(); 2705 } else { 2706 $('#back-top').stop().fadeOut(); 2707 } 2708 }); 2709 2710 // scroll body to 0px on click 2711 $('#back-top').click(function () { 2712 $('body,html').animate({ 2713 scrollTop: 0 2714 }, 800); 2715 return false; 2716 }); 2717 }, 2718 2719 scroll: function (e) { 2720 /*var $footer = $(".footer-content__background--shifted"), 2721 $headline = $footer.find('.footer-content__headline').first(), 2722 headlinePosition = $headline.offset(), 2723 scrollTop = $(window).scrollTop(), 2724 $toTop = $('#back-top'), 2725 attr = $toTop.attr('data-old-bottom-value'); 2726 2727 if ((scrollTop + $(window).height()) >= headlinePosition.top + $headline.outerHeight(true)) { 2728 if (attr === undefined) { 2729 $toTop.attr('data-old-bottom-value', $toTop.css('bottom')); 2730 } 2731 2732 $toTop.css({ 2733 transition: 'none', 2734 bottom: (scrollTop + $(window).height()) - headlinePosition.top - ($headline.outerHeight(true) / 2) 2735 }); 2736 } else { 2737 if (attr !== undefined) { 2738 $toTop.css({ 2739 bottom: attr 2740 }); 2741 } 2742 }*/ 2743 }, 2744 2745 initQuoteHeight: function () { 2746 var $quote = $('.quote__text'); 2747 if ($quote.length) { 2748 $quote.closest($('[class*=background__]').css('minHeight', 0)); 2749 } 2750 }, 2751 2752 initMobileHeader: function () { 2753 var $heroUnitContainer = $('.hero-unit .container-fluid').first(); 2754 if (typeof $heroUnitContainer.css('backgroundImage') !== 'undefined' && parseInt($heroUnitContainer.css('minHeight')) > 0) { 2755 if (!this.isMedium()) { 2756 $heroUnitContainer.after($('.hero-unit .container-fluid .hero__content').first()); 2757 } else { 2758 $heroUnitContainer.append($heroUnitContainer.next('.hero__content')); 2759 } 2760 } 2761 }, 2762 2763 initDifferentBillingAddress: function () { 2764 var $billingAddressCheckbox = $('#billingAddressLabel'), 2765 $eventBilling = $('.event__booking-billing'); 2766 2767 if ($billingAddressCheckbox.length) { 2768 if ($billingAddressCheckbox.prev().is(':checked')) { 2769 //$eventBilling.show(); 2770 } 2771 2772 $billingAddressCheckbox.on('touch click', function () { 2773 //$eventBilling.stop().slideToggle(); 2774 }); 2775 } 2776 }, 2777 2778 initSearchFilter: function () { 2779 var $searchFilterLink = $('.search__result-show-filter'), 2780 $searchItemList = $('.search__result-filter-items'); 2781 2782 if ($searchItemList.length) { 2783 $searchFilterLink.on('click touch', function (evt) { 2784 evt.preventDefault(); 2785 $(this).find('i').toggleClass('fa-angle-down fa-angle-up'); 2786 $searchItemList.stop().slideToggle(); 2787 }); 2788 } 2789 }, 2790 2791 initSwiperSlider: function () { 2792 var mySwiper = new Swiper('.swiper-container', { 2793 loop: true, 2794 autoplay: 7000, 2795 effect: 'fade', 2796 speed: 1250, 2797 paginationClickable: true, 2798 2799 pagination: '.swiper-pagination' 2800 }); 2801 }, 2802 2803 initSliderCaptionPosition: function () { 2804 var $sliderHeadCaption = $('.slider__head-caption-content'); 2805 var $sliderBodytextCaption = $('.slider__bodytext-caption-content'); 2806 if ($sliderHeadCaption.length) { 2807 if (this.isMedium()) { 2808 var windowWidth = $(window).width(), 2809 containerWidth = $('.container').first().width(), 2810 captionDistance = (windowWidth - containerWidth) / 2; 2811 $sliderHeadCaption.css('paddingRight', captionDistance + 'px'); 2812 $sliderBodytextCaption.css('paddingRight', captionDistance + 'px'); 2813 } else { 2814 $sliderHeadCaption.css('paddingRight', 0); 2815 $sliderBodytextCaption.css('paddingRight', 0); 2816 } 2817 } 2818 }, 2819 2820 initSearchModalBox: function () { 2821 var $body = $('body'), 2822 $fields = $('input.search__input, input.meta-navigation__search-input'),
2823 $modal = $('<div><div id="modal_box" class="modal-overlay"><div class="modal-box"><div class="modal-box__text">Bitte verwenden Sie als Suchbegriff Worte mit einer Länge von mindestens 4 Zeichen.</div><div class="modal-box__button" id="modal-close"><span class="button__primary">Schließen</span></div></div></div></div>'); 2824 2825 if ($fields.length) { 2826 2827 $fields.each(function () { 2828 var _field = $(this); 2829 var _form = _field.closest('form'); 2830 2831 _form.submit(function (event) { 2832 if (_field.val().length < 4) { 2833 _field.attr('autocomplete', 'off'); 2834 event.preventDefault(); 2835 notifyUser(_field); 2836 } 2837 }); 2838 }); 2839 2840 } 2841 2842 function notifyUser(dependingField) { 2843 2844 $body.append($modal.html()); 2845 2846 $('#modal-close').bind('click', function () { 2847 removeBox(dependingField); 2848 }); 2849 2850 dependingField.focus(); 2851 } 2852 2853 function removeBox(dependingField) { 2854 $('.modal-overlay').fadeOut(200, function () { 2855 $(this).remove(); 2856 }); 2857 2858 dependingField.removeAttr('autocomplete'); 2859 } 2860 }, 2861 2862 initEventTeaserEqualHeight: function() { 2863 var $teaser = $('.event__teaser'); 2864 var captionMax = 0; 2865 var infoMax = 0; 2866 var contentMax = 0; 2867 var windowWidth = $(window).width(); 2868 2869 if ($teaser.length) { 2870 if (windowWidth > 768) { 2871 $teaser.each(function() { 2872 var captionHeight = $(this).find('.event__teaser-caption').outerHeight(); 2873 var infoHeight = $(this).find('.event__teaser-information').innerHeight(); 2874 var contentHeight = $(this).find('.event__teaser-content').innerHeight(); 2875 2876 if (captionHeight > captionMax) { 2877 captionMax = captionHeight; 2878 } 2879 2880 if (infoHeight > infoMax) { 2881 infoMax = infoHeight; 2882 } 2883 2884 if (contentHeight > contentMax) { 2885 contentMax = contentHeight; 2886 } 2887 }); 2888 2889 $teaser.each(function() { 2890 $(this).find('.event__teaser-caption').css('min-height', captionMax); 2891 $(this).find('.event__teaser-information').css('min-height', infoMax); 2892 $(this).find('.event__teaser-content').css('min-height', contentMax); 2893 }); 2894 2895 } else { 2896 $teaser.each(function() { 2897 $(this).find('.event__teaser-caption').removeAttr('style'); 2898 $(this).find('.event__teaser-information').removeAttr('style'); 2899 $(this).find('.event__teaser-content').removeAttr('style'); 2900 }); 2901 } 2902 } 2903 }, 2904 2905 initMapRouteLink: function() { 2906 $('.map__route-link').click(function(evt) { 2907 if(typeof _paq != 'undefined') { 2908 _paq.push(['trackEvent', 'Map', 'findRoute', document.title]); 2909 } 2910 }); 2911 }, 2912 2913 mailForm: function() { 2914 $('div.csc-mailform li.csc-form-element-radio input').each(function () { 2915 $(this).insertBefore($(this).prev('label')); 2916 }); 2917 }, 2918 2919 initTabs: function() { 2920 $('.mega-tab-menu').each(function() { 2921 var highest = 0; 2922 $(this).find('li').each(function() { 2923 var $this = $(this); 2924 if ( $this.outerHeight() > highest ) { 2925 highest = $this.outerHeight(); 2926 } 2927 }); 2928 $(this).find('li a').css('height', highest); 2929 $(this).find('li button').css('height', highest); 2930 }); 2931 } 2932 2933}; 2934 2935$(document).ready(function () { 2936 Limbach.initContactVitae(); 2937 Limbach.initAccordion(); 2938 Limbach.initMainNaviSearchButton(); 2939 Limbach.initSliderTeaser(); 2940 Limbach.initTeaserEqualHeights(); 2941 Limbach.initEqualHeights(); 2942 Limbach.initContact();
2943 Limbach.initCalculatorDescription(); 2944 Limbach.initCalculatorShowResult(); 2945 Limbach.initCalculatorSubmit(); 2946 Limbach.initPayType(); 2947 Limbach.initOverlay(); 2948 Limbach.initFooterOverlay(); 2949 Limbach.initMobileMenu(); 2950 Limbach.initTriggerFirstLink(); 2951 Limbach.initDownloadSlider(); 2952 Limbach.initToTop(); 2953 Limbach.scroll(); 2954 Limbach.initQuoteHeight(); 2955 Limbach.initMobileHeader(); 2956 Limbach.initDifferentBillingAddress(); 2957 Limbach.initSearchFilter(); 2958 Limbach.initSwiperSlider(); 2959 Limbach.initSliderCaptionPosition(); 2960 Limbach.initSearchModalBox(); 2961 Limbach.initEventTeaserEqualHeight(); 2962 Limbach.initMapRouteLink(); 2963 Limbach.mailForm(); 2964 Limbach.initTabs(); 2965 Limbach.initDatepicker(); 2966 2967 var resizeTimeout; 2968 $(window).on('resize', function () { 2969 clearTimeout(resizeTimeout); 2970 resizeTimeout = setTimeout(function () { 2971 Limbach.initThrottledResize(); 2972 Limbach.initEventTeaserEqualHeight(); 2973 }, 100); 2974 }); 2975 2976 $(window).scroll(function (e) { 2977 Limbach.scroll(e); 2978 }); 2979 2980 if ($(".meta-navigation__link--highlighted.last").children("i").length > 0) { 2981 $(".hide-sm #language-switch").addClass("mr"); 2982 } 2983}); 2984 2985// Generated by CoffeeScript 2.2.4 2986(function() { 2987 var cart, chunks, shop; 2988 2989 chunks = function(array, size) { 2990 var results; 2991 results = []; 2992 while (array.length) { 2993 results.push(array.splice(0, size)); 2994 } 2995 return results; 2996 }; 2997 2998 cart = { 2999 sel: { 3000 cart: '.simpleshop-cart', 3001 inner: '.simpleshop-cart-inner', 3002 itemAmount: '.simpleshop-item-amount', 3003 toggle: '#cart-trigger' 3004 }, 3005 updateAmount: function() { 3006 return console.log('wurst'); 3007 }, 3008 run: function() { 3009 var me; 3010 me = this; 3011 ($(this.sel.itemAmount)).change(function() { 3012 var amount, item, url; 3013 amount = ($(this)).val(); 3014 item = ($(this)).data('item'); 3015 url = "/simpleshop/update-item-amount?item="; 3016 url += item; 3017 url += "&amount="; 3018 url += amount; 3019 return $.ajax({ 3020 url: url 3021 }); 3022 }); 3023 return ($(this.sel.toggle)).click(function() { 3024 ($(me.sel.cart)).toggleClass('open'); 3025 return ($(me.sel.inner)).finish().slideToggle(); 3026 }); 3027 } 3028 }; 3029 3030 shop = { 3031 sel: { 3032 parent: '.parent-category-checkbox', 3033 products: '.tx-simpleshop .products', 3034 addBtn: '.add-to-cart', 3035 categoryTree: '.category-tree', 3036 categoryToggle: '.category-toggle' 3037 }, 3038 init: function() { 3039 var me, sets; 3040 me = this; 3041 this.equalizeHeight(); 3042 sets = $('.childs'); 3043 return sets.each(function(i, set) { 3044 var checkedSilbings, childs, parent; 3045 parent = ($(set)).siblings(me.sel.parent); 3046 childs = ($(set)).find('input[type="checkbox"]'); 3047 checkedSilbings = ($(set)).find('input[type="checkbox"]:checked'); 3048 if (childs.length === checkedSilbings.length) { 3049 return ($(parent)).prop('checked', true); 3050 } 3051 }); 3052 }, 3053 parentChange: function(parent) { 3054 var childs; 3055 childs = ($(parent)).siblings('.childs').find('input[type="checkbox"]'); 3056 if (($(parent)).is(':checked')) { 3057 return ($(childs)).each(function(i, cb) { 3058 return ($(cb)).prop('checked', true); 3059 }); 3060 } else { 3061 return ($(childs)).each(function(i, cb) { 3062 return ($(cb)).prop('checked', false); 3063 }); 3064 } 3065 }, 3066 childChange: function(child) { 3067 var checkedSilbings, parent, siblings; 3068 parent = (($(child)).parents('.childs')).siblings(shop.sel.parent); 3069 siblings = (($(child)).parents('.childs')).find('input[type="checkbox"]'); 3070 if (!(($(child)).is(':checked'))) { 3071 return ($(parent)).prop('checked', false); 3072 } else { 3073 checkedSilbings = (($(child)).parents('.childs')).find('input[type="checkbox"]:checked'); 3074 if (siblings.length === checkedSilbings.length) { 3075 return ($(parent)).prop('checked', true); 3076 } 3077 } 3078 }, 3079 equalizeHeight: function() { 3080 var bottomHeight, count, first, heights, heightsPerRow, itemsPerRow, products; 3081 heights = []; 3082 itemsPerRow = 0; 3083 first = 0; 3084 count = true; 3085 ($('.tx-simpleshop .simpleshop-product')).each(function(i, el) { 3086 if (i === 0) { 3087 first = ($(el)).offset().left; 3088 } 3089 if ((i !== 0) && (first === ($(el)).offset().left)) { 3090 count = false;
3091 } 3092 if (count) { 3093 itemsPerRow += 1; 3094 } 3095 return heights.push(($(el)).height()); 3096 }); 3097 heightsPerRow = []; 3098 (chunks(heights, itemsPerRow)).forEach(function(a) { 3099 var height; 3100 height = 0; 3101 a.forEach(function(h) { 3102 if (height < h) { 3103 return height = h; 3104 } 3105 }); 3106 return heightsPerRow.push(height); 3107 }); 3108 bottomHeight = ($('.tx-simpleshop .simpleshop-product .product form .inner-bottom')).first().height(); 3109 products = chunks($('.tx-simpleshop .simpleshop-product .product form .inner-top'), itemsPerRow); 3110 return ($(products)).each(function(row, productsInRow) { 3111 return ($(productsInRow)).each(function(_i, product) { 3112 return ($(product)).height(heightsPerRow[row] - bottomHeight); 3113 }); 3114 }); 3115 }, 3116 run: function() { 3117 var me; 3118 me = this; 3119 this.init(); 3120 ($(this.sel.categoryToggle)).on('click', function(ev) { 3121 return ($(me.sel.categoryTree)).finish().slideToggle(); 3122 }); 3123 ($(this.sel.categoryTree)).on('click', 'a.parent', function(ev) { 3124 ev.preventDefault(); 3125 return ($(this)).siblings('.childs').slideToggle(); 3126 }); 3127 ($(this.sel.parent)).on('change', function() { 3128 return me.parentChange(this); 3129 }); 3130 ($('.childs input[type="checkbox"]')).on('change', function() { 3131 return me.childChange(this); 3132 }); 3133 return ($('.form-add-to-cart')).on('submit', function(ev) { 3134 var btn; 3135 if (($(this)).hasClass('submitted')) { 3136 ev.preventDefault(); 3137 false; 3138 } 3139 ($(this)).addClass('submitted'); 3140 btn = ($(this)).find(shop.sel.addBtn); 3141 ($(btn)).attr('readOnly', true); 3142 ($(btn)).addClass('button-disabled'); 3143 return (($(btn)).find('i')).show(); 3144 }); 3145 } 3146 }; 3147 3148 $(window).load(function() { 3149 if (($('.tx-simpleshop')).length > 0) { 3150 shop.run(); 3151 } 3152 if (($('.simpleshop-cart')).length > 0) { 3153 return cart.run(); 3154 } 3155 }); 3156 3157}).call(this);
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.