Remove an injected analytics library from browser memory in Bigcommerce? - javascript

How can we remove this script injector system and clear functions from memory?
Briefing) Recently the malfeasants at Bigcommerce created an analytics injector (JS) under guise of "monitoring" that is locked in a global variable. They have pushed it to all their 50,000 front facing stores without consent from any OP's. This puts in 2 JS libraries and sets up (plain code) triggers for them to track customer, behavior, and store plans throwing data to their shared 3rd party analytics bay. The issue is that although they run the code, they do not own rights to put in 3rd party libraries like this across thousands of domains out of their realm. Does anyone have ideas on how we can kill this + remove from memory? Is this even legal for them to do?
1) The injector is found in the shared global %%GLOBAL_AdditionalScriptTags%% in the HTMLhead.html panel, which means it non-accessible. The AdditionalScriptTags is also dynamic, meaning it loads different JS helpers based on what page is being requested. Removing the variable is a no-go for that reason.
2) The injector uses various DSL variables PHP side to build out its settings. Here is what it looks like in <head> as I browse logged into our store as a customer. This is putting 2 lines for 2 separate libraries which I will define below (note certain tokens hidden as 1234)
(function(){
window.analytics||(window.analytics=[]),window.analytics.methods=["debug","identify","track","trackLink","trackForm","trackClick","trackSubmit","page","pageview","ab","alias","ready","group","on","once","off","initialize"],window.analytics.factory=function(a){return function(){var b=Array.prototype.slice.call(arguments);return b.unshift(a),window.analytics.push(b),window.analytics}};for(var i=0;i<window.analytics.methods.length;i++){var method=window.analytics.methods[i];window.analytics[method]=window.analytics.factory(method)}window.analytics.load=function(){var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/analytics.min.js";var b=document.getElementsByTagName("script")[0];b.parentNode.insertBefore(a,b)},window.analytics.SNIPPET_VERSION="2.0.8",window.analytics.load();
// uncomment the following line to turn analytics.js debugging on
// shows verbose events and other useful information
// analytics.debug();
var storeId = '123456',
userId = '921';
// initialize with Fornax and Segment.io
var providers = {
Fornax: {
host: 'https://analytics.bigcommerce.com',
cdn: 'http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/fornax.min.js',
defaultEventProperties: {
storeId: storeId
}
},
'Segment.io': {
apiKey: '1sbkkbifdq'
}
};
var fornaxEnabled = false;
var segmentIOEnabled = false;
var isStorefront = true;
if (!fornaxEnabled) {
delete providers.Fornax;
}
if (!segmentIOEnabled || isStorefront) {
delete providers['Segment.io'];
}
analytics.initialize(providers);
// identify this user
analytics.identify(
userId || null,
{"name":"Test Dude","email":"test#test.com","storeHash":"123456","storeId":123456,"namespace":"bc.customers","storeCountry":"United States","experiments":{"shopping.checkout.cart_to_paid":"legacy_ui","search.storefront.backend":"mysql"},"storefront_session_id":"6b546880d5c34eec4194b5825145ad60d312bdfe"}
);
})();
3) The output libraries are found as 2 references in the <head> and as you see if you own/demo a BC store, are rather un-touchable:
<script type="text/javascript" async="" src="http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/fornax.min.js"></script>
<script type="text/javascript" async="" src="http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/analytics.min.js"></script>
How can we break the injector and these trackers and prevent them from loading? Is there a way to remove their functions from memory? Speaking on behalf of many thousands of OP's and segment.io here, we are all at our wits end with this.

I've been hacking away at this too and I found something that works well to disable most/all of it.
Before this line:
%%GLOBAL_AdditionalScriptTags%%
Use this code:
<script type="text/javascript">
window.bcanalytics = function () {};
</script>
So you will end up with something like this:
%%GLOBAL_AdditionalScriptTags%%
<script type="text/javascript">
window.bcanalytics = function () {};
</script>
The <script> tags from part 3 of your question will still load as those are always PREpended before the first non-commented out <script> tag, but most, if not all, the analytics functionality will break, including external calls, and even fornax.js won't load. Hope this helps.

Per the question I linked, for you case to at least remove the scripts from Step 3 this is what you should do :
var xhr = new XMLHttpRequest,
content,
doc,
scripts;
xhr.open( "GET", document.URL, false );
xhr.send(null);
content = xhr.responseText;
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
doc.open();
doc.write(content);
doc.close();
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
if(script.getAttribute("src") == "http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/fornax.min.js"
|| script.getAttribute("src") == "http://cdn2.bigcommerce.com/r6cb05f0157ab6c6a38c325c12cfb4eb064cc3d6f/app/assets/js/analytics.min.js") {
script.removeAttribute("src");
}
});
//Doing this will activate all the modified scripts and the "old page" will be gone as the document is replaced
document.replaceChild( document.importNode(doc.documentElement, true), document.documentElement);
You must make sure that this is the first thing to run, otherwise the other scripts can and will be executed.

Related

How to include jquery online version, or fall back to offline if not available [duplicate]

What would be a good way to attempt to load the hosted jQuery at Google (or other Google hosted libs), but load my copy of jQuery if the Google attempt fails?
I'm not saying Google is flaky. There are cases where the Google copy is blocked (apparently in Iran, for instance).
Would I set up a timer and check for the jQuery object?
What would be the danger of both copies coming through?
Not really looking for answers like "just use the Google one" or "just use your own." I understand those arguments. I also understand that the user is likely to have the Google version cached. I'm thinking about fallbacks for the cloud in general.
Edit: This part added...
Since Google suggests using google.load to load the ajax libraries, and it performs a callback when done, I'm wondering if that's the key to serializing this problem.
I know it sounds a bit crazy. I'm just trying to figure out if it can be done in a reliable way or not.
Update: jQuery now hosted on Microsoft's CDN.
http://www.asp.net/ajax/cdn/
You can achieve it like this:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="/path/to/your/jquery"><\/script>');
</script>
This should be in your page's <head> and any jQuery ready event handlers should be in the <body> to avoid errors (although it's not fool-proof!).
One more reason to not use Google-hosted jQuery is that in some countries, Google's domain name is banned.
The easiest and cleanest way to do this by far:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="path/to/your/jquery"><\/script>')</script>
This seems to work for me:
<html>
<head>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// has the google object loaded?
if (window.google && window.google.load) {
google.load("jquery", "1.3.2");
} else {
document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
window.onload = function() {
$('#test').css({'border':'2px solid #f00'});
};
</script>
</head>
<body>
<p id="test">hello jQuery</p>
</body>
</html>
The way it works is to use the google object that calling http://www.google.com/jsapi loads onto the window object. If that object is not present, we are assuming that access to Google is failing. If that is the case, we load a local copy using document.write. (I'm using my own server in this case, please use your own for testing this).
I also test for the presence of window.google.load - I could also do a typeof check to see that things are objects or functions as appropriate. But I think this does the trick.
Here's just the loading logic, since code highlighting seems to fail since I posted the whole HTML page I was testing:
if (window.google && window.google.load) {
google.load("jquery", "1.3.2");
} else {
document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
Though I must say, I'm not sure that if this is a concern for your site visitors you should be fiddling with the Google AJAX Libraries API at all.
Fun fact: I tried initially to use a try..catch block for this in various versions but could not find a combination that was as clean as this. I'd be interested to see other implementations of this idea, purely as an exercise.
If you have modernizr.js embedded on your site, you can use the built-in yepnope.js to load your scripts asynchronously - among others jQuery (with fallback).
Modernizr.load([{
load : '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'
},{
test : window.jQuery,
nope : 'path/to/local/jquery-1.7.2.min.js',
both : ['myscript.js', 'another-script.js'],
complete : function () {
MyApp.init();
}
}]);
This loads jQuery from the Google-cdn. Afterwards it's checked, if jQuery was loaded successfully. If not ("nope"), the local version is loaded. Also your personal scripts are loaded - the "both" indicates, that the load-process is iniated independently from the result of the test.
When all load-processes are complete, a function is executed, in the case 'MyApp.init'.
I personally prefer this way of asynchronous script loading. And as I rely on the feature-tests provided by modernizr when building a site, I have it embedded on the site anyway. So there's actually no overhead.
There are some great solutions here, but I'll like to take it one step further regarding the local file.
In a scenario when Google does fail, it should load a local source but maybe a physical file on the server isn't necessarily the best option. I bring this up because I'm currently implementing the same solution, only I want to fall back to a local file that gets generated by a data source.
My reasons for this is that I want to have some piece of mind when it comes to keeping track of what I load from Google vs. what I have on the local server. If I want to change versions, I'll want to keep my local copy synced with what I'm trying to load from Google. In an environment where there are many developers, I think the best approach would be to automate this process so that all one would have to do is change a version number in a configuration file.
Here's my proposed solution that should work in theory:
In an application configuration file, I'll store 3 things: absolute URL for the library, the URL for the JavaScript API, and the version number
Write a class which gets the file contents of the library itself (gets the URL from app config), stores it in my datasource with the name and version number
Write a handler which pulls my local file out of the db and caches the file until the version number changes.
If it does change (in my app config), my class will pull the file contents based on the version number, save it as a new record in my datasource, then the handler will kick in and serve up the new version.
In theory, if my code is written properly, all I would need to do is change the version number in my app config then viola! You have a fallback solution which is automated, and you don't have to maintain physical files on your server.
What does everyone think? Maybe this is overkill, but it could be an elegant method of maintaining your AJAX libraries.
Acorn
if (typeof jQuery == 'undefined') {
// or if ( ! window.jQuery)
// or if ( ! 'jQuery' in window)
// or if ( ! window.hasOwnProperty('jQuery'))
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/libs/jquery.js';
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
After you attempt to include Google's copy from the CDN.
In HTML5, you don't need to set the type attribute.
You can also use...
window.jQuery || document.write('<script src="/libs/jquery.js"><\/script>');
You might want to use your local file as a last resort.
Seems as of now jQuery's own CDN does not support https. If it did you then might want to load from there first.
So here's the sequence:
Google CDN =>
Microsoft CDN =>
Your local copy.
<!-- load jQuery from Google's CDN -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<!-- fallback to Microsoft's Ajax CDN -->
<script> window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js">\x3C/script>')</script>
<!-- fallback to local file -->
<script> window.jQuery || document.write('<script src="Assets/jquery-1.8.3.min.js">\x3C/script>')</script>
Conditionally load latest/legacy jQuery version and fallback:
<!--[if lt IE 9]>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/public/vendor/jquery-legacy/dist/jquery.min.js">\x3C/script>')</script>
<![endif]-->
<!--[if gte IE 9]><!-->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/public/vendor/jquery/dist/jquery.min.js">\x3C/script>')</script>
<!--<![endif]-->
Step 1: Did jQuery fail to load? (check jQuery variable)
How to check a not-defined variable in JavaScript
Step 2: Dynamically import (the backup) javascript file
How do I include a JavaScript file in another JavaScript file?
Because of the Google's banning problem I prefer to use Microsoft's cdn
http://www.asp.net/ajaxlibrary/cdn.ashx
For those people using ASP.NET MVC 5, add this code in your BundleConfig.cs to enable the CDN for jquery:
bundles.UseCdn = true;
Bundle jqueryBundle = new ScriptBundle("~/bundles/jquery", "//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js").Include("~/Scripts/jquery-{version}.js");
jqueryBundle.CdnFallbackExpression = "window.jQuery";
bundles.Add(jqueryBundle);
UPDATE:
This answer turned out to be wrong. Please see the comments for the real explanation.
Most of you question has been answered, but as for the final part:
What would be the danger of both copies coming through?
None really. You'd waste bandwidth, might add some milliseconds downloading a second useless copy, but there's not actual harm if they both come through. You should, of course, avoid this using the techniques mentioned above.
I made a Gist that should dynamically load jQuery if it isn't already loaded, and if the source fails, it proceeds onto fallbacks (stitched together from many answers): https://gist.github.com/tigerhawkvok/9673154
Please note I plan to keep the Gist updated but not this answer, for what it's worth!
/* See https://gist.github.com/tigerhawkvok/9673154 for the latest version */
function cascadeJQLoad(i) { // Use alternate CDNs where appropriate to load jQuery
if (typeof(i) != "number") i = 0;
// the actual paths to your jQuery CDNs
var jq_paths = [
"ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js",
"ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.0.min.js"
];
// Paths to your libraries that require jQuery
var dependent_libraries = [
"js/c.js"
];
if (window.jQuery === undefined && i < jq_paths.length) {
i++;
loadJQ(jq_paths[i], i, dependent_libraries);
}
if (window.jQuery === undefined && i == jq_paths.length) {
// jQuery failed to load
// Insert your handler here
}
}
/***
* You shouldn't have to modify anything below here
***/
function loadJQ(jq_path, i, libs) { //load jQuery if it isn't already
if (typeof(jq_path) == "undefined") return false;
if (typeof(i) != "number") i = 1;
var loadNextJQ = function() {
var src = 'https:' == location.protocol ? 'https' : 'http';
var script_url = src + '://' + jq_path;
loadJS(script_url, function() {
if (window.jQuery === undefined) cascadeJQLoad(i);
});
}
window.onload = function() {
if (window.jQuery === undefined) loadNextJQ();
else {
// Load libraries that rely on jQuery
if (typeof(libs) == "object") {
$.each(libs, function() {
loadJS(this.toString());
});
}
}
}
if (i > 0) loadNextJQ();
}
function loadJS(src, callback) {
var s = document.createElement('script');
s.src = src;
s.async = true;
s.onreadystatechange = s.onload = function() {
var state = s.readyState;
try {
if (!callback.done && (!state || /loaded|complete/.test(state))) {
callback.done = true;
callback();
}
} catch (e) {
// do nothing, no callback function passed
}
};
s.onerror = function() {
try {
if (!callback.done) {
callback.done = true;
callback();
}
} catch (e) {
// do nothing, no callback function passed
}
}
document.getElementsByTagName('head')[0].appendChild(s);
}
/*
* The part that actually calls above
*/
if (window.readyState) { //older microsoft browsers
window.onreadystatechange = function() {
if (this.readyState == 'complete' || this.readyState == 'loaded') {
cascadeJQLoad();
}
}
} else { //modern browsers
cascadeJQLoad();
}
Google Hosted jQuery
If you care about older browsers, primarily versions of IE prior to IE9, this is the most widely compatible jQuery version
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
If you don’t care about oldIE, this one is smaller and faster:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
Backup/Fallback Plan!
Either way, you should use a fallback to local just in case the Google CDN fails (unlikely) or is blocked in a location that your users access your site from (slightly more likely), like Iran or sometimes China.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>if (!window.jQuery) { document.write('<script src="/path/to/your/jquery"><\/script>'); }
</script>
Reference: http://websitespeedoptimizations.com/ContentDeliveryNetworkPost.aspx
I consider that should escape the last < to \x3C in string. When the browser sees , it considers this to be the end of the script block (since the HTML parser has no idea about JavaScript, it can't distinguish between something that just appears in a string, and something that's actually meant to end the script element). So appearing literally in JavaScript that's inside an HTML page will (in the best case) cause errors, and (in the worst case) be a huge security hole.
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-2.0.0.min.js">\x3C/script>')</script>
if (typeof jQuery == 'undefined')) { ...
Or
if(!window.jQuery){
Will not works if cdn version not loaded, because browser will run through this condition and during it still downloading the rest of javascripts which needs jQuery and it returns error. Solution was to load scripts through that condition.
<script src="http://WRONGPATH.code.jquery.com/jquery-1.4.2.min.js" type="text/javascript"></script><!-- WRONGPATH for test-->
<script type="text/javascript">
function loadCDN_or_local(){
if(!window.jQuery){//jQuery not loaded, take a local copy of jQuery and then my scripts
var scripts=['local_copy_jquery.js','my_javascripts.js'];
for(var i=0;i<scripts.length;i++){
scri=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
scri.type='text/javascript';
scri.src=scripts[i];
}
}
else{// jQuery loaded can load my scripts
var s=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
s.type='text/javascript';
s.src='my_javascripts.js';
}
}
window.onload=function(){loadCDN_or_local();};
</script>
Almost all public CDNs are pretty reliably. However, if you are worried about blocked google domain, then you can simply fallback to an alternative jQuery CDN. However, in such a case, you may prefer to do it opposite way and use some other CDN as your preferred option and fallback to Google CDN to avoid failed requests and waiting time:
<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"><\/script>');
</script>
Using Razor syntax in ASP.NET, this code provides fallback support and works with a virtual root:
#{var jQueryPath = Url.Content("~/Scripts/jquery-1.7.1.min.js");}
<script type="text/javascript">
if (typeof jQuery == 'undefined')
document.write(unescape("%3Cscript src='#jQueryPath' type='text/javascript'%3E%3C/script%3E"));
</script>
Or make a helper (helper overview):
#helper CdnScript(string script, string cdnPath, string test) {
#Html.Raw("<script src=\"http://ajax.aspnetcdn.com/" + cdnPath + "/" + script + "\" type=\"text/javascript\"></script>" +
"<script type=\"text/javascript\">" + test + " || document.write(unescape(\"%3Cscript src='" + Url.Content("~/Scripts/" + script) + "' type='text/javascript'%3E%3C/script%3E\"));</script>")
}
and use it like this:
#CdnScript("jquery-1.7.1.min.js", "ajax/jQuery", "window.jQuery")
#CdnScript("jquery.validate.min.js", "ajax/jquery.validate/1.9", "jQuery.fn.validate")
Although writing document.write("<script></script>") seems easier for jQuery backoff, Chrome gives validation error on that case. So I prefer breaking "script" word. So it becomes safer like above.
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script>if (typeof jQuery === "undefined") {
window.jqFallback = true;
document.write("<scr"+"ipt src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js'></scr"+"ipt>");
} </script>
For long term issues, it would be better to log JQuery fallbacks. In the code above, if first CDN is not available JQuery is loaded from another CDN. But you could want to know that erroneous CDN and remove it permanently. (this case is very exceptional case) Also it is better to log fallback issues. So you can send erroneous cases with AJAX. Because of JQuery isn't defined, you should use vanilla javascript for AJAX request.
<script type="text/javascript">
if (typeof jQuery === 'undefined' || window.jqFallback == true) {
// XMLHttpRequest for IE7+, Firefox, Chrome, Opera, Safari
// ActiveXObject for IE6, IE5
var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
var url = window.jqFallback == true ? "/yourUrl/" : "/yourUrl2/";
xmlhttp.open("POST", url, true);
xmlhttp.send();
}
</script>
The inability to load the resource from an external data store beyond your control is difficult. Looking for missing functions is totally fallacious as a means to avoid suffering a timeout, as described herein:
http://www.tech-101.com/support/topic/4499-issues-using-a-cdn/
Yet another fallback that replaces ajax.googleapis.com with cdnjs.cloudflare.com:
(function (doc, $)
{
'use strict';
if (typeof $ === 'undefined')
{
var script = doc.querySelector('script[src*="jquery.min.js"]'),
src = script.src.replace('ajax.googleapis.com', 'cdnjs.cloudflare.com');
script.parentNode.removeChild(script);
doc.write('<script src="' + src + '"></script>');
}
})(document, window.jQuery || window.Zepto);
You can stick to a jQuery version by specifying it in the string
Perfect for Asset Management that doesn't work with HTML snips
Tested in the wild - works perfect for users from China
You can use code like:
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>window.jQuery || document.write('<script type="text/javascript" src="./scripts/jquery.min.js">\x3C/script>')</script>
But also there are libraries you can use to setup several possible fallbacks for your scripts and optimize the loading process:
basket.js
RequireJS
yepnope
Examples:
basket.js
I think the best variant for now. Will cach your script in the localStorage, that will speed up next loadings. The simplest call:
basket.require({ url: '/path/to/jquery.js' });
This will return a promise and you can do next call on error, or load dependencies on success:
basket
.require({ url: '/path/to/jquery.js' })
.then(function () {
// Success
}, function (error) {
// There was an error fetching the script
// Try to load jquery from the next cdn
});
RequireJS
requirejs.config({
enforceDefine: true,
paths: {
jquery: [
'//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min',
//If the CDN location fails, load from this location
'js/jquery-2.0.0.min'
]
}
});
//Later
require(['jquery'], function ($) {
});
yepnope
yepnope([{
load: 'http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js',
complete: function () {
if (!window.jQuery) {
yepnope('js/jquery-2.0.0.min.js');
}
}
}]);
You should never need to use a personal copy. Here is my full belt and braces script.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>jQuery</title>
<!-- EMBED JQUERY USING GOOGLE APIs -->
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- IF THAT FAILS EMBED JQUERY USING CLOUDFLARE CDN -->
<script type="text/javascript">
window.jQuery || document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"><\/script>');
</script>
<!-- IF THAT FAILS ************* HOW??? *********** EMBED JQUERY FROM MICROSOFT -->
<script type="text/javascript">
window.jQuery || document.write('<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.0.min.js"><\/script>');
</script>
<!-- IF THAT FAILS ************* WOW!!! *********** EMBED JQUERY FROM JQUERY.COM -->
<script type="text/javascript">
window.jQuery || document.write('<script src="https://code.jquery.com/jquery-3.6.0.min.js"><\/script>');
</script>
<!-- IF THAT FAILS ************* LET'S STOP!!! *********** EMBED JQUERY FROM PERSONAL COPY -->
<script type="text/javascript">
window.jQuery || document.write('<script src="jquery.min.js"><\/script>');
</script>
</head>
<body>
</body>
</html>

Google jspai fails to load, but only for customer

My website needs to use the Google Earth plugin for just a bit longer (I know, the API is deprecated, but I'm stuck with it for several more months). I load it by including google.com/jsapi, then calling google.load like so:
...
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("earth", "1", {"other_params": "sensor=false"});
google.setOnLoadCallback(function () {
// call some JavaScript to begin initializing the GE plugin
});
</script>
</body>
</html>
This works well from multiple computers and with multiple browser inside our company's firewall. It works well from my home computer, and from my colleagues' home computers. However, when my customer tries to load it, she gets an error message that google is not defined on the line that begins google.load(.
Of course, global variable google is defined at the start of file www.google.com/jsapi, so presumably that file isn't loading. I initially assumed that her corporate firewall was blocking that file, but when I asked her to paste "https://www.google.com/jsapi" into her browser's address bar, she said that immediately loaded up a page of JavaScript.
The entire output to the browser console is:
Invalid URI. Load of media resource failed. main.html
ReferenceError: google is not defined main.html:484
And I believe the Invalid URI business is just because we don't have a favicon.ico file.
She is running Firefox 35.0.1, though she says the same error occurred with IE (she didn't mention the version of IE).
Short of asking her to install Firebug, which I don't think is going to be feasible, how can I troubleshoot this issue?
I'm really not sure with that assumption but:
Could it be, that your first script loads asynchronous? Then for slow connections (your customer) this problem would occur (i know that you are not using the async tag - but maybe the source can trigger to load async).
Best thing to do here is to make sure that the Google code you're using is the sync kind and redeploy.
Also https://bugsnag.com/ can be a really interesting tool for you. Just implement the js and you can track every error your customer gets.
Redeploy your code as follows,
<script type="text/javascript">
try {
google.load("earth", "1", {"other_params": "sensor=false"});
google.setOnLoadCallback(function () {
// call some JavaScript to begin initializing the GE plugin
});
} catch (e) {
$.post('http://<your-remote-debug-script-or-service>',e)
}
</script>
Then, when your customer encounters the error, the full details will be sent directly to your server and you can troubleshoot as necessary.
It could be something as simple as the clients browser is blocking javascript from being executed. Maybe specifically blocking your domain or something crazy like that.
Can you try an external script that loads the google jsapi, then put your code in the callback to ensure it is loaded?
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("https://www.google.com/jsapi", function(){
google.load("earth", "1", {"other_params": "sensor=false"});
google.setOnLoadCallback(function () {
// call some JavaScript to begin initializing the GE plugin
});
});
</script>
(Modified from http://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/)
You may also want to look at jsapi Auto-Loading to minimize what is loaded, but it may get tricky with an older library. https://developers.google.com/loader/

Firefox addon SDK replace request

I tried to replace by a local script the url of scripts loaded by websites.
I tried to channel.redirectTo() with data.url() and chrome:// (with contentaccessible=yes flag in manifest), but doesn't work, so I compared a regex pattern, if true, it will cancel the XHR GET request.
For example
<script src="http://url/to/script.js"></script>
become
<script src="resource://url/to/new/script.js"></script>
or
<script src="chrome://url/to/new/script.js"></script>
Now I need to replace the url or inject my new script to the page
main.js
var listener = function (event) {
var channel = event.subject.QueryInterface(Ci.nsIHttpChannel);
var match = someFunctionToMatchRegex(channel.URI.spec);
if (match) {
channel.cancel(Cr.NS_BINDING_ABORTED);
}
};
events.on("http-on-modify-request", listener);
I have a redirect module here which is a good example for what you want to do. You might be able to use it, though the module only takes specific urls and not reg expressions at the moment. I'd certainly take a pull request to make this change though.
The code basically just uses redirectTo as you mention, so something else is wrong.

Chrome extension regarding injected script + localstorage

I am puzzling my way through my first 'putting it all together' Chrome extension, I'll describe what I am trying to do and then how I have been going about it with some script excerpts:
I have an options.html page and an options.js script that lets the user set a url in a textfield -- this gets stored using localStorage.
function load_options() {
var repl_adurl = localStorage["repl_adurl"];
default_img.src = repl_adurl;
tf_default_ad.value = repl_adurl;
}
function save_options() {
var tf_ad = document.getElementById("tf_default_ad");
localStorage["repl_adurl"] = tf_ad.value;
}
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button').addEventListener('click', save_options);
});
document.addEventListener('DOMContentLoaded', load_options );
My contentscript injects a script 'myscript' into the page ( so it can have access to the img elements from the page's html )
var s = document.createElement('script');
s.src = chrome.extension.getURL("myscript.js");
console.log( s.src );
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
myscript.js is supposed to somehow grab the local storage data and that determines how the image elements are manipulated.
I don't have any trouble grabbing the images from the html source, but I cannot seem to access the localStorage data. I realize it must have to do with the two scripts having different environments but I am unsure of how to overcome this issue -- as far as I know I need to have myscript.js injected from contentscript.js because contentscript.js doesn't have access to the html source.
Hopefully somebody here can suggest something I am missing.
Thank you, I appreciate any help you can offer!
-Andy
First of all: You do not need an injected script to access the page's DOM (<img> elements). The DOM is already available to the content script.
Content scripts cannot directly access the localStorage of the extension's process, you need to implement a communication channel between the background page and the content script in order to achieve this. Fortunately, Chrome offers a simple message passing API for this purpose.
I suggest to use the chrome.storage API instead of localStorage. The advantage of chrome.storage is that it's available to content scripts, which allows you to read/set values without a background page. Currently, your code looks quite manageable, so switching from the synchronous localStorage to the asynchronous chrome.storage API is doable.
Regardless of your choice, the content script's code has to read/write the preferences asynchronously:
// Example of preference name, used in the following two content script examples
var key = 'adurl';
// Example using message passing:
chrome.extension.sendMessage({type:'getPref',key:key}, function(result) {
// Do something with result
});
// Example using chrome.storage:
chrome.storage.local.get(key, function(items) {
var result = items[key];
// Do something with result
});
As you can see, there's hardly any difference between the two. However, to get the first to work, you also have to add more logic to the background page:
// Background page
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
if (message.type === 'getPref') {
var result = localStorage.getItem(message.key);
sendResponse(result);
}
});
On the other hand, if you want to switch to chrome.storage, the logic in your options page has to be slightly rewritten, because the current code (using localStorage) is synchronous, while chrome.storage is asynchronous:
// Options page
function load_options() {
chrome.storage.local.get('repl_adurl', function(items) {
var repl_adurl = items.repl_adurl;
default_img.src = repl_adurl;
tf_default_ad.value = repl_adurl;
});
}
function save_options() {
var tf_ad = document.getElementById('tf_default_ad');
chrome.storage.local.set({
repl_adurl: tf_ad.value
});
}
Documentation
chrome.storage (method get, method set)
Message passing (note: this page uses chrome.runtime instead chrome.extension. For backwards-compatibility with Chrome 25-, use chrome.extension (example using both))
A simple and practical explanation of synchronous vs asynchronous ft. Chrome extensions

IE6 does not parse the loaded JavaScript file (Recaptcha hosted by Google)

This is a really strange issue, I am trying to use the Recaptcha on one of the website, and it works for all browsers tested except for IE6.
I have made a reference to the google's js:
http://www.google.com/recaptcha/api/challenge?k=the_key
and it is loaded according to fiddler2 & the 'onreadystatechange' event (which have a readystate == 'loaded')
The normal work flow should be the loaded JS been parsed, and another js been requested, then the image loaded from google. my problem is that the first loaded JS file (content similar to below):
var RecaptchaState = {
site : 'xxxxxxxxxxxx',
challenge : 'xxxxxxxxxxxxxxxxxxxxxxxxx',
is_incorrect : false,
programming_error : '',
error_message : '',
server : 'http://www.google.com/recaptcha/api/',
timeout : 18000
};
document.write('<scr'+'ipt type="text/javascript" s'+'rc="' + RecaptchaState.server + 'js/recaptcha.js"></scr'+'ipt>');
is not parsed. First, the following JS test:
typeof RecaptchaState == 'undefined'
Secondly, there is no second script request (according to fiddler2), not to say the recaptcha image...
The script tag is put inside the body, after the recaptcha markups, and I have even tried to load the JS dynamically:
function GetJavaScript(url, callback) {
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0];
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function () {
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
done = true;
callback();
// remove the hanlder
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
which gives same behaviour... what confuses me most is:
this issue occurs occasionally only when the page is redirectly from another page. (open the url directly in new browser window or refresh the page always works fine, however refresh page using JavaScript does not work...)
Please help, any advice and/or idea would be appreciated...
Double check that your script's src in the page source isn't api.recaptcha.net (some libraries use that, I know the Java one I was using did). If it is, that gets forwarded to www.google.com/recaptcha/api, and that seems to cause issues with IE6. Once I switched to using www.google.com/recaptcha/api as the actual script src, IE6 was completely happy. Good luck!
I solved this problem by using the https call, as per this thread in reCaptcha's Google Group.
This is not a solve, just an workaround.
Request the first js file: http://www.google.com/recaptcha/api/challenge?k=the_key
on the server site, and inject the first part of the script on the page directly:
var RecaptchaState = {
site : 'xxxxxxxxxxxx',
challenge : 'xxxxxxxxxxxxxxxxxxxxxxxxx',
is_incorrect : false,
programming_error : '',
error_message : '',
server : 'http://www.google.com/recaptcha/api/',
timeout : 18000
};
Then, using the GetJavaScript function and/or JQuery.getScript() function to load the second script:
http://www.google.com/recaptcha/api/js/recaptcha.js
This solution works for IE6 based on my test, and to make the server less load, I detect the user's browser at server end as well as client end to inject different logic.
I know this is dirty workaround, just in case this might help someone.
NOT ANSWER (or is it?):fo_Ok ie6. Seriously, forget it. Without this attitude ie6 will live forever. It is like ancient evil spirit which will be alive until someone believe in it.

Categories