{"id":420,"date":"2012-12-29T08:38:57","date_gmt":"2012-12-29T15:38:57","guid":{"rendered":"http:\/\/blog.monnet-usa.com\/?p=420"},"modified":"2012-12-29T08:38:57","modified_gmt":"2012-12-29T15:38:57","slug":"creating-mobile-apps-with-knockoutjs-phonegap-jquery-mobile","status":"publish","type":"post","link":"https:\/\/blog.monnet-usa.com\/?p=420","title":{"rendered":"Creating Mobile Apps With KnockOutJS, PhoneGap &#038; jQuery Mobile"},"content":{"rendered":"<h3>Intro <a id='part5-intro'><\/a><\/h3>\n<p>In <a href=\"https:\/\/blog.monnet-usa.com\/?p=411\">part 4<\/a> we built a complete working version of the <a href=\"http:\/\/savings-goal-simulator.heroku.com\/\">Savings Goal Simulator<\/a> as <strong><em>a rich web app<\/em><\/strong> where each view model, view and view mediator is nicely modularized. How could we leverage our browser-side markup and logic to create a <strong><em>mobile app<\/em><\/strong>, portable across <strong><em>multiple platforms<\/em><\/strong> such as IOS, Android, Windows Mobile? Well, the goal of this post is to present a potential approach. (Of course you can take a peek at the <a href=\"#part5-sowhat\">&#8220;So What?&#8221;<\/a> section)<\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/mobile-app-stack.png\" alt=\"Mobile App Framework Stack\" title=\"Mobile App Framework Stack\" \/><\/p>\n<p>The idea behind <a href=\"#acrdva\">PhoneGap<\/a> is to provide a cross-mobile-platform framework allowing a <strong><em>self-contained web app<\/em><\/strong> built using HTML5 + CSS3 + Javascript to run inside a <strong><em>full screen web view<\/em><\/strong>, while allowing access to the device features such as for example: geolocation, accelerometer, camera, storage, etc..<\/p>\n<p>So PhoneGap will serve as our foundation. For each platform you intend on supporting, you will use the corresponding SDK, code project templates and PhoneGap runtime. Let&#8217;s take a look at the 3 most common platforms:<\/p>\n<p>On IOS, the SDK and application consist of:<\/p>\n<ul>\n<li>a CordovaLib libray<\/li>\n<li>an XCode project template<\/li>\n<li>a main module launching a UIApplicationMain<\/li>\n<li>an AppDelegate class launching a ViewController<\/li>\n<li>a ViewController loading the main page (index.html) of your app into a WebView<\/li>\n<\/ul>\n<p>On Android, the SDK and application consist of:<\/p>\n<ul>\n<li>an Apache Cordova JAR containing the Java implementation of PhoneGap for the Android platform<\/li>\n<li>an Eclipse project template<\/li>\n<li>a DroidGap parent class used for your own Java Droid activity class loading the main page (index.html) of your app.<\/li>\n<li>an assets\/www folder containing web resources (.html, .js, .css, images, etc.)<\/li>\n<\/ul>\n<p>On Windows Mobile Phone, the SDK and application consist of:<\/p>\n<ul>\n<li>a WP7CordovaClassLib library<\/li>\n<li>a XAML application with a phone application page containing a CordovaView<\/li>\n<li>a CordovaView including a browser window hosting the main page (index.html) of your app<\/li>\n<li>a www folder containing web resources (.html, .js, .css, images, etc.)<\/li>\n<\/ul>\n<p>In summary, for each platform, PhoneGap is used to create a sort of &#8220;<strong><em>launcher<\/em><\/strong>&#8221; application, which starts a &#8220;<strong><em>controller<\/em><\/strong>&#8220;, which in turns loads your main .html web page into a full-screen <strong><em>web view<\/em><\/strong>, and exposes underlying platform features through a <strong><em>Javascript API<\/em><\/strong>.<\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/phonegap.png\" alt=\"PhoneGap Framework\" title=\"PhoneGap Framework\" \/><\/p>\n<p>So with PhoneGap, we will keep the launcher part of our mobile app fairly small and trivial. Consequently the minimum skills and associated learning curve for each platform we&#8217;ll want to target will also be more manageable. The majority of your app components will be based on standard HTML5 + CSS3 + Javascript.<\/p>\n<h3>Synopsis<a id='part5-approach'><\/a><\/h3>\n<p>The approach will consist of:<\/p>\n<ol>\n<li>Creating (or leveraging parts of) a minimal web app using HTML and Javascript<\/li>\n<li>Hosting the web app in a PhoneGap &#8220;<strong><em>launcher<\/em><\/strong>&#8221; app (based on the desired mobile platform)<\/li>\n<li>Leveraging jQuery Mobile UI widgets for a consistent look and feel<\/li>\n<li>Theme the app using CSS3 including transitions<\/li>\n<li>Expand the web app functionality while:\n<ul>\n<li>Modularizing pages and views using a templating engine<\/li>\n<li>Partitioning application logic between view models and view mediators<\/li>\n<li>Using KnockoutJS for data-binding <\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h3>Prerequisites<a id='part5-prerequisites'><\/a><\/h3>\n<p>Your starting point may differ based on your current skills and experience:<\/p>\n<ul>\n<li>\n<p>If you already have a significant development experience on a given platform (e.g. Mac <strong><em>OSX<\/em><\/strong> with <strong><em>XCode<\/em><\/strong>, or <strong><em>Windows<\/em><\/strong> with <strong><em>Visual Studio<\/em><\/strong>), then download the SDK for your platform and follow the corresponding <a href=\"http:\/\/docs.phonegap.com\/en\/2.1.0\/guide_getting-started_index.md.html#Getting%20Started%20Guides\">Getting Started Guide<\/a>. You will benefit from a more integrated experience and faster device emulation.<\/p>\n<p><em>Note for IOS: an Apple Developer License will be required if you want to deploy the app to your device (otherwise you will only be able to use the emulator).<\/em>  <\/p>\n<\/li>\n<li>\n<p>If you have limited overall experience, starting out with the <strong><em>Android<\/em><\/strong> platform and the <strong><em>Aptana Studio IDE<\/em><\/strong> (a custom version of the Eclipse IDE optimized for HTML5\/CSS3 and Javascript development) might be easier as you can run it on any OS (Windows, Mac, Linux). To get started you will need to install the following tools (in order):<\/p>\n<ul>\n<li><a href=\"http:\/\/www.aptana.com\/products\/studio3\">Aptana Studio 3<\/a>    <\/li>\n<li><a href=\"http:\/\/developer.android.com\/sdk\/installing\/index.html\">Android SDK<\/a><\/li>\n<li><a href=\"http:\/\/developer.android.com\/sdk\/installing\/installing-adt.html\">Android Development Tools (ADT) plugin for Eclipse \/ Aptana Studio<\/a> <\/li>\n<\/ul>\n<p><em>Note: I recommend the basic tutorial to create a <a href=\"http:\/\/developer.android.com\/training\/basics\/firstapp\/creating-project.html\">basic Android app<\/a> to ensure all the prerequisites are installed correctly. Then you can follow the PhoneGap <a href=\"http:\/\/docs.phonegap.com\/en\/2.1.0\/guide_getting-started_android_index.md.html#Getting%20Started%20with%20Android\">Getting Started Guide for Android<\/a>.<\/em><\/p>\n<\/li>\n<\/ul>\n<p>In the rest of this tutorial I will assume that your mobile development environment is configured and working, and that you have successfully followed the PhoneGap Getting Started Guide. In the next sections I will use the <strong><em>Android<\/em><\/strong> platform as an example.<\/p>\n<h3>High-Level Steps<a id='part5-high-level-steps'><\/a><\/h3>\n<p><em>Note: this tutorial builds upon the previous four <a href=\"#criwawkotuts\">tutorials<\/a> and assumes you have built a rich interactive web application using view models, view mediators, and modularized views.<\/em><\/p>\n<p>Before we get started, here is a <em>bird&#8217;s-eye-view<\/em> of the steps we will be going through in details in later sections:<\/p>\n<ol>\n<li>Creating the actual <strong><em>app shell<\/em><\/strong> using PhoneGap&#8217;s <a href=\"http:\/\/docs.phonegap.com\/en\/2.1.0\/guide_getting-started_index.md.html#Getting%20Started%20Guides\">Getting Started Guide<\/a> for the platform you want to start out with.<\/li>\n<li>Test the basic sample shell to make sure everything is working correctly<\/li>\n<li>Customize the assets\/www <strong><em>folder structure<\/em><\/strong> including all needed JS libraries<\/li>\n<li>Add all needed references for CSS and Javascript files<\/li>\n<li>Define placeholder \/ container divs for each <strong><em>&#8220;logical&#8221; jQuery Mobile page<\/em><\/strong><\/li>\n<li>Create separate sets of files for each logical page:\n<ul>\n<li>view markup (.html) based on a templating engine<\/li>\n<li>view model code (.js)<\/li>\n<li>view mediator code (.js)<\/li>\n<\/ul>\n<\/li>\n<li>Create a main application Javascript module responsible for\n<ul>\n<li>loading all view markup \/ templates<\/li>\n<li>initialize all view mediators (wich in turn initialize their respective view model and data bind the view model to the view)<\/li>\n<li>trigger the overall rendering of the main page by jQuery Mobile<\/li>\n<\/ul>\n<\/li>\n<li>Create a <strong><em>phone and a tablet version<\/em><\/strong> of the main web page (e.g. index.phone.html vs. index.tablet.html<\/li>\n<li>Customize the <strong><em>mobile controller<\/em><\/strong> to load the appropriate index page based on device type (phone vs. tablet) <\/li>\n<li>Tailor the look and feel (layout, styling, etc.) for the tablet vs. phone version<\/li>\n<li>Prepare for packaging<\/li>\n<li>Host your app on an <strong><em>&#8220;app store&#8221;<\/em><\/strong><\/li>\n<\/ol>\n<h3>Building Our App Step-By-Step<a id='part5-implementation'><\/a><\/h3>\n<p>Once we are finished the mobile version of the Savings Goal Simulator will look like this:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-newlook.png\" alt=\"New Look\" title=\"New Look\" \/>   <\/p>\n<p>Let&#8217;s download the version of PhoneGap from <a href=\"http:\/\/phonegap.com\/download\">http:\/\/phonegap.com\/download<\/a> and extract the content to a PhoneGap folder (e.g. PhoneGapSDK). We&#8217;ll use the content later.<\/p>\n<h3>Creating The Android Application Shell<a id='part5-android-shell'><\/a><\/h3>\n<p>On the Android platform, the application shell takes the form a <strong>Java class<\/strong> called an &#8220;<strong>activity<\/strong>&#8220;. It inherits from the <a href=\"#android-activity\">android.app.Activity<\/a> which is the mobile app&#8217;s main application <a href=\"#controller\">&#8220;<strong>controller<\/strong>&#8220;<\/a> in the <a href=\"#mvc\">Model-View-Controller<\/a> sense.<\/p>\n<p>So let&#8217;s get started in Aptana Studio and follow the steps from the <a href=\"http:\/\/docs.phonegap.com\/en\/2.1.0\/guide_getting-started_android_index.md.html#Getting%20Started%20with%20Android\">PhoneGap Getting Started With Android guide<\/a>:<\/p>\n<ol>\n<li>From Aptana Studio, select File, New Project to start the project creation wizard<\/li>\n<li>Expand the Android folder and select &#8220;Android Application Project&#8221;<\/li>\n<li>Let&#8217;s name the application and Eclipse project &#8220;Savings Goal Simulator&#8221;<\/li>\n<li>Customize the namespace e.g.: com.yournamespace.savingsgoalsimulator <\/li>\n<li>Select the minimum version of the Android SDK your app should require, e.g. API7: Android 2.1 <\/li>\n<li>By default the project folder will be created in the default Eclipse workspace project unless you want to customize the path.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-new-android-project.png\" alt=\"Creating the base Android app\" title=\"Creating the base Android app\" \/><\/li>\n<li>Click Next <\/li>\n<li>Accept the default icon settings for now (you can customize them later) and click Next<\/li>\n<li>Accept the default kind of activity settings and click Next<\/li>\n<li>Accept the default activity settings and click Next &#8211; the project will then be created:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-default-android-project-S.png\" alt=\"Creating the base Android app\" title=\"Creating the base Android app\" \/>  <\/li>\n<\/ol>\n<p>At this point we have a basic Android app that you can run in the simulator:<\/p>\n<ul>\n<li>Select Run, then Run, <\/li>\n<li>Select &#8220;Android Application&#8221; in the Run As dialog, then click Ok<\/li>\n<li>The emulator will boot Android<\/li>\n<li>\n<p>Then click and drag the lock to the right to unlock the virtual phone, your app will appear:  <\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-runnin-default-android-app.png\" alt=\"Running the base Android app\" title=\"Running the base Android app\" \/>  <\/p>\n<\/li>\n<\/ul>\n<p>Let&#8217;s take a brief look at what was generated:<\/p>\n<ol>\n<li>\n<p>Expand the src folder recursively until you can see and double-click on the MainActivity.java file.<\/p>\n<pre class=\"brush: javascript\">package com.yournamespace.savingsgoalsimulator;\r\nimport android.os.Bundle;\r\nimport android.app.Activity;\r\nimport android.view.Menu;  \r\n\r\npublic class MainActivity extends Activity {\r\n\r\n    @Override\r\n    public void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.activity_main);\r\n    }   \r\n\r\n    @Override\r\n    public boolean onCreateOptionsMenu(Menu menu) {\r\n        getMenuInflater().inflate(R.menu.activity_main, menu);\r\n        return true;\r\n    }\r\n}\t\r\n<\/pre>\n<p>}\n<\/li>\n<li>The <strong>MainActivity<\/strong> class inherits from the core Android Activity class<\/li>\n<li>The <strong>OnCreate<\/strong> method calls <strong>setContentView<\/strong> to create the main view based on the layout provided<\/li>\n<li>Under the <strong>res\\layout<\/strong> folder, open the <strong>activity_main.xml<\/strong> resource<\/li>\n<li>The <strong>RelativeLayout<\/strong> includes a <strong>TextView<\/strong> for which the text is provided by the string resource named <strong>hello_world<\/strong><\/li>\n<li>Under <strong>res\\values<\/strong>, open the <strong>strings.xml<\/strong> resource and find the definition of <strong>hello_world<\/strong> <\/li>\n<\/ol>\n<p>Once we upgrade our app to PhoneGap, we will no longer use an Android content view nor a layout. Instead we will use a web view loaded with a local web page.<\/p>\n<h3>Creating The PhoneGap Application Shell<a id='part5-phonegap-shell'><\/a><\/h3>\n<p>PhoneGap provides a subclass of Activity called <strong>DroidGap<\/strong> which instead of creating an Android content view based on a layout, will <strong>create a web view and load the .html page of your choice<\/strong>, which like any page will load stylesheets and scripts, and run the Javascript code. So we&#8217;ll make the necessary changes to upgrade the default app to PhoneGap:<\/p>\n<ol>\n<li>\n<p>Let&#8217;s copy the <strong>Cordova Java archive<\/strong> (<strong>cordova-2.2.0.jar<\/strong> as of this writing) of the <strong>lib\\android<\/strong> located in our <strong>PhoneGapSDK<\/strong> folder we extracted earlier to the <strong>libs<\/strong> folder of your project (the folder currently contains a file named android-support-v4.jar). The Cordova JAR will now appear in Aptana Studio under <strong>libs<\/strong>.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-cordova-jar.png\" alt=\"Adding the Cordova JAR to the project\" title=\"Adding the Cordova JAR to the project\" \/><\/p>\n<\/li>\n<li>\n<p>Now we need to add the Cordova JAR to the Java Build Path. In AptanaStudio, right click on the <strong>libs<\/strong> folder, select &#8220;Build Path&#8221;, then select &#8220;Configure Build Path&#8221;, select the &#8220;Libraries&#8221; tab, click on &#8220;Add JARs &#8230;&#8221;, drill down to the libs folder of your project, select the <strong>Cordova JAR<\/strong>, and click OK.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-cordova-jar-lib.png\" alt=\"Adding the Cordova JAR to the Java Build Path\" title=\"Adding the Cordova JAR to the Java Build Path\" \/>  <\/p>\n<\/li>\n<li>\n<p>The Cordova JAR will also appear under the &#8220;<strong>Referenced Libraries<\/strong>&#8221; folder of your project.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-ref-libs.png\" alt=\"Java Build Path - Referenced Libraries\" title=\"Java Build Path - Referenced Libraries\" \/><\/p>\n<\/li>\n<li>\n<p>Let&#8217;s go to the <strong>MainActivity<\/strong> Java class source file. Since we&#8217;re no longer going to need the default Android Activity approach, let&#8217;s replace the 2 imports related to android.app.Activity and android.view.Menu by a import for the <strong>org.apache.cordova.*<\/strong> namespace:<\/p>\n<pre class=\"brush: javascript\">package com.yournamespace.savingsgoalsimulator;\r\nimport android.os.Bundle;\r\nimport org.apache.cordova.*\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s change the <strong>parent class<\/strong> for MainActivity to the new <strong>DroidGap<\/strong> class:<\/p>\n<pre class=\"brush: javascript\">public class MainActivity extends DroidGap\r\n<\/pre>\n<\/li>\n<li>\n<p>Now we&#8217;ll change the implementation of the <strong>onCreate<\/strong> method. So let&#8217;s replace the line starting with setContentView by the following code which load the content from a local url:<\/p>\n<pre class=\"brush: javascript\">@Override\r\npublic void onCreate(Bundle savedInstanceState) {\r\n    super.onCreate(savedInstanceState);\r\n    super.loadUrl(\"file:\/\/\/android_asset\/www\/index.html\");\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s remove the <strong>onCreateOptionsMenu<\/strong> method as it is no longer needed.<\/p>\n<\/li>\n<li>\n<p>If we ran the app again, the emulator would tell us that the index.html could not be loaded which makes sense since we have not created it yet!<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-missing-index.html.png\" alt=\"No index.html found yet\" title=\"No index.html found yet\" \/><\/p>\n<\/li>\n<li>\n<p>Note that PhoneGap is looking for the resource in a <strong>www<\/strong> folder under a logical android<em>asset folder. Although it might seem counter-intuitive Android maps the &#8220;<strong>android<\/em>asset<\/strong>&#8221; url folder to the physical <strong>assets<\/strong> folder! So right click on <strong>assets<\/strong> and create the <strong>www<\/strong> folder.<\/p>\n<\/li>\n<li>\n<p>Under <strong>assets\/www<\/strong>, create an <strong>index.html<\/strong> web page file with the following basic HTML source:<\/p>\n<pre class=\"brush: html\">&lt;!DOCTYPE HTML&gt;\r\n&lt;html&gt;\r\n    &lt;head&gt;\r\n        &lt;title&gt;Cordova&lt;\/title&gt;\r\n    &lt;\/head&gt;\r\n    &lt;body&gt;\r\n        &lt;h1&gt;Hello World&lt;\/h1&gt;\r\n    &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Re-run the app and the page should now display in the emulator. At this point we just exercized the basic ability for the <strong>DroidGap<\/strong> class to load a web page into a web view. We&#8217;ll soon add the Cordova Javascript library to the page so we can expose the bi-directional API between the web page and the actual device. But first we need to add the permissions PhoneGap will need to the application. For that, double-click on the <strong>AndroidManifest.xml<\/strong> and switch to the XML view. Locate the line starting with the <strong>&lt;application tag<\/strong> and insert the following lines <strong>above<\/strong> it, then save the XML file:<\/p>\n<pre class=\"brush: javascript\">&lt;supports-screens \r\n    android:largeScreens=\"true\" \r\n    android:normalScreens=\"true\" \r\n    android:smallScreens=\"true\" \r\n    android:resizeable=\"true\" \r\n    android:anyDensity=\"true\" \/&gt;\r\n\r\n\r\n&lt;uses-permission android:name=\"android.permission.VIBRATE\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.READ_PHONE_STATE\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.INTERNET\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.RECEIVE_SMS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.RECORD_AUDIO\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.READ_CONTACTS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.WRITE_CONTACTS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" \/&gt; \r\n&lt;uses-permission android:name=\"android.permission.GET_ACCOUNTS\" \/&gt;\r\n&lt;uses-permission android:name=\"android.permission.BROADCAST_STICKY\" \/&gt;\r\n<\/pre>\n<p>Technically your application may only need a subset of these permissions, but for this tutorial we&#8217;ll add them all. Additionally, if you want the application to support orientation changes, add the following attribute before the end of the &lt;activity tag !(located underneath the &lt;application tag):<\/p>\n<pre class=\"brush: javascript\">android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale\"\r\n<\/pre>\n<p>The whole opening &lt;activity tag should now look as follows: <\/p>\n<pre class=\"brush: javascript\">&lt;activity\r\n    android:name=\".MainActivity\"\r\n    android:label=\"@string\/title_activity_main\"\r\n    android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale\"\r\n &gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>To allow our WebView to interact with the PhoneGap \/ Cordova plugins for the mobile device we need to add configuration information to our <strong>res<\/strong> resource folder. So copy the <strong>xml<\/strong> folder located in the <strong>android<\/strong> folder of the <strong>PhoneGapSDK<\/strong> folder to the <strong>res<\/strong> folder of our project:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-cordova-xml-S.png\" alt=\"Adding the Cordova xml folder to the project\" title=\"Adding the Cordova xml folder to the project\" \/><\/p>\n<p><em>Note: if you forget this step, later the mobile app will show an error message indicating that the Cordova class cannot be found since the WebView will not be able to locate the Cordova plugins.<\/em><\/p>\n<\/li>\n<li>\n<p>As in our original <a href=\"https:\/\/blog.monnet-usa.com\/?p=368\">Creating Rich Interactive Web Apps With KnockOut.js \u2013 Part 2<\/a> tutorial, let&#8217;s further expand the www folder into subfolders, by creating a <strong>scripts<\/strong> subfolder, and below that a <strong>vendor<\/strong> subfolder. Now go back to the PhoneGap SDK folder and copy the Cordova Javascript library file (cordova-2.2.0.js as of this writing) to our new assets\/www\/scripts\/vendor folder.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-cordova.js.png\" alt=\"Adding Cordova JS to the project\" title=\"Adding Cordova JS to the project\" \/>   <\/p>\n<\/li>\n<li>\n<p>Let&#8217;s create a separate <strong>application.js<\/strong> shell module under our <strong>assets\/www\/scripts<\/strong> folder. The module will contain the code to initialize our web page but for now, let&#8217;s keep it minimal and let&#8217;s provide 2 functions: InitializeApplication and OnLoad:<\/p>\n<pre class=\"brush: javascript\">function InitializeApplication() {\r\n    alert(\"Application Initialized!\");\r\n}  \r\n\r\n\r\nfunction OnLoad() {\r\n    InitializeApplication();\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s switch to the index.html page and add our 2 Javascript files to our &lt;head&gt; tag:<\/p>\n<pre class=\"brush: html\">&lt;head&gt;\r\n    &lt;title&gt;Cordova&lt;\/title&gt;\r\n    &lt;script src=\"scripts\/vendor\/cordova-2.2.0.js\"&gt;&lt;\/script&gt;\r\n    &lt;script src=\"scripts\/application.js\"&gt;&lt;\/script&gt; \r\n&lt;\/head&gt;\r\n<\/pre>\n<p>And for now, let&#8217;s add a call to the OnLoad function to the body onload event of the page:<\/p>\n<pre class=\"brush: html\">&lt;body onload=\"OnLoad();\"&gt;\r\n    &lt;h1&gt;Hello World&lt;\/h1&gt;\r\n&lt;\/body&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Now you should be able to re-run the application and see the alert pop-up.<\/p>\n<p>Note: if you don&#8217;t see the alert, verify the scripts and the page. You can also troubleshoot the index.html from your favorite browser debugging tools (e.g. Chrome, Developer Tools). This is always the best technique to troubleshoot your Javascript anyway.<\/p>\n<\/li>\n<li>\n<p>We&#8217;re now ready to dig into the application initialization lifecycle! The browser triggered the <strong>onload<\/strong> event once the page has been loaded. PhoneGap \/ Cordova will trigger a different event named <strong>deviceready<\/strong> once the bidirectional API between the mobile device and Javascript has been fully initialized. To react to the <strong>deviceready<\/strong> event we will need to add an <strong>event listener<\/strong> once the <strong>onload<\/strong> event has been triggered. So let&#8217;s add a new function named <strong>OnDeviceReady<\/strong> to our <strong>application.js<\/strong> module. We&#8217;ll move the call to InitializeApplication from OnLoad to the new function since we actually want to initialize our app once the deviceready event has occurred.<\/p>\n<pre class=\"brush: javascript\">function OnDeviceReady() {  \r\n    InitializeApplication();\r\n}\r\n<\/pre>\n<p>Now let&#8217;s setup the event listener in the OnLoad function:<\/p>\n<pre class=\"brush: javascript\">function OnLoad() {\r\n    document.addEventListener(\"deviceready\", OnDeviceReady, false);\r\n}\r\n<\/pre>\n<p>This allows us to customize the InitializeApplication to use some of the device information exposed by PhoneGap:<\/p>\n<pre class=\"brush: javascript\">function InitializeApplication() {\r\n    alert(\"Welcome to PhoneGap \" \r\n            + device.cordova \r\n            + \" version \" + device.version \r\n            + \" for \" + device.platform \r\n            + \" on device: \" + device.name );\r\n}\r\n<\/pre>\n<p>Now let&#8217;s run the application again and our alert with the device information should appear!<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-basic-initialize-app.png\" alt=\"Application Initialization On deviceready Event\" title=\"Application Initialization On deviceready Event\" \/><\/p>\n<\/li>\n<\/ol>\n<p>This concludes the creation of the basic PhoneGap application shell. In summary here is what our application does:<\/p>\n<ol>\n<li>Android launches our MainActivity class which inherits from the DroidGap<\/li>\n<li>The onCreate method of DroidGap creates a WebView<\/li>\n<li>The onCreate method of MainActivity sets the url content of the WebView to index.html<\/li>\n<li>Our index.html loads the Cordova library and our application.js<\/li>\n<li>The onload event of the page sets up an event listener for the PhoneGap deviceready event<\/li>\n<li>Once PhoneGap is fully initialized the deviceready event is triggered<\/li>\n<li>Our custom InitializeApplication function is called &#8211; at that point the PhoneGap API is fully operational<\/li>\n<\/ol>\n<h3>Applying a jQuery Mobile &#8220;Veneer&#8221;<a id='part5-jquery-mobile'><\/a><\/h3>\n<h4>Introduction to jQuery Mobile<a id='part5-jquery-mobile-intro'><\/a><\/h4>\n<p>Since our app is built using HTML and CSS we can create a complete custom look and feel for our app. This is fine if you are creating something truly unique like a game or something very graphical.<br \/>\nBut if you are planning on creating an application focused on <strong>manipulating data<\/strong> I would recommend following <strong>mobile UI conventions<\/strong>. One easy way to ensure a more standardized look that your customers will feel familiar with is to leverage jQuery Mobile. You will benefit from:<\/p>\n<ul>\n<li>a set of common UI elements optimized for a mobile experience\n<ul>\n<li>larger buttons and easier to use form controls<\/li>\n<li>toolbars, listviews, collapsible panels, grids<\/li>\n<\/ul>\n<\/li>\n<li>navigation between &#8220;logical pages&#8221;<\/li>\n<li>cross-platform compatibility helping you create a uniform experience across devices<\/li>\n<li>customizable themes<\/li>\n<\/ul>\n<p>Here is how jQuery Mobile makes this possible:<\/p>\n<ul>\n<li>\n<p>jQuery Mobile allows declaration of its custom UI elements by augmenting traditional HTML elements with new attributes. E.g.: a jQM button is created by enhancing the anchor tag with a data-role=&#8221;button&#8221; attribute (as well as other attributes to add details about how the button should be rendered, such as with an icon positioned above the label):  <\/p>\n<pre class=\"brush: javascript\">&lt;a href=\"#addContact\" \r\n    data-role=\"button\" \r\n    data-icon=\"plus\" \r\n    data-iconpos=\"top\"&gt;Add Contact&lt;\/a&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>As the DOM is loaded, jQuery Mobile injects additional needed elements and CSS classes based on the various <strong>data-*<\/strong> attributes to produce the desired effect.<\/p>\n<\/li>\n<\/ul>\n<p>I would not be providing a balanced view point if I did not mention the following associated challenges:<\/p>\n<ul>\n<li>\n<p>Since &#8220;logical pages&#8221; are defined using div elements enhanced with data-role=&#8221;page&#8221; attribute which causes the physical .html page to become large and difficult to maintain:  <\/p>\n<pre class=\"brush: javascript\">&lt;div id=\"page1\" data-role=\"page\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/div&gt;\r\n\r\n\r\n&lt;div id=\"page2\" data-role=\"page\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/div&gt;\r\n\r\n\r\n&lt;!-- ... --&gt;\r\n\r\n\r\n&lt;div id=\"pageN\" data-role=\"page\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>We&#8217;ll use external views based on templates and the jQuery View Loader plugin to address this issue.  <\/p>\n<\/li>\n<li>\n<p>Defining common elements such as the bottom toolbar for each &#8220;logical page&#8221; also requires the use of templates if we do not want to duplicate the markup over and over.<\/p>\n<\/li>\n<li>\n<p>Since the markup is automatically enhanced as it is being loaded, dynamically changing elements becomes a bit more complicated since jQuery Mobile needs to be notified to &#8220;re-enhance&#8221; the new markup fragments. This will become even more apparent when we use a viewmodel to view data-binding library like KnockoutJS. But again there are some solutions.<\/p>\n<\/li>\n<\/ul>\n<p>Overall despite the learning curve and the need to make jQuery Mobile &#8220;play nice&#8221; with other libraries manipulating the DOM, the benefits strongly outweigh the challenges. This tutorial will provide the necessary guidance to increase your chances of success. <\/p>\n<h4>Adding A jQuery Mobile Shell<a id='part5-jquery-mobile-shell'><\/a><\/h4>\n<ol>\n<li>\n<p>First, download the full ZIP version of jQueryMobile and extract it out locally.  <\/p>\n<\/li>\n<li>\n<p>Before we add the files to our project, let&#8217;s create a <strong>css<\/strong> folder under <strong>assets\/www<\/strong>. This will provide a central place for all CSS files needed for our application.  <\/p>\n<\/li>\n<li>\n<p>Let&#8217;s create a <strong>jquery.mobile<\/strong> folder under <strong>assets\/www\/css<\/strong>  <\/p>\n<\/li>\n<li>\n<p>Now copy the .css files (only) and the images folder from your extracted jQueryMobile ZIP to <strong>assets\/www\/css\/jquery.mobile<\/strong>. Your project structure should now look like this:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-jqm-css.png\" alt=\"jQuery Mobile CSS Folder Setup\" title=\"jQuery Mobile CSS Folder Setup\" \/><\/p>\n<\/li>\n<li>\n<p>And then copy the .js files from your extracted jQueryMobile ZIP to <strong>assets\/www\/scripts\/vendor<\/strong><\/p>\n<\/li>\n<li>\n<p>Since jQuery Mobile builds on top of the jQuery Core, download both the development and production versions of jQuery and copy the corresponding .js files to <strong>assets\/www\/scripts\/vendor<\/strong>. Your project structure should now look like this:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-jqm-js.png\" alt=\"jQuery Mobile JS Folder Setup\" title=\"jQuery Mobile JS Folder Setup\" \/><\/p>\n<\/li>\n<li>\n<p>Now let&#8217;s go back to our index.html page and add the jQuery Mobile stylesheets. We&#8217;ll also add a meta tag to provide device information which will be leveraged by jQuery Mobile to optimize rendering for the device.<\/p>\n<pre class=\"brush: javascript\">&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" \/&gt;                \r\n&lt;link href=\"css\/jquery.mobile\/jquery.mobile-1.2.0.css\" rel=\"stylesheet\" type=\"text\/css\"  \/&gt;  \r\n&lt;link href=\"css\/jquery.mobile\/jquery.mobile.structure-1.2.0.css\" rel=\"stylesheet\" type=\"text\/css\"  \/&gt; \r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s include the .js files for jQuery and jQuery Mobile in between the cordova JS and our application.js<\/p>\n<pre class=\"brush: javascript\">&lt;script src=\"scripts\/vendor\/cordova-2.2.0.js\"&gt;&lt;\/script&gt;     \r\n&lt;script src=\"scripts\/vendor\/jquery-1.8.2.js\"&gt;&lt;\/script&gt; \r\n&lt;script src=\"scripts\/vendor\/jquery.mobile-1.2.0.js\"&gt;&lt;\/script&gt;       \r\n&lt;script src=\"scripts\/application.js\"&gt;&lt;\/script&gt; \r\n<\/pre>\n<\/li>\n<li>\n<p>And let&#8217;s define a minimal jQuery Mobile &#8220;<strong>logical page<\/strong>&#8220;<\/p>\n<pre class=\"brush: javascript\">&lt;body onload=\"OnLoad();\"&gt;\r\n    &lt;div id=\"page-home\" data-role=\"page\"&gt;\r\n        &lt;div data-role=\"header\"&gt;\r\n            Savings Goal Simulator\r\n        &lt;\/div&gt;\r\n        &lt;div data-role=\"content\"&gt;\r\n            Welcome!\r\n        &lt;\/div&gt;\r\n        &lt;div data-role=\"footer\"&gt;\r\n            powered by PhoneGap and jQueryMobile\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/body&gt;\r\n<\/pre>\n<p>Let&#8217;s run the app in the simulator:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-basic-jqm.png\" alt=\"Minimal jQuery Mobile Logical Page\" title=\"Minimal jQuery Mobile Logical Page\" \/><\/p>\n<\/li>\n<li>\n<p>jQueryMobile fires an event named <strong>mobileinit<\/strong> as soon as it starts executing. Since we may want to override some of the framework defaults (more on that later), let&#8217;s add a script with an event handler for <strong>mobileinit<\/strong>. Since we want to setup the event handler before jQuery Mobile starts to load, we&#8217;ll define the script right before the inclusion of jQuery Mobile.<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    \/\/ IMPORTANT: This must execute before jQueryMobile is loaded!\r\n    $(document).bind(\"mobileinit\", function() {\r\n         console.log(\"mobileinit\",\"info\");\r\n         \/\/ Placeholder for framework settings overrides\r\n         \/\/ See http:\/\/jquerymobile.com\/demos\/1.2.0\/docs\/api\/globalconfig.html\r\n    });            \r\n&lt;\/script&gt;       \r\n&lt;script src=\"scripts\/vendor\/jquery.mobile-1.2.0.js\"&gt;&lt;\/script&gt; \r\n<\/pre>\n<\/li>\n<li>\n<p>In the jQuery world, a <strong>ready<\/strong> event is triggered on the document once the page has fully loaded. But in jQuery Mobile, since you can have multiple &#8220;logical pages&#8221;, jQuery Mobile will trigger a <strong>pageinit<\/strong> event on a given page once it has been created and initialized. So typically we&#8217;ll setup an event handler for pageinit when triggered on the main \/ &#8220;home&#8221; logical page. This handler will perform any  initialization specific to that page. So let&#8217;s add the following script below our &#8220;logical page&#8221; definition:<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    $(\"#page-home\").live(\"pageinit\", function() {\r\n        \/\/ When ALL scripts have been loaded AND executed:\r\n        alert(\"page-home is ready!\");\r\n    })\r\n&lt;\/script&gt;       \r\n<\/pre>\n<p>Now run the application. What do you notice?<br \/>\nThe &#8220;page-home is ready!&#8221; alert message appears BEFORE the PhoneGap alert message!<br \/>\nWhy is that?<br \/>\nWell, <em>jQuery Mobile starts processing the markup as soon as it is created<\/em> while PhoneGap <strong>deviceready<\/strong> event is not triggered until the whole physical page is loaded and until the PhoneGap library is fully initialized.  <\/p>\n<p>IMPORTANT: The initialization code for our application will need to be split according to when various initializations are needed. For example: <\/p>\n<ul>\n<li>If we need to <strong>fetch data<\/strong> from a remote app, we should trigger the fetch from a [jQuery Mobile] <strong>pageinit<\/strong> event<\/li>\n<li>If we need to <strong>use data from or related to the device<\/strong> (e.g. local storage), we should retrieve the data from the <strong>deviceready<\/strong> [PhoneGap] event<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>So at this point, we have a minimal PhoneGap + jQuery Mobile application!<\/p>\n<h3>Transforming Our Savings Goal Simulator Web App Into A Mobile App<a id='part5-web-to-mobile'><\/a><\/h3>\n<p>At the end of <a href=\"#criwawko4\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 4<\/a> we had a very modular browser-side rich web app. As you will see we&#8217;ll be able to adapt the code to &#8220;make it fit&#8221; with the PhoneGap and jQuery Mobile framework. At a high-level we will need to:<\/p>\n<ul>\n<li>adapt our views to fit mobile device dimensions<\/li>\n<li>enhance our view markup with jQuery Mobile attributes (e.g.: data-role, etc.)<\/li>\n<\/ul>\n<h4>Installing The Web Version Of The Savings Goal Simulator<a id='part5-installing-the-web-version-of-the-savings-goal-simulator'><\/a><\/h4>\n<p>So let&#8217;s first migrate the source of the web version of the Savings Goal Simulator to our mobile project:<\/p>\n<ol>\n<li>Download the <a href=\"#criwawkosrc\">source code for the web version of the Savings Goal Simulator<\/a> from GitHub<\/li>\n<li>Extract and open the download source<\/li>\n<li>Copy the <strong>application.css<\/strong> from savings-goal-simulator-master\\css\\ to the <strong>assets\\www\\css<\/strong> folder of your project<\/li>\n<li>Copy only the subfolders of savings-goal-simulator-master\\scripts\\ to the assets\\www\\scripts folder of your project (do not copy the application.js)<\/li>\n<li>Rename the index.html in savings-goal-simulator-master to index.web.html<\/li>\n<li>Copy index.web.html from savings-goal-simulator-master to assets\\www<\/li>\n<li>Rename the application.js in savings-goal-simulator-master to application.web.js<\/li>\n<li>Copy application.web.js in savings-goal-simulator-master to assets\\www\\scripts<\/li>\n<li>So far your project workspace should now reflect the new folders under assets\\www\\scripts and the new files:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-web-migration.png\" alt=\"Workspace with web version of the Savings Goal Simulator\" title=\"Workspace with web version of the Savings Goal Simulator\" \/><\/li>\n<li>To make troubleshooting easier, let&#8217;s make a couple changes so that we can run the web version using index.web.html in a browser. This way we can always compare our mobile version to the web version to help identify and resolve potential issues.\n<ul>\n<li>Open index.web.html, look for a reference to application.js and rename it to application.web.js (since we renamed that Javascript file earlier)<\/li>\n<li>Open Google Chrome (or your favorite browser), and open the Developer Tools<\/li>\n<li>Drag index.web.html to the browser<\/li>\n<li>Test the app &#8211; it should work fine &#8211; you will notice that there is no &#8220;styling&#8221; since we did not copy the jQuery UI theme (because we will use jQuery Mobile instead) <\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>Now we are ready to adapt our web app into a mobile app.<\/p>\n<h3>Rudimentary Migration Of The Savings Goal Simulator Source Code<a id='part5-rudimentary-migration'><\/a><\/h3>\n<p>The idea is to progressively enhance the basic <strong>index.html<\/strong> we created in the <a href=\"#part5-jquery-mobile-shell\">&#8220;Adding A jQuery Mobile Shell&#8221;<\/a> section by incorporating elements of the web version of the app. The modest goal for this section is to be able to bring up the Savings Goal Simulator in the mobile device simulator.<\/p>\n<ol>\n<li>\n<p>In <a href=\"#criwawko2\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 2<\/a>, we implemented asynchronous parallel loading of Javascript libraries using <a href=\"#labjs\">LAB.js<\/a> to speed up the loading process of our web application. We will apply the same technique to our mobile app.<\/p>\n<ul>\n<li>\n<p>In index.html, let&#8217;s load the <strong>LAB.js<\/strong> Javascript library <strong>after<\/strong> loading application.js and let&#8217;s add a script block:<\/p>\n<pre class=\"brush: javascript\">&lt;script src=\"scripts\/vendor\/cordova-2.2.0.js\"&gt;&lt;\/script&gt;     \r\n&lt;script src=\"scripts\/vendor\/jquery-1.8.2.js\"&gt;&lt;\/script&gt; \r\n&lt;script src=\"scripts\/vendor\/jquery.mobile-1.2.0.js\"&gt;&lt;\/script&gt;       \r\n&lt;script src=\"scripts\/application.js\"&gt;&lt;\/script&gt;  \r\n&lt;script src=\"scripts\/vendor\/LAB.min.js\" type=\"text\/javascript\" &gt;&lt;\/script&gt;\r\n&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n        .wait(function () {\r\n        });\r\n&lt;\/script&gt;   \r\n<\/pre>\n<\/li>\n<li>\n<p>Now let&#8217;s replace the script loading for jquery.mobile-1.2.0.js by a call to load the script via LAB.js:<\/p>\n<pre class=\"brush: javascript\">&lt;script src=\"scripts\/vendor\/LAB.min.js\" type=\"text\/javascript\" &gt;&lt;\/script&gt;\r\n&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n        .script(\"scripts\/vendor\/jquery.mobile-1.2.0.js\").wait()\r\n        .wait(function () {\r\n        \/\/ When ALL scripts have been loaded AND executed:                  \r\n        });\r\n&lt;\/script&gt;   \r\n<\/pre>\n<\/li>\n<li>\n<p>If we try to run our app we will notice that the &#8220;page-home is ready!&#8221; alert never pops-up and that we see an &#8220;$ undefined&#8221; error in the console. This is due to the fact that our scripts are being loaded in parallel of our page execution. So the code we had pageinit event handler we had placed below our page definition needs to be synchronized with our LAB.js-based script loading:<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n    .wait(function () {\r\n        $(\"#page-home\").live(\"pageinit\", function() {\r\n            \/\/ When ALL scripts have been loaded AND executed:\r\n            \/\/alert(\"page-home is ready!\");\r\n            console.log(\"page-home is ready!\",\"info\");\r\n        })\r\n    })\r\n    .script(\"scripts\/vendor\/jquery.mobile-1.2.0.js\")\r\n    .wait(function () {\r\n        \/\/ When ALL scripts have been loaded AND executed:                  \r\n    });\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>Now the application should work correctly in the simulator. <\/p>\n<\/li>\n<li>\n<p>We can proceed and add requests to load all other Javascript libraries we&#8217;ll need:<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n    .wait(function () {\r\n        $(\"#page-home\").live(\"pageinit\", function() {\r\n            \/\/ When ALL scripts have been loaded AND executed:\r\n            alert(\"page-home is ready!\");\r\n            console.log(\"page-home is ready!\",\"info\");\r\n        })\r\n    })\r\n    .wait(function (){\r\n        \/\/ IMPORTANT: This must execute before jQueryMobile is loaded!\r\n        $(document).bind(\"mobileinit\", function() {\r\n             console.log(\"mobileinit\",\"info\");\r\n             \/\/ Placeholder for framework settings overrides\r\n             $.support.cors = true;\r\n        });            \r\n    })\r\n    .script(\"scripts\/vendor\/jquery.tmpl.min.js\").wait()\r\n    .script(\"scripts\/vendor\/knockout-latest.debug.js\").wait()\r\n    .script(\"scripts\/vendor\/jquery.viewloader.js\").wait()\r\n    .script(\"scripts\/vendor\/jquery.cookie.js\")\r\n    .script(\"scripts\/vendor\/jquery.blockUI.js\")\r\n    .script(\"scripts\/vendor\/jquery.hotkeys.js\")\r\n    .script(\"scripts\/vendor\/jquery.maskedinput-1.3.min.js\")\r\n    .script(\"scripts\/vendor\/jquery.validate.min.js\")\r\n    .script(\"scripts\/vendor\/accounting.min.js\")\r\n    .script(\"scripts\/vendor\/jstorage.min.js\")\r\n    .script(\"scripts\/vendor\/currency-mask-0.5.0-min.js\")\r\n    .script(\"scripts\/viewmodel\/sgs.model.common.js\")\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.common.js\")\r\n    .script(\"scripts\/viewmodel\/sgs.model.savings-goal.js\").wait()\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.savings-goal.js\")\r\n    .script(\"scripts\/viewmodel\/sgs.model.coffee-pricing.js\").wait()\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.coffee-pricing.js\")\r\n    .script(\"scripts\/viewmodel\/sgs.model.coffee-consumption.js\").wait()\r\n    .script(\"scripts\/viewmodel\/sgs.model.consumption-scenarios.js\").wait()\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.consumption-scenarios.js\")\r\n    .script(\"scripts\/viewmodel\/sgs.model.savings-forecast.js\").wait()\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.savings-forecast.js\")\r\n    .script(\"scripts\/vendor\/jquery.mobile-1.2.0.js\")\r\n    .wait(function () {\r\n        \/\/ When ALL scripts have been loaded AND executed:                  \r\n    });\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>If you run the app in the simulator or the browser you will see in the console that each of the module is being loaded.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Currently the <strong>body<\/strong> of our <strong>index.html<\/strong> page has a div with a <strong>data-role<\/strong> of &#8220;<strong>content<\/strong>&#8220;, let&#8217;s replace our &#8220;Welcome!&#8221; text in the div with the content of body tag from our index.web.html:<\/p>\n<pre class=\"brush: html\">&lt;body onload=\"OnLoad();\"&gt;\r\n    &lt;div id=\"page-home\" data-role=\"page\"&gt;\r\n        &lt;div data-role=\"header\"&gt;\r\n            Savings Goal Simulator\r\n        &lt;\/div&gt;\r\n        &lt;div data-role=\"content\"&gt;\r\n            &lt;script id='savings-goal-view-template'\r\n                    type='text\/html'\r\n                    src='scripts\/view\/index\/_savings-goal.view.html' &gt;\r\n            &lt;\/script&gt;\r\n            &lt;div id='savings-goal-view-container'\r\n                    data-bind='template: { name: \"savings-goal-view-template\", afterRender: sgs.mediator.savingsgoal.setupViewDataBindings } ' &gt;\r\n            &lt;\/div&gt;                  \r\n            &lt;script id='consumption-scenarios-view-template'\r\n                    type='text\/html'\r\n                    src='scripts\/view\/index\/_consumption-scenarios.view.html' &gt;\r\n            &lt;\/script&gt;\r\n            &lt;div id='consumption-scenarios-view-container'\r\n                    data-bind='template: { name: \"consumption-scenarios-view-template\", afterRender: sgs.mediator.consumptionscenarios.setupViewDataBindings } ' &gt;\r\n            &lt;\/div&gt;                  \r\n            &lt;script id='savings-forecast-view-template'\r\n                    type='text\/html'\r\n                    src='scripts\/view\/index\/_savings-forecast.view.html' &gt;\r\n            &lt;\/script&gt;\r\n            &lt;div id='savings-forecast-view-container'\r\n                    data-bind='template: { name: \"savings-forecast-view-template\", afterRender: sgs.mediator.savingsforecast.setupViewDataBindings } ' &gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div data-role=\"footer\"&gt;\r\n            powered by PhoneGap and jQueryMobile\r\n        &lt;\/div&gt;                          \r\n    &lt;\/div&gt;      \r\n&lt;\/body&gt;\r\n<\/pre>\n<p>As a brief reminder, the scripts of type <strong>text\/html<\/strong> represent definition of <strong>jQuery Templates<\/strong> used to render views \/ panels within the app using data-bind tags. So the data-role=&#8221;content&#8221; div contains 3 views to track the savings goal, scenarios, and corresponding forecast. If we run the app now the views will not appear as we have not yet ported over the view loading logic.<\/p>\n<\/li>\n<li>\n<p>In the web version, the view loading is done in the InitializeApplication function of application.web.js. Let&#8217;s copy the content of application.web.js at the end of application.js and rename the InitializeApplication we just copied to <strong>LoadApplicationViews<\/strong>.  <\/p>\n<\/li>\n<li>\n<p>Now we need to make the call to LoadApplicationViews before jQuery Mobile gets a chance to run. So let&#8217;s insert a wait block before loading it using LAB.js:<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n    \/\/ ... shortened for brevity\r\n    \/\/ ...\r\n    .script(\"scripts\/viewmediator\/sgs.mediator.savings-forecast.js\")\r\n    .wait(function () {\r\n        LoadApplicationViews();\r\n    })\r\n    .script(\"scripts\/vendor\/jquery.mobile-1.2.0.js\")\r\n    .wait(function () {\r\n        \/\/ When ALL scripts have been loaded AND executed:              \r\n    });\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>If you look at the code for <strong>LoadApplicationViews<\/strong> you will notice that it using the <a href=\"#jqvl\">jQuery View Loader library<\/a> to identify and load all templates (scripts of type text\/html).<br \/>\nNote: make sure you at least have version 0.3 of jQuery View Loader (to ensure compatibility with jQuery Mobile).<\/p>\n<pre class=\"brush: javascript\">function LoadApplicationViews() {\r\n    if (typeof(console) != 'undefined' &amp;&amp; console) \r\n        console.info(\"LoadApplicationViews starting ...\");          \r\n    \/\/ Configure the ViewLoader\r\n    $(document).viewloader({\r\n        logLevel: \"debug\",\r\n        success: function (successfulResolution) {\r\n            InitializeViewMediators();\r\n        },          \r\n        error: function (failedResolution) {\r\n            \/\/ Loading failed somehow\r\n            if (typeof(console) != 'undefined' &amp;&amp; console) {\r\n                console.error(\"index.html page failed to load\");    \r\n            }\r\n        }\r\n    });     \r\n    if (typeof(console) != 'undefined' &amp;&amp; console) \r\n        console.info(\"LoadApplicationViews done ...\");  \r\n}\r\n<\/pre>\n<p>You should now be able to run the application and see the views appear (in the simulator or browser).<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-before-jqm.png\" alt=\"Savings Goal Simulator Before jQuery Mobile Conversion\" title=\"Savings Goal Simulator Before jQuery Mobile Conversion\" \/><br \/>\nAs you can see, even though the app actually works, it is not really usable and we will definitely need adapt the layout and styling of the web app to fit the much smaller screen dimensions of a mobile device.<\/p>\n<\/li>\n<\/ol>\n<h3>Adapting The UI For Mobile Use<a id='part5-adapting-the-ui-for-mobile-use'><\/a><\/h3>\n<p>Overall our goals will be to: <\/p>\n<ul>\n<li>optimize the layout of all panels to ensure you can see all critical elements of the app<\/li>\n<li>make input fields, buttons and controls larger so our fingers can easily work with them<\/li>\n<li>adding sliders where appropriate to allow easy change to numeric values<\/li>\n<li>using collapsible panels to maximize the use of space<\/li>\n<\/ul>\n<p>So let&#8217;s start with the &#8220;savings goal&#8221; panel first.<\/p>\n<ol>\n<li>\n<p>In Aptana Studio, expand the <strong>assets\/www\/script\/views<\/strong> folder containing all our views, then expand <strong>index<\/strong> which contains <strong>partial views<\/strong> (with an <strong>underscore prefix<\/strong>) for the index.html page, and open the <strong>_savings-goal.view.html<\/strong> page:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-views-folder.png\" alt=\"Views Folder\" title=\"Views Folder\" \/>  <\/p>\n<p>I would recommend to make a copy of the file and name it _savings-goal.<strong>web<\/strong>.view.html and change the reference to the file in <strong>index.web.html<\/strong>: <\/p>\n<pre class=\"brush: html\">&lt;script id='savings-goal-view-template'\r\n        type='text\/html'\r\n        src='scripts\/view\/index\/_savings-goal.web.view.html' &gt;&lt;\/script&gt;\r\n<\/pre>\n<p>This way we can keep a baseline of what the web version looks like while we customize the mobile version.<\/p>\n<\/li>\n<li>\n<p>Currently, it consists simply of a section with pairs of label + input (or span) tags:  <\/p>\n<pre class=\"brush: html\">&lt;section id='savings-goal-view' &gt;\r\n    &lt;label for=\"savings-goal-amount\"&gt;Savings Goal Amount:&lt;\/label&gt;\r\n    &lt;input id=\"savings-goal-amount\"  \/&gt;&lt;br\/&gt;        \r\n    &lt;label for=\"savings-max-duration\"&gt;Savings Max Duration (In Months):&lt;\/label&gt;\r\n    &lt;input id=\"savings-max-duration\" maxlength=\"3\" \/&gt;&lt;br\/&gt;      \r\n    &lt;label for=\"savings-target-per-month\"&gt;Savings Target Per Month:&lt;\/label&gt;\r\n    &lt;span id=\"savings-target-per-month\"  \/&gt;&lt;br\/&gt;\r\n&lt;\/section&gt;      \r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s wrap the first pair of label + input with a div with a data-role=&#8221;fieldcontain&#8221; so that jQuery Mobile can enhance it to make the field bigger.<\/p>\n<pre class=\"brush: html\">&lt;div id=\"goal-amount\" data-role=\"fieldcontain\"&gt;\r\n    &lt;label for=\"savings-goal-amount\"&gt;Savings Goal Amount:&lt;\/label&gt;\r\n    &lt;input id=\"savings-goal-amount\"  \/&gt;&lt;br\/&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s repeat the process for the remaining pairs:<\/p>\n<pre class=\"brush: html\">&lt;div id=\"max-duration\" data-role=\"fieldcontain\"&gt;\r\n    &lt;label for=\"savings-max-duration\"&gt;Savings Max Duration (In Months):&lt;\/label&gt;\r\n    &lt;input id=\"savings-max-duration\" maxlength=\"3\" \/&gt;\r\n&lt;\/div&gt;\r\n&lt;div id=\"target-per-month\" data-role=\"fieldcontain\"&gt;\r\n    &lt;label for=\"savings-target-per-month\"&gt;Savings Target Per Month:&lt;\/label&gt;\r\n    &lt;span id=\"savings-target-per-month\"&gt;&lt;\/span&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s add a header above all data-role=&#8221;field-contain&#8221; blocks:<\/p>\n<pre class=\"brush: html\">&lt;section id='savings-goal-view' &gt;\r\n    &lt;header&gt;\r\n        &lt;h2&gt;Goal&lt;\/h2&gt;\r\n    &lt;\/header&gt;\r\n    &lt;div id=\"goal-amount\" data-role=\"fieldcontain\"&gt;\r\n        &lt;label for=\"savings-goal-amount\"&gt;Savings Goal Amount:&lt;\/label&gt;\r\n        &lt;input id=\"savings-goal-amount\"  \/&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div id=\"max-duration\" data-role=\"fieldcontain\"&gt;\r\n        &lt;label for=\"savings-max-duration\"&gt;Savings Max Duration (In Months):&lt;\/label&gt;\r\n        &lt;input id=\"savings-max-duration\" maxlength=\"3\" \/&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div id=\"target-per-month\" data-role=\"fieldcontain\"&gt;\r\n        &lt;label for=\"savings-target-per-month\"&gt;Savings Target Per Month:&lt;\/label&gt;\r\n        &lt;span id=\"savings-target-per-month\"&gt;&lt;\/span&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/section&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>If you run the app in the simulator you will notice that sometimes the jQuery Mobile &#8220;look&#8221; appears and sometimes it does not. This is due to timing issues between the web view and jQuery Mobile. To remedy this, we&#8217;ll request a jQuery Mobile &#8220;refresh&#8221; of our page by triggering a <strong>create<\/strong> event on our page div. Let&#8217;s add that as the final script statement to execute once LAB.js has completed the processing of jquery.mobile-1.2.0.js:<\/p>\n<pre class=\"brush: javascript\">&lt;script type=\"text\/javascript\"&gt;\r\n    $LAB\r\n    \/\/ ... shortened for brevity\r\n    \/\/ ...\r\n    .script(\"scripts\/vendor\/jquery.mobile-1.2.0.js\")\r\n    .wait(function () {\r\n        \/\/ When ALL scripts have been loaded AND executed:\r\n        setTimeout(function() {\r\n            \/\/ Refresh the jQuery Mobile rendering\r\n            $('#page-home').trigger('create');\r\n        }, 50);\r\n    });\r\n&lt;\/script&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s run the app in the simulator again:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-goal-panel-increment1.png\" alt=\"jQueryMobile-ized Goal Panel Increment 1\" title=\"jQueryMobile-ized Goal Panel Increment 1\" \/>  <\/p>\n<\/li>\n<li>\n<p>Now the 2 input fields are big enough for a phone but notice the type of keyboard that comes up if you bring focus to the amount field:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-goal-panel-increment1b.png\" alt=\"Goal Amount With Text Keyboard\" title=\"Goal Amount With Text Keyboard\" \/><br \/>\nThis is a text keyboard. What we need is to tell the device to use a numeric keyboard. This is accomplished using <strong>new HTML5 attributes<\/strong>: <strong>type<\/strong>, <strong>min<\/strong>, and <strong>max<\/strong>. So let&#8217;s add them to the amount input tag:<\/p>\n<pre class=\"brush: html\">&lt;input id=\"savings-goal-amount\" type=\"number\" min=\"1\" max=\"1000\" maxlength=\"4\" \/&gt;\r\n<\/pre>\n<p>And let&#8217;s test again:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-goal-panel-increment2.png\" alt=\"Goal Amount With Numeric Keyboard\" title=\"Goal Amount With Numeric Keyboard\" \/><br \/>\nYippee, we now have a numeric keyboard, and notice that there is a Next button to navigate to the next input field!<\/p>\n<\/li>\n<li>\n<p>Let&#8217;s repeat the technique for the max number of months field:<\/p>\n<pre html\">&lt;input id=\"savings-max-duration\"  type=\"number\" min=\"1\" max=\"120\" maxlength=\"3\" \/&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Now we can use the appropriate numeric keypad to enter values, but it is still not very convenient if we want to vary the values and really test their impact. What we need is the jQuery Mobile slider.<\/p>\n<\/li>\n<li>\n<p>In AptanaStudio expand the <strong>assets\/www\/scripts\/viewmediator<\/strong> folder and let&#8217;s open the sgs.mediator.savings-goal.js module which includes the view mediator responsible for the UI interactions between the view and the viewmodel. Look for the function named sgs.mediator.savingsgoal.<strong>setupViewDataBindings<\/strong> and let&#8217;s add the following statements to initialize sliders for our input fields:<\/p>\n<pre class=\"brush: javascript\">\/\/ Add sliders to our input fields\r\n$('#savings-goal-amount').slider(); \r\n$(\"#savings-max-duration\").slider();\r\n<\/pre>\n<p>The whole function should now look like this:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.savingsgoal.setupViewDataBindings = function(renderedNodes, boundViewModel) {\r\n    \/\/ Declare the HTML element-level data bindings\r\n    $(\"#savings-goal-amount\")       .attr(\"data-bind\",\"value: savingsGoalAmount\");\r\n    $(\"#savings-max-duration\")      .attr(\"data-bind\",\"value: savingsMaxDuration\");\r\n    $(\"#savings-target-per-month\")  .attr(\"data-bind\",\"text: savingsTargetPerMonthFormatted()\");        \r\n    \/\/ Ask KnockoutJS to data-bind the view model to the view\r\n    var viewNode = $('#savings-goal-view')[0];\r\n    var viewModel = sgs.mediator.savingsgoal.getViewModel();                \r\n    ko.applyBindings(viewModel, viewNode);              \r\n    \/\/ Initialize default for value models linked to masked fields \r\n    var pageSettings = GetPageSettings();\r\n    viewModel.savingsGoalAmount(pageSettings.defaultSavingsGoal || 0);\r\n    viewModel.savingsMaxDuration(pageSettings.defaultSavingsMaxDuration || 0);      \r\n    \/\/ Add sliders to our input fields      \r\n    $('#savings-goal-amount').slider(); \r\n    $(\"#savings-max-duration\").slider();            \r\n    if (typeof(console) != 'undefined' &amp;&amp; console) \r\n            console.info(\"sgs.mediator.savingsgoal.setupViewDataBindings done!\");\r\n}\r\n<\/pre>\n<p>Now let&#8217;s re-run the app, you should see the 2 sliders<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-goal-panel-increment3.png\" alt=\"Savings Goal Panel With Sliders\" title=\"Savings Goal Panel With Sliders\" \/><br \/>\nAnd moving the sliders should automatically change the values.<\/p>\n<\/li>\n<li>\n<p>Let&#8217;s shorten our labels to save space:<\/p>\n<ul>\n<li>Shorten &#8220;Savings Goal Amount:&#8221; to just &#8220;Amount:&#8221;<\/li>\n<li>Shorten &#8220;Savings Max Duration (In Months)&#8221; to just &#8220;Period (In Months):&#8221;<\/li>\n<li>Shorten &#8220;Savings Target Per Month:&#8221; to &#8220;Per Month:&#8221;<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h3>Adapting The Stylesheets For Mobile Use<a id='part5-adapting-css-for-mobile-use'><\/a><\/h3>\n<p>So far we have only used the default style sheets from jQuery Mobile. Let&#8217;s integrate the original application.css from the web version.<\/p>\n<ol>\n<li>\n<p>In <strong>index.html<\/strong>, after the jQuery Mobile stylesheet, let&#8217;s include <strong>application.css<\/strong>:  <\/p>\n<pre class=\"brush: html\">&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" \/&gt;                   \r\n&lt;link href=\"css\/jquery.mobile\/jquery.mobile-1.2.0.css\" rel=\"stylesheet\" type=\"text\/css\"  \/&gt;  \r\n&lt;link href=\"css\/jquery.mobile\/jquery.mobile.structure-1.2.0.css\" rel=\"stylesheet\" type=\"text\/css\"  \/&gt;   \r\n&lt;link href=\"css\/application.css\" rel=\"stylesheet\" type=\"text\/css\"  \/&gt;   \r\n<\/pre>\n<\/li>\n<li>\n<p>To allow the application to adapt to different form factors, we will create different CSS files and direct the browser to use the appropriate stylesheet based on the media directive.<\/p>\n<\/li>\n<li>\n<p>Under <strong>assets\/www\/css<\/strong>, let&#8217;s create two new CSS files:<\/p>\n<ul>\n<li>application.<strong>phone<\/strong>.css <\/li>\n<li>application.<strong>tablet<\/strong>.css <\/li>\n<ul>\n<\/li>\n<li>\n<p>In index.html, after the include for application.css, let&#8217;s add two new include statements, each specifying what values the <strong>media query<\/strong> should match.<\/p>\n<pre class=\"brush: html\">&lt;link href=\"css\/application.tablet.css\" type=\"text\/css\" rel=\"stylesheet\" \r\n        media=\"only screen and (max-device-width: 1024px)\" \/&gt;\r\n<\/pre>\n<p>This stylesheet should be used if the maximum device width is 1024px which usually means a tablet.<\/p>\n<pre class=\"brush: html\">&lt;link href=\"css\/application.iphone.css\" type=\"text\/css\" rel=\"stylesheet\" \r\n        media=\"only screen and (max-device-width: 480px)\" \/&gt;        \r\n<\/pre>\n<p>This stylesheet should be used if the maximum device width is 480px which usually means a phone.<\/p>\n<\/li>\n<li>\n<p>Now let&#8217;s tweak our original application.css<\/p>\n<ul>\n<li>\n<p>Let&#8217;s remove the styles details for the section element:  <\/p>\n<pre class=\"brush: css\">section\r\n{\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s remove the standard padding for the h2 element:  <\/p>\n<pre class=\"brush: css\">section header h2\r\n{\r\n    margin: 0;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s remove the styles details for the #savings-goal-view element:<\/p>\n<pre class=\"brush: css\">#savings-goal-view\r\n{   \r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust the styles details for the #savings-goal-view label rule:  <\/p>\n<pre class=\"brush: css\">#savings-goal-view label\r\n{\r\n    display: inline-block;\r\n    text-align: right;\r\n    width: 90px;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust the styles details for the #consumption-scenarios-view rule:  <\/p>\n<pre class=\"brush: css\">#consumption-scenarios-view\r\n{\r\n    margin: 5px 0;\r\n} \r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust the styles details for the #savings-forecast-view rule:  <\/p>\n<pre class=\"brush: css\">#savings-forecast-view\r\n{\r\n    margin: 5px 0;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust a new rule to override the border and padding used in our jQuery Mobile ui-field-contain divs:  <\/p>\n<pre class=\"brush: css\">.ui-field-contain\r\n{\r\n    border: none;\r\n    padding: 0;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust a new rule to override the font used in our jQuery Mobile labels:  <\/p>\n<pre class=\"brush: css\">.ui-field-contain label\r\n{\r\n    font-size: 10px;\r\n    margin: 2px 2px;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s adjust a new rule to override the width used in our jQuery Mobile sliders:  <\/p>\n<pre class=\"brush: css\">div.ui-slider\r\n{\r\n    width: 30%;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s create an override rule for the collapsible panels:<\/p>\n<pre class=\"brush: css\">.ui-collapsible-inset .ui-collapsible-content\r\n{\r\n    border-color: black;\r\n    border-style: solid;\r\n    border-width: 1px;\r\n}\r\n<\/pre>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Let&#8217;s run the app in the browser and simulator to see our new styling:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-goal-panel-increment4.png\" alt=\"Savings Goal Panel With New Styling\" title=\"Savings Goal Panel With New Styling\" \/>  <\/p>\n<\/li>\n<\/ol>\n<p>At this point we could continue adapting the look of the Savings Goal panel, by adjusting the CSS styles to make it fit a little bit more tightly. The easiest way to do that is to use your trusted browser developer tool to learn what styles are being used by jQuery Mobile and to experiment.<\/p>\n<p>I will leave this as an exercise for the reader. <\/p>\n<h3>Adapting The Consumption Scenarios View For Mobile Use<a id='part5-adapting-consumption-scenario-view-for-mobile-use'><\/a><\/h3>\n<p>In the web version of the application, the different type of coffee consumption options consists or set of radio buttons layed out in a table. But at minimum for a phone version of the app we need to <strong>conserve the vertical space<\/strong> so we can see the goals, a subset of the consumption options, and the savings forecast. <\/p>\n<p>So let&#8217;s use <strong>collapsible panels<\/strong> for each type of consumption option such as &#8220;type&#8221; (of beverage), size, and frequency. This way as a user you can expand the option you want to play with, tweak the current and proposed settings, see the impact on the forecast, and repeat the process.<\/p>\n<ol>\n<li>\n<p>First, let&#8217;s make a copy of the <strong>_consumption-scenarios.view.html<\/strong> file under assets\/www\/scripts\/view\/index and rename the copy to _consumption-scenarios.<strong>web<\/strong>.view.html and change the reference to the file in <strong>index.web.html<\/strong>:<\/p>\n<pre class=\"brush: html\">&lt;script id='consumption-scenarios-view-template'\r\n        type='text\/html'\r\n        src='scripts\/view\/index\/_consumption-scenarios.web.view.html' &gt;&lt;\/script&gt;\r\n<\/pre>\n<p>Again we now have a baseline of the original web version.<\/p>\n<\/li>\n<li>\n<p>Now open the <strong>_consumption-scenarios.view.html<\/strong> file in Aptana Studio. Locate the header tag, and let&#8217;s wrap the &#8220;Weekly Coffee Consumption&#8221; title in an h2 tag:<\/p>\n<pre class=\"brush: html\">&lt;header&gt;\r\n    &lt;h2&gt;Weekly Coffee Consumption&lt;\/h2&gt;\r\n&lt;\/header&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Right below the header but before the table, let&#8217;s add a div of [jQuery Mobile] data-role &#8220;<strong>collapsible-set<\/strong>&#8221; indicating that contained elements of data-role &#8220;collapsible&#8221; can be grouped together.<\/p>\n<pre class=\"brush: html\">&lt;div id=\"weekly-consumption-accordion\" data-role=\"collapsible-set\"&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s add a div of data-role &#8220;<strong>collapsible<\/strong>&#8221; for our consumption type option. The div will contain a h3 for the panel title, followed by a table element to hold the details of our option<\/p>\n<pre class=\"brush: html\">&lt;div id=\"weekly-consumption-accordion\" data-role=\"collapsible-set\"&gt;\r\n    &lt;div data-role=\"collapsible\" data-theme=\"a\"  data-mini=\"true\" &gt;\r\n        &lt;h3&gt;Consumption Type&lt;\/h3&gt;\r\n        &lt;table&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;Current Habits&lt;\/th&gt;\r\n                &lt;th&gt;Proposed Change&lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n            &lt;tr&gt;\r\n            &lt;\/tr&gt;\r\n        &lt;\/table&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Now we can move the markup related to the drink types from the original table over to the second row of our new table within the collapsible div:<\/p>\n<pre class=\"brush: html\">&lt;div id=\"weekly-consumption-accordion\" data-role=\"collapsible-set\"&gt;\r\n    &lt;div data-role=\"collapsible\" data-theme=\"a\"  data-mini=\"true\" &gt;\r\n        &lt;h3&gt;Consumption Type&lt;\/h3&gt;\r\n        &lt;table&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;Current Habits&lt;\/th&gt;\r\n                &lt;th&gt;Proposed Change&lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n            &lt;tr&gt;\r\n                &lt;td&gt;\r\n                    &lt;div id=\"current-drink-type\"&gt;\r\n                        &lt;input type=\"radio\" id=\"current-drink-regular\"  name=\"CurrentDrinkType\" value=\"Regular\" \/&gt;\r\n                            &lt;label for=\"current-drink-regular\"&gt;Regular&lt;\/label&gt;&lt;br\/&gt;\r\n                        &lt;input type=\"radio\" id=\"current-drink-latte\"  name=\"CurrentDrinkType\" value=\"Latte\" \/&gt;\r\n                            &lt;label for=\"current-drink-latte\"&gt;Latte&lt;\/label&gt;&lt;br\/&gt;\r\n                        &lt;input type=\"radio\" id=\"current-drink-espresso\"  name=\"CurrentDrinkType\" value=\"Espresso\" \/&gt;\r\n                            &lt;label for=\"current-drink-espresso\"&gt;Espresso&lt;\/label&gt;&lt;br\/&gt;\r\n                    &lt;\/div&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td&gt;\r\n                    &lt;div id=\"proposed-drink-type\"&gt;\r\n                        &lt;input type=\"radio\" id=\"proposed-drink-regular\"  name=\"ProposedDrinkType\" value=\"Regular\" \/&gt;\r\n                            &lt;label for=\"proposed-drink-regular\"&gt;Regular&lt;\/label&gt;&lt;br\/&gt;\r\n                        &lt;input type=\"radio\" id=\"proposed-drink-latte\"  name=\"ProposedDrinkType\" value=\"Latte\" \/&gt;\r\n                            &lt;label for=\"proposed-drink-latte\"&gt;Latte&lt;\/label&gt;&lt;br\/&gt;\r\n                        &lt;input type=\"radio\" id=\"proposed-drink-espresso\"  name=\"ProposedDrinkType\" value=\"Espresso\" \/&gt;\r\n                            &lt;label for=\"proposed-drink-espresso\"&gt;Espresso&lt;\/label&gt;&lt;br\/&gt;\r\n                    &lt;\/div&gt;\r\n                &lt;\/td&gt;\r\n            &lt;\/tr&gt;\r\n        &lt;\/table&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s remove all the &lt;br\/&gt; tags since we no longer need them since jQuery Mobile is enhancing our DOM and styling new elements. <\/p>\n<\/li>\n<li>\n<p>Let&#8217;s run in the browser and the simulator.<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-scenarios-panel-increment1.png\" alt=\"Collapsible Consumption Type Panel\" title=\"Collapsible Consumption Type Panel\" \/>  <\/p>\n<p>You will notice a couple things:<\/p>\n<ul>\n<li>\n<p>The collapsible element does expand and collapse when we toggle it<\/p>\n<\/li>\n<li>\n<p>jQuery Mobile has rendered our radio buttons as regular buttons. Apparently jQuery Mobile require a radio button group to be codified as a <strong>fieldset<\/strong> with a data-role of &#8220;<strong>controlgroup<\/strong>&#8220;.<br \/>\nSo let&#8217;s replace our div for the current-drink-type:<\/p>\n<pre class=\"brush: html\">&lt;div id=\"current-drink-type\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>by a fieldset &#8211; note that we&#8217;ll request a &#8220;mini&#8221; version (using data-mini=&#8221;true&#8221;) to minimize the vertical space taken by the resulting jQuery Mobile radio button group:<\/p>\n<pre class=\"brush: html\">&lt;fieldset id=\"current-drink-type\" data-role=\"controlgroup\" data-mini=\"true\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/fieldset&gt;\r\n<\/pre>\n<p>Let&#8217;s re-run:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-scenarios-panel-increment2.png\" alt=\"Collapsible Consumption Type Panel\" title=\"Collapsible Consumption Type Panel\" \/>   <\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Now let&#8217;s repeat the process for the other 2 types of consumption options (Size and Frequency):<\/p>\n<ul>\n<li>Add a div of data-role &#8220;<strong>collapsible<\/strong>&#8221; for our consumption option. The div will contain a h3 for the panel title, followed by a table element to hold the details of our option.<\/li>\n<li>Move the markup related to the consumption option from the original table over to the second row of our new table within the collapsible div<\/li>\n<li>Change the div containing the radio buttons into a fieldset of data-role &#8220;radiobuttongroup&#8221;<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>For the frequency collapsible panel, the &#8220;Other:&#8221; option has an input field associated with it. So we will need to wrap the input field with a div of data-role &#8220;field-contain&#8221;, and add HTML5 type, min and max attributes:<\/p>\n<pre class=\"brush: html\">&lt;div data-role=\"fieldcontain\"&gt;\r\n    &lt;input type=\"text\" \r\n            id=\"current-custom-frequency\" \r\n            name=\"CurrentCustomFrequency\"\r\n            type=\"number\" min=\"1\" max=\"999\" maxlength=\"3\" \/&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>To allow the input field to be positioned to the right of the radio button, we will need to move the markup for data-role &#8220;fieldcontain&#8221; inside the label tag of the radio button like so:<\/p>\n<pre class=\"brush: html\">&lt;label for=\"current-frequency-other\"&gt;Other:\r\n    &lt;div data-role=\"fieldcontain\"&gt;\r\n        &lt;input type=\"text\" \r\n                id=\"current-custom-frequency\" \r\n                name=\"CurrentCustomFrequency\"\r\n                type=\"number\" min=\"1\" max=\"999\" maxlength=\"3\" \/&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/label&gt;\r\n<\/pre>\n<p>If you run in the browser or simulator, you will notice that the input field is not positioned where we would like it. So let&#8217;s add a CSS class on the div <strong>data-role &#8220;fieldcontain&#8221;<\/strong> named &#8220;<strong>other-input<\/strong>&#8220;:  <\/p>\n<pre class=\"brush: html\">    &lt;div data-role=\"fieldcontain\" class=\"other-input\"&gt;\r\n        &lt;input type=\"text\" \r\n                id=\"current-custom-frequency\" \r\n                name=\"CurrentCustomFrequency\"\r\n                type=\"number\" min=\"1\" max=\"999\" maxlength=\"3\" \/&gt;\r\n    &lt;\/div&gt;\r\n<\/pre>\n<p>And let&#8217;s define the new class in <strong>application.css<\/strong>: <\/p>\n<pre class=\"brush: css\">.other-input\r\n{\r\n    display: inline-block;\r\n    left: -20px;\r\n    margin: 0 0 5px 0;\r\n    top: -10px;\r\n    width: 30px;    \r\n}\r\n\r\n\r\n.other-input input\r\n{\r\n    height: 24px;\r\n}\r\n<\/pre>\n<p>Let&#8217;s re-run:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-scenarios-panel-increment3.png\" alt=\"Collapsible Consumption Frequency Panel\" title=\"Collapsible Consumption Frequency Panel\" \/>   <\/p>\n<p>Now the positioning is much better!<\/p>\n<\/li>\n<li>\n<p>For the number of drinks per day, let&#8217;s create a <strong>separate collapsible panel<\/strong> like so:<\/p>\n<pre class=\"brush: html\">    &lt;div data-role=\"collapsible\" data-theme=\"a\"  data-mini=\"true\" &gt;\r\n        &lt;h3&gt;Drinks Per Day&lt;\/h3&gt;\r\n        &lt;table&gt;\r\n            &lt;tr&gt;\r\n                &lt;th&gt;Current Habits&lt;\/th&gt;\r\n                &lt;th&gt;Proposed Change&lt;\/th&gt;\r\n            &lt;\/tr&gt;\r\n            &lt;tr&gt;\r\n                &lt;td&gt;\r\n                    &lt;input type=\"text\" id=\"current-drinks-per-day\" name=\"CurrentDrinksPerDay\" \/&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td&gt;\r\n                    &lt;input type=\"text\" id=\"proposed-drinks-per-day\" name=\"ProposedDrinksPerDay\" \/&gt;\r\n                &lt;\/td&gt;\r\n            &lt;\/tr&gt;               \r\n        &lt;\/table&gt;\r\n    &lt;\/div&gt;\r\n<\/pre>\n<p>Let&#8217;s re-run:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-scenarios-panel-increment4.png\" alt=\"Collapsible Drinks Per Day Panel\" title=\"Collapsible Drinks Per Day Panel\" \/>   <\/p>\n<\/li>\n<li>\n<p>Now we need to cleanup the bottom remaining table in _consumption-scenarios.view.html.<br \/>\nLet&#8217;s give the table an id of <strong>consumption-summary-table<\/strong>:  <\/p>\n<pre class=\"brush: html\">&lt;table id=\"consumption-summary-table\"&gt;\r\n&lt;\/table&gt;\r\n<\/pre>\n<p>Let&#8217;s cleanup the obsolete table header and let&#8217;s reuse it for our weekly summary.<\/p>\n<pre class=\"brush: html\">&lt;thead&gt;\r\n    &lt;tr&gt;\r\n        &lt;th&gt;Weekly Summary&lt;\/th&gt;\r\n        &lt;th&gt;Current Habits&lt;\/th&gt;\r\n        &lt;th&gt;Proposed Change&lt;\/th&gt;\r\n    &lt;\/tr&gt;\r\n&lt;\/thead&gt;\r\n<\/pre>\n<p>And remove the obsolete column titles:<\/p>\n<pre class=\"brush: html\">&lt;tbody&gt;\r\n    &lt;tr&gt;                    \r\n        &lt;td&gt;Type:&lt;\/td&gt;\r\n    &lt;\/tr&gt;       \r\n    &lt;tr&gt;\r\n        &lt;td&gt;Size:&lt;\/td&gt;\r\n    &lt;\/tr&gt;           \r\n    &lt;tr&gt;\r\n        &lt;td&gt;Frequency:&lt;\/td&gt;\r\n    &lt;\/tr&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/tbody&gt;\r\n<\/pre>\n<p>Let&#8217;s define styling for consumption-summary-table in application.css:<\/p>\n<pre class=\"brush: html\">#consumption-summary-table\r\n{\r\n    border-radius: 8px;\r\n    background-color: white;\r\n    width: 100%;\r\n}\r\n\r\n\r\n#consumption-summary-table th\r\n{\r\n    border-bottom: 1px solid Grey;  \r\n}\r\n<\/pre>\n<p>Let&#8217;s re-run:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-scenarios-panel-increment5.png\" alt=\"Collapsible Consumption Option Panels\" title=\"Collapsible Consumption Option Panels\" \/>   <\/p>\n<p>You will notice that we&#8217;re pretty close to our goal of optimizing for the vertical height of a phone!  <\/p>\n<\/li>\n<\/ol>\n<h3>Adapting The Savings Forecast View For Mobile Use<a id='part5-adapting-savings-forecast-view-for-mobile-use'><\/a><\/h3>\n<ol>\n<li>\n<p>First, let&#8217;s make a copy of the <strong>_savings-forecast.view.html<\/strong> file under assets\/www\/scripts\/view\/index and rename the copy to _savings-forecast.<strong>web<\/strong>.view.html and change the reference to the file in <strong>index.web.html<\/strong>:<\/p>\n<pre class=\"brush: html\">&lt;script id='savings-forecast-view-template'\r\n        type='text\/html'\r\n        src='scripts\/view\/index\/__savings-forecast.web.view.html' &gt;&lt;\/script&gt;\r\n<\/pre>\n<p>Again we now have a baseline of the original web version.<\/p>\n<\/li>\n<li>\n<p>Now open the <strong>_savings-forecast.view.html<\/strong> file in Aptana Studio. To maximize vertical space, let&#8217;s turn the current markup into a horizontal table where the labels become column headers. We&#8217;ll also shorten the column headers and the unit of measure in each data cell:<\/p>\n<pre class=\"brush: html\">&lt;section id='savings-forecast-view'&gt;\r\n    &lt;table id='savings-forecast-table'&gt; \r\n        &lt;tr&gt;\r\n            &lt;th&gt;Forecast&lt;\/th&gt;\r\n            &lt;th&gt;Variance&lt;\/th&gt;\r\n            &lt;th&gt;Months&lt;\/th&gt;\r\n        &lt;\/tr&gt;\r\n        &lt;tr&gt;\r\n            &lt;td&gt;&lt;span id=\"savings-forecast-per-month\" &gt;&lt;\/span&gt;\/m&lt;\/td&gt;\r\n            &lt;td&gt;&lt;span id=\"forecast-variance-per-month\"&gt;&lt;\/span&gt;\/m&lt;\/td&gt;\r\n            &lt;td&gt;&lt;span id=\"time-to-goal-in-months\"&gt;&lt;\/span&gt;m&lt;\/td&gt;\r\n        &lt;\/tr&gt;\r\n    &lt;\/table&gt;\r\n&lt;\/section&gt; \r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s define styles for our new table in application.css:  <\/p>\n<pre class=\"brush: css\">#savings-forecast-table {\r\n    width: 100%;\r\n}\r\n\r\n\r\n#savings-forecast-table tr td {\r\n    text-align: center;\r\n}\r\n\r\n\r\n#savings-forecast-view input\r\n{\r\n    width: 30px;\r\n}\r\n<\/pre>\n<p>Run the app:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-forecast-panel-increment1.png\" alt=\"Forecast Panel\" title=\"Forecast Panel\" \/>   <\/p>\n<\/li>\n<li>\n<p>Ideally we would like the forecast panel to be visible at all times. An idea is to integrate it in the footer of the page and &#8220;<strong>pin<\/strong>&#8221; the footer at the bottom of the screen.<br \/>\nSo open the <strong>index.html<\/strong> page, and let&#8217;s move the script include for the <strong>savings-forecast-view-template<\/strong> and the actual <strong>savings-forecast-view-container<\/strong> div into the footer:<\/p>\n<pre class=\"brush: html\">&lt;div data-role=\"footer\" data-position=\"fixed\"  data-theme=\"e\"&gt;\r\n    &lt;script id='savings-forecast-view-template'\r\n            type='text\/html'\r\n            src='scripts\/view\/index\/_savings-forecast.view.html' &gt;&lt;\/script&gt;\r\n    &lt;div id='savings-forecast-view-container'\r\n            data-bind='template: { name: \"savings-forecast-view-template\", afterRender: sgs.mediator.savingsforecast.setupViewDataBindings } ' &gt;&lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>To &#8220;<strong>pin<\/strong>&#8221; the header, let&#8217;s add a <strong>data-position=&#8221;fixed&#8221;<\/strong> and <strong>data-fullscreen=&#8221;true&#8221;<\/strong> as attributes on the footer. And while were at it let&#8217;s specify a different jQuery Mobile theme for the footer using <strong>data-theme=&#8221;e&#8221;<\/strong>.<\/p>\n<pre class=\"brush: html\">&lt;div data-role=\"footer\" data-position=\"fixed\" data-fullscreen=\"true\" data-theme=\"e\"&gt;\r\n    &lt;!-- --&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>Let&#8217;s refresh:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-forecast-panel-increment2.png\" alt=\"Forecast Panel\" title=\"Forecast Panel\" \/>   <\/p>\n<p>This is much nicer!<\/p>\n<\/li>\n<\/ol>\n<h3>More Visual Tweaks<a id='part5-more-visual-tweaks'><\/a><\/h3>\n<ol>\n<li>\n<p>In the <strong>index.html<\/strong>, let&#8217;s customize the jQuery Mobile <strong>theme<\/strong> to be used on all its widgets<\/p>\n<pre class=\"brush: html\">&lt;div id=\"page-home\" data-role=\"page\" data-content-theme=\"b\"&gt;\r\n    &lt;!-- ... --&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s increase the horizontal <strong>padding<\/strong> for all section elements:<\/p>\n<pre class=\"brush: css\">section\r\n{\r\n    padding: 0px 2px;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s tweak the <strong>height<\/strong> of the <strong>#page-home<\/strong> div and its <strong>data-role=&#8221;content&#8221;<\/strong> div:<\/p>\n<pre class=\"brush: css\">#page-home \r\n{\r\n    height: 100%;\r\n    overflow: hidden;\r\n}\r\n\r\n\r\n#page-home div.ui-content\r\n{\r\n    height: 88%;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s move the &#8220;Per Month&#8221; elements from the bottom of the savings goal panel to the section header:<\/p>\n<pre class=\"brush: html\">&lt;header&gt;\r\n    &lt;h2&gt;Goal&lt;\/h2&gt;\r\n    &lt;div id=\"target-per-month\" data-role=\"fieldcontain\"&gt;\r\n        &lt;label for=\"savings-target-per-month\"&gt;Per Month:&lt;\/label&gt;\r\n        &lt;span id=\"savings-target-per-month\"&gt;&lt;\/span&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/header&gt;\r\n<\/pre>\n<p>And let&#8217;s add the following styles to application.css:  <\/p>\n<pre class=\"brush: css\">#savings-goal-view header h2 {\r\n    display: inline-block; \r\n}\r\n\r\n\r\n#target-per-month {\r\n  display: inline-block;\r\n  right: -114px;\r\n}\r\n\r\n\r\n#savings-goal-view #target-per-month label\r\n{\r\n    min-width: 30px;\r\n}\r\n\r\n\r\n#savings-goal-view #savings-target-per-month {\r\n    font-size: 0.8em;\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Let&#8217;s remove the word &#8220;Consumption&#8221; from our collapsible panels.<\/p>\n<\/li>\n<li>\n<p>Let&#8217;s re-run:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-newlook.png\" alt=\"New Look\" title=\"New Look\" \/>   <\/p>\n<p>The look is now a bit more &#8220;professional&#8221;, the visual elements stand out and the vertical positioning is optimized.<\/p>\n<\/li>\n<\/ol>\n<h3>UI &#8220;Feel&#8221; \/ Behavior Tweaks<a id='part5-ui-feel-behavior-tweaks'><\/a><\/h3>\n<p>Originally the web app depended on jQuery UI, but since we&#8217;re usin jQuery Mobile instead, we will need to tweak the mediators associated with our panels.<\/p>\n<ol>\n<li>\n<p>Two view mediators use the jQuery UI <strong>highlight<\/strong> effect to provide a visual indication of key changes. But now that we have sliders, the effect would take too long since it would be triggered on each mouse move. So let&#8217;s remove the effect.   <\/p>\n<\/li>\n<li>\n<p>Open <strong>sgs.mediator.savings-goal.js<\/strong> under <strong>assets\\www\\scripts\\viewmediator<\/strong> and look for a call to the <strong>effect<\/strong> function:<\/p>\n<pre class=\"brush: javascript\">viewModel.savingsTargetPerMonthFormatted.subscribe(function() {\r\n    $(\"#savings-target-per-month\")\r\n        .effect('highlight', { color: 'LightGreen' }, 3000); \/\/ for 3 seconds\r\n});\r\n<\/pre>\n<p>and let&#8217;s remove the overall statement altogether:<\/p>\n<pre class=\"brush: javascript\">viewModel.savingsTargetPerMonthFormatted.subscribe(function() {\r\n    \/\/ Placeholder for future use\r\n});\r\n<\/pre>\n<\/li>\n<li>\n<p>Open <strong>sgs.mediator.consumption-scenarios.js<\/strong> and repeat the process:  <\/p>\n<pre class=\"brush: javascript\">viewModel.savingsPerWeekFormatted.subscribe(function() {\r\n    \/\/ Placeholder for future use\r\n});\r\n<\/pre>\n<\/li>\n<\/ol>\n<p>So if you had been noticing exceptions for the effect call in the browser console, now these are a thing of the past.<\/p>\n<h3>Adding An Other jQuery Mobile Page<a id='part5-adding-another-jquery-mobile-page'><\/a><\/h3>\n<p>Currently our application has only one jQuery Mobile &#8220;<strong>logical page<\/strong>&#8220;, that is, a div with a <strong>data-role=&#8221;page&#8221;<\/strong>. It would be nice if we could have another page to edit the coffee beverage list prices! jQuery Mobile allow the definition of multiple pages and provide associated navigation mechanisms. So let&#8217;s see how to do that.<\/p>\n<ol>\n<li>\n<p>First, in the <strong>index.html<\/strong>, before the end of the body tag, let&#8217;s add the shell for our new page:<\/p>\n<pre class=\"brush: html\">&lt;div id=\"coffee-pricing-dialog\"\r\n     data-role=\"page\" \r\n     data-content-theme=\"b\" \r\n     data-url=\"edit-coffee-pricing.html\" &gt;\r\n        Shell for our price list!\r\n       &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>We need a button to navigate to the new page. Let&#8217;s edit <strong>_consumption-scenarios.view.html<\/strong> and add a button (div with <strong>data-role=&#8221;button&#8221;<\/strong>) in the <strong>header<\/strong>, after the <strong>h2<\/strong> title:<\/p>\n<pre class=\"brush: html\">&lt;section id='consumption-scenarios-view'&gt;\r\n    &lt;header&gt;\r\n        &lt;h2&gt;Weekly Coffee Consumption&lt;\/h2&gt;\r\n        &lt;a id=\"edit-pricing-button\" href=\"#coffee-pricing-dialog\" class=\"ui-btn-right\"\r\n            data-rel=\"dialog\" \r\n            data-role=\"button\" \r\n            data-mini=\"true\"\r\n            data-icon=\"gear\"\r\n            data-theme=\"e\" &gt;Pricing&lt;\/a&gt;\r\n    &lt;\/header&gt;\r\n    &lt;!-- ... ---&gt; \r\n&lt;\/section&gt;\r\n<\/pre>\n<p>You will notice that we specified a <strong>data-rel<\/strong> attribute with a value of &#8220;<strong>dialog<\/strong>&#8220;. This tells jQuery Mobile that we would like a pop-up transition effect and a dialog theme: dark background, rounded corners, etc.. See the <a href=\"http:\/\/jquerymobile.com\/demos\/1.2.0\/docs\/pages\/dialog\/index.html\">Dialog documentation<\/a> for more details.<\/p>\n<p>If you run the app in the simulator, you should see the new Pricing button:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-button.png\" alt=\"New Pricing Button\" title=\"New Pricing Button\" \/>   <\/p>\n<p>And clicking on it will cause the navigation to the new page to occur:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-panel-shell.png\" alt=\"New Pricing Panel Shell\" title=\"New Pricing Panel Shell\" \/>   <\/p>\n<p>If you hit the back button, the page-home page will be displayed again (without any of our values to be lost &#8211; since the pages are just hidden\/shown during the navigation process).<\/p>\n<\/li>\n<li>\n<p>Like for other panels, let&#8217;s externalize the markup for our new pricing page. So let&#8217;s create a new markup folder (one for each page) under <strong>assets\/www\/scripts\/<\/strong>views\/named &#8220;<strong>pricing<\/strong>&#8220;, and then create a file named <strong>index.view.html<\/strong>, an cut\/paste our shell message inside the following template:<\/p>\n<pre class=\"brush: html\">&lt;section id='coffee-pricing-view'&gt;\r\n    &lt;header data-role=\"header\"&gt;\r\n        &lt;h3&gt;Edit Pricing&lt;\/h3&gt;\r\n    &lt;\/header&gt;           \r\n    &lt;div data-role=\"content\"&gt;\r\n        Shell for our price list!\r\n    &lt;\/div&gt;\r\n&lt;\/section&gt;\r\n<\/pre>\n<\/li>\n<li>\n<p>To render the externalized view we have to:<\/p>\n<ul>\n<li>include a text\/html template script reference in the main index.html file<\/li>\n<li>create a container div data-bound to the template<\/li>\n<li>ask KnockoutJS to render it. <\/li>\n<\/ul>\n<p>So remove the &#8220;Shell for our price list!&#8221; content from the <strong>coffee-pricing-dialog<\/strong> div and replace it with:<\/p>\n<pre class=\"brush: html\">&lt;script id='coffee-pricing-view-template'\r\n        type='text\/html'\r\n        src='scripts\/view\/pricing\/index.view.html' &gt;\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>This allows the inclusion of our new jQuery template in the app.<br \/>\nAnd right below let&#8217;s add our div container:  <\/p>\n<pre class=\"brush: html\">&lt;div id='coffee-pricing-view-container'\r\n        data-bind='template: { name: \"coffee-pricing-view-template\", afterRender: sgs.mediator.coffeepricing.setupViewDataBindings} ' &gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>Note: the <strong>afterRender<\/strong> callback references a <strong>setupViewDataBindings<\/strong> function we have not yet created in the sgs.mediator.coffeepricing module. So if you run the app now, the content of the view will not be rendered. <\/p>\n<\/li>\n<li>\n<p>Open up the <strong>sgs.mediator.coffeepricing.js<\/strong> module under <strong>assets\/www\/scripts\/viewmediator<\/strong> and let&#8217;s create a shell for the <strong>setupViewDataBindings<\/strong> function:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.setupViewDataBindings = function(renderedNodes, boundViewModel) {\r\n\r\n\r\n}\r\n<\/pre>\n<p>Then let&#8217;s update the <strong>createViewMediator<\/strong> function to request <strong>data binding<\/strong> of the coffee pricing <strong>view container<\/strong> with the <strong>template<\/strong>. This will cause KnockoutJS to render the template using jQuery Template:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.createViewMediator = function (pageSettings) {\r\n    \/\/ Create the view Pricing Editor view-specific view model\r\n    var viewModel = sgs.model.coffeepricing.initializeViewModel(pageSettings);\r\n    \/\/ Save the view model\r\n    sgs.mediator.coffeepricing.setViewModel(viewModel);         \r\n    \/\/ Ask KnockoutJS to data-bind the view model to the view\r\n    var viewNode = $('#coffee-pricing-view-container')[0];\r\n    var viewModel = sgs.mediator.coffeepricing.getViewModel();\r\n    ko.applyBindings(viewModel, viewNode);      \r\n    if (typeof(console) != 'undefined' &amp;&amp; console) \r\n        console.info(\"sgs.mediator.coffeepricing ready!\");  \r\n}\r\n<\/pre>\n<p>Now we can expand the code for <strong>setupViewDataBindings<\/strong> to data-bind the coffee pricing <strong>view<\/strong> div with our KnockoutJS coffee pricing <strong>view model<\/strong>:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.setupViewDataBindings = function(renderedNodes, boundViewModel) {\r\n    \/\/ Apply the bindings\r\n    var viewNode = $('#coffee-pricing-view')[0];\r\n    var viewModel = sgs.mediator.coffeepricing.getViewModel();\r\n    ko.applyBindings(viewModel, viewNode);\r\n    \/\/ Get jQuery Mobile to refresh\/enhance the page        \r\n    $('#coffee-pricing-dialog').trigger('create');\r\n}\r\n<\/pre>\n<p>Now if run the app and click on the pricing button we should see our new pricing shell page:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-panel-externalized.png\" alt=\"Externalized Pricing Panel Shell\" title=\"Externalized Pricing Panel Shell\" \/>   <\/p>\n<\/li>\n<li>\n<p>Let&#8217;s add some input fields to our pricing\/<strong>index.view.html<\/strong> view to capture the pricing data:<\/p>\n<pre class=\"brush: html\">&lt;section id='coffee-pricing-view'&gt;\r\n    &lt;header data-role=\"header\"&gt;\r\n        &lt;h3&gt;Edit Pricing&lt;\/h3&gt;\r\n    &lt;\/header&gt;           \r\n    &lt;div data-role=\"content\"&gt;\r\n        &lt;div class=\"ui-bar ui-bar-b\" data-position=\"inline\"&gt;\r\n            Regular Coffee\r\n        &lt;\/div&gt;\r\n        &lt;div data-role=\"fieldcontain\"&gt;\r\n            &lt;label for=\"pricing-regular-tall\"&gt;Tall:&lt;\/label&gt;\r\n            &lt;input id=\"pricing-regular-tall\" type=\"number\" min=\"1\" max=\"5\" maxlength=\"4\" \/&gt;\r\n        &lt;\/div&gt;              \r\n        &lt;div data-role=\"fieldcontain\"&gt;\r\n            &lt;label for=\"pricing-regular-grande\"&gt;Grande:&lt;\/label&gt;\r\n            &lt;input id=\"pricing-regular-grande\" type=\"number\" min=\"1\" max=\"5\" maxlength=\"4\" \/&gt;\r\n        &lt;\/div&gt;              \r\n        &lt;div data-role=\"fieldcontain\"&gt;\r\n            &lt;label for=\"pricing-regular-venti\"&gt;Venti:&lt;\/label&gt;\r\n            &lt;input id=\"pricing-regular-venti\" type=\"number\" min=\"1\" max=\"5\" maxlength=\"4\" \/&gt;\r\n        &lt;\/div&gt;      \r\n    &lt;\/div&gt;\r\n&lt;\/section&gt;  \r\n<\/pre>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-panel-regular-coffee.png\" alt=\"Regular Coffee Pricing Shell\" title=\"Regular Coffee Pricing Shell\" \/>   <\/p>\n<p>You can repeat the process for the beverages like caffe latte and espresso.    <\/p>\n<p>If you run the app you will see an empty dialog since we have not created a value models for each of the pricing options in the pricing view model, nor  added the data binding for the various fields yet.  <\/p>\n<\/li>\n<li>\n<p>If we look at the current definition of our coffee pricing <strong>view model<\/strong>, we&#8217;ll see that it includes only one <strong>value model<\/strong>: &#8220;<strong>pricing<\/strong>&#8220;:<\/p>\n<pre class=\"brush: javascript\">sgs.model.coffeepricing.initializeViewModel = function (pageSettings) {  \r\n    \/\/ ...          \r\n    \/\/ Lazy-initialize the price list\r\n    var priceList = sgs.model.coffeepricing.getPriceList();         \r\n    var viewModel = {\r\n        pricing:        ko.observable(priceList)\r\n    }  \r\n    \/\/ ...          \r\n    return viewModel;  \r\n}\r\n<\/pre>\n<p>If we evaluate \/ inspect the both the <strong>view model<\/strong> and the <strong>pricing value model<\/strong> in the browser tools console we&#8217;ll see:  <\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-viewmodel-current.png\" alt=\"Current Coffee Pricing ViewModel\" title=\"Current Coffee Pricing ViewModel\" \/>   <\/p>\n<p>But so the coffee pricing view model does not have a separate <strong>value model<\/strong> for each <strong>property<\/strong> of the pricing value model. We have two options: a) declare a value model for each property, or b) leverage the dynamic nature of Javascript to dynamically declare a new value model  for each property of our pricing object. Let&#8217;s choose the latter approach.<br \/>\nLet&#8217;s add a new function named <strong>publishPriceList<\/strong> to our sgs.model.coffeepricing module. <\/p>\n<pre class=\"brush: javascript\">sgs.model.coffeepricing.publishPriceList = function (viewModel) {\r\n    var priceList = viewModel.pricing();\r\n    \/\/ Find all property names for the pricing object\r\n    var productKeys = Object.keys(priceList);           \r\n    \/\/ Process all properties\r\n    for ( var i = 0; i &lt; productKeys.length; i++) {\r\n        \/\/ Get product details\r\n        var productKey = productKeys[i];\r\n        var productPrice = priceList[productKey];\r\n        \/\/ Create an observable name based on the property\r\n        var observableName = 'pricing' + productKey.replace('-','');        \r\n        \/\/ Check if we already have value model (observable) with the same name\r\n        var valueModel = viewModel[observableName];\r\n        if (typeof(valueModel) == 'undefined') {\r\n            \/\/ Dynamically create a value model\r\n            valueModel = ko.observable(null);\r\n            \/\/ Add it to the overall view model\r\n            viewModel[observableName] = valueModel;\r\n        }               \r\n        \/\/ Update the value model's actual pricing\r\n        valueModel(productPrice);\r\n    }\r\n}\r\n<\/pre>\n<p>You can test it in the browser console by evaluating:  <\/p>\n<p>sgs.model.coffeepricing.publishPriceList(sgs.mediator.coffeepricing.getViewModel())<\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-viewmodel-enhanced.png\" alt=\"Enhanced Coffee Pricing ViewModel\" title=\"Enhanced Coffee Pricing ViewModel\" \/>   <\/p>\n<p>You now see the new value models!<br \/>\nSo let&#8217;s add a call to the <strong>publishPriceList<\/strong> function from <strong>initializeViewModel<\/strong>:<\/p>\n<pre class=\"brush: javascript\">sgs.model.coffeepricing.initializeViewModel = function (pageSettings) {\r\n    \/\/ ..\r\n    var priceList = sgs.model.coffeepricing.getPriceList();         \r\n    var viewModel = {\r\n        pricing:        ko.observable(priceList)\r\n    }           \r\n    \/\/ Publish the pricing data as individual value models\r\n    sgs.model.coffeepricing.publishPriceList(viewModel);            \r\n    \/\/ ...\r\n    return viewModel;\r\n}\r\n<\/pre>\n<p>This way the <strong>new value models<\/strong> will be created <strong>upon initialization<\/strong> of the view model.<\/p>\n<\/li>\n<li>\n<p>Now we can add the corresponding <strong>data bindings<\/strong> in the <strong>setupViewDataBindings<\/strong> function of the sgs.mediator.coffee-pricing.js module:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.setupViewDataBindings = function(renderedNodes, boundViewModel) {\r\n    \/\/ Ask KnockoutJS to data-bind the view model to the view\r\n    $(\"#pricing-regular-tall\").attr(\"data-bind\",\"value: pricingRegularTall\");\r\n    $(\"#pricing-regular-grande\").attr(\"data-bind\",\"value: pricingRegularGrande\");\r\n    $(\"#pricing-regular-venti\").attr(\"data-bind\",\"value: pricingRegularVenti\");\r\n    \/\/ Apply the bindings\r\n    var viewNode = $('#coffee-pricing-view')[0];\r\n    var viewModel = sgs.mediator.coffeepricing.getViewModel();\r\n    ko.applyBindings(viewModel, viewNode);\r\n    \/\/ Get jQuery Mobile to refresh\/enhance the page\r\n    $('#coffee-pricing-dialog').trigger('create');\r\n}\r\n<\/pre>\n<p>If we re-run the app we now see the pricing details:<br \/>\n<img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-panel-regular-coffee-databound.png\" alt=\"Regular Coffee Pricing\" title=\"Regular Coffee Pricing\" \/><\/p>\n<p>And if you update a price, close the dialog, then reopen it the new pricing is still there. But you might notice that the calculations are not reflecting the price update. And if you closed and terminated the mobile app and relaunched it your new price would be lost. <\/p>\n<\/li>\n<li>\n<p>What we need is to add logic to <strong>save the new pricing data<\/strong> once we&#8217;re done with our updates on the pricing dialog. First, let&#8217;s add a <strong>save<\/strong> button to the pricing\/<strong>index.view.html<\/strong> view:  <\/p>\n<pre class=\"brush: html\">&lt;section id='coffee-pricing-view'&gt;\r\n    &lt;!-- ... --&gt;            \r\n    &lt;div data-role=\"content\"&gt;\r\n    &lt;!-- ... --&gt;            \r\n        &lt;a id=\"save-pricing-button\" href=\"#\" \r\n                data-rel=\"back\"\r\n                data-role=\"button\" \r\n                data-icon=\"check\"\r\n                data-theme=\"e\" &gt;Save Pricing&lt;\/a&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/section&gt;\r\n<\/pre>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-save-button.png\" alt=\"Save Pricing Button\" title=\"Save Pricing Button\" \/><\/p>\n<\/li>\n<li>\n<p>Let&#8217;s add a <strong>click event handler<\/strong> for our new button in the <strong>setupViewDataBindings<\/strong> function of the sgs.mediator.coffee-pricing.js module.<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.setupViewDataBindings = function(renderedNodes, boundViewModel) {\r\n    \/\/ ...  \r\n    \/\/ Get jQuery Mobile to refresh\/enhance the page\r\n    $('#coffee-pricing-dialog').trigger('create');          \r\n    \/\/ Add a click handler of the Save Pricing button\r\n    $('#save-pricing-button')\r\n        .die('click')\r\n        .bind('click',sgs.mediator.coffeepricing.savePriceList);            \r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>Le&#8217;s define the sgs.mediator.coffeepricing.savePriceList and make it call savePriceList in sgs.model.coffee-pricing.js module:<\/p>\n<pre class=\"brush: javascript\">sgs.mediator.coffeepricing.savePriceList = function() {\r\n    sgs.model.coffeepricing.savePriceList();\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>We just need to implement the new <strong>savePriceList<\/strong> function in the sgs.model.coffee-pricing.js module. For now let&#8217;s create a shell:<\/p>\n<pre class=\"brush: javascript\">sgs.model.coffeepricing.savePriceList = function () {  \r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>The requirements for the savePriceList are to:<\/p>\n<ul>\n<li>Apply the current data of the individual price value models back to the master pricing value model<\/li>\n<li>Save our pricing value model data to local storage using the jStorage library<\/li>\n<\/ul>\n<p>Here is the code:  <\/p>\n<pre class=\"brush: javascript\">sgs.model.coffeepricing.savePriceList = function () {\r\n    var viewModel = sgs.mediator.coffeepricing.getViewModel();\r\n    var priceList = viewModel.pricing();\r\n    \/\/ Find all property names for the pricing object\r\n    var productKeys = Object.keys(priceList);\r\n    \/\/ Process all properties\r\n    for ( var i = 0; i &lt; productKeys.length; i++) {\r\n        \/\/ Get product details\r\n        var productKey = productKeys[i];\r\n        \/\/ Find the corresponding observable\r\n        var observableName = 'pricing' + productKey.replace('-','');\r\n        var valueModel = viewModel[observableName];\r\n        \/\/ Update the value model\r\n        var productPrice = parseFloat(valueModel());\r\n        priceList[productKey] = productPrice;\r\n    }\r\n    \/\/ Store the updated pricing in local storage\r\n    $.jStorage.set(\"coffee-price-list\", priceList);\r\n}\r\n<\/pre>\n<\/li>\n<li>\n<p>If you re-run the app, you now should be able to see the impact of updating the pricing of a regular tall coffee from 1.4 to 1.5. And if you restart the app you should see that your pricing data had been  saved and restored.<br \/>\nIn the browser developer toolls, you should be able to inspect the local storage and confirm that your can see your pricing data:<\/p>\n<p><img decoding=\"async\" src=\".\/wp-content\/media\/knockoutjs\/savings-goal-simulator-part5-sgs-pricing-local-storage.png\" alt=\"Pricing In Local Storage\" title=\"Pricing In Local Storage\" \/><\/p>\n<\/li>\n<\/ol>\n<p>At this point you have a well-rounded mobile version of the Savings Goal Simulator!<\/p>\n<p>Note: as an exercise for the reader, go ahead and externalize the content of the #page-home as a separate template named <strong>index.view.html<\/strong> in the <strong>assets\/www\/views\/index<\/strong> folder. This will keep the overall index.html main page as small as possible.<\/p>\n<h3>Phone And Table Versions Of The App<a id='part5-phone-and-tablet-versions'><\/a><\/h3>\n<p>Currently we have an index.html which is primarily designed to fit on a phone. And although it will take the full size of the display on a tablet, the application will still work fine. But what if we want to offer a more optimized design for a tablet such as for example do away with collapsible sections, or change the layout of the app?<\/p>\n<p>Well, this is where the <strong>modularity<\/strong> of our views and application, plus the use of HTML and CSS will pay off.<\/p>\n<p>Two approaches can be considered based on your design need:<\/p>\n<ol>\n<li>Have a separate main web page for the phone and for the tablet version and launch the appropriate page based on the device display size.  <\/li>\n<li>Keep a single web page, but just custom CSS stylesheets to tweak the layout as needed<\/li>\n<\/ol>\n<p>The first option provide the most flexibility, so let&#8217;s look at how to implement it.<\/p>\n<p>Let&#8217;s look back at the code we used in the &#8220;controller&#8221; or &#8220;launcher&#8221; (implemented as Java Activity on the Android platform):<\/p>\n<pre class=\"brush: javascript\">    package com.yournamespace.savingsgoalsimulator;\r\n\r\n    import android.os.Bundle;\r\n    import org.apache.cordova.*;\r\n\r\n    public class MainActivity extends DroidGap {\r\n\r\n        @Override\r\n        public void onCreate(Bundle savedInstanceState) {\r\n            super.onCreate(savedInstanceState);\r\n            super.loadUrl(\"file:\/\/\/android_asset\/www\/index.html\");\r\n        }\r\n\r\n    }\r\n<\/pre>\n<p>At a high-level, we need to:<\/p>\n<ul>\n<li>Rename our index.html to index.<strong>phone<\/strong>.html<\/li>\n<li>Clone our index.phone.html into a new index.<strong>tablet<\/strong>.html<\/li>\n<li>Change the <strong>onCreate<\/strong> method to detect the device size and load index.phone.html or index.tablet.html<\/li>\n<\/ul>\n<p>Here is an implementation for onCreate based on a check for the width of the device:<\/p>\n<pre class=\"brush: javascript\">\r\n        @Override\r\n        public void onCreate(Bundle savedInstanceState) {\r\n            protected float TABLET_WIDTH = 768;\r\n\r\n            super.onCreate(savedInstanceState);\r\n\r\n            \/\/ Get actual screen size so we can determine if the device is a tablet or not\r\n            Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();\r\n            int width  = display.getWidth(); \r\n            int height = display.getHeight(); \r\n\r\n            \/\/ Is the width is at least the minimum width of a tablet?\r\n            boolean isTablet = (width &gt;= TABLET_WIDTH); \r\n            Log.v(\"Device size detection\", \"width=\" + width + \" height=\" + height + \" isTablet=\" + isTablet );\r\n\r\n            \/\/ Determine the name of the corresponding main page\r\n            String deviceType = isTablet ? \"tablet\" : \"phone\";\r\n            String pageUrl = \"file:\/\/\/android_asset\/www\/index.\" + deviceType + \".html\";\r\n\r\n            super.loadUrl(pageUrl);\r\n        }\r\n<\/pre>\n<p>Now you can customize the index.tablet.html page to your liking, such as for example:<\/p>\n<ul>\n<li>Adding new functionality<\/li>\n<li>Adding new jQuery Mobile logical pages<\/li>\n<li>Adding new template views<\/li>\n<li>Splitting existing views into separate phone and tablet versions<\/li>\n<\/ul>\n<p>Note: we had already included a media query and conditional inclusion of device-specific CSS (see  the <a href=\"#part5-adapting-css-for-mobile-use'\">&#8220;Adapting The Stylesheets For Mobile Use&#8221;<\/a> section details) in our main html page. So now you can expand the customization to suit the appropriate device.<\/p>\n<h3>Prepare For Packaging <a id='todo-part5-packaging'><\/a><\/h3>\n<p>Since packaging is different for each device OS platform, I would recommend consulting the corresponding platform documentation. But the following items for consideration apply to all platforms:<\/p>\n<ul>\n<li>Creating the application icon in several resolutions<\/li>\n<li>Creating the initial screen image which is displayed while the app is loading or being re-activated<\/li>\n<li>Applying code signing certificates<\/li>\n<\/ul>\n<h3>Hosting The Application Package On An &#8220;AppStore&#8221;<a id='todo-part5-hosting'><\/a><\/h3>\n<p>Of course, if your mobile application is for public consumption (for free or for sale) then the hosting guidelines of your platform apply (e.g. Apple AppStore, Google Play, Microsoft Windows Phone App Store).<\/p>\n<p>But if your mobile application is for enterprise consumption, provided that you are enrolled in an enterprise program (e.g. iOS Developer Enterprise Program), you can create your own &#8220;custom app store&#8221; as a web application, require user authentication and authorization, tailor the app catalog based on user privileges. In that situation, each app has a link to either the package (for Android) or an application descriptor (for iOS &#8211; .plist file) which contains the location of your application package (e.g. Android .apk file, iOS .ipa file). Clicking on the link triggers an &#8220;Over The Air&#8221; installation of the application once you have approved the process.<\/p>\n<p>This section was meant to just give you a sense of the possibilities, but you will need to refer the corresponding platform documentation for details.<\/p>\n<p><a id='todo-part5-recap'><\/a><\/p>\n<h3>5. Recap<\/h3>\n<p>Even though we touched on all the basic minimum aspects of a PhoneGap + jQuery Mobile app, in some ways we barely scratched the surface. For example, we did not cover the following topics:<\/p>\n<ul>\n<li>leveraging device capabilities such as media, geolocation, etc. &#8211; but you easily lookup the corresponding details in the <a href=\"#acrdvadoc\">PhoneGap documentation<\/a><\/li>\n<li>using PhoneGap plugins such as the mobile notifications <\/li>\n<li>dynamically altering jQuery Mobile-enhanced HTML controls &#8211; in a nutshell you would create <a href=\"#kocustombindingHandlers\">custom bindingHandlers for KnockoutJS<\/a><\/li>\n<li>interfacing with web services (via AJAX)<\/li>\n<\/ul>\n<p>These more advanced topics will be the subject of a separate tutorial.<\/p>\n<p>Here is a quick recap of the steps we followed:<\/p>\n<table class=\"details_table\" style=\"border:1px solid #DDD;border-collapse:collapse;background-color:#FFF;\">\n<tr>\n<th style=\"border:1px solid #DDD;background-color:#DDD;\">\n<div style=\"width:30px;\">#<\/div>\n<\/th>\n<th style=\"border:1px solid #DDD;background-color:#DDD;text-align:left;\">To Do<\/th>\n<th style=\"border:1px solid #DDD;background-color:#DDD;text-align:left;\">\n<div style=\"width:90px;text-align:center;\">Area<\/div>\n<\/th>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">1<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n<ul>\n<li>Create a PhoneGap project for your mobile platform<\/li>\n<li>Create a www folder structure under \/assets for all types of resources:\n<ul>\n<li>www\/css<\/li>\n<li>www\/css\/jquery.mobile<\/li>\n<li>www\/scripts<\/li>\n<li>www\/scripts\/vendor<\/li>\n<li>www\/scripts\/view<\/li>\n<li>www\/scripts\/viewmediator<\/li>\n<li>www\/scripts\/viewmodel<\/li>\n<\/ul>\n<\/li>\n<li>Add external JS libraries including Cordova, jQuery, jQuery Mobile, etc. under www\/scripts\/vendor<\/li>\n<li>Add the jQuery Mobile stylesheets and theme content under www\/css\/jquery.mobile<\/li>\n<li>Create a shell page for each type of device:\n<ul>\n<li>index.phone.html<\/li>\n<li>index.tablet.html<\/li>\n<\/ul>\n<\/li>\n<li>Customize the application controller (Activity in Android, AppDelegate in iOS) logic to detect the device display dimensions and launch the phone or tablet version of the index page<\/li>\n<li>Under www\/scripts, create an application.js module with the following function placeholders:\n<ul>\n<li>InitializeApplication<\/li>\n<li>InitializeViewMediators<\/li>\n<li>LoadApplicationViews<\/li>\n<li>OnLoad<\/li>\n<li>OnDeviceReady<\/li>\n<\/ul>\n<\/li>\n<li>Wire-up the onload event on the body tag of both index pages to the OnLoad function<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">Application<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">2<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n<ul>\n<li>Customize the application style sheets under www\/css\n<ul>\n<li>application.css<\/li>\n<li>application.phone.css<\/li>\n<li>application.tablet.css<\/li>\n<\/ul>\n<\/li>\n<li>Starting with the index.phone.html:\n<ul>\n<li>include core JS libraries, <\/li>\n<li>add logic to asynchronously load other libraries via LAB.js<\/li>\n<li>add a call to LoadApplicationViews (from application.js) once LAB.js has completed all loading<\/li>\n<li>include stylesheets<\/li>\n<\/ul>\n<\/li>\n<li>Customize the LoadApplicationViews (from application.js) to use the jQuery View Loader to load all views and call InitializeViewMediators once done<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">View<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">3<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n            For each &#8220;logical page&#8221; in the app:<\/p>\n<ul>\n<li>Create a folder under www\/scripts\/view to group all markup templates for that page<\/li>\n<li>Create an index.view.html for the page in the new folder<\/li>\n<li>Add a div with data-role=&#8217;page&#8217; in the body of both index.phone.html and index.tablet.html<\/li>\n<li>Inside the new div, add a script tag referencing the index.view.html template from the page folder <\/li>\n<li>Inside the new div, add a container div with data-bind=&#8217;template: { &#8230; }&#8217; binding the div with the template<\/li>\n<li>Create templates for any sub-panels of the page with the naming convention: _{panel-name}.view.html<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">View<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">4<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n            For each view model you need (e.g one global plus one per logical page):<\/p>\n<ul>\n<li>Create a Javascript module under www\/scripts\/viewmodel<\/li>\n<li>Customize the namespace<\/li>\n<li>Define a function named initializeViewModel to instantiate and return a view model and define value models (observables) and dependent observable functions<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">View Model<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">4<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n            For each view mediator you need (e.g one per logical page):<\/p>\n<ul>\n<li>Create a Javascript module under www\/scripts\/viewmediator<\/li>\n<li>Customize the namespace<\/li>\n<li>Define a function named createViewMediator to:\n<ul>\n<li>request a corresponding view model<\/li>\n<li>save off the view model<\/li>\n<li>bind the corresponding container div with its template<\/li>\n<\/ul>\n<\/li>\n<li>Define a function named setupViewDataBindings to bind each element of the page\/view with their corresponding value model<\/li>\n<li>If you need custom pre page rendering logic, create a function and wire it up to the pageinit event in the LAB.js post-loading code section<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">View Mediator<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">4<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n            Customize the application initialization:<\/p>\n<ul>\n<li>Update InitializeApplication with any custom initialization logic based on device data or features<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">Application<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:30px;text-align:center;\">\n                <a href=\"#xxx\">4<\/a><\/div>\n<\/td>\n<td style=\"border:1px solid #DDD\">\n            Update the InitializeViewMediators function of the www\/scripts\/application.js for each &#8220;logical page&#8221;:<\/p>\n<ul>\n<li>Add a call to the createViewMediator of each mediator<\/li>\n<\/ul>\n<\/td>\n<td style=\"border:1px solid #DDD;vertical-align:top;\">\n<div style=\"width:90px;text-align:center;\">Application<\/div>\n<\/td>\n<\/tr>\n<\/table>\n<h3>So What? <a id='part5-sowhat'><\/a><\/h3>\n<p>The &#8220;<a href=\"#acrdva\">PhoneGap<\/a> + <a href=\"#jqtpl\">jQuery Mobile<\/a> + <a href=\"#jqvl\">jQuery ViewLoader<\/a> + <a href=\"#kojs\">Knockout JS<\/a>&#8221; <strong>stack<\/strong> allows you to create portable mobile apps by leveraging your existing HTML5 and Javascript skills, while minimizing your need to master all the details of each platform.<\/p>\n<p>This is a big deal because you can:<\/p>\n<ul>\n<li>achieve a much <strong>greater portable code base<\/strong> (the platform-specific code base will be small)<\/li>\n<li>get started quickly <\/li>\n<li>use your favorite IDE<\/li>\n<li>style your mobile app using CSS3 (including transitions and effects)<\/li>\n<li>adopt the look of the base platform &#8220;automagically&#8221; (via jQuery Mobile rendering)<\/li>\n<li><strong>debug<\/strong> a great portion of the app using <strong>standard browser tools<\/strong> (e.g.: Google Chrome, Firefox + Firebug, &#8230;)<\/li>\n<li>target one platform initially then expand to other ones over time<\/li>\n<\/ul>\n<p>So if you have solid web skills it is time for you to apply them towards the creation of mobile apps!<\/p>\n<h3>References And Resources<\/h3>\n<h5>PhoneGap Platform<\/h5>\n<ul>\n<li><a id='acrdva'><\/a> <a href=\"http:\/\/www.phonegap.com\/\">PhoneGap \/ Apache Cordova<\/a> mobile framework : http:\/\/www.phonegap.com\/<\/li>\n<li><a id='acrdvagsg'><\/a> <a href=\"http:\/\/docs.phonegap.com\/en\/2.2.0\/guide_getting-started_index.md.html\">PhoneGap Getting Started Guides<\/a> : http:\/\/docs.phonegap.com\/en\/2.2.0\/guide<em>getting-started<\/em>index.md.html<\/li>\n<li><a id='acrdvadoc'><\/a> <a href=\"http:\/\/docs.phonegap.com\/en\/2.2.0\/index.html\/\">PhoneGap Documentation<\/a> : http:\/\/docs.phonegap.com\/en\/2.2.0\/index.html<\/li>\n<\/ul>\n<h5>Android Platform<\/h5>\n<ul>\n<li><a id='androidsdk'><\/a> <a href=\"http:\/\/developer.android.com\/sdk\/installing\/index.html\" title=\"Android SDK\">Android SDK<\/a> : http:\/\/developer.android.com\/sdk\/installing\/index.html<\/li>\n<li><a id='aptanastudio'><\/a> <a href=\"http:\/\/www.aptana.com\/products\/studio3\" title=\"Aptana Studio 3\">Aptana Studio 3<\/a> : http:\/\/www.aptana.com\/products\/studio3<\/li>\n<li><a id='adt'><\/a> <a href=\"http:\/\/developer.android.com\/sdk\/installing\/installing-adt.html\" title=\"Android Development Tools (ADT) plugin for Eclipse \/ Aptana Studio\">Android Development Tools (ADT) plugin for Eclipse \/ Aptana Studio<\/a> : http:\/\/developer.android.com\/sdk\/installing\/installing-adt.html<\/li>\n<\/ul>\n<h5>Javascript Frameworks<\/h5>\n<ul>\n<li><a id='jqm'><\/a> <a href=\"http:\/\/jquerymobile.com\/\" title=\"jQuery Mobile\">jQuery Mobile<\/a> : http:\/\/jquerymobile.com\/<\/li>\n<li><a id='jqmd'><\/a> <a href=\"http:\/\/jquerymobile.com\/demos\/\" title=\"jQuery Mobile Documentation\">jQuery Mobile Documentation<\/a> : http:\/\/jquerymobile.com\/demos\/<\/li>\n<li><a id='jqtpl'><\/a> <a href=\"http:\/\/api.jquery.com\/category\/plugins\/templates\/\" title=\"jQuery Template\">jQuery Template<\/a> : http:\/\/api.jquery.com\/category\/plugins\/templates\/<\/li>\n<li><a id='jqvl'><\/a> <a href=\"http:\/\/github.com\/techarch\/jquery-viewloader\" title=\"jQuery ViewLoader\">jQuery ViewLoader<\/a> : http:\/\/github.com\/techarch\/jquery-viewloader<\/li>\n<li><a id='viewldr'><\/a> <a href=\"https:\/\/github.com\/techarch\/jquery-viewloader\">jQuery ViewLoader<\/a> rendering engine : https:\/\/github.com\/techarch\/jquery-viewloader<\/li>\n<li><a id='kojs'><\/a> <a href=\"http:\/\/knockoutjs.com\/\">Knockout JS<\/a> Model-View-ViewModel framework : http:\/\/knockoutjs.com\/<\/li>\n<li><a id='kojs'><\/a> <a href=\"http:\/\/www.knockmeout.net\/\">Knock Me Out<\/a> &#8211; Ryan Niemeyer&#8217;s blog : http:\/\/www.knockmeout.net\/<\/li>\n<li><a id='labjs'><\/a> <a href=\"http:\/\/labjs.com\/\">LAB.js<\/a> Parallel and Asynchronous Javascript Loader : http:\/\/labjs.com\/<\/li>\n<\/ul>\n<h5>Miscellaneous<\/h5>\n<ul>\n<li><a id='koafterrender'><\/a> <a href=\"http:\/\/knockoutjs.com\/documentation\/template-binding.html#note_4_using_the__option\" title=\"KnockoutJS afterRender\">KnockoutJS afterRender<\/a> : http:\/\/knockoutjs.com\/documentation\/template-binding.html#note<em>4<\/em>using<em>the<\/em>_option<\/li>\n<li><a id='kocustombindingHandlers'><\/a> <a href=\"http:\/\/www.knockmeout.net\/2011\/07\/another-look-at-custom-bindings-for.html\" title=\"KnockoutJS afterRender\">Another Look at Custom Bindings for KnockoutJS<\/a> : http:\/\/www.knockmeout.net\/2011\/07\/another-look-at-custom-bindings-for.html<\/li>\n<li><a id='jqmkocustombindingHandlers'><\/a> <a href=\"http:\/\/jsfiddle.net\/rniemeyer\/mydTu\/\" title=\"Example of KnockoutJS custom bindings for jQuery Mobile\">Example of KnockoutJS custom bindings for jQuery Mobile<\/a> <\/li>\n<li><a id='sgs-site'><\/a> <a href=\"http:\/\/savings-goal-simulator.heroku.com\/\" title=\"Savings Goal Simulator\">Savings Goal Simulator<\/a> : http:\/\/savings-goal-simulator.heroku.com\/<\/li>\n<\/ul>\n<h5><a id='patterns'><\/a>Patterns<\/h5>\n<ul>\n<li><a id='mvc'><\/a> <a href=\"http:\/\/c2.com\/cgi\/wiki?ModelViewController\" title=\"Model-View-Controller pattern\">Model-View-Controller pattern<\/a> : http:\/\/c2.com\/cgi\/wiki?ModelViewController<\/li>\n<li><a id='controller'><\/a> <a href=\"http:\/\/c2.com\/cgi\/wiki?ApplicationController\" title=\"Controller pattern\">Controller pattern<\/a> : http:\/\/c2.com\/cgi\/wiki?ApplicationController<\/li>\n<\/ul>\n<h5><a id='criwawkotuts'><\/a>Other Tutorials In The Same Series<\/h5>\n<ul>\n<li><a id='criwawko1'><\/a> <a href=\"https:\/\/blog.monnet-usa.com\/?p=354\" title=\"Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 1\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 1<\/a> : Introduction to KnockoutJS<\/li>\n<li><a id='criwawko2'><\/a> <a href=\"https:\/\/blog.monnet-usa.com\/?p=368\" title=\"Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 2\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 2<\/a> : Using view models and view mediators<\/li>\n<li><a id='criwawko3'><\/a> <a href=\"https:\/\/blog.monnet-usa.com\/?p=404\" title=\"Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 3\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 3<\/a> : Modularizing view models and view mediators<\/li>\n<li><a id='criwawko4'><\/a> <a href=\"https:\/\/blog.monnet-usa.com\/?p=411\" title=\"Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 4\">Creating Rich Interactive Web Apps With KnockoutJS \u2013 Part 4<\/a> : Modularizing and extracting view components  <\/li>\n<\/ul>\n<h5><a id='sgspkg'><\/a>Savings Goal Simulator<\/h5>\n<ul>\n<li><a id='criwawkosrc'><\/a>Web <a href=\"https:\/\/github.com\/techarch\/savings-goal-simulator\" title=\"Web Savings Goal Simulator Source Code\">Savings Goal Simulator Source Code<\/a><\/li>\n<li>Download the Mobile App:<\/li>\n<\/ul>\n<table class=\"details_table\" style=\"\">\n<tr>\n<td><a id='sgsforios'><\/a><a href='https:\/\/itunes.apple.com\/pk\/app\/savings-goal-simulator\/id507644102?mt=8' title='Mobile Savings Goal Simulator for iOS' target=\"_blank\"><img src='.\/wp-content\/media\/appstores\/Download_on_the_App_Store_Badge_US-UK_135x40.png' height='49'\/><\/a><\/td>\n<td><a id='sgsforandroid'><\/a><a href='http:\/\/www.amazon.com\/Philippe-Monnet-techarch-Savings-Simulator\/dp\/B006RUAIFE' title='Mobile Savings Goal Simulator for Android' target=\"_blank\"><img src='.\/wp-content\/media\/appstores\/6a0148c71fb71b970c014e8a07bf5a970d.png' height=\"50\"\/><\/a><\/td>\n<\/tr>\n<\/table>\n<div class=\"blog-msm-ad\">\n<div class=\"blog-msm-ad1\">\n\t\tIf you enjoyed this post, I would love it if you could check out <a href=\"http:\/\/bit.ly\/myskillsmap\"  target=\"_blank\">mySkillsMap<\/a>, my skills management app.\n\t<\/div>\n<div class=\"blog-msm-ad2\">\n\t\t\t<a href=\"http:\/\/www.myskillsmap.com\/take-charge-of-your-skills-development\" target=\"_blank\"><br \/>\n\t\t\t\t<img decoding=\"async\" src=\".\/wp-content\/media\/msm\/start-your-free-trial-now.png\"><br \/>\n\t\t\t<\/a>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Intro In part 4 we built a complete working version of the Savings Goal Simulator as a rich web app where each view model, view and view mediator is nicely modularized. How could we leverage our browser-side markup and logic to create a mobile app, portable across multiple platforms such as IOS, Android, Windows Mobile? [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[59,30,48,47,46],"tags":[54,32,51,64,49,50],"class_list":["post-420","post","type-post","status-publish","format-standard","hentry","category-javascript","category-jquery","category-knockoutjs","category-patterns","category-rich-web-apps","tag-ajax","tag-javascript","tag-jquery-mobile","tag-knockoutjs","tag-mobile","tag-phonegap"],"_links":{"self":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/420","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=420"}],"version-history":[{"count":9,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/420\/revisions"}],"predecessor-version":[{"id":429,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/420\/revisions\/429"}],"wp:attachment":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=420"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=420"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}