--- /dev/null
+/**\r
+ *\r
+ * A JQUERY GOOGLE MAPS LATITUDE AND LONGITUDE LOCATION PICKER\r
+ * version 1.2\r
+ *\r
+ * Supports multiple maps. Works on touchscreen. Easy to customize markup and CSS.\r
+ *\r
+ * To see a live demo, go to:\r
+ * http://www.wimagguc.com/projects/jquery-latitude-longitude-picker-gmaps/\r
+ *\r
+ * by Richard Dancsi\r
+ * http://www.wimagguc.com/\r
+ *\r
+ */\r
+\r
+(function($) {\r
+\r
+// for ie9 doesn't support debug console >>>\r
+if (!window.console) window.console = {};\r
+if (!window.console.log) window.console.log = function () { };\r
+// ^^^\r
+\r
+/* local modification */\r
+window.gMapsLatLonPickerState = {};\r
+\r
+$.fn.gMapsLatLonPicker = (function() {\r
+\r
+ var _self = this;\r
+\r
+ ///////////////////////////////////////////////////////////////////////////////////////////////\r
+ // PARAMETERS (MODIFY THIS PART) //////////////////////////////////////////////////////////////\r
+ _self.params = {\r
+ defLat : 0,\r
+ defLng : 0,\r
+ defZoom : 1,\r
+ queryLocationNameWhenLatLngChanges: true,\r
+ queryElevationWhenLatLngChanges: true,\r
+ mapOptions : {\r
+ mapTypeId: google.maps.MapTypeId.ROADMAP,\r
+ /* local modification */\r
+ //mapTypeControl: false,\r
+ disableDoubleClickZoom: true,\r
+ zoomControlOptions: true,\r
+ streetViewControl: false\r
+ },\r
+ strings : {\r
+ markerText : "Drag this Marker",\r
+ error_empty_field : "Couldn't find coordinates for this place",\r
+ error_no_results : "Couldn't find coordinates for this place"\r
+ }\r
+ };\r
+\r
+\r
+ ///////////////////////////////////////////////////////////////////////////////////////////////\r
+ // VARIABLES USED BY THE FUNCTION (DON'T MODIFY THIS PART) ////////////////////////////////////\r
+ _self.vars = {\r
+ ID : null,\r
+ LATLNG : null,\r
+ map : null,\r
+ marker : null,\r
+ geocoder : null\r
+ };\r
+\r
+ ///////////////////////////////////////////////////////////////////////////////////////////////\r
+ // PRIVATE FUNCTIONS FOR MANIPULATING DATA ////////////////////////////////////////////////////\r
+ var setPosition = function(position) {\r
+ _self.vars.marker.setPosition(position);\r
+ _self.vars.map.panTo(position);\r
+\r
+ $(_self.vars.cssID + ".gllpZoom").val( _self.vars.map.getZoom() );\r
+ $(_self.vars.cssID + ".gllpLongitude").val( position.lng() );\r
+ $(_self.vars.cssID + ".gllpLatitude").val( position.lat() );\r
+\r
+ $(_self.vars.cssID).trigger("location_changed", $(_self.vars.cssID));\r
+\r
+ if (_self.params.queryLocationNameWhenLatLngChanges) {\r
+ getLocationName(position);\r
+ }\r
+ if (_self.params.queryElevationWhenLatLngChanges) {\r
+ getElevation(position);\r
+ }\r
+ };\r
+\r
+ // for reverse geocoding\r
+ var getLocationName = function(position) {\r
+ var latlng = new google.maps.LatLng(position.lat(), position.lng());\r
+ _self.vars.geocoder.geocode({'latLng': latlng}, function(results, status) {\r
+ if (status == google.maps.GeocoderStatus.OK && results[1]) {\r
+ $(_self.vars.cssID + ".gllpLocationName").val(results[1].formatted_address);\r
+ } else {\r
+ $(_self.vars.cssID + ".gllpLocationName").val("");\r
+ }\r
+ $(_self.vars.cssID).trigger("location_name_changed", $(_self.vars.cssID));\r
+ });\r
+ };\r
+\r
+ // for getting the elevation value for a position\r
+ var getElevation = function(position) {\r
+ var latlng = new google.maps.LatLng(position.lat(), position.lng());\r
+\r
+ var locations = [latlng];\r
+\r
+ var positionalRequest = { 'locations': locations };\r
+\r
+ _self.vars.elevator.getElevationForLocations(positionalRequest, function(results, status) {\r
+ if (status == google.maps.ElevationStatus.OK) {\r
+ if (results[0]) {\r
+ $(_self.vars.cssID + ".gllpElevation").val( results[0].elevation.toFixed(3));\r
+ } else {\r
+ $(_self.vars.cssID + ".gllpElevation").val("");\r
+ }\r
+ } else {\r
+ $(_self.vars.cssID + ".gllpElevation").val("");\r
+ }\r
+ $(_self.vars.cssID).trigger("elevation_changed", $(_self.vars.cssID));\r
+ });\r
+ };\r
+\r
+ // search function\r
+ var performSearch = function(string, silent) {\r
+ if (string == "") {\r
+ if (!silent) {\r
+ displayError( _self.params.strings.error_empty_field );\r
+ }\r
+ return;\r
+ }\r
+ _self.vars.geocoder.geocode(\r
+ {"address": string},\r
+ function(results, status) {\r
+ if (status == google.maps.GeocoderStatus.OK) {\r
+ $(_self.vars.cssID + ".gllpZoom").val(11);\r
+ _self.vars.map.setZoom( parseInt($(_self.vars.cssID + ".gllpZoom").val()) );\r
+ setPosition( results[0].geometry.location );\r
+ } else {\r
+ if (!silent) {\r
+ displayError( _self.params.strings.error_no_results );\r
+ }\r
+ }\r
+ }\r
+ );\r
+ };\r
+\r
+ // error function\r
+ var displayError = function(message) {\r
+ alert(message);\r
+ };\r
+\r
+ ///////////////////////////////////////////////////////////////////////////////////////////////\r
+ // PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////\r
+ var publicfunc = {\r
+\r
+ // INITIALIZE MAP ON DIV //////////////////////////////////////////////////////////////////\r
+ init : function(object) {\r
+\r
+ if ( !$(object).attr("id") ) {\r
+ if ( $(object).attr("name") ) {\r
+ $(object).attr("id", $(object).attr("name") );\r
+ } else {\r
+ $(object).attr("id", "_MAP_" + Math.ceil(Math.random() * 10000) );\r
+ }\r
+ }\r
+\r
+ _self.vars.ID = $(object).attr("id");\r
+ _self.vars.cssID = "#" + _self.vars.ID + " ";\r
+\r
+ _self.params.defLat = $(_self.vars.cssID + ".gllpLatitude").val() ? $(_self.vars.cssID + ".gllpLatitude").val() : _self.params.defLat;\r
+ _self.params.defLng = $(_self.vars.cssID + ".gllpLongitude").val() ? $(_self.vars.cssID + ".gllpLongitude").val() : _self.params.defLng;\r
+ _self.params.defZoom = $(_self.vars.cssID + ".gllpZoom").val() ? parseInt($(_self.vars.cssID + ".gllpZoom").val()) : _self.params.defZoom;\r
+\r
+ _self.vars.LATLNG = new google.maps.LatLng(_self.params.defLat, _self.params.defLng);\r
+\r
+ _self.vars.MAPOPTIONS = _self.params.mapOptions;\r
+ _self.vars.MAPOPTIONS.zoom = _self.params.defZoom;\r
+ _self.vars.MAPOPTIONS.center = _self.vars.LATLNG;\r
+\r
+ _self.vars.map = new google.maps.Map($(_self.vars.cssID + ".gllpMap").get(0), _self.vars.MAPOPTIONS);\r
+ _self.vars.geocoder = new google.maps.Geocoder();\r
+ _self.vars.elevator = new google.maps.ElevationService();\r
+\r
+ _self.vars.marker = new google.maps.Marker({\r
+ position: _self.vars.LATLNG,\r
+ map: _self.vars.map,\r
+ title: _self.params.strings.markerText,\r
+ draggable: true\r
+ });\r
+\r
+ // Set position on doubleclick\r
+ google.maps.event.addListener(_self.vars.map, 'dblclick', function(event) {\r
+ setPosition(event.latLng);\r
+ });\r
+\r
+ // Set position on marker move\r
+ google.maps.event.addListener(_self.vars.marker, 'dragend', function(event) {\r
+ setPosition(_self.vars.marker.position);\r
+ });\r
+\r
+ // Set zoom feld's value when user changes zoom on the map\r
+ google.maps.event.addListener(_self.vars.map, 'zoom_changed', function(event) {\r
+ $(_self.vars.cssID + ".gllpZoom").val( _self.vars.map.getZoom() );\r
+ $(_self.vars.cssID).trigger("location_changed", $(_self.vars.cssID));\r
+ });\r
+\r
+ // Update location and zoom values based on input field's value\r
+ $(_self.vars.cssID + ".gllpUpdateButton").bind("click", function() {\r
+ var lat = $(_self.vars.cssID + ".gllpLatitude").val();\r
+ var lng = $(_self.vars.cssID + ".gllpLongitude").val();\r
+ var latlng = new google.maps.LatLng(lat, lng);\r
+ _self.vars.map.setZoom( parseInt( $(_self.vars.cssID + ".gllpZoom").val() ) );\r
+ setPosition(latlng);\r
+ });\r
+\r
+ // Search function by search button\r
+ $(_self.vars.cssID + ".gllpSearchButton").bind("click", function() {\r
+ performSearch( $(_self.vars.cssID + ".gllpSearchField").val(), false );\r
+ });\r
+\r
+ // Search function by gllp_perform_search listener\r
+ $(document).bind("gllp_perform_search", function(event, object) {\r
+ performSearch( $(object).attr('string'), true );\r
+ });\r
+\r
+ // Zoom function triggered by gllp_perform_zoom listener\r
+ $(document).bind("gllp_update_fields", function(event) {\r
+ var lat = $(_self.vars.cssID + ".gllpLatitude").val();\r
+ var lng = $(_self.vars.cssID + ".gllpLongitude").val();\r
+ var latlng = new google.maps.LatLng(lat, lng);\r
+ _self.vars.map.setZoom( parseInt( $(_self.vars.cssID + ".gllpZoom").val() ) );\r
+ setPosition(latlng);\r
+ });\r
+\r
+ /* local modification */\r
+ window.gMapsLatLonPickerState[_self.vars.ID] =\r
+ {\r
+ vars : _self.vars,\r
+ params : _self.params\r
+ };\r
+ } // publicfunc\r
+\r
+ }\r
+\r
+ return publicfunc;\r
+});\r
+\r
+}(jQuery));\r
+\r
+$(document).ready( function() {\r
+ $(".gllpLatlonPicker").each(function() {\r
+ $(document).gMapsLatLonPicker().init( $(this) );\r
+ });\r
+});\r
+\r
+$(document).bind("location_changed", function(event, object) {\r
+ console.log("changed: " + $(object).attr('id') );\r
+});\r
--- /dev/null
+<%init>
+my $conf = new FS::Conf;
+my $apikey = $conf->config('google_maps_api_key');
+
+my %opt = @_;
+
+# Currently requires two fields named 'latitude' and 'longitude'.
+# Those should be in the edit form. This widget should NOT be in the
+# edit form (or it will submit a bunch of spurious fields, plus pressing
+# "enter" in the search box will submit the form).
+
+</%init>
+<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&key=<% $apikey %>"></script>
+<script src="<% $fsurl %>elements/jquery-gmaps-latlon-picker.js"></script>
+<style>
+ .gllpLatlonPicker, .gllpMap { width: 600px; height: 600px }
+ #search_location { width: 300px }
+</style>
+<fieldset id="latlonpicker" class="gllpLatlonPicker" style="float: right">
+ <input type="text" id="search_location">
+ <input type="hidden" class="gllpLatitude" id="map_lat">
+ <input type="hidden" class="gllpLongitude" id="map_lon">
+ <input type="hidden" class="gllpElevation" id="map_alt">
+ <input type="hidden" class="gllpZoom" id="map_zoom" value="12">
+ <div class="gllpMap"></div>
+</fieldset>
+<br/>
+
+<script>
+
+$(function() {
+ var container = $('#latlonpicker');
+ var map = gMapsLatLonPickerState['latlonpicker'].vars.map;
+
+ var lat = $('#latitude');
+ var lon = $('#longitude');
+ var alt = $('#altitude');
+ $('#map_lat').val(lat.val());
+ $('#map_lon').val(lon.val());
+ $('#map_alt').val(alt.val());
+ $(document).trigger('gllp_update_fields');
+
+ $(document).on('location_changed', function(ev, obj) {
+ lat.val($('#map_lat').val());
+ lon.val($('#map_lon').val());
+ });
+
+ // requires the Elevation API to be enabled
+ $(document).on('elevation_changed', function(ev, obj) {
+ alt.val($('#map_alt').val());
+ });
+
+ // bypass gllp's search mechanism, use the cooler Places search
+ var searchbox_input = $('#search_location')[0];
+ var searchbox = new google.maps.places.SearchBox(searchbox_input);
+ map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchbox_input);
+
+ map.addListener('bounds_changed', function() {
+ searchbox.setBounds(map.getBounds());
+ });
+
+ searchbox.addListener('places_changed', function() {
+ var places = searchbox.getPlaces();
+ if (places[0]) {
+ $('#map_lat').val( places[0].geometry.location.lat() );
+ $('#map_lon').val( places[0].geometry.location.lng() );
+ $('#map_zoom').val(12);
+ $(document).trigger('gllp_update_fields');
+ }
+ });
+});
+</script>
+++ /dev/null
-% unless ( $opt{'js_only'} ) {
-
- <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
-
- <TABLE>
- <TR>
-% foreach my $field ( @fields ) {
-
- <TD>
- <INPUT TYPE = "text"
- NAME = "<%$name%>_<%$field%>"
- ID = "<%$id%>_<%$field%>"
- SIZE = "<% $size{$field} || 15 %>"
- VALUE = "<% scalar($cgi->param($name."_$field"))
- || $tower_sector->get($field) |h %>"
- <% $onchange %>
- ><BR>
- <FONT SIZE="-1"><% $label{$field} %></FONT>
- </TD>
-% }
- </TR>
- </TABLE>
-
-
-% }
-<%init>
-
-my( %opt ) = @_;
-
-my $name = $opt{'element_name'} || $opt{'field'} || 'sectornum';
-my $id = $opt{'id'} || 'sectornum';
-
-my $curr_value = $opt{'curr_value'} || $opt{'value'};
-
-my $onchange = '';
-if ( $opt{'onchange'} ) {
- $onchange = $opt{'onchange'};
- $onchange .= '(this)' unless $onchange =~ /\(\w*\);?$/;
- $onchange =~ s/\(what\);/\(this\);/g; #ugh, terrible hack. all onchange
- #callbacks should act the same
- $onchange = 'onChange="'. $onchange. '"';
-}
-
-my $tower_sector;
-if ( $curr_value ) {
- $tower_sector = qsearchs('tower_sector', { 'sectornum' => $curr_value } );
-} else {
- $tower_sector = new FS::tower_sector {};
-}
-
-my %size = ( 'title' => 12 );
-
-tie my %label, 'Tie::IxHash',
- 'sectorname' => 'Name',
- 'ip_addr' => 'IP Address',
- 'height' => 'Height',
- 'freq_mhz' => 'Freq. (MHz)',
- 'direction' => 'Direction', # or a button to set these to 0 for omni
- 'downtilt' => 'Downtilt',
- 'width' => 'Horiz. width',
- 'v_width' => 'Vert. width',
- 'sector_range' => 'Range',
- 'db_high' => 'High quality margin (dB)',
- 'db_low' => 'Low quality margin (dB)',
-;
-
-my @fields = keys %label;
-
-</%init>