var Map = { };

Map.Search = new Class
({
 options:
 {
 spacing: 15,
 query: '/',
 reload: 9,
 advanced: '/',
 reset: '/',
 save: '/',
 minheight: 350,
 minwidth: 400
 },

 initialize: function(el, options)
 {
 this.setOptions(options);

 this.el = $(el);
 this.limitstart = 0;

 this.bound =
 {
 'advanced': this.advanced.bindWithEvent(this),
 'draw': this.draw.bindWithEvent(this),
 'reset': this.reset.bindWithEvent(this),
 'query': this.query.bindWithEvent(this),
 'prevent': this.prevent.bindWithEvent(this),
 'save': this.save.bindWithEvent(this),
 'submit': this.submit.bindWithEvent(this)
 }

 this.el.addEvent('keypress', this.bound.prevent);

 this.el.addEvent('submit', this.bound.submit);

 this.search = new Element
 (
 'div',
 {
 'class': 'search'
 }
 ).injectInside(this.el).setHTML(options.form);

 this.search.getElements('button').each
 (
 function(el, i)
 {
 if (el.title == 'advanced') el.addEvent('click', this.bound.advanced);
 else if (el.title == 'zoom') el.addEvent('click', this.bound.draw);
 else if (el.title == 'reset') el.addEvent('click', this.bound.reset);
 else if (el.title == 'save') el.addEvent('click', this.bound.save);
 else el.addEvent('click', this.bound.submit);

 el.title = '';
 }.bind(this)
 );

 this.rangers = [];
 $ES('.slider', this.search).each
 (
 function (el, i)
 {
 var data = Json.evaluate(el.title);
 el.title = '';
 this.rangers.include(new Ranger(el, data));
 },
 this
 );

 this.el.getElements('input').each
 (
 function(el, i)
 {
 el.addEvent('change', this.bound.query);
 },
 this
 );

 this.container = new Element
 (
 'div',
 {
 'class': 'container',
 'styles':
 {
 'position': 'relative'
 }
 }
 ).injectInside(this.el);

 this.results = new Element
 (
 'div',
 {
 'class': 'results',
 'styles':
 {
 'position': 'absolute',
 'left': this.options.spacing,
 'top': this.options.spacing
 }
 }
 ).injectInside(this.container);

 this.sort = new Element
 (
 'div',
 {
 'class': 'sort'
 }
 ).injectInside(this.results);

 this.list = new Element
 (
 'div',
 {
 'class': 'list'
 }
 ).injectInside(this.results);

 this.map = new Element
 (
 'div',
 {
 'class': 'map',
 'styles':
 {
 'position': 'absolute',
 'top': this.options.spacing
 }
 }
 ).injectInside(this.container);

 this.bounds = new google.maps.LatLngBounds();

 this.resize();

 this.gmap = new google.maps.Map2(this.map);
 this.gmap.addControl(new google.maps.LargeMapControl());
 this.gmap.enableScrollWheelZoom();

 this.listen = google.maps.Event.addListener(this.gmap, 'moveend', this.move.bind(this));

 this.manager = new Map.Manager(this.gmap, this.list, options);

 window.addEvent('resize', this.resize.bind(this));

 this.ajax = new Ajax
 (
 this.options.query,
 {
 onSuccess: this.load.bind(this),
 method: 'post',
 autoCancel: true
 }
 );

 this.attempt = new Ajax
 (
 this.options.query,
 {
 onSuccess: this.trial.bind(this),
 method: 'post',
 autoCancel: true
 }
 );

 this.script = new Ajax
 (
 this.options.advanced,
 {
 onSuccess: this.parse.bind(this),
 method: 'get',
 autoCancel: true
 }
 );

 this.selectable = new Selectable
 (
 this.gmap.getContainer(),
 {
 onComplete: this.select.bind(this)
 }
 );

 this.ajax.send(this.el.action, '');
 },

 prevent: function(event)
 {
 if (event.code == 13) this.submit(event);
 },

 submit: function(event)
 {
 try { event.preventDefault().stopPropagation(); }
 catch (err) { }

 this.query();

 return false;
 },

 save: function(event)
 {
 event.stopPropagation().preventDefault();

 this.script.send(this.options.save);
 },

 advanced: function(event)
 {
 event.stopPropagation().preventDefault();

 this.manager.empty();
 this.sort.empty();
 this.list.addClass('loading');

 this.script.send(this.options.advanced);
 },

 parse: function(event)
 {
 if ((/(ecma|java)script/).test(this.script.getHeader('Content-type'))) eval(this.script.response.text);
 },

 draw: function(event)
 {
 event.stopPropagation().preventDefault();

 if (this.selectable.attached) this.selectable.detach();
 else this.selectable.attach();
 },

 select: function(dim)
 {
 if (dim.w < 15 || dim.h < 15) return;

 var bounds = new google.maps.LatLngBounds();
 bounds.extend(this.gmap.fromContainerPixelToLatLng(new google.maps.Point(dim.x, dim.y)));
 bounds.extend(this.gmap.fromContainerPixelToLatLng(new google.maps.Point(dim.x + dim.w, dim.y + dim.h)));

 this.manager.empty();
 this.sort.empty();
 this.list.addClass('loading');

 this.bounds = new google.maps.LatLngBounds();

 this.attempt.send(this.el.action, 'limitstart=0&search[x_latitude][>]=' + bounds.getNorthEast().lat() + '&search[x_longitude][>]=' + bounds.getNorthEast().lng() + '&search[x_latitude][<]=' + bounds.getSouthWest().lat() + '&search[x_longitude][<]=' + bounds.getSouthWest().lng());
 },

 resize: function()
 {
 var height = window.getSize().size.y - this.container.getTop();
 height = height.limit(this.options.minheight, this.options.minheight + height);
 this.container.setStyle('height', height + 'px');

 height -= (this.options.spacing * 2);
 this.results.setStyle('height', height + 'px');

 var width = this.container.offsetWidth - this.map.getStyle('left').toInt();
 width = width.limit(this.options.minwidth, this.options.minwidth + width);
 width -= this.options.spacing

 this.map.setStyles
 ({
 'height': height + 'px',
 'width': width + 'px'
 });

 this.list.setStyle('height', (height - (this.list.getTop() - this.results.getTop())) + 'px');

 if (!this.bounds.isEmpty()) this.center(this.bounds);

 if (window.ie6) this.el.setStyle('display', 'none').setStyle('display', 'block');
 },

 move: function()
 {
 if (this.bounds.isEmpty()) return;
 else if (this.gmap.getZoom() < this.options.reload) return;
 else if (this.manager.visible() != 0)
 {
 if (!(Math.abs(this.gmap.getZoom() - this.zoom) > 1))
 {
 if (this.gmap.getBounds().containsLatLng(this.bounds.getCenter())) return;
 }
 }

 this.reload();
 },

 reset: function(event)
 {
 try { event.preventDefault().stopPropagation(); }
 catch (err) { }

 this.empty();
 this.bounds = new google.maps.LatLngBounds();

 this.ajax.send(this.el.action, 'reset=1&search[x_latitude][<]=-90&search[x_longitude][<]=-180&search[x_latitude][>]=90&search[x_longitude][>]=180');
 },

 build: function()
 {
 var query = '';

 if (!this.bounds.isEmpty())
 {
 var latlng = this.gmap.getBounds().getSouthWest();
 query = 'search[x_latitude][<]=' + latlng.lat();
 query += '&search[x_longitude][<]=' + latlng.lng();

 latlng = this.gmap.getBounds().getNorthEast();
 query += '&search[x_latitude][>]=' + latlng.lat();
 query += '&search[x_longitude][>]=' + latlng.lng();
 }
 else
 {
 query = 'search[x_latitude][<]=-90';
 query += '&search[x_longitude][<]=-180';
 query += '&search[x_latitude][>]=90';
 query += '&search[x_longitude][>]=180';
 }

 return query;
 },

 empty: function()
 {
 this.manager.empty();
 this.sort.empty();
 this.list.addClass('loading');
 },

 query: function(event)
 {
 try { event.preventDefault().stopPropagation(); }
 catch (err) { }

 this.empty();

 if (this.limitstart == this.el.limitstart.value) this.el.limitstart.value = 0;
 else
 {
 this.ajax.send(this.el.action, 'limitstart=' + this.el.limitstart.value);
 return;
 }

 var query = this.el.toQueryString();

 query += '&' + this.build();
 this.ajax.send(this.el.action, query);
 },

 center: function(bounds)
 {
 if (bounds.isEmpty()) return;
 this.gmap.checkResize();
 this.gmap.setCenter(bounds.getCenter(), this.gmap.getBoundsZoomLevel(bounds));
 },

 reload: function()
 {
 var query = (query == null) ? 'limitstart=0' : query;
 this.empty();
 this.ajax.send(this.el.action, query + "&" + this.build());
 },

 trial: function()
 {
 this.list.removeClass('loading');
 json = Json.evaluate(this.attempt.response.text);

 if (!$defined(json.listings) || json.listings.length < 1)
 {
 this.list.setHTML('<h2>No Results Found</h2>');
 this.bounds = this.gmap.getBounds();
 this.query.delay(2500, this);
 }
 else
 {
 this.ajax.response.text = this.attempt.response.text;
 this.load();
 }
 },

 load: function()
 {
 this.list.removeClass('loading');
 json = Json.evaluate(this.ajax.response.text);

 if (json.eval.length > 0) eval(json.eval);

 this.el.getElements('input').each
 (
 function(el, i)
 {
 switch (el.type)
 {
 case 'checkbox':
 el.checked = false;
 break;
 case 'text':
 if (el.value.substring(0, 2) != '**') el.value = '';
 break;
 default:
 break;
 }
 },
 this
 );

 if ($defined(json.search))
 {
 $each
 (
 json.search,
 function(value, key)
 {
 el = $(this.el[key]);
 if ($defined(el))
 {
 switch (el.type)
 {
 case 'text':
 el.value = value;
 break;
 case 'checkbox':
 if (value.length > 0) el.checked = true;
 break;
 default:
 break;
 }
 }
 },
 this
 );
 }

 this.rangers.each
 (
 function(ranger)
 {
 ranger.set();
 }
 );

 if (!$defined(json.listings) || json.listings.length < 1)
 {
 if (!this.bounds.isEmpty())
 {
 this.bounds = new google.maps.LatLngBounds();
 this.reload.delay(1, this);
 }
 else
 {
 this.list.setHTML('<h2>No Results Found</h2>');
 this.reset.delay(2500, this);
 }
 }
 else
 {
 var bounds = new google.maps.LatLngBounds();

 bounds.extend(new google.maps.LatLng(json.limits.minlat, json.limits.minlng));
 bounds.extend(new google.maps.LatLng(json.limits.maxlat, json.limits.maxlng));

 if (!bounds.isEmpty())
 {
 google.maps.Event.removeListener(this.listen);
 this.gmap.setCenter(bounds.getCenter(), this.gmap.getBoundsZoomLevel(bounds));
 this.listen = google.maps.Event.addListener(this.gmap, 'moveend', this.move.bind(this));
 }

 this.zoom = this.gmap.getZoom();
 this.bounds = bounds;

 this.sort.setHTML(json.sort);
 $each(json.listings, this.manager.add.bind(this.manager));

 this.limitstart = this.el.limitstart.value;
 }
 }
});

Map.Search.implement(new Events(), new Options());

Map.Manager = new Class
({
 options:
 {
 marker: '',
 shadow: '',
 height: 20,
 width: 14
 },

 initialize: function(map, list, options)
 {
 this.setOptions(options);
 this.map = map;
 this.list = list;

 this.icon = new google.maps.Icon(G_DEFAULT_ICON);
 if (this.options.marker.length > 0)
 {
 this.icon.image = this.options.marker;
 this.icon.iconSize = new google.maps.Size(this.options.width, this.options.height);
 this.icon.iconAnchor = new google.maps.Point(Math.floor(this.options.width / 2), this.options.height);

 if (this.options.shadow.length > 0)
 {
 this.icon.shadow = this.options.shadow;
 this.icon.shadowSize = new google.maps.Size(this.options.width, this.options.height);
 }
 }

 this.markers = [];
 this.listings = [];
 this.windows = [];
 this.latlngs = [];

 this.scroll = new Fx.Scroll
 (
 this.list,
 {
 duration: 500,
 transition: Fx.Transitions.Quad.easeInOut,
 wait: false
 }
 );
 },

 visible: function()
 {
 var visible = 0;

 this.latlngs.each
 (
 function (latlng, i)
 {
 if (this.map.getBounds().containsLatLng(latlng)) visible++;
 }.bind(this)
 );

 return visible;
 },

 add: function(data, i)
 {
 var options = { icon: this.icon };

 this.latlngs.include(new google.maps.LatLng(data.latitude, data.longitude));

 this.markers[i] = new google.maps.Marker(this.latlngs.getLast(), options);
 google.maps.Event.addListener(this.markers[i], 'click', this.activate.pass(i, this));
 this.map.addOverlay(this.markers[i]);

 this.windows[i] = data.map;

 this.listings[i] = new Element
 (
 'div',
 {
 'class': 'listing'
 }
 ).setHTML(data.listing);
 this.listings[i].addEvent('click', this.activate.pass(i, this));
 this.list.adopt(this.listings[i]);
 },

 activate: function(i)
 {
 this.list.getElements('.active').each
 (
 function (el)
 {
 el.removeClass('active');
 }
 );

 this.listings[i].addClass('active');
 this.markers[i].openInfoWindowHtml(this.windows[i]);

 this.scroll.toElement(this.listings[i]);
 },

 empty: function()
 {
 this.list.empty();
 this.map.clearOverlays();
 this.markers.length = 0;
 this.listings.length = 0;
 this.latlngs.length = 0;
 }
});

Map.Manager.implement(new Events(), new Options());

Map.Simple = new Class
({
 options:
 {
 zoom: 14,
 marker: '',
 shadow: '',
 height: 20,
 width: 14
 },

 initialize: function(el, data, options)
 {
 if (!GoogleAPI.Maps.loaded || !GBrowserIsCompatible()) return;

 this.el = $(el);
 this.data = data;
 this.setOptions(options);

 this.map = new google.maps.Map2(this.el);
 this.map.setCenter(new google.maps.LatLng(this.data.latitude, this.data.longitude), this.options.zoom.toInt());
 this.map.addControl(new google.maps.SmallMapControl());

 this.icon = new google.maps.Icon(G_DEFAULT_ICON);
 if (this.options.marker.length > 0)
 {
 this.icon.image = this.options.marker;
 this.icon.iconSize = new google.maps.Size(this.options.width, this.options.height);
 this.icon.iconAnchor = new google.maps.Point(Math.floor(this.options.width / 2), this.options.height);

 if (this.options.shadow.length > 0)
 {
 this.icon.shadow = this.options.shadow;
 this.icon.shadowSize = new google.maps.Size(this.options.width, this.options.height);
 }
 }

 this.marker(this.data);
 },

 marker: function(data)
 {
 var options = { icon: this.icon };

 this.map.addOverlay(new google.maps.Marker(new google.maps.LatLng(data.latitude, data.longitude), options));
 }
});

Map.Simple.implement(new Events(), new Options());