2.17.2012

Nomination part 9 - routes and ajax


Version en espaƱol


This post is part of the series about creating an app with node.js, express for Facebook


Part 4
Part 5
Part 6
Part 7
Part 8
Part 9
Part 10

Ready the ui lets put this to work, first lets create our routes to handle all the possible request from our front-end, we have to handle the nominations, creation, erase, add, end, cancel and... i think its all, all this will be in "dashboard.js":

/**
 * Rutas dashboard
*/
var fb = require('facebook-js'),
    url = 'http://nomination.cloudno.de/';

function findIndexByKeyValue(obj, key, value)
{
    var l = obj.length;
    for (var i = 0; i < l; i++) {
        if (obj[i][key] == value) {
            return i;
  }
 }
 return -1;
}

module.exports = function(app, log){
    var nominator = require('../controllers/nominator.js');

    app.error(function(err, req, res, next){
        log.error('Error: ' + err);
        if (err.message === 'nli'){
            res.redirect('?error='+err.message);
        }else{
            res.send(err.message);
        }
    });

    function checkUser(req, res, next){
        if (req.session.user){
            next();
        }else{
            log.notice('someone try to go directly to dashboard on: ' +
                new Date() );
            next(new Error('nli'));
        }
    }
    /**
     * Dashboard landing
    */
    app.get('/dashboard', checkUser, function(req, res){
        log.notice('landed on dashboard user: ' +
            req.session.user.id + ' on: ' + new Date() );
        var invited = req.param('invited');
        res.render('dashboard', { user: req.session.user, error : req.param('error'), type: 'dashboard', invited: invited });
    });

    /**
     * lista de amigos de facebook
     */
    app.get('/friends', checkUser, function(req, res){
        fb.apiCall('GET', '/me/friends', {access_token: req.session.user.access_token}, function(error, response, body){
            if (error){
                log.debug('error getting friends list:' + error);
                throw new Error('Error getting friends list');
            }
            log.notice('getting friends from user:' + req.session.user.id);
            res.json(body);
        });
    });

    /**
     * lista de mis nominaciones
     */
    app.get('/nominations/mine', checkUser, function(req, res){
        nominator.findMyNominations(req.session.user.id,function(err, data){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            res.json(data);
        });
    });

    /**
     * lista de nominaciones donde vote
     */
    app.get('/nominations/voted', checkUser, function(req, res){
        nominator.findVoted(req.session.user.id,function(err, data){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            res.json(data);
        });
    });

    /**
     * lista de nominaciones donde estoy nominado
     */
    app.get('/nominations/appear', checkUser, function(req, res){
        nominator.findNominated(req.session.user.id,function(err, data){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            res.json(data);
        });
    });

    /**
     * regresar nominacion
     */
    app.get('/nominations/:id', checkUser, function(req, res){
        nominator.findNomination(req.params.id,function(err, data){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            res.json(data);
        });
    });

    /**
     * Crear nominacion
     */
    app.post('/nominations/create', checkUser, function(req, res){
        var nomination = {
            'name' : req.param('name'),
            'owner' : req.session.user.id,
            'endDate' : new Date(req.param('datep')),
            'category' : "cat1",
            'sub_cat' : "sub1",
            'active' : true
        };
        nominator.createNomination(nomination, function(err, doc){
            if (err) { log.debug(err); return; }
            log.notice('nomination '+ req.param('name') +' created by: ' + req.session.user.id );
            res.json(doc);
        });
    });

    /**
     * Borrar nominacion
     */
    app.post('/nominations/erase', checkUser, function(req, res){
        //TODO: erase nomination
        var id = req.param('id');
        nominator.eraseNomination(id, function(err){
            if (err) { log.debug(err); res.json(null); return; }
            log.notice('nomination '+ req.param('name') +' erased by: ' + req.session.user.id );
            res.json(true);
        });
    });

    /**
     * Votar en nominacion
     */
    app.post('/nominations/vote', checkUser, function(req, res){
        //TODO: vote nomination, write in own wall, try to write in other user wall
        var voterid = req.session.user.id;
        var id = req.param('id');
        var userid = req.param('userid');
        //add a friend, try to write to the user wall and local
        nominator.findNomination(id,function(err, doc){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            nominator.vote(doc, voterid, userid, function(err, nom){
                if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
                var index = findIndexByKeyValue(nom.users, "_id", req.param('userid'));
                res.json(nom.users[index].votes);
                //post to my wall that i voted
                fb.apiCall(
                    'POST',
                    '/'+voterid+'/feed',
                    {
                        access_token: req.session.user.access_token,
                        message: 'Vote por "' + nom.users[index].name + '" en "' +
                            nom.name + '" en nomi-nation, vota tu tambien',
                        name: "Votar",
                        link: url + '?invited=' + req.param('id')
                    },
                    function (error, response, body) {
                        if (error) { log.debug('error posting on my wall'); return; }
                        log.notice('posted on current user wall: ' + voterid);
                    }
                );
                //post to the victim
                fb.apiCall(
                    'POST',
                    '/'+nom.users[index]._id+'/feed',
                    {
                        access_token: req.session.user.access_token,
                        message: 'Vote por ti en "' + nom.name + '" en nomi-nation ' +
                            'vota tu tambien',
                        name: "Votar",
                        link: url + '?invited=' + req.param('id')
                    },
                    function (error, response, body) {
                        if (error) { log.debug('error posting on voted user'); return; }
                        log.notice('posted on the voted user wall: ' + nom.users[index]._id);
                    }
                );
                log.notice('nomination '+ req.param('id') +' voted by: ' + req.session.user.id );
            });
        });
    });

    /**
     * Agregar usuario a nominacion
     */
    app.post('/nominations/adduser', checkUser, function(req, res){
        var users = req.param('users');
        var id = req.param('id');
        //add a friend, try to write to the user wall and local
        nominator.findNomination(id,function(err, doc){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            nominator.addUser(doc, users, function(err){
                if (err) { log.debug('error adding users'); res.json(null); return; }
                res.json(true);
                var usersl = req.param('users');
                var userl = 0;
                if (usersl instanceof Array){
                    userl = usersl.length;
                    for (var i=0;i<userl;i++){
                        fb.apiCall(
                            'POST',
                            '/'+usersl[i]._id+'/feed',
                            {
                                access_token: req.session.user.access_token,
                                message: 'Te agregue a "' + doc.name + '" en nomi-nation ' +
                                    'agrega a tus amigos tambien',
                                name: "Votar",
                                link: url + '?invited=' + req.param('id')
                            },
                            function (error, response, body) {
                                if (error) { log.debug('error posting on voted user'); return; }
                                log.notice('posted on the added user wall: ' + usersl[i]._id);
                            }
                        );
                    }
                }else{
                    fb.apiCall(
                        'POST',
                        '/'+usersl._id+'/feed',
                        {
                            access_token: req.session.user.access_token,
                            message: 'Te agregue a "' + doc.name + '" en nomi-nation ' +
                                'agrega a tus amigos tambien',
                            name: "Votar",
                            link: url + '?invited=' + req.param('id')
                        },
                        function (error, response, body) {
                            if (error) { log.debug('error posting on voted user'); return; }
                            log.notice('posted on the added user wall: ' + usersl._id);
                        }
                    );
                }
            });
        });
    });

    /**
     * Borrar usuario de nominacion
     */
    app.post('/nominations/eraseuser', checkUser, function(req, res){
        //just erased, dont notice anyone :(
        var user = req.param('user');
        var id = req.param('id');
        //add a friend, try to write to the user wall and local
        nominator.findNomination(id,function(err, doc){
            if (err) { log.debug('error getting nominations'); res.json(null); return; }
            if (user === 'eraseme'){
                var index = findIndexByKeyValue(doc.users, "_id", req.session.user.id);
                user = doc.users[index];
            }
            nominator.eraseUser(doc, user, function(err){
                if (err) { log.debug('error erasing nominations'); res.json(null); return; }
                res.json(true);
            });
        });
    });

    /**
     * Terminar nominacion
     */
    app.post('/nominations/end', checkUser, function(req, res){
        //TODO: end the nomination and declare a winner
        var id = req.param('id');
        nominator.findNomination(id,function(err, doc){
            if (err) { log.debug('error getting nominations:' + err); res.json(null); return; }
            var users = doc.users;
            var usersl = doc.users.length;
            var voters = doc.voters;
            var votersl = doc.voters.length;
            var winner = users[0];
            for (var j=1; j<usersl;j++){
                if (winner.votes < users[j].votes){
                    winner = users[j];
                }
            }
            res.json(winner);
            fb.apiCall(
                'POST',
                '/'+req.session.user.id+'/feed',
                {
                    access_token: req.session.user.access_token,
                    message: winner.name + ' gano "' + doc.name + '" en nomi-nation ' +
                        'crea tu propia nominacion',
                    name: "Crear",
                    link: url + '?invited=' + req.param('id')
                },
                function (error, response, body) {
                    if (error) { log.debug('error posting on voted user'); return; }
                    log.notice('posted on the created user wall: ' + req.session.user.id);
                }
            );
            for (var i=0;i<usersl;i++){
                if (users[i]._id == req.session.user.id){ continue; }
                fb.apiCall(
                    'POST',
                    '/'+users[i]._id+'/feed',
                    {
                        access_token: req.session.user.access_token,
                        message: winner.name + ' gano "' + doc.name + '" en nomi-nation ' +
                            'crea tu propia nominacion',
                        name: "Crear",
                        link: url
                    },
                    function (error, response, body) {
                        if (error) { log.debug('error posting on voted user'); return; }
                        log.notice('posted on the user wall: ' + users[i]._id);
                    }
                );
            }
            nominator.eraseNomination(id, function(err){
                if (err) { log.debug('error erasing nomination'); return; }
                log.notice('nomination '+ req.param('name') +' erased by: ' + req.session.user.id );
            });
            for (var i=0;i<votersl;i++){
                if (voters[i]._id == req.session.user.id){ continue; }
                fb.apiCall(
                    'POST',
                    '/'+voters[i]._id+'/feed',
                    {
                        access_token: req.session.user.access_token,
                        message: winner.name + ' gano "' + doc.name + '" en nomi-nation ' +
                            'crea tu propia nominacion',
                        name: "Crear",
                        link: url
                    },
                    function (error, response, body) {
                        if (error) { log.debug('error posting on voted user'); return; }
                        log.notice('posted on the user wall: ' + voters[i]._id);
                    }
                );
            }
            nominator.eraseNomination(id, function(err){
                if (err) { log.debug('error erasing nomination'); return; }
                log.notice('nomination '+ req.param('name') +' erased by: ' + req.session.user.id );
            });
        });
    });

};


First lets see our middleware and a little helper function

Line 7-16: "findIndexByKeyValue" this function will help us to find the index of creation object in the array, since we use this a lot we put in his own function


21-28: "app.error" will help us to catch any error the app send and show it to the user

30-38: small middleware to check if the user its logged in, if he is we just call "next()" if not we send the user to the index page with the error


Note on middleware: check that the middleware gets behind of the function that handles the route, you can put any number of middleware divided by ",", for example lets imagine we have "checkotherstuff" too, then the route will be: "checkuser, checkstuff, function..."

Now the routes
42-61: The dashboard its simple, we use our middleware and if everything is good we render the "dashboard.jade", "get friends", here we use the Facebook api to bring the list of friends from the user logged in, if we don't have errors we return the json with the friends array.

66-91: We get the type of list the client is asking, we call the controller and we return the json with the list of nominations array, same thing with all the other functions... another way to do it could be add a ":type" to the route and we end with something like: "/nominations/:type" and with code decide what to call to the controller and send the corresponding json, but for this example we leave like this

138-185: vote for an user, we take the parameters send in the post, the voting user which its in session, nomination id and the user to vote for, we search the nomination, if everything its ok we vote for it, if everything its ok we return the number of votes this user have, we post in the user wall who voted and in the voted wall.


Remaining routes are similar, any doubt ask in the comments


Now lets see how to call all this from our client, all this goes in "script.js"

/* Author: mrpix
*/
var options = {
    ns: { namespaces: ['translation'], defaultNs: 'translation'},
    useLocalStorage: true,
    resGetPath: 'locales/resources.json?lng=__lng__&ns=__ns__',
    dynamicLoad: true
};
$.i18n.init(options, function() {
    //TODO: add more text
});
// expresion para buscar texto que contenga cierto termino
jQuery.expr[':'].Contains = function(a,i,m){
    return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
// expresion para buscar texto que sea igual a otro
jQuery.extend(jQuery.expr[':'], {
     containsExactly: "$(a).text() == m[3]"
});
//mostrar mensajes modales
function showMsg(title, msg, extra){
    var dialog = $( "#dialog-modal" );
    dialog.attr('title', $.t(title));
    if (extra){
        dialog.find('#msg').text($.t(msg, {winner: extra}));
    }else{
        dialog.find('#msg').text($.t(msg));
    }
    dialog.dialog('open');
}
$(function() {
    //instanciar dialogo
    $( "#dialog-modal" ).dialog({
        autoOpen: false,
        modal: true,
        buttons: {
            Ok: function() {
                $( this ).dialog( "close" );
            }
        }
    });
    //guardamos la lista para futuras referencias
    var list = $('#selectable');
    //funcion para votar por un usuario
    var vote = function(ev){
        ev.preventDefault();
        var a = $(this);
        var tr = a.parent().parent();
        var id = tr.attr('id');
        var nid = $('.details').attr('nid');
        var name = $('.details').find('legend').text();
        $.post("/nominations/vote", { id: nid, userid: id },
            function(data) {
                if (data){
                    var votes = tr.find('.votes');
                    votes.html(data);
                }else{
                    showMsg('dashboard.error', 'dashboard.error_voting');
                }
            }
        ).error(function() {showMsg('dashboard.error', 'dashboard.error_voting'); });
        var list = $('#voted');
        var found = list.find('li:containsExactly('+name+')');
        if (found.length < 1){
            var li = $('<li id="'+nid+'" type="voted"><input type="checkbox" /><label>'+name+'</label></li>');
            list.append(li);
            li.find("label").click(checkOne);
            li.find("input").click(checkOne);
        }
    };
    //funcion para borrar usuario
    var erase = function(ev){
        ev.preventDefault();
        var a = $(this);
        var tr = a.parent().parent();
        var id = tr.attr('id');
        var name = tr.find('.name').text();
        var votes = tr.find('.votes').text();
        var nid = $('.details').attr('nid');
        var user = {
            _id : id,
            name : name,
            votes : votes
        };
        $.post("/nominations/eraseuser", { id: nid, user: user },
            function(data) {
                if (data){
                   tr.remove();
                }else{
                    showMsg('dashboard.error', 'dashboard.error_erasing_user');
                }
            }
        ).error(function() { showMsg('dashboard.error', 'dashboard.error_erasing_user'); });
    };
    //cargando amigos del usuario en session
    function loadUsers(next){
        $.getJSON(next || '/friends', function(data) {
            if (data.data.length > 0){
                $.each(data.data, function(key, value){
                    list.append('<li class="ui-state-default" id="'+value.id+'"><img width="30px" src="https://graph.facebook.com/'+value.id+'/picture"/><a>'+value.name+'</a></li>');
                });
                loadUsers(data.paging.next);
            }else{
                return;
            }
        }).error(function() { showMsg('dashboard.error', 'dashboard.error_friends'); });
    }
    //cargando nominaciones del usuario en session, depende del tipo
    function loadNominations(type){
        $.getJSON('/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+'"><input type="checkbox" /><label>'+value.name+'</label></li>');
                });
            }
            list.find("label").click(checkOne);
            list.find("input").click(checkOne);
        }).error(function() { showMsg('dashboard.error', 'dashboard.warning_nominations'); });
    }
    //para q solo se puede seleccionar una nominacion a la vez
    function checkOne(){
        var currentEl = $(this);
        $("input:checked").each(function(){
            $(this).attr('checked', false);
        });
        if (currentEl.is('input')){
            currentEl.attr('checked', true);
            showNomination(currentEl.parent().attr('id'), currentEl.parent().attr('type'));
            return;
        }
        var checkbox = currentEl.siblings('input');
        checkbox.attr('checked', !checkbox.attr('checked'));
        showNomination(currentEl.parent().attr('id'), currentEl.parent().attr('type'));
    }
    //cargar la nominacion y llenar details
    function showNomination(id, type, refresh){
        //TODO: show avatar
        $.getJSON('/nominations/'+id, function(data) {
            if (!data){
                //alert('Du! nominacion ya no existe o termino :(');
                showMsg('dashboard.warning', 'dashboard.warning_erased');
            }
            var details = $('.details');
            details.attr('nid', data._id);
            details.find('legend').html(data.name);
            var daten = new Date(''+data.endDate);
            details.find('.date').html($.t('dashboard.end_date')+' '+ daten.getDate()+'/'+(daten.getMonth()+1)+'/'+daten.getUTCFullYear());
            details.find('.refresh').attr('nid', data._id);
            details.find('.refresh').attr('type', type);
            var ntype = type;
            //console.log(data._id);
            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 tbody = details.find('.userst').find('tbody');
            tbody.html('');
            var userl = data.users.length;
            for (var i=0; i<userl;i++){
                var id = data.users[i]._id;
                var name = data.users[i].name;
                var votes = data.users[i].votes;
                var tr = $('<tr id="'+id+'"></tr>');
                tr.append('<td class="pic"><img width="20px" src="https://graph.facebook.com/'+id+'/picture"/></td>');
                tr.append('<td class="name">'+name+'</td>');
                tr.append('<td class="votes">'+votes+'</td>');
                var avote = $('<a class=".vote" href="#">'+$.t('dashboard.vote')+'</a>');
                avote.click(vote);
                var aerase = $('<a class=".erase" href="#">'+$.t('dashboard.erase')+'</a>');
                aerase.click(erase);
                var menu = $('<td></td>');
                menu.append(avote);
                menu.append(' / ');
                menu.append(aerase);
                menu.appendTo(tr);
                tbody.append(tr);
            }
            if (!refresh){
                details.show('slide');
            }
        }).error(function() { showMsg('dashboard.error', 'dashboard.error_showing'); });
    }
    //actualizar la nominacion en contexto
    $('.refresh').click(function(ev){
        ev.preventDefault();
        showNomination($(this).attr('nid'), $(this).attr('type'), 'refresh');
    });
    //cargar las nominaciones y usuarios
    loadNominations('mine');
    loadNominations('appear');
    loadNominations('voted');
    loadUsers(null);
    //viene referenciado, mostrar esa nominacion
    var invited = $('.invited');
    if (invited[0]){
        showNomination(invited.attr('invited'), 'voted');
    }
    //para filtrar la lista de amigos
    $("#filterinput").change( function () {
        var filter = $(this).val();
        if(filter) {
            // this finds all links in a list that contain the input,
            // and hide the ones not containing the input while showing the ones that do
            list.find("a:not(:Contains(" + filter + "))").parent().hide();
            list.find("a:Contains(" + filter + ")").parent().show();
        } else {
            list.find("li").show();
        }
        return false;
    }).keyup( function () {
        // fire the above change event after every letter
        $(this).change();
    });
    //inicializar el seleccionador de fecha
    $( "#datep" ).datepicker();
    //hacer seleccionable la lista de nominaciones
    $( "#selectable" ).selectable();
    //abrir el dialog de nueva nominacion
    $("#nn").click(function(){
        $( "#dialog-new" ).dialog( "open" );
    });
    //abrir el dialogo de agregar usuarios
    $("#am").click(function(){
        $( "#dialog-add" ).dialog( "open" );
    });
    //creamos el dialogo de nueva nominacion
    $( "#dialog-new" ).dialog({
        autoOpen: false,
        height: 300,
  width: 450,
  modal: true,
  buttons: {
            //TODO: put a t string :S
   "Create a nomination": function() {
                var dialog = $(this);
                var name = dialog.find('#name').val();
                var datep = dialog.find('#datep').val();
                $.post("/nominations/create", { name: name, datep: datep },
                    function(data) {
                        var list = $('#mine');
                        var li = $('<li id="'+data._id+'" type="mine"><input type="checkbox" checked="true" /><label>'+data.name+'</label></li>');
                        list.append(li);
                        li.find("label").click(checkOne);
                        li.find("input").click(checkOne);
                        li.find("label").trigger('click');
                        dialog.dialog( "close" );
                    }
                ).error(function() { showMsg('dashboard.error', 'dashboard.warning_creating'); });
   },
   Cancel: function() {
    $( this ).dialog( "close" );
   }
  },
  close: function() {
      var dialog = $(this);
            dialog.find('#name').val('');
            dialog.find('#datep').val('');
  }
 });
    //creamos el dialog de agregar usuarios
    $( "#dialog-add" ).dialog({
        autoOpen: false,
  height: 300,
  width: 450,
  modal: true,
  buttons: {
   "Add friend(s)": function() {
                var dialog = $(this);
                var tbody = $('.details').find('.userst').find('tbody');
                var users = [];
                var userp;
                $('#selectable').find('.ui-selected').each(function(key, value){
                    users.push({
                        "_id" : $(value).attr('id'),
                        "name" : $(value).text(),
                        "votes" : 0
                    });
                });
                var ul = users.length;
                if (ul > 0 && ul <= 1){
                    userp = users[0];
                }else{
                    userp = users;
                }
                var nid = $('.details').attr('nid');
                $.post("/nominations/adduser", { id: nid, users: userp },
                    function(data) {
                        if (data){
                            $.each(users,function(key, value){
                                var id = value._id;
                                var name = value.name;
                                var tr = $('<tr id="'+id+'"></tr>');
                                tr.append('<td class="pic"><img width="20px" src="https://graph.facebook.com/'+id+'/picture"/></td>');
                                tr.append('<td class="name">'+name+'</td>');
                                tr.append('<td class="votes">0</td>');
                                var avote = $('<a class=".vote" href="#">'+$.t('dashboard.vote')+'</a>');
                                avote.click(vote);
                                var aerase = $('<a class=".erase" href="#">'+$.t('dashboard.erase')+'</a>');
                                aerase.click(erase);
                                var menu = $('<td></td>');
                                menu.append(avote);
                                menu.append(' / ');
                                menu.append(aerase);
                                menu.appendTo(tr);
                                tbody.append(tr);
                            });
                            dialog.dialog( "close" );
                        }else{
                            dialog.dialog( "close" );
                            showMsg('dashboard.error', 'dashboard.error_adduser');
                        }
                    }
                ).error(function() { showMsg('dashboard.error', 'dashboard.error_adduser'); });
   },
   Cancel: function() {
    $( this ).dialog( "close" );
   }
  },
  close: function() {
   //TODO:
  }
 });
    //para cancelar nominacion
    $('#cancel').click(function(ev){
        ev.preventDefault();
        var nid = $('.details').attr('nid');
        $.ajax({
            type: 'POST',
            url: '/nominations/erase',
            data: {id : nid},
            success: function(data){
                if (!data){ showMsg('dashboard.error', 'dashboard.error_erasing'); return;}
                showMsg('dashboard.warning', 'dashboard.warning_erasing');
                $('#'+nid).remove();
                while($('#'+nid).length > 0){
                    $('#'+nid).remove();
                }
                $('.details').hide();
            },
            error: function(){
                showMsg('dashboard.error', 'dashboard.error_erasing');
            },
            dataType: 'json'
        });
    });
    //para terminar nominacion
    $('#end').click(function(ev){
        ev.preventDefault();
        var nid = $('.details').attr('nid');
        $.ajax({
            type: 'POST',
            url: '/nominations/end',
            data: {id : nid},
            success: function(data){
                if (!data){ showMsg('dashboard.error', 'dashboard.error_ending'); return;}
                showMsg('dashboard.warning','dashboard.win', data.name);
                $('#'+nid).remove();
                while($('#'+nid).length > 0){
                    $('#'+nid).remove();
                }
                $('.details').hide();
            },
            error: function(){
                showMsg('dashboard.error', 'dashboard.error_ending');
            },
            dataType: 'json'
        });
    });
    //para remover al usuario actual de la nominacion
    $('#remove').click(function(ev){
        ev.preventDefault();
        var uid = $(this).attr('uid');
        var nid = $('.details').attr('nid');
        $.post("/nominations/eraseuser", { id: nid, user: 'eraseme' },
            function(data) {
                if (data){
                    //get the row of the user and erase it
                   $('.details').find('#'+uid).remove();
                }else{
                    showMsg('dashboard.error', 'dashboard.error_removing');
                }
            }
        ).error(function() { showMsg('dashboard.error', 'dashboard.error_removing'); });
    });
    //escuchar por los clicks en votar y borrar
    $('.vote').click(vote);
    $('.erase').click(erase);
});

Line 1-11: we start the i18next

12-19: lets extend jquery functionality to find text that contains or match the term passed

21-30: this will show a modal dialog with errors or announcements to the user so we done use an ugly alert

33-41: initialize our messages dialog

45-94: vote and erase user, first lets prevent the regular behavior with "preventDefault", we get the data from the dom and we make a post with jquery sending the data to "/nominations/vote" or erase, if everything goes ok we put the number of votes or erase the user from the list, in any error we show an error msg

96-107: loading the list of friends, i got an error in opera here, it loads the list anyway, im not sure how to fix this, any idea?

109-122: load the nominations by type and we add them to the left list

124-137: we are just letting the user to select one nomination at the time, once is selected we will show the details for it, we un-select first others selected

139-194: showing the details, load first the data and fill out the details with jquery, we generate the dom with jquery in the for to fill out the table

239-334: add new nomination and add user opens a dialog, in the first one just ask the name of the nomination and the end date, the other one show the list of friends of the user with a filter at the top to search quickly an user... the list load once the user log in to the page so we don't have delay while opening this dialog.

336-396: cancel, remove and end button, cancel just end the nomination and don't declare any winner, remove its to remove yourself for one nomination, in case you don't want to be listed there, if you erase yourself or another user it cannot be added again and end its to declare a winner and end the nomination, all the participants will be notified with a wall post

Cool, this post don't pretend to be a jquery tutorial but if you need help or want a better tutorial about this i can create a post for that.

Also if you need help creating an app in Facebook or how to deploy a node app i can explain that more in depth

With this we have an app with enough functionality, we still need to polish it but for now lets put it in a server and we start to notify to the users to test our beta version

Start nominating your friends in:


If you have feedback please comment it or put it on the facebook page


We are creating a logo, send us your examples we may put yours :)

Next time we will add just some things and we will create our github readme to declare it a beta version ready

Fork the code in github:


Greetings 

No comments:

Post a Comment