var app = angular.module("myApp", ["ngMap"]);

app.controller("MyController", function($scope, $timeout) {
  
  $timeout(function() { // wait for map directive to load
    new RichMarker({
      map: $scope.map,   // !! $scope.map
      position: new google.maps.LatLng(40.80, -74.13),
      draggable: true,
      flat: true,
      anchor: RichMarkerPosition.MIDDLE,
      content: '<img src="http://farm4.static.flickr.com/3212/3012579547_097e27ced9_m.jpg"/>'
    });  
  });
});

  map, div[map] {display:block; width: 600px; height: 400px}
<!doctype html>
<html ng-app="myApp">
  <head>
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="http://code.angularjs.org/1.2.5/angular.js"></script>
<script src="ng-map.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" href="style.css"/>
<script src="richmarker.js"></script>
  </head>
  <body ng-controller="MyController">


    <map zoom="11" center="[40.74, -74.18]">
      <marker position="[40.71, -74.21]" title="hello"></marker>
      <marker position="[40.72, -74.20]" title="marker" animation="Animation.DROP"></marker>
      <marker position="[40.73, -74.19]" title="drag me" draggable="true"></marker>
      <marker position="[40.74, -74.18]" title="how" animation="Animation.BOUNCE"></marker>
      <marker position="[40.75, -74.17]" title="are"></marker>
      <marker position="[40.76, -74.16]" title="you" icon="https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png"></marker>
      <marker position="[40.77, -74.15]" title="from"></marker>
      <marker position="[40.78, -74.14]" title="allen" centered="true"></marker>
    </map>
  

  </body>
</html>
var ngMap={services:{},directives:{}};ngMap.services.Attr2Options=function(){return{filter:function(e){var t={};for(var n in e)n.match(/^\$/)||(t[n]=e[n]);return t},getOptions:function(attrs,scope){var options={};for(var key in attrs)if(attrs[key]){if(key.match(/^on[A-Z]/))continue;if(key.match(/ControlOptions$/))continue;var val=attrs[key];try{var num=Number(val);if(isNaN(num))throw"Not a number";options[key]=num}catch(err){try{options[key]=JSON.parse(val)}catch(err2){if(val.match(/^[A-Z][a-zA-Z0-9]+\(.*\)$/))try{var exp="new google.maps."+val;options[key]=eval(exp)}catch(e){options[key]=val}else if(val.match(/^[A-Z][a-zA-Z0-9]+\.[A-Z]+$/))try{options[key]=scope.$eval("google.maps."+val)}catch(e){options[key]=val}else if(val.match(/^[A-Z]+$/))try{var capitializedKey=key.charAt(0).toUpperCase()+key.slice(1);options[key]=scope.$eval("google.maps."+capitializedKey+"."+val)}catch(e){options[key]=val}else options[key]=val}}}return options},getEvents:function(e,t){var n={},r=function(e){return"_"+e.toLowerCase()},o=function(t){var n=t.match(/([^\(]+)\(([^\)]*)\)/),r=n[1],o=n[2].replace(/event[ ,]*/,""),i=e.$eval("["+o+"]");return function(t){e[r].apply(this,[t].concat(i))}};for(var i in t)if(t[i]){if(!i.match(/^on[A-Z]/))continue;var a=i.replace(/^on/,"");a=a.charAt(0).toLowerCase()+a.slice(1),a=a.replace(/([A-Z])/g,r);var s=t[i];n[a]=new o(s)}return n},getControlOptions:function(e){var t={};if("object"!=typeof e)return!1;for(var n in e)if(e[n]){if(!n.match(/(.*)ControlOptions$/))continue;var r=e[n],o=r.replace(/'/g,'"');o=o.replace(/([^"]+)|("[^"]+")/g,function(e,t,n){return t?t.replace(/([a-zA-Z0-9]+?):/g,'"$1":'):n});try{var i=JSON.parse(o);for(var a in i)if(i[a]){var s=i[a];if("string"==typeof s?s=s.toUpperCase():"mapTypeIds"===a&&(s=s.map(function(e){return google.maps.MapTypeId[e.toUpperCase()]})),"style"===a){var p=n.charAt(0).toUpperCase()+n.slice(1),c=p.replace(/Options$/,"")+"Style";i[a]=google.maps[c][s]}else i[a]="position"===a?google.maps.ControlPosition[s]:s}t[n]=i}catch(l){}}return t}}},ngMap.services.GeoCoder=function(e){return{geocode:function(t){var n=e.defer(),r=new google.maps.Geocoder;return r.geocode(t,function(e,t){t==google.maps.GeocoderStatus.OK?n.resolve(e):n.reject("Geocoder failed due to: "+t)}),n.promise}}},ngMap.services.GeoCoder.$inject=["$q"],ngMap.services.NavigatorGeolocation=function(e){return{getCurrentPosition:function(){var t=e.defer();return navigator.geolocation?navigator.geolocation.getCurrentPosition(function(e){t.resolve(e)},function(e){t.reject(e)}):t.reject("Browser Geolocation service failed."),t.promise},watchPosition:function(){return"TODO"},clearWatch:function(){return"TODO"}}},ngMap.services.NavigatorGeolocation.$inject=["$q"],ngMap.services.StreetView=function(e){return{getPanorama:function(t,n){n=n||t.getCenter();var r=e.defer(),o=new google.maps.StreetViewService;return o.getPanoramaByLocation(n||t.getCenter,100,function(e,t){r.resolve(t===google.maps.StreetViewStatus.OK?e.location.pano:!1)}),r.promise},setPanorama:function(e,t){var n=new google.maps.StreetViewPanorama(e.getDiv(),{enableCloseButton:!0});n.setPano(t)}}},ngMap.services.StreetView.$inject=["$q"],ngMap.directives.infoWindow=function(Attr2Options){var parser=Attr2Options;return{restrict:"AE",require:"^map",link:function(scope,element,attrs,mapController){var filtered=parser.filter(attrs);scope.google=google;var options=parser.getOptions(filtered,scope);options.pixelOffset&&(options.pixelOffset=google.maps.Size.apply(this,options.pixelOffset));var infoWindow=new google.maps.InfoWindow(options);infoWindow.contents=element.html();var events=parser.getEvents(scope,filtered);for(var eventName in events)eventName&&google.maps.event.addListener(infoWindow,eventName,events[eventName]);mapController.infoWindows.push(infoWindow),element.css({display:"none"}),scope.showInfoWindow=function(event,id,options){var infoWindow=scope.infoWindows[id],contents=infoWindow.contents,matches=contents.match(/\[\[[^\]]+\]\]/g);if(matches)for(var i=0,length=matches.length;length>i;i++){var expression=matches[i].replace(/\[\[/,"").replace(/\]\]/,"");try{contents=contents.replace(matches[i],eval(expression))}catch(e){expression="options."+expression,contents=contents.replace(matches[i],eval(expression))}}infoWindow.setContent(contents),infoWindow.open(scope.map,this)}}}},ngMap.directives.infoWindow.$inject=["Attr2Options"],ngMap.directives.map=function(e,t,n,r){var o=e;return{restrict:"AE",controller:ngMap.directives.MapController,link:function(e,t,i,a){e.google=google;var s=document.createElement("div");s.style.width="100%",s.style.height="100%",t.prepend(s),e.map=new google.maps.Map(s,{});var p=o.filter(i),c=o.getOptions(p,e),l=o.getControlOptions(p),g=o.getEvents(e,p),v=angular.extend(c,l);if(v.zoom=v.zoom||15,v.center instanceof Array){var u=v.center[0],f=v.center[1];a.initMap(v,new google.maps.LatLng(u,f),g)}else"string"==typeof v.center?r.geocode({address:v.center}).then(function(e){a.initMap(v,e[0].geometry.location,g)}):v.center||n.getCurrentPosition().then(function(e){var t=e.coords.latitude,n=e.coords.longitude;a.initMap(v,new google.maps.LatLng(t,n),g)},function(){if(v.geoFallbackCenter instanceof Array){var e=v.geoFallbackCenter[0],t=v.geoFallbackCenter[1];a.initMap(v,new google.maps.LatLng(e,t),g)}else a.initMap(v,new google.maps.LatLng(0,0),g)});var m=a.initMarkers();e.$emit("markersInitialized",m);var d=a.initShapes();e.$emit("shapesInitialized",d);var h=a.initInfoWindows();e.$emit("infoWindowsInitialized",[h,e.showInfoWindow]);var k=a.initMarkerClusterer();e.$emit("markerClustererInitialized",k)}}},ngMap.directives.map.$inject=["Attr2Options","$parse","NavigatorGeolocation","GeoCoder","$compile"],ngMap.directives.MapController=function(e){this.controls={},this.markers=[],this.shapes=[],this.infoWindows=[],this.markerClusterer=null,this.initMap=function(t,n,r){t.center=null,e.map.setOptions(t),e.map.setCenter(n);for(var o in r)o&&google.maps.event.addListener(e.map,o,r[o]);e.$emit("mapInitialized",e.map)},this.addMarker=function(t){t.setMap(e.map),t.centered&&e.map.setCenter(t.position);var n=Object.keys(e.markers).length;e.markers[t.id||n]=t},this.initMarkers=function(){e.markers={};for(var t=0;t<this.markers.length;t++){var n=this.markers[t];this.addMarker(n)}return e.markers},this.addShape=function(t){t.setMap(e.map);var n=Object.keys(e.shapes).length;e.shapes[t.id||n]=t},this.initShapes=function(){e.shapes={};for(var t=0;t<this.shapes.length;t++){var n=this.shapes[t];n.setMap(e.map),e.shapes[n.id||t]=n}return e.shapes},this.initInfoWindows=function(){e.infoWindows={};for(var t=0;t<this.infoWindows.length;t++){var n=this.infoWindows[t];e.infoWindows[n.id||t]=n}return e.infoWindows},this.initMarkerClusterer=function(){return this.markerClusterer&&(e.markerClusterer=new MarkerClusterer(e.map,this.markerClusterer.data,this.markerClusterer.options)),e.markerClusterer}},ngMap.directives.MapController.$inject=["$scope"],ngMap.directives.marker=function(e,t,n){var r=e;return{restrict:"AE",require:"^map",link:function(e,o,i,a){var s=r.filter(i);e.google=google;var p=r.getOptions(s,e),c=r.getEvents(e,s),l=function(e,t){var n=new google.maps.Marker(e);Object.keys(t).length>0;for(var r in t)r&&google.maps.event.addListener(n,r,t[r]);return n};if(p.position instanceof Array){var g=p.position[0],v=p.position[1];p.position=new google.maps.LatLng(g,v);var u=l(p,c);p.ngRepeat?a.addMarker(u):a.markers.push(u)}else if("string"==typeof p.position){var f=p.position;f.match(/^current/i)?n.getCurrentPosition().then(function(e){var t=e.coords.latitude,n=e.coords.longitude;p.position=new google.maps.LatLng(t,n);var r=l(p,c);a.addMarker(r)}):t.geocode({address:p.position}).then(function(e){var t=e[0].geometry.location;p.position=t;var n=l(p,c);a.addMarker(n)})}}}},ngMap.directives.marker.$inject=["Attr2Options","GeoCoder","NavigatorGeolocation"],ngMap.directives.markerClusterer=function(e){var t=e;return{restrict:"AE",require:"^map",link:function(e,n,r,o){var i=e.$eval(r.markers);delete r.markers;for(var a=t.filter(r),s=[],p=0;p<i.length;p++){var c=i[p],l=c.position[0],g=c.position[1];c.position=new google.maps.LatLng(l,g);var v=new google.maps.Marker(c),u=t.getEvents(e,c);for(var f in u)f&&google.maps.event.addListener(v,f,u[f]);s.push(v)}o.markers=s,o.markerClusterer={data:s,options:a}}}},ngMap.directives.markerClusterer.$inject=["Attr2Options"],ngMap.directives.shape=function(e){var t=e,n=function(e){return e[0]&&e[0]instanceof Array?e.map(function(e){return new google.maps.LatLng(e[0],e[1])}):new google.maps.LatLng(e[0],e[1])},r=function(e){var t=n(e);return new google.maps.LatLngBounds(t[0],t[1])},o=function(e,t){switch(e){case"circle":return t.center=n(t.center),new google.maps.Circle(t);case"polygon":return t.paths=n(t.paths),new google.maps.Polygon(t);case"polyline":return t.path=n(t.path),new google.maps.Polyline(t);case"rectangle":return t.bounds=r(t.bounds),new google.maps.Rectangle(t);case"groundOverlay":case"image":var o=t.url,i=r(t.bounds),a={opacity:t.opacity,clickable:t.clickable,id:t.id};return new google.maps.GroundOverlay(o,i,a)}return null};return{restrict:"AE",require:"^map",link:function(e,n,r,i){var a=t.filter(r),s=a.name;delete a.name;var p=t.getOptions(a),c=o(s,p);p.ngRepeat?i.addShape(c):c&&i.shapes.push(c);var l=t.getEvents(e,a);for(var g in l)l[g]&&google.maps.event.addListener(c,g,l[g])}}},ngMap.directives.shape.$inject=["Attr2Options"];var ngMapModule=angular.module("ngMap",[]);for(var key in ngMap.services)ngMapModule.service(key,ngMap.services[key]);for(var key in ngMap.directives)"MapController"!=key&&ngMapModule.directive(key,ngMap.directives[key]);
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3.js
// @output_wrapper (function() {%output%})();
// ==/ClosureCompiler==

/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * 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.
 */

/**
 * A RichMarker that allows any HTML/DOM to be added to a map and be draggable.
 *
 * @param {Object.<string, *>=} opt_options Optional properties to set.
 * @extends {google.maps.OverlayView}
 * @constructor
 */
function RichMarker(opt_options) {
  var options = opt_options || {};

  /**
   * @type {boolean}
   * @private
   */
  this.ready_ = false;

  /**
   * @type {boolean}
   * @private
   */
  this.dragging_ = false;

  if (opt_options['visible'] == undefined) {
    opt_options['visible'] = true;
  }

  if (opt_options['shadow'] == undefined) {
    opt_options['shadow'] = '7px -3px 5px rgba(88,88,88,0.7)';
  }

  if (opt_options['anchor'] == undefined) {
    opt_options['anchor'] = RichMarkerPosition['BOTTOM'];
  }

  this.setValues(options);
}
RichMarker.prototype = new google.maps.OverlayView();
window['RichMarker'] = RichMarker;


/**
 * Returns the current visibility state of the marker.
 *
 * @return {boolean} The visiblity of the marker.
 */
RichMarker.prototype.getVisible = function() {
  return /** @type {boolean} */ (this.get('visible'));
};
RichMarker.prototype['getVisible'] = RichMarker.prototype.getVisible;


/**
 * Sets the visiblility state of the marker.
 *
 * @param {boolean} visible The visiblilty of the marker.
 */
RichMarker.prototype.setVisible = function(visible) {
  this.set('visible', visible);
};
RichMarker.prototype['setVisible'] = RichMarker.prototype.setVisible;


/**
 *  The visible changed event.
 */
RichMarker.prototype.visible_changed = function() {
  if (this.ready_) {
    this.markerWrapper_.style['display'] = this.getVisible() ? '' : 'none';
    this.draw();
  }
};
RichMarker.prototype['visible_changed'] = RichMarker.prototype.visible_changed;


/**
 * Sets the marker to be flat.
 *
 * @param {boolean} flat If the marker is to be flat or not.
 */
RichMarker.prototype.setFlat = function(flat) {
  this.set('flat', !!flat);
};
RichMarker.prototype['setFlat'] = RichMarker.prototype.setFlat;


/**
 * If the makrer is flat or not.
 *
 * @return {boolean} True the marker is flat.
 */
RichMarker.prototype.getFlat = function() {
  return /** @type {boolean} */ (this.get('flat'));
};
RichMarker.prototype['getFlat'] = RichMarker.prototype.getFlat;


/**
 * Get the width of the marker.
 *
 * @return {Number} The width of the marker.
 */
RichMarker.prototype.getWidth = function() {
  return /** @type {Number} */ (this.get('width'));
};
RichMarker.prototype['getWidth'] = RichMarker.prototype.getWidth;


/**
 * Get the height of the marker.
 *
 * @return {Number} The height of the marker.
 */
RichMarker.prototype.getHeight = function() {
  return /** @type {Number} */ (this.get('height'));
};
RichMarker.prototype['getHeight'] = RichMarker.prototype.getHeight;


/**
 * Sets the marker's box shadow.
 *
 * @param {string} shadow The box shadow to set.
 */
RichMarker.prototype.setShadow = function(shadow) {
  this.set('shadow', shadow);
  this.flat_changed();
};
RichMarker.prototype['setShadow'] = RichMarker.prototype.setShadow;


/**
 * Gets the marker's box shadow.
 *
 * @return {string} The box shadow.
 */
RichMarker.prototype.getShadow = function() {
  return /** @type {string} */ (this.get('shadow'));
};
RichMarker.prototype['getShadow'] = RichMarker.prototype.getShadow;


/**
 * Flat changed event.
 */
RichMarker.prototype.flat_changed = function() {
  if (!this.ready_) {
    return;
  }

  this.markerWrapper_.style['boxShadow'] =
      this.markerWrapper_.style['webkitBoxShadow'] =
      this.markerWrapper_.style['MozBoxShadow'] =
      this.getFlat() ? '' : this.getShadow();
};
RichMarker.prototype['flat_changed'] = RichMarker.prototype.flat_changed;


/**
 * Sets the zIndex of the marker.
 *
 * @param {Number} index The index to set.
 */
RichMarker.prototype.setZIndex = function(index) {
  this.set('zIndex', index);
};
RichMarker.prototype['setZIndex'] = RichMarker.prototype.setZIndex;


/**
 * Gets the zIndex of the marker.
 *
 * @return {Number} The zIndex of the marker.
 */
RichMarker.prototype.getZIndex = function() {
  return /** @type {Number} */ (this.get('zIndex'));
};
RichMarker.prototype['getZIndex'] = RichMarker.prototype.getZIndex;


/**
 * zIndex changed event.
 */
RichMarker.prototype.zIndex_changed = function() {
  if (this.getZIndex() && this.ready_) {
    this.markerWrapper_.style.zIndex = this.getZIndex();
  }
};
RichMarker.prototype['zIndex_changed'] = RichMarker.prototype.zIndex_changed;

/**
 * Whether the marker is draggable or not.
 *
 * @return {boolean} True if the marker is draggable.
 */
RichMarker.prototype.getDraggable = function() {
  return /** @type {boolean} */ (this.get('draggable'));
};
RichMarker.prototype['getDraggable'] = RichMarker.prototype.getDraggable;


/**
 * Sets the marker to be draggable or not.
 *
 * @param {boolean} draggable If the marker is draggable or not.
 */
RichMarker.prototype.setDraggable = function(draggable) {
  this.set('draggable', !!draggable);
};
RichMarker.prototype['setDraggable'] = RichMarker.prototype.setDraggable;


/**
 * Draggable property changed callback.
 */
RichMarker.prototype.draggable_changed = function() {
  if (this.ready_) {
    if (this.getDraggable()) {
      this.addDragging_(this.markerWrapper_);
    } else {
      this.removeDragListeners_();
    }
  }
};
RichMarker.prototype['draggable_changed'] =
    RichMarker.prototype.draggable_changed;


/**
 * Gets the postiton of the marker.
 *
 * @return {google.maps.LatLng} The position of the marker.
 */
RichMarker.prototype.getPosition = function() {
  return /** @type {google.maps.LatLng} */ (this.get('position'));
};
RichMarker.prototype['getPosition'] = RichMarker.prototype.getPosition;


/**
 * Sets the position of the marker.
 *
 * @param {google.maps.LatLng} position The position to set.
 */
RichMarker.prototype.setPosition = function(position) {
  this.set('position', position);
};
RichMarker.prototype['setPosition'] = RichMarker.prototype.setPosition;


/**
 * Position changed event.
 */
RichMarker.prototype.position_changed = function() {
  this.draw();
};
RichMarker.prototype['position_changed'] =
    RichMarker.prototype.position_changed;


/**
 * Gets the anchor.
 *
 * @return {google.maps.Size} The position of the anchor.
 */
RichMarker.prototype.getAnchor = function() {
  return /** @type {google.maps.Size} */ (this.get('anchor'));
};
RichMarker.prototype['getAnchor'] = RichMarker.prototype.getAnchor;


/**
 * Sets the anchor.
 *
 * @param {RichMarkerPosition|google.maps.Size} anchor The anchor to set.
 */
RichMarker.prototype.setAnchor = function(anchor) {
  this.set('anchor', anchor);
};
RichMarker.prototype['setAnchor'] = RichMarker.prototype.setAnchor;


/**
 * Anchor changed event.
 */
RichMarker.prototype.anchor_changed = function() {
  this.draw();
};
RichMarker.prototype['anchor_changed'] = RichMarker.prototype.anchor_changed;


/**
 * Converts a HTML string to a document fragment.
 *
 * @param {string} htmlString The HTML string to convert.
 * @return {Node} A HTML document fragment.
 * @private
 */
RichMarker.prototype.htmlToDocumentFragment_ = function(htmlString) {
  var tempDiv = document.createElement('DIV');
  tempDiv.innerHTML = htmlString;
  if (tempDiv.childNodes.length == 1) {
    return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild));
  } else {
    var fragment = document.createDocumentFragment();
    while (tempDiv.firstChild) {
      fragment.appendChild(tempDiv.firstChild);
    }
    return fragment;
  }
};


/**
 * Removes all children from the node.
 *
 * @param {Node} node The node to remove all children from.
 * @private
 */
RichMarker.prototype.removeChildren_ = function(node) {
  if (!node) {
    return;
  }

  var child;
  while (child = node.firstChild) {
    node.removeChild(child);
  }
};


/**
 * Sets the content of the marker.
 *
 * @param {string|Node} content The content to set.
 */
RichMarker.prototype.setContent = function(content) {
  this.set('content', content);
};
RichMarker.prototype['setContent'] = RichMarker.prototype.setContent;


/**
 * Get the content of the marker.
 *
 * @return {string|Node} The marker content.
 */
RichMarker.prototype.getContent = function() {
  return /** @type {Node|string} */ (this.get('content'));
};
RichMarker.prototype['getContent'] = RichMarker.prototype.getContent;


/**
 * Sets the marker content and adds loading events to images
 */
RichMarker.prototype.content_changed = function() {
  if (!this.markerContent_) {
    // Marker content area doesnt exist.
    return;
  }

  this.removeChildren_(this.markerContent_);
  var content = this.getContent();
  if (content) {
    if (typeof content == 'string') {
      content = content.replace(/^\s*([\S\s]*)\b\s*$/, '$1');
      content = this.htmlToDocumentFragment_(content);
    }
    this.markerContent_.appendChild(content);

    var that = this;
    var images = this.markerContent_.getElementsByTagName('IMG');
    for (var i = 0, image; image = images[i]; i++) {
      // By default, a browser lets a image be dragged outside of the browser,
      // so by calling preventDefault we stop this behaviour and allow the image
      // to be dragged around the map and now out of the browser and onto the
      // desktop.
      google.maps.event.addDomListener(image, 'mousedown', function(e) {
        if (that.getDraggable()) {
          if (e.preventDefault) {
            e.preventDefault();
          }
          e.returnValue = false;
        }
      });

      // Because we don't know the size of an image till it loads, add a
      // listener to the image load so the marker can resize and reposition
      // itself to be the correct height.
      google.maps.event.addDomListener(image, 'load', function() {
        that.draw();
      });
    }

    google.maps.event.trigger(this, 'domready');
  }

  if (this.ready_) {
    this.draw();
  }
};
RichMarker.prototype['content_changed'] = RichMarker.prototype.content_changed;

/**
 * Sets the cursor.
 *
 * @param {string} whichCursor What cursor to show.
 * @private
 */
RichMarker.prototype.setCursor_ = function(whichCursor) {
  if (!this.ready_) {
    return;
  }

  var cursor = '';
  if (navigator.userAgent.indexOf('Gecko/') !== -1) {
    // Moz has some nice cursors :)
    if (whichCursor == 'dragging') {
      cursor = '-moz-grabbing';
    }

    if (whichCursor == 'dragready') {
      cursor = '-moz-grab';
    }

    if (whichCursor == 'draggable') {
      cursor = 'pointer';
    }
  } else {
    if (whichCursor == 'dragging' || whichCursor == 'dragready') {
      cursor = 'move';
    }

    if (whichCursor == 'draggable') {
      cursor = 'pointer';
    }
  }

  if (this.markerWrapper_.style.cursor != cursor) {
    this.markerWrapper_.style.cursor = cursor;
  }
};

/**
 * Start dragging.
 *
 * @param {Event} e The event.
 */
RichMarker.prototype.startDrag = function(e) {
  if (!this.getDraggable()) {
    return;
  }

  if (!this.dragging_) {
    this.dragging_ = true;
    var map = this.getMap();
    this.mapDraggable_ = map.get('draggable');
    map.set('draggable', false);

    // Store the current mouse position
    this.mouseX_ = e.clientX;
    this.mouseY_ = e.clientY;

    this.setCursor_('dragready');

    // Stop the text from being selectable while being dragged
    this.markerWrapper_.style['MozUserSelect'] = 'none';
    this.markerWrapper_.style['KhtmlUserSelect'] = 'none';
    this.markerWrapper_.style['WebkitUserSelect'] = 'none';

    this.markerWrapper_['unselectable'] = 'on';
    this.markerWrapper_['onselectstart'] = function() {
      return false;
    };

    this.addDraggingListeners_();

    google.maps.event.trigger(this, 'dragstart');
  }
};


/**
 * Stop dragging.
 */
RichMarker.prototype.stopDrag = function() {
  if (!this.getDraggable()) {
    return;
  }

  if (this.dragging_) {
    this.dragging_ = false;
    this.getMap().set('draggable', this.mapDraggable_);
    this.mouseX_ = this.mouseY_ = this.mapDraggable_ = null;

    // Allow the text to be selectable again
    this.markerWrapper_.style['MozUserSelect'] = '';
    this.markerWrapper_.style['KhtmlUserSelect'] = '';
    this.markerWrapper_.style['WebkitUserSelect'] = '';
    this.markerWrapper_['unselectable'] = 'off';
    this.markerWrapper_['onselectstart'] = function() {};

    this.removeDraggingListeners_();

    this.setCursor_('draggable');
    google.maps.event.trigger(this, 'dragend');

    this.draw();
  }
};


/**
 * Handles the drag event.
 *
 * @param {Event} e The event.
 */
RichMarker.prototype.drag = function(e) {
  if (!this.getDraggable() || !this.dragging_) {
    // This object isn't draggable or we have stopped dragging
    this.stopDrag();
    return;
  }

  var dx = this.mouseX_ - e.clientX;
  var dy = this.mouseY_ - e.clientY;

  this.mouseX_ = e.clientX;
  this.mouseY_ = e.clientY;

  var left = parseInt(this.markerWrapper_.style['left'], 10) - dx;
  var top = parseInt(this.markerWrapper_.style['top'], 10) - dy;

  this.markerWrapper_.style['left'] = left + 'px';
  this.markerWrapper_.style['top'] = top + 'px';

  var offset = this.getOffset_();

  // Set the position property and adjust for the anchor offset
  var point = new google.maps.Point(left - offset.width, top - offset.height);
  var projection = this.getProjection();
  this.setPosition(projection.fromDivPixelToLatLng(point));

  this.setCursor_('dragging');
  google.maps.event.trigger(this, 'drag');
};


/**
 * Removes the drag listeners associated with the marker.
 *
 * @private
 */
RichMarker.prototype.removeDragListeners_ = function() {
  if (this.draggableListener_) {
    google.maps.event.removeListener(this.draggableListener_);
    delete this.draggableListener_;
  }
  this.setCursor_('');
};


/**
 * Add dragability events to the marker.
 *
 * @param {Node} node The node to apply dragging to.
 * @private
 */
RichMarker.prototype.addDragging_ = function(node) {
  if (!node) {
    return;
  }

  var that = this;
  this.draggableListener_ =
    google.maps.event.addDomListener(node, 'mousedown', function(e) {
      that.startDrag(e);
    });

  this.setCursor_('draggable');
};


/**
 * Add dragging listeners.
 *
 * @private
 */
RichMarker.prototype.addDraggingListeners_ = function() {
  var that = this;
  if (this.markerWrapper_.setCapture) {
    this.markerWrapper_.setCapture(true);
    this.draggingListeners_ = [
      google.maps.event.addDomListener(this.markerWrapper_, 'mousemove', function(e) {
        that.drag(e);
      }, true),
      google.maps.event.addDomListener(this.markerWrapper_, 'mouseup', function() {
        that.stopDrag();
        that.markerWrapper_.releaseCapture();
      }, true)
    ];
  } else {
    this.draggingListeners_ = [
      google.maps.event.addDomListener(window, 'mousemove', function(e) {
        that.drag(e);
      }, true),
      google.maps.event.addDomListener(window, 'mouseup', function() {
        that.stopDrag();
      }, true)
    ];
  }
};


/**
 * Remove dragging listeners.
 *
 * @private
 */
RichMarker.prototype.removeDraggingListeners_ = function() {
  if (this.draggingListeners_) {
    for (var i = 0, listener; listener = this.draggingListeners_[i]; i++) {
      google.maps.event.removeListener(listener);
    }
    this.draggingListeners_.length = 0;
  }
};


/**
 * Get the anchor offset.
 *
 * @return {google.maps.Size} The size offset.
 * @private
 */
RichMarker.prototype.getOffset_ = function() {
  var anchor = this.getAnchor();
  if (typeof anchor == 'object') {
    return /** @type {google.maps.Size} */ (anchor);
  }

  var offset = new google.maps.Size(0, 0);
  if (!this.markerContent_) {
    return offset;
  }

  var width = this.markerContent_.offsetWidth;
  var height = this.markerContent_.offsetHeight;

  switch (anchor) {
   case RichMarkerPosition['TOP_LEFT']:
     break;
   case RichMarkerPosition['TOP']:
     offset.width = -width / 2;
     break;
   case RichMarkerPosition['TOP_RIGHT']:
     offset.width = -width;
     break;
   case RichMarkerPosition['LEFT']:
     offset.height = -height / 2;
     break;
   case RichMarkerPosition['MIDDLE']:
     offset.width = -width / 2;
     offset.height = -height / 2;
     break;
   case RichMarkerPosition['RIGHT']:
     offset.width = -width;
     offset.height = -height / 2;
     break;
   case RichMarkerPosition['BOTTOM_LEFT']:
     offset.height = -height;
     break;
   case RichMarkerPosition['BOTTOM']:
     offset.width = -width / 2;
     offset.height = -height;
     break;
   case RichMarkerPosition['BOTTOM_RIGHT']:
     offset.width = -width;
     offset.height = -height;
     break;
  }

  return offset;
};


/**
 * Adding the marker to a map.
 * Implementing the interface.
 */
RichMarker.prototype.onAdd = function() {
  if (!this.markerWrapper_) {
    this.markerWrapper_ = document.createElement('DIV');
    this.markerWrapper_.style['position'] = 'absolute';
  }

  if (this.getZIndex()) {
    this.markerWrapper_.style['zIndex'] = this.getZIndex();
  }

  this.markerWrapper_.style['display'] = this.getVisible() ? '' : 'none';

  if (!this.markerContent_) {
    this.markerContent_ = document.createElement('DIV');
    this.markerWrapper_.appendChild(this.markerContent_);

    var that = this;
    google.maps.event.addDomListener(this.markerContent_, 'click', function(e) {
      google.maps.event.trigger(that, 'click');
    });
    google.maps.event.addDomListener(this.markerContent_, 'mouseover', function(e) {
      google.maps.event.trigger(that, 'mouseover');
    });
    google.maps.event.addDomListener(this.markerContent_, 'mouseout', function(e) {
      google.maps.event.trigger(that, 'mouseout');
    });
  }

  this.ready_ = true;
  this.content_changed();
  this.flat_changed();
  this.draggable_changed();

  var panes = this.getPanes();
  if (panes) {
    panes.overlayMouseTarget.appendChild(this.markerWrapper_);
  }

  google.maps.event.trigger(this, 'ready');
};
RichMarker.prototype['onAdd'] = RichMarker.prototype.onAdd;


/**
 * Impelementing the interface.
 */
RichMarker.prototype.draw = function() {
  if (!this.ready_ || this.dragging_) {
    return;
  }

  var projection = this.getProjection();

  if (!projection) {
    // The map projection is not ready yet so do nothing
    return;
  }

  var latLng = /** @type {google.maps.LatLng} */ (this.get('position'));
  var pos = projection.fromLatLngToDivPixel(latLng);

  var offset = this.getOffset_();
  this.markerWrapper_.style['top'] = (pos.y + offset.height) + 'px';
  this.markerWrapper_.style['left'] = (pos.x + offset.width) + 'px';

  var height = this.markerContent_.offsetHeight;
  var width = this.markerContent_.offsetWidth;

  if (width != this.get('width')) {
    this.set('width', width);
  }

  if (height != this.get('height')) {
    this.set('height', height);
  }
};
RichMarker.prototype['draw'] = RichMarker.prototype.draw;


/**
 * Removing a marker from the map.
 * Implementing the interface.
 */
RichMarker.prototype.onRemove = function() {
  if (this.markerWrapper_ && this.markerWrapper_.parentNode) {
    this.markerWrapper_.parentNode.removeChild(this.markerWrapper_);
  }
  this.removeDragListeners_();
};
RichMarker.prototype['onRemove'] = RichMarker.prototype.onRemove;


/**
 * RichMarker Anchor positions
 * @enum {number}
 */
var RichMarkerPosition = {
  'TOP_LEFT': 1,
  'TOP': 2,
  'TOP_RIGHT': 3,
  'LEFT': 4,
  'MIDDLE': 5,
  'RIGHT': 6,
  'BOTTOM_LEFT': 7,
  'BOTTOM': 8,
  'BOTTOM_RIGHT': 9
};
window['RichMarkerPosition'] = RichMarkerPosition;