Get httprequests in html5 phonegap app (cross domain?) - html

I am building a semi dynamic app in html5 w/jquerymobile, target atm. is iOS to begin with.
But the problem is that i need to contact a webservice with a (request)header paramater, and this is not doable.
My question might be very elementary, but since i'm developing in netbeans i ran into the problem of not being able to get data from crossdomain.
(Getting to the point). In phonegap/{non native "offline" apps}. How do i set the domain/is there a workaround for making http get/post requests with headerparams?

If you are using Phonegap the web application will run off of the file:// protocol and is not bound by cross domain restrictions.
EDIT:
If you are using jQuery Mobile and Phonegap you will need to set $.support.cors and $.mobile.allowCrossDomainPages to true.
From the jQueryMobile docs:
Since jQuery Mobile relies on jQuery core's $.ajax() functionality, $.support.cors must be set to true to tell $.ajax to load cross-domain pages. We've heard reports that webviews on some platforms, like BlackBerry, support cross-domain loading, but that jQuery core incorrectly sets $.support.cors value to false which disables cross-domain $.ajax() requests and will cause the page or assets to fail to load.
$(document).bind( "mobileinit", function(){
$.support.cors = true;
$.mobile.allowCrossDomainPages = true;
});

Related

Chromium + Raw Html (directly from a C# string) + Angular => Problems when attempting to use routing

CefSharp: 1.25.0 (based on Chromium 25.0.1364.152)
Angular: 1.3.0-beta16
UIRouter: 0.2.10
I'm developing a stand-alone C# application that uses CefSharp Chromium + Angular + UIRouter as the stack upon which the GUI will be relying on.
I hit it off by trying to make the above stack load the sample-code provided here:
http://scotch.io/tutorials/javascript/angular-routing-using-ui-router
For the sake of elegance the HTML + Javascript-libs of the GUI, get cobundled in a single resource file inside the .Net executable of the application.
This resource is then passed programmatically during application-init to the Chromium control (by means of .LoadHtml) to be loaded directly into the browser, aka the HTML is not loaded from a separate .html file residing in the hard-drive or on a remote HTTP server. If the HTML gets loaded from the later ("standard") venues then everything works flawlessly.
I noticed that when loading the HTML directly as a string, as described above, the url of the resulting static web page (aka window.location) is set to 'about:blank'. It appears that angular has some sort of pet peeve with such a url, especially when it comes to using routing:
First of all, the invocation of:
history.pushState(null, "", url);
inside
self.url = function(url, replace) { ... }
throws an exception ala
Error: SecurityError: DOM Exception 18
Error: An attempt was made to break through the security policy of the user agent.
at Browser.self.url (about:blank:8004:21)
at about:blank:10049:24
at Scope.$eval (about:blank:11472:28)
at Scope.$digest (about:blank:11381:31)
at Scope.$apply (about:blank:11493:24)
at about:blank:6818:15
at Object.invoke (about:blank:7814:19)
at doBootstrap (about:blank:6817:16)
at bootstrap (about:blank:6827:14)
at angularInit (about:blank:6796:7)
the url that is passed to .pushState is:
about:blank#/home
which appears to be the result of concatenating 'about:blank' with the default state '/home'.
Secondly, even if the above problem is solved there appears to be a major issue inside:
$rootScope.$watch(function $locationWatch() { ... })
which causes the following error:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
the reason is that when 'window.location' is set to 'about:blank' then
$browser.url()
always returns
about:blank
while
$location.absUrl()
returns
about:blank#/home
causing $watch to fire non-stop.
Is there any proper way to handle this shortcoming of angular when its dealing with web pages loaded directly into the browser in the manner described here?
If there is no workaround for this issue then I'm afraid that I will have to resort to loading the HTML directly from a file in the hard drive, which apart from being slower (can't cache the string to memory for subsequent usages), it's also a noticable deviation from the goal of developing a stand-alone-exe. :(
Thanks in advance and I apologize if this issue has been addressed elsewhere.
By default Firefox allows loading of external files within html file that loaded from "file:///...". but Chrome does not. in CefSharp(Chrome) you can do it in this way:
// Allow angular routing and load external files
BrowserSettings setting = new BrowserSettings();
setting.FileAccessFromFileUrls = CefState.Enabled;
browser.BrowserSettings = setting;
this.Controls.Add(browser);
Most browsers don't allow to do AJAX on the file-system. But Chromium can be tweaked to do so:
browser = new ChromiumWebBrowser(path);
browser.BrowserSettings = new BrowserSettings();
browser.BrowserSettings.FileAccessFromFileUrlsAllowed = true;

How to build PhoneGap + StackMob apps without Backbone.js?

I want to build a PhoneGap HTML5 app with a StackMob backend. There seems to be a shortage of books, videos, and tutorials on the topic.
Specifically, how can I build a Phonegap + StackMob app without using Require.js and Backbone.js?
I think stackmob developer website: https://developer.stackmob.com/ is the best resource.
Using phoneGap with StackMob is independent of using Backbone.js and Require.js. The StackMob SDK is built using Backbone.js for managing models and collections.
So, if you want to build an app without Backbone.js, you can make bare AJAX calls to StackMob. Here is a JSFiddle showing how.
http://jsfiddle.net/ericktai/mr925/
/*
We want to prepare the Request headers we're going to send to StackMob. It should look like:
{
'Accept': application/vnd.stackmob+json; version=0',
'X-StackMob-API-Key-dc0e228a-ccd3-4799-acd5-819f6c074ace': 1,
'Range': 'objects=0-9' //this is optional, but I did it here to show pagination and extra header fields
}
You can actually have the public key in the header as:
'X-StackMob-API-Key': dc0e228a-ccd3-4799-acd5-819f6c074ace
OR
'X-StackMob-API-Key-dc0e228a-ccd3-4799-acd5-819f6c074ace': 1
The first is our original format. The reason why I chose the latter is because of this specific example. I'm making cross domain requests jsfiddle.net to api.stackmob.com, which the browser doesn't allow UNLESS we use CORS (cross origin resource sharing). StackMob servers support CORS, but it needs the latter header format to do so. (it was introduced later). iOS and Android SDKs use the first format.
Node.js should be able to use the first format because it doesn't restrict cross domain calls.
The "1" value in the latter format is arbitrary. IE just doesn't allow the value of a header to be empty, so we filled it with "1".
*/
var publicKeyHeader = 'X-StackMob-API-Key-dc0e228a-ccd3-4799-acd5-819f6c074ace';
var requestHeaders = {};
requestHeaders['Accept'] = 'application/vnd.stackmob+json; version=0';
requestHeaders[publicKeyHeader] = 1;
requestHeaders['Range'] = 'objects=0-9'; //set pagination to first 10
$.ajax({
url: 'https://api.stackmob.com/item',
headers: requestHeaders, //set the headers
type: 'GET',
success: function(data, textStatus, xhr) {
console.debug(data);
},
error: function(xhr, textStatus, error) {
console.debug(error);
}
});
Regarding phoneGap, you'll want to look at the following docs.
https://developer.stackmob.com/js-sdk/using-the-js-sdk-with-phonegap-guide
I've used Adobe phoneGap build successfully.
Btw- I am the Platform Evangelist at StackMob

localStorage on wp8 error

I have a WP8 HTML/JS app and I need to save some simple data on the local storage. It should be something very easy, but it is giving my a headache already.
I tried to call the localStorage in many different ways but it doesn't work. The error message I get is:
The system cannot find the file specified.
The strange part, is that the sessionStorage seems to be fine. At least I don't get any error using that object.
Additional info:
- The ways I called localStorage are: localStorage.setItem(), window['localStorage'], window.localStorage, etc. they all say the same message.
- I am developing a Windows Phone HTML app OS8.
- The method I call the localStorage is in $('#channels').bind('pagebeforeshow', function (e, data) {...}
- The only references in the project are .Net for Windows Phone and Windows Phone.
- Some of the js libs I included are jQuery, jQuery mobile and ko.
- I am testing on both WP8 device and Emulator
I prefer not to use phoneGap and any other known db for devices, since I wouldn't like to involve interaction with the native code just to make the call to fetch and save some data.
UPDATE 1:
After thefrontender comment, I investigated one by one my js refs. The problem appear when I add the jqm 1.3 min.
All js are bundled with my app. Any other suggestions?
$(function () {
try {
localStorage.setItem('aaa', 123);
alert(localStorage.aaa);
}
catch (err) { alert(err.message) }
});
And if you replace
alert(localStorage.aaa);
with
alert(localStorage.getItem('aaa');
I got my answer form jQuery official forum after all.
You need to add modernizr with atleast localStorage from HTML5 section.
Add: (first or last doent matter)
as indicated in the following post too:
http://www.pksoftlab.com/?p=1073

Using local file for Web Audio API in Javascript

I'm trying to get sound working on my iPhone game using the Web Audio API. The problem is that this app is entirely client side. I want to store my mp3s in a local folder (and without being user input driven) so I can't use XMLHttpRequest to read the data. I was looking into using FileSystem but Safari doesn't support it.
Is there any alternative?
Edit: Thanks for the below responses. Unfortunately the Audio API is horribly slow for games. I had this working and the latency just makes the user experience unacceptable. To clarify, what I need is sounething like -
var request = new XMLHttpRequest();
request.open('GET', 'file:///./../sounds/beep-1.mp3', true);
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
dogBarkingBuffer = buffer;
}, onError);
}
request.send();
But this gives me the errors -
XMLHttpRequest cannot load file:///sounds/beep-1.mp3. Cross origin requests are only supported for HTTP.
Uncaught Error: NETWORK_ERR: XMLHttpRequest Exception 101
I understand the security risks with reading local files but surely within your own domain should be ok?
I had the same problem and I found this very simple solution.
audio_file.onchange = function(){
var files = this.files;
var file = URL.createObjectURL(files[0]);
audio_player.src = file;
audio_player.play();
};
<input id="audio_file" type="file" accept="audio/*" />
<audio id="audio_player" />
You can test here:
http://jsfiddle.net/Tv8Cm/
Ok, it's taken me two days of prototyping different solutions and I've finally figured out how I can do this without storing my resources on a server. There's a few blogs that detail this but I couldn't find the full solution in one place so I'm adding it here. This may be considered a bit hacky by seasoned programmers but it's the only way I can see this working, so if anyone has a more elegent solution I'd love to hear it.
The solution was to store my sound files as a Base64 encoded string. The sound files are relatively small (less than 30kb) so I'm hoping performance won't be too much of an issue. Note that I put 'xxx' in front of some of the hyperlinks as my n00b status means I can't post more than two links.
Step 1: create Base 64 sound font
First I need to convert my mp3 to a Base64 encoded string and store it as JSON. I found a website that does this conversion for me here - xxxhttp://www.mobilefish.com/services/base64/base64.php
You may need to remove return characters using a text editor but for anyone that needs an example I found some piano tones here - xxxhttps://raw.github.com/mudcube/MIDI.js/master/soundfont/acoustic_grand_piano-mp3.js
Note that in order to work with my example you're need to remove the header part data:audio/mpeg;base64,
Step 2: decode sound font to ArrayBuffer
You could implement this yourself but I found an API that does this perfectly (why re-invent the wheel, right?) - https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js
Resource taken from - here
Step 3: Adding the rest of the code
Fairly straightforward
var cNote = acoustic_grand_piano.C2;
var byteArray = Base64Binary.decodeArrayBuffer(cNote);
var context = new webkitAudioContext();
context.decodeAudioData(byteArray, function(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer;
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.noteOn(0);
}, function(err) { console.log("err(decodeAudioData): "+err); });
And that's it! I have this working through my desktop version of Chrome and also running on mobile Safari (iOS 6 only of course as Web Audio is not supported in older versions). It takes a couple of seconds to load on mobile Safari (Vs less than 1 second on desktop Chrome) but this might be due to the fact that it spends time downloading the sound fonts. It might also be the fact that iOS prevents any sound playing until a user interaction event has occured. I need to do more work looking at how it performs.
Hope this saves someone else the grief I went through.
Because ios apps are sandboxed, the web view (basically safari wrapped in phonegap) allows you to store your mp3 file locally. I.e, there is no "cross domain" security issue.
This is as of ios6 as previous ios versions didn't support web audio api
Use HTML5 Audio tag for playing audio file in browser.
Ajax request works with http protocol so when you try to get audio file using file://, browser mark this request as cross domain request. Set following code in request header -
header('Access-Control-Allow-Origin: *');

HTML5+jQuery+phonegap mobile app security

I'm new to this area and I'm developing a HTML5 mobile app that calls a restful webservices api and exchange JSON objects.
I want to authenticate the client once and give a a key/token that can be used afterwards until a pre-defined expiration date. I have 4 questions:
How can I secure the serverside webservices api? any tools whatsoever?
Can I use the local storage to store the key/token?
What are the phonegap security tools I can use for the client side?
How can I use OAUTH in this case?
How can I secure the serverside webservices api? any tools whatsoever?
OAuth may be overkill for your need, verify that you really need to use such a powerful (and complex) standard.
Two examples of PHP server side software that you may use:
Solberg-OAuth
SimpleSAMLphp
Can I use the local storage to store the key/token?
Yes! Be aware that you MUST use the OAuth 2.0 implicit grant flow in order to obtain the token at the client side.
What are the phonegap security tools I can use for the client side?
ChildBrowser is a plugin to open a separate browserwindow for the authentication process.
I've written a javascript library JSO that can do OAuth 2.0 for you. Other libraries exists as well.
https://github.com/andreassolberg/jso
How can I use OAUTH in this case?
Using JSO with Phonegap and ChildBrowser
Using JSO to perform OAuth 2.0 authorization in WebApps running on mobile devices in hybrid environment is an important deployment scenario for JSO.
Here is a detailed instruction on setting up JSO with Phonegap for iOS and configure OAuth 2.0 with Google. You may use it with Facebook or other OAuth providers as well.
Preparations
Install XCode from App Store, and iOS development kit
Install Phonegap 2.0, Cordova 2.0
Setup App
To create a new App
./create /Users/andreas/Sites/cordovatest no.erlang.test "CordovaJSOTest"
Install ChildBrowser
The original ChildBrowser plugin is available here.
https://github.com/purplecabbage/phonegap-plugins/tree/master/iPhone/ChildBrowser
However, it is not compatible with Cordova 2.0. Instead, you may use this fork of ChildBrowser which should be working with Cordova 2.0:
https://github.com/Shereef/ChildBrowserOnCordova200
What you need to do is to copy these files:
https://github.com/Shereef/ChildBrowserOnCordova200/tree/master/ChildBrowserOnCordova200/Plugins
in to your WebApp project area, by using drag and drop into the Plugins folder in XCode.
Now you need to edit the file found in Resources/Cordova.plist found in your WebApp project area.
In this file you need to add one array entry with '*' into ExternalHosts, and two entries into Plugins:
ChildBrowser -> ChildBrowser.js
ChildBrowserCommand -> ChildBrowserCommand
as seen on the screenshot.
(source: erlang.no)
Setting up your WebApp with ChildBrowser
I'd suggest to test and verify that you get ChildBrowser working before moving on to the OAuth stuff.
In your index.html file try this, and verify using the Simulator.
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0.js"></script>
<script type="text/javascript" charset="utf-8" src="ChildBrowser.js"></script>
<script type="text/javascript">
var deviceready = function() {
if(window.plugins.childBrowser == null) {
ChildBrowser.install();
}
window.plugins.childBrowser.showWebPage("http://google.com");
};
document.addEventListener('deviceready', this.deviceready, false);
</script>
Setting up JSO
Download the latest version of JSO:
https://github.com/andreassolberg/jso
The documentation on JSO is available there as well.
The callback URL needs to point somewhere, and one approach would be to put a callback HTML page somewhere, it does not really matter where, although a host you trust. And put a pretty blank page there:
<!doctype html>
<html>
<head>
<title>OAuth Callback endpoint</title>
<meta charset="utf-8" />
</head>
<body>
Processing OAuth response...
</body>
</html>
Now, setup your application index page. Here is a working example:
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0.js"></script>
<script type="text/javascript" charset="utf-8" src="ChildBrowser.js"></script>
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
<script type="text/javascript" charset="utf-8" src="jso/jso.js"></script>
<script type="text/javascript">
var deviceready = function() {
var debug = true;
/*
* Setup and install the ChildBrowser plugin to Phongap/Cordova.
*/
if(window.plugins.childBrowser == null) {
ChildBrowser.install();
}
// Use ChildBrowser instead of redirecting the main page.
jso_registerRedirectHandler(window.plugins.childBrowser.showWebPage);
/*
* Register a handler on the childbrowser that detects redirects and
* lets JSO to detect incomming OAuth responses and deal with the content.
*/
window.plugins.childBrowser.onLocationChange = function(url){
url = decodeURIComponent(url);
console.log("Checking location: " + url);
jso_checkfortoken('facebook', url, function() {
console.log("Closing child browser, because a valid response was detected.");
window.plugins.childBrowser.close();
});
};
/*
* Configure the OAuth providers to use.
*/
jso_configure({
"facebook": {
client_id: "myclientid",
redirect_uri: "https://myhost.org/callback.html",
authorization: "https://www.facebook.com/dialog/oauth",
presenttoken: "qs"
}
}, {"debug": debug});
// For debugging purposes you can wipe existing cached tokens...
// jso_wipe();
// jso_dump displays a list of cached tokens using console.log if debugging is enabled.
jso_dump();
// Perform the protected OAuth calls.
$.oajax({
url: "https://graph.facebook.com/me/home",
jso_provider: "facebook",
jso_scopes: ["read_stream"],
jso_allowia: true,
dataType: 'json',
success: function(data) {
console.log("Response (facebook):");
console.log(data);
}
});
};
document.addEventListener('deviceready', this.deviceready, false);
</script>
How can I secure the serverside webservices api? any tools whatsoever?
Depends on which language the web service is written, php has zend framework for creating web services / nusoap etc. So all of the languages do provide info on how to secure the webservice.
Can I use the local storage to store the key/token?
Yes you can use local storage look at the phonegap documentation
What are the phonegap security tools I can use for the client side?
I dont think so there are any but you can search for some plugins or create your own plugin. Depends on what kind of security do you want to implement.
How can I use OAUTH in this case?
Here is a library for OAuth and this seems to be helpful. You can create a phone gap plugin to interact with the library or use a javascript oauth library(its with sample also).