<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>PhoneGap Tips</title>
		<description>HTML5, CSS3, and JavaScript techniques to supercharge your PhoneGap app.</description>
		<link>http://phonegap-tips.com</link>
		<atom:link href="http://phonegap-tips.com/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Installing Plugins with PhoneGap's Command-Line Interface</title>
				<description>&lt;p&gt;A few weeks ago I wrote about &lt;a href='/articles/the-nodejs-command-line-interface-for-phonegap.html'&gt;PhoneGap's Node.js command-line interface&lt;/a&gt;. A reader stopped by to check out the article and asked how to use the command-line interface to install plugins, which of course I had conveniently glossed over.&lt;/p&gt;

&lt;p&gt;Well, PhoneGap 3.0 was released, and one of the big changes in version 3.0 was that a lot of the device functionality that used to ship with PhoneGap has been extracted to individual plugins. Having recently written about &lt;a href='/articles/google-api-oauth-with-phonegaps-inappbrowser.html'&gt;using InAppBrowser to build an OAuth login&lt;/a&gt; I figured I would update my OAuth example project for PhoneGap 3.0 and see how plugins work with the command-line interface.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;First, I updated my &lt;code&gt;package.json&lt;/code&gt; to use the latest &lt;code&gt;cordova&lt;/code&gt; node module:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;google-api-oauth-phonegap&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;0.0.3&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='s2'&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='s2'&gt;&amp;quot;cordova&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;~3.0.0&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I ran &lt;code&gt;npm update&lt;/code&gt; to update my project&amp;#8217;s node modules.&lt;/p&gt;

&lt;p&gt;At this point, I was all set up to develop with PhoneGap 3.0. However, since InAppBrowser is a plugin now, and I had not installed it yet, I wanted to see what would happen if I just ran the project without installing the plugin.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;&amp;gt; cordova build ios
&amp;gt; cordova emulate ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When I tested the app, the consent page opened just fine, but sure enough, when I got to the last step and tapped the &lt;em&gt;Accept&lt;/em&gt; button, the button became disabled and the consent page did not disappear, which left me staring at this:&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='InAppBrowser not installed' src='/img/inappbrowser_not_installed.png' /&gt;&lt;/p&gt;

&lt;p&gt;So, as advertised, I had to install the InAppBrowser plugin to get my app working again:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;&amp;gt; cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I built the project and ran the emulator again, and this time the OAuth login worked!&lt;/p&gt;

&lt;p&gt;As you can see, installing plugins with the command-line interface about as easy as it gets. You simply provide a URL (or path) to the plugin you want and the command-line interface takes care of the rest.&lt;/p&gt;

&lt;p&gt;The only somewhat difficult part is discovering the correct plugin URL.&lt;/p&gt;

&lt;p&gt;The plugin URLs for functionality like InAppBrowser that used to ship with PhoneGap &lt;a href='http://docs.phonegap.com/en/3.0.0/guide_cli_index.md.html#The%20Command-line%20Interface_add_features'&gt;can be found here&lt;/a&gt;. Most of the other community plugins are slowly being migrated out of the &lt;a href='https://github.com/phonegap/phonegap-plugins'&gt;phonegap plugins GitHub repository&lt;/a&gt;, so it can sometimes be helpful to check there first to see if you can find a link to the 3.0-compatible plugin you want to use.&lt;/p&gt;</description>
				<pubDate>Tue, 23 Jul 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/installing-plugins-with-phonegaps-command-line-interface.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/installing-plugins-with-phonegaps-command-line-interface.html</guid>
			</item>
		
			<item>
				<title>OAuth with PhoneGap's InAppBrowser: Expiration and Revocation</title>
				<description>&lt;p&gt;A few weeks ago I wrote about how to implement &lt;a href='/articles/google-api-oauth-with-phonegaps-inappbrowser.html'&gt;an OAuth login with PhoneGap's InAppBrowser&lt;/a&gt;. Well, it seems like a lot of people are looking for information about PhoneGap and OAuth, so I figured I&amp;#8217;d write a follow up article.&lt;/p&gt;

&lt;p&gt;At the end of the last article we had a way to launch the consent page in PhoneGap&amp;#8217;s InAppBrowser, retrieve the authorization code, and exchange it for an access token. That&amp;#8217;s great, but for a release-quality application we need to take things a bit further.&lt;/p&gt;

&lt;p&gt;There are two more conditions we need to consider: access tokens can &lt;strong&gt;expire&lt;/strong&gt; and access tokens can be &lt;strong&gt;revoked&lt;/strong&gt;.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id='refreshing_expired_access_tokens'&gt;Refreshing Expired Access Tokens&lt;/h2&gt;

&lt;p&gt;Access tokens are valid for a finite period of time, then they expire. This means that after we receive an access token we can cache it and continue to use it until it expires. When an access token expires, we need to go get a new one.&lt;/p&gt;

&lt;p&gt;An OAuth 2.0 provider will typically issue an access token with a &lt;em&gt;refresh token&lt;/em&gt;. We can use the refresh token to retrieve a new access token without showing the consent page again.&lt;/p&gt;

&lt;p&gt;At first the solution seems simple. Use &lt;code&gt;setTimeout&lt;/code&gt; to refresh the token a few seconds before it expires. However, this could lead to hard-to-debug race conditions if the token refresh takes a long time to complete, but that&amp;#8217;s not the worst problem.&lt;/p&gt;

&lt;p&gt;Our PhoneGap app is running on a mobile device. What if we do not have a network connection when the refresh timer fires? What if the app is suspended in the background when the refresh timer fires? We could subscribe to PhoneGap&amp;#8217;s &lt;a href='http://docs.phonegap.com/en/2.9.0/cordova_events_events.md.html#online'&gt;online&lt;/a&gt; or &lt;a href='http://docs.phonegap.com/en/2.9.0/cordova_events_events.md.html#resume'&gt;resume&lt;/a&gt; events and refresh the token or reset the timer in each of those event handlers&amp;#8230; but that will quickly become a mess.&lt;/p&gt;

&lt;p&gt;The best way I&amp;#8217;ve found to handle this is to check the status of the access token before each API request is sent. If the token is still valid, we&amp;#8217;ll just pass it on to the original request. If the token is expired, we&amp;#8217;ll refresh the token, cache the new token, and then pass it on to the original request.&lt;/p&gt;

&lt;p&gt;Building on the OAuth example from the last article, we&amp;#8217;re going to add two new methods. First, we need a &lt;code&gt;setToken&lt;/code&gt; method to cache some information about the token:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;setToken&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;refresh_token&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;refresh_token&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;refresh_token&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

  &lt;span class='c1'&gt;//Calculate exactly when the token will expire, then subtract&lt;/span&gt;
  &lt;span class='c1'&gt;//one minute to give ourselves a small buffer.&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;now&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nb'&gt;Date&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;getTime&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;expiresAt&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;now&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nb'&gt;parseInt&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;expires_in&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='mi'&gt;1000&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='mi'&gt;60000&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;expires_at&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;expiresAt&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m storing the token information in &lt;code&gt;localStorage&lt;/code&gt; in this example, but you could also store it in a cookie, or in a file by using PhoneGap&amp;#8217;s file system API if you want. Anywhere we retrieve an access token we&amp;#8217;ll need to call this method to update the cache.&lt;/p&gt;

&lt;p&gt;Next we need a &lt;code&gt;getToken&lt;/code&gt; method that will transparently validate a cached access token or update an expired access token with a refresh token.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getToken&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;deferred&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Deferred&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;now&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nb'&gt;Date&lt;/span&gt;&lt;span class='p'&gt;().&lt;/span&gt;&lt;span class='nx'&gt;getTime&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;

  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;now&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;expires_at&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;//The token is still valid, so immediately return it from the cache&lt;/span&gt;
    &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;resolve&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
      &lt;span class='nx'&gt;access_token&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt;
    &lt;span class='p'&gt;});&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;refresh_token&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;//The token is expired, but we can get a new one with a refresh token&lt;/span&gt;
    &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;post&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;https://accounts.google.com/o/oauth2/token&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;refresh_token&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;localStorage&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;refresh_token&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;grant_type&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;refresh_token&amp;#39;&lt;/span&gt;
    &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;done&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;setToken&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt; &lt;span class='c1'&gt;//Remember I said we need to call this to cache the token?&lt;/span&gt;
      &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;resolve&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;fail&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;response&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;response&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;responseJSON&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;});&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;//We do not have any cached token information yet&lt;/span&gt;
    &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;promise&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is how we can use the &lt;code&gt;getToken&lt;/code&gt; method to ensure we have a valid access token before making an API request:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getToken&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR_CLIENT_ID&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR_CLIENT_SECRET&amp;#39;&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;then&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='c1'&gt;//Call an API method and return a new promise&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;userInfo&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt; &lt;span class='nx'&gt;access_token&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt; &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;done&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;user&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;alert&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Hello &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;user&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;!&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Naturally, if the application involves many different API calls that all require access tokens, we&amp;#8217;ll want to figure out some way to do this in one place. How to accomplish that will depend on the specific application, so I&amp;#8217;ll leave it as an exercise for you!&lt;/p&gt;

&lt;h2 id='revoked_access_tokens'&gt;Revoked Access Tokens&lt;/h2&gt;

&lt;p&gt;Now we&amp;#8217;re handling expired tokens, but there is one more problem. Tokens can be revoked, and not necessarily only from within your app. Most OAuth providers have some sort of console where you can manage all of your OAuth grants. One of your app&amp;#8217;s users might go to this screen and clean things up, and now that access token you cached doesn&amp;#8217;t work anymore. Even if it was not yet expired!&lt;/p&gt;

&lt;p&gt;If this happens, the only way to get a new access token is to ask the user for one by showing the consent page again.&lt;/p&gt;

&lt;p&gt;The best way to handle this is to add an error handler for API requests that fail authorization. If authorization fails, there is likely some problem with the access token we used, so we need to throw it out and ask for a new one. Fortunately, we can accomplish this easily:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;getToken&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR_CLIENT_ID&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR_CLIENT_SECRET&amp;#39;&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;then&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='c1'&gt;//Call an API method and return a new promise&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;userInfo&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt; &lt;span class='nx'&gt;access_token&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt; &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;done&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;user&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;alert&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Hello &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;user&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;name&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;!&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;fail&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='c1'&gt;//The token was revoked, prompt the user to login again&lt;/span&gt;
  &lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;showLoginView&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just like in the last post, the full source code for this example &lt;a href='https://github.com/mdellanoce/google-api-oauth-phonegap/tree/v0.0.2'&gt;is available on github&lt;/a&gt;. If you want to try out what happens when you revoke an access token, head to your &lt;a href='https://accounts.google.com'&gt;Google Accounts&lt;/a&gt; page, click on the &lt;strong&gt;manage security&lt;/strong&gt; link, then click on the &lt;strong&gt;review permissions&lt;/strong&gt; link under &lt;strong&gt;connected applications and sites&lt;/strong&gt; to manage your active OAuth grants.&lt;/p&gt;</description>
				<pubDate>Fri, 12 Jul 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/oauth-with-phonegaps-inappbrowser-expiration-and-revocation.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/oauth-with-phonegaps-inappbrowser-expiration-and-revocation.html</guid>
			</item>
		
			<item>
				<title>Conditional Dependency Injection with AngularJS</title>
				<description>&lt;p&gt;The excitement level around AngularJS got high enough that I thought I should give it a shot, so I took it for a spin in a PhoneGap application I am working on.&lt;/p&gt;

&lt;p&gt;My application defines an OAuth service that handles authentication and authorization. I want to inject different implementations of the OAuth service depending on the environment the application is running in. In a web browser the OAuth service simply wraps the &lt;a href='https://code.google.com/p/google-api-javascript-client/'&gt;Google API JavaScript client&lt;/a&gt;. In the PhoneGap web view the OAuth service uses a &lt;a href='/articles/google-api-oauth-with-phonegaps-inappbrowser.html'&gt;custom implementation that relies on InAppBrowser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think conditional dependency injection will be a common need for most PhoneGap developers, especially developers that hope to reuse some, or all, of their code in a web application.&lt;/p&gt;

&lt;p&gt;Unfortunately, it is not immediately obvious &lt;em&gt;how&lt;/em&gt; to do this&amp;#8230;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Here was my first attempt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;app&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;angular&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;module&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;[]);&lt;/span&gt;
&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;cordova&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;oauth&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;phonegapDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;oauth&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;webDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;controller&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;LoginController&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is simple, but the problem with this approach becomes apparent when you need to make a decision about which service to use &lt;em&gt;after&lt;/em&gt; bootstrapping. It also hurts testability. You can&amp;#8217;t test both implementations in the same test run.&lt;/p&gt;

&lt;p&gt;My next attempt was to define two services, declare a dependency on both, and choose which to use at runtime:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;app&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;angular&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;module&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;[]);&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;webOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;phonegapOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;controller&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;LoginController&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;webOauthImpl&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;phonegapOauthImpl&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;cordova&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  	&lt;span class='nx'&gt;phonegapOauthImpl&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  	&lt;span class='nx'&gt;webOauthImpl&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is somewhat better from a testability standpoint, but I still don&amp;#8217;t like the thought of having these checks littered throughout my code. I want my controller to only be aware of one OAuth service. This will make it easier to use some other useful features of AngularJS, like decorators for example.&lt;/p&gt;

&lt;p&gt;After digging around in the AngularJS documentation some more, I discovered providers, which allow more control over the dependency injection process. Here was my attempt at using providers:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;app&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;angular&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;module&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;[]);&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;provider&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;oauth&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;phonegapDependencies&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;webDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;

  &lt;span class='kd'&gt;function&lt;/span&gt; &lt;span class='nx'&gt;webOauthImpl&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;webDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;
  
  &lt;span class='kd'&gt;function&lt;/span&gt; &lt;span class='nx'&gt;phonegapOauthImpl&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;phonegapDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;
  
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;$get&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;cordova&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nx'&gt;phonegapOauthImpl&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;phonegapDependencies&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nx'&gt;webOauthImpl&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;webDependencies&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;

&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;controller&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;LoginController&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Okay, now we&amp;#8217;re getting somewhere. This is easy to test, I managed to keep the conditional in one place, and the controller only depends on a single OAuth service. The only thing I still don&amp;#8217;t like is having to specify the dependencies for both implementations in the provider, and then pass the dependencies to the appropriate controller. I want AngularJS to do this for me&amp;#8230;&lt;/p&gt;

&lt;p&gt;Well, it can! I realized I could use the &lt;code&gt;$injector&lt;/code&gt; service with a &lt;code&gt;factory&lt;/code&gt; to accomplish exactly what I wanted:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;app&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;angular&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;module&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;[]);&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;webOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;webDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;service&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;phonegapOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;phonegapDependencies&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{};&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;factory&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;oauth&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nx'&gt;$injector&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;cordova&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$injector&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;phonegapOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$injector&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;webOauthImpl&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='nx'&gt;app&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;controller&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;LoginController&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;oauth&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s it! First I register two different OAuth services, each with the same interface. Then I register a factory method that depends on the &lt;code&gt;$window&lt;/code&gt; and &lt;code&gt;$injector&lt;/code&gt; services. In the factory method I check whether or not the &lt;code&gt;cordova&lt;/code&gt; global variable is defined and resolve the appropriate OAuth implementation using the &lt;code&gt;$injector.get&lt;/code&gt; method. Now I can test each implementation in isolation &lt;em&gt;and&lt;/em&gt; test that the OAuth factory gives the right implementation under the right conditions.&lt;/p&gt;</description>
				<pubDate>Tue, 02 Jul 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/conditional-dependency-injection-with-angularjs.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/conditional-dependency-injection-with-angularjs.html</guid>
			</item>
		
			<item>
				<title>Debugging iOS PhoneGap Apps with Safari's Web Inspector</title>
				<description>&lt;p&gt;At some point, something will go wrong in your PhoneGap application, and you&amp;#8217;ll want to know why. Ideally, you&amp;#8217;ll be able to reproduce the problem in a desktop web browser, fix it, and move on. Inevitably, however, you&amp;#8217;ll run into a problem that is only reproducible on a device. If this happens to you on iOS, and you are targetting iOS 6 or higher, you&amp;#8217;re in luck&amp;#8230; well, aside from that nasty defect you just found, but hey, it happens!&lt;/p&gt;

&lt;p&gt;With Safari 6 and higher (OS X only, sorry Windows) you can use Safari&amp;#8217;s developer tools to remotely debug web pages in mobile Safari on an iOS device.&lt;/p&gt;

&lt;p&gt;But wait, there&amp;#8217;s more!&lt;/p&gt;

&lt;p&gt;Not only can you remotely debug web pages in mobile Safari, you can remotely debug any web page in any plain old UIWebView, which just happens to include that UIWebView hosting your PhoneGap application. Note that you can only inspect UIWebView content in apps that were transferred to a device from XCode. Sorry, no inspecting apps downloaded from the App Store!&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s how to set up for debugging PhoneGap apps with Safari&amp;#8217;s web inspector.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id='disable_private_browsing'&gt;Disable Private Browsing&lt;/h2&gt;

&lt;p&gt;Open your device&amp;#8217;s Safari settings and ensure that &lt;strong&gt;Private Browsing is turned off&lt;/strong&gt;. Remote debugging will not work if Private Browsing is enabled. I learned this the hard way after wasting a couple hours trying to set up remote debugging.&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='Disable private browsing' src='/img/disable_ios_private_browsing.png' /&gt;&lt;/p&gt;

&lt;h2 id='enable_web_inspector'&gt;Enable Web Inspector&lt;/h2&gt;

&lt;p&gt;Tap the Advanced tab on your device&amp;#8217;s Safari settings and ensure that &lt;strong&gt;Web Inspector is turned on&lt;/strong&gt;.&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='Enable web inspector' src='/img/enable_ios_web_inspector.png' /&gt;&lt;/p&gt;

&lt;h2 id='enable_safaris_develop_menu'&gt;Enable Safari&amp;#8217;s Develop Menu&lt;/h2&gt;

&lt;p&gt;On your desktop or laptop, open Safari&amp;#8217;s Preferences and click on the Advanced tab. Check the box to &lt;strong&gt;Show Develop menu in menu bar&lt;/strong&gt;.&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='Enable Safaris develop menu' src='/img/enable_safari_develop_menu.png' /&gt;&lt;/p&gt;

&lt;h2 id='start_web_inspector'&gt;Start Web Inspector&lt;/h2&gt;

&lt;p&gt;Launch your app either in the iOS simulator or on a physical device. If you are using a physical device you&amp;#8217;ll need to connect it to your desktop or laptop with the standard USB cable. Once the app has launched, switch to Safari, select the &lt;strong&gt;Develop&lt;/strong&gt; menu item, then find the entry corresponding to the web page you want to debug.&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='Attach remote web inspector' src='/img/remote_web_inspector_attach.png' /&gt;&lt;/p&gt;

&lt;p&gt;Now you can use web inspector just like you would to debug a web page.&lt;/p&gt;

&lt;p class='text-center well'&gt;&lt;img alt='Use remote web inspector' src='/img/remote_web_inspector_use.png' /&gt;&lt;/p&gt;

&lt;h2 id='pitfalls'&gt;Pitfalls&lt;/h2&gt;

&lt;p&gt;One disadvantage to using web inspector like this is that you cannot attach early enough to debug issues that occur at start up. You can fake it a little by adding a delay to your start up code with &lt;code&gt;setTimeout&lt;/code&gt;, but even that won&amp;#8217;t help catch every issue. You&amp;#8217;ll have to rely on the old standbys &lt;code&gt;alert()&lt;/code&gt; and &lt;code&gt;console.log()&lt;/code&gt; to figure out these issues, and of course, just opening your app&amp;#8217;s main HTML page in a browser can be quite helpful for diagnosing start up problems.&lt;/p&gt;</description>
				<pubDate>Wed, 26 Jun 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/debugging-ios-phonegap-apps-with-safaris-web-inspector.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/debugging-ios-phonegap-apps-with-safaris-web-inspector.html</guid>
			</item>
		
			<item>
				<title>Google API OAuth with PhoneGap's InAppBrowser</title>
				<description>&lt;p&gt;If your PhoneGap project requires access to one of Google&amp;#8217;s APIs, the first challenge you&amp;#8217;ll likely run into is how to handle the OAuth dance in a PhoneGap application.&lt;/p&gt;

&lt;p&gt;Google&amp;#8217;s OAuth documentation seems to indicate &lt;a href='https://developers.google.com/accounts/docs/OAuth2InstalledApp'&gt;OAuth 2.0 for installed applications&lt;/a&gt; fits the bill for a mobile application.&lt;/p&gt;

&lt;p&gt;The idea is to use an embedded web browser to show the OAuth consent page. If the user grants access, we can get the authorization code out of the embedded browser&amp;#8217;s title property. We PhoneGap developers have &lt;a href='http://docs.phonegap.com/en/2.8.0/cordova_inappbrowser_inappbrowser.md.html#InAppBrowser'&gt;InAppBrowser&lt;/a&gt; for embedded browsing, so this should be a piece of cake!&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;If you want to follow along, head on over to the &lt;a href='https://code.google.com/apis/console'&gt;API console&lt;/a&gt; and create a new project. Select &lt;strong&gt;API Access&lt;/strong&gt; and then click the button to create a new client ID. In the dialog that pops up, select &lt;strong&gt;installed application&lt;/strong&gt;, choose your platform, and fill in any other required information for your platform. Afterwards you&amp;#8217;ll see your client ID, client secret, and redirect URIs.&lt;/p&gt;

&lt;p class='figure'&gt;&lt;img alt='Installed application client ID' src='/img/installed_app_client_id.png' /&gt;&lt;/p&gt;

&lt;p&gt;Now that the application is registered with Google, go ahead and create a new PhoneGap project. I recommend the &lt;a href='/articles/the-nodejs-command-line-interface-for-phonegap.html'&gt;command-line interface&lt;/a&gt;, but use whatever you&amp;#8217;re comfortable with. I tested this solution with PhoneGap 2.8, 2.9, and 3.0. &lt;strong&gt;If you are using PhoneGap 3.0 or higher&lt;/strong&gt;, &lt;a href='/articles/installing-plugins-with-phonegaps-command-line-interface.html'&gt;you will need to install the InAppBrowser plugin&lt;/a&gt;. I have heard from some readers that this solution may not work with earlier PhoneGap versions, so if you run into problems, check your PhoneGap version first.&lt;/p&gt;

&lt;p&gt;In the examples that follow, I&amp;#8217;ll be using jQuery since it is fairly ubiquitous and it keeps the code terse. With little effort you could make this work with your library of choice, or no library at all.&lt;/p&gt;

&lt;p&gt;To keep things simple let&amp;#8217;s start with a view that has a login button and a placeholder for a message:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;div&lt;/span&gt; &lt;span class='na'&gt;id=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;login&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;a&amp;gt;&lt;/span&gt;Sign In With Google!&lt;span class='nt'&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class='nt'&gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class='nt'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the application starts up, we need to wait for the &lt;strong&gt;deviceready&lt;/strong&gt; event so we can interact with parts of the PhoneGap API like InAppBrowser. After &lt;strong&gt;deviceready&lt;/strong&gt; fires, we can bind an event handler to show the OAuth consent page when the login button is tapped, and update the placeholder with the status of the OAuth dance after it completes. Here&amp;#8217;s what that looks like in code:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;googleapi&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;deferred&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;Deferred&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
        &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt; &lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Not Implemented&amp;#39;&lt;/span&gt; &lt;span class='p'&gt;});&lt;/span&gt;
        &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;promise&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;

&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;document&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;deviceready&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;$loginButton&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#login a&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;$loginStatus&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;#login p&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;

  &lt;span class='nx'&gt;$loginButton&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
      &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR CLIENT ID HERE&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR CLIENT SECRET HERE&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;urn:ietf:wg:oauth:2.0:oob&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
      &lt;span class='nx'&gt;scope&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;SPACE SEPARATED LIST OF SCOPES&amp;#39;&lt;/span&gt;
    &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;done&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;$loginStatus&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;Access Token: &amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;access_token&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;fail&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
      &lt;span class='nx'&gt;$loginStatus&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;html&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
    &lt;span class='p'&gt;});&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we run this and tap the button, we&amp;#8217;ll see the &lt;em&gt;Not Implemented&lt;/em&gt; message below the button. We just need to make that call to &lt;code&gt;googleapi.authorize&lt;/code&gt; work! The first thing we need to do is show the consent page. Fortunately, using InAppBrowser in PhoneGap is the same as opening a popup from JavaScript using the &lt;code&gt;window.open&lt;/code&gt; method. Let&amp;#8217;s replace that &lt;code&gt;deferred.reject&lt;/code&gt; call with some code to open the consent page:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;authUrl&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;https://accounts.google.com/o/oauth2/auth?&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;param&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
    &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;response_type&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;code&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;scope&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;scope&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;

&lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;authWindow&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;window&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;authUrl&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;_blank&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;location=no,toolbar=no&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What we&amp;#8217;re doing here is building the URL for Google&amp;#8217;s OAuth consent page and opening it in a new web view. Next we need to deal with retrieving the authorization code at the end of the consent process. That&amp;#8217;s where things get interesting!&lt;/p&gt;

&lt;p&gt;The recommendation is to retrieve the authorization code from the browser&amp;#8217;s title. However, there are a couple of problems with that.&lt;/p&gt;

&lt;p&gt;First, the consent window is pointing at a different origin, so according to the same origin policy, we are not allowed to access any details of its document, including the title. In PhoneGap the rules around the same origin policy are relaxed, like for AJAX requests, but I suspect we aren&amp;#8217;t dealing with a fully specification conformant window object either. So the document title likely isn&amp;#8217;t exposed in any way that would allow us to access it.&lt;/p&gt;

&lt;p&gt;Second, even if we &lt;em&gt;could&lt;/em&gt; access the document title, the authorization code isn&amp;#8217;t set in the title until the very end of the OAuth flow, and we have no way of knowing when it is set. So we would have to poll for the change. Alternatively, we could use InAppBrowser&amp;#8217;s &lt;code&gt;executeScript&lt;/code&gt; method to insert some code to use &lt;code&gt;window.opener.postMessage&lt;/code&gt; to notify us when we have the authorization code. However, again, we&amp;#8217;re not dealing with a real popup here, so opener is not set, which rules out &lt;code&gt;postMessage&lt;/code&gt; as a means of communicating between our two web views.&lt;/p&gt;

&lt;p&gt;The key to this problem is the &lt;strong&gt;loadstart&lt;/strong&gt; event that InAppBrowser fires. The &lt;strong&gt;loadstart&lt;/strong&gt; event includes the URL that InAppBrowser is about to load whenever the InAppBrowser&amp;#8217;s location changes. So we need a way to detect and parse the authorization code from the URL. Unfortunately, with the redirect URI we are using, the authorization code does not get set in the URL. However, there are two redirect URI options for installed applications. Let&amp;#8217;s modify our code to try &lt;em&gt;http://localhost&lt;/em&gt; as the redirect URI:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;googleapi&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;authorize&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
  &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR CLIENT ID HERE&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;YOUR CLIENT SECRET HERE&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;http://localhost&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='nx'&gt;scope&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;SPACE SEPARATED LIST OF SCOPES&amp;#39;&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we try the OAuth flow again with this redirect URI, we see that granting access redirects us to &lt;em&gt;http://localhost?code=your_authorization_code&lt;/em&gt;. Denying access redirects us to &lt;em&gt;http://localhost?error=access_denied&lt;/em&gt;. We probably don&amp;#8217;t have an HTTP server running on our mobile device, so we will most likely end up staring at some sort of error page, but it doesn&amp;#8217;t matter because we can detect the authorization code in the &lt;strong&gt;loadstart&lt;/strong&gt; event handler and close the InAppBrowser before any errors are displayed.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s handle &lt;strong&gt;loadstart&lt;/strong&gt; and parse the authorization code after the line where we opened the consent page with InAppBrowser:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;authWindow&lt;/span&gt;&lt;span class='p'&gt;).&lt;/span&gt;&lt;span class='nx'&gt;on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;loadstart&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;url&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nx'&gt;e&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;originalEvent&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;url&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;code&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='sr'&gt;/\?code=(.+)$/&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;exec&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;url&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='kd'&gt;var&lt;/span&gt; &lt;span class='nx'&gt;error&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='sr'&gt;/\?error=(.+)$/&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;exec&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;url&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;

  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;code&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;authWindow&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;close&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
  &lt;span class='p'&gt;}&lt;/span&gt;

  &lt;span class='c1'&gt;//TODO - exchange code for access token...&lt;/span&gt;
&lt;span class='p'&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally! We have an authorization code (or an error) and we can exchange it for an access token:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;code&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;post&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;https://accounts.google.com/o/oauth2/token&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;code&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;code&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt;
    &lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;client_id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;client_secret&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;options&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;redirect_uri&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
    &lt;span class='nx'&gt;grant_type&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;authorization_code&amp;#39;&lt;/span&gt;
  &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;done&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;resolve&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;data&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;}).&lt;/span&gt;&lt;span class='nx'&gt;fail&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;response&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;response&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;responseJSON&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;deferred&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;({&lt;/span&gt;
    &lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='nx'&gt;error&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;});&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s it! Now we can use our access token to interact with one of the &lt;a href='https://developers.google.com/apis-explorer'&gt;many Google APIs&lt;/a&gt;. We still need to think about access token expiration and revocation, but fortunately I&amp;#8217;ve covered those topics in another &lt;a href='/articles/oauth-with-phonegaps-inappbrowser-expiration-and-revocation.html'&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, keep in mind that OAuth is a standard, so even though this article is written specifically for Google&amp;#8217;s OAuth implementation, there&amp;#8217;s a good chance it will work (perhaps with a few small changes) for most OAuth 2.0 providers that support a client-side flow. For example, one reader reported that she was able to adapt this code to work with Quizlet&amp;#8217;s OAuth implementation.&lt;/p&gt;

&lt;p&gt;The complete source code for this example is &lt;a href='https://github.com/mdellanoce/google-api-oauth-phonegap/tree/v0.0.1'&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;</description>
				<pubDate>Mon, 17 Jun 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/google-api-oauth-with-phonegaps-inappbrowser.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/google-api-oauth-with-phonegaps-inappbrowser.html</guid>
			</item>
		
			<item>
				<title>The Node.js Command-line Interface for PhoneGap</title>
				<description>&lt;p&gt;PhoneGap ships with a simple set of scripts that you can use to create a new project from &lt;a href='http://docs.phonegap.com/en/2.8.0/guide_command-line_index.md.html#Command-Line%20Usage'&gt;the command-line&lt;/a&gt;. Newly created projects include scripts to build, &lt;a href='/articles/running-the-ipad-simulator-with-the-phonegap-command-line-tools.html'&gt;emulate&lt;/a&gt;, and deploy your app.&lt;/p&gt;

&lt;p&gt;There is another &lt;a href='https://github.com/apache/cordova-cli'&gt;command-line interface for PhoneGap written in JavaScript for node.js&lt;/a&gt; that the PhoneGap/Cordova documentation does not mention. I don&amp;#8217;t know why this is. Maybe it will be documented in future releases. Hopefully I am not stealing the PhoneGap team&amp;#8217;s thunder by writing about it!&lt;/p&gt;

&lt;p&gt;What I really like about this package is that it bundles the PhoneGap/Cordova release. So by installing the package, you have effectively installed PhoneGap. It also provides some fantastic guidance about how to structure a multi-platform PhoneGap application.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;The documentation recommends installing the &lt;abbr title='command-line interface'&gt;CLI&lt;/abbr&gt; globally by running:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; npm install -g cordova
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, if you install the CLI locally you will be able to have different PhoneGap versions for different projects. So here are the steps I follow when starting a new PhoneGap project.&lt;/p&gt;

&lt;p&gt;Install node.js, and add &lt;code&gt;./node_modules/.bin&lt;/code&gt; to your PATH environment variable.&lt;/p&gt;

&lt;p&gt;Create a directory for your new project:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; mkdir myproject &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='nb'&gt;cd &lt;/span&gt;myproject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a package.json for your project:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; npm init
&lt;span class='c'&gt;# Follow the (many) prompts... or just hit enter a bunch of times.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install the PhoneGap CLI:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; npm install cordova --save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that the CLI is installed, let&amp;#8217;s see what it can do for us:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;&amp;gt; cordova
Synopsis

    cordova command [options]

Global Commands

    create [path] [id] [name] ........... creates a cordova project in the specified
                                          directory optional name and id (package
                                          name, reverse-domain style)

Project-Level Commands

    platform(s) [add|remove|ls [name]] .. adds or removes a platform, or lists all
                                          currently-added platforms
    plugin(s) [add|remove|ls [path]] .... adds or removes a plugin (from the specified
    	                                  path), or lists all currently-added plugins
    prepare [platform..] ................ copies files into the specified platforms, or
                                          all platforms it is then ready for building
                                          by Eclipse/Xcode/etc
    compile [platform..] ................ builds the app for the specified (or all)
                                          platforms
    build [platform..]................... alias for prepare and then compile
    emulate [platform..] ................ starts emulator for the specified (or all)
                                          platforms, then deploys application to
                                          emulator
    run [platform..] .................... deployes the application to the specified
                                          (or all) platform devices, which must be
                                          connected and configured for running via
                                          your machine
    serve &amp;lt;platform&amp;gt; [port] ............. runs a local web server for the www/
                                          directory of the given platform the default
                                          port is 8000
                                          note that you must edit the native code to
                                          point at the server!
    ripple &amp;lt;platform&amp;gt; [port] ............ uses the serve command as a base and then
                                          wraps the server with ripple to test your
                                          app in your desktop browser
    help ................................ shows this!

Command-line Flags/Options

    -v, --version ....................... prints out this utility&amp;#39;s version
    -d, --verbose ....................... debug, or verbose, mode Makes this utility
                                          very chatty, logging everything it does,
                                          including redirecting output of commands
                                          it shells out to back to stdout

Example usage

    $ cordova create Baz
    $ cd Baz
    $ cordova platform add android
    $ cordova build
    $ cordova serve android
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The help output shows global commands and project-level commands. The only global command is &lt;strong&gt;create&lt;/strong&gt;. Let&amp;#8217;s create a new project in the directory we created above:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; cordova create . com.myproject MyProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This gives us a project tree like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;&amp;gt; tree -I node_modules --charset ascii
.
|-- merges
|-- package.json
|-- platforms
|-- plugins
`-- www
    |-- config.xml
    |-- css
    |   `-- index.css
    |-- img
    |   `-- cordova.png
    |-- index.html
    |-- js
    |   `-- index.js
    |-- res
    |   |-- icon
    |   |   |-- cordova_128.png
    |   |   |-- cordova_16.png
    |   |   |-- cordova_24.png
    |   |   |-- cordova_256.png
    |   |   |-- cordova_32.png
    |   |   |-- cordova_48.png
    |   |   |-- cordova_512.png
    |   |   |-- cordova_64.png
    |   |   |-- cordova_android_36.png
    |   |   |-- cordova_android_48.png
    |   |   |-- cordova_android_72.png
    |   |   |-- cordova_android_96.png
    |   |   |-- cordova_bb_80.png
    |   |   |-- cordova_ios_114.png
    |   |   |-- cordova_ios_144.png
    |   |   |-- cordova_ios_57.png
    |   |   `-- cordova_ios_72.png
    |   `-- screen
    |       |-- android_hdpi_landscape.png
    |       |-- android_hdpi_portrait.png
    |       |-- android_ldpi_landscape.png
    |       |-- android_ldpi_portrait.png
    |       |-- android_mdpi_landscape.png
    |       |-- android_mdpi_portrait.png
    |       |-- android_xhdpi_landscape.png
    |       |-- android_xhdpi_portrait.png
    |       |-- blackberry_transparent_300.png
    |       |-- blackberry_transparent_400.png
    |       |-- ipad_landscape.png
    |       |-- ipad_portrait.png
    |       |-- ipad_retina_landscape.png
    |       |-- ipad_retina_portrait.png
    |       |-- iphone_landscape.png
    |       |-- iphone_portrait.png
    |       |-- iphone_retina_landscape.png
    |       |-- iphone_retina_portrait.png
    |       `-- windows_phone_portrait.jpg
    |-- spec
    |   |-- helper.js
    |   |-- index.js
    |   `-- lib
    |       `-- jasmine-1.2.0
    |           |-- MIT.LICENSE
    |           |-- jasmine-html.js
    |           |-- jasmine.css
    |           `-- jasmine.js
    `-- spec.html

13 directories, 49 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;em&gt;www&lt;/em&gt; folder we get the standard PhoneGap project template, with assets for every major mobile platform. The &lt;em&gt;plugins&lt;/em&gt; folder, naturally, is for &lt;a href='/articles/installing-plugins-with-phonegaps-command-line-interface.html'&gt;plugins&lt;/a&gt;. The &lt;em&gt;platforms&lt;/em&gt; folder is where the project files for each platform will live. The &lt;em&gt;merges&lt;/em&gt; folder is where you keep platform-specific code that will be substituted into each platform&amp;#8217;s build step.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s wrap up by getting our project running in the iOS simulator. First, add the platform:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; cordova platform add ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, build it:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; cordova build ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Build is an alias for &lt;strong&gt;prepare&lt;/strong&gt; and &lt;strong&gt;compile&lt;/strong&gt;. &lt;strong&gt;Prepare&lt;/strong&gt; will copy the source files into the platform-specific directory and apply any applicable merges. &lt;strong&gt;Compile&lt;/strong&gt; builds the platform-specific package. Now launch the iOS simulator:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&amp;gt; cordova emulate ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s it, we&amp;#8217;re up and running!&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s a lot more to this tool, and I&amp;#8217;m something of a nerd when it comes to slick command-line interfaces, so I hope you&amp;#8217;ll stick with me as I return to it from time to time.&lt;/p&gt;</description>
				<pubDate>Tue, 11 Jun 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/the-nodejs-command-line-interface-for-phonegap.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/the-nodejs-command-line-interface-for-phonegap.html</guid>
			</item>
		
			<item>
				<title>Font Smoothing and CSS Transitions</title>
				<description>&lt;p&gt;&lt;a href='/demos/font-smoothing.html'&gt;Try the demo!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On iOS devices and Safari, when you combine 3D transforms with elements containing text, you may notice some strange things happening with font rendering under certain conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text will appear thinner when an element with text is 3D transformed&lt;/strong&gt;, or an element with text is composited with another 3D transformed element. When the 3D transform is removed, the text will become thicker again.&lt;/p&gt;

&lt;p&gt;The resulting flickering text effect is not desirable.&lt;/p&gt;

&lt;p&gt;The font-thinning behavior is most noticeable in high contrast color schemes, like white text on a dark background, though the problem is still present in more traditional black on white color schemes. Below is a screenshot showing normal text on the left and 3D transformed text on the right, or you can &lt;a href='/demos/font-smoothing.html'&gt;see it for yourself&lt;/a&gt;.&lt;/p&gt;

&lt;p class='figure'&gt;&lt;img alt='Font smoothing' src='/img/font-smoothing-small.png' /&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;To make matters interesting, you will not see this behavior in the iOS simulator. More interesting still, on iOS devices, you will only see this problem when the device orientation is horizontal! If you change the orientation of your iOS device from vertical to horizontal, and watch closely, you will notice the fonts become thicker.&lt;/p&gt;

&lt;p&gt;When I first encountered this problem, a little research seemed to indicate setting &lt;code&gt;-webkit-font-smoothing&lt;/code&gt; to &lt;code&gt;antialiased&lt;/code&gt; would fix the problem. It &lt;em&gt;does&lt;/em&gt; fix the problem in Safari, however, it &lt;em&gt;does not&lt;/em&gt; fix the problem on iOS. After yet more research, I found &lt;a href='http://bjango.com/articles/subpixeltext/'&gt;some sites&lt;/a&gt; suggesting that sub-pixel antialiasing is not available on iOS. This makes sense since iOS devices need to work in multiple orientations, and that kind of throws a wrench in the whole sub-pixel rendering thing, but it does not explain why text still appears thicker in horizontal orientation.&lt;/p&gt;

&lt;p&gt;Sadly, I still haven&amp;#8217;t found a good explanation for this behavior. My working theory is that subpixel antialiasing &lt;em&gt;does&lt;/em&gt; work on iOS devices, but only in horizontal orientation because of the pixel orientation. I also think there may be a browser bug that ignores the &lt;code&gt;-webkit-font-smoothing&lt;/code&gt; property on iOS devices. Again, this is just a theory, please don&amp;#8217;t take this as a statement of fact!&lt;/p&gt;

&lt;p&gt;To further complicate things, you&amp;#8217;ll find that disabling sub-pixel antialiasing &lt;a href='http://www.usabilitypost.com/2010/08/26/font-smoothing/'&gt;is controversial&lt;/a&gt;. Pragmatically, however, if you want to use CSS 3D transforms and transitions, disabling sub-pixel antialiasing is really your best option at this point.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;-webkit-font-smoothing&lt;/code&gt; has no effect on iOS, you can eliminate this behavior by &lt;a href='/articles/force-hardware-acceleration-with-translate3d-sometimes.html'&gt;forcing hardware acceleration&lt;/a&gt; with an identity &lt;code&gt;translate3d&lt;/code&gt; on the text element. Remember that GPU memory is limited. If anyone has a better approach for dealing with this issue, I&amp;#8217;d love to hear it!&lt;/p&gt;</description>
				<pubDate>Thu, 06 Jun 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/font-smoothing-and-css-transitions.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/font-smoothing-and-css-transitions.html</guid>
			</item>
		
			<item>
				<title>Nitro JavaScript Engine in iOS PhoneGap Apps</title>
				<description>&lt;p&gt;One advantage to building PhoneGap applications is that you can test your application on a physical device before registering with a developer program. Just host all of your source files on a web server and and point the device&amp;#8217;s web browser at the server.&lt;/p&gt;

&lt;p&gt;$100 per year to enroll in a developer program isn&amp;#8217;t the end of the world, but I&amp;#8217;d rather keep it until I know I have something to release!&lt;/p&gt;

&lt;p&gt;Of course, with this testing method your application will not have access to device functionality exposed through the PhoneGap API, but it is still a good way to rapidly prototype your application&amp;#8217;s user interface.&lt;/p&gt;

&lt;p&gt;On iOS, there is another caveat to this testing method. &lt;strong&gt;Mobile Safari executes JavaScript faster than PhoneGap&amp;#8217;s UIWebView.&lt;/strong&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Mobile Safari&amp;#8217;s JavaScript engine is called JavaScriptCore, and is also referred to as Nitro. Nitro in mobile Safari just-in-time (JIT) compiles JavaScript into native machine code, rather than interpreting it. According to some &lt;a href='http://blog.dave.io/2012/07/chrome-for-ios-is-crap-but-that-might-have-been-the-plan-all-along/'&gt;benchmarks&lt;/a&gt;, JIT compilation in Nitro can improve the speed of JavaScript execution by 200% to 300%.&lt;/p&gt;

&lt;p&gt;As of iOS 6, JIT compilation is only enabled in mobile Safari. JavaScript executing in a UIWebView will not be JIT compiled.&lt;/p&gt;

&lt;p&gt;While this may sound like a crippling issue for iOS PhoneGap applications, it usually isn&amp;#8217;t much of a problem. &lt;strong&gt;JIT compilation only speeds up JavaScript execution&lt;/strong&gt;, it has no effect on layout and rendering, including hardware accelerated rendering.&lt;/p&gt;

&lt;p&gt;If your application performs intensive computations in JavaScript, you can use the old &lt;a href='http://www.nczonline.net/blog/2009/01/13/speed-up-your-javascript-part-1/'&gt;setTimeout trick&lt;/a&gt;, or possibly web workers, as a way to keep the UI thread responsive. Even &lt;em&gt;with&lt;/em&gt; JIT compilation, using these techniques to keep the UI thread responsive is considered best practice.&lt;/p&gt;

&lt;p&gt;You should test how (the lack of) JIT compilation affects your application&amp;#8217;s performance. The most obvious way to do this is to deploy your application to a physical iOS device. There is another way that will let you delay enrolling in the iOS developer program a little longer. &lt;a href='https://itunes.apple.com/us/app/chrome/id535886823'&gt;Install Google Chrome for iOS&lt;/a&gt;. Google Chrome for iOS uses the same UIWebView component that your PhoneGap application will use, and therefore is similarly stunted when it comes to JIT compilation.&lt;/p&gt;</description>
				<pubDate>Thu, 30 May 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/nitro-javascript-engine-in-ios-phonegap-apps.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/nitro-javascript-engine-in-ios-phonegap-apps.html</guid>
			</item>
		
			<item>
				<title>Force Hardware Acceleration with translate3d... Sometimes</title>
				<description>&lt;p&gt;&lt;a href='/demos/force-hardware-acceleration-with-translate3d.html'&gt;Try the demo!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last PhoneGap application that I developed had a feature that allowed you to drag an image around the screen so you could drop it in a folder or a trash can&amp;#8230; you get the idea. The images were relatively large. Each image took up most of the display area of an iPad. The dragging animation worked by dynamically updating the &lt;code&gt;-webkit-transform&lt;/code&gt; property with a &lt;code&gt;translate3d&lt;/code&gt; value that followed a touch around the screen.&lt;/p&gt;

&lt;p&gt;While testing the application I noticed that the image appeared to &amp;#8220;stick&amp;#8221; when a drag operation started. I could drag a short distance from the starting point of the pan gesture before the image snapped to the right location. When I dropped the image and started dragging again, I experienced the same stickiness. This was most noticeable on a first generation iPad (remember, always &lt;a href='/articles/performance-testing-ios-phonegap-apps.html'&gt;test on old hardware&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I found that by applying an identity &lt;code&gt;translate3d&lt;/code&gt; to the image element in CSS, there was no delay at the start of a drag operation. For example:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='css'&gt;&lt;span class='nc'&gt;.draggable-image&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;webkit&lt;/span&gt;&lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;transform&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='n'&gt;translate3d&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='m'&gt;0px&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt;&lt;span class='m'&gt;0px&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt;&lt;span class='m'&gt;0px&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Why does this work? And what are the performance implications?&lt;/strong&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;This &lt;a href='http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome'&gt;article&lt;/a&gt; explains the hardware accelerated rendering path in Chrome in a lot of detail. The web view powering an iOS or Android PhoneGap application may have a slightly different approach to hardware accelerated rendering, but hopefully it is close enough!&lt;/p&gt;

&lt;p&gt;Allow me to grossly oversimplify the hardware accelerated rendering path for a moment&amp;#8230;&lt;/p&gt;

&lt;p&gt;When an element is rendered in hardware, the element and all of its children are first rendered to a bitmap, and then the bitmap is uploaded to the GPU as a texture. Once the texture is uploaded to the GPU, the element can be transformed very quickly. Whenever the element or its children need to be redrawn, the element must be re-rendered to a bitmap and uploaded to the GPU again.&lt;/p&gt;

&lt;p&gt;It stands to reason that larger or more complex elements will take longer to render to a bitmap and subsequently upload to the GPU. To avoid this performance penalty, you can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;make elements smaller or less complex&lt;/strong&gt;, or&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;force elements into hardware acceleration as early as possible&lt;/strong&gt;, and ensure they stay hardware accelerated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the latter is your only option, the simplest way to do this is to put an identity &lt;code&gt;translate3d&lt;/code&gt; on the element. The tradeoff is that the element&amp;#8217;s texture remains in VRAM for longer, and VRAM is limited on mobile devices. In my case, a smoother dragging transition was worth the cost of some extra VRAM.&lt;/p&gt;

&lt;p&gt;One quick way to &lt;a href='/demos/force-hardware-acceleration-with-translate3d.html'&gt;see this in action&lt;/a&gt; is to exceed the maximum texture size for a mobile device. When an element larger than the maximum texture size switches to hardware acceleration, you will see an ugly flicker and a sort of &amp;#8220;blocking in&amp;#8221; effect as the texture tiles comprising the element make their way to the GPU. By applying an identity &lt;code&gt;translate3d&lt;/code&gt; in CSS, this ugly flicker will only happen once, provided the element does not change later on.&lt;/p&gt;

&lt;p&gt;With smaller, less complex elements, you will see no noticeable performance penalty when switching between software and hardware rendering, so saving VRAM may be the better option. As with any piece of application performance advice, test it for yourself before blindly applying a rule!&lt;/p&gt;</description>
				<pubDate>Fri, 24 May 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/force-hardware-acceleration-with-translate3d-sometimes.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/force-hardware-acceleration-with-translate3d-sometimes.html</guid>
			</item>
		
			<item>
				<title>Image Replacement Without Flickering 3D Transforms</title>
				<description>&lt;p&gt;&lt;a href='/demos/image-replacement.html'&gt;Try the demo!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Image replacement techniques improve a web page&amp;#8217;s accessibility by allowing you to write nice semantic HTML and progressively enhance it with CSS. If you are writing a PhoneGap application, you may not care about the accessibility and &amp;#8220;semanticness&amp;#8221; of your HTML, but if you do, read on!&lt;/p&gt;

&lt;p&gt;The idea behind image replacement is to use CSS to hide the text of an HTML element and instead show a background image. This improves accessibility by keeping the text available to screen readers, while also rendering pretty pictures in web browsers.&lt;/p&gt;

&lt;p&gt;A standard technique for image replacement that is still quite prevalent works by setting a large, negative &lt;code&gt;text-indent&lt;/code&gt;. For example suppose you have this anchor tag in your HTML:&lt;/p&gt;
&lt;!-- more --&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='nt'&gt;&amp;lt;a&lt;/span&gt; &lt;span class='na'&gt;class=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;html5logo&amp;quot;&lt;/span&gt;
  &lt;span class='na'&gt;href=&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;javascript:void(0);&amp;quot;&lt;/span&gt;&lt;span class='nt'&gt;&amp;gt;&lt;/span&gt;HTML5&lt;span class='nt'&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is how you can replace the anchor&amp;#8217;s text with a background image using CSS:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='css'&gt;&lt;span class='nc'&gt;.html5logo&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;display&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='k'&gt;block&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;width&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;128px&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;height&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;128px&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;background&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='sx'&gt;url(/img/html5-badge-128.png)&lt;/span&gt; &lt;span class='k'&gt;no-repeat&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;text-indent&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;-9999px&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This hides the element&amp;#8217;s text far off-screen to the left. Provided the element does not have &lt;strong&gt;a lot&lt;/strong&gt; of text, causing the text to reach back into the element, this will work fine. However, when you combine this technique with hardware accelerated 3D transforms, transitions, or animations on a mobile device, you will notice an ugly flicker whenever a 3D transform on the element is initiated.&lt;/p&gt;

&lt;p&gt;The issue is that the large indent effectively makes the entire element 10,000 pixels wide, and GPUs have a maximum texture size that is generally far less than 10,000 pixels. Fortunately, rather than unceremoniously crashing your application, WebKit will break the element up into tiles before uploading the texture to the GPU. This process is not cheap, and so you see that ugly flicker.&lt;/p&gt;

&lt;p&gt;So clearly you need an image replacement technique that keeps texture size below the device maximum. A good solution can be &lt;a href='http://www.zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement/'&gt;found here&lt;/a&gt;. The approach is similar, but rather than a large indent to the left, you indent to the right by the width of the element and clip the element&amp;#8217;s content. For example:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='css'&gt;&lt;span class='nc'&gt;.html5logo&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;display&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='k'&gt;block&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;width&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;128px&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;height&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;128px&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;background&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='sx'&gt;url(/img/html5-badge-128.png)&lt;/span&gt; &lt;span class='k'&gt;no-repeat&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;text-indent&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='m'&gt;100&lt;/span&gt;&lt;span class='o'&gt;%&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;overflow&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='k'&gt;hidden&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='k'&gt;white-space&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='k'&gt;nowrap&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when you initiate a 3D transform on the element there will be no flicker! &lt;a href='/demos/image-replacement.html'&gt;Try it out and see for yourself!&lt;/a&gt;&lt;/p&gt;</description>
				<pubDate>Tue, 21 May 2013 00:00:00 -0400</pubDate>
				<link>http://phonegap-tips.com/articles/image-replacement-without-flickering-3d-transforms.html</link>
				<guid isPermaLink="true">http://phonegap-tips.com/articles/image-replacement-without-flickering-3d-transforms.html</guid>
			</item>
		
	</channel>
</rss>
