/*
* MathGraph32 Javascript : Software for animating online dynamic mathematics figures
* https://www.mathgraph32.org/
* @Author Yves Biton (yves.biton@sesamath.net)
* @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
*/
import NatObj from '../types/NatObj'
import NatCal from '../types/NatCal'
import Complexe from '../types/Complexe'
import Dimf from '../types/Dimf'
import Fonte from '../types/Fonte'
import Nat from '../types/Nat'
import Pointeur from '../types/Pointeur'
import StyleEncadrement from '../types/StyleEncadrement'
import { ce, cens, preventDefault, traiteAccents } from '../kernel/kernel'
import CalcR from '../kernel/CalcR'
import CalcC from '../kernel/CalcC'
import CAffLiePt from './CAffLiePt'
import CLatex from './CLatex'
import CValeurAngle from './CValeurAngle'
import addQueue from 'src/kernel/addQueue'
import CCbGlob from 'src/kernel/CCbGlob'
export default CEditeurFormule
/**
* Classe représentant un éditeur de formule permettant à l'utilisateur de modifier
* la formule d'un calcul sur la figure.
* On peut associer à l'éditeur une fonction de callBack nommée callBackOK qui
* sera appelée quand l'utilisateur aura validé sa réponse.
* @constructor
* @extends CAffLiePt
* @param {CListeObjets} listeProprietaire La liste propriétaire.
* @param {CImplementationProto} impProto null ou la construction propriétaire.
* @param {boolean} estElementFinal true si l'objet est un élément final de construction.
* @param {Color} couleur La couleur d'éciture de l'éditeur (et du cadre éventuel).
* @param {number} xNom L'abscisse d'affichage de l'éditeur
* @param {number} yNom L'ordonnée d'affichage de l'éditeur
* @param {number} decX Décalage horizontal du nom
* @param {number} decY Décalage vertical du nom
* @param {boolean} masque true si l'éditeur est masqué
* @param {CPt} pointLie Le pointauquel l'éditeur est lié
* @param {number} taillePolice Indice de la taiile de police utilisée
* @param {boolean} encadrement true si encadré.
* @param {boolean} effacementFond true si on efface le fond de l'en-tête.
* @param {Color} couleurFond La couleur de fond de l'en-tête.
* @param {CCalcul} calculAssocie Le calcul dont l'éditeur peut changer la formule.
* @param {string} enTete Ce qui est affiiché devant l'éditeur proprement dit. Encadrer par des $
* pour obtenir un affichage LaTeX.
* @param {number} nbcol Le nombre de colonnes de l'éditeur.
* @param {boolean} affichageFormule true si on affiche la formule à droite de l'éditeur.
* @param {string} preCodeLaTeX Donne le code LaTeX de ce qu'il faut afficher entre l'éditeur
* et l'affichage de la formule quand cet affichage est demandé.
* @param {boolean} variables1Car true si on n'utilise que des calculs et variables dont les nomes sont de 1 caractère.
* Dans ce cas les signes de multiplications sont omis dans l'affichage de formule s'il est demané.
* @param {boolean} affLaTeXPremierAffichage true si la formule dit être affichée à droite de l'éditeur
* immédiatement après le chargement de la figure.
* @param {boolean} affichageTempsReel true si l'affichage de formule (en LaTeX) à droite de l'éditeur doit se faire au fur et à mesure que l'utilisateur tape au clavier.
* @param {boolean} fixed true si l'affichage est punaisé et ne peut pas être capturé à la souris
* @returns {CEditeurFormule}
*/
function CEditeurFormule (listeProprietaire, impProto, estElementFinal, couleur, xNom, yNom,
decX, decY, masque, pointLie, taillePolice, encadrement, effacementFond, couleurFond, calculAssocie,
enTete, nbcol, affichageFormule, preCodeLaTeX, variables1Car,
affLaTeXPremierAffichage, affichageTempsReel, fixed) {
if (arguments.length !== 0) {
if (arguments.length === 1) {
CAffLiePt.call(this, listeProprietaire)
} else {
CAffLiePt.call(this, listeProprietaire, impProto, estElementFinal, couleur,
xNom, yNom, decX, decY, masque, pointLie, taillePolice, encadrement, effacementFond, couleurFond,
CAffLiePt.alignHorRight, CAffLiePt.alignVerTop, null, fixed)
this.calculAssocie = calculAssocie
this.enTete = enTete
this.nbcol = nbcol
this.affichageFormule = affichageFormule
// Lorsqu'on exporte la figure il n'a pas d'éditeur affiché et on ne tient pas compte du preCodeLatex
this.preCodeLaTeX = this.listeProprietaire.isForExport ? '' : preCodeLaTeX
this.variables1Car = variables1Car
this.affLaTeXPremierAffichage = affLaTeXPremierAffichage
this.affichageTempsReel = affichageTempsReel
}
this.indErr = new Pointeur(0)
this.angText = new CValeurAngle(listeProprietaire, 0) // Le seul affichage de texte qui ne peut pas être tourbé. Ajout version 6.3.3
}
this.callBackOK = null // Ajout version 4.9.3
}
CEditeurFormule.prototype = new CAffLiePt()
CEditeurFormule.prototype.constructor = CEditeurFormule
CEditeurFormule.prototype.superClass = 'CAffLiePt'
CEditeurFormule.prototype.className = 'CEditeurFormule'
CEditeurFormule.prototype.numeroVersion = function () {
return 4
}
CEditeurFormule.prototype.getClone = function (listeSource, listeCible) {
const ind1 = listeSource.indexOf(this.calculAssocie)
const ind2 = listeSource.indexOf(this.pointLie)
const ind3 = listeSource.indexOf(this.impProto)
return new CEditeurFormule(listeCible, listeCible.get(ind3, 'CImplementationProto'),
this.estElementFinal, this.couleur, this.xNom, this.yNom, this.decX, this.decY, this.masque, listeCible.get(ind2, 'CPt'),
this.taillePolice, this.encadrement, this.effacementFond, this.couleurFond,
listeCible.get(ind1, 'CCalcul'), this.enTete, this.nbcol, this.affichageFormule, this.preCodeLaTeX,
this.variables1Car, this.affLaTeXPremierAffichage, this.affichageTempsReel, this.fixed)
}
/**
* Fonction translatant le point de (decalagex, decalagey)
* @param {number} decalagex
* @param {number} decalagey
* @returns {void}
*/
CEditeurFormule.prototype.translateDe = function (decalagex, decalagey) {
this.placeNom(this.xNom + decalagex, this.yNom + decalagey)
}
CEditeurFormule.prototype.depDe = function (p) {
if (this.elementTestePourDependDe === p) return this.dependDeElementTeste
return this.memDep(CAffLiePt.prototype.depDe.call(this, p) || this.calculAssocie.depDe(p))
}
CEditeurFormule.prototype.dependDePourBoucle = function (p) {
return CAffLiePt.prototype.dependDePourBoucle.call(this, p) || this.calculAssocie.dependDePourBoucle(p)
}
/**
* Fournit la chaîne représentant l'en-tête à afficher devant l'éditeur proprement dit
* @returns {string}
*/
CEditeurFormule.prototype.rendChaineAffichage = function () {
let i
if (this.enTete === '') {
let ch = this.calculAssocie.nomCalcul
if (this.calculAssocie.estDeNatureCalcul(Nat.or(NatCal.NFonction, NatCal.NFonctionComplexe))) {
ch = ch + '(' + this.calculAssocie.nomsVariables + ')'
} else {
if (this.calculAssocie.estDeNatureCalcul(NatCal.NTteFoncRouC)) {
// CFoncNVar fonc = (CFoncNVar) calculAssocie;
ch = ch + '('
for (i = 0; i < this.calculAssocie.nbVar; i++) {
ch = ch + this.calculAssocie.nomsVariables[i]
if (i !== this.calculAssocie.nbVar - 1) ch = ch + ','
}
ch = ch + ')'
}
}
ch = ch + ' = '
ch = '$' + ch + '$'
return ch
} else return this.enTete
}
CEditeurFormule.prototype.getNature = function () {
return NatObj.NEditeurFormule
}
CEditeurFormule.prototype.montre = function () {
CAffLiePt.prototype.montre.call(this)
this.editeur.style.visibility = 'visible'
if (this.affichageFormule) {
if (this.glatex === undefined) {
this.glatex = this.latex.createg()
const listePr = this.listeProprietaire
const svgApp = document.getElementById(listePr.id + 'figurePanel')
const svg = svgApp !== null ? svgApp : document.getElementById(listePr.id) // Modifié version 6.3.0
svg.appendChild(this.glatex)
}
this.glatex.setAttribute('visibility', 'visible')
}
}
CEditeurFormule.prototype.cache = function () {
CAffLiePt.prototype.cache.call(this)
this.editeur.style.visibility = 'hidden'
if (this.affichageFormule && this.glatex) this.glatex.setAttribute('visibility', 'hidden')
}
/**
* Fonction appelée pour l'événement onblur de l'éditeur.
* Attention this pointe sur le composant
* @returns {void}
*/
CEditeurFormule.prototype.onblur = function () {
// S'il n'y a plus d'implémentation graphique de l'éditeur il ne faut pas traiter l'événement onblur
if (this.hasgElement) {
const b = this.validation(true)
if (b) this.demarquePourErreur()
else this.marquePourErreur()
}
}
/**
* Fonction appelée par l'événement onkeyup de l'éditeur
* Attention this pointe sur le composant
* @param {Event} evt
* @returns {void}
*/
CEditeurFormule.prototype.onkeyup = function (evt) {
// Non traité si c'est la touche entrée ou une touche de déplacement de curseur
const kc = evt.keyCode
if (kc === 13) { // Touche entrée
// Version 7.4.2 : pour que l'événement touche Entrée ne remonte pas vers les exos j3P car sinon
// on va évaluer la réponse de l'élève
preventDefault(evt)
evt.stopPropagation()
}
// On sort si touche entrée ou déplacement à gauche ou à droite
if ((evt.charCode === 0) && ((kc === 13) || (kc === 37) || (kc === 39))) return
// On regarde si un set de caractères autorisés a été spécifié :
const listePr = this.listeProprietaire
const svgApp = document.getElementById(listePr.id + 'figurePanel') // Modifié version 6.3.0
const svg = svgApp !== null ? svgApp : document.getElementById(listePr.id)
const dimf = new Dimf(svg)
// var doc = listePr.documentProprietaire;
const chCalcul = this.variables1Car ? listePr.addImplicitMult(this.editeur.value) : this.editeur.value
const indErr = this.indErr
const calculAssocie = this.calculAssocie
// On autorise d'utiliser un calcul non créé par l'utilisateur
const indDernier = listePr.indexOf(calculAssocie) - 1
const varFor = calculAssocie.variableFormelle()
const bcomplexe = calculAssocie.estDeNatureCalcul(
Nat.or(NatCal.NCalculComplexe, NatCal.NFonctionComplexe, NatCal.NFoncCPlusieursVar)
)
if (bcomplexe
? CalcC.verifieSyntaxeComplexe(listePr, chCalcul, indErr,
indDernier, varFor)
: CalcR.verifieSyntaxe(listePr, chCalcul, indErr, indDernier, varFor)) {
this.demarquePourErreur()
}
if (this.affichageFormule && this.affichageTempsReel) {
const calc = bcomplexe
? CalcC.ccbComp(chCalcul, listePr, 0,
chCalcul.length - 1, varFor)
: CalcR.ccb(chCalcul, listePr, 0, chCalcul.length - 1, varFor)
// try rajouté version 4.7.3
// cadre.repaintFigureSansCalcul();
this.latex.chaineCommentaire = this.preCodeLaTeX + ' ' + calc.chaineLatexSansPar(varFor)
this.latex.positionne(false, dimf)
this.latex.setReady4MathJax()
// Pas de double appel de queue quand on est en train de taper dans le champ d'édition'
this.callBack(svg)
}
}
/**
* Fonction qui, dans le cas où l'éditeur a un affichage associé de la formule à sa droite en Latex,
* actualise cet affichage LaTeX
* Cette fonction est appelée quand on modifie la formule d'une fonction via l'outil de modification d'objet
* numérique
* @param {SVGElement} svg le svg contenant la figure
*/
CEditeurFormule.prototype.updateLatex = function (svg) {
if (this.existe && this.affichageFormule) {
const dimf = new Dimf(svg)
const calculAssocie = this.calculAssocie
// On autorise d'utiliser un calcul non créé par l'utilisateur
const varFor = calculAssocie.variableFormelle()
this.latex.chaineCommentaire = this.preCodeLaTeX + ' ' + calculAssocie.calcul.chaineLatexSansPar(varFor)
this.latex.positionne(false, dimf)
this.latex.setReady4MathJax()
// Pas de double appel de queue quand on est en train de taper dans le champ d'édition'
this.callBack(svg)
}
}
CEditeurFormule.prototype.metAJour = function () {
const listePr = this.listeProprietaire
const svgApp = document.getElementById(listePr.id + 'figurePanel') // Modifié version 6.3.0
const svg = svgApp !== null ? svgApp : document.getElementById(listePr.id)
this.updateLatex(svg)
}
/**
* Fonction appelée par l'événement onkeypress de l'éditeur
* Attention this pointe sur le composant
* @param {Event} evt
* @returns {void}
*/
CEditeurFormule.prototype.onkeypress = function (evt) {
let i, edit, ind, ch, s, b
const self = this
if (evt.keyCode === 13) {
preventDefault(evt) // Pour compatibilité avec j3p
// Version 7.4.2 : en remet evt.stopPropagation() car sinon ds les exos j3P on appelle l'évaluatio de la réponse
// après avoir appuyé sur la touche entrée dans l'éditeur
evt.stopPropagation() // Pour compatibilité avec j3p
b = this.validation(true)
if (b) this.demarquePourErreur()
else this.marquePourErreur()
// Ajout version 4.9.3
if ((this.callBackOK !== null) && b && (this.editeur.value.length !== 0)) this.callBackOK.call()
// evt.preventDefault(); // Supprimé version 5.2 pour que le clavier des périphériques mobile se replie
// Sinon avec clavier on garde le focus avec le point d'insertion à l'endroit de la faute
// Ci-dessous nécessaire d'utiliser la queue de MathJax car sinon on blur peut appeler la fonction
// validation() trop tôt
if (!this.listeProprietaire.documentProprietaire.hasMouse) {
addQueue(function () {
self.blur()
})
}
return
}
// On regarde si un set de caractères autorisés a été spécifié :
if (this.charset && this.charset !== '') {
if ((evt.charCode !== 0) && this.charset.indexOf(String.fromCharCode(evt.charCode)) === -1) {
preventDefault(evt)
return
}
}
if (evt.charCode === 40) { // On tape une parenthèse ouvrante
// Si l'expression est correcte au niveau des parenthèses et si le curseur n'est suivi
// que d'espaces ou de parenthèses fermantes, on rajoute une parenthèse fermante
edit = this.editeur
ind = edit.selectionStart
ch = edit.value
s = 0
for (i = 0; i < ch.length; i++) {
if (ch.charAt(i) === '(') s++
else if (ch.charAt(i) === ')') s--
}
b = true
for (i = ind; i < ch.length; i++) {
if ((ch.charAt(i) !== ')') || (ch.charAt(i) !== ')')) b = false
}
if ((s === 0) && b) {
edit.value = edit.value.substring(0, ind) + '()' + edit.value.substring(ind)
edit.selectionStart = ind + 1
edit.selectionEnd = ind + 1
preventDefault(evt)
}
}
}
// Modifié version 6.9.1 car quand on modifiait la formule ça ne marchait plus pour l'affichage LaTeX
/**
* Lance la mise à jour l'affichage LaTeX de la formule si celui-ci est activé.
* Utiliser ready() pour savoir quand ce sera fini.
* @param {SVGElement} svg
* @returns {void}
*/
CEditeurFormule.prototype.callBack = function callBack (svg) {
addQueue(() => this.updategLatex(svg))
}
// La fonction suivante est appelée par update sans passer par addQueue
/**
* Met à jour l'affichage LaTeX associé à un éditeur de formule quand il existe
* @param svg
*/
// Sans l'appel de addQueue, il y a un problème quand on modifie un éditeur de formule via
// la boîte de dialogue de protocole en demandant un affichageLaTeX de la formule
CEditeurFormule.prototype.updategLatex = function (svg) {
if (svg) {
addQueue(() => {
if (this.glatex && (this.glatex.getAttribute('visibility') === 'hidden')) return
const newglatex = this.latex.createg()
// if (this.glatex) svg.replaceChild(newglatex, this.glatex);
if (this.glatex && this.glatex.parentNode === svg) svg.replaceChild(newglatex, this.glatex)
else {
svg.appendChild(newglatex)
}
this.glatex = newglatex
this.latex.g = newglatex
})
}
}
/**
* Fonction encadrant l'éditeur de rouge en cas de faute de syntaxe
* @returns {void}
*/
CEditeurFormule.prototype.marquePourErreur = function () {
// this.form.style.border = "1px solid red"; // Modifié version mtgApp
this.editeur.style['background-color'] = '#FF9999'
this.editeur.setSelectionRange(this.indErr.getValue(), this.indErr.getValue())
}
/**
* Fonction retirant l'éventuel cadre rouge encadrant l'éditeur en cas de
* faute de syntaxe.
* @returns {void}
*/
CEditeurFormule.prototype.demarquePourErreur = function () {
// this.form.style.border = "none" // Modifié version mtgApp
this.editeur.style['background-color'] = '#FFFFFF'
}
// CEditeurFormule.prototype.creeEditeur = function(svg) { // Rectifié version 6.4
/**
* Fonction créant un div contenant un formulaire et à l'intérieur de celui-ci
* un éditeur input contenant l'éditeur de formule.
* Ce div est rajouté au div propriétaire du svg contenant la figure.
* @returns {void}
*/
CEditeurFormule.prototype.creeEditeur = function () {
// Modification version mtgApp : Le parent du svg de la figure est un svg. Il faudra tenir compte
// de son décalage par rapport au div parent
const id = this.listeProprietaire.id
if (id === null) return // Pour les figures servant seulement pour leurs objets de type calcul
// Version 7.9.2 : L'éditeur est maintenant contenu dans un foreignObject ajouté au svg de la figure
// this.divi = ce('div')
this.foreignElt = cens('foreignObject', {
style: 'overflow:visible;pointer-events:all;'
})
// this.divi.setAttribute('focusable', true)
// this.divi.setAttribute('tabindex', '0')
this.editeur = ce('input')
this.editeur.onblur = this.onblur.bind(this)
// this.form.onsubmit = this.onsubmit;
if (this.affichageTempsReel) {
this.editeur.onkeyup = (ev) => this.onkeyup(ev)
}
this.editeur.onkeypress = (ev) => this.onkeypress(ev)
// this.foreignElt.appendChild(this.editeur)
this.editeur.setAttribute('type', 'text')
this.editeur.setAttribute('size', this.nbcol)
// this.editeur.setAttribute("style","fontSize:"+Fonte.taille(this.taillePolice)+"px;")
this.editeur.style.border = '1px solid grey'
this.editeur.style.fontSize = this.taillePolice + 'px'
this.editeur.style.fontFamily = 'Roboto'
this.editeur.style.padding = '2px'
this.editeur.id = id + this.calculAssocie.nomCalcul // Pour les échanges avec J3P
// Version 7.9.2 l'élément contenant l'éditeur est maintenant un foreignObject contenu dans le svg de la figure
// document.body.appendChild(this.divi)
const svgFig = document.getElementById(id)
// Version 7.9.2 l'élément contenant l'éditeur est maintenant un foreignObject contenu dans le svg de la figure
// document.body.appendChild(this.divi)
// On affiche provisoiremnt l'éditeur dans le div parent du svg de la figure ou de l'appli car cela ne marche pas correctement si on le met
// dans le foreign object que l'on met dans le svg de la figure pour les dimensions
const parentDiv = svgFig.containerDiv
parentDiv.appendChild(this.editeur)
// On mémorise les dimensions de l'éditeur
this.clientHeight = this.editeur.clientHeight
this.clientWidth = this.editeur.clientWidth
// Sans les deux lignes ci-dessous, le foreign object n'est pas visible dans FireFox
this.foreignElt.setAttribute('width', this.clientWidth)
this.foreignElt.setAttribute('height', this.clientHeight)
// On retire l'éditeur du body on l'y avait mis seulement pour récupérer ses dimensions
parentDiv.removeChild(this.editeur)
this.foreignElt.appendChild(this.editeur)
// Version 8.4.3 : on met dans l'éditeur la formule du calcul par défaut
this.editeur.value = this.calculAssocie.chaineCalcul
// This.foreignElt sera mis dans ma svg dans la fonction trace
if (this.affichageFormule) {
this.latex = new CLatex(this.listeProprietaire, null, false, this.couleur,
this.xNom + this.clientWidth + 3, this.yNom + this.taillePolice / 2 + 4, this.decX, this.decY, false,
null, this.taillePolice, StyleEncadrement.Sans, this.effacementFond, this.couleurFond,
CAffLiePt.alignHorLeft, CAffLiePt.alignVerCent,
this.preCodeLaTeX + ' ' + this.calculAssocie.calcul.chaineLatexSansPar(this.calculAssocie.variableFormelle()))
// Ligne suivante ajoutée version WebBack pour pouvoir déplacer un éditeur avec l'outil de capture
// Utilisé dans CEditeurFormule.placeNom()
this.latex.owner = this
}
}
CEditeurFormule.prototype.placeEditeur = function () {
this.foreignElt.setAttribute('x', String(this.xNom + this.decX + 2))
this.foreignElt.setAttribute('y', String(this.yNom + this.decY + this.taillePolice / 2 - this.clientHeight / 2 + 2))
}
/**
* Fonction retirant le foreign object contenant le input de l'éditeur
* Appelée quand on détruit un éditeur de formule
* @returns {void}
*/
CEditeurFormule.prototype.deleteComponent = function () {
try {
this.deleteEditor()
} catch (e) {
// on lâche l'affaire, inutile de râler si on voulait supprimer un truc qui n'existe déjà plus
}
}
CEditeurFormule.prototype.update = function (svg) {
CAffLiePt.prototype.update.call(this, svg)
this.placeEditeur()
// this.placeEditeur() // retiré version 7.3.3 car on appelle de nouveau positionne quand on déplace un affichage
if (this.affichageFormule) {
if (this.glatex === undefined) {
this.glatex = this.latex.createg()
svg.appendChild(this.glatex)
} else {
if (this.latex.g === null) this.latex.g = this.glatex // Ajouté version 6.3.3
this.placeLatex()
this.updategLatex(svg)
}
}
}
CEditeurFormule.prototype.placeLatex = function () {
// Version 7.9.2 : on laisse 6 pixels de plus avant le signe = pour l'affichage LaTeX
// car sinon sous chrome le cadre qui apparaît au focus est tronqué
this.latex.xNom = this.xNom + (this.listeProprietaire.isForExport ? 2 : this.clientWidth) + 9
// this.latex.yNom = this.yNom + this.taillePolice / 2 + 2;
this.latex.yNom = this.yNom + this.taillePolice / 2 + 4
this.latex.decX = this.decX
this.latex.decY = this.decY
}
CEditeurFormule.prototype.deleteEditor = function () {
const id = this.listeProprietaire.id
if (id === null) return // Pour les figures servant seulement pour leurs objets de type calcul
let svgFig = document.getElementById(id)
const svgPanel = document.getElementById(this.listeProprietaire.id + 'figurePanel')
if (svgPanel !== null) {
svgFig = svgPanel
}
if (this.glatex && svgFig.contains(this.glatex)) {
svgFig.removeChild(this.glatex)
}
if (this.foreignElt && svgFig.contains(this.foreignElt)) {
svgFig.removeChild(this.foreignElt)
}
this.foreignElt = null
this.isInSvg = false
}
CEditeurFormule.prototype.donneCouleur = function (color) {
CAffLiePt.prototype.donneCouleur.call(this, color)
if (this.affichageFormule) this.latex.donneCouleur(color)
}
CEditeurFormule.prototype.setReady4MathJax = function setReady4MathJax () {
// on appelle la méthode de notre ancêtre (ça va appeler CLatex.setReady4MathJax avec notre intance de CEditeurFormule)
CAffLiePt.prototype.setReady4MathJax.call(this)
// mais aussi celle de notre objet latex si y'en a un (ça va appeler CLatex.setReady4MathJax avec notre propriété latex, qui est une instance CLatex)
if (this.affichageFormule && this.latex) this.latex.setReady4MathJax()
}
// Fonction suivante supprimée car inutime et, quand on déplaçait un éditeur de formule avec affichage LeTeX temps réel;
// provoquait des créations de div d'id 'prov" non détruits
/*
CEditeurFormule.prototype.setReady4MathJaxUpdate = function() {
CAffLiePt.prototype.setReady4MathJaxUpdate.call(this);
if (this.affichageFormule && this.latex) {
this.latex.setReady4MathJax(); // Spécial pour cet objet
}
};
*/
/**
* Fonction mettant dans l'éditeur la chaîne de caractères st et mettant à jour
* en conséquence l'affichage de formule LaTeX s'il est activé.
* @param {string} st
* @returns {void}
*/
CEditeurFormule.prototype.setEditorValue = function setEditorValue (st) {
this.editeur.value = st
const liste = this.listeProprietaire
const ch = this.variables1Car ? liste.addImplicitMult(st) : st
const svg = document.getElementById(liste.id)
const dimf = new Dimf(svg)
const varFor = this.calculAssocie.variableFormelle()
const bcomplexe = this.calculAssocie.estDeNatureCalcul(
Nat.or(NatCal.NCalculComplexe, NatCal.NFonctionComplexe, NatCal.NFoncCPlusieursVar)
)
if (this.affichageFormule) {
const calc = bcomplexe
? CalcC.ccbComp(ch, liste, 0,
ch.length - 1, varFor)
: CalcR.ccb(ch, liste, 0, ch.length - 1, varFor)
this.latex.chaineCommentaire = this.preCodeLaTeX + ' ' + calc.chaineLatexSansPar(varFor)
this.latex.positionne(false, dimf)
this.latex.setReady4MathJax()
// Ici il faut un double appel de addQueue (callBack va faire le 2e) car on est en train de charger la figure
addQueue(() => this.callBack(svg))
}
}
CEditeurFormule.prototype.addForeignEltToSvg = function () {
const id = this.listeProprietaire.id
if (id === null) return // Pour les figures servant seulement pour leurs objets de type calcul
let svgFig = document.getElementById(id)
const svgPanel = document.getElementById(this.listeProprietaire.id + 'figurePanel')
if (svgPanel !== null) {
svgFig = svgPanel
}
// Version 7.9.2 l'élément contenant l'éditeur est maintenant un foreignObject contenu dans le svg de la figure
// document.body.appendChild(this.divi)
svgFig.appendChild(this.foreignElt)
this.isInSvg = true
}
CEditeurFormule.prototype.positionne = function (infoRandom, dimfen) {
CAffLiePt.prototype.positionne.call(this, infoRandom, dimfen)
if (!this.foreignElt) this.creeEditeur()
if (!this.isInSvg) {
this.isInSvg = true
this.addForeignEltToSvg()
}
if (this.existe && this.affichageFormule && this.latex) {
this.placeLatex()
this.latex.positionne(infoRandom, dimfen)
}
if (this.existe) {
this.editeur.style.visibility = this.masque ? 'hidden' : 'visible' // Modifié version 6.5.2
// Version 6.5.1 : La ligne suivante est déplacée dans trace()
// Version 6.5.2 : Ce n'était pas une bonne idée car alors si au cahrgement l'éditeur est masqué il est mal placé
// quand on veut le démasquer
this.placeEditeur()
} else this.editeur.style.visibility = 'hidden'
}
CEditeurFormule.prototype.trace = function (svg, couleurFond) {
// if (!this.existe) this.editeur.style.visibility = "hidden";
CAffLiePt.prototype.trace.call(this, svg, couleurFond)
if (!svg.contains(this.foreignElt)) svg.appendChild(this.foreignElt)
// this.placeEditeur() // Déplacé ici version 6.5.1 // Supprimé version 6.5.2
if (this.affichageFormule && this.affLaTeXPremierAffichage) {
// this.latex.positionne();
this.glatex = this.latex.createg()
if (!this.affLaTeXPremierAffichage) this.glatex.setAttribute('visibility', 'hidden')
svg.appendChild(this.glatex)
}
}
/**
* Fonction appelée pour valider ou non sur le plan syntaxique le contenu de l'éditeur.
* @param {boolean} redraw Di true on réactualise l'affichage de la formule.
* @returns {boolean}
*/
CEditeurFormule.prototype.validation = function (redraw) {
let v, calc
const listePr = this.listeProprietaire
const txt = this.editeur.value
if (txt.length === 0) return true
// String chCalcul = getText(); // Modifié version 4.6
const chCalcul = this.variables1Car ? listePr.addImplicitMult(txt) : txt
// Spécial version Applet !
const calculAssocie = this.calculAssocie
// On autorise d'utiliser un calcul non créé par l'utilisateur
const indDernier = listePr.indexOf(calculAssocie) - 1
const varFor = calculAssocie.variableFormelle()
const bcomplexe = calculAssocie.estDeNatureCalcul(
Nat.or(NatCal.NCalculComplexe, NatCal.NFonctionComplexe, NatCal.NFoncCPlusieursVar)
)
if (bcomplexe
? CalcC.verifieSyntaxeComplexe(listePr, chCalcul, this.indErr,
indDernier, varFor)
: CalcR.verifieSyntaxe(listePr, chCalcul, this.indErr,
indDernier, varFor)) {
calc = bcomplexe
? CalcC.ccbComp(chCalcul, listePr, 0,
chCalcul.length - 1, varFor)
: CalcR.ccb(chCalcul, listePr, 0,
chCalcul.length - 1, varFor)
if (calculAssocie.estDeNatureCalcul(Nat.or(NatCal.NCalculReel, NatCal.NCalculComplexe))) {
try {
calc.listeProprietaire.initialiseNombreIterations()
if (bcomplexe) {
v = new Complexe()
calc.resultatComplexe(false, v)
} else {
calc.resultat(false)
}
} catch (e) {
this.indErr.setValue(txt.length)
return false
}
}
calculAssocie.calcul = calc
// Modification version 4.5.2 : Quand la chaîne de calcul sera reconstruite elle pourra être différente de celle ici entrée
// On la change tout de suite
calculAssocie.chaineCalcul = calculAssocie.calcul.chaineCalculSansPar(varFor)
//
// Modification bug version 4.7.1 : On remet à jour les objets dépendant, en particulier les calculs de dérivées
// Modifié version 4.7.2
// Modifié version 4.7.3
listePr.metAJourObjetsDependantDeSaufEditeursFormule(calculAssocie)
// On recalcule la figure et on la réaffiche
if (redraw) {
const svgApp = document.getElementById(listePr.id + 'figurePanel')
const svg = svgApp !== null ? svgApp : document.getElementById(listePr.id) // Ajout version 2.6.4
const dimf = new Dimf(svg)
// listePr.positionne(false, dimf);
const doc = listePr.documentProprietaire
// Ajout version 5.2. Indispensable
// listePr.setReady4MathJaxUpdate();
//
// listePr.update(svg, doc.couleurFond, doc.opacity, true);
// Version 6.8.0 : On rajoute à la ligen ci-dessous un dernier paramètre à true pour que ce soit
// positionneFull qui soit appelé oour chaque objet dépendant de façon à recalculer les dérivées et dérivées partielles
// listePr.positionneDependantsDe(false, dimf, calculAssocie)
listePr.positionneDependantsDe(false, dimf, calculAssocie, true)
listePr.updateDependants(calculAssocie, svg, doc.couleurFond, true)
/* Version 7.6.1 : les lignes suivantes deviennent inutiles car listePr.updateDependants se charge
// de mettre à jour l'affichage de formule en LaTeX
if (this.affichageFormule) {
this.latex.chaineCommentaire = this.preCodeLaTeX + ' ' + this.calculAssocie.calcul.chaineLatexSansPar(varFor)
this.latex.positionne(false, dimf)
// Modification version 4.9.2
this.latex.setReady4MathJax()
// Ici pas de double appel de addQueue car la figure et déjà chargée
this.callBack(svg)
}
*/
}
return true
} else {
this.adapteIndiceErreur()
return false
}
}
/**
* Fonction renvoyant true si car est une chaine de un caractère contenant soit
* un chiffre soit un caractère ²
* @param {string} car
* @returns {boolean}
*/
CEditeurFormule.prototype.chiffreOuCarre = function (car) {
return ((car === '²') || Fonte.chiffre(car))
}
/**
* Fonction renvoyant true si car est une chaine de un caractère contenant soit
* un chiffre soit le point décimal .
* @param {string} car
* @returns {boolean}
*/
CEditeurFormule.prototype.chiffreOuPointDecimal = function (car) {
return ((car === '.') || Fonte.chiffre(car))
}
/**
* Fonction renvoyant true si car est une chaine de un caractère contenant soit
* une lettre soit un caractère ' ou "
* @param {string} car
* @returns {boolean}
*/
CEditeurFormule.prototype.lettreOuPrime = function (car) {
return (Fonte.lettre(car) || (car === '\'') || (car === '"'))
}
/**
* Fonction mettant, dans le cas d'une erreur de syntaxe, dans this .indErr l'indice
* de l'erreur dans la chaîne de caractères de l'éditeur, dans le cas où
* les signes de multiplication implicites sont utilisés.
* @returns {void}
*/
CEditeurFormule.prototype.adapteIndiceErreur = function () {
const liste = this.listeProprietaire
const lon = new Pointeur(0)
if (this.variables1Car) {
const ch = this.editeur.value
let carprecedent = '('
let k = 0 // Représente l'indice dans la chaîne à laquelle ont été rajoutés des éventuels signs de multiplication
let i = 0 // Reprsente l'indice dans la chaîe qui a été entrée avec des multiplications implicites.
const len = ch.length
while ((k <= this.indErr.getValue()) && (i < len)) {
const car = ch.charAt(i)
if (this.chiffreOuPointDecimal(car) || (car === ')') || (car === '+') || (car === '-') ||
(car === '*') || (car === '/') || (car === '^') || (car === '\'') || (car === '"') || (car === '²')) {
carprecedent = car
i++
k++
} else {
if (ch.indexOf('pi', i) === 0) {
k += 2
if (!(this.chiffreOuPointDecimal(carprecedent) || this.lettreOuPrime(carprecedent) || (carprecedent === ')'))) k++
carprecedent = ')'
i += 2
} else {
if (CCbGlob.commenceParNomFonctionPredefinieSuivieParOuvr(ch, i, lon)) {
k += lon.getValue()
if (this.chiffreOuPointDecimal(carprecedent) || this.lettreOuPrime(carprecedent) || (carprecedent === ')')) k++
i += lon.getValue()
carprecedent = '('
} else {
if (liste.commenceParNomFonctionUtilisateurSuivieParOuvr(ch, i, lon)) {
k += lon.getValue()
if (this.chiffreOuPointDecimal(carprecedent) || this.lettreOuPrime(carprecedent) || (carprecedent === ')')) k++
i += lon.getValue()
carprecedent = '('
} else {
k++
i++
// Si lettre suivie d'une autre lettre
if (this.lettreOuPrime(carprecedent) && Fonte.lettre(car)) k++
// Si chiffre suivi d'une lettre
else if (this.chiffreOuCarre(carprecedent) && Fonte.lettre(car)) k++
// Si chiffre ou lettre suivi d'une parenthèse on rajoute un signe de multiplication
else if ((this.chiffreOuCarre(carprecedent) || this.lettreOuPrime(carprecedent)) && (car === '(')) k++
// Si parenthèse fermante suivie d'un chiffre ou une lettre ou une parenthèse ouvrante on rajoute un signe de multiplication
else if ((carprecedent === ')') && (Fonte.lettre(car) || Fonte.chiffre(car) || (car === '('))) k++
carprecedent = car
}
}
}
}
}
this.indErr.setValue(i)
}
}
CEditeurFormule.prototype.chaineDesignation = function () {
return 'desEditeur'
}
CEditeurFormule.prototype.read = function (inps, list) {
CAffLiePt.prototype.read.call(this, inps, list)
const ind1 = inps.readInt()
this.calculAssocie = list.get(ind1, 'CFonc')
this.enTete = traiteAccents(inps.readUTF())
this.nbcol = inps.readInt()
if (this.nVersion < 2) this.variables1Car = false
else this.variables1Car = inps.readBoolean()
if (this.nVersion < 3) {
this.affichageFormule = false
this.preCodeLaTeX = ''
} else {
this.affichageFormule = inps.readBoolean()
this.preCodeLaTeX = inps.readUTF()
}
if (this.nVersion < 4) {
this.affLaTeXPremierAffichage = true
this.affichageTempsReel = false
} else {
this.affLaTeXPremierAffichage = inps.readBoolean()
this.affichageTempsReel = inps.readBoolean()
}
// Ajout version 4.9.3
this.callBackOK = null
const entete = this.enTete
// Pour un chargement de MathJax optimal du lecteur on renseigne la liste propriétaire du fait
// que l'objet nécessite ou non MathJax
this.listeProprietaire.useLatex = (entete.charAt(0) === '$' && entete.charAt(entete.length - 1) === '$') ||
this.affichageFormule
}
CEditeurFormule.prototype.write = function (oups, list) {
CAffLiePt.prototype.write.call(this, oups, list)
const ind1 = list.indexOf(this.calculAssocie)
oups.writeInt(ind1)
oups.writeUTF(this.enTete)
oups.writeInt(this.nbcol)
oups.writeBoolean(this.variables1Car)
// Ajout version 4.7.2
oups.writeBoolean(this.affichageFormule)
oups.writeUTF(this.preCodeLaTeX)
oups.writeBoolean(this.affLaTeXPremierAffichage)
oups.writeBoolean(this.affichageTempsReel)
}
/**
* Fonction à appeler depuis un autre code JavaScript.
* Quant l'utilisateur validera sa formule dans l'éditeur
* par la touche Entée, a fonction f possédée par le code appelant sera allors appelée.
* @param {function} f
* @returns {void}
*/
CEditeurFormule.prototype.setCallBackOK = function (f) {
this.callBackOK = f
}
CEditeurFormule.prototype.hasComponent = function () {
return true
}
CEditeurFormule.prototype.showComponent = function (bvisible) {
this.editeur.style.visibility = bvisible ? 'visible' : 'hidden'
if (this.affichageFormule && this.glatex) {
this.glatex.setAttribute('visibility', bvisible ? 'visible' : 'hidden')
}
}