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

//Cadre d'applications sofad
(function() {
    sofad.chemins = {
        images:      '../../lib-javascript/images/',
        zoom:        '../../lib-javascript/lib/sofad/zoom.html',
        reinitOutil: '../../interactivites/reinit_outil.php'
    };


    // 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.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;
            });

            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);
                sofad.widgets.Widget.apply(this, [jQuery(this.requete, widget_parent.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);
        };
        
        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);
        };
        
        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);

        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');
        };

        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);
                    });
                })(ii);
            }
        };

        constructeur.prototype = proto;

        return constructeur;
    };


    //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.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;

        //TODO : faire le traitement de langues pour ces elements
        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.hide(); };

        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();
            if(this.valide()) {
                this.controleurs.bonneReponse    = new (this.factory('questionnaires.controleurs.BonneReponse'))(this, [this.noeuds[0].value]);
            } 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.valide = function() {
            return -1 != jQuery.inArray(this.noeuds[0].value,
                                        this.reponsesAcceptees);
        };

        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();
        };

        return constructeur;
    })();


    //Outils sciences

    widgets.OutilScience = (function() {
        var constructeur = sofad.sousType( widgets.Element, function() {
            widgets.Element.apply(this, arguments);
            delete this.events; //Supprimer les evenements pour laisser transparaitre celui du prototype
            if(!('outilOuvre' in this.events)) {
                this.events.outilOuvre = new YAHOO.util.CustomEvent('outilOuvre'); //initialisation d'un evenement du prototype (partagé)
            }
            this.href         = this.noeuds.find( this.requeteHrefOutil ).text();
            this.blocCaptures = this.noeuds.find( this.requeteBlocCaptures );
            this.captures     = this.blocCaptures.find( this.requeteCaptures );
            
            var barre = this.controleurs.barreBtnOutil = new (this.factory('questionnaires.controleurs.BarreBtnOutil'))(this);
            this.controleurs.boutonOuvreOutil          = new (this.factory('questionnaires.controleurs.BoutonOuvreOutil'))(barre);
            this.controleurs.boutonEnregistreOutil     = new (this.factory('questionnaires.controleurs.BoutonEnregistreOutil'))(barre);
            this.controleurs.boutonFermeOutil          = new (this.factory('questionnaires.controleurs.BoutonFermeOutil'))(barre);
            this.controleurs.boutonReinitOutil         = new (this.factory('questionnaires.controleurs.BoutonReinitOutil'))(barre);
        } );
        var proto     = constructeur.prototype;
        proto.requete = function(contexte) { 
            return jQuery('.outil', contexte).filter(function() { return !jQuery(this).parents().find('.page-demo').length; }); 
        };
        proto.requeteBlocCaptures = "div.conteneur-images";
        proto.requeteCaptures   = "> div";
        proto.requeteHrefOutil  = "span.href";
        proto.events            = {};

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

            this.events.outilOuvre.subscribe(this.ferme, null, this);

            var that = this;
            this.controleurs.boutonOuvreOutil.noeuds.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.boutonFermeOutil.noeuds.bind('click', function() {
                that.comportement.ferme(); return false;
            });
        };

        proto.ferme = function() { if(this.comportement.ferme) this.comportement.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;
                    outil.controleurs.boutonOuvreOutil.noeuds.show();
                    outil.controleurs.boutonReinitOutil.noeuds.show();
                    outil.controleurs.boutonEnregistreOutil.noeuds.hide();
                    outil.controleurs.boutonFermeOutil.noeuds.hide();
                    outil.blocCaptures.show();
                    this.afficheCapture(0);
                };

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

                        jQuery.ajax({ 
                            url: chemin_image,
                            success: function() {
                                if(that.outil.blocCaptures.is(':visible')) {
                                    that.outil.captures.eq(ii).show();
                                    that.outil.captures.eq(ii).find('img').attr('src', chemin_image);
                                }
                            },
                            error: function(XMLHttpRequest, textStatus, errorThrown) {
                                if('parsererror' == textStatus) { //ie6 hack
                                    if(that.outil.blocCaptures.is(':visible')) {
                                        that.outil.captures.eq(ii).show();
                                        that.outil.captures.eq(ii).find('img').attr('src', chemin_image);
                                    }
                                } else if((ii+1) < that.outil.captures.length) {
                                    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;
                    this.outil.events.outilOuvre.fire();
                    outil.controleurs.boutonOuvreOutil.noeuds.hide();
                    outil.controleurs.boutonReinitOutil.noeuds.show();
                    outil.controleurs.boutonEnregistreOutil.noeuds.show();
                    outil.controleurs.boutonFermeOutil.noeuds.show();

                    outil.controleurs.cadreOutil = new (outil.factory('questionnaires.controleurs.CadreOutil'))(outil);
                    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.flashProxy.call('enregistreImageOutil');
                        this.outil.changeComportement('attenteEnregistre');
                    },

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

                    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('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.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;
    })();


    //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.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.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}</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.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.noeuds.prepend( sofad.format(this.html, widget_parent.href) );
        };
        return constructeur;
    })();


    //Outils Sciences DEMO

    widgets.OutilScienceDemo = (function() {
        var parent_constructeur = widgets.OutilScience;
        var constructeur = sofad.sousType(parent_constructeur);
        var proto = constructeur.prototype;
        proto.requete = function(contexte) { 
            return jQuery('.outil', contexte).filter(function() { return jQuery(this).parents().find('.page-demo').length; }); 
        };

        proto.factory('questionnaires.controleurs.BoutonEnregistreOutil', (function() {
            var constructeur = sofad.sousType(controleurs.BoutonEnregistreOutil);
            constructeur.prototype.html = "";
            return constructeur;
        })());
        proto.factory('questionnaires.controleurs.BoutonReinitOutil', (function() {
            var constructeur = sofad.sousType(controleurs.BoutonReinitOutil);
            constructeur.prototype.html = "";
            return constructeur;
        })());
        proto.factory('questionnaires.controleurs.BoutonFermeOutil', (function() {
            var constructeur = sofad.sousType(controleurs.BoutonFermeOutil);
            constructeur.prototype.html = '<button class="btn-ferme-outil">{L:questionnaires.btn_outil_fermer_demo}</button>';
            return constructeur;
        })());


        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",
                              outilScienceDemo: "questionnaires.widgets.OutilScienceDemo"
                             };

        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}') );
            }

            //TODO TESTER!!!
            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;

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.declencheParentEvent('affichePointage',
                                      function(fn) { 
                                          that.noeuds.bind('click', 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);
        });
        var proto = constructeur.prototype;

        proto.requeteFormulaire      = 'form';
        proto.htmlImageChargement    = '<img class="ajax-loader" alt="{L:questionnaires.enregistre_ajax_encours}" src="{0}ajax-loader.gif" />';
        proto.requeteImageChargement = 'img.ajax-loader';
        proto.messageEnvoieReussi    = 'Vos réponses ont été enregistrées.';
        proto.messageEnvoieEchoue    = 'L\'enregistrement a échoué.';

        proto.envoieFormulaire = function() {
            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();
            var that = this;
            jQuery.ajax({type:    formulaire.attr('method'),
                         url:     formulaire.attr('action'),
                         data:    valeurs_post,
                         success: function() { that.estEnvoiReussi(); },
                         error:   function() { that.estEnvoiEchoue(); }
                        });
        };

        proto.estEnvoiCommence = function () {
            this.controleurs.boutonEnregistreAjax.noeuds.after( 
                sofad.lang( 
                    sofad.format(this.htmlImageChargement, sofad.chemins.images)
                )
            );
        };

        proto.estEnvoiReussi = function () {
            if(this.requeteImageChargement) this.controleurs.boutonEnregistreAjax.noeuds.nextAll( this.requeteImageChargement ).remove();
            if(this.messageEnvoieReussi)    alert( this.messageEnvoieReussi );
        };

        proto.estEnvoiEchoue = function () {
            if(this.requeteImageChargement) this.controleurs.boutonEnregistreAjax.noeuds.nextAll( this.requeteImageChargement ).remove();
            if(this.messageEnvoieEchoue)    alert( 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();
                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 = pointage[0]; 
            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>';
        proto.insere = function(widget_parent) { 
            var rendu_html = this.renduHtml();
            if(this.paramsRendu[0]) rendu_html += '<span class="espacement-mauvaise-reponse"> </span>';
            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=400,scrollbars=yes,resizable=yes,status=yes,toolbar=yes,location=yes,menubar=yes"; 
        proto.open       = function () { return window.open(arguments[0],
                                                            arguments[1],
                                                            arguments[2]); };
        proto.requete    = 'a.popup';
        proto.nomFenetre = 'surfenetre';
        
        proto.adresse = function() {
            return this.noeuds.attr('href');
        };

        proto.ouvre = function () {
            var w = this.open(this.adresse(),
                this.nomFenetre,
                this.parametres);
            w.focus();
            return false;
        };

        var parent_demarre = proto.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            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';
        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=yes,resizable=yes,status=no,toolbar=no,location=no,menubar=no"; 
        proto.regExpZoom = new RegExp("^zoom-(\\d+)-(\\d+)$");

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

            if(undefined === largeur || undefined === hauteur) {
                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, largeur, 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');
            }

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

        proto.adresse = function() {
            var dossier = location.href.substr(0, location.href.lastIndexOf('/')+1);
            return sofad.chemins.zoom + "?src=" + dossier + this.adresseImage();
        };

        var parent_demarre = sofad.widgets.Widget.prototype.demarre;
        proto.demarre = function() {
            parent_demarre.apply(this, arguments);
            var that = this;
            this.controleurs.loupe.noeuds.bind('click', function() {
                return that.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');

        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;

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

            html = sofad.format(html, src, jQuery(window).width(), jQuery(window).height()); 
            this.noeuds.append(html);
        };

        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;
    })();


    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.parametres = sofad.format( this.parametres, largeur, hauteur );
    };
    SofadZoom.prototype = sofad.objet( widgets.surfenetres.Zoom.prototype );
    SofadZoom.prototype.adresseImage = SofadPopup.prototype.adresse;

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


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

    
}) ();
