/**
 * bgMax
 * =============================================================================
 * ajustes les dimensions d'une image en fonction de celles de la fen�tre
 * (con�u en particulier pour �muler des images de fond de page)
 * 
 * @author      Erwan Lef�vre <erwan.lefevre@gmail.com>
 * @copyright   Erwan Lef�vre 2009
 * @license     Creative Commons - Paternit� 2.0 France - http://creativecommons.org/licenses/by/2.0/fr/
 * @version     v1.0 / 2010-07-02
 * @see         http://www.webbricks.org/bricks/bgMax/
 
 * @compatible  au 2 juillet 2010, compatibilit� assur�e pour :
 *              Firefox 1.5+, Internet Explorer 5.5+, Op�ra, Safari, Chrome 
 */



/**
 * transfer
 * =============================================================================
 * retourne un objet contenant les propri�t�s et m�thodes de l'objet /dest/,
 * compl�t�es et/ou �cras�es par celles de l'objet /source/
 *
 * @param       source       {object}        l'objet source
 * @param       dest         {object}        l'objet de destination
 * @return      {object}
 *
 */ 
function transfer (source, dest) {
    var prop, transfered={};
    for ( prop in dest ) { transfered[prop] = dest[prop]; }
    for ( prop in source ) { transfered[prop] = source[prop]; }
    return transfered; 
}



/**
 * byTN()
 * 
 * raccourci pour [element].getElementsByTagName()
 * retourne l'�l�ment html de type /tagName/
 *
 * @param       tagName     {String}        Le type d'�l�ments recherch�
 *
 * @returns     {HTMLCollection}
 * 
 * =============================================================================
 */
function byTN(tagName,container) {
    return (container||document).getElementsByTagName(tagName) ;
}





/** 
 * winDim v2.0.1, 2010-07-07
 * 
 * retourne les dimentions int�rieurs de la fen�tre
 *
 * @returns     {Object}
 * 
 * =============================================================================
 */
function winDim() {
    var w,h,
        i = window,
        d = document,
        de = d.documentElement,
        db = d.body;
        
    if ( i.innerWidth ) { // autres que IE
        w = i.innerWidth;
        h = i.innerHeight;
    } else if ( de.clientWidth ) { // IE8
        w = de.clientWidth;
        h = de.clientHeight;
    }
    else { // IE6
        w = db.clientWidth;
        h = db.clientHeight;
    }

    return {'w':w, 'h':h} ;
}






/**
 * addEvent()
 * 
 * ajoute la fonction /fn/ � la pile de r�solution de l'�v�nement /evenType/ de
 * l'objet /obj/
 * 
 * merci � : http://www.scottandrew.com/weblog/articles/cbs-events
 *
 * @param       {Mixed}             obj         window, ou document, ou un �l�ment HTML
 * @param       {String}            evType      type d'event (click, mouseover, mouseout, etc.�)
 * @param       {String}            fn          la fonction � ajouter
 * @param       {Boolean}           useCapture  "useCapture un bool�en : true pour la phase de capture, ou false pour la phase de bouillonnement et la cible. On utilise quasiment toujours la valeur false." (cf : http://www.alsacreations.com/article/lire/578-La-gestion-des-evenements-en-JavaScript.html)
 * 
 * @returns     void
 * 
 * =============================================================================
 */
function addEvent (obj, evType, fn, useCapture){
    if (obj.addEventListener) { obj.addEventListener(evType, fn, useCapture); }
    else { obj.attachEvent("on"+evType, fn); }
}




/** 
 * setStyle v1.0
 * 
 * Modifie l'attribut style de l'�l�ment /element/, selon le tableau associatif /styles/.
 *
 * @param           elem            {HTMLElement}           l'�l�ment dont on veut modifier les styles
 * @param           styles          {Object}                d�finition (javascript) des styles � appliquer � l'�l�ment
 * @returns         {void}
 * 
 * =============================================================================
 */
function setStyle(elem, styles) { // 58 octets
    for (var prop in styles) {
        elem.style[prop] = styles[prop];
    }
}




/**
 * setOpacity
 * 
 * r�gle l'opacit� d'un �l�ment
 *
 * @param       elem            {element}       l'�l�ment � traiter
 * @param       value           {float}         valeur souhait�e (0=transparent, 1=opaque)
 * @return      string
 *
 * =============================================================================
 */
function setOpacity(elem, value) {
    value = (value == 1)?0.99999:value;

    elem.style.opacity = value;
    elem.style.filter = 'alpha(opacity=' + value*100 + ')';
    elem.style.MozOpacity = value;
    elem.style.KhtmlOpacity = value;
}




/**
 * fade()
 * 
 * permet d'effectuer une animation d'opacit� sur un �l�ment HTML
 *
 * @requires    setOpacity
 *
 * @param       {HTMLelement}   elem            l'�l�ment HTML � animer
 * @param       {float}         to              l'opacit� finale (0=transparent, 1=opaque)
 * @param       {float}         from            l'opacit� initiale
 * @param       {integer}       duration        la dur�e de l'animation, en millisecondes
 * @param       {object}        options         tableau associatif contenant les options suppl�mentaires :
 *                                                      -   duration : integer - dur�e de l'animation, en millisecondes
 *                                                      -   frameRate : integer - nombre d'images par secondes
 *                                                      -   onFinish : function - fonction � appeler � la fin de l'animation
 * 
 * @returns     {void}
 * =============================================================================
 */
function fade (elem, to, from, options) {
    
    // initialisation des param�tre principaux
    this.elem = elem || document.body;
    this.to = to!==undefined ? to : 1;
    var st = this.elem.style;
    this.from = (from===undefined ? ( !st.opacity&&st.opacity!==0 ? (this.to>0?0:1) : parseFloat(st.opacity) ) : from);
    
    // initialisation des options
    options = options || {};
    this.duration = options.duration || 500;
    this.frameRate = options.frameRate || 30;
    this.onFinish = options.onFinish;
    
    // calculs pour d�coupage de l'animation en plusieurs �tapes
    this.totalFrames = Math.ceil(this.duration/1000*this.frameRate);
    this.perFrame = (this.to-this.from)/this.totalFrames;
    this.frameNb = 0;
    
    // utile pour les setTimeout
    var self = this;



    /**
     * next
     * -------------------------------------------------------------------------
     * lance l'�tape suivante de l'animation
     * 
     * @returns     {void}
     * 
     */
    this.next = function () {
        this.prog = setTimeout (
            function(){self.frame();},
            1000/this.frameRate
        );
    };
        
    
    /**
     * frame()
     * -------------------------------------------------------------------------
     * ex�cute une �tape de l'animation
     */
    this.frame = function () {
        // r�gle l'opacit� de l'�l�ment
        setOpacity(this.elem, this.from + this.perFrame*this.frameNb);
        
        // si anim termin�e
        if ( this.frameNb===this.totalFrames ) {
                setOpacity(this.elem, this.to);
                if (typeof this.onFinish=='function') { setTimeout(this.onFinish,1); } // fonction callback
                }
                
        // sinon lancer le frame suivant
        else {
                this.frameNb++;
                this.next();
        }
    };
    
    
    // lancer la premi�re �tape de l'anim
    this.next();
}





/**
 * redimArea v1.2 / 2010-06-26
 * 
 * retourne les mesures /{w,h}/ de /src_w/ et /scr_h/, apr�s redimentionnement homot�tique
 * d'apr�s les crit�res /mesures{max_w, min_w, max_h, max_h}/
 *
 * @param       {Integer}       src_w       largeur de la surface � redimentionner
 * @param       {Integer}       src_h       hauteur de la surface � redimentionner
 * @param       {Object}        mesures     mesures maximales et/ou minimales pour
 *                                          la largeur et/ou la hauteur
 *
 * @returns     {Object}
 *                                          
 * =============================================================================
 */
function redimArea (src_w, src_h, options) {
    
    // initialisations
    
        var max_w, min_w, max_h, min_h, // contraintes donn�es en options
            round,                      // option indiquant d'arrondir les dimensions obtenues
            wh, hw,                     // rapports de proportion de la surface
            height, width;              // dimensions finales de la surface
        
        // mesures souhait�es
        options = options || {};
        max_w = options.max_w;
        min_w = options.min_w;
        max_h = options.max_h;
        min_h = options.max_h;
        
        // autres options
        round = options.round===undefined ? 1 : options.round; // pour r�trocompatibilit� : undefined=>true
    
        // calcul du rapport largeur/hauteur de la source
        wh = src_w / src_h ;
        hw = src_h / src_w ;
        
        // par d�faut, garder les mesures initiales
        height = src_h ;
        width = src_w ;
        
    // redimentionnements
        
        // agrandissement largeur
        if ( width < min_w ) {
            width = min_w;
            height = width * hw ;
        }
        
        // agrandissement hauteur
        if ( height < min_h ) {
            height = min_h;
            width = height * wh ;
        }
    
        // r�duction largeur
        if ( max_w && (width > max_w) ) {
            width = max_w;
            height = width * hw ;
        }
    
        // r�duction hauteur
        if ( max_h && (height > max_h) ) {
            height = max_h;
            width = height * wh ;
        }
        
    // valeurs n�gatives interdites
        width = width<0 ? 0 : width;
        height = height<0 ? 0 : height;
    
    return {
        w : round ? Math.round(width) : width,
        h : round ? Math.round(height) : height
    };
}





/**
 * bgMax    v1.0 / 2010-07-02
 * 
 * ajustes les dimensions d'une image en fonction de celles de la fen�tre
 *
 * @requires        addEvent, byTN, fade, redimArea, setOpacity, setStyle, transfer, winDim
 * 
 * =============================================================================
 */
var bgMax = {
    
    /*
     * @var defaults
     *
     * options par d�faut
     *
     * -------------------------------------------------------------------------
     */
    defaults : {
        mode : 'max',           // string - max (occuper toute la fen�tre, quite � d�border) | full (toute l'image est visible, et aussi grande que possible)
        enlarge : 1,            // boolean - autorise ou non � agrandir l'image au dessus de ses dim r�elles
        reduce: 1,              // boolean - autorise ou non � r�duire l'image au dessus de ses dim r�elles
        //ffHack : 1,               // String - d�calage vers le haut � appliquer au body, pour Firefox<3.
        zIndex : -1,            // integer - profondeur du bloc-image
        position : 'absolute',  // string - positionnement de l'image (absolute|fixed)
        align : 'center',       // string - alignement horizontal de l'image
        vertAlign : 'top',      // string - alignement vertical de l'image
        fadeAfter : 400,        // integer - indique de faire appara�tre l'image en fondu, si elle n'est pas charg�e � l'issue de ce d�lai, exprim� en millisecondes
                                                // 0 : pour faire un fondu dans tous les cas
                                                // false : pour interdire le fondu
        fadeOptions : {         // Object - options du fondu. Voir les options de fade.js : http://www.webbricks.org/bricks/fade/
            duration : 1000,
            frameRate : 25
        }
    },                                          
    
    /**
     * redim
     *
     * redimentionne l'image de fa�on proportionnelle
     *
     * @access      protected
     * @returns     void
     * 
     * -------------------------------------------------------------------------
     */
    redim : function () {
        // pr�paratifs
        var self = bgMax,
            img,            // l'�l�ment image manipul�
            options,    // options
            win = winDim(),                     // mesure de la fen�tre
            imgTop, imgBottom, imgLeft, imgRight,       // positionnement de l'image
            imgDim,                             // mesures de l'image
            max_W, max_H, min_W, min_H;         // contraintes de mesures pour l'image
        
        img  = self.img;
        imgTop = imgBottom = imgLeft = imgRight = '';
        options = self.opt;
        imgDim = img.fileDim;
        
        if (imgDim) {
            
            // dimentionnement de l'image
            
                // �tablir les mesures max/min, horiz/vert
                
                    // mode "full" (toute l'image est visible, et aussi grande que possible)
                    if (options.mode == 'full') {
                        max_W = min_W = win.w;
                        max_H = min_H = win.h;
                        if (!options.enlarge) {
                            if (max_W > imgDim.w) { max_W = min_W = imgDim.w; }
                            if (max_H > imgDim.h) { max_H = min_H = imgDim.h; }
                        }
                        if (!options.reduce) {
                            if (min_W < imgDim.w) { min_W = imgDim.w; max_W = Math.max(min_W, max_W); }
                            if (min_H < imgDim.h) { min_H = imgDim.h; max_H = Math.max(min_H, max_H); }
                        }
                    }
                    
                    // mode "max" (occuper toute la fen�tre, quite � d�border)
                    else {
                        min_W = max_W = win.w;
                        min_H = max_H = 0;
                        if (!options.reduce) {
                            max_W = 0;
                        }
                        // on ne traite pas options.enlarge qui est sous-entendu � 1
                    }
    
                // optimiser la surface
                imgDim = redimArea(imgDim.w, imgDim.h, {
                    min_w : min_W,
                    win_h : min_H,
                    max_w : max_W,
                    max_h : max_H,
                    round : 1
                });
                
                // appliquer les dimensions
                img.width = imgDim.w;
                img.height = imgDim.h;
                
            // positionnement de l'image
                
                // calcul de la position horizontale
                switch (options.align) {
                    case 'left' :
                        imgLeft = '0px';
                        break;
                    case 'right' :
                        imgRight = '0px';
                        break;
                    default :
                        imgLeft = -Math.round((imgDim.w - win.w)/2) + 'px';
                }
                
                
                // calcul de la position vericale
                if (options.vertAlign=='bottom') {
                    imgBottom = '0px';
                }
                else {
                    imgTop = img.hack||'0px';
                }
                
                // positionnement effectif
                setStyle(img,{
                    left : imgLeft,
                    right : imgRight,
                    top : imgTop,
                    bottom : imgBottom
                });
        }
    },
                                                
    
    /**
     * show
     *
     * affiche l'image
     *
     * @access      protected
     * @returns     void
     * 
     * -------------------------------------------------------------------------
     */
    show : function() {
        
        var self = bgMax,
            img;
            
        img = self.img;
        
        // relever les mesures normales de l'image
        img.fileDim = {
            w : img.clientWidth,
            h : img.clientHeight
        };
        
        // redimentionner l'image
        self.redim();
        
        // faire appara�tre l'image
        if (self.fadeIt) { fade(self.img,1,0,self.opt.fadeOptions); }
        else { setOpacity(img,1); } // parfois inutile, mais est-ce la peine de mettre une condition ?
    },
    
    /**
     * init
     *
     * initialise le script
     * 
     * @access      public
     *
     * @param       src         {String}        url de l'image
     * @param       options     {Object}        tableau associatif des options (voir this.defaults)
     *
     * @returns     void
     * 
     * -------------------------------------------------------------------------
     */
    init : function (src, options) {
        
        var self = bgMax,
            body = byTN('body')[0],
            fadeAfter,
            img,
            ff;  // version de Firefox
    
        options = self.opt = transfer(options, this.defaults);
        fadeAfter = options.fadeAfter;
        img = self.img = document.createElement('img');
        
        img.id = "bgMax";
        
        setStyle(img,{
            zIndex : options.zIndex,
            position : options.position
        });
        
        // hack pour Firefox<3 (pour une fois, c'est Firefox !)
        ff = navigator.userAgent.match(/Firefox.(\d+(\.\d+))/);
        if (ff && parseFloat(ff[1]) < 3) {
            setStyle(body,{
                zIndex : 0,
                position : 'relative',
                top : 0,
                left : 0
            });
            img.hack = '-'+options.ffHack;
        }
        
        //body.appendChild(img);
        body.insertBefore(img, body.childNodes[0]);
        img.onload = self.show;
        
        if (fadeAfter!==false) {
            setOpacity(img,0);
            if (fadeAfter) {
                setTimeout(function(){
                    self.fadeIt = 1;
                },fadeAfter);
            }
            else {
                self.fadeIt = 1;
            }
            
        }
        
        addEvent(window,'resize',self.redim);
        
        img.src = src;
    }
};
