286 lines
9.6 KiB
JavaScript
286 lines
9.6 KiB
JavaScript
|
||
/* CONSTRUCTEUR D'UN DEFLATER DE formulaire
|
||
*
|
||
* @container<Element> Formulaire ou autre élément contenant les champs
|
||
* @tags<Array> Tableau contenant les éléments à prendre en compte
|
||
* @attr<Array> 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' (<form> 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<Object> 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> Element en question
|
||
*
|
||
* @return result<Boolean> Renvoie si TRUE or FALSE il en est un
|
||
*
|
||
*/
|
||
FormDeflater.prototype.checkable = function(element){
|
||
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<Object> Données "brutes"
|
||
*
|
||
* @return output<Object> 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<Element> Parent duquel on veut les enfants
|
||
*
|
||
* @return children<Array> 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<Array> Le tableau contenant les éléments à trier
|
||
*
|
||
* @return filtered<Array> 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*///
|