.. class:: center

    Making the Toast and Tea

    Going Mobile with ``pyramid_jqm``

    Tres Seaver

    tseaver@agendaless.com

----

About Me
--------

- Principal, Agendaless Consulting

.. image:: python-logo.gif
   :align: right

- Python web developer:  12 years 

.. image:: zope-logo.png
   :align: right

- Zope developer: 11 years

- CMF project lead: 11 years

- Current president, Zope Foundation

.. image:: repoze-logo.gif
   :align: right

- Repoze project, 4 years

----

Overview
--------

Moving from traditional web development to mobile development is hard.

Re-using what we as web developers know can ease the pain.

- HTML, JS, and CSS are increasingly capable of delivering high-quality UX

``pyramid_jqm`` capitalizes on that reuse

- Provides a quick starting point for a useful, web-based mobile app.

----

Distributed development is hard
-------------------------------

... especially compared to traditional web development

- *Everything* is async

- Tradeoffs: Consistency vs. performance

----

Distributed development is hard
-------------------------------

Ugly new failure modes

.. image:: oh_my.jpeg

Livelocks and deadlocks and netsplits, oh my!

----

Mobile development is harder
----------------------------

- Slower, smaller machines deliver UX

- Apps need to "degrade gracefully" for connectivity, real-estate.

.. image:: whack-a-mole.jpeg
   :align: right

- Explosion of platforms makes QA rough^Wimpossible

----

Native apps are no fun
----------------------

- New platform => new app 

  - Conversion is not feasible (in general)

- New (old) development paradigm

  - GUI vs. request-response

----

Native apps are no fun
-------------------------------

.. image:: sharecropping.jpg
   :align: right

- Deep platform commitment ("sharecropping")

----

You already grok^Wknow webapps
------------------------------

Your bread-and-butter skills should be transferable:

- Generating "customized" HTML is natural

- Javscript can put "lively" UX in play

  ... even for curmudgeons like me

- Caching and other tuning mechanisms are familiar ground.

----

HTML5 FT(mobile)W
-----------------

Apps run using "stock" browser components.

.. image:: dog_cat_with_umbrella.png
   :align: right

Testable on desktops (but watch out for performance!)

Mark Pilgrim's "Dive into HTML5" is the essential docs.

----

HTML5 Features:  JSON
---------------------

(OK, not really HTML5)

.. image:: jason_fleece.jpg
   :align: left

But JSON rocks!

... and feels "Pythonic"

----

HTML5 Features:  JSON
------------------------------

Return a Python dict or list directly.

Super testable.

Blazing speed compared to rendering HTML.

----

HTML5 Features:  JSON
------------------------------

Factoring a web app to expose JSON can even benefit the traditional desktop UX:

- "Base" HTML pages can be cached more efficiently.

- "Dynamic" bits can be aded via JSON queries.


----

HTML5 Features:  DOMStorage
---------------------------

The ``localStorage`` element stores key-value pairs as strings.

.. image:: treasurechest.gif
   :align: right

Think "cookies on steriods", local to the "page" (the DOM).

----

HTML5 Features:  DOMStorage
------------------------------------

``setItem``, ``getItem``, and ``removeItem`` are the API.  Like
Python, you can use square brackets.

You have to convert values yourself.

Lawnchair puts a slicker, more cross-platform face on it.

- `http://westcoastlogic.com/lawnchair/ <http://westcoastlogic.com/lawnchair/>`_

----


HTML5 Features:  Geolocation
----------------------------

The ``geolocation`` element provides access to the API.

.. image:: geo_location.png

Can use GPS, cell tower triangulation, IP mapping

----


HTML5 Features:  Geolocation
-------------------------------------

API takes asynchronous callback function::

    function show_map(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
        // Now show a map.....
    }

    navigator.geolocation.getCurrentPosition(show_map);

----

HTML5 Features:  Offline manifest
---------------------------------

Offline mode:  the ``cache.manifest`` file gives fine-grained control over
resources

.. image:: unplug.jpg
   :align: right

Resources can be "never cache", "always cache", or "fallback"

----

HTML5 Features:  Offline manifest
---------------------------------

Each page in an app should reference the **same** ``cache.manifest`` URL::

 <!DOCTYPE HTML>
 <html manifest="/cache.manifest">

MIMEtype for that URL **must** be ``text/cache-manifest``

----

HTML5 Features:  Improved forms
-------------------------------

HTML forms :(

.. image:: angry_guy.png

and then add thumb-typing :|

----

HTML5 Features:  Improved forms
-------------------------------

Improved form elements are especially useful for mobile devices.

Declarative, client-side validation (``required``, plus custom
types) can be an easy mobile win.

- Unfortunately, devices / browsers support varies a lot, at least
  for now.

----

HTML5 Mobile Downsides
----------------------

Performance:  JS in HTML may be noticeably slower than native code

- Especially with large data sets.

"Deep" access to platform APIs may be missing

- But philikon is going to fix that :) 

Integration with platform-specific stores missing.

- But see PhoneGap for a solution:
  `http://pnonegap.com <http://phonegap.com>`_

----

Why ``jquery.mobile``?
-----------------------

Supports all popular mobile device platforms and modern browsers:

- iOS
- Android
- Blackberry
- Palm WebOS
- Nokia/Symbian
- Windows Phone 7
- MeeGo
- Opera Mobile/Mini
- Firefox Mobile
- Kindle, Nook

----

Why ``jquery.mobile``?
-------------------------------

Built on the jQuery and jQuery UI foundation

Active, friendly community

Unfied touch / mouse event model.

AJAX page loads avoid "refresh" pain


----

Why ``jquery.mobile``?
-------------------------------

Progressive enhancement

Responsive design:

- Both based on clean, semantic HTML5 markup

Flexible, easily themeable design.

----

Getting Started with ``pyramid_jqm``
------------------------------------

Create a new virtual environment::

 $ /opt/Python-2.7.2/bin/virtualenv \
     --no-site-packages /path/to/jqmdemo

Install ``pyramid`` and ``pyramid_jqm`` from PyPI::

  $ cd /path/to/jqmdemo
  $ bin/easy_install pyramid pyramid_jqm

----

Getting Started with ``pyramid_jqm``
------------------------------------

Generate the app skeleton via ``paster create``::

 $ mkdir src && cd src
 $ ../bin/paster create \
     -t pyramid_jqm_starter ploneconf

Install the generated package::

 $ cd ploneconf
 $ ../../bin/python setup.py develop

----

Getting Started with ``pyramid_jqm``
------------------------------------

Start the server::

 $ ../../bin/paster serve --reload development.ini

Profit!

- By default the app runs on http://localhost:6543/

----

Walkthrough:  Main Menu
-----------------------

.. image:: demo-main_page.png


----

Walkthrough:  Main Menu
-----------------------

Simple, semantic HTML::

    <div data-role="page" id="home">

      <div data-role="header">
        <h1>pyramid_jqm</h1>
      </div>

      <div data-role="content">

        <ul data-role="listview" data-inset="true">
          <li data-role="list-divider">Demos</li>
          <li>
            <a href="#about">About</a>
          </li>

Hash URLs refer to other "pages" (divs) in the DOM.

----

Walkthrough:  About Page
------------------------

.. image:: demo-about_page.png

----

Walkthrough:  About Page
------------------------

Static HTML provides markers::

  <p>This application is using <i>pyramid_jqm</i> version
     <span id="about-pyramid-jqm-version"></span> and Pyramid version
     <span id="about-pyramid-version"></span>.</p>

... for update by ``jQuery``::

    function about_pageshow(div) {
        $.getJSON(api_prefix + '/versions.json', function (data) {
                $('#about-pyramid-jqm-version').text(data.pjqm_version);
                $('#about-pyramid-version').text(data.pyramid_version);
            }).error(jqxhr_error);
    }

----

Walkthrough:  Map Demo
----------------------

.. image:: demo-map_page.png

----

Walkthrough:  Map Demo
----------------------

Google Maps API initialized using "asynch" API (for bundling)::

 var script = document.createElement("script");
 script.type = "text/javascript";
 script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=pyramid.init_google_maps";
 document.body.appendChild(script);

----

Walkthrough:  Map Demo
----------------------

Using ``jquery.Deferred`` to manage async completions::

   var google_maps_ready = new $.Deferred();
   //...
   function init_google_maps() {
       var map_options = {
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            mayTypeControl: false,
            //...
            zoomControl: true;
       };
       google_maps_ready.resolve(map_options);
   }

----

Walkthrough:  Map Demo
----------------------

Geolocation API is queried, with completion through another deferred::

  var device_location_ready = new $.Deferred();
  //...
  function set_device_location(loc) {
      device_location_ready.resolve(loc);
  }
  if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        set_device_location, fail_device_location,
        {'timeout': 2000});
  }

----

Walkthrough:  Map Demo
----------------------

When device location and maps API ready, initialize the map::

  $.when(google_maps_ready, device_location_ready).done(
      function (map_options, loc) {
          var
              point = new google.maps.LatLng(loc.coords.latitude,
                                             loc.coords.longitude),
              local_map_options = {'center': point},
              canvas = $('#map-canvas')[0],
              map,
              marker;
          $.extend(local_map_options, map_options);
          map = new google.maps.Map(canvas, local_map_options);
          map_ready.resolve(map);
       });

----

Walkthrough:  Form Page
-----------------------

.. image:: demo-form_page.png

----

Walkthrough:  Form Page
-----------------------

Simple, structural HTML::

  <form id="personalinfo-form" method="post"
        action="/change_personalinfo">

   <fieldset data-role="fieldcontain">
   <label for="personalinfo-email">Email:</label>
   <input type="email" name= "email" id="personalinfo-email"
          class="required"/>

----

Walkthrough:  Form Page
-----------------------

Simple Pyramid view code for drop-down population::

  countrylist = [
      {'abbr': 'US', 'name': 'United States'},
      {'abbr': 'CA', 'name': 'Canada'},
      #...
  ]
  @view_config(name='countries.json', renderer='json',
               http_cache=cache_one_day)
  def countries(request):
    return countrylist

----

Walkthrough:  Form Page
-----------------------

Populating the drop-down client side::

  var countries_select = $('#personalinfo-country');
  //...
  countries_api(function (data) {
      countries_select[0].options.length = 0;
      $.each(data, function (index, item) {
          countries_select.append(
            $('<option value="' + item.abbr + '">' +
               item.name + '</option>'));
      });
      countries_select.selectmenu('refresh');
  });

----

Walkthrough:  Dynamic Pages Demo
--------------------------------

.. image:: demo-dynpages_list.png

----

Walkthrough:  Dynamic Pages Demo
--------------------------------

Static list has only the header::

  <ul id ="dynpages-languages" data-role="listview" data-inset="true">
    <li data-role="list-divider">Languages</li>
  </ul>

Add links (via JSON) to server-side URLs::

  var ul = $('#dynpages-languages');
  webframeworks_api(function (data) {
      ul.listview();
      $.each(data, function (index, lang) {
          var li = $('<li><a href="/language/' + lang.name + '">' +
                     lang.desc + '</a></li>');
          ul.append(li);
      });
      ul.listview('refresh');
  });

----

Walkthrough:  Dynamic Pages Demo
--------------------------------

New pages get folded into the main DOM.

.. image:: demo-dynpages_detail.png

----

Resources, Q&A
--------------

`Dive into HTML5 <http://diveintohtml5.info/>`_

`jquery.mobile Homepage <http://jquerymobile.com/>`_

`pyramid_jqm on GitHub <http://github.com/pylons/pyramid_jqm>`_

`pyramid_jqm Docs <http://docs.pylonsproject.org/projects/pyramid_jqm/dev/>`_

tseaver@agendaless.com


