import $ from 'jquery'
import { j3pBarre, j3pElement, j3pEnsureHtmlElement, j3pFocus, j3pFreezeElt, j3pMathquillXcas, j3pNombre, j3pPGCD, j3pSupprimeEspaces, j3pValeurde } from 'src/legacy/core/functions'
import { legacyStyles } from 'src/legacy/core/StylesJ3p'
import Tarbre from 'src/legacy/outils/calculatrice/Tarbre'
import { j3pDesactive } from 'src/lib/mathquill/functions'
/**
* Constructeur des zones de saisie, qui retourne leur validateur
* Il permet de créer les fonctions de validation que l’on pourra ensuite appeler pour valider les réponses données.
* Cf le tutorial {@tutorial ValidationZones}
* @param {Object} zonesOptions
* @param {HTMLInputElement[]|string[]} zonesOptions.zones La liste des zones (ou de leurs ids)
* @param {HTMLInputElement[]|string[]} [zonesOptions.validePerso] La liste des zones dont la validation est gérée dans la section (et pas par ValidationZones)
* @param {Parcours} zonesOptions.parcours Le parcours courant (pour accéder à donneesSection.debug et
* @constructor
*/
function ValidationZones (zonesOptions) {
if (!this || typeof this !== 'object') throw Error('Constructeur qui doit être appelé avec new')
if (typeof zonesOptions !== 'object') throw Error('paramètre invalide')
if (!Array.isArray(zonesOptions.zones)) throw Error('Paramètre zones invalide (pas un tableau)')
/**
* @private
* @type {HTMLInputElement[]}
*/
const inputs = []
for (const zone of zonesOptions.zones) {
try {
const zoneSaisie = j3pEnsureHtmlElement(zone)
// Pour connaître la position de la zone de saisie dans le tableau zone (utile pour valider ou colorer une zone unique)
zoneSaisie.indexZone = inputs.length
inputs.push(zoneSaisie)
} catch (error) {
console.error(`création de la zone ${zone} impossible, pas un id ni un élément html`, error)
}
}
if (!Array.isArray(zonesOptions.validePerso)) {
zonesOptions.validePerso = []
}
/**
* @typedef ZonesProps
* @property {HTMLInputElement[]} inputs Liste des inputs (ou zones mathquill)
* @property {boolean[]} validePerso Liste de booléen
* @property {boolean[]} aRepondu
* @property {string[]} reponseSaisie Liste des réponses saisies par l’élève (en cas de sélection dans une liste ça contient l’index)
* @property {boolean[]} bonneReponse
* @property {boolean[]} reponseSimplifiee (chaque élément peut être un tableau de booléen)
*/
/**
* La liste des propriétés pour nos zones à valider
* Il devrait rester privé (interne à cette classe), mais bcp de sections le manipule et affectent des valeurs
* @type {ZonesProps}
*/
this.zones = {
// array d’elts html
inputs,
// liste de booléen, true si on a filé l’id (ou la zone de saisie) dans validePerso, false sinon
// zonesOptions.validePerso peut contenir l’id ou l’élément html
validePerso: inputs.map(input => zonesOptions.validePerso.includes(input) || zonesOptions.validePerso.includes(input.id)),
aRepondu: [], // propriété aRepondu de chaque zone
reponseSaisie: [], // on récupère la réponse saisie
bonneReponse: inputs.map(() => false), // pour chaque zone (sans validation perso) on sait si la réponse est bonne
reponseSimplifiee: inputs.map(() => [true, true]), // par défaut, on considère toutes les réponses simplifiées
isActive: inputs.map(() => true) // par défaut, toutes les zones sont actives
}
// pour la compatibilité ascendante (inputs s’appelait id)
Object.defineProperty(this.zones, 'id', {
enumerable: true,
get: () => {
console.error(Error('Il ne faut plus utiliser la propriété id, elle est remplacée par la propriété inputs'))
return this.zones.inputs
},
set: () => {
throw Error('Il ne faut plus utiliser la propriété id, elle est remplacée par la propriété inputs, et elle ne doit pas être modifiée')
}
})
/**
* Le parcours courant
* @type {Parcours|undefined}
*/
this.parcours = zonesOptions.parcours
/**
* @type {boolean}
* @private
*/
this._isDebug = this.parcours?.isDebug
}
/**
* Valide toutes les zones (et colorie)
* @param {boolean} [lastChance] Si le parcours n’a pas été fourni au constructeur, il faut passer un booléen (pour savoir si c’est le dernier essai et geler les réponses élève)
* @return {{detailsreponses: {}, aRepondu: boolean, bonneReponse: boolean}}
*/
ValidationZones.prototype.validationGlobale = function (lastChance) {
if (typeof lastChance !== 'boolean') {
if (this.parcours) {
lastChance = this.parcours.essaiCourant >= this.parcours.donneesSection.nbchances
} else {
console.warn('Sans avoir fourni de parcours au constructeur il faut passer l’argument lastChance à validationGlobale, pas de freeze possible sans lui')
}
}
// On vérifie tout d’abord si l’élève a répondu à chaque zone
const aRepondu = this.valideReponses()
let bonneReponse = true
const detailsreponses = {}
if (aRepondu) {
for (let i = this.zones.inputs.length - 1; i >= 0; i--) {
// pour les zones dont la validation est perso, on ne les prend pas en compte (idem pour celle qui ne sont plus actives car déjà considérées comme bonnes)
if (!this.zones.validePerso[i] && this.zones.isActive[i]) {
const zoneSaisie = this.zones.inputs[i]
const bilanReponseZone = this.valideUneZone(zoneSaisie, zoneSaisie.reponse)
this.zones.bonneReponse[i] = bilanReponseZone.bonneReponse
bonneReponse = (bonneReponse && this.zones.bonneReponse[i])
detailsreponses[this.zones.inputs[i]] = { correct: this.zones.bonneReponse[i], valeur: this.zones.reponseSaisie[i], zonesimplifiee: this.zones.reponseSimplifiee[i], detailPolynome: bilanReponseZone.detailPolynome }
} else {
// ce qui suit évite d’avoir à le préciser dans la section dans le cas d’une bonne réponse'
this.zones.bonneReponse[i] = true
}
}
this.coloreLesZones()
if (lastChance) this._freezeAll()
} else {
bonneReponse = false
}
// console.log('validationGlobale avec lastChance', lastChance, 'va retourner', { aRepondu, bonneReponse, detailsreponses })
return { aRepondu, bonneReponse, detailsreponses }
} // fin validationGlobale
/**
* Vérifie que toutes les zones ont bien été remplies (appelée par validationGlobale)
* @return {boolean} True si toutes les zones ont une réponse
*/
ValidationZones.prototype.valideReponses = function () {
let repEleve// cette variable récupère le contenu de la zone de saisie
let aRepondu = true
// On part de la dernière zone car si elle n’est pas complétée, alors on lui donne le focus.
// Ainsi, la zone avec le focus est la première zone vide
for (let i = this.zones.inputs.length - 1; i >= 0; i--) {
this.zones.aRepondu[i] = true
const elt = this.zones.inputs[i]
if (elt) {
if (!Array.isArray(elt.solutionSimplifiee)) {
elt.solutionSimplifiee = ['valide et on accepte', 'valide et on accepte']
}
for (let k = 0; k <= 1; k++) {
if (
(elt.solutionSimplifiee[k] === 'reponse fausse') ||
(elt.solutionSimplifiee[k] === 'reponsefausse') ||
(elt.solutionSimplifiee[k] === 'réponse fausse') ||
(elt.solutionSimplifiee[k] === 'reponseFausse') ||
(elt.solutionSimplifiee[k] === 'fausse')
) {
elt.solutionSimplifiee[k] = 'reponse fausse'
} else if (
(elt.solutionSimplifiee[k] === 'non valide') ||
(elt.solutionSimplifiee[k] === 'nonValide') ||
(elt.solutionSimplifiee[k] === 'reponse non valide') ||
(elt.solutionSimplifiee[k] === 'réponse non valide') ||
(elt.solutionSimplifiee[k] === 'nonvalide') ||
(elt.solutionSimplifiee[k] === 'reponsenonvalide')
) {
elt.solutionSimplifiee[k] = 'non valide'
} else {
elt.solutionSimplifiee[k] = 'valide et on accepte'
}
}
}
if (elt?.id.includes('input')) {
// c’est une zone de saisie
repEleve = j3pValeurde(elt)
} else {
// c’est une liste'
repEleve = elt.selectedIndex
}
// on récupère la réponse de chaque zone de saisie
this.zones.reponseSaisie[i] = repEleve
if (elt?.id.includes('input')) {
// la zone de saisie est-elle complétée
// console.log('elt.id:', elt.id, repEleve, 'et elt:', elt, $(elt).mathquill('latex'), elt.innerHTML)
if (repEleve === '') {
this.zones.aRepondu[i] = false
// à chaque fois, on redonne le focus à la zone concernée.
// A la fin, c’est la première zone vide qui l’aura (d’où l’ordre dans lequel le for est construit)
j3pFocus(elt)
} else {
const input = elt
if (input.typeReponse === undefined) {
input.typeReponse = ['texte']
}
if (input.typeReponse[0] === 'nombre') {
this.zones.reponseSimplifiee[i] = this.reponseSimplifiee(elt)
} else {
this.zones.reponseSimplifiee[i] = true
}
for (let k = 0; k <= 1; k++) {
if ((elt.solutionSimplifiee[k] === 'non valide') && !this.zones.reponseSimplifiee[i][k]) {
// la réponse n’est pas simplifiée (somme ou racine restante pour k=0, fraction réductible pour k=1),
// et on demande dans la section à ce que cela soit considéré comme une réponse non valide (l’élève doit proposer autre chose)
this.zones.aRepondu[i] = false
}
}
}
if (!this.zones.aRepondu[i]) {
// on redonne le focus à la zone si l’élève n’a pas répondu (ou avec une réponse non simplifiée si c’est pris en compte)
j3pFocus(elt)
}
} else {
if (elt.typeReponse === undefined) {
elt.typeReponse = [true]
}
if (elt.typeReponse[0]) {
// on veut dans ce cas que l’élève modifie la valeur par défaut de la liste
// la liste est-elle complétée
if (repEleve === 0) {
this.zones.aRepondu[i] = false
j3pFocus(elt)
}
} else {
this.zones.aRepondu[i] = true
}
}
aRepondu = (aRepondu && this.zones.aRepondu[i])
// console.log('aRepondu ? zone', i, this.zones.aRepondu[i])
}
// console.log('valideReponses va retourner', aRepondu, 'pour', this.zones)
return aRepondu
}
/**
* Met en couleur les zones de saisie (suivant qu’elles soient bonne ou fausses)
* Elle permet aussi de barrer la réponse à la fin et de désactiver la zone
* Elle ne sera appelée que si toutes les réponses ont été données et n’agit pas sur les zones où la validaton est perso.
*/
ValidationZones.prototype.coloreLesZones = function coloreLesZones () {
for (let i = this.zones.inputs.length - 1; i >= 0; i--) {
// si la réponse est bonne on la remet dans la couleur de l’énoncé
// sinon, on la met en rouge
if (!this.zones.validePerso[i] && this.zones.isActive[i]) this.coloreUneZone(this.zones.inputs[i])
}
}
/**
* Met en couleur une seule zone de saisie (suivant qu’elle soit bonne ou fausse)
* Permet aussi de barrer la réponse à la fin et de désactiver la zone.
* Elle est appelée par coloreLesZones, mais peut l'être de manière autonome (pour une validation perso)
* Si la réponse est bonne on la remet dans la couleur de l’énoncé, sinon, on la met en rouge
* @param {string|HTMLElement} zone zone de saisie qui peut être identifiée par son id
*/
ValidationZones.prototype.coloreUneZone = function coloreUneZone (zone) {
// nos deux listeners, this est l’élément html
function onInput () {
this.style.color = legacyStyles.petit.enonce.color
this.removeEventListener('input', onInput)
this.removeEventListener('keyup', onKeyup)
}
function onKeyup (e) {
const keynum = (window.event) ? e.keyCode : e.which
if (keynum === 46 || keynum === 8) {
this.style.color = legacyStyles.petit.enonce.color
this.removeEventListener('input', onInput)
this.removeEventListener('keyup', onKeyup)
}
}
let elt
try {
elt = j3pEnsureHtmlElement(zone)
} catch (error) {
console.error(error)
return
}
if (!this.zones.isActive[elt.indexZone]) return // La zone est déjà inactive, donc on ne fait rien
if (this.zones.bonneReponse[elt.indexZone]) {
elt.style.color = legacyStyles.cbien
j3pDesactive(elt)
// On gèle cette zone
j3pFreezeElt(elt)
this.zones.isActive[elt.indexZone] = false
} else {
elt.style.color = legacyStyles.cfaux
// on désactive la zone si l’élève n’a plus de chance pour répondre
// sinon on redonne le focus
if (!this.parcours) console.error(Error('Il faut passer le parcours au constructeur ValidationZones pour pouvoir appeler coloreUneZone'))
const parcours = this.parcours || window.j3p
if (parcours.essaiCourant < parcours.donneesSection.nbchances) {
j3pFocus(elt)
// Je fais en sorte que lorsqu’on va de nouveau compléter la zone, le texte va revenir en noir
// On pourrait ajouter l’objet {once:true} comme argument pour détruire l’écouteur dès sa première utilisation, mais ça ne fonctionne pas sur tous les navigateurs
elt.addEventListener('input', onInput)
elt.addEventListener('keyup', onKeyup)
} else {
j3pDesactive(elt)
this.zones.isActive[elt.indexZone] = false
if ((elt.barrer === undefined) || elt.barrer) j3pBarre(elt)
if (elt.couleur !== undefined) elt.style.color = elt.couleur
}
}
} // fin coloreUneZone
/**
* Valide une zone (et la colorie)
* Vérifie juste si une seule zone est correctement remplie
* zone est l’élément (input) ou l’id de la zone testée, que l’on suppose non vide (valideReponses doit avoir été appelée avant).
* reponse est un tableau de réponse si zone.typeReponse[0] vaut "texte" ou undefined, sinon, c’est un nombre ou un polynôme
* @param {HTMLElement|string} zone
* @param {string[]|string|number} reponse
* @return {{detailPolynome: {}, bonneReponse: boolean}}
*/
ValidationZones.prototype.valideUneZone = function valideUneZone (zone, reponse) {
/**
* @type {string}
* @private
*/
let idZone
if (typeof zone === 'string') {
idZone = zone
zone = j3pElement(zone)
} else {
idZone = zone.id || '' // on assure d’avoir une string pour la suite du code
}
let puisPrecision
let bonneReponse = false
let repEleve = this.zones.reponseSaisie[zone.indexZone]
let detailPolynome = {}// cela servira pour récupérer les informations sur la validation d’un polynôme (pour mieux cerner les erreurs)'
if (idZone.includes('input')) {
// on a une zone de saisie
if (zone.typeReponse === undefined) {
zone.typeReponse = ['texte']
}
if (zone.typeReponse[0] === 'texte') {
// on veut un tableau de string pour simplifier la suite
if (!Array.isArray(reponse)) {
if (typeof reponse === 'number') {
reponse = [String(reponse)]
} else if (typeof reponse === 'string') {
reponse = [reponse]
} else {
console.error(Error(`reponse attendue invalide : ${reponse} (${typeof reponse})`))
reponse = ['']
}
}
bonneReponse = reponse.includes(repEleve)
if (bonneReponse) {
// dans ce cas, on remplace la réponse par la première acceptée par le correcteur
if (idZone.includes('inputmq')) {
// pour une zone mathquill
$(zone).mathquill('latex', reponse[0])
} else {
// pour un autre type de zone de saisie
zone.value = reponse[0]
// cette zone peut être dynamique donc il faut bricoler pour que la taille s’adapte au textes éventuellement modifiés
let fontZone = zone.style.fontFamily
let elParent = zone
while (!fontZone) {
elParent = elParent.parentNode
if (elParent) {
fontZone = elParent.style.fontFamily
} else {
// il n’y a plus d’élément parent (on est arrivé sur le document)
fontZone = legacyStyles.petit.enonce.style.fontFamily
break
}
}
const tailleFontZone = zone.style.fontSize
const texteZone = zone.value
const longueur = $(zone).textWidth(texteZone, fontZone, tailleFontZone)
zone.style.width = (longueur + 16) + 'px'
}
}
} else if (zone.typeReponse[0] === 'polynome') {
// la zone est nécessairement une zone mathquill dans ce cas
const varPoly = zone.typeReponse[1]
const unarbre1 = new Tarbre(reponse, [varPoly])// pour le polynôme attendu
// on interprète repEleve pour qu’il soit compris par la suite'
const tabFrac = repEleve.split('frac')
let irreductible = true// si des fractions sont présentes, sont-elles simplifiées ?
let vraiPolynome = true// savoir si c’est bien un polynome (pas une fonction rationnelle par ex)
let autreVariable = false// existe-t-il une autre variable que varPoly ?
let signeDouble = false// pour contrôler si l’élève a écrit deux signes consécutifs'
let egalitePolynome = true// cette variable pourra évoluer si vraiPolynome==true et autreVariable==false
let estDeveloppe = true// savoir si on retrouve bien une forme développée (sans parenthèses)
let estSimplifie = true// si le polynôme est développé, on vérifie qu’il est simplifié'
let monomesDegresDifferents = true// si on a un polynôme développé et qu’il est égal à celui demandé, on vérifie si une même puissance de x 'apparaît pas deux fois.'
let degreNonSimplifie = -1// si le polynôme n’est pas simplifié, cette variable reçoit le degré du premier monôme non réduit (par puissance décroissantes)'
let degreErrone = -1// on cherche quel coefficient est inexacte (on renvoie le degré le plus élevé)
const numFrac = []
const denFrac = []
const numFracInit = []
const denFracInit = []
const pgcdFrac = []
const xFrac = []// servira pour récupérer x s’il se trouve au numérateur ou au dénominateur'
const puisFrac = []
const varXFrac = []// pour récupérer les puissances de x présentes dans les fractions
// repEleveSimp sera le polynôme où les fractions auront été remplacée par leur valeur décimale approchée
let repEleveSimp = repEleve
const regX = new RegExp('(\\\\cdot)*(' + varPoly + ')(\\^)*(\\{?\\(?([0-9]+)\\)?)*', 'g')
const regX2 = new RegExp('(\\\\cdot)*(' + varPoly + ')(\\^)*\\{?\\(?', 'g')
// je regarde ici si toutes les éventuelles fractions sont bien réduites
if (tabFrac.length > 1) {
for (let j = 1; j < tabFrac.length; j++) {
xFrac[j] = []
numFrac[j] = tabFrac[j].split('}')[0].substring(1)
numFracInit[j] = numFrac[j]
xFrac[j][0] = numFrac[j].match(regX)
numFrac[j] = numFrac[j].replace(regX, '')
if (!numFrac[j]) {
numFrac[j] = '1'
}
// j’ai récupéré le coef du numérateur et la puissance de x
denFrac[j] = tabFrac[j].split('}')[1].split('}')[0].substring(1)
denFracInit[j] = denFrac[j]
xFrac[j][1] = denFrac[j].match(regX)
denFrac[j] = denFrac[j].replace(regX, '')
if (!denFrac[j]) {
denFrac[j] = '1'
}
// j’ai récupéré le coef du dénominateur et la puissance de x
pgcdFrac[j] = this.pgcd(Math.abs(numFrac[j]), Math.abs(denFrac[j]))
if ((pgcdFrac[j] > 1) || (pgcdFrac[j] === 'pb')) {
irreductible = false
}
puisFrac[j] = []
// pour la suite, je simplifie les puissances de x (s’il y en a) et gère l’affichage de la puissance de x simplifiée'
if (xFrac[j][0] != null) {
if (xFrac[j][1] != null) {
// x est présent au numérateur et au dénominateur
puisFrac[j][0] = xFrac[j][0][0].replace(regX2, '')
puisFrac[j][1] = xFrac[j][1][0].replace(regX2, '')
if (!puisFrac[j][0]) {
if (!puisFrac[j][1]) {
varXFrac[j] = ''
} else {
if (Number(puisFrac[j][1]) === 2) {
varXFrac[j] = '(1)/(' + varPoly + ')'
} else {
varXFrac[j] = '(1)/(' + varPoly + '^' + String(Number(puisFrac[j][1] - 1)) + ')'
}
}
} else {
if (!puisFrac[j][1]) {
if (Number(puisFrac[j][0]) === 2) {
varXFrac[j] = varPoly
} else {
varXFrac[j] = varPoly + '^' + String(Number(puisFrac[j][0] - 1))
}
} else {
if (Number(puisFrac[j][0]) > Number(puisFrac[j][1])) {
if (Number(puisFrac[j][0]) === Number(puisFrac[j][1]) + 1) {
varXFrac[j] = varPoly
} else {
varXFrac[j] = varPoly + '^' + String(Math.round(Number(puisFrac[j][0] - puisFrac[j][1])))
}
} else {
if (Number(puisFrac[j][1]) === Number(puisFrac[j][0]) + 1) {
varXFrac[j] = '(1)/(' + varPoly + ')'
} else {
varXFrac[j] = '(1)/(' + varPoly + '^' + String(Math.round(Number(puisFrac[j][1] - puisFrac[j][0]))) + ')'
}
}
}
}
irreductible = false
} else {
// on a x au numérateur et pas au dénominateur
puisFrac[j][0] = xFrac[j][0][0].replace(regX2, '')
if (!puisFrac[j][0]) {
varXFrac[j] = varPoly
} else {
varXFrac[j] = varPoly + '^' + puisFrac[j][0]
}
}
} else {
if (xFrac[j][1] != null) {
// on a x au dénominateur et pas au numérateur
puisFrac[j][1] = xFrac[j][1][0].replace(regX2, '')
if (!puisFrac[j][1]) {
varXFrac[j] = '(1)/(' + varPoly + ')'
} else {
varXFrac[j] = '(1)/(' + varPoly + '^' + puisFrac[j][1] + ')'
}
} else {
// sinon x n’est ni au numérateur, ni au dénominateur, on n’a donc pas à gérer ces "x"'
varXFrac[j] = ''
}
}
// console.log("varXFrac:"+varXFrac);
// console.log("!!!!avant repEleveSimp:"+repEleveSimp)
// je remplace alors chaque fraction par la valeur décimale et je mets aussi la puissance de x pour reconstruire le monome
repEleveSimp = repEleveSimp.replace('\\frac{' + numFracInit[j] + '}}{' + denFracInit[j] + '}', String(numFrac[j] / denFrac[j]) + varXFrac[j])
repEleveSimp = repEleveSimp.replace('\\frac{' + numFracInit[j] + '}}{' + denFracInit[j] + '}}', String(numFrac[j] / denFrac[j]) + varXFrac[j])
repEleveSimp = repEleveSimp.replace('\\frac{' + numFracInit[j] + '}{' + denFracInit[j] + '}}', String(numFrac[j] / denFrac[j]) + varXFrac[j])
repEleveSimp = repEleveSimp.replace('\\frac{' + numFracInit[j] + '}{' + denFracInit[j] + '}', String(numFrac[j] / denFrac[j]) + varXFrac[j])
if (this._isDebug) console.debug('repEleveSimp:' + repEleveSimp)
}
}
// on vérifie tout d’abord que 2 signes ne sont pas consécutifs
// je dois prendre en compte les signes doubles
const tabSigneDouble = ['++', '+-', '-+', '--', '*+', '*-', '**']
const tabSigneEquiv = ['+', '-', '-', '+', '*', '*(-1)', '*']
if (this._isDebug) console.debug('repEleveSimp avant signes :' + repEleveSimp)
for (let j = 0; j < tabSigneDouble.length; j++) {
while (repEleveSimp.includes(tabSigneDouble[j])) {
// le signe double est présent
signeDouble = true
repEleveSimp = repEleveSimp.replace(tabSigneDouble[j], tabSigneEquiv[j])
}
}
if (this._isDebug) console.debug('repEleveSimp:' + repEleveSimp)
// ici je ne dois plus avoir de double signe
let repEleveSuite = j3pMathquillXcas(repEleve)
// on doit remplacer sqrt par racine pour l’évaluation'
while (repEleveSuite.includes('sqrt')) {
repEleveSuite = repEleveSuite.replace('sqrt', 'racine')
}
while (repEleveSimp.includes('sqrt')) {
repEleveSimp = repEleveSimp.replace('sqrt', 'racine')
}
const regVarpol = new RegExp('(' + varPoly + ')', 'g')
let repEleveSansFracetsqrt = repEleveSuite.replace(/(racine)/g, '')// racineTxt,"");
repEleveSansFracetsqrt = repEleveSansFracetsqrt.replace(regVarpol, '')
if (/[a-z]/g.test(repEleveSansFracetsqrt)) {
autreVariable = true
}
let ordrePuissance // en lien avec puisPrecision;
if (!autreVariable) {
const unarbre2 = new Tarbre(repEleveSuite, [varPoly])// pour le polynôme entré par l’élève'
for (let j = 0; j <= 14; j++) {
if (unarbre1.evalue([j]) === 0) {
puisPrecision = 12
} else {
ordrePuissance = String(Math.abs(unarbre1.evalue([j]))).indexOf('.')
// ordrePuissance = Math.floor(Math.log(Math.abs(unarbre1.evalue([j])))/Math.log(10))+1;
puisPrecision = (ordrePuissance === '-1') ? 12 - String(Math.abs(reponse)).length : 12 - ordrePuissance
}
egalitePolynome = (egalitePolynome && (Math.abs(unarbre1.evalue([j]) - unarbre2.evalue([j])) < Math.pow(10, -puisPrecision)))
}
if (zone.typeReponse[2] === 'developpe') {
// s’il reste des parenthèses, c’est que ce n’est pas vraiment développé'
if ((repEleve.includes('(')) || (repEleve.includes(')'))) {
estDeveloppe = false
} else if (repEleveSimp.includes('/')) {
vraiPolynome = false
// ce n’est pas un polynôme (il reste après simplification 1/x^...)
} else {
const repSansSommeTab = []
const decomp1 = repEleveSimp.split('+')
const decomp2 = []
let j2
for (let j = 0; j < decomp1.length; j++) {
if (decomp1[j] !== '') {
decomp2[j] = decomp1[j].split('-')
for (j2 = 0; j2 < decomp2[j].length; j2++) {
if (decomp2[j][j2] !== '') {
if (j2 > 0) {
repSansSommeTab.push('-' + decomp2[j][j2])
} else {
repSansSommeTab.push(decomp2[j][j2])
}
}
}
}
}
if (this._isDebug) console.debug('repSansSommeTab:' + repSansSommeTab)
// à cet instant repSansSommeTab est un tableau contenant les monomes
// je reprends chaque élément de ce tableau et en extrait le coef qui est écrit aiinsi que le degré
const degreRepTab = []
const monomeReduit = []
const passageDansMonome = []// si on trouve plusieurs puissances de x dans un monome, c’est qu’il n’est pas réduit'
let degMax = 0
let degMaxTab
const regDeg = /\^\{?\(?\d{1,3}\)?}?/g // new RegExp('(\\^\\{?\\(?[0-9]{1,3}\\)?\\}?)', 'g')
const regDegNb = /\d/g // new RegExp('[0-9]', 'g')
for (let j = 0; j < repSansSommeTab.length; j++) {
degreRepTab[j] = 0
monomeReduit[j] = true
passageDansMonome[j] = 0
// recherche du degré le plus élevé du monome
degMaxTab = 1
if (regDeg.test(repSansSommeTab[j])) {
degMaxTab = repSansSommeTab[j].match(regDeg)
}
for (let k = 0; k < degMaxTab.length; k++) {
degMax = Math.max(degMax, degMaxTab[k].match(regDegNb)[0])
if (this._isDebug) console.debug('j=' + j + ' degMax' + degMax)
}
// au max, on va dire que le degré peut être de 20
j2 = degMax
while (j2 >= 1) {
if (repSansSommeTab[j].includes(varPoly + '^' + j2)) {
// x^j2 est dans ce monome (x étant varPoly)
passageDansMonome[j]++
degreRepTab[j] += j2
repSansSommeTab[j] = repSansSommeTab[j].replace(varPoly + '^' + String(j2), '')// cela vire x^j2
} else {
j2--
}
}
while (repSansSommeTab[j].includes(varPoly)) {
// x est présent dans ce monome (x étant varPoly)
degreRepTab[j] += 1
passageDansMonome[j]++
repSansSommeTab[j] = repSansSommeTab[j].replace(varPoly, '')// cela vire x
}
if (passageDansMonome[j] > 1) {
monomeReduit[j] = false
degreNonSimplifie = Math.max(degreNonSimplifie, degreRepTab[j])
}
if (this._isDebug) {
console.debug('degreRepTab[' + j + ']:' + degreRepTab[j])
console.debug('degreNonSimplifie:' + degreNonSimplifie)
}
// console.log("repSansSommeTab[j]="+repSansSommeTab[j]);
// s’il reste un signe * qui soit au début ou à la fin, on le vire.
while (repSansSommeTab[j].includes('**')) {
repSansSommeTab[j] = repSansSommeTab[j].replace('**', '*')
}
// console.log("repSansSommeTab[j] sans ** ="+repSansSommeTab[j]);
const lgCoefMonome = repSansSommeTab[j].length
// console.log("index:"+repSansSommeTab[j].lastIndexOf("*")+" lgCoefMonome="+lgCoefMonome)
if (repSansSommeTab[j].lastIndexOf('*') === lgCoefMonome - 1) {
repSansSommeTab[j] = repSansSommeTab[j].substring(0, lgCoefMonome - 1)// on enlève les signes de multiplication
}
if (repSansSommeTab[j].lastIndexOf('\\cdot') === lgCoefMonome - 6) {
repSansSommeTab[j] = repSansSommeTab[j].substring(0, lgCoefMonome - 6)// on enlève les signes de multiplication
}
if (repSansSommeTab[j].indexOf('*') === 0) {
repSansSommeTab[j] = repSansSommeTab[j].slice(1)// on enlève les signes de multiplication
}
if (repSansSommeTab[j].indexOf('\\cdot') === 0) {
repSansSommeTab[j] = repSansSommeTab[j].slice(5)// on enlève les signes de multiplication
}
// j’ai un pb avec les valeur décimale'
if (!repSansSommeTab[j]) {
repSansSommeTab[j] = '1'
} else if (repSansSommeTab[j] === '-') {
repSansSommeTab[j] = '-1'
}
if (this._isDebug) console.debug('repSansSommeTab[' + j + ']=' + repSansSommeTab[j])
if (Number.isNaN(Number(repSansSommeTab[j]))) {
if (this._isDebug) console.debug(repSansSommeTab[j] + ' n’est pas un nombre')
monomeReduit[j] = false
} else if (Math.abs(repSansSommeTab[j]) < Math.pow(10, -12)) {
// dans ce cas est présent le monome 0x^... ce qui n’est pas réduit'
monomeReduit[j] = false
}
if (!monomeReduit[j]) {
estSimplifie = false
}
}
// j’ai vérifié que le polynôme ne contenait plus de parenthèses
// et si c’est le cas que tous les coefficients sont bien des nombres (pas 3*5 par ex)
// pour que le polynôme soit bien développé, il suffit de vérifier que je n’ai pas 2 monomes de même degré'
const tabDegreOrdonne = []
for (let j = 0; j < degreRepTab.length; j++) {
tabDegreOrdonne.push(degreRepTab[j])
}
tabDegreOrdonne.sort()
if (this._isDebug) console.debug('degreRepTab:' + degreRepTab + ' tabDegreOrdonne:' + tabDegreOrdonne)
// si un élément de ce tableau est égal à son suivant, alors on a deux monomes de même degré
for (let j = 0; j < tabDegreOrdonne.length - 1; j++) {
if (tabDegreOrdonne[j] === tabDegreOrdonne[j + 1]) {
monomesDegresDifferents = false
}
}
// je contrôle enfin le coefficient de chaque monome, voir s’il est bon'
// d’abord, si une puissance de x apparaît plusieurs fois'
for (let j = tabDegreOrdonne.length - 1; j >= 1; j--) {
if (tabDegreOrdonne[j] === tabDegreOrdonne[j - 1]) {
degreNonSimplifie = Math.max(degreNonSimplifie, tabDegreOrdonne[j])
}
}
// je reprends la liste des monomes non réduits
for (let j = 0; j < degreRepTab.length; j++) {
if (!monomeReduit[j]) {
degreNonSimplifie = Math.max(degreNonSimplifie, degreRepTab[j])
}
}
if (this._isDebug) console.debug('degreNonSimplifie 2 :' + degreNonSimplifie)
// il faut aussi que je réordonne les coefficients dans l’ordre du degré du monôme'
// le coefficient repSansSommeTab[j] du monome de degré degreRepTab[j] doit être égal à celui du tableau des réponses zone.typeReponse[3]
const tabMonomePrisEnCompte = []// ceci sert à vérifier que tous les monômes de la réponses sont comparés
for (let j = 0; j < zone.typeReponse[3].length; j++) {
tabMonomePrisEnCompte[j] = false
}
for (let j = 0; j < degreRepTab.length; j++) {
tabMonomePrisEnCompte[degreRepTab[j]] = true// le monôme de degré degreRepTab[j] de la réponse attendu est vérifié
if (Math.abs(repSansSommeTab[j] - zone.typeReponse[3][degreRepTab[j]]) > Math.pow(10, -10)) {
// Il y a un pb de coef dans le monôme de degré degreRepTab[j]
if (this._isDebug) console.debug(repSansSommeTab[j] + ' et ' + zone.typeReponse[3][degreRepTab[j]] + ' avec ' + degreRepTab[j])
degreErrone = Math.max(degreErrone, degreRepTab[j])
if (this._isDebug) console.debug('degreErrone:' + degreErrone)
}
}
for (let j = 0; j < zone.typeReponse[3].length; j++) {
if (!tabMonomePrisEnCompte[j] && (Math.abs(zone.typeReponse[3][j]) > Math.pow(10, -12))) {
degreErrone = Math.max(degreErrone, j)
}
}
if (degreNonSimplifie > -1) {
estSimplifie = false
}
}
if (this._isDebug) console.debug('vraiPolynome :' + vraiPolynome + ' autreVariable : ' + autreVariable + ' egalitePolynome:' + egalitePolynome + ' estDeveloppe:' + estDeveloppe + ' estSimplifie:' + estSimplifie + ' monomesDegresDifferents:' + monomesDegresDifferents + ' signeDouble:' + signeDouble + ' irreductible:' + irreductible + ' degreNonSimplifie=' + degreNonSimplifie + ' degreErrone:' + degreErrone)
bonneReponse = (estDeveloppe && estSimplifie && monomesDegresDifferents && !signeDouble && irreductible)
detailPolynome = { autreVariable, vraiPolynome, signeDouble, irreductible, estDeveloppe, estSimplifie, degreNonSimplifie, degreErrone, monomesDegresDifferents }
// fin de la partie sur la forme développée
}
bonneReponse = (bonneReponse && egalitePolynome)
} else {
bonneReponse = false// car autrevariable==true
}
} else {
// on a un nombre
if (idZone.includes('inputmq')) {
// pour une zone mathquill, je dois le convertir
if (repEleve.includes('infty')) {
// c’est +/-infini
repEleve = repEleve.replace('\\infty', 'Infinity')
} else {
repEleve = this._calculeValeur(repEleve)
}
} else {
// Dans le cas d’un nombre entier, un copier-coller peut mettre des espaces donc il faudra se débarrasser. Cela por
if (Number.isInteger(j3pNombre(j3pSupprimeEspaces(repEleve)))) repEleve = repEleve.replace(/\s([0-9]{3})/g, '$1')
if (!Number.isNaN(j3pNombre(repEleve))) { repEleve = j3pNombre(repEleve) }
}
let ordrePuissance // en lien avec puisPrecision;
if (zone.typeReponse[1] === 'exact') {
if (reponse === 0) {
puisPrecision = 15
} else {
ordrePuissance = String(Math.abs(reponse)).indexOf('.')
puisPrecision = (ordrePuissance === '-1') ? 13 - String(Math.abs(reponse)).length : 13 - ordrePuissance
}
bonneReponse = (Math.abs(repEleve - reponse) < Math.pow(10, -puisPrecision))
} else if (zone.typeReponse[1] === 'approche') {
bonneReponse = (Math.abs(repEleve - reponse) <= zone.typeReponse[2])
} else if (zone.typeReponse[1] === 'arrondi') {
const nbRep1 = Math.floor(reponse / zone.typeReponse[2]) * zone.typeReponse[2]
const nbRep2 = Math.ceil(reponse / zone.typeReponse[2]) * zone.typeReponse[2]
// ordrePuissance de nbRep1 est le nombre n tel que 10^n<= nbRep1 < 10^{n+1}
if (nbRep1 === 0) {
puisPrecision = 15
} else {
ordrePuissance = String(Math.abs(reponse)).indexOf('.')
// ordrePuissance = Math.floor(Math.log(Math.abs(nbRep1))/Math.log(10))+1;
puisPrecision = (ordrePuissance === '-1') ? 15 - String(Math.abs(reponse)).length : 15 - ordrePuissance
}
let bonArrondi
if (Math.round((reponse - nbRep1) * Math.pow(10, puisPrecision)) / Math.pow(10, puisPrecision) < zone.typeReponse[2] / 2) {
bonArrondi = nbRep1
} else {
bonArrondi = nbRep2
}
// console.log(bonArrondi, puisPrecision, repEleve) // ne pas laisser ça en prod ! ça donne la solution en console
bonneReponse = (Math.abs(repEleve - bonArrondi) < Math.pow(10, -puisPrecision))
}
if (bonneReponse) {
for (let k = 0; k <= 1; k++) {
if ((zone.solutionSimplifiee[k] === 'réponse fausse') && !this.zones.reponseSimplifiee[zone.indexZone][k]) {
// la réponse n’est pas simplifiée (somme ou racine restante pour k=0, fraction réductible pour k=1),
// et on demande dans la section à ce que cela soit considéré comme une réponse non valide (l’élève doit proposer autre chose)
bonneReponse = false
}
}
}
}
} else {
// on a une liste
bonneReponse = (repEleve === Number(reponse))
}
return { bonneReponse, detailPolynome }
} // fin valideUneZone
/**
* À documenter
*/
ValidationZones.prototype.redonneFocus = function redonneFocus () {
// cette fonction permet de redonner le focus à la première zone fausse dans le cas d’une validation
// où on aurait des zones gérées de manière perso'
for (let i = this.zones.inputs.length - 1; i >= 0; i--) {
if (!this.zones.bonneReponse[i]) {
j3pFocus(this.zones.inputs[i], true)
}
}
}
/**
* Utilisez plutôt {@link module:lib/utils/number.pgcd} si vous voulez un vrai pgcd
* @deprecated
* @param x
* @param y
* @return {string|number}
*/
ValidationZones.prototype.pgcd = function pgcd (x, y) {
const pgcd = j3pPGCD(x, y)
if (typeof pgcd !== 'number') return 'pb'
return pgcd
}
/**
* À documenter
*/
ValidationZones.prototype.reponseSimplifiee = function reponseSimplifiee (laZone) {
// cette fonction vérifie si laZone contient une expression simplifiée
// elle renvoie un tableau contenant 2 valeurs : [0] si l’expression n’est pas une somme simplifiable
// [1] pour savoir si la fraction éventuellement présente est bien simplifiée'
let elt, idZone
if (typeof laZone === 'string') {
elt = j3pElement(laZone)
idZone = laZone
} else {
elt = laZone
idZone = laZone.id
}
let repEleve
if (idZone.includes('inputmq')) {
// c’est une zone mq
repEleve = $(elt).mathquill('latex')
} else if (idZone.includes('input')) {
// c’est une zone de saisie non mq'
repEleve = j3pValeurde(elt)
} else {
// c’est une liste'
repEleve = elt.selectedIndex
}
if (idZone.includes('inputmq')) {
// pour une zone mathquill, je dois le convertir
repEleve = j3pMathquillXcas(repEleve)
// on doit remplacer sqrt par racine pour l’évaluation'
while (repEleve.includes('sqrt')) {
repEleve = repEleve.replace('sqrt', 'racine')
}
} else {
repEleve = j3pNombre(repEleve)
}
let simplification
let fractionsSimplifiees = true
const sqrtfracRegexp1 = /racine\(\(\d+\)\/\(\d+\)\)/ // new RegExp('racine\\(\\([0-9]{1,}\\)/\\([0-9]{1,}\\)\\)', 'i')
const sqrtfracRegexp2 = /\(racine\(\d+\)\)\/\(\d+\)/ // new RegExp('\\(racine\\([0-9]{1,}\\)\\)/\\([0-9]{1,}\\)', 'i')
const nbrestantRegexp = /-?[\d,.]+\*/ // new RegExp('\\-?[0-9\\,\\.]{1,}\\*', 'i')
let reponseSimplifiee = true
if (String(repEleve).includes('racine')) {
// J’ai une racine carrée, je vérifie que le radical est simplifié
if (sqrtfracRegexp1.test(repEleve)) {
// de la forme sqrt(...)/... ou ...*sqrt(...)/...
const tabSqrtfrac1 = repEleve.match(sqrtfracRegexp1)
reponseSimplifiee = (reponseSimplifiee && ((tabSqrtfrac1.length === 1) && ((repEleve.replace(tabSqrtfrac1[0], '') === '') || (nbrestantRegexp.test(repEleve.replace(tabSqrtfrac1[0], ''))))))
let fracSqrt = tabSqrtfrac1[0].substring(tabSqrtfrac1[0].indexOf('(') + 1, tabSqrtfrac1[0].length - 1)
while ((fracSqrt.includes('(')) || (fracSqrt.includes(')'))) {
fracSqrt = fracSqrt.replace('(', '')
fracSqrt = fracSqrt.replace(')', '')
}
reponseSimplifiee = (reponseSimplifiee && this.estNbFrac(fracSqrt)[0])
}
if (sqrtfracRegexp2.test(repEleve)) {
// de la forme sqrt(.../...) ou ...*sqrt(.../...)
const tabSqrtfrac2 = repEleve.match(sqrtfracRegexp2)
reponseSimplifiee = (reponseSimplifiee && ((tabSqrtfrac2.length === 1) && ((repEleve.replace(tabSqrtfrac2[0], '') === '') || (nbrestantRegexp.test(repEleve.replace(tabSqrtfrac2[0], ''))))))
}
const sqrtRegexp0 = /[+-]?\d*\*?racine\(\d+\)/g // new RegExp('(\\+|\\-)?[0-9]{0,}\\*?racine\\([0-9]{1,}\\)', 'ig')
const sqrtRegexp1 = /racine\(\d+\)/g // new RegExp('racine\\([0-9]{1,}\\)', 'ig')
if (sqrtRegexp0.test(repEleve)) {
// on a donc une racine carrée dans la réponse de l’élève'
// reponseSimplifiee = true;
const tabSqrt1 = repEleve.match(sqrtRegexp1)
// var tabSqrt0 = repEleve.match(sqrtRegexp0)
for (let k0 = 0; k0 < tabSqrt1.length; k0++) {
const arbreSqrt = new Tarbre(tabSqrt1[k0], [])
if (Math.abs(Math.round(arbreSqrt.evalue([])) - arbreSqrt.evalue([])) < Math.pow(10, -12)) {
// l’une des racine carrée est simplifiable'
reponseSimplifiee = false
}
if (tabSqrt1.length > 1) {
// cela signifie qu’il existe plus d’une racine carrée'
for (let k1 = 0; k1 < tabSqrt1.length; k1++) {
if (k0 !== k1) {
const arbreDif01 = new Tarbre(tabSqrt1[k0] + '/(' + tabSqrt1[k1] + ')', [])
const maValeur = arbreDif01.evalue([])
if (Math.abs(Math.round(maValeur) - maValeur) < Math.pow(10, -12)) {
reponseSimplifiee = false
}
}
}
}
}
}
} else if (!String(repEleve).includes('/')) {
// on n’a pas de fraction
const repValeur = this._calculeValeur(repEleve)
if (Math.abs(Math.round(Math.pow(10, 8) * repValeur) / Math.pow(10, 8) - repValeur) < Math.pow(10, -12)) {
// on a une valeur décimale
// pour être écrite de manière simple, elle doit donc être décmale et ne pas comporter d’opération
reponseSimplifiee = ((String(repEleve).indexOf('+') <= 0) && (String(repEleve).indexOf('-') <= 0) && (String(repEleve).indexOf('*') <= 0))
fractionsSimplifiees = true
}
} else {
try {
simplification = this.estNbFrac(repEleve)
} catch (e) {
simplification = [false, false]
}
reponseSimplifiee = simplification[0]
fractionsSimplifiees = simplification[1]
}
return [reponseSimplifiee, fractionsSimplifiees]
} // fin reponseSimplifiee
/**
* À documenter
* @param {string} texte
* @return {boolean[]} Deux booléens, le premier si ??? et le 2e si ???
*/
ValidationZones.prototype.estNbFrac = function estNbFrac (texte) {
// cette fonction teste si texte est juste un nombre ou juste une fraction (éventuellement sous la forme a*b/c)
let repSimple = false
let fractSimple = true
let tabNum
let num, den
// on peut n’avoir qu’un nombre 'entier ou décimal'
if (!isNaN(Number(j3pNombre(String(texte))))) {
repSimple = true
} else if (!texte.includes('/')) {
// le nombre n’est pas une fraction
repSimple = true
}
// on seulement une fraction ou bien un nb de la forme a*b/c
const fractRegexp1 = /\(\d+\)\/\(\d+\)/ // new RegExp('\\([0-9]{1,}\\)/\\([0-9]{1,}\\)', 'i')
const fractRegexp2 = /\(-\d+\)\/\(\d+\)/ // new RegExp('\\(\\-[0-9]{1,}\\)/\\([0-9]{1,}\\)', 'i')
const fractRegexp3 = /-?[\d,.]+\*?\(-?\d+\)\/\(\d+\)/ // new RegExp('\\-?[0-9\\.\\,]{1,}\\*?\\(\\-?[0-9]{1,}\\)/\\([0-9]{1,}\\)', 'i')
if (fractRegexp1.test(texte)) {
const tabFrac1 = texte.match(fractRegexp1)
repSimple = ((tabFrac1.length === 1) && ((texte.replace(tabFrac1[0], '') === '') || (texte.replace(tabFrac1[0], '') === '-') || (texte.replace(tabFrac1[0], '') === '+')))
if (repSimple) {
// c’est que je n’ai qu’une fraction de la forme -?(...)/(...)'
// on vérifie que cette fraction est simplifiée
tabNum = texte.split('/')
num = j3pNombre(tabNum[0].substring(tabNum[0].indexOf('(') + 1, tabNum[0].length - 1))
den = j3pNombre(tabNum[1].substring(1, tabNum[1].length - 1))
fractSimple = ((this.pgcd(num, den) === 1) && (den !== 1))
}
}
if (!repSimple) {
if (fractRegexp2.test(texte)) {
const tabFrac2 = texte.match(fractRegexp2)
repSimple = ((tabFrac2.length === 1) && (texte.replace(tabFrac2[0], '') === ''))
if (repSimple) {
// c’est que je n’ai qu’une fraction de la forme (-...)/(...)'
// on vérifie que cette fraction est simplifiée
tabNum = texte.split('/')
num = j3pNombre(tabNum[0].substring(2, tabNum[0].length - 1))
den = j3pNombre(tabNum[1].substring(1, tabNum[1].length - 1))
fractSimple = ((this.pgcd(num, den) === 1) && (den !== 1))
}
}
}
if (!repSimple) {
if (fractRegexp3.test(texte)) {
const tabFrac3 = texte.match(fractRegexp3)
repSimple = ((tabFrac3.length === 1) && (texte.replace(tabFrac3[0], '') === ''))
if (repSimple) {
// c’est que je n’ai qu’une fraction de la forme ...*(-?...)/(...)'
// on vérifie que cette fraction est simplifiée
tabNum = texte.split('/')
const posPar = tabNum[0].indexOf('(')
if (tabNum[0].charAt(posPar - 1) === '*') {
num = j3pNombre(tabNum[0].substring(0, posPar - 1)) * j3pNombre(tabNum[0].substring(posPar + 1, tabNum[0].length - 1))
} else {
num = j3pNombre(tabNum[0].substring(0, posPar)) * j3pNombre(tabNum[0].substring(posPar + 1, tabNum[0].length - 1))
}
den = j3pNombre(tabNum[1].substring(1, tabNum[1].length - 1))
fractSimple = ((this.pgcd(num, den) === 1) && (den !== 1))
}
}
}
return [repSimple, fractSimple]
} // fin estNbFrac
/**
* À documenter
* @param fDeX
* @return {string}
*/
ValidationZones.prototype.transformeExp = function transformeExp (fDeX) {
// fDeX est une chaine de caractère correspondant à l’espression d’une fonction : f(x)
// elle revoie la même chaine en remplaçant e^... par exp(...)
// on appelle cette fonction après avoir utilisé j3pMathquillXcas, donc il n’y a plus frac{..}{..} mais (..)/(..)
// Attention car e n’est pas seulement pour exp, il peut aussi l'être pour racine
let newFDeX = ''
let finFdex = fDeX
while (finFdex.includes('ne')) {
newFDeX += finFdex.substring(0, finFdex.indexOf('ne') + 2)
finFdex = finFdex.substring(finFdex.indexOf('ne') + 2)
}
let posE = finFdex.indexOf('e')
while (posE > -1) {
// e est présent et ce n’est aps celui de racine
newFDeX += finFdex.substring(0, posE)
// on regarde ce qui suit e
if ((finFdex.charAt(posE + 1) === 'x') && (finFdex.charAt(posE + 2) === 'p')) {
// il est déjà écrit exp, donc on passe
newFDeX += 'exp'
finFdex = finFdex.substring(posE + 3)
} else if ((finFdex.charAt(posE + 1) === '+') || (finFdex.charAt(posE + 1) === '-') || (finFdex.charAt(posE + 1) === '*') || (finFdex.charAt(posE + 1) === ')') || (finFdex.charAt(posE + 1) === 'x')) {
// e est tout seul, je le remplace par exp(1)
newFDeX += 'exp(1)'
finFdex = finFdex.substring(posE + 1)
} else {
// ce qui suit e est ^. Il faut alors trouver les parenthèses qui encadrent ce dont on prend l’exponentielle
if (finFdex.charAt(posE + 2) === '(') {
// c’est de la forme e^(...)
let parOuverte = 1
let posParferme = posE + 3
while (parOuverte > 0) {
if (finFdex.charAt(posParferme) === '(') {
parOuverte++
} else if (finFdex.charAt(posParferme) === ')') {
parOuverte--
}
posParferme++
}
newFDeX += 'exp(' + finFdex.substring(posE + 3, posParferme)
finFdex = finFdex.substring(posParferme)
} else {
// c’est uste de la forme e^., cad que seul le caractère suivant est est en puissance
newFDeX += 'exp(' + finFdex.substring(posE + 2, posE + 3) + ')'
finFdex = finFdex.substring(posE + 3)
}
}
posE = finFdex.indexOf('e')
}
newFDeX += finFdex
// J’ai encore un souci car il ne reconnait pas 3e comme un produit, donc il faut le lui expliquer...
newFDeX = newFDeX.replace(/(\d)e/g, '$1*e')
return newFDeX
} // fin transformeExp
/**
* À documenter
* @param texteFonction
* @return {string}
* @private
*/
ValidationZones.prototype._getEcriturePourCalcul = function _getEcriturePourCalcul (texteFonction) {
// texte fonction est au format latex ou tout autre format intermédiaire
// on transforme cette écriture pour avoir l’expression sous la forme permettant de créer un arbre et d’appliquer la fonction evalue
// je convertis pour que ce soit plus facile à utiliser
let newExpressionFct = j3pMathquillXcas(texteFonction)
// gestion de la racine carrée
// il faut que je transforme sqrt en racine
while (newExpressionFct.includes('sqrt')) {
newExpressionFct = newExpressionFct.replace('sqrt', 'racine')
}
// mais j’ai encore un problème avec l’exponentielle : je peux avoir e isolé, ou bien e^x ou bien e^{...}. Ceschoses ne sont pas comprises, il faut modifier ça et écrire sous la forme exp(...)
newExpressionFct = this.transformeExp(newExpressionFct)
while (newExpressionFct.includes('exp()')) {
newExpressionFct = newExpressionFct.replace('exp()', 'exp(1)')
}
return newExpressionFct
}
/**
* À documenter
* @param chaine
* @return {number}
* @private
*/
ValidationZones.prototype._calculeValeur = function _calculeValeur (chaine) {
// un nombre écrit sous forme d’une chaine de caractères va être calculé pour qu’on ait sa valeur décimale
const newChaine = this._getEcriturePourCalcul(chaine)
const arbreChaine = new Tarbre(newChaine, [])
return arbreChaine.evalue([])
} // _calculeValeur
/**
* Gèle le DOM des zones d’input, pour empêcher toute modif du DOM via l’inspecteur d’élément
* (contre la triche des élèves qui trafiquent la page avant de faire une capture)
*/
ValidationZones.prototype._freezeAll = function _freezeAll () {
this.zones.inputs.forEach((input, index) => {
// Cela ne s’appliquera pas à des zones dont la validation doit être traitée de manière particulière
if (!this.zones.validePerso[index]) j3pFreezeElt(input)
})
}
export default ValidationZones