Windows Phone 7 sports application with Apache Cordova, jQuery Mobile Metro and ESPN API

· 4922 words · 24 minutes to read

One of the finest news recently has been the announcement of jQuery Mobile Metro theme for WP7. The nice folks at Windows Phone Interoperability Bridges and Sergei Grebnov have released that (also available as a Nuget package) last week.

I immediately thought that this is one of the things that should be definitely checked out - after all jQuery Mobile is a wonderful technology for building robust mobile applications - and jQuery Mobile Metro seems to have so much potential! Coupled with Apache Cordova (formerly PhoneGap), it allows us to quickly and easily brew a mobile application that can be deployed to any platform. What happens with Cordova, is that you build your application with HTML5, CSS and Javascript - and then it’s packaged into an environment specific package and run as if it was a native application (using an embedded browser control). Things couldn’t be any easier.

So today, the plan is to build a sports application for Windows Phone 7 - using all the goodies: Apache Cordova, jQuery Mobile Metro and our good friend, ESPN API.

Getting started with Apache Cordova 🔗

I assume you have WP7 SDK already (otherwise I don’t think you’d even be interested in this article, would you?), but if you don’t you’d obviously need to install it - you can find it here.

To start developing on top of Apache Cordova, you need to get the latest VS project template off their GIT first. Go for the CordovaStarter-1.7.0.zip. Copy that file over to “Program FilesMicrosoft Visual Studio 10.0Common7IDEProjectTemplatesCSharpSilverlight for Windows Phone” folder (of any other place where you have VS2010 installed, as long as you drop it into the ProjectTemplates.

When you now launch Visual Studio, you should get an option to create an Apache Cordova WP7 project. If you still can’t locate the project template, use the search box, to search the installed templates, it should definitely come up if you dropped it in the right place.

Try building the solution. Hopefully it runs fine. If you encounter the following error: Error. The command “CScript “c:usersdocumentsvisual studio 2010ProjectsCordovaStarter3CordovaStarter3/BuildManifestProcessor.js” “c:usersdocumentsvisual studio 2010ProjectsCordovaStarter3CordovaStarter3CordovaStarter3.csproj”” exited with code 9009, that means that you don’t have a CScript environmental variable. Either add it:

  • CScript - C:windowssystem32cscript.exe
    or edit your project file and change this:
<PreBuildEvent>CScript "$(ProjectDir)/BuildManifestProcessor.js" "$(ProjectPath)"</PreBuildEvent>  

to

<PreBuildEvent>C:windowssystem32cscript.exe "$(ProjectDir)/BuildManifestProcessor.js" "$(ProjectPath)"</PreBuildEvent>  

Not quite a WP7 Metro app yet, but at least it’s a step in a right direction.

Getting jQuery Mobile Metro 🔗

Now that you have Apache Cordova template up and running, it’s time to include jQuery Mobile Metro. You’d to that by using NuGet. Go to “Manage NuGet packages” and search for “jQMMetroCordovaTheme”.

Install the package (notice that it will also install SilverlightToolkitWP, as a prerequisite). Once that’s done, inspect your solution. In addition to a couple of new DLL references, the most important stuff happened under the www folder.

A whole lot of files appeared - few sample pages, a number of JS files, CSS files - all that is our jQuery Mobile Metro base. Now, if you run the project again, you’d see nothing changed though. That’s casue you’d still need to apply Metro styling to your page. There is a good guide on what and how here - but in principle, all we need to do, is copy the contents of metroStartPage.html to your index.html.

Additionally, you’d need to include the Cordova js library, so just drop that line under all the jQuery Metro CSS.

Now if you run the project, you’d see that all Metro styling is out there, and you have access to all features of the jQuery Mobile Metro demo. That’s a great place to start our real app.

Our sports application 🔗

So our sports app which we’ll build is probably not going to change the world as we know, but hopefully will highglight a few things you can accomplish very easily with jQuery Mobile Metro and Apache Cordova.

The things we’ll have:

  • a custom logo/splash screen for the App
  • a list of latest 3 ESPN news showing on home screen
  • a list of ESPN sports categories available - tapping on each will load news from that category
  • each category will then load 10 latest news, but if there is more, user can continue by tapping a button - which would load more
  • a separate settings screen will be a part of the application; in there user can select a default category for the 3 homescreen news items
  • additionally, on the settings screens, there will be an option to toggle geolocation on/off (just to show off the capabilities of cordova). If it’s switched on, the home screen will get your geolocation, find the closest MLB team, and display news for that team (it’s gonna be MLB only, since it’s too tedious to implement it for all teams in all leagues for the sole purpose of writing a blog post - but hopefully will illustrate the techniques correctly)
  • we will also have a typical WP7 bottom application bar with links

Few screenshots so that we know what we are aiming for:

Adding new files to the project 🔗

Before we begin coding we need to add a few files to our project. They will be placeholders for the code that we will write later, but it’s good to have everything structured and ready.

So we need to add the following files:

  • /www/css/appstyle.css - our custom style, to complement jQuery Mobile Metro
  • /www/js/pages/app.js - JS for the generic stuff
  • /www/js/pages/homePage.js - JS for the home page
  • /www/js/pages/settingsPage.js - JS for the settings page
  • /www/js/pages/headlinesPage.js - JS for the news list page
  • /www/settings.html - the settings page
  • /www/espn.html - the news page

Important – make sure that all new files you add to the solution have the build action set to “content". We also include the jquery.tmpl.js, jQuery template plugin since it’s so useful 🙂 You need to load all the JS and CSS files in the index.html, so do that immediately - the way jQuery Mobile works, everything should be included either on homepage.

We can delete all other HTML files created by jQuery Mobile Metro. From the index.html, since we already verified that Metro is working fine, we can remove everything in between

<div data-role="page" class="type-interior" data-theme="a">
  // GONE!!!!
</div>

By now your solution should be ready for coding.

Generic data to be used 🔗

But before we start with any real code, we need to set up some data objects which we’ll use. Those should be part of app.js.

var App = App || {};  
App.sportid = 0;  
App.offset = 0;

App.headline_list = {  
sport: [{ id: 0, name: "All ESPN stories", url: "/sports" },  
{ id: 1, name: "MLB", url: "/sports/baseball/mlb" },  
{ id: 2, name: "NCAA Men's BB", url: "/sports/basketball/mens-college-basketball" },  
{ id: 3, name: "NBA", url: "/sports/basketball/nba" },  
{ id: 4, name: "WNBA", url: "/sports/basketball/wnba" },  
{ id: 5, name: "NCAA Women's BB", url: "/sports/basketball/womens-college-basketball" },  
{ id: 6, name: "Boxing", url: "/sports/boxing" },  
{ id: 7, name: "NCAA FB", url: "/sports/football/college-football" },  
{ id: 8, name: "NFL", url: "/sports/football/nfl" },  
{ id: 9, name: "Golf", url: "/sports/golf" },  
{ id: 10, name: "NHL", url: "/sports/hockey/nhl" },  
{ id: 11, name: "Horse Racing", url: "/sports/horse-racing" },  
{ id: 12, name: "Auto Racing", url: "/sports/racing" },  
{ id: 13, name: "Soccer", url: "/sports/soccer" },  
{ id: 14, name: "NASCAR", url: "/sports/racing/nascar" },  
{ id: 15, name: "Tennis", url: "/sports/tennis" }  
]  
};

App.teams = [{ id: 0, teams: [  
{ coords: [33.445278, -112.066667], name: "Arizona Diamondbacks", espnid: 29 },  
{ coords: [33.735206, -84.389642], name: "Atlanta Braves", espnid: 15 },  
{ coords: [39.283642, -76.621803], name: "Baltimore Orioles", espnid: 1 },  
{ coords: [42.34635, -71.097611], name: "Boston Red Sox", espnid: 2 },  
{ coords: [41.948036, -87.655681], name: "Chicago Cubs", espnid: 16 },  
{ coords: [41.830081, -87.634047], name: "Chicago White Sox", espnid: 4 },  
{ coords: [39.097392, -84.506858], name: "Cincinnati Reds", espnid: 17 },  
{ coords: [41.495703, -81.685283], name: "Cleveland Indians", espnid: 5 },  
{ coords: [39.755872, -104.994172], name: "Colorado Rockies", espnid: 27 },  
{ coords: [42.339281, -83.048867], name: "Detroit Tigers", espnid: 6 },  
{ coords: [25.957919, -80.238842], name: "Miami Marlins", espnid: 28 },  
{ coords: [29.756844, -95.355417], name: "Houston Astros", espnid: 18 },  
{ coords: [39.051358, -94.480703], name: "Kansas City Royals", espnid: 16 },  
{ coords: [33.800031, -117.883014], name: "Los Angeles Angels", espnid: 3 },  
{ coords: [34.073561, -118.240122], name: "Los Angeles Dodgers", espnid: 19 },  
{ coords: [43.02835, -87.971125], name: "Milwaukee Brewers", espnid: 8 },  
{ coords: [44.981667, -93.278333], name: "Minnesota Twins", espnid: 9 },  
{ coords: [40.756867, -73.845530], name: "New York Mets", espnid: 21 },  
{ coords: [40.82916, -73.926389], name: "New York Yankees", espnid: 10 },  
{ coords: [37.751411, -122.200889], name: "Oakland Athletics", espnid: 11 },  
{ coords: [39.905733, -75.166525], name: "Philadelphia Phillies", espnid: 22 },  
{ coords: [40.446986, -80.005994], name: "Pittsburgh Pirates", espnid: 23 },  
{ coords: [32.707467, -117.156989], name: "San Diego Padres", espnid: 25 },  
{ coords: [37.778372, -122.389689], name: "San Francisco Giants", espnid: 26 },  
{ coords: [47.591392, -122.331861], name: "Seattle Mariners", espnid: 12 },  
{ coords: [38.6225, -90.193056], name: "St. Louis Cardinals", espnid: 24 },  
{ coords: [27.768317, -82.653381], name: "Tampa Bay Rays", espnid: 30 },  
{ coords: [32.751461, -97.082847], name: "Texas Rangers", espnid: 13 },  
{ coords: [43.6413, -79.389214], name: "Toronto Blue Jays", espnid: 14 },  
{ coords: [38.872778, -77.0075], name: "Washington Nationals", espnid: 20}]  
}];  

If you followed my recent tutorial on knockout.js and ESPN API, you might recognize the first object - that’s just a list of all ESPN API headlines urls, by sport category.

We’ll use that as our category master list to display on the home screen and in the settings (to allow users to select favorite sport). It needs to be hardcoded like this, because ESPN API currently doesn’t expose any method to retrieve a list of all available sports categories. Update: Roger from ESPN API contacted me and said that you can actually request this data by calling api.espn.com/v1/sports/?apikey=yourkey. This is very handy, so it should definitely be used instead in production application. However, there is no point in refactoring the tutorial code here with that as it doesn’t change anything in terms of learning how to build this sample WP7 application.End Update.

Second object is a list of MLB teams with their ESPN ID (to pull team specific news) and geographical coordinates. We’ll use them to find out the closest team and display its news using the phone’s geolocation capabilities.

Additionally we add two JS extension methods which I’ll use in the application:

if (typeof (Array.prototype.min) === "undefined") {  
Array.prototype.min = function () {  
var min = this[0];  
var len = this.length;  
for (var i = 1; i < len; i++) if (this\[i\]\[1\] < min[1]) min = this[i]; return min; } } if (typeof (Number.prototype.toRad) === "undefined") { Number.prototype.toRad = function () { return this * Math.PI / 180; } }
``` More on them later. We also add one more method to the _App_ namespace - this one is going to be key for our application - the method to retrieve ESPN data. Since we'll call the method from different pages (home, news list), it only makes sense to put the method into a common place.  

```javascript
App.queryESPNAPI = function(targeturl, limit, offset, target, template, hd) {  
var api_call = 'http://api.espn.com/v1' + targeturl + '/news';  
if (hd)  
api_call += "/headlines";  
App.offset += limit;  
$.ajax({  
url: api_call,  
dataType: 'jsonp',  
data: {  
apikey: "YOUR API KEY GOES HERE!!!!",  
_accept: "application/json",  
limit: limit,  
offset: offset  
},  
success: function (result) {  
if (result.headlines != null) {  
$("#" + template).tmpl(result.headlines).appendTo("#" + target);  
}  
}  
});  
}  

The method takes a partial URL of the stories to query, limit of items to show, offset of items to show (to allow loading news items in batches), ID of the DOM object in which results should appear, ID of the jQuery template to use to render results and a flag whether the API call should be to “headlines” (most calls) or not (for team-specific news only).

To use that, remember to generate an ESPN API key here.

Home page HTML coding 🔗

Our home page, as seen on the screenshots before, is going to consist of five main elements:

  • header - application title
  • latest news (All news or category news, if set in the settings by user)
  • latest team news for local team (if geolocation enabled)
  • all categories links
  • application bar

Let’s go through all of them. Note, all HTML we write now, goes inside of the data-role=“page” div of the index.html.
1. header

<div data-role="header" data-theme="a">
  <span class="ui-app-title">By strathweb.com via <a href="http://espn.go.com">ESPN API</a></span></p> 
  
  <h1>
    Sports News
  </h1></p>
</div>

OK, that’s rather self explanatory. It’s super nice that all we have to do is write HTML and the Metro styling is applied auto-magically.

2. Latest news

<ul id="latestList" name="list-view-1" data-role="listview" data-inset="true">
  <li data-role="list-divider">
    Latest<br /> <span id="latestNewsHeader"></span> news
  </li>
</ul>

The latest news template is a duo of the ul item which will act as the list placeholder (already includes one li item which will be the header), and the jQuery template. The template will be used to render individual stories.

If you look at the template, we parse the headline item from ESPN API, so the properties there are determined by whatever ESPN provides us with. If there are images attached to the story, we display them. We link the whole thing to a mobile story on ESPN website.

3. Latest news - local team

<ul id="latestTeamList" name="list-view-1" data-role="listview" data-inset="true">
  <li data-role="list-divider">
    <span id="latestTeamListHeader"></span>
  </li>
</ul>

This bit is virtually identical to the previous one. In fact, they both utilize the same jQuery template, since both lists display news in the same way, the only difference is obviously what will be bound to these lists.

4. Categories links

<ul id="categoriesList" name="list-view-2" data-role="listview" data-inset="true">
  <li data-role="list-divider">
    All categories
  </li>
</ul>

Categories list is quite simple, since it’s going to be a Metro style text-list (easily “tappable”). Notice how we bind the category ID to data-sportid attribute. This will allow us to pass the selected category ID to the next page (espn.html, or the so called “news list page”), which will then use it to pull appropriate data from ESPN API. More on that soon, when we get to our JS.

5. Application bar
Application bar, one of the recognizable features of WP7, is very easy to implement in your app as it’s provided by the jQuery Mobile Metro.


<div data-role='app-bar' >
  <a href='settings.html' data-transition="pop" data-icon='demo-set' >settings</a></p> 
  
  <ul>
    <li>
      <a href='https://www.strathweb.com'>strathweb.com</a>
    </li>
    <li>
      <a href='http://espn.go.com'>ESPN</a>
    </li>
  </ul>
</div>

We just add a div with a data-role=“app-bar” and then go ahead and define the links – both the visible icon links, and the hidden links shown after tapping on the application bar (the ones inside the ul in the snippet above.

That’s really all the HTML we need for the home page. We’ll come back to that page later when we implement JS, but for now, let’s continue with HTML coding and implement the markup for settings.html page.

Settings page markup 🔗

If you remember, we already created empty stubs for settings.html and espn.html. Let’s open settings then and get going.

If you are already familiar with jQuery Mobile you’d know that already, but let me say that there is a specific way jQuery Mobile handles pages and navigation between different views in the application. It is necessary that all resources are loaded on the homepage – because the default behavior for jQuery when you navigate to a new page is to fetch it via AJAX. This means that you still have access to the resources you declared on the home page and the “head" section of the subpage isn’t even being processed. That is why we added references to all our JavaScript files on the index.html.

The generic markup for any page other than index.html in our application should be (sans the “settingsPage" ID and the h1 title.

  
  
  
</p> 

<div data-role="page" data-overlay-theme="a" id="settingsPage">
  <div data-role="header">
    <h1>
      Settings
    </h1></p>
  </div>
  
  <div data-role="content">
  </div></p>
</div>

</body>  
</html>  

All the rest goes inside the div with data-role=“page” - either into the div data-role=“content”, or after it (depends on the type of element we are adding).

The settings page will have two functionalities only:

  • A list of sports categories (same as on home page), which acts as a select list, allowing the user to pick his favorite sport
  • A checkbox to switch geolocation on and off
  • A button to clear local storage (or in other words, reset settings)

Additionally the settings page will also write to phone memory (localStorage), as the settings need to be preserved. More on that later, in the Javascript part.

1. Select list of sports
The select list is, unsurprisingly, going to be a select element, which is very beautifully styled into a Metro type select by jQuery Mobile Metro.


<div data-role="fieldcontain">
  <label for="settingsSelect" class="select">Favorite sport</label><br /> <select name="settingsSelect" id="settingsSelect"><br /> </select>
</div>

  

We use jQuery templating again to bind the option elements to it; as data source we’ll use the list of sports categories already declared in the App namespace.

2. Geolocation checkbox
This is another simple piece of HTML – just a checkbox and a label. Notice that, just as in the previous example, we wrap the field in div data-role=“fieldcontain” (jQuery Mobile requirement).


<div data-role="fieldcontain">
  <input type="checkbox" name="geolocationCheckbox" id="geolocationCheckbox" class="custom" value="0" /><br /> <label for="geolocationCheckbox">Use geolocation</label>
</div>

3. Clear local storage button
The button will actually be a styled combination of span and a elements. The CSS classes are jQuery Mobile Metro specific and provide us with a nice sleek metro button as an output.

<a id="clearStorage" data-role="button" data-corners="true" data-shadow="true" data-iconshadow="true" data-wrapperels="span" data-theme="a" class="ui-btn ui-shadow ui-btn-corner-all ui-btn-hover-a ui-btn-up-a"><br /> <span class="ui-btn-inner ui-btn-corner-all"><br /> <span class="ui-btn-text">Clear local storage</span><br /> </span><br /> </a>  

Again, we don’t worry about even handling for now – so let’s move on to our final page, espn.html, where we’ll dump the actual news items.

News list page 🔗

The news list page will display the news from a category requested by the user. It will consist of:

  • A list of headlines, with images and link to full story on ESPN (same type of list as on home screen)
  • A button to load more stories (by default we load in 10, the button loads 10 more)

The basic structure of the page should be the same as of settings.html – don’t forget to change the ID of the page – we’ll use “headlinesPage".

1. List of headlines


<ul data-role="listview" id="headlines">
</ul>

  

The list uses exactly the same template and setup as the lists on the homepage, so there is not much to discuss here.

2. Load more stories button
Similarly, the button is the exact copy of the Metro-style button we just discussed while designing the settings.html. So let’s just go ahead and add it:

<a id="loadMore" data-role="button" data-corners="true" data-shadow="true" data-iconshadow="true" data-wrapperels="span" data-theme="a" class="ui-btn ui-shadow ui-btn-corner-all ui-btn-hover-a ui-btn-up-a"><br /> <span class="ui-btn-inner ui-btn-corner-all"><br /> <span class="ui-btn-text">Load more stories</span><br /> </span><br /> </a>  

Adding JavaScript and Event handling 🔗

We are now very close to the Promised Land with our application – we have a Cordova solution that compiles just fine, which will package our stuff for deployment on WP7, the Metro style is in place, the HTML is ready and waiting.

All that’s needed is a little bit of JavaScript to turn it into a fully fledged application. We already discussed the common JS methods that went into the App namespace. All we need to do know is populate our three JS files - homePage.js, settingsPage.js and headlinesPage.js.

Implementing home page JavaScript 🔗

If you are familiar with jQuery Mobile, this is going to be a piece of cake for you; if you aren’t – well it’s also going to be very easy, as long as you learn a few minor differences from jQuery desktop. First of all, on mobile we don’t use $(document).ready(). Instead we bind to pageinit event.

$('#homePage').live('pageinit', function (event, data) {  
});  

Normally we’d start coding in there right away, but since this is a homepage (index), and we are going to be using phone functionalities (not just jQuery Mobile browser-specific stuff) we need to make sure that the phone is working fine, therefore we’ll listen for an Apache Cordova specific event, called deviceready.

document.addEventListener("deviceready", onDeviceReady, false);  
function onDeviceReady(e) {  
//do magic here  
}  

Next:

function onDeviceReady(e) {  
$("#categoriesListTemplate").tmpl(App.headline_list.sport).appendTo("#categoriesList");  
if (!localStorage.getItem("settingsFavoriteSportId")) {  
localStorage.setItem("settingsFavoriteSportId", 0);  
}  
if (!localStorage.getItem("settingsUseGeolocation")) {  
localStorage.setItem("settingsUseGeolocation", 0);  
}  
$("#latestNewsHeader").text(App.headline_list.sport[localStorage.getItem("settingsFavoriteSportId")].name);  
App.queryESPNAPI(App.headline_list.sport[localStorage.getItem("settingsFavoriteSportId")].url, 3, 0, "latestList", "latestListTemplate", 1);  
if (parseInt(localStorage.getItem("settingsUseGeolocation"), "10") == 1) {  
getGeolocatedNews();  
}  
};  

What happens here:

  1. First we use jQuery template to populate the list of all sports categories (from App.headline_list.sport

  2. We want to initialize two values in the localStorage (HTML5 repository for local data). It’s a persistent storage that we’ll use for keeping settings data. One of the keys will be for the selected favorite sport, the other for setting geolcoation on/off. We initialize both of them with 0.

  3. We set the header of the latest sports news based on the category set as favorite category. On the first run it will always be 0, so it will default to “All ESPN News", but as soon as this is changed in the settings, different, relevant title would appear.

  4. We query ESPN API using the common method from the App namespace. We want 3 of the news items from the favorite category of the user. The news should appear in the latestList element and each item be displayed using latestListTemplate.

  5. Finally, if the geolocation is switched on, we’ll call the method to grab geolocated news.

Let’s look into the geolocation method. Our idea is that we grab the user’s geolocation from the phone, using Cordova’s capabilities, then iterate through a list of MLB teams we defined earlier in the App namespace (remember, we only implement geolocation for MLB teams in this tutorial).

function getGeolocatedNews() {  
var fail = function (e) {  
console.log("geolocation fail " + e);  
};  
var coords = null;  
var closestTeamId = null;  
navigator.geolocation.getCurrentPosition(function (result) {  
var distances = [];  
for (i = 0; i < App.teams[0].teams.length; i++) { var dist = calcDistance([result.coords.latitude, result.coords.longitude], App.teams[0].teams[i]); distances.push([i, dist]); } closestTeamId = distances.min()[0]; var delayedExec = function () { App.queryESPNAPI(App.headline_list.sport[1].url + "/teams/" + App.teams[0].teams[closestTeamId].espnid, 3, 0, "latestTeamList", "latestListTemplate", 0); } setTimeout(delayedExec, 750) $("#latestTeamListHeader").text(App.teams[0].teams[closestTeamId].name); $("#latestTeamList").show(); }, fail, { enableHighAccuracy: true }); }

Retrieving user’s geolocation using JavaScript in Apache Cordova apps is dead easy. As you can see, it all comes down to calling navigator.geolocation.getCurrentPosition() and handling the coords in the callback function.

In the above example, we grab the coordinates and then iterate through the list of MLB teams, and find out how far away we are from that team using the App.calcDistance method. That method is actually borrowed from an excellent source here . Thanks!

App.calcDistance = function(start, end) {  
team = end;  
end = end.coords;

var R = 6371; // Radius of the earth in km  
var dLat = (Number(end[0]) - Number(start[0])).toRad(); // Javascript functions in radians  
var dLon = (Number(end[1]) - Number(start[1])).toRad();  
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +  
Math.cos(Number(start[0]).toRad()) \* Math.cos(Number(end[0]).toRad()) \*  
Math.sin(dLon / 2) * Math.sin(dLon / 2);  
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));  
var d = R * c; // Distance in km

return d;  
}  

So we add the above code to the App.js. Then we use our custom extension method Array.min() , to find out the closest team by its ID (that’s why I added it to App.js before). Once we have that, we can query ESPN API again, to pull team-specific news. You can see in the code that I delayed that call by 750ms. This is needed because ESPN API allows only one call per second, and since on the home screen we make one call before already, there is a chance that ESPN refuses us access.

One more thing we need to add to the homePage.js is a handler for the tap on the list of news categories. “Tap” is a phone’s equivalent of “click”, and that’s the event we have to handle. Since this is nothing Cordova specific, we don’t need to handle that event inside the deviceready event, so we put the handler outside of that, directly inside pageinit.

If a user taps on one of the categories, we want to send him to espn.html (a.k.a. Headlines page), where we’d display all the news from a given category.

$(".headlineLink").live("tap", function () {  
App.sportid = $(this).data("sportid");  
$.mobile.changePage("espn.html", { transition: $(this).data("transition") });  
});  

We add the event handler with live, because the list of items is dynamically created in runtime. We then pull the sportid data item from the link (corresponding to ID in the App.headlines_list.sport collection), and add it to a global variable App.sportid. This way we can access it on the next page, and display relevant news. After that we use jQuery Mobile way of sending user to a different page – by calling $.mobile.changePage

That’s all that’s needed for the home page. Let’s have a look at settings.html.

Settings page JavaScript 🔗

Again, we put all our stuff inside the init event, as that’s the place where all our handlers should be declared.

$('#settingsPage').live('pageinit', function (event, data) {  
$("#settingsTemplate").tmpl(App.headline_list.sport).appendTo("#settingsSelect");  
if (localStorage.getItem("settingsFavoriteSportId")) {  
$("#settingsSelect")[0].selectedIndex = localStorage.getItem("settingsFavoriteSportId");  
$("#settingsSelect").selectmenu("refresh");  
}  
if (localStorage.getItem("settingsUseGeolocation") == 1) {  
$("input#geolocationCheckbox").attr("checked", true).checkboxradio('refresh'); ;  
}  
});  

First, we populate the select list of sports categories with items from our static object in the App namespace. This would allow users to choose their favorite sport.

Then we check in the localStorage, if something has already been selected as favorite sport before – if so, we use that ID to preselct the sports category. The same is done for the geolocation checkbox.

The next thing we need to add are 3 event handlers:

  • when a selection of favorite sport is made in the categories list
  • when geolocation checkbox is checked
  • when clear storage button is tapped
$("#clearStorage").bind("tap", function () {  
localStorage.clear();  
$("#clearMessage").text("Cleared!");  
});

$("#settingsSelect").live("change", function (event) {  
localStorage.setItem("settingsFavoriteSportId", $("#settingsSelect").val());  
});

$("input#geolocationCheckbox").bind("change", function (event) {  
if ($(this).attr("checked")) {  
localStorage.setItem("settingsUseGeolocation", 1);  
} else {  
localStorage.setItem("settingsUseGeolocation", 0);  
}  
});  

The event handlers are pretty simple – in each case we manipulate the localStorage to set or remove values. Notice that I only access localStorage using square brackets notation – the dot notation is also available in the HTML5 world, however it is not supported in the WP7 implementation of Apache Cordova.

The final page is our headlines page, espn.html.

Headlines page JavaScript 🔗

This is the simplest one we deal with. It only has one button and a list to work with.

$('#headlinesPage').live('pageinit', function (event) {  
App.offset = 0;  
App.queryESPNAPI(App.headline_list.sport[App.sportid].url, 10, App.offset, "headlines", "headlineTemplate", 1);  
$("h1#page-title").text(App.headline_list.sport[App.sportid].name);  
$("#loadMore").on("tap", function () {  
App.queryESPNAPI(App.headline_list.sport[App.sportid].url, 10, App.offset, "headlines", "headlineTemplate", 1);  
});  
});  

We use the global variable App.sportid to query the ESPN API. We also have an App.offset variable, which we’ll use to keep track of how many stories we loaded. The query method increments the offset by the number of items retrieved. This allows us to bind an event to the button tap which will load the next batch of news (11-20, 21-30 and so on).

Some final touches 🔗

To cap everything off, we just need to add some final touches to our application. The jQuery Mobile Metro style is great, but we have some custom stuff (i.e. news list from ESPN) so we need to add some custom CSS. We already created appstyle.css, so let’s add some code there:

li.listview-txt {font-size:10pt !important; margin-bottom:15px; display:block; position:relative;}  
li.listview-txt h3 {margin:0;}  
li.listview-txt p {margin:5px 0;}  
li.listview-txt a {color:#fff; text-decoration:none;}  
li.listview-txt img {float:left; margin: 30px 10px 0 0;}  

Moreover, you’d probably want some fancy logo and splash screen for you application. The easiest way to add these, is to overwrite the default images: ApplicationIcon.png and ApplicationIcon.png. After the rebuild (make sure to delete all the files from the build folder first), you should see your custom imagery appear in the application. Alternatively you can specify your own icon in the properties/WMAppManifest.xml.

I apologize for my crappy design skills, I just did what I could 🙂 By the way, I used the free stock photo from here, so props to the author.

One more thing to do is to add ESPN branding. Remember that using ESPN API requires you to adhere to ESPN API branding policies. Therefore we add espn.png to our project and include this in both index.html and espn.html.


Powered by [![][23]][24]

Running the application 🔗

That’s it! After long hours, blood sweat and tears (OK, maybe I’m exaggerating a tiny bit…) we have our WP7 Metro application!

I already showed some screenshots beforehand, but here are a few more. Please observe how nicely our application comes up - with a name and logo, beside other apps in the phone (including the Apache Cordova default one).

Also, let’s verify if geolcoation works fine. By default, on the emulator, it should show Seattle Mariners, since we are (virtually) in Redmond.

Now let’s go to Madison, WI.

Go Brewers?

Bonus 🔗

Now, the super amazing thing about Apache Cordova phone application development is that pretty much everything you do is platform-independent. So you can now take your HTML, CSS, JS, Imagery, and build an Android application, or an iPhone application. Instructions are here.

Source files 🔗

As you usually, you can find the source code below. Obviously there is infinitely more to mobile application development this way, and we only scratched the surface here – anyway, hope you enjoyed the tutorial and hopefully this will help you get started! See you next time!

  • source project - ZIP, 3.2MB - remember, to use the source you need to generate your own ESPN API key here and add to App.js!

About


Hi! I'm Filip W., a cloud architect from Zürich 🇨🇭. I like Toronto Maple Leafs 🇨🇦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁢󠁳󠁣󠁴󠁿.

You can find me on Github and on Mastodon.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP