I have a site that loads information using the XMLHttpRequest when a user clicks a link. The system works well but I would like to be able to execute JavaScript gathered in this process.
This is a problem as I would like to download the scripts 'on demand' if it were, rather than loading them all when the page is loaded.
Thanks for any help
I believe the recommended solution is something like this:
function include(scriptUrl)
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", scriptUrl);
xmlhttp.onreadystatechange = function()
{
if ((xmlhttp.status == 200) && (xmlhttp.readyState == 4))
{
eval(xmlhttp.responseText);
}
};
xmlhttp.send();
}
Or something like it.
However, be wary of this approach. It's vulnerable to cross-site scripting, which can open you (and your users) up to all sorts of nastiness. You'll want to take suitable precautions.
Recently I found the answer (It works in Chrome, in another browsers it was not tested).
You can create dataURL string and put it into src attribute of script element.
var xhr = XMLHttpRequest(),
doc = document;
xhr.open('GET', pathToJSFile, true);
xhr.onload = function () {
var script = doc.createElement('script'),
base64 = 'data:application/javascript;base64,';
try {
base64 += btoa(data.responseText);
} catch (e) {
// script file may contain characters that not included in Latin1
var symbols = data.responseText.split('');
for (var i = 0, l = symbols.length; i < l; i++) {
var symbol = symbols[i];
// here we are trying to find these symbols in catch branch
try {
btoa(symbol);
} catch (e) {
var code = symbol.charCodeAt(0).toString(16);
while (code.length < 4) {
code = '0' + code;
}
// replace original symbol to unicode character
symbols[i] = '\\u' + code;
}
}
// create new base64 string from string with replaced characters
base64 += btoa(symbols.join(''));
} finally {
script.src = base64;
// run script
doc.body.appendChild(script);
}
};
xhr.send();
You can subscribe to xhr.onprogress to show progress bar.
Update. You can download your script file as blob, and then create blob-url.
var xhr = XMLHttpRequest(),
doc = document;
xhr.responseType = 'blob';
xhr.open('GET', pathToJSFile, true);
xhr.onload = function () {
var script = doc.createElement('script'),
src = URL.createObjectURL(xhr.response);
script.src = src;
doc.body.appendChild(script);
};
xhr.send();
You can run script downloaded in form of a string using
eval()
However I would recommend you to add new
<script src='..'></script>
to your document and have a callback which will be called when it will be downloaded. There are many utils and jquery plug-ins for that.
I had the challenge on a mobile web-project, the magic was to set "overrideMimeType".
This has been verified to work on Android 4.1 to Android 6.0.
var head = document.getElementsByTagName('head');
var injectedScript = document.createElement('script');
head[0].appendChild(injectedScript);
var myRequest = new XMLHttpRequest();
myRequest.onreadystatechange = function() {
if (myRequest.readyState == 4 && myRequest.status == 200) {
injectedScript.innerHTML = myRequest.responseText;
//run a function in the script to load it
}
};
function start(){
myRequest.open('GET', 'javascript-url-to-download', true);
myRequest.overrideMimeType('application/javascript');
myRequest.send();
}
start();
You would need to use eval to parse the javascript from the XHR, note that this is EXTREMELY dangerous if you don't have absolute trust in the source of the javascript.
Related
I'am trying to parse site. The site (i suppose) using scripts and data bases to load data from (dynamically?). And this is my problem... I am trying to grab data through C# (unfortunately i don't have access to code right now) or JS. And it seems like either C# and JS, get only template of the site, but don't wait until all scripts executed. So this is my question, is there any way to get ALL html source? Maybe call scripts somehow. Or make a request, wait for 10 seconds, and then write source html data into variable?
Here is my JS code.
function request(link)
{
var xhr = new XMLHttpRequest();
xhr.open('GET', link, true);
xhr.onreadystatechange = function() .
{console.log(xhr.readyState);};
xhr.send();
let data = xhr.responseText;
var tempDiv = document.createElement('div');
tempDiv.innerHTML = data.replace(/<script(.|\s)*?\/script>/g,
'');
return tempDiv;
}
function loadFile(url, timeout, callback)
{
var args = Array.prototype.slice.call(arguments, 3);
var xhr = new XMLHttpRequest();
xhr.ontimeout = function () {
console.error("The request for " + url + " timed out.");
};
xhr.onload = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback.apply(xhr, args);
} else {
console.error(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.timeout = timeout;
xhr.send(null);
let data = xhr.responseText;
return data;
}
function showMessage (message) {
console.log(message + this.responseText);
}
function include(scriptUrl)
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", scriptUrl);
xmlhttp.onreadystatechange = function()
{
if ((xmlhttp.status == 200) && (xmlhttp.readyState == 4))
{
eval(xmlhttp.responseText);
}
};
xmlhttp.send();
let data = JSON.parse(xmlhttp.responseText);
var tempDiv = document.createElement('div');
tempDiv.innerHTML = data.replace(/<script(.|\s)*?\/script>/g,
'');
return tempDiv;
}
All this functions do not work as i want.
This isn't really practical - you're trying to load an HTML page, all associated scripts, then run them on the HTML page as if they were in a proper browser environment, but within your current browser session.
This sort of thing is feasible with the jsdom library if you were running on the server-side (NodeJS), because it simulates browser behaviour: https://github.com/jsdom/jsdom. So you could do
JSDOM.fromURL("https://example.com/", { runScripts: "dangerously" }).then(dom => {
console.log(dom.serialize()); //turn the page back into HTML
});
...to get the whole thing.
I'm trying to handle a simple XML file with JS (server-side). So far, with every solution I've tried out, the value shows as "undefined".
I've tried a couple of different approaches, yet I still cannot get past loading the XML file itself and successfully displaying it.
When I've tried to parse it with a DOMParser, the node value is "undefined" (see the present code please).
Another approach I've taken before is to treat it as an XML file without changing it to string beforehand - then I get the '.getElementsByTagName('node')[0]; is not a function' error.
function loadXML(postID){
var xmlhttp = new XMLHttpRequest();
var file = 'logs/queue'+postID+'.xml';
xmlhttp.onreadystatechange = function() {
console.log('[DEBUG]Readystate: '+this.readyState+', status: '+this.status);
if (this.readyState == 4 && this.status ==200) {
myFunction(this);
};
function myFunction(xmlfile) {
var xmlDoc=null;
var data = "";
var parser = new DOMParser();
var user;
xmlDoc = parser.parseFromString(String(xmlfile), "text/xml");
var nodes = xmlDoc.getElementsByTagName("node");
for(i=0; i< nodes.length; i++){
tipper = nodes[i].getAttribute("id");
}
console.log(user)
}
};
xmlhttp.open("GET", file, true);
xmlhttp.send();
}
My only expectation is to obtain the node attribute value of the "node" element.
Instead, I keep getting the "undefined" value as a result.
After two days of struggling with this matter, I finally came up with a working solution.
Therefore, in case somebody was having a problem similar to mine, here goes:
First, I've npm-installed libxmljs. From this point, after quickly checking its wiki page, things went almost silky smooth. The modified code can be analyzed below:
function loadXML(postID){
var xmlhttp = new XMLHttpRequest();
var file = 'file.xml';
xmlhttp.onreadystatechange = function() {
console.log('[DEBUG]Readystate: '+this.readyState+', status: '+this.status);
if (this.readyState == 4 && this.status ==200) {
myFunction(this);
};
}
function myFunction(xmlfile) {
var xmlDoc = libxmljs.parseXml(xmlfile.responseText);
var nodes = xmlDoc.root().childNodes();
var node = nodes[0];
var node_id = xmlDoc.get('//user').attr('id').value();
var uname = xmlDoc.get('//name');
var uamount = xmlDoc.get('//u_amount');
var umessage = xmlDoc.get('//u_message');
console.log('[DEBUG] [id=%s] User: %s. Amount: %s. Message: %s.', node_id, uname.text(), uamount.text(), umessage.text() );
}
xmlhttp.open("GET", file, true);
xmlhttp.send();
};
I have this function in my JavaScript function:
var xhr = new XMLHttpRequest();
var url = 'url';
xhr.open('GET', url, false);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var str = xhr.responseText;
alert(str);
var resp = JSON.parse(str);
alert('12');
if (0 == resp.ErrorCode) {
alert('13');
}
}
}
xhr.send();
The str is always :
{"ErrorCode":0,"ErrorMessage":"OK","Command":"/api/getvideoinfo/","data":[{"VideoID":"ehcVomMexkY","IsInCache":true,"IsDownloading":false,"AvailableFormats":[{"DisplayName":"720","IsHD":true,"VidEncMimeType":"video/H264","AudEndMimeType":"audio/aac","Width":1280,"Height":720,"PlaybackURL":"","IsDefaultStream":false},{"DisplayName":"360","IsHD":false,"VidEncMimeType":"video/H264","AudEndMimeType":"audio/aac","Width":640,"Height":360,"PlaybackURL":"url","IsDefaultStream":true}]}]}
And i noticed that the script never get to :
alert('12');
Any idea what can cause this?Why the json won't parse? did i need to add any library to the html?
The url is the location of the file on the server. So the url variable should be the path from where you get the data which in this case should be
var url = "/api/getvideoinfo/";
While I was trying to create a workaround for Chrome unsupporting blobs in IndexedDB I discovered that I could read an image through AJAX as an arraybuffer, store it in IndexedDB, extract it, convert it to a blob and then show it in an element using the following code:
var xhr = new XMLHttpRequest(),newphoto;
xhr.open("GET", "photo1.jpg", true);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newphoto = xhr.response;
/* store "newphoto" in IndexedDB */
...
}
}
document.getElementById("show_image").onclick=function() {
var store = db.transaction("files", "readonly").objectStore("files").get("image1");
store.onsuccess = function() {
var URL = window.URL || window.webkitURL;
var oMyBlob = new Blob([store.result.image], { "type" : "image\/jpg" });
var docURL = URL.createObjectURL(oMyBlob);
var elImage = document.getElementById("photo");
elImage.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
This code works fine. But if I try the same process, but this time loading a video (.mp4) I can't show it:
...
var oMyBlob = new Blob([store.result.image], { "type" : "video\/mp4" });
var docURL = URL.createObjectURL(oMyBlob);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
...
<video id="showvideo" controls ></video>
...
Even if I use xhr.responseType = "blob" and not storing the blob in IndexedDB but trying to show it immediately after loading it, it still does not works!
xhr.responseType = "blob";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newvideo = xhr.response;
var docURL = URL.createObjectURL(newvideo);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
The next step was trying to do the same thing for PDF files, but I'm stuck with video files!
This is a filler answer (resolved via the OP found in his comments) to prevent the question from continuing to show up under "unanswered" questions.
From the author:
OK, I solved the problem adding an event that waits for the
video/image to load before executing the revokeObjectURL method:
var elImage = document.getElementById("photo");
elImage.addEventListener("load", function (evt) { URL.revokeObjectURL(docURL); }
elImage.setAttribute("src", docURL);
I suppose the revokeObjectURL method was executing before the video
was totally loaded.
I'd like to refer to the version number as defined in my manifest.json in my extension's JavaScript files.
Is there any way to do this?
Since chrome 22 you should use chrome.runtime.getManifest(). See docs here.
So now it is as simple as:
var manifest = chrome.runtime.getManifest();
console.log(manifest.name);
console.log(manifest.version);
I think that this is what you're looking for http://www.martinsikora.com/accessing-manifest-json-in-a-google-chrome-extension
chrome.manifest = (function() {
var manifestObject = false;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
manifestObject = JSON.parse(xhr.responseText);
}
};
xhr.open("GET", chrome.extension.getURL('/manifest.json'), false);
try {
xhr.send();
} catch(e) {
console.log('Couldn\'t load manifest.json');
}
return manifestObject;
})();
And that's all. This short code snippet loads manifest object and put's it among other chrome.* APIs. So, now you can get any information you want:
// current version
chrome.manifest.version
// default locale
chrome.manifest.default_locale