(function($){
	//扩展：$().isMapCanvas()
	$.fn.isMapCanvas = function(admin) {
		if (this.size()==0) { return; }
		var _root = this;
		var _map;
		var _init = function() {
			_class.root = _root;
			var defaultLat = _root.data('latitude') || 39.91;
			var defaultLng = _root.data('longitude') || 116.4602;
			var zoom = _root.data('zoom') || 15;
			_map = _root.googleMap(defaultLat, defaultLng, zoom, admin);
			google.maps.event.addListener(_map, 'dragend',
					_class.dragEnd);
			google.maps.event.addListener(_map, 'zoom_changed',
					_class.zoomChanged);
			google.maps.event.addListener(_map, 'click',
					_class.mapClicked);
			_root.data('class', _class);
		};
		var _class = {
			// Constants
			RED_PIN:    1,
			BLUE_PIN:   2,
			GOLD_PIN: 3,
			RED_DOT_TINY:    4,
			BLUE_DOT:   5,
			RED_DOT:    6,
			GOLD_DOT:    7,
			GOLD_DOT_TINY:    8,
			RED_STAR:    9,
			GOLD_STAR:    10,
			DEFAULT_PIN_OPTIONS: {
				latitude: 0,
				longitude: 0,
				style:1,
				title:'',
				zIndex:0,
				draggable:false,
				onClick:null,
				onDragEnd:null,
				onDragStart:null,
				onDrag:null,
				radius:false
			},
			DEFAULT_BUBBLE_OPTIONS: {
				latitude: 0,
				longitude: 0,
				size:'normal',
				content:''
			},

			// Properties
			root: null,
			pinHash: {},
			circleHash: {},
			bubbleHash: {},

			// Methods
			getMap: function() {
				return _map;
			},
			getZoom: function() {
				return _map.getZoom();
			},
			zoomIn: function() {
				var currentZoom = _map.getZoom();
				_map.setZoom(currentZoom+1);
			},
			zoomOut: function() {
				var currentZoom = _map.getZoom();
				_map.setZoom(currentZoom-1);
			},
			getCenter: function() {
				var latLng = _map.getCenter();
				return [latLng.lat(), latLng.lng()];
			},
			getBounds: function() {
				var bounds = _map.getBounds();
				return [bounds.getSouthWest().lat(),
						bounds.getSouthWest().lng(),
						bounds.getNorthEast().lat(),
						bounds.getNorthEast().lng()];
			},
			boundsContains: function(latitude, longitude) {
				var bounds = _map.getBounds();
				var latLng = new google.maps.LatLng(latitude, longitude);
				return bounds.contains(latLng);
			},
			setCenter: function(latitude, longitude) {
				var latLng = new google.maps.LatLng(latitude, longitude);
				_map.setCenter(latLng);
				_class.boundsChanged();
			},
			setZoom: function(zoom) { //by jumbo
				_map.setZoom(zoom);
				_class.boundsChanged();
			},
			panTo: function(latitude, longitude) {
				var latLng = new google.maps.LatLng(latitude, longitude);
				_map.panTo(latLng);
			},
			panBy: function(rightPx, downPx) {
				_map.panBy(rightPx, downPx);
			},
			getPinPosition: function(pinId) {
				var pin = _class.pinHash[pinId];
				if (!pin) {
					return;
				}
				var latLng = pin.getPosition();
				return [latLng.lat(), latLng.lng()];
			},
			pin: function(pinId, options) {
				if (!pinId) {
					return;
				}
				var pin = _class.pinHash[pinId];
				var updating = (pin) ? true : false;
				if (!updating) {
					options = $.extend({}, _class.DEFAULT_PIN_OPTIONS, options);
				}
				var icon;
				var shadow;
				if (options.style) {
					if (options.style == _class.RED_PIN) {
						icon = 'images/map-pinMd-red';
						shadow = 'images/map-pinMd-shadow.png';
					} else if (options.style == _class.BLUE_PIN) {
						icon = 'images/map-pinMd-blue';
						shadow = 'images/map-pinMd-shadow.png';
					} else if (options.style == _class.GOLD_PIN) {
						icon = 'images/map-pinMd-gold';
						shadow = 'images/map-pinMd-shadow.png';
					} else if (options.style == _class.RED_DOT_TINY) {
						icon = 'images/map-dot-red-tiny';
						shadow = false;
					} else if (options.style == _class.BLUE_DOT) {
						icon = 'images/map-dot-blue';
						shadow = false;
					} else if (options.style == _class.RED_DOT) {
						icon = 'images/map-dot-red';
						shadow = false;
					} else if (options.style == _class.GOLD_DOT) {
						icon = 'images/map-dot-gold';
						shadow = false;
					} else if (options.style == _class.GOLD_DOT_TINY) {
						icon = 'images/map-dot-gold';
						shadow = false;
					} else if (options.style == _class.RED_STAR) {
						icon = 'images/map-star-red';
						shadow = 'images/map-star-shadow.png';
					} else if (options.style == _class.GOLD_STAR) {
						icon = 'images/map-star-gold';
						shadow = 'images/map-star-shadow.png';
					}
					if ($('body').hasClass('windows')) {
						icon += '-win'
					}
					icon += '.png';
				}
				if (!updating) {
					pin = new google.maps.Marker();
					_class.pinHash[pinId] = pin;
				}
				if (options.latitude && options.longitude) {
					var latLng = new google.maps.LatLng(options.latitude,
							options.longitude);
					pin.setPosition(latLng);
				}
				if (options.title) {
					pin.setTitle(options.title);
				}
				if (typeof(options.zIndex) != 'undefined') {
					if (!options.zIndex) {
						options.zIndex = 0;
					}
					pin.setZIndex(options.zIndex);
				}
				if (icon) {
					pin.setIcon(icon);
				}
				if (shadow === false) {
					pin.setShadow();
				} else if (shadow) {
					pin.setShadow(shadow);
				}
				if (!updating) {
					pin.setMap(_class.getMap());
				}
				if (options.draggable === true) {
					pin.setDraggable(true);
				} else if (options.draggable === false) {
					pin.setDraggable(false);
				}
				if (typeof(options.onDragEnd) == 'function') {
					google.maps.event.addListener(pin, 'dragend',
							options.onDragEnd);
				}
				if (typeof(options.onDragStart) == 'function') {
					google.maps.event.addListener(pin, 'dragstart',
							options.onDragStart);
				}
				if (typeof(options.onDrag) == 'function') {
					google.maps.event.addListener(pin, 'drag',
							options.onDragStart);
				}
				if (typeof(options.onClick) == 'function') {
					google.maps.event.addListener(pin, 'click',
						options.onClick);
				}
				if (options.radius === false) {
					_class.removeCircle(pinId);
				} else if (options.radius) {
					_class.circle(pinId, options.radius);
				}
				return pin;
			},
			removePin: function(pinId) {
				var pin = _class.pinHash[pinId];
				if (pin) {
					pin.setMap();
					delete _class.pinHash[pinId];
					_class.removeCircle(pinId);
				}
			},
			circle: function(pinId, radius) {
				if (radius == 0) {
					return;
				}
				var circle = _class.circleHash[pinId];
				var updating = (!circle) ? false : true;
				if (!updating) {
					circle = new google.maps.Circle({
						fillColor:'#b2bed4',
						fillOpacity:.5,
						strokeColor:'#2987ff',
						strokeWeight:1.5,
						strokeOpacity:.3
					});
				}
				circle.setRadius(radius);
				circle.setMap(_map);
				if (!updating) {
					_class.circleHash[pinId] = circle;
					google.maps.event.addListener(circle, 'click',
							_class.mapClicked);
				}
				var pin = _class.pinHash[pinId];
				circle.bindTo('center', pin, 'position');
			},
			removeCircle: function(pinId) {
				var circle = _class.circleHash[pinId];
				if (circle) {
					circle.setMap();
					delete _class.circleHash[pinId];
				}
			},
			bubble: function(bubbleId, options) {
				_class.removeBubble(bubbleId);
				options = $.extend(_class.DEFAULT_BUBBLE_OPTIONS, options);
				var latLng = new google.maps.LatLng(options.latitude,
						options.longitude);
				var bubble = new GlassBubble({latlng: latLng, map: _map,
						content: options.content, size:options.size,
						offsetVertical: options.offsetVertical,
						offsetHorizontal: options.offsetHorizontal});
				function removeBubbleClosure(bubbleId) {
					return function(){_class.removeBubble(bubbleId)};
				}
				google.maps.event.addListener(_map, 'click',
						removeBubbleClosure(bubbleId));
				_class.bubbleHash[bubbleId] = bubble;
				return bubble;
			},
			removeBubble: function(bubbleId) {
				if (_class.bubbleHash[bubbleId]) {
					_class.bubbleHash[bubbleId].close();
					delete _class.bubbleHash[bubbleId];
				}
			},
			removeAllBubbles: function() {
				for (var bubbleId in _class.bubbleHash) {
					_class.bubbleHash[bubbleId].close();
					delete _class.bubbleHash[bubbleId];
				}
			},
			findMe: function(callback) {
				if (navigator.geolocation) {
					function applyCallback(position) {
						var latitude = position
								? position.coords.latitude : null;
						var longitude = position
								? position.coords.longitude : null;
						/*
						_class.setCenter(latitude, longitude);
						_class.pin('me', {
							latitude: latitude,
							longitude: longitude,
							style: _class.BLUE_DOT
						});
						*/
						if (typeof(callback) == 'function') {
							callback(latitude, longitude);
						}
					}
					navigator.geolocation.getCurrentPosition(
							applyCallback, applyCallback, {timeout:7000});
				}
			},
			geocode: function(city, query, callback) {
				if (!query) {
					return false;
				}
				var geocoder = new google.maps.Geocoder();
				var sublocality = '';
				function applyCallback(response, statusCode) {
					if (typeof(callback) == 'function') {
						callback(city, query, response, statusCode);
					}
				}
				geocoder.geocode({'address': city + sublocality + query,
						'language': 'zh-CN'}, applyCallback);
			},
			one: function(events, callback) {
				_root.one(events, callback);
			},
			bind: function(events, callback) {
				_root.bind(events, callback);
			},
			zoomChanged: function() {
				_root.trigger('zoom_changed');
				_root.trigger('bounds_changed');
			},
			dragEnd: function() {
				_root.trigger('dragend');
				_root.trigger('bounds_changed');
			},
			boundsChanged: function() {
				_root.trigger('bounds_changed');
			},
			mapClicked: function() {
				_root.trigger('map_clicked');
			},
			toast: function(content) {
				$('#map_notification').html(content);
				var notificationOuter = $('#map_notification_outer');
				notificationOuter.css('top', (_root.outerHeight(true) - 180) / 2);
				notificationOuter.fadeIn(300).delay(1500).fadeOut(350);
			}
		};
		_init();
		return _class;
	};
})(jQuery);
