JQuery .load() function silently fails when offline - iphone

I'm trying to write an iPhone offline webapp using jqtouch/jquery. When I disable my connectivity, it looks like the jquery .load() function silently fails and doesn't even call its callback function. I've set $.ajaxSetup ({cache: true}); but it seems to have no effect. I also have my manifest file etc. working fine - the only issue is the .load() function.
Any ideas?

Are you serving your manifest files with the correct MIME type? From JQTouch Offline Support:
Also, the manifest must be served with
a MIME type of text/cache-mainfest,
which you can accomplish on most
servers with a .htaccess directive:
AddType text/cache-manifest .manifest
To implement the Cache Manifest,
simply reference it in your HTML like
this:
<html manifest="sample.manifest">

Turns out they way I was calling .load() was causing it to do a POST instead of a GET, which meant it bypassed the cache.
I called it as
$.('#some-element').load('path/to/data',
[],
function(responseText, status, XMLHttpRequest) {
alert("Load finished: " + status + ". " + responseText);
}
);
I assumed second empty array was the correct invocation, but this made JQuery do a POST, presumably with zero arguments. The correct invocation for a GET is:
$.('#some-element').load('path/to/data',
function(responseText, status, XMLHttpRequest) {
alert("Load finished: " + status + ". " + responseText);
}
);

Local files return 0 as HTTP status code. This is because they are not retrieved with HTTP (it's local!). jQuery treats this as an error, which is not necessarily bad.
Try the onComplete handler to retrieve both status code, compare it to 0 and try to read the response text.
You may also want to test window.navigator.online (onLine?) to check if you're offline (because the status code 0 should only occur when you're offline).

Related

ZK8 Filedownload.save Cut Filenames

I use "Filedownload.save" to download files with Zk, but i have a problem.
Zk cut my filenames in some characters, for example, if the linema is "FISH#CHIPS.pdf", the file dowload as "FISH.pdf"
Anyone knows how to solve it?
UPDATE:
I have follow the instructions, and i finally see that the server response this JSON:
{"rs":[["download",["/myApp/zkau/view/z_aq5/dwnmed-3/son/FISH#CHIPS.pdf"]]],"rid":9}
And i am lost now, what do Zk with this JSON on the cliente side?
The official ZK-Bug is tracked as ZK-3809
A server side workaround is the following:
split download code such as ...
Filedownload.save("test content", "text/plain", "test#test.txt");
... into ...
AMedia media = new AMedia("test#test.txt", "txt", "text/plain", "test content");
Clients.response(new AuDownload((DeferredValue) () ->
Executions.getCurrent().getDesktop().getDownloadMediaURI(
media, "test#test.txt").replace("#", "%23")));
... allowing to encode special chars as needed.
UPDATE: ZK-3809 has been fixed and will be included in ZK version 8.5.1
The problem is that # is one of those "reserved characters" that, while valid in a URL, are treated in special ways. Look at this question for more details. My guess is that everything after the # is interpreted as a fragment on the page, and hence ignored in this case.
There are ways to fix this, for example by replacing # by %23. But doing this on the server side when calling Filedownload.save changes the filename to literally FISH%23CHIPS.pdf.
Instead, we can intercept the client side method that downloads the file when the response you showed arrives. This way, zk will still give the file its normal name, and only the download will sanitize the URL. Add this to a script tag or loaded js file:
zk.afterLoad('zk', function() {
var oldMethod = zAu.cmd0.download;
zAu.cmd0.download = function(filename) {
return oldMethod(filename.replace(new RegExp('#', 'g'), '%23'));
}
});
Then it will download the file with the complete name. You might want to take the extra time and sanitize the other reserved characters as well. Read this wiki article about "percent encoding" for the right codes.
I have also filed a support ticket with zk, I think this should be handled by the client side method out-of-the-box.

Authentication That Doesn't Require Javascript?

I have a Web API app, initialized thusly:
app.UseCookieAuthentication();
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
For calls to most controllers, it works great. However, it also requires a bit of javascript before client-side service calls are made:
function getSecurityHeaders() {
var accessToken = sessionStorage["accessToken"] || localStorage["accessToken"];
if (accessToken) {
return { "Authorization": "Bearer " + accessToken };
}
return {};
}
The problem is that we have a certain type of controller (one that accesses files) where no javascript can be run during the call. For example, the call might be to:
http://mysite/mycontroller/file/filename.jpg
...where the value is assigned as the src attribute of an img tag. The call works, but Thread.CurrentPrincipal.Identity is unauthenticated with a null name, so there's currently not a way to enforce security.
I'm new to Web API, so it may be a dumb question, but what's the way around this? What switches do I need to flip to not require javascript to add security headers? I was considering trying to find a way to force an authorization header in an IAuthorizationFilter or something, but I'm not even sure that would work.
So I figured out the solution to my problem.
First, I needed to configure the app to use an authentication type of external cookies thusly:
//the line below is the one I needed to change
app.UseCookieAuthentication(AuthenticationType = DefaultAuthenticationTypes.ExternalCookie);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
Second, it turned out there was a line of code in my WebApiConfig file that was disabling reading the external cookie:
//this line needed to be removed
//config.SuppressDefaultHostAuthentication();
After that, I could see the external cookie from Google, which passed along an email address I could identify the user with.

working with cookies in dispatch-classic

I need to programmatically log into a backend server, which returns an auth token as a cookie, then use that cookie to make requests going forward. I'm working in Lift 2.4, and everything I read seems to recommend using http-dispatch, but that has been cumbersome to learn! :-/ I'm working in dispatch-classic because of my SBT version (0.1-SNAPSHOT) and scala version 2.9.1. So I'm currently loading the dispatch 0.8.6 libraries.
I found the following at
https://groups.google.com/forum/#!msg/dispatch-scala/m7oWv2YAtjQ/imnkYoCDVUcJ
For retrieving cookies:
To read a cookie from a response, you have to call the Response#getCookies method. For example, you could do something like this:
val res = Http(url("http://www.google.com/ig/api").addQueryParameter("weather", "Bonn, Germany"))
val response = for { r <- res } yield (r.getCookies, r.getResponseBody)
for adding cookies to subsequent requests:
url("http://www.google.com/ig/api").addCookie(cookie)
But I can't get this to work.
My preference is code that works with dispatch 0.8.6, but if you can make it work in another version and don't see what that version won't work with my SBT and scala/Lift versions, I'll try using your recommended library version.
To get a the cookie, you should be able to do something like this:
Http(url("http://www.google.com/ig/api") <<? List("weather" -> "Bonn, Germany") >:> ((h) => h.get("Set-Cookie")))
That will request the URL, append the weather param, and then pass the response headers to a handler function which looks for the Set-Cookie header and returns an Option with the value, or None if it was not present.
To set a cookie, you can do:
Http(url("http://www.google.com/ig/api") <<? List("weather" -> "Bonn, Germany") <:< Map("Set-Cookie" -> "something") >| )
This will add the headers in the Map following the <:< directive, which in the case above includes the cookie. The >| handler simply ignores the response, but you can use any handler you want.
This guide is a pretty good reference for the different functions and handlers available.
I checked this out with 0.8.8, as I don't have the earlier version, and everything seemed to work for me. I can't sure for sure, but I think it should be the same with 0.8.6.

Ember js RESTAdapter PUT request adds .json to the end

I've been trying to learn Ember and I have a question.
In my store I'am getting data from .json like below. I have tried without buildUrl function but cant load the json file, then found this solution on SO.
CocktailApp.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.extend({
bulkCommit: false,
url: "http://localhost:8888",
buildURL: function(record, suffix) {
var s = this._super(record, suffix);
return s + ".json";
}
})
});
Now comes my question: When I commit the chances (by pressing add to favs or remove from favs) RESTAdapter adds ".json" at the end of to PUT request. See the below code and screenshot
CocktailApp.CocktailController = Ember.ObjectController.extend({
addToFav: function () {
this.set('fav',true);
this.get('store').commit();
},
removeFromFav: function () {
this.set('fav',false);
this.get('store').commit();
}
});
I think thats why my PUT request can not be handled. But If I remove the builtURL function no json loaded at all. How can I resolve this problem?
Thanks
If the API endpoint url does not require .json at the end of it, then remove that line from your buildURL function. My guess is that the example code you got was consuming a ruby on rails api, or something similar.
remember, when you send a GET, PUT, POST, or DELETE to a url, that url needs to actually be a real endpoint. You can't just add extraneous stuff to it and have it still work.

Soundmanager2 won't load sound from google translate

I want to speak some text; I can get the audio-file(mp3) from google translate tts if I enter a properly formatted url in the browser.
But if I try to createSound it, I only see a 404-error in firebug.
I use this, but it fails:
soundManager.createSound(
{id:'testsound',
autoLoad:true,
url:'http://translate.google.com/translate_tts?ie=UTF-8&tl=da&q=testing'}
);
I have pre-fetched the fixed voiceprompts with wget, so they are as local mp3-files on the same webserver as the page. But I would like to say a dynamic prompt.
I see this was asked long time ago, but I have come to a similar issue, and I was able to make it work for Chrome and Firefox, but with the Audio Tag.
Here is the demo page I have made
http://jsfiddle.net/royriojas/SE6ET/
here is the code that made the trick for me...
var sayIt;
function createSayIt() {
// Tiny trick to make the request to google actually work!, they deny the request if it comes from a page but somehow it works when the function is inside this iframe!
//create an iframe without setting the src attribute
var iframe = document.createElement('iframe');
// don't know if the attribute is required, but it was on the codepen page where this code worked, so I just put this here. Might be not needed.
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-pointer-lock');
// hide the iframe... cause you know, it is ugly letting iframes be visible around...
iframe.setAttribute('class', 'hidden-iframe')
// append it to the body
document.body.appendChild(iframe);
// obtain a reference to the contentWindow
var v = iframe.contentWindow;
// parse the sayIt function in this contentWindow scope
// Yeah, I know eval is evil, but its evilness fixed this issue...
v.eval("function sayIt(query, language, cb) { var audio = new Audio(); audio.src = 'http://translate.google.com/translate_tts?ie=utf-8&tl=' + language + '&q=' + encodeURIComponent(query); cb && audio.addEventListener('ended', cb); audio.play();}");
// export it under sayIt variable
sayIt = v.sayIt;
}
I guess that I was able to byPass that restriction. They could potentially fix this hack in the future I don't know. I actually hope they don't...
You can also try to use the Text2Speech HTML5 api, but it is still very young...
IE 11 is not working with this hack, some time in the future I might try to fix it
Even though you see this as a 404 error, you're actually running into a cross-domain restriction.
Some of the response headers from that 404 will also give you a clue of what's going on:
X-Content-Type-Options:nosniff
X-XSS-Protection:1; mode=block
So, you won't be able to do this client-side, as Google does not (and probably will never) allow you to do so.
In order to do this dynamic loading of audio, you need to work around this x-domain restriction by setting up a proxy on your own server, which would download whatever file requested by the end-user from Google's servers (via wget or whatever) and spitting whatever data comes from google.
Code I used to reproduce the issue:
soundManager.setup({
url: 'swf',
onready: function() {
soundManager.createSound({
id:'testsound',
autoLoad:true,
url:'http://translate.google.com/translate_tts?ie=UTF-8&tl=da&q=testing'
});
}
});
Your code should look like this:
soundManager.createSound({
id:'testsound',
autoLoad:true,
url:'/audioproxy.php?ie=UTF-8&tl=da&q=testing' // Same domain!
});
Regards and good luck!