/*
    (C)2008-2010 ID-Informatik. All rights reserved.
    ---------------------------------------------------------------------------------------------------------------------------------------
    Warning this script is protected by copyright, if you want to use this code you must ask permission:
    Attention ce script est protege part des droits d'auteur, si vous souhaitez utiliser ce code vous devez en demander la permission:
        ID-Informatik
        MR AUGUEY THOMAS
        contact@id-informatik.com
    ---------------------------------------------------------------------------------------------------------------------------------------

    WebFrameWork(R) version: 1.3

    Fonctions globales de bases utiles

    [XX-XX-2010], Ajout de la fonction parseHTTPHeader
    [XX-XX-2010], Ajout de la fonction str_url_to_array
    [05-08-2010], Les fonctions sont prototype a l'objet String
    [13-10-2010], Ajoute les fonctions: rtrim, ltrim, trim, empty
    [18-10-2010], Ajoute la fonction: filename
    [23-10-2010], Met a jour les constantes ERR_FAILED et ERR_OK.
    [25-10-2010], Supprime les fonctions: str_urlencoding_to_array() et object_to_urlencoding(), Remplace par wfw.uri.query_to_object() et wfw.uri.object_to_query()
    [25-10-2010], Modifie x_request_arguments_parse() ajout de l'argument 'bencoded'.
    [25-10-2010], Supprime la fonction x_request_arguments_urlencoded_parse() qui est remplace par x_request_arguments_parse().
    [17-02-2011], x_request_arguments_parse(), version 4.
    [28-02-2011], Ajout de copy().
    [28-02-2011], Ajout de associate_array().
    [31-03-2011], Ajout de cInputString{}.
    [25-06-2011], Ajout de length();
    [23-09-2011], Ajout de keyfirst() et keylast()
    [04-10-2011], Ajout de strtoid()
    [11-11-2011], Ajout de strimplode() et fileext()
    [11-11-2011], Ajout de uniqid()
    [19-11-2011], Ajout de zerolead(), minToHour(), date()
    [21-11-2011], Ajout de dirname(), class_exists()
    [21-11-2011], Modify class_exists()
    [28-11-2011], Modify strtoid()
    [12-12-2011], Ajout de trimtext()
    [12-12-2011], Ajout de sizeToByte(), byteToSize()
    [13-12-2011], Update date(), si l'argument 'timestamp' n'est pas définit la date en cours est utilisé
    [14-12-2011], Add set_fileext()
    [17-12-2011], Update x_request_arguments_parse(), utilise new Object() au lieu de new Array() 
    [17-12-2011], Debug objAlertMembers(), n'affiche pas la valeur des nombres negatifs [resolue]
    [21-12-2011], Debug objAlertMembers() gere les objets indéfini
    [28-12-2011], Add have_method()
    [02-01-2012], Remplace associate_array() par object_merge()
    [02-01-2012], Remplace array_flip() par object_flip()
    [02-01-2012], Update zerolead(), utilise 'trim'
    [02-01-2012], Implémente le format de documentation en ligne
    [30-01-2012], Update cInput.. classes
*/

/*--------------------------------------------------------------------------------------------------------------------------------------
    Divers
--------------------------------------------------------------------------------------------------------------------------------------*/


/**
    Génére un identifiant unique
    Retourne:
        [string] L'Identifiant
*/
var _uniqid_cnt = 0;
function uniqid()
{
    _uniqid_cnt++;
    var newDate = new Date;
    return "uid_"+parseInt(Math.random()*456)+"_"+newDate.getTime()+"_"+_uniqid_cnt;//doit commencer par une lettre pour etre un identificateur valide
}

/**
    Vérifie si une classe existe
    Arguments:
        [string] class_name : Nom de la classe de base
    Remarques:
        Pour être reconnue comme une classe, l'objet "class_name" doit être de type fonction
    Retourne:
        [bool] true si la classe existe sinon false
*/
function class_exists(class_name)
{
    //test l'existance de l'objet
    if(typeof(window[class_name])!="function")
        return false;
    return true;
}

/**
    Vérifie si une méthode de classe existe
    Arguments:
        [string] class_name : Nom de la classe de base
        [string] method     : Nom de la méthode
    Retourne:
        [bool] true si la classe existe sinon false
    Remarques:
        Pour être reconnue comme une classe, l'objet "class_name" doit être de type fonction
*/
function method_exists(object_name,method)
{
    //test l'existance de l'objet
    if(typeof(window[object_name])!="object")
        return false;
    //test l'existance de la methode
    return ((typeof(window[object_name][method])=="function") ? true : false); 
}

/**
    Vérifie si un objet possède une méthode
    Arguments:
        [object] object : L'objet de classe
        [string] method : Nom de la méthode
    Retourne:
        [bool] true si la classe existe sinon false
    Remarques:
        Pour être reconnue comme une classe, l'objet doit être une instance de "Object"
*/
function have_method(object, method) {
    //test l'existance de l'objet
    if (!(object instanceof Object))
        return false;
    //test l'existance de la methode
    return ((typeof (object[method]) == "function") ? true : false);
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Object Section
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Copie une instance de variable
    Arguments:
        [mixed] a : La variable à dupliquer
    Retourne:
        [mixed] Nouvelle instance de la variable
    Remarques:
        copy éffectue une copie de chacun des éléments d'un objet ou d'un tableau puis retourne la nouvelle référence
*/
function copy(a){
    var new_var;
    switch(typeof(a)){
        case 'object':
            new_var = {};
            for(key in a)
                new_var[key] = copy(a[key]);
            break;
        case 'array'://?
            new_var = [];
            for(var key=0;key<a.length;key++)
                new_var[key] = copy(a[key]);
            break;
        default:
//        case 'string':
//        case 'number':
            new_var = a;
            break;
    }
    return new_var;
}

/**
    Retourne le nombre de membres enfant d'un objet
    Arguments:
        [object] obj : L'Objet
    Retourne:
        [int] Nombre de membres enfant de l'objet 'obj'
*/
function length(obj){
    var i=0;
    for(element in obj)
        i++;
    return i;
}

/**
    Remonte un membre d'un objet en première position
    Arguments:
        [object] obj : L'Objet
        [string] key : Clef du membre à repositionner
    Retourne:
        [object] Nouvel objet
    Remarques:
        L'Instance de 'obj' n'est pas modifié
*/
function keyfirst(obj,key)
{
    var newObj=new Object();
    //commence par la cles voulue
    for(cur_key in obj)
    {
        if(cur_key==key)
            newObj[cur_key]=obj[cur_key];
    }
    //recopie toutes les cles sauf la cles recherché
    for(cur_key in obj)
    {
        if(cur_key!=key)
            newObj[cur_key]=obj[cur_key];
    }
    return newObj;
}

/**
    Descend un membre d'un objet en dernière position
    Arguments:
        [object] obj : L'Objet
        [string] key : Clef du membre à repositionner
    Retourne:
        [object] Nouvel objet
    Remarques:
        L'Instance de 'obj' n'est pas modifié
*/
function keylast(obj,key)
{
    var newObj=new Object();
    //recopie toutes les cles sauf la cles recherché
    for(cur_key in obj)
    {
        if(cur_key!=key)
            newObj[cur_key]=obj[cur_key];
    }
    //termine par la cles voulue
    for(cur_key in obj)
    {
        if(cur_key==key)
            newObj[cur_key]=obj[cur_key];
    }
    return newObj;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    String object Section
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Transforme un nombre d'octet en taille avec suffix
    Arguments:
        [number] bytes_count : Nombre d'octets
    Retourne:
        [string] Taille agréable à lire
*/
function byteToSize(bytes_count)
{
    var suffix=["octets","Ko","Mo","Go","To","Po","Zo","Yo"];
    var i=0;
    while(bytes_count>=1024 && i<suffix.length)
    {
        bytes_count/=1024;
        i++;
    }
    if(!i)
        return bytes_count+" "+suffix[i];
    return bytes_count.toFixed(1)+" "+suffix[i];
}

/**
    Transforme une taille en nombre d'octets
    Arguments:
        [string] size : Taille
    Retourne:
        [number] Nombre d'octet. Si null, 'size' est mal formé
    Remarques:
        Les suffix suivants sont acceptés:
        "octets", "ko", "mo", "go", "to", "po", "zo", "yo"
        "octet", "kio", "mio", "gio", "tio", "pio", "zio", "yio"
        "o", "k", "m", "g", "t", "p", "z", "y"
*/
function sizeToByte(size) {
    var suffix_typesA = ["octets", "ko", "mo", "go", "to", "po", "zo", "yo"];
    var suffix_typesB = ["octet", "kio", "mio", "gio", "tio", "pio", "zio", "yio"];
    var suffix_typesC = ["o", "k", "m", "g", "t", "p", "z", "y"];
    //obtient la valeur et le suffix
    size += " "; //assure a size.substr de trouver la fin du nombre
    var value = size.substr(0, size.search(/[^0-9.,]/));
    if (!value.length)
        return null;
    var suffix = trim(size.substr(value.length));
    if (empty(suffix))
        return parseInt(value);//octets
    //parse la valeur
    value=value.replace(",", "."); // parseFloat ne prend pas les virgules
    if (isNaN(value = parseFloat(value)))
        return null;
    //calcule la taille en bytes
    var i = 0;
    suffix = suffix.toLowerCase();
    while ((suffix != suffix_typesA[i]) && (suffix != suffix_typesB[i]) && (suffix != suffix_typesC[i])) {
        if (i >= suffix_typesA.length) {
            if (typeof (wfw) == "object")
                wfw.puts("sizeTobyte: unknown suffix " + suffix);
            return null;
        }
        value *= 1024;
        i++;
    }
    return parseInt(value);
}

/**
    Retourne l'extension d'un fichier
    Arguments:
        [string] path : Chemin d'accès ou nom de fichier
    Retourne:
        [string] Extension du fichier, vide si aucune
*/
function fileext(path){
    var file_name = filename(path);
    var startIndex = file_name.lastIndexOf('.');
    if (startIndex >= 0) {
        return file_name.substring(startIndex+1);
    }
    return "";
}

/**
Change l'extension d'un fichier
    Arguments:
        [string] path : Chemin d'accès ou nom de fichier
        [string] ext  : Nouvelle extension
    Retourne:
        [string] Nouveau chemin d'accès ou nom de fichier
*/
function set_fileext(path, ext) {
    var startIndex = path.lastIndexOf('.');
    if (startIndex >= 0) {
        return path.substring(0, startIndex + 1) + ext;
    }
    return path + "." + ext;
}

/**
    Retourne le nom d'un fichier ou du dernier dossier dans un chemin d'accès
    Arguments:
        [string] path : Chemin d'accès
    Retourne:
        [string] Nom du fichier ou du dernier dossier
*/
var basename = filename;
function filename(path){
    var startIndex = path.lastIndexOf('/');
    if(startIndex>=0){
        return path.substring(startIndex+1);
    }
    startIndex = path.lastIndexOf('\\');
    if(startIndex>=0){
        return path.substring(startIndex+1);
    }
    return path;
}

/**
    Retourne le chemin d'accès à un fichier
    Arguments:
        [string] path : Chemin d'accès complet
    Retourne:
        [string] Chemin d'accès du fichier (avec le slash de fin)
*/
function dirname(path){
    var startIndex = path.lastIndexOf('/');
    if(startIndex){
        return path.substring(0,startIndex+1);
    }
    startIndex = path.lastIndexOf('\\');
    if(startIndex){
        return path.substring(0,startIndex+1);
    }
    return "";
}

/**
    Compare deux chaines à la recherche de caractères différents
    Arguments:
        [string] str : Chaine de caractères 1
        [string] chr : Chaine de caractères 2
    Retourne:
        [bool] true, si 'str' est formé uniquement avec les caractères de 'chr', sinon false
*/
function is_strof(str,chr){
	for(var i=0;i<str.length;i++){
		var ok=0;
		for(var x=0;x<chr.length;x++){
			if(chr.charCodeAt(x)==str.charCodeAt(i)){
				ok=1;//ok ce caractere est permis
				x=chr.length;//continue
			}
		}
        //caractere non permis?
		if(!ok){
			return false;
        }
	}
	return true;
}


/**
    Compare deux chaines à la recherche de caractères identiques
    Arguments:
        [string] str : Chaine de caractères 1
        [string] chr : Chaine de caractères 2
    Retourne:
        [bool] true, si 'str' n'est pas formé avec les caractères de 'chr', sinon false
*/
function is_not_strof(str,chr){
	for(var i=0;i<str.length;i++){
		var ok=1;
		for(var x=0;x<chr.length;x++){
			if(chr.charCodeAt(x)==str.charCodeAt(i)){
				ok=0;
				x=chr.length;//continue
			}
		}
        //caractere non permis?
		if(!ok)
			return false;
	}
	return true;
}


/**
    Supprime les espaces en début et en fin de chaine
    Arguments:
        [string] str : Chaine
    Retourne:
        [string] Nouvelle chaine
*/
function trim(str){
    return str.replace(/^\s+|\s+$/g, '');
}

/**
    Supprime les espaces en début de chaine
    Arguments:
        [string] str : Chaine
    Retourne:
        [string] Nouvelle chaine
*/
function ltrim(str){
    return str.replace(/^\s+/g, '');
}

/**
    Supprime les espaces en fin de chaine
    Arguments:
        [string] str : Chaine
    Retourne:
        [string] Nouvelle chaine
*/
function rtrim(str){
    return str.replace(/\s+$/g, '');
}

/**
    Compare le début d'une chaine
    Arguments:
        [string] str  : Chaine à tester
        [string] find : Chaine à rechercher
    Retourne:
        [int] 0 Si 'find' est contenu en début de chaine 'str', sinon 1
*/
function lstrcmp(text,find){
    if(text.substr(0,find.length) == find)
        return 0;
    return 1;
}

/**
    Vérifie si un objet ou une chaine est vide
    Arguments:
        [object] obj : Objet à tester
    Retourne:
        [bool] true Si l'objet est vide, sinon false
*/
function empty(obj){
    //text
    if(typeof(obj)=='string')
    {
        var rest = trim(obj);
        if(rest == "")
            return true;
        return false;
    }
    //objet
    if(typeof(obj)=='object')
    {
        //object
        var i=0;
        for(element in obj){
            //array (possible confusion avec un objet !)
            if(element == 'length')
            {
                return (obj.length==0)?true:false;
            }

            i++;
        }
        return (i==0)?true:false;
    }
    return true;
}

/**
	strToFlags
	  Explose une chaine en tableau
    Arguments:
	  [string] text      : Chaine 
	  [string] options   :  
    Retourne:
	  [array] Tableau des éléments de la chaine
	Remarques:
	  Les éléments vides sont ignorés
*/
function strToFlags(text,options){
    var text_list = strexplode(text," ",true);
    var opt_list = strexplode(options," ",true);
    //supprime les elements vides
    var opt = {};
    for(x=0;x<opt_list.length;x++)
    {
        var opt_name = opt_list[x];
        opt[opt_name]=0;
        var i=0;
        while(i<text_list.length)
        {
            if(opt_name == text_list[i])
            {
                opt[opt_name]=1;
                i=text_list.length;
                continue;
            }
            i++;
        }
    }
    return opt;
}

/**
	strexplode
	  Explose une chaine en tableau
    Arguments:
	  [string] text      : Chaine 
	  [string] separator : Chaine de séparation
	  [bool]   bTrim     : Si true, les espaces blanc sont supprimés en début est fin de chaine
    Retourne:
	  [array] Tableau des éléments de la chaine
	Remarques:
	  Les éléments vides sont ignorés
*/
function strexplode(text,separator,bTrim){
    var list = text.split(separator);
    //supprime les elements vides
    var new_list = new Array();
    for(element in list)
    {
        if(bTrim)
            list[element] = trim(list[element]);
        if(!empty(list[element]))
            new_list.push(list[element]);
    }
    return new_list;
}

/**
	strimplode
	  Implose un tableau en une chaine de caractères
	Arguments:
	  [array]  array     : Tableau 
	  [string] separator : Chaine de séparation
	  [bool]   bTrim     : Si true, les espaces blanc sont supprimés en début est fin de chaine
    Retourne:
	  [string] Chaine implosé
	Remarques:
	  Les éléments vides sont ignorés
*/
function strimplode(array,separator,bTrim)
{
    //supprime les elements vides
    var list = "";
    for(var key in array)
    {
        var value = array[key];
        if(bTrim)
            value = trim(value);
        if(!empty(value))
            list += value+separator;
    }
    return list;
}

//transforme une chaine de caractere au format "name1:value1;name2:value2;..." en tableau associatif
function parseHTTPHeader(text){
    var rslt = new Array()
    var response = text.split(';');
    for(var i=0;i<response.length;i++){
        var element = response[i].split(':');
        rslt[(element[0].toString())]=element[1];
    }
    return rslt;
}

/**
	strlen
	  Retourne la taille d'une chaine
    Arguments:
	  [string] text      : Chaine 
    Retourne:
	  [int] Nombre de caractéres dans la chaine
	Remarques:
	  Equivaut à 'text.length'
*/
function strlen(text){
    return text.length;
}

/**
	strtoid
	  Convertie une chaine en identificateur (voir cInputIdentifier)
    Arguments:
	  [string] str : Chaine à convertir
    Retourne:
	  [string] Chaine convertie
*/
function strtoid(str)
{
    str = trim(str);
    //remplace les caracteres speciaux
    str = str.replace(/[-,;:]+/g, '_');
    //remplace les espaces
    str = str.replace(/\s+/g, '_');
    //remplace les accents
    str = str.replace(/[éèêë]+/gi, 'e');
    str = str.replace(/[àâä]+/gi, 'a');
    //supprime les caracteres speciaux restants
    str = str.replace(/[^A-Za-z0-9_]+/g, '');
    //ne commence pas par un nombre
    if(str.substr(0,1)>="0" && str.substr(0,1)<="9")
        str="_"+str;
    return str;
}

/**
    Convertie une chaine en nom (voir cInputName)
    Arguments:
	  [string] str : Chaine à convertir
    Retourne:
	  [string] Chaine convertie
*/
function strtoname(str)
{
    str = trim(str);
    //remplace les espaces
    str = str.replace(/\s+/g, '_');
    //remplace les accents
    str = str.replace(/[éèêë]+/gi, 'e');
    str = str.replace(/[àâä]+/gi, 'a');
    //supprime les caracteres speciaux restants
    str = str.replace(/[^a-zA-Z0-9_\-\.]+/g, '');
    return str;
}

/**
    Tronque le contenu d'un texte
    Arguments:
        [string] text   : Le texte à tronquer
        [int] maxchar   : Maximum de caractères dans le texte
        [int] maxline   : Maximum de lignes dans le texte
        [string] endstr : Optionnel, Chaine à ajouter en fin de texte (si tronqué). Par défaut " [...]"
    Retourne:
        [string] Le texte tronqué
*/
function trimtext(text, maxchar, maxline, endstr) {
    if (typeof (endstr) == "undefined")
        endstr = " [...]";
    var idx = 0;
    while (idx < maxchar && maxline--) {
        var idx2 = text.indexOf("\n", idx);
        if (idx2 < 0)
            return text.substr(0, maxchar) + ((maxchar < text.length) ? endstr : "");
        idx = idx2 + 1;
    }
    return text.substr(0, idx) + ((idx < text.length) ? endstr : "");
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Array/Object object Section
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Supprime un clé d'un tableau
    Arguments:
        [Array] ar   : Le tableau
        [int]   key  : La clé
    Retourne:
        [mixed] Copie de l'item supprimé
*/
function remove_key(ar,key){
    if(typeof(ar[key]))
    {
       var item = ar[key];
       delete ar[key];
       //decale le reste des items
       for(var i=key; i<ar.length-1; i++){
        ar[i]=ar[i+1];
       }
       ar.length--;
       return item;
    }
    return null;
}

/**
    Associe deux objets dans une nouvelle instance
    Arguments:
        [Object] obj1  : Le première objet
        [Object] obj2  : Le deuxième objet
    Remarques:
        Si deux champs porte la même clé, la valeur de 'ar2' écrase la valeur de 'ar1'
    Retourne:
        [Object] Le nouvel objet
*/
function object_merge(obj1, obj2) {
    var new_ar = copy(obj1);
    for (key in obj2) {
        new_ar[key] = obj2[key];
    }
    return new_ar;
}

/**
    Associe deux tableaux
    Arguments:
        [Array] ar1   : Le première objet
        [Array] ar2   : Le deuxième objet
        [bool]  bcopy : Si true une nouvelle instance est créé, sinon ar1 reçois les champs de 'ar2'
    Remarques:
        Tous les champs de 'ar2' sont ajoutés à 'ar1'
    Retourne:
        [Array] Le nouveau tableau
*/
function array_merge(ar1,ar2,bcopy){
    if(bcopy)
        ar1 = copy(ar1);
    for(var key in ar2)
        ar1.push(ar2[key]);
    return ar1;
}

/**
    Inverse les clés d'un objet avec leurs valeurs
    Arguments:
        [Object] ar1   : L'objet
    Remarques:
        Les valeurs de l'objet sont converties en texte
    Retourne:
        [Object] Le nouvel objet
*/
function object_flip(ar1){
    var ar2={};

    for(var key in ar1)
    {
        var value = ""+ar1[key];//convertie en texte
        ar2[value] = key;
    }
    return ar2;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Date object Section
--------------------------------------------------------------------------------------------------------------------------------------*/

// timeToDate (obselete)
// Convertie un timestamp UNIX en date
//   Integer -timestamp  : secondes depuis le 01/01/1970
//   Retourne      : string de la date
function timeToDate(timestamp) {
    var theDate = new Date(timestamp * 1000);
    return theDate.toGMTString();
}

/**
    Convertie une date en timestamp UNIX
    Arguments:
        [int] year  : Année depuis 1970
        [int] month : Mois de 1~12
        [int] day   : Jour de 1~31
        [int] hour  : Heure de 0~23
        [int] min   : Minutes de 0~59
        [int] sec   : Secondes de 0~59
    Remarques:
        dateToTime utilise l'objet Date pour obtenir le timestamp
    Retourne:
        [int] timestamp en secondes
*/
function dateToTime(year, month, day, hour, min, sec) {
    var humDate = new Date(Date.UTC(year, month - 1, day, hour, min, sec));
    return (humDate.getTime() / 1000.0);
}

/**
    Retourne le timestamp actuel en secondes
    Remarques:
        dateToTime utilise l'objet Date pour obtenir le timestamp
    Retourne:
        [int] timestamp en secondes
*/
function getTime() {
    var current_time=new Date();
    return parseInt(current_time.getTime() / 1000);//en secondes
}

/**
    Retourne le timestamp actuel en millisecondes
    Remarques:
        dateToTime utilise l'objet Date pour obtenir le timestamp
    Retourne:
        [int] timestamp en secondes
*/
function getTimeMS() {
    var current_time=new Date();
    return current_time.getTime();//en millisecondes
}

/**
    Formate un timestamp en date textuel (PHP style)
    Arguments:
        [string] format    : Format (voir remarques)
        [int]    timestamp : timestamp en secondes
    Remarques:
        Les formats prédéfinit sont:
            DATE_ATOM
            DATE_COOKIE
            DATE_ISO8601
            DATE_RFC822
            DATE_RFC850
            DATE_RFC1036
            DATE_RFC1123
            DATE_RFC2822
            DATE_RFC3339
            DATE_RSS
            DATE_W3C
        Pour plus d'informations sur la syntaxe du format voir la documentation PHP de 'date()'
    Retourne:
        [string] La date fomatée
*/
var DATE_DEFAULT = "d/m/Y H:i:s";
var DATE_ATOM    = "Y-m-d\TH:i:sP";
var DATE_COOKIE  = "l, d-M-y H:i:s T";
var DATE_ISO8601 = "Y-m-d\TH:i:sO";
var DATE_RFC822  = "D, d M y H:i:s O";
var DATE_RFC850  = "l, d-M-y H:i:s T";
var DATE_RFC1036 = "D, d M y H:i:s O";
var DATE_RFC1123 = "D, d M Y H:i:s O";
var DATE_RFC2822 = "D, d M Y H:i:s O";
var DATE_RFC3339 = "Y-m-d\TH:i:sP";
var DATE_RSS     = "D, d M Y H:i:s O";
var DATE_W3C     = "Y-m-d\TH:i:sP";

function date(format,timestamp)
{
    var str_month   = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
    var str_month_l = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "Octember", "November", "December");
    var str_day     = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
    var str_day_l   = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");

    var time;
    if (typeof (timestamp) == "undefined")
        time = new Date();
    else
        time=new Date(timestamp * 1000);

    return format.replace(
        new RegExp("([dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZU]){1}", "g"),
        function () {
            var matches = arguments;
            var ret = "";
            switch (matches[1]) {
                case "d": // Jour du mois, sur deux chiffres (avec un zéro initial)
                    ret = zerolead(time.getDate(), 2);
                    break;
                case "D": // Jour de la semaine, en trois lettres (en anglais)
                    ret = str_day[time.getDay()];
                    break;
                case "j": // Jour du mois sans les zéros initiaux
                    ret = time.getDate();
                    break;
                case "l": // Jour de la semaine, textuel, version longue (en anglais)
                    ret = str_day_l[time.getDay() - 1];
                    break;
                case "N": // Représentation numérique ISO-8601 du jour de la semaine (1 pour Lundi à 7 pour Dimanche)
                    ret = ((time.getDay() == 0) ? "7" : time.getDay());
                    break;
                case "S": // Suffixe ordinal d'un nombre pour le jour du mois, en anglais, sur deux lettres (st, nd, rd ou th)
                    ret = "";
                    break;
                case "w": // Jour de la semaine au format numérique (0 (pour dimanche) à 6 (pour samedi))
                    ret = time.getDay();
                    break;
                case "z": // Jour de l'année (0 à 365)
                    ret = "";
                    break;
                //Semaines   
                case "W": // Numéro de semaine dans l'année ISO-8601, les semaines commencent le lundi (Exemple : 42 (la 42ème semaine de l'année))
                    ret = "";
                    break;
                //Mois   
                case "F": // Mois, textuel, version longue; en anglais (January à December)
                    ret = str_month_l[time.getMonth()];
                    break;
                case "m": // Mois au format numérique, avec zéros initiaux (01 à 12)
                    ret = zerolead(time.getMonth() + 1, 2);
                    break;
                case "M": // Mois, en trois lettres, en anglais (Jan à Dec)
                    ret = str_month[time.getMonth()];
                    break;
                case "n": // Mois sans les zéros initiaux (1 à 12)
                    ret = time.getMonth() + 1;
                    break;
                case "t": // Nombre de jours dans le mois (28 à 31)
                    ret = "";
                    break;
                //Annees   
                case "L": // Est ce que l'année est bissextile (1 si bissextile, 0 sinon)
                    ret = "";
                    break;
                case "o": // L'année ISO-8601. C'est la même valeur que Y, excepté que si le numéro de la semaine ISO (W) appartient à l'année précédente ou suivante, cette année sera utilisé à la place (Exemples : 1999 ou 2003)
                    ret = "";
                    break;
                case "Y": // Année sur 4 chiffres (Exemples : 1999 ou 2003)
                    ret = time.getFullYear();
                    break;
                case "y": // Année sur 2 chiffres (Exemples : 99 ou 03)
                    ret = time.getFullYear().toString().substr(2); //ne pas utiliser time.getYear() pour les dates apres '1900'
                    break;
                //Heures   
                case "a": // Ante meridiem et Post meridiem en minuscules (am ou pm)
                    ret = "";
                    break;
                case "A": // Ante meridiem et Post meridiem en majuscules (AM ou PM)
                    ret = "";
                    break;
                case "B": // Heure Internet Swatch (000 à 999)
                    ret = "";
                    break;
                case "g": // Heure, au format 12h, sans les zéros initiaux (1 à 12)
                    ret = "";
                    break;
                case "G": // Heure, au format 24h, sans les zéros initiaux (0 à 23)
                    ret = time.getHours();
                    break;
                case "h": // Heure, au format 12h, avec les zéros initiaux (01 à 12)
                    ret = "";
                    break;
                case "H": // Heure, au format 24h, avec les zéros initiaux (00 à 23)
                    ret = zerolead(time.getHours(), 2);
                    break;
                case "i": // Minutes avec les zéros initiaux (00 à 59)
                    ret = zerolead(time.getMinutes(), 2);
                    break;
                case "s": // Secondes, avec zéros initiaux (00 à 59)
                    ret = zerolead(time.getSeconds(), 2);
                    break;
                case "u": // Microsecondes (Exemple : 654321)
                    ret = "";
                    break;
                //Fuseau horaire   
                case "e": // L'identifiant du fuseau horaire  (UTC, GMT, Atlantic/Azores)
                    ret = "GMT+" + time.getTimezoneOffset();
                    break;
                case "I": // L'heure d'été est activée ou pas (1 si oui, 0 sinon)
                    ret = "";
                    break;
                case "O": // Différence d'heures avec l'heure de Greenwich (GMT), exprimée en heures (Exemple : +0200)
                    var h = minToHour(time.getTimezoneOffset());
                    ret = "+" + zerolead(h.hour, 4);
                    break;
                case "P": // Différence avec l'heure Greenwich (GMT) avec un deux-points entre les heures et les minutes (Exemple : +02:00)
                    var h = minToHour(time.getTimezoneOffset());
                    ret = "+" + zerolead(h.hour, 2) + ":" + zerolead(h.min, 2);
                    break;
                case "T": // Abréviation du fuseau horaire (Exemples : EST, MDT ...)
                    ret = "";
                    break;
                case "Z": // Décalage horaire en secondes. Le décalage des zones à l'ouest de la zone UTC est négative, et à l'est, il est positif. (-43200 à 50400)
                    ret = "";
                    break;
                //Date et Heure complète   
                case "U": // Secondes depuis l'époque Unix
                    ret = timestamp;
                    break;
            }
            return "" + ret;
        }
    );
}

/* [ PRIVATE ] */
function minToHour(minutes)
{
    var hours=0;
    while(minutes>=60)
    {
        hour++;
        minutes-=60;
    }
    return {hour:hours,min:minutes};
}

/**
    Ajoute les zeros manquants en début de nombre/texte
    Arguments:
        [mixed] num    : Valeur
        [int]   length : Taille du nombre
    Exemple:
        zerolead(14,4); // retourne 0014
        zerolead("4.12",5); // retourne 04.12
    Retourne:
        [string] Nouvelle valeur
*/
function zerolead(num, length)
{
    var r = "" + trim(num.toString());
    while (r.length < length) {
        r = "0" + r;
    }
    return r;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Debug Section
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Affiche une boite de dialogue avec les attributs d'un élément
    Arguments:
        [HTMLElement] obj : L'Elément
*/
function objAlertAttributes(obj){
	var txt;
	var tmp;
	for(var i=0;i<obj.attributes.length;i++){
		tmp += (obj.attributes[i].name+" = "+obj.attributes[i].value+", ");
		if(tmp.length>120){
			txt+=tmp+"\n";
			tmp="";
		}
	}
	txt+=tmp;
	alert(obj.tagName+" ("+obj.id+"):\n"+txt);
}

/**
    Affiche une boite de dialogue avec les membres d'un objet
    Arguments:
        [HTMLElement] obj : L'Elément
*/
function objAlertMembers(obj) {
	var txt="";
	for(var key in obj){
		txt += (key+" ["+typeof(obj[key])+"]");
        if(typeof(obj[key])!="undefined" && typeof(obj[key].toString)=="function")
		    txt += (" {"+obj[key].toString()+"}");
		txt += "\n";
		//txt += (key+" ["+typeof(obj[key])+"] {"+obj[key]+"}\n");
	}
	alert(txt);
}

/**
    Affiche une boite de dialogue avec les clés d'un objet
    Arguments:
        [HTMLElement] obj : L'Elément
*/
function objAlertMembersKey(obj){
	var txt="";
	for(key in obj){
		txt += (key+" ["+typeof(obj[key])+"]\n");
	}
	alert(txt);
}


/*--------------------------------------------------------------------------------------------------------------------------------------
    WebFrameWork Specific
--------------------------------------------------------------------------------------------------------------------------------------*/
var XARG_START_OF_TEXT_CODE = 0x02;
var XARG_END_OF_TEXT_CODE   = 0x03; 
var XARG_START_OF_TEXT_CHAR = String.fromCharCode(0x2);
var XARG_END_OF_TEXT_CHAR   = String.fromCharCode(0x3);
/**
    Convertie le corps d'un document 'text/wfw.xra' en objet javascript
    Parametres:
        [string] text     : Corps du document XARG
        [bool]   bencoded : Si true, scan 'text' avec l'encodage d'une URL
    Retourne:
       [Object] Tableau associatif des arguments
*/
function x_request_arguments_parse(text,bencoded) {//v4
	var rslt = new Object();
	var begin_pos = 0;
	var pos;
	var separator = XARG_START_OF_TEXT_CHAR;
	var end       = XARG_END_OF_TEXT_CHAR;

    if(bencoded){
	    separator = "%02";//STX
	    end       = "%03";//ETX
    }

	while((pos=text.indexOf(separator,begin_pos)) != -1)
	{
		var pos2  = text.indexOf(end,pos);
		if(pos2 == -1){ // fin anticipe
            wfw.puts("x_request_arguments_parse(), attention: fin anormale de requete!");
			return rslt;
        }

		//alert(begin_pos+"->"+pos+"\n"+pos+"->"+pos2);

		var name  = text.substr(begin_pos,pos-begin_pos);
		var value = text.substr(pos+separator.length,pos2-(pos+separator.length));

		begin_pos = pos2+end.length; //prochaine position de depart

		rslt[name]=value;
	}
	return rslt;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Webframework standard errors codes
--------------------------------------------------------------------------------------------------------------------------------------*/

//Resultats
var ERR_SYSTEM              = 2;
var ERR_FAILED              = 1;
var ERR_OK                  = 0;

//Bases
var ERR_TEXT                = 1000;
var ERR_REQ                 = 2000;

//Format Texte
var ERR_TEXT_EMPTY          = ERR_TEXT+1;
var ERR_TEXT_INVALIDCHAR    = ERR_TEXT+2;

//Requete
var ERR_REQ_OPEN_URL        = ERR_REQ+1;
var ERR_REQ_MISSING_ARG     = ERR_REQ+2;
var ERR_REQ_INVALID_ARG     = ERR_REQ+3;

/*--------------------------------------------------------------------------------------------------------------------------------------
    XML-Document Specific
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Convertie le corps d'un document 'text/xml' en objet javascript
    Parametres:
        [string] text : Corps du document XML
    Remarques:
        L'Objet retourné est dépendant de l'implémentation du navigteur
    Retourne:
        [Object] Instance de l'objet XML-DOM. null en cas d'échec
*/
function xml_parse(text) {
    var xmlDoc=null;
    if(typeof(text)!='string'){
        wfw.puts('xml_parse not a string');
        return null;
    }
    if(window.DOMParser)
    {
        parser=new DOMParser();
        xmlDoc=parser.parseFromString(text,"text/xml");
    }
    else // Internet Explorer
    {
        xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async="false";
        xmlDoc.loadXML(text); 
        if(xmlDoc.parseError.errorCode != 0 && typeof(wfw)=="object") {
           var myErr = xmlDoc.parseError;
           wfw.puts("xml_parse: error " + myErr.reason);
           return null;
        }
    } 
    return xmlDoc;
}

//charge un fichier XML
function xml_load2(file_name,loaded) {
    var content = wfw.http_get(file_name);
    if(!((wfw.nav.httpRequest.readyState == wfw.request.READYSTATE_DONE) && (wfw.nav.httpRequest.status == 200)))
        return false;
    var doc = xml_parse(content);
    if(!doc)
        return false;
    loaded(doc);
    return true;
}

//charge un fichier XML (compatible)
function xml_load(file_name,callback) {
    //cree la frame
    var frame_obj;

    if((frame_obj = document.createElement("iframe"))==null)
    {
        wfw.puts("xml_load : iframe element creation failed !");
        return false;
    }
    objSetAtt(frame_obj,"src",file_name);
    objSetAtt(frame_obj,"style","width:100%; height:600px; display:none;");

    //au chargement du document ...
    objSetEvent(frame_obj,"load",function()
    {
        callback(this.contentWindow.document);
    });

    document.body.appendChild(frame_obj);
    return true;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Divers
--------------------------------------------------------------------------------------------------------------------------------------*/

function strToArray(text,separator) {
	var rslt = new Array();
	var begin_pos = 0;
	var pos;

	while((pos=text.indexOf(separator,begin_pos)) != -1)
	{
		var value  = text.substr(begin_pos,pos-begin_pos);

		begin_pos = pos+separator.length; //prochaine position de depart

		rslt.push(value);
	}

    return rslt;
}

/**
    Obtient le sous-type d'un MIME
    Parametres:
        [string] mime_type : MIME type, ex: text/plain
    Retourne:
        [string] Sous-type du MIME
*/
function MIME_lowerType(mime_type) {
    var i = mime_type.indexOf('/',0);
    if(i<0)
        return "";
    return mime_type.substring(0,i);
}

/**
    Obtient le super-type d'un MIME
    Parametres:
        [string] mime_type : MIME type, ex: text/plain
    Retourne:
        [string] Super-type du MIME
*/
function MIME_upperType(mime_type) {
    var i = mime_type.indexOf('/',0);
    if(i<0)
        return "";
    return mime_type.substring(i+1,mime_type.length);
}

function isEditable(obj) {
    if((typeof(obj)=='object') && (typeof(obj.constructor)!='undefined'))
        return true;
    return false;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Mouse detection
--------------------------------------------------------------------------------------------------------------------------------------*/

// Globales

var cX = 0; var cY = 0; // position dans le client
var rX = 0; var rY = 0;

// Initilaise les Evenements

function updateCursorPosition(e){ cX = e.pageX; cY = e.pageY; }

function updateCursorPositionDocAll(e){ cX = event.clientX; cY = event.clientY; }

if(document.all)
	{ document.onmousemove = updateCursorPositionDocAll; }
else
	{ document.onmousemove = updateCursorPosition; }

// Fonctions

// Retourne la position X(pixel) en cours du curseur
function getMouseX(){
	return parseInt(cX);
//	return document.documentElement.scrollLeft + window.event.clientX;//XHTML
//	return document.body.scrollLeft + window.event.clientX;//HTML
}

// Retourne la position Y(pixel) en cours du curseur
function getMouseY(){
	return parseInt(cY);
//	return document.documentElement.scrollTop + window.event.clientY;//XHTML
//	return document.body.scrollTop + window.event.clientY;//HTML
}

// Verifie si le curseur survol actuelement l'objet specifier
function isPtrOver(element){
	element = docGetElement(element);
	if(element){
		var x  = objGetX(element);
		var y  = objGetY(element);
		var x1 = x + element.offsetWidth;
		var y1 = y + element.offsetHeight;
		var mx = getMouseX();
		var my = getMouseY();
		
		//alert(x+";"+y+";"+x1+";"+y1+"; ? "+mx+";"+my);
	
		if( ((mx >= x) && (my <= y)) && ((mx <= x1) && (my >= y1)) )
			return true;
	}
	return false;
}

/*--------------------------------------------------------------------------------------------------------------------------------------
    Input types
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
    Vérifie le format d'un identificateur
    Base-Object:
        cInput
*/
var cInputIdentifier = {
    regExp: '[A-Za-z]{1}[A-Za-z0-9_]*',
    isValid: function (value) {
        if (empty(value))
            return ERR_TEXT_EMPTY;
        var exp = new RegExp('^[A-Za-z]{1}[A-Za-z0-9_]*$', 'g');
        if (exp.test(value)==true)
            return ERR_OK;
        return ERR_TEXT_INVALIDCHAR;
    }
};

function cInputIdentifier_isValid(value) {
    if (empty(value))
        return ERR_TEXT_EMPTY;
    var exp = new RegExp('^[A-Za-z]{1}[A-Za-z0-9_]*$', 'g');
    if (exp.test(value) == true)
        return ERR_OK;
    return ERR_TEXT_INVALIDCHAR;
}

/**
    Vérifie le format d'un nom
    Base-Object:
        cInput
*/
var cInputName = {
    regExp : '[a-zA-Z_]{1}[a-zA-Z0-9_\\-\\.]*',
    isValid : function(value){
		if( empty(value) )
			return ERR_TEXT_EMPTY;

        var exp = new RegExp('^'+this.regExp+'$','g');
        if(exp.test(value))
            return ERR_OK;

        return ERR_TEXT_INVALIDCHAR;
    }
};

/**
    Vérifie le format d'un texte contenu entre guillemets
    Base-Object:
        cInput
*/
var cInputString = {
    regExp: '[^"\n\r]*',
    isValid: function (value) {
        if (empty(value))
            return ERR_TEXT_EMPTY;

        var exp = new RegExp('^' + this.regExp + '$', 'g');
        if (exp.test(value))
            return ERR_OK;

        return ERR_TEXT_INVALIDCHAR;
    }
};

/**
    Vérifie le format d'un entier numérique
    Base-Object:
        cInput
*/
var cInputInteger = {
    regExp: '[0-9]+',
    isValid: function (value) {
        if (empty(value))
            return ERR_TEXT_EMPTY;

        var exp = new RegExp('^' + this.regExp + '$', 'g');
        if (exp.test(value))
            return ERR_OK;

        return ERR_TEXT_INVALIDCHAR;
    }
};

/**
    Vérifie le format d'une adresse email
    Base-Object:
        cInput
*/
var cInputMail = {
    regExp : '',
    isValid: function (value) { return ERR_OK; }
};

/**
    Vérifie le format d'un nom de fichier UNIX (les chemins d'accès ne sont pas autorisés)
    Base-Object:
        cInput
    Remarques:
        Les chemins d'accès ne sont pas autorisés
*/
var cInputUNIXFileName = {
    regExp : '',
    isValid: function (value) { return ERR_OK; }
};

/**
    Vérifie le format d'un mot-de-passe
    Base-Object:
        cInput
*/
var cInputPassword = {
    regExp: '[a-zA-Z0-9_\\-\\@\\#\\&\\+\\~]+',
    isValid: function (value) {
        if (empty(value))
            return ERR_TEXT_EMPTY;

        var exp = new RegExp('^' + this.regExp + '$', 'g');
        if (exp.test(value))
            return ERR_OK;

        return ERR_TEXT_INVALIDCHAR;
    }
};


/**
Vérifie le format d'une adresse IP version 4
*/
var cInputIPv4 = {
    regExp: '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}',
    isValid: function (value) {
        if (empty(value))
            return ERR_TEXT_EMPTY;

        var exp = new RegExp('^' + cInputIPv4.regExp + '$', 'g');
        if (exp.test(value))
            return ERR_OK;

        return ERR_TEXT_INVALIDCHAR;
    }
};


