3.28.2012

Nomination mobile - part 4




This post its a part of a series about creating an app with node.js and express for Facebook... this time for mobile browsers




Desktop version:


*NOTE: jqm = jquery mobile

Now lets put the functionality for vote, delete and invite friends to the application

To start lets add a feature to the list of friends in the details of a nomination, when the user swipe left or right it will appear two buttons vote or delete, for that lets update "mscript.js", after add the user to the list we add (around line 150):

$('.users li').bind('swiperight swipeleft', swipe);

This will look for swipe function when the user swipe in any direction on any list item, lets add the function in the same file

function swipe(){
    // reference the just swiped list item
    var $li = $(this);
    // remove all buttons first
    $('.aDeleteBtn').remove();
    $('.aVoteBtn').remove();
    // create buttons and div container
 var $deleteBtn = $('<a>Delete</a>').attr({
   'class': 'aDeleteBtn ui-btn-up-r',
   'href': '#'
  });
    var $voteBtn = $('<a>Vote</a>').attr({
            'class': 'aVoteBtn ui-btn-up-bl',
   'href': '#'
  });
 // insert swipe div into list item
 $li.prepend($deleteBtn);
    $li.prepend($voteBtn);
    $deleteBtn.slideToggle();
    $voteBtn.slideToggle();
}

Here we take the li as a reference, first erase the buttons if they exist already, then we create 2 buttons which have 2 clases, 1 for reference only and the other one to give them style and the position, for that lets add them to the css

.aDeleteBtn, .aVoteBtn {
    -moz-border-radius: 5px;
 -webkit-border-radius: 5px;
 float: right;
 height: 15px;
 line-height: 15px;
 margin: 10px 10px 0 0;
 padding: 0.6em;
 position: absolute;
 right: 0;
 top: 0;
 z-index: 10;
    display: none;
}

.aDeleteBtn{
    right: 60px;
}

/* red color buttons */

.ui-btn-up-r { border: 1px solid #953403; background: #2567ab; font-weight: bold; color: #fff; cursor: pointer;  text-shadow: 0 -1px 1px #953403; text-decoration: none; background-image: -moz-linear-gradient(top, #ec4a0b, #ad390c); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ec4a0b),color-stop(1, #ad390c));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ec4a0b', EndColorStr='#ad390c')"; }

.ui-btn-up-r a.ui-link-inherit { color: #fff; }

.ui-btn-hover-r { border: 1px solid #953403; background: #f15c22; font-weight: bold; color: #fff;  text-shadow: 0 -1px 1px #014D68; background-image: -moz-linear-gradient(top, #f15c22, #f15c22); text-decoration: none; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f15c22),color-stop(1, #f15c22));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#f15c22', EndColorStr='#f15c22')";  }

.ui-btn-hover-r a.ui-link-inherit { color: #fff; }

.ui-btn-down-r { border: 1px solid #225377; background: #79ae21; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #225377; background-image: -moz-linear-gradient(top, #bc770f, #e6590c); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #bc770f),color-stop(1, #e6590c));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#bc770f', EndColorStr='#e6590c')"; }

.ui-btn-down-r a.ui-link-inherit { color: #fff; }

.ui-btn-up-r, .ui-btn-hover-r, .ui-btn-down-r { font-family: Helvetica, Arial, sans-serif; }

/* blue color buttons */

.ui-btn-up-bl { border: 1px solid #036596; background: #2567ab; font-weight: bold; color: #fff; cursor: pointer;  text-shadow: 0 -1px 1px #036596; text-decoration: none; background-image: -moz-linear-gradient(top, #2567ab, #036596); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #2567ab),color-stop(1, #036596));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#2567ab', EndColorStr='#036596')"; }

.ui-btn-up-bl a.ui-link-inherit { color: #fff; }

.ui-btn-hover-bl { border: 1px solid #036596; background: #2567ab; font-weight: bold; color: #fff;  text-shadow: 0 -1px 1px #014D68; background-image: -moz-linear-gradient(top, #2567ab, #2567ab); text-decoration: none; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #2567ab),color-stop(1, #2567ab));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#2567ab', EndColorStr='#2567ab')";  }

.ui-btn-hover-bl a.ui-link-inherit { color: #fff; }

.ui-btn-down-bl { border: 1px solid #225377; background: #79ae21; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #225377; background-image: -moz-linear-gradient(top, #2567ab, #2567ab); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #2567ab),color-stop(1, #2567ab));   -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#2567ab', EndColorStr='#2567ab')"; }

.ui-btn-down-bl a.ui-link-inherit { color: #fff; }

.ui-btn-up-bl, .ui-btn-hover-bl, .ui-btn-down-bl { font-family: Helvetica, Arial, sans-serif; }

OK we got the buttons, now lets give them some functionality, lets start with delete adding this to "mscript.js"

$('.aDeleteBtn').live('click', function(ev){
    ev.preventDefault();
    $.mobile.showPageLoadingMsg();
    var li = $(this).parents('li');
    var details = $('#details');
    var nid = details.find('#attd').attr('nid');
    var user = {
        _id : li.attr("id"),
        name : li.attr("name"),
        votes : li.find(".count").text()
    };
    $.post("/nominations/eraseuser", { id: nid, user: user },
        function(data) {
            if (data){
               li.remove();
               var usersl = details.find('.users');
               usersl.listview('refresh');
            }else{
                showMsg('dashboard.error', 'dashboard.error_erasing_user');
            }
            $.mobile.hidePageLoadingMsg();
        }
    ).error(function() { $.mobile.hidePageLoadingMsg();
        showMsg('dashboard.error', 'dashboard.error_erasing_user'); });
    $('.aVoteBtn').slideToggle();
    $('.aDeleteBtn').slideToggle();
});

Here we take the li from where we get called and the details div to get the nomination id

We create the user object with the details and we send it to the server to erase that user and we hide the buttons, after calling the server we erase the user from the list if everything went right or show the error if any

Now, lets see the voting function

$('.aVoteBtn').live('click', function(ev){
    $.mobile.showPageLoadingMsg();
    ev.preventDefault();
    var li = $(this).parents('li');
    var id = li.attr('id');
    var details = $('#details');
    var nid = details.find('#attd').attr('nid');
    //var name = $('.details').find('legend').text();
    $.post("/nominations/vote", { id: nid, userid: id },
        function(data) {
            if (data){
                var votes = li.find('.count');
                votes.html(data);
                //updat voted list
            }else{
                showMsg('dashboard.error', 'dashboard.error_voting');
            }
            $.mobile.hidePageLoadingMsg();
        }
    ).error(function() {
        $.mobile.hidePageLoadingMsg();
        showMsg('dashboard.error', 'dashboard.error_voting');
    });
    $('.aVoteBtn').slideToggle();
    $('.aDeleteBtn').slideToggle();
});

Same way as before we got the data from the li and details, post to the server the information and hide the buttons, on return from server, lets update the count of the votes or show if any error

Now we are only missing the "invite" functionality, for this one we are going to recycle the add friends screen, first lets update the template "dashboardm.jade", lets add a class to our invite button to look like this:

a.doinvite(href="#", data-icon="plus", class="ui-btn-right") Invite

Then lets add to out page with id "#addf" this button to the footer

a.invite(href="#", data-role="button", data-theme="b") Invite

And then lets initialize the button hidden in our css

.invite{
    display: none;
}

Good now the script, lets update "#adduser" onclick to hide the button

$('.invite').hide();

Then we add the listener for the invite button

$('.doinvite').live('click', function(){
    $.mobile.showPageLoadingMsg();
    $('.invite').show();
    $.mobile.changePage( "#addf",
    {
        transition: "pop",
        reverse: false,
     changeHash: false
 });
});

We show our invite button and then the add friends panel.

To finish lets add our functionality to the invite button

$('.invite').live('click', function(){
    $.mobile.showPageLoadingMsg();
    var users = [];
    var userp;
    $('#lof').find(':checked').each(function(){
        users.push({
            "_id" : $(this).attr('id'),
            "name" : $(this).attr('name'),
            "votes" : 0
        });
    });
    var ul = users.length;
    if (ul > 0 && ul <= 1){
        userp = users[0];
    }else{
        userp = users;
    }
    $.post("/invite", { users: userp },
        function(data) {
            if (data){
                history.back();
                //showMsg('dashboard.warning', 'dashboard.invited');
            }else{
                showMsg('dashboard.error', 'dashboard.warning_invited');
            }
            $.mobile.hidePageLoadingMsg();
        }
    ).error(function() {
        $.mobile.hidePageLoadingMsg();
        showMsg('dashboard.warning', 'dashboard.warning_invited');
    });
});

We take the list of friends selected, send it to the server to invite the friends and show a message in any error

Done!! we have enough functionality...

Next we will see erase me, refresh, update our strings, check for any active session and update the routes for mobile to get our beta version complete

If you can help me out with the bugs or the features

Fork the code

https://github.com/nodejs-mexico/nomi-nation

Greetings

3.14.2012

Nomination mobile - part 3


This post its a part of a series about creating an app with node.js and express for Facebook... this time for mobile browsers

Part 1
Part 2

Version en español


Desktop version:


Part 10

*NOTE: jqm = jquery mobile

This time we will see how to create a new nomination, how to see the details of one and how to add friends

Lets start updating the "dashboardm.jade"

#newn(data-role="page")
    div(data-role="header", data-theme="e")
        h1 New nomination
    //header

    div(data-role="header", data-theme="e")
        #errornf
        form#newf.ui-body.ui-body-a.ui-corner-all
            fieldset
                div(data-role="fieldcontain")
                    label(for="name") Name:
                    input#name(type="text", cols="40", rows="8", name="name")
                    label(for="date") End Date:
                    input#date(type="date", cols="40", rows="8", name="date")
                    button#newnfs(data-theme="b") Submit
    //content

    div(data-role="footer")
        a( href="#", data-rel="back", data-icon="back") Back
    //footer

///page new nomination
#addf(data-role="page")
    div(data-role="header", data-theme="e")
        h1 Add friends
    //header

    div(data-role="header", data-theme="d")
        div(data-role="fieldcontain")
            fieldset#lof(data-role="controlgroup")
    //content

    div.ui-bar(data-role="footer", data-theme="b", data-position="fixed")
        a#bina(href="#", data-icon="back") Back
        a.add(href="#", data-role="button", data-theme="b") Add

///page add friends
#details(data-role="page")

    div(data-role="header", data-theme="b")
        a#cancel(href="#", data-icon="delete") Cancel
        h1 Details
        a#end(href="#", data-icon="check") End
    ///header

    div(data-role="content")
        #attd
        .name
        .endD
        ul.users(data-role="listview")
            li(data-role="list-divider") Swipe to Vote/Delete
    ///content

    .ui-bar(data-role="footer", data-theme="b", data-position="fixed")
        div(data-role="controlgroup", data-type="horizontal")
            a(href="#", data-rel="back", data-icon="back") Back
            a#adduser(href="#", data-icon="plus") Add
            a(href="#", data-icon="refresh") Refresh
            a#remove(href="#", data-icon="minus") Remove Me
    ///footer

///page details

We are adding 3 pages, one for each thing all inside the same html

In new form we add a form where we ask for the name and the end date

In add friends, we show the list of friends so the user can select and add to the nomination

And last its the details page, this template will serve for all the types of nominations, we will show some buttons depending on the type but this will be done via jquery

Good, lets update the script in "mscript.js"

First lets add our function to load the friends

function loadUsers(next){
    $.getJSON(next || 'http://nomination.cloudno.de/friends', function(data) {
    if (data.data.length > 0){
        var list = $('#lof');
     $.each(data.data, function(key, value){
      list.append('<input type="checkbox" name="'+value.name+'" id="'+value.id+'" />');
      list.append('<label for="'+value.id+'">'+value.name+'</label>');
     });
     $('#lof').trigger( 'updatelayout' );
     loadUsers(data.paging.next);
 }else{
     return;
 }
    }).error(function() { showMsg('dashboard.error', 'dashboard.error_friends'); });
}

In this case we are loading the list of friends from facebook and adding it to the list to use it later, this list will be loaded as soon as the user land on the page, for that lets call the function when jqm create the page:

$('#dashboard-mine').live('pagecreate', function(){    
    loadNominations('mine');
    loadUsers(null);
});

We have our friends loaded, lets create a new nomination, remember that we put a button in all the pages to create one, lets give that button some functionality in "mscript" we add:

$('.create').live('click', function(){
    $.mobile.changePage( "#newn", { transition: "pop"} );
});

With this we tell jqm that at clicking this btn we will go to the new nomination page

In that page, lets wait for the user input and handle the submit button

$('#newnfs').live('click', function(ev){
    ev.preventDefault();
    $.mobile.showPageLoadingMsg();
    var name = $('#name').val();
    var date = $('#date').val();
    if (name!=='' && date !==''){
 $('#errornf').html('');
 $.post("http://nomination.cloudno.de/nominations/create", { name: name, datep: date },
     function(data) {
            var list = $('#mine');
            list.append('<li id="' + 
                data._id + '" type="mine"><a class="details" href="#">' + 
                data.name + '</a></li>');
            list.listview('refresh');
            $.mobile.hidePageLoadingMsg();
            $.mobile.changePage( "#dashboard-mine" );
            return false;
        }
 ).error(function() {
     $.mobile.hidePageLoadingMsg();
     $('#errornf').html('Error saving the nomination, try again later');
     return false;
 });
 return false;
    }else{
 $('#errornf').html('Name and date required');
 $.mobile.hidePageLoadingMsg();
 return false;
    }
});

First we show the msg that we are working, we retrieve the data from the form, we do a simple check and post it, show a message if any error, if the post goes without errors lets add the nomination to the mine list, then refresh the list so all the elements gets the styles, close the loading message and return to mine list

We have a new nomination, lets see the details, for this lets add to our lists that functionality, all the elements in the lists have the class "details" so lets use that

$('.details').live('click', function(){
    $.mobile.showPageLoadingMsg();
 var li = $(this).parents('li');
 var id = li.attr('id');
 var type = li.attr('type');
 $('#details').find('#attd').attr('past',$.mobile.activePage.attr('id'));
 showNomination(id, type, false);
 $.mobile.changePage($("#details"));
});

First take the data to know which nomination is selected, add some data to details page and then load the nomination with the "showNomination" function, after that lets change the page to details

"showNomination" functions its like this:

//cargar la nominacion y llenar details
function showNomination(id, type, refresh){
    $.mobile.showPageLoadingMsg();
    $.getJSON('http://nomination.cloudno.de/nominations/'+id, function(data) {
        if (!data){
            //alert('Du! nominacion ya no existe o termino :(');
            showMsg('dashboard.warning', 'dashboard.warning_erased');
        }
        var details = $('#details');
        details.find('#attd').attr('nid',id);
        details.find('#attd').attr('type',type);
        details.find('.name').html(data.name);
        var daten = new Date();
        daten.setISO8601(data.endDate);
        details.find('.endD').html( daten.getDate()+'/'+(daten.getMonth()+1)+'/'+daten.getUTCFullYear());
        var ntype = type;
        if (ntype === 'appear'){
            $('#end').hide();
            $('#cancel').hide();
            $('#remove').show();
        }else if (type === 'mine'){
            $('#end').show();
            $('#cancel').show();
            $('#remove').hide();
        }else{
            $('#end').hide();
            $('#cancel').hide();
            $('#remove').hide();
        }
        var usersl = details.find('.users');
        usersl.html('');
        var userl = data.users.length;
        usersl.hide();
        usersl.append('<li data-role="list-divider">Swipe to Vote/Delete</li>');
        for (var i=0; i<userl;i++){
      usersl.append('<li id="'+data.users[i]._id+'" type="'+type+'">'+
    '<img src="https://graph.facebook.com/'+data.users[i]._id+'/picture"/>'+
                data.users[i].name+
                '<span class="ui-li-count count">'+data.users[i].votes+'</span></li>');
        }
        usersl.listview('refresh');
        usersl.show();
        $.mobile.hidePageLoadingMsg();
    }).error(function() {
        $.mobile.hidePageLoadingMsg();
        showMsg('dashboard.error', 'dashboard.error_showing'); 
    });
}

We bring the data for the selected nomination and we fill out the details page, depending on the type of the nomination is the buttons we show, we add the list of friends already nominated and we refresh the list, if any error we show the message to the user, in this function we also use a function to parse the date from Facebook called "setISO8601"

Date.prototype.setISO8601 = function (string) {
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));

    var offset = 0;
    var date = new Date(d[1], 0, 1);

    if (d[3]) { date.setMonth(d[3] - 1); }
    if (d[5]) { date.setDate(d[5]); }
    if (d[7]) { date.setHours(d[7]); }
    if (d[8]) { date.setMinutes(d[8]); }
    if (d[10]) { date.setSeconds(d[10]); }
    if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
    if (d[14]) {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }

    offset -= date.getTimezoneOffset();
    var time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
};

Ok, we have the nomination, now lets add friends:

$('#adduser').live('click', function(){
    $.mobile.changePage( "#addf",
    {
     transition: "pop",
     reverse: false,
     changeHash: false
 });
});

When the user click on add user we change to the list of friends page, in tis page we show all the friends and an add button

$('.add').live('click', function(){
    $.mobile.showPageLoadingMsg();
    var users = [];
    var userp;
    $('#lof').find(':checked').each(function(){
        users.push({
            "_id" : $(this).attr('id'),
            "name" : $(this).attr('name'),
            "votes" : 0
        });
    });
    var ul = users.length;
    if (ul > 0 && ul <= 1){
        userp = users[0];
    }else{
     userp = users;
    }
    var details = $('#details');
    var nid = details.find('#attd').attr('nid');
    var type = details.find('#attd').attr('type');
    $.post("http://nomination.cloudno.de/nominations/adduser", { id: nid, users: userp },
 function(data) {
     if (data){
            $.each(users,function(key, value){
                var usersl = details.find('.users');
                usersl.append('<li id="'+value._id+'" type="'+type+'">'+
                    '<img src="https://graph.facebook.com/'+value._id+'/picture"/>'+
                    value.name+
                    '<span class="ui-li-count count">0</span></li>');
                usersl.listview('refresh');
            });
            $.mobile.changePage( "#details" );
     }else{
      $.mobile.changePage( "#details" );
      showMsg('dashboard.error', 'dashboard.error_adduser');
     }
     $.mobile.hidePageLoadingMsg();
 }).error(function() { 
        $.mobile.hidePageLoadingMsg(); 
        showMsg('dashboard.error', 'dashboard.error_adduser'); 
    });
});

We got the list of selected users, we add them to the nomination and if everything its ok we add the to the list to the page to later return to the details page

Great, we are set now, next time we will see how to add/erase users in a nomination and how to invite more friends to play

Fork the code

[https://github.com/nodejs-mexico/nomi-nation](https://github.com/nodejs-mexico/nomi-nation)

Greetings

3.02.2012

Nomination mobile - part 2

This post its a part of a series about creating an app with node.js and express for Facebook... this time for mobile browsers

Part 1

Version en español

Desktop version:


Part 10


*NOTE: jqm = jquery mobile

We have our index, lets create the dashboard view, in this post we will create the views for all the nominations that an user can have such as "Mine", "Voted", "Appear", screen will look like this:

Dashboard

Dashboard

OK, lets start with the page layout, for that we will create a new file called "dashboardm.jade"

- if (invited)
    .invited(invited="#{invited}")
#dashboard-mine(data-role="page", data-theme="d")
    div(data-role="header", data-theme="b", data-position="inline")
        h1= t('app.name')
        a(href="#", data-icon="plus", class="ui-btn-right") Invite
        div(data-role="navbar")
            ul
                li
                    a#minel.ui-btn-active(href="#dashboard-mine", data-icon="star") Mine
                li
                    a#votedl(href="#dashboard-voted", data-icon="check") Voted
                li
                    a#appearl(href="#dashboard-appear", data-icon="alert") Appear
        ///navbar
    ///header
    div(data-role="content")
        fieldset.ui-grid-b
            .ui-block-a
                a.create(href="#", data-role="button", data-theme="b") Create
            .ui-block-b
            .ui-block-c
                a(href="#", data-role="button", data-theme="b", data-icon="refresh") Refresh
        br
        ul#mine(data-role="listview")
            li(data-role="list-divider") Select to see the details
    ///content

    div(data-role="footer", data-theme="b", data-position="fixed")
        label(data-theme="c", for="search-basic") Search nomination:
        input#searc-basic(data-theme="c", type="search", name="search", value="")
    ///footer

///page mine
#dashboard-voted(data-role="page", data-theme="d")
    div(data-role="header", data-theme="b", data-position="inline")
        h1= t('app.name')
        a(href="#", data-icon="plus", class="ui-btn-right") Invite
        div(data-role="navbar")
            ul
                li
                    a#minel(href="#dashboard-mine", data-icon="star") Mine
                li
                    a#votedl.ui-btn-active(href="#dashboard-voted", data-icon="check") Voted
                li
                    a#appearl(href="#dashboard-appear", data-icon="alert") Appear
        ///navbar
    ///header

    div(data-role="content")
        fieldset.ui-grid-b
            .ui-block-a
                a.create(href="#", data-role="button", data-theme="b") Create
            .ui-block-b
            .ui-block-c
                a(href="#", data-role="button", data-theme="b", data-icon="refresh") Refresh
        br
        ul#voted(data-role="listview")
            li(data-role="list-divider") Select to see the details
    ///content

    div(data-role="footer", data-theme="b", data-position="fixed")
        label(data-theme="c", for="search-basic") Search nomination:
        input#searc-basic(data-theme="c", type="search", name="search", value="")
    ///footer

///page voted
#dashboard-appear(data-role="page", data-theme="d")

    div(data-role="header", data-theme="b", data-position="inline")
        h1= t('app.name')
        a(href="#", data-icon="plus", class="ui-btn-right") Invite
        div(data-role="navbar")
            ul
                li
                    a#minel(href="#dashboard-mine", data-icon="star") Mine
                li
                    a#votedl(href="#dashboard-voted", data-icon="check") Voted
                li
                    a#appearl.ui-btn-active(href="#dashboard-appear", data-icon="alert") Appear
        ///navbar
    ///header

    div(data-role="content")
        fieldset.ui-grid-b
            .ui-block-a
                a.create(href="#", data-role="button", data-theme="b") Create
            .ui-block-b
            .ui-block-c
                a(href="#", data-role="button", data-theme="b", data-icon="refresh") Refresh
        br
        ul#appear(data-role="listview")
            li(data-role="list-divider") Select to see the details
    ///content

    div(data-role="footer", data-theme="b", data-position="fixed")
        label(data-theme="c", for="search-basic") Search nomination:
        input#searc-basic(data-theme="c", type="search", name="search", value="")
    ///footer

///page appear
#popup(data-role="page")
    
    div(data-role="header", data-theme="e")
        h1 Message
    ///header

    div(data-role="content", data-theme="d")
        h2#title
        p#msg
        p
            a(href="#one", data-rel="back", data-role="button", data-inline="true", data-icon="back") Back
    ///content

    div(data-role="footer")
        h4= t('app.name')
    ///footer

///page message

Line1-2: check if the user come with a reference nomination

3: Page for "mine" nominations, we specify that this div its a page with `data-role="page"` and we are also using the theme "d" with `data-theme="d"`

4: `data-role="header"` is for page header and we use the "b" theme and the position will be "in-line", jqm will put it at the top of the page as a bar

5: App title header, since its inside the header, this will be shown centered in the header bar

6: Invite link, its a regular link but since its on the header part jqm put styles to look like a button, we are adding an icon with `data-icon="plus"` this is the "+" sign and also we are adding a class `class="ui-btn-right"`, this class will tell jqm that we want this button on the right side, if we don't put this class the button will be on the left by default

7-14: Navigation bar `data-role="navbar"`, its a list that jqm will put in a vertical bar with big buttons, each button is a list item, each list item have a link tag, we will add the class `.ui-btn-active` to the current link so it looks different

17: the actual page content `data-role="content"`

18: for this page we will use the class `.ui-grid-b` in a fieldset, this will make jqm to divide this section of the page in 3 parts, which we will fill out with divs, other classes divide the screen with different sizes

19-23: all the divs to fill out the fieldset have the class `.ui-block-a`, the last character change for "b" or "c" to change the position in the fieldset, we are also adding links, in this case since we are not inside a navegation bar or the heade/footer bar we need to specify that we want to look like a button with `data-role="button"`

25-26: This will hold the list of nominations, in this case of type "mine", the list are created with `data-role="listview"`, we are also adding a list divider item that its just an item to display kind of a title in the list but it doesn't do anything, we put that with `data-role="list-divider"`, dividers can go in any part of the list

29: The footer `data-role="footer"` with theme "b" and fixed position, this will make the footer to always appear at the bottom, for example if the list of nominations is too big, the user have to scroll down to see all of the nominations, while the user is scrolling the footer will hide, when the user finish scrolling the footer will appear again at the bottom

30-31: A search form with a label and an input, will hide/appear with the footer

35-101: the others pages are like mine just changing the type of nomination and the active button in the navigation bar

We have all the "pages" in one, the first page described is the one that jqm will show for default, other wont be processed until the user change to that page or something, remember jqm load the pages with ajax

102-119: this page is different since we are going to use it to show messages to the user, at call we will fill out the divs here and show the page

This is all for the jade part, now lets add functionality with js, for that we need to create a new filled called"mscript.js"

$( document ).bind( "mobileinit", function() {
    // Make your jQuery Mobile framework configuration changes here!
    $.mobile.touchOverflowEnabled = true;
    $.mobile.allowCrossDomainPages = true;
});
function loadNominations(type){
    $.mobile.showPageLoadingMsg();
    $.getJSON('http://nomination.cloudno.de/nominations/'+type, function(data) {
        var list = $('#'+type);
        if (data.length < 1 ){
            //showMsg('dashboard.warning', 'dashboard.warning_zarro');
        }else{
            $.each(data, function(key, value){
                list.append('<li id="'+value._id+'" type="'+type+'"><a class="details" href="http://www.blogger.com/blogger.g?blogID=3456447871634044420#">'+value.name+'</a></li>');
            });
            list.listview('refresh');
        }
        $.mobile.hidePageLoadingMsg();
    }).error(function() { $.mobile.hidePageLoadingMsg(); showMsg('dashboard.error', 'dashboard.warning_nominations'); });
}
$('#dashboard-mine').live('pagecreate', function(){ 
    loadNominations('mine');
});
$('#dashboard-voted').live('pagecreate', function(){ 
    loadNominations('voted');
});
$('#dashboard-appear').live('pagecreate', function(){ 
    loadNominations('appear');
});
function showMsg(err, msg){
    var dialog = $("#popup");
 dialog.find("#title").html(err);
 dialog.find("#msg").html(msg);
 $.mobile.changePage( dialog );
}

1-5: start some jqm stuff

6-20: with this function we will load all the nominations by type, we first show a loading msg and then with ajax load this nominations in json format, if we don't have errors and we have some data lets add the results to the list of nominations and at the end we need to do a refresh() to the listview if not jqm wont format them and we erase the loading msg; if we have errors lets show or error page with some details

21-29: this is some kind of "ready" in jquery but for mobile, we are telling jqm here that when the page is ready load the nominations of that kind

30-35: The functionality to show msgs, in this case we load the dialog, fill it out with some details and then show the dialog with "changepage", this is to change the page programatically

OK, to see this page running lets add a temporary route to "dashboard.js", since we don't have a lot functionality we don't want users to get stuck on this page... add this around line 56:

/**
 * Dashboardm landing, TODO: erase this
*/
app.get('/dashboardm', function(req, res){
    res.render('dashboardm', 
        { 
            user: req.session.user, 
            error : req.param('error'), 
            type: 'dashboard', 
            invited: false,
            layout: 'layoutm'
        });
});

We are just showing the mobile dashboard page with the mobile layout and passing some needed variables, to see this page, login like always and when you are on the dashboard change the end of the url to "dashboardm" and you will see the mobile part

Fork the code

[https://github.com/nodejs-mexico/nomi-nation](https://github.com/nodejs-mexico/nomi-nation)

Greetings