/** * Created by Cap Adresse on 05/11/2014. * * Pour plus de sécurité * On englobe l'application dans une fonction anonyme qui s'execute immédiatement * Elle prend en paramètre l'objet jQuery pour pouvoir utiliser le symbole '$' sans risque de conflit * * @param {object} $ Objet jQuery permettant ainsi d'éviter les conflits avec l'utilisation du '$' * @return {void} */ (function($) { /** * Objet contenant les fonctions / variables pour Cap Birth * @type {object} */ cap.birth = cap.birth || {}; /** * Simple chaîne de caractère contenant le numéro de version * @type {string} */ cap.birth.version = '1.0.0'; /** * Options par défaut * @type {object} */ cap.birth.defaults = { // Gestion des messages d'erreur errorMessages: { // Pour afficher les messages d'erreur lors de la saisie showMessage: false, // Positionne le message d'erreur au dessus ou en dessous de l'input text (2 valeurs : 'top' || 'bottom') position: 'top' }, // Chemin permettant de faire les appels au service web path: '', // Timeout qu'on autorise au service web avant de considérer qu'il est HS (en millisecondes) timeout: 3000, // Callback à appeler si service down downCallback: '', // Username pour l'authentification HTTP username: '', // Mot de passe pour l'authentification HTTP password: '', // ID du champ Date de naissance birthDate_ID: 'birth-date', // ID du champ Ville de naissance birthPlace_ID: 'birth-place', // ID du champ Département department_ID: 'department' }; /** * Objet content les noms par défaut des champs * @type {object} */ cap.birth.defaultInputs = { // Champ Date de naissance birthDate: '', // Champ Ville de naissance birthPlace: '', // Champ Département department: '' }; /** * Tableau où seront stockés les différents champs (via jQuery) * C'est grâce à ces éléments qu'on pourra activer l'autocomplétion * @type {array} */ cap.birth.inputs = []; /** * Permet d'arrêter les appels Ajax lorsqu'on choisit une proposition * @type {boolean} */ cap.birth.stopCalling = false; /** * Permet de savoir si on a déjà appelé la fonction de validation ou non * @type {boolean} */ cap.birth.validated = false; /** * Permet de savoir si le service ne répond plus * @type {boolean} */ cap.birth.serviceHS = false; /** * Tableau contenant les différentes configurations * Une pour chaque formulaire différent * @type {array} */ cap.birth.settings = []; /** * Pour savoir sur quel formulaire on se situe * @type {integer} */ cap.birth.formIndex = 0; /** * Nombre de champs/formulaires présents * Pour savoir dans quel champ/formulaire on se situe * @type {integer} */ cap.birth.nbForms = 0; /** * Tableau contenant les propositions à afficher * @type {array} */ cap.birth.propositions = []; /** * Permet de stocker une fonction contenant l'appel Ajax * Pour pouvoir l'appeler quand on en a besoin * @type {mixed} */ cap.birth.appelAjax = null; /** * Variable permettant de savoir si une requête Ajax est en cours ou non * @type {boolean} */ cap.birth.inProgress = false; /** * Peut contenir une fonction permettant de mettre en attente une requête Ajax * Elle est automatiquement mise à null dès qu'elle a été appelée * @type {mixed} */ cap.birth.waitingCall = null; /** * Permet de définir des options par défaut pour l'ensemble des formulaires * Ce qui permet d'éviter de définir plusieurs fois des options qui sont identiques à chaque formulaire * * @param {object} options Options que l'on souhaite redéfinir */ cap.birth.setDefaultOptions = function( options ) { var _this = this; options = options || {}; $.extend(true, _this.defaults, options); }; /** * Initialisation de l'application * On récupère les champs du formulaire via leur ID * * @param {object} settings Paramètres de l'utilisateur * @return {void} */ cap.birth.init = function( settings ) { var _this = this; var settings_length = 1; var i; if (!$.isArray(settings)) { _this.settings.push($.extend(true, _this.defaults, settings)); } else { settings_length = settings.length; $.each(settings, function(index, setting) { _this.settings.push($.extend(true, {}, _this.defaults, setting)); }); } _this.nbForms = settings_length; for (i = 0; i < settings_length; i++) { // On utilise $.extend pour éviter qu'il n'écrase l'objet defaultInputs _this.inputs.push($.extend(true, {}, _this.defaultInputs)); _this.retrieveInputFields(i); if (_this.settings[i].errorMessages.showMessage && _this.inputs[i].birthPlace !== '') { cap.utils.addDivError(_this.inputs[i].birthPlace, _this.settings[i].errorMessages.position); } } // Vérification du service web _this.checkWebService(); }; /** * Permet de récupérer les différents champs via jQuery * grâce aux ID qui ont été passés en paramètres lors de l'initialisation * * @param {integer} i Instance/champ dans lequel on se trouve * @return {void} */ cap.birth.retrieveInputFields = function( i ) { var _this = this; var inputs = _this.inputs[i]; var settings = _this.settings[i]; $.each(inputs, function(index) { if (settings[index+'_ID'] !== '') { inputs[index] = document.getElementById(settings[index+'_ID']) !== null ? $('[id="'+settings[index+'_ID']+'"]') : ''; } }); }; /** * Permet de récupérer les ID des formulaires contenant les champs * * @return {void} */ cap.birth.retrieveFormID = function() { var _this = this; var closest_form; $.each(_this.inputs, function(index, value) { $.each(value, function(inputName, inputValue) { closest_form = cap.utils.inputExists(inputValue) ? inputValue.closest('form') : ''; if (closest_form.length && closest_form.attr('id') !== '') { _this.inputs[index].formID = closest_form.attr('id'); } }); }); }; /** * Permet de gérer les évènements sur les input * * @return {void} */ cap.birth.bindInputsEvents = function() { var _this = this; $.each(_this.inputs, function(index, value) { if (_this.inputs[index].birthPlace !== '') { // Capture de l'évènement keyup sur le champ Ville de naissance _this.inputs[index].birthPlace.on('keyup', function(event) { _this.formIndex = index; _this.search(event); }) .on('blur', function() { _this.stopCalling = true; }); } if (_this.inputs[index].department !== '') { // Capture de l'évènement keyup sur le champ Ville de naissance _this.inputs[index].department.on('keyup', function(event) { cap.utils.updateInputValue(_this.inputs[index].birthPlace, ''); }); } /** * HACK POUR CHROME POUR L'EMPÊCHER D'ACTIVER SON AUTOCOMPLETE * EN GROS, ON MET L'ATTRIBUT AUTOCOMPLETE À FALSE * CAR CHROME IGNORE TOTALEMENT LA VALEUR OFF */ cap.utils.changeAutocompleteAttribute(_this.inputs[index]); }); }; /** * Permet de vérifier que le service web répond * On l'appelle dès l'initialisation pour éviter des appels superflus * * @return {void} */ cap.birth.checkWebService = function() { var _this = this; var form_index = _this.formIndex; var settings = _this.settings[form_index]; if (settings.path !== '') { $.ajax({ dataType: 'json', timeout: settings.timeout, url: settings.path, beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Basic ' + btoa(settings.username + ':' + settings.password)); }, data: { request: 'SearchBirthplaceWS', sInput: '' } }) .fail(function(data) { if (cap.utils.nonDefini(data.statusText) === 'timeout') { _this.serviceDown(); } }); } }; /** * Fonction appelée lorsque le service web ne répond pas * Dans ce cas, on valide tout et on réinitialise les codes qualités * * @return {void} */ cap.birth.serviceDown = function() { var _this = this; this.serviceHS = true; if (_this.defaults.downCallback !== "") _this.defaults.downCallback(); }; /** * Fonction appelée à chaque caractère saisi dans le champ Ville de naissance * * @param {object} event Touche saisie par l'utilisateur * @return {void} */ cap.birth.search = function( event ) { var _this = this; var birth_date = ''; var department = ''; var form_index = _this.formIndex; var inputs = _this.inputs[form_index]; var settings = _this.settings[form_index]; var touche_autorisee = cap.utils.toucheAutorisee(event, false); var user_input = ''; // Lancement du traitement uniquement s'il s'agit d'une touche autorisée (voir la fonction pour plus de détails)... if (touche_autorisee && !_this.serviceHS) { _this.stopCalling = false; department = cap.utils.getInputValue(inputs.department); birth_date = cap.utils.getInputValue(inputs.birthDate); user_input = cap.utils.getInputValue(inputs.birthPlace); // Si on repasse ici, c'est qu'on modifie une ville de naissance qui a été validée _this.validated = false; if (user_input === '') { _this.propositions = []; _this.autocompleteInit(); // Suppression du message d'erreur if (cap.utils.inputExists(inputs.birthPlace)) { cap.utils.afficherMessage(inputs.birthPlace); } } else if (settings.path !== '' && !_this.serviceHS) { _this.appelAjax = function() { $.ajax({ dataType: 'json', timeout: settings.timeout, url: settings.path, beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Basic ' + btoa(settings.username + ':' + settings.password)); }, data: { request: 'SearchBirthplaceWS', sInput: user_input, sDate: birth_date, sDepartment: department } }) .done(function(data) { _this.list(data); }) .fail(function(data) { if (cap.utils.nonDefini(data.statusText) === 'timeout') { _this.serviceDown(); } }); }; if (!_this.inProgress) { _this.inProgress = true; // Appel ajax (via jQuery) _this.appelAjax(); } else { _this.waitingCall = function() { _this.inProgress = true; _this.appelAjax(); }; } } } }; /** * Fonction permettant d'afficher la liste des villes de naissance retournées par le service web * La liste sera affichée sous le champ grâce à jQueryUI * * @param {object} response Objet JSON renvoyé par la recherche de la ville de naissance * @return {void} */ cap.birth.list = function( response ) { var _this = this; var birth_place = ''; var form_index = _this.formIndex; var inputs = _this.inputs[form_index]; var jQinput = cap.utils.inputExists(inputs.birthPlace) ? inputs.birthPlace : ''; var message = cap.utils.nonDefini(response.sMessage); var proposition = {}; var settings = _this.settings[form_index]; if (!_this.stopCalling) { // Affichage du message d'erreur if (settings.errorMessages.showMessage && jQinput !== '' && message !== '') { cap.utils.afficherMessage(jQinput, message, settings.errorMessages.position); } // Suppression du message d'erreur else if (jQinput !== '') { cap.utils.afficherMessage(jQinput); } _this.propositions = []; if (cap.utils.nonDefini(response.Birthplace) !== '') { $.each(response.Birthplace, function(index, value) { birth_place = _this.afficherProposition(value); proposition = { value: birth_place, label: birth_place, Birthplace: value }; _this.propositions.push(proposition); }); } if (jQinput !== '') { if (jQinput.autocomplete('instance')) { jQinput.autocomplete('option', { source: _this.propositions }).autocomplete('search', ''); } else { _this.autocompleteInit(); jQinput.autocomplete('search', ''); } _this.inProgress = false; if (_this.waitingCall) { _this.waitingCall(); _this.waitingCall = null; } } } else { _this.inProgress = false; _this.waitingCall = null; _this.stopCalling = false; } }; /** * Permet de générer les propositions qui seront affichées dans les listes autocomplete * * @param {object} birthPlace Objet JSON renvoyé par la fonction SearchBirthplaceWS * @return {string} Chaîne de caractères qu'on affiche à l'utilisateur */ cap.birth.afficherProposition = function( birthPlace ) { birthPlace = birthPlace || {}; var birth_place = cap.utils.nonDefini(birthPlace.sBirthplace); var department = cap.utils.nonDefini(birthPlace.sDepartment); if (cap.utils.nonDefini(birthPlace.sDepartment) !== '') { birth_place += (birth_place !== '') ? ' ' : ''; birth_place += '(' + birthPlace.sDepartment + ')'; } return birth_place; } /** * Fonction appelée lors du choix d'une proposition dans la liste affichée à l'utilisateur * Remplis automatiquement les champs avec la sélection choisie * * @param {object} birthPlace Objet correspondant au choix de l'utilisateur * @return {boolean} */ cap.birth.choisirProposition = function( birthPlace ) { birthPlace = birthPlace || {}; var _this = this; var form_index = _this.formIndex; var inputs = _this.inputs[form_index]; if (cap.utils.nonDefini(event) !== '') { event.preventDefault(); } // Suppression du message d'erreur if (cap.utils.inputExists(inputs.birthPlace)) { cap.utils.afficherMessage(inputs.birthPlace); } cap.utils.updateInputValue(inputs.birthPlace, cap.utils.nonDefini(birthPlace.sBirthplace).substr(0, 64)); cap.utils.updateInputValue(inputs.department, cap.utils.nonDefini(birthPlace.sDepartment)); return false; }; /** * Validation * * @param {string} formID ID du formulaire dans lequel on se trouve * @param {function} callback Fonction de callback * @return {boolean} */ cap.birth.validate = function( formID, callback ) { if (!cap.utils.isString(formID) || cap.utils.nonDefini(formID) === '') { throw { message: "L'ID du formulaire n'est pas renseigné ou vide" }; } var _this = this; var form_index = _this.formIndex; var settings = _this.settings[form_index]; if (!_this.validated && !_this.serviceHS) { $.ajax({ dataType: 'json', url: settings.path, beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Basic ' + btoa(settings.username + ':' + settings.password)); }, data: { request: 'ValidateFormCiv' } }) .always(function() { _this.validated = true; if (cap.utils.isFunction(callback)) { callback(); } }); } else if (_this.serviceHS) { _this.validated = true; if (cap.utils.isFunction(callback)) { callback(); } } }; /** * Fonction permettant d'initialiser l'autocomplete de jQueryUI * L'initialisation se fait via le tableau cap.birth.propositions * * @return {void} */ cap.birth.autocompleteInit = function() { var _this = this; var form_index = _this.formIndex; var inputs = _this.inputs[form_index]; if (cap.utils.inputExists(inputs.birthPlace)) { inputs.birthPlace.autocomplete({ // Spécifie la source de données // On utilise une fonction pour empêcher jQuery de filtrer les résultats source: function(request, response) { response(_this.propositions); }, // Permet de sélectionner automatiquement la première proposition autoFocus: true, // Permet d'afficher les propositions même si rien n'est saisi minLength: 0, // Permet d'annuler le changement du input text lors de la sélection d'une proposition au clavier focus: function() { return false; }, // Action lors de la sélection d'une proposition select: function(event, ui) { /** * On test que la touche 'shift' n'est pas appuyée * Pour empêcher de sélectionner automatiquement la première proposition * Et permettre de remonter les champs input avec 'shift + tab' */ if (!event.shiftKey) { _this.choisirProposition(ui.item.Birthplace, event); } return false; }, change: function (event, ui) { if (!ui.item) { cap.utils.updateInputValue(inputs.birthPlace, ''); } } }); } }; // Ajout de l'objet dans le DOM window.cap.birth = cap.birth; // Lorsque la page est chargée, on peut capturer l'évènement keyup $(document).ready(function() { cap.birth.retrieveFormID(); cap.birth.bindInputsEvents(); }); })(jQuery);