/**
 * Module  : string.js
 * Contenu : Fonctions en rapport avec les chaînes de caractères
 * Auteur  : Frank Malenfant
 * Date    : 10 mai 2006
 **/

/**
 * FONCTIONS DOCUMENTÉES:
 *
 * addCar(char c, str s, int i)       : Ajoute un caractère à un endroit précis dans une chaîne
 * date2human(Date d)                 : Transforme un objet Date en chaîne litérale (Ex : 13 juillet 2010, 08h00)
 * date2str(Date d)                   : Transforme un objet Date en chaîne DateTime au format AAAA-MM_JJ HH:II:SS
 * delCar(str s, int i)               : Supprime un caractère à un endroit précis dans une chaine
 * escapePlus(str str)                : Alternative à la fonction escape() qui n'encode pas les +. La fonction encoreURIComponent, quant à elle, détruit les caractères accentués.
 * fillStr(str s, char c, int i)      : Remplis la chaine "s" du caractere "c" pour qu'elle atteigne la longueur "i"     
 * formatTIme(date d, str format)			: Formatte l'heure à partir d'un objet date
 * isAN(str s)                        : Contraire de isNaN, prend correctement en charge les chaînes vides
 * is_email(str $str)                 : Retourne true si la chaîne correspond à la sytaxe d'une adresse de courriel, false sinon
 * ltrim(str, chars)                  : Équivalent de la fonction PHP
 * remplacer(char c1, char c2, str s) : Remplace la caractère c1 par le caractère c2 dans la chaîne s
 * rtrim(str, chars)                  : Équivalent de la fonction PHP
 * sansAccent(string texte)           : Remplace les catactères accentués pour leur équivalent
 * sprintf(string format [,mixed args [, mixed ...]]) : Équivalent Javascript de la fonction sprintf()
 * trim(str, chars)                   : Équivalent de la fonction PHP
 **/

function formatTime(timeValue, format) { 
	var fmt = format.toUpperCase(); 
  var re = /^(H|HH)(:MM)(:SS)?( AM)?$/; 
  if (!re.test(fmt)) { fmt = "H:MM AM"; } 
	var MM = "0" + (timeValue.getMinutes()); 
  MM = MM.substring(MM.length-2, MM.length); 
  var SS = "0" + (timeValue.getSeconds()); 
  SS = SS.substring(SS.length-2, SS.length); 
  var H = "" + (timeValue.getHours()); 
  var HH = "0" + H; 
  HH = HH.substring(HH.length-2, HH.length); 
  var meridian = ""; 
  if(fmt.indexOf(" AM") != -1) { 
		meridian = "AM"; 
		if(HH == "00") HH = "12";
		if(HH == "12") meridian = "PM";
		if(parseInt(HH, 10) > 12) {
			meridian = "PM"; 
			var hrs = (parseInt(HH, 10)-12); 
			H = "" + hrs; 
			HH = "0" + H; 
			HH = HH.substring(HH.length-2, HH.length); 
		} 
	} 

	var meridian = ""; 
	var result = ""; 
	if (fmt.indexOf("HH") == -1) { result += H + ":" + MM; } else { result += HH + ":" + MM; } 
	if (fmt.indexOf("SS") != -1) { result += ":" + SS; } 
	if (fmt.indexOf(" AM") != -1) { result += " " + meridian; } 
	return result; 
} 



/**
 * Ajoute un caractere a un endroit precis dans une chaine
 * Paramètres : char c     (Caractère à ajouter)
 *              str  s     (Chaine à modifier)
 *              int  i     (Indice d'emplacement du caractere)
 * Retourne   : str chaine 
 **/
function addCar(c, s, i) {
  var chaine = "";
  if(i>0) {
    for(var j=0;j<i;j++) {
      chaine += s.charAt(j);
    }
  }
  chaine += c;
  for(var j=i;j<s.length;j++) {
    chaine += s.charAt(j);
  }
  return chaine;
}

/**
 * Transforme un objet Date en chaîne litérale (Ex : 13 juillet 2010, 08h00)
 * Paramètre : Date d
 * Retourne  : str
 **/
function date2human(d) {
	var mois = new Array("", "janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre");
	return d.getDate()+" "+mois[d.getMonth() * 1]+" "+d.getFullYear()+", "+(d.getHours() < 10?"0"+d.getHours():d.getHours())+"h"+(d.getMinutes() < 10?"0"+d.getMinutes():d.getMinutes());
}

/**
 * Transforme un objet Date en chaîne DateTime au format AAAA-MM_JJ HH:II:SS
 * Paramètre : Date d
 * Retourne  : str
 **/
function date2str(d) {
	var mois = d.getMonth() + 1;
	return d.getFullYear()+"-"+(mois < 10?"0"+mois:mois)+"-"+(d.getDate() < 10?"0"+d.getDate():d.getDate())+" "+(d.getHours() < 10?"0"+d.getHours():d.getHours())+":"+(d.getMinutes() < 10?"0"+d.getMinutes():d.getMinutes())+":"+(d.getSeconds() < 10?"0"+d.getSeconds():d.getSeconds());
}

/**
 * Supprime un caractère à un endroit précis dans une chaîne
 * Paramètres : str  s     (Chaîne à modifier)
 *              int  i     (Indice d'emplacement du caractère)
 * Retourne   : str chaine
 **/
function delCar(s, i) {
  var chaine = "";
  if(i>0) {
    for(var j=0;j<i;j++) {
      chaine += s.charAt(j);
    }
  }
  for(var j=(i+1);j<s.length;j++) {
    chaine += s.charAt(j);
  }
  return chaine;
}

/**
 * Alternative à la fonction escape() qui n'encode pas les +. La fonction encoreURIComponent, quant à elle, détruit les caractères accentués.
 * Paramètre : string str
 * Retourne  : string
 **/
function escapePlus(str) {
	return escape(str).replace(new RegExp( "\\+", "g" ), "%2B");
}

/**
 * Remplis la chaine "s" du caractere "c" pour qu'elle atteigne la longueur "i"
 * Parametres : str  s  (Chaine a modifier)
 *              char c  (Caractere de remplissage)
 *              int  i  (Longueur de chaine souhaitee : Negatif pour remplissage a droite
 **/
function fillStr(s, c, i) {
  var j = 1;
  s = addCar("a", String(s), 0);
    
  while(s.length <= Math.abs(i)) {
    if(i < 0) j = s.length;
    s = addCar(c, s, j);
  }

  return delCar(s, 0);
}

/**
 * Contraire de isAN, prend correctement en charge les chaînes vides
 * Paramètre : str s
 **/
function isAN(s) {
  if(s == "") return false;
  if(s == null) return false;
	else return !isNaN(s);
}

function IsGoodDate(mydate){
    var mysplit=mydate.split(' ');
    if(mysplit.length!=2) return false;

    var thedate=mysplit[0].split('-');
    if((mysplit[0].length!=10)||
       (thedate.length!=3)||
       (isNaN(parseInt(thedate[0])))||
       (isNaN(parseInt(thedate[1])))||
       (isNaN(parseInt(thedate[2])))||
       (thedate[0].length<4)||
       (thedate[1].length<2)||
       (thedate[2].length<2)) return false;
    var an=eval(thedate[0]);
    var mo=eval(thedate[1]);
    var jo=eval(thedate[2]);
    var onedate=new Date(an,mo-1,jo);
    an = onedate.getFullYear();
    if((onedate.getDate()!=jo)||
       (onedate.getMonth()!=mo-1)||
       (onedate.getFullYear()!=an)) return false;
    var thetime=mysplit[1].split(':');
		if(thetime.length == 2) { //Tolérance de l'heure sans les secondes
			mysplit[1] += ":00";
			thetime = mysplit[1].split(':');
		}
		if((thetime[0].length == 1)&&(!isNaN(thetime[0]))) { //Tolérance de l'heure à un seul chiffre
			mysplit[1] = "0"+mysplit[1];
			thetime = mysplit[1].split(':');
		}
    if((mysplit[1].length!=8)||
       (thetime.length!=3)||
       (isNaN(parseInt(thetime[0])))||
       (isNaN(parseInt(thetime[1])))||
       (isNaN(parseInt(thetime[2])))||
       (thetime[0].length<2)||
       (thetime[1].length<2)||
       (thetime[2].length<2)) return false;
    var he=eval(thetime[0]);
    var mi=eval(thetime[1]);
    var se=eval(thetime[2]);
    var onetime=new Date(an,mo-1,jo,he,mi,se);
    if((onetime.getHours()!=he)||
       (onetime.getMinutes()!=mi)||
       (onetime.getSeconds()!=se)) return false;

    return true;
}

/**
 * Retourne true si la chaîne correspond à la sytaxe d'une adresse de courriel, false sinon
 * Paramètre: str str
 **/
function is_email(str) {
	//Vérifie si tous les caractères essentiels sont présents dans la chaîne
	if(str.indexOf("@") < 0) return false;
	if(str.indexOf(".") < 0) return false;
	var tmp = str.split("@");
	if(tmp[1].indexOf(".") < 0) return false;
	if(tmp[2] != undefined) return false;
	
	//Vérifie la présence des trois sections nécessaires
	var tmp2 = tmp[1].split(".")
	if((tmp[0].length <= 0)&&(tmp2[0].length <= 0)&&(tmp2[1].length <= 0)) return false;
	
	//Vérifie que la chaîne ne contient que des caractères autorisés
	var allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.@";
	for(var c = 0; c < str.length; c++) {
		var tmp = str.charAt(c);
		if(allowed.indexOf(tmp) < 0) return false;
	}
	return true;
}

/**
 * Équivalent à la fonction PHP
 * Javascript trim, ltrim, rtrim
 * http://www.webtoolkit.info/
 **/ 
function ltrim(str, chars) {
	chars = chars || "\\s";
	return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}

/**
 * Décompose l'url fourni et retourne un objet avec ses caractéristiques
 * Paramètre: str url
 **/
function readUrl(url) {
	var o = {"document":null,"anchor":null,"params":new Array()};
	if(url.indexOf("mailto:") == 0) {
		o.type = "mailto";
		var tmp = url.split("mailto:");
		if(tmp[1].indexOf(",") >= 0) var tmp = tmp[1].split(",");
		else if(tmp[1].indexOf(";") >= 0) var tmp = tmp[1].split(";");
		else if(tmp[1].indexOf(" ") >= 0) var tmp = tmp[1].split(" ");
		for(i in tmp) {
			if(is_email(trim(tmp[i]))) o.params.push(tmp[i]);
		}
	}
	else {
		var tmp = url.split("?");
		try { 
			if(tmp[1] == undefined) {
				var tmp = tmp[0].split("#");
				o.document = tmp[0];
				try { 
					if(tmp[1] == undefined) throw("No anchor found");
					else o["anchor"] = tmp[1];
				}
				catch(e) {}
			}
			else {
				o.document = tmp[0];
				throw("Paramaters found");
			}
		}
		catch(e) {
			params = tmp[1].split("&");
			for(p in params) {
				var param = params[p].split("=");
				o.params[param[0]] = param[1];
			}
		}
	}
	return o;
}

/**
 * Remplace la caractère c1 par le caractère c2 dans la chaîne s
 *
 * Paramètres : char c1 (Caractère à remplacer)
 *              char c2 (Caractère de remplacement)
 *              str  s  (Chaîne à modifier)
 * Retourne   : str  s
 **/
function remplacer(c1, c2, s) {
 for(var j = 0;j < s.length;j++) {
   if (c1.indexOf(s.charAt(j),0) != -1) s = addCar(c2, delCar(s, j), j--);
 }
 return s;
}

/**
 * Équivalent à la fonction PHP
 * Javascript trim, ltrim, rtrim
 * http://www.webtoolkit.info/
 **/ 
function rtrim(str, chars) {
	chars = chars || "\\s";
	return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}

/** 
 * Remplace les catactères accentués pour leur équivalent
 * Ex: sansAccent("été"); //ete
 * Paramètre : string texte
 **/
function sansAccent(texte) {
  var accenta = "àâäÀÂÄ";
  var accente = "éèêëÉÈÊË";
  var accenti = "îïÎÏ";
  var accento = "òôöÒÔÖ";
  var accentu = "ùûüÙÛÜ";

  for(var i=0;i<texte.length;i++) {
    if (accenta.indexOf(texte.charAt(i),0)!=-1) {
      texte = delCar(texte,i);
      texte = addCar('o',texte,i);
    }
    else if (accente.indexOf(texte.charAt(i),0)!=-1) {
      texte = delCar(texte,i);
      texte = addCar('e',texte,i);
    }
    else {
      if (accenti.indexOf(texte.charAt(i),0)!=-1) {
        texte = delCar(texte,i);
	texte = addCar('i',texte,i);
      }
      else if (accento.indexOf(texte.charAt(i),0)!=-1) {
        texte = delCar(texte,i);
	texte = addCar('o',texte,i);
      }
      else {
        if(accentu.indexOf(texte.charAt(i),0)!=-1) {
	  texte = delCar(texte,i);
	  texte = addCar('u',texte,i);
	}	
      }
    }	
  } 
  return texte;
}

/***********************************************************************************************
 * Équivalent javascript de la fonction PHP sprintf()
 * http://kevin.vanzonneveld.net/
 ***********************************************************************************************
 * Description
 *   string sprintf( string format [, mixed args [, mixed ...]] )
 ***********************************************************************************************
 * Parameters 
 *   string format 
 *     % - a literal percent character. No argument is required. 
 *     b - the argument is treated as an integer, and presented as a binary number. 
 *     c - the argument is treated as an integer, and presented as the character with that ASCII value. 
 *     d - the argument is treated as an integer, and presented as a (signed) decimal number. 
 *     e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands 
 *         for the number of digits after the decimal point since PHP 5.2.1. In earlier versions, it was 
 *         taken as number of significant digits (one less). 
 *     u - the argument is treated as an integer, and presented as an unsigned decimal number. 
 *     f - the argument is treated as a float, and presented as a floating-point number (locale aware). 
 *     F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
 *     o - the argument is treated as an integer, and presented as an octal number. 
 *     s - the argument is treated as and presented as a string. 
 *     x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). 
 *     X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). 
 *     The format string supports argument numbering/swapping. 
 *   mixed args 
 ***********************************************************************************************
 * Return Values 
 *   Returns a string produced according to the formatting string format .
 **********************************************************************************************/
function sprintf( ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Ash Searle (http://hexmen.com/blog/)
    // + namespaced by: Michael White (http://crestidg.com)
    // *     example 1: sprintf("%01.2f", 123.1);
    // *     returns 1: 123.10
 
    var regex = /%%|%(\d+\$)?([-+#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
    var a = arguments, i = 0, format = a[i++];
 
    // pad()
    var pad = function(str, len, chr, leftJustify) {
        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
        return leftJustify ? str + padding : padding + str;
    };
 
    // justify()
    var justify = function(value, prefix, leftJustify, minWidth, zeroPad) {
        var diff = minWidth - value.length;
        if (diff > 0) {
            if (leftJustify || !zeroPad) {
            value = pad(value, minWidth, ' ', leftJustify);
            } else {
            value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
            }
        }
        return value;
    };
 
    // formatBaseX()
    var formatBaseX = function(value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
        // Note: casts negative numbers to positive ones
        var number = value >>> 0;
        prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
        value = prefix + pad(number.toString(base), precision || 0, '0', false);
        return justify(value, prefix, leftJustify, minWidth, zeroPad);
    };
 
    // formatString()
    var formatString = function(value, leftJustify, minWidth, precision, zeroPad) {
        if (precision != null) {
            value = value.slice(0, precision);
        }
        return justify(value, '', leftJustify, minWidth, zeroPad);
    };
 
    // finalFormat()
    var doFormat = function(substring, valueIndex, flags, minWidth, _, precision, type) {
        if (substring == '%%') return '%';
 
        // parse flags
        var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false;
        for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
            case ' ': positivePrefix = ' '; break;
            case '+': positivePrefix = '+'; break;
            case '-': leftJustify = true; break;
            case '0': zeroPad = true; break;
            case '#': prefixBaseX = true; break;
        }
 
        // parameters may be null, undefined, empty-string or real valued
        // we want to ignore null, undefined and empty-string values
        if (!minWidth) {
            minWidth = 0;
        } else if (minWidth == '*') {
            minWidth = +a[i++];
        } else if (minWidth.charAt(0) == '*') {
            minWidth = +a[minWidth.slice(1, -1)];
        } else {
            minWidth = +minWidth;
        }
 
        // Note: undocumented perl feature:
        if (minWidth < 0) {
            minWidth = -minWidth;
            leftJustify = true;
        }
 
        if (!isFinite(minWidth)) {
            throw new Error('sprintf: (minimum-)width must be finite');
        }
 
        if (!precision) {
            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
        } else if (precision == '*') {
            precision = +a[i++];
        } else if (precision.charAt(0) == '*') {
            precision = +a[precision.slice(1, -1)];
        } else {
            precision = +precision;
        }
 
        // grab value using valueIndex if required?
        var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
 
        switch (type) {
            case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad);
            case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
            case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
            case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
            case 'i':
            case 'd': {
                        var number = parseInt(+value);
                        var prefix = number < 0 ? '-' : positivePrefix;
                        value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                        return justify(value, prefix, leftJustify, minWidth, zeroPad);
                    }
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case 'g':
            case 'G':
                        {
                        var number = +value;
                        var prefix = number < 0 ? '-' : positivePrefix;
                        var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                        var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                        value = prefix + Math.abs(number)[method](precision);
                        return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
                    }
            default: return substring;
        }
    };
 
    return format.replace(regex, doFormat);
}

/**
 * Équivalent à la fonction PHP
 * Javascript trim, ltrim, rtrim
 * http://www.webtoolkit.info/
 **/ 
function trim(str, chars) {
	return ltrim(rtrim(str, chars), chars);
}
