{"id":73,"date":"2013-06-02T15:43:43","date_gmt":"2013-06-02T15:43:43","guid":{"rendered":"http:\/\/thomasgr.im\/shares\/?p=73"},"modified":"2013-08-29T14:16:00","modified_gmt":"2013-08-29T14:16:00","slug":"making-app-with-phonegap-jqm-part-2-loading-javascript-properly","status":"publish","type":"post","link":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/","title":{"rendered":"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly"},"content":{"rendered":"<p>If you missed <a href=\"http:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-1-simple-html5-skeleton\/\" title=\"Making An App With PhoneGap &#038; jQm Part I: A Simple HTML5 Skeleton\">Part I: A Simple HTML5 Skeleton<\/a>, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part.<\/p>\n<p>One of the challenges in developing apps using PhoneGap and jQuery.Mobile is to make sure that the libraries are properly loaded before any critical code is executed.&nbsp;Another challenge is to make the difference between code which is due to execute only once, code which will execute on every activity, etc.&nbsp;In this part we&#8217;ll explore those differences and I&#8217;ll present to you a way of making sure that your functions execute at the right time.<\/p>\n<h2>Namespacing The Application&#8217;s JavaScript<\/h2>\n<p>Namespacing your functions into an object is a great way of avoiding potential conflicts with existing functions; it also serves as an additional separation of your JS code from your HTML, it can make it easier to debug errors by keeping related entities close to each other.&nbsp;It may also make the use of a test framework like <a href=\"http:\/\/qunitjs.com\/\" title=\"JavaScript Test Framework\" target=\"_blank\">QUnit<\/a> easier to implement.<\/p>\n<p>Here is the base skeleton we&#8217;ll use for our app:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">var App = {\r\n\t&quot;app_loaded&quot;: false,\r\n\t&quot;testing_on_desktop&quot;: true,\r\n\r\n\t&quot;init&quot;: function () {\r\n\t},\r\n\r\n\t&quot;utilities&quot;: {\r\n\t},\r\n};<\/pre>\n<p>A quick note before we go deeper: you can make use of the object notation in JavaScript to nest multiple namespaces, like I did with &#8220;utilities&#8221;.&nbsp;This is a pretty cool feature to make your code a bit more organized than putting everything at the same level.<\/p>\n<h2>Setting Up jQuery.Mobile In &lt;head&gt;<\/h2>\n<p>As we&#8217;ve seen in <a href=\"http:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-1-simple-html5-skeleton\/\" title=\"Making An App With PhoneGap &#038; jQm Part I: A Simple HTML5 Skeleton\">Part 1<\/a>, jQuery.Mobile is a bit special in the sense that it modifies the <abbr title=\"Document Object Model\">DOM<\/abbr> upon loading.&nbsp;For this reason, any configuration code we&#8217;d like to see executed must be set <em>before<\/em> the library is loaded, using the &#8220;<em>mobileinit<\/em>&#8221; event which marks the beginning of jQuery.Mobile&#8217;s execution.<\/p>\n<p>As an example of what you can configure, as my app needs to &#8220;phone home&#8221; I had to activate the cors option in both jQuery and jQuery.Mobile.&nbsp;The full list of configuration variables is available <a href=\"http:\/\/jquerymobile.com\/demos\/1.2.0\/docs\/api\/globalconfig.html\" target=\"_blank\">here<\/a>.&nbsp;Here&#8217;s the result in &lt;head&gt;:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\t&lt;script charset=&quot;utf-8&quot; src=&quot;js\/app.js&quot;&gt;&lt;\/script&gt;\r\n\t&lt;script&gt;\r\n\t\tjQuery(document).bind(&quot;mobileinit&quot;, function () {\r\n\t\t\tconsole.log(&quot;configuring jqm...&quot;);\r\n\r\n\t\t\t\/\/ required for &quot;phoning home&quot; (load external assets, etc.)\r\n\t\t\tjQuery.support.cors = true;\r\n\t\t\tjQuery.mobile.allowCrossDomainPages = true;\r\n\t\t\t\r\n\t\t\tjQuery.mobile.phonegapNavigationEnabled = true;\r\n\t\t\tjQuery.mobile.defaultDialogTransition = &quot;pop&quot;;\r\n\t\t\tjQuery.mobile.defaultPageTransition = &quot;none&quot;;\r\n\t\t\t\r\n\t\t\tjQuery.mobile.loader.prototype.options.text = &quot;loading&quot;;\r\n\t\t\tjQuery.mobile.loader.prototype.options.textVisible = true;\r\n\t\t\tjQuery.mobile.loader.prototype.options.theme = &quot;a&quot;;\r\n\t\t});\r\n\t\tApp.init();\r\n\t&lt;\/script&gt;\r\n\t&lt;script charset=&quot;utf-8&quot; src=&quot;js\/jquery.mobile-1.3.1.min.js&quot;&gt;&lt;\/script&gt;<\/pre>\n<p>I advise you to <strong>systematically<\/strong> add <kbd>console.log(\"[funcName]\")<\/kbd> at the beginning of every function as it makes debugging much much easier, especially when callbacks are involved because they can be rather messy in the way\/order they fire.&nbsp;Oh, notice the <kbd>App.init()<\/kbd> call?&nbsp;Let&#8217;s see what we can put in there.<\/p>\n<p><!--more--><\/p>\n<h2>Setting Up Our App In App.init<\/h2>\n<p>Now that we&#8217;ve set up jQuery.Mobile, let&#8217;s list the different functions\/actions which need to be performed and when they are to be executed:<\/p>\n<dl>\n<dt>When the application launches<\/dt>\n<dd>\n<ul>\n<li>Determine whether we&#8217;re on a desktop browser or on a smartphone<\/li>\n<li>Hide the application&#8217;s splashscreen<\/li>\n<\/ul>\n<\/dd>\n<dt>When any activity (or &#8220;screen&#8221;, &#8220;page&#8221;, whatever) launches<\/dt>\n<dd>\n<ul>\n<li>Make sure the device is connected to the Internet<\/li>\n<li>Determine whether the user is logged in or not<\/li>\n<li>If not, determine whether the user is logging in\/registering<\/li>\n<li>If not, show the login form<\/li>\n<\/ul>\n<\/dd>\n<dt>When a specific activity launches<\/dt>\n<dd>\n<ul>\n<li>Set up callbacks on the activity&#8217;s forms<\/li>\n<li>Fetch the activity-specific data<\/li>\n<li>Execute other activity-specific JS code<\/li>\n<\/ul>\n<\/dd>\n<\/ul>\n<p>As you can see, there are three distinct levels of code execution we have to handle.&nbsp;On top of that, we have to make sure that both PhoneGap and jQuery.Mobile are properly loaded before executing some critical code&#8230; a real challenge!<\/p>\n<p>Let&#8217;s do this slowly.&nbsp;Take a look at the following <kbd>App.init()<\/kbd> snippet:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\t&quot;init&quot;: function () {\r\n\t\tconsole.log(&quot;&#x5B;init]&quot;);\r\n\r\n\t\tif (document.URL.indexOf(&quot;http:\/\/&quot;) === -1) {\r\n\t\t\tApp.testing_on_desktop = false;\r\n\t\t}\r\n\r\n\t\tjQuery(document).ready(function () {\r\n\t\t\tconsole.log(&quot;jQuery finished loading&quot;);\r\n\r\n\t\t\tif (App.testing_on_desktop) {\r\n\t\t\t\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t\t\t\t_onDeviceReady();\r\n\t\t\t} else {\r\n\t\t\t\tdocument.addEventListener(&quot;deviceReady&quot;, function () {\r\n\t\t\t\t\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t\t\t\t\t_onDeviceReady();\r\n\t\t\t\t}, false);\r\n\t\t\t}\r\n\r\n\t\t\tjQuery(document).one(&quot;pageinit&quot;, function () {\r\n\t\t\t\tconsole.log(&quot;jQuery.Mobile finished loading&quot;);\r\n\t\t\t});\r\n\r\n\t\t\tconsole.log(&quot;PhoneGap &amp; jQuery.Mobile finished loading&quot;);\r\n\t\t\tinitPages();\r\n\t\t\tconsole.log(&quot;App finished loading&quot;);\r\n\t\t\tApp.app_loaded = true;\r\n\t\t});\r\n\r\n\t\tfunction _onDeviceReady () {\r\n\t\t};\r\n\t\tfunction initPages () {\r\n\t\t};\r\n\t},\r\n<\/pre>\n<p>Phew, that&#8217;s a lot of code! Let&#8217;s break it down.<\/p>\n<p>First, note that we&#8217;re calling <kbd>App.init()<\/kbd> from <kbd>&lt;head&gt;<\/kbd> which means that the code present in it will only be executed once per launch.&nbsp;However, <strong>we can&#8217;t be sure that any of the libraries have loaded yet<\/strong>!<\/p>\n<p>Let&#8217;s immediately determine whether we&#8217;re in a desktop browser or in the smartphone&#8217;s browser engine.&nbsp;This is using a small trick, but it&#8217;s really simple: when you&#8217;re executing in PhoneGap (emulator &#038; real device), the <em>index.html<\/em> file gets loaded <strong>from the filesystem<\/strong> and not via a server as would be the case on a desktop browser.&nbsp;This means that the URL won&#8217;t begin with &#8220;http:\/\/&#8221; on a smartphone, which is what we check.<\/p>\n<p>The call to <kbd>jQuery(document).ready()<\/kbd> allows us to be sure that jQuery has loaded completely at this point, so we&#8217;ll put the remainder of our logic in this callback.<\/p>\n<p>Then we have:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">if (App.testing_on_desktop) {\r\n\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t_onDeviceReady();\r\n} else {\r\n\tdocument.addEventListener(&quot;deviceReady&quot;, function () {\r\n\t\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t\t_onDeviceReady();\r\n\t}, false);\r\n}<\/pre>\n<p>This is making use of another trick to handle the possibility that we&#8217;re on a desktop browser.&nbsp;When PhoneGap has loaded and established a link between JavaScript and the smartphone, it fires a <em>deviceReady<\/em> event.&nbsp;However, when you&#8217;re testing your app on Chrome, this link can obviously never be established hence <em>deviceReady<\/em> never gets fired!&nbsp;This snippet takes care of that by firing our callback immediately when we&#8217;re on a desktop browser, and only upon linking otherwise.<\/p>\n<p>The next part uses the &#8220;<em>pageinit<\/em>&#8221; event to make sure that jQuery.Mobile has loaded.&nbsp;Note that we can&#8217;t use &#8220;<em>mobileinit<\/em>&#8221; because the event gets fired <strong>before<\/strong> the lib has fully loaded.<\/p>\n<p>Finally, we have some code firing when everything is ready, calling <kbd>initPages()<\/kbd> to set up the code executing on every activity and setting <kbd>App.app_loaded<\/kbd> to true.<\/p>\n<p>At this point, it seems that everything is going fine, but <strong>let&#8217;s take a step back and think<\/strong> about it for a minute.&nbsp;Do you see a problem with the code I&#8217;ve shown you so far?&nbsp;I do!<\/p>\n<p>Here&#8217;s the thing: the last four lines of code require PhoneGap, jQuery and jQuery.Mobile to be fully working before they are reached, right?&nbsp;The problem is that we can&#8217;t be sure that both deviceReady and pageinit have fired by the time we reach those lines, because those events are firing callbacks and our last four lines will be executed sequentially upon setting up the callbacks, not upon executing them.&nbsp;This can get pretty confusing, don&#8217;t hesitate to read the last paragraph again if this isn&#8217;t clear to you.<\/p>\n<p>So what we need is a way to say: &#8220;<em>hey, I want to execute function <kbd>C<\/kbd> when and only when functions <kbd>A<\/kbd> and <kbd>B<\/kbd> have executed<\/em>&#8220;.&nbsp;Fortunately, jQuery provides us with a way to do just that: <a href=\"http:\/\/api.jquery.com\/jQuery.Deferred\/\" target=\"_blank\"><kbd>jQuery.Deferred()<\/kbd><\/a>.&nbsp;Here&#8217;s how it works: we&#8217;ll define two variables <kbd>deviceReadyDeferred<\/kbd> and <kbd>jqmReadyDeferred<\/kbd> containing special objects called Deferred objects.&nbsp;Those objects are considered in a &#8220;<em>pending<\/em>&#8221; state.&nbsp;In each of the two callbacks on &#8220;<em>deviceReady<\/em>&#8221; and &#8220;<em>pageinit<\/em>&#8220;, we&#8217;ll set the corresponding object&#8217;s state to &#8220;<em>resolved<\/em>&#8221; using calls like <kbd>deviceReadyDeferred.resolve()<\/kbd>.&nbsp;Last but not least, we&#8217;ll use a special callback that will fire only when both objects have resolved, ensuring that both PhoneGap and jQuery.Mobile have loaded completely.&nbsp;Here&#8217;s the modified code:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\t\tjQuery(document).ready(function () {\r\n\t\t\tconsole.log(&quot;jQuery finished loading&quot;);\r\n\r\n\t\t\tvar deviceReadyDeferred = jQuery.Deferred();\r\n\t\t\tvar jqmReadyDeferred    = jQuery.Deferred();\r\n\t\t\tif (App.testing_on_desktop) {\r\n\t\t\t\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t\t\t\t_onDeviceReady();\r\n\t\t\t\tdeviceReadyDeferred.resolve();\r\n\t\t\t} else {\r\n\t\t\t\tdocument.addEventListener(&quot;deviceReady&quot;, function () {\r\n\t\t\t\t\tconsole.log(&quot;PhoneGap finished loading&quot;);\r\n\t\t\t\t\t_onDeviceReady();\r\n\t\t\t\t\tdeviceReadyDeferred.resolve();\r\n\t\t\t\t}, false);\r\n\t\t\t}\r\n\r\n\t\t\tjQuery(document).one(&quot;pageinit&quot;, function () {\r\n\t\t\t\tconsole.log(&quot;jQuery.Mobile finished loading&quot;);\r\n\t\t\t\tjqmReadyDeferred.resolve();\r\n\t\t\t});\r\n\r\n\t\t\tjQuery.when(deviceReadyDeferred, jqmReadyDeferred).then(function () {\r\n\t\t\t\tconsole.log(&quot;PhoneGap &amp; jQuery.Mobile finished loading&quot;);\r\n\t\t\t\tinitPages();\r\n\t\t\t\tconsole.log(&quot;App finished loading&quot;);\r\n\t\t\t\tApp.app_loaded = true;\r\n\t\t\t});\r\n\t\t});<\/pre>\n<p>Now if you load your page in a desktop browser and open the console, you should see all the <kbd>console.log()<\/kbd> calls we&#8217;ve places ocurring in the right order:<\/p>\n<ul>\n<li>[initApp]<\/li>\n<li>configuring jqm&#8230;<\/li>\n<li>jQuery finished loading<\/li>\n<li>PhoneGap finished loading<\/li>\n<li>[onDeviceReady]<\/li>\n<li>jQueryMobile finished loading<\/li>\n<li>PhoneGap &#038; jQueryMobile finished loading<\/li>\n<li>[initPages]<\/li>\n<li>App finished loading<\/li>\n<\/ul>\n<p>Pretty cool, isn&#8217;t it?&nbsp;Now all we have to do is add some code in <kbd>App.init()<\/kbd>:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\tfunction _onDeviceReady () {\r\n\t\tPGproxy.navigator.splashscreen.hide();\r\n\t};\r\n\tfunction initPages&quot;: function () {\r\n\t\tconsole.log(&quot;&#x5B;initPages]&quot;);\r\n\t\tjQuery(document).bind(&quot;pageinit&quot;, _initPages);\r\n\t\t\r\n\t\tfunction _initPages () {\r\n\t\t};\r\n\t};\r\n<\/pre>\n<p>Our <kbd>initPages()<\/kbd> sets up a callback which will be fired on every page load.&nbsp;This callback will contain code to check the login\/registration status and maybe show logged out users a login box.&nbsp;As for <kbd>onDeviceReady()<\/kbd>, it hides the application&#8217;s splashscreen.&nbsp;Now you may wonder where this PGproxy object comes from.&nbsp;I&#8217;ll explain, don&#8217;t worry.<\/p>\n<h2>Introducing PhoneGap Proxy &#8220;PGproxy&#8221;<\/h2>\n<p>One of the challenges I faced whan I began coding was that the smartphone native functions, which are made available to JavaScript by PhoneGap, weren&#8217;t available when testing the app in Chrome (obviously).&nbsp;Now there are ways to avoid errors in Cordova by testing systematically that the functions exist before executing them, like this:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">if (navigator.notification &amp;&amp; navigator.notification.vibrate) {\r\n\tnavigator.notification.vibrate(a);\r\n} else {\r\n\tconsole.log(&quot;navigator.notification.vibrate&quot;);\r\n}<\/pre>\n<p>&#8230;but as you can see it&#8217;s kind of polluting our space with lines of code.&nbsp;To avoid this or at least hide the checks away from my functions, I added a small mock object called &#8220;PGproxy&#8221;, which handles both cases (testing on Chrome or using from emulator\/app) silently:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\/\/ emulate PhoneGap for testing on Chrome\r\nvar PGproxy = {\r\n\t&quot;navigator&quot;: {\r\n\t\t&quot;connection&quot;: function () {\r\n\t\t\tif (navigator.connection) {\r\n\t\t\t\treturn navigator.connection;\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log('navigator.connection');\r\n\t\t\t\treturn {\r\n\t\t\t\t\t&quot;type&quot;:&quot;WIFI&quot; \/\/ Avoids errors on Chrome\r\n\t\t\t\t};\r\n\t\t\t}\r\n\t\t},\r\n\t\t&quot;notification&quot;: {\r\n\t\t\t&quot;vibrate&quot;: function (a) {\r\n\t\t\t\tif (navigator.notification &amp;&amp; navigator.notification.vibrate) {\r\n\t\t\t\t\tnavigator.notification.vibrate(a);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.log(&quot;navigator.notification.vibrate&quot;);\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\t&quot;alert&quot;: function (a, b, c, d) {\r\n\t\t\t\tif (navigator.notification &amp;&amp; navigator.notification.alert) {\r\n\t\t\t\t\tnavigator.notification.alert(a, b, c, d);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.log(&quot;navigator.notification.alert&quot;);\r\n\t\t\t\t\talert(a);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\t\t&quot;splashscreen&quot;: {\r\n\t\t\t&quot;hide&quot;: function () {\r\n\t\t\t\tif (navigator.splashscreen) {\r\n\t\t\t\t\tnavigator.splashscreen.hide();\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.log('navigator.splashscreen.hide');\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};<\/pre>\n<p>Here is how it works: for each PhoneGap <abbr title=\"Application Programming Interface\">API<\/abbr> function you use in your app, mock it once in PGproxy using the same kind of checks I used in the example code snippet.&nbsp;All you then have to do for using <kbd>navigator.notification.alert()<\/kbd> is to prefix it with <kbd>PGproxy<\/kbd>.&nbsp;If we&#8217;re testing on Chrome and the Cordova library isn&#8217;t available, then the proxy will fall back to native functions when possible (say for alerts) and fail silently with a <kbd>console.log()<\/kbd> trace otherwise.&nbsp;Usage example:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">&quot;myfunction&quot;: function () {\r\n\t\/\/ code\r\n\tPGproxy.navigator.notification.alert(&quot;message&quot;, callback, &quot;title&quot;, &quot;buttons&quot;);\r\n\t\/\/ code\r\n}<\/pre>\n<h2>Wrapping Up With Activity-Specific Code<\/h2>\n<p>Now we&#8217;ve successfully coded a way to execute our code at two levels out of three, the remaining one being on specific activities.&nbsp;To accomplish this last requirement, we&#8217;ll use a &#8220;<em>pageshow<\/em>&#8221; event which we&#8217;ll attach to those specific activities, directly in <kbd>index.html<\/kbd>:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\t&lt;div data-role=&quot;page&quot; id=&quot;ActivityFoo&quot;&gt;\r\n\t\t&lt;div data-role=&quot;header&quot;&gt;\r\n\t\t\t&lt;h1&gt;Header 1&lt;\/h1&gt;\r\n\t\t\t&lt;div class=&quot;topmenu&quot; data-role=&quot;controlgroup&quot; data-type=&quot;horizontal&quot;&gt;\r\n\t\t\t\t&lt;a class=&quot;current-page-item&quot; href=&quot;index.html#ActivityFoo&quot; title=&quot;Foo&quot; data-role=&quot;button&quot;&gt;&lt;i class=&quot;fontawesome icon-home&quot;&gt;&lt;\/i&gt;&lt;\/a&gt;\r\n\t\t\t\t&lt;a href=&quot;index.html#ActivityBar&quot; title=&quot;Bar&quot; data-role=&quot;button&quot;&gt;&lt;i class=&quot;fontawesome icon-ticket&quot;&gt;&lt;\/i&gt;&lt;\/a&gt;\r\n\t\t\t&lt;\/div&gt;\r\n\t\t&lt;\/div&gt;&lt;!-- \/header --&gt;\r\n\t\t&lt;div data-role=&quot;content&quot;&gt;\r\n\t\t\t&lt;h1&gt;Content 1&lt;\/h1&gt;\r\n\t\t&lt;\/div&gt;&lt;!-- \/content --&gt;\r\n\t\t&lt;div data-role=&quot;footer&quot;&gt;\r\n\t\t\t&lt;h1&gt;Footer 1&lt;\/h1&gt;\r\n\t\t&lt;\/div&gt;&lt;!-- \/footer --&gt;\r\n\t\t&lt;script&gt;\r\n\t\t\tjQuery(&quot;#ActivityFoo&quot;).on(&quot;pageshow&quot;, function(e) {\r\n\t\t\t\t\/\/ foo\r\n\t\t\t});\r\n\t\t&lt;\/script&gt;\r\n\t&lt;\/div&gt;<\/pre>\n<p>I know this isn&#8217;t necessarily the most beautiful way of doing things, but:<\/p>\n<ul>\n<li>It&#8217;s dead simple<\/li>\n<li>It works wonderfully<\/li>\n<li>It&#8217;s really flexible: you can add an activity and its specific code right away<\/li>\n<li>It&#8217;s clean enough in the sense that you can always put the callback away and simply point to it from <kbd>index.html<\/kbd><\/li>\n<\/ul>\n<p>As far as I&#8217;m concerned, all those advantages far outweigh the fact that we&#8217;ve been obliged to put javascript in our document.&nbsp;If you have a better way to do such a thing, please tell me in the comments section!<\/p>\n<p>All right, we&#8217;re finished for now.&nbsp;The next step is <a href=\"http:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-3-stateless-authentication-layer\/\">Part III<\/a> of this tutorial.&nbsp;<strong>UPDATE:<\/strong> It&#8217;s online! I&#8217;ll talk about authentication, which is a must-have is many mobile applications.&nbsp;In the meantime, <strong>happy coding<\/strong>!<\/p>\n<p><strong>Changes<\/strong>:<\/p>\n<ul>\n<li><ins date=\"2013-08\"><strong>2013-08<\/strong>: Changed how callbacks are handled to make the code clearer.<\/ins><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>If you missed Part I: A Simple HTML5 Skeleton, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. One of the challenges in developing apps using PhoneGap and jQuery.Mobile is to make sure that the libraries are &#8230;<a class=\"post-readmore\" href=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/\">read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":72,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[10,2],"tags":[12,13,14,17,15,11,16],"class_list":["post-73","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mobile","category-programming","tag-javascript","tag-jquery","tag-jquery-mobile","tag-js","tag-oop","tag-phonegap","tag-tutorial"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Making An App With PhoneGap &amp; jQm Part II: Loading JavaScript Properly - ThomasGR.IM<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Making An App With PhoneGap &amp; jQm Part II: Loading JavaScript Properly - ThomasGR.IM\" \/>\n<meta property=\"og:description\" content=\"If you missed Part I: A Simple HTML5 Skeleton, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. One of the challenges in developing apps using PhoneGap and jQuery.Mobile is to make sure that the libraries are ...read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/\" \/>\n<meta property=\"og:site_name\" content=\"ThomasGR.IM\" \/>\n<meta property=\"article:published_time\" content=\"2013-06-02T15:43:43+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2013-08-29T14:16:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Thomas\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/\"},\"author\":{\"name\":\"Thomas\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\"},\"headline\":\"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly\",\"datePublished\":\"2013-06-02T15:43:43+00:00\",\"dateModified\":\"2013-08-29T14:16:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/\"},\"wordCount\":2470,\"commentCount\":10,\"image\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/wp-content\\\/uploads\\\/sites\\\/3\\\/2013\\\/06\\\/PhoneGap.png\",\"keywords\":[\"JavaScript\",\"jQuery\",\"jQuery.Mobile\",\"JS\",\"OOP\",\"PhoneGap\",\"Tutorial\"],\"articleSection\":[\"Mobile\",\"Programming\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/\",\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/\",\"name\":\"Making An App With PhoneGap & jQm Part II: Loading JavaScript Properly - ThomasGR.IM\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/wp-content\\\/uploads\\\/sites\\\/3\\\/2013\\\/06\\\/PhoneGap.png\",\"datePublished\":\"2013-06-02T15:43:43+00:00\",\"dateModified\":\"2013-08-29T14:16:00+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#primaryimage\",\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/wp-content\\\/uploads\\\/sites\\\/3\\\/2013\\\/06\\\/PhoneGap.png\",\"contentUrl\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/wp-content\\\/uploads\\\/sites\\\/3\\\/2013\\\/06\\\/PhoneGap.png\",\"width\":512,\"height\":512,\"caption\":\"PhoneGap - www.phonegap.com\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/2013\\\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#website\",\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/\",\"name\":\"ThomasGR.IM\",\"description\":\"Geeky Musing\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/#\\\/schema\\\/person\\\/a9f452898a055ceb0f17967802c852da\",\"name\":\"Thomas\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g\",\"caption\":\"Thomas\"},\"sameAs\":[\"https:\\\/\\\/thomasgr.im\\\/\"],\"url\":\"https:\\\/\\\/thomasgr.im\\\/shares\\\/author\\\/thomadmin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Making An App With PhoneGap & jQm Part II: Loading JavaScript Properly - ThomasGR.IM","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/","og_locale":"en_US","og_type":"article","og_title":"Making An App With PhoneGap & jQm Part II: Loading JavaScript Properly - ThomasGR.IM","og_description":"If you missed Part I: A Simple HTML5 Skeleton, you may want to go there and grab the code we wrote so far so that you can follow more easily what we&#8217;ll do in this part. One of the challenges in developing apps using PhoneGap and jQuery.Mobile is to make sure that the libraries are ...read more","og_url":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/","og_site_name":"ThomasGR.IM","article_published_time":"2013-06-02T15:43:43+00:00","article_modified_time":"2013-08-29T14:16:00+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png","type":"image\/png"}],"author":"Thomas","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#article","isPartOf":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/"},"author":{"name":"Thomas","@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da"},"headline":"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly","datePublished":"2013-06-02T15:43:43+00:00","dateModified":"2013-08-29T14:16:00+00:00","mainEntityOfPage":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/"},"wordCount":2470,"commentCount":10,"image":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#primaryimage"},"thumbnailUrl":"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png","keywords":["JavaScript","jQuery","jQuery.Mobile","JS","OOP","PhoneGap","Tutorial"],"articleSection":["Mobile","Programming"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/","url":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/","name":"Making An App With PhoneGap & jQm Part II: Loading JavaScript Properly - ThomasGR.IM","isPartOf":{"@id":"https:\/\/thomasgr.im\/shares\/#website"},"primaryImageOfPage":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#primaryimage"},"image":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#primaryimage"},"thumbnailUrl":"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png","datePublished":"2013-06-02T15:43:43+00:00","dateModified":"2013-08-29T14:16:00+00:00","author":{"@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da"},"breadcrumb":{"@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#primaryimage","url":"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png","contentUrl":"https:\/\/thomasgr.im\/shares\/wp-content\/uploads\/sites\/3\/2013\/06\/PhoneGap.png","width":512,"height":512,"caption":"PhoneGap - www.phonegap.com"},{"@type":"BreadcrumbList","@id":"https:\/\/thomasgr.im\/shares\/2013\/making-app-with-phonegap-jqm-part-2-loading-javascript-properly\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/thomasgr.im\/shares\/"},{"@type":"ListItem","position":2,"name":"Making An App With PhoneGap &#038; jQm Part II: Loading JavaScript Properly"}]},{"@type":"WebSite","@id":"https:\/\/thomasgr.im\/shares\/#website","url":"https:\/\/thomasgr.im\/shares\/","name":"ThomasGR.IM","description":"Geeky Musing","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/thomasgr.im\/shares\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/thomasgr.im\/shares\/#\/schema\/person\/a9f452898a055ceb0f17967802c852da","name":"Thomas","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/24002a8c89532079ac75ffa31bf174b5f4b88ef34601417767fd11b3d48436b7?s=96&d=mm&r=g","caption":"Thomas"},"sameAs":["https:\/\/thomasgr.im\/"],"url":"https:\/\/thomasgr.im\/shares\/author\/thomadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts\/73","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/comments?post=73"}],"version-history":[{"count":0,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/posts\/73\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/media\/72"}],"wp:attachment":[{"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/media?parent=73"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/categories?post=73"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thomasgr.im\/shares\/wp-json\/wp\/v2\/tags?post=73"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}