NxTIC/js/lib/form-builder.js

961 lines
29 KiB
JavaScript

/****************************************************/
/* _ _ _____ ___ _ ___ _______ __ */
/* | | | |_ _|_ _| | |_ _|_ _\ \ / / */
/* | | | | | | | || | | | | | \ V / */
/* | |_| | | | | || |___ | | | | | | */
/* \___/ |_| |___|_____|___| |_| |_| */
/****************************************************/
/* [1] REFERENCES DE VALEURS
=========================================================*/
// Construction d'un référence
var ref = function(ref_table, variable){
/* (1) On cherche une nouvelle clé */
var key = null;
while( key == null || ref_table.hasOwnProperty(key) )
key = '$'+ ( 0x10000000+Math.floor( Math.random()*0xefffffff) ).toString(16)+'$';
/* (2) On attribue la valeur */
ref_table[key] = variable;
/* (3) On retourne la clé */
return key;
};
/************************************************************************************/
/* _____ ___ ____ __ __ ____ _ _ ___ _ ____ _____ ____ */
/* | ___/ _ \| _ \| \/ | | __ )| | | |_ _| | | _ \| ____| _ \ */
/* | |_ | | | | |_) | |\/| |_____| _ \| | | || || | | | | | _| | |_) | */
/* | _|| |_| | _ <| | | |_____| |_) | |_| || || |___| |_| | |___| _ < */
/* |_| \___/|_| \_\_| |_| |____/ \___/|___|_____|____/|_____|_| \_\ */
/************************************************************************************/
/* CONSTRUCTEUR -> INITIALISE UNE L'INSTANCE
*
* @form_object<Object> Objet définissant le formulaire
*
*/
var FormBuilder = function(form_object){
/* On définit le formulaire (sous forme de description formelle) */
this.form_object = form_object;
};
/************************************************************************/
/* _ _____ _____ ____ ___ ____ _ _ _____ _____ ____ */
/* / \|_ _|_ _| _ \|_ _| __ )| | | |_ _| ____/ ___| */
/* / _ \ | | | | | |_) || || _ \| | | | | | | _| \___ \ */
/* / ___ \| | | | | _ < | || |_) | |_| | | | | |___ ___) | */
/* /_/ \_\_| |_| |_| \_\___|____/ \___/ |_| |_____|____/ */
/************************************************************************/
/* DEFINITION DES ATTRIBUTS
*
*/
FormBuilder.prototype = {
form_object: this.form_object, // objet permettant la construction du formulaire
defs_object: {}, // objet des définitions des éléments
parent_element: null, // element qui contiendra le formulaire
built_form: null, // Object correspondant au formulaire construit
root_element: null, // Element correspondant à l'objet construit
ref_table: {}, // Tableau contenant la liste des références
ref_assoc: {} // Association entre table de référence et scope d'entrée
};
/* DEFINITION DES ATTRIBUTS STATIQUES
*
*/
FormBuilder.regex = {
reg_in_key: /^\/\^(.+)\$\/$/, // Regex associée à une "regex" incluse dans une clé
reg_out_val: /\{(\$[1-9])\}/, // Regex associée à la valeur du dernier match de "regex"
pri_out_val: /\{([a-z-]+)\}/g, // Regex associée à une variable primitif à remplacer
pri_in_key: /^\$([a-z-]+)$/, // Regex associée à la clé d'une variable primitive
arr_out_set: /^\{\{([a-z-]+)\}\}$/, // Regex associée à un tableau à remplacer
arr_out_val: /\{([a-z-]+)\.([a-z-]+)\}/g, // Regex associée à une valeur de tableau à remplacer (primitif)
arr_in_key: /^\$\$([a-z-]+)$/, // Regex associée à la clé d'un tableau
fun_out_val: /^\{([a-z-]+)\}\(\)$/, // Regex associée à une function incluse dans une clé
ref_pri: /^\$[a-f0-9]{8}\$$/ // Clé de référence
};
FormBuilder.spread_attr = [ // Liste des attributs diffusant le scope
'children', // diffuse aux éléments
'next_nodes', // diffuse aux éléments
'prev_nodes', // diffuse aux éléments
'attributes', // diffuse aux attributs
'node_link',
'listeners'
];
FormBuilder.allowed_attr = [ // Liste des attributs fixes autorisés
'node', /* nom du modèle à utiliser (dans @defs_object) */
'node_type', /* nom réel de l'élément */
'next_nodes', /* tableau des enfants à ajouter à la suite de l'élément */
'prev_nodes', /* tableau des enfants à ajouter à avant l'élément */
'attributes', /* tableau des attributs à passer au niveau inférieur */
'children', /* tableau des enfants à passer au niveau inférieur */
'text', /* texte à insérer (innerHTML), REMPLACE 'children' */
'repeat' /* nombre/tableau déterminant qu'il faut répéter @n fois ou en fonction de la taille du tableau */
];
/************************************************************/
/* __ __ _____ _____ _ _ ___ ____ ____ */
/* | \/ | ____|_ _| | | |/ _ \| _ \/ ___| */
/* | |\/| | _| | | | |_| | | | | | | \___ \ */
/* | | | | |___ | | | _ | |_| | |_| |___) | */
/* |_| |_|_____| |_| |_| |_|\___/|____/|____/ */
/************************************************************/
/* AJOUTE UNE DEFINITION
*
* @def_object<Object> Objet de définition
*
*/
FormBuilder.prototype.add_definition = function(def_object){
/* [1] On ajoute la définition
==================================================================*/
for( var key in def_object )
this.defs_object[key] = def_object[key];
};
/* ON CONSTRUIT L'OBJET
*
* @scope<Object> [OPT] Objet contenant les variables à passer
*
*/
FormBuilder.prototype.build = function(scope){
/* [0] Gestion du paramètres
==================================================================*/
/* (1) On initialise le scope vide s'il n'est pas donné */
scope = (scope == null) ? {} : scope;
/* (2) On transforme toutes les variables en références */
for( var key in scope ){
// On enregistre la référence
scope[key] = ref(this.ref_table, scope[key]);
// On enregistre l'association, pour modifier plus tard
this.ref_assoc[key] = scope[key];
}
/* [1] On clone l'object
=========================================================*/
this.built_form = JSON.parse(JSON.stringify(this.form_object));
/* [2] On formatte l'objet
==================================================================*/
// - Ajout des références children[n].parent vers élément parent
// - Ajout des références element.node_link vers définition (en fonction de element.node)
this.built_form = FormBuilder.formatFormObject(this.built_form, this.defs_object);
/* [3] On remplace les valeurs
==================================================================*/
this.built_form = FormBuilder.replaceStatements(this.built_form, scope, this.defs_object, this.ref_table);
};
/* ON MODIFIE L'OBJET (scope uniquement)
*
* @scope<Object> [OPT] Objet contenant les variables à modifier
*
*/
FormBuilder.prototype.update = function(scope){
/* [0] Gestion du paramètres
==================================================================*/
/* (1) On initialise le scope vide s'il n'est pas donné */
scope = (scope == null) ? {} : scope;
/* (2) On récupère toutes les références des variables */
for( var key in scope ){
// si on trouve l'association
if( this.ref_assoc.hasOwnProperty(key) ){
// On modifie la valeur
this.ref_table[ this.ref_assoc[key] ] = scope[key];
}
}
};
/* CONSTRUIT LES DOM ASSOCIE A L'OBJET CONSTRUIT
*
* @parent<Element> Element auquel rattacher le formulaire
*
*/
FormBuilder.prototype.attach = function(parent){
/* [0] Initialisation
=========================================================*/
/* (1) Gestion du paramètre @parent */
if( !(parent instanceof Element) && this.parent_element === null )
return false;
this.parent_element = (parent instanceof Element) ? parent : this.parent_element;
var i;
/* [1] On construit le DOM
=========================================================*/
var dom = FormBuilder.buildElements(this.ref_table, this.built_form);
/* (1) On ajoute les éléments précédents */
for( i in dom.prev )
this.parent_element.appendChild( dom.prev[i] );
/* (2) On ajoute l'élément cible */
this.parent_element.appendChild( dom.node );
/* (3) On ajoute les éléments suivants */
for( i in dom.next )
this.parent_element.appendChild( dom.next[i] );
};
/************************************************/
/* ____ _____ _ _____ ___ ____ */
/* / ___|_ _|/ \|_ _|_ _/ ___| */
/* \___ \ | | / _ \ | | | | | */
/* ___) || |/ ___ \| | | | |___ */
/* |____/ |_/_/ \_\_| |___\____| */
/************************************************/
/* FORMATTE L'OBJET DE DESCRIPTION DU FORMULAIRE
*
* @object<Object> Objet dans lequel remplacer les valeurs
* @defs<Object> Objet de définition des nodes
*
*
* @desc
* 1. Ajoute l'attribut PARENT aux enfants pour qu'ils puisse accéder à l'élément parent
* 2. Ajout l'attribut NODE_LINK aux éléments afin de lier leur définition en fonction de la valeur de 'node'
*
*/
FormBuilder.formatFormObject = function(object, defs){
/* Pour chaque enfant, s'il y a */
if( object.hasOwnProperty('children') ){
for( var child in object.children ){
/* On ajoute le parent + on lance récursivement */
object.children[child].parent = object;
FormBuilder.formatFormObject(object.children[child], defs);
}
}
return object;
};
/* CHERCHE UNE DEFINITION CORRESPONDANT A L'ATTRIBUT 'node'
*
* @node<String> Nom du node pour lequel trouver la définition
* @defs<Object> Objet de définition
*
* @return node_definition<Object> Retourne la définition pour le 'node' en question
* ou NULL si on ne trouve rien
*
*/
FormBuilder.fetchNodeDefinition = function(node, defs){
/* [0] Initialisation
==================================================================*/
var m = null, key, i, regex;
r = FormBuilder.regex.reg_in_key;
/* [1] Si la définition existe, on la retourne
==================================================================*/
if( typeof defs != 'undefined' && defs.hasOwnProperty(node) ){
return { def: defs[node] };
/* [2] Sinon, on cherche une REGEX
==================================================================*/
}else{
// Pour chaque définition
for( key in defs ){
/* (2.1) On regarde s'il n'y a pas de REGEX dans la clé
---------------------------------------------------------*/
if( r.test(key) ){
// On construit la regex
regex = new RegExp( key.slice(1, -1) );
/* (1) Si la regex match */
if( (m=regex.test(node)) ){
/* (2) On récupère les 'match' */
matches = {};
for( i = 1 ; i < RegExp.length && i < 10 ; i++ )
matches['$'+i] = RegExp['$'+i];
/* (3) On renvoie le lien + le scope */
return { def: defs[key], scope: matches };
}
}
}
}
// Si on a rien trouvé, on retourne NULL
return {};
};
/* REMPLACE RECURSIVEMENT LES VALEURS DE @OBJECT AVEC LE @SCOPE -> RECURSIF
*
* @object<Object> Objet dans lequel remplacer les valeurs
* @scope<Object> Ensemble des variables permettant le remplacement
* @definitions<Object> Définitions des éléments
* @ref_table<Object> Table des références
*
* @return replaced<Object> Objet avec les remplacements effectués
*
*/
FormBuilder.replaceStatements = function(object, scope, definitions, ref_table){
/* [0] Initialisation
==================================================================*/
/* (1) Paramètres */
object = (object instanceof Object) ? object : {};
scope = (scope instanceof Object) ? JSON.parse(JSON.stringify(scope)) : {};
/* (2) Variables */
var key, r, tmp, m, found, lasti, s, parts;
var m_fun, m_arr, m_reg, m_pri, m_aval;
/* (3) On récupère le scope s'il est dans l'attribut 'scope' */
if( object.hasOwnProperty('scope') && object.scope instanceof Object )
for( key in object.scope )
scope[key] = object.scope[key];
/* [1] On lie les définitions de l'attribut 'node' -> 'node_link'
==================================================================*/
if( object.hasOwnProperty('node') && typeof object.node == 'string' ){
/* On cherche une définition */
found = FormBuilder.fetchNodeDefinition(object.node, definitions);
// Si on trouve
if( found.hasOwnProperty('def') ){
// 1. On clone la définition dans l'attribut 'node_link'
object.node_link = JSON.parse(JSON.stringify(found.def));
// 2. On ajoute les matches dans l'attribut 'scope'
if( found.hasOwnProperty('scope') )
for( key in found.scope )
scope[key] = found.scope[key];
}
}
/* [2] On remplace les valeurs
==================================================================*/
for( key in object ){
/* [1.1] Si c'est une string, on regarde s'il faut remplacer
==================================================================*/
if( typeof object[key] == 'string' ){
// On transforme en tableau
object[key] = [object[key]];
/* (2.1) On cherche toutes les à remplacer
---------------------------------------------------------*/
/* (1) On récupère le remplacement */
m_fun = FormBuilder.replaceStatementsFunction(ref_table, object[key][0], scope);
/* (2) Si on un remplacement, on remplace, et on passe à la clé suivante */
if( m_fun !== false ){
object[key] = m_fun;
continue;
}
/* (2.2) On cherche tous les TABLEAUX à remplacer
---------------------------------------------------------*/
/* (1) On récupère le remplacement */
m_arr = FormBuilder.replaceStatementsArray(ref_table, object[key][0], scope);
/* (2) Si on un remplacement, on remplace, et on passe à la clé suivante */
if( m_arr !== false ){
object[key] = m_arr;
continue;
}
/* (2.3) On cherche toutes les match de REGEX à remplacer
---------------------------------------------------------*/
/* (1) On récupère les remplacements */
object[key] = FormBuilder.replaceStatementsRegex(object[key], scope);
/* (2.4) On cherche toutes les variables primitives à remplacer
---------------------------------------------------------*/
/* (1) On récupère les remplacements */
object[key] = FormBuilder.replaceStatementsPrimary(object[key], scope);
/* (2.5) On cherche toutes les valeurs de tableaux à remplacer
---------------------------------------------------------*/
/* (1) On récupère les remplacements */
object[key] = FormBuilder.replaceStatementsArrayValue(object[key], scope);
}
}
/* [3] On ajoute les variables '$var' et '$$arr' au scope suivant
==================================================================*/
for( key in object ){
/* (1) Ajout des variables de type '$nomVar'
---------------------------------------------------------*/
if( FormBuilder.regex.pri_in_key.test(key) )
scope[ key.substring(1) ] = ref( ref_table, object[key] );
/* (2) Ajout des tableaux de type '$$nomArr'
---------------------------------------------------------*/
else if( FormBuilder.regex.arr_in_key.test(key) )
scope[ key.substring(2) ] = ref( ref_table, object[key] );
}
/* [4] On lance récursivement
==================================================================*/
/* on clone le scope */
scope = JSON.parse(JSON.stringify(scope));
for( key in object ){
/* S'il ne s'agit d'un attribut interdit */
if( FormBuilder.spread_attr.indexOf(key) > -1 ){
/* (1) Si c'est un tableau, on lance récursivement pour chaque item */
if( object[key] instanceof Array )
for( var i in object[key] )
FormBuilder.replaceStatements(object[key][i], scope, definitions, ref_table);
// console.log(object.node, key+'['+i+']');
/* (2) Si c'est un objet, on lance récursivement */
else if( object[key] instanceof Object )
FormBuilder.replaceStatements(object[key], scope, definitions, ref_table);
// console.log(object.node, key);
}
}
/* [5] On retourne l'object courant
==================================================================*/
return object;
};
/* REMPLACE UNE FUNCTION SOUS LA FORME "{funcName}()" par sa référence
*
* @ref_table<Object> Objet contenant les références
* @statement<String> String contenant la chaine
* @scope<Object> Objet contenant le scope
*
* @return newVal<String> Retourne la nouvelle (ref) valeur ou FALSE si rien n'a été fais
*
*/
FormBuilder.replaceStatementsFunction = function(ref_table, statement, scope){
/* (1) On initialise les variables */
var match = null;
var regex = FormBuilder.regex.fun_out_val;
/* (2) On exécute la regex */
match = regex.exec(statement);
/* (3) Si ça match pas, on retourne FALSE */
if( match === null )
return false;
/* (4) Sinon, si la fonction n'est pas dans le scope, on l'initialise */
if( !scope.hasOwnProperty(match[1]) )
scope[match[1]] = ref( ref_table, function(){} ); // on met une fonction vide
/* (5) On remplace le 'statement' par la fonction */
return scope[match[1]];
};
/* REMPLACE UN TABLEAU SOUS LA FORME "{{arrayName}}" par sa référence
*
* @ref_table<Object> Objet contenant les références
* @statement<String> String contenant la chaine
* @scope<Object> Objet contenant le scope
*
* @return newVal<String> Retourne la nouvelle (ref) valeur ou FALSE si rien n'a été fais
*
*/
FormBuilder.replaceStatementsArray = function(ref_table, statement, scope){
/* (1) On initialise les variables */
var match = null;
var regex = FormBuilder.regex.arr_out_set;
/* (2) On exécute la regex */
match = regex.exec(statement);
/* (3) Si ça match pas, on retourne FALSE */
if( match === null )
return false;
/* (4) Sinon, si le tableau n'est pas dans le scope, on l'initialise */
if( !scope.hasOwnProperty(match[1]) )
scope[match[1]] = ref( ref_table, []); // on met un tableau vide
/* (5) On remplace le 'statement' par le tableau */
return scope[match[1]];
};
/* REMPLACE LES VALEURS DE MATCH DE REGEX SOUS LA FORME "{$matchNumber}" par leur référence
*
* @statements<Array> Tableau contenant les parties de la chaine
* @scope<Object> Objet contenant le scope
*
* @return splitVal<Array> Tableau contenant les parties de la chaine (références + chaine)
*
*/
FormBuilder.replaceStatementsRegex = function(statements, scope){
/* [1] Initialisation
=========================================================*/
var regex = FormBuilder.regex.reg_out_val;
var match = null;
var matches = [];
var lasti, parts, p, i, key;
/* [2] Pour chaque partie de la chaine
=========================================================*/
for( p = 0 ; p < statements.length ; p++ ){
/* (1) Initialisation */
m = null;
matches = [];
lasti = -1;
parts = [];
/* (2) Tant que ça match, on récupère les infos du match */
while( (match=regex.exec(statements[p])) !== null ){
// si on boucle, on sort
if( lasti >= regex.lastIndex ) break;
lasti = regex.lastIndex;
matches.push( match );
}
lasti = 0;
/* [3] Pour chaque match
=========================================================*/
for( i = 0 ; i < matches.length ; i++ ){
key = matches[i][1];
/* (1) On met la chaine d'avant le match (si existe) */
if( lasti > 0 || matches[i].index > 0 )
parts.push( statements[p].substr(lasti, matches[i].index-lasti) );
/* (2) Si la var n'est pas dans le scope, on l'initialise vide */
if( !scope.hasOwnProperty(key) )
scope[key] = ''; // on met une chaine vide
/* (3) On insère la valeur du scope */
parts.push( scope[key] );
/* (4) On met à jour l'index de fin pour la suite */
lasti = matches[i].index + matches[i][0].length;
}
/* (5) On ajoute la partie après le match */
if( lasti < statements[p].length )
parts.push( statements[p].substr(lasti, statements[p].length) );
/* [4] on remplace statements[p] par sa décomposition
=========================================================*/
statements = statements.slice(0, p).concat(parts).concat(statements.slice(p+1));
}
/* [5] On retourne la chaine modifiée (ou non)
=========================================================*/
return statements;
};
/* REMPLACE LES VARIABLE SOUS LA FORME "{varName}" par leur référence
*
* @statements<Array> Tableau contenant les parties de la chaine
* @scope<Object> Objet contenant le scope
*
* @return splitVal<Array> Tableau contenant les parties de la chaine (références + chaine)
*
*/
FormBuilder.replaceStatementsPrimary = function(statements, scope){
/* [1] Initialisation
=========================================================*/
var regex = FormBuilder.regex.pri_out_val;
var match = null;
var matches = [];
var lasti, parts, p, i, key;
/* [2] Pour chaque partie de la chaine
=========================================================*/
for( p = 0 ; p < statements.length ; p++ ){
/* (1) Initialisation */
m = null;
matches = [];
lasti = 0;
parts = [];
/* (2) Tant que ça match, on récupère les infos du match */
while( (match=regex.exec(statements[p])) !== null )
matches.push( match );
/* [3] Pour chaque match
=========================================================*/
for( i = 0 ; i < matches.length ; i++ ){
key = matches[i][1];
/* (1) On met la chaine d'avant le match (si existe) */
if( lasti > 0 || matches[i].index > 0 )
parts.push( statements[p].substr(lasti, matches[i].index-lasti) );
/* (2) Si la var n'est pas dans le scope, on l'initialise vide */
if( !scope.hasOwnProperty(key) )
scope[key] = ''; // on met une chaine vide
/* (3) On insère la valeur du scope */
parts.push( scope[key] );
/* (4) On met à jour l'index de fin pour la suite */
lasti = matches[i].index + matches[i][0].length;
}
/* (5) On ajoute la partie après le match */
if( lasti < statements[p].length )
parts.push( statements[p].substr(lasti, statements[p].length) );
/* [4] on remplace statements[p] par sa décomposition
=========================================================*/
statements = statements.slice(0, p).concat(parts).concat(statements.slice(p+1));
}
/* [5] On retourne la chaine modifiée (ou non)
=========================================================*/
return statements;
};
/* REMPLACE LES VARIABLE SOUS LA FORME "{arrName.itemValName}" par leur référence
*
* @statements<Array> Tableau contenant les parties de la chaine
* @scope<Object> Objet contenant le scope
*
* @return splitVal<Array> Tableau contenant les parties de la chaine (références + chaine)
*
*/
FormBuilder.replaceStatementsArrayValue = function(statements, scope){
/* [1] Initialisation
=========================================================*/
var regex = FormBuilder.regex.arr_out_val;
var match = null;
var matches = [];
var lasti, parts, p, i, key;
/* [2] Pour chaque partie de la chaine
=========================================================*/
for( p = 0 ; p < statements.length ; p++ ){
/* (1) Initialisation */
m = null;
matches = [];
lasti = 0;
parts = [];
/* (2) Tant que ça match, on récupère les infos du match */
while( (match=regex.exec(statements[p])) !== null )
matches.push( match );
/* [3] Pour chaque match
=========================================================*/
for( i = 0 ; i < matches.length ; i++ ){
key = matches[i][1];
/* (1) On met la chaine d'avant le match (si existe) */
if( lasti > 0 || matches[i].index > 0 )
parts.push( statements[p].substr(lasti, matches[i].index-lasti) );
/* (2) Si la var n'est pas dans le scope, on l'initialise vide */
if( !scope.hasOwnProperty(key) )
scope[key] = ''; // on met une chaine vide
/* (3) On insère la valeur du scope */
parts.push( scope[key] );
/* (4) On met à jour l'index de fin pour la suite */
lasti = matches[i].index + matches[i][0].length;
}
/* (5) On ajoute la partie après le match */
if( lasti < statements[p].length )
parts.push( statements[p].substr(lasti, statements[p].length) );
/* [4] on remplace statements[p] par sa décomposition
=========================================================*/
statements = statements.slice(0, p).concat(parts).concat(statements.slice(p+1));
}
/* [5] On retourne la chaine modifiée (ou non)
=========================================================*/
return statements;
};
/* CONSTRUIT UNE CHAINE A PARTIR DE SES PARTIES (VARIABLES OU TEXTE BRUT)
*
* @ref_table<Object> Objet contenant les références
* @parts<Array> Tableau contenant les parties
*
* @return built<String> Chaine recomposée
*
*/
FormBuilder.readRef = function(ref_table, parts){
/* [0] Initialisation
=========================================================*/
var i, built = "";
/* [1] Si c'est un tableau ou une fonction, on le retourne
=========================================================*/
if( typeof parts == 'string' && FormBuilder.regex.ref_pri.test(parts) ){
// tant qu'on a une référence
while( typeof parts == 'string' && FormBuilder.regex.ref_pri.test(parts) ){
parts = ref_table[parts];
if( parts instanceof Array && parts.length == 1)
parts = parts[0];
}
return parts;
}
/* [2] Sinon, on remplace par les valeurs
=========================================================*/
for( i in parts ){
/* (1) Si <refObject>, on ajoute la valeur (si elle existe) */
if( FormBuilder.regex.ref_pri.test(parts[i]) && ref_table.hasOwnProperty(parts[i]) ){
// si le résultat est un tableau, on lance récursivement
if( ref_table[parts[i]] instanceof Array )
built += FormBuilder.readRef(ref_table, ref_table[parts[i]]);
// sinon
else
built += ref_table[parts[i]];
/* (2) Sinon, on ajoute simplement */
}else
built += parts[i].toString();
}
/* [3] On retourne le résultat
=========================================================*/
return built;
};
/* CONSTRUIT UN ELEMENT A PARTIR D'UNE DEFINITION
*
* @ref_table<Object> Objet contenant les références
* @definition<Object> Objet de définition de l'élément
*
* @return built<Element[]> Retourne les éléments construits
*
*/
FormBuilder.buildElements = function(ref_table, definition){
/* [0] Initialisation
===========================================================*/
var built = {
prev: [], // Les éléments précédents
node: null, // L'éléments actuel et ses antécédents/+ prédécesseurs
next: [] // Les éléments suivants
};
var i, j, tmp;
/* [1] On construit les éléments @prev_nodes, s'ils existent
===========================================================*/
if( definition.hasOwnProperty('prev_nodes') )
for( i in definition.prev_nodes )
built.prev = FormBuilder.buildElements(ref_table, definition.prev_nodes[i]);
/* [2] On construit les éléments @next_nodes s'ils existent
===========================================================*/
if( definition.hasOwnProperty('next_nodes') )
for( i in definition.next_nodes )
built.next = FormBuilder.buildElements(ref_table, definition.next_nodes[i]);
/* [3] On construit l'objet actuel
===========================================================*/
/* (1) On crée l'élément
---------------------------------------------------------*/
/* (1) Si on a le type de <tag> hmtl */
if( definition.hasOwnProperty('node_type') )
built.node = document.createElement( FormBuilder.readRef(ref_table, definition.node_type) );
/* (2) Si c'est un sub-node, on récupère la définition récursivement */
else if( definition.hasOwnProperty('node_link') ){
tmp = FormBuilder.buildElements(ref_table, definition.node_link);
/* On ajoute les éléments à l'instance actuelle */
built.prev = built.prev.concat( tmp.prev );
built.node = tmp.node;
built.next = tmp.next.concat( built.next );
}
/* (2) On ajoute les attributs
---------------------------------------------------------*/
if( definition.hasOwnProperty('attributes') ){
// Pour chaque attribut, on définit
for( i in definition.attributes )
built.node.setAttribute( i, FormBuilder.readRef(ref_table, definition.attributes[i]) );
}
/* (3) On ajoute le contenu HTML
---------------------------------------------------------*/
if( definition.hasOwnProperty('text') ){
// Note: Override les enfants
built.node.innerHTML = definition.text;
}
/* (4) On ajoute les listeners
---------------------------------------------------------*/
if( definition.hasOwnProperty('listeners') ){
for( i in definition.listeners ){
built.node.addEventListener(
i,
FormBuilder.readRef(ref_table, definition.listeners[i]),
false
);
console.log('listener', i, FormBuilder.readRef(ref_table, definition.listeners[i]));
}
}
/* [4] On ajoute tous les enfants
=========================================================*/
if( definition.hasOwnProperty('children') )
/* (1) Pour chaque enfant */
for( i in definition.children ){
tmp = FormBuilder.buildElements(ref_table, definition.children[i]);
/* (2) On ajoute les éléments précédent l'enfant */
for( j in tmp.prev )
built.node.appendChild( tmp.prev[j] );
/* (3) On ajoute l'enfant */
built.node.appendChild( tmp.node );
/* (4) On ajoute les éléments suivants l'enfant */
for( j in tmp.prev )
built.node.appendChild( tmp.next[j] );
}
return built;
};