Subversion Repositories eFlore/Applications.cel

Rev

Rev 2746 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/**
 * Constructeur WidgetSaisie par défaut
 */
function WidgetSaisie() {
        this.obsNbre = 0;
        this.nbObsEnCours = 1;
        this.totalObsATransmettre = 0;
        this.nbObsTransmises = 0;
        this.map = null;
        this.marker = null;
        this.latLng = null; // position en cours
        this.geocoder = null;
        this.debug = null;
        this.html5 = null;
        this.tagProjet = null;
        this.tagImg = null;
        this.tagObs = null;
        this.separationTagImg = null;
        this.separationTagObs = null;
        this.obsId = null;
        this.serviceSaisieUrl = null;
        this.serviceObsUrl = null;
        this.nomSciReferentiel = null;
        this.especeImposee = false;
        this.infosEspeceImposee = null;
        this.autocompletionElementsNbre = null;
        this.referentielImpose = null;
        this.serviceAutocompletionNomSciUrl = null;
        this.serviceAutocompletionNomSciUrlTpl = null;
        this.obsMaxNbre = null;
        this.dureeMessage = null;
        this.serviceAnnuaireIdUrl = null;
        this.serviceNomCommuneUrl = null;
        this.serviceNomCommuneUrlAlt = null;
        this.googleMapMarqueurUrl = null;
        this.chargementIconeUrl = null;
        this.chargementImageIconeUrl = null;
        this.calendrierIconeUrl = null;
        this.pasDePhotoIconeUrl = null;
}

/**
 * Initialisation du widget
 */
WidgetSaisie.prototype.init = function() {
        this.initCarto();
        this.initForm();
        this.initEvts();
        this.requeterIdentite();
};

/**
 * Initialise la cartographie
 */
WidgetSaisie.prototype.initCarto = function() {
        this.initialiserGoogleMap();
        this.initialiserAutocompleteCommune();
}

/**
 * Initialise le formulaire, les validateurs, les listes de complétion...
 */
WidgetSaisie.prototype.initForm = function() {
        if (this.obsId != '') {
                console.log('obsId: (' + typeof obsId + ') : ' + obsId);
                this.chargerInfoObs();
        }

        this.configurerDatePicker('#date');
        this.ajouterAutocompletionNoms();
        this.configurerFormValidator();
        this.definirReglesFormValidator();

        if(this.especeImposee) {
                $("#taxon").attr("disabled", "disabled");
                $("#taxon-input-groupe").attr("title","");
                var infosAssociee = {
                        label : this.infosEspeceImposee.nom_sci_complet,
                        value : this.infosEspeceImposee.nom_sci_complet,
                        nt : this.infosEspeceImposee.num_taxonomique,
                        nomSel : this.infosEspeceImposee.nom_sci,
                        nomSelComplet : this.infosEspeceImposee.nom_sci_complet,
                        numNomSel : this.infosEspeceImposee.id,
                        nomRet : this.infosEspeceImposee["nom_retenu.libelle"],
                        numNomRet : this.infosEspeceImposee["nom_retenu.id"],
                        famille : this.infosEspeceImposee.famille,
                        retenu : (this.infosEspeceImposee.retenu == 'false') ? false : true
                };
                $("#taxon").data(infosAssociee);
        }
}

/**
 * Initialise les écouteurs d'événements
 */
WidgetSaisie.prototype.initEvts = function() {
        var lthis = this;
        $('body').on('click', '.effacer-miniature', function() {
                $(this).parent().remove();
        });
        $("#fichier").bind('change', function (e) {
                arreter(e);
                var options = {
                        success: lthis.afficherMiniature.bind(lthis), // post-submit callback
                        dataType: 'xml', // 'xml', 'script', or 'json' (expected server response type)
                        resetForm: true // reset the form after successful submit
                };
                $("#miniature").append('<img id="miniature-chargement" class="miniature" alt="chargement" src="'+this.chargementImageIconeUrl+'"/>');
                $("#ajouter-obs").attr('disabled', 'disabled');
                if(lthis.verifierFormat($("#fichier").val())) {
                        $("#form-upload").ajaxSubmit(options);
                } else {
                        $('#form-upload')[0].reset();
                        window.alert("Le format de fichier n'est pas supporté, les formats acceptés sont "+   $("#fichier").attr("accept"));
                }
                return false;
        });
        // identité
        $("#courriel").on('blur', this.requeterIdentite.bind(this));
        $("#courriel").on('keypress', this.testerLancementRequeteIdentite.bind(this));
        $(".alert .close").on('click', this.fermerPanneauAlert);
        $(".has-tooltip").tooltip('enable');
        $("#btn-aide").on('click', this.basculerAffichageAide);
        $("#prenom").on("change", this.formaterPrenom.bind(this));
        $("#nom").on("change", this.formaterNom.bind(this));

        $("#courriel_confirmation").on('paste', this.bloquerCopierCollerCourriel.bind(this));
        $(".afficher-coord").on('click', this.basculerAffichageCoord.bind(this));
        $("#ajouter-obs").on('click', this.ajouterObs.bind(this));
        $(".obs-nbre").on('changement', this.surChangementNbreObs.bind(this));
        $("body").on('click', ".supprimer-obs", function() {
                var that = this,
                        suppObs = lthis.supprimerObs.bind(lthis);
                // bricolage pour avoir les deux contextes en même temps (objet et elt. du DOM)
                suppObs(that);
        });
        $("#transmettre-obs").on('click', this.transmettreObs.bind(this));
        $("#referentiel").on('change', this.surChangementReferentiel.bind(this));

        $("body").on('click', ".defilement-miniatures-gauche", function(event) {
                event.preventDefault();
                lthis.defilerMiniatures($(this));
        });
        $("body").on('click', ".defilement-miniatures-droite", function(event) {
                event.preventDefault();
                lthis.defilerMiniatures($(this));
        });

        // fermeture fenêtre
        if (this.debug == false) {
                $(window).on('beforeunload', function(event) {
                        return 'Êtes vous sûr de vouloir quiter la page?\nLes observations saisies mais non transmises seront perdues.';
                });
        }

        // Autocompletion du champ adresse
        $("#carte-recherche").on('focus', function() {
                $(this).select();
        });
        $("#carte-recherche").on('mouseup', function(event) {// Pour Safari...
                event.preventDefault();
        });
        $("#carte-recherche").keypress(function(e) {
                if (e.which == 13) {
                        e.preventDefault();
                }
        });
}

/**
 * Retourne true si l'extension de l'image "nom" est .jpg ou .jpeg
 */
WidgetSaisie.prototype.verifierFormat = function(nom) {
        var parts = nom.split('.');
        extension = parts[parts.length - 1];
        return (extension.toLowerCase() == 'jpeg' || extension.toLowerCase() == 'jpg');
};

/**
 * Affiche la miniature d'une image temporaire (formulaire) qu'on a ajoutée à l'obs
 */
WidgetSaisie.prototype.afficherMiniature = function(reponse) {
        if (this.debug) {
                var debogage = $("debogage", reponse).text();
                //console.log("Débogage upload : "+debogage);
        }
        var message = $("message", reponse).text();
        if (message != '') {
                $("#miniature-msg").append(message);
        } else {
                $("#miniatures").append(this.creerWidgetMiniature(reponse));
        }
        $('#ajouter-obs').removeAttr('disabled');
};

/**
 * Crée la miniature d'une image temporaire (formulaire), avec le bouton pour l'effacer
 */
WidgetSaisie.prototype.creerWidgetMiniature = function(reponse) {
        var miniatureUrl = $("miniature-url", reponse).text();
        var imgNom = $("image-nom", reponse).text();
        var html =
                '<div class="miniature">'+
                        '<img class="miniature-img" class="miniature" alt="'+imgNom+'" src="'+miniatureUrl+'"/>'+
                        '<button class="effacer-miniature" type="button">Effacer</button>'+
                '</div>'
        return html;
};

/**
 * Efface une miniature (formulaire)
 */
WidgetSaisie.prototype.supprimerMiniature = function(miniature) {
        miniature.parents('.miniature').remove();
}

/**
 * Efface toutes les miniatures (formulaire)
 */
WidgetSaisie.prototype.supprimerMiniatures = function() {
        $("#miniatures").empty();
        $("#miniature-msg").empty();
};

/**
 * Initialise l'autocomplétion de la commune, en fonction du référentiel
 */
WidgetSaisie.prototype.initialiserAutocompleteCommune = function() {
        var geocoderOptions = {
        },
        addressSuffix = '',
        lthis = this;

        switch(this.nomSciReferentiel) {
                case 'isfan':
                        // Si des résultats se trouvent dans ce rectangle, ils apparaîtront en premier.
                        // Ça marche moyen...
                        geocoderOptions.bounds = new google.maps.LatLngBounds(
                                new google.maps.LatLng(20.756114, -22.023927),
                                new google.maps.LatLng(38.065392, 33.78662)
                        );
                        break;
                case 'apd':
                        geocoderOptions.bounds = new google.maps.LatLngBounds(
                                        new google.maps.LatLng(-6.708254, -26.154786),
                                        new google.maps.LatLng(27.488781, 30.490722)
                                );
                        break;
                case 'bdtfx':
                case 'bdtxa':
                        geocoderOptions.region = 'fr';
                        addressSuffix = ', France';
        }

        $("#carte-recherche").autocomplete({
                //Cette partie utilise geocoder pour extraire des valeurs d'adresse
                source: function(request, response) {
                        geocoderOptions.address = request.term + addressSuffix;
                        lthis.geocoder.geocode( geocoderOptions, function(results, status) {
                                if (status == google.maps.GeocoderStatus.OK) {
                                        response($.map(results, function(item) {
                                                var retour = {
                                                        label: item.formatted_address,
                                                        value: item.formatted_address,
                                                        latitude: item.geometry.location.lat(),
                                                        longitude: item.geometry.location.lng()
                                                };
                                                return retour;
                                        }));
                                } else {
                                        lthis.afficherErreurGoogleMap(status);
                                }
                        });
                },
                // Cette partie est executee a la selection d'une adresse
                select: function(event, ui) {
                        lthis.latLng = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
                        lthis.deplacerMarker(lthis.latLng);
                }
        });
};

WidgetSaisie.prototype.afficherErreurGoogleMap = function(status) {
        if (this.debug) {
                $('#dialogue-google-map .contenu').empty().append(
                        '<pre class="msg-erreur">'+
                        "Le service de Géocodage de Google Map a échoué à cause de l'erreur : "+status+
                        '</pre>');
                this.afficherPanneau('#dialogue-google-map');
        }
};

WidgetSaisie.prototype.surDeplacementMarker = function() {
        this.latLng = this.marker.getPosition();
        this.trouverCommune(this.latLng);
        this.mettreAJourMarkerPosition(this.marker.getPosition());
};

WidgetSaisie.prototype.surClickDansCarte = function(event) {
        this.latLng = event.latLng;
        this.deplacerMarker(this.latLng);
};

/**
 * Place le marqueur aux coordonnées indiquées dans les champs "latitude" et "longitude"
 */
WidgetSaisie.prototype.geolocaliser = function() {
        var latitude = $('#latitude').val();
        var longitude = $('#longitude').val();
        this.latLng = new google.maps.LatLng(latitude, longitude);
        this.deplacerMarker(this.latLng);
};

WidgetSaisie.prototype.initialiserGoogleMap = function(localisationNavigateur) {
        // par défaut on tente de localiser le navigateur (avec son sextant)
        if (localisationNavigateur === undefined) {
                localisationNavigateur = true;
        }

        var lthis = this;
        var latLng,
                zoomDefaut;
        // Carte @TODO mettre ça dans la config
        if(this.nomSciReferentiel == 'bdtre') {
                latLng = new google.maps.LatLng(-21.10, 55.30);// Réunion
                zoomDefaut = 7;
        } else if(this.nomSciReferentiel == 'lbf') {
                latLng = new google.maps.LatLng(33.72211, 35.8603);// Liban
                zoomDefaut = 7;
        } else if(this.nomSciReferentiel == 'bdtxa') {
                latLng = new google.maps.LatLng(14.6, -61.08334);// Fort-De-France
                zoomDefaut = 8;
        } else if(this.nomSciReferentiel == 'isfan') {
                latLng = new google.maps.LatLng(29.28358, 10.21884);// Afrique du Nord
                zoomDefaut = 4;
        } else if(this.nomSciReferentiel == 'apd') {
                latLng = new google.maps.LatLng(8.75624, 1.80176);// Afrique de l'Ouest et du Centre
                zoomDefaut = 4;
        } else {
                latLng = new google.maps.LatLng(46.30871, 2.54395);// Centre de la France - @WARNING Prémilhat !! :)
                zoomDefaut = 5;
        }

        var options = {
                zoom: zoomDefaut,
                center: latLng,
                mapTypeId: google.maps.MapTypeId.HYBRID,
                mapTypeControlOptions: {
                        mapTypeIds: ['OSM', google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID, google.maps.MapTypeId.SATELLITE, google.maps.MapTypeId.TERRAIN]}
        };

        // Ajout de la couche OSM à la carte
        osmMapType = new google.maps.ImageMapType({
                getTileUrl: function(coord, zoom) {
                        return "http://tile.openstreetmap.org/" +
                        zoom + "/" + coord.x + "/" + coord.y + ".png";
                },
                tileSize: new google.maps.Size(256, 256),
                isPng: true,
                alt: 'OpenStreetMap',
                name: 'OSM',
                maxZoom: 19
        });

        // Création de la carte Google
        this.map = new google.maps.Map(document.getElementById('map-canvas'), options); //affiche la google map dans la div map_canvas
        this.map.mapTypes.set('OSM', osmMapType);

        // Création du Geocoder
        this.geocoder = new google.maps.Geocoder();

        // Marqueur google draggable
        this.marker = new google.maps.Marker({
                map: this.map,
                draggable: true,
                title: 'Ma station',
                icon: this.googleMapMarqueurUrl,
                position: latLng
        });

        this.initialiserMarker(latLng);

        // Tentative de geocalisation depuis le navigateur
        if (localisationNavigateur && navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                        var latitude = position.coords.latitude;
                        var longitude = position.coords.longitude;
                        lthis.latLng = new google.maps.LatLng(latitude, longitude);
                        lthis.deplacerMarker(lthis.latLng);
                });
        }

        // intéraction carte
        $("#geolocaliser").on('click', this.geolocaliser.bind(this));
        google.maps.event.addListener(this.marker, 'dragend', this.surDeplacementMarker.bind(this));
        google.maps.event.addListener(this.map, 'click', this.surClickDansCarte.bind(this));
};

WidgetSaisie.prototype.initialiserMarker = function(latLng) {
        if (this.marker != undefined) {
                this.marker.setPosition(latLng);
                this.map.setCenter(latLng);
                this.mettreAJourMarkerPosition(latLng);
        }
};

WidgetSaisie.prototype.deplacerMarker = function(latLng) {
        if (this.marker != undefined) {
                this.marker.setPosition(latLng);
                this.map.setCenter(latLng);
                this.mettreAJourMarkerPosition(latLng);
                this.trouverCommune(latLng);
        }
};

WidgetSaisie.prototype.mettreAJourMarkerPosition = function(latLng) {
        var lat = latLng.lat().toFixed(5);
        var lng = latLng.lng().toFixed(5);
        this.remplirChampLatitude(lat);
        this.remplirChampLongitude(lng);
};

WidgetSaisie.prototype.remplirChampLatitude = function(latDecimale) {
        var lat = Math.round(latDecimale * 100000) / 100000;
        $('#latitude').val(lat);
};

WidgetSaisie.prototype.remplirChampLongitude = function(lngDecimale) {
        var lng = Math.round(lngDecimale * 100000) / 100000;
        $('#longitude').val(lng);
};

WidgetSaisie.prototype.trouverCommune = function(pos) {
        if (this.latLng == null) { // tentative de protection contre le démon de Prémilhat
                return;
        }
        var lthis = this;
        $(function() {

                var url_service = lthis.serviceNomCommuneUrl;

                var urlNomCommuneFormatee = url_service.replace('{lat}', pos.lat()).replace('{lon}', pos.lng());
                $.ajax({
                        url : urlNomCommuneFormatee,
                        type : "GET",
                        dataType : "jsonp",
                        beforeSend : function() {
                                $(".commune-info").empty();
                                $("#dialogue-erreur .alert-txt").empty();
                        },
                        success : function(data, textStatus, jqXHR) {
                                $(".commune-info").empty();
                                $("#commune-nom").append(data.nom);
                                $("#commune-code-insee").append(data.codeINSEE);
                                $("#marqueur-commune").data('commune', {'nom' : data.nom, 'codeInsee' : data.codeINSEE});
                        },
                        statusCode : {
                            500 : function(jqXHR, textStatus, errorThrown) {
                                        if (this.debug) {
                                                $("#dialogue-erreur .alert-txt").append('<p id="msg">Un problème est survenu lors de l\'appel au service fournissante le nom des communes.</p>');
                                                reponse = jQuery.parseJSON(jqXHR.responseText);
                                                var erreurMsg = "";
                                                if (reponse != null) {
                                                        $.each(reponse, function (cle, valeur) {
                                                                erreurMsg += valeur + "<br />";
                                                        });
                                                }

                                                $("#dialogue-erreur .alert-txt").append('<p class="msg-erreur">Erreur 500 : '+errorThrown+"<br />"+erreurMsg+'</p>');
                                        }
                            }
                        },
                        error : function(jqXHR, textStatus, errorThrown) {
                                if (this.debug) {
                                        $("#dialogue-erreur .alert-txt").append('<p class="msg">Une erreur Ajax est survenue lors de la recherche de la commune.</p>');
                                        reponse = jQuery.parseJSON(jqXHR.responseText);
                                        var erreurMsg = "";
                                        if (reponse != null) {
                                                $.each(reponse, function (cle, valeur) {
                                                        erreurMsg += valeur + "<br />";
                                                });
                                        }

                                        $("#dialogue-erreur .alert-txt").append('<p class="msg-erreur">Erreur Ajax : '+errorThrown+' (type : '+textStatus+') <br />'+erreurMsg+'</p>');
                                }
                        },
                        complete : function(jqXHR, textStatus) {
                                var debugMsg = extraireEnteteDebug(jqXHR);
                                if (debugMsg != '') {
                                        if (this.debug) {
                                                $("#dialogue-erreur .alert-txt").append('<pre class="msg-debug msg">Débogage : '+debugMsg+'</pre>');
                                        }
                                }
                                if ($("#dialogue-erreur .msg").length > 0) {
                                        $("#dialogue-erreur").show();
                                }
                        }
                });
        });
};

WidgetSaisie.prototype.testerLancementRequeteIdentite = function(event) {
        if (event.which == 13) {
                this.requeterIdentite();
                this.event.preventDefault();
                this.event.stopPropagation();
        }
};

WidgetSaisie.prototype.requeterIdentite = function() {
        var lthis = this;
        var courriel = $("#courriel").val();
        var urlAnnuaire = this.serviceAnnuaireIdUrl + courriel;
        if (courriel != '') {
                $.ajax({
                        url : urlAnnuaire,
                        type : "GET",
                        success : function(data, textStatus, jqXHR) {
                                if (lthis.debug) {
                                        console.log('SUCCESS: '+textStatus);
                                }
                                if (data != undefined && data[courriel] != undefined) {
                                        var infos = data[courriel];
                                        lthis.surSuccesCompletionCourriel(infos, courriel);
                                } else {
                                        lthis.surErreurCompletionCourriel();
                                }
                        },
                        error : function(jqXHR, textStatus, errorThrown) {
                                if (lthis.debug) {
                                        console.log('ERREUR: '+textStatus);
                                }
                                lthis.surErreurCompletionCourriel();
                        },
                        complete : function(jqXHR, textStatus) {
                                if (lthis.debug) {
                                        console.log('COMPLETE: '+textStatus);
                                }
                                // @TODO harmoniser class="hidden" VS style="display:none;"
                                $("#zone-prenom-nom").removeClass("hidden").show();
                                $("#zone-courriel-confirmation").removeClass("hidden").show();
                        }
                });
        }
};

WidgetSaisie.prototype.surSuccesCompletionCourriel = function(infos, courriel) {
        $("#id_utilisateur").val(infos.id);
        $("#prenom").val(infos.prenom);
        $("#nom").val(infos.nom);
        $("#courriel_confirmation").val(courriel);
        $("#prenom, #nom, #courriel_confirmation").attr('disabled', 'disabled');
        this.focusChampFormulaire();
        this.masquerPanneau("#dialogue-courriel-introuvable");
};

WidgetSaisie.prototype.surErreurCompletionCourriel = function() {
        $("#prenom, #nom, #courriel_confirmation").val('');
        $("#prenom, #nom, #courriel_confirmation").removeAttr('disabled');
        this.afficherPanneau("#dialogue-courriel-introuvable");
};

WidgetSaisie.prototype.focusChampFormulaire = function() {
        $("#date").focus();
};

WidgetSaisie.prototype.chargerInfoObs = function() {
        var urlObs = this.serviceObsUrl + '/' + this.obsId;
        var lthis = this;
        $.ajax({
                url: urlObs,
                type: 'GET',
                success: function(data, textStatus, jqXHR) {
                        if (data != undefined && data != "") {
                                this.prechargerForm(data);
                        } else {
                                lthis.surErreurChargementInfosObs();
                        }
                },
                error: function(jqXHR, textStatus, errorThrown) {
                        lthis.surErreurChargementInfosObs();
                }
        });
};

// @TODO faire mieux que ça !
WidgetSaisie.prototype.surErreurChargementInfosObs = function() {
        alert("Erreur lors du chargement de l'observation");
}

WidgetSaisie.prototype.prechargerForm = function(data) {

        $("#milieu").val(data.milieu);

        $("#carte-recherche").val(data.zoneGeo);
        $("#commune-nom").text(data.zoneGeo);

        if(data.hasOwnProperty("codeZoneGeo")) {
                // TODO: trouver un moyen qui fonctionne lorsqu'on aura d'autres référentiels que INSEE
                $("#commune-code-insee").text(data.codeZoneGeo.replace('INSEE-C:', ''));
        }

        if(data.hasOwnProperty("latitude") && data.hasOwnProperty("longitude")) {
                var latLng = new google.maps.LatLng(data.latitude, data.longitude);
                this.mettreAJourMarkerPosition(latLng);
                this.marker.setPosition(latLng);
                this.map.setCenter(latLng);
                this.map.setZoom(16);
        }
};

WidgetSaisie.prototype.configurerFormValidator = function() {
        $.validator.addMethod(
                "dateCel",
                function (value, element) {
                        return value == "" || (/^[0-9]{2}[-\/][0-9]{2}[-\/][0-9]{4}$/.test(value));
                },
                "Format : jj/mm/aaaa. Date incomplète, utiliser 0, exemple : 00/12/2011.");
        $.extend($.validator.defaults, {
                errorClass: "control-group error",
                validClass: "control-group success",
                errorElement: "span",
                highlight: function(element, errorClass, validClass) {
                        if (element.type === 'radio') {
                                this.findByName(element.name).parent("div").parent("div").removeClass(validClass).addClass(errorClass);
                        } else {
                                $(element).parent("div").parent("div").removeClass(validClass).addClass(errorClass);
                        }
                },
                unhighlight: function(element, errorClass, validClass) {
                        if (element.type === 'radio') {
                                this.findByName(element.name).parent("div").parent("div").removeClass(errorClass).addClass(validClass);
                        } else {
                                if ($(element).attr('id') == 'taxon') {
                                        if ($("#taxon").val() != '') {
                                                // Si le taxon n'est pas lié au référentiel, on vide le data associé
                                                if ($("#taxon").data("value") != $("#taxon").val()) {
                                                        $("#taxon").data("numNomSel","");
                                                        $("#taxon").data("nomRet","");
                                                        $("#taxon").data("numNomRet","");
                                                        $("#taxon").data("nt","");
                                                        $("#taxon").data("famille","");
                                                }
                                                $("#taxon-input-groupe").removeClass(errorClass).addClass(validClass);
                                                $(element).next(" span.help-inline").remove();
                                        }
                                } else {
                                        $(element).parent("div").parent("div").removeClass(errorClass).addClass(validClass);
                                        $(element).next(" span.help-inline").remove();
                                }
                        }
                }
        });
};

WidgetSaisie.prototype.definirReglesFormValidator = function() {
        $("#form-observateur").validate({
                rules: {
                        courriel : {
                                required : true,
                                email : true},
                        courriel_confirmation : {
                                required : true,
                                equalTo: "#courriel"}
                }
        });
        $("#form-station").validate({
                rules: {
                        latitude : {
                                range: [-90, 90]},
                        longitude : {
                                range: [-180, 180]}
                }
        });
        $("#form-obs").validate({
                rules: {
                        date : "dateCel",
                        taxon : "required"
                }
        });
};

WidgetSaisie.prototype.configurerDatePicker = function(selector) {
        $.datepicker.setDefaults($.datepicker.regional["fr"]);
        $(selector).datepicker({
                dateFormat: "dd/mm/yy",
                maxDate: new Date,
                showOn: "button",
                buttonImageOnly: true,
                buttonImage: this.calendrierIconeUrl,
                buttonText: "Afficher le calendrier pour saisir la date.",
                showButtonPanel: true,
                onSelect: function(date) {
                        $(this).valid();
                }
        });
        $(selector + ' + img.ui-datepicker-trigger').appendTo(selector + '-icone.add-on');
};

WidgetSaisie.prototype.fermerPanneauAlert = function() {
        $(this).parentsUntil(".zone-alerte", ".alert").hide();
};

WidgetSaisie.prototype.formaterNom = function() {
        $(this).val($(this).val().toUpperCase());
};

WidgetSaisie.prototype.formaterPrenom = function() {
        var prenom = new Array();
        var mots = $(this).val().split(' ');
        for (var i = 0; i < mots.length; i++) {
                var mot = mots[i];
                if (mot.indexOf('-') >= 0) {
                        var prenomCompose = new Array();
                        var motsComposes = mot.split('-');
                    for (var j = 0; j < motsComposes.length; j++) {
                        var motSimple = motsComposes[j];
                        var motMajuscule = motSimple.charAt(0).toUpperCase() + motSimple.slice(1);
                        prenomCompose.push(motMajuscule);
                    }
                    prenom.push(prenomCompose.join('-'));
                } else {
                        var motMajuscule = mot.charAt(0).toUpperCase() + mot.slice(1);
                        prenom.push(motMajuscule);
                }
        }
        $(this).val(prenom.join(' '));
};

WidgetSaisie.prototype.basculerAffichageAide = function()  {
        if ($(this).hasClass('btn-warning')) {
                $(".has-tooltip").tooltip('enable');
                $(this).removeClass('btn-warning').addClass('btn-success');
                $('#btn-aide-txt', this).text("Désactiver l'aide");
        } else {
                $(".has-tooltip").tooltip('disable');
                $(this).removeClass('btn-success').addClass('btn-warning');
                $('#btn-aide-txt', this).text("Activer l'aide");
        }
};

WidgetSaisie.prototype.bloquerCopierCollerCourriel = function() {
        this.afficherPanneau("#dialogue-bloquer-copier-coller");
        return false;
};

WidgetSaisie.prototype.basculerAffichageCoord = function() {
        $("a.afficher-coord").toggle();
        $("#coordonnees-geo").toggle('slow');
        //valeur false pour que le lien ne soit pas suivi
        return false;
};

/**
 * Ajoute une observation saisie dans le formulaire à la liste des observations à transmettre
 */
WidgetSaisie.prototype.ajouterObs = function() {
        // Fermeture automatique des dialogue de transmission de données
        // @WARNING TEST
        $('#dialogue-obs-transaction-ko').hide();
        $('#dialogue-obs-transaction-ok').hide();

        if (this.validerFormulaire() == true) {
                this.obsNbre = this.obsNbre + 1;
                $(".obs-nbre").text(this.obsNbre);
                $(".obs-nbre").triggerHandler('changement');
                this.afficherObs();
                this.stockerObsData();
                this.supprimerMiniatures();
                if(! this.especeImposee) {
                        $("#taxon").val("");
                        $("#taxon").data("numNomSel",undefined);
                }
                $('#barre-progression-upload').attr('aria-valuemax', this.obsNbre);
                $('#barre-progression-upload .sr-only').text('0/'+this.obsNbre+" observations transmises");
        } else {
                this.afficherPanneau('#dialogue-form-invalide');
        }
};

/**
 * Affiche une observation dans la liste des observations à transmettre
 */
WidgetSaisie.prototype.afficherObs = function() {
        
        var commune = $("#commune-nom").text();
        commune = commune.trim() != "" ? commune : $("#carte-recherche").val();
        
        var code_insee = $('#commune-code-insee').text();
        code_insee = code_insee.trim() != "" ? "("+code_insee+")" : "";
        
        console.log(commune+'  -  '+code_insee);
        
        $("#liste-obs").prepend(
                '<div id="obs'+this.obsNbre+'" class="row-fluid obs obs'+this.obsNbre+'">'+
                        '<div class="span12">'+
                                '<div class="well">'+
                                        '<div class="obs-action pull-right has-tooltip" data-placement="bottom" '+
                                                'title="Supprimer cette observation de la liste à transmettre">'+
                                                '<button class="btn btn-danger supprimer-obs" value="'+this.obsNbre+'" title="'+this.obsNbre+'">'+
                                                        '<i class="icon-trash icon-white"></i>'+
                                                '</button>'+
                                        '</div> '+
                                        '<div class="row-fluid">'+
                                                '<div class="thumbnail span2">'+
                                                this.ajouterImgMiniatureAuTransfert()+
                                                '</div>'+
                                                '<div class="span9">'+
                                                        '<ul class="unstyled">'+
                                                                '<li>'+
                                                                        '<span class="nom-sci">'+$("#taxon").val()+'</span> '+
                                                                        this.ajouterNumNomSel()+'<span class="referentiel-obs">'+
                                                                        ($("#taxon").data("numNomSel") == undefined ? '' : '['+ this.nomSciReferentiel +']')+'</span>'+
                                                                        ' observé à '+
                                                                        '<span class="commune">'+commune+'</span> '+
                                                                        code_insee+' ['+$("#latitude").val()+' / '+$("#longitude").val()+']'+
                                                                        ' le '+
                                                                        '<span class="date">'+$("#date").val()+'</span>'+
                                                                '</li>'+
                                                                '<li>'+
                                                                        '<span>Lieu-dit :</span> '+$('#lieudit').val()+' '+
                                                                        '<span>Station :</span> '+$('#station').val()+' '+
                                                                        '<span>Milieu :</span> '+$('#milieu').val()+' '+
                                                                '</li>'+
                                                                '<li>'+
                                                                        'Commentaires : <span class="discretion">'+$("#notes").val()+'</span>'+
                                                                '</li>'+
                                                        '</ul>'+
                                                '</div>'+
                                        '</div>'+
                                '</div>'+
                        '</div>'+
                '</div>');
        $('#zone-liste-obs').removeClass("hidden").show();
};

WidgetSaisie.prototype.stockerObsData = function() {
        var lthis = this;
        
        var commune = $("#commune-nom").text();
        commune = commune.trim() == "" ? commune : $("#carte-recherche").val();
        
        $("#liste-obs").data('obsId'+this.obsNbre, {
                'date' : $("#date").val(),
                'notes' : $("#notes").val().trim(),

                'nom_sel' : $("#taxon").val(),
                'num_nom_sel' : $("#taxon").data("numNomSel"),
                'nom_ret' : $("#taxon").data("nomRet"),
                'num_nom_ret' : $("#taxon").data("numNomRet"),
                'num_taxon' : $("#taxon").data("nt"),
                'famille' : $("#taxon").data("famille"),
                'referentiel' : ($("#taxon").data("numNomSel") == undefined ? '' : lthis.nomSciReferentiel),

                'latitude' : $("#latitude").val(),
                'longitude' : $("#longitude").val(),
                'commune_nom' : commune,
                'commune_code_insee' : $("#commune-code-insee").text(),
                'lieudit' : $("#lieudit").val(),
                'station' : $("#station").val(),
                'milieu' : $("#milieu").val(),

                //Ajout des champs images
                'image_nom' : lthis.getNomsImgsOriginales(),
                'image_b64' : lthis.getB64ImgsOriginales(),

                // Ajout des champs étendus de l'obs
                'obs_etendue': lthis.getObsChpEtendus()
        });
};

/**
 * Retourne un Array contenant les valeurs des champs étendus
 */
WidgetSaisie.prototype.getObsChpEtendus = function() {
        var champs = [];

        $('.obs-chp-etendu').each(function() {
                var valeur = $(this).val(),
                        cle = $(this).attr('name'),
                        label = $(this).data('label');
                if (valeur != '') {
                        var chpEtendu = {cle: cle, label: label, valeur: valeur};
                        champs.push(chpEtendu);
                }
        });
        return champs;
}

WidgetSaisie.prototype.surChangementReferentiel = function() {
        this.nomSciReferentiel = $('#referentiel').val();
        $('#taxon').val('');
        this.initialiserAutocompleteCommune();
        this.initialiserGoogleMap(false);
};

WidgetSaisie.prototype.surChangementNbreObs = function() {
        if (this.obsNbre == 0) {
                $("#transmettre-obs").attr('disabled', 'disabled');
                $("#ajouter-obs").removeAttr('disabled');
        } else if (this.obsNbre > 0 && this.obsNbre < this.obsMaxNbre) {
                $("#transmettre-obs").removeAttr('disabled');
                $("#ajouter-obs").removeAttr('disabled');
        } else if (this.obsNbre >= this.obsMaxNbre) {
                $("#ajouter-obs").attr('disabled', 'disabled');
                this.afficherPanneau("#dialogue-bloquer-creer-obs");
        }
};

WidgetSaisie.prototype.transmettreObs = function() {
        var observations = $("#liste-obs").data();
        if (this.debug) {
                console.log(observations);
        }
        if (observations == undefined || jQuery.isEmptyObject(observations)) {
                this.afficherPanneau("#dialogue-zero-obs");
        } else {
                this.nbObsEnCours = 1;
                this.nbObsTransmises = 0;
                this.totalObsATransmettre = $.map(observations, function(n, i) { return i; }).length;
                this.depilerObsPourEnvoi();
        }
        return false;
};

WidgetSaisie.prototype.depilerObsPourEnvoi = function() {
        var observations = $("#liste-obs").data();
        // la boucle est factice car on utilise un tableau
        // dont on a besoin de n'extraire que le premier élément
        // or javascript n'a pas de méthode cross browsers pour extraire les clés
        // TODO: utiliser var.keys quand ça sera plus répandu
        // ou bien utiliser un vrai tableau et pas un objet
        for (var obsNum in observations) {
                var obsATransmettre = {
                        'projet' : this.tagProjet,
                        'tag-obs' : this.tagObs,
                        'tag-img' : this.tagImg
                };
                var utilisateur = {
                        id_utilisateur : $("#id_utilisateur").val(),
                        prenom : $("#prenom").val(),
                        nom : $("#nom").val(),
                        courriel : $("#courriel").val()
                };
                obsATransmettre['utilisateur'] = utilisateur;
                obsATransmettre[obsNum] = observations[obsNum];
                var idObsNumerique = obsNum.replace('obsId', '');
                if(idObsNumerique != "") {
                        this.envoyerObsAuCel(idObsNumerique, obsATransmettre);
                }

                break;
        }
};

WidgetSaisie.prototype.mettreAJourProgression = function() {
        this.nbObsTransmises++;
        var pct = (this.nbObsTransmises/this.totalObsATransmettre)*100;
        $('#barre-progression-upload').attr('aria-valuenow', this.nbObsTransmises);
        $('#barre-progression-upload').attr('style', "width: "+pct+"%");
        $('#barre-progression-upload .sr-only').text(this.nbObsTransmises+"/"+this.totalObsATransmettre+" observations transmises");

        if(this.obsNbre == 0) {
                $('.progress').removeClass('active');
                $('.progress').removeClass('progress-striped');
        }
};

WidgetSaisie.prototype.envoyerObsAuCel = function(idObs, observation) {
        var lthis = this;
        var erreurMsg = "";
        $.ajax({
                url : lthis.serviceSaisieUrl,
                type : "POST",
                data : observation,
                dataType : "json",
                beforeSend : function() {
                        $("#dialogue-obs-transaction-ko").hide();
                        $("#dialogue-obs-transaction-ok").hide();
                        $('.alert-txt').empty();
                        $(".alert-txt .msg-erreur").remove();
                        $(".alert-txt .msg-debug").remove();
                        $("#chargement").show();
                },
                success : function(data, textStatus, jqXHR) {
                        // mise à jour du nombre d'obs à transmettre
                        // et suppression de l'obs
                        lthis.supprimerObsParId(idObs);
                        lthis.nbObsEnCours++;
                        // mise à jour du statut
                        lthis.mettreAJourProgression();
                        if(lthis.obsNbre > 0) {
                                // dépilement de la suivante
                                lthis.depilerObsPourEnvoi();
                        }
                },
                statusCode : {
                        500 : function(jqXHR, textStatus, errorThrown) {
                                erreurMsg += "Erreur 500 :\ntype : "+textStatus+' '+errorThrown+"\n";
                    }
                },
                error : function(jqXHR, textStatus, errorThrown) {
                        erreurMsg += "Erreur Ajax :\ntype : "+textStatus+' '+errorThrown+"\n";
                        try {
                                reponse = jQuery.parseJSON(jqXHR.responseText);
                                if (reponse != null) {
                                        $.each(reponse, function (cle, valeur) {
                                                erreurMsg += valeur + "\n";
                                        });
                                }
                        } catch(e) {
                                erreurMsg += "Erreur inconnue: " + jqXHR.responseText;
                        }
                },
                complete : function(jqXHR, textStatus) {
                        var debugMsg = extraireEnteteDebug(jqXHR);

                        if (erreurMsg != '') {
                                if (this.debug) {
                                        $("#dialogue-obs-transaction-ko .alert-txt").append('<pre class="msg-erreur">'+erreurMsg+'</pre>');
                                        $("#dialogue-obs-transaction-ko .alert-txt").append('<pre class="msg-debug">Débogage : '+debugMsg+'</pre>');
                                }
                                var hrefCourriel = "mailto:cel_remarques@tela-botanica.org?"+
                                        "subject=Dysfonctionnement du widget de saisie "+ this.tagProjet +
                                        "&body="+erreurMsg+"%0D%0ADébogage :%0D%0A"+debugMsg;

                                // mise en valeur de l'obs en erreur + scroll vers celle ci en changeant le hash
                                $('#obs'+idObs+' div div').addClass('obs-erreur');
                                window.location.hash = "obs"+idObs;

                                $('#dialogue-obs-transaction-ko .alert-txt').append($("#tpl-transmission-ko").clone()
                                        .find('.courriel-erreur')
                                        .attr('href', hrefCourriel)
                                        .end()
                                        .html());
                                $("#dialogue-obs-transaction-ko").show();
                                $("#chargement").hide();
                                lthis.initialiserBarreProgression();
                        } else {
                                if (lthis.debug) {
                                        $("#dialogue-obs-transaction-ok .alert-txt").append('<pre class="msg-debug">Débogage : '+debugMsg+'</pre>');
                                }
                                if(lthis.obsNbre == 0) {
                                        setTimeout(function() {
                                                $("#chargement").hide();
                                                $('#dialogue-obs-transaction-ok .alert-txt').append($('#tpl-transmission-ok').clone().html());
                                                $("#dialogue-obs-transaction-ok").show();
                                                window.location.hash = "dialogue-obs-transaction-ok";
                                                lthis.initialiserObs();
                                        }, 1500);

                                }
                        }
                }
        });
};

WidgetSaisie.prototype.validerFormulaire = function() {
        $observateur = $("#form-observateur").valid();
        $station = $("#form-station").valid();
        $obs = $("#form-obs").valid();
        return ($observateur == true && $station == true && $obs == true) ? true : false;
};

WidgetSaisie.prototype.getNomsImgsOriginales = function() {
        var noms = new Array();
        $(".miniature-img").each(function() {
                noms.push($(this).attr('alt'));
        });
        return noms;
};

WidgetSaisie.prototype.getB64ImgsOriginales = function() {
        var b64 = new Array();
        $(".miniature-img").each(function() {
                if ($(this).hasClass('b64')) {
                        b64.push($(this).attr('src'));
                } else if ($(this).hasClass('b64-canvas')) {
                        b64.push($(this).data('b64'));
                }
        });

        return b64;
};

WidgetSaisie.prototype.supprimerObs = function(selector) {
        var obsId = $(selector).val();
        // Problème avec IE 6 et 7
        if (obsId == "Supprimer") {
                obsId = $(selector).attr("title");
        }
        this.supprimerObsParId(obsId);
};

WidgetSaisie.prototype.supprimerObsParId = function(obsId) {
        this.obsNbre = this.obsNbre - 1;
        $(".obs-nbre").text(this.obsNbre);
        $(".obs-nbre").triggerHandler('changement');
        $('.obs'+obsId).remove();
        $("#liste-obs").removeData('obsId'+obsId);
};

WidgetSaisie.prototype.initialiserBarreProgression = function() {
        $('#barre-progression-upload').attr('aria-valuenow', 0);
        $('#barre-progression-upload').attr('style', "width: 0%");
        $('#barre-progression-upload .sr-only').text("0/0 observations transmises");
        $('.progress').addClass('active');
        $('.progress').addClass('progress-striped');
};

WidgetSaisie.prototype.initialiserObs = function() {
        this.obsNbre = 0;
        this.nbObsTransmises = 0;
        this.nbObsEnCours = 0;
        this.totalObsATransmettre = 0;
        this.initialiserBarreProgression();
        $(".obs-nbre").text(this.obsNbre);
        $(".obs-nbre").triggerHandler('changement');
        $("#liste-obs").removeData();
        $('.obs').remove();
        $("#dialogue-bloquer-creer-obs").hide();
};

/**
 * Ajoute une boîte de miniatures avec défilement des images,
 * pour une obs de la liste des obs à transmettre
 */
WidgetSaisie.prototype.ajouterImgMiniatureAuTransfert = function() {
        var html = '',
                miniatures = '',
                premiere = true;
        if ($("#miniatures img").length >= 1) {
                $("#miniatures img").each(function() {
                        var visible = premiere ? 'miniature-selectionnee' : 'miniature-cachee';
                        premiere = false;
                        var css = $(this).hasClass('b64') ? 'miniature b64' : 'miniature',
                                src = $(this).attr("src"),
                                alt = $(this).attr("alt"),
                                //miniature = '<img class="'+css+' '+visible+'"  alt="'+alt+'"src="'+src+'" />';
                                miniature = '<div class="'+css+' '+visible+'"  alt="'+alt+'" style="background-image: url('+src+')" ></div>';
                        miniatures += miniature;
                });
                visible = ($("#miniatures img").length > 1) ? '' : 'defilement-miniatures-cache';
                var html =
                        '<div class="defilement-miniatures">'+
                                '<a href="#" class="defilement-miniatures-gauche '+visible+'">&#60;</a>'+
                                miniatures+
                                '<a href="#" class="defilement-miniatures-droite '+visible+'">&#62;</a>'+
                        '</div>';
        } else {
                html = '<img class="miniature" alt="Aucune photo"src="'+ this.pasDePhotoIconeUrl +'" />';
        }
        return html;
};

WidgetSaisie.prototype.defilerMiniatures = function(element) {

        var miniatureSelectionne = element.siblings("div.miniature-selectionnee");
        miniatureSelectionne.removeClass('miniature-selectionnee');
        miniatureSelectionne.addClass('miniature-cachee');
        var miniatureAffichee = miniatureSelectionne;

        if(element.hasClass('defilement-miniatures-gauche')) {
                if(miniatureSelectionne.prev('.miniature').length != 0) {
                        miniatureAffichee = miniatureSelectionne.prev('.miniature');
                } else {
                        miniatureAffichee = miniatureSelectionne.siblings(".miniature").last();
                }
        } else {
                if(miniatureSelectionne.next('.miniature').length != 0) {
                        miniatureAffichee = miniatureSelectionne.next('.miniature');
                } else {
                        miniatureAffichee = miniatureSelectionne.siblings(".miniature").first();
                }
        }
        //console.log(miniatureAffichee);
        miniatureAffichee.addClass('miniature-selectionnee');
        miniatureAffichee.removeClass('miniature-cachee');
};

WidgetSaisie.prototype.ajouterNumNomSel = function() {
        var nn = '';
        if ($("#taxon").data("numNomSel") == undefined) {
                nn = '<span class="alert-error">[non lié au référentiel]</span>';
        } else {
                nn = '<span class="nn">[nn'+$("#taxon").data("numNomSel")+']</span>';
        }
        return nn;
};

WidgetSaisie.prototype.ajouterAutocompletionNoms = function() {
        var lthis = this;
        $('#taxon').autocomplete({
                source: function(requete, add){
                        // la variable de requête doit être vidée car sinon le parametre "term" est ajouté
                        requete = "";
                        if($("#referentiel").val() != "autre") {
                                var url = lthis.getUrlAutocompletionNomsSci();
                                $.getJSON(url, requete, function(data) {
                                        var suggestions = lthis.traiterRetourNomsSci(data);
                                        add(suggestions);
                    });
                        }
        },
        html: true
        });

        $( "#taxon" ).bind("autocompleteselect", function(event, ui) {
                $("#taxon").data(ui.item);
                if (ui.item.retenu == true) {
                        $("#taxon").addClass('ns-retenu');
                } else {
                        $("#taxon").removeClass('ns-retenu');
                }
        });
};

WidgetSaisie.prototype.getUrlAutocompletionNomsSci = function() {
        var mots = $('#taxon').val();
        var url = this.serviceAutocompletionNomSciUrlTpl.replace('{referentiel}', this.nomSciReferentiel);
        url = url.replace('{masque}', mots);
        return url;
};

WidgetSaisie.prototype.traiterRetourNomsSci = function(data) {
        var suggestions = [];
        if (data.resultat != undefined) {
                $.each(data.resultat, function(i, val) {
                        val.nn = i;
                        var nom = {label : '', value : '', nt : '', nomSel : '', nomSelComplet : '', numNomSel : '',
                                nomRet : '', numNomRet : '', famille : '', retenu : false
                        };
                        if (suggestions.length >= this.autocompletionElementsNbre) {
                                nom.label = "...";
                                nom.value = $('#taxon').val();
                                suggestions.push(nom);
                                return false;
                        } else {
                                nom.label = val.nom_sci_complet;
                                nom.value = val.nom_sci_complet;
                                nom.nt = val.num_taxonomique;
                                nom.nomSel = val.nom_sci;
                                nom.nomSelComplet = val.nom_sci_complet;
                                nom.numNomSel = val.nn;
                                nom.nomRet = val.nom_retenu_complet;
                                nom.numNomRet = val["nom_retenu.id"];
                                nom.famille = val.famille;
                                // Tester dans ce sens, permet de considérer "absent" comme "false" => est-ce opportun ?
                                // en tout cas c'est harmonisé avec le CeL
                                nom.retenu = (val.retenu == 'true') ? true : false;

                                suggestions.push(nom);
                        }
                });
        }

        return suggestions;
};

WidgetSaisie.prototype.afficherPanneau = function(selecteur) {
        $(selecteur).fadeIn("slow").delay(this.dureeMessage).fadeOut("slow");
};

WidgetSaisie.prototype.masquerPanneau = function(selecteur) {
        $(selecteur).hide();
};

// lib hors objet --

/**
* Stope l'évènement courant quand on clique sur un lien.
* Utile pour Chrome, Safari...
*/
function arreter(evenement) {
        if (evenement.stopPropagation) {
                evenement.stopPropagation();
        }
        if (evenement.preventDefault) {
                evenement.preventDefault();
        }
        return false;
}

/**
 * Extrait les données de désinsectisation d'une requête AJAX de jQuery
 * @param jqXHR
 * @returns {String}
 */
function extraireEnteteDebug(jqXHR) {
        var msgDebug = '';
        if (jqXHR.getResponseHeader("X-DebugJrest-Data") != '') {
                var debugInfos = jQuery.parseJSON(jqXHR.getResponseHeader("X-DebugJrest-Data"));
                if (debugInfos != null) {
                        $.each(debugInfos, function (cle, valeur) {
                                msgDebug += valeur + "\n";
                        });
                }
        }
        return msgDebug;
}

/*
 * jQuery UI Autocomplete HTML Extension
 *
 * Copyright 2010, Scott González (http://scottgonzalez.com)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * http://github.com/scottgonzalez/jquery-ui-extensions
 *
 * Adaptation par Aurélien Peronnet pour la mise en gras des noms de taxons valides
 */
(function( $ ) {
        var proto = $.ui.autocomplete.prototype,
                initSource = proto._initSource;

        WidgetSaisie.prototype.filter = function( array, term ) {
                var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
                return $.grep( array, function(value) {
                        return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() );
                });
        }

        $.extend( proto, {
                _initSource: function() {
                        if ( this.options.html && $.isArray(this.options.source) ) {
                                this.source = function( request, response ) {
                                        response( filter( this.options.source, request.term ) );
                                };
                        } else {
                                initSource.call( this );
                        }
                },
                _renderItem: function( ul, item) {
                        if (item.retenu == true) {
                                item.label = "<strong>"+item.label+"</strong>";
                        }

                        return $( "<li></li>" )
                                .data( "item.autocomplete", item )
                                .append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) )
                                .appendTo( ul );
                }
        });
})( jQuery );