I am looking for an equivalent to jquery's load() method that will work offline. I know from jquery's documentation that it only works on a server. I have some files from which I need to call the html found inside a particular <div> in those files. I simply want to take the entire site and put it on a computer without an internet connection, and have that portion of the site (the load() portion) function just as if it was connected to the internet. Thanks.
Edit: BTW, it doesn't have to be js; it can be any language that will work.
Edit2:
My sample code (just in case there are syntax errors I am missing; this is for the files in the same directory):
function clickMe() {
var book = document.getElementById("book").value;
var chapter = document.getElementById("chapter").value;
var myFile = "'" + book + chapter + ".html'";
$('#text').load(myFile + '#source')
}
You can't achieve load() over the file protocol, no other ajax request is going to work for html files. I have tried even with the crossDomain and isLocale option on without anything success, even if precising the protocol.
The problem is that even if jQuery is trying the browser will stop the request for security issues (well most browsers as the snippet below works in FF) as it allows you to load locale file so you could get access to a lot of things.
The one thing you could load locally is javascript files, but that probably means changing a lot of the application/website architecture.
Only works in FF
$.ajax({
url: 'test.html',
type: 'GET',
dataType: 'text',
isLocale: true,
success: function(data) {
document.body.innerHTML = data;
}
});
What FF does well is that it detect that the file requesting local files is on the file protocol too when other don't. I am not sure if it has restriction over the type of files you can request.
You can still use the JQuery load function in this context:
You would could add an OfflineContent div on your page:
<div id="OfflineContent">
</div>
And then click a button which calls:
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
Button code:
$("#btnLoadContent").click(function() {
$('#OfflineContent').load('OfflinePage.html #contentToLoad');
});
In the OfflinePage.html you could have to have another section called contentToLoad which would display on the initial page.
Related
I have an html file with many <a> tags with href links.
I would like to have the page do nothing when these links point to an outside url (http://....) or an internal link that is broken.
The final goal is to have the html page used offline without having any broken links. Any thoughts?
I have tried using a Python script to change all links but it got very messy.
Currently I am trying to use JavaScript and calls such as $("a").click(function(event) {} to handle these clicks, but these have not been working offline.
Also, caching the pages will not be an option because they will never be opened online. In the long run, this may also need to be adapted to src attributes, and will be used in thousands of html files.
Lastly, it would be preferable to use only standard and built in libraries, as external libraries may not be accessible in the final solution.
UPDATE: This is what I have tried so far:
//Register link clicks
$("a").click(function(event) {
checkLink(this, event);
});
//Checks to see if the clicked link is available
function checkLink(link, event){
//Is this an outside link?
var outside = (link.href).indexOf("http") >= 0 || (link.href).indexOf("https") >= 0;
//Is this an internal link?
if (!outside) {
if (isInside(link.href)){
console.log("GOOD INSIDE LINK CLICKED: " + link.href);
return true;
}
else{
console.log("BROKEN INSIDE LINK CLICKED: " + link.href);
event.preventDefault();
return false;
}
}
else {
//This is outside, so stop the event
console.log("OUTSIDE LINK CLICKED: " + link.href);
event.preventDefault();
return false;
}
}
//DOESNT WORK
function isInside(link){
$.ajax({
url: link, //or your url
success: function(data){
return true;
},
error: function(data){
return false;
},
})
}
Also an example:
Outside Link : Do Nothing ('#')
Outside Link : Do Nothing ('#')
Existing Inside Link : Follow Link
Inexistent Inside Link : Do Nothing ('#')
Javascript based solution:
If you want to use javascript, you can fix your isInside() function by setting the $.ajax() to be non asynchronous. That is will cause it to wait for a response before returning. See jQuery.ajax. Pay attention to the warning that synchronous requests may temporarily lock the browser, disabling any actions while the request is active (This may be good in your case)
Also instead of doing a 'GET' which is what $.ajax() does by default, your request should be 'HEAD' (assuming your internal webserver hasn't disabled responding to this HTTP verb). 'HEAD' is like 'GET' except it doesn't return the body of the response. So it's a good way to find out if a resource exists on a web server without having to download the entire resource
// Formerly isInside. Renamed it to reflect its function.
function isWorking(link){
$.ajax({
url: link,
type: 'HEAD',
async: false,
success: function(){ return true; },
error: function(){ return false; },
})
// If we get here, it obviously did not succeed.
return false;
}
Python based solution:
If you don't mind preprocessing the html page (and even caching the result), I would go with parsing the HTML in Python using a library like BeautifulSoup.
Essentially I would find all the links on the page, and replace the href attribute of those starting with http or https with #. You can then use a library like requests to check the internal urls and update the appropriate urls as suggested.
Here is some javascript that will prevent you from going to external site:
var anchors = document.getElementsByTagName('a');
for(var i=0, ii=anchors.length; i < ii; i++){
anchors[i].addEventListener('click',function(evt){
if(this.href.slice(0,4) === "http"){
evt.preventDefault();
}
});
}
EDIT:
As far as checking if a local path is good on the client side, you would have to send and ajax call and then check the status code of the call (infamous 404). However, you can't do ajax from a static html file (e.g. file://index.html). It would need to be running on some kind of local server.
Here is another stackoverflow that talks about that issue.
This is similar to: How to open a file using JavaScript?
Goal: to retrieve/open a file on an image's double click
function getFile(filename){
// setting mime this way is for example only
var mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
jQuery.ajax({ url : 'get_file.pl',
data : {filename:filename},
success : function(data){
var win = window.open('','title');
win.document.open(mime);
win.document.write(data);
win.document.close();
}
});
}
jQuery('#imgID').dblclick(function(){
getFile('someFile.docx');
});
I'm doing this off the top of my head, but I think the above would work for text files, but not binary. Is there a plugin that does this properly? The ideal would be to open the file in the browser (or application), rather than download, but I doubt that is a dream. If the file must be downloaded with the save/open dialog, that's fine.
Edit:
One piece of information that I forgot to mention is that I'd like this to be a POST request. This is partly why I was looking at AJAX to begin with. I've seen workarounds that have created forms/iframes to do something similar, but I was looking for a better handler of the returned info.
Seems to me there's no reason to do this via AJAX. Just open the new window to get_file.pl?filename=... and let the browser handle it. If the user has a plugin capable of handling the Content-Type sent by get_file.pl, the file will display; otherwise, it should download like any other file.
function getFile(filename) {
window.open('get_file.pl?filename=' + filename,'title');
}
jQuery('#imgID').dblclick(function() {
getFile('someFile.docx');
});
Edit: If you want to POST to your script, you can do it with some <form> hackery:
function getFile(filename) {
var win = 'w' + Math.floor(Math.random() * 10000000000000);
window.open('', win,'width=250,height=100');
var f = $('<form></form>')
.attr({target: win, method:'post', action: 'get_file.pl'})
.appendTo(document.body);
var i = $('<input>')
.attr({type:'hidden',name:'filename',value:filename})
.appendTo(f);
f[0].submit();
f.remove();
}
Of course, this is somewhat silly since it is impossible to hide your data from "prying eyes" with developer tools. If your filename really is sensitive, issue access tokens to the client, and look up the data in your sever script.
I have seen similar questions around, but none of them seem to have answers that help my case...
Basically, I want to load some HTML in using $.ajax() (Which is on a different domain), and have it parsed into it's own DOM, so I can apply attributes and manipulate HTML in my actual window DOM.
$.ajax({
type: 'GET',
url: 'http://example.com/index.html',
dataType: 'html',
crossDomain: true,
cache: false,
success: function(data)
{
var src = $('body img', data).first().attr("src");
//also tried: var src = $('body', $(data)).first().attr("src");
$('#someDiv img').attr("src", src);
}
});
Where an example HTML file is:
<html>
<body>
<img src="someurl"></img>
</body>
</html>
It works in Firefox, but not IE, no matter what I try, whenever I try to parse and read, it returns null.
Any suggestions?
EDIT:
It appears there was some ambiguity with my question. The issue is the parsing, not the AJAX. The AJAX returns the html string correctly, but jQuery fails to parse it.
EDIT 2:
I found a 'solution', but it isn't nearly as nice as I wanted it to be, it chopping and sorting through the HTML string, and extracting data, rather than applying it to a DOM. Seems to run efficiently, as I can predict the order of data.
Boiled down, it is something like this:
var imgsrcs = new Array(5);
var searchItem = '<img src="';
for (var a=0; a<5; a++) {
var startLoc = data.search(searchItem) + searchItem.length;
for (var i=0; i<data.length; i++) {
if (data.charAt(startLoc + i) == '"')
break;
imgsrcs[a] += data.charAt(startLoc + i);
}
data = data.substring(startLoc + i, data.length);
}
$('.image').each(function(i) {
$(this).attr("src", imgsrcs[i]);
});
Fairly ugly, but I solved my problem, so I thought I may as well post it.
This is a Same Origin Policy problem.
The crossDomain flag in jquery's ajax function doesn't automatically make cross domain requests work in all browsers (not all browsers support CORS). Since you're requesting this from a different domain, a normal request won't actually be able to read the data (or even make the request).
Normally, for json data, you can do JSONP, which is what the crossDomain often flag enables. However, JSON is unique because it can be natively read in javascript. Since HTML cannot be read, you'd need to wrap it in parseable javascript to employ a trick like JSONP.
Rather than do that on your own, though, I'd highly suggest that you look into the easyXDM library in order to do cross domain messages like this. You'd essentially open up a hidden iframe on the other domain, and pass messages back and forth between the parent and the hidden frame. And, since the hidden frame is on the same domain as the html, it will have no problem ajaxing for it.
http://easyxdm.net/wp/
I have an html/javascript frontend that is using JQuery's AJAX request to send XML containing user-entered form data to a backend application which in turn creates a PDF from that information. The frontend receives a UUID in response, which it then uses in the download url to download the generated PDF.
This works wonderfully in Firefox and Safari, but is being blocked by Internet Explorer 8's protection against scripted downloads. Telling IE8 to download the file via the spawned Information Bar forces a reload of the page, which blanks out all of the entered user content.
A single onMouseUp event on a button-esque element is triggering the generation of the XML to send, sending the XML, getting its response, then initiating the download via setting the url in the window.location object. Separating out that download into a different button (having one generate and send the xml and fetch the UUID, and the other only initiate the download using the url made from the UUID) bypasses the information bar but ruins the simplicity and intuitiveness of the interface.
Here are the relevant javascript functions:
function sendXml()
{
var documentXml = generateDocumentXml();
var percentEncodedDocumentXml = escape(DocumentXml);
var url = "generate?document=" + percentEncodedDocumentXml;
$.ajax({
url: url,
type: "GET",
dataType: "xml",
success: function (xml)
{
var uuid = $(xml).find('uuid').text();
getPdf(uuid);
},
error: function (xhr)
{
alert("There was an error creating your PDF template");
}
});
}
function getPdf(uuid)
{
var url = "generate?get-pdf=" + uuid;
window.location = url;
}
I'm fishing for suggestions about how to best handle this issue. My first preference would be to have the information bar not interfere at all, but minimizing its harm would be a dramatic improvement over the current situation. If it could not reload and wipe the frontend interface, and actually proceed to downloading the file when the user chooses to "Download File..." via the Information Bar's menu, that would help.
I tested it and the reason for the bar to occur seems to be the fact, that there is no direct relation between the user-action(mouseover) and the loading of the URL(guess a PDF-file).
This workaround will solve the issue:
Create an iframe(may be hidden) inside the document and use
window.open(url,'nameAttributeOfTheIframe')
...to load the PDF. The bar occurs too, but if the user chooses to download, the current document will reload too, but the user-content(if you mean form-data) will remain, as the bar belongs to the iframe not to the parent document.
Be sure to send a attachment-header with the PDF too, to beware of showing it inside the browser(if the browser is able to), because if you use a hidden iframe the user cannot see what's loaded there.
<iframe name="nameAttributeOfTheIframe" style="display:none"></iframe>
<input type="button" value="click here" onclick="f1()"/>
<input value="default value">
<script type="text/javascript">
<!--
function f1()
{
//simulate delayed download
setTimeout(f2,1000)
}
function f2()
{
window.open('http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf','nameAttributeOfTheIframe');
}
document.getElementsByTagName('input')[1].value='this is modified value, should remain';
//-->
</script>
I'm getting a syntax error (undefined line 1 test.js) in Firefox 3 when I run this code. The alert works properly (it displays 'work') but I have no idea why I am receiving the syntax error.
jQuery code:
$.getJSON("json/test.js", function(data) {
alert(data[0].test);
});
test.js:
[{"test": "work"}]
Any ideas? I'm working on this for a larger .js file but I've narrowed it down to this code. What's crazy is if I replace the local file with a remote path there is no syntax error (here's an example):
http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?
I found a solution to kick that error
$.ajaxSetup({'beforeSend': function(xhr){
if (xhr.overrideMimeType)
xhr.overrideMimeType("text/plain");
}
});
Now the explanation:
In firefox 3 (and I asume only firefox THREE) every file that has the mime-type of "text/xml" is parsed and syntax-checked. If you start your JSON with an "[" it will raise an Syntax Error, if it starts with "{" it's an "Malformed Error" (my translation for "nicht wohlgeformt").
If I access my json-file from an local script - no server is included in this progress - I have to override the mime-type... Maybe you set your MIME-Type for that very file wrong...
How ever, adding this little piece of code will save you from an error-message
Edit: In jquery 1.5.1 or higher, you can use the mimeType option to achieve the same effect. To set it as a default for all requests, use
$.ajaxSetup({ mimeType: "text/plain" });
You can also use it with $.ajax directly, i.e., your calls translates to
$.ajax({
url: "json/test.js",
dataType: "json",
mimeType: "textPlain",
success: function(data){
alert(data[0].test);
} });
getJSON may be insisting on at least one name:value pair.
A straight array ["item0","item1","Item2"] is valid JSON, but there's nothing to reference it with in the callback function for getJSON.
In this little array of Zip codes:
{"result":[["43001","ALEXANDRIA"],["43002","AMLIN"],["43003","ASHLEY"],["43004","BLACKLICK"],["43005","BLADENSBURG"],["43006","BRINKHAVEN"]]}
... I was stuck until I added the {"result": tag. Afterward I could reference it:
<script>
$.getJSON("temp_test_json.php","",
function(data) {
$.each(data.result, function(i, item) {
alert(item[0]+ " " + i);
if (i > 4 ) return false;
});
});
</script>
... I also found it was just easier to use $.each().
This may sound really really dumb, but change the file extension for test.js from .js to .txt. I had the same thing happen with perfectly valid JSON data files with pretty well any extension except .txt (example: .json, .i18n). Since I've changed the extension, I get the data and use it just fine.
Like I said, it may sound dumb but it worked for me.
HI
I have this same error when testing the web page on my local PC, but once it is up on the hosting server the error no longer happens. Sorry - I have no idea of the reason, but thought it may help someone else track down the reason
Try renaming "test.js" to "test.json", which is what Wikipedia says is the official extension for JSON files. Maybe it's being processed as Javascript at some point.
Have you tried disabling all the Firefox extensions?
I usually get some errors in the Firebug console that are caused by the extensions, not by the webs being visited.
Check if there's ; at the end of the test.js. jQuery executes eval("(" + data + ")") and semicolon would prevent Firefox from finding closing parenthesis. And there might be some other unseen characters that prevents it from doing so.
I can tell you why this remote location working though, it's because it's executed in completely different manner. Since it has jsoncallback=? as the part of query parameters, jQuery thinks of it as of JSONP and actually inserts it into the DOM inside <script> tags. Try use "json/test.js?callback=?" as target, it might help too.
What kind of webserver are you running that on? I once had an issue reading a JSON file on IIS because it wasn't defined as a valid MIME type.
Try configuring the content type of the .js file. Firefox expects it to be text/plain, apparently. You can do that as Peter Hoffmann does above, or you can set the content type header server side.
This might mean a server-side configuration change (like apache's mime.types file), or if the json is served from a script, setting the content-type header in the script.
Or at least that seems to have made the error go away for me.
I had a similar problem but was looping through a for loop. I think the problem might be that the index is out of bound.
Kien
For the people who don't use jQuery, you need to call the overrideMimeType method before sending the request:
var r = new XMLHttpRequest();
r.open("GET", filepath, true);
r.overrideMimeType("text/plain");