Dynamically load google maps api v3 with namespaced callback function - javascript

I recently ran across the following problem when lazy loading/dynamically loading the google maps api v3: In order to check whether the api has fully loaded or not, I followed the instructions on google's documentation page. Works fine with a normal function callback name like &callback=initialize in global scope. Also works fine with a function callback that is within a certain namespace like &callback=Namespace.initialize.
Now my function is placed under Namespace.foo[0].initialize ("Namespace" and "foo" are of type object). The problem now is that I can't properly access this function. I tried several ways of building the callback string, each listed below with whatever firebug tells me is not working:
&callback=Namespace.foo[0].initialize
Firebug: NetworkError: 403 Forbidden - <http://maps.googleapis.com/maps/api/js?v=3...&callback=Namespace.foo[0].initialize...>
&callback=Namespace.foo.0.initialize
Firebug: missing ; before statement
Firebug: [Break On This Error] window.Namespace.foo[0].initialize()
&callback=Namespace.foo%5B0%5D.initialize
Firebug: NetworkError: 403 Forbidden - <http://maps.googleapis.com/maps/api/js?v=3...&callback=Namespace.foo[0].initialize...>
I'm thankful for every hint and/or solution!
Background information: What I do might also be interesting. A script is dynamically loading html, css, data and the google maps api into a external webpage (within a web-widget). Now I'm using jQuery's deferred object to queue the different includes such as loading the css/javascript/html first, then receive the data and finally setup the google map within the previously loaded structure. Deferred object performs way better than handling the different steps with callbacks (and much easier too!), but when the jQuery.getScript method is done (loads the google maps api) the success event is fired and deferred.resolve gets returned. Too bad that this probably just handles the authentification (via client-key) and then asynchronously loads the google maps main.js. This pretty much sucks because I need to wait for the api to fully load...

Related

Google Maps API error: MissingKeyMapError on another domain

The number of the problem related questions is huge. I spent a lot of time and probably took a look on each of them, however I haven’t found a solution for my case.
There is a domain (let's call it domain.com). Google maps is connected to it with the key for years: <script src="//google.com/jsapi?key=the_key_is_here"></script>
There is a new domain (let's call it domain-new.com) - the full clone of the first domain. But the Google Map doesn't work on it and there is an error in the console: Google Maps API error: MissingKeyMapError.
What methods of solution have I tried:
Creating a new key
Include all Google Maps APIs in console.cloud.google.com
Specify all possible referrers in console.cloud.google.com:
domain.com
*.domain.com/*
domain.com/contact
domain-new.com
*.domain-new.com/*
domain-new.com/contact
Specifying the versions of the included script (?v=3/?v=3.exp/?v=3.26)
Replace the included script with: <script async defer src="//maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&key=the_key_is_here"></script>
All the changes were made simultaneously for domain.com and domain-new.com. The map worked successfully on domain.com, but there still was an error on domain-new.com.
Therefore, the exactly same map with the same key works on domain.com at the moment, but nevertheless it doesn't work on domain-new.com. All relevant documentation for the Google Maps API was read and no suitable solutions have been found. In conclusion, I hope your will help me.

Is there an alternative to preprocessorScript for Chrome DevTools extensions?

I want to create a custom profiler for Javascript as a Chrome DevTools Extension. To do so, I'd have to instrument all Javascript code of a website (parse to AST, inject hooks, generate new source). This should've been easily possible using chrome.devtools.inspectedWindow.reload() and its parameter preprocessorScript described here: https://developer.chrome.com/extensions/devtools_inspectedWindow.
Unfortunately, this feature has been removed (https://bugs.chromium.org/p/chromium/issues/detail?id=438626) because nobody was using it.
Do you know of any other way I could achieve the same thing with a Chrome Extension? Is there any other way I can replace an incoming Javascript source with a changed version? This question is very specific to Chrome Extensions (and maybe extensions to other browsers), I'm asking this as a last resort before going a different route (e.g. dedicated app).
Use the Chrome Debugging Protocol.
First, use DOMDebugger.setInstrumentationBreakpoint with eventName: "scriptFirstStatement" as a parameter to add a break-point to the first statement of each script.
Second, in the Debugger Domain, there is an event called scriptParsed. Listen to it and if called, use Debugger.setScriptSource to change the source.
Finally, call Debugger.resume each time after you edited a source file with setScriptSource.
Example in semi-pseudo-code:
// Prevent code being executed
cdp.sendCommand("DOMDebugger.setInstrumentationBreakpoint", {
eventName: "scriptFirstStatement"
});
// Enable Debugger domain to receive its events
cdp.sendCommand("Debugger.enable");
cdp.addListener("message", (event, method, params) => {
// Script is ready to be edited
if (method === "Debugger.scriptParsed") {
cdp.sendCommand("Debugger.setScriptSource", {
scriptId: params.scriptId,
scriptSource: `console.log("edited script ${params.url}");`
}, (err, msg) => {
// After editing, resume code execution.
cdg.sendCommand("Debugger.resume");
});
}
});
The implementation above is not ideal. It should probably listen to the breakpoint event, get to the script using the associated event data, edit the script and then resume. Listening to scriptParsed and then resuming the debugger are two things that shouldn't be together, it could create problems. It makes for a simpler example, though.
On HTTP you can use the chrome.webRequest API to redirect requests for JS code to data URLs containing the processed JavaScript code.
However, this won't work for inline script tags. It also won't work on HTTPS, since the data URLs are considered unsafe. And data URLs are can't be longer than 2MB in Chrome, so you won't be able to redirect to large JS files.
If the exact order of execution of each script isn't important you could cancel the script requests and then later send a message with the script content to the page. This would make it work on HTTPS.
To address both issues you could redirect the HTML page itself to a data URL, in order to gain more control. That has a few negative consequences though:
Can't reload page because URL is fixed to data URL
Need to add or update <base> tag to make sure stylesheet/image URLs go to the correct URL
Breaks ajax requests that require cookies/authentication (not sure if this can be fixed)
No support for localStorage on data URLs
Not sure if this works: in order to fix #1 and #4 you could consider setting up an HTML page within your Chrome extension and then using that as the base page instead of a data URL.
Another idea that may or may not work: Use chrome.debugger to modify the source code.

ReCaptcha and AJAX API

I'm trying to display ReCaptcha image through its AJAX API. I'm following this documentation and this demo. I still fail in creating a fiddle that works... I added jsfiddle.net as allowed referer to my APIs.
I can't understand what's wrong with this fiddle!
Console says "showRecaptcha is not defined".
function showRecaptcha() {
Recaptcha.create("PUBLIC_APIs", 'captchadiv', {
tabindex: 1,
theme: "clean",
callback: Recaptcha.focus_response_field
});
}
Under Frameworks and Extensions on jsfiddle, you need to change onLoad to No wrap - in <body> - otherwise the javascript is loaded after the button is, and references a then-non-existent function.
After that, the showRecaptcha() function definitely runs, but no captcha is shown. The only thing I can think of is that, as per this page:
Your reCAPTCHA key is restricted to the specified domains, and any subdomains for additional security. A key for foo.com works on test.foo.com.
your public key won't work for jsfiddle.net. I assume therefore that if you used this code (having generated a key for your domain) on your own web-page, it would work.

Using Google Drive in an iFrame doesn't work

I'm running into some problems using Google Drive in an iFrame. Normally, using it in an iFrame wouldn't be a good idea anyways, but this is for teaching Google Drive, where a student codes up something and then they can preview it.
The preview needs to happen in an iFrame, because that way it's sandboxed, and won't potentially disturb the rest of the page. I've been attempting to do this in a few different ways. I'm thinking it's related to a limitation of using OAuth an iframe, but open to other suggestions.
Here's the non-iframe version which works. This just sets up a Google Picker using my client_id and developer_key. It works fine. http://sandbox.codeschool.com.s3.amazonaws.com/dart/drive.html
Attempt #1
If you try to run this same script in an iframe, things go wrong. Here's an example of this:
http://sandbox.codeschool.com.s3.amazonaws.com/dart/loader-src.html
The entire code for this page is
<iframe height='600' width='800' src='drive.html' sandbox='allow-scripts'></iframe>
Google loader gapi loads, and calls the auth callback correctly, but the script doesn't properly load. This gives this error on this code:
gapi.auth.authorize({
'client_id': clientId,
'scope': ['https://www.googleapis.com/auth/drive']
}, handleAuthentication);
Uncaught TypeError: Cannot call method 'authorize' of undefined
Uncaught TypeError: Cannot read property 'prototype' of undefined
So somehow Google Drive is saying the library loaded, but auth isn't being defined.
Attempt #2
Rather than loading everything in from an iframe via the src, attempting to create an iframe dynamically via JavaScript and write the content in. Here's the link to this example: http://sandbox.codeschool.com.s3.amazonaws.com/dart/loader-js.html Basically something like this:
iframe = document.getElementsByTagName('iframe')[0];
iframe.sandbox = 'allow-scripts';
doc = iframe.contentDocument;
doc.open();
doc.write(htmlContent());
doc.close();
This seems to get us a step farther. Rather than getting the JS error on authorize, the script progresses down to where it creates the picker before failing when it tries to set the developer key.
This gives an error: "The API developer key is invalid.".
I'm initially thinking this is because the browser key referrers are set wrong. Here's what they're set at for this developer key:
http://sandbox.codeschool.com.s3.amazonaws.com/*
http://*
*
Last two were really just to test things. Anyone have any suggestions on possible ways of getting around this restriction?

Ext.Loader.loadScript callback called too soon

I'm trying to draw a Bing Bird's Eye View map into an ExtJS window. I'd like to load the script just before the window is created, so I'm using Ext.Loader.loadScript like so:
Ext.Loader.loadScript({
url: 'http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0',
onLoad: function () {
console.log(Microsoft.Maps.MapTypeId.birdseye);
}
});
The problem is that the onLoad callback executes before the Microsoft Maps object is fully loaded, so Microsoft.Maps.MaptypeId is undefined. If I open a web console a few seconds later, everything is loaded.
Is this a bug?
Is there another way to delay the callback function until the url and all subsequent scripts have been loaded?
The issue is that when the Bing Maps api loads it downloads several additional files. The initially downloaded file will trigger your onLoad function while the other resources for the Bing Maps control are being downloaded, which is why your onLoad function is firing before the Microsoft.Map namespace is fully functional. There are a couple of ways to handle this. One method is to use a timeout in your onLoad function that, when fired checks to see if the Microsoft.Maps namespace is available and the Microsoft.Maps.Map object is not undefined. If these tests fail, then have the timeout fire again after a short period of time. If they are valid, then call the function you want to run to load the map.
I also put together a blog post about lazy loading the map control here: http://rbrundritt.wordpress.com/2011/11/20/bing-maps-v7-control-lazy-loader/

Categories