if(!window.sofad) sofad = {};

//Cadre d'applications sofad
(function() {
    Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
    };
    Function.prototype.prop = function() {
        if(2 <= arguments.length) {
            this.method.apply(this, arguments);
        } else if(1 == arguments.length) {
            return this.prototype[arguments[0]];
        }
    };


    sofad.chemins = {
        images:      sofad.info.base_url + 'assets/js/lib-javascript/images/',
        zoom:        sofad.info.base_url + 'assets/js/lib-javascript/lib/sofad/zoom.html',
        reinitOutil: sofad.info.base_url + 'index.php/services_interactivites/reinit_outil'
    };


    // http://blog.davglass.com/files/yui/docs/overview-summary-tools.js.html
    sofad.format = function() {
        var num = arguments.length; 
        var oStr = arguments[0];   
        for (var i = 1; i < num; i++) { 
            var pattern = "\\{" + (i-1) + "\\}"; 
            var re = new RegExp(pattern, "g"); 
            oStr = oStr.replace(re, arguments[i]); 
        } 
        return oStr; 
    } 

    sofad.grandeurVue = function() {
        var largeur = 0;
        var hauteur = 0;
        if('number' == typeof( window.innerWidth )) {
            //Non-IE
            largeur = window.innerWidth;
            hauteur = window.innerHeight;
        } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
            //IE 6+ in 'standards compliant mode'
            largeur = document.documentElement.clientWidth;
            hauteur = document.documentElement.clientHeight;
        } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
            //IE 4 compatible
            largeur = document.body.clientWidth;
            hauteur = document.body.clientHeight;
        }

        return { largeur: largeur, hauteur: hauteur };
    }

    sofad.lang = function(chaine) { 
        var re      = new RegExp("\\{L:([^\\}]+)\\}");
        var matches;
        var lang    = jQuery('html').attr('lang') ? jQuery('html').attr('lang') : 'fr';
        while(matches = chaine.match(re)) {
            var marqueur = matches[0];
            var terme    = matches[1].split(".");
            var msg     = sofad.msgLang[lang];
            for(var ii=0; ii<terme.length; ++ii) {
                if(!(terme[ii] in msg)) throw "Le terme " + matches[1] + " n'existe pas dans le dictionnaire des messages";
                msg = msg[terme[ii]];
            }
            chaine = chaine.replace(marqueur, msg);
        }
        return chaine;
    } 


    sofad.obtientGET = function () {
        var params_get = {};

        var chaine_get = (0 == arguments.length) ?
            document.location.search :
            arguments[0];
        chaine_get = decodeURIComponent(chaine_get);

        if(0 == chaine_get.length) return params_get;
        
        if('?' == chaine_get.charAt(0)) chaine_get = chaine_get.substr(1);

        var chaines_params = chaine_get.split('&');
        var chaine_param;
        while(chaine_param = chaines_params.shift()) {
            var operands = chaine_param.split('=');
            if(2 != operands.length) continue;
            params_get[operands[0]] = operands[1];
        }

        return params_get;
    };


    sofad.valeursFormulaire = function(formulaire) {
            var valeurs = {};

            formulaire.find('input:text, textarea, input:hidden').each(function() {
                var nom = this.getAttribute('name');
                if(!nom) return true;
                valeurs[nom] = this.value;
            });

            formulaire.find('select').each(function() {
                var nom = this.getAttribute('name');
                if(!nom) return true;
                var index    = this.options.selectedIndex;
                valeurs[nom] = '' !== this.options[index].value ? 
                    this.options[index].value :
                    this.options[index].text;
            });

            formulaire.find('input:radio').each(function() {
                var nom = this.getAttribute('name');
                if(!nom) return true;
                if(this.checked) valeurs[nom] = this.value;
            });

            formulaire.find('input:checkbox').each(function() {
                var nom = this.getAttribute('name');
                if(!nom) return true;
                valeurs[nom] = this.checked ? this.value : "";
            });


        return valeurs;
    };



    sofad.ExceptionAucunSousWidget = function(widget) {
        var requetes_sous_widgets = [];
        
        if(widget.typesEnfants) {
            for(var nom in widget.typesEnfants) {
                var constructeur = sofad.ns( widget.typesEnfants[nom] );
                if(constructeur && constructeur.prototype) {
                    requetes_sous_widgets[ requetes_sous_widgets.length ] = constructeur.prototype.requete;
                }
            }
        }

        var message = "Le widget «" +widget+ "» requiert des sous-widgets, mais n'en a aucun. Requêtes des sous-widgets : "+requetes_sous_widgets.join(" | ")+".";  

        return {
            name:   'AucunSousWidget',
            widget: widget,
            message: message,
            toString: function() { return this.message; }
        }; 
    };


    sofad.exposeExceptionAucunSousWidget = function(e) {
        var noeuds = e.widget.noeuds;
        noeuds.css('border',     '1px solid red');
        noeuds.css('min-height', '1em');
        noeuds.prepend('<a id="ERREUR-WIDGET-MANQUANT"></a>');

        var href_sans_ancre = -1 == location.href.indexOf('#') ?
            location.href :
            location.href.substring(0, location.href.indexOf('#')) ;
        location.href = href_sans_ancre + '#ERREUR-WIDGET-MANQUANT';

        noeuds.css('border', '3px solid red');
        setTimeout(function() { alert(e); }, 500);
    };


    sofad.objet = function(proto) {
        function F() {};
        F.prototype = proto;
        return new F();
    };


    sofad.copie = function(obj) {
        var copie = {};
        for(var prop in obj) copie[prop] = obj[prop];
        return copie;
    };


    sofad.sousType = function(type, constructeur) {
        if(undefined == constructeur) {
            constructeur = function() { type.apply(this, arguments); };
        }
        constructeur.prototype = sofad.objet( type.prototype );
        return constructeur;
    };


    (function () {
        function obtient(nom) {
            var categories = nom.split('.');
            var objet = window.sofad;
            for(var ii=0; ii<categories.length; ++ii) {
                var categorie = categories[ii];
                if(undefined == objet[categorie]) return;
                objet = objet[categorie];
            }

            return objet;
        }

        function modifie(nom, nouv) {
            var categories = nom.split('.');
            var objet = window.sofad;
            for(var ii=0; ii<categories.length-1; ++ii) {
                var categorie = categories[ii];
                if(undefined == objet[categorie]) return false;
                objet = objet[categorie];
            }

            objet[ categories[ii] ] = nouv;

            return true;
        }

        sofad.ns = function(nom, nouv) {
            if(undefined == nom) return;
            if(undefined == nouv) return obtient(nom);
            return modifie(nom, nouv);
        }
    })();


 

    sofad.widgets = {
        Widget: (function() {
            var constructeur = function(noeuds, widget_parent) {
                this.noeuds       = jQuery(noeuds);
                this.widgetParent = widget_parent;
                this.controleurs  = {};
                this.events       = {};
                this.declencheursEvents = {};

                this._instancieEnfants();
            };
            
            constructeur.prototype = { 
                nomWidget:     'widget',
                requete:       'aucun',
                typesEnfants: {},

                _instancieEnfants: function() {
                    this.enfants = [];
                    for(var nom_proto in this.typesEnfants) {
                        var constructeur = this.factory(this.typesEnfants[nom_proto]);
                        if(undefined === constructeur) {
                            throw this.typesEnfants[nom_proto]+" n'a pas été trouvé pour l'instanciation des enfants de "+this;
                        }
                        this.enfants = this.enfants.concat( 
                            sofad.batiWidgets( constructeur, this ) );
                    }
                },

                factory: function(ns_type, type_modifie) {
                    if(!type_modifie) { //GET
                        var widget = this;
                        do {
                            if(widget._factory && ns_type in widget._factory) {
                                return widget._factory[ns_type];
                            }
                        } while(widget = widget.widgetParent);

                        return sofad.ns(ns_type);
                    } else { //SET
                        if(!this._factory) {
                            this._factory = {};
                        } else if(!this.hasOwnProperty('_factory')) {
                            this._factory = sofad.objet( this._factory );
                        }

                        return this._factory[ns_type] = type_modifie;
                    }
                },

                parentEvent: function(nom_event) {
                    var widget_parent = this;
                    while(widget_parent = widget_parent.widgetParent) {
                        if(nom_event in widget_parent.events) {
                            return widget_parent.events[nom_event];
                        }
                    }
                },

                declencheParentEvent: function(nom_event, declencheur) {
                    var widget_parent = this;
                    while(widget_parent = widget_parent.widgetParent) {
                        if(widget_parent.inscritDeclencheurEvent(nom_event, declencheur)) return true;
                    }
                    return false;
                },

                inscritDeclencheurEvent: function(nom_event, declencheur) {
                    if(!(nom_event in this.events)) return false;

                    if(!(nom_event in this.declencheursEvents)) {
                        this.declencheursEvents[nom_event] = [];
                    }
                    this.declencheursEvents[nom_event][ this.declencheursEvents[nom_event].length ] = declencheur;
                    var evenement = this.events[nom_event];
                    declencheur(function() { evenement.fire(); });
                    return true;
                },

                demarre: function () {
                    for(var ii=0; ii<this.enfants.length; ++ii) {
                        this.enfants[ii].demarre();
                    }
                },

                toString: function() { return this.nomWidget + ' (' + this.requete + ')'; }
            };

            return constructeur;
        })()
    };


    sofad.controleurs = {
        Controleur: (function() {
            var constructeur = function(widget_parent, params_rendu) {
                this.paramsRendu = params_rendu;
                if(params_rendu) {
                    this.requete = sofad.format.apply(window, [this.requete].concat(params_rendu));
                }
                this.insere(widget_parent);
                var contexte = widget_parent ? 
                    widget_parent.noeuds :
                    undefined;
                var noeuds = 'function' == typeof this.requete ?
                    this.requete(contexte) :
                    jQuery(this.requete, contexte) ;
                sofad.widgets.Widget.apply(this, [noeuds, widget_parent]);
            };
            var proto = sofad.objet( sofad.widgets.Widget.prototype );
            constructeur.prototype = proto;

            proto.nomWidget = 'controleur';

            proto.insere = function (widget_parent) {
                widget_parent.noeuds.append(this.renduHtml());
            };

            proto.renduHtml = function() {
                var html = this.html;
                if(this.paramsRendu) {
                    html = sofad.format.apply(window, [this.html].concat(this.paramsRendu));
                }
                return sofad.lang(html);
            }

            return constructeur;
        })()
    };


    sofad.batiWidgets = function(type_widget, widget_parent) {
        var contexte = undefined === widget_parent ? undefined : widget_parent.noeuds;

        var requete = ('function' == typeof type_widget.prototype.requete) ?
            type_widget.prototype.requete :
            function (contexte) { return jQuery(type_widget.prototype.requete, contexte); };

        return jQuery.map(
            requete(contexte),
            function (noeuds, index) { 
                return new type_widget(noeuds, widget_parent);
            }
        );
    };
})();




//Sequences
(function () {
    sofad.sequences = {};
    var widgets     = sofad.sequences.widgets     = {};
    var controleurs = sofad.sequences.controleurs = {};
    var extensions  = sofad.sequences.extensions  = {};


    // WIDGETS

    widgets.Segment = (function () {
        var constructeur = function () {
            sofad.widgets.Widget.apply(this, arguments);
            this.desactive();
        };
        var proto = sofad.objet( sofad.widgets.Widget.prototype );
        proto.requete   = 'div.segment';
        proto.active    = function() { this.noeuds.show(); };
        proto.desactive = function() { this.noeuds.hide(); };
        constructeur.prototype = proto;

        return constructeur;
    })();

    widgets.Sequence = (function () {
        var constructeur = function () {
            sofad.widgets.Widget.apply(this, arguments);

            if(0 == this.enfants.length) {
                throw sofad.ExceptionAucunSousWidget(this);
            }
        };

        var proto = sofad.objet(sofad.widgets.Widget.prototype);
        proto.requete       = 'div.sequence';
        proto.typesEnfants = {'segment': "sequences.widgets.Segment"};

        var parent_demarre = proto.demarre;
        proto.demarre = function () { 
            this.activeSegment(0);
            parent_demarre.apply(this, arguments);
        };

        proto.incrementeSegmentActif = function(incr) {
            if(undefined === this.index) return;
            try {
                this.activeSegment(incr+this.index);
                return true;
            } catch(e) {
                if("RangeError" == e.name) {
                    return false;
                }
                throw e;
            }
        };

        proto.activeSegment = function(nouv_index) {
            if(false === nouv_index) {
                if(undefined !== this.index) {
                    this.enfants[ this.index ].desactive();
                }
                this.index = undefined;
                return;
            }

            if(0 > nouv_index || nouv_index >= this.enfants.length) throw new RangeError();
            if(undefined !== this.index) {
                this.enfants[ this.index ].desactive();
            }
            this.index = nouv_index;
            this.enfants[ this.index ].active();
        };

        proto.activeSegmentSuivant = function() {
            return this.incrementeSegmentActif(1);
        };

        proto.activeSegmentPrecedent = function() {
            return this.incrementeSegmentActif(-1);
        };

        constructeur.prototype = proto;

        return constructeur;
    }) ();

    for(var nom_widget in widgets) widgets[nom_widget].prototype.nomWidget = nom_widget;

    //EXTENSIONS

    extensions.precSuiv = function(original) {
        var constructeur = function() {
            original.apply(this, arguments);

            if(undefined === this.controleurs.barreNav) {
                this.controleurs.barreNav = new (this.factory('sequences.controleurs.BarreNav'))(this);
            }
            this.controleurs.btnPrec  = new (this.factory('sequences.controleurs.Precedent'))(this.controleurs.barreNav);
            this.controleurs.btnSuiv  = new (this.factory('sequences.controleurs.Suivant'))(this.controleurs.barreNav);

            if(1 >= this.enfants.length) { // inutile si on a un seul segment
                this.controleurs.btnPrec.noeuds.remove();
                this.controleurs.btnSuiv.noeuds.remove();
            }
        };
        
        var proto = sofad.objet(original.prototype);

        var parent_activeSegment = proto.activeSegment;
        proto.activeSegment = function() {
            parent_activeSegment.apply(this, arguments);
            if(0 == this.index || undefined === this.index) {
                this.controleurs.btnPrec.noeuds.addClass('inactif');
            } else {
                this.controleurs.btnPrec.noeuds.removeClass('inactif');
            }
            if(this.enfants.length-1 == this.index || undefined === this.index) {
                this.controleurs.btnSuiv.noeuds.addClass('inactif');
            } else {
                this.controleurs.btnSuiv.noeuds.removeClass('inactif');
            }
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.controleurs.btnPrec.noeuds.click(
                function() { that.activeSegmentPrecedent(); return false; }
            );

            this.controleurs.btnSuiv.noeuds.click(
                function() { that.activeSegmentSuivant(); return false; } 
            );
        };

        constructeur.prototype = proto;

        return constructeur;
    };


    extensions.pagination = function(original) {
        var constructeur = function() {
            original.apply(this, arguments);

            if(undefined === this.controleurs.barreNav) {
                this.controleurs.barreNav = new (this.factory('sequences.controleurs.BarreNav'))(this);
            }

            this.controleurs.pagination   = new (this.factory('sequences.controleurs.Pagination'))(this.controleurs.barreNav);
            this.controleurs.pageCourante = new (this.factory('sequences.controleurs.PageCourante'))(this.controleurs.pagination);
            this.controleurs.nombrePages  = new (this.factory('sequences.controleurs.NombrePages'))(this.controleurs.pagination);

            if(1 >= this.enfants.length) { // inutile si on a un seul segment
                this.controleurs.pagination.noeuds.remove();
            }
        };
        
        var proto = sofad.objet(original.prototype);
        

        var parent_activeSegment = proto.activeSegment;
        proto.activeSegment = function() {
            parent_activeSegment.apply(this, arguments);
            if(undefined !==this.index) this.controleurs.pageCourante.noeuds.text( 1+this.index );
        }

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            this.controleurs.nombrePages.noeuds.text( this.enfants.length );
        }
        
        constructeur.prototype = proto;

        return constructeur;
    };
    

    extensions.btnParPage = function(original) {
        var constructeur = function() {
            original.apply(this, arguments);

            if(undefined === this.controleurs.barreNav) {
                this.controleurs.barreNav = new (this.factory('sequences.controleurs.BarreNav'))(this);
            }

            this.controleurs.boutonsNav = [];
            for(var ii=0; ii<this.enfants.length; ++ii) {
                this.controleurs.boutonsNav[ii] = new (this.factory('sequences.controleurs.BoutonsNav'))(this.controleurs.barreNav, [ii, 1+ii]);
            }

        };

        var proto = sofad.objet(original.prototype);

        var parent_activeSegment = proto.activeSegment;
        proto.activeSegment = function(nouv_index) {
            var ancien_index = this.index;
            parent_activeSegment.apply(this, arguments);

            if(undefined !== ancien_index) {
                this.controleurs.boutonsNav[ancien_index].noeuds.removeClass('actif');
            }

            if(undefined !== this.index) this.controleurs.boutonsNav[this.index].noeuds.addClass('actif');
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            for(var ii=0; ii<this.enfants.length; ++ii) {
                var that = this;
                (function(index) {
                    that.controleurs.boutonsNav[index].noeuds.bind('click', function() {
                        that.activeSegment(index);
                        return false;
                    });
                })(ii);
            }
        };

        constructeur.prototype = proto;

        return constructeur;
    };

    extensions.pagesNombreuses = function(original) {
        var constructeur = function() {
            original.apply(this, arguments);

            if(undefined === this.controleurs.barreNav) {
                this.controleurs.barreNav = new (this.factory('sequences.controleurs.BarreNav'))(this);
            }

            this.controleurs.btnDebut = new (this.factory('sequences.controleurs.Debut'))(this.controleurs.barreNav);
            this.controleurs.btnFin = new (this.factory('sequences.controleurs.Fin'))(this.controleurs.barreNav);
        };
        var proto = sofad.objet(original.prototype);
        constructeur.prototype = proto;
        var parent_activeSegment = proto.activeSegment;
        var options = extensions.pagesNombreuses.defaults;

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;

            this.controleurs.btnDebut.noeuds.bind("click", function() {
                that.activeSegment(0); return false;
            });

            this.controleurs.btnFin.noeuds.bind("click", function() {
                that.activeSegment(that.enfants.length -1); return false;
            });

        };

        proto.activeSegment = function() {
            parent_activeSegment.apply(this, arguments);
            if(undefined === this.index) {
                this.controleurs.btnDebut.noeuds.removeClass("inactif");
                this.controleurs.btnFin.noeuds.removeClass("inactif");
                return;
            }

            if(0 == this.index) {
                this.controleurs.btnDebut.noeuds.addClass("inactif");
            } else {
                this.controleurs.btnDebut.noeuds.removeClass("inactif");
            }

            if(this.enfants.length-1 == this.index) {
                this.controleurs.btnFin.noeuds.addClass("inactif");
            } else {
                this.controleurs.btnFin.noeuds.removeClass("inactif");
            }


            for(var ii=0; ii<this.controleurs.boutonsNav.length; ++ii) {
                var bouton = this.controleurs.boutonsNav[ii].noeuds;
                if(Math.abs(ii - this.index) > options.boutons_chaque_cote) {
                    bouton.hide();
                } else {
                    bouton.show();
                }
            }
        };

        return constructeur;
    };
    extensions.pagesNombreuses.defaults = {
        boutons_chaque_cote: 3
    };


    //CONTROLEURS

    controleurs.BarreNav = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'div.barre-navigation';
        proto.html    = '<div class="barre-navigation"></div>';
        return constructeur;
    })();

    controleurs.Precedent = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'button.precedent';
        proto.html    = '<button class="precedent">{L:sequences.btn_precedent}</button>';
        proto.insere  = function(widget_parent) { widget_parent.noeuds.prepend(this.renduHtml()); };
        return constructeur;
    })();
    
    controleurs.Suivant = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'button.suivant';
        proto.html    = '<button class="suivant">{L:sequences.btn_suivant}</button>';
        return constructeur;
    })();

    controleurs.Debut = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'button.debut';
        proto.html    = '<button class="debut">{L:sequences.btn_debut}</button>';
        proto.insere  = function(widget_parent) { widget_parent.noeuds.prepend(this.renduHtml()); };
        return constructeur;
    })();
    
    controleurs.Fin = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'button.fin';
        proto.html    = '<button class="fin">{L:sequences.btn_fin}</button>';
        return constructeur;
    })();


    controleurs.Pagination = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'span.compteur-pages';
        proto.html    = '<span class="compteur-pages">{L:sequences.pagination}</span>';
        return constructeur;
    })();

    controleurs.PageCourante = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'span.page-courante';
        proto.html    = '<span class="page-courante"></span>';
        proto.insere  = function(widget_parent) { widget_parent.noeuds.prepend(this.html); };
        return constructeur;
    })();

    controleurs.NombrePages = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'span.nombre-pages';
        proto.html    = '<span class="nombre-pages"></span>';
        return constructeur;
    })();
    
    controleurs.BoutonsNav = (function () {
        var constructeur = function () { sofad.controleurs.Controleur.apply(this, arguments) };
        var proto        = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;
        proto.requete = 'button.aller-page-{0}';
        proto.html    = '<button class="aller-page aller-page-{0}">{1}</button>';
        return constructeur;
    })();
})();


//Questionnaires
(function () {
    sofad.questionnaires = {};
    var widgets     = sofad.questionnaires.widgets     = {};
    var controleurs = sofad.questionnaires.controleurs = {};
    var extensions  = sofad.questionnaires.extensions  = {};

    //WIDGETS

    widgets.Element = (function() {
        var constructeur = function() { sofad.widgets.Widget.apply(this, arguments); };
        var proto = sofad.objet( sofad.widgets.Widget.prototype );
        constructeur.prototype = proto;

        //Pointages
        proto.points = function() {
            var total       = 0;
            var ponderation = 0;
            for(var ii=0; ii<this.enfants.length; ++ii) {
                var points_enfant = this.enfants[ii].points();
                total +=  points_enfant[0];
                ponderation +=  points_enfant[1];
            }
            return [total, ponderation];
        };


        //Remettre à zéro un questionnaire (implémentation imcomplète, utilisée par les Résumer)
        proto.reset = function() {
            for(var ii=0; ii<this.enfants.length; ++ii) {
                this.enfants[ii].reset();
            }
        };

        return constructeur;
    })();



    //Question a Choix Multiples et Reponse Unique

    widgets.RadioBon = (function() {
        var constructeur = function() { widgets.Element.apply(this, arguments); };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.requete   = 'input:radio.bonne';
        proto.imagesCorrige = {
            coche:    '<img src="{0}cases-cocher/bon.png" alt="{L:questionnaires.bonne_reponse}"/>',
            nonCoche: '<img src="{0}cases-cocher/bon.png" alt="{L:questionnaires.bonne_reponse}"/>'
        };
        proto.bonneReponse = true;
        
        proto.corrige = function() {
            if(this._estCorrige) return;
            this._estCorrige = true;
            this.noeuds.before( sofad.lang( sofad.format( this.imagesCorrige[ this.noeuds.is(':checked') ? 'coche' : 'nonCoche' ],
                                                          sofad.chemins.images)));
            this.verrouille();
        };

        proto.verrouille = function() { this.noeuds.attr("disabled", true); };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            this.parentEvent('correction').subscribe(this.corrige, null, this);
        };

        return constructeur;
    })();

    widgets.CaseBon = (function() {
        var constructeur = function() { widgets.RadioBon.apply(this, arguments); };
        var proto = sofad.objet( widgets.RadioBon.prototype );
        constructeur.prototype = proto;

        proto.requete    = 'input:checkbox.bonne';
        proto.verrouille = function() { this.noeuds.attr('disabled', 'disabled'); };

        return constructeur;
    })();

    widgets.RadioMauvais = (function() {
        var constructeur = function() { widgets.RadioBon.apply(this, arguments); };
        var proto = sofad.objet( widgets.RadioBon.prototype );
        constructeur.prototype = proto;

        proto.requete       = 'input:radio:not(.bonne)'; 
        proto.imagesCorrige = {
            coche:    '<img src="{0}cases-cocher/mauvais.png" alt="{L:questionnaires.mauvaise_reponse}"/>',
            nonCoche: ' ' };
        proto.bonneReponse = false;
        return constructeur;
    })();

    widgets.CaseMauvais = (function() {
        var constructeur = function() { widgets.RadioMauvais.apply(this, arguments); };
        var proto = sofad.objet( widgets.RadioMauvais.prototype );
        constructeur.prototype = proto;

        proto.requete    = 'input:checkbox:not(.bonne)';
        proto.verrouille = widgets.CaseBon.prototype.verrouille;

        return constructeur;
    })();

    widgets.Qcmru = (function () {
        var constructeur = function() { widgets.Element.apply(this, arguments); };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.requete           = 'table.choix-multiples:has(input:radio)';
        proto.typesEnfants     = {radioBon:     "questionnaires.widgets.RadioBon",
                                  radioMauvais: "questionnaires.widgets.RadioMauvais"};

        //Rédigée pour satisfaire aussi aux QCMRM
        proto.points = function() {
            var note        = 1;
            var ponderation = 1;
            for(var ii=0; ii<this.enfants.length; ++ii) {
                var enfant = this.enfants[ii];
                if(( enfant.noeuds.is(':checked') && !enfant.bonneReponse) ||
                   (!enfant.noeuds.is(':checked') && enfant.bonneReponse)) {
                    note = 0;
                    break;
                }
            }
            return [note, ponderation];
        };

        return constructeur;

    })();

    widgets.Qcmrm = (function () {
        var constructeur = function() { widgets.Element.apply(this, arguments); };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.requete           = 'table.choix-multiples:has(input:checkbox)';
        proto.typesEnfants     = {radioBon:     "questionnaires.widgets.CaseBon",
                                  radioMauvais: "questionnaires.widgets.CaseMauvais"};

        return constructeur;
    })();

    
    //Textes a trou : correction automatique
    widgets.TexteCorrige = (function () {
        var constructeur = function() { 
            widgets.Element.apply(this, arguments);
            this.obtientReponsesAcceptees();
        };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.requete = 'input:text.texte-corrige, input:text.texte-resumer';
        proto.requeteReponsesAcceptees = 'input:hidden#rep-{0}';
        proto.obtientReponsesAcceptees = function() {
            this.reponsesAcceptees = [];

            var noeuds_reponses_acceptees = jQuery(
                sofad.format( this.requeteReponsesAcceptees, this.noeuds.attr('id') )
            );

            if(0 == noeuds_reponses_acceptees.length) {
                //TODO faire une exception appropriee
                throw sofad.ExceptionAucunSousWidget(this);
            }

            this.reponsesAcceptees = noeuds_reponses_acceptees[0].value.split('|');
        };

        proto.corrige = function() {
            if(this._estCorrige) return;
            this._estCorrige = true;
            this.noeuds.hide();
            var reponse_acceptee = this.valide();
            if(reponse_acceptee) {
                this.controleurs.bonneReponse    = new (this.factory('questionnaires.controleurs.BonneReponse'))(this, [reponse_acceptee]);
            } else {
                this.controleurs.corrigeReponse  = new (this.factory('questionnaires.controleurs.CorrigeReponse'))(this, [this.reponsesAcceptees[0]]);
                this.controleurs.mauvaiseReponse = new (this.factory('questionnaires.controleurs.MauvaiseReponse'))(this, [this.noeuds[0].value]);
            }
        };

        proto.reset = function() {
            this._estCorrige = false;
            this.noeuds[0].value = '';
            this.noeuds.show();
            var ctrl_corrige = ['bonneReponse', 'corrigeReponse', 'mauvaiseReponse'];
            for(var ii=0; ii<ctrl_corrige.length; ++ii) {
                var nom_ctrl = ctrl_corrige[ii];
                if(nom_ctrl in this.controleurs) {
                    this.controleurs[nom_ctrl].noeuds.remove();
                    delete this.controleurs[nom_ctrl];
                }
            }
        };

        proto.tableTolerance = {
            'minuscule':
            ['a', 'à', 'b', 'c', 'ç', 'd', 'e', 'é', 'è',
             'ê', 'f', 'g', 'h', 'i', 'î', 'ï', 'j', 'k',
             'l', 'm', 'n', 'o', 'ô', 'ö', 'p', 'q', 'r',
             's', 't', 'u', 'û', 'ü', 'v', 'w', 'x', 'y', 'z'],
            'majuscule':
            ['A', 'À', 'B', 'C', 'Ç', 'D', 'E', 'É', 'È', 
             'Ê', 'F', 'G', 'H', 'I', 'Î', 'Ï', 'J', 'K', 
             'L', 'M', 'N', 'O', 'Ô', 'Ö', 'P', 'Q', 'R',
             'S', 'T', 'U', 'Û', 'Ü', 'V', 'W', 'X', 'Y', 'Z'],
            'accents':
            ['À', 'Ç', 'É', 'È', 'Ê', 'Î', 'Ï', 'Ô', 'Ö', 'Û', 'Ü',
             'à', 'ç', 'é', 'è', 'ê', 'î', 'ï', 'ô', 'ö', 'û', 'ü'],
            'sans_accents':
            ['A', 'C', 'E', 'E', 'E', 'I', 'I', 'O', 'O', 'U', 'U',
             'a', 'c', 'e', 'e', 'e', 'i', 'i', 'o', 'o', 'u', 'u']
        };
        
        proto.valide = function() {
            var reponse_donnee = jQuery.trim(this.noeuds[0].value);
            var est_explicite_utf8 = !!jQuery('meta[content*=charset=]').attr('content').match(/charset=(utf-8|utf8)/i);
            
            for(var ii=0; ii<this.reponsesAcceptees.length; ++ii) {
                var reponse_acceptee = jQuery.trim(this.reponsesAcceptees[ii]);

                //exact
                if(reponse_donnee == reponse_acceptee) return reponse_acceptee;

                var reponse_donnee_tolere   = reponse_donnee;
                var reponse_acceptee_tolere = reponse_acceptee;
 
                //tolerance minuscule/majuscule
                if(est_explicite_utf8) {
                    for(var jj=0; jj<this.tableTolerance.minuscule.length; ++jj) {
                        reponse_donnee_tolere = reponse_donnee_tolere.replace(new RegExp(this.tableTolerance.majuscule[jj], 'g'),
                                                                              this.tableTolerance.minuscule[jj]);
                        reponse_acceptee_tolere = reponse_acceptee_tolere.replace(new RegExp(this.tableTolerance.majuscule[jj], 'g'),
                                                                                  this.tableTolerance.minuscule[jj]);
                    }
                    if(reponse_donnee_tolere == reponse_acceptee_tolere) return reponse_acceptee;
                } else {
                    if(reponse_donnee.toLowerCase() == reponse_acceptee.toLowerCase()) return reponse_acceptee;
                }

                //tolerance accents
                if(est_explicite_utf8) {
                    for(var jj=0; jj<this.tableTolerance.sans_accents.length; ++jj) {
                        reponse_donnee_tolere = reponse_donnee_tolere.replace(new RegExp(this.tableTolerance.accents[jj], 'g'),
                                                                              this.tableTolerance.sans_accents[jj]);
                        reponse_acceptee_tolere = reponse_acceptee_tolere.replace(new RegExp(this.tableTolerance.accents[jj], 'g'),
                                                                                  this.tableTolerance.sans_accents[jj]);
                    }
                    if(reponse_donnee_tolere == reponse_acceptee_tolere) return reponse_acceptee;
                }
            }
            return false;
        };

        proto.points = function() {
            return [
                this.valide() ? 1 : 0,
                1 ];
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function () {
            parent_demarre.apply(this, arguments);
            this.parentEvent('correction').subscribe(this.corrige, null, this);
        };

        return constructeur;
    })(); 


    widgets.ImageCorrige = (function () {
        var constructeur = sofad.sousType( widgets.Element );
        var proto = constructeur.prototype;
        proto.requete = 'div.image-avant-apres';
        
        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            if(2 != this.noeuds.children().length) return;
            this.cacheCorrige();
            var that = this;
            that.parentEvent('correction').subscribe(this.afficheCorrige, null, this);
        };

        proto.cacheCorrige   = function() {
            this.noeuds.children().eq(0).show();
            this.noeuds.children().eq(1).hide();
        };

        proto.afficheCorrige = function() {
            this.noeuds.children().eq(1).show();
            this.noeuds.children().eq(0).hide();
        };

        proto.reset = function() {
            this.cacheCorrige();
        }

        return constructeur;
    })();


    //Outils sciences

    widgets.OutilScience = (function() {
        var constructeur = sofad.sousType( widgets.Element, function() {
            widgets.Element.apply(this, arguments);

            this.href         = this.noeuds.find( this.requeteHrefOutil ).text();
            this.blocCaptures = this.noeuds.find( this.requeteBlocCaptures );
            this.captures     = this.blocCaptures.find( this.requeteCaptures );
            
            this[this.estDemo() ? 'obtientBarreActionsDemo' : 'obtientBarreActions']();
        } );
        var proto     = constructeur.prototype;
        proto.requete = '.outil:not(.phyequations)';
        proto.requeteToutOutil = '.outil';
        proto.requeteBtnEnregistrer = '.btn-enregistrer, .btn-evaluation-app, .btn-transmettre, .btn-ouvre-outil, div.conteneur-images > div';
        proto.requeteBlocCaptures = "div.conteneur-images";
        proto.requeteCaptures   = "> div";
        proto.requeteHrefOutil  = "span.href";
        proto.estDemo           = function() { return !!this.noeuds.parents('.page-demo').length; };
        proto.estSurfenetre     = function() { return this.noeuds.hasClass('outil-en-surfenetre'); };



        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            this.changeComportement('ferme');

            var that = this;
            if(!this.estSurfenetre()) {
                jQuery(this.requeteToutOutil).bind('outilouvre.sofad', function() {
                    that.autreOutilOuvert(); 
                });
            }
            this.controleurs.boutonOuvreOutil.noeuds.add(this.captures).bind('click', function() {
                that.comportement.ouvre(); return false;
            });
            this.controleurs.boutonReinitOutil.noeuds.bind('click', function() {
                that.comportement.reinit(); return false;
            });
            this.controleurs.boutonEnregistreOutil.noeuds.bind('click', function() {
                that.comportement.enregistre(); return false;
            });
            this.controleurs.boutonEnregistreOutilDemo.noeuds.bind('click', function() { return false; });

            this.controleurs.boutonFermeOutil.noeuds.bind('click', function() {
                that.comportement.ferme(); return false;
            });
        };

        proto.obtientBarreActions = function() {
            var barre = this.controleurs.barreBtnOutil = new (this.factory('questionnaires.controleurs.BarreBtnOutil'))(this);
            this.controleurs.boutonEnregistreOutil     = new (this.factory('questionnaires.controleurs.BoutonEnregistreOutil'))(barre);
            this.controleurs.boutonOuvreOutil          = new (this.factory('questionnaires.controleurs.BoutonOuvreOutil'))(barre) ;
            this.controleurs.boutonFermeOutil          = new (this.factory('questionnaires.controleurs.BoutonFermeOutilSansEnr'))(barre) ;
            this.controleurs.boutonReinitOutil         = new (this.factory('questionnaires.controleurs.BoutonReinitOutil'))(barre);
            this.controleurs.boutonEnregistreOutilDemo      = {noeuds: jQuery('aucun')};
        };

        proto.obtientBarreActionsDemo = function() {
            var barre = this.controleurs.barreBtnOutil = new (this.factory('questionnaires.controleurs.BarreBtnOutil'))(this);
            this.controleurs.boutonEnregistreOutil     = {noeuds: jQuery('aucun')};
            this.controleurs.boutonOuvreOutil          = new (this.factory('questionnaires.controleurs.BoutonOuvreOutil'))(barre) ;
            this.controleurs.boutonFermeOutil          = new (this.factory('questionnaires.controleurs.BoutonFermeOutil'))(barre) ;
            this.controleurs.boutonReinitOutil         = {noeuds: jQuery('aucun')};
            this.controleurs.boutonEnregistreOutilDemo      = new (this.factory('questionnaires.controleurs.BoutonEnregistreOutilDemo'))(barre) ;
        };


        proto.ferme = function() { if(this.comportement.ferme) this.comportement.ferme(); };

        proto.autreOutilOuvert = function() {
            // abandonné pour l'instant
            // this.ferme();
        };

        proto.reinit = function(succes, erreur) {
            jQuery.post( sofad.chemins.reinitOutil,
                         {nom_outil: this.noeuds.attr('id')},
                         succes );
        } 

        proto.changeComportement = function(nom_comportement) {
            this.comportement = new (this.comportements[nom_comportement])(this);
        };

        proto.fenetreCadre = function() { 
            return this.controleurs.cadreOutil.noeuds[0].contentWindow;
        };




        proto.comportements = {
            ferme: (function() {
                var constructeur = function(outil) {
                    this.outil = outil;
                    var visibilite_btn_edition = this.outil.href.length ? "show" : "hide";
                    outil.controleurs.boutonOuvreOutil.noeuds.show();
                    outil.controleurs.boutonReinitOutil.noeuds[visibilite_btn_edition]();
                    outil.controleurs.boutonEnregistreOutil.noeuds.hide();
                    outil.controleurs.boutonEnregistreOutilDemo.noeuds.hide();
                    outil.controleurs.boutonFermeOutil.noeuds.hide();
                    outil.blocCaptures.show();
                    this.afficheCapture(0);

                    jQuery(outil.requeteBtnEnregistrer).each(function() {
                        jQuery(this).data('events',
                                          jQuery(this).data('sleepingEvents'));
                        jQuery(this).removeData('sleepingEvents');
                    }).unbind('click.outilscience');
                };

                constructeur.prototype = {
                    afficheCapture: function(ii) {
                        var that = this;
                        var chemin_image = that.outil.captures.eq(ii).find('img').attr('src');
                        if(!chemin_image) return;
                        if(-1 != (index_params = chemin_image.indexOf('?'))) chemin_image = chemin_image.substr(0, index_params);
                        chemin_image += '?timestamp=' + escape(Date());

                        jQuery.ajax( { url: sofad.chemins.imageOutilExiste,
                                       type: 'get',
                                       data: { location: location.pathname,
                                               chemin_rel_image: chemin_image },
                                       success: function(data, textStatus) {
                                           if(!that.outil.blocCaptures.is(':visible')) return;

                                           if(-1 != data.indexOf('existe=1')) {
                                               that.outil.captures.eq(ii).show();
                                               that.outil.captures.eq(ii).find('img').attr('src', chemin_image);
                                           } else {
                                               that.afficheCapture(1+ ii);
                                           }
                                       }
                                   } );


                    },

                    ouvre: function() {
                        this.outil.blocCaptures.hide();
                        this.outil.captures.hide();
                        this.outil.changeComportement('ouvert');
                    },

                    reinit: function() {
                        if(!confirm(sofad.lang("{L:questionnaires.confirme_reinit_outil}"))) return false;
                        var that = this;
                        this.outil.reinit( function() {
                            that.outil.captures.hide();
                            that.afficheCapture(0);
                        } );
                    },

                    toString: function() { return "Comportement outil «ferme»"; }
                };

                return constructeur;
            })(),


            ouvert: (function () {
                var constructeur =  function(outil) {
                    this.outil = outil;
                    if(!this.outil.estSurfenetre()) {
                        this.outil.noeuds.trigger('outilouvre.sofad');
                    }
                    outil.controleurs.boutonOuvreOutil.noeuds.hide();
                    outil.controleurs.boutonReinitOutil.noeuds.show();
                    outil.controleurs.boutonEnregistreOutil.noeuds.show();
                    outil.controleurs.boutonEnregistreOutilDemo.noeuds.show();
                    outil.controleurs.boutonFermeOutil.noeuds.show();

                    this.outil.controleurs.cadreOutil = new (this.outil.factory(
                        this.outil.estSurfenetre() ? 
                            'questionnaires.controleurs.CadreOutilSurfenetre' :
                            'questionnaires.controleurs.CadreOutil' ))
                    (this.outil); 

                    jQuery(outil.requeteBtnEnregistrer).each(function() {
                        jQuery(this).data('sleepingEvents', 
                                          jQuery(this).data('events'));
                        jQuery(this).removeData('events');
                    }).bind('click.outilscience', function() {
                        alert(sofad.lang("{L:questionnaires.outil_ouvert_sur_enregistre_form}"));
                        location.href = '#' + outil.noeuds.attr('id');
                        return false;
                    });

                    return this;
                };

                constructeur.prototype = {
                    enregistre: function() {
                        var outil = this.outil;
                        var fenetre_cadre = this.outil.fenetreCadre() ;
                        fenetre_cadre.termineEnregistrement = function() {
                            outil.comportement.ferme();
                        };
                        fenetre_cadre.enregistreImageOutil();
                        this.outil.changeComportement('attenteEnregistre');
                    },
                    
                    ferme: function() {
                        this.outil.controleurs.cadreOutil.noeuds.remove();
                        delete this.outil.controleurs.cadreOutil;
                        this.outil.changeComportement('ferme');
                        return false;
                    },

                    reinit: function() {
                        if(!confirm(sofad.lang("{L:questionnaires.confirme_reinit_outil}"))) return false;
                        var that = this;
                        this.outil.reinit(function() {
                            that.outil.controleurs.cadreOutil.noeuds.remove();
                            delete that.outil.controleurs.cadreOutil;
                            that.outil.controleurs.cadreOutil = new (that.outil.factory(
                                that.outil.estSurfenetre() ? 
                                    'questionnaires.controleurs.CadreOutilSurfenetre' :
                                    'questionnaires.controleurs.CadreOutil' ))
                            (that.outil); 
                        } );
                    },

                    toString: function() { return "Comportement outil «ouvert»"; }
                };

                return constructeur;
            })(),


            attenteEnregistre: (function() {
                var constructeur = function(outil) {
                    this.outil = outil;
                    outil.controleurs.boutonOuvreOutil.noeuds.hide();
                    outil.controleurs.boutonReinitOutil.noeuds.hide();
                    outil.controleurs.boutonEnregistreOutil.noeuds.hide();
                    outil.controleurs.boutonEnregistreOutilDemo.noeuds.hide();
                    outil.controleurs.boutonFermeOutil.noeuds.hide();
                    return this;
                };

                constructeur.prototype = {
                    ferme: function() {
                        this.outil.controleurs.cadreOutil.noeuds.remove();
                        delete this.outil.controleurs.cadreOutil;
                        this.outil.changeComportement('ferme');
                    },

                    toString: function() { return "Comportement outil «attenteEnregistrement»"; }
                };

                return constructeur;
            })()
        };


        return constructeur;
    })();

    //Editeur d'equations

    widgets.EditeurEquation = (function() {
        var constructeur = sofad.sousType(widgets.OutilScience);

        constructeur.prop('requete', 'div.outil.phyequations');

        constructeur.prop('htmlDialogueFermer', 
                          "<div id='dialogue-fermer-editeur-eq'>"+
                          "<p class='message'>{L:questionnaires.editeur_eq_confirmation_fermeture_message}</p>" +
                          "<label class='etiquette-case-cocher' for='souvenir-fermer-editeur'>"+
                          "<input type='checkbox' id='souvenir-fermer-editeur' name='souvenir-fermer-editeur'/>"+
                          "{L:questionnaires.editeur_eq_confirmation_sauter}</label>" +
                          "<p class='boutons'>"+
                          "<button id='oui-fermer-editeur'>{L:questionnaires.oui}</button> "+
                          "<button id='non-fermer-editeur'>{L:questionnaires.non}</button></p></div>");

        constructeur.method('obtientBarreActions', function() {
            var barre = this.controleurs.barreBtnOutil = new (this.factory('questionnaires.controleurs.BarreBtnOutil'))(this);
            this.controleurs.boutonOuvreOutil          = this.estSurfenetre() ?
                new (this.factory('questionnaires.controleurs.BoutonOuvreEditeurSurfenetre'))(barre) :
                new (this.factory('questionnaires.controleurs.BoutonOuvreEditeurEq'))(barre) ;
            this.controleurs.boutonEnregistreOutil     = {noeuds: jQuery('aucun')};
            this.controleurs.boutonFermeOutil          = this.estSurfenetre() ?
                {noeuds: jQuery('aucun')} :
                new (this.factory('questionnaires.controleurs.BoutonFermeOutil'))(barre);
            this.controleurs.boutonReinitOutil         = this.estSurfenetre() ?
                {noeuds: jQuery('aucun')} :
                new (this.factory('questionnaires.controleurs.BoutonReinitOutil'))(barre);
            this.controleurs.boutonEnregistreOutilDemo      = {noeuds: jQuery('aucun')};
        });

        constructeur.method('obtientBarreActionsDemo', function() {
            var barre = this.controleurs.barreBtnOutil = new (this.factory('questionnaires.controleurs.BarreBtnOutil'))(this);
            this.controleurs.boutonOuvreOutil          = this.estSurfenetre() ?
                new (this.factory('questionnaires.controleurs.BoutonOuvreEditeurSurfenetre'))(barre) :
                new (this.factory('questionnaires.controleurs.BoutonOuvreEditeurEq'))(barre) ;
            this.controleurs.boutonEnregistreOutil     = {noeuds: jQuery('aucun')};
            this.controleurs.boutonFermeOutil          = this.estSurfenetre() ?
                {noeuds: jQuery('aucun')} :
                new (this.factory('questionnaires.controleurs.BoutonFermeOutil'))(barre);
            this.controleurs.boutonReinitOutil         = {noeuds: jQuery('aucun')};
            this.controleurs.boutonEnregistreOutilDemo      = new (this.factory('questionnaires.controleurs.BoutonEnregistreOutilDemo'))(barre) ;
        });

        constructeur.method('autreOutilOuvert', function() {
            //if(this.comportement.ferme) this.comportement.ferme(true);
        });

        constructeur.method('dialogueFermer', function(fonction_fermer) {
            if(!('blockUI' in jQuery) ||
               'oui' == jQuery.cookie('saute-confirmation-sur-fermeture-editeur-equation')) {
                return fonction_fermer();
            }

            var contenant_editeur = $('body.est-surfenetre').length ? 
                $('body.est-surfenetre') :
                $($('iframe')[0].contentDocument);

            if(contenant_editeur.length) {
                $('object, embed', contenant_editeur).css({visibility: 'hidden'});
            }

            jQuery.blockUI({ message: sofad.lang(this.htmlDialogueFermer) });

            jQuery('#oui-fermer-editeur').bind('click', function() {
                jQuery.unblockUI();
                return fonction_fermer();
            });
            jQuery('#non-fermer-editeur').bind('click', function() {
                jQuery.unblockUI();
                if(contenant_editeur.length) {
                    $('object, embed', contenant_editeur).css({visibility: 'visible'});
                }
                return false;
            });
            jQuery('#souvenir-fermer-editeur').bind('click', function() {
                jQuery.cookie('saute-confirmation-sur-fermeture-editeur-equation',
                              (this.checked ? 'oui' : 'non'));
            });
        });

        constructeur.prop('comportements', sofad.objet(widgets.OutilScience.prop('comportements')));

        constructeur.prop('comportements').ouvert = sofad.sousType( constructeur.prop('comportements').ouvert );
        
        var parent_ferme = constructeur.prop('comportements').ouvert.prop('ferme');
        constructeur.prop('comportements').ouvert.method('ferme', function(sans_dialogue) {
            var that = this;
            var fonction_fermer = function() {
                parent_ferme.apply(that, arguments);
            }

            if(sans_dialogue || this.outil.estSurfenetre() || this.outil.estDemo()) return fonction_fermer();

            this.outil.dialogueFermer(fonction_fermer);
        });

        return constructeur;
    })();



    //CONTROLEURS Outils Sciences

    controleurs.BarreBtnOutil = (function() {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'p.boutons';
        constructeur.prototype.html      = '<p class="boutons"></p>';
        return constructeur;
    })();

    controleurs.BoutonOuvreOutil = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-ouvre-outil';
        constructeur.prototype.html      = '<button class="btn-ouvre-outil">{L:questionnaires.btn_outil_ouvrir}</button>';
        return constructeur;
    })();

    controleurs.BoutonOuvreEditeurEq = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-ouvre-outil';
        constructeur.prototype.html      = '<button class="btn-ouvre-outil">{L:questionnaires.btn_editeur_eq_ouvrir}</button>';
        return constructeur;
    })();


    controleurs.BoutonOuvreEditeurSurfenetre = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-ouvre-outil';
        constructeur.prototype.html      = '<button class="btn-ouvre-outil" title="{L:questionnaires.btn_editeur_eq_ouvrir}"> </button>';
        return constructeur;
    })();


    controleurs.BoutonReinitOutil = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-reinit-outil';
        constructeur.prototype.html      = '<button class="btn-reinit-outil">{L:questionnaires.btn_outil_reinit}</button>';
        return constructeur;
    })();

    controleurs.BoutonFermeOutilSansEnr = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-ferme-outil';
        constructeur.prototype.html      = '<button class="btn-ferme-outil">{L:questionnaires.btn_outil_fermer}</button>';
        return constructeur;
    })();
    
    controleurs.BoutonFermeOutil = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-ferme-outil';
        constructeur.prototype.html      = '<button class="btn-ferme-outil">{L:questionnaires.btn_outil_fermer_demo}</button>';
        return constructeur;
    })();

    controleurs.BoutonEnregistreOutil = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-enregistre-outil';
        constructeur.prototype.html      = '<button class="btn-enregistre-outil">{L:questionnaires.btn_outil_enregistrer}</button>';
        return constructeur;
    })();

    controleurs.BoutonEnregistreOutilDemo = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        constructeur.prototype.requete   = 'button.btn-enregistre-outil-demo';
        constructeur.prototype.html      = '<button class="btn-enregistre-outil-demo btn-inactif" title="{L:questionnaires.outil_enregis_inactif_demo}">{L:questionnaires.btn_outil_enregistrer_demo}</button>';
        return constructeur;
    })();

    //Outil ouvert en iframe

    controleurs.CadreOutil = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto = constructeur.prototype;
        proto.requete = "iframe";
        proto.html    = '<iframe src="{0}" width="725" height="500" scrolling="no" frameborder="0"></iframe>';
        proto.insere  = function(widget_parent) {
            widget_parent.controleurs.barreBtnOutil.noeuds.prepend( sofad.format(this.html, widget_parent.href) );
        };
        return constructeur;
    })();

    //Outil ouvert en surfenetre

    controleurs.CadreOutilSurfenetre = (function () {
        var constructeur = sofad.sousType(controleurs.CadreOutil);
        var proto = constructeur.prototype;
        proto.requete = 'div.cadre-outil-surfenetre-en-cours';
        proto.html    = '<div class="cadre-outil-surfenetre-en-cours"><button class="focus-surfenetre">{L:questionnaires.btn_outil_focus_surfenetre}</button></div>';
        proto.insere  = function(widget_parent) {
            widget_parent.noeuds.prepend(this.renduHtml());

            this.surfenetre = window.open(widget_parent.href,
                                          'outil'+widget_parent.noeuds.attr('id').replace('-', ''),
                                          'height=540,width=750');


            widget_parent.noeuds.data('sofad.surfenetreOutil', 
                                      this.surfenetre);
            this.surfenetre.focus();

            //Surveillance du popup.
            // - S'il se ferme, on declenche la fermeture de l'outil
            // - Si le bouton reinit est marque clique, on declenche la reinit.
            var surfenetre = this.surfenetre;
            var interval_id = setInterval(function() {
                if(surfenetre.closed) {
                    clearInterval(interval_id);
                    widget_parent.ferme();
                    return;
                }

                if(!surfenetre.closed && jQuery(surfenetre.document).find('button.btn-reinit.est-clique').length) {
                    widget_parent.reinit();
                    surfenetre.location.reload();
                }
            }, 200);

            widget_parent.noeuds.find('button.focus-surfenetre').bind('click', function() {
                surfenetre.focus();
                return false;
            });

            return false;
        };
        return constructeur;
    })();



    widgets.BoutonEnregistrerAncre = (function() {
        var constructeur = sofad.sousType(widgets.Element);
        var proto = constructeur.prototype;
        proto.requete      = '.btn-enregistrer';
        proto.requeteAncre = 'a[@id^=enonce-]';
       
        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            this.ancre = this.widgetParent.noeuds.find( this.requeteAncre );
            if(!this.ancre.length) return;
            var that = this;
            this.noeuds.click(function() {
                var formulaire = that.noeuds.parents().filter('form');
                if(!formulaire.length) return;

                var index_ancre;
                var href = formulaire.attr('action');
                if(-1 !== (index_ancre = href.indexOf('#'))) {
                    href = href.substr(0, index_ancre);
                }

                formulaire.attr('action', href + '#' + that.ancre.attr('id'));
            });
        };

        return constructeur;
    })();

    //Questions et questionnaires

    widgets.Question = (function () {
        var constructeur = function() {
            widgets.Element.apply(this, arguments);

            this._estCorrige = false;
            this.events.correction = new YAHOO.util.CustomEvent('correction');
            var that = this;
            this.events.correction.subscribe(this.corrige, null, this);

            //Si un élément de la question s'inscrit à l'événement de correction après que
            //ce dernier ait été déclenché, provoquer immédiatement l'exécution de cette nouvelle inscription.
            this.events.correction.subscribeEvent.subscribe(function() {
                var params_event = arguments[1];
                if(that._estCorrige) {
                    params_event[0].apply(params_event[2], [params_event[1]]);
                }
            });

            this.blocCorrige       = this.noeuds.find(this.requeteCorrige).hide();
            this.blocCorrigeReussi = this.noeuds.find(this.requeteCorrigeReussi).hide();
            this.blocCorrigeEchoue = this.noeuds.find(this.requeteCorrigeEchoue).hide();
        };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.requete              = '.question';
        proto.requeteCorrige       = '.corrige';
        proto.requeteCorrigeReussi = '.corrige .reussi';
        proto.requeteCorrigeEchoue = '.corrige .echoue';
        proto.typesEnfants = {qcmru:        "questionnaires.widgets.Qcmru",
                              qcmrm:        "questionnaires.widgets.Qcmrm",
                              texteCorrige: "questionnaires.widgets.TexteCorrige",
                              imageCorrige: "questionnaires.widgets.ImageCorrige",
                              outilScience: "questionnaires.widgets.OutilScience",
                              editeurEquation: "questionnaires.widgets.EditeurEquation",
                              boutonEnregistrerAncre: "questionnaires.widgets.BoutonEnregistrerAncre"
                             };

        proto.estCorrige = function() { return this._estCorrige; };

        proto.corrige = function() {
            this._estCorrige = true;
            this.blocCorrige.toggle();
            if(!this.blocCorrige.length) { }
            else if(this.blocCorrige.is(':visible')) {
                this.controleurs.boutonCorrige.noeuds.text( sofad.lang('{L:questionnaires.btn_corrige_masquer}') );
            } else {
                this.controleurs.boutonCorrige.noeuds.text( sofad.lang('{L:questionnaires.btn_corrige}') );
            }

            //SANS UNIT TESTS
            if(this.blocCorrige.is(':visible')) {
                var pointage = this.points();
                if(pointage[0] == pointage[1]) {
                    this.blocCorrigeReussi.show();
                    this.blocCorrigeEchoue.hide();
                } else {
                    this.blocCorrigeReussi.hide();
                    this.blocCorrigeEchoue.show();
                }
            }
        };

        proto.cliqueBoutonCorrige = function () { this.events.correction.fire(); };

        var parent_reset = proto.reset;
        proto.reset = function() {
            this._estCorrige = false;
            this.blocCorrige.hide();
            parent_reset.apply(this, arguments);
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var question = this;

            //Si des subscribers sont "unsubscribed", la liste des subscribers
            //ne retrecit pas, elle contient plutot un null a la pace du subscriber.
            //On ne peut donc pas verifier qu'on a des subscribers simplement en verifiant la longueur de la liste.
            var nb_vrais = (function () {
                function recur (ls, val) { 
                    return (ls.length) ?
                    recur(ls.slice(1), val + (ls[0] ? 1: 0)) :
                    val;
                }
                return function(ls) { return recur(ls, 0); };
            })();

            if( (this.blocCorrige.length || nb_vrais(this.events.correction.subscribers) > 1) && 
                !this.declencheursEvents.correction) {
                this.controleurs.boutonCorrige = new (this.factory('questionnaires.controleurs.BoutonCorrige'))( this );
                this.controleurs.boutonCorrige.noeuds.bind('click', function() {
                    question.cliqueBoutonCorrige();
                    return false;
                } );

            }
        };

        return constructeur;
    })();


    widgets.Questionnaire = (function() {
        var constructeur = function() { 
            widgets.Element.apply(this, arguments);
            if(this.enfantsRequis && 0 == this.enfants.length) {
                throw sofad.ExceptionAucunSousWidget(this);
            }
        };
        var proto = sofad.objet( widgets.Element.prototype );
        constructeur.prototype = proto;

        proto.enfantsRequis = true;
        proto.requete       = '.questionnaire';
        proto.typesEnfants = { question: "questionnaires.widgets.Question" };

        return constructeur;
    })();


    widgets.PageImprimable = (function() {
        var constructeur = function() {
            sofad.widgets.Widget.apply(this, arguments); 
            this.controleurs.boutonImprimer = new (this.factory('questionnaires.controleurs.BoutonImprimer'))(this);
        };
        var proto = sofad.objet( sofad.widgets.Widget.prototype );
        constructeur.prototype = proto;

        proto.requete            = 'body';
        proto.requeteCasesRadios = 'input:radio, input:checkbox';
        proto.requeteChampsTexte = 'input:text';
        proto.requeteZonesTexte  = 'textarea';
        proto.imagesCrochet      = { coche:    '<img src="{0}cases-cocher/bon.png" alt="{L:questionnaires.page_imprimable_coche}"/>',
                                     nonCoche: ' ' };
        proto.htmlChampTexte     = "<span class='text-imprime'>{0}</span>";
        proto.htmlZoneTexte      = "<div class='textarea-imprime'>{0}</div>";
        proto.print              = function() { print(); };
        
        var parent_demarre = proto.demarre;
        proto.demarre = function () {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.controleurs.boutonImprimer.noeuds.click(function() { that.imprime(); return false; });
        };

        proto.imprime = function () {
            var cases_radios = this.noeuds.find( this.requeteCasesRadios );
            for(var ii=0; ii<cases_radios.length; ++ii) {
                var elm = jQuery(cases_radios[ii]);
                if(!elm.is(':visible')) continue;
                elm.hide();
                elm.after( sofad.lang(sofad.format( this.imagesCrochet[ elm.is(':checked') ? 'coche' : 'nonCoche' ],
                                                    sofad.chemins.images ) ) );
            }

            var champs_texte = this.noeuds.find( this.requeteChampsTexte );
            for(var ii=0; ii<champs_texte.length; ++ii) {
                var elm = jQuery(champs_texte[ii]);
                if(!elm.is(':visible')) continue;
                elm.hide();
                elm.after( sofad.format(this.htmlChampTexte, elm[0].value) );
            }

            var zones_texte = this.noeuds.find( this.requeteZonesTexte );
            for(var ii=0; ii<zones_texte.length; ++ii) {
                var elm = jQuery(zones_texte[ii]);
                if(!elm.is(':visible')) continue;
                elm.hide();
                elm.after( sofad.format( this.htmlZoneTexte, elm[0].value) );
            }


            this.print();
        };


        return constructeur;
    })();
    
    //PASSIF, l'extension affichePointage décide s'il est affiché
    widgets.RenforcementResultat = (function() {
        var constructeur = sofad.sousType(widgets.Element);
        var proto = constructeur.prototype;
        proto.requete     = '.renforcement-resultat';
        proto.regExpPourc = /^pourc-([0-9]+)$/;

        proto.pourcentageMinimal = function() {
            var classes = this.noeuds.attr('class').split(' ');
            var matches;
            for(var ii=0; ii<classes.length; ++ii) {
                if(matches = classes[ii].match(this.regExpPourc)) {
                    return Number(matches[1])/100;
                }
            }
        };

        return constructeur;
    })();



    for(var nom_widget in widgets) widgets[nom_widget].prototype.nomWidget = nom_widget;


    //EXTENSIONS

    extensions.radioCorrectionImmediate = function(original) {
        var constructeur = function() { original.apply(this, arguments); };
        var proto = sofad.objet( original.prototype );
        constructeur.prototype = proto;
        
        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.declencheParentEvent('correction', 
                                      function(fn) {
                                          that.noeuds.bind('click', function() {
                                              //L'effet par defaut du clic sur un bouton radio est de le cocher.
                                              //Cet effet ne semble cependant pas toujours se realiser avant
                                              //Les effets personnalises. Je simule donc ici l'effet par defaut avant
                                              //L'execution des effets personnalises.
                                              this.checked = true; 
                                              fn();
                                          });
                                      });
        };

        return constructeur;
    };


    extensions.radioFeedback = function(original) {
        var constructeur = function() { 
            original.apply(this, arguments); 
            this.feedback = jQuery( sofad.format( this.requeteFeedback, this.noeuds.attr('id')) ).hide();
        };
        var proto = sofad.objet( original.prototype );
        constructeur.prototype = proto;

        proto.requeteFeedback = "#choix-feedback-{0}";

        var parent_corrige = proto.corrige;
        proto.corrige = function() {
            if(this._estCorrige) return;
            if(this.noeuds[0].checked) this.feedback.show();
            parent_corrige.apply(this, arguments);
        };

        return constructeur;
    };


    extensions.radioCorrigeEnregistre = function(original) {
        var constructeur = sofad.sousType( original );
        
        var parent_demarre = constructeur.prototype.demarre;
        constructeur.prototype.demarre = function() {
            parent_demarre.apply(this, arguments);
            if(this.noeuds.is(':checked')) {
                //Creer un déclencheur de correction activé immédiatement
                //permet d'indiquer à la question qu'elle n'a pas besoin de 
                //bouton Corrigé, car la correction est déclenchée ailleurs.
                var declencheur_immediat;
                this.declencheParentEvent('correction', function(fn) {
                    declencheur_immediat = function() { fn(); };
                });
                declencheur_immediat();
            }
        };

        return constructeur;
    };


    extensions.texteCorrigeEnregistre = function(original) {
        var constructeur = sofad.sousType( original );
        
        var parent_demarre = constructeur.prototype.demarre;
        constructeur.prototype.demarre = function() {
            parent_demarre.apply(this, arguments);
            if('' != this.noeuds[0].value) {
                //Creer un déclencheur de correction activé immédiatement
                //permet d'indiquer à la question qu'elle n'a pas besoin de 
                //bouton Corrigé, car la correction est déclenchée ailleurs.
                var declencheur_immediat;
                this.declencheParentEvent('correction', function(fn) {
                    declencheur_immediat = function() { fn(); };
                });
                declencheur_immediat();
            }
        };

        return constructeur;
    };

    
    extensions.affichePointageImmediat = function (original, dom_event) {
        var constructeur = sofad.sousType( original );
        var proto = constructeur.prototype;

        if(!dom_event) dom_event = 'click';

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.declencheParentEvent('affichePointage',
                                      function(fn) { 
                                          that.noeuds.bind(dom_event, function() {
                                              if('radio' == this.getAttribute('type')) {
                                                  //L'effet par defaut du clic sur un bouton radio est de le cocher.
                                                  //Cet effet ne semble cependant pas toujours se realiser avant
                                                  //Les effets personnalises. Je simule donc ici l'effet par defaut avant
                                                  //L'execution des effets personnalises.
                                                  this.checked = true; 
                                              }
                                              fn();
                                          });
                                      });
            
        };

        return constructeur;
    };


    extensions.affichagePointage = function(original) {
        var constructeur = sofad.sousType(original, function() {
            original.apply(this, arguments);
            this.events.affichePointage = new YAHOO.util.CustomEvent('affichePointage');
            this.controleurs.afficheurPointage = new (this.factory('questionnaires.controleurs.AfficheurPointage'))(this);

        });
        var proto = constructeur.prototype;
        proto.typesEnfants = sofad.objet(proto.typesEnfants);
        proto.typesEnfants.renforcementResultat = 'questionnaires.widgets.RenforcementResultat';


        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            this.events.affichePointage.subscribe(this.affichePointage, null, this);
            this.affichePointage();
        };

        constructeur.prototype.affichePointage = function() {
            var points        = this.points();
            var pourc_obtenu  = points[1] ? points[0] / points[1] : 0;
            this.controleurs.afficheurPointage.affiche( points );

            var minimum_satisfait  = 0;
            var resultat_satisfait;
            for(var ii=0; ii<this.enfants.length; ++ii) {
                var renforcement_resultat = this.enfants[ii];
                if(!('pourcentageMinimal' in renforcement_resultat)) continue;

                var pourc_minimal = renforcement_resultat.pourcentageMinimal();
                if(pourc_minimal >= minimum_satisfait &&
                   pourc_obtenu  >= pourc_minimal) {
                    minimum_satisfait  = pourc_minimal;
                    resultat_satisfait = this.enfants[ii];
                }

                renforcement_resultat.noeuds.hide();
            }

            if(resultat_satisfait) {
                resultat_satisfait.noeuds.show();
            }
        };

        return constructeur;
    };


    extensions.enregistreAjax = function(original) {
        var constructeur = sofad.sousType(original, function() {
            original.apply(this, arguments);
            this.controleurs.boutonEnregistreAjax = new (this.factory('questionnaires.controleurs.BoutonEnregistreAjax'))(this);
            this.enEnvoi = false;
        });
        var proto = constructeur.prototype;

        proto.requeteFormulaire      = 'form:not(#pagenav)';
        proto.htmlImageChargement    = '<img class="ajax-loader" alt="{L:questionnaires.enregistre_ajax_encours}" src="{0}ajax-loader.gif" />';
        proto.requeteImageChargement = 'img.ajax-loader';
        proto.messageEnvoieReussi    = '{L:questionnaires.enregistre_ajax_reussi}';
        proto.messageEnvoieEchoue    = '{L:questionnaires.enregistre_ajax_echoue}';

        proto.alert = function(msg) { alert(msg); };

        proto.envoieFormulaire = function(btn) {
            if(this.enEnvoi) return;
            var formulaire = this.noeuds.filter(this.requeteFormulaire);
            if(!formulaire.length) {
                formulaire = this.noeuds.find(this.requeteFormulaire);
            }
            if(!formulaire.length) {
                formulaire = this.noeuds.parents().filter(this.requeteFormulaire);
            }
            if(!formulaire.length) return;

            var valeurs_post = sofad.valeursFormulaire(formulaire);

            this.estEnvoiCommence(btn);
            var that = this;
            
            var href = 'http://' + window.location.host + formulaire.attr('action');
            var index_ancre;
            if(-1 !== (index_ancre = href.indexOf('#'))) {
                href = href.substr(0, index_ancre);
            }

            jQuery.ajax({
                type: formulaire.attr('method'),
                url:  href,
                data: valeurs_post,
                success: function() { that.estEnvoiReussi(btn); },
                error: function() { that.estEnvoiEchoue(btn); }
            });
        };

        proto.estEnvoiCommence = function (btn) {
            this.enEnvoi = true;
            jQuery(btn).before( 
                sofad.lang( 
                    sofad.format(this.htmlImageChargement, sofad.chemins.images)
                )
            );
        };

        proto.estEnvoiReussi = function (btn) {
            this.enEnvoi = false;
            if(this.requeteImageChargement) jQuery(btn).prevAll( this.requeteImageChargement ).remove();
            if(this.messageEnvoieReussi)    this.alert( sofad.lang(this.messageEnvoieReussi) );
        };

        proto.estEnvoiEchoue = function (btn) {
            this.enEnvoi = false;
            if(this.requeteImageChargement) jQuery(btn).prevAll( this.requeteImageChargement ).remove();
            if(this.messageEnvoieEchoue)    this.alert( sofad.lang(this.messageEnvoieEchoue) );
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.controleurs.boutonEnregistreAjax.noeuds.bind('click', function() {
                that.envoieFormulaire(this);
                if(jQuery(this).is('button, input:button, input:submit')) return false;
            });
            this.controleurs.boutonEnregistreAjax.noeuds.filter('input:text').bind('change', function() {
                that.envoieFormulaire();
            });
        }

        return constructeur;
    };
    

    //CONTROLEURS

    controleurs.BoutonCorrige = (function () {
        var constructeur = function() { sofad.controleurs.Controleur.apply(this, arguments); };
        var proto = sofad.objet( sofad.controleurs.Controleur.prototype );
        constructeur.prototype = proto;

        proto.requete   = 'button.btn-corrige';
        proto.html      = '<p><button class="btn-corrige">{L:questionnaires.btn_corrige}</button></p>';
        proto.insere = function(widget_parent) { 
            if(widget_parent.blocCorrige && widget_parent.blocCorrige.length) {
                widget_parent.blocCorrige.before(this.renduHtml());
            } else {
                widget_parent.noeuds.append(this.renduHtml());
            }
        };

        return constructeur;
    })();


    controleurs.BoutonEnregistreAjax = (function() {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto        = constructeur.prototype;

        proto.requete    = 'button.btn-enregistrer-ajax';
        proto.html       = '<p><button class="btn-enregistrer-ajax">{L:questionnaires.btn_ajax_enregistrer}</button></p>';
        
        return constructeur;
    })();

    controleurs.BoutonImprimer = (function () {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto = constructeur.prototype;

        proto.requete   = 'button.btn-imprimer';
        proto.html      = '<p class="btn-imprimer"><button class="btn-imprimer">{L:questionnaires.btn_imprimer}</button></p>';

        return constructeur;
    })();

    controleurs.AfficheurPointage = (function () {
        var constructeur = sofad.sousType( sofad.controleurs.Controleur );
        constructeur.prototype.requete            = 'p.afficheur-pointage';
        constructeur.prototype.requeteNote        = 'input:text';
        constructeur.prototype.requetePonderation = 'span.ponderation';
        constructeur.prototype.html      = '<p class="afficheur-pointage"><input type="text" name="afficheur-pointage"/> / <span class="ponderation"></span></p>';
        constructeur.prototype.insere    = function(widget_parent) { widget_parent.noeuds.prepend(this.html); };
        constructeur.prototype.affiche   = function(pointage) { 
            this.noeuds.find( this.requeteNote )[0].value = Math.round(pointage[0]*10000)/10000; 
            this.noeuds.find( this.requetePonderation ).text( pointage[1] );
        };
        return constructeur;
    })();

    controleurs.BonneReponse = (function() {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto = constructeur.prototype;
        proto.requete = "~ .bonne-reponse:first";
        proto.html    = '<strong class="bonne-reponse">{0}</strong>';
        proto.insere = function(widget_parent) { 
            widget_parent.noeuds.after( this.renduHtml() ); 
        };
        return constructeur;
    })();

    controleurs.MauvaiseReponse = (function() {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto = constructeur.prototype;
        proto.requete = "~ .mauvaise-reponse:first, ~ .mauvaise-reponse:first + .espacement-mauvaise-reponse";
        proto.html    = '<strong class="mauvaise-reponse">{0}</strong><span class="espacement-mauvaise-reponse">&nbsp;</span>';
        proto.insere = function(widget_parent) { 
            if(!jQuery.trim(this.paramsRendu[0])) this.paramsRendu[0] = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
            var rendu_html = this.renduHtml();
            widget_parent.noeuds.after( rendu_html ); 
        };
        return constructeur;
    })();

    controleurs.CorrigeReponse = (function() {
        var constructeur = sofad.sousType(sofad.controleurs.Controleur);
        var proto = constructeur.prototype;
        proto.requete = "~ .corrige-reponse:first";
        proto.html    = '<strong class="corrige-reponse">{0}</strong>';
        proto.insere = function(widget_parent) {
            widget_parent.noeuds.after( this.renduHtml() );
        };
        return constructeur;
    })();


}) ();


//Surfenetres
(function () {
    sofad.surfenetres = {};
    var widgets     = sofad.surfenetres.widgets     = {surfenetres: {}};
    var controleurs = sofad.surfenetres.controleurs = {};
    var extensions  = sofad.surfenetres.extensions  = {};
    

    widgets.surfenetres.Surfenetre = (function () {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        var proto        = constructeur.prototype;
        proto.parametres = "width=700,height=760,scrollbars=yes,resizable=yes,status=yes,toolbar=yes,location=yes,menubar=yes"; 
        proto.regExpClassLargeur = new RegExp('^largeur-(\\d+)$');
        proto.regExpClassHauteur = new RegExp('^hauteur-(\\d+)$');
        proto.regExpParamLargeur = new RegExp('(width=)\\d+');
        proto.regExpParamHauteur = new RegExp('(height=)\\d+');
        proto.open       = function () { return window.open(arguments[0],
                                                            arguments[1],
                                                            arguments[2]); };
        proto.requete    = 'a.popup';
        proto.nomFenetre = 'surfenetre';
        
        proto.adresse = function() {
            if(jQuery('base').length && 0 !== this.noeuds.attr('href').indexOf('http://')) {
                return jQuery('base').attr("href") + this.noeuds.attr('href');
            }
            return this.noeuds.attr('href');
        };

        proto.dimensionsPersonnalises = function() {
            var dimensions = {};
            if(!this.noeuds.attr('class')) return dimensions;

            var classes = this.noeuds.attr('class').split(' ');

            for(var ii=0; ii< classes.length; ++ii) {
                var largeur = classes[ii].match( this.regExpClassLargeur );
                var hauteur = classes[ii].match( this.regExpClassHauteur );
                if(largeur && 2 == largeur.length) dimensions.largeur = largeur[1];
                if(hauteur && 2 == hauteur.length) dimensions.hauteur = hauteur[1];
            }

            return dimensions;
        };

        proto.parseParams = function(chaine) {
            var params = {};
            var chaines_param = chaine.split(",");
            for(var ii=0; ii<chaines_param.length; ++ii) {
                var operands = chaines_param[ii].split("=");
                if(2 != operands.length) continue;
                params[ jQuery.trim(operands[0]) ] = jQuery.trim(operands[1]);
            }
            return params;
        };

        proto.ouvre = function () {
            var est_externe = 0 === this.adresse().indexOf('http://') && 
                              0 !== this.adresse().indexOf('http://' + location.host);
            
            var w = this.open(this.adresse(),
                              est_externe ? '_blank' : this.nomFenetre,
                              this.parametres);
            w.focus();
            
            if(!est_externe) {
                var params = this.parseParams(this.parametres);
                if(params.width && params.height) {
                    w.resizeTo(params.width, params.height);
                }
            }
            
            return false;
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            
            var dim_perso = this.dimensionsPersonnalises();
            if("largeur" in dim_perso) {
                this.parametres = this.parametres.replace(this.regExpParamLargeur, "$1"+dim_perso.largeur);
            }
            if("hauteur" in dim_perso) {
                this.parametres = this.parametres.replace(this.regExpParamHauteur, "$1"+dim_perso.hauteur);
            }

            var that = this;
            this.noeuds.bind('click', function() {
                return that.ouvre();
            });
        };

        return constructeur;
    })();

    widgets.surfenetres.Lexique = (function() {
        var constructeur = sofad.sousType( widgets.surfenetres.Surfenetre );
        constructeur.prototype.requete = function(contexte) {
            return jQuery('a.lexique', contexte).filter(function() {
                return 0 == jQuery(this).parents().filter('.type-lexique').length;
            });
        };
        constructeur.prototype.nomFenetre = 'lexique';
        return constructeur;
    })();

    widgets.surfenetres.Aide = (function() {
        var constructeur = sofad.sousType( widgets.surfenetres.Surfenetre );
        constructeur.prototype.requete    = 'a.aide';
        constructeur.prototype.nomFenetre = 'aide';
        constructeur.prototype.parametres = "width=800,height=500,scrollbars=yes,resizable=yes,status=yes,toolbar=no,location=no,menubar=no"; 
        return constructeur;
    })();

    widgets.surfenetres.FCourriel = (function() {
        var constructeur = sofad.sousType( widgets.surfenetres.Surfenetre );
        constructeur.prototype.requete    = 'a.fcourriel';
        constructeur.prototype.nomFenetre = 'fcourriel';
        return constructeur;
    })();

    widgets.surfenetres.Externe = (function() {
        var constructeur = sofad.sousType( widgets.surfenetres.Surfenetre );
        constructeur.prototype.requete    = 'a:not(.meme-fenetre)[href^="http://"]';
        constructeur.prototype.nomFenetre = 'externe';

        var parent_demarre = constructeur.prototype.demarre;
        constructeur.prototype.demarre = function() {
            var href           = this.noeuds.attr("href");
            var href_sans_http = href.substring("http://".length);
            if(location.host != href_sans_http.substring(0, href_sans_http.indexOf('/'))) {
                parent_demarre.apply(this, arguments);
            }
        };

        return constructeur;
    })();

    widgets.surfenetres.Zoom = (function() {
        var parent_constructeur = widgets.surfenetres.Surfenetre;
        var constructeur = sofad.sousType( parent_constructeur, function() {
            parent_constructeur.apply(this, arguments);

            this.controleurs.loupe = new (this.factory('surfenetres.controleurs.Loupe'))(this);

            this.personnaliseParams();
        } );

        var proto = constructeur.prototype;
        proto.requete    = 'div[class^="zoom-"], div[class*=" zoom-"]';
        proto.nomFenetre = 'zoom';
        proto.parametres = "width={0},height={1},scrollbars=auto,resizable=yes,status=no,toolbar=no,location=no,menubar=no"; 
        proto.regExpZoom = new RegExp("^zoom-(\\d+)-(\\d+)$");

        proto.dimensionsZoom = function() {
            var classes = this.noeuds.attr('class').split(' ');
            for(var ii=0; ii< classes.length; ++ii) {
                var resultat = classes[ii].match( this.regExpZoom );
                if(!resultat || 3 != resultat.length) continue;
                return { largeur: resultat[1],
                         hauteur: resultat[2] };
            }
        };

        proto.personnaliseParams = function() {
            var dimensions = this.dimensionsZoom();
            if(!dimensions) {
                throw "La classe «"+ this.noeuds.att("class") +"» de l'image zoomable contient une erreur. La hauteur ou la largeur n'ont pu être trouvé. " +
                    "Le format doit être " + this.regExpZoom.toString();
            }
            
            this.parametres = sofad.format(this.parametres, dimensions.largeur, dimensions.hauteur);
        };

        proto.adresseImage = function() {

            var that = this;
            var dom_img = this.noeuds.find('img').filter(function() {
                if(!that.controleurs.loupe.noeuds.find('img').length) return true;
                return this !== that.controleurs.loupe.noeuds.find('img')[0];
            });
            if(dom_img.length) {
                return dom_img.attr('src');
            } 

            dom_img = this.noeuds.find('object');
            if(dom_img.length) {
                return dom_img.attr('data') || dom_img.attr('movie'); //IE FIX
            }

            throw "Aucune image trouvée dans un bloc à image zoomable";            
        };

        proto.adresse = function() {
            var dossier    = sofad.info.base_url; //location.href.substr(0, location.href.lastIndexOf('/')+1);
            var dimensions = this.dimensionsZoom();
            
            var adresse = sofad.chemins.zoom +
                "?src=" + dossier + this.adresseImage() + 
                "&largeur=" + dimensions.largeur +
                "&hauteur=" + dimensions.hauteur ;

            if(this.noeuds && this.noeuds.find("div.src-titre").length) {
                adresse += "&titre=" + encodeURIComponent(this.noeuds.find("div.src-titre").text());
            }

            return adresse;
        };

        var parent_demarre = sofad.widgets.Widget.prototype.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;

            this.nomFenetre = this.adresseImage().replace(/[\/\.]/g, "");
            
            var classes = this.noeuds.attr("class").split(" ");
            var regexp_largeur = new RegExp("^largeur-(\\d+)$");
            for(var ii=0; ii<classes.length; ++ii) {
                var resultat = classes[ii].match( regexp_largeur );
                if(!resultat || 2 != resultat.length) continue;
                this.noeuds.width( resultat[1] + "px" );
            }

            var fn_ouvre = function() { return that.ouvre(); };

            this.controleurs.loupe.noeuds.bind('click', fn_ouvre);
            
            if(this.noeuds.find("img").length) {
                this.noeuds.find("img")
                .css("cursor", "pointer")
                .bind("click", fn_ouvre);
            }
        };

        return constructeur;
    })();

    //BUGFIX Dans ie6, un redirect HTTP vers une uri avec ancre provoque toutes sortes de bobo.
    //       Ce widget permet de reconnaître un paramètre get «ancre» et de le traiter comme substitut d'une ancre.
    widgets.surfenetres.Ancre = (function() {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        var proto        = constructeur.prototype;
        proto.requete = 'html';
        
        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var params_get = sofad.obtientGET();
            if(!('ancre' in params_get)) return;
            
            var index_ancre;
            var href = location.href;
            if(-1 !== (index_ancre = href.indexOf('#'))) {
                href = href.substr(0, index_ancre);
            }

            location.href = href + '#' + params_get.ancre;
        };

        return constructeur;
    })();

    widgets.surfenetres.Console = (function() {
        var parent_constructeur = sofad.widgets.Widget;
        var constructeur = sofad.sousType( parent_constructeur, function() {
            parent_constructeur.apply(this, arguments);
            this.cheminSon         = this.noeuds.find('a').hide().attr('href');
            this.controleurs.console = new (this.factory('surfenetres.controleurs.ConsoleSonoreFlash')) (this, [this.cheminConsole(), this.cheminSon]);
            this.intervalActions   = false;
            this.pileActions       = [];
        } );
        var proto = constructeur.prototype;
        proto.requete        = ".console";
        proto.delaiInterval  = 250;
        proto.consolePersonnalise = false;
        
        proto.cheminConsole = function() {
            if(this.consolePersonnalise) return this.consolePersonnalise;
            return sofad.chemins.images + "console.swf";
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.intervalActions = setInterval(function() {
                that.boucleInterval();
            }, this.delaiInterval);
        };

        proto.boucleInterval = function() {
            var obj_console = this.controleurs.console.noeuds[0];
            if("undefined" != typeof obj_console.PercentLoaded && 
               100 == obj_console.PercentLoaded()) {
                obj_console.Play();

                for(var ii=0; ii < this.pileActions.length; ++ii) {
                    var action = this.pileActions[ii];
                    obj_console[action]();
                }
            }

        };

        proto.appelConsole = function(nom_fonction) {
            //methodes disponibles : jsJoue, jsPause, jsArrete
            this.pileActions[ this.pileActions.length ] = nom_fonction;
        };

        return constructeur;
    })();

    widgets.ChargeurImage = (function() {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        var proto = constructeur.prototype;
        proto.requete    = ".chargeur-images";
        proto.htmlImg    = '<img src="{0}" alt="" width="{1}" height="{2}"/>';
        proto.htmlObject = '<object id="image-1" width="{1}" height="{2}" data="{0}" type="application/x-shockwave-flash"> \
                              <param valuetype="data" value="{0}" name="movie"/> \
                              <param valuetype="data" value="opaque" name="wmode"/> \
                            </object>';
        proto.regExpSwf  = new RegExp('\\.swf$', 'i');

        proto.redimensionneFenetre = function(params_get) {
            var largeur_image = 'largeur' in params_get ? params_get.largeur : jQuery(window).width() ;
            var hauteur_image = 'hauteur' in params_get ? params_get.hauteur : jQuery(window).height() ;

            //on corrige la dimension de la fenetre en ajoutant aux dimension de la fenetre
            //l'espace occupe par  la barre d'adresse, d'etat, etc.
            setTimeout(function() {
                //difference entre la dimension voulue et la dimension obtenue
                var diff_largeur  = largeur_image - jQuery(window).width();
                var diff_hauteur  = hauteur_image - jQuery(window).height();

                if(diff_largeur + diff_hauteur) window.resizeBy(diff_largeur, diff_hauteur);
            }, 250);
            
            return {largeur: largeur_image, hauteur: hauteur_image};
        }; 

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var params_get = sofad.obtientGET();
            if(!('src' in params_get)) return;
            var src   = params_get.src;
            
            var dim_img = this.redimensionneFenetre(params_get);

            if(src.match(this.regExpSwf)) {
                var html = this.htmlObject;
            } else {
                var html = this.htmlImg;
            }

            html = sofad.format(html, src, dim_img.largeur, dim_img.hauteur); 
            this.noeuds.append(html);

            if("titre" in params_get) {
                document.title = params_get.titre;
            } else if(opener && opener.document) {
                document.title = opener.document.title;
            }

            
        };

        return constructeur;
    })();

    
    widgets.surfenetres.AlerteChargement = (function() {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        var proto = constructeur.prototype;
        proto.requete = "#journaldebordEnregistre, .journaldebordEnregistre, #stockageEnregistre";
        proto.alert   = function(m) { alert(m); };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var msg_alerte = jQuery.trim( this.noeuds.hide().text() );
            if(msg_alerte.length) this.alert( msg_alerte );
        };

        return constructeur;
    })();

    widgets.surfenetres.PageImprimable = (function() {
        var constructeur = sofad.sousType( sofad.questionnaires.widgets.PageImprimable );
        constructeur.prototype.requete = '.page-imprimable';
        return constructeur;
    })();

    //a voir si necessaire
    widgets.DroitsCaches = (function() {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        var proto        = constructeur.prototype;
        proto.requete    = '.src-rights';
        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var separateur = this.noeuds.find('br').eq(0);
            if(!separateur.length) return;
            this.noeuds.prepend('<div class="affiche"></div>');
            this.noeuds.append('<div class="cache"></div>');

            var noeud_affiche = separateur[0];
            while(noeud_affiche = noeud_affiche.previousSibling) {
                this.noeuds.find('.affiche').prepend(noeud_affiche);
            }

            var noeud_cache = separateur[0];
            while(noeud_cache = noeud_cache.nextSibling) {
                this.noeuds.find('.cache').append(noeud_cache);
            }

            separateur.hide();
            var that = this;
            this.noeuds.find('.cache').css('visibility', 'hidden');
            this.noeuds.bind('mouseover', function() { that.noeuds.find('.cache').css('visibility', 'visible'); }); 
            this.noeuds.bind('mouseout',  function() { that.noeuds.find('.cache').css('visibility', 'hidden'); }); 
        }
        return constructeur;
    })();


    widgets.surfenetres.InactifDansDemo = (function() {
        var constructeur = sofad.sousType( sofad.widgets.Widget );
        constructeur.prototype.alert = function(m) { alert(m); };
        constructeur.prototype.requete =  '.page-demo button.btn-enregistrer, \
                                           .page-demo button.btn-transmettre, \
                                           .page-demo input.btn-enregistrer, \
                                           .page-demo input.btn-transmettre, \
                                           .page-demo button.btn-enregistrer-demo, \
                                           .page-demo .help a, \
                                           .page-demo .lien-inactif,\
                                           .page-demo button.lancer-quiz';
                                           
        var parent_demarre = constructeur.prototype.demarre;
        constructeur.prototype.demarre = function() {
            var that = this;
            this.noeuds.unbind('click').bind("click", function() {
                if(jQuery(this).attr('title')) {
                    that.alert( jQuery(this).attr('title') );
                }
                return false;
            });
        };
        
        return constructeur;
    })();

    controleurs.Loupe = (function() {
        var constructeur = sofad.sousType( sofad.controleurs.Controleur );
        constructeur.prototype.requete = 'div.loupe > a';
        constructeur.prototype.html    = '<div class="loupe"><a title="{L:surfenetres.zoom_agrandir}" href="#"><img border="0" src="{0}zoom.png"/></a></div>';
        constructeur.prototype.insere  = function(widget_parent) {
            widget_parent.noeuds.find('img, object').after( sofad.format(this.renduHtml(), sofad.chemins.images ) );
        };
        return constructeur;
    })();

    controleurs.ConsoleSonoreFlash = (function() {
        var constructeur = sofad.sousType( sofad.controleurs.Controleur );
        constructeur.prototype.requete = 'object';
        constructeur.prototype.html    = 
            '<object type="application/x-shockwave-flash" width="110" height="40" data="{0}" swLiveConnect="true"> \
            <param name="flashvars" value="item1={1}" valuetype="data" /> \
            <param name="swremote" value="item1={1}" valuetype="data" />  \
            <param name="movie" value="{0}" valuetype="data" />  \
            <param name="wmode" value="opaque" valuetype="data" /> \
        </object>';
        return constructeur;
    })();


    //FONCTIONS GLOBALES (utilisees par Flash)

    SofadPopup = function(href) { this.href = href; };
    SofadPopup.prototype = sofad.objet( widgets.surfenetres.Surfenetre.prototype );
    SofadPopup.prototype.adresse = function() { return this.href; };
    window.sofad_popup = function(href) {
        var popup = new SofadPopup(href);
        popup.ouvre();
    };


    var SofadZoom = function(href, largeur, hauteur) { 
        SofadPopup.apply(this, arguments);
        this.hauteurZoom = hauteur;
        this.largeurZoom = largeur;
        this.parametres = sofad.format( this.parametres, largeur, hauteur );
    };
    SofadZoom.prototype = sofad.objet( widgets.surfenetres.Zoom.prototype );
    SofadZoom.prototype.adresseImage = SofadPopup.prototype.adresse;
    SofadZoom.prototype.dimensionsZoom = function() {
        return { largeur: this.largeurZoom,
                 hauteur: this.hauteurZoom };
    };

    window.sofad_zoom = function(href, largeur, hauteur) {
        var zoom = new SofadZoom(href, largeur, hauteur);
        zoom.ouvre();
    };


    sofad.surfenetres.autres = {
        SofadPopup: SofadPopup,
        SofadZoom:  SofadZoom
    };

    
}) ();

//COOKIES
/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // NOTE Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
