oxjs/source/UI/js/Map/Map.js

1646 lines
55 KiB
JavaScript
Raw Normal View History

2011-11-05 16:46:53 +00:00
'use strict';
2011-05-05 18:02:56 +00:00
/*@
2012-05-30 20:39:47 +00:00
Ox.Map <f> Basic map object
2011-05-05 18:02:56 +00:00
# DESCRIPTION --------------------------------------------------------------
`Ox.Map` is a wrapper around the [Google Maps
API](http://code.google.com/apis/maps/documentation/javascript/).
2011-05-05 18:02:56 +00:00
# ARGUMENTS ----------------------------------------------------------------
options <o|{}> options
clickable <b|false> If true, clicking on the map finds a place
editable <b|false> If true, places are editable
find <s|""> Initial query
2011-05-05 18:02:56 +00:00
findPlaceholder <s|"Find"> Placeholder text for the find input element
keys <a|[]> Additional place properties to be requested
markerColor <[n]|f|s|'auto'> Color of place markers ([r, g, b])
markerSize <n|f||s|'auto'> Size of place markers in px
markerTooltip <f> Format function for place marker tooltips
2011-05-29 09:52:33 +00:00
maxMarkers <n|100> Maximum number of markers to be displayed
places <[o]|f|null> Array of, or function that returns, place objects
2011-05-05 18:02:56 +00:00
countryCode <s> ISO 3166 country code
east <n> Longitude of the eastern boundary in degrees
editable <b|false> If true, the place is editable
geoname <s> Geoname (like "Paris, Île-de-France, France")
lat <n> Latitude in degrees
lng <n> Longitude in degrees
name <s> Name (like "Paris")
north <n> Latitude of the northern boundary in degrees
south <n> Latitude of the southern boundary in degrees
type <s> Type (like "city" or "country")
west <n> Longitude of the western boundary in degrees
selected <s|""> Id of the selected place
2011-05-22 12:39:57 +00:00
showControls <b|false> If true, show controls
showLabels <b|false> If true, show labels on the map
showStatusbar <b|false> If true, the map has a statusbar
showToolbar <b|false> If true, the map has a toolbar
showZoombar <b|false> If true, the map has a zoombar
2012-01-27 14:29:11 +00:00
zoomOnlyWhenFocused <b|false> If true, scroll-zoom only when focused
2011-05-05 18:02:56 +00:00
self <o|{}> Shared private variable
2012-05-30 20:39:47 +00:00
# USAGE --------------------------------------------------------------------
([options[, self]]) -> <o:Ox.Element> Map object
# EVENTS ---------------------------------------------------------------
addplace <!> Fires when a place has been added
place <o> Place object
editplace <!> Fires when a place has been edited
place <o> Place object
geocode <!> Fires when a google geocode request returns
latLng <o|u> Query coordinates, or undefined
lat <n> latitude
lng <n> longitude
address <s|u> Query string, or undefined
results <[o]> Google Maps geocode results
address_components <[o]> Address components
long_name <s> Long name
short_name <s> Short name
types <[s]> Types (like "country" or "political")
formatted_address <s> Formatted address
geometry <o> Geometry
bounds <o> Bounds
northEast <o> North-east corner
lat <n> Latitude
lng <n> Longitude
southWest <o> South-west corner
lat <n> Latitude
lng <n> Longitude
location <o> Location
2011-05-05 18:02:56 +00:00
lat <n> Latitude
lng <n> Longitude
2012-05-30 20:39:47 +00:00
location_type <s> Location type (like "APPROXIMATE")
viewport <o> Viewport
northEast <o> North-east corner
lat <n> Latitude
lng <n> Longitude
southWest <o> South-west corner
lat <n> Latitude
lng <n> Longitude
types <[s]> Types (like "country" or "political")
2012-06-17 22:38:26 +00:00
load <!> load
select <!> Fires when a place has been selected or deselected
2012-05-30 20:39:47 +00:00
place <o> Place object
selectplace <!> Deprecated
2012-06-17 22:38:26 +00:00
togglecontrols <!> togglecontrols
togglelabels <!> togglelabels
2011-05-05 18:02:56 +00:00
@*/
2011-04-22 22:03:10 +00:00
Ox.Map = function(options, self) {
self = self || {};
var that = Ox.Element({}, self)
.defaults({
// fixme: isClickable?
clickable: false,
2012-08-29 15:42:50 +00:00
editable: false,
find: '',
findPlaceholder: 'Find',
keys: [],
markerColor: 'auto',
markerSize: 'auto',
markerTooltip: function(place) {
return place.name || '<span class="OxLight">Unnamed</span>';
},
2011-05-29 09:52:33 +00:00
maxMarkers: 100,
places: null,
2011-10-09 21:13:16 +00:00
selected: '',
2011-05-22 12:39:57 +00:00
showControls: false,
showLabels: false,
showStatusbar: false,
showToolbar: false,
showZoombar: false,
2012-01-27 14:29:11 +00:00
zoomOnlyWhenFocused: false
// fixme: width, height
})
.options(options || {})
2012-05-28 19:35:41 +00:00
.update({
find: function() {
self.$findInput
.value(self.options.find)
.triggerEvent('submit', {value: self.options.find});
},
height: function() {
2014-09-24 18:19:54 +00:00
that.css({height: self.options.height + 'px'});
2012-05-28 19:35:41 +00:00
that.resizeMap();
},
places: function() {
if (Ox.isArray(self.options.places)) {
self.options.places.forEach(function(place) {
if (Ox.isUndefined(place.id)) {
place.id = Ox.encodeBase32(Ox.uid());
}
});
if (self.options.selected && !Ox.getObjectById(
self.options.places, self.options.selected
)) {
self.options.selected = '';
selectPlace(null);
}
self.options.places = Ox.api(self.options.places, {
geo: true,
sort: '-area'
});
}
2014-04-11 21:08:02 +00:00
self.loaded && getMapBounds(function(mapBounds) {
2012-05-28 19:35:41 +00:00
if (mapBounds) {
self.map.fitBounds(mapBounds);
} else {
self.map.setZoom(self.minZoom);
self.map.setCenter(new google.maps.LatLng(0, 0));
}
// fixme: the following is just a guess
self.boundsChanged = true;
mapChanged();
self.options.selected && selectPlace(self.options.selected);
2012-05-28 19:35:41 +00:00
});
},
selected: function() {
selectPlace(self.options.selected || null);
},
type: function() {
// ...
},
width: function() {
2014-09-24 18:19:54 +00:00
that.css({width: self.options.width + 'px'});
2012-05-28 19:35:41 +00:00
that.resizeMap();
}
})
.addClass('OxMap')
2012-08-29 15:42:50 +00:00
.bindEvent({
gainfocus: function() {
self.options.zoomOnlyWhenFocused && self.map.setOptions({scrollwheel: true});
},
losefocus: function() {
self.options.zoomOnlyWhenFocused && self.map.setOptions({scrollwheel: false});
},
key_0: function() {
panToPlace()
},
key_c: toggleControls,
key_down: function() {
2012-08-29 15:42:50 +00:00
pan(0, 1);
},
key_enter: pressEnter,
key_escape: pressEscape,
2012-08-29 15:42:50 +00:00
key_equal: function() {
zoom(1);
},
key_l: toggleLabels,
key_left: function() {
2012-08-29 15:42:50 +00:00
pan(-1, 0);
},
'key_meta': function() {
2012-08-29 15:42:50 +00:00
self.metaKey = true;
$(document).one({
keyup: function() {
self.metaKey = false;
}
});
},
key_minus: function() {
zoom(-1);
},
key_right: function() {
2012-08-29 15:42:50 +00:00
pan(1, 0);
},
key_shift: function() {
self.shiftKey = true;
$(document).one({
keyup: function() {
self.shiftKey = false;
}
});
},
key_shift_down: function() {
pan(0, 2);
},
key_shift_0: function() {
zoomToPlace();
},
key_shift_equal: function() {
zoom(2);
},
key_shift_left: function() {
pan(-2, 0);
},
key_shift_minus: function() {
zoom(-2);
},
key_shift_right: function() {
pan(2, 0);
},
key_shift_up: function() {
pan(0, -2);
},
key_up: function() {
2012-08-29 15:42:50 +00:00
pan(0, -1);
},
key_z: undo,
mousedown: function(e) {
2012-01-27 14:29:11 +00:00
!$(e.target).is('input') && that.gainFocus();
}
});
2011-04-22 22:03:10 +00:00
// HANDLE DEPRECATED OPTIONS
2012-06-11 21:47:06 +00:00
options && ['statusbar', 'toolbar', 'zoombar'].forEach(function(key) {
if (options[key]) {
self.options['show' + Ox.toTitleCase(key)] = options[key];
}
});
//FIXME: duplicated in update
if (Ox.isArray(self.options.places)) {
self.options.places.forEach(function(place) {
if (Ox.isUndefined(place.id)) {
place.id = Ox.encodeBase32(Ox.uid());
}
});
2012-04-04 07:06:55 +00:00
self.options.places = Ox.api(self.options.places, {
geo: true,
sort: '-area'
});
}
2011-04-22 22:03:10 +00:00
self.mapHeight = getMapHeight();
2012-01-13 06:57:42 +00:00
self.metaKey = false;
2011-04-22 22:03:10 +00:00
self.minZoom = getMinZoom();
2012-05-24 07:45:33 +00:00
self.placeKeys = [
2011-05-30 12:06:58 +00:00
'id', 'name', 'alternativeNames', 'geoname', 'countryCode', 'type',
'lat', 'lng', 'south', 'west', 'north', 'east', 'area',
'editable'
2012-05-24 07:45:33 +00:00
].concat(self.options.keys);
self.places = [],
2012-01-13 06:57:42 +00:00
self.resultPlace = null;
2011-04-22 22:03:10 +00:00
self.scaleMeters = [
50000000, 20000000, 10000000,
5000000, 2000000, 1000000,
500000, 200000, 100000,
50000, 20000, 10000,
5000, 2000, 1000,
500, 200, 100,
50, 20, 10
];
2012-06-04 09:49:27 +00:00
self.shiftKey = false,
self.tileSize = 256;
2011-04-22 22:03:10 +00:00
if (self.options.showToolbar) {
self.$toolbar = Ox.Bar({
2011-04-22 22:03:10 +00:00
size: 24
})
.appendTo(that);
2013-03-02 05:51:40 +00:00
self.$menu = Ox.MenuButton({
items: [
{
id: 'toggleLabels',
title: self.options.showLabels
2013-05-09 13:03:33 +00:00
? [Ox._('Hide Labels'), Ox._('Show Labels')]
: [Ox._('Show Labels'), Ox._('Hide Labels')],
keyboard: 'l'
},
{
id: 'toggleControls',
title: self.options.showControls
2013-05-09 13:03:33 +00:00
? [Ox._('Hide Controls'), Ox._('Show Controls')]
: [Ox._('Show Controls'), Ox._('Hide Controls')],
keyboard: 'c'
}
],
2012-04-04 15:44:32 +00:00
title: 'set',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Map Options'),
2012-04-04 15:44:32 +00:00
type: 'image'
2011-05-22 12:39:57 +00:00
})
.css({float: 'left', margin: '4px'})
2011-10-04 02:26:24 +00:00
.bindEvent({
click: function(data) {
if (data.id == 'toggleLabels') {
toggleLabels();
} else if (data.id == 'toggleControls') {
toggleControls();
}
}
})
2011-05-22 12:39:57 +00:00
.appendTo(self.$toolbar);
self.$findInput = Ox.Input({
2011-04-22 22:03:10 +00:00
clear: true,
placeholder: self.options.findPlaceholder,
width: 192
})
.css({float: 'right', margin: '4px 4px 4px 2px'})
2011-04-22 22:03:10 +00:00
.bindEvent({
submit: submitFind
})
.appendTo(self.$toolbar);
self.$loadingIcon = Ox.LoadingIcon({
size: 16
})
.css({float: 'right', margin: '4px 2px 4px 2px'})
.appendTo(self.$toolbar);
2011-04-22 22:03:10 +00:00
}
self.$map = Ox.Element()
2011-04-22 22:03:10 +00:00
.css({
left: 0,
top: self.options.showToolbar * 24 + 'px',
2011-04-22 22:03:10 +00:00
right: 0,
bottom: self.options.showZoombar * 16 + self.options.showStatusbar * 24 + 'px'
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
if (self.options.showZoombar) {
self.$zoombar = Ox.Bar({
2011-04-22 22:03:10 +00:00
size: 16
})
.css({
bottom: self.options.showStatusbar * 24 + 'px'
2011-04-22 22:03:10 +00:00
})
.appendTo(that);
}
if (self.options.showStatusbar) {
self.$statusbar = Ox.Bar({
2011-04-22 22:03:10 +00:00
size: 24
})
.css({
bottom: 0
})
.appendTo(that);
2013-12-06 21:15:04 +00:00
self.$placeFlag = Ox.$('<img>')
2011-05-22 17:12:21 +00:00
.addClass('OxFlag')
.attr({
src: Ox.PATH + 'Ox.Geo/png/icons/16/NTHH.png'
})
.css({float: 'left', margin: '4px 2px 4px 4px'})
2014-09-24 18:19:54 +00:00
.appendTo(self.$statusbar);
self.$placeNameInput = Ox.Input({
2011-04-22 22:03:10 +00:00
placeholder: 'Name',
width: 96
})
//.css({position: 'absolute', left: 4, top: 4})
2011-05-22 17:12:21 +00:00
.css({float: 'left', margin: '4px 2px 4px 2px'})
2011-04-22 22:03:10 +00:00
.appendTo(self.$statusbar);
self.$placeGeonameInput = Ox.Input({
2011-04-22 22:03:10 +00:00
placeholder: 'Geoname',
width: 96
})
//.css({position: 'absolute', left: 104, top: 4})
2011-05-22 17:12:21 +00:00
.css({float: 'left', margin: '4px 2px 4px 2px'})
2011-04-22 22:03:10 +00:00
.appendTo(self.$statusbar);
self.$placeButton = Ox.Button({
2011-04-22 22:03:10 +00:00
title: 'New Place',
width: 96
})
.css({float: 'right', margin: '4px 4px 4px 2px'})
.bindEvent({
click: clickPlaceButton
})
.appendTo(self.$statusbar);
}
2011-10-04 02:26:24 +00:00
self.$controls = {
2011-10-30 21:05:32 +00:00
center: Ox.Button({
2011-10-04 02:26:24 +00:00
title: 'center',
2011-04-22 22:03:10 +00:00
type: 'image'
})
2011-10-04 02:26:24 +00:00
.addClass('OxMapControl OxMapButtonCenter')
.bindEvent({
singleclick: function() {
panToPlace();
},
doubleclick: function() {
zoomToPlace();
}
2011-04-22 22:03:10 +00:00
}),
2011-10-30 21:05:32 +00:00
east: Ox.Button({
2011-04-22 22:03:10 +00:00
title: 'right',
type: 'image'
})
2011-10-04 02:26:24 +00:00
.addClass('OxMapControl OxMapButtonEast')
.bindEvent({
singleclick: function() {
pan(1, 0);
},
doubleclick: function() {
pan(2, 0);
}
2011-04-22 22:03:10 +00:00
}),
2011-10-30 21:05:32 +00:00
north: Ox.Button({
2011-04-22 22:03:10 +00:00
title: 'up',
type: 'image'
})
2011-10-04 02:26:24 +00:00
.addClass('OxMapControl OxMapButtonNorth')
.bindEvent({
singleclick: function() {
pan(0, -1);
},
doubleclick: function() {
pan(0, -2);
}
2011-04-22 22:03:10 +00:00
}),
2011-10-30 21:05:32 +00:00
south: Ox.Button({
2011-04-22 22:03:10 +00:00
title: 'down',
type: 'image'
})
2011-10-04 02:26:24 +00:00
.addClass('OxMapControl OxMapButtonSouth')
.bindEvent({
singleclick: function() {
pan(0, 1);
},
doubleclick: function() {
pan(0, 2);
}
2011-04-22 22:03:10 +00:00
}),
2011-10-30 21:05:32 +00:00
west: Ox.Button({
2011-04-22 22:03:10 +00:00
title: 'left',
type: 'image'
})
2011-10-04 02:26:24 +00:00
.addClass('OxMapControl OxMapButtonWest')
.bindEvent({
singleclick: function() {
pan(-1, 0);
},
doubleclick: function() {
pan(-2, 0);
}
}),
2011-10-30 21:05:32 +00:00
scale: Ox.Label({
textAlign: 'center'
2011-04-22 22:03:10 +00:00
})
2011-10-30 21:05:32 +00:00
.addClass('OxMapControl OxMapScale')
2011-04-22 22:03:10 +00:00
};
2011-10-04 02:26:24 +00:00
!self.options.showControls && Ox.forEach(self.$controls, function($control) {
$control.css({opacity: 0}).hide();
2011-05-22 12:39:57 +00:00
});
2011-04-22 22:03:10 +00:00
2011-10-30 21:05:32 +00:00
self.$placeControls = {
flag: Ox.Element()
2012-04-03 14:45:49 +00:00
.addClass('OxPlaceControl OxPlaceFlag')
.bindEvent({
anyclick: function() {
2012-04-03 22:37:42 +00:00
// FIXME: doesn't work for 'Georgia', use Ox.Geo data!
2012-04-03 14:45:49 +00:00
var country = this.data('country');
country && getPlaceByName(country, function(place) {
place && self.map.fitBounds(place.bounds);
});
}
}),
2011-10-30 21:05:32 +00:00
name: Ox.Label({
textAlign: 'center',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Click to pan, doubleclick to zoom')
2011-10-30 21:05:32 +00:00
})
.addClass('OxPlaceControl OxPlaceName')
.bindEvent({
singleclick: function() {
panToPlace();
},
doubleclick: function() {
zoomToPlace();
}
}),
deselectButton: Ox.Button({
title: 'close',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Deselect'),
2012-05-26 15:48:19 +00:00
type: 'image'
2011-10-30 21:05:32 +00:00
})
.addClass('OxPlaceControl OxPlaceDeselectButton')
.bindEvent({
click: function() {
selectPlace(null);
}
})
}
Ox.forEach(self.$placeControls, function($placeControl) {
$placeControl.css({opacity: 0}).hide();
});
if (window.google) {
2012-06-19 15:42:53 +00:00
// timeout needed so that the map is in the DOM
setTimeout(initMap);
} else if (window.googleCallback) {
(function interval() {
isLoaded() ? initMap() : setTimeout(interval, 100);
}());
} else {
2011-04-22 22:03:10 +00:00
window.googleCallback = function() {
delete window.googleCallback;
initMap();
};
2012-01-06 11:57:54 +00:00
$.getScript(
document.location.protocol
+ '//maps.google.com/maps/api/js?callback=googleCallback&sensor=false'
);
2011-04-22 22:03:10 +00:00
}
function addPlaceToMap(place) {
2011-06-14 14:45:29 +00:00
// via find, click, shift-click, or new place button
2011-11-04 15:54:28 +00:00
Ox.Log('Map', 'addPlaceToMap', place)
2011-06-14 14:41:20 +00:00
var exists = false;
2011-04-22 22:03:10 +00:00
if (!place) {
2011-06-14 14:41:20 +00:00
var bounds = self.map.getBounds(),
center = self.map.getCenter(),
2011-04-22 22:03:10 +00:00
southwest = new google.maps.LatLngBounds(
bounds.getSouthWest(), center
).getCenter(),
northeast = new google.maps.LatLngBounds(
center, bounds.getNorthEast()
).getCenter(),
2011-06-20 12:53:00 +00:00
place = new Ox.MapPlace({
2011-06-01 13:25:07 +00:00
alternativeNames: [],
2011-04-22 22:03:10 +00:00
countryCode: '',
editable: true,
geoname: '',
2011-05-29 17:44:48 +00:00
id: '_' + Ox.encodeBase32(Ox.uid()), // fixme: stupid
2011-04-22 22:03:10 +00:00
map: that,
name: '',
2011-06-01 12:08:29 +00:00
type: 'feature',
2011-04-22 22:03:10 +00:00
south: southwest.lat(),
2012-08-29 15:42:50 +00:00
west: southwest.lng(),
north: northeast.lat(),
east: northeast.lng()
2011-04-22 22:03:10 +00:00
});
}
Ox.forEach(self.places, function(p, i) {
if (place.bounds.equals(p.bounds)) {
place = p;
exists = true;
2012-07-05 08:42:15 +00:00
return false; // break
2011-04-22 22:03:10 +00:00
}
});
if (!exists) {
2011-04-27 07:13:12 +00:00
self.resultPlace && self.resultPlace.remove();
2011-04-22 22:03:10 +00:00
self.resultPlace = place;
place.add();
}
selectPlace(place.id);
}
2011-05-24 08:40:17 +00:00
function addPlaceToPlaces(data) {
2012-09-10 12:05:11 +00:00
var place = Ox.extend(getSelectedPlace() || {}, data),
2012-09-10 12:25:04 +00:00
country = Ox.getCountryByGeoname(place.geoname);
2011-05-24 11:43:27 +00:00
place.countryCode = country ? country.code : '';
2012-09-10 12:05:11 +00:00
self.options.selected = place.id;
2012-02-20 18:31:45 +00:00
setPlaceControls(place);
2011-04-22 22:03:10 +00:00
place.marker.update();
place.rectangle.update();
2011-04-22 22:03:10 +00:00
self.places.push(place);
self.resultPlace = null;
that.triggerEvent('addplace', place)
}
function boundsChanged() {
setScale();
self.boundsChanged = true;
}
function canContain(outerBounds, innerBounds) {
// checks if outerBounds _can_ contain innerBounds
2011-04-22 22:03:10 +00:00
var outerSpan = outerBounds.toSpan(),
innerSpan = innerBounds.toSpan();
return outerSpan.lat() > innerSpan.lat() &&
outerSpan.lng() > innerSpan.lng();
}
function centerChanged() {
var tooltip = $('.OxMapMarkerTooltip');
2014-09-25 16:47:29 +00:00
tooltip.length && Ox.$elements[$(tooltip[0]).data('oxid')].hide();
2011-04-22 22:03:10 +00:00
self.center = self.map.getCenter();
self.centerChanged = true;
}
function changeZoom(data) {
2011-04-22 22:03:10 +00:00
self.map.setZoom(data.value);
}
function clickMap(event) {
var place = getSelectedPlace();
2012-08-29 15:42:50 +00:00
if (self.options.clickable/* && !editing()*/) {
getPlaceByLatLng(event.latLng, self.map.getBounds(), function(place) {
2011-04-22 22:03:10 +00:00
if (place) {
addPlaceToMap(place);
//selectPlace(place.id);
} else {
selectPlace(null);
}
});
2012-08-29 15:42:50 +00:00
} else {
pressEscape();
}
2011-04-22 22:03:10 +00:00
}
function clickPlaceButton() {
var place = getSelectedPlace(),
title = self.$placeButton.options('title');
2013-05-09 13:03:33 +00:00
if (title == Ox._('New Place')) {
2011-04-22 22:03:10 +00:00
addPlaceToMap();
2013-05-09 13:03:33 +00:00
} else if (title == Ox._('Add Place')) {
2011-04-22 22:03:10 +00:00
addPlaceToPlaces();
}
}
function constructZoomInput() {
if (self.options.showZoombar) {
self.$zoomInput && self.$zoomInput.remove();
self.$zoomInput = Ox.Range({
2011-04-22 22:03:10 +00:00
arrows: true,
2011-12-21 15:33:52 +00:00
changeOnDrag: true,
2011-04-22 22:03:10 +00:00
max: self.maxZoom,
min: self.minZoom,
size: that.width(),
thumbSize: 32,
thumbValue: true,
value: self.map.getZoom()
})
.bindEvent({
change: changeZoom
})
.appendTo(self.$zoombar);
}
}
function crossesDateline() {
var bounds = self.map.getBounds();
return bounds.getSouthWest().lng() > bounds.getNorthEast().lng();
}
2011-04-22 22:03:10 +00:00
function editing() {
var place = getSelectedPlace();
return place && place.editing;
}
2013-05-10 09:01:06 +00:00
function formatTerms() {
var $element;
setTimeout(function() {
try {
2013-07-09 23:32:16 +00:00
// "Terms of Use"
2013-05-10 09:01:06 +00:00
$element = $(self.$map.find('a[href$="terms_maps.html"]').parent());
$element.css({
color: 'rgb(192, 192, 192)',
textShadow: '1px 1px 0 rgb(64, 64, 64)'
});
['moz', 'o', 'webkit'].forEach(function(browser) {
$element.css({
backgroundImage: '-' + browser
+ '-linear-gradient(left, rgba(255, 255, 255, 0) 0px, rgba(255, 255, 255, 0.1) 50px)'
});
});
$element.children().css({
color: 'rgb(192, 192, 192)'
});
// Report Map Error
2013-07-09 15:24:34 +00:00
$element = $(self.$map.find('a[href$="output=classic"]').parent())
2013-05-10 09:01:06 +00:00
$element.css({
borderColor: 'transparent',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
color: 'rgb(192, 192, 192)',
textShadow: '1px 1px 0 rgb(64, 64, 64)'
});
$element.children().css({
color: 'rgb(192, 192, 192)',
fontSize: '10px',
fontWeight: 'normal',
textDecoration: 'underline'
});
} catch (e) {}
});
}
2011-04-22 22:03:10 +00:00
function getElevation(point, callback) {
// fixme: unused
if (arguments.length == 1) {
callback = point;
point = self.map.getCenter();
}
self.elevationService.getElevationForLocations({
locations: [point]
}, function(data) {
callback(data.elevation);
});
}
2011-05-29 09:52:33 +00:00
function getMapBounds(callback) {
2014-04-11 21:08:02 +00:00
// get initial map bounds
self.options.places({}, function(result) {
var area = result.data.area;
callback(new google.maps.LatLngBounds(
new google.maps.LatLng(area.south, area.west),
new google.maps.LatLng(area.north, area.east)
));
});
2011-05-29 09:52:33 +00:00
}
2011-04-22 22:03:10 +00:00
function getMapHeight() {
return self.options.height
- self.options.showStatusbar * 24
- self.options.showToolbar * 24
- self.options.showZoombar * 16;
2011-04-22 22:03:10 +00:00
}
function getMapType() {
2011-05-22 12:39:57 +00:00
return self.options.showLabels ? 'HYBRID' : 'SATELLITE'
2011-04-22 22:03:10 +00:00
}
function getMaxZoom(point, callback) {
if (arguments.length == 1) {
callback = point;
point = self.map.getCenter();
}
self.maxZoomService.getMaxZoomAtLatLng(point, function(data) {
callback(data.status == 'OK' ? data.zoom : null);
});
}
2011-05-24 08:40:17 +00:00
function getMetersPerPixel() {
2011-11-03 15:42:41 +00:00
// m/px = m/deg * deg/px
2012-06-04 09:49:27 +00:00
var degreesPerPixel = 360 / (self.tileSize * Math.pow(2, self.map.getZoom()));
return Ox.getMetersPerDegree(self.map.getCenter().lat()) * degreesPerPixel;
2011-05-24 08:40:17 +00:00
}
2011-04-22 22:03:10 +00:00
function getMinZoom() {
return self.mapHeight > 1024 ? 3
: self.mapHeight > 512 ? 2
: self.mapHeight > 256 ? 1
: 0;
// fixme: there must be a function for this...
/*
2011-04-22 22:03:10 +00:00
return Math.ceil(
2012-06-04 09:49:27 +00:00
Ox.log(self.mapHeight / self.tileSize, 2)
2011-04-22 22:03:10 +00:00
);
*/
2011-04-22 22:03:10 +00:00
}
function getPlaceById(id) {
2012-01-13 06:57:42 +00:00
return self.resultPlace && self.resultPlace.id == id
? self.resultPlace
: Ox.getObjectById(self.places, id);
2011-04-22 22:03:10 +00:00
}
function getPlaceByLatLng(latlng, bounds, callback) {
// gets the largest place at latlng that would fit in bounds
2011-04-22 22:03:10 +00:00
var callback = arguments.length == 3 ? callback : bounds,
bounds = arguments.length == 3 ? bounds : null;
self.$loadingIcon && self.$loadingIcon.start();
2011-04-22 22:03:10 +00:00
self.geocoder.geocode({
latLng: latlng
}, function(results, status) {
self.$loadingIcon && self.$loadingIcon.stop();
2011-04-22 22:03:10 +00:00
if (status == google.maps.GeocoderStatus.OK) {
if (bounds) {
Ox.forEach(results.reverse(), function(result, i) {
if (
i == results.length - 1 ||
canContain(bounds, result.geometry.bounds || result.geometry.viewport)
) {
2011-06-20 12:53:00 +00:00
callback(new Ox.MapPlace(parseGeodata(results[i])));
2012-07-05 08:42:15 +00:00
return false; // break
}
});
2011-04-22 22:03:10 +00:00
} else {
2011-06-20 12:53:00 +00:00
callback(new Ox.MapPlace(parseGeodata(results[0])));
2011-04-22 22:03:10 +00:00
}
}
if (
status == google.maps.GeocoderStatus.OK ||
status == google.maps.GeocoderStatus.ZERO_RESULTS
) {
triggerGeocodeEvent({
latLng: latlng,
results: results
});
2011-04-22 22:03:10 +00:00
} else {
2011-11-04 15:54:28 +00:00
Ox.Log('Map', 'geocode failed:', status);
2011-04-22 22:03:10 +00:00
callback(null);
}
});
}
function getPlaceByName(name, callback) {
self.$loadingIcon && self.$loadingIcon.start();
2011-04-22 22:03:10 +00:00
self.geocoder.geocode({
address: name
}, function(results, status) {
self.$loadingIcon && self.$loadingIcon.stop();
2011-04-22 22:03:10 +00:00
if (status == google.maps.GeocoderStatus.OK) {
2011-06-20 12:53:00 +00:00
callback(new Ox.MapPlace(parseGeodata(results[0])));
}
if (
status == google.maps.GeocoderStatus.OK
&& status != google.maps.GeocoderStatus.ZERO_RESULTS
) {
triggerGeocodeEvent({
address: name,
results: results
});
2011-04-22 22:03:10 +00:00
} else {
2011-11-04 15:54:28 +00:00
Ox.Log('Map', 'geocode failed:', status);
2011-04-22 22:03:10 +00:00
callback(null);
}
});
}
2012-08-29 15:42:50 +00:00
function getPositionByName(name) {
var position = -1;
Ox.forEach(self.options.places, function(place, i) {
if (place.name == name) {
position = i;
return false; // break
}
});
return position;
}
2011-04-22 22:03:10 +00:00
function getSelectedMarker() {
// needed in case self.options.selected
// is changed from outside
var id = null;
if (self.resultPlace && self.resultPlace.selected) {
id = self.resultPlace.id;
} else {
Ox.forEach(self.places, function(place) {
if (place.selected) {
id = place.id;
2012-07-05 08:42:15 +00:00
return false; // break
2011-04-22 22:03:10 +00:00
}
});
}
return id;
}
function getSelectedPlace() {
2012-01-13 06:57:42 +00:00
return self.options.selected
? getPlaceById(self.options.selected)
: null;
2011-04-22 22:03:10 +00:00
}
function initMap() {
2011-05-29 09:52:33 +00:00
getMapBounds(function(mapBounds) {
2012-01-15 15:05:17 +00:00
//Ox.Log('Map', 'init', mapBounds.getSouthWest(), mapBounds.getNorthEast(), mapBounds.getCenter())
2011-05-29 09:52:33 +00:00
self.elevationService = new google.maps.ElevationService();
self.geocoder = new google.maps.Geocoder();
self.maxZoomService = new google.maps.MaxZoomService();
self.center = mapBounds ? mapBounds.getCenter() : new google.maps.LatLng(0, 0);
self.zoom = self.minZoom;
2012-06-26 16:01:47 +00:00
that.map = self.map = new google.maps.Map(self.$map[0], {
2011-05-29 09:52:33 +00:00
center: self.center,
disableDefaultUI: true,
disableDoubleClickZoom: true,
mapTypeId: google.maps.MapTypeId[getMapType()],
2012-01-16 14:02:30 +00:00
noClear: true,
2012-01-27 14:29:11 +00:00
scrollwheel: !self.options.zoomOnlyWhenFocused,
2011-05-29 09:52:33 +00:00
zoom: self.zoom
});
google.maps.event.addListener(self.map, 'bounds_changed', boundsChanged);
google.maps.event.addListener(self.map, 'center_changed', centerChanged);
google.maps.event.addListener(self.map, 'click', clickMap);
google.maps.event.addListener(self.map, 'idle', mapChanged);
google.maps.event.addListener(self.map, 'zoom_changed', zoomChanged);
google.maps.event.addListenerOnce(self.map, 'tilesloaded', tilesLoaded);
2012-06-19 15:42:53 +00:00
google.maps.event.trigger(self.map, 'resize');
2011-10-31 11:29:59 +00:00
// needed to get mouse x/y coordinates on marker mouseover,
// see http://code.google.com/p/gmaps-api-issues/issues/detail?id=2342
that.overlayView = new google.maps.OverlayView();
that.overlayView.setMap(self.map);
that.overlayView.draw = function () {
if (!this.ready) {
this.ready = true;
google.maps.event.trigger(this, 'ready');
}
}
that.overlayView.draw();
2012-01-16 14:02:30 +00:00
Ox.forEach(self.$controls, function($control) {
$control.appendTo(self.$map);
});
Ox.forEach(self.$placeControls, function($placeControl) {
$placeControl.appendTo(self.$map);
});
if (self.options.find) {
self.$findInput
.value(self.options.find)
.triggerEvent('submit', {value: self.options.find});
} else {
2012-01-16 14:02:30 +00:00
if (self.options.selected) {
selectPlace(self.options.selected, true);
}
if (mapBounds) {
if (isEmpty(mapBounds)) {
self.map.setZoom(self.minZoom);
} else {
self.map.fitBounds(mapBounds);
}
}
if (self.map.getZoom() < self.minZoom) {
self.map.setZoom(self.minZoom);
}
}
updateFormElements();
2012-08-29 15:42:50 +00:00
self.loaded = true;
that.triggerEvent('load');
2011-05-29 09:52:33 +00:00
2011-04-22 22:03:10 +00:00
});
2011-05-29 09:52:33 +00:00
2011-04-22 22:03:10 +00:00
}
function isEmpty(bounds) {
// Google's bounds.isEmpty() is not reliable
var southWest = bounds.getSouthWest(),
northEast = bounds.getNorthEast();
return southWest.lat() == northEast.lat()
&& southWest.lng() == northEast.lng();
}
function isLoaded() {
return window.google && window.google.maps && window.google.maps.LatLng;
}
2011-04-22 22:03:10 +00:00
function mapChanged() {
// This is the handler that actually adds the places to the map.
// Gets called after panning or zooming, and when the map is idle.
2011-04-22 22:03:10 +00:00
if (self.boundsChanged) {
var bounds = self.map.getBounds(),
southWest = bounds.getSouthWest(),
northEast = bounds.getNorthEast(),
south = southWest.lat(),
west = southWest.lng(),
north = northEast.lat(),
east = northEast.lng();
self.options.places({
keys: self.placeKeys,
query: {
2012-05-24 07:45:33 +00:00
conditions: [].concat([
{key: 'lat', value: [south, north], operator: '='}
], spansGlobe() ? [
{key: 'lng', value: [-180, 180], operator: '='}
] : crossesDateline() ? [
{key: 'lng', value: [east, west], operator: '!='}
] : [
{key: 'lng', value: [west, east], operator: '='}
]),
operator: '&'
},
range: [0, self.options.maxMarkers],
sort: [{key: 'area', operator: '-'}]
}, function(result) {
var ids = self.options.selected ? [self.options.selected] : [],
previousIds = self.places.map(function(place) {
return place.id;
});
// add new places
result.data.items.forEach(function(item, i) {
var place = getPlaceById(item.id);
if (!place) {
place = new Ox.MapPlace(Ox.extend({
map: that
}, item)).add();
self.places.push(place);
} else if (!place.visible) {
2011-05-29 09:52:33 +00:00
place.add();
}
item.id != self.options.selected && ids.push(item.id);
2011-05-29 09:52:33 +00:00
});
// remove old places
previousIds.forEach(function(id) {
var place = getPlaceById(id);
if (place && ids.indexOf(id) == -1) {
place.remove();
}
2011-05-29 09:52:33 +00:00
});
// update places array
self.places = self.places.filter(function(place) {
return place.visible;
});
});
2011-04-22 22:03:10 +00:00
self.boundsChanged = false;
}
if (self.centerChanged) {
getMaxZoom(function(zoom) {
2011-05-23 19:38:52 +00:00
if (zoom && zoom != self.maxZoom) {
2011-04-22 22:03:10 +00:00
self.maxZoom = zoom;
if (self.map.getZoom() > zoom) {
self.map.setZoom(zoom);
}
constructZoomInput();
}
});
self.centerChanged = false;
}
if (self.zoomChanged) {
self.zoomChanged = false;
}
2012-01-15 15:05:17 +00:00
formatTerms();
2011-04-22 22:03:10 +00:00
}
2012-08-29 15:42:50 +00:00
function pan(x, y) {
self.map.panBy(x * self.$map.width() / 2, y * self.$map.height() / 2);
};
2011-04-22 22:03:10 +00:00
2011-10-04 02:26:24 +00:00
function panToPlace() {
var place;
2012-08-29 15:42:50 +00:00
if (!self.loaded) {
setTimeout(function() {
panToPlace();
}, 100);
} else {
2012-08-29 15:42:50 +00:00
place = getSelectedPlace();
place && self.map.panTo(place.center);
}
2011-10-04 02:26:24 +00:00
}
2011-04-22 22:03:10 +00:00
function parseGeodata(data) {
var bounds = data.geometry.bounds || data.geometry.viewport,
northEast = bounds.getNorthEast(),
southWest = bounds.getSouthWest(),
2011-04-22 22:03:10 +00:00
place = {
2011-06-01 08:50:49 +00:00
alternativeNames: [],
2011-04-22 22:03:10 +00:00
components: data.address_components,
2012-08-29 15:42:50 +00:00
countryCode: getCountryCode(data.address_components),
east: northEast.lng(),
editable: self.options.editable,
fullGeoname: getFullGeoname(data.address_components),
id: '_' + Ox.encodeBase32(Ox.uid()),
map: that,
north: northEast.lat(),
south: southWest.lat(),
type: getType(data.address_components[0].types),
west: southWest.lng()
2011-04-22 22:03:10 +00:00
};
2012-08-29 15:42:50 +00:00
place.geoname = data.formatted_address || place.fullGeoname;
place.name = (place.geoname || place.fullGeoname).split(', ')[0];
2011-06-14 09:38:58 +00:00
if (Math.abs(place.west) == 180 && Math.abs(place.east) == 180) {
place.west = -179.99999999;
place.east = 179.99999999;
}
place.south = Ox.limit(place.south, Ox.MIN_LATITUDE, Ox.MAX_LATITUDE - 0.00000001);
place.north = Ox.limit(place.north, Ox.MIN_LATITUDE + 0.00000001, Ox.MAX_LATITUDE);
2011-04-22 22:03:10 +00:00
function getCountryCode(components) {
2011-11-05 17:32:47 +00:00
var countryCode = '';
2011-04-22 22:03:10 +00:00
Ox.forEach(components, function(component) {
if (component.types.indexOf('country') > -1) {
countryCode = component.short_name;
2012-07-05 08:42:15 +00:00
return false; // break
2011-04-22 22:03:10 +00:00
}
});
return countryCode;
}
function getFullGeoname(components) {
var country = false;
return components.map(function(component, i) {
var name = component.long_name;
if (i && components[i - 1].types.indexOf('country') > -1) {
country = true;
}
return !country && (
i == 0 || name != components[i - 1].long_name
) ? name : null;
2012-08-29 15:42:50 +00:00
}).join(', ');
}
function getType(types) {
2012-07-05 08:42:15 +00:00
// see https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes
var strings = {
'country': ['country'],
2011-06-14 09:38:58 +00:00
'region': ['administrative_area', 'colloquial_area'],
'city': ['locality'],
2011-06-01 10:48:11 +00:00
'borough': ['neighborhood', 'postal_code', 'sublocality'],
'street': [
'intersection', 'route',
'street_address', 'street_number'
],
2011-06-01 10:48:11 +00:00
'building': [
2012-07-05 08:42:15 +00:00
'airport', 'floor', 'premise', 'room', 'subpremise'
],
'feature': ['natural_feature', 'park']
},
2012-07-05 08:42:15 +00:00
type;
2012-01-13 06:57:42 +00:00
function find(type) {
var ret;
Ox.forEach(types, function(v) {
ret = Ox.startsWith(v, type);
2012-07-05 08:42:15 +00:00
if (ret) {
return false; // break
}
2012-01-13 06:57:42 +00:00
});
return ret;
}
Ox.forEach(strings, function(values, key) {
Ox.forEach(values, function(value) {
if (find(value)) {
type = key;
2012-07-05 08:42:15 +00:00
return false; // break
}
});
2012-07-05 08:42:15 +00:00
if (type) {
return false; // break
}
});
2012-07-05 08:42:15 +00:00
return type || 'feature';
2011-04-22 22:03:10 +00:00
}
2012-08-29 15:42:50 +00:00
return place;
2011-04-22 22:03:10 +00:00
}
function pressEnter() {
var place = getSelectedPlace();
if (place) {
if (place.editing) {
place.submit();
} else {
place.edit();
}
} else if (self.resultPlace) {
selectPlace(self.resultPlace.id)
}
}
function pressEscape() {
var place = getSelectedPlace();
if (place) {
if (place.editing) {
place.cancel();
} else {
selectPlace(null);
}
} else if (self.resultPlace) {
self.resultPlace.remove();
self.resultPlace = null;
}
}
2012-01-16 14:02:30 +00:00
// fixme: removePlacefromMap?
2011-05-24 08:40:17 +00:00
function removePlace() {
var place = getSelectedPlace();
place.id = '_' + place.id;
self.options.selected = place.id;
self.places.splice(Ox.getIndexById(self.places, place.id), 1);
self.resultPlace && self.resultPlace.remove();
2011-05-24 08:40:17 +00:00
self.resultPlace = place;
place.marker.update();
place.rectangle.update();
2011-04-22 22:03:10 +00:00
}
2012-08-29 15:42:50 +00:00
function reset() {
self.map.getZoom() == self.zoom
? self.map.panTo(self.center)
: self.map.fitBounds(self.bounds);
}
2011-04-22 22:03:10 +00:00
function selectPlace(id, zoom) {
2011-05-29 12:22:54 +00:00
// id can be null (deselect)
var place, selected;
if (!self.loaded) {
setTimeout(function() {
selectPlace(id, zoom);
}, 100);
} else {
selected = getSelectedMarker();
Ox.Log('Map', 'Ox.Map selectPlace()', id, selected);
if (id != selected) {
place = getPlaceById(selected);
place && place.deselect();
if (id !== null) {
place = getPlaceById(id);
if (place) {
select();
} else {
// async && place doesn't exist yet
self.options.places({
keys: self.placeKeys,
query: {
conditions: [
{key: 'id', value: id, operator: '=='}
],
operator: '&'
}
}, function(result) {
if (result.data.items.length) {
place = new Ox.MapPlace(Ox.extend({
map: that
}, result.data.items[0])).add();
self.places.push(place);
select();
if (zoom) {
zoomToPlace();
} else {
panToPlace();
}
}
});
}
} else {
place = null;
select();
2011-05-29 12:22:54 +00:00
}
}
}
function select() {
2011-05-29 17:44:48 +00:00
place && place.select();
2011-04-22 22:03:10 +00:00
self.options.selected = id;
2011-10-30 21:05:32 +00:00
setPlaceControls(place);
2011-04-22 22:03:10 +00:00
setStatus();
that.triggerEvent('selectplace', place); // FIXME: deprecated, remove
that.triggerEvent('select', place);
2011-04-22 22:03:10 +00:00
}
};
2011-10-30 21:05:32 +00:00
function setPlaceControls(place) {
2012-05-22 13:14:40 +00:00
var $placeControls = that.find('.OxPlaceControl'),
2012-04-03 14:45:49 +00:00
country,
2011-10-30 21:05:32 +00:00
isVisible = self.$placeControls.name.is(':visible');
if (place) {
2012-04-03 14:45:49 +00:00
country = place.geoname.indexOf(', ') > -1
? place.geoname.split(', ').pop()
: '';
self.$placeControls.flag.options({
tooltip: country ? 'Zoom to ' + country : ''
2011-10-31 11:29:59 +00:00
})
2012-04-03 14:45:49 +00:00
.data({country: country})
.empty()
.append(
2013-12-06 21:15:04 +00:00
Ox.$('<img>').attr({
2012-04-03 14:45:49 +00:00
src: Ox.getFlagByGeoname(place.geoname, 16)
})
)
.show();
2012-02-20 18:31:45 +00:00
self.$placeControls.name.options({
title: place.name ||'<span class="OxLight">Unnamed</span>'
});
2011-10-30 21:05:32 +00:00
!isVisible && $placeControls.show().animate({opacity: 1}, 250);
} else {
isVisible && $placeControls.animate({opacity: 0}, 250, function() {
$placeControls.hide();
});
}
}
2011-04-22 22:03:10 +00:00
function setScale() {
var metersPerPixel = getMetersPerPixel();
Ox.forEach(self.scaleMeters, function(meters) {
var mapWidth = self.options.width || that.width(),
scaleWidth = Math.round(meters / metersPerPixel);
if (scaleWidth <= mapWidth / 2 - 4) {
2011-10-04 02:26:24 +00:00
self.$controls.scale
2011-04-22 22:03:10 +00:00
.options({
title: '\u2190 ' + (
meters > 1000 ? Ox.formatNumber(meters / 1000) + ' k' : meters + ' '
) + 'm \u2192'
})
.css({
2011-05-24 06:15:44 +00:00
width: (scaleWidth - 16) + 'px'
2012-07-05 08:42:15 +00:00
});
return false; // break
2011-04-22 22:03:10 +00:00
}
});
}
function setStatus() {
2011-11-04 15:54:28 +00:00
//Ox.Log('Map', 'setStatus()', self.options.selected)
2011-05-22 17:12:21 +00:00
var code, country, disabled, place, title;
if (self.options.showStatusbar) {
2011-04-22 22:03:10 +00:00
place = getSelectedPlace();
2011-05-22 17:12:21 +00:00
country = place ? Ox.getCountryByGeoname(place.geoname) : '';
code = country ? country.code : 'NTHH';
disabled = place && !place.editable;
2011-04-22 22:03:10 +00:00
if (place) {
2013-05-09 13:03:33 +00:00
title = place.id[0] == '_' ? Ox._('Add Place') : Ox._('Remove Place');
2011-04-22 22:03:10 +00:00
} else {
2013-05-09 13:03:33 +00:00
title = Ox._('New Place');
2011-04-22 22:03:10 +00:00
}
2011-05-22 17:12:21 +00:00
self.$placeFlag.attr({
src: Ox.PATH + 'Ox.Geo/png/icons/16/' + code + '.png'
});
2011-04-22 22:03:10 +00:00
self.$placeNameInput.options({
disabled: disabled,
value: place ? place.name : ''
});
self.$placeGeonameInput.options({
disabled: disabled,
value: place ? place.geoname : ''
});
self.$placeButton.options({
disabled: disabled,
title: title
});
}
2011-11-04 15:54:28 +00:00
//Ox.Log('Map', 'STATUS DONE');
2011-04-22 22:03:10 +00:00
}
function spansGlobe() {
// fixme: or self.options.width ??
2012-06-04 09:49:27 +00:00
return self.$map.width() > self.tileSize * Math.pow(2, self.map.getZoom());
};
function submitFind(data) {
2011-10-08 18:35:33 +00:00
self.options.find = data.value;
if (data.value === '') {
2012-02-20 18:31:45 +00:00
if (self.options.selected && self.options.selected[0] == '_') {
selectPlace(null);
}
} else {
that.findPlace(data.value, function(place) {
setStatus(place);
});
}
that.triggerEvent('find', {value: data.value});
2011-04-22 22:03:10 +00:00
}
2012-06-19 15:36:08 +00:00
function tilesLoaded() {
setTimeout(formatTerms, 250);
}
2011-10-04 02:26:24 +00:00
function toggleControls() {
2012-05-22 13:14:40 +00:00
var $controls = that.find('.OxMapControl');
2011-10-04 02:26:24 +00:00
self.options.showControls = !self.options.showControls;
if (self.options.showControls) {
$controls.show().animate({opacity: 1}, 250);
} else {
$controls.animate({opacity: 0}, 250, function() {
$controls.hide();
});
}
that.triggerEvent('togglecontrols', {
visible: self.options.showControls
});
}
2011-04-22 22:03:10 +00:00
function toggleLabels() {
2011-10-04 02:26:24 +00:00
self.options.showLabels = !self.options.showLabels;
2011-04-22 22:03:10 +00:00
self.map.setMapTypeId(google.maps.MapTypeId[getMapType()]);
2011-10-04 02:26:24 +00:00
that.triggerEvent('togglelabels', {
visible: self.options.showLabels
2011-04-22 22:03:10 +00:00
});
}
function triggerGeocodeEvent(data) {
// someone may want to cache google geocode data, so we fire an event.
// google puts functions like lat or lng on the objects' prototypes,
// so we create properly named properties, for json encoding
if (data.latLng) {
data.latLng = {
lat: data.latLng.lat(),
lng: data.latLng.lng()
}
}
data.results.forEach(function(result) {
['bounds', 'viewport'].forEach(function(key) {
if (result.geometry[key]) {
result.geometry[key] = {
northEast: {
lat: result.geometry[key].getNorthEast().lat(),
lng: result.geometry[key].getNorthEast().lng()
},
southWest: {
lat: result.geometry[key].getSouthWest().lat(),
lng: result.geometry[key].getSouthWest().lng()
}
}
}
});
if (result.geometry.location) {
result.geometry.location = {
lat: result.geometry.location.lat(),
lng: result.geometry.location.lng()
}
}
});
that.triggerEvent('geocode', data);
}
2011-04-22 22:03:10 +00:00
function undo() {
2011-11-04 15:54:28 +00:00
Ox.Log('Map', 'Map undo')
2011-04-22 22:03:10 +00:00
var place = getSelectedPlace();
place.editing && place.undo();
}
function updateFormElements() {
var width = that.width();
if (self.options.showZoombar) {
getMaxZoom(function(zoom) {
self.maxZoom = zoom;
constructZoomInput();
});
}
if (self.options.showStatusbar) {
2011-05-22 17:12:21 +00:00
self.$placeNameInput.options({
width: Math.floor((width - 132) / 2)
});
self.$placeGeonameInput.options({
width: Math.ceil((width - 132) / 2)
});
}
2011-04-22 22:03:10 +00:00
}
2012-08-29 15:42:50 +00:00
function zoom(z) {
2014-04-11 19:45:49 +00:00
if (!self.loaded) {
setTimeout(function() {
zoom(z);
}, 100);
} else {
self.map.setZoom(self.map.getZoom() + z);
}
2012-08-29 15:42:50 +00:00
}
2011-04-22 22:03:10 +00:00
function zoomChanged() {
var zoom = self.map.getZoom();
if (zoom < self.minZoom) {
self.map.setZoom(self.minZoom);
} else if (self.maxZoom && zoom > self.maxZoom) {
self.map.setZoom(self.maxZoom);
} else {
self.zoomChanged = true;
2011-12-21 15:33:52 +00:00
self.$zoomInput && self.$zoomInput.value(zoom);
2011-04-22 22:03:10 +00:00
that.triggerEvent('zoom', {
value: zoom
});
}
}
2012-08-29 15:42:50 +00:00
function zoomToPlace() {
var place;
if (!self.loaded) {
setTimeout(function() {
zoomToPlace();
}, 100);
} else {
2012-08-29 15:42:50 +00:00
place = getSelectedPlace();
place && self.map.fitBounds(place.bounds);
}
}
2011-04-22 22:03:10 +00:00
2012-05-21 10:38:18 +00:00
/*@
addPlace <f> addPlace
(data) -> <u> add place to places
@*/
2011-05-24 08:40:17 +00:00
that.addPlace = function(data) {
addPlaceToPlaces(data);
2011-05-24 06:15:44 +00:00
};
/*@
getCenter <f> Returns the map center
() -> <o> Map center
lat <n> Latitude
lng <n> Longitude
@*/
that.getCenter = function() {
var center = self.map.getCenter();
return {lat: center.lat(), lng: center.lng()};
};
2012-05-21 10:38:18 +00:00
/*@
getKey <f> getKey
() -> <o> get key
@*/
2011-04-22 22:03:10 +00:00
that.getKey = function() {
2012-01-13 06:57:42 +00:00
return self.shiftKey ? 'shift'
: self.metaKey ? 'meta'
: null;
2011-06-01 08:39:33 +00:00
};
2011-04-22 22:03:10 +00:00
2012-05-21 10:38:18 +00:00
/*@
getSelectedPlace <f> getSelectedPlace
() -> <o> get selected place
@*/
2011-06-14 14:34:09 +00:00
that.getSelectedPlace = function() {
return getSelectedPlace();
}
2012-05-21 10:38:18 +00:00
/*@
editPlace <f> editPlace
() -> <o> edit selected place
@*/
2011-04-22 22:03:10 +00:00
that.editPlace = function() {
2011-06-14 14:34:09 +00:00
getSelectedPlace().edit();
2012-08-29 15:42:50 +00:00
return that;
2011-06-01 08:39:33 +00:00
};
2012-05-21 10:38:18 +00:00
/*@
findPlace <f> findPlace
(name, callback) -> <o> find place and pass to callback
@*/
2011-04-22 22:03:10 +00:00
that.findPlace = function(name, callback) {
getPlaceByName(name, function(place) {
if (place) {
addPlaceToMap(place);
self.map.fitBounds(place.bounds);
2011-04-27 07:13:12 +00:00
} else {
name && self.$findInput.addClass('OxError');
2011-04-22 22:03:10 +00:00
}
callback(place);
});
return that;
2011-04-22 22:03:10 +00:00
};
2012-05-21 10:38:18 +00:00
/*@
newPlace <f> newPlace
(place) -> <o> add place to map
@*/
2011-06-14 14:34:09 +00:00
that.newPlace = function(place) {
addPlaceToMap(place);
return that;
2011-05-24 06:15:44 +00:00
};
2012-05-21 10:38:18 +00:00
/*@
panToPlace <f> panToPlace
() -> <o> pan to place
@*/
2012-08-29 15:42:50 +00:00
that.panToPlace = function() {
panToPlace();
return that;
};
2011-04-22 22:03:10 +00:00
2012-05-21 10:38:18 +00:00
/*@
removePlace <f> removePlace
() -> <o> remove selected place from places
@*/
2011-05-24 08:40:17 +00:00
that.removePlace = function() {
// fixme: removePlaceFromPlaces() ?
removePlace();
2012-08-29 15:42:50 +00:00
return that;
2011-04-22 22:03:10 +00:00
};
2012-05-21 10:38:18 +00:00
/*@
resizeMap <f> resizeMap
() -> <o> resize map
@*/
2011-04-22 22:03:10 +00:00
that.resizeMap = function() {
// keep center on resize has been commented out
// var center = self.map.getCenter();
self.options.height = that.height();
self.options.width = that.width();
// check if map has initialized
if (self.map) {
self.mapHeight = getMapHeight();
self.minZoom = getMinZoom();
if (self.minZoom > self.map.getZoom()) {
self.map.setZoom(self.minZoom);
}
self.$map.css({
height: self.mapHeight + 'px',
width: self.options.width + 'px'
});
self.options.$zoomInput && self.$zoomInput.options({
size: self.options.width
});
updateFormElements();
Ox.Log('Map', 'triggering google maps resize event, height', self.options.height)
google.maps.event.trigger(self.map, 'resize');
// self.map.setCenter(center);
}
2012-08-29 15:42:50 +00:00
return that;
2011-04-22 22:03:10 +00:00
}
/*@
setCenter <f> Set map center
(center) -> <o> Map
center <o> Map center
lat <n> Latitude
lng <n> Longitude
@*/
that.setCenter = function(center) {
self.map.setCenter(new google.maps.LatLng(center.lat, center.lng));
return that;
};
2012-05-21 10:38:18 +00:00
/*@
value <f> value
(id, key, value) -> <o> set id, key to value
@*/
2011-05-24 06:15:44 +00:00
that.value = function(id, key, value) {
2012-06-27 07:41:10 +00:00
// fixme: should be like the corresponding List/TableList/etc value function
Ox.Log('Map', 'Map.value', id, key, value);
getPlaceById(id).options(key, value);
2012-02-20 18:31:45 +00:00
if (id == self.options.selected) {
if (key == 'name') {
self.$placeControls.name.options({title: value});
} else if (key == 'geoname') {
self.$placeControls.flag.empty().append(
2013-12-06 21:15:04 +00:00
Ox.$('<img>').attr({
2012-02-20 18:31:45 +00:00
src: Ox.getFlagByGeoname(value, 16)
})
);
}
}
return that;
2011-05-24 06:15:44 +00:00
}
2012-05-21 10:38:18 +00:00
/*@
zoomToPlace <f> zoomToPlace
() -> <o> zoom to selected place
@*/
2011-04-22 22:03:10 +00:00
that.zoomToPlace = function() {
2011-10-04 02:26:24 +00:00
zoomToPlace();
2012-08-29 15:42:50 +00:00
return that;
2011-04-22 22:03:10 +00:00
};
2012-05-21 10:38:18 +00:00
/*@
zoom <f> zoom
(value) -> <o> zoom to value
@*/
2011-04-22 22:03:10 +00:00
that.zoom = function(value) {
zoom(value);
2012-08-29 15:42:50 +00:00
return that;
2011-04-22 22:03:10 +00:00
};
return that;
};