1// ==ClosureCompiler== 2// @compilation_level ADVANCED_OPTIMIZATIONS 3// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3.js 4// ==/ClosureCompiler== 5 6/** 7 * @name CSS3 InfoBubble with tabs for Google Maps API V3 8 * @version 0.8 9 * @author Luke Mahe 10 * @fileoverview 11 * This library is a CSS Infobubble with tabs. It uses css3 rounded corners and 12 * drop shadows and animations. It also allows tabs 13 */ 14 15/* 16 * Licensed under the Apache License, Version 2.0 (the "License"); 17 * you may not use this file except in compliance with the License. 18 * You may obtain a copy of the License at 19 * 20 * http://www.apache.org/licenses/LICENSE-2.0 21 * 22 * Unless required by applicable law or agreed to in writing, software 23 * distributed under the License is distributed on an "AS IS" BASIS, 24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 25 * See the License for the specific language governing permissions and 26 * limitations under the License. 27 */ 28 29 30/** 31 * A CSS3 InfoBubble v0.8 32 * @param {Object.<string, *>=} opt_options Optional properties to set. 33 * @extends {google.maps.OverlayView} 34 * @constructor 35 */ 36function InfoBubble(opt_options) { 37 this.extend(InfoBubble, google.maps.OverlayView); 38 this.tabs_ = []; 39 this.activeTab_ = null; 40 this.baseZIndex_ = 100; 41 this.isOpen_ = false; 42 43 var options = opt_options || {}; 44 45 if (options['backgroundColor'] == undefined) { 46 options['backgroundColor'] = this.BACKGROUND_COLOR_; 47 } 48 49 if (options['borderColor'] == undefined) { 50 options['borderColor'] = this.BORDER_COLOR_; 51 } 52 53 if (options['borderRadius'] == undefined) { 54 options['borderRadius'] = this.BORDER_RADIUS_; 55 } 56 57 if (options['borderWidth'] == undefined) { 58 options['borderWidth'] = this.BORDER_WIDTH_; 59 } 60 61 if (options['padding'] == undefined) { 62 options['padding'] = this.PADDING_; 63 } 64 65 if (options['arrowPosition'] == undefined) { 66 options['arrowPosition'] = this.ARROW_POSITION_; 67 } 68 69 if (options['disableAutoPan'] == undefined) { 70 options['disableAutoPan'] = false; 71 } 72 73 if (options['disableAnimation'] == undefined) { 74 options['disableAnimation'] = false; 75 } 76 77 if (options['minWidth'] == undefined) { 78 options['minWidth'] = this.MIN_WIDTH_; 79 } 80 81 if (options['shadowStyle'] == undefined) { 82 options['shadowStyle'] = this.SHADOW_STYLE_; 83 } 84 85 if (options['arrowSize'] == undefined) { 86 options['arrowSize'] = this.ARROW_SIZE_; 87 } 88 89 if (options['arrowStyle'] == undefined) { 90 options['arrowStyle'] = this.ARROW_STYLE_; 91 } 92 93 this.buildDom_(); 94 95 this.setValues(options); 96} 97window['InfoBubble'] = InfoBubble; 98 99 100/** 101 * Default arrow size 102 * @const 103 * @private 104 */ 105InfoBubble.prototype.ARROW_SIZE_ = 15; 106 107 108/** 109 * Default arrow style 110 * @const 111 * @private 112 */ 113InfoBubble.prototype.ARROW_STYLE_ = 0; 114 115 116/** 117 * Default shadow style 118 * @const 119 * @private 120 */ 121InfoBubble.prototype.SHADOW_STYLE_ = 1; 122 123 124/** 125 * Default min width 126 * @const 127 * @private 128 */ 129InfoBubble.prototype.MIN_WIDTH_ = 50; 130 131 132/** 133 * Default arrow position 134 * @const 135 * @private 136 */ 137InfoBubble.prototype.ARROW_POSITION_ = 50; 138 139 140/** 141 * Default padding 142 * @const 143 * @private 144 */ 145InfoBubble.prototype.PADDING_ = 10; 146 147 148/** 149 * Default border width 150 * @const 151 * @private 152 */ 153InfoBubble.prototype.BORDER_WIDTH_ = 1; 154 155 156/** 157 * Default border color 158 * @const 159 * @private 160 */ 161InfoBubble.prototype.BORDER_COLOR_ = '#ccc'; 162 163 164/** 165 * Default border radius 166 * @const 167 * @private 168 */ 169InfoBubble.prototype.BORDER_RADIUS_ = 10; 170 171 172/** 173 * Default background color 174 * @const 175 * @private 176 */ 177InfoBubble.prototype.BACKGROUND_COLOR_ = '#fff'; 178 179 180/** 181 * Extends a objects prototype by anothers. 182 * 183 * @param {Object} obj1 The object to be extended. 184 * @param {Object} obj2 The object to extend with. 185 * @return {Object} The new extended object. 186 * @ignore 187 */ 188InfoBubble.prototype.extend = function(obj1, obj2) { 189 return (function(object) { 190 for (var property in object.prototype) { 191 this.prototype[property] = object.prototype[property]; 192 } 193 return this; 194 }).apply(obj1, [obj2]); 195}; 196 197 198/** 199 * Builds the InfoBubble dom 200 * @private 201 */ 202InfoBubble.prototype.buildDom_ = function() { 203 var bubble = this.bubble_ = document.createElement('DIV'); 204 bubble.style['position'] = 'absolute'; 205 bubble.style['zIndex'] = this.baseZIndex_; 206 bubble.className = 'bubble-info'; 207 var tabsContainer = this.tabsContainer_ = document.createElement('DIV'); 208 tabsContainer.style['position'] = 'relative'; 209 210 // Close button 211 var close = this.close_ = document.createElement('IMG'); 212 close.style['position'] = 'absolute'; 213 close.style['width'] = this.px(8); 214 close.style['height'] = this.px(8); 215 close.style['padding-top'] = this.px(1); 216 close.style['padding-right'] = this.px(1); 217 close.style['margin-right'] = this.px(1); 218 close.style['border'] = 0; 219 close.style['zIndex'] = this.baseZIndex_ + 1; 220 close.style['cursor'] = 'pointer'; 221 close.src = $('#website_url').val() + 'plugins/gmaps/system/layout/images/close.gif'; 222 223 var that = this; 224 close.addEventListener('click', (e => { 225 that.close(); 226 google.maps.event.trigger(that, 'closeclick'); 227 })); 228 229 // Content area 230 var contentContainer = this.contentContainer_ = document.createElement('DIV');
231 contentContainer.style['overflowX'] = 'auto'; 232 contentContainer.style['overflowY'] = 'auto'; 233 contentContainer.style['cursor'] = 'default'; 234 contentContainer.style['clear'] = 'both'; 235 contentContainer.style['position'] = 'relative'; 236 237 var content = this.content_ = document.createElement('DIV'); 238 contentContainer.appendChild(content); 239 240 // Arrow 241 var arrow = this.arrow_ = document.createElement('DIV'); 242 arrow.style['position'] = 'relative'; 243 244 var arrowOuter = this.arrowOuter_ = document.createElement('DIV'); 245 var arrowInner = this.arrowInner_ = document.createElement('DIV'); 246 247 var arrowSize = this.getArrowSize_(); 248 249 arrowOuter.style['position'] = arrowInner.style['position'] = 'absolute'; 250 arrowOuter.style['left'] = arrowInner.style['left'] = '50%'; 251 arrowOuter.style['height'] = arrowInner.style['height'] = '0'; 252 arrowOuter.style['width'] = arrowInner.style['width'] = '0'; 253 arrowOuter.style['marginLeft'] = this.px(-arrowSize); 254 arrowOuter.style['borderWidth'] = this.px(arrowSize); 255 arrowOuter.style['borderBottomWidth'] = 0; 256 257 // Shadow 258 var bubbleShadow = this.bubbleShadow_ = document.createElement('DIV'); 259 bubbleShadow.style['position'] = 'absolute'; 260 261 // Hide the InfoBubble by default 262 bubble.style['display'] = bubbleShadow.style['display'] = 'none'; 263 264 bubble.appendChild(this.tabsContainer_); 265 bubble.appendChild(close); 266 bubble.appendChild(contentContainer); 267 arrow.appendChild(arrowOuter); 268 arrow.appendChild(arrowInner); 269 bubble.appendChild(arrow); 270 271 var stylesheet = document.createElement('style'); 272 stylesheet.setAttribute('type', 'text/css'); 273 274 /** 275 * The animation for the infobubble 276 * @type {string} 277 */ 278// this.animationName_ = '_ibani_' + Math.round(Math.random() * 10000); 279// 280// var css = '.' + this.animationName_ + '{-webkit-animation-name:' + 281// this.animationName_ + ';-webkit-animation-duration:0.5s;' + 282// '-webkit-animation-iteration-count:1;}' + 283// '@-webkit-keyframes ' + this.animationName_ + ' {from {' + 284// '-webkit-transform: scale(0)}50% {-webkit-transform: scale(1.2)}90% ' + 285// '{-webkit-transform: scale(0.95)}to {-webkit-transform: scale(1)}}'; 286// 287// stylesheet.textContent = css; 288// document.getElementsByTagName('head')[0].appendChild(stylesheet); 289}; 290 291 292/** 293 * Sets the background class name 294 * 295 * @param {string} className The class name to set. 296 */ 297InfoBubble.prototype.setBackgroundClassName = function(className) { 298 this.set('backgroundClassName', className); 299}; 300InfoBubble.prototype['setBackgroundClassName'] = 301 InfoBubble.prototype.setBackgroundClassName; 302 303 304/** 305 * changed MVC callback 306 */ 307InfoBubble.prototype.backgroundClassName_changed = function() { 308 this.content_.className = this.get('backgroundClassName'); 309}; 310InfoBubble.prototype['backgroundClassName_changed'] = 311 InfoBubble.prototype.backgroundClassName_changed; 312 313 314/** 315 * Sets the class of the tab 316 * 317 * @param {string} className the class name to set. 318 */ 319InfoBubble.prototype.setTabClassName = function(className) { 320 this.set('tabClassName', className); 321}; 322InfoBubble.prototype['setTabClassName'] = 323 InfoBubble.prototype.setTabClassName; 324 325 326/** 327 * tabClassName changed MVC callback 328 */ 329InfoBubble.prototype.tabClassName_changed = function() { 330 this.updateTabStyles_(); 331}; 332InfoBubble.prototype['tabClassName_changed'] = 333 InfoBubble.prototype.tabClassName_changed; 334 335 336/** 337 * Gets the style of the arrow 338 * 339 * @private 340 * @return {number} The style of the arrow. 341 */ 342InfoBubble.prototype.getArrowStyle_ = function() { 343 return parseInt(this.get('arrowStyle'), 10) || 0; 344}; 345 346 347/** 348 * Sets the style of the arrow 349 * 350 * @param {number} style The style of the arrow. 351 */ 352InfoBubble.prototype.setArrowStyle = function(style) { 353 this.set('arrowStyle', style); 354}; 355InfoBubble.prototype['setArrowStyle'] = 356 InfoBubble.prototype.setArrowStyle; 357 358 359/** 360 * Arrow style changed MVC callback 361 */ 362InfoBubble.prototype.arrowStyle_changed = function() { 363 this.arrowSize_changed(); 364};
365InfoBubble.prototype['arrowStyle_changed'] = 366 InfoBubble.prototype.arrowStyle_changed; 367 368 369/** 370 * Gets the size of the arrow 371 * 372 * @private 373 * @return {number} The size of the arrow. 374 */ 375InfoBubble.prototype.getArrowSize_ = function() { 376 return parseInt(this.get('arrowSize'), 10) || 0; 377}; 378 379 380/** 381 * Sets the size of the arrow 382 * 383 * @param {number} size The size of the arrow. 384 */ 385InfoBubble.prototype.setArrowSize = function(size) { 386 this.set('arrowSize', size); 387}; 388InfoBubble.prototype['setArrowSize'] = 389 InfoBubble.prototype.setArrowSize; 390 391 392/** 393 * Arrow size changed MVC callback 394 */ 395InfoBubble.prototype.arrowSize_changed = function() { 396 this.borderWidth_changed(); 397}; 398InfoBubble.prototype['arrowSize_changed'] = 399 InfoBubble.prototype.arrowSize_changed; 400 401 402/** 403 * Set the position of the InfoBubble arrow 404 * 405 * @param {number} pos The position to set. 406 */ 407InfoBubble.prototype.setArrowPosition = function(pos) { 408 this.set('arrowPosition', pos); 409}; 410InfoBubble.prototype['setArrowPosition'] = 411 InfoBubble.prototype.setArrowPosition; 412 413 414/** 415 * Get the position of the InfoBubble arrow 416 * 417 * @private 418 * @return {number} The position.. 419 */ 420InfoBubble.prototype.getArrowPosition_ = function() { 421 return parseInt(this.get('arrowPosition'), 10) || 0; 422}; 423 424 425/** 426 * arrowPosition changed MVC callback 427 */ 428InfoBubble.prototype.arrowPosition_changed = function() { 429 var pos = this.getArrowPosition_(); 430 this.arrowOuter_.style['left'] = this.arrowInner_.style['left'] = pos + '%'; 431 432 this.redraw_(); 433}; 434InfoBubble.prototype['arrowPosition_changed'] = 435 InfoBubble.prototype.arrowPosition_changed; 436 437 438/** 439 * Set the zIndex of the InfoBubble 440 * 441 * @param {number} zIndex The zIndex to set. 442 */ 443InfoBubble.prototype.setZIndex = function(zIndex) { 444 this.set('zIndex', zIndex); 445}; 446InfoBubble.prototype['setZIndex'] = InfoBubble.prototype.setZIndex; 447 448 449/** 450 * Get the zIndex of the InfoBubble 451 * 452 * @return {number} The zIndex to set. 453 */ 454InfoBubble.prototype.getZIndex = function() { 455 return parseInt(this.get('zIndex'), 10) || this.baseZIndex_; 456}; 457 458 459/** 460 * zIndex changed MVC callback 461 */ 462InfoBubble.prototype.zIndex_changed = function() { 463 var zIndex = this.getZIndex(); 464 465 this.bubble_.style['zIndex'] = this.baseZIndex_ = zIndex; 466 this.close_.style['zIndex'] = zIndex + 1; 467}; 468InfoBubble.prototype['zIndex_changed'] = InfoBubble.prototype.zIndex_changed; 469 470 471/** 472 * Set the style of the shadow 473 * 474 * @param {number} shadowStyle The style of the shadow. 475 */ 476InfoBubble.prototype.setShadowStyle = function(shadowStyle) { 477 this.set('shadowStyle', shadowStyle); 478}; 479InfoBubble.prototype['setShadowStyle'] = InfoBubble.prototype.setShadowStyle; 480 481 482/** 483 * Get the style of the shadow 484 * 485 * @private 486 * @return {number} The style of the shadow. 487 */ 488InfoBubble.prototype.getShadowStyle_ = function() { 489 return parseInt(this.get('shadowStyle'), 10) || 0; 490}; 491 492 493/** 494 * shadowStyle changed MVC callback 495 */ 496InfoBubble.prototype.shadowStyle_changed = function() { 497 var shadowStyle = this.getShadowStyle_(); 498 499 var display = ''; 500 var shadow = ''; 501 var backgroundColor = ''; 502 switch (shadowStyle) { 503 case 0: 504 display = 'none'; 505 break; 506 case 1: 507 shadow = '40px 15px 10px rgba(33,33,33,0.3)'; 508 backgroundColor = 'transparent'; 509 break; 510 case 2: 511 shadow = '0 0 2px rgba(33,33,33,0.3)'; 512 backgroundColor = 'rgba(33,33,33,0.35)'; 513 break; 514 } 515 this.bubbleShadow_.style['boxShadow'] = 516 this.bubbleShadow_.style['webkitBoxShadow'] = 517 this.bubbleShadow_.style['MozBoxShadow'] = shadow; 518 this.bubbleShadow_.style['backgroundColor'] = backgroundColor; 519 if (this.isOpen_) { 520 this.bubbleShadow_.style['display'] = display; 521 this.draw(); 522 } 523}; 524InfoBubble.prototype['shadowStyle_changed'] = 525 InfoBubble.prototype.shadowStyle_changed; 526 527 528/** 529 * Show the close button 530 */ 531InfoBubble.prototype.showCloseButton = function() { 532 this.set('hideCloseButton', false); 533}; 534InfoBubble.prototype['showCloseButton'] = InfoBubble.prototype.showCloseButton; 535 536 537/** 538 * Hide the close button 539 */ 540InfoBubble.prototype.hideCloseButton = function() { 541 this.set('hideCloseButton', true); 542}; 543InfoBubble.prototype['hideCloseButton'] = InfoBubble.prototype.hideCloseButton; 544 545 546/** 547 * hideCloseButton changed MVC callback 548 */ 549InfoBubble.prototype.hideCloseButton_changed = function() { 550 this.close_.style['display'] = this.get('hideCloseButton') ? 'none' : ''; 551};
552InfoBubble.prototype['hideCloseButton_changed'] = 553 InfoBubble.prototype.hideCloseButton_changed; 554 555 556/** 557 * Set the background color 558 * 559 * @param {string} color The color to set. 560 */ 561InfoBubble.prototype.setBackgroundColor = function(color) { 562 if (color) { 563 this.set('backgroundColor', color); 564 } 565}; 566InfoBubble.prototype['setBackgroundColor'] = 567 InfoBubble.prototype.setBackgroundColor; 568 569 570/** 571 * backgroundColor changed MVC callback 572 */ 573InfoBubble.prototype.backgroundColor_changed = function() { 574 var backgroundColor = this.get('backgroundColor'); 575 this.contentContainer_.style['backgroundColor'] = backgroundColor; 576 577 this.arrowInner_.style['borderColor'] = backgroundColor + 578 ' transparent transparent'; 579 this.updateTabStyles_(); 580}; 581InfoBubble.prototype['backgroundColor_changed'] = 582 InfoBubble.prototype.backgroundColor_changed; 583 584 585/** 586 * Set the border color 587 * 588 * @param {string} color The border color. 589 */ 590InfoBubble.prototype.setBorderColor = function(color) { 591 if (color) { 592 this.set('borderColor', color); 593 } 594}; 595InfoBubble.prototype['setBorderColor'] = InfoBubble.prototype.setBorderColor; 596 597 598/** 599 * borderColor changed MVC callback 600 */ 601InfoBubble.prototype.borderColor_changed = function() { 602 var borderColor = this.get('borderColor'); 603 604 var contentContainer = this.contentContainer_; 605 var arrowOuter = this.arrowOuter_; 606 contentContainer.style['borderColor'] = borderColor; 607 608 arrowOuter.style['borderColor'] = borderColor + 609 ' transparent transparent'; 610 611 contentContainer.style['borderStyle'] = 612 arrowOuter.style['borderStyle'] = 613 this.arrowInner_.style['borderStyle'] = 'solid'; 614 615 this.updateTabStyles_(); 616}; 617InfoBubble.prototype['borderColor_changed'] = 618 InfoBubble.prototype.borderColor_changed; 619 620 621/** 622 * Set the radius of the border 623 * 624 * @param {number} radius The radius of the border. 625 */ 626InfoBubble.prototype.setBorderRadius = function(radius) { 627 this.set('borderRadius', radius); 628}; 629InfoBubble.prototype['setBorderRadius'] = InfoBubble.prototype.setBorderRadius; 630 631 632/** 633 * Get the radius of the border 634 * 635 * @private 636 * @return {number} The radius of the border. 637 */ 638InfoBubble.prototype.getBorderRadius_ = function() { 639 return parseInt(this.get('borderRadius'), 10) || 0; 640}; 641 642 643/** 644 * borderRadius changed MVC callback 645 */ 646InfoBubble.prototype.borderRadius_changed = function() { 647 var borderRadius = this.getBorderRadius_(); 648 var borderWidth = this.getBorderWidth_(); 649 650 this.contentContainer_.style['borderRadius'] = 651 this.contentContainer_.style['MozBorderRadius'] = 652 this.contentContainer_.style['webkitBorderRadius'] = 653 this.bubbleShadow_.style['borderRadius'] = 654 this.bubbleShadow_.style['MozBorderRadius'] = 655 this.bubbleShadow_.style['webkitBorderRadius'] = this.px(borderRadius); 656 657 this.tabsContainer_.style['paddingLeft'] = 658 this.tabsContainer_.style['paddingRight'] = 659 this.px(borderRadius + borderWidth); 660 661 this.redraw_(); 662}; 663InfoBubble.prototype['borderRadius_changed'] = 664 InfoBubble.prototype.borderRadius_changed; 665 666 667/** 668 * Get the width of the border 669 * 670 * @private 671 * @return {number} width The width of the border. 672 */ 673InfoBubble.prototype.getBorderWidth_ = function() { 674 return parseInt(this.get('borderWidth'), 10) || 0; 675}; 676 677 678/** 679 * Set the width of the border 680 * 681 * @param {number} width The width of the border. 682 */ 683InfoBubble.prototype.setBorderWidth = function(width) { 684 this.set('borderWidth', width); 685}; 686InfoBubble.prototype['setBorderWidth'] = InfoBubble.prototype.setBorderWidth; 687 688 689/** 690 * borderWidth change MVC callback 691 */ 692InfoBubble.prototype.borderWidth_changed = function() { 693 var borderWidth = this.getBorderWidth_(); 694 695 this.contentContainer_.style['borderWidth'] = this.px(borderWidth); 696 this.tabsContainer_.style['top'] = this.px(borderWidth); 697 698 this.updateArrowStyle_(); 699 this.updateTabStyles_(); 700 this.borderRadius_changed(); 701 this.redraw_(); 702};
703InfoBubble.prototype['borderWidth_changed'] = 704 InfoBubble.prototype.borderWidth_changed; 705 706 707/** 708 * Update the arrow style 709 * @private 710 */ 711InfoBubble.prototype.updateArrowStyle_ = function() { 712 var borderWidth = this.getBorderWidth_(); 713 var arrowSize = this.getArrowSize_(); 714 var arrowStyle = this.getArrowStyle_(); 715 var arrowOuterSizePx = this.px(arrowSize); 716 var arrowInnerSizePx = this.px(Math.max(0, arrowSize - borderWidth)); 717 718 var outer = this.arrowOuter_; 719 var inner = this.arrowInner_; 720 721 this.arrow_.style['marginTop'] = this.px(-borderWidth); 722 outer.style['borderTopWidth'] = arrowOuterSizePx; 723 inner.style['borderTopWidth'] = arrowInnerSizePx; 724 725 // Full arrow or arrow pointing to the left 726 if (arrowStyle == 0 || arrowStyle == 1) { 727 outer.style['borderLeftWidth'] = arrowOuterSizePx; 728 inner.style['borderLeftWidth'] = arrowInnerSizePx; 729 } else { 730 outer.style['borderLeftWidth'] = inner.style['borderLeftWidth'] = 0; 731 } 732 733 // Full arrow or arrow pointing to the right 734 if (arrowStyle == 0 || arrowStyle == 2) { 735 outer.style['borderRightWidth'] = arrowOuterSizePx; 736 inner.style['borderRightWidth'] = arrowInnerSizePx; 737 } else { 738 outer.style['borderRightWidth'] = inner.style['borderRightWidth'] = 0; 739 } 740 741 if (arrowStyle < 2) { 742 outer.style['marginLeft'] = this.px(-(arrowSize)); 743 inner.style['marginLeft'] = this.px(-(arrowSize - borderWidth)); 744 } else { 745 outer.style['marginLeft'] = inner.style['marginLeft'] = 0; 746 } 747 748 // If there is no border then don't show thw outer arrow 749 if (borderWidth == 0) { 750 outer.style['display'] = 'none'; 751 } else { 752 outer.style['display'] = ''; 753 } 754}; 755 756 757/** 758 * Set the padding of the InfoBubble 759 * 760 * @param {number} padding The padding to apply. 761 */ 762InfoBubble.prototype.setPadding = function(padding) { 763 this.set('padding', padding); 764}; 765InfoBubble.prototype['setPadding'] = InfoBubble.prototype.setPadding; 766 767 768/** 769 * Set the padding of the InfoBubble 770 * 771 * @private 772 * @return {number} padding The padding to apply. 773 */ 774InfoBubble.prototype.getPadding_ = function() { 775 return parseInt(this.get('padding'), 10) || 0; 776}; 777 778 779/** 780 * padding changed MVC callback 781 */ 782InfoBubble.prototype.padding_changed = function() { 783 var padding = this.getPadding_(); 784 this.contentContainer_.style['padding'] = this.px(padding); 785 this.updateTabStyles_(); 786 787 this.redraw_(); 788}; 789InfoBubble.prototype['padding_changed'] = InfoBubble.prototype.padding_changed; 790 791 792/** 793 * Add px extention to the number 794 * 795 * @param {number} num The number to wrap. 796 * @return {string|number} A wrapped number. 797 */ 798InfoBubble.prototype.px = function(num) { 799 if (num) { 800 // 0 doesn't need to be wrapped 801 return num + 'px'; 802 } 803 return num; 804}; 805 806 807/** 808 * Add events to stop propagation 809 * @private 810 */ 811InfoBubble.prototype.addEvents_ = function() { 812 // We want to cancel all the events so they do not go to the map 813 var events = ['mousedown', 'mousemove', 'mouseover', 'mouseout', 'mouseup', 814 'mousewheel', 'DOMMouseScroll', 'touchstart', 'touchend', 'touchmove', 815 'dblclick', 'contextmenu']; 816 817 var bubble = this.bubble_; 818 this.listeners_ = []; 819 for (var i = 0, event; event = events[i]; i++) { 820 this.listeners_.push( 821 bubble.addEventListener(event, (e => { 822 e.cancelBubble = true; 823 if (e.stopPropagation) { 824 e.stopPropagation(); 825 } 826 })) 827 ); 828 } 829}; 830 831 832/** 833 * On Adding the InfoBubble to a map 834 * Implementing the OverlayView interface 835 */ 836InfoBubble.prototype.onAdd = function() { 837 if (!this.bubble_) { 838 this.buildDom_(); 839 } 840 841 this.addEvents_(); 842 843 var panes = this.getPanes(); 844 if (panes) { 845 panes.floatPane.appendChild(this.bubble_); 846 panes.floatShadow.appendChild(this.bubbleShadow_); 847 } 848}; 849InfoBubble.prototype['onAdd'] = InfoBubble.prototype.onAdd; 850 851 852/** 853 * Draw the InfoBubble 854 * Implementing the OverlayView interface 855 */ 856InfoBubble.prototype.draw = function() { 857 var projection = this.getProjection(); 858 859 if (!projection) { 860 // The map projection is not ready yet so do nothing 861 return; 862 } 863 864 var latLng = /** @type {google.maps.LatLng} */ (this.get('position')); 865 866 if (!latLng) { 867 this.close(); 868 return; 869 } 870 871 var tabHeight = 0; 872 873 if (this.activeTab_) { 874 tabHeight = this.activeTab_.offsetHeight; 875 } 876 877 var anchorHeight = this.getAnchorHeight_(); 878 var arrowSize = this.getArrowSize_(); 879 var arrowPosition = this.getArrowPosition_(); 880 881 arrowPosition = arrowPosition / 100; 882 883 var pos = projection.fromLatLngToDivPixel(latLng); 884 var width = this.contentContainer_.offsetWidth; 885 var height = this.bubble_.offsetHeight; 886 887 if (!width) { 888 return; 889 } 890 891 // Adjust for the height of the info bubble 892 var top = pos.y - (height + arrowSize); 893 894 if (anchorHeight) { 895 // If there is an anchor then include the height 896 top -= anchorHeight; 897 } 898 899 var left = pos.x - (width * arrowPosition); 900 901 this.bubble_.style['top'] = this.px(top); 902 this.bubble_.style['left'] = this.px(left); 903 904 var shadowStyle = parseInt(this.get('shadowStyle'), 10); 905 906 switch (shadowStyle) { 907 case 1: 908 // Shadow is behind 909 this.bubbleShadow_.style['top'] = this.px(top + tabHeight - 1); 910 this.bubbleShadow_.style['left'] = this.px(left); 911 this.bubbleShadow_.style['width'] = this.px(width); 912 this.bubbleShadow_.style['height'] = 913 this.px(this.contentContainer_.offsetHeight - arrowSize); 914 break; 915 case 2: 916 // Shadow is below 917 width = width * 0.8; 918 if (anchorHeight) { 919 this.bubbleShadow_.style['top'] = this.px(pos.y); 920 } else { 921 this.bubbleShadow_.style['top'] = this.px(pos.y + arrowSize); 922 } 923 this.bubbleShadow_.style['left'] = this.px(pos.x - width * arrowPosition); 924 925 this.bubbleShadow_.style['width'] = this.px(width); 926 this.bubbleShadow_.style['height'] = this.px(2); 927 break; 928 } 929}; 930InfoBubble.prototype['draw'] = InfoBubble.prototype.draw; 931 932 933/** 934 * Removing the InfoBubble from a map 935 */ 936InfoBubble.prototype.onRemove = function() { 937 if (this.bubble_ && this.bubble_.parentNode) { 938 this.bubble_.parentNode.removeChild(this.bubble_); 939 } 940 if (this.bubbleShadow_ && this.bubbleShadow_.parentNode) { 941 this.bubbleShadow_.parentNode.removeChild(this.bubbleShadow_); 942 } 943 944 for (var i = 0, listener; listener = this.listeners_[i]; i++) { 945 google.maps.event.removeListener(listener); 946 } 947}; 948InfoBubble.prototype['onRemove'] = InfoBubble.prototype.onRemove; 949 950 951/** 952 * Is the InfoBubble open 953 * 954 * @return {boolean} If the InfoBubble is open. 955 */ 956InfoBubble.prototype.isOpen = function() { 957 return this.isOpen_; 958}; 959InfoBubble.prototype['isOpen'] = InfoBubble.prototype.isOpen; 960 961 962/** 963 * Close the InfoBubble 964 */ 965InfoBubble.prototype.close = function() { 966 if (this.bubble_) { 967 this.bubble_.style['display'] = 'none'; 968 // Remove the animation so we next time it opens it will animate again 969 this.bubble_.className = 970 this.bubble_.className.replace(this.animationName_, ''); 971 } 972 973 if (this.bubbleShadow_) { 974 this.bubbleShadow_.style['display'] = 'none'; 975 this.bubbleShadow_.className = 976 this.bubbleShadow_.className.replace(this.animationName_, ''); 977 } 978 this.isOpen_ = false; 979}; 980InfoBubble.prototype['close'] = InfoBubble.prototype.close; 981 982 983/** 984 * Open the InfoBubble (asynchronous). 985 * 986 * @param {google.maps.Map=} opt_map Optional map to open on. 987 * @param {google.maps.MVCObject=} opt_anchor Optional anchor to position at. 988 */ 989InfoBubble.prototype.open = function(opt_map, opt_anchor) { 990 var that = this; 991 window.setTimeout(function() { 992 that.open_(opt_map, opt_anchor); 993 }, 0); 994}; 995 996/** 997 * Open the InfoBubble 998 * @private 999 * @param {google.maps.Map=} opt_map Optional map to open on. 1000 * @param {google.maps.MVCObject=} opt_anchor Optional anchor to position at. 1001 */ 1002InfoBubble.prototype.open_ = function(opt_map, opt_anchor) { 1003 this.updateContent_(); 1004 1005 if (opt_map) { 1006 this.setMap(opt_map); 1007 } 1008 1009 if (opt_anchor) {
1010 this.set('anchor', opt_anchor); 1011 this.bindTo('anchorPoint', opt_anchor); 1012 this.bindTo('position', opt_anchor); 1013 } 1014 1015 // Show the bubble and the show 1016 this.bubble_.style['display'] = this.bubbleShadow_.style['display'] = ''; 1017 var animation = !this.get('disableAnimation'); 1018 1019 /*if (animation) { 1020 // Add the animation 1021 this.bubble_.className += ' ' + this.animationName_; 1022 this.bubbleShadow_.className += ' ' + this.animationName_; 1023 }*/ 1024 1025 this.redraw_(); 1026 this.isOpen_ = true; 1027 1028 var pan = !this.get('disableAutoPan'); 1029 if (pan) { 1030 var that = this; 1031 window.setTimeout(function() { 1032 // Pan into view, done in a time out to make it feel nicer :) 1033 that.panToView(); 1034 }, 200); 1035 } 1036}; 1037InfoBubble.prototype['open'] = InfoBubble.prototype.open; 1038 1039 1040/** 1041 * Set the position of the InfoBubble 1042 * 1043 * @param {google.maps.LatLng} position The position to set. 1044 */ 1045InfoBubble.prototype.setPosition = function(position) { 1046 if (position) { 1047 this.set('position', position); 1048 } 1049}; 1050InfoBubble.prototype['setPosition'] = InfoBubble.prototype.setPosition; 1051 1052 1053/** 1054 * Returns the position of the InfoBubble 1055 * 1056 * @return {google.maps.LatLng} the position. 1057 */ 1058InfoBubble.prototype.getPosition = function() { 1059 return /** @type {google.maps.LatLng} */ (this.get('position')); 1060}; 1061InfoBubble.prototype['getPosition'] = InfoBubble.prototype.getPosition; 1062 1063 1064/** 1065 * position changed MVC callback 1066 */ 1067InfoBubble.prototype.position_changed = function() { 1068 this.draw(); 1069}; 1070InfoBubble.prototype['position_changed'] = 1071 InfoBubble.prototype.position_changed; 1072 1073 1074/** 1075 * Pan the InfoBubble into view 1076 */ 1077InfoBubble.prototype.panToView = function() { 1078 var projection = this.getProjection(); 1079 1080 if (!projection) { 1081 // The map projection is not ready yet so do nothing 1082 return; 1083 } 1084 1085 if (!this.bubble_) { 1086 // No Bubble yet so do nothing 1087 return; 1088 } 1089 1090 var anchorHeight = this.getAnchorHeight_(); 1091 var height = this.bubble_.offsetHeight + anchorHeight; 1092 var map = this.get('map'); 1093 var mapDiv = map.getDiv(); 1094 var mapHeight = mapDiv.offsetHeight; 1095 1096 var latLng = this.getPosition(); 1097 var centerPos = projection.fromLatLngToContainerPixel(map.getCenter()); 1098 var pos = projection.fromLatLngToContainerPixel(latLng); 1099 1100 // Find out how much space at the top is free 1101 var spaceTop = centerPos.y - height; 1102 1103 // Fine out how much space at the bottom is free 1104 var spaceBottom = mapHeight - centerPos.y; 1105 1106 var needsTop = spaceTop < 0; 1107 var deltaY = 0; 1108 1109 if (needsTop) { 1110 spaceTop *= -1; 1111 deltaY = (spaceTop + spaceBottom) / 2; 1112 } 1113 1114 pos.y -= deltaY; 1115 latLng = projection.fromContainerPixelToLatLng(pos); 1116 1117 if (map.getCenter() != latLng) { 1118 map.panTo(latLng); 1119 } 1120}; 1121InfoBubble.prototype['panToView'] = InfoBubble.prototype.panToView; 1122 1123 1124/** 1125 * Converts a HTML string to a document fragment. 1126 * 1127 * @param {string} htmlString The HTML string to convert. 1128 * @return {Node} A HTML document fragment. 1129 * @private 1130 */ 1131InfoBubble.prototype.htmlToDocumentFragment_ = function(htmlString) { 1132 htmlString = htmlString.replace(/^\s*([\S\s]*)\b\s*$/, '$1'); 1133 var tempDiv = document.createElement('DIV'); 1134 tempDiv.innerHTML = htmlString; 1135 if (tempDiv.childNodes.length == 1) {
1136 return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild)); 1137 } else { 1138 var fragment = document.createDocumentFragment(); 1139 while (tempDiv.firstChild) { 1140 fragment.appendChild(tempDiv.firstChild); 1141 } 1142 return fragment; 1143 } 1144}; 1145 1146 1147/** 1148 * Removes all children from the node. 1149 * 1150 * @param {Node} node The node to remove all children from. 1151 * @private 1152 */ 1153InfoBubble.prototype.removeChildren_ = function(node) { 1154 if (!node) { 1155 return; 1156 } 1157 1158 var child; 1159 while (child = node.firstChild) { 1160 node.removeChild(child); 1161 } 1162}; 1163 1164 1165/** 1166 * Sets the content of the infobubble. 1167 * 1168 * @param {string|Node} content The content to set. 1169 */ 1170InfoBubble.prototype.setContent = function(content) { 1171 this.set('content', content); 1172}; 1173InfoBubble.prototype['setContent'] = InfoBubble.prototype.setContent; 1174 1175 1176/** 1177 * Get the content of the infobubble. 1178 * 1179 * @return {string|Node} The marker content. 1180 */ 1181InfoBubble.prototype.getContent = function() { 1182 return /** @type {Node|string} */ (this.get('content')); 1183}; 1184InfoBubble.prototype['getContent'] = InfoBubble.prototype.getContent; 1185 1186 1187/** 1188 * Sets the marker content and adds loading events to images 1189 */ 1190InfoBubble.prototype.updateContent_ = function() { 1191 if (!this.content_) { 1192 // The Content area doesnt exist. 1193 return; 1194 } 1195 1196 this.removeChildren_(this.content_); 1197 var content = this.getContent(); 1198 if (content) { 1199 if (typeof content == 'string') { 1200 content = this.htmlToDocumentFragment_(content); 1201 } 1202 this.content_.appendChild(content); 1203 1204 var that = this; 1205 var images = this.content_.getElementsByTagName('IMG'); 1206 for (var i = 0, image; image = images[i]; i++) { 1207 // Because we don't know the size of an image till it loads, add a 1208 // listener to the image load so the marker can resize and reposition 1209 // itself to be the correct height. 1210 image.addEventListener('load', (e => { 1211 that.imageLoaded_(); 1212 })); 1213 } 1214 google.maps.event.trigger(this, 'domready'); 1215 } 1216 this.redraw_(); 1217}; 1218 1219/** 1220 * Image loaded 1221 * @private 1222 */ 1223InfoBubble.prototype.imageLoaded_ = function() { 1224 var pan = !this.get('disableAutoPan'); 1225 this.redraw_(); 1226 if (pan && (this.tabs_.length == 0 || this.activeTab_.index == 0)) { 1227 this.panToView(); 1228 } 1229}; 1230 1231/** 1232 * Updates the styles of the tabs 1233 * @private 1234 */ 1235InfoBubble.prototype.updateTabStyles_ = function() { 1236 if (this.tabs_ && this.tabs_.length) { 1237 for (var i = 0, tab; tab = this.tabs_[i]; i++) { 1238 this.setTabStyle_(tab.tab); 1239 } 1240 this.activeTab_.style['zIndex'] = this.baseZIndex_; 1241 var borderWidth = this.getBorderWidth_(); 1242 var padding = this.getPadding_() / 2; 1243 this.activeTab_.style['borderBottomWidth'] = 0; 1244 this.activeTab_.style['paddingBottom'] = this.px(padding + borderWidth); 1245 } 1246}; 1247 1248 1249/** 1250 * Sets the style of a tab 1251 * @private 1252 * @param {Element} tab The tab to style. 1253 */ 1254InfoBubble.prototype.setTabStyle_ = function(tab) { 1255 var backgroundColor = this.get('backgroundColor'); 1256 var borderColor = this.get('borderColor'); 1257 var borderRadius = this.getBorderRadius_(); 1258 var borderWidth = this.getBorderWidth_(); 1259 var padding = this.getPadding_(); 1260 1261 var marginRight = this.px(-(Math.max(padding, borderRadius))); 1262 var borderRadiusPx = this.px(borderRadius); 1263 1264 var index = this.baseZIndex_; 1265 if (tab.index) { 1266 index -= tab.index; 1267 } 1268 1269 // The styles for the tab 1270 var styles = { 1271 'cssFloat': 'left', 1272 'position': 'relative', 1273 'cursor': 'pointer', 1274 'backgroundColor': backgroundColor, 1275 'border': this.px(borderWidth) + ' solid ' + borderColor, 1276 'padding': this.px(padding / 2) + ' ' + this.px(padding), 1277 'marginRight': marginRight, 1278 'whiteSpace': 'nowrap', 1279 'borderRadiusTopLeft': borderRadiusPx, 1280 'MozBorderRadiusTopleft': borderRadiusPx, 1281 'webkitBorderTopLeftRadius': borderRadiusPx, 1282 'borderRadiusTopRight': borderRadiusPx, 1283 'MozBorderRadiusTopright': borderRadiusPx, 1284 'webkitBorderTopRightRadius': borderRadiusPx, 1285 'zIndex': index, 1286 'display': 'inline' 1287 }; 1288 1289 for (var style in styles) { 1290 tab.style[style] = styles[style]; 1291 } 1292 1293 var className = this.get('tabClassName'); 1294 if (className != undefined) { 1295 tab.className += ' ' + className; 1296 } 1297}; 1298 1299 1300/** 1301 * Add user actions to a tab 1302 * @private 1303 * @param {Object} tab The tab to add the actions to. 1304 */ 1305InfoBubble.prototype.addTabActions_ = function(tab) { 1306 var that = this; 1307 tab.listener_ = tab.addEventListener('click', (e => { 1308 that.setTabActive_(this); 1309 })); 1310}; 1311 1312 1313/** 1314 * Set a tab at a index to be active 1315 * 1316 * @param {number} index The index of the tab. 1317 */ 1318InfoBubble.prototype.setTabActive = function(index) { 1319 var tab = this.tabs_[index - 1]; 1320 1321 if (tab) { 1322 this.setTabActive_(tab.tab); 1323 } 1324}; 1325InfoBubble.prototype['setTabActive'] = InfoBubble.prototype.setTabActive; 1326 1327 1328/** 1329 * Set a tab to be active 1330 * @private 1331 * @param {Object} tab The tab to set active. 1332 */ 1333InfoBubble.prototype.setTabActive_ = function(tab) { 1334 if (!tab) { 1335 this.setContent(''); 1336 this.updateContent_(); 1337 return; 1338 } 1339 1340 var padding = this.getPadding_() / 2; 1341 var borderWidth = this.getBorderWidth_(); 1342 1343 if (this.activeTab_) { 1344 var activeTab = this.activeTab_; 1345 activeTab.style['zIndex'] = this.baseZIndex_ - activeTab.index; 1346 activeTab.style['paddingBottom'] = this.px(padding); 1347 activeTab.style['borderBottomWidth'] = this.px(borderWidth);
1348 } 1349 1350 tab.style['zIndex'] = this.baseZIndex_; 1351 tab.style['borderBottomWidth'] = 0; 1352 tab.style['marginBottomWidth'] = '-10px'; 1353 tab.style['paddingBottom'] = this.px(padding + borderWidth); 1354 1355 this.setContent(this.tabs_[tab.index].content); 1356 this.updateContent_(); 1357 1358 this.activeTab_ = tab; 1359 1360 this.redraw_(); 1361}; 1362 1363 1364/** 1365 * Set the max width of the InfoBubble 1366 * 1367 * @param {number} width The max width. 1368 */ 1369InfoBubble.prototype.setMaxWidth = function(width) { 1370 this.set('maxWidth', width); 1371}; 1372InfoBubble.prototype['setMaxWidth'] = InfoBubble.prototype.setMaxWidth; 1373 1374 1375/** 1376 * maxWidth changed MVC callback 1377 */ 1378InfoBubble.prototype.maxWidth_changed = function() { 1379 this.redraw_(); 1380}; 1381InfoBubble.prototype['maxWidth_changed'] = 1382 InfoBubble.prototype.maxWidth_changed; 1383 1384 1385/** 1386 * Set the max height of the InfoBubble 1387 * 1388 * @param {number} height The max height. 1389 */ 1390InfoBubble.prototype.setMaxHeight = function(height) { 1391 this.set('maxHeight', height); 1392}; 1393InfoBubble.prototype['setMaxHeight'] = InfoBubble.prototype.setMaxHeight; 1394 1395 1396/** 1397 * maxHeight changed MVC callback 1398 */ 1399InfoBubble.prototype.maxHeight_changed = function() { 1400 this.redraw_(); 1401}; 1402InfoBubble.prototype['maxHeight_changed'] = 1403 InfoBubble.prototype.maxHeight_changed; 1404 1405 1406/** 1407 * Set the min width of the InfoBubble 1408 * 1409 * @param {number} width The min width. 1410 */ 1411InfoBubble.prototype.setMinWidth = function(width) { 1412 this.set('minWidth', width); 1413}; 1414InfoBubble.prototype['setMinWidth'] = InfoBubble.prototype.setMinWidth; 1415 1416 1417/** 1418 * minWidth changed MVC callback 1419 */ 1420InfoBubble.prototype.minWidth_changed = function() { 1421 this.redraw_(); 1422}; 1423InfoBubble.prototype['minWidth_changed'] = 1424 InfoBubble.prototype.minWidth_changed; 1425 1426 1427/** 1428 * Set the min height of the InfoBubble 1429 * 1430 * @param {number} height The min height. 1431 */ 1432InfoBubble.prototype.setMinHeight = function(height) { 1433 this.set('minHeight', height); 1434}; 1435InfoBubble.prototype['setMinHeight'] = InfoBubble.prototype.setMinHeight; 1436 1437 1438/** 1439 * minHeight changed MVC callback 1440 */ 1441InfoBubble.prototype.minHeight_changed = function() { 1442 this.redraw_(); 1443}; 1444InfoBubble.prototype['minHeight_changed'] = 1445 InfoBubble.prototype.minHeight_changed; 1446 1447 1448/** 1449 * Add a tab 1450 * 1451 * @param {string} label The label of the tab. 1452 * @param {string|Element} content The content of the tab. 1453 */ 1454InfoBubble.prototype.addTab = function(label, content) { 1455 var tab = document.createElement('DIV'); 1456 tab.innerHTML = label; 1457 1458 this.setTabStyle_(tab); 1459 this.addTabActions_(tab); 1460 1461 this.tabsContainer_.appendChild(tab); 1462 1463 this.tabs_.push({ 1464 label: label, 1465 content: content, 1466 tab: tab 1467 }); 1468 1469 tab.index = this.tabs_.length - 1; 1470 tab.style['zIndex'] = this.baseZIndex_ - tab.index; 1471 1472 if (!this.activeTab_) { 1473 this.setTabActive_(tab); 1474 } 1475 1476 tab.className = tab.className + ' ' + this.animationName_; 1477 1478 this.redraw_(); 1479}; 1480InfoBubble.prototype['addTab'] = InfoBubble.prototype.addTab; 1481 1482/** 1483 * Update a tab at a speicifc index 1484 * 1485 * @param {number} index The index of the tab. 1486 * @param {?string} opt_label The label to change to. 1487 * @param {?string} opt_content The content to update to. 1488 */ 1489InfoBubble.prototype.updateTab = function(index, opt_label, opt_content) { 1490 if (!this.tabs_.length || index < 0 || index >= this.tabs_.length) { 1491 return; 1492 } 1493 1494 var tab = this.tabs_[index]; 1495 if (opt_label != undefined) { 1496 tab.tab.innerHTML = tab.label = opt_label; 1497 } 1498 1499 if (opt_content != undefined) { 1500 tab.content = opt_content; 1501 } 1502 1503 if (this.activeTab_ == tab.tab) { 1504 this.setContent(tab.content); 1505 this.updateContent_(); 1506 } 1507 this.redraw_(); 1508}; 1509InfoBubble.prototype['updateTab'] = InfoBubble.prototype.updateTab; 1510 1511 1512/** 1513 * Remove a tab at a specific index 1514 * 1515 * @param {number} index The index of the tab to remove. 1516 */ 1517InfoBubble.prototype.removeTab = function(index) { 1518 if (!this.tabs_.length || index < 0 || index >= this.tabs_.length) { 1519 return; 1520 } 1521 1522 var tab = this.tabs_[index]; 1523 tab.tab.parentNode.removeChild(tab.tab); 1524 1525 google.maps.event.removeListener(tab.tab.listener_); 1526 1527 this.tabs_.splice(index, 1); 1528 1529 delete tab; 1530 1531 for (var i = 0, t; t = this.tabs_[i]; i++) { 1532 t.tab.index = i; 1533 } 1534 1535 if (tab.tab == this.activeTab_) { 1536 // Removing the current active tab 1537 if (this.tabs_[index]) { 1538 // Show the tab to the right 1539 this.activeTab_ = this.tabs_[index].tab; 1540 } else if (this.tabs_[index - 1]) { 1541 // Show a tab to the left 1542 this.activeTab_ = this.tabs_[index - 1].tab; 1543 } else { 1544 // No tabs left to sho 1545 this.activeTab_ = undefined; 1546 } 1547 1548 this.setTabActive_(this.activeTab_); 1549 } 1550 1551 this.redraw_(); 1552}; 1553InfoBubble.prototype['removeTab'] = InfoBubble.prototype.removeTab; 1554 1555 1556/** 1557 * Get the size of an element 1558 * @private 1559 * @param {Node|string} element The element to size. 1560 * @param {number=} opt_maxWidth Optional max width of the element. 1561 * @param {number=} opt_maxHeight Optional max height of the element. 1562 * @return {google.maps.Size} The size of the element. 1563 */ 1564InfoBubble.prototype.getElementSize_ = function(element, opt_maxWidth, 1565 opt_maxHeight) { 1566 var sizer = document.createElement('DIV'); 1567 sizer.style['display'] = 'inline'; 1568 sizer.style['position'] = 'absolute'; 1569 sizer.style['visibility'] = 'hidden'; 1570 1571 if (typeof element == 'string') { 1572 sizer.innerHTML = element; 1573 } else { 1574 sizer.appendChild(element.cloneNode(true)); 1575 } 1576 1577 document.body.appendChild(sizer); 1578 var size = new google.maps.Size(sizer.offsetWidth, sizer.offsetHeight); 1579 1580 // If the width is bigger than the max width then set the width and size again 1581 if (opt_maxWidth && size.width > opt_maxWidth) { 1582 sizer.style['width'] = this.px(opt_maxWidth); 1583 size = new google.maps.Size(sizer.offsetWidth, sizer.offsetHeight); 1584 } 1585 1586 // If the height is bigger than the max height then set the height and size 1587 // again 1588 if (opt_maxHeight && size.height > opt_maxHeight) { 1589 sizer.style['height'] = this.px(opt_maxHeight); 1590 size = new google.maps.Size(sizer.offsetWidth, sizer.offsetHeight); 1591 } 1592 1593 document.body.removeChild(sizer); 1594 delete sizer; 1595 return size; 1596}; 1597 1598 1599/** 1600 * Redraw the InfoBubble 1601 * @private 1602 */ 1603InfoBubble.prototype.redraw_ = function() { 1604 this.figureOutSize_(); 1605 this.positionCloseButton_(); 1606 this.draw(); 1607}; 1608 1609 1610/** 1611 * Figure out the optimum size of the InfoBubble 1612 * @private 1613 */ 1614InfoBubble.prototype.figureOutSize_ = function() { 1615 var map = this.get('map'); 1616 1617 if (!map) { 1618 return; 1619 } 1620 1621 var padding = this.getPadding_(); 1622 var borderWidth = this.getBorderWidth_(); 1623 var borderRadius = this.getBorderRadius_(); 1624 var arrowSize = this.getArrowSize_(); 1625 1626 var mapDiv = map.getDiv(); 1627 var gutter = arrowSize * 2; 1628 var mapWidth = mapDiv.offsetWidth - gutter; 1629 var mapHeight = mapDiv.offsetHeight - gutter - this.getAnchorHeight_(); 1630 var tabHeight = 0; 1631 var width = /** @type {number} */ (this.get('minWidth') || 0); 1632 var height = /** @type {number} */ (this.get('minHeight') || 0); 1633 var maxWidth = /** @type {number} */ (this.get('maxWidth') || 0); 1634 var maxHeight = /** @type {number} */ (this.get('maxHeight') || 0); 1635 1636 maxWidth = Math.min(mapWidth, maxWidth); 1637 maxHeight = Math.min(mapHeight, maxHeight); 1638 1639 var tabWidth = 0; 1640 if (this.tabs_.length) { 1641 // If there are tabs then you need to check the size of each tab's content 1642 for (var i = 0, tab; tab = this.tabs_[i]; i++) { 1643 var tabSize = this.getElementSize_(tab.tab, maxWidth, maxHeight); 1644 var contentSize = this.getElementSize_(tab.content, maxWidth, maxHeight); 1645 1646 if (width < tabSize.width) { 1647 width = tabSize.width; 1648 } 1649 1650 // Add up all the tab widths because they might end up being wider than 1651 // the content 1652 tabWidth += tabSize.width; 1653 1654 if (height < tabSize.height) { 1655 height = tabSize.height; 1656 } 1657 1658 if (tabSize.height > tabHeight) { 1659 tabHeight = tabSize.height; 1660 } 1661 1662 if (width < contentSize.width) { 1663 width = contentSize.width; 1664 } 1665 1666 if (height < contentSize.height) { 1667 height = contentSize.height; 1668 } 1669 } 1670 } else { 1671 var content = /** @type {string|Node} */ (this.get('content')); 1672 if (typeof content == 'string') { 1673 content = this.htmlToDocumentFragment_(content); 1674 } 1675 if (content) { 1676 var contentSize = this.getElementSize_(content, maxWidth, maxHeight); 1677 1678 if (width < contentSize.width) { 1679 width = contentSize.width; 1680 } 1681 1682 if (height < contentSize.height) { 1683 height = contentSize.height; 1684 } 1685 } 1686 } 1687 1688 if (maxWidth) { 1689 width = Math.min(width, maxWidth); 1690 } 1691 1692 if (maxHeight) { 1693 height = Math.min(height, maxHeight); 1694 } 1695 1696 width = Math.max(width, tabWidth);
1697 1698 if (width == tabWidth) { 1699 width = width + 2 * padding; 1700 } 1701 1702 arrowSize = arrowSize * 2; 1703 width = Math.max(width, arrowSize); 1704 1705 // Maybe add this as a option so they can go bigger than the map if the user 1706 // wants 1707 if (width > mapWidth) { 1708 width = mapWidth; 1709 } 1710 1711 if (height > mapHeight) { 1712 height = mapHeight - tabHeight; 1713 } 1714 1715 if (this.tabsContainer_) { 1716 this.tabHeight_ = tabHeight; 1717 this.tabsContainer_.style['width'] = this.px(tabWidth); 1718 } 1719 1720 this.contentContainer_.style['width'] = this.px(width); 1721 this.contentContainer_.style['height'] = this.px(height); 1722}; 1723 1724 1725/** 1726 * Get the height of the anchor 1727 * 1728 * This function is a hack for now and doesn't really work that good, need to 1729 * wait for pixelBounds to be correctly exposed. 1730 * @private 1731 * @return {number} The height of the anchor. 1732 */ 1733InfoBubble.prototype.getAnchorHeight_ = function() { 1734 var anchor = this.get('anchor'); 1735 if (anchor) { 1736 var anchorPoint = /** @type google.maps.Point */(this.get('anchorPoint')); 1737 1738 if (anchorPoint) { 1739 return -1 * anchorPoint.y; 1740 } 1741 } 1742 return 0; 1743}; 1744 1745InfoBubble.prototype.anchorPoint_changed = function() { 1746 this.draw(); 1747}; 1748InfoBubble.prototype['anchorPoint_changed'] = InfoBubble.prototype.anchorPoint_changed; 1749 1750 1751/** 1752 * Position the close button in the right spot. 1753 * @private 1754 */ 1755InfoBubble.prototype.positionCloseButton_ = function() { 1756 var br = this.getBorderRadius_(); 1757 var bw = this.getBorderWidth_(); 1758 1759 var right = 2; 1760 var top = 2; 1761 1762 if (this.tabs_.length && this.tabHeight_) { 1763 top += this.tabHeight_; 1764 } 1765 1766 top += bw; 1767 right += bw; 1768 1769 var c = this.contentContainer_; 1770 if (c && c.clientHeight < c.scrollHeight) { 1771 // If there are scrollbars then move the cross in so it is not over 1772 // scrollbar 1773 right += 15; 1774 } 1775 1776 this.close_.style['right'] = this.px(right); 1777 this.close_.style['top'] = this.px(top); 1778};
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.