text: /**
   * @license
   *
   * Copyright 2011 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /**
   * @fileoverview Map Label.
   *
   * @author Luke Mahe (lukem@google.com),
   *         Chris Broadfoot (cbro@google.com)
   */
  
  /**
   * Creates a new Map Label
   * @constructor
   * @extends google.maps.OverlayView
   * @param {Object.<string, *>=} opt_options Optional properties to set.
   */
  function MapLabel(opt_options) {
    this.set('fontFamily', 'sans-serif');
    this.set('fontSize', 12);
    this.set('fontColor', '#000000');
    this.set('strokeWeight', 4);
    this.set('strokeColor', '#ffffff');
    this.set('align', 'center');
  
    this.set('zIndex', 1e3);
  
    this.setValues(opt_options);
  }
  MapLabel.prototype = new google.maps.OverlayView;
  
  window['MapLabel'] = MapLabel;
  
  
  /** @inheritDoc */
  MapLabel.prototype.changed = function(prop) {
    switch (prop) {
      case 'fontFamily':
      case 'fontSize':
      case 'fontColor':
      case 'strokeWeight':
      case 'strokeColor':
      case 'align':
      case 'text':
        return this.drawCanvas_();
      case 'maxZoom':
      case 'minZoom':
      case 'position':
        return this.draw();
    }
  };
  
  /**
   * Draws the label to the canvas 2d context.
   * @private
   */
  MapLabel.prototype.drawCanvas_ = function() {
    var canvas = this.canvas_;
    if (!canvas) return;
  
    var style = canvas.style;
    style.zIndex = /** @type number */(this.get('zIndex'));
  
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = this.get('strokeColor');
    ctx.fillStyle = this.get('fontColor');
    ctx.font = this.get('fontSize') + 'px ' + this.get('fontFamily');
  
    var strokeWeight = Number(this.get('strokeWeight'));
  
    var text = this.get('text');
    if (text) {
      if (strokeWeight) {
        ctx.lineWidth = strokeWeight;
        ctx.strokeText(text, strokeWeight, strokeWeight);
      }
  
      ctx.fillText(text, strokeWeight, strokeWeight);
  
      var textMeasure = ctx.measureText(text);
      var textWidth = textMeasure.width + strokeWeight;
      style.marginLeft = this.getMarginLeft_(textWidth) + 'px';
      // Bring actual text top in line with desired latitude.
      // Cheaper than calculating height of text.
      style.marginTop = '-0.4em';
    }
  };
  
  /**
   * @inheritDoc
   */
  MapLabel.prototype.onAdd = function() {
    var canvas = this.canvas_ = document.createElement('canvas');
    var style = canvas.style;
    style.position = 'absolute';
  
    var ctx = canvas.getContext('2d');
    ctx.lineJoin = 'round';
    ctx.textBaseline = 'top';
  
    this.drawCanvas_();
  
    var panes = this.getPanes();
    if (panes) {
      panes.floatPane.appendChild(canvas);
    }
  };
  MapLabel.prototype['onAdd'] = MapLabel.prototype.onAdd;
  
  /**
   * Gets the appropriate margin-left for the canvas.
   * @private
   * @param {number} textWidth  the width of the text, in pixels.
   * @return {number} the margin-left, in pixels.
   */
  MapLabel.prototype.getMarginLeft_ = function(textWidth) {
    switch (this.get('align')) {
      case 'left':
        return 0;
      case 'right':
        return -textWidth;
    }
    return textWidth / -2;
  };
  
  /**
   * @inheritDoc
   */
  MapLabel.prototype.draw = function() {
    var projection = this.getProjection();
  
    if (!projection) {
      // The map projection is not ready yet so do nothing
      return;
    }
  
    if (!this.canvas_) {
      // onAdd has not been called yet.
      return;
    }
  
    var latLng = /** @type {google.maps.LatLng} */ (this.get('position'));
    if (!latLng) {
      return;
    }
    var pos = projection.fromLatLngToDivPixel(latLng);
  
    var style = this.canvas_.style;
  
    style['top'] = pos.y + 'px';
    style['left'] = pos.x + 'px';
  
    style['visibility'] = this.getVisible_();
  };
  MapLabel.prototype['draw'] = MapLabel.prototype.draw;
  
  /**
   * Get the visibility of the label.
   * @private
   * @return {string} blank string if visible, 'hidden' if invisible.
   */
  MapLabel.prototype.getVisible_ = function() {
    var minZoom = /** @type number */(this.get('minZoom'));
    var maxZoom = /** @type number */(this.get('maxZoom'));
  
    if (minZoom === undefined && maxZoom === undefined) {
      return '';
    }
  
    var map = this.getMap();
    if (!map) {
      return '';
    }
  
    var mapZoom = map.getZoom();
    if (mapZoom < minZoom || mapZoom > maxZoom) {
      return 'hidden';
    }
    return '';
  };
  
  /**
   * @inheritDoc
   */
  MapLabel.prototype.onRemove = function() {
    var canvas = this.canvas_;
    if (canvas && canvas.parentNode) {
      canvas.parentNode.removeChild(canvas);
    }
  };
  MapLabel.prototype['onRemove'] = MapLabel.prototype.onRemove;
  