I am on an e-commerce platform where I can edit the <head>, however some things that are injected into the head are out of reach for users. So even though we can edit the <head>, there are injections which are out of reach and therefore unremovable via the traditional method.
PS: I can put script before or after these injected JS script tags, which are generated and populated along with my scripts. And so my script would run before the injected tags if I place my script before their "tag injection line."
The Problem
The problem is, this platform started injecting analytics and spam into the head, basically jacking our customers info and selling it to third parties. So I want to disable their crappy scripts.
<script type="text/javascript" async="" src="/some.JS.file.min.js"></script>
<script type="text/javascript" async="" src="/another.JS.file.min.js"></script>
The Question
Is it possible with javascript or jquery to write a script that will edit tags before they run? I can insert this custom script before the tags are in injected. I was wrong -- the unwanted <script> tags are always PREpended to the first non-commented <script> tag, and so no javascript will work to hack up the tags before they run.
What I Have Tried So Far
I found this incomplete and not working answer from this SO question.
When I run the full script with the right details entered for my own site, I get so many errors it's difficult to know where to begin as I have no idea what all the XHR stuff is for or what it does, and some of the errors are ones I've never even seen before.
When I run just this part, which I somewhat understand:
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
if(script.getAttribute("src") == "/some.JS.file.min.js"
|| script.getAttribute("src") == "/another.JS.file.min.js") {
script.removeAttribute("src");
}
});
EDIT UPDATE:
Their script is inserted AFTER my scripts. That is, I can insert the script into the <head> before their script tags or after. We are looking into new platforms now but I still need to solve this in the meantime as it will be months before we switch. I was hoping g there is some JavaScript I am not aware of that can edit HTML script tags before they run, if this script runs before they do.
EDIT 2:
Nit's answer window.bcanalytics = function () {}; works great and breaks most of it by breaking window.bcanalytics.push but somehow some of it still survives.
In this block:
<script type="text/javascript">
(function() {
window.bcanalytics || (window.bcanalytics = []), window.bcanalytics.methods = ["debug", "identify", "track",
"trackLink", "trackForm", "trackClick", "trackSubmit", "page", "pageview", "ab", "alias", "ready", "group",
"on", "once", "off", "initialize"], window.bcanalytics.factory = function(a) {
return function()
{
var b = Array.prototype.slice.call(arguments);
return b.unshift(a), window.bcanalytics.push(b),
window.bcanalytics
}
};
for (var i = 0; i < window.bcanalytics.methods.length; i++)
{
var method = window.bcanalytics.methods[i];
window.bcanalytics[method] = window.bcanalytics.factory(method)
}
window.bcanalytics.load = function() {
var a = document.createElement("script");
a.type = "text/javascript",
a.async = !0, a.src = "http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/app/assets/js/vendor/bigcommerce/analytics.min.js";
var b = document.getElementsByTagName("script")[0];
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
b.parentNode.insertBefore(a, b)
// ^^^ This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
// This line still runs and loads analytics.min.js
}, window.bcanalytics.SNIPPET_VERSION = "2.0.8", window.bcanalytics.load();
bcanalytics.initialize({"Fornax": {"host": "https:\/\/analytics.bigcommerce.com","cdn": "http:\/\/cdn5.bigcommerce.com\/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707\/app\/assets\/js\/vendor\/bigcommerce\/fornax.min.js","defaultEventProperties": {"storeId": 729188,"experiments": {"shipping.eldorado.ng-shipment.recharge-postage": "on","shipping.eldorado.label_method": "on","cp2.lightsaber": "on","PMO-272.cp1_new_product_options": "on","cart.limit_number_of_unique_items": "control","cart.auto_remove_items_over_limit": "control","BIG-15465.limit_flash_messages": "control","BIG-15230.sunset_design_mode": "control","bigpay.checkout_authorizenet.live": "on","bigpay.checkout_authorizenet.live.employee.store": "control","bigpay.checkout_authorizenet.test": "on","bigpay.checkout_authorizenet.test.employee.store": "control","bigpay.checkout_stripe.live": "on","bigpay.checkout_stripe.live.employee.store": "control","bigpay.checkout_stripe.test": "on","bigpay.checkout_stripe.test.employee.store": "control","sessions.flexible_storage": "on","PMO-439.ng_payments.phase1": "control","PMO-515.ng_payments.phase2": "control","PROJECT-331.pos_manager": "control","PROJECT-453.enterprise_apps": "control","shopping.checkout.cart_to_paid": "legacy_ui","onboarding.initial_user_flow.autoprovision": "on","faceted_search.enabled": "off","faceted_search.displayed": "off","themes.previewer": "enabled"}},"defaultContext": {"source": "Bigcommerce Storefront"},"anonymousId": "24a35a36-7153-447e-b784-c3203670f644"}});
})();
</script>
window.bcanalytics.load manages to survive and loads analytics.min.js (according to the Network tab), though I can't tell if the script then runs or doesn't.
Also, I've figured out that these pesky HTML lines:
<script type="text/javascript" defer="" async="" src="http://tracker.boostable.com/boost.bigcommerce.js"></script>
<script type="text/javascript" async="" defer="" src="http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/javascript/jirafe/beacon_api.js"></script>
<script type="text/javascript" async="" src="http://cdn5.bigcommerce.com/r-2b2d3f12176a8a1ca3cbd41bddc9621d2657d707/app/assets/js/vendor/bigcommerce/analytics.min.js"></script>
<script type="text/javascript" async="" src="http://www.google-analytics.com/plugins/ua/ecommerce.js"></script>
are Always PREpended to the first non-commented <script> opening tag, so unfortunately, none of the creatively destructive methods below will work, as any script I try to insert ahead of these tags will automatically find the pesky unwanted lines appended before it.
Assuming the offending code is similar to that of the question you linked to, I would simply try to break the offending code so it fails to execute.
From hereon the answer relies on code from the other question since you didn't provide any.
The offending code relies on analytics, which is ensured on the page at the beginning of the script:
(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();
//The rest of the script
})();
To break the whole script and prevent it from running you should simply assign window.analytics a value that will conflict with the methods that are used.
So, for example, you could run a script before the offending script that simply assigns the following:
window.analytics = function () {};
Which will result in the offending script failing due to a type error.
If you know you can at least get your scripts to run first, one (albeit hacky) solution is to just absolutely "trash" the JS environment for the next script, so it has some problems. For example:
//trash it
document.getElementById=null;
document.querySelector=null;
document.querySelectorAll=null;
window.console=null;
window.alert=null;
document.getElementsByTagName=null;
document.getElementsByClassName=null;
As soon as the enemy script tries using one of those functions, it will just crap out. Those are just some common methods off the top of my head... find out which ones its using, and nuke those. Of course, nuking anything you need for events on your own page could be an issue.
How are the scripts being injected? If it's through something like document.createElement, you could attempt to hijack that function and disable it if the element name is script:
var origCreate = document.createElement;
document.createElement = function (name) {
if (name.toLowerCase() !== 'script') {
origCreate.call(document, name);
}
};
Since the scripts are being inserted server-side, you won't be able to disable the running of the scripts in your JavaScript. However, if you're able to inject any arbitrary text before and after the scripts being inserted, you could try commenting out the script tags by inserting this first:
<!--
...then this after:
-->
If the scripts get injected between these, it will hopefully cause the HTML parser to ignore the scripts.
Update:
Sounds like you need to disable just some of this content, so commenting everything out won't work. However, if before/after hijacking works, you could potentially wrap the injected scripts in a DOM element, parse that content, strip out the scripts you don't want, and inject the scripts so they run:
Inject something like this before:
<style id="hijack" type="text/html">
...and this after:
</style>
<script>
var hijackedWrapper = document.getElementById('hijack');
var scripts = hijackedWrapper.textContent;
scripts = scripts.replace('<script src="http://some.domain.com/foo.js"></s' + 'cript>', '');
document.write(scripts); // There's better ways to do this, but is just an illustration
</script>
Like the others, I would suggest sabotaging the js environment for the hostile script, and then recovering it back once you need it.
For example, if the script relies on document.getElementById, you can do this
var restore = {
getElementById: document.getElementById
};
document.getElementById = null;
and then if you have a need to use document.getElementById later, you can restore it back:
document.getElementById = restore.getElementById;
I also wanted to note that removing the actual script tags, as far as I can tell, is not possible:
If you put in a script before the hostile scripts, then they will not be loaded in the DOM yet, so it can't see anything to remove.
If you put in a script after the hostile scripts, the hostile scripts will already be loaded.
I have read in several places how to fallback on a local copy of the jQuery library should the link hosted by either google or microsoft or other fail.
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
if (typeof jQuery == 'undefined')
{
document.write(unescape("%3Cscript src='/Scripts/jquery-1.3.2.min.js' type='text/javascript'%3E%3C/script%3E"));
}
</script>
My application works within an intranet environment however and occasionally the external jQuery link doesn't so much fail but takes a long time to load (due to external internet connection issues).
I'm wondering if there is a way to not only use such a fallback but set a timeout for the CDN link so that if the link takes a certain amount of time it should fail and call on the fallback.
Something like:
if(timetoloadjquery > n) {
Use fallback local jQuery library.
}
Perhaps some kind of loop that checks if the jQuery is defined and if after so many iterations it is not....do something else?
Thanks for the help.
This may help you. After 5-seconds have passed, Javascript checks if jQuery is available, if not, then loads the library from local server.
1. With a timer
<script>
setTimeout(function() {
if(window.jQuery) return;
var n = document.getElementsByTagName("script")[0];
n.parentNode.insertBefore(document.createElement("script"), n).src = "assets/jQuery/jquery-1.7.2.min.js";
}, 5000);
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
2. This one doesn't have a timer, it loads a local version if the CDN version fails.
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.js"></script>
<script type="text/javascript">window.jQuery || document.write("<script type='text/javascript' src='js/jquery-1.8.3.min.js'>\x3C/script>")</script>
Consider the following HTML:
<!DOCTYPE html>
<html>
<body>
Test page
<!--Start of Zopim Live Chat Script-->
<script type="text/javascript">
window.$zopim||(function(d,s){var z=$zopim=function(c){z._.push(c)},$=z.s=
d.createElement(s),e=d.getElementsByTagName(s)[0];z.set=function(o){z.set.
_.push(o)};z._=[];z.set._=[];$.async=!0;$.setAttribute('charset','utf-8');
$.src='//cdn.zopim.com/?pTR0FiicfJ4aMcmuHI9HfVAB4uzKeFIT';z.t=+new Date;$.
type='text/javascript';e.parentNode.insertBefore($,e)})(document,'script');
</script>
<!--End of Zopim Live Chat Script-->
<script type="text/javascript" async="">
window.addEventListener('load', function() {
var b = document.getElementsByTagName("body")[0];
var o = document.createElement("object");
o.setAttribute("type", "application/x-shockwave-flash");
var t = b.appendChild(o);
});
</script>
</body>
</html>
jsFiddle here: http://jsfiddle.net/V9jtD/
It contains Zopim widget code, another async script that just adds an "object" tag to DOM. Let the above file be served by a webserver (you can use the jsFiddle: http://fiddle.jshell.net/V9jtD/show/). Open it in MacOSX Firefox 15+ with Firebug activated.
You will see the following error repeated many times:
TypeError: non-native scope object
If the errors donot show up, just refresh the page. And mainly Zopim fails to show up (sometimes appears with incomplete functionality). I could not reproduce this in Firefox/Ubuntu. (It might be reproducible in Firefox/Windows I could not check).
However, in the following cases Zopim loads fine:
Comment out "var t = b.appendChild(o);" (ie. dont append the "object" tag to DOM, or you can just remove second script tag entirely).
Deactivate Firebug.
I essentially want to understand who is causing the error so I can debug it.
Is it Firebug, because deactivating it Zopim works fine? But then removing the second script tag with Firebug enabled does not cause the error.
Is it the second script tag, because removing it Zopim works fine? But then deactivate Firebug and Zopim works fine even with second script tag.
Or is it Zopim itself?
Note that the content in the second script tag is part of function testPlayerVersion() in swfobject.js. I have hit this error when I am trying to add Zopim to a page that already has swfobject.
It would probably be easier to work out if the fiddle wasn't loading minified JS from Zopim, but the offending line (roughly beautified) is:
try {
(0)()
} catch (v) {
s = v.arguments ?
'chrome' : v.stack ?
'firefox' : window.opera && !('stacktrace' in v) ?
'opera' : 'other'
}
Which looks like a hacky way of detecting the browser. I'm not sure why it's causing Firebug to bail, it might be worth raising an issue with them if it's reproducible.
It's been a common problem among my team for a while. For aparently no reason, IE8 only loads the scripts sometimes. I'm using asp.net mvc3 and some scripts are loaded through _layout.cshtml (which is like a master page that all my others pages inherits). The other bunch of scripts are loaded only when needed. I must mention that i began to use Head.js to load my scripts. I was actually hoping that it would fix the IE8 scripts load problem.
These problems don't occur on firefox or chrome (which is another motive i'm certain it only happens on IE). And as it seems, if a single script breaks on IE8, this break propagates through other scripts.
I understand this may be a problem too broad to be solved with just the forementioned situation. So what i'd like to know is have you had a problem like this? If yes, what did you do to solve it? I really appreciate some directions, since i have to use IE.
Here is the scripts that are being loaded on _layout.cshtml, in the order of loading:
Head.js;
jquery-1.5.1.min.js;
modernizr-1.7.min.js;
jquery-ui-1.8.11.min.js;
jquery.ui.datepicker-pt-BR.js;
jquery.validate.min.js;
jquery.validate.unobtrusive.min.js;
An these are some of the other scripts that i use on some pages (it's not in their order):
jquery-blockUI.js;
jquery.jqGrid-4.1.2.min.js;
jquery.orbit-1.2.3.min.js;
jquery.jqplot.min.js (and some of it's renderers);
jquery.form.wizard-min.js;
ZeroClipboard.js;
EDIT
As T.J. asked for some piece of code, since it would be to hard to identify the problem with only the name of scripts used, here it is.
Scripts loaded on _layout.cshtml
<head>
<script src="#Url.Content("~/Scripts/head.min.js")" language="javascript" type="text/javascript"></script>
//Lots of css files loaded mostly through document.write
<script type="text/javascript" language="javascript">
// if (head.browser.mozilla || head.browser.webkit || head.browser.opera ||
// (head.browser.ie && (head.browser.version == '9.0'))) {
head.js('#Url.Content("~/Scripts/jquery-1.5.1.min.js")')
.js('#Url.Content("~/Scripts/modernizr-1.7.min.js")')
.js('#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")')
.js('#Url.Content("~/Scripts/jquery.ui.datepicker-pt-BR.js")')
.js('#Url.Content("~/Scripts/jquery.validate.min.js")')
.js('#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")');
if (screen.width == 1024) {
head.js('#Url.Content("~/Scripts/resolucao1024.js")');
} else {
head.js('#Url.Content("~/Scripts/resolucaoMaior1024.js")');
}
</script>
#RenderSection("Header", false)
</head>
Scripts loaded on a charts and graphics page
#section Header{
<script type="text/javascript" language="javascript">
head
.js('#Url.Content("~/Scripts/jquery-blockUI.js")')
.js('#Url.Content("~/Scripts/jqGridMVC/grid.locale-pt-br.js")')
.js('#Url.Content("~/Scripts/jqGridMVC/jquery.jqGrid-4.1.2.min.js")')
.js('#Url.Content("~/Scripts/jquery-ConfigAjax.js")')
.js('#Url.Content("~/Scripts/jqPlot/jquery.jqplot.min.js")')
.js('#Url.Content("~/Scripts/jqPlot/jqplot.highlighter.min.js")')
.js('#Url.Content("~/Scripts/jqPlot/jqplot.dateAxisRenderer.min.js")')
.js('#Url.Content("~/Scripts/jqPlot/jqplot.pieRenderer.min.js")')
.js('#Url.Content("~/Scripts/jqPlot/jqplot.donutRenderer.min.js")')
.js('#Url.Content("~/Scripts/jqPlot/jqplot.cursor.min.js")');
</script>
}
EDIT 2
Oh! i forgot to mention that every time i call some javascript functionality, i use head(function() {}) instead of $(function(){}) (it's recommended on head.js site).
EDIT 3
As Zeta asked, i'm providing a print from IE console (it's from my charts and graphic page).
Since i'm brazilian, it's in portuguese. Simple to understand though: it means 'jqplot' is null or is not an object. Like jqplot was never loaded or had some problem during the loading.
I resolved doing this:
head.js("js/jquery-1.7.1.min.js",
function() {
head.js("js/script.js");
head.js("js/script1.js");
head.js("js/script2.js");
head.js("js/script3.js");
});
Hope it helps
Broad question, so I cant promise this will fix it.
But I think about a year ago I had a problem with IE8 and script tags which sounds just like yours.
I noticed that if the script tag did not have all the added extras:
type="text/javascript"
language="javascript"
and the server did not send
http header-> content type: text/javascript
It did not work.
Give it a check, but thats all I got
This is a basic question but google didn't provide any help.
I have a website and what to beable to run javascript on it.
In my directories I have index.html, and index.css. For the javascript file, I'm assuming it should be called index.js.
In my index.js file I have this:
var countTime = 0; // Number of seconds
var redirectURL = "http://example.com"; // URL to direct to
countTime = (countTime+1)*1000;
function updateCount(){
countTime = countTime-1000;
if(document.getElementById("countdownDisplay"))
document.getElementById("countdownDisplay").innerHTML = (countTime/1000);
if(countTime <= 0)
location.href = redirectURL;
else
setTimeout("updateCount()",1000);
}
updateCount();
However it's not working when I visit the page with a browser.
Do I have to do something in my html file like include index.js or something?
<script src="index.js" type="text/javascript"></script>
Should go in your <head>.
This will load the script for you and then the code gets executed.
Your also going to need something like
<div id="countdownDisplay"></div> in your <body> for the countdown to work.
Whilst I'm at it you probably want a
<style src="index.css" type="text/css"></style> in your <head> as well if you havn't already.
Yes, you need to include it in the HTML file. Here are some instructions.
basically when trying to write some html, you can either search on google how to write the code or as well search for a page which provides what you want to do and look into it's source. This way google would have helped you, because google uses javascript.
In addition, check your totalvalidator. It is a very useful firefox plugin for advanced html validation. It supports better evaluation than the w3c validator does.