1// MODIFIED!!! DO NOT UPDATE WITH NEW VERSION!!! 2 3 4// ==ClosureCompiler== 5// @compilation_level ADVANCED_OPTIMIZATIONS 6// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3.js 7// @output_wrapper (function() {%output%})(); 8// ==/ClosureCompiler== 9 10/** 11 * @license 12 * Copyright 2013 Google Inc. All Rights Reserved. 13 * 14 * Licensed under the Apache License, Version 2.0 (the "License"); 15 * you may not use this file except in compliance with the License. 16 * You may obtain a copy of the License at 17 * 18 * http://www.apache.org/licenses/LICENSE-2.0 19 * 20 * Unless required by applicable law or agreed to in writing, software 21 * distributed under the License is distributed on an "AS IS" BASIS, 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 * See the License for the specific language governing permissions and 24 * limitations under the License. 25 */ 26 27/** 28 * A RichMarker that allows any HTML/DOM to be added to a map and be draggable. 29 * 30 * @param {Object.<string, *>=} opt_options Optional properties to set. 31 * @extends {google.maps.OverlayView} 32 * @constructor 33 */ 34function RichMarker(opt_options) { 35 var options = opt_options || {}; 36 37 /** 38 * @type {boolean} 39 * @private 40 */ 41 this.ready_ = false; 42 43 /** 44 * @type {boolean} 45 * @private 46 */ 47 this.dragging_ = false; 48 49 if (opt_options['visible'] == undefined) { 50 opt_options['visible'] = true; 51 } 52 53 if (opt_options['shadow'] == undefined) { 54 opt_options['shadow'] = '7px -3px 5px rgba(88,88,88,0.7)'; 55 } 56 57 if (opt_options['anchor'] == undefined) { 58 opt_options['anchor'] = RichMarkerPosition['BOTTOM']; 59 } 60 61 this.setValues(options); 62} 63RichMarker.prototype = new google.maps.OverlayView(); 64window['RichMarker'] = RichMarker; 65 66 67/** 68 * Returns the current visibility state of the marker. 69 * 70 * @return {boolean} The visiblity of the marker. 71 */ 72RichMarker.prototype.getVisible = function() { 73 return /** @type {boolean} */ (this.get('visible')); 74}; 75RichMarker.prototype['getVisible'] = RichMarker.prototype.getVisible; 76 77 78/** 79 * Sets the visiblility state of the marker. 80 * 81 * @param {boolean} visible The visiblilty of the marker. 82 */ 83RichMarker.prototype.setVisible = function(visible) { 84 this.set('visible', visible); 85}; 86RichMarker.prototype['setVisible'] = RichMarker.prototype.setVisible; 87 88 89/** 90 * The visible changed event. 91 */ 92RichMarker.prototype.visible_changed = function() { 93 if (this.ready_) { 94 this.markerWrapper_.style['display'] = this.getVisible() ? '' : 'none'; 95 this.draw(); 96 } 97}; 98RichMarker.prototype['visible_changed'] = RichMarker.prototype.visible_changed; 99 100 101/** 102 * Sets the marker to be flat. 103 * 104 * @param {boolean} flat If the marker is to be flat or not. 105 */ 106RichMarker.prototype.setFlat = function(flat) { 107 this.set('flat', !!flat); 108}; 109RichMarker.prototype['setFlat'] = RichMarker.prototype.setFlat; 110 111 112/** 113 * If the makrer is flat or not. 114 * 115 * @return {boolean} True the marker is flat. 116 */ 117RichMarker.prototype.getFlat = function() { 118 return /** @type {boolean} */ (this.get('flat'));
119}; 120RichMarker.prototype['getFlat'] = RichMarker.prototype.getFlat; 121 122 123/** 124 * Get the width of the marker. 125 * 126 * @return {Number} The width of the marker. 127 */ 128RichMarker.prototype.getWidth = function() { 129 return /** @type {Number} */ (this.get('width')); 130}; 131RichMarker.prototype['getWidth'] = RichMarker.prototype.getWidth; 132 133 134/** 135 * Get the height of the marker. 136 * 137 * @return {Number} The height of the marker. 138 */ 139RichMarker.prototype.getHeight = function() { 140 return /** @type {Number} */ (this.get('height')); 141}; 142RichMarker.prototype['getHeight'] = RichMarker.prototype.getHeight; 143 144 145/** 146 * Sets the marker's box shadow. 147 * 148 * @param {string} shadow The box shadow to set. 149 */ 150RichMarker.prototype.setShadow = function(shadow) { 151 this.set('shadow', shadow); 152 this.flat_changed(); 153}; 154RichMarker.prototype['setShadow'] = RichMarker.prototype.setShadow; 155 156 157/** 158 * Gets the marker's box shadow. 159 * 160 * @return {string} The box shadow. 161 */ 162RichMarker.prototype.getShadow = function() { 163 return /** @type {string} */ (this.get('shadow')); 164}; 165RichMarker.prototype['getShadow'] = RichMarker.prototype.getShadow; 166 167 168/** 169 * Flat changed event. 170 */ 171RichMarker.prototype.flat_changed = function() { 172 if (!this.ready_) { 173 return; 174 } 175 176 this.markerWrapper_.style['boxShadow'] = 177 this.markerWrapper_.style['webkitBoxShadow'] = 178 this.markerWrapper_.style['MozBoxShadow'] = 179 this.getFlat() ? '' : this.getShadow(); 180}; 181RichMarker.prototype['flat_changed'] = RichMarker.prototype.flat_changed; 182 183 184/** 185 * Sets the zIndex of the marker. 186 * 187 * @param {Number} index The index to set. 188 */ 189RichMarker.prototype.setZIndex = function(index) { 190 this.set('zIndex', index); 191}; 192RichMarker.prototype['setZIndex'] = RichMarker.prototype.setZIndex; 193 194 195/** 196 * Gets the zIndex of the marker. 197 * 198 * @return {Number} The zIndex of the marker. 199 */ 200RichMarker.prototype.getZIndex = function() { 201 return /** @type {Number} */ (this.get('zIndex')); 202}; 203RichMarker.prototype['getZIndex'] = RichMarker.prototype.getZIndex; 204 205 206/** 207 * zIndex changed event. 208 */ 209RichMarker.prototype.zIndex_changed = function() { 210 if (this.getZIndex() && this.ready_) { 211 this.markerWrapper_.style.zIndex = this.getZIndex(); 212 } 213}; 214RichMarker.prototype['zIndex_changed'] = RichMarker.prototype.zIndex_changed; 215 216/** 217 * Whether the marker is draggable or not. 218 * 219 * @return {boolean} True if the marker is draggable. 220 */ 221RichMarker.prototype.getDraggable = function() { 222 return /** @type {boolean} */ (this.get('draggable')); 223}; 224RichMarker.prototype['getDraggable'] = RichMarker.prototype.getDraggable; 225 226 227/** 228 * Sets the marker to be draggable or not. 229 * 230 * @param {boolean} draggable If the marker is draggable or not. 231 */ 232RichMarker.prototype.setDraggable = function(draggable) { 233 this.set('draggable', !!draggable); 234}; 235RichMarker.prototype['setDraggable'] = RichMarker.prototype.setDraggable; 236 237 238/** 239 * Draggable property changed callback. 240 */ 241RichMarker.prototype.draggable_changed = function() { 242 if (this.ready_) { 243 if (this.getDraggable()) { 244 this.addDragging_(this.markerWrapper_); 245 } else { 246 this.removeDragListeners_(); 247 } 248 } 249}; 250RichMarker.prototype['draggable_changed'] = 251 RichMarker.prototype.draggable_changed; 252 253 254/** 255 * Gets the postiton of the marker. 256 * 257 * @return {google.maps.LatLng} The position of the marker. 258 */ 259RichMarker.prototype.getPosition = function() { 260 return /** @type {google.maps.LatLng} */ (this.get('position')); 261}; 262RichMarker.prototype['getPosition'] = RichMarker.prototype.getPosition; 263 264 265/** 266 * Sets the position of the marker. 267 * 268 * @param {google.maps.LatLng} position The position to set. 269 */ 270RichMarker.prototype.setPosition = function(position) { 271 this.set('position', position); 272}; 273RichMarker.prototype['setPosition'] = RichMarker.prototype.setPosition; 274 275 276/** 277 * Position changed event. 278 */ 279RichMarker.prototype.position_changed = function() { 280 this.draw(); 281}; 282RichMarker.prototype['position_changed'] = 283 RichMarker.prototype.position_changed; 284 285 286/**
287 * Gets the anchor. 288 * 289 * @return {google.maps.Size} The position of the anchor. 290 */ 291RichMarker.prototype.getAnchor = function() { 292 return /** @type {google.maps.Size} */ (this.get('anchor')); 293}; 294RichMarker.prototype['getAnchor'] = RichMarker.prototype.getAnchor; 295 296 297/** 298 * Sets the anchor. 299 * 300 * @param {RichMarkerPosition|google.maps.Size} anchor The anchor to set. 301 */ 302RichMarker.prototype.setAnchor = function(anchor) { 303 this.set('anchor', anchor); 304}; 305RichMarker.prototype['setAnchor'] = RichMarker.prototype.setAnchor; 306 307 308/** 309 * Anchor changed event. 310 */ 311RichMarker.prototype.anchor_changed = function() { 312 this.draw(); 313}; 314RichMarker.prototype['anchor_changed'] = RichMarker.prototype.anchor_changed; 315 316 317/** 318 * Converts a HTML string to a document fragment. 319 * 320 * @param {string} htmlString The HTML string to convert. 321 * @return {Node} A HTML document fragment. 322 * @private 323 */ 324RichMarker.prototype.htmlToDocumentFragment_ = function(htmlString) { 325 var tempDiv = document.createElement('DIV'); 326 tempDiv.innerHTML = htmlString; 327 if (tempDiv.childNodes.length == 1) {
328 return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild)); 329 } else { 330 var fragment = document.createDocumentFragment(); 331 while (tempDiv.firstChild) { 332 fragment.appendChild(tempDiv.firstChild); 333 } 334 return fragment; 335 } 336}; 337 338 339/** 340 * Removes all children from the node. 341 * 342 * @param {Node} node The node to remove all children from. 343 * @private 344 */ 345RichMarker.prototype.removeChildren_ = function(node) { 346 if (!node) { 347 return; 348 } 349 350 var child; 351 while (child = node.firstChild) { 352 node.removeChild(child); 353 } 354}; 355 356 357/** 358 * Sets the content of the marker. 359 * 360 * @param {string|Node} content The content to set. 361 */ 362RichMarker.prototype.setContent = function(content) { 363 this.set('content', content); 364}; 365RichMarker.prototype['setContent'] = RichMarker.prototype.setContent; 366 367 368/** 369 * Get the content of the marker. 370 * 371 * @return {string|Node} The marker content. 372 */ 373RichMarker.prototype.getContent = function() { 374 return /** @type {Node|string} */ (this.get('content')); 375}; 376RichMarker.prototype['getContent'] = RichMarker.prototype.getContent; 377 378 379/** 380 * Sets the marker content and adds loading events to images 381 */ 382RichMarker.prototype.content_changed = function() { 383 if (!this.markerContent_) { 384 // Marker content area doesnt exist. 385 return; 386 } 387 388 this.removeChildren_(this.markerContent_); 389 var content = this.getContent(); 390 if (content) { 391 if (typeof content == 'string') { 392 content = content.replace(/^\s*([\S\s]*)\b\s*$/, '$1'); 393 content = this.htmlToDocumentFragment_(content); 394 } 395 this.markerContent_.appendChild(content); 396 397 var that = this; 398 var images = this.markerContent_.getElementsByTagName('IMG'); 399 for (var i = 0, image; image = images[i]; i++) { 400 // By default, a browser lets a image be dragged outside of the browser,
401 // so by calling preventDefault we stop this behaviour and allow the image 402 // to be dragged around the map and now out of the browser and onto the 403 // desktop. 404 google.maps.event.addDomListener(image, 'mousedown', function(e) { 405 if (that.getDraggable()) { 406 if (e.preventDefault) { 407 e.preventDefault(); 408 } 409 e.returnValue = false; 410 } 411 }); 412 413 // Because we don't know the size of an image till it loads, add a 414 // listener to the image load so the marker can resize and reposition 415 // itself to be the correct height. 416 google.maps.event.addDomListener(image, 'load', function() { 417 that.draw(); 418 }); 419 } 420 421 google.maps.event.trigger(this, 'domready'); 422 } 423 424 if (this.ready_) { 425 this.draw(); 426 } 427}; 428RichMarker.prototype['content_changed'] = RichMarker.prototype.content_changed; 429 430/** 431 * Sets the cursor. 432 * 433 * @param {string} whichCursor What cursor to show. 434 * @private 435 */ 436RichMarker.prototype.setCursor_ = function(whichCursor) { 437 if (!this.ready_) { 438 return; 439 } 440 441 var cursor = ''; 442 if (navigator.userAgent.indexOf('Gecko/') !== -1) { 443 // Moz has some nice cursors :) 444 if (whichCursor == 'dragging') { 445 cursor = '-moz-grabbing'; 446 } 447 448 if (whichCursor == 'dragready') { 449 cursor = '-moz-grab'; 450 } 451 452 if (whichCursor == 'draggable') { 453 cursor = 'pointer'; 454 } 455 } else { 456 if (whichCursor == 'dragging' || whichCursor == 'dragready') { 457 cursor = 'move'; 458 } 459 460 if (whichCursor == 'draggable') { 461 cursor = 'pointer'; 462 } 463 } 464 465 if (this.markerWrapper_.style.cursor != cursor) { 466 this.markerWrapper_.style.cursor = cursor; 467 } 468}; 469 470/** 471 * Start dragging. 472 * 473 * @param {Event} e The event. 474 */ 475RichMarker.prototype.startDrag = function(e) { 476 if (!this.getDraggable()) { 477 return; 478 } 479 480 if (!this.dragging_) { 481 this.dragging_ = true; 482 var map = this.getMap(); 483 this.mapDraggable_ = map.get('draggable'); 484 map.set('draggable', false); 485 486 // Store the current mouse position 487 this.mouseX_ = e.clientX; 488 this.mouseY_ = e.clientY; 489 490 this.setCursor_('dragready'); 491 492 // Stop the text from being selectable while being dragged 493 this.markerWrapper_.style['MozUserSelect'] = 'none'; 494 this.markerWrapper_.style['KhtmlUserSelect'] = 'none'; 495 this.markerWrapper_.style['WebkitUserSelect'] = 'none'; 496 497 this.markerWrapper_['unselectable'] = 'on'; 498 this.markerWrapper_['onselectstart'] = function() { 499 return false; 500 }; 501 502 this.addDraggingListeners_(); 503 504 google.maps.event.trigger(this, 'dragstart'); 505 } 506}; 507 508 509/** 510 * Stop dragging. 511 */ 512RichMarker.prototype.stopDrag = function() { 513 if (!this.getDraggable()) { 514 return; 515 } 516 517 if (this.dragging_) { 518 this.dragging_ = false; 519 this.getMap().set('draggable', this.mapDraggable_); 520 this.mouseX_ = this.mouseY_ = this.mapDraggable_ = null; 521 522 // Allow the text to be selectable again 523 this.markerWrapper_.style['MozUserSelect'] = ''; 524 this.markerWrapper_.style['KhtmlUserSelect'] = ''; 525 this.markerWrapper_.style['WebkitUserSelect'] = ''; 526 this.markerWrapper_['unselectable'] = 'off'; 527 this.markerWrapper_['onselectstart'] = function() {}; 528 529 this.removeDraggingListeners_(); 530 531 this.setCursor_('draggable'); 532 google.maps.event.trigger(this, 'dragend'); 533 534 this.draw(); 535 } 536}; 537 538 539/** 540 * Handles the drag event. 541 * 542 * @param {Event} e The event. 543 */ 544RichMarker.prototype.drag = function(e) { 545 if (!this.getDraggable() || !this.dragging_) { 546 // This object isn't draggable or we have stopped dragging
547 this.stopDrag(); 548 return; 549 } 550 551 var dx = this.mouseX_ - e.clientX; 552 var dy = this.mouseY_ - e.clientY; 553 554 this.mouseX_ = e.clientX; 555 this.mouseY_ = e.clientY; 556 557 var left = parseInt(this.markerWrapper_.style['left'], 10) - dx; 558 var top = parseInt(this.markerWrapper_.style['top'], 10) - dy; 559 560 this.markerWrapper_.style['left'] = left + 'px'; 561 this.markerWrapper_.style['top'] = top + 'px'; 562 563 var offset = this.getOffset_(); 564 565 // Set the position property and adjust for the anchor offset 566 var point = new google.maps.Point(left - offset.width, top - offset.height); 567 var projection = this.getProjection(); 568 this.setPosition(projection.fromDivPixelToLatLng(point)); 569 570 this.setCursor_('dragging'); 571 google.maps.event.trigger(this, 'drag'); 572}; 573 574 575/** 576 * Removes the drag listeners associated with the marker. 577 * 578 * @private 579 */ 580RichMarker.prototype.removeDragListeners_ = function() { 581 if (this.draggableListener_) { 582 google.maps.event.removeListener(this.draggableListener_); 583 delete this.draggableListener_; 584 } 585 this.setCursor_(''); 586}; 587 588 589/** 590 * Add dragability events to the marker. 591 * 592 * @param {Node} node The node to apply dragging to. 593 * @private 594 */ 595RichMarker.prototype.addDragging_ = function(node) { 596 if (!node) { 597 return; 598 } 599 600 var that = this; 601 this.draggableListener_ = 602 google.maps.event.addDomListener(node, 'mousedown', function(e) { 603 that.startDrag(e); 604 }); 605 606 this.setCursor_('draggable'); 607};
608 609 610/** 611 * Add dragging listeners. 612 * 613 * @private 614 */ 615RichMarker.prototype.addDraggingListeners_ = function() { 616 var that = this; 617 if (this.markerWrapper_.setCapture) { 618 this.markerWrapper_.setCapture(true); 619 this.draggingListeners_ = [ 620 google.maps.event.addDomListener(this.markerWrapper_, 'mousemove', function(e) { 621 that.drag(e); 622 }, true), 623 google.maps.event.addDomListener(this.markerWrapper_, 'mouseup', function() { 624 that.stopDrag(); 625 that.markerWrapper_.releaseCapture(); 626 }, true) 627 ]; 628 } else { 629 this.draggingListeners_ = [ 630 google.maps.event.addDomListener(window, 'mousemove', function(e) { 631 that.drag(e); 632 }, true), 633 google.maps.event.addDomListener(window, 'mouseup', function() { 634 that.stopDrag(); 635 }, true) 636 ]; 637 } 638}; 639 640 641/** 642 * Remove dragging listeners. 643 * 644 * @private 645 */ 646RichMarker.prototype.removeDraggingListeners_ = function() { 647 if (this.draggingListeners_) { 648 for (var i = 0, listener; listener = this.draggingListeners_[i]; i++) { 649 google.maps.event.removeListener(listener); 650 } 651 this.draggingListeners_.length = 0; 652 } 653}; 654 655 656/** 657 * Get the anchor offset. 658 * 659 * @return {google.maps.Size} The size offset. 660 * @private 661 */ 662RichMarker.prototype.getOffset_ = function() { 663 var anchor = this.getAnchor(); 664 if (typeof anchor == 'object') { 665 return /** @type {google.maps.Size} */ (anchor); 666 } 667 668 var offset = new google.maps.Size(0, 0); 669 if (!this.markerContent_) { 670 return offset; 671 } 672 673 var width = this.markerContent_.offsetWidth; 674 var height = this.markerContent_.offsetHeight; 675 676 switch (anchor) { 677 case RichMarkerPosition['TOP_LEFT']: 678 break; 679 case RichMarkerPosition['TOP']: 680 offset.width = -width / 2; 681 break; 682 case RichMarkerPosition['TOP_RIGHT']: 683 offset.width = -width; 684 break; 685 case RichMarkerPosition['LEFT']: 686 offset.height = -height / 2; 687 break; 688 case RichMarkerPosition['MIDDLE']: 689 offset.width = -width / 2; 690 offset.height = -height / 2; 691 break; 692 case RichMarkerPosition['RIGHT']: 693 offset.width = -width; 694 offset.height = -height / 2; 695 break; 696 case RichMarkerPosition['BOTTOM_LEFT']: 697 offset.height = -height; 698 break; 699 case RichMarkerPosition['BOTTOM']: 700 offset.width = -width / 2; 701 offset.height = -height; 702 break; 703 case RichMarkerPosition['BOTTOM_RIGHT']: 704 offset.width = -width; 705 offset.height = -height; 706 break; 707 } 708 709 return offset; 710}; 711 712 713/** 714 * Adding the marker to a map. 715 * Implementing the interface. 716 */ 717RichMarker.prototype.onAdd = function() { 718 if (!this.markerWrapper_) { 719 this.markerWrapper_ = document.createElement('DIV'); 720 this.markerWrapper_.className += 'js-rich-marker'; 721 this.markerWrapper_.style['position'] = 'absolute'; 722 } 723 724 if (this.getZIndex()) { 725 this.markerWrapper_.style['zIndex'] = this.getZIndex(); 726 } 727 728 this.markerWrapper_.style['display'] = this.getVisible() ? '' : 'none'; 729 730 if (!this.markerContent_) { 731 this.markerContent_ = document.createElement('DIV'); 732 this.markerWrapper_.appendChild(this.markerContent_); 733 734 var that = this; 735 google.maps.event.addDomListener(this.markerContent_, 'click', function(e) { 736 google.maps.event.trigger(that, 'click'); 737 }); 738 google.maps.event.addDomListener(this.markerContent_, 'mouseover', function(e) { 739 google.maps.event.trigger(that, 'mouseover'); 740 }); 741 google.maps.event.addDomListener(this.markerContent_, 'mouseout', function(e) { 742 google.maps.event.trigger(that, 'mouseout'); 743 }); 744 } 745 746 this.ready_ = true; 747 this.content_changed(); 748 this.flat_changed(); 749 this.draggable_changed(); 750 751 var panes = this.getPanes(); 752 if (panes) { 753 panes.overlayMouseTarget.appendChild(this.markerWrapper_); 754 } 755 756 google.maps.event.trigger(this, 'ready'); 757}; 758RichMarker.prototype['onAdd'] = RichMarker.prototype.onAdd; 759 760 761/** 762 * Impelementing the interface. 763 */ 764RichMarker.prototype.draw = function() { 765 if (!this.ready_ || this.dragging_) { 766 return; 767 } 768 769 var projection = this.getProjection(); 770 771 if (!projection) { 772 // The map projection is not ready yet so do nothing 773 return; 774 } 775 776 var latLng = /** @type {google.maps.LatLng} */ (this.get('position')); 777 var pos = projection.fromLatLngToDivPixel(latLng); 778 779 var offset = this.getOffset_(); 780 this.markerWrapper_.style['top'] = (pos.y + offset.height) + 'px'; 781 this.markerWrapper_.style['left'] = (pos.x + offset.width) + 'px'; 782 783 var height = this.markerContent_.offsetHeight; 784 var width = this.markerContent_.offsetWidth; 785 786 if (width != this.get('width')) { 787 this.set('width', width); 788 } 789 790 if (height != this.get('height')) { 791 this.set('height', height); 792 } 793}; 794RichMarker.prototype['draw'] = RichMarker.prototype.draw; 795 796 797/** 798 * Removing a marker from the map. 799 * Implementing the interface. 800 */ 801RichMarker.prototype.onRemove = function() { 802 if (this.markerWrapper_ && this.markerWrapper_.parentNode) { 803 this.markerWrapper_.parentNode.removeChild(this.markerWrapper_); 804 } 805 this.removeDragListeners_(); 806}; 807RichMarker.prototype['onRemove'] = RichMarker.prototype.onRemove; 808 809 810/** 811 * RichMarker Anchor positions 812 * @enum {number} 813 */ 814var RichMarkerPosition = { 815 'TOP_LEFT': 1, 816 'TOP': 2, 817 'TOP_RIGHT': 3, 818 'LEFT': 4, 819 'MIDDLE': 5, 820 'RIGHT': 6, 821 'BOTTOM_LEFT': 7, 822 'BOTTOM': 8, 823 'BOTTOM_RIGHT': 9 824}; 825window['RichMarkerPosition'] = RichMarkerPosition;
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.