diff --git a/front/public/JS/group.app.js b/front/public/JS/group.app.js deleted file mode 100644 index 6cb2414..0000000 --- a/front/public/JS/group.app.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e={6028:function(e,t,s){const n=s(671),i=s(4988);e.exports={apiUrl:"http://localhost:3000/api",siteUrl:"http://localhost:8080",siteUrlProd:"https://www.wikilerni.com",adminName:"Fab",adminEmail:"dev@wikilerni.com",senderName:"WikiLerni",senderEmail:"bonjour@wikilerni.com",adminLang:"fr",theme:"wikilerni",availableLangs:["fr"],siteName:"WikiLerni",beginCodeGodfather:"WL",defaultReceiptDays:"147",cronTimingAlertInSeconde:120,responseTimingAlertInSeconde:3,tokenSignupValidationTimeInHours:"48h",tokenLoginLinkTimeInHours:"1h",tokenConnexionMinTimeInHours:"24h",tokenConnexionMaxTimeInDays:"180 days",tokenLoginChangingTimeInHours:"1h",tokenDeleteUserTimeInHours:"1h",tokenUnsubscribeLinkTimeInDays:"7 days",freeAccountTimingInDays:0,freeAccountExpirationNotificationInDays:3,accountExpirationFirstNotificationInDays:10,accountExpirationSecondNotificationInDays:3,inactiveAccountTimeToDeleteInDays:180,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,nbNewQuestionnaires:12,hourGiveNewQuestionnaireBegin:3,hourGiveNewQuestionnaireEnd:8,maxQuestionnaireSendedAtSameTime:50,minSearchQuestionnaires:3,fieldNewQuestionnaires:"publishingAt",nbQuestionnairesByGroupMin:1,nbQuestionnairesByGroupMax:0,nbIllustrationsMin:1,nbIllustrationsMax:1,maxIllustrationSizeinOctet:1e6,mimeTypesForIllustration:["image/jpg","image/jpeg","image/png","image/gif","image/png"],illustrationsWidthMaxInPx:500,illustrationsMiniaturesWidthMaxInPx:200,nbLinksMin:1,nbLinksMax:0,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,passwordMinLength:n.password.minlength,dirCacheUsers:n.dirCacheUsers,dirCacheUsersAnswers:n.dirCacheUsersAnswers,dirCacheQuestionnaires:i.dirCacheQuestionnaires,dirCacheQuestions:i.dirCacheQuestions,dirCacheUsersQuestionnaires:i.dirCacheUsersQuestionnaires,dirHTMLQuestionnaires:i.dirHTMLQuestionnaires,dirWebQuestionnaires:i.dirWebQuestionnaires}},45:function(e){e.exports={matomo:{url:"https://stats.le-fab-lab.com/",siteId:"5"}}},4988:function(e){e.exports={questionnaireRoutes:"/questionnaire",getListNextQuestionnaires:"/getlistnextquestionnaires/",getQuestionnaireRoutes:"/get",getRandomQuestionnairesRoute:"/getrandom",getStatsQuestionnaires:"/stats/",previewQuestionnaireRoutes:"/preview",publishedQuestionnaireRoutes:"/quiz/",regenerateHTML:"/htmlregenerated",searchAdminQuestionnairesRoute:"/searchadmin",searchQuestionnairesRoute:"/search",groupRoutes:"/group",getGroupRoute:"/get/",previewGroupRoutes:"/preview",searchGroupsRoute:"/search",questionsRoute:"/question/",tagsSearchRoute:"/tags/search/",getAdminStats:"/getadminstats/",getPreviousAnswers:"/user/answers/",getStatsAnswers:"/user/anwswers/stats/",saveAnswersRoute:"/answer/",Questionnaire:{title:{maxlength:255,required:!0},slug:{maxlength:150},introduction:{required:!0}},searchQuestionnaires:{minlength:3,required:!0},Group:{title:{maxlength:255,required:!0},slug:{maxlength:150}},Question:{text:{maxlength:255,required:!0},rank:{required:!0,min:1,defaultValue:1}},Choice:{text:{maxlength:255,required:!0}},search:{minlength:3,required:!0},searchGroups:{minlength:3,required:!0},dirCacheGroups:"datas/questionnaires/groups",dirCacheQuestionnaires:"datas/questionnaires",dirCacheQuestions:"datas/questionnaires/questions",dirCacheTags:"datas/questionnaires/tags",dirCacheUsersQuestionnaires:"datas/users/questionnaires",dirHTMLGroups:"front/public/quiz/gp",dirHTMLQuestionnaires:"front/public/quiz",dirHTMLNews:"front/public/quizs",dirHTMLTags:"front/public/quizs",dirWebGroups:"quiz/gp",dirWebQuestionnaires:"quiz",dirWebNews:"quizs/",dirWebTags:"quizs/",nbRandomResults:3,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,nbTagsMin:0,nbTagsMax:0}},671:function(e){e.exports={userRoutes:"/user",checkDeleteLinkRoute:"/confirmdelete/",checkIfIsEmailfreeRoute:"/isemailfree",checkLoginRoute:"/checklogin/",checkNewLoginLinkRoute:"/confirmnewlogin/",checkSubscribeTokenRoute:"/validation/",connectionRoute:"/login",connectionWithLinkRoute:"/checkloginlink",createUserRoute:"/create",getAdminStats:"/getadminstats/",getGodChilds:"/getgodchilds/",getGodfatherRoute:"/getgodfatherid",getLoginLinkRoute:"/getloginlink",getPayments:"/payment/getforoneuser/",getUserInfos:"/get/",getUsersQuestionnairesRoute:"/getusersquestionnaires/",searchUserRoute:"/search/",signupCompletionRoute:"/signupcompletion/",subscribeRoute:"/signup",unsubscribeRoute:"/subscription/stop/",updateUserInfos:"/modify/",validateUserRoute:"/validate/",name:{maxlength:70,required:!0},email:{maxlength:255,required:!0},password:{minlength:8,maxlength:72,required:!0},newPassword:{minlength:8,maxlength:72},codeGodfather:{maxlength:255},cguOk:{value:"true",required:!0},search:{minlength:1,required:!0},timeDifferenceMin:-720,timeDifferenceMax:840,dirCacheUsers:"datas/users",dirCacheUsersAnswers:"datas/users/questionnaires/answers",dirCacheUsersWithoutAnswers:"datas/users/questionnaires/without"}},6369:function(e){e.exports={apiUrl:"http://localhost:3000/api",usersGetConfigUrl:"/user/getconfig",lang:"fr",userHomePage:"accueil.html",adminHomePage:"admin.html",managerHomePage:"gestion.html",subscribePage:"inscription.html",connectionPage:"connexion.html",accountPage:"compte.html",questionnairesManagementPage:"gestion-quizs.html",usersManagementPage:"gestion-utilisateurs.html",nbQuestionnairesUserHomePage:10,illustrationDir:"/img/quizs/"}},7194:function(e){e.exports={checkResponsesOuputFail:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est certain, vous ferez mieux la prochaine fois !",checkResponsesOuputMedium:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est pas mal du tout !",checkResponsesOuputSuccess:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. Bravo ! Rien ne vous échappe !",needIntegerNumberCorrectResponses:"Le nombre de réponses correctes doit être un nombre entier.",needIntegerNumberSecondesResponse:"La durée de la réponse doit être un nombre entier de secondes.",needIntegerNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu doit être un nombre entier.",needMaxNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu ne peut être aussi élevé.",needMinNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu ne peut être inférieur à 1.",needNumberCorrectResponses:"Le nombre de réponses correctes doit être fourni.",needNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu doit être fourni.",needMaxNumberCorrectResponses:"Le nombre de réponses correctes ne peut être supérieur au nombre de questions.",needMinNumberCorrectResponses:"Le nombre de réponses correctes ne peut être négatif.",needMinNumberSecondesResponse:"La durée de la réponse ne peut être négative.",noPreviousAnswer:"On dirait que c'est la première fois que vous répondez à ce quiz. Bonne lecture !",previousAnswersLine:"Le DATEANSWER, vous avez répondu correctement à NBCORRECTANSWERS questions sur NBQUESTIONS en AVGDURATION secondes.",previousAnswersStats:"En moyenne, vous avez répondu à ce quiz en AVGDURATION secondes, en ayant AVGCORRECTANSWERS % de bonnes réponses.",previousAnswersTitle:"Bonjour #NOM, voici vos précédents résultats à ce quiz",responseSavedError:"Cependant une erreur a été rencontrée durant l'enregistrement de votre résultat. Accèder à tous vos quizs.",responseSavedMessage:"Votre résultat a été enregistré. Accèder à tous vos quizs.",statsUser:"Vous avez enregistré NBANSWERS réponseS1 à NBQUESTIONNAIRES quizS2 différentS3 sur les NBTOTQUESTIONNAIRES proposéS4 par le site.
En moyenne, vous avez mis AVGDURATION secondes à répondre et avez correctement répondu à AVGCORRECTANSWERS % des questions.",wantToSaveResponses:'Si vous le souhaitez, vous pouvez sauvegarder votre résultat en créant votre compte ci-dessous. Cela vous permettra aussi de recevoir régulièrement de nouvelles "graines de culture" directement sur votre e-mail.'}},223:function(e){e.exports={addBtnTxt:"Ajouter",addOkMessage:"Les données ont bien été enregistrées.",alertNewWindow:"nouvelle fenêtre",badUrl:"Tentative d'accès à une page n'existant pas :",btnLinkToQuestionnaire:"Afficher !",btnProposeConnection:"Je me connecte.",btnProposeSubscribe:"Je crée mon compte.",btnShowOnWebSite:"Lire la suite sur #SITE_NAME",deleteBtnTxt:"Supprimer",deleteFailMessage:"La suppression de l'enregistrement #ID a échoué.",deleteOkMessage:"La suppression a bien été enregistrée.",failAuth:"Erreur d'authentification.",failAuthCron:"Tentative de lancement d'un cron sans le bon token.",failAuthHeader:"Absence de header Authorization.",failAuthId:"Identifiant non valide : ",failAuthToken:"Token invalide ou utilisateur non trouvé.",neededParams:"Des paramètres nécessaires manquants sont manquants.",nextPage:"Page suivante",notAllowed:"Vous n'avez pas les droits nécessaires pour cette action.",notRequired:"Facultatif.",notValidFormat:"Format non valide.",previousPage:"Page précédente",serverError:"Désolé. Une erreur imprévue est survenue. Si cela persiste, n'hésitez à prévenir l'administrateur du site.",serverErrorAdmin:"Bug de l'application :",siteHTMLTitle:"WikiLerni : la culture générale en liberté",siteMetaDescription:"Avec WikiLerni, vous apprenez régulièrement de nouvelles choses. Vous recevez de courts articles lisibles en quelques minutes. Des quizs vous permettent ensuite de tester ce que vous avez retenu.",scriptTimingAlert:"*** Script lent : SCRIPT_TIMING millisecondes, route : SCRIPT_URL",scriptTimingInfo:"Durée de la réponse : SCRIPT_TIMING millisecondes, route : SCRIPT_URL",statsAdmin:"Durant les dernières 24h : NB_USERS_24H comptes ont été créés, NB_SUBSCRIPTIONS_24H validés et NB_USERS_DELETED_24H supprimés. NB_ANSWERS_24H réponses aux quizs ont été enregistrées.
En tout, il y a : NB_USERS_TOT comptes, dont NB_SUBSCRIPTIONS_TOT validés et NB_SUBSCRIPTIONS_PREMIUM comptes prémium. NB_ANSWERS_TOT réponses aux quizs ont été enregistrées.
Parmi les NB_USERS_DELETED_TOT comptes supprimés, NB_USERS_DELETED_VALIDED avaient validé leur compte et NB_USERS_DELETED_PREMIUM avaient souscrit un compte prémium.",subscriptionCall:"Inscrivez-vous !",updateBtnTxt:"Modifier",updateOkMessage:"La mise à jour à jour a bien été enregistrée."}},9864:function(e){class t{static isEmpty(e){return null==e||""===(e+="").trim()}static trimIfNotNull(e){return t.isEmpty(e)?null:(e+="").trim()}static shortenIfLongerThan(e,t){return(e+="").length>t&&(e=e.substring(0,t-3)+"…"),e}static replaceAll(e,t){const s=new RegExp(Object.keys(t).join("|"),"gi");return e.replace(s,(e=>t[e]))}static getRandomInt(e,t){return e=Math.ceil(e),t=Math.floor(t),Math.floor(Math.random()*(t-e))+e}static dateFormat(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"fr";if(t.isEmpty(e))return"";let n=new Date(e),i=n.getDate()+"";1===i.length&&(i="0"+i);let r=n.getMonth()+1+"";1===r.length&&(r="0"+r);let o=n.getFullYear();return"fr"===s?i+"/"+r+"/"+o:"form"===s?o+"-"+r+"-"+i:r+"/"+i+"/"+o}static getPassword(e,t){const s=e+Math.floor(Math.random()*(t-e)),n="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz",i="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ";let r=n[Math.floor(Math.random()*n.length)];for(let e=1;eDes articles de Wikipédia sont sélectionnés pour vous et sont suivis d\'un quiz vous permettant de tester ce que vous en avez retenu.
De jour en jour de nouvelles graines de savoir sont ainsi semées dans votre "jardin".',homeTitle2:"La culture en liberté",homeP2:'Tout comme sur Wikipédia (*), le logiciel et le contenu partagé sur WikiLerni sont libres.
Vous pouvez les utiliser, les modifier et les diffuser selon votre souhait.
Sur WikiLerni, pas de publicité, ni de commercialisation de vos données personnelles.
Vous pouvez venir y "cultiver votre jardin" en toute tranquillité.

(*) Bien que partageant ses valeurs, WikiLerni est un projet indépendant de la fondation Wikipédia.',newQuestionnairesTitle:"Les derniers quizs publiés sur WikiLerni",newQuestionnairesIntro:"Liste des derniers quizs publiés sur WikiLerni.",explanationTitle:"Vous découvrez WikiLerni ?",explanationTxt:"Le principe est simple : vous commencez par lire l'article Wikipédia dont le lien vous est proposé.
Puis vous afficher le quiz pour vérifier ce que vous en avez retenu de votre lecture. Vous obtenez alors votre résultat immédiatement.

Toutes les réponses se trouvent dans l'article proposé à la lecture. Vous êtes ici pour apprendre de nouvelles choses, mais libre à vous d'essayer d'y répondre immédiatement.

Quand le sujet s'y prête, ne vous étonnez pas si certaines des réponses proposées peuvent être un peu décalées, absurdes... On peut apprendre avec le sourire, non ? :-)

Une fois votre résultat obtenu, il vous sera proposé de créer un compte pour le sauvegarder.
Ce compte vous permettra de tester de nouveau ce quiz pour vérifier ce que vous en avez retenu plusieurs jours, semaines, mois... Et de recevoir régulièrement de nouvelles suggestions de lectures.

Mais la création de ce compte est facultative et vous pouvez parcourir WikiLerni librement.",noJSNotification:"Désolé, mais pour l'instant, l'utilisation de WikiLerni nécessite l'activation du JavaScript.",tagsListTxt:"Parcourir les rubriques :"}},4326:function(e){e.exports={headLinks:[{anchor:"Contact",attributes:{href:"/contact.html",rel:"nofollow"}},{anchor:"Parcourir",attributes:{href:"/quizs/",id:"indexHeadLink",title:"Les dernières publications"}},{anchor:"À propos",attributes:{href:"/a-propos.html",title:"En savoir + sur WikiLerni"}},{anchor:"Accueil",attributes:{href:"/",title:"Page d'accueil"}}],footLinks:[{anchor:"Blog",attributes:{href:"https://diaspora-fr.org/people/815767c0c09e0139ec6f32a01d0dfba2",title:"Le blog WikiLerni sur diaspora*"}},{anchor:"Crédits",attributes:{href:"/credits.html",title:"Qui a créé WikiLerni ? Quels sont vos droits ?"}},{anchor:"Mentions légales",attributes:{href:"/mentions-legales.html",rel:"nofollow"}},{anchor:"Données personnelles",attributes:{href:"/donnees.html",title:"Vos données personnelles sur WikiLerni"}},{anchor:"CGV & CGU",attributes:{href:"/CGV-CGU.html",rel:"nofollow"}}],accountPage:"compte.html",aboutPage:"a-propos.html",adminHomePage:"admin.html",cguPage:"CGV-CGU.html",connectionPage:"connexion.html",deleteLinkPage:"aurevoir.html?t=",loginLinkPage:"login.html?t=",managerHomePage:"gestion.html",newLoginLinkPage:"newlogin.html?t=",questionnairesManagementPage:"gestion-quizs.html",stopMailPage:"stop-mail.html?t=",subscribePage:"inscription.html",updateAccountPage:"compte.html",userHomePage:"accueil.html",userHomePageTxt:"Ma page d'accueil.",usersManagementPage:"gestion-utilisateurs.html",validationLinkPage:"validation.html?t=",siteSlogan:"Cultivons notre jardin !",noJSNotification:"Désolé, mais pour l'instant, l'utilisation de WikiLerni nécessite l'activation du JavaScript.",mailRecipientTxt:"Message envoyé à :",licenceTxt:'@copyleft Le contenu de WikiLerni est libre et vous est offert sans publicité. Vous pouvez participer à son financement en cliquant ici.',homePageTxt:"Page d'accueil",homeTitle1:"De nature curieuse ?",homeP1:"Avec WikiLerni, vous apprenez régulièrement de nouvelles choses.
Vous découvrez de courts articles, lisibles en quelques minutes.
Des quizs vous permettent ensuite de tester ce que vous avez retenu.
De jour en jour de nouvelles graines de culture sont ainsi semées dans votre jardin.",homeTitle2:"La culture générale en liberté",homeP2:'Pas de faits alternatifs, tous les contenus sont sourcés par des articles Wikipédia.
Et tout comme sur Wikipédia, le logiciel et le contenu publié sur WikiLerni sont partagés sous licences libres.
Le tout sans publicité, ni commercialisation de vos données.
Sur WikiLerni, vous cultivez votre jardin en toute tranquillité.',homeBtnAboutTxt:"En savoir plus sur WikiLerni ?",homeBtnSubscribeTxt:"Testez WikiLerni",homeSubcriptionFormTitle:"Recevez les prochains articles WikiLerni",newQuestionnairesTitle:"Culture générale - apprenez de nouvelles choses avec WikiLerni",newQuestionnairesIntro:"WikiLerni : testez vos connaissances et apprenez de nouvelles choses avec WikiLerni.",newsListTitle:"

Avec WikiLerni, vous pouvez toujours apprendre quelque chose de nouveau
Si dessous les dernières publications. Vous pouvez aussi parcourir le site par thèmes ou mots-clés.

",tagListTitle:"Culture générale - des articles et quizs sur de nombreux thèmes !",tagListMetaDesc:"WikiLerni : découvrir les différents thèmes abordés par WikiLerni. Inxex du site.",tagListIntro:"

Avec WikiLerni, devenez fort en thèmes... Oui mais quels thèmes ? :)

Aristote : « L’homme a naturellement la passion de connaître… »
",answersExplanationsLinkText:"Relire",quizElementLinksIntro:"En savoir plus",quizElementSubcriptionFormTitle:"Recevez les prochains articles WikiLerni",explanationTitle:"Vous découvrez WikiLerni ?",explanationTxt:"

Le principe est simple : vous commencez par lire l’article Wikipédia dont le lien vous est proposé. Puis vous afficher le quiz pour vérifier ce que vous avez retenu de votre lecture. Suivant les questions, une ou plusieurs réponses peuvent être correctes et doivent donc être cochées. C’est toujours le contenu de l’article Wikipédia qui fait foi concernant les « bonnes » réponses. C’est une façon de tester à la fois votre capacité d’attention et votre mémoire. Les articles de Wikipédia peuvent évoluer, donc n’hésitez pas à me signaler une erreur.

WikiLerni vous propose d’autres solutions pour améliorer votre culture générale. Pour en savoir plus, cliquez sur le bouton ci-dessous.

",explanationElementTxt:"

WikiLerni vous propose de découvrir de courts articles lisibles en quelques minutes et portant sur des sujets très variés de culture générale (arts, histoire, littérature, sciences, etc.).

Ces articles sont basés sur une ou plusieurs pages de Wikipédia (fournies en lien), dont ils extraient certaines informations.

Chaque série d’articles est suivie d’un quiz permettant de tester ce que vous en avez retenu.

Vous apprenez ainsi régulièrement de nouvelles choses très simplement.

",illustrationDir:"/img/quizs/",twitterAccount:"WikiLerni",maxQuestionnairesByPage:12,maxQuestionnairesFeed:5,maxQuestionnairesSiteHomePage:3,nbQuestionnairesUserHomePage:3}},9274:function(e,t,s){var n={"./fr/answer":7194};function i(e){var t=r(e);return s(t)}function r(e){if(!s.o(n,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return n[e]}i.keys=function(){return Object.keys(n)},i.resolve=r,e.exports=i,i.id=9274},2111:function(e,t,s){var n={"./fr/general":223};function i(e){var t=r(e);return s(t)}function r(e){if(!s.o(n,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return n[e]}i.keys=function(){return Object.keys(n)},i.resolve=r,e.exports=i,i.id=2111},9729:function(e,t,s){var n={"./default/config/fr.js":2095,"./wikilerni/config/fr.js":4326};function i(e){var t=r(e);return s(t)}function r(e){if(!s.o(n,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return n[e]}i.keys=function(){return Object.keys(n)},i.resolve=r,e.exports=i,i.id=9729}},t={};function s(n){var i=t[n];if(void 0!==i)return i.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,s),r.exports}s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){"use strict";var e=s(6028),t=(s(4988),s(9864));const n=s(6369),i=s(9274)("./"+n.lang+"/answer"),r=function(e,s){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},a=!(arguments.length>6&&void 0!==arguments[6])||arguments[6];if((0,t.isEmpty)(s)||(0,t.isEmpty)(e))return!1;{const u=document.createElement(s);if((0,t.isEmpty)(i)||(u.id=i),Array.isArray(r)&&0!=r.length)for(let e in r)u.classList.add(r[e]);if("object"==typeof o)for(let e in o)u.setAttribute(e,o[e]);(0,t.isEmpty)(n)||(u.innerHTML=n.replace(/\n/g,"
")),a&&(e.innerHTML=""),e.appendChild(u)}};var o=s(45);const a=()=>{var e=window._paq=window._paq||[];e.push([function(){var e,t,s;this.setVisitorCookieTimeout((e=new Date,t=Math.round(e.getTime()/1e3),s=this.getVisitorInfo(),parseInt(s[2])+33696e3-t))}]),e.push(["trackPageView"]),e.push(["enableLinkTracking"]),function(){var t=o.matomo.url;e.push(["setTrackerUrl",t+"matomo.php"]),e.push(["setSiteId",o.matomo.siteId]);var s=document,n=s.createElement("script"),i=s.getElementsByTagName("script")[0];n.type="text/javascript",n.async=!0,n.src=t+"matomo.js",i.parentNode.insertBefore(n,i)}()};s(671);const u=e.availableLangs[0],l=(s(9729)("./"+e.theme+"/config/"+u+".js"),e.availableLangs[0]),{noPreviousAnswer:c,previousAnswersLine:d,previousAnswersStats:p,previousAnswersTitle:m,responseSavedError:h,wantToSaveResponses:g}=(s(9729)("./"+e.theme+"/config/"+l+".js"),s(9274)("./"+l+"/answer")),{serverError:b}=s(2111)("./"+l+"/general"),v=document.getElementById("checkResponses"),f=document.getElementById("response"),L=(document.getElementById("explanationsTitle"),document.getElementById("explanationsContent"),document.getElementById("group"));let T=0;(async()=>{try{T=Date.now(),v.style.display="block",a()}catch(e){console.error(e)}})(),console.log("**** Hello ami développeur :-)\n\nLe code de WikiLerni est libre et vous pouvez le trouver à cette adresse :\nhttps://forge.chapril.org/Fab_Blab/WikiLerni\n\nPour les suggestions d'amélioration ou questions : dev@wililerni.com ****");let k={};L.addEventListener("submit",(function(e){try{e.preventDefault(),v.style.display="none",f.innerHTML="";const s=(e=>{const t={},s=new FormData(e);for(let e of s.entries())t[e[0]]=e[1];return t})(L);k.duration=Math.round((Date.now()-T)/1e3),k.nbQuestions=0,k.nbCorrectAnswers=0,k.GroupId=document.getElementById("groupId").value;let n,o="",a=!1;for(let e in s)e.startsWith("isCorrect_response_")&&(n=e.substring(e.lastIndexOf("_")+1),s["question_id_response_"+n]!=o&&(o=s["question_id_response_"+n],k.nbQuestions++,a&&k.nbCorrectAnswers++,a=!0),"true"==s[e]?(document.getElementById("response_"+n).parentNode.classList.add("isCorrect"),void 0===s["response_"+n]&&(a=!1)):"on"===s["response_"+n]&&(a=!1,document.getElementById("response_"+n).parentNode.classList.add("isNotCorrect")));a&&k.nbCorrectAnswers++;let u=(e=>{if((0,t.isEmpty)(e.duration)||(0,t.isEmpty)(e.nbCorrectAnswers)||(0,t.isEmpty)(e.nbQuestions))return"";{const s=e.nbCorrectAnswers/e.nbQuestions,n={DURATION:e.duration,NBCORRECTANSWERS:e.nbCorrectAnswers,NBQUESTIONS:e.nbQuestions};let r="";return r=s<.4?(0,t.replaceAll)(i.checkResponsesOuputFail,n):s<.8?(0,t.replaceAll)(i.checkResponsesOuputMedium,n):(0,t.replaceAll)(i.checkResponsesOuputSuccess,n),r||""}})(k);r(f,"p",u,"",["success"]),window.location.hash="",window.location,window.location.hash="response";const l=document.querySelectorAll(".help");for(let e in l)void 0!==l[e].style&&(l[e].style.display="block")}catch(e){r(f,"p",b,"",["error"]),console.error(e)}}))}()}(); \ No newline at end of file diff --git a/front/public/JS/questionnaire.app.js b/front/public/JS/questionnaire.app.js deleted file mode 100644 index 2b601cb..0000000 --- a/front/public/JS/questionnaire.app.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e={6028:function(e,t,n){const s=n(671),i=n(4988);e.exports={apiUrl:"http://localhost:3000/api",siteUrl:"http://localhost:8080",siteUrlProd:"https://www.wikilerni.com",adminName:"Fab",adminEmail:"dev@wikilerni.com",senderName:"WikiLerni",senderEmail:"bonjour@wikilerni.com",adminLang:"fr",theme:"wikilerni",availableLangs:["fr"],siteName:"WikiLerni",beginCodeGodfather:"WL",defaultReceiptDays:"147",cronTimingAlertInSeconde:120,responseTimingAlertInSeconde:3,tokenSignupValidationTimeInHours:"48h",tokenLoginLinkTimeInHours:"1h",tokenConnexionMinTimeInHours:"24h",tokenConnexionMaxTimeInDays:"180 days",tokenLoginChangingTimeInHours:"1h",tokenDeleteUserTimeInHours:"1h",tokenUnsubscribeLinkTimeInDays:"7 days",freeAccountTimingInDays:0,freeAccountExpirationNotificationInDays:3,accountExpirationFirstNotificationInDays:10,accountExpirationSecondNotificationInDays:3,inactiveAccountTimeToDeleteInDays:180,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,nbNewQuestionnaires:12,hourGiveNewQuestionnaireBegin:3,hourGiveNewQuestionnaireEnd:8,maxQuestionnaireSendedAtSameTime:50,minSearchQuestionnaires:3,fieldNewQuestionnaires:"publishingAt",nbQuestionnairesByGroupMin:1,nbQuestionnairesByGroupMax:0,nbIllustrationsMin:1,nbIllustrationsMax:1,maxIllustrationSizeinOctet:1e6,mimeTypesForIllustration:["image/jpg","image/jpeg","image/png","image/gif","image/png"],illustrationsWidthMaxInPx:500,illustrationsMiniaturesWidthMaxInPx:200,nbLinksMin:1,nbLinksMax:0,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,passwordMinLength:s.password.minlength,dirCacheUsers:s.dirCacheUsers,dirCacheUsersAnswers:s.dirCacheUsersAnswers,dirCacheQuestionnaires:i.dirCacheQuestionnaires,dirCacheQuestions:i.dirCacheQuestions,dirCacheUsersQuestionnaires:i.dirCacheUsersQuestionnaires,dirHTMLQuestionnaires:i.dirHTMLQuestionnaires,dirWebQuestionnaires:i.dirWebQuestionnaires}},45:function(e){e.exports={matomo:{url:"https://stats.le-fab-lab.com/",siteId:"5"}}},4988:function(e){e.exports={questionnaireRoutes:"/questionnaire",getListNextQuestionnaires:"/getlistnextquestionnaires/",getQuestionnaireRoutes:"/get",getRandomQuestionnairesRoute:"/getrandom",getStatsQuestionnaires:"/stats/",previewQuestionnaireRoutes:"/preview",publishedQuestionnaireRoutes:"/quiz/",regenerateHTML:"/htmlregenerated",searchAdminQuestionnairesRoute:"/searchadmin",searchQuestionnairesRoute:"/search",groupRoutes:"/group",getGroupRoute:"/get/",previewGroupRoutes:"/preview",searchGroupsRoute:"/search",questionsRoute:"/question/",tagsSearchRoute:"/tags/search/",getAdminStats:"/getadminstats/",getPreviousAnswers:"/user/answers/",getStatsAnswers:"/user/anwswers/stats/",saveAnswersRoute:"/answer/",Questionnaire:{title:{maxlength:255,required:!0},slug:{maxlength:150},introduction:{required:!0}},searchQuestionnaires:{minlength:3,required:!0},Group:{title:{maxlength:255,required:!0},slug:{maxlength:150}},Question:{text:{maxlength:255,required:!0},rank:{required:!0,min:1,defaultValue:1}},Choice:{text:{maxlength:255,required:!0}},search:{minlength:3,required:!0},searchGroups:{minlength:3,required:!0},dirCacheGroups:"datas/questionnaires/groups",dirCacheQuestionnaires:"datas/questionnaires",dirCacheQuestions:"datas/questionnaires/questions",dirCacheTags:"datas/questionnaires/tags",dirCacheUsersQuestionnaires:"datas/users/questionnaires",dirHTMLGroups:"front/public/quiz/gp",dirHTMLQuestionnaires:"front/public/quiz",dirHTMLNews:"front/public/quizs",dirHTMLTags:"front/public/quizs",dirWebGroups:"quiz/gp",dirWebQuestionnaires:"quiz",dirWebNews:"quizs/",dirWebTags:"quizs/",nbRandomResults:3,nbQuestionsMin:1,nbQuestionsMax:0,nbChoicesMax:10,nbTagsMin:0,nbTagsMax:0}},671:function(e){e.exports={userRoutes:"/user",checkDeleteLinkRoute:"/confirmdelete/",checkIfIsEmailfreeRoute:"/isemailfree",checkLoginRoute:"/checklogin/",checkNewLoginLinkRoute:"/confirmnewlogin/",checkSubscribeTokenRoute:"/validation/",connectionRoute:"/login",connectionWithLinkRoute:"/checkloginlink",createUserRoute:"/create",getAdminStats:"/getadminstats/",getGodChilds:"/getgodchilds/",getGodfatherRoute:"/getgodfatherid",getLoginLinkRoute:"/getloginlink",getPayments:"/payment/getforoneuser/",getUserInfos:"/get/",getUsersQuestionnairesRoute:"/getusersquestionnaires/",searchUserRoute:"/search/",signupCompletionRoute:"/signupcompletion/",subscribeRoute:"/signup",unsubscribeRoute:"/subscription/stop/",updateUserInfos:"/modify/",validateUserRoute:"/validate/",name:{maxlength:70,required:!0},email:{maxlength:255,required:!0},password:{minlength:8,maxlength:72,required:!0},newPassword:{minlength:8,maxlength:72},codeGodfather:{maxlength:255},cguOk:{value:"true",required:!0},search:{minlength:1,required:!0},timeDifferenceMin:-720,timeDifferenceMax:840,dirCacheUsers:"datas/users",dirCacheUsersAnswers:"datas/users/questionnaires/answers",dirCacheUsersWithoutAnswers:"datas/users/questionnaires/without"}},6369:function(e){e.exports={apiUrl:"http://localhost:3000/api",usersGetConfigUrl:"/user/getconfig",lang:"fr",userHomePage:"accueil.html",adminHomePage:"admin.html",managerHomePage:"gestion.html",subscribePage:"inscription.html",connectionPage:"connexion.html",accountPage:"compte.html",questionnairesManagementPage:"gestion-quizs.html",usersManagementPage:"gestion-utilisateurs.html",nbQuestionnairesUserHomePage:10,illustrationDir:"/img/quizs/"}},7194:function(e){e.exports={checkResponsesOuputFail:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est certain, vous ferez mieux la prochaine fois !",checkResponsesOuputMedium:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est pas mal du tout !",checkResponsesOuputSuccess:"Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. Bravo ! Rien ne vous échappe !",needIntegerNumberCorrectResponses:"Le nombre de réponses correctes doit être un nombre entier.",needIntegerNumberSecondesResponse:"La durée de la réponse doit être un nombre entier de secondes.",needIntegerNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu doit être un nombre entier.",needMaxNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu ne peut être aussi élevé.",needMinNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu ne peut être inférieur à 1.",needNumberCorrectResponses:"Le nombre de réponses correctes doit être fourni.",needNumberUserResponses:"Le nombre de questions auxquelles l'utilisateur a répondu doit être fourni.",needMaxNumberCorrectResponses:"Le nombre de réponses correctes ne peut être supérieur au nombre de questions.",needMinNumberCorrectResponses:"Le nombre de réponses correctes ne peut être négatif.",needMinNumberSecondesResponse:"La durée de la réponse ne peut être négative.",noPreviousAnswer:"On dirait que c'est la première fois que vous répondez à ce quiz. Bonne lecture !",previousAnswersLine:"Le DATEANSWER, vous avez répondu correctement à NBCORRECTANSWERS questions sur NBQUESTIONS en AVGDURATION secondes.",previousAnswersStats:"En moyenne, vous avez répondu à ce quiz en AVGDURATION secondes, en ayant AVGCORRECTANSWERS % de bonnes réponses.",previousAnswersTitle:"Bonjour #NOM, voici vos précédents résultats à ce quiz",responseSavedError:"Cependant une erreur a été rencontrée durant l'enregistrement de votre résultat. Accèder à tous vos quizs.",responseSavedMessage:"Votre résultat a été enregistré. Accèder à tous vos quizs.",statsUser:"Vous avez enregistré NBANSWERS réponseS1 à NBQUESTIONNAIRES quizS2 différentS3 sur les NBTOTQUESTIONNAIRES proposéS4 par le site.
En moyenne, vous avez mis AVGDURATION secondes à répondre et avez correctement répondu à AVGCORRECTANSWERS % des questions.",wantToSaveResponses:'Si vous le souhaitez, vous pouvez sauvegarder votre résultat en créant votre compte ci-dessous. Cela vous permettra aussi de recevoir régulièrement de nouvelles "graines de culture" directement sur votre e-mail.'}},223:function(e){e.exports={addBtnTxt:"Ajouter",addOkMessage:"Les données ont bien été enregistrées.",alertNewWindow:"nouvelle fenêtre",badUrl:"Tentative d'accès à une page n'existant pas :",btnLinkToQuestionnaire:"Afficher !",btnProposeConnection:"Je me connecte.",btnProposeSubscribe:"Je crée mon compte.",btnShowOnWebSite:"Lire la suite sur #SITE_NAME",deleteBtnTxt:"Supprimer",deleteFailMessage:"La suppression de l'enregistrement #ID a échoué.",deleteOkMessage:"La suppression a bien été enregistrée.",failAuth:"Erreur d'authentification.",failAuthCron:"Tentative de lancement d'un cron sans le bon token.",failAuthHeader:"Absence de header Authorization.",failAuthId:"Identifiant non valide : ",failAuthToken:"Token invalide ou utilisateur non trouvé.",neededParams:"Des paramètres nécessaires manquants sont manquants.",nextPage:"Page suivante",notAllowed:"Vous n'avez pas les droits nécessaires pour cette action.",notRequired:"Facultatif.",notValidFormat:"Format non valide.",previousPage:"Page précédente",serverError:"Désolé. Une erreur imprévue est survenue. Si cela persiste, n'hésitez à prévenir l'administrateur du site.",serverErrorAdmin:"Bug de l'application :",siteHTMLTitle:"WikiLerni : la culture générale en liberté",siteMetaDescription:"Avec WikiLerni, vous apprenez régulièrement de nouvelles choses. Vous recevez de courts articles lisibles en quelques minutes. Des quizs vous permettent ensuite de tester ce que vous avez retenu.",scriptTimingAlert:"*** Script lent : SCRIPT_TIMING millisecondes, route : SCRIPT_URL",scriptTimingInfo:"Durée de la réponse : SCRIPT_TIMING millisecondes, route : SCRIPT_URL",statsAdmin:"Durant les dernières 24h : NB_USERS_24H comptes ont été créés, NB_SUBSCRIPTIONS_24H validés et NB_USERS_DELETED_24H supprimés. NB_ANSWERS_24H réponses aux quizs ont été enregistrées.
En tout, il y a : NB_USERS_TOT comptes, dont NB_SUBSCRIPTIONS_TOT validés et NB_SUBSCRIPTIONS_PREMIUM comptes prémium. NB_ANSWERS_TOT réponses aux quizs ont été enregistrées.
Parmi les NB_USERS_DELETED_TOT comptes supprimés, NB_USERS_DELETED_VALIDED avaient validé leur compte et NB_USERS_DELETED_PREMIUM avaient souscrit un compte prémium.",subscriptionCall:"Inscrivez-vous !",updateBtnTxt:"Modifier",updateOkMessage:"La mise à jour à jour a bien été enregistrée."}},9864:function(e){class t{static isEmpty(e){return null==e||""===(e+="").trim()}static trimIfNotNull(e){return t.isEmpty(e)?null:(e+="").trim()}static shortenIfLongerThan(e,t){return(e+="").length>t&&(e=e.substring(0,t-3)+"…"),e}static replaceAll(e,t){const n=new RegExp(Object.keys(t).join("|"),"gi");return e.replace(n,(e=>t[e]))}static getRandomInt(e,t){return e=Math.ceil(e),t=Math.floor(t),Math.floor(Math.random()*(t-e))+e}static dateFormat(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"fr";if(t.isEmpty(e))return"";let s=new Date(e),i=s.getDate()+"";1===i.length&&(i="0"+i);let r=s.getMonth()+1+"";1===r.length&&(r="0"+r);let o=s.getFullYear();return"fr"===n?i+"/"+r+"/"+o:"form"===n?o+"-"+r+"-"+i:r+"/"+i+"/"+o}static getPassword(e,t){const n=e+Math.floor(Math.random()*(t-e)),s="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz",i="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ";let r=s[Math.floor(Math.random()*s.length)];for(let e=1;eDes articles de Wikipédia sont sélectionnés pour vous et sont suivis d\'un quiz vous permettant de tester ce que vous en avez retenu.
De jour en jour de nouvelles graines de savoir sont ainsi semées dans votre "jardin".',homeTitle2:"La culture en liberté",homeP2:'Tout comme sur Wikipédia (*), le logiciel et le contenu partagé sur WikiLerni sont libres.
Vous pouvez les utiliser, les modifier et les diffuser selon votre souhait.
Sur WikiLerni, pas de publicité, ni de commercialisation de vos données personnelles.
Vous pouvez venir y "cultiver votre jardin" en toute tranquillité.

(*) Bien que partageant ses valeurs, WikiLerni est un projet indépendant de la fondation Wikipédia.',newQuestionnairesTitle:"Les derniers quizs publiés sur WikiLerni",newQuestionnairesIntro:"Liste des derniers quizs publiés sur WikiLerni.",explanationTitle:"Vous découvrez WikiLerni ?",explanationTxt:"Le principe est simple : vous commencez par lire l'article Wikipédia dont le lien vous est proposé.
Puis vous afficher le quiz pour vérifier ce que vous en avez retenu de votre lecture. Vous obtenez alors votre résultat immédiatement.

Toutes les réponses se trouvent dans l'article proposé à la lecture. Vous êtes ici pour apprendre de nouvelles choses, mais libre à vous d'essayer d'y répondre immédiatement.

Quand le sujet s'y prête, ne vous étonnez pas si certaines des réponses proposées peuvent être un peu décalées, absurdes... On peut apprendre avec le sourire, non ? :-)

Une fois votre résultat obtenu, il vous sera proposé de créer un compte pour le sauvegarder.
Ce compte vous permettra de tester de nouveau ce quiz pour vérifier ce que vous en avez retenu plusieurs jours, semaines, mois... Et de recevoir régulièrement de nouvelles suggestions de lectures.

Mais la création de ce compte est facultative et vous pouvez parcourir WikiLerni librement.",noJSNotification:"Désolé, mais pour l'instant, l'utilisation de WikiLerni nécessite l'activation du JavaScript.",tagsListTxt:"Parcourir les rubriques :"}},4326:function(e){e.exports={headLinks:[{anchor:"Contact",attributes:{href:"/contact.html",rel:"nofollow"}},{anchor:"Parcourir",attributes:{href:"/quizs/",id:"indexHeadLink",title:"Les dernières publications"}},{anchor:"À propos",attributes:{href:"/a-propos.html",title:"En savoir + sur WikiLerni"}},{anchor:"Accueil",attributes:{href:"/",title:"Page d'accueil"}}],footLinks:[{anchor:"Blog",attributes:{href:"https://diaspora-fr.org/people/815767c0c09e0139ec6f32a01d0dfba2",title:"Le blog WikiLerni sur diaspora*"}},{anchor:"Crédits",attributes:{href:"/credits.html",title:"Qui a créé WikiLerni ? Quels sont vos droits ?"}},{anchor:"Mentions légales",attributes:{href:"/mentions-legales.html",rel:"nofollow"}},{anchor:"Données personnelles",attributes:{href:"/donnees.html",title:"Vos données personnelles sur WikiLerni"}},{anchor:"CGV & CGU",attributes:{href:"/CGV-CGU.html",rel:"nofollow"}}],accountPage:"compte.html",aboutPage:"a-propos.html",adminHomePage:"admin.html",cguPage:"CGV-CGU.html",connectionPage:"connexion.html",deleteLinkPage:"aurevoir.html?t=",loginLinkPage:"login.html?t=",managerHomePage:"gestion.html",newLoginLinkPage:"newlogin.html?t=",questionnairesManagementPage:"gestion-quizs.html",stopMailPage:"stop-mail.html?t=",subscribePage:"inscription.html",updateAccountPage:"compte.html",userHomePage:"accueil.html",userHomePageTxt:"Ma page d'accueil.",usersManagementPage:"gestion-utilisateurs.html",validationLinkPage:"validation.html?t=",siteSlogan:"Cultivons notre jardin !",noJSNotification:"Désolé, mais pour l'instant, l'utilisation de WikiLerni nécessite l'activation du JavaScript.",mailRecipientTxt:"Message envoyé à :",licenceTxt:'@copyleft Le contenu de WikiLerni est libre et vous est offert sans publicité. Vous pouvez participer à son financement en cliquant ici.',homePageTxt:"Page d'accueil",homeTitle1:"De nature curieuse ?",homeP1:"Avec WikiLerni, vous apprenez régulièrement de nouvelles choses.
Vous découvrez de courts articles, lisibles en quelques minutes.
Des quizs vous permettent ensuite de tester ce que vous avez retenu.
De jour en jour de nouvelles graines de culture sont ainsi semées dans votre jardin.",homeTitle2:"La culture générale en liberté",homeP2:'Pas de faits alternatifs, tous les contenus sont sourcés par des articles Wikipédia.
Et tout comme sur Wikipédia, le logiciel et le contenu publié sur WikiLerni sont partagés sous licences libres.
Le tout sans publicité, ni commercialisation de vos données.
Sur WikiLerni, vous cultivez votre jardin en toute tranquillité.',homeBtnAboutTxt:"En savoir plus sur WikiLerni ?",homeBtnSubscribeTxt:"Testez WikiLerni",homeSubcriptionFormTitle:"Recevez les prochains articles WikiLerni",newQuestionnairesTitle:"Culture générale - apprenez de nouvelles choses avec WikiLerni",newQuestionnairesIntro:"WikiLerni : testez vos connaissances et apprenez de nouvelles choses avec WikiLerni.",newsListTitle:"

Avec WikiLerni, vous pouvez toujours apprendre quelque chose de nouveau
Si dessous les dernières publications. Vous pouvez aussi parcourir le site par thèmes ou mots-clés.

",tagListTitle:"Culture générale - des articles et quizs sur de nombreux thèmes !",tagListMetaDesc:"WikiLerni : découvrir les différents thèmes abordés par WikiLerni. Inxex du site.",tagListIntro:"

Avec WikiLerni, devenez fort en thèmes... Oui mais quels thèmes ? :)

Aristote : « L’homme a naturellement la passion de connaître… »
",answersExplanationsLinkText:"Relire",quizElementLinksIntro:"En savoir plus",quizElementSubcriptionFormTitle:"Recevez les prochains articles WikiLerni",explanationTitle:"Vous découvrez WikiLerni ?",explanationTxt:"

Le principe est simple : vous commencez par lire l’article Wikipédia dont le lien vous est proposé. Puis vous afficher le quiz pour vérifier ce que vous avez retenu de votre lecture. Suivant les questions, une ou plusieurs réponses peuvent être correctes et doivent donc être cochées. C’est toujours le contenu de l’article Wikipédia qui fait foi concernant les « bonnes » réponses. C’est une façon de tester à la fois votre capacité d’attention et votre mémoire. Les articles de Wikipédia peuvent évoluer, donc n’hésitez pas à me signaler une erreur.

WikiLerni vous propose d’autres solutions pour améliorer votre culture générale. Pour en savoir plus, cliquez sur le bouton ci-dessous.

",explanationElementTxt:"

WikiLerni vous propose de découvrir de courts articles lisibles en quelques minutes et portant sur des sujets très variés de culture générale (arts, histoire, littérature, sciences, etc.).

Ces articles sont basés sur une ou plusieurs pages de Wikipédia (fournies en lien), dont ils extraient certaines informations.

Chaque série d’articles est suivie d’un quiz permettant de tester ce que vous en avez retenu.

Vous apprenez ainsi régulièrement de nouvelles choses très simplement.

",illustrationDir:"/img/quizs/",twitterAccount:"WikiLerni",maxQuestionnairesByPage:12,maxQuestionnairesFeed:5,maxQuestionnairesSiteHomePage:3,nbQuestionnairesUserHomePage:3}},9274:function(e,t,n){var s={"./fr/answer":7194};function i(e){var t=r(e);return n(t)}function r(e){if(!n.o(s,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return s[e]}i.keys=function(){return Object.keys(s)},i.resolve=r,e.exports=i,i.id=9274},2111:function(e,t,n){var s={"./fr/general":223};function i(e){var t=r(e);return n(t)}function r(e){if(!n.o(s,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return s[e]}i.keys=function(){return Object.keys(s)},i.resolve=r,e.exports=i,i.id=2111},9729:function(e,t,n){var s={"./default/config/fr.js":2095,"./wikilerni/config/fr.js":4326};function i(e){var t=r(e);return n(t)}function r(e){if(!n.o(s,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return s[e]}i.keys=function(){return Object.keys(s)},i.resolve=r,e.exports=i,i.id=9729}},t={};function n(s){var i=t[s];if(void 0!==i)return i.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){"use strict";var e=n(6028),t=(n(4988),n(9864));const s=n(6369),i=n(9274)("./"+s.lang+"/answer"),r=function(e,n){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[],o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},a=!(arguments.length>6&&void 0!==arguments[6])||arguments[6];if((0,t.isEmpty)(n)||(0,t.isEmpty)(e))return!1;{const u=document.createElement(n);if((0,t.isEmpty)(i)||(u.id=i),Array.isArray(r)&&0!=r.length)for(let e in r)u.classList.add(r[e]);if("object"==typeof o)for(let e in o)u.setAttribute(e,o[e]);(0,t.isEmpty)(s)||(u.innerHTML=s.replace(/\n/g,"
")),a&&(e.innerHTML=""),e.appendChild(u)}};var o=n(45);const a=()=>{var e=window._paq=window._paq||[];e.push([function(){var e,t,n;this.setVisitorCookieTimeout((e=new Date,t=Math.round(e.getTime()/1e3),n=this.getVisitorInfo(),parseInt(n[2])+33696e3-t))}]),e.push(["trackPageView"]),e.push(["enableLinkTracking"]),function(){var t=o.matomo.url;e.push(["setTrackerUrl",t+"matomo.php"]),e.push(["setSiteId",o.matomo.siteId]);var n=document,s=n.createElement("script"),i=n.getElementsByTagName("script")[0];s.type="text/javascript",s.async=!0,s.src=t+"matomo.js",i.parentNode.insertBefore(s,i)}()};n(671);const u=e.availableLangs[0],l=(n(9729)("./"+e.theme+"/config/"+u+".js"),e.availableLangs[0]),{noPreviousAnswer:c,previousAnswersLine:d,previousAnswersStats:p,previousAnswersTitle:m,responseSavedError:h,wantToSaveResponses:g}=(n(9729)("./"+e.theme+"/config/"+l+".js"),n(9274)("./"+l+"/answer")),{serverError:b}=n(2111)("./"+l+"/general"),v=document.getElementById("questionnaire"),f=document.getElementById("response"),L=document.getElementById("showQuestionnaire"),q=document.getElementById("checkResponses");document.getElementById("explanationsTitle"),document.getElementById("explanationsContent"),(async()=>{try{L.style.display="inline",a()}catch(e){console.error(e)}})(),console.log("**** Hello ami développeur :-)\n\nLe code de WikiLerni est libre et vous pouvez le trouver à cette adresse :\nhttps://forge.chapril.org/Fab_Blab/WikiLerni\n\nPour les suggestions d'amélioration ou questions : dev@wililerni.com ****");const T=()=>{k=Date.now(),v.style.display="block",L.style.display="none";const e=window.location;""!==window.location.hash?(window.location.hash="",window.location.assign(e+"questionnaire")):window.location.assign(e+"#questionnaire")};let k=0;L.addEventListener("click",(function(e){try{e.preventDefault(),T()}catch(e){r(f,"p",b,"",["error"]),console.error(e)}})),""!=location.hash&&"#questionnaire"===location.hash&&T();let R={};v.addEventListener("submit",(function(e){try{e.preventDefault(),q.style.display="none",f.innerHTML="";const n=(e=>{const t={},n=new FormData(e);for(let e of n.entries())t[e[0]]=e[1];return t})(v);R.duration=Math.round((Date.now()-k)/1e3),R.nbQuestions=0,R.nbCorrectAnswers=0,R.QuestionnaireId=document.getElementById("questionnaireId").value;let s,o="",a=!1;for(let e in n)e.startsWith("isCorrect_response_")&&(s=e.substring(e.lastIndexOf("_")+1),n["question_id_response_"+s]!=o&&(o=n["question_id_response_"+s],R.nbQuestions++,a&&R.nbCorrectAnswers++,a=!0),"true"==n[e]?(document.getElementById("response_"+s).parentNode.classList.add("isCorrect"),void 0===n["response_"+s]&&(a=!1)):"on"===n["response_"+s]&&(a=!1,document.getElementById("response_"+s).parentNode.classList.add("isNotCorrect")));a&&R.nbCorrectAnswers++;let u=(e=>{if((0,t.isEmpty)(e.duration)||(0,t.isEmpty)(e.nbCorrectAnswers)||(0,t.isEmpty)(e.nbQuestions))return"";{const n=e.nbCorrectAnswers/e.nbQuestions,s={DURATION:e.duration,NBCORRECTANSWERS:e.nbCorrectAnswers,NBQUESTIONS:e.nbQuestions};let r="";return r=n<.4?(0,t.replaceAll)(i.checkResponsesOuputFail,s):n<.8?(0,t.replaceAll)(i.checkResponsesOuputMedium,s):(0,t.replaceAll)(i.checkResponsesOuputSuccess,s),r||""}})(R);r(f,"p",u,"",["info"]),window.location.hash="";const l=window.location;window.location.assign(l+"response");const c=document.querySelectorAll(".help");for(let e in c)null!=c[e].style&&(c[e].style.display="block")}catch(e){r(f,"p",b,"",["error"]),console.error(e)}}))}()}(); \ No newline at end of file diff --git a/front/src/group.js b/front/src/group.js deleted file mode 100644 index 0ef1805..0000000 --- a/front/src/group.js +++ /dev/null @@ -1,228 +0,0 @@ -// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN GROUPE DE QUIZS - -/// Il n'est pas nécessaire d'être connecté pour répondre au quiz et voir son résultat. -/// Mais si pas connecté, on propose à l'internaute de se connecter ou de créer un compte pour sauvegarder son résultat. -/// Dans ce but son résultat est stocké dans son navigateur. -/// Si il est connecté, l'enregistrement de son résultat se fait automatiquement côté serveur et ses éventuels précédents résultats sont affichés. - -// Fichier de configuration tirés du backend : -import { apiUrl, availableLangs, theme } from "../../config/instance.js"; -const lang=availableLangs[0]; -import { getPreviousAnswers, groupRoutes, saveAnswersRoute } from "../../config/questionnaires.js"; -const configTemplate = require("../../views/"+theme+"/config/"+lang+".js"); - -import { checkAnswerOuput, saveAnswer } from "./tools/answers.js"; -import { addElement } from "./tools/dom.js"; -import { helloDev, updateAccountLink } from "./tools/everywhere.js"; -import { getLocaly } from "./tools/clientstorage.js"; -import { getDatasFromInputs } from "./tools/forms.js"; -import { dateFormat, replaceAll } from "../../tools/main"; -import { loadMatomo } from "./tools/matomo.js"; -import { checkSession, getTimeDifference } from "./tools/users.js"; - -// Dictionnaires : -const { noPreviousAnswer, previousAnswersLine, previousAnswersStats, previousAnswersTitle, responseSavedError, wantToSaveResponses } = require("../../lang/"+lang+"/answer"); -const { serverError } = require("../../lang/"+lang+"/general"); - -// Principaux éléments du DOM manipulés : -const btnSubmit = document.getElementById("checkResponses"); -const divResponse = document.getElementById("response"); -const explanationsTitle = document.getElementById("explanationsTitle"); -const explanationsContent = document.getElementById("explanationsContent"); -const myForm = document.getElementById("group"); - -// Affiche le bouton de soumission + déclenche le chronomètre mesurant la durée de la réponse. -let chronoBegin=0; -const beginAnswer = () => -{ - chronoBegin=Date.now(); - btnSubmit.style.display="block"; -} - -let isConnected, user; -const initialise = async () => -{ - try - { - // Si JS activé, on affiche le bouton de soumission du formulaire : - beginAnswer(); - /* - isConnected=await checkSession(["user"]);// "user" car seuls les utilisateurs de base peuvent enregistrer leurs réponses aux quizs - // Si l'utilisateur est connecté et a déjà répondu à ce quiz, on affiche ses précédentes réponses à la place du texte servant à expliquer le topo aux nouveaux - if(isConnected) - { - user=getLocaly("user", true); - updateAccountLink(user.status, configTemplate);// lien vers le compte adapté pour les utilisateurs connectés - checkPreviousResponses(user); - } - else */ - loadMatomo(); - } - catch(e) - { - console.error(e); - } -} -initialise(); -helloDev(); - -// Traitement de l'envoi de la réponse de l'utilisateur : -let answer = {}; -myForm.addEventListener("submit", function(e) -{ - try - { - e.preventDefault(); - btnSubmit.style.display="none";// seulement un envoi à la fois, SVP :) - divResponse.innerHTML="";// supprime les éventuels messages déjà affichés. - const userResponses=getDatasFromInputs(myForm); - answer.duration=Math.round((Date.now()-chronoBegin)/1000); - answer.nbQuestions=0; - answer.nbCorrectAnswers=0; - answer.GroupId=document.getElementById("groupId").value; - // Les réponses sont regroupées par question, donc quand idQuestion change, on connaît le résultat pour la question précédente. - // Pour qu'une réponse soit bonne, il faut cocher toutes les bonnes réponses (si QCM) à la question ET cocher aucune des mauvaises. - let idChoice, idQuestion="", goodResponse=false; - for(let item in userResponses) - { - if(item.startsWith("isCorrect_response_"))// = Nouvelle réponse possible. - { - idChoice = item.substring(item.lastIndexOf("_") + 1); - if(userResponses["question_id_response_"+idChoice] != idQuestion) // = on commence à traiter une nouvelle question. - { - idQuestion=userResponses["question_id_response_"+idChoice]; - answer.nbQuestions++; - if(goodResponse) // = pas d'erreur à la question précédente - answer.nbCorrectAnswers++; - goodResponse=true;// La réponse est considérée comme bonne, jusqu'à la première erreur... - } - if(userResponses[item] == "true") - { - document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect"); - if(userResponses["response_"+idChoice] === undefined)// = une bonne réponse n'a pas été sélectionnée - goodResponse=false; - } - else - { - if(userResponses["response_"+idChoice] === "on") - { - goodResponse=false; // = une mauvaise réponse a été sélectionnée - document.getElementById("response_"+idChoice).parentNode.classList.add("isNotCorrect"); - } - } - } - } - // Si j'ai bien répondu à la dernière question, il faut le compter ici, car on est sorti de la boucle : - if(goodResponse) - answer.nbCorrectAnswers++; - - // Affichage du résultat, suivant si l'utilisateur est connecté ou pas et son score : - let getOuput=checkAnswerOuput(answer); - /*if(isConnected) - { - // Si l'utilisateur est connecté, on passe son résultat au serveur pour le sauvegarder. - const xhrSaveAnswer = new XMLHttpRequest(); - xhrSaveAnswer.open("POST", apiUrl+groupRoutes+saveAnswersRoute); - xhrSaveAnswer.onreadystatechange = function() - { - if (this.readyState == XMLHttpRequest.DONE) - { - let xhrResponse=JSON.parse(this.responseText); - if (this.status === 201 && (xhrResponse.message)) - { - getOuput+="
"+xhrResponse.message.replace("#URL", configTemplate.userHomePage); - checkPreviousResponses(user); - } - else - getOuput+="
"+responseSavedError.replace("#URL", configTemplate.userHomePage); - // Puis on le redirige vers son résultat : - window.location.hash=""; - const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"... - window.location.hash="explanations"; - } - } - xhrSaveAnswer.setRequestHeader("Authorization", "Bearer "+user.token); - xhrSaveAnswer.setRequestHeader("Content-Type", "application/json"); - answer.timeDifference=getTimeDifference();// On en profite pour mettre les pendules à l'heure. - xhrSaveAnswer.send(JSON.stringify(answer)); - } - else - { // Si internaute non connecté, on enregistre le résultat côté client pour permettre de le retrouver au moment de la création du compte ou de la connexion. - /*if(saveAnswer(answer)) - { - getOuput+="

"+wantToSaveResponses+"

"; - addElement(divResponse, "p", getOuput, "", ["success"]); - document.querySelector(".subscribeBtns").style.display="block"; - } - else // Mais inutile de proposer de créer un compte si le stockage local ne fonctionne pas */ - addElement(divResponse, "p", getOuput, "", ["success"]); - // Puis on le redirige vers son résultat : - window.location.hash=""; - const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"... - window.location.hash="response"; - //} - // + Affichage des textes d'explications pour chaque question - const explanations=document.querySelectorAll(".help"); - for(let i in explanations) - if(explanations[i].style !== undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?) - explanations[i].style.display="block"; - } - catch(e) - { - addElement(divResponse, "p", serverError, "", ["error"]); - console.error(e); - } -}) - -/* -// Fonction vérifiant les précédentes réponses de l'utilisateur. -// Utile si connecté lors du premier chargement de la page, puis après une nouvelle réponse. -const checkPreviousResponses = (user) => -{ - const xhrPreviousRes = new XMLHttpRequest(); - xhrPreviousRes.open("GET", apiUrl+groupRoutes+getPreviousAnswers+user.id+"/"+document.getElementById("groupId").value); - xhrPreviousRes.onreadystatechange = function() - { - if (this.readyState == XMLHttpRequest.DONE) - { - let response=JSON.parse(this.responseText); - if (this.status === 200) - { - const nbResponses=response.length; - let previousAnswersContent=""; - addElement(explanationsTitle, "span", previousAnswersTitle.replace("#NOM", user.name)); - if(nbResponses!==0) - { - let totNbQuestions=0, totNbCorrectAnswers=0, totDuration=0, mapLineContent; - for(let i in response) - { - totNbQuestions+=response[i].nbQuestions;// ! on ne peut se baser sur la version actuelle du quiz, car le nombre de questions a pu évoluer. - totNbCorrectAnswers+=response[i].nbCorrectAnswers; - totDuration+=response[i].duration; - mapLineContent = - { - DATEANSWER : dateFormat(response[i].createdAt, lang), - NBCORRECTANSWERS : response[i].nbCorrectAnswers, - NBQUESTIONS : response[i].nbQuestions, - AVGDURATION : response[i].duration - }; - previousAnswersContent+="
  • "+replaceAll(previousAnswersLine, mapLineContent)+"
  • "; - } - mapLineContent = - { - AVGDURATION : Math.round(totDuration/nbResponses), - AVGCORRECTANSWERS : Math.round(totNbCorrectAnswers/totNbQuestions*100) - }; - previousAnswersContent="
    "+replaceAll(previousAnswersStats, mapLineContent)+"
    "+previousAnswersContent; - addElement(explanationsContent, "ul", previousAnswersContent); - } - else - addElement(explanationsContent, "ul", noPreviousAnswer); - // dans un cas comme dans l'autre, bouton pour revenir à l'accueil du compte - addElement(explanationsContent, "p", ""+configTemplate.userHomePageTxt+"", "", ["btn"], "", false); - } - } - } - xhrPreviousRes.setRequestHeader("Authorization", "Bearer "+user.token); - xhrPreviousRes.send(); -} */ \ No newline at end of file diff --git a/front/src/questionnaire.js b/front/src/questionnaire.js deleted file mode 100644 index 2bafc1b..0000000 --- a/front/src/questionnaire.js +++ /dev/null @@ -1,259 +0,0 @@ -// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN QUIZ - -/// Il n'est pas nécessaire d'être connecté pour répondre au quiz et voir son résultat. -/// Mais si pas connecté, on propose à l'internaute de se connecter ou de créer un compte pour sauvegarder son résultat. -/// Dans ce but son résultat est stocké dans son navigateur. -/// Si il est connecté, l'enregistrement de son résultat se fait automatiquement côté serveur et ses éventuels précédents résultats sont affichés. - -// Fichier de configuration tirés du backend : -import { apiUrl, availableLangs, theme } from "../../config/instance.js"; -const lang=availableLangs[0]; -import { getPreviousAnswers, questionnaireRoutes, saveAnswersRoute } from "../../config/questionnaires.js"; -const configTemplate = require("../../views/"+theme+"/config/"+lang+".js"); - -import { checkAnswerOuput, saveAnswer } from "./tools/answers.js"; -import { addElement } from "./tools/dom.js"; -import { helloDev, updateAccountLink } from "./tools/everywhere.js"; -import { getLocaly } from "./tools/clientstorage.js"; -import { getDatasFromInputs } from "./tools/forms.js"; -import { dateFormat, replaceAll } from "../../tools/main"; -import { loadMatomo } from "./tools/matomo.js"; -import { checkSession, getTimeDifference } from "./tools/users.js"; - -// Dictionnaires : -const { noPreviousAnswer, previousAnswersLine, previousAnswersStats, previousAnswersTitle, responseSavedError, wantToSaveResponses } = require("../../lang/"+lang+"/answer"); -const { serverError } = require("../../lang/"+lang+"/general"); - -// Principaux éléments du DOM manipulés : -const myForm = document.getElementById("questionnaire"); -const divResponse = document.getElementById("response"); -const btnShow = document.getElementById("showQuestionnaire"); -const btnSubmit = document.getElementById("checkResponses"); -const explanationsTitle = document.getElementById("explanationsTitle"); -const explanationsContent = document.getElementById("explanationsContent"); - -let isConnected, user; -const initialise = async () => -{ - try - { - btnShow.style.display="inline";// bouton caché si JS inactif, car JS nécessaire pour vérifier les réponses - /* - isConnected=await checkSession(["user"]);// "user" car seuls les utilisateurs de base peuvent enregistrer leurs réponses aux quizs - // Si l'utilisateur est connecté et a déjà répondu à ce quiz, on affiche ses précédentes réponses à la place du texte servant à expliquer le topo aux nouveaux - if(isConnected) - { - user=getLocaly("user", true); - updateAccountLink(user.status, configTemplate);// lien vers le compte adapté pour les utilisateurs connectés - checkPreviousResponses(user); - } - else */ - loadMatomo(); - } - catch(e) - { - console.error(e); - } -} -initialise(); -helloDev(); - -// Affichage du questionnaire quand l'utilisateur clique sur le bouton ou si l'id du formulaire est passée par l'url. -// Déclenche en même temps le chronomètre mesurant la durée de la réponse aux questions. -const showQuestionnaire = () => -{ - chronoBegin=Date.now(); - myForm.style.display="block"; - btnShow.style.display="none"; - const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview". - if(window.location.hash!=="") - { - window.location.hash="";// ! le "#" reste - window.location.assign(here+"questionnaire"); - } - else - window.location.assign(here+"#questionnaire"); -} -let chronoBegin=0; -btnShow.addEventListener("click", function(e) -{ - try - { - e.preventDefault(); - showQuestionnaire(); - } - catch(e) - { - addElement(divResponse, "p", serverError, "", ["error"]); - console.error(e); - } -}); -// Lien passé par mail pour voir directement le quiz -if(location.hash!="" && location.hash==="#questionnaire") - showQuestionnaire(); - -// Traitement de l'envoi de la réponse de l'utilisateur : -let answer = {}; -myForm.addEventListener("submit", function(e) -{ - try - { - e.preventDefault(); - btnSubmit.style.display="none";// seulement un envoi à la fois, SVP :) - divResponse.innerHTML="";// supprime les éventuels messages déjà affichés - const userResponses=getDatasFromInputs(myForm); - answer.duration=Math.round((Date.now()-chronoBegin)/1000); - answer.nbQuestions=0; - answer.nbCorrectAnswers=0; - answer.QuestionnaireId=document.getElementById("questionnaireId").value; - // Les réponses sont regroupées par question, donc quand idQuestion change, on connaît le résultat pour la question précédente. - // Pour qu'une réponse soit bonne, il faut cocher toutes les bonnes réponses (si QCM) à la question ET cocher aucune des mauvaises. - let idChoice, idQuestion="", goodResponse=false; - for(let item in userResponses) - { - if(item.startsWith("isCorrect_response_"))// = Nouvelle réponse possible. - { - idChoice = item.substring(item.lastIndexOf("_") + 1); - // si on change de queston - if(userResponses["question_id_response_"+idChoice]!=idQuestion) // on commence à traiter une nouvelle question - { - idQuestion=userResponses["question_id_response_"+idChoice]; - answer.nbQuestions++; - if(goodResponse) // résultat de la question précédente - answer.nbCorrectAnswers++; - goodResponse=true;// réponse bonne jusqu'à la première erreur... - } - if(userResponses[item]=="true") - { - document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect"); - if(userResponses["response_"+idChoice]===undefined)// une bonne réponse n'a pas été sélectionnée - goodResponse=false; - } - else - { - if(userResponses["response_"+idChoice]==="on")// réponse cochée ne faisant pas partie des bonnes - { - goodResponse=false; - document.getElementById("response_"+idChoice).parentNode.classList.add("isNotCorrect"); - } - } - } - } - // si j'ai bien répondu à la dernière question, il faut le compter ici, car je suis sorti de la boucle : - if(goodResponse) - answer.nbCorrectAnswers++; - - // Affichage du résultat, suivant si l'utilisateur est connecté ou pas et son score : - let getOuput=checkAnswerOuput(answer); - /* - if(isConnected) - { - // Si l'utilisateur est connecté, on enregistre son résultat sur le serveur. - const xhrSaveAnswer = new XMLHttpRequest(); - xhrSaveAnswer.open("POST", apiUrl+questionnaireRoutes+saveAnswersRoute); - xhrSaveAnswer.onreadystatechange = function() - { - if (this.readyState == XMLHttpRequest.DONE) - { - let xhrResponse=JSON.parse(this.responseText); - if (this.status === 201 && (xhrResponse.message)) - { - getOuput+="
    "+xhrResponse.message.replace("#URL", configTemplate.userHomePage); - checkPreviousResponses(user); - } - else - getOuput+="
    "+responseSavedError.replace("#URL", configTemplate.userHomePage); - // on redirige vers le résultat - window.location.hash=""; - const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"... - window.location.assign(here+"explanations"); - } - } - xhrSaveAnswer.setRequestHeader("Authorization", "Bearer "+user.token); - xhrSaveAnswer.setRequestHeader("Content-Type", "application/json"); - answer.timeDifference=getTimeDifference();// on en profite pour mettre les pendules à l'heure. - xhrSaveAnswer.send(JSON.stringify(answer)); - } - else - { // si pas connecté, on enregistre le résultat côté client pour permettre de le retrouver au moment de la création du compte ou de la connexion. - if(saveAnswer(answer)) - { - getOuput+="

    "+wantToSaveResponses; - addElement(divResponse, "p", getOuput, "", ["info"]); - document.querySelector(".subscribeBtns").style.display="block"; - } - else // inutile de proposer de créer un compte si le stockage local ne fonctionne pas*/ - addElement(divResponse, "p", getOuput, "", ["info"]); - // on redirige vers le résultat - window.location.hash=""; - const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"... - window.location.assign(here+"response"); - //} - // + affichage des textes d'explications pour chaque question - const explanations=document.querySelectorAll(".help"); - for(let i in explanations) - { - if(explanations[i].style!=undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?) - explanations[i].style.display="block"; - } - } - catch(e) - { - addElement(divResponse, "p", serverError, "", ["error"]); - console.error(e); - } -}) - -/* -// Fonction vérifiant les précédentes réponses de l'utilisateur -// Utile si connecté lors du premier chargement de la page, puis après une nouvelle réponse -const checkPreviousResponses = (user) => -{ - const xhrPreviousRes = new XMLHttpRequest(); - xhrPreviousRes.open("GET", apiUrl+questionnaireRoutes+getPreviousAnswers+user.id+"/"+document.getElementById("questionnaireId").value); - xhrPreviousRes.onreadystatechange = function() - { - if (this.readyState == XMLHttpRequest.DONE) - { - let response=JSON.parse(this.responseText); - if (this.status === 200) - { - const nbResponses=response.length; - let previousAnswersContent=""; - addElement(explanationsTitle, "span", previousAnswersTitle.replace("#NOM", user.name)); - if(nbResponses!==0) - { - let totNbQuestions=0, totNbCorrectAnswers=0, totDuration=0, mapLineContent; - for(let i in response) - { - totNbQuestions+=response[i].nbQuestions;// ! on ne peut se baser sur la version actuelle du quiz, car le nombre de questions a pu évoluer. - totNbCorrectAnswers+=response[i].nbCorrectAnswers; - totDuration+=response[i].duration; - mapLineContent = - { - DATEANSWER : dateFormat(response[i].createdAt, lang), - NBCORRECTANSWERS : response[i].nbCorrectAnswers, - NBQUESTIONS : response[i].nbQuestions, - AVGDURATION : response[i].duration - }; - previousAnswersContent+="
  • "+replaceAll(previousAnswersLine, mapLineContent)+"
  • "; - } - mapLineContent = - { - AVGDURATION : Math.round(totDuration/nbResponses), - AVGCORRECTANSWERS : Math.round(totNbCorrectAnswers/totNbQuestions*100) - }; - previousAnswersContent="
    "+replaceAll(previousAnswersStats, mapLineContent)+"
    "+previousAnswersContent; - addElement(explanationsContent, "ul", previousAnswersContent); - } - else - addElement(explanationsContent, "ul", noPreviousAnswer); - // dans un cas comme dans l'autre, bouton pour revenir à l'accueil du compte - addElement(explanationsContent, "p", ""+configTemplate.userHomePageTxt+"", "", ["btn"], "", false); - - } - } - } - xhrPreviousRes.setRequestHeader("Authorization", "Bearer "+user.token); - xhrPreviousRes.send(); -}*/ \ No newline at end of file diff --git a/front/src/quiz.js b/front/src/quiz.js new file mode 100644 index 0000000..a764c32 --- /dev/null +++ b/front/src/quiz.js @@ -0,0 +1,219 @@ +// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN QUIZ + +/// Il n'est pas nécessaire d'avoir une base de données locale active pour répondre à un quiz. +/// Mais si ce n'est pas déjà le cas et que cela semble techniquement possible, on propose au répondant de sauvegarder son résultat. +/// Dans ce but son résultat est stocké temporairement. +/// Si la base de donnée locale existe déjà, l'enregistrement de son résultat se fait automatiquement dans IndexedDB et ses éventuels précédents résultats sont affichés. + +// Configurations générales provenant du backend : +import { availableLangs, theme } from "../../config/instance.js"; +const lang=availableLangs[0]; +const configTemplate=require("../../views/"+theme+"/config/"+lang+".js"); + +// Fonctions : +import { checkAllPreviousResults, checkUserAnswers, getResultOutput, resultsOpenDb, saveNewQuiz, saveResult, saveResultTemp, showPreviousResults } from "./tools/answers.js"; +import { saveIsReady } from "./tools/clientstorage.js"; +import { addElement } from "./tools/dom.js"; +import { helloDev } from "./tools/everywhere.js"; +import { isEmpty } from "../../tools/main"; +import { loadMatomo } from "./tools/matomo.js"; + +// Textes : +const { wantToSaveResponses, wantToSeaPreviousResults }=require("../../lang/"+lang+"/answer"); +const { localDBConnexionFail, serverError }=require("../../lang/"+lang+"/general"); + +// Informations du quiz, utile pour enregistrement dans base de donnée locale : +const myForm=document.getElementById("quiz");// quiz +const quizInfos= +{ + url: window.location.pathname, + GroupId: document.getElementById("groupId").value, + QuestionnaireId: document.getElementById("questionnaireId").value, + title: myForm.dataset.title +}; + +// Autres éléments du DOM manipulés : +const propose2Save=document.getElementById("propose2Save"); +const btnSave=document.getElementById("want2Save"); +const btnSubmit=document.getElementById("checkResponses"); +const divResponse=document.getElementById("response"); + +// L'url permet de savoir si nous sommes sur un quiz unique ou groupé : +let btnShow; +if(quizInfos.url.indexOf("/gp/") == -1) + btnShow=document.getElementById("showQuestionnaire"); // le quiz est affiché directement pour les groupes + +let userDB, allPreviousAnswers=[]; +const initialise = async () => +{ + try + { + if(btnShow) + btnShow.style.display="inline"; // Le bouton est caché si le JS inactif, car le JS est nécessaire pour la suite... + // On vérifie si la navigateur accepte l'utilisation d'IndexedDB : + const saveIsPossible=saveIsReady(); + if(saveIsPossible) + { + // On essaye ensuite de se connecter à la base de données (ce qui va la créer si inexistante) : + userDB=await resultsOpenDb("myAnswers", 1); + if(userDB === undefined) + console.error(localDBConnexionFail); + else + { + // Vérifie si l'utilisateur a déjà sauvegardé au moins un résultat (et donc est ok pour les sauvegardes) : + allPreviousAnswers=await checkAllPreviousResults(userDB); + if(allPreviousAnswers.length !== 0) + await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId); + } + } + loadMatomo(); + } + catch(e) + { + console.error(e); + } +} +initialise(); +helloDev(); + +// Affichage du quiz, quand il est caché par défaut (= l'internaute doit d'abord lire un article Wikipédia). +// Déclenche en même temps le chronomètre mesurant la durée de réponse aux questions. +const showQuestionnaire = () => +{ + chronoBegin=Date.now(); + myForm.style.display="block"; + btnShow.style.display="none"; + const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview". + if(window.location.hash !== "") + { + window.location.hash="";// ! le "#" reste + window.location.assign(here+"questionnaire"); + } + else + window.location.assign(here+"#questionnaire"); +} + +// L'utilisateur demande à voir le quiz : +let chronoBegin=0; +if(btnShow) +{ + btnShow.addEventListener("click", function(e) + { + try + { + e.preventDefault(); + showQuestionnaire(); + } + catch(e) + { + addElement(divResponse, "p", serverError, "", ["error"]); + console.error(e); + } + }); + // Un lien peut être passé pour voir directement le quiz : + if(location.hash !== "" && location.hash === "#questionnaire") + showQuestionnaire(); +} + +// Dans le cas d'un quiz groupé, le chrono est lancé dès l'affichage : +if(quizInfos.GroupId != "0") +{ + chronoBegin=Date.now(); + btnSubmit.style.display="block"; +} + +// Traitement de l'envoi de la réponse de l'utilisateur : +let answer; +myForm.addEventListener("submit", async function(e) +{ + try + { + e.preventDefault(); + btnSubmit.style.display="none"; // seulement une réponse à la fois, SVP :) + divResponse.innerHTML=""; // supprime les éventuels messages déjà affichés + answer=checkUserAnswers(myForm); + answer.duration=Math.round((Date.now()-chronoBegin)/1000); + answer.QuestionnaireId=quizInfos.QuestionnaireId; + answer.GroupId=quizInfos.GroupId; + + // Affichage du résultat, suivant les cas : + let getOuput=getResultOutput(answer); + // S'il y a déjà une réponse dans la bd, c'est que l'utilisateur est ok pour les enregister. + if(allPreviousAnswers.length !==0) + { + const saveResponses=await saveResult(userDB, answer); + if(saveResponses) + await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId); + getOuput+="

    "+wantToSeaPreviousResults.replace("URL","#explanations"); + // Nouveau quiz pour cette personne ? + await saveNewQuiz(userDB, quizInfos); + } + else + { + // S'il n'a pas encore de données, on stocke temporairement le résultat et propose de l'enregistrer : + if(saveResultTemp(answer) && !isEmpty(userDB)) + { + getOuput+="

    "+wantToSaveResponses; + propose2Save.style.display="block"; + } + } + addElement(divResponse, "p", getOuput, "", ["info"]); + // On redirige vers le résultat : + const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview". + if(window.location.hash !== "") + { + window.location.hash=""; // ! le "#" reste + window.location.assign(here+"response"); + } + else + window.location.assign(here+"#response"); + // + Affichage des textes d'explication pour chaque question + const explanations=document.querySelectorAll(".help"); + for(let i in explanations) + { + if(explanations[i].style != undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?) + explanations[i].style.display="block"; + } + } + catch(e) + { + addElement(divResponse, "p", serverError, "", ["error"]); + console.error(e); + } +}) + +// L'utilisateur demande à sauvegarder son résultat : +btnSave.addEventListener("click", async function(e) +{ + try + { + e.preventDefault(); + if(!isEmpty(userDB) && !isEmpty(answer)) // On ne devrait pas me proposer d'enregistrer dans ce cas, mais... + { + const saveResponses=await saveResult(userDB, answer); + if(saveResponses) + { + // Nouvel enregistrement = actualisation nécessaire de la liste des résultats pour ce quiz : + await showPreviousResults(userDB, quizInfos.QuestionnaireId, quizInfos.GroupId); + // Nouveau quiz + await saveNewQuiz(userDB, quizInfos); + // Redirection vers la liste des résultats : + const here=window.location; // window.location à ajouter pour ne pas quitter la page en mode "preview". + if(window.location.hash !== "") + { + window.location.hash="";// ! le "#" reste + window.location.assign(here+"explanations"); + } + else + window.location.assign(here+"#explanations"); + } + propose2Save.style.display="none"; + } + } + catch(e) + { + addElement(divResponse, "p", serverError, "", ["error"]); + console.error(e); + } + +}); \ No newline at end of file diff --git a/front/src/tools/answers.js b/front/src/tools/answers.js index 2ad8466..9d73dd6 100644 --- a/front/src/tools/answers.js +++ b/front/src/tools/answers.js @@ -1,46 +1,350 @@ -const configFrontEnd = require("../config/general"); +// Fonctions externes : +import { addElement } from "./dom.js"; +import { dateFormat, isEmpty, replaceAll } from "../../../tools/main"; +import { saveLocaly, getStore } from "./clientstorage.js"; +import { getDatasFromInputs } from "./forms.js"; -import { saveLocaly } from "./clientstorage.js"; -import { isEmpty, replaceAll } from "../../../tools/main"; +// Textes : +import { availableLangs } from "../../../config/instance.js"; +const lang=availableLangs[0]; +const { localDBNeedDatas, localDBNeedQuizId, noPreviousResults, previousResultsLine, previousResultsTitle, previousResultsStats, userAnswersFail, userAnswersMedium, userAnswersSuccess }=require("../../../lang/"+lang+"/answer"); -const txt = require("../../../lang/"+configFrontEnd.lang+"/answer"); - -// Enregistrement côté client du dernier résultat à un quiz en attendant d'être connecté -export const saveAnswer = (answer) => +// Vérification des réponses de l'utilisateur au quiz +export const checkUserAnswers = (myForm) => { - if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions) && (!isEmpty(answer.QuestionnaireId) || !isEmpty(answer.GroupId))) + // Les réponses sont regroupées par question, donc quand idQuestion change, on connaît le résultat pour la question précédente. + // Pour qu'une réponse soit bonne, il faut avoir coché toutes les bonnes réponses (si QCM) à la question ET n'avoir coché aucune des mauvaises. + const userResponses=getDatasFromInputs(myForm); + let idChoice, idQuestion="", goodResponse=false, answer={ nbCorrectAnswers:0, nbQuestions:0 }; + for(let item in userResponses) { - saveLocaly("lastAnswer", answer); - return true; + if(item.startsWith("isCorrect_response_")) // = Nouvelle réponse possible. + { + idChoice=item.substring(item.lastIndexOf("_")+1); + // Si on change de question : + if(userResponses["question_id_response_"+idChoice] != idQuestion) // on commence à traiter une nouvelle question + { + idQuestion=userResponses["question_id_response_"+idChoice]; + answer.nbQuestions++; + if(goodResponse) // Résultat de la question précédente + answer.nbCorrectAnswers++; + goodResponse=true;// Réponse bonne jusqu'à la première erreur... + } + if(userResponses[item] == "true") + { + document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect"); + if(userResponses["response_"+idChoice] === undefined) // Une des bonnes réponses n'a pas été sélectionnée :( + goodResponse=false; + } + else + { + if(userResponses["response_"+idChoice] === "on") // Réponse cochée ne faisant pas partie des bonnes :( + { + goodResponse=false; + document.getElementById("response_"+idChoice).parentNode.classList.add("isNotCorrect"); + } + } + } } + // Si j'ai bien répondu à la dernière question, il faut le compter ici, car je suis sorti de la boucle : + if(goodResponse) + answer.nbCorrectAnswers++; + return answer; +} + +// Retourne un texte suivant le nombre de bonnes réponses +export const getResultOutput = (result) => +{ + if(!isEmpty(result.duration) && !isEmpty(result.nbCorrectAnswers) && !isEmpty(result.nbQuestions)) + { + const ratio=result.nbCorrectAnswers/result.nbQuestions; + const mapObj= + { + DURATION: result.duration, + NBCORRECTANSWERS: result.nbCorrectAnswers, + NBQUESTIONS: result.nbQuestions + } + let output=""; + if(ratio < 0.4) + output=replaceAll(userAnswersFail, mapObj); + else if(ratio < 0.8) + output=replaceAll(userAnswersMedium, mapObj); + else + output=replaceAll(userAnswersSuccess, mapObj); + return output; + } + else + return ""; +} + +const checkIfResultIsComplete = (result) => +{ + if(!isEmpty(result.duration) && !isEmpty(result.nbCorrectAnswers) && !isEmpty(result.nbQuestions) && (!isEmpty(result.QuestionnaireId) || !isEmpty(result.GroupId))) + return true; else return false; } -// Retourne un texte suivant le nombre de bonnes réponses -export const checkAnswerOuput = (answer) => +// Enregistrement temporaire du dernier résultat à un quiz en attendant de savoir si l'utilisateur souhaite une sauvegarde durable : +export const saveResultTemp = (result) => { - if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions)) + if(checkIfResultIsComplete(result)) { - const ratio=answer.nbCorrectAnswers/answer.nbQuestions; - const mapObj= - { - DURATION: answer.duration, - NBCORRECTANSWERS: answer.nbCorrectAnswers, - NBQUESTIONS: answer.nbQuestions - } - let output=""; - if(ratio < 0.4) - output=replaceAll(txt.checkResponsesOuputFail, mapObj); - else if(ratio < 0.8) - output=replaceAll(txt.checkResponsesOuputMedium, mapObj); - else - output=replaceAll(txt.checkResponsesOuputSuccess, mapObj); - if(output) - return output; - else - return ""; + saveLocaly("lastResult", result); // écrasera l'éventuel résultat précédent. + return true; } else - return ""; + { + console.error(localDBNeedDatas); + console.log(result); + return false; + } +} + +// Se connecte à la base de données de sauvegarde des résultats +// Et la créé si c'est la première connexion (ou mise à jour de version) +export const resultsOpenDb = (dbName, dbVersion) => +{ + return new Promise( (resolve, reject) => + { + let req=indexedDB.open(dbName, dbVersion); + req.onupgradeneeded= (e) => + { + // Création du stockage des quizs auxquels l'utilisateur a déjà répondu : + let store=e.currentTarget.result.createObjectStore("userQuizs", { keyPath: "id", autoIncrement: true }); + store.createIndex("url", "url", { unique: true }); + store.createIndex("QuestionnaireId", "id", { unique: true }); // = simple quiz + store.createIndex("GroupId", "id", { unique: true }); // = quiz après lecture d'un ou +sieurs articles. + store.createIndex("title", "title", { unique: false }); + // Création du stockage des résultats : + store=e.currentTarget.result.createObjectStore("userResults", { keyPath: "id", autoIncrement: true }); + store.createIndex("QuestionnaireId", "QuestionnaireId", { unique: false }); + store.createIndex("GroupId", "GroupId", { unique: false }); + store.createIndex("duration", "duration", { unique: true }); + store.createIndex("nbCorrectAnswers", "nbCorrectAnswers", { unique: false }); + store.createIndex("nbQuestions", "nbQuestions", { unique: false }); + store.createIndex("date", "date", { unique: false }); // bien que doublons peu probables... + /// Revoir comment gérér les mises à jour de version de la BD sans tout casser... + }; + req.onsuccess= (e) => + { + resolve(req.result); // On retourne la base de données + }; + req.onerror= (e) => + { + console.error(e); // dans cette fonction, peut simplement planter parce que navigation privée... + resolve(undefined); + }; + }) +} + +// Fonction cherchant les éventuels résultats déjà enregistrés pour un quiz : +export const checkPreviousResultsForId= (db, QuestionnaireId=0, GroupId=0) => +{ + return new Promise( (resolve, reject) => + { + const resultsStore=getStore(db, "userResults", "readonly"); + let myIndex, getResults; + if(QuestionnaireId != 0) + { + myIndex=resultsStore.index("QuestionnaireId"); + getResults=myIndex.openCursor(QuestionnaireId); + } + else if(GroupId != 0) + { + myIndex=resultsStore.index("GroupId"); + getResults=myIndex.openCursor(GroupId); + } + else + reject(localDBNeedQuizId); + + const answers=[]; + getResults.onsuccess = (e) => + { + const cursor=e.target.result; + if (cursor) + { + answers.push(cursor.value); + cursor.continue(); + } + else // = on a parcouru toutes les données + resolve(answers); + }; + getResults.onerror= (e) => + { + console.error(e); + reject(e); + }; + }) +} + +// Fonction affichant les précédents résultats connus pour le quiz encours : +export const showPreviousResults = async (userDB, QuestionnaireId, GroupId) => +{ + if(isEmpty(userDB) || (isEmpty(QuestionnaireId) && isEmpty(GroupId))) + return false; + + // Recherche dans la base de données : + const previousResults=await checkPreviousResultsForId(userDB, QuestionnaireId, GroupId); + if(previousResults === undefined) // Peut être un tableau vide si ancun résultat enregistré, mais pas undefined. + console.error(localDBGetPreviousResultsFail); + else + { + const explanationsContent=document.getElementById("explanationsContent"); + const explanationsTitle=document.getElementById("explanationsTitle"); + // Les précédents résultats sont classés par ordre d'enregistrement et sont donc à inverser : + previousResults.reverse(); + const nbPrevious=previousResults.length; + let previousResultsContent=""; + addElement(explanationsTitle, "span", previousResultsTitle); + if(nbPrevious !== 0) + { + let totNbQuestions=0, totNbCorrectAnswers=0, totDuration=0, mapLineContent; + for(const i in previousResults) + { + totNbQuestions+=previousResults[i].nbQuestions; // ! on ne peut se baser sur la version actuelle du quiz, car le nombre de questions peut évolué. + totNbCorrectAnswers+=previousResults[i].nbCorrectAnswers; + totDuration+=previousResults[i].duration; + mapLineContent= + { + DATEANSWER: dateFormat(previousResults[i].date, lang), + NBCORRECTANSWERS: previousResults[i].nbCorrectAnswers, + NBQUESTIONS: previousResults[i].nbQuestions, + AVGDURATION: previousResults[i].duration + }; + previousResultsContent+="
  • "+replaceAll(previousResultsLine, mapLineContent)+"
  • "; + } + mapLineContent= + { + AVGDURATION: Math.round(totDuration/nbPrevious), + AVGCORRECTANSWERS: Math.round(totNbCorrectAnswers/totNbQuestions*100) + }; + previousResultsContent="
    "+replaceAll(previousResultsStats, mapLineContent)+"
    "+previousResultsContent; + addElement(explanationsContent, "ul", previousResultsContent); + } + else + addElement(explanationsContent, "ul", noPreviousResults); + /// Revoir : ajouter un lien vers la page listant les quizs auxquels l'utilisateur a répondu + /// addElement(explanationsContent, "p", ""+configTemplate.userHomePageTxt+"", "", ["btn"], "", false); + } +} + +// Recherche de tous les résultats déjà enregistrés +export const checkAllPreviousResults = (db) => +{ + return new Promise( (resolve, reject) => + { + const resultsStore=getStore(db, "userResults", "readonly"); + const getResults=resultsStore.getAll(); + getResults.onsuccess = (e) => + { + resolve(e.target.result); + }; + getResults.onerror = (e) => + { + console.error(e); + reject(e); + }; + }) +} + +// Tentative d'enregistrement d'un résultat : +export const saveResult = (db, result) => +{ + return new Promise( (resolve, reject) => + { + if(checkIfResultIsComplete(result)) + { + const resultsStore=getStore(db, "userResults", "readwrite"); + let req; + try + { + result.date=new Date(); + req=resultsStore.add(result); + } + catch (e) + { + console.error(e); + reject(e); + } + req.onsuccess= (e) => + { + resolve(true); + }; + req.onerror= (e) => + { + console.error(e); + reject(e); + }; + } + else + { + console.error(localDBNeedDatas); + reject(localDBNeedDatas); + } + }) +} + +// Fonction retournant tous les quizs enregistrés pour cette personne +export const getAllQuizs = (db) => +{ + return new Promise( (resolve, reject) => + { + const quizsStore=getStore(db, "userQuizs", "readonly"); + const getquizs=quizsStore.getAll(); + getquizs.onsuccess = (e) => + { + resolve(e.target.result); + }; + getquizs.onerror= (e) => + { + console.error(e); + reject(e); + }; + }) +} + +// Ajout le quiz à la base de donnée de l'utilisateur, s'il n'y existe pas déjà +export const saveNewQuiz = async (userDB, quizInfos) => +{ + const getUserQuizs=await getAllQuizs(userDB); + const checkQuizExist=getUserQuizs.find(quiz => quiz.url == quizInfos.url); + if(checkQuizExist === undefined) + await saveQuiz(userDB, quizInfos); +} + +// Enregistre le quiz parmi ceux auxquels l'internaute a répondu, s'il n'y est pas déjà. +export const saveQuiz = (db, quiz) => +{ + return new Promise( (resolve, reject) => + { + if(!isEmpty(quiz.url) && !isEmpty(quiz.title) && (!isEmpty(quiz.QuestionnaireId) || !isEmpty(quiz.GroupId))) + { + const quizsStore=getStore(db, "userQuizs", "readwrite"); + let req; + try + { + req=quizsStore.add(quiz); + } + catch (e) + { + console.error(e); + reject(e); + } + req.onsuccess= (e) => + { + resolve(true); + }; + req.onerror= (e) => + { + console.error(e); + reject(e); + }; + } + else + { + console.error(localDBNeedDatas); + reject(localDBNeedDatas); + } + }) } \ No newline at end of file diff --git a/front/src/tools/clientstorage.js b/front/src/tools/clientstorage.js index 5182280..1b672bf 100644 --- a/front/src/tools/clientstorage.js +++ b/front/src/tools/clientstorage.js @@ -1,6 +1,6 @@ // FONCTIONS UTILES AU STOCKAGE LOCAL (SESSION, COOKIES, INDEXDB, ETC.) -// Revenir pour gérer le cas où local.storage n'est pas connu pour utiliser cookie - +// Éviter sessionStorage dont le contenu n'est pas gardé d'un onglet à l'autre : https://developer.mozilla.org/fr/docs/Web/API/Window/sessionStorage + export const saveLocaly = (name, data) => { localStorage.setItem(name, JSON.stringify(data)); @@ -17,4 +17,18 @@ export const getLocaly = (name, json=false) => export const removeLocaly = (name) => { localStorage.removeItem(name); -} \ No newline at end of file +} + +export const saveIsReady = () => +{ + if (!window.indexedDB) + return false; + else + return true; +} + +export const getStore = (db, store_name, mode) => + { + const tx=db.transaction(store_name, mode); + return tx.objectStore(store_name); + } \ No newline at end of file diff --git a/front/src/tools/users.js b/front/src/tools/users.js index ca00b99..8611102 100644 --- a/front/src/tools/users.js +++ b/front/src/tools/users.js @@ -53,6 +53,9 @@ export const checkAnswerDatas = (datas) => return datas; } + +/// La suite est toujours utile pour l'admin. + // Cette fonction teste la connexion de l'utilisateur d'une page // On peut fournis une liste de statuts acceptés (si vide = tous), ainsi qu'une url de redirection si non connecté, un message d'erreur à afficher sur la page de destination et l'url sur laquelle revenir une fois connecté export const checkSession = async (status=[], urlRedirection, message, urlWanted) => diff --git a/front/webpack.config.js b/front/webpack.config.js index b763039..30a47ce 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -9,7 +9,6 @@ module.exports = connection: "./src/connection.js", deconnection: "./src/deconnection.js", deleteValidation: "./src/deleteValidation.js", - group: "./src/group.js", groupElement: "./src/groupElement.js", homeManager: "./src/homeManager.js", homeUser: "./src/homeUser.js", @@ -21,7 +20,7 @@ module.exports = newLoginValidation: "./src/newLoginValidation.js", paymentPage: "./src/paymentPage.js", polyfill: "babel-polyfill", - questionnaire: "./src/questionnaire.js", + quiz: "./src/quiz.js", subscribe: "./src/subscribe.js", subscribeValidation: "./src/subscribeValidation.js", unsubscribe: "./src/unsubscribe.js" diff --git a/lang/fr/answer.js b/lang/fr/answer.js index 38546ad..4b98029 100644 --- a/lang/fr/answer.js +++ b/lang/fr/answer.js @@ -1,8 +1,8 @@ module.exports = { - checkResponsesOuputFail : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est certain, vous ferez mieux la prochaine fois !", - checkResponsesOuputMedium : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est pas mal du tout !", - checkResponsesOuputSuccess : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. Bravo ! Rien ne vous échappe !", + localDBGetPreviousResultsFail: "Bug de récupération des résultats précédents.", + localDBNeedDatas: "Il manque des données nécessaires à l'enregistrement.", + localDBNeedQuizId: "Aucun identifiant n'a été fourni pour le quiz.", needIntegerNumberCorrectResponses : "Le nombre de réponses correctes doit être un nombre entier.", needIntegerNumberSecondesResponse : "La durée de la réponse doit être un nombre entier de secondes.", needIntegerNumberUserResponses : "Le nombre de questions auxquelles l'utilisateur a répondu doit être un nombre entier.", @@ -13,12 +13,16 @@ module.exports = needMaxNumberCorrectResponses : "Le nombre de réponses correctes ne peut être supérieur au nombre de questions.", needMinNumberCorrectResponses : "Le nombre de réponses correctes ne peut être négatif.", needMinNumberSecondesResponse : "La durée de la réponse ne peut être négative.", - noPreviousAnswer: "On dirait que c'est la première fois que vous répondez à ce quiz. Bonne lecture !", - previousAnswersLine: "Le DATEANSWER, vous avez répondu correctement à NBCORRECTANSWERS questions sur NBQUESTIONS en AVGDURATION secondes.", - previousAnswersStats: "En moyenne, vous avez répondu à ce quiz en AVGDURATION secondes, en ayant AVGCORRECTANSWERS % de bonnes réponses.", - previousAnswersTitle: "Bonjour #NOM, voici vos précédents résultats à ce quiz", + noPreviousResults: "On dirait que c'est la première fois que vous répondez à ce quiz. Bonne lecture !", + previousResultsLine: "Le DATEANSWER, vous avez répondu correctement à NBCORRECTANSWERS questions sur NBQUESTIONS en AVGDURATION secondes.", + previousResultsStats: "En moyenne, vous avez répondu à ce quiz en AVGDURATION secondes, en ayant AVGCORRECTANSWERS % de bonnes réponses.", + previousResultsTitle: "Voici vos précédents résultats à ce quiz", responseSavedError : "Cependant une erreur a été rencontrée durant l'enregistrement de votre résultat. Accèder à tous vos quizs.", responseSavedMessage : "Votre résultat a été enregistré. Accèder à tous vos quizs.", statsUser: "Vous avez enregistré NBANSWERS réponseS1 à NBQUESTIONNAIRES quizS2 différentS3 sur les NBTOTQUESTIONNAIRES proposéS4 par le site.
    En moyenne, vous avez mis AVGDURATION secondes à répondre et avez correctement répondu à AVGCORRECTANSWERS % des questions.", - wantToSaveResponses: "Si vous le souhaitez, vous pouvez sauvegarder votre résultat en créant votre compte ci-dessous. Cela vous permettra aussi de recevoir régulièrement de nouvelles \"graines de culture\" directement sur votre e-mail.", + userAnswersFail : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est certain, vous ferez mieux la prochaine fois !", + userAnswersMedium : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est pas mal du tout !", + userAnswersSuccess : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. Bravo ! Rien ne vous échappe !", + wantToSaveResponses: "Si vous le souhaitez, vous pouvez sauvegarder vos résultats (il n'est pas nécessaire de créer un compte) :", + wantToSeaPreviousResults: "Cliquez ici pour tous vos résultats et vos statistiques de réussite à ce quiz." }; \ No newline at end of file diff --git a/lang/fr/general.js b/lang/fr/general.js index 2651903..b21aa96 100644 --- a/lang/fr/general.js +++ b/lang/fr/general.js @@ -16,6 +16,7 @@ module.exports = failAuthHeader : "Absence de header Authorization.", failAuthId : "Identifiant non valide : ", failAuthToken : "Token invalide ou utilisateur non trouvé.", + localDBConnexionFail : "La connexion à la base de donnée locale est impossible.", neededParams : "Des paramètres nécessaires manquants sont manquants.", nextPage : "Page suivante", notAllowed : "Vous n'avez pas les droits nécessaires pour cette action.", diff --git a/lang/fr/questionnaire.js b/lang/fr/questionnaire.js index 9342c22..6563330 100644 --- a/lang/fr/questionnaire.js +++ b/lang/fr/questionnaire.js @@ -2,6 +2,7 @@ module.exports = { btnProposeConnection: "Je me connecte.",// déplacé dans general.js btnProposeSubscribe: "Je crée mon compte.",//idem + btnProposeSave: "Enregistrer mes réponses.",//idem btnSendResponse: "Testez vos réponses.", btnShareQuizTxt: "Partager via ", btnShareQuizMailBody: "Bonjour,%0A%0AVoici%20un%20lien%20internet%20qui%20devrait%20t'intéresser :%0A", diff --git a/views/wikilerni/quiz-group.pug b/views/wikilerni/quiz-group.pug index 8ef0666..340bf88 100644 --- a/views/wikilerni/quiz-group.pug +++ b/views/wikilerni/quiz-group.pug @@ -1,7 +1,7 @@ extends layout.pug block append scripts script(src="/JS/polyfill.app.js" defer) - script(src="/JS/group.app.js" defer) + script(src="/JS/quiz.app.js" defer) block content - if(group.Questionnaires.length !==0) @@ -41,14 +41,12 @@ block content noscript div strong #{configTpl.noJSNotification} - form(id="group" method="POST") + form(id="quiz" method="POST" data-title=group.Group.title) h2 #{group.Group.title} div#response - // div(class="subscribeBtns") - // p - // a(class="button cardboard" href=configTpl.subscribePage) #{txtGeneral.btnProposeSubscribe} - // p - // a(class="button cardboard" href=configTpl.connectionPage) #{txtGeneral.btnProposeConnection} + div(id="propose2Save" class="needJS") + span(class="input_wrapper") + input(class="button cardboard" id="want2Save" type="button" value=txtQuestionnaire.btnProposeSave) for questionnaire in group.Questionnaires for question in questionnaire.Questions p(id="question_"+question.Question.id) #{question.Question.text} @@ -69,6 +67,7 @@ block content em #{response.text} input(type="hidden" name="isCorrect_response_"+response.id id="isCorrect_response_"+response.id value=""+response.isCorrect) input(type="hidden" name="question_id_response_"+response.id id="question_id_response_"+response.id value=question.Question.id) + input(name="questionnaireId" id="questionnaireId" value="0" type="hidden") input(name="groupId" id="groupId" value=group.Group.id type="hidden") p span(class="input_wrapper") diff --git a/views/wikilerni/quiz.pug b/views/wikilerni/quiz.pug index 363d8e4..5b3f3a2 100644 --- a/views/wikilerni/quiz.pug +++ b/views/wikilerni/quiz.pug @@ -1,7 +1,7 @@ extends layout.pug block append scripts script(src="/JS/polyfill.app.js" defer) - script(src="/JS/questionnaire.app.js" defer) + script(src="/JS/quiz.app.js" defer) block content div(id="tags" class="cardboard") ul @@ -52,9 +52,12 @@ block content noscript div strong #{configTpl.noJSNotification} - form(id="questionnaire" method="POST" class="needJS") + form(id="quiz" method="POST" class="needJS" data-title=questionnaire.Questionnaire.title) h2 #{questionnaire.Questionnaire.title} div#response + div(id="propose2Save" class="needJS") + span(class="input_wrapper") + input(class="button cardboard" id="want2Save" type="button" value=txtQuestionnaire.btnProposeSave) for question in questionnaire.Questions p(id="question_"+question.Question.id) #{question.Question.text} if(question.Question.explanation) @@ -73,9 +76,10 @@ block content input(type="hidden" name="isCorrect_response_"+reponse.id id="isCorrect_response_"+reponse.id value=""+reponse.isCorrect) input(type="hidden" name="question_id_response_"+reponse.id id="question_id_response_"+reponse.id value=question.Question.id) input(name="questionnaireId" id="questionnaireId" value=questionnaire.Questionnaire.id type="hidden") + input(name="groupId" id="groupId" value="0" type="hidden") p span(class="input_wrapper") - input(id="checkResponses" type="submit" value=txtQuestionnaire.btnSendResponse class="cardboard" title=txtQuestionnaire.btnSendResponse) + input(id="checkResponses" type="submit" value=txtQuestionnaire.btnSendResponse class="cardboard") div#licence p !{configTpl.licenceTxt} div#zerozozio