/* CONSTRUCTEUR D'UN DEFLATER DE formulaire * * @container Formulaire ou autre élément contenant les champs * @tags Tableau contenant les éléments à prendre en compte * @attr Tableau contenant les attributs à prendre pour le nom (par ordre de priorité) * */ function FormDeflater(container, tags, attr){ /* [0] Vérification des INPUT =========================================================*/ var correctParams = container instanceof Element; correctParams = correctParams && tags instanceof Array; correctParams = correctParams && attr instanceof Array; /* [1] On formatte les données =========================================================*/ // On met les tags en minuscule for( var i = 0 ; i < tags.length ; i++ ) tags[i] = tags[i].toLowerCase(); // On met les attributs en minuscule for( var i = 0 ; i < attr.length ; i++ ) attr[i] = attr[i].toLowerCase(); /* [2] On enregistre les attributs =========================================================*/ this.container = container; this.tags = tags; this.attr = attr; } FormDeflater.prototype = { container: this.container, // Contiendra le 'formulaire' (
ou autre) tags: this.tags, // Contiendra les balises HTML à ne pas prendre en compte attr: this.attr // Contiendra la liste des attributs à prendre pour nom (par ordre de priorité) }; /* RETOURNE UN OBJET CONTENANT LES DONNÉES DU FORMULAIRE * * @return form Objet correspondant aux données du formulaire * */ FormDeflater.prototype.deflate = function(){ /* [1] On récupère tous les enfants =========================================================*/ var children = this.getChildren( this.container ); /* [2] On filtre les éléments qui ont pas le bon tag =========================================================*/ children = this.filterElements( children ); /* [3] On essaie de trouver les attributs primants (non vides et en premier dans la liste @this.attr) =========================================================*/ /* (0) On initialise l'objet de retour */ var object = {}; /* (1) Pour chacun des éléments */ for( var c = 0 ; c < children.length ; c++ ){ /* (2) Pour chacun des attributs par ordre de priorité */ for( var a = 0 ; a < this.attr.length ; a++ ){ // On récupère l'attribut var attr = children[c].getAttribute(this.attr[a]); /* (3) Si l'attribut est défini (pas null ni vide) */ if( attr !== null && attr.length > 0 ){ /* (4) Si on a pas déja un champ de même nom */ if( object.hasOwnProperty(attr) ){ var existing = object[attr]; // {1} Si l'existant est un tableau, on ajoute notre valeur // if( existing instanceof Array ) object[attr].push( { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked } ); // {2} Sinon, si c'est une valeur seule, on crée un tableau // else object[attr] = [ object[attr], { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked } ]; /* (5) Si c'est le premier champ avec ce nom, on le crée */ }else object[attr] = { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked }; // On en a fini pour cet élément break; } } } /* [4] On met en forme les données =========================================================*/ object = this.cleanOutput(object); return object; }; /* RETOURNE SI UN ELEMENT EST UN BOUTON DE TYPE RADIO/CHECKBOX OU NON * * @element Element en question * * @return result Renvoie si TRUE or FALSE il en est un * */ FormDeflater.prototype.checkable = function(element){ if( !(element instanceof Element) ) return false; if( element.tagName != 'INPUT' ) return false; if( ['radio', 'checkbox'].indexOf( element.getAttribute('type').toLowerCase() ) == -1 ) return false; return true; }; /* NETTOIE LES DONNÉES EN SORTIE POUR QU'ELLES SOIENT UTILISABLES ET OPTIMISÉES * * @input Données "brutes" * * @return output Données sans les valeurs inutiles et explicitées * */ FormDeflater.prototype.cleanOutput = function(input){ var output = {}; /* [1] On parcourt toutes les valeurs récupérées =========================================================*/ for( var key in input ){ /* [2] Si c'est un tableau =========================================================*/ if( input[key] instanceof Array ){ // VRAI si tous les éléments sont radio/checkbox var areCheckable = true; // Contiendra les indices des valeurs ou 'checked=TRUE' var checkedIndexes = []; /* (1) On vérifie si tous les champs sont checkables */ for( var i in input[key]) // si pas checkable, on arrête de vérifier if( !this.checkable(input[key][i].target) ){ areCheckable = false; break; // Sinon si checkable et checked=TRUE, on incrémente @nbChecked }else if( input[key][i].checked === true ) checkedIndexes.push(i); /* (2) Si c'est que des radio ou des checkbox avec une seule valeur à TRUE */ if( areCheckable ) if( checkedIndexes.length == 1 ) output[key] = input[key][checkedIndexes[0]].value; /* (3) Si c'est que des radio ou des checkbox avec plusieurs valeurs à TRUE */ else{ output[key] = []; for( var i in checkedIndexes ) output[key].push( input[key][checkedIndexes[i]].value ); } /* (4) Si c'est pas que des radio ou des checkbox, on met les valeurs */ else{ output[key] = []; for( var i in input[key] ) output[key].push( input[key][i].value ); } /* [3] S'il n'y a qu'une donnée (pas un tableau) =========================================================*/ }else{ /* (1) Si de type 'radio' ou 'checkbox', on met la valeur de 'checked' */ if( this.checkable(input[key].target) ) output[key] = input[key].checked ? input[key].value : null; /* (2) Sinon, on met la valeur de 'value' */ else output[key] = input[key].value; } } return output; }; /* RETOURNE LA LISTE DE TOUS LES ÉLÉMENTS QUEL QUE SOIT LE NIVEAU HIÉRARCHIQUE * * @parent Parent duquel on veut les enfants * * @return children Tableau contenant tous les enfants * */ FormDeflater.prototype.getChildren = function(parent){ // Si le parent n'est pas un élément, on retourne aucun enfant if( !(parent instanceof Element) ) return []; /* [1] Initialisation des variables =========================================================*/ // Contient la liste des enfants directs var children = [].slice.call(parent.children); // Contiendra la liste des enfants directs et indirects var allChildren = children; /* [2] On parcourt tous les enfants =========================================================*/ for( var i = 0 ; i < children.length ; i++ ){ // On relance la fonction récursivement sur tous les enfants allChildren = allChildren.concat( [].slice.call(this.getChildren(children[i])) ); } /* [3] On retourne le résultat =========================================================*/ return allChildren; }; /* FILTRE LES éléments en fonction de @this.tags et @this.attr * * @elements Le tableau contenant les éléments à trier * * @return filtered Retourne le tableau des éléments filtrés * */ FormDeflater.prototype.filterElements = function(elements){ // Contiendra les éléments correspondants aux critères var filtered = []; /* [1] On parcourt tous les éléments =========================================================*/ for( var i = 0 ; i < elements.length ; i++ ) // Si l'élément a le bon tag, on le garde if( this.tags.indexOf( elements[i].tagName.toLowerCase() ) > -1 ) filtered.push(elements[i]); /* [2] On retourne les éléments filtržés =========================================================*/ return filtered; }; /************/ /* USE CASE */ /************/ /* (1) Instanciation */ /*HIDDEN*/// var instance = new FormDeflater( /*HIDDEN*/// document.getElementById('myform'), /*HIDDEN*/// ['input', 'select'], // éléments à prendre en compte (tagName) /*HIDDEN*/// ['id', 'name', 'data-elementname'] // Attributs par ordre de priorité /*HIDDEN*/// ); /*HIDDEN*/// /* (2) On récupère l'objet */ /*HIDDEN*/// /*HIDDEN*/// var object = instance.deflate(); /*HIDDEN*///