info343/labs/10/writeup.php

<style type="text/css">
   figure.mobile {
      width: 225px;
      margin-right: -300px;
   }
</style>
<section id="introduction">
   <h3>Introduction</h3>
   
   <p>Today we’ll build a mobile interface for displaying Twitter search results and a person’s Twitter profile and timeline.</p>
   
   <p>It will consist of two internal “pages” — a search page, and a profile page — that we will use jQuery Mobile to render and enhance.</p>
</section>
      
<section>
   <?= t_head('Add jQuery Mobile <code>data-role</code> Attributes', "10") ?>

   <figure class="example mobile">
      <a href="output/lab10_ex1_search.png">
         <img src="output/lab10_ex1_search.png" alt="The initial search page.">
         <figcaption>The initial search page.</figcaption>
      </a>
   </figure>

   <figure class="example mobile" style="margin-top: 20em;">
      <a href="output/lab10_ex1_profile.png">
         <img src="output/lab10_ex1_profile.png" alt="The initial (unpopulated) profile page.">
         <figcaption>The initial (unpopulated) profile page.</figcaption>
      </a>
   </figure>

   <p>Download the following HTML skeleton file:</p>

   <p class="resource"><a href="twitter.html">twitter.html</a></p>

   <p>Begin by adding the necessary <code>data-role</code> attributes for internal subpage structure that jQuery Mobile looks for.</p>

   <p>Refer to the <a href="../../lectures/internal-nav-jquery-mobile/#slide5">lecture slides here</a> for examples of the jQuery Mobile page structure.</p>

   <p>The first page, <code>#search-form</code>, is for searching and displaying tweet results. The second page, <code>#show-timeline</code>, will be shown when the user taps or clicks on a tweet in the search results; it will contain profile data and list all tweets from the user whose tweet was selected.</p>
</section>

<section>
   <?= t_head('Inject Search Results', "10–15") ?>

   <p>Download the following JavaScript skeleton file:</p>

   <p class="resource"><a href="twitter.js">twitter.js</a></p>

   <p>It contains a few things we haven’t seen before, some of which will be explained later.</p>

   <p>In particular, it captures the <samp>search</samp> event on the search form, so that when the user hits Enter in the search box, the <code>search()</code> function gets called.</p>

   <p>Begin by adding the necessary JavaScript code to the <code>search()</code> function to make a JSONP Ajax request to the <a href="https://dev.twitter.com/docs/api/1/get/search">Twitter Search API</a> (the same web service we’ve used before) to search for the term entered into the box.</p>

   <p>Inject the search results as <code>li</code>s into the <code>#results</code> list. If a particular tweet’s data is stored in a variable called <code>tweet</code>, then each result item should be in the following format:</p>

<pre><code>&lt;li&gt;
  &lt;a href="#show-timeline"&gt;
     &lt;img src="<var>tweet.profile_image_url</var>" alt="<var>tweet.from_user</var>"&gt;
     &lt;h3&gt;<var>tweet.from_user_name</var> &lt;small&gt;@<var>tweet.from_user</var>&lt;/small&gt;&lt;/h3&gt;
     &lt;p&gt;<var>tweet.text</var>&lt;/p&gt;
  &lt;/a&gt;
&lt;/li&gt;</code></pre>

   <p>(This should be accomplished as we’ve always done, using jQuery’s <code>$('&lt;<var>tagname</var>&gt;')</code> syntax to create new elements, <code>.append()</code>ing them into other elements, etc.)</p>

   <p>Note that the <code>profile_image_url</code> will require some modification. The one given to us is the medium-sized version, suffixed with <code>_normal</code>.</p>

   <p>This is too small; replace the <code>_normal</code> with <code>_bigger</code> in the URL to load the larger version. Use the string <a href="http://www.w3schools.com/jsref/jsref_replace.asp"><code>.replace()</code></a> function to do this.</p>

   <figure class="example mobile" style="margin-top: -15em">
      <a href="output/lab10_ex2.png">
         <img src="output/lab10_ex2.png" alt="Search results before “enhancement”" />
         <figcaption>Search results before “enhancement.”</figcaption>
      </a>
   </figure>
</section>

<section>
   <?= t_head('Enhance Search Links', '5') ?>

   <ins><p>When you’ve finished injecting the data into the list, the list will be ugly and unstyled. Now we’ll make jQuery mobile “enhance” each list item into a nice mobified button.</p></ins>

   <ins><p>Add a new <code>data-role="listview"</code> attribute to the <code>#results</code> div. This will cause jQuery Mobile to automatically “enhance” the list.</p></ins>

   <ins><p>However, it will try to enhance it when the page loads, <em>before</em> it contains any list items. Any list items added after the page loads will not be enhanced.</p></ins>

   <ins><p>To tell jQuery Mobile to enhance the items you added after the page loaded, add the following to your JavaScript code:</p></ins>

   <ins><pre><code>$('#results').listview('refresh');</code></pre></ins>

   <ins><p>Now whenever you search for tweets, the resulting list will be a mobile-styled list of links.</p></ins>

   <figure class="example mobile" style="margin-top: -20em">
      <a href="output/lab10_ex3.png">
         <img src="output/lab10_ex3.png" alt="Search results after jQuery Mobile enhancement" />
         <figcaption>Search results after jQuery Mobile enhancement.</figcaption>
      </a>
   </figure>
</section>

<section>
   <?= t_head('Fetch &amp; Populate Profile Data', '15–20') ?>

   <p>Currently, selecting a tweet will take you to a mostly-blank <code>#show-timeline</code> page. Now we will fetch and display the profile data of the user whose tweet was selected.</p>

   <p>First, add a query string parameter to the link you created for each tweet, so that it now links to:</p>

   <pre><code>#show-timeline?screen_name=<var>tweet.<ins>from_user</ins></var></code></pre>

   <p>This causes the <code>pageChange()</code> function in the skeleton to call a <code>loadProfile()</code> function and pass to it the value of the <code>screen_name</code> parameter.</p>

   <div class="note">
      <p>By default jQuery Mobile doesn’t handle parameters to subpages properly, so we’ve included a plugin (<samp>jqm.page.params.js</samp>) which adds this functionality.</p>
      <p>jQuery Mobile already passes to the <code>pagebeforechange</code> event handler a <code>data</code> object with various information about the page. The page-params plugin adds to this the information in the query string parameter.</p>
      <p>If you’re curious, uncomment the <code>console.log(data)</code> inside the <code>pageChange()</code> function to see what kinds of information jQuery Mobile and the plugin give us about the page change.</p>
   </div>

   <p>Your <code>loadProfile()</code> function will make a request to a second Twitter web service (one we haven’t used before) that provides user profile information. Its documentation is here:</p>

   <p class="resource"><a href="https://dev.twitter.com/docs/api/1/get/users/lookup">https://dev.twitter.com/docs/api/1/get/users/lookup</a></p>

   <p>Add the necessary JSONP request to pass the <code>screen_name</code> parameter to the <code>lookup.json</code> web service.</p>

   <p>Use the Net tab of Firebug to inspect the data returned by the service. The data should consist of <em>a list</em> of only one element, containing that user’s profile information. Meaning to get that user’s information, you’ll need to get the 0th element of the list.</p>

   <p>Inject the pieces of user data as appropriate into the page’s header and <code>#user_stats</code>. In particular:</p>

   <ul>
      <li>Set the <code>src</code> attribute of the existing <code>#avatar</code> image to be the same <code>_bigger</code> variant of <code>profile_image_url</code> we used before. Set its <code>alt</code> attribute to be the user’s <code>name</code>.</li>
      <li>Inject the user’s <code>name</code> into the <code>h1</code>, and their <code>screen_name</code> (prefixed with an @ sign) into the <code>h2</code>.</li>
      <li>Inject <code>statuses_count</code>, <code>friends_count</code>, <code>followers_count</code>, and <code>location</code> into the appropriate <code>strong</code> tags in <code>#user_stats</code>.</li>
   </ul>

   <figure class="example mobile" style="margin-top: -15em">
      <a href="output/lab10_ex4.png">
         <img src="output/lab10_ex4.png" alt="Timeline page with profile data injected." />
         <figcaption>Timeline page with profile data injected.</figcaption>
      </a>
   </figure>

   <p>You should now see the profile data of any user whose tweet you select from the search page.</p>
</section>

<section>
   <?= t_head('Fetch &amp; Populate Twitter Timeline', '10–15') ?>

   <p>Now we can populate the user’s twitter timeline as well. To do this we’ll make a request to a third web service, documented here:</p>

   <p class="resource"><a href="https://dev.twitter.com/docs/api/1/get/statuses/user_timeline">https://dev.twitter.com/docs/api/1/get/statuses/user_timeline</a></p>

   <p>You should request this information in the same place where you request the profile information — that is, in the <code>loadProfile</code> function.</p>

   <p>As with the last web service, the format of the data returned by this one is different (because every service is different), so be sure to look at the JSON data in the Net tab to see how to extract the data from this response.</p>

   <p>As before, inject the tweets as list items — this time, in the <code>#timeline</code> list. Their format is idential to the search result tweets, but without a link surrounding them.</p>

   <p>Assuming the data for a particular tweet is in a variable called <code>tweet</code>, the HTML for an entry would look as follows:</p>

<pre><code>&lt;li&gt;
  &lt;img src="<var>tweet.<ins>user.</ins>profile_image_url</var>" alt="<var>tweet.<ins>user.</ins>screen_name</var>"&gt;
  &lt;h3&gt;<var>tweet.<ins>user.</ins>name</var> &lt;small&gt;@<var>tweet.<ins>user.</ins>screen_name</var>&lt;/small&gt;&lt;/h3&gt;
  &lt;p&gt;<var>tweet.text</var>&lt;/p&gt;
&lt;/li&gt;</code></pre>

   <p>(The elements are exacty as before but without an <code>a</code> tag; the names of the user’s full name and their @handle are slightly different.)</p>

   <p>Replace the image URL with the <code>_bigger</code> variant here too.</p>

   <p>When done, don’t forget to add the <code>data-role="listview"</code> attribute to the <code>#timeline</code> list, and call <code>.listview('refresh')</code> on it to enhance the list items.</p>

   <figure class="example mobile" style="margin-top: -15em">
      <a href="output/lab10_ex5.png">
         <img src="output/lab10_ex5.png" alt="Complete timeline page, with most recent tweets." />
         <figcaption>Complete timeline page, with most recent tweets.</figcaption>
      </a>
   </figure>

   <p>You should now have a full profile and complete Twitter timeline!</p>
</section>

<section>
   <?= t_head('Finishing Touches (optional)', '10–15') ?>

   <p>There are a few things that could be a little nicer. Firstly, if this page were to be full-screen on an iPhone, there would be no way to navigate backward.</p>

   <p>jQuery Mobile makes adding one extremely easy: simply add a <code>data-add-back-btn="true"</code> attribute to the <code>#show-timeline</code> page. A back button will appear in the header of that page.</p>

   <p>Secondly, the “flash to white” transition when we select a tweet or navigate backward is not terribly useful. Change the transition by attaching a <code>data-transition</code> property to each link in the search results.</p>

   <p>To do this you’ll need to use jQuery’s <code>.data()</code> function. Set the value be one of the supported transitions here (<code>slide</code> is recommended in a mobile environment):</p>

   <p class="resource"><a href="http://jquerymobile.com/demos/1.2.0/docs/pages/page-transitions.html">http://jquerymobile.com/demos/1.2.0/docs/pages/page-transitions.html</a></p>

   <p>Lastly, the tweet timeline on the profile page might look better if we make it “inset” — with rounded corners and space around it on all sides. Add a <code>data-inset="true"</code> parameter to the <code>#timeline</code> and <code>#results</code> lists.</p>

   <figure class="example mobile" style="margin-top: -35em">
      <a href="output/lab10_ex6_search.png">
         <img src="output/lab10_ex6_search.png" alt="Final search page, with inset-style list." />
         <figcaption>Final search page, with inset-style list.</figcaption>
      </a>
   </figure>

   <figure class="example mobile" style="margin-top: -15em">
      <a href="output/lab10_ex6_profile.png">
         <img src="output/lab10_ex6_profile.png" alt="Final profile page, with inset-style list and back button." />
         <figcaption>Final profile page, with inset-style list and back button.</figcaption>
      </a>
   </figure>
</section>

<footer>
   <p>If you’ve finished everything, good job! Add a new entry at the bottom of each list that will fetch more results.</p>
</footer>