Is there a way to check if the page has changed when the page is on the local filesystem (file:/// not http:// or https://)?
There are answers here that use AJAX to check the headers for a "Last-Modified" header but this is not returned when using the file protocol.
Here's the headers returned in Firefox when testing locally:
// headers
Content-Type: text/xml
Content-Length: 20941
Update:
It looks like the response is set to the file and that has a lastModified property on response. I've added an answer.
Based off of your replies in the comments, I would use some form of caching the page.
Check this answer to get the page's current contents as a string and cache it/a hash of it. From there, perform an AJAX request to the current page and compare the contents/hashes. If they do not match, then you can reload the page.
let contents = document.documentElement.outerHTML;
let frequency = 5000; // How often to check for changes (ms)
setInterval(() => {
let xmlhttp = new XMLHttpRequest();
xmlhttp.addEventListener("load", (res) => {
if(res.responseText != contents)
window.location.reload(true);
});
xmlhttp.open("GET", window.location.href);
xmlhttp.send();
}, frequency);
What you could do is change the request header from HEAD to GET/POST, which'll return a responseText value. From there, it's very easy to check - have a value before the GET/POST call named original or something similar, compare it to the response, and change stuff if necessary.
After some debugging I noticed that the response is set to the file in the response property and that has a lastModified property on it!
Modifiying the code in #bugfroggy's answer this seems to work:
function checkForChanges() {
var contentDate = null;
var frequency = 5000;
setInterval(() => {
let xmlhttp = new XMLHttpRequest();
xmlhttp.addEventListener("load", (progressEvent) => {
console.log("Content loaded()");
if (contentDate==null) {
contentDate = xmlhttp.response.lastModified;
return;
}
if(xmlhttp.response.lastModified != contentDate) {
window.location.reload(true);
}
});
//xmlhttp.open("GET", window.location.href);
xmlhttp.open("HEAD", window.location.href);
//xmlhttp.responseType = "blob";
xmlhttp.send();
}, frequency);
}
checkForChanges();
Note: I think the code above uses ES5 or ES6 features and I don't know if this will work on a server (http:// or https://).
Use a dev webserver that auto reloads on changes, better to test with a web server over serving from the file system anyway.
Related
I developed a small Javascript/jQuery program to access a collection of pdf files for internal use. And I wanted to have the information div of a pdf file highlighted if the file actually exist.
Is there a way to programmatically determine if a link to a file is broken? If so, How?
Any guide or suggestion is appropriated.
If the files are on the same domain, then you can use AJAX to test for their existence as Alex Sexton said; however, you should not use the GET method, just HEAD and then check the HTTP status for the expect value (200, or just less than 400).
Here's a simple method provided from a related question:
function urlExists(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.status < 400);
}
};
xhr.open('HEAD', url);
xhr.send();
}
urlExists(someUrl, function(exists) {
console.log('"%s" exists?', someUrl, exists);
});
Issue is that JavaScript has the same origin policy so you can not grab content from another domain. This won't change by upvoting it (wondering about the 17 votes).
I think you need it for external links, so it is impossible just with .js ...
If the files are not on an external website, you could try making an ajax request for each file. If it comes back as a failure, then you know it doesn't exist, otherwise, if it completes and/or takes longer than a given threshold to return, you can guess that it exists. It's not always perfect, but generally 'filenotfound' requests are quick.
var threshold = 500,
successFunc = function(){ console.log('It exists!'); };
var myXHR = $.ajax({
url: $('#checkme').attr('href'),
type: 'text',
method: 'get',
error: function() {
console.log('file does not exist');
},
success: successFunc
});
setTimeout(function(){
myXHR.abort();
successFunc();
}, threshold);
You can $.ajax to it. If file does not exist you will get 404 error and then you can do whatever you need (UI-wise) in the error callback. It's up to you how to trigger the request (timer?) Of course if you also have ability to do some server-side coding you can do a single AJAX request - scan the directory and then return results as say JSON.
Like Sebastian says it is not possible due to the same origin policy. If the site can be published (temporarily) on a public domain you could use one of the link checker services out there. I am behind checkerr.org
As others have mentioned, because of JavaScript's same origin policy, simply using the function from the accepted answer does not work. A workaround to this is to use a proxy server. You don't have to use your own proxy for this, you can use this service for example: https://cors-escape.herokuapp.com (code here).
The code looks like this:
var proxyUrl = "https://cors-anywhere.herokuapp.com/";
function urlExists(url, callback) {
var sameOriginURL = proxyUrl + url;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.status < 400);
}
};
xhr.open('HEAD', sameOriginURL);
xhr.send();
}
urlExists(someUrl, function(exists) {
console.log('"%s" exists?', someUrl, exists);
});
i want to make a script that makes every video's comment section look like the ones that still have the old kind.
for example, videos on this channel:https://www.youtube.com/user/TheMysteryofGF/videos
in Firebug, in the Net tab, i noticed the comment JSON file's URL it is requested from is different.
i tried to run a code on the youtube watch page which would request the file the same way, but it doesnt work, and in firebug it says it was forbidden.
the URL is the same, they are both POST, and i cant figure out what is different. i can even resend the original request in firebug and it works... so anyway, here is a code i tried on a video with "1vptNpkysBQ" video url.
var getJSON = function(url, successHandler, errorHandler) {
var xhr = typeof XMLHttpRequest != 'undefined'
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
xhr.open('post', url, true);
xhr.onreadystatechange = function() {
var status;
var data;
// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-readystate
if (xhr.readyState == 4) { // `DONE`
status = xhr.status;
if (status == 200) {
data = JSON.parse(xhr.responseText);
successHandler && successHandler(data);
} else {
errorHandler && errorHandler(status);
}
}
};
xhr.send();
};
getJSON('https://www.youtube.com/watch_fragments_ajax?v=1vptNpkysBQ&tr=time&frags=comments&spf=load', function(data) {
alert('Your public IP address is: ' + data);
}, function(status) {
alert('Something went wrong.');
});
You are using Ajax to get data. Ajax has 1 restriction: You can only get data from your own server. When you try to get data from another server/domain, you get a "Access-Control-Allow-Origin" error.
Any time you put http:// (or https://) in the url, you get this error.
You'll have to do it the Youtube way.
That's why they made the javascript API. Here is (the principal of) how it works. You can link javascript files from other servers, with the < script > tag
So if you could find a javascript file that starts with
var my_videos = ['foo', 'bar', 'hello', 'world'];
then you can use var my_videos anywhere in your script. This can be used both for functions and for data. So the server puts this (dynamically generated) script somewhere, on a specific url. You, the client website can use it.
If you want to really understand it, you should try building your own API; you'll learn a lot.
Secondary thing: Use GET.
POST means the client adds data to the server (example: post a comment, upload a file, ...). GET means you send some kind of ID to the server, then the server returns its own data to the client.
So what you are doing here, is pure GET.
I am grabbing some (random) images from a search machine and display them to the users. The problem is: It may happen that some images require a http authentification (username/password). I dont want to have those images... they should be removed without displaying the popup where you can enter the username and password.
Actually I am using simple jquery methods to display my images.
var displayNode = ....
....
var m_img = $("<img />", {src : "...."});
m_img.bind('error', function (e) {
$(this).remove();
});
displayNode.append(m_img);
Now I load the image directly and if an error occurs it will be removed. But.. when server sends back a HTTP (Basic) Authentification flag this is of course not an error. Hence there is an input prompt. When I click on "cancel" the propmt closes, jquery treats this as an error and removes the image.
So.. what is the best way to check if there is authentification and if not display it to the user?
You can make a HEAD request that only returns headers, then check the headers for the authentication header before actually making the request:
The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request. This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
You would use this method to first check the headers, then if OK, get the image. You'll have to test it with your protected resource, I'm not sure what the browser will do when requesting just the HEAD and I don't have a local protected resource to test against (CORS got me on the online resources I was trying to test against). Fiddle:
var basicAuthProtectedURL = 'http://fiddle.jshell.net';
var xhr = new XMLHttpRequest();
xhr.open("HEAD", basicAuthProtectedURL, true);
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.getAllResponseHeaders()); //all headers
console.log(xhr.getResponseHeader('Content-Type')); //just the one you want
if (xhr.getResponseHeader('WWW-Authenticate')) {
console.log('I got the authentication header, skip this request.');
} else {
console.log('no header, resource unsecure');
}
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.send(null);
http://jsfiddle.net/5z5bnwgz/
Post back how it goes!
I'm using javascript to pass a dynamic url to iframe src. but sometimes the url does not exist, how could i detect the non-exist url beforehand, so that i can hide the iframe that with 404 error.
Due to my low reputation I couldn't comment on Derek 朕會功夫's answer.
I've tried that code as it is and it didn't work well. There are three issues on Derek 朕會功夫's code.
The first is that the time to async send the request and change its property 'status' is slower than to execute the next expression - if(request.status === "404"). So the request.status will eventually, due to internet band, remain on status 0 (zero), and it won't achieve the code right below if. To fix that is easy: change 'true' to 'false' on method open of the ajax request. This will cause a brief (or not so) block on your code (due to synchronous call), but will change the status of the request before reaching the test on if.
The second is that the status is an integer. Using '===' javascript comparison operator you're trying to compare if the left side object is identical to one on the right side. To make this work there are two ways:
Remove the quotes that surrounds 404, making it an integer;
Use the javascript's operator '==' so you will be testing if the two objects are similar.
The third is that the object XMLHttpRequest only works on newer browsers (Firefox, Chrome and IE7+). If you want that snippet to work on all browsers you have to do in the way W3Schools suggests: w3schools ajax
The code that really worked for me was:
var request;
if(window.XMLHttpRequest)
request = new XMLHttpRequest();
else
request = new ActiveXObject("Microsoft.XMLHTTP");
request.open('GET', 'http://www.mozilla.org', false);
request.send(); // there will be a 'pause' here until the response to come.
// the object request will be actually modified
if (request.status === 404) {
alert("The page you are trying to reach is not available.");
}
Use a XHR and see if it responds you a 404 or not.
var request = new XMLHttpRequest();
request.open('GET', 'http://www.mozilla.org', true);
request.onreadystatechange = function(){
if (request.readyState === 4){
if (request.status === 404) {
alert("Oh no, it does not exist!");
}
}
};
request.send();
But notice that it will only work on the same origin. For another host, you will have to use a server-side language to do that, which you will have to figure it out by yourself.
I found this worked in my scenario.
The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callback methods introduced in jQuery 1.5 are deprecated as of jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
$.get("urlToCheck.com").done(function () {
alert("success");
}).fail(function () {
alert("failed.");
});
I created this method, it is ideal because it aborts the connection without downloading it in its entirety, ideal for checking if videos or large images exist, decreasing the response time and the need to download the entire file
// if-url-exist.js v1
function ifUrlExist(url, callback) {
let request = new XMLHttpRequest;
request.open('GET', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('Accept', '*/*');
request.onprogress = function(event) {
let status = event.target.status;
let statusFirstNumber = (status).toString()[0];
switch (statusFirstNumber) {
case '2':
request.abort();
return callback(true);
default:
request.abort();
return callback(false);
};
};
request.send('');
};
Example of use:
ifUrlExist(url, function(exists) {
console.log(exists);
});
You could test the url via AJAX and read the status code - that is if the URL is in the same domain.
If it's a remote domain, you could have a server script on your own domain check out a remote URL.
Using async/await, this worked for me for opening a new tab; I needed to detect a 404 for the same reason as the OP:
openHelp : async function(iPossiblyBogusURL) {
const defaultURL = `http://guaranteedToWork.xyz`;
const response = await fetch(iPossiblyBogusURL);
if (response.status == 200) {
window.open(iPossiblyBogusURL, `_blank`);
} else if (response.status === 404) {
window.open(defaultURL, `_blank`);
}
},
You can try and do a simple GET on the page, if you get a 200 back it means the page exists. Try this (using jQuery), the function is the success callback function on a successful page load. Note this will only work on sites within your domain to prevent XSS. Other domains will have to be handled server side
$.get(
yourURL,
function(data, textStatus, jqXHR) {
//load the iframe here...
}
);
There is no need to make a separate HTTP request to check beforehand.
You could switch the logic around: only display the iframe if it has been loaded successfully. For this purpose, you can attach an onload event listener to the iframe.
See this related question for details: Capture iframe load complete event
Can I control the HTTP headers sent by window.open (cross browser)?
If not, can I somehow window.open a page that then issues my request with custom headers inside its popped-up window?
I need some cunning hacks.
Can I control the HTTP headers sent by window.open (cross browser)?
No
If not, can I somehow window.open a page that then issues my request with custom headers inside its popped-up window?
You can request a URL that triggers a server side program which makes the request with arbitrary headers and then returns the response
You can run JavaScript (probably saying goodbye to Progressive Enhancement) that uses XHR to make the request with arbitrary headers (assuming the URL fits within the Same Origin Policy) and then process the result in JS.
I need some cunning hacks...
It might help if you described the problem instead of asking if possible solutions would work.
Sadly you can't control headers when doing window.open()
Nice and easy, how I managed to open a file with custom headers:
const viewFile = async (url) => {
// Change this to use your HTTP client
fetch(url, {/*YOUR CUSTOM HEADER*/} ) // FETCH BLOB FROM IT
.then((response) => response.blob())
.then((blob) => { // RETRIEVE THE BLOB AND CREATE LOCAL URL
var _url = window.URL.createObjectURL(blob);
window.open(_url, "_blank").focus(); // window.open + focus
}).catch((err) => {
console.log(err);
});
};
Download file to cache
window.open to cache
If you are in control of server side, it might be possible to set header value in query string and send it like that?
That way you could parse it from query string if it's not found in the headers.
Just an idea... And you asked for a cunning hack :)
As the best anwser have writed using XMLHttpResponse except window.open, and I make the abstracts-anwser as a instance.
The main Js file is download.js Download-JS
// var download_url = window.BASE_URL+ "/waf/p1/download_rules";
var download_url = window.BASE_URL+ "/waf/p1/download_logs_by_dt";
function download33() {
var sender_data = {"start_time":"2018-10-9", "end_time":"2018-10-17"};
var x=new XMLHttpRequest();
x.open("POST", download_url, true);
x.setRequestHeader("Content-type","application/json");
// x.setRequestHeader("Access-Control-Allow-Origin", "*");
x.setRequestHeader("Authorization", "JWT " + localStorage.token );
x.responseType = 'blob';
x.onload=function(e){download(x.response, "test211.zip", "application/zip" ); }
x.send( JSON.stringify(sender_data) ); // post-data
}
You can also use an F5 load balancer, and map the cross-browser URL that you are trying to fetch to an URL inside your domain of origin.
Mapping can be something like:
companyA.com/api/of/interest----> companyB.com/api/of/interest
Assuming your domain of origin is "companyA.com" then the browser will not have any problems in sending all cookies on the header of that request, since it's towards the same domain.
The request hits the load balancer and is forwarded towards "companyB.com" with all headers responses will be sent to the from server side.
You can't directly add custom headers with window.open() in popup window
but to work that we have two possible solutions
Write Ajax method to call that particular URL with headers in a separate HTML file and use that HTML as url in<i>window.open()</i>
here is abc.html
$.ajax({
url: "ORIGIONAL_URL",
type: 'GET',
dataType: 'json',
headers: {
Authorization : 'Bearer ' + data.id_token,
AuthorizationCheck : 'AccessCode ' +data.checkSum ,
ContentType :'application/json'
},
success: function (result) {
console.log(result);
},
error: function (error) {
} });
call html
window.open('*\abc.html')
here CORS policy can block the request if CORS is not enabled in requested URL.
You can request a URL that triggers a server-side program which makes the request with custom headers and then returns the response redirecting to that particular url.
Suppose in Java Servlet(/requestURL) we'll make this request
`
String[] responseHeader= new String[2];
responseHeader[0] = "Bearer " + id_token;
responseHeader[1] = "AccessCode " + checkSum;
String url = "ORIGIONAL_URL";
URL obj = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) obj.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Authorization", responseHeader[0]);
urlConnection.setRequestProperty("AuthorizationCheck", responseHeader[1]);
int responseCode = urlConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new
InputStreamReader(urlConnection.getInputStream()));
String inputLine;
StringBuffer response1 = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response1.append(inputLine);
}
in.close();
response.sendRedirect(response1.toString());
// print result
System.out.println(response1.toString());
} else {
System.out.println("GET request not worked");
}
`
call servlet in window.open('/requestURL')
Use POST instead
Although it is easy to construct a GET query using window.open(), it's a bad idea (see below). One workaround is to create a form that submits a POST request. Like so:
<form id="helper" action="###/your_page###" style="display:none">
<inputtype="hidden" name="headerData" value="(default)">
</form>
<input type="button" onclick="loadNnextPage()" value="Click me!">
<script>
function loadNnextPage() {
document.getElementById("helper").headerData.value = "New";
document.getElementById("helper").submit();
}
</script>
Of course you will need something on the server side to handle this; as others have suggested you could create a "proxy" script that sends headers on your behalf and returns the results.
Problems with GET
Query strings get stored in browser history,
can be shoulder-surfed
copy-pasted,
and often you don't want it to be easy to "refresh" the same transaction.