/* Refine Bar, Property Type, */
$(document).ready(function () {
    $('input.idx-search-type, select.idx-search-type').bind('click', function () {
        
        /* Selected Type */
        type = $(this).val();

        /* Random Process ID */
        pid = Math.random() * 5;
        
        /* Price Ranges */
        if (type in {'Rental' : true, 'Lease' : true, 'Residential Lease' : true, 'Commercial Lease' : true}) {
            
            /* Show Rental Prices */
            $('#search_rent').show().find('select').removeAttr('disabled');
            
            /* Hide Price Ranges */
            $('#search_price').hide().find('select').attr('disabled', 'disabled');
            
        } else {
         
            /* Show Price Ranges */
            $('#search_price').show().find('select').removeAttr('disabled');
            
            /* Hide Rental Prices */
            $('#search_rent').hide().find('select').attr('disabled', 'disabled');
            
        }
        
        /* Sub-Type Selection */
        if ($('select[name="search_subtype"]').length > 0) {
            
            $.ajax({
                'url'      : '/idx/inc/php/ajax/json.php?searchTypes',
                'type'     : 'POST',
                'dataType' : 'json',
                'data'     : {
                    'pid' : pid,
                    'search_type' : type
                },
                'error'    : function (XMLHttpRequest, textStatus, errorThrown) {
                    $('input[name="search_subtype"]').hide();
                },
                'success'  : function (json, textStatus) {
                    if (typeof(json) == 'undefined') return;
                    if (typeof(json.pid) != 'undefined') {
                        if (json.pid != pid) {
                            return;
                        }
                    }
                    if (json.returnCode == 200) {
                        var html = '<select name="search_subtype">' + "\n";
                        html += '<option value=""> All ' + type + '</option>' + "\n";
    
                        if (json.options.length > 0) {
                            var i = 0, len = json.options.length;
                            var subtype = $('select[name="search_subtype"]').val();
                            while (i < len) {
                                var checked = (subtype == json.options[i].value) ? ' selected="selected"' : '';
                                html += '<option value= "' + json.options[i].value + '"' + checked + '>' + json.options[i].title + '</option>' + "\n";
                                i++;
                            }
                        }
    
    
                        html += '</select>';
                        var parent = $('select[name="search_subtype"]').parent();
                        $('select[name="search_subtype"]').remove();
                        parent.append(html);
    
                    }
                }
            });
            
        }
        
    });
    $('input.idx-search-type:checked, select.idx-search-type').trigger('click');
});

/* Enable Location Fields */
function enableLocations () {

    /* Return if Viewport Checked */
    if ($('input[name="map[viewport]"]').attr('checked')) return disableLocations();

    /* Return if PolygonSearch Exists */
    if (polygonControl != null || (polygonControl && polygonControl.polygonSearch)) return disableLocations();

    /* Return if RadiusSearch Exists */
    if (radiusControl && radiusControl._radiusSearches && radiusControl._radiusSearches.length != 0) return disableLocations();

    var $form = $('#idx-map-form');
    $form.find('input.location').removeAttr('disabled');
    $form.find('select.location').removeAttr('disabled');
    
    var $form = $('#searchForm');
    $form.find('input.location').removeAttr('disabled');
    $form.find('select.location').removeAttr('disabled');

};

/* Disable Location Fields */
function disableLocations () {

    var $form = $('#idx-map-form');
    $form.find('input.location').attr('disabled', 'disabled');
    $form.find('select.location').attr('disabled', 'disabled');
    
    var $form = $('#searchForm');
    $form.find('input.location').attr('disabled', 'disabled');
    $form.find('select.location').attr('disabled', 'disabled');

};
var glatlng;
var gmarker;
var manager;
var tooltip;
var zoom    = 12;
var gmap    = null;
var loader  = null;

/* Collection of POIs */
var points = [];

/* Map State Cookie */
var cookie_name = 'rew-map';

/* Initialize */
function initialize (container, options) {

    var container = container ? container : 'listings-map';

    var defaults  = {
        'map_cache' : true,
        'map_type' : G_NORMAL_MAP,
        'loader'   : false,
        'tooltip'  : true,
        'control_types' : true,
        'bounds_center' : true,
        'bounds_zoom'   : true
    };

    var options   = options   ? options : defaults;

    if (gmap != null) return;

    /* Create Map */
    gmap = new GMap2(document.getElementById(container));

    /* Set Map Type */
    options.map_type = (typeof(options.map_type) != 'undefined') ? options.map_type : defaults.map_type;
    if (options.map_type) {
        gmap.setMapType(options.map_type);
    }

    /* Center Map */
    gmap.setCenter(glatlng, zoom);

    /* Add 3D Map Control */
    gmap.addControl(new GLargeMapControl3D());

    /* Add Map Type Controls */
    options.control_types = (typeof(options.control_types) != 'undefined') ? options.control_types : defaults.control_types;
    if (options.control_types) {
        gmap.addMapType(G_PHYSICAL_MAP);
        var hControl = new GHierarchicalMapTypeControl();
        hControl.addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", true);
        gmap.addControl(hControl);
    }

    /* Enable Scroll Wheel Zoom */
    //gmap.enableScrollWheelZoom();

    /* POI Manager */
    manager = new MapMarkerManager (gmap, points, {
        'bounds_center' : (typeof(options.bounds_center) != 'undefined') ? options.bounds_center : defaults.bounds_center,
        'bounds_zoom'   : (typeof(options.bounds_zoom)   != 'undefined') ? options.bounds_zoom   : defaults.bounds_zoom
    });
    manager.plot();

    /* Map Loader Overlay */
    options.loader = (typeof(options.loader) != 'undefined') ? options.loader : defaults.loader;
    if (options.loader) {
        loader = new MapLoader ();
    }

    /* Map Tooltip */
    options.tooltip = (typeof(options.bounds_center) != 'undefined') ? options.tooltip : defaults.tooltip;
    if (options.tooltip) {
        tooltip = new MapTooltip('map-label');
    }

    /* Attach Save State on Move End */
    GEvent.addListener(gmap, 'moveend', function () {
        mapSave();
    });

    /* Restore Saved Map State */
    if (options.map_cache) mapRestore();

}

/**
 * Create Marker
 */
function createMarker (point, html, icon) {

    /* Create Marker */
    var marker = new GMarker(point, {icon: icon});

    /* Attach Onclick Event */
    GEvent.addListener(marker, 'click', function() {

        /* Open Tooltip */
        if (tooltip) {

            /* Show Tooltip */
    		if ($('#map-label:hidden')) {
    			tooltip.show(marker, html, true);
    		}

        }

    });

    /* Attach Mouse Over Event */
    GEvent.addListener(marker, 'mouseover', function() {
        /* Show Tooltip */
    	if (tooltip) {
    	    if ($('body').hasClass('mapHasSticky')) return;
           tooltip.show(marker, html);
        }
    });

    /* Attach Mouse Out Event */
    GEvent.addListener(marker, 'mouseout', function() {
        /* Hide Tooltip */
        if (tooltip) tooltip.hide();
    });

    /* Return GMarker */
    return marker;

}

/**
 * Format Number
 */
function formatNumber (number) {
    if (typeof(number) == 'undefined' || typeof(number) == 'object') return 0;
	x = number.toString();
	iLen = x.length;
	pos = x.indexOf(".");
	if (pos > -1){
		iLen = pos;
	}
	var temp = "";
	temp = x.substring(0, iLen);
	for (var i = 0; i < Math.floor((temp.length-(1+i))/3); i++){
		temp = temp.substring(0,temp.length-(4*i+3))+','+temp.substring(temp.length-(4*i+3));
	}
	return temp;
}

/* Map Marker */
function MapMarker (title, latitude, longitude, html, options) {

    /* Marker Instance */
    var $marker = this;

    /* Marker Options */
    $marker.options = options ? options : {};

    /* Marker Parameters */
    $marker.title      = title;
    $marker.latitude   = latitude;
    $marker.longitude  = longitude;
    $marker.html       = html;
    $marker.icon       = $marker.options.icon ? $marker.options.icon : '/img/map/ico-home.png';
    $marker.icon            = new GIcon(G_DEFAULT_ICON, $marker.icon);
    $marker.icon.shadow     = "";
    $marker.icon.iconSize   = new GSize(19, 19);
    $marker.icon.shadowSize = new GSize(0, 0);
    $marker.icon.iconAnchor = new GPoint(0, 0);
    $marker.resultdiv       = $marker.options.resultdiv ? $marker.options.resultdiv : null;

    /* Marker Point */
    $marker.point = new GLatLng ($marker.latitude, $marker.longitude);

    /* Google Marker */
    $marker.marker = new GMarker($marker.point, {
        icon: $marker.icon,
        title: $marker.title
    });

    /* Select Marker */
    $marker.select = function () {
        /* Trigger Marker Click */
        GEvent.trigger($marker.marker, 'click');
        /* Set Active Marker */
        if (typeof(manager) != 'undefined') {
            manager.activeMarker($marker);
        }
    };

    /* Highlight Marker */
    $marker.highlight = function (toggle) {
        if ($marker.resultdiv == null) return;
        var toggle = typeof(toggle) != 'undefined' ? toggle : true;
        if (toggle) {
            $($marker.resultdiv).addClass('highlight');
        } else {
            if (!$marker.isActiveMarker()) {
                $($marker.resultdiv).removeClass('highlight');
            }
        }
    };

    /* Is Active Marker */
    $marker.isActiveMarker = function () {
        if (typeof(manager) != 'undefined') {
            var active = manager.activeMarker();
            if (active != null) {
                if (active.title == $marker.title) {
                    return true;
                }
            }
        }
        return false;
    }

}

function MapMarkerManager (map, points, options) {

    /* Manager Instance */
    var $manager = this;

    /* Manager Parameters */
    $manager.map     = map;
    $manager.options = options || {};
    $manager.points  = [];
    $manager.active  = null;

    /* Manager Bounds */
    $manager.bounds = new GLatLngBounds();

    /* Add New MapMarker */
    $manager.addMarker = function (marker) {

        /* Marker Index */
        var index = $manager.points.length;

        /* Add to Collection */
        $manager.points[index] = marker;

        /* Extend Bounds */
        $manager.bounds.extend(marker.point);

    };

    /* Loop through Results */
    var i = 0, len = points.length;
    while (i < len) {
        $manager.addMarker(points[i]);
        i++;
    }

    /* Remove Existing Marker */
    $manager.removeMarker = function (title) {

        /* Find Marker */
        var marker = $manager.getMarker(title);
        if (marker) {

            // TODO

        }

    };

    /* Set Active Marker */
    $manager.activeMarker = function (marker) {
        /* Set Active Marker */
        if (typeof(marker) != 'undefined') {
            var actifve = $manager.active;
            $manager.active = marker;
            if (actifve != null) {
                actifve.highlight(false);
            }
        }
        /* Return Active Marker */
        return $manager.active;

    };

    /* Select Existing Marker */
    $manager.selectMarker = function (title) {

        /* Find Marker */
        var marker = $manager.getMarker(title);
        if (marker) {

            /* Center on Marker */
            //gmap.setCenter(marker.point);
            gmap.panTo(marker.point);

            /* Select Marker */
            marker.select();

        }

    };

    /* Get Marker by Title */
    $manager.getMarker = function (title) {

        var marker = null;

        /* Loop through Markers */
        $.each($manager.points, function (index) {

            /* Match Against Title */
            if (title == this.title) {
                marker = $manager.points[index];
                return;
            }

        });

        /* Return Marker */
        return marker;

    };

    /* Plot Manager Points */
    $manager.plot = function (points) {

        /* Show Loader */
        if (loader) loader.show();

        /* Points to Plot */
        if (typeof(points) != 'undefined') {
            var i = 0, len = points.length;
            while (i < len) {
                $manager.addMarker(points[i]);
                i++;
            }
        }

        /* Loop through Marker Collection */
        $.each($manager.points, function () {

            /* Set Scope */
            var marker  = this;
            var gmarker = this.marker;
            var html    = this.html;

            /* Add to Map */
            gmap.addOverlay(gmarker);

            /* Attach Onclick Event */
            GEvent.addListener(gmarker, 'click', function() {

                /* Open Tooltip */
                if (tooltip) {
                    /* Show Tooltip */
					if ($('#map-label:hidden')) {
					    if (html != '') {
                            tooltip.show(gmarker, html, true);
                        }
					}
                }

                /* Highlight Marker */
                marker.highlight(true);

                /* Set Active Marker */
                $manager.activeMarker(marker);

            });

            /* Attach Mouse Over Event */
            GEvent.addListener(gmarker, 'mouseover', function() {

                /* Highlight Marker */
                marker.highlight(true);

                /* Show Tooltip */
            	if (tooltip) {
                    /* Don't Activate */
                    if ($('body').hasClass('mapHasSticky')) return;
                    if (html != '') tooltip.show(gmarker, html);
                }

            });

            /* Attach Mouse Out Event */
            GEvent.addListener(gmarker, 'mouseout', function() {

                /* Hide Tooltip */
                if (tooltip) tooltip.hide();

                /* Un-Highlight Marker */
                marker.highlight(false);

            });

            i++;

        });

        /* Fit All Markers within Map */
        if (manager.bounds.getCenter().lat() != 0) {
            if ($manager.options.bounds_center) {
                gmap.setCenter(manager.bounds.getCenter());
            }
            if ($manager.options.bounds_zoom) {
                gmap.setZoom(gmap.getBoundsZoomLevel(manager.bounds));
            }
        }

        /* Hide Loader */
        if (loader) loader.hide();

    };

    /* Reset Marker Manager */
    $manager.reset = function () {

        /* Hide Tooltip */
		if (tooltip) tooltip.hide(true);

//		/* Loop through Marker Collection */
//        var i = 0, len = $manager.points.length;
//        while (i < len) {
//
//            /* Remove Listeners */
//            GEvent.clearInstanceListeners($manager.points[i].marker);
//
//            /* Remove Overlay */
//            gmap.removeOverlay($manager.points[i].marker);
//
//            /* Increment */
//            i++;
//
//        }

        /* Remove OVerlays */
        map.clearOverlays();

        /* Reset Points */
        $manager.points = [];

        /* Reset Bounds */
        $manager.bounds = new GLatLngBounds();

        /* Reset Active Marker */
        $manager.activeMarker(null);

    };

}


function MapTooltip (elem, options) {

    /* Tooltip Instance */
    var $tooltip = this;

    /* Over */
    $tooltip.over = false;

    /* Sticky */
    $tooltip.sticky = false;

    /* Tooltip Options */
    $tooltip.options = options ? options : {};

    /* Tooltip Timout Delay */
    $tooltip.delay = $tooltip.options.delay ? $tooltip.options.delay : 500;

    /* Create Container */
    $('body').append('<div id="' + elem + '" style="position: absolute; z-index: 300; display: none;"></div>');

    /* Tooltip Container */
    var $contain = $('#' + elem);

    /* Container Mouseover */
    $contain.bind('mouseover', function () {
        $tooltip.over = true;
    });

    /* Container Mouseout */
    $contain.bind('mouseout', function () {
        $tooltip.hide();
    });

    /* Show MapTooltip */
    $tooltip.show = function (marker, html, sticky) {
   		$tooltip.over   = true;
		$tooltip.sticky = (typeof(sticky) != 'undefined') ? sticky : false;
		var offset = gmap.fromLatLngToContainerPixel(marker.getLatLng());
		$contain.show().css({
			position: 'absolute',
			top:  $('#' + gmap.getContainer().id).offset().top  + offset.y + 10 + 'px',
			left: $('#' + gmap.getContainer().id).offset().left + offset.x + 10 + 'px'
		}).html(html);
		if ($tooltip.sticky) {
			$close = ('<a href="javascript:void(0);" class="action-close">hide</a>');
			$('body').addClass('mapHasSticky');
			$('.title', $contain). append($close);
			$('.action-close', $contain).click(function(){
				$tooltip.hide(true);
			});
		} else {
			$contain.removeClass('sticky');
		}
    };

    /* Hide MapTooltip */
    $tooltip.hide = function (force) {
        if (force == true) {
            $('body').removeClass('mapHasSticky');
            $tooltip.sticky = false;
            $tooltip.over = false;
            $contain.hide();
            if (typeof(manager) != 'undefined') manager.activeMarker(null);
            return;
        }
        if ($tooltip.sticky) return;
        $tooltip.over = false;
        clearTimeout($tooltip.timeout);
        $tooltip.timeout = setTimeout(function () {
            if ($tooltip.over == false) {
                $contain.hide();
                if (typeof(manager) != 'undefined') manager.activeMarker(null);
            }
        }, $tooltip.delay);
    };

}

/* Map Loader Overlay Object */
function MapLoader (options) {

    /* Loader Instance */
    var $loader = this;

    /* Loader Options */
    $loader.options = options ? options : {};

    /* Loading Overlay */
    $loader.overlays = [];
    $loader.overlays[$loader.overlays.length] = new GPolygon([new GLatLng(-85,0),new GLatLng(85,0),new GLatLng(85,90),new GLatLng(-85,90)],null,0,0,"#000",0.4);
    $loader.overlays[$loader.overlays.length] = new GPolygon([new GLatLng(-85,90),new GLatLng(85,90),new GLatLng(85,180),new GLatLng(-85,180)],null,0,0,"#000",0.4);
    $loader.overlays[$loader.overlays.length] = new GPolygon([new GLatLng(-85,180.000001),new GLatLng(85,180.000001),new GLatLng(85,270),new GLatLng(-85,270)],null,0,0,"#000",0.4);
    $loader.overlays[$loader.overlays.length] = new GPolygon([new GLatLng(-85,270),new GLatLng(85,270),new GLatLng(85,360),new GLatLng(-85,360)],null,0,0,"#000",0.4);

    /* Add Loading Overlays */
    $loader.show = function () {
        var i = 0, len = $loader.overlays.length;
        while (i < len) {
            gmap.addOverlay($loader.overlays[i]);
            i++;
        }
    }

    /* Remove Loading Overlays */
    $loader.hide = function () {
        var i = 0, len = $loader.overlays.length;
        while (i < len) {
            gmap.removeOverlay($loader.overlays[i]);
            i++;
        }
    }

}

/* Save Map State to Cookie */
function mapSave () {
    var mapzoom   = gmap.getZoom();
    var mapcenter = gmap.getCenter();
    var maplat    = mapcenter.lat();
    var maplng    = mapcenter.lng();
    var cookiestring = maplat + "_" + maplng + "_" + mapzoom;
    var exp = new Date(); //set new date object
    exp.setTime(exp.getTime() + (1000 * 60 * 60 * 24 * 30)); //set it 30 days ahead
    rew_setCookie(cookie_name, cookiestring, exp);
}

/* Load Map State from Cookie */
function mapRestore () {
    var loadedstring = rew_getCookie(cookie_name);
    var splitstr = loadedstring.split("_");
    if (splitstr != '') {
        gmap.setCenter(new GLatLng(parseFloat(splitstr[0]), parseFloat(splitstr[1])), parseFloat(splitstr[2]));
    }
}

/* Set Cookie */
function rew_setCookie (name, value, expires) {
    document.cookie = name + "=" + escape(value) + "; path=/" + ((expires == null) ? "" : "; expires=" + expires.toGMTString());
}

/* Get Cookie */
function rew_getCookie (c_name) {
    if (document.cookie.length > 0) {
        c_start = document.cookie.indexOf(c_name + "=");
        if (c_start != -1) {
            c_start = c_start + c_name.length + 1;
            c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1) c_end = document.cookie.length;
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return "";
}
/**
 * Radius Search Tools
 */

var metric = false;
var singleClick = false;
var queryCenterOptions = new Object();
var queryLineOptions = new Object();

queryCenterOptions.icon = new GIcon();
queryCenterOptions.icon.image = "/img/map/tools/arrow-center.png";
queryCenterOptions.icon.iconSize = new GSize(20, 20);
queryCenterOptions.icon.shadowSize = new GSize(0, 0);
queryCenterOptions.icon.iconAnchor = new GPoint(10, 10);
queryCenterOptions.draggable = true;
queryCenterOptions.bouncy = false;

queryLineOptions.icon = new GIcon();
queryLineOptions.icon.image = "/img/map/tools/arrow-resize.png";
queryLineOptions.icon.iconSize = new GSize(25, 20);
queryLineOptions.icon.shadowSize = new GSize(0, 0);
queryLineOptions.icon.iconAnchor = new GPoint(12, 10);
queryLineOptions.draggable = true;
queryLineOptions.bouncy = false;

function createCircle(point, radius) {
    singleClick = false;
    radiusSearch = new RadiusSearch();
    radiusSearch.initializeCircle(radius, point, gmap);
    radiusControl.addRadiusSearch(radiusSearch);
    radiusSearch.render();
}

function destination(orig, hdng, dist) {
    var R = 6371; // earth's mean radius in km
    var oX, oY;
    var x, y;
    var d = dist/R;  // d = angular distance covered on earth's surface
    hdng = hdng * Math.PI / 180; // degrees to radians
    oX = orig.x * Math.PI / 180;
    oY = orig.y * Math.PI / 180;
    //
    y = Math.asin( Math.sin(oY)*Math.cos(d) + Math.cos(oY)*Math.sin(d)*Math.cos(hdng) );
    x = oX + Math.atan2(Math.sin(hdng)*Math.sin(d)*Math.cos(oY), Math.cos(d)-Math.sin(oY)*Math.sin(y));
    //
    y = y * 180 / Math.PI;
    x = x * 180 / Math.PI;
    return new GLatLng(y, x);
}

function distance(point1, point2) {
    var R = 6371; // earth's mean radius in km
    var lon1 = point1.lng() * Math.PI / 180;
    var lat1 = point1.lat() * Math.PI / 180;
    var lon2 = point2.lng() * Math.PI / 180;
    var lat2 = point2.lat() * Math.PI / 180;
    //
    var deltaLat = lat1 - lat2
    var deltaLon = lon1 - lon2
    //
    var step1 = Math.pow(Math.sin(deltaLat/2), 2) + Math.cos(lat2) * Math.cos(lat1) * Math.pow(Math.sin(deltaLon/2), 2);
    var step2 = 2 * Math.atan2(Math.sqrt(step1), Math.sqrt(1 - step1));
    //
    return step2 * R;
}

/**
 * RadiusSearch
 */

function RadiusSearch () {

}

RadiusSearch.prototype.CIRCLE = 'circle';
//RadiusSearch.prototype.COLORS = ["#0000ff", "#0ebe09", "#ff0000", "#d68c03", "#287b72", "#ff00fc"];
RadiusSearch.prototype.COLORS = ["#0ebe09"]; // Green Only
var COLORI = 0;

RadiusSearch.prototype = new RadiusSearch();
RadiusSearch.prototype._map;
RadiusSearch.prototype._type;
RadiusSearch.prototype._radius;
RadiusSearch.prototype._dragHandle;
RadiusSearch.prototype._centerHandle;
RadiusSearch.prototype._polyline;
RadiusSearch.prototype._color ;
RadiusSearch.prototype._control;
RadiusSearch.prototype._points;
RadiusSearch.prototype._dragHandlePosition;
RadiusSearch.prototype._centerHandlePosition;

RadiusSearch.prototype.initializeCircle = function(radius, point, map) {
    this._type = this.CIRCLE;
    this._radius = radius;
    this._map = map;
    this._dragHandlePosition = destination(point, 90, this._radius / 1000);
    this._dragHandle = new GMarker(this._dragHandlePosition, queryLineOptions);
    this._centerHandlePosition = point;
    this._centerHandle = new GMarker(this._centerHandlePosition, queryCenterOptions);
    this._color = this.COLORS[COLORI++ % RadiusSearch.prototype.COLORS.length];
    map.addOverlay(this._dragHandle);
    map.addOverlay(this._centerHandle);
    var myObject = this;
    GEvent.addListener (this._dragHandle, "dragend", function() { myObject.updateCircle(1); });
    GEvent.addListener (this._dragHandle, "drag", function() { myObject.updateCircle(1); });
    GEvent.addListener(this._centerHandle, "dragend", function() { myObject.updateCircle(2); });
    GEvent.addListener(this._centerHandle, "drag", function() { myObject.updateCircle(2); });
}

RadiusSearch.prototype.updateCircle = function (type) {
    this._map.removeOverlay(this._polyline);
    if (type == 1) {
        this._dragHandlePosition = this._dragHandle.getPoint();
        this._radius = distance(this._centerHandlePosition, this._dragHandlePosition) * 1000;
        this.render();
    } else {
        this._centerHandlePosition = this._centerHandle.getPoint();
        this.render();
        this._dragHandle.setPoint(this.getEast());
    }
}

RadiusSearch.prototype.render = function() {
    if (this._type == this.CIRCLE) {
        this._points = [];
        var distance = this._radius / 1000;
        for (i = 0; i < 72; i++) {
            this._points.push(destination(this._centerHandlePosition, i * 360 / 72, distance) );
        }
        this._points.push(destination(this._centerHandlePosition, 0, distance) );
        //this._polyline = new GPolyline(this._points, this._color, 6);
        this._polyline = new GPolygon(this._points, this._color, 1, 1, this._color, 0.2);
        this._map.addOverlay(this._polyline)
        this._control.render();
    }
}

RadiusSearch.prototype.serialize = function() {

    /* Return Empty */
    if (!this._polyline) return '';

    /* Build JSON Array */
    this.wkt  = '';
    this.wkt += this._centerHandle.getPoint().lat() + ','; // radius search center latitude
    this.wkt += this._centerHandle.getPoint().lng() + ','; // radius search center longitude
    this.wkt += ((this._radius * 3.2808399) / 5280).toFixed(1); // radius in miles
    //this.wkt += this.getRadius();

    /* Return Radius Center Point */
    return this.wkt;

    /* WKT MultiPolygon */
    //this.wkt = "MULTIPOLYGON(((";
    this.wkt = "";

    /* Loop through vertex points */
    for (i = 0; i < this._polyline.getVertexCount(); i++) {

        /* Select point */
        var point = this._polyline.getVertex(i);

        /* WKT */
        this.wkt += point.lat() + " " + point.lng() + ",";

    }

    this.wkt = this.wkt.replace(/,$/,"");
    //this.wkt += ")))";

    return this.wkt;

}

RadiusSearch.prototype.remove = function() {
    this._map.removeOverlay(this._polyline);
    this._map.removeOverlay(this._dragHandle);
    this._map.removeOverlay(this._centerHandle);
}

RadiusSearch.prototype.getRadius = function() {
    return this._radius;
}

RadiusSearch.prototype.getHTML = function() {
    return "<span><font color='"+ this._color + "''>" + this.getDistHtml() + "</font></span>";
}

RadiusSearch.prototype.getDistHtml = function() {
    result = "Radius ";
    if (metric) {
        if (this._radius < 1000) {
            result += "in meters : " + this._radius.toFixed(1);
        } else {
            result += "in kilometers : " + (this._radius / 1000).toFixed(1);
        }
    } else {
        var radius = this._radius * 3.2808399;
        if (radius < 5280) {
            result += "in feet: " + radius.toFixed(1);
        } else {
            result += "in miles: " + (radius / 5280).toFixed(1);
        }
    }
    return result;
}

RadiusSearch.prototype.getNorth = function() {
    return this._points[0];
}

RadiusSearch.prototype.getSouth = function() {
    return this._points[(72/2)];
}

RadiusSearch.prototype.getEast = function() {
    return this._points[(72/4)];
}

RadiusSearch.prototype.getWest = function() {
    return this._points[(72/4*3)];
}

/**
 * RadiusControl
 */
function RadiusControl () {
    //
}

RadiusControl.prototype = new GControl();
RadiusControl.prototype._radiusSearches ;
RadiusControl.prototype._mainDiv;
RadiusControl.prototype._radiusList;

RadiusControl.prototype.initialize = function(map) {

    this._radiusSearches = new Array();

    this._mainDiv = document.getElementById('GRadiusControl');

    /* GRadiusControl Container does not exist, Create It */
    if (typeof(this._mainDiv) == 'undefined') {

        /* Create 'GRadiusControl' Container */
        this._mainDiv = document.createElement("div");
        this._mainDiv.id = "GRadiusControl";

        /* Create 'radiusList' List */
        this._radiusList = document.createElement("ul");
        this._radiusList.id = "radiusList";
        this._mainDiv.appendChild(this._radiusList);

        /* Add to DOM */
        map.getContainer().appendChild(this._mainDiv);

        return this._mainDiv;

    /* GRadiusControl Container exists, Use It */
    } else {

        /* Create 'radiusList' List */
        this._radiusList = document.createElement("ul");
        this._radiusList.id = "radiusList";
        this._mainDiv.appendChild(this._radiusList);

        /* Return Empty Container */
        return document.createElement("div");

    }

    //return this._mainDiv;

}

RadiusControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(100, 10));
}

RadiusControl.prototype.addRadiusSearch = function(radiusSearch) {
    this._radiusSearches.push(radiusSearch);
    radiusSearch._control = this;
    newDiv = document.createElement("li");
    newDiv.innerHTML = radiusSearch.getHTML();
    this._radiusList.appendChild(newDiv);
}

RadiusControl.prototype.render = function() {
    for (i = 0; i < this._radiusSearches.length; i++) {
        radiusSearch = this._radiusSearches[i];
        this._radiusList.childNodes[i].innerHTML = radiusSearch.getHTML();
    }
}

RadiusControl.prototype.removeSearch = function(index) {
    this._radiusSearches[index].remove();
    this._radiusList.removeChild(this._radiusList.childNodes[index]);
    delete this._radiusSearches[index];
    this._radiusSearches.splice(index, 1);
    this.render();
}

RadiusControl.prototype.removeSearches = function() {
    while (this._radiusSearches.length > 0) {
        this.removeSearch(0);
    }
}

RadiusControl.prototype.serialize = function() {
	if (this._radiusSearches.length <= 0) return '';
    this.json = '[';
    var i = 0, len = this._radiusSearches.length;
    while (i < len) {
        this.json += '"' + this._radiusSearches[i].serialize() + '"';
        i++;
        if (i != len) this.json += ', ';
    }
    this.json += ']';
    return this.json;
}

RadiusControl.prototype.getIndex = function(radiusSearch) {
    for (i = 0; i < this._radiusSearches.length; i++) {
        if (radiusSearch == this._radiusSearches[i]) {
            return i;
        }
    }
    return -1;
}
/**
 * Polygon Search Tools
 */

var polygon = null;

/* PolygonSearch Object */
function PolygonSearch () {

    /* Polygon */
    this.polygon = null;

    /* Control */
    this.control = null;

    /* Points */
    this.points = [];
    //this.points[this.points.length] = gmap.getCenter();

    /* Mode */
    this.mode      = 0;
    this.MODE_DRAW = 1;
    this.MODE_EDIT = 2;
    this.MODE_IDLE = 3;

    /* WKT */
    this.wkt = null;

}

/* PolygonSearch.findPoint */
PolygonSearch.prototype.findPoint = function (latitude, longitude) {

    /* Return Empty */
    if (this.polygon) {

        var i;
        var j = this.polygon.getVertexCount() - 1;
        var inPoly = false;

        /* Loop through vertex points */
        for (i = 0; i < this.polygon.getVertexCount(); i++) {

            var iPoint = this.polygon.getVertex(i);
            var jPoint = this.polygon.getVertex(j);

            if ((iPoint.lng() < longitude && jPoint.lng() >= longitude) ||
                (jPoint.lng() < longitude && iPoint.lng() >= longitude))  {

                if (iPoint.lat() + (longitude - iPoint.lng()) / (jPoint.lng() - iPoint.lng()) * (jPoint.lat() - iPoint.lat()) < latitude)  {

                    inPoly = !inPoly;

                }

            }

            j = i;

        }

        return inPoly;

    }

}

/* PolygonSearch.serialize */
PolygonSearch.prototype.serialize = function () {

    /* Return Empty */
    if (!this.polygon) return '';

    /* WKT MultiPolygon */
    //this.wkt = "MULTIPOLYGON(((";
    this.wkt = "";

    /* Loop through vertex points */
    for (i = 0; i < this.polygon.getVertexCount(); i++) {

        /* Select point */
        var point = this.polygon.getVertex(i);

        /* WKT */
        this.wkt += point.lat() + " " + point.lng() + ",";

    }

    this.wkt = this.wkt.replace(/,$/,"");
    //this.wkt += ")))";

    return this.wkt;

}

/* PolygonSearch.draw */
PolygonSearch.prototype.draw = function () {

    /* Remove Overlay */
    this.remove();

    /* Create Polygon */
    this.polygon = new GPolygon(this.points);

    /* Add Polygon to Map */
    gmap.addOverlay(this.polygon);

    /* Enable Drawing */
    this.polygon.enableDrawing();

    /* Set Mode */
    this.mode = this.MODE_DRAW;

    /* Scope */
    var polygonSearch = this;

    /* Polygon Event Listener - lineupdated */
    GEvent.addListener(this.polygon, 'lineupdated', function () {

        /* Redraw Controls */
        polygonSearch.control.draw();

    });

    /* Polygon Event Listener - endline */
    GEvent.addListener(this.polygon, 'endline', function () {

        /* Set Mode */
        polygonSearch.mode = polygonSearch.MODE_IDLE;

        /* Redraw Controls */
        polygonSearch.control.draw();

    });

    /* Polygon Event Listener - endline */
    GEvent.addListener(this.polygon, 'cancelline', function () {

        /* Remove Cancelled Polygon */
        polygonSearch.remove();

        /* Redraw Controls */
        polygonSearch.control.draw();

    });

}

/* PolygonSearch.load */
PolygonSearch.prototype.load = function (polygon) {

    /* Remove Overlay */
    this.remove();

    /* Assign Polygon */
    this.polygon = polygon;

    /* Add Polygon to Map */
    gmap.addOverlay(this.polygon);

    /* Set Mode */
    this.mode = this.MODE_IDLE;

}

/* PolygonSearch.remove */
PolygonSearch.prototype.remove = function () {

    /* Remove Overlay */
    if (this.polygon) {

        /* Disable Edit Mode */
        this.polygon.disableEditing();

        /* Remove Listeners */
        GEvent.clearInstanceListeners(this.polygon);

        /* Remove Polygon */
        this.polygon.remove();

        /* Reset Points */
        this.points = [];

        /* Clear Polygon */
        this.polygon = null;

        /* Set Mode */
        this.mode = this.MODE_IDLE;

    }

}

/* PolygonControl */
function PolygonControl(polygon) {};
PolygonControl.prototype = new GControl();

/* PolygonControl.initialize */
PolygonControl.prototype.initialize = function () {

    this.css_control = {
        'font-family' : 'Arial,sans-serif',
    	'text-align' : 'center',
        'font-size-adjust' : 'none',
        'font-style' : 'normal',
        'font-variant' : 'normal',
        'font-weight' : 'normal'
    };

    this.contain = 'GPolygonControl';

    /* GPolygonControl Container does not exist, Create It */
    if ($('#' + this.contain).length == 0) {

        /* Create Container */
        $('body').append('<div id="' + this.contain + '" class="gmnoprint"></div>');

        this.control = $("#" + this.contain).get(0);

        $(this.control).css(this.css_control);

        gmap.getContainer().appendChild(this.control);

        /* Draw Control */
        //this.draw();

        return this.control;

    /* GPolygonControl Container exists, Use it */
    } else {

        /* Get Control */
        this.control = $("#" + this.contain).get(0);

        /* Draw Control */
        //this.draw();

        /* Return Empty Container */
        return document.createElement("div");

    }

}

/* Position Control */
PolygonControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 30));
}

/* Remove Control */
PolygonControl.prototype.remove = function() {

    /* Remove PolygonSearch */
    this.polygonSearch.remove();

    /* Clear Control */
    $(this.control).html('');

}

/* Load Polygon */
PolygonControl.prototype.load = function(polygon) {

    /* Create PolygonSearch */
    this.polygonSearch = new PolygonSearch();

    /* Set Control */
    this.polygonSearch.control = this;

    /* Load Polygon */
    if (typeof(polygon) != 'undefined') {

        /* Load Polygon Search */
        this.polygonSearch.load(polygon);

    } else {

        /* Draw Polygon Search */
        this.polygonSearch.draw();

    }

    /* Draw Control */
    this.draw();

}

/* Draw Control */
PolygonControl.prototype.draw = function() {

    /* Toggle Polygon Tooltip  */
    if (this.polygonSearch.mode == this.polygonSearch.MODE_DRAW) {
        $('#rs-polygon .tooltip').show();
    } else {
        $('#rs-polygon .tooltip').hide();
    }        

    /* Scope Variable */
    var polygonSearch = this.polygonSearch;
    
    /* Reset Control Contents */
    $(this.control).html('');
    
    if (this.polygonSearch.polygon) {

//        if (this.polygonSearch.mode == this.polygonSearch.MODE_IDLE) {
//            $('<span><a href="javascript:void(0);">Draw Polygon</a></span>').appendTo(this.control).bind('click', function () {
//
//                /* Draw Polygon */
//                polygonSearch.draw();
//
//                /* Redraw */
//                polygonSearch.control.draw();
//
//            });
//        }

    } else {
        
        $('<a id="editPolygonSearch" href="javascript:void(0);"><span class="ico"></span> Draw Polygon</a></span>').appendTo(this.control).bind('click', function () {

            /* Draw Polygon */
            polygonSearch.draw();

            /* Redraw */
            polygonSearch.control.draw();

        });

    }

    if (this.polygonSearch.polygon && this.polygonSearch.polygon.getVertexCount()) {

        if (this.polygonSearch.mode == this.polygonSearch.MODE_IDLE) {
            $('<a id="editPolygonSearch" href="javascript:void(0);"><span class="ico"></span> Edit Polygon</a>').appendTo(this.control).bind('click', function () {

                /* Enable Edit Mode */
                polygonSearch.polygon.enableEditing();

                /* Set Mode */
                polygonSearch.mode = polygonSearch.MODE_EDIT;

                /* Redraw */
                polygonSearch.control.draw();

            });
        }

        if (this.polygonSearch.mode == this.polygonSearch.MODE_EDIT) {
            $('<a id="editPolygonSearch" href="javascript:void(0);"><span class="ico"></span> Done Editing</a>').appendTo(this.control).bind('click', function () {

                /* Disable Edit Mode */
                polygonSearch.polygon.disableEditing();

                /* Set Mode */
                polygonSearch.mode = polygonSearch.MODE_IDLE;

                /* Redraw */
                polygonSearch.control.draw();

            });
        }

    }

}
/**
 * @name MarkerClusterer
 * @version 1.0
 * @author Xiaoxi Wu
 * @copyright (c) 2009 Xiaoxi Wu
 * @fileoverview
 * This javascript library creates and manages per-zoom-level
 * clusters for large amounts of markers (hundreds or thousands).
 * This library was inspired by the <a href="http://www.maptimize.com">
 * Maptimize</a> hosted clustering solution.
 * <br /><br/>
 * <b>How it works</b>:<br/>
 * The <code>MarkerClusterer</code> will group markers into clusters according to
 * their distance from a cluster's center. When a marker is added,
 * the marker cluster will find a position in all the clusters, and
 * if it fails to find one, it will create a new cluster with the marker.
 * The number of markers in a cluster will be displayed
 * on the cluster marker. When the map viewport changes,
 * <code>MarkerClusterer</code> will destroy the clusters in the viewport
 * and regroup them into new clusters.
 *
 */

/*
 * 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.
 */


/**
 * @name MarkerClustererOptions
 * @class This class represents optional arguments to the {@link MarkerClusterer}
 * constructor.
 * @property {Number} [maxZoom] The max zoom level monitored by a
 * marker cluster. If not given, the marker cluster assumes the maximum map
 * zoom level. When maxZoom is reached or exceeded all markers will be shown
 * without cluster.
 * @property {Number} [gridSize=60] The grid size of a cluster in pixel. Each
 * cluster will be a square. If you want the algorithm to run faster, you can set
 * this value larger.
 * @property {Array of MarkerStyleOptions} [styles]
 * Custom styles for the cluster markers.
 * The array should be ordered according to increasing cluster size,
 * with the style for the smallest clusters first, and the style for the
 * largest clusters last.
 */

/**
 * @name MarkerStyleOptions
 * @class An array of these is passed into the {@link MarkerClustererOptions}
 * styles option.
 * @property {String} [url] Image url.
 * @property {Number} [height] Image height.
 * @property {Number} [height] Image width.
 * @property {Array of Number} [opt_anchor] Anchor for label text, like [24, 12].
 *    If not set, the text will align center and middle.
 * @property {String} [opt_textColor="black"] Text color.
 */

/**
 * Creates a new MarkerClusterer to cluster markers on the map.
 *
 * @constructor
 * @param {GMap2} map The map that the markers should be added to.
 * @param {Array of GMarker} opt_markers Initial set of markers to be clustered.
 * @param {MarkerClustererOptions} opt_opts A container for optional arguments.
 */
function MarkerClusterer(map, opt_markers, opt_opts) {
  // private members
  var clusters_ = [];
  var map_ = map;
  var maxZoom_ = null;
  var me_ = this;
  var gridSize_ = 60;
  var sizes = [53, 56, 66, 78, 90];
  var styles_ = [];
  var leftMarkers_ = [];
  var mcfn_ = null;

  var i = 0;
  for (i = 1; i <= 5; ++i) {
    styles_.push({
      'url': "/img/map/m" + i + ".png",
      'height': sizes[i - 1],
      'width': sizes[i - 1]
    });
  }

  if (typeof opt_opts === "object" && opt_opts !== null) {
    if (typeof opt_opts.gridSize === "number" && opt_opts.gridSize > 0) {
      gridSize_ = opt_opts.gridSize;
    }
    if (typeof opt_opts.maxZoom === "number") {
      maxZoom_ = opt_opts.maxZoom;
    }
    if (typeof opt_opts.styles === "object" && opt_opts.styles !== null && opt_opts.styles.length !== 0) {
      styles_ = opt_opts.styles;
    }
  }

  /**
   * When we add a marker, the marker may not in the viewport of map, then we don't deal with it, instead
   * we add the marker into a array called leftMarkers_. When we reset MarkerClusterer we should add the
   * leftMarkers_ into MarkerClusterer.
   */
  function addLeftMarkers_() {
    if (leftMarkers_.length === 0) {
      return;
    }
    var leftMarkers = [];
    for (i = 0; i < leftMarkers_.length; ++i) {
      me_.addMarker(leftMarkers_[i], true, null, null, true);
    }
    leftMarkers_ = leftMarkers;
  }

  /**
   * Get cluster marker images of this marker cluster. Mostly used by {@link Cluster}
   * @private
   * @return {Array of String}
   */
  this.getStyles_ = function () {
    return styles_;
  };

  /**
   * Remove all markers from MarkerClusterer.
   */
  this.clearMarkers = function () {
    for (var i = 0; i < clusters_.length; ++i) {
      if (typeof clusters_[i] !== "undefined" && clusters_[i] !== null) {
        clusters_[i].clearMarkers();
      }
    }
    clusters_ = [];
    leftMarkers_ = [];
    GEvent.removeListener(mcfn_);
  };

  /**
   * Check a marker, whether it is in current map viewport.
   * @private
   * @return {Boolean} if it is in current map viewport
   */
  function isMarkerInViewport_(marker) {
    return map_.getBounds().containsLatLng(marker.getLatLng());
  }

  /**
   * When reset MarkerClusterer, there will be some markers get out of its cluster.
   * These markers should be add to new clusters.
   * @param {Array of GMarker} markers Markers to add.
   */
  function reAddMarkers_(markers) {
    var len = markers.length;
    var clusters = [];
    for (var i = len - 1; i >= 0; --i) {
      me_.addMarker(markers[i].marker, true, markers[i].isAdded, clusters, true);
    }
    addLeftMarkers_();
  }

  /**
   * Add a marker.
   * @private
   * @param {GMarker} marker Marker you want to add
   * @param {Boolean} opt_isNodraw Whether redraw the cluster contained the marker
   * @param {Boolean} opt_isAdded Whether the marker is added to map. Never use it.
   * @param {Array of Cluster} opt_clusters Provide a list of clusters, the marker
   *     cluster will only check these cluster where the marker should join.
   */
  this.addMarker = function (marker, opt_isNodraw, opt_isAdded, opt_clusters, opt_isNoCheck) {
    if (opt_isNoCheck !== true) {
      if (!isMarkerInViewport_(marker)) {
        leftMarkers_.push(marker);
        return;
      }
    }

    var isAdded = opt_isAdded;
    var clusters = opt_clusters;
    var pos = map_.fromLatLngToDivPixel(marker.getLatLng());

    if (typeof isAdded !== "boolean") {
      isAdded = false;
    }
    if (typeof clusters !== "object" || clusters === null) {
      clusters = clusters_;
    }

    var length = clusters.length;
    var cluster = null;
    for (var i = length - 1; i >= 0; i--) {
      cluster = clusters[i];
      var center = cluster.getCenter();
      if (center === null) {
        continue;
      }
      center = map_.fromLatLngToDivPixel(center);

      // Found a cluster which contains the marker.
      if (pos.x >= center.x - gridSize_ && pos.x <= center.x + gridSize_ &&
          pos.y >= center.y - gridSize_ && pos.y <= center.y + gridSize_) {
        cluster.addMarker({
          'isAdded': isAdded,
          'marker': marker
        });
        if (!opt_isNodraw) {
          cluster.redraw_();
        }
        return;
      }
    }

    // No cluster contain the marker, create a new cluster.
    cluster = new Cluster(this, map);
    cluster.addMarker({
      'isAdded': isAdded,
      'marker': marker
    });
    if (!opt_isNodraw) {
      cluster.redraw_();
    }

    // Add this cluster both in clusters provided and clusters_
    clusters.push(cluster);
    if (clusters !== clusters_) {
      clusters_.push(cluster);
    }
  };

  /**
   * Remove a marker.
   *
   * @param {GMarker} marker The marker you want to remove.
   */

  this.removeMarker = function (marker) {
    for (var i = 0; i < clusters_.length; ++i) {
      if (clusters_[i].remove(marker)) {
        clusters_[i].redraw_();
        return;
      }
    }
  };

  /**
   * Redraw all clusters in viewport.
   */
  this.redraw_ = function () {
    var clusters = this.getClustersInViewport_();
    for (var i = 0; i < clusters.length; ++i) {
      clusters[i].redraw_(true);
    }
  };

  /**
   * Get all clusters in viewport.
   * @return {Array of Cluster}
   */
  this.getClustersInViewport_ = function () {
    var clusters = [];
    var curBounds = map_.getBounds();
    for (var i = 0; i < clusters_.length; i ++) {
      if (clusters_[i].isInBounds(curBounds)) {
        clusters.push(clusters_[i]);
      }
    }
    return clusters;
  };

  /**
   * Get max zoom level.
   * @private
   * @return {Number}
   */
  this.getMaxZoom_ = function () {
    return maxZoom_;
  };

  /**
   * Get map object.
   * @private
   * @return {GMap2}
   */
  this.getMap_ = function () {
    return map_;
  };

  /**
   * Get grid size
   * @private
   * @return {Number}
   */
  this.getGridSize_ = function () {
    return gridSize_;
  };

  /**
   * Get total number of markers.
   * @return {Number}
   */
  this.getTotalMarkers = function () {
    var result = 0;
    for (var i = 0; i < clusters_.length; ++i) {
      result += clusters_[i].getTotalMarkers();
    }
    return result;
  };

  /**
   * Get total number of clusters.
   * @return {int}
   */
  this.getTotalClusters = function () {
    return clusters_.length;
  };

  /**
   * Collect all markers of clusters in viewport and regroup them.
   */
  this.resetViewport = function () {
    var clusters = this.getClustersInViewport_();
    var tmpMarkers = [];
    var removed = 0;

    for (var i = 0; i < clusters.length; ++i) {
      var cluster = clusters[i];
      var oldZoom = cluster.getCurrentZoom();
      if (oldZoom === null) {
        continue;
      }
      var curZoom = map_.getZoom();
      if (curZoom !== oldZoom) {

        // If the cluster zoom level changed then destroy the cluster
        // and collect its markers.
        var mks = cluster.getMarkers();
        for (var j = 0; j < mks.length; ++j) {
          var newMarker = {
            'isAdded': false,
            'marker': mks[j].marker
          };
          tmpMarkers.push(newMarker);
        }
        cluster.clearMarkers();
        removed++;
        for (j = 0; j < clusters_.length; ++j) {
          if (cluster === clusters_[j]) {
            clusters_.splice(j, 1);
          }
        }
      }
    }

    // Add the markers collected into marker cluster to reset
    reAddMarkers_(tmpMarkers);
    this.redraw_();
  };


  /**
   * Add a set of markers.
   *
   * @param {Array of GMarker} markers The markers you want to add.
   */
  this.addMarkers = function (markers) {
    for (var i = 0; i < markers.length; ++i) {
      this.addMarker(markers[i], true);
    }
    this.redraw_();
  };

  // initialize
  if (typeof opt_markers === "object" && opt_markers !== null) {
    this.addMarkers(opt_markers);
  }

  // when map move end, regroup.
  mcfn_ = GEvent.addListener(map_, "moveend", function () {
    me_.resetViewport();
  });

}

/**
 * Create a cluster to collect markers.
 * A cluster includes some markers which are in a block of area.
 * If there are more than one markers in cluster, the cluster
 * will create a {@link ClusterMarker_} and show the total number
 * of markers in cluster.
 *
 * @constructor
 * @private
 * @param {MarkerClusterer} markerClusterer The marker cluster object
 */
function Cluster(markerClusterer) {
  var center_ = null;
  var markers_ = [];
  var markerClusterer_ = markerClusterer;
  var map_ = markerClusterer.getMap_();
  var clusterMarker_ = null;
  var zoom_ = map_.getZoom();

  /**
   * Get markers of this cluster.
   *
   * @return {Array of GMarker}
   */
  this.getMarkers = function () {
    return markers_;
  };

  /**
   * If this cluster intersects certain bounds.
   *
   * @param {GLatLngBounds} bounds A bounds to test
   * @return {Boolean} Is this cluster intersects the bounds
   */
  this.isInBounds = function (bounds) {
    if (center_ === null) {
      return false;
    }

    if (!bounds) {
      bounds = map_.getBounds();
    }
    var sw = map_.fromLatLngToDivPixel(bounds.getSouthWest());
    var ne = map_.fromLatLngToDivPixel(bounds.getNorthEast());

    var centerxy = map_.fromLatLngToDivPixel(center_);
    var inViewport = true;
    var gridSize = markerClusterer.getGridSize_();
    if (zoom_ !== map_.getZoom()) {
      var dl = map_.getZoom() - zoom_;
      gridSize = Math.pow(2, dl) * gridSize;
    }
    if (ne.x !== sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x)) {
      inViewport = false;
    }
    if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y)) {
      inViewport = false;
    }
    return inViewport;
  };

  /**
   * Get cluster center.
   *
   * @return {GLatLng}
   */
  this.getCenter = function () {
    return center_;
  };

  /**
   * Add a marker.
   *
   * @param {Object} marker An object of marker you want to add:
   *   {Boolean} isAdded If the marker is added on map.
   *   {GMarker} marker The marker you want to add.
   */
  this.addMarker = function (marker) {
    if (center_ === null) {
      /*var pos = marker['marker'].getLatLng();
       pos = map.fromLatLngToContainerPixel(pos);
       pos.x = parseInt(pos.x - pos.x % (GRIDWIDTH * 2) + GRIDWIDTH);
       pos.y = parseInt(pos.y - pos.y % (GRIDWIDTH * 2) + GRIDWIDTH);
       center = map.fromContainerPixelToLatLng(pos);*/
      center_ = marker.marker.getLatLng();
    }
    markers_.push(marker);
  };

  /**
   * Remove a marker from cluster.
   *
   * @param {GMarker} marker The marker you want to remove.
   * @return {Boolean} Whether find the marker to be removed.
   */
  this.removeMarker = function (marker) {
    for (var i = 0; i < markers_.length; ++i) {
      if (marker === markers_[i].marker) {
        if (markers_[i].isAdded) {
          map_.removeOverlay(markers_[i].marker);
        }
        markers_.splice(i, 1);
        return true;
      }
    }
    return false;
  };

  /**
   * Get current zoom level of this cluster.
   * Note: the cluster zoom level and map zoom level not always the same.
   *
   * @return {Number}
   */
  this.getCurrentZoom = function () {
    return zoom_;
  };

  /**
   * Redraw a cluster.
   * @private
   * @param {Boolean} isForce If redraw by force, no matter if the cluster is
   *     in viewport.
   */
  this.redraw_ = function (isForce) {
    if (!isForce && !this.isInBounds()) {
      return;
    }

    // Set cluster zoom level.
    zoom_ = map_.getZoom();
    var i = 0;
    var mz = markerClusterer.getMaxZoom_();
    if (mz === null) {
      mz = map_.getCurrentMapType().getMaximumResolution();
    }
    if (zoom_ >= mz || this.getTotalMarkers() === 1) {

      // If current zoom level is beyond the max zoom level or the cluster
      // have only one marker, the marker(s) in cluster will be showed on map.
      for (i = 0; i < markers_.length; ++i) {
        if (markers_[i].isAdded) {
          if (markers_[i].marker.isHidden()) {
            markers_[i].marker.show();
          }
        } else {
          map_.addOverlay(markers_[i].marker);
          markers_[i].isAdded = true;
        }
      }
      if (clusterMarker_ !== null) {
        clusterMarker_.hide();
      }
    } else {
      // Else add a cluster marker on map to show the number of markers in
      // this cluster.
      for (i = 0; i < markers_.length; ++i) {
        if (markers_[i].isAdded && (!markers_[i].marker.isHidden())) {
          markers_[i].marker.hide();
        }
      }
      if (clusterMarker_ === null) {
        clusterMarker_ = new ClusterMarker_(center_, this.getTotalMarkers(), markerClusterer_.getStyles_(), markerClusterer_.getGridSize_());
        map_.addOverlay(clusterMarker_);
      } else {
        if (clusterMarker_.isHidden()) {
          clusterMarker_.show();
        }
        clusterMarker_.redraw(true);
      }
    }
  };

  /**
   * Remove all the markers from this cluster.
   */
  this.clearMarkers = function () {
    if (clusterMarker_ !== null) {
      map_.removeOverlay(clusterMarker_);
    }
    for (var i = 0; i < markers_.length; ++i) {
      if (markers_[i].isAdded) {
        map_.removeOverlay(markers_[i].marker);
      }
    }
    markers_ = [];
  };

  /**
   * Get number of markers.
   * @return {Number}
   */
  this.getTotalMarkers = function () {
    return markers_.length;
  };
}

/**
 * ClusterMarker_ creates a marker that shows the number of markers that
 * a cluster contains.
 *
 * @constructor
 * @private
 * @param {GLatLng} latlng Marker's lat and lng.
 * @param {Number} count Number to show.
 * @param {Array of Object} styles The image list to be showed:
 *   {String} url Image url.
 *   {Number} height Image height.
 *   {Number} width Image width.
 *   {Array of Number} anchor Text anchor of image left and top.
 *   {String} textColor text color.
 * @param {Number} padding Padding of marker center.
 */
function ClusterMarker_(latlng, count, styles, padding) {
  var index = 0;
  var dv = count;
  while (dv !== 0) {
    dv = parseInt(dv / 10, 10);
    index ++;
  }

  if (styles.length < index) {
    index = styles.length;
  }
  this.url_ = styles[index - 1].url;
  this.height_ = styles[index - 1].height;
  this.width_ = styles[index - 1].width;
  this.textColor_ = styles[index - 1].opt_textColor;
  this.anchor_ = styles[index - 1].opt_anchor;
  this.latlng_ = latlng;
  this.index_ = index;
  this.styles_ = styles;
  this.text_ = count;
  this.padding_ = padding;
}

ClusterMarker_.prototype = new GOverlay();

/**
 * Initialize cluster marker.
 * @private
 */
ClusterMarker_.prototype.initialize = function (map) {
  this.map_ = map;
  var div = document.createElement("div");
  var latlng = this.latlng_;
  var pos = map.fromLatLngToDivPixel(latlng);
  pos.x -= parseInt(this.width_ / 2, 10) ;
  pos.y -= parseInt(this.height_ / 2, 10);
  var mstyle = "";
  if (document.all) {
    mstyle = 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="' + this.url_ + '");';
  } else {
    mstyle = "background:url(" + this.url_ + ");";
  }
  if (typeof this.anchor_ === "object") {
    if (typeof this.anchor_[0] === "number" && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) {
      mstyle += 'height:' + (this.height_ - this.anchor_[0]) + 'px;padding-top:' + this.anchor_[0] + 'px;';
    } else {
      mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
    }
    if (typeof this.anchor_[1] === "number" && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) {
      mstyle += 'width:' + (this.width_ - this.anchor_[1]) + 'px;padding-left:' + this.anchor_[1] + 'px;';
    } else {
      mstyle += 'width:' + this.width_ + 'px;text-align:center;';
    }
  } else {
    mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
    mstyle += 'width:' + this.width_ + 'px;text-align:center;';
  }
  var txtColor = this.textColor_ ? this.textColor_ : 'black';

  div.style.cssText = mstyle + 'cursor:pointer;top:' + pos.y + "px;left:" +
      pos.x + "px;color:" + txtColor +  ";position:absolute;letter-spacing: -0.5px;font-size:9px; line-height: 120%;" +
      'font-family:"Trebuchet MS",Arial,sans-serif;font-weight:bold';
  div.innerHTML = this.text_;
  div.title = 'Click to Zoom In';
  map.getPane(G_MAP_MARKER_PANE).appendChild(div);
  //map.getPane(G_MAP_FLOAT_PANE).appendChild(div);
  //map.getPane(G_MAP_OVERLAY_LAYER_PANE).appendChild(div);
  //map.getPane(G_MAP_MAP_PANE).appendChild(div);
  var padding = this.padding_;
  GEvent.addDomListener(div, "click", function () {

    var pos = map.fromLatLngToDivPixel(latlng);
    var sw = new GPoint(pos.x - padding, pos.y + padding);
    sw = map.fromDivPixelToLatLng(sw);
    var ne = new GPoint(pos.x + padding, pos.y - padding);
    ne = map.fromDivPixelToLatLng(ne);
    var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize());
    map.setCenter(latlng, zoom);

  });

  this.div_ = div;
};

/**
 * Remove this overlay.
 * @private
 */
ClusterMarker_.prototype.remove = function () {
  this.div_.parentNode.removeChild(this.div_);
};

/**
 * Copy this overlay.
 * @private
 */
ClusterMarker_.prototype.copy = function () {
  return new ClusterMarker_(this.latlng_, this.index_, this.text_, this.styles_, this.padding_);
};

/**
 * Redraw this overlay.
 * @private
 */
ClusterMarker_.prototype.redraw = function (force) {
  if (!force) {
    return;
  }
  var pos = this.map_.fromLatLngToDivPixel(this.latlng_);
  pos.x -= parseInt(this.width_ / 2, 10);
  pos.y -= parseInt(this.height_ / 2, 10);
  this.div_.style.top =  pos.y + "px";
  this.div_.style.left = pos.x + "px";
};

/**
 * Hide this cluster marker.
 */
ClusterMarker_.prototype.hide = function () {
  this.div_.style.display = "none";
};

/**
 * Show this cluster marker.
 */
ClusterMarker_.prototype.show = function () {
  this.div_.style.display = "";
};

/**
 * Get whether the cluster marker is hidden.
 * @return {Boolean}
 */
ClusterMarker_.prototype.isHidden = function () {
  return this.div_.style.display === "none";
};


/* Polygon Search Control */
var polygonControl = null;

/* Radius Search Control */
var radiusControl = null;
var radiusClickListener = null;

/* Document Ready */
$(document).ready(function () {

    /* Trigger */
    enableLocations();

    $('input[name="map[viewport]"]').bind('change', function () {
        if ($(this).attr('checked')) {
            disableLocations();
        } else {
            enableLocations();
        }
    });

    /* Toggle Polygon Search Tools */
    $('#togglePolygonSearch').bind('click', function () {
        loadPolygonControl();
    });

    /* Toggle Radius Search Tools */
    $('#drawRadiusSearch').bind('click', function () {
        loadRadiusControl();
    });
    
    /* Tooltip Close Action */    
    $('#rs-polygon .tooltip .action-close, #rs-radius .tooltip .action-close').bind('click', function () {
        $(this).closest('.tooltip').hide();
    });

});

/* Load Polygon Control */
function loadPolygonControl (polygon) {

    var button = $('#togglePolygonSearch');

    /* Turn it On */
    if (polygonControl == null) {

        /* Create Control */
        polygonControl = new PolygonControl();

        /* Add Control */
        gmap.addControl(polygonControl);

        /* Load Polygon Search */
        polygonControl.load(polygon);

        /* Update Button */
        button.html('<span class="ico"></span> Clear Polygon').addClass('on');

        /* Disable Location Fields */
        disableLocations();
        
        /* Show Polygon Tooltip  */
        $('#rs-polygon .tooltip').show();

    /* Turn it Off */
    } else {

        /* Remove Control */
        polygonControl.remove();

        /* Remove Control */
        gmap.removeControl(polygonControl);
        polygonControl = null;

        /* Update Button */
        button.html('<span class="ico"></span> Draw Polygon').removeClass('on');

        /* Enable Location Fields */
        enableLocations();
        
        /* Hide Polygon Tooltip  */
        $('#rs-polygon .tooltip').hide();

    }

}

/* Load Radius Control */
function loadRadiusControl (radius) {

    var button = $('#drawRadiusSearch');

    /* Cancel Draw Tool */
    if (button.hasClass('cancel')) {

        /* Remove Listener */
        GEvent.removeListener(radiusClickListener);

        /* Update Button */
        button.html('<span class="ico"></span> Draw Radius').removeClass('cancel');

        /* Enable Location Fields */
        enableLocations();
        
        /* Hide Radius Tooltip  */
        $('#rs-radius .tooltip').hide();

    /* Reset Tool */
    } else if (button.hasClass('reset')) {

        /* Remove Listener */
        radiusControl.removeSearches();

        /* Update Button */
        button.html('<span class="ico"></span> Draw Radius').removeClass('reset');

        /* Enable Location Fields */
        enableLocations();
        
        /* Hide Radius Tooltip  */
        $('#rs-radius .tooltip').hide();

    /* Activate Draw Tool */
    } else {

        /* Create Control */
        if (radiusControl == null) {
            radiusControl = new RadiusControl();
            gmap.addControl(radiusControl);
        }

        /* Show Radius Tooltip  */
        $('#rs-radius .tooltip').show();
        
        /* Update Button */
        button.html('<span class="ico"></span> Cancel Draw').addClass('cancel');

        /* Load Radius Search */
        if (typeof(radius) != 'undefined') {

            /* Switch from Miles to Feet */
            radius.radius = ((radius.radius / 3.2808399) * 5280).toFixed(1)

            /* Draw Circle */
            createCircle(new GLatLng(radius.latitude, radius.longitude), radius.radius);

            /* Update Button */
            button.html('<span class="ico"></span> Clear Radius').removeClass('cancel').addClass('reset');
            
            /* Hide Radius Tooltip  */
            $('#rs-radius .tooltip').hide();

        } else {
            
            /* Map Click Listener */
            //radiusClickListener = GEvent.bind(gmap, "click", this, function (overlay, latlng) {
            radiusClickListener = GEvent.addListener(gmap,"click", function(overlay, latlng) { 
                if (latlng) {

                    /* Draw Circle */
                    createCircle(new GLatLng(latlng.y, latlng.x), 1610);

                    /* Remove Listener */
                    GEvent.removeListener(radiusClickListener);

                    /* Update Button */
                    button.html('<span class="ico"></span> Clear Radius').removeClass('cancel').addClass('reset');
                    
                    /* Hide Radius Tooltip  */
                    $('#rs-radius .tooltip').hide();

                }
            });

        }

        /* Disable Location Fields */
        disableLocations();

    }

}

/**
* Google Streetview Functions
*/
function openPanoramaBubble() {

    /* Hide Tooltip */
    if (tooltip) tooltip.hide(true);

    var contentNode = document.createElement('div');
    contentNode.style.textAlign = 'center';
    contentNode.style.width = '100%';
    contentNode.style.height = '400px';

    $.facebox({
        title : 'Google&trade; Streetview',
        data  : '<div id="streetview-pano" style="width: 450px; height: 450px;"></div>'
    });

    var loadStreetview = function () {

        /* Load Panorama */
        panorama = new GStreetviewPanorama(document.getElementById("streetview-pano"));
        panorama.setLocationAndPOV(svm.getLatLng(), null);

        /* Event Listeners */
        GEvent.addListener(panorama, "initialized", onNewLocation);
        GEvent.addListener(panorama, "yawchanged", onYawChange);

        /* Check Resize */
        panorama.checkResize();

    }

    setTimeout(loadStreetview, 200);

}

function onYawChange(newYaw) {
    var GUY_NUM_ICONS = 16;
    var GUY_ANGULAR_RES = 360/GUY_NUM_ICONS;
    if (newYaw < 0) {
        newYaw += 360;
    }
    guyImageNum = Math.round(newYaw/GUY_ANGULAR_RES) % GUY_NUM_ICONS;
    guyImageUrl = "http://maps.gstatic.com/mapfiles/cb/man_arrow-" + guyImageNum + ".png";
    svm.setImage(guyImageUrl);
}

function onNewLocation(location) {
    svm.setLatLng(location.latlng);
}

function onDragEnd() {
    var latlng = svm.getLatLng();
    svc = new GStreetviewClient();
    svc.getNearestPanorama(latlng, onResponse);
    gmap.closeInfoWindow();
}

function onResponse(response) {
    if (response.code != 200) {
        if (lastMarkerLocation != null) svm.setLatLng(lastMarkerLocation);
    } else {
        var latlng = new GLatLng(response.Location.lat, response.Location.lng);
        svm.setLatLng(latlng);
        lastMarkerLocation = latlng;
        openPanoramaBubble();
    }
}/* hide while loading */
$('#feature .panes').css({'position':'absolute', 'left':'-999em'});

/* Onload */
$(document).ready(function () {

    var feature = 'test';
    $("ul.tabs").tabs("div.panes > div",{
        'tabs'    : 'li',
        'effect'  : 'fade',
        'onClick' : function() {
            $('#feature .photo img:visible').not('.fixed').each(function(){
                $(this).css({width:'auto', height:'auto'}).rewImgSizer({method:'crop'}).addClass('fixed');
            });
        }
    });
    $("#pane-1").load('/idx/inc/php/ajax/featured-listings.php?limit=5', function(){
        feature = $("#pane-1").scrollable({
            'clickable' : false,
            'size'      : 1,
            'speed'     : 900,
            'loop'      : true,
            'easing'    : 'swing',
            'onBeforeSeek' : function() {
                if ($('#feature #pane-1').is(':visible')) {
                    $('#feature #pane-1').fadeOut(450).fadeIn(450);
                }
            }
        }).autoscroll({'interval' : 15000, 'api' : true });
        $('#feature .panes').css({'position':'relative', 'left':'0'});
        Cufon.refresh();
    });

});
