I need to load the Recaptcha library on demand, with javascript (using Prototype):
var captcha = new Element('script', { type: 'text/javascript', src: 'http://api.recaptcha.net/js/recaptcha_ajax.js' });
$$('head')[0].appendChild(captcha);
captcha.onload = function()
{
Recaptcha.create("dsfahsldkjfhlasdjfc","recaptcha_image", {theme: "custom"});
};
The problem here is with IE, of course. It seems that the captcha.onload wont work in IE. As usual, it works on other browsers.
So, how can I check that the script is loaded on IE, and call Recaptcha.create afterwards?
Is there an easier and cross-browser way of loading and evaluating external scripts with Prototype?
Thanks.
Looks like IE doesn't support onload, but DOES support onreadystatechange:
Sample code for onreadystatechange:
http://gist.github.com/461797
Post with some info, source of above link:
http://blog.andrewcantino.com/post/211671748/replacement-for-script-onload-in-ie
Related
Disclaimer. I am fairly new to JavaScript so if my workflow does not make sense at all or my vocabulary is yet not precise enough, please advise me of how I should do it differently.
Current Approach
I want to learn more about Javascript and external libraries. Thus, I created a snippet under the DevTools sources panel in Chrome, opened about:blank and executed the following code (which I copied and paste from different sources):
Code
(function(head) {
var newscript = document.createElement('script');
newscript.type = 'text/javascript';
newscript.async = true;
newscript.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js";
(document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(newscript);
})(document.getElementsByTagName('head'));
function jQueryReady() {
if (window.jQuery) {
jQuery.noConflict();
my_code(jQuery);
} else {
setTimeout(jQueryReady, 100);
}
}
jQueryReady();
function my_code($) {
console.log("OK");
$('head').append($('script', {
type : 'text/javascript',
async: true,
src : 'https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.js'
}));
$('head').append($('link', {
rel : 'stylesheet',
href: 'https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.css'
}));
}
Results
When I then look at the source code of about:blank, I see that the jQuery script is correctly inserted into the head, I see that the console shows OK, but neither the spectrum JavaScript nor the CSS is inserted into the head.
Questions
Why is the code not added to the HTML file? What do I need to change to make it work?
If I run teh code from my_code directly in teh console everything seems to work fine? Why?
What is a proper way of playing around with JavaScript without writing an own html file? Does the approach via snippet and about:blank make sense? I need to reload about:blank whenever I make changes to the snippet, so I guess there is a more elegant way to do so. Any tipps on how to do that better?
I believe it is because in jQuery, when you want to create new elements, it is necessary to wrap tag name in "<>" signs like this:
$('head').append($('<script>', {
type: 'text/javascript',
async: true,
src: 'https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.js'
}));
In my opinion it is good idea to try sites like jsfiddle.net or codepen.io, where at least you can add libraries like jQuery easier without writing additional importing scripts and it is easier to maintain.
My question regards the Apps CSP https://developer.mozilla.org/en-US/Apps/CSP
Here it says that all the remote script, inline script, javascript URIs, and other security issues won't work on a Firefox OS app.
So, I tried to download a script that is necessary for my app (Flurry and Ad service) and neither would work on the device. The way I made the call was with AJAX, that way I would avoid the remote and inline scripting that both scripts ment. In the simulator works perfectly, but on the device the ads never show and the Flurry session never starts.
Here is the part of my code where I make the AJAX call for Flurry:
$.ajax({
url: 'https://cdn.flurry.com/js/flurry.js',
dataType: "script",
xhrFields: {
mozSystem: true
},
success: function(msg){
console && console.log("Script de Flurry: luego de la descarga en AJAX "+msg);
flurryLibrary = true;
FlurryAgent.startSession("7ZFX9Z4CVT66KJBVP7CF");
},
error:function(object,status,errortxt){
console && console.log("The script wasn't downloaded as text. The error:" +errortxt);
flurryLibrary = false;
},
always: function(object,status,errortxt){
console && console.log("The script may or may not be downloaded or executed. The error could be:" +errortxt);
}
});
In my app I use the systemXHR permission and make the calls for other websites using this line:
request = new XMLHttpRequest({ mozSystem: true });
Wich is the same as using the xhrFields{mozSystem:true} in the AJAX call.
I believe it's not a cross domain problem because in the rest of my app I make calls for xml files that are not in my domain, and the calls are returned succesfully.
So, my question is, can a Firefox OS app execute scripts that are downloaded via AJAX? Is there a way to get around this problem?
Thank you for your time.
PS: I forgot to add that my app is privileged, just in case you ask
I believe that is a security feature and the short answer to your question would be NO. To quote the CSP doc that you linked to yourself:
You cannot point a at a remote JavaScript file. This means that all JS files that you reference must be included in your app's package.
If you load a JS file using ajax from a remote server, that JS is not included in your app package. You should be careful to obey CSP restrictions. It is possible to get many things working in the simulator or even the phone while developing without fully complying to CSP, but that does not mean it is OK. When you submit your app in future to any credible marketplace (such as Firefox Marketplace), it will be reviewed carefully to make sure it does not violate CSP restrictions. As a general rule of thumb, I would say any attempt at dynamically evaluating JS code will be a security risk and most likely banned by CSP regulations.
First, I'll point out that your two examples are not equivalent.
$.ajax({
xhrFields: {
mozSystem: true
},
});
Is the same as
request = new XMLHttpRequest();
request.mozSystem = true;
which is not the same as
request = new XMLHttpRequest({ mozSystem: true });
Instead, we can follow the advice in the linked bug report and run the following at application load time:
$.ajaxSetup( {
xhr: function() {
return new window.XMLHttpRequest( {
mozSystem: true
} );
}
} );
This alone should fix your problem. However, if it doesn't work, then the next workaround here is to fetch the script resource as plain text and then load that text content as a script.
However, inline scripts and data: URLs are off-limits for privileged Firefox OS apps. We might still accomplish this goal through a blob: URL, however:
window.URL = window.URL || window.webkitURL;
var request = new XMLHttpRequest({ mozSystem: true });
request.open("GET", "https://cdn.flurry.com/js/flurry.js");
// when the Ajax request resolves, load content into a <script> tag
request.addEventListener("load", function() {
// make a new blob whose content is the script
var blob = new Blob([request.textContent], {type: 'text/javascript'});
var script = document.createElement('script');
script.src = window.URL.createObjectURL(blob);
// after the script finishes, do something else
script.addEventListener("load", function() {
flurryLibrary = true;
FlurryAgent.startSession("7ZFX9Z4CVT66KJBVP7CF");
});
document.body.appendChild(script);
});
However, if the script itself does something not allowed by the CSP, then you're definitely out of luck.
You must use mozSystem and mozAnon properties, example:
var xMLHttpRequest = new XMLHttpRequest({
mozAnon: true,
mozSystem: true
});
Its a shame this is a problem, I was hoping on getting loadScript working, as firefoxOS is an environment, and in my app all the application code is HTML5 and local, the current rule is all the scripts need to be loaded in memory in one shot, unless you url load a full page, which means you can not have a persisten wrapper around the site, and ajax inthe pages with assosiated scripts when needed. you would have thought that firefox would have enabled local lazy load for scripts at least. works in chrome, but not in firefox.
I have a JsFiddle here, and added Microsoft AJAX to be loaded through external JS/resource section. How can I tell whether or not my JS code is run after the AJAX file has finished loading?
Seems that the AJAX does not load either. :(
Here is the code in the JSFiddle:
Type.registerNamespace("Tutorial.Chapter1");
Tutorial.Chapter1.Person = function(firstName, lastName) {
this._firstName = firstName;
this._lastName = lastName;
};
Tutorial.Chapter1.Person.prototype = {
set_firstName: function(value) {
this._firstName = value;
},
get_firstName: function() {
return this._firstName;
},
set_lastName: function(value) {
this._lastName = value;
},
get_lastName: function() {
return this._lastName;
},
_firstName: "",
_lastName: "",
displayName: function() {
alert("Hi! " + this._firstName + " " + this._lastName);
}
};
Tutorial.Chapter1.Person.registerClass("Tutorial.Chapter1.Person", null);
The External Resources tab of jsFiddle is currently somewhat tricky and unstable to use.
The resources defined here are often not correctly included into the code. There seems to be an issue with the automatic recognition of JS and CSS resources. If this happens, the external resource is simply not added to the head section of the resulting code. You can check that by reviewing the source code of the Result frame of your jsFiddle. You will find that your MS AJAX resource is simply NOT mentioned in the resulting HTML code.
The correct recognition can actually be forced by adding a dummy value to the resource's URL like this (see –>jsFiddle docs for more info):
...&dummy=.js
Here is an example that shows how to add the external Google Maps API resource to a jsFiddle (mind the dummy parameter at the very end!):
https://maps.googleapis.com/maps/api/js?sensor=false&dummy=.js
Unfortunately this won't work for you as the MS AJAX URL will fail when additional parameters are appended.
A solution (and currently the safest way to load external resources) is to avoid the External Resources tab altogether and load external code manually in the first line(s) of jsFiddle's HTML window like this:
<script type='text/javascript' src="http://ajax.aspnetcdn.com/ajax/3.5/MicrosoftAjax.js"></script>
Here is your jsFiddle modified to use that method: http://jsfiddle.net/rEzW5/12/
It actually does not do a lot (I did not check what is wrong with the rest of your code), but at least it does not throw JavaScript errors anymore.
Open "Add Resources" section and add the url of your external script...
#Jpsy's approach no longer seems to work (see my comment under his answer).
For me, adding the resource under External Resources also didn't work. (According to the Firefox Debugger, it couldn't find the resource).
The only way I was able to get an external bit of JavaScript code (in my case jquery.backstretch.js) to work, was to use Google to find a Fiddle which used this resource (and worked), then Fork this Fiddle and copy/paste all my code into the HTML, CSS and JavaScript panels. Ugh!
#clayRay, You absolutely went thru a code surgery. Just resolved that by mentioning external source in plain html which in my case is
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
Using External Resources tab didn't help a bit...
I have the following code for submitting data using ajax from forms of class ajax. this works perfectly in Firefox, Safari and Chrome but fails in IE.
ajax: function() {
$('form.ajax').live('submit', function() {
var form_ajax = $(this);
$.ajax({
url: form_ajax.attr('action'),
data: form_ajax.serialize(),
type: form_ajax.attr('method'),
dataType: 'script',
beforeSend: function(xhr) {
$('#ajax-bid-new .ajax-form-error, #ajax-bid-new .ajax-form-success').remove();
form_ajax.slideUp();
}
});
return false;
});
Please help - I am stuck here for past 2 days. I am returning a Javascript file from server to be evaluated inside browser. This works as expected in Firefox, Chrome and Safari, but IE receives it as a file and opens up the file download dialog.
What can I do in IE to make this work? I tried by dropping the following code in my application.js file (I'm doing a rails project btw)
// public/javascripts/application.js
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})
I get the same behavior from IE even after writing the ajaxSetup block like above.
Looks like live doesn't work with submit in IE. Have you tried using a normal submit instead:
$('form.ajax').submit(function() {
To catch live form submit events in IE, instead of:
$("form").live("submit", function() { ... });
to
var submitHandler = function() { ... };
$("body").children().each(function() {
$("form", this).live("submit", submitHandler);
})
Point to be noted
IE caches AJAX requests really aggressively (more so than Firefox, anyway). You need to set the Cache-Control headers in the response appropriately if this is not right for your site.
change your content type, last time i fixed similar problem by changing content-type from application/json; charset=utf8 to just plain application/json
jQueries' bind and live behaviours along with the liveQuery plugin
LiveQuery plugin solved the problem http://github.com/brandonaaron/livequery
I find this excellent code, posted by aemkei as answers to this questions:
How do you dynamically load a javascript file? (Think C’s #include)
Use javascript to inject script references as needed?
You may write dynamic script tags
(using Prototype):
new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
The problem here is that we do not
know when the external script file is
fully loaded.
We often want our dependant code on
the very next line and like to write
something like:
if (iNeedSomeMore){
Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
myFancyMethod(); // cool, no need for callbacks!
}
There is a smart way to inject script
dependencies without the need of
callbacks. You simply have to pull the
script via a synchronous AJAX request
and eval the script on global level.
If you use Prototype the Script.load
method looks like this:
var Script = {
_loadedScripts: [],
include: function(script){
// include script only once
if (this._loadedScripts.include(script)){
return false;
}
// request file synchronous
var code = new Ajax.Request(script, {
asynchronous: false, method: "GET",
evalJS: false, evalJSON: false
}).transport.responseText;
// eval code on global level
if (Prototype.Browser.IE) {
window.execScript(code);
} else if (Prototype.Browser.WebKit){
$$("head").first().insert(Object.extend(
new Element("script", {type: "text/javascript"}), {text: code}
));
} else {
window.eval(code);
}
// remember included script
this._loadedScripts.push(script);
}
};
I found that, the code does not work on IE if the all of them is executed in 'file://' protocol, however, it is not the problem since its use case involved real web application.
I tried it once to include http://www.google-analytics.com/urchin.js by google, but from one of web page, but it looks like it cannot request javascript file from different domain.
How we could dynamically add javascript, just like what above scripts does, but from another domain?
You can use the onload and onreadystatechange event to understand when the <script> tag is loaded.
var script = new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
script.onload = script.onreadystatechange = function(){
if (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") {
//script is loaded
}
};
The security model in modern browsers prevents JavaScript from making cross-domain requests. That has holes (see every website exploit since the beginning of the internet), but using them is more than a little shady and it's only a matter of time before they're patched.
What Rex said is correct, although HTML5 has added cross domain messaging and xhr, which require a little bit of work on your part but should be usable to achieve this. Alas they're not yet present in all released browsers (i think the latest betas of safari, firefox, and IE have support for some of these features, but i'm not sure which browsers support which apis)