Nothing I've tried seems to work.
I found these two links and thought they'd be helpful, but that hasn't worked either.
Dynamically load JavaScript with JavaScript
https://developers.google.com/loader/
Here's roughly what my Greasemonkey script looks like currently:
var init_map = function() {
new google.maps.Geocoder().geocode({address:'1125 NW 12th Ave, Portland, OR'},function(result){
alert(result);
});
}
function loadMaps() {
GM_log("loadMaps called");
google.load("maps", "3", {"callback" : init_map, "other_params":"key=foo&sensor=false"});
}
function loadScript(filename,callback){
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.onload = callback;
fileref.setAttribute("src", filename);
if (typeof fileref!="undefined"){
document.getElementsByTagName("head")[0].appendChild(fileref);
}
}
$(document).ready(
function() {
GM_log("document ready");
loadScript('http://www.google.com/jsapi?key=ABQIAAAAfoo',function(){
loadMaps();
});
}
);
I've found that if I don't include
// #require http://www.google.com/jsapi?key=ABQIAAAAfoo
in the Greasemonkey script, I get a google is undefined error. If I do include it, init_map() never gets called. Any suggestions?
var init_map defines a local variable in the GreaseMonkey context.
If you want to run JavaScript in the context of a webpage, I recommend to inject two <script> tags in the web page (another method is to prefix all of your global variables with unsafeWindow.):
Google's map API
Your script.
Example:
// ==UserScript==
// #name Name of script
// #namespace YourNameSpaceHere
// #match http://blabla.com/*
// #version 1.0
// #run-at document-end
// ==/UserScript==
var head = document.head || document.documentElement;
var script = document.createElement('script');
script.src = 'http://www.google.com/jsapi?key=ABQIAAAAfoo';
head.appendChild(script);
var script2 = document.createElement('script');
script2.textContent = '... code here ..';
head.appendChild(script2);
// Clean-up:
script.parentNode.removeChild(script);
script2.parentNode.removeChild(script2);
E4X instead of a plain string
The easiest option to embed a string of JavaScript code in your GreaseMonkey script, without escaping quotes and newlines is to use the E4X format:
script2.textContent = <x><![CDATA[
alert("test");
]]></x>.toString();
I flagged this question as duplicate of how to use the google maps api with greasemonkey to read a table of addresses and trace the route? but the mod "found no evidence to support it".
So i will just copy-paste what i did in my question, since its not a duplicate...
Nah, just kidding :)
Lets start with your last statement:
I've found that if I don't include // #require
http://www.google.com/jsapi?key=ABQIAAAAfoo in the Greasemonkey
script, I get a google is undefined error. If I do include it,
init_map() never gets called. Any suggestions?
Yes.
First, the google maps API should not be loaded as a #require. Instead, do it like this
API_js_callback = "http://maps.google.com/maps/api/js?sensor=false®ion=BR&callback=initialize";
var script = document.createElement('script');
script.src = API_js_callback;
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild(script);
Second, add google = unsafeWindow.google, otherwise you get the "google is undefined" error.
So, your code should start like this
var init_map = function() {
google = unsafeWindow.google
new google.maps.Geocoder().geocode . . . . . .
About the rest of your code... well, just click on the link above and there you will find how to create a DIV on the fly, add the map to it, append the DIV to the page in a fixed position, etc.
Feel free to copy whatever you want.
Greasemonkey scripts are free anyway :)
I tested the answers here and in many other places and nothing would work. Maybe because the API is now v3 or who knows.
I am going to post the answer that worked for me, which is quite different from the others I found, and I believe can be used for many other cases. It's arguably a bit ugly, but after all this is script injection and nobody likes injections.
I don't copy the whole thing in jsbin / codepen / etc. because they simply cannot replicate the GS (Greasemonkey) environment (at least yet) and inner workings.
LOADING API
I had control over the destination webpage so this was there instead of being added via GS.
<script src="https://maps.googleapis.com/maps/api/js?key=my-personal-key"></script>
On my experience, if you don't add the key, after a few requests it will fail and you will have to wait some time until it works again.
I also have there a div whith a floating window where I would create my map.
<div style="overflow:hidden; height:500px; width:700px; position:fixed; top:20px; right:20px; border:3px solid #73AD21;">
<div id="gmap_canvas" style="height:500px;width:700px;"></div>
<style>#gmap_canvas img{max-width:none!important;background:none!important}</style>
<div id="Content_Title"></div>
</div>
GS SCRIPT
// Pass whatever data you need to the window
unsafeWindow.mapdata=JSON.stringify(mapdata);
// Define content of script
var script2 = document.createElement('script');
script2.textContent = `
// Get data
mapdata=JSON.parse(window.mapdata);
// Create map and use data
function initializeX2() {
// some stuff ...
// Create map
var mapCanvas = document.getElementById('gmap_canvas');
var myLatLng = {lat: parseFloat(mapdata[max].latitude), lng: parseFloat(mapdata[max].longitude)};
var mapOptions = {
center: myLatLng,
zoom: 15,
mapTypeControl: false,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(mapCanvas, mapOptions);
var marker=[];
var contentInfoWindow=[];
var infowindow=[];
// Create markers
for (var i = max ; i > max-iterations ; i--) {
// Create marker
var BLatLng={lat: parseFloat(mapdata[i].latitude), lng: parseFloat(mapdata[i].longitude)};
console.log(BLatLng);
marker[i] = new google.maps.Marker({
position: BLatLng,
map: map
});
// Create infowindow
contentInfoWindow[i]=mapdata[i].number + " - " + mapdata[i].name;
infowindow[i] = new google.maps.InfoWindow({content: contentInfoWindow[i] });
// The function has this strange form to take values of references instead of references (pointers)
google.maps.event.addListener(marker[i], 'click', function(innerKey) {
return function() {
infowindow[innerKey].open(map, marker[innerKey]);
}
}(i));
// Open markers
infowindow[i].open(map, marker[i]);
}; // end of for
}; // end of initializeX2
initializeX2();
`; // End of string to be added to page
// Add script to the page
var head = document.head || document.documentElement;
head.appendChild(script2);
// Clean-up:
script2.parentNode.removeChild(script2);
Some explanations
In my case the markers are opened when created, and multiple may stay open. That is my desired behaviour. If you want something else you have to search around.
This may help you.
Create only ONE window to have only one infowindow open at a time ( http://www.aspsnippets.com/Articles/Google-Maps-API-V3-Open-Show-only-one-InfoWindow-at-a-time-and-close-other-InfoWindow.aspx )
If someone has got the other solutions working with API v3 (via google = unsafeWindow.google ) I would be very interested to know.
Related
I want to create a div using javascript to put my google map into.
It creates a div, but doesnt put the map api into it. Please help.
You need to make sure the Google Maps API script has loaded before running your code. Currently, you are trying to build the map before the browser has downloaded the map API.
The simplest way to fix this is to change your HTML to this:
<script src="https://maps.googleapis.com/maps/api/js?key="Entered key here deleted it for stackoverflow"&callback=initMap">
<script src="javascript.js"></script>
You could also remove the Google Maps script tag and load it dynamically in javascript.js using jQuery’s $.getScript() or with plain JS using https://github.com/filamentgroup/loadJS, running your code as the callback.
initMap is firing when the script loads, but create isn't getting called until you click that p tag. Here's a way to get around that issue, but still wait for the script to load before allowing clicks:
var mapready = false, createcalled = false;
function create()
{
createcalled = true;
if(mapready){
var newDiv = document.createElement("map");
newDiv.id = "map";
newDiv.style.border="solid";
document.body.appendChild(newDiv);
var uluru = {lat: 54.278556, lng: -8.460095};
var map = new google.maps.Map(document.getElementById('map'), {zoom: 16,center: uluru});
var marker = new google.maps.Marker({position: uluru,map: map});
}
}
function initMap()
{
mapready = true;
if(createcalled) create();
}
In this scenario, if the user clicks the p tag before the map is ready, the create function will fire as soon as the map API finishes loading.
I'm working on a site that will use the Google Maps / Places APIs. I have verified that my API key is working fine, so that's not an issue. I just can't even get the map to display - that's my issue.
I've copied the example exactly, and it still won't work.
Here's my code in a gist because it's a few files. It should be runnable, that's all the content.
What I've tried so far:
- Putting it all in a document ready function (jQuery).
- Taking it out of the getCurrentPosition function so it was just out in the main.js file.
- Moving the position of the script link in my HTML file, nothing changed at any position.
- If I console.log() the lat and long variables, they print my location. But they're "undefined" when I type them into Chrome Dev Tools console. However, my "map" variable prints the map div to console.
- Using static coords, rather than coords gathered from the navigator HTML5 element.
- Setting height and width for the map div with pixels, percent, and vh/vw. It's taking the screen - I know because I gave it a background color that is showing.
- Added parameters to the end of my API load: &callback=initialize&libraries=places
This is my first time using Maps, so I'm totally lost here. I followed Google's example to a tee, and still nothing.
WebStorm is underlining all of the google.maps.x classes and saying they're "unresolved variables or types". That may be my problem, but I don't know what causes that or how to fix it. And to be clear, I have looked through every article I could find on Stack Overflow related to this, and haven't found a solution.
UPDATE:
- I just tried copying and pasting this example from Google exactly as it appears (with my API key, of course), and it still didn't work. I put it in script tags in my HTML. The only way it works is if I delete the initMap() function - not the contents of it, just the declaration of it as a function. So why is the scope acting so weird here? It's treating everything inside of the function (which is in a script tag in my HTML) as if it's all undefined just because it's in a function... at the highest scope level.
You are trying to run main.js before the Google Maps SDK has loaded. Move it above main.js' inindex.html`:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=MY_KEY&libraries=places"></script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
Then, remove the initialize function:
if ("geolocation" in navigator) {
console.log("Geolocation is enabled.");
navigator.geolocation.getCurrentPosition(function (position) {
var lat = position.coords.latitude;
var long = position.coords.longitude;
var map;
var service;
var infoWindow;
console.log("lat: " + lat);
//Initialize Google Map. Starts at user's location.
var userLocation = new google.maps.LatLng(lat, long);
map = new google.maps.Map(document.getElementById('map'), {
center: userLocation,
zoom: 15
});
var request = {
location: userLocation,
radius: '500',
types: ['store']
};
service = new google.maps.places.PlaceService(map);
service.nearbySearch(request, callback);
//Callback function for nearbySearch
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
createMarker(results[i]);
}
}
}
});
} else {
console.log("Geolocation is disabled.");
//Inform user why we use location
//Allow user to enter location manually
}
That works for me.
I have tried to solve this issue for weeks, kinda ignored it lately, but it is really starting to annoy me. If someone could tell me what's wrong with my code, i would appreciate it.
Everything works, except that it makes my code run twice, and when i dynamically load my ajax content, it makes my code run even more times.
I am fairly sure it is because of the callback when asynchronous loading the google maps.
if (window.google && google.maps) {
initialize();
} else {
if (!$('script[src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=xxxxxxxxxxxxxxxxx&signed_in=true&language=en&callback=initialize"]').length > 0) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' + '&key=xxxxxxxxxxxxxxxxx&signed_in=true&language=en&callback=initialize';
document.body.appendChild(script);
}
}
This is how i load the map. It works if you refresh the site, it jumps to the Else then. If i move around my site, which is loaded dynamic with Ajax, it goes into the IF, and run initialize().
Now... a simple console.log('test'); always fires twice, no matter what. And if i move away from the site, and go back, it fires three times, and if i go away and back one more time... you guessed it, four times test is written in console.
Now, is this where my problem is? Or does it look normal, and i should start looking at the script that loads my content with ajax?
This is my initialize function.
function initialize() {
var cell;
$('.integrator-country').text(localStorage.country);
$("#select-country").children("option[value='" + localStorage.country + "']").prop('selected', true);
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(localStorage.latitude, localStorage.longitude);
var mapOptions = {
zoom: 7,
center: latlng
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
google.maps.event.addDomListener(window, "resize", function () {
var center = map.getCenter();
google.maps.event.trigger(map, "resize");
map.setCenter(center);
});
google.maps.event.addDomListener(window, 'load', function () {
map.setCenter(latlng);
});
getIntegrators(localStorage.country, cell);
}
I´m working a dynamic way to map certain locations, I´m basing my code on an example by Googlemaps, in this example they map locations statically, using an external file (week) where you write the call to the function, an online initialization and mapping functions, like this:
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 16,
center: new google.maps.LatLng(19.43,-99.15),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var script = document.createElement('script');
script.src = 'week';
document.getElementsByTagName('head')[0].appendChild(script);
}
function eqfeed_callback(results){
mapping code
}
the content of week, the external file, is:
eqfeed_callback({"type":"FeatureCollection","features":[{feature01, feature02,... feature_n}]});
I´m able to generate dinamically the features content of week (in fact, the whole content, with the very same structure), but now I need to pass it to the initialization function, now that is the value of a global variable instead of an external file´s content, what I´ve made is to rewrite initialize as a parameter dependant function, in order to make it wait for its parameter to be generated, like this:
function initialize(scriptSource){
map = new google.maps.Map(document.getElementById('map_canvas'), { zoom: 16, center: new google.maps.LatLng(19.43,-99.15), mapTypeId:google.maps.MapTypeId.ROADMAP});
var script = document.createElement('script');
script.src = scriptSource;
document.getElementsByTagName('head')[0].appendChild(script);
}
when initialize is called, scriptSource will be the value of a global variable, with its value being exactly the same as the content of the external file (but now generated dinamically) week; I´ve been trying to make it work, but I think there´s a problem with the way I´m passing the src, how do I do this correctly?
It appears you are attempt to load javascript into a script tag.
Rather than setting the src member, instead set the innerHtml.
The src member is actually the url, not the content, of a script tag.
Also, be weary of other places you are setting the src.
script.src = 'week'; will not work as a uri
script.src = 'week.js'; will work as a uri
I'm trying to create a Ember View for Google Maps, and load the script in a on-demand manner, i.e. asynchronously Loading the API.
I have two functions inside the view, one is used to load the Google Maps API and the other is to initialize the map. But since Google requires me to call the callback function through the link that requires the API. But in Ember.JS, I couldn't get the right result. All I've got is an ERROR message saying that the object "google" is undefined when trying to initialize the map.
Here is the Ember view code for now.
App.MapsView = Ember.View.extend({
templateName: 'maps',
map: null,
didInsertElement: function() {
this._super();
this.loadGoogleMapsScript();
},
initiateMaps:function(){
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(this.$().get(0), mapOptions);
this.set('map',map);
},
loadGoogleMapsScript: function(){
var map_callback = this.initiateMaps();
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=map_callback';
document.body.appendChild(script);
},
});
Any ideas how to solve this callback problem? And, what is the optimal way to initialize the map? Should it be in the template or from the JS?
Thanks in advance.
There are two problems here. One is that you're calling your initiateMaps() function in this line:
var map_callback = this.initiateMaps();
This call is made before the Maps API is loaded, leading to the undefined google error.
The other problem is that map_callback is local to this function. The callback used in the Maps API script URL has to be a global function.
(You solved this problem yourself; I'm just adding it here for the benefit of future visitors.)
The fix for both of these problems is to change that line to:
var self = this;
window.map_callback = function() {
self.initiateMaps();
}
There may be a more "native" Ember way to do this, but that should do the trick in any case.
Also, since you're using jQuery along with Ember, you can replace this code:
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=map_callback';
document.body.appendChild(script);
with:
$.getScript( 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=map_callback' );