/*
* 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 NatCal from '../types/NatCal'
import Dimf from '../types/Dimf'
import { ce, cens, getStr } from '../kernel/kernel'
import CValDyn from './CValDyn'
import addQueue from 'src/kernel/addQueue'
import { setAttrs } from '../kernel/dom'
export default CVariableBornee
/**
* Classe représentant une variable définie par sa valeur actuelle, ses valeurs mini
* eet maxi et son pas d'incrémentation.
* @constructor
* @extends CValDyn
* @param {CListeObjets} listeProprietaire La liste proprétaire de l'objet
* @param {CImplementationProto} impProto null ou la construction propriétaire
* @param {boolean} estElementFinal true si l'objet est un élément final de construction
* @param {string} nomCalcul Le nom de la variable.
* @param {number} valeurActuelle La valeur actuelle de la variable
* @param {number} valeurMini La valeur mini de la variable
* @param {number} valeurMaxi La valeur maxi de la variable
* @param {number} valeurPas Le pas d'incrémentation
* @param {string} chaineValeurMini : Chaîne de caractères représentant le calcul donnant la valeur mini.
* Ce calcul doit être constant.
* @param {string} chaineValeurMaxi Chaîne de caractères représentant le calcul donnant la valeur maxi.
* Ce calcul doit être constant.
* @param {string} chaineValeurPas Chaîne de caractères représentant le calcul donnant le pas.
* Ce calcul doit être constant.
* @param {boolean} dialogueAssocie true si la figure doit contenir en bas et à droite une boîte
* de dialogue comportant des boutons + - et = pour modifier la variable.
* @returns {CVariableBornee}
*/
function CVariableBornee (listeProprietaire, impProto, estElementFinal, nomCalcul,
valeurActuelle, valeurMini, valeurMaxi, valeurPas,
chaineValeurMini, chaineValeurMaxi, chaineValeurPas, dialogueAssocie) {
if (arguments.length === 1) CValDyn.call(this, listeProprietaire)
else {
CValDyn.call(this, listeProprietaire, impProto, estElementFinal)
this.nomCalcul = nomCalcul
this.valeurActuelle = valeurActuelle
this.valeurMini = valeurMini
this.valeurMaxi = valeurMaxi
this.valeurPas = valeurPas
this.chaineValeurMini = chaineValeurMini
this.chaineValeurMaxi = chaineValeurMaxi
this.chaineValeurPas = chaineValeurPas
this.dialogueAssocie = dialogueAssocie
}
}
CVariableBornee.prototype = new CValDyn()
CVariableBornee.prototype.constructor = CVariableBornee
CVariableBornee.prototype.superClass = 'CValDyn'
CVariableBornee.prototype.className = 'CVariableBornee'
CVariableBornee.prototype.getClone = function (listeSource, listeCible) {
const ind1 = listeSource.indexOf(this.impProto)
return new CVariableBornee(listeCible, listeCible.get(ind1, 'CImplementationProto'),
this.estElementFinal, this.nomCalcul, this.valeurActuelle, this.valeurMini, this.valeurMaxi, this.valeurPas,
this.chaineValeurMini, this.chaineValeurMaxi, this.chaineValeurPas, this.dialogueAssocie)
}
CVariableBornee.prototype.setClone = function (ptel) {
this.valeurActuelle = ptel.valeurActuelle
this.valeurMini = ptel.valeurMini
this.valeurMaxi = ptel.valeurMaxi
this.valeurPas = ptel.valeurPas
}
CVariableBornee.prototype.getNatureCalcul = function () {
return NatCal.NVariable
}
CVariableBornee.prototype.donneNom = function (nouveauNom) {
this.nomCalcul = nouveauNom
}
CVariableBornee.prototype.rendValeur = function () {
return this.valeurActuelle
}
/**
* Fonction incrémentant la valeur actuelle de la valeur du pas si le résultat
* est inférieur à la valeur maxi.
* @returns {void}
*/
CVariableBornee.prototype.incremente = function () {
const nouv = this.valeurActuelle + this.valeurPas
if ((nouv >= this.valeurMini) && (nouv <= this.valeurMaxi)) this.valeurActuelle = nouv
}
/**
* Fonction décrémentant la valeur actuelle de la valeur du pas si le résultat
* est supérieur à la valeur mini.
* @returns {void}
*/
CVariableBornee.prototype.decremente = function () {
const nouv = this.valeurActuelle - this.valeurPas
if ((nouv >= this.valeurMini) && (nouv <= this.valeurMaxi)) { this.valeurActuelle = nouv }
}
/**
* Fonction donnant à la valeur actuelle la valeur valeur, sans vérifier
* si cette valeur est comprise entre le valeurs mini et maxi.
* @param {number} valeur
* @returns {void}
*/
CVariableBornee.prototype.donneValeur = function (valeur) {
this.valeurActuelle = valeur
}
// A revoir. Utilisé ?
/**
* Retourne le nom du calcul
* @returns {string}
*/
CVariableBornee.prototype.getNom = function () {
return this.nomCalcul
}
CVariableBornee.prototype.read = function (inps, list) {
CValDyn.prototype.read.call(this, inps, list)
this.nomCalcul = inps.readUTF()
this.valeurActuelle = inps.readDouble()
this.valeurMini = inps.readDouble()
this.valeurMaxi = inps.readDouble()
this.valeurPas = inps.readDouble()
this.dialogueAssocie = inps.readBoolean()
this.chaineValeurMini = inps.readUTF()
this.chaineValeurMaxi = inps.readUTF()
this.chaineValeurPas = inps.readUTF()
}
CVariableBornee.prototype.write = function (oups, list) {
CValDyn.prototype.write.call(this, oups, list)
oups.writeUTF(this.nomCalcul)
oups.writeDouble(this.valeurActuelle)
oups.writeDouble(this.valeurMini)
oups.writeDouble(this.valeurMaxi)
oups.writeDouble(this.valeurPas)
oups.writeBoolean(this.dialogueAssocie)
oups.writeUTF(this.chaineValeurMini)
oups.writeUTF(this.chaineValeurMaxi)
oups.writeUTF(this.chaineValeurPas)
}
// Fonction créant dans le document un Div associé contenant l'affichage de la valeur de la variable
// et deux boutons + et - pour l'incrémenter et la décrémenter
// ind est l'indice dans la liste des variables associées à des boutons
/**
* Fonction créant dans le document un foreign object associé contenant l'affichage de la valeur de la variable
* et deux boutons + et - pour l'incrémenter et la décrémenter.
* N'est appelée que si this.dialogueAssocie est true.
* Le nom creeDiv a été gardé mais on ne crée pas un div mais un foreign object
* @param {number} ind L'indice de cette variable dans la liste des variables ayant un dialogue associé
* en bas et à droite de la figure.
* @returns {void}
*/
CVariableBornee.prototype.creeDiv = function (ind) {
// Version 7.9.2 : On n'utilise plus un div mais un foreignObject
// this.div = ce('div')
const id = this.listeProprietaire.id
this.foreignElt = cens('foreignObject', {
style: 'overflow:visible;position:absolute;margin:0px;pointer-events:all'
})
this.foreignElt.setAttribute('x', '0') // Provisoire
this.foreignElt.setAttribute('y', '0') // Provisoire
let svgFig = document.getElementById(id)
let parentDiv = svgFig.parentNode
if (parentDiv.tagName.toLowerCase() === 'svg') parentDiv = parentDiv.parentNode
const svgPanel = document.getElementById(this.listeProprietaire.id + 'figurePanel')
if (svgPanel !== null) {
svgFig = svgPanel
}
// Il est nécessaire d'utiliser un tableau avec des td car sur tablettes les boutons se retrouvent à la ligne
const tab = ce('table')
// this.foreignElt.appendChild(tab)
const tr = ce('tr')
tab.appendChild(tr)
// Affecter le style ci-dessous aux td créés ci-dessous est indispensable sinon il y a des différences
// de rendu entre les versions electron et en ligne
const styletd = 'padding:0px;font-style:9pt;line-height:1'
let td = ce('td', {
style: styletd
})
tr.appendChild(td)
this.editeur = ce('input', {
readOnly: 'true',
style: 'font-size:9pt;padding:2px;border:1px solid grey;'
})
td.appendChild(this.editeur)
this.editeur.name = this.listeProprietaire.id + this.nomCalcul
this.updateDisplay()
td = ce('td', {
style: styletd
})
tr.appendChild(td)
// Attention : Utiliser des inputs car si on utilise des buttons il ne faut pas utiliser value mais innerHtml
this.buttonplus = ce('input', {
type: 'button',
value: '+'
})
td.appendChild(this.buttonplus)
const t = this
this.buttonplus.onclick = function () {
CVariableBornee.onClickPlus.call(t)
}
td = ce('td', {
style: styletd
})
tr.appendChild(td)
this.buttonmoins = ce('input', {
type: 'button',
value: '-'
})
td.appendChild(this.buttonmoins)
this.buttonmoins.onclick = function () {
CVariableBornee.onClickMoins.call(t)
}
td = ce('td', {
style: styletd
})
tr.appendChild(td)
this.buttonegal = ce('input', {
type: 'button',
value: '='
})
td.appendChild(this.buttonegal)
this.buttonegal.onclick = () => {
this.onClickEgal()
}
// On met dans x et y les dimensions de travail de la figure qui ont été mis dans le svg.dimWork
// de la figure. En effet depuis la version 9, les dimensions de la figure peuvent dépasser le cadre de travail
let x = parseInt(svgFig.dimWork.x) - 24
let y = parseInt(svgFig.dimWork.y) - 15 // Pour compenser la présence d'un ascenseur éventuel
// On ajoute d'abord le div au parent du SVG de la figure qui est div pour connaître sa taille puis on le retire
// svgFig.appendChild(this.foreignElt)
parentDiv.appendChild(tab) // Pour connaître les dimensions
const width = tab.clientWidth
const height = tab.clientHeight
x = x - width
y = y - height * (ind + 1)
parentDiv.removeChild(tab)
const attr = { x, y, width, height }
setAttrs(this.foreignElt, attr)
this.foreignElt.appendChild(tab)
svgFig.appendChild(this.foreignElt)
// Les lignes suivantes sont indipensables pour que le foreign object soit visible dans FireFox
}
/**
* Fonction appelée quand l'utilisateur clique sur le bouton + du dialogue associé
* à la variable (si this.dialogueAssocie est true).
* Augmente la variable de la valeur de son pas.
* @returns {void}
*/
CVariableBornee.onClickPlus = function () {
const liste = this.listeProprietaire
const id = liste.id
const doc = liste.documentProprietaire
// Modification version 6.3.0
const svgApp = document.getElementById(id + 'figurePanel')
const svg = svgApp === null ? document.getElementById(id) : svgApp
const dimf = new Dimf(svg)
this.incremente()
liste.positionneDependantsDe(false, dimf, this)
liste.setDependantsReady4MathJaxUpdate(this) // Ajout version 5.0.6
addQueue(() => liste.updateDependants(this, svg, doc.couleurFond, true))
// Ce code doit être après la mise sur la pile de updateDependants,
// car updateDisplay (qui sera donc appelé avant updateDependants) peut ajouter des actions sur la pile
this.updateDisplay()
}
/**
* Fonction appelée quand l'utilisateur clique sur le bouton - du dialoge associé
* à la variable (si this.dialogueAssocie est true).
* Diminue la variable de la valeur de son pas.
* @returns {void}
*/
CVariableBornee.onClickMoins = function () {
const liste = this.listeProprietaire
const id = liste.id
const doc = liste.documentProprietaire
const svgApp = document.getElementById(id + 'figurePanel')
const svg = svgApp === null ? document.getElementById(id) : svgApp
const dimf = new Dimf(svg)
this.decremente()
liste.positionneDependantsDe(false, dimf, this)
liste.setDependantsReady4MathJaxUpdate(this) // Ajout version 5.0.6
addQueue(() => liste.updateDependants(this, svg, doc.couleurFond, true))
// FIXME expliquer pourquoi il faut lancer ça avant updateDependants
this.updateDisplay()
}
/**
* On définit cette fonction pour qu'on puisse l'appeler dans le cas d'un mtgAppLecteur
* sur lequel a été appliquée l'API car dans ce cas CVariableBornee.prototype.onClickEgal
* a été écrasé par la version contenue dans le fichier CVariableBorneeAdd.js
*/
CVariableBornee.prototype.onClickEgalBase = function () {
const ch = getStr('var1') + this.nomCalcul + '\n' + getStr('var2') + this.valeurMini + getStr('et') + this.valeurMaxi
// On n'utilise pas de boîte de dialogue car on peut être dans le player
const va = prompt(ch, this.valeurActuelle)
if (va === null) return
const val = parseFloat(va)
if (!isFinite(val) || (val < this.valeurMini) || (val > this.valeurMaxi)) {
// On n'utilise pas AvertDlg car on peut être dans le player
alert(getStr('invalidEntry'))
return
}
this.donneValeur(val)
const list = this.listeProprietaire
const id = list.id
const doc = list.documentProprietaire
const svgApp = document.getElementById(id + 'figurePanel')
const svg = svgApp === null ? document.getElementById(id) : svgApp
const dimf = new Dimf(svg)
list.initialiseDependances()
list.positionneDependantsDe(false, dimf, this)
list.metAJourObjetsDependantDe(this)
// Il faut rappeler positionneDependantsDe avec un dernier paramètre à true
// Pour que ce soit par positionneFull qui soit appelé pour chaque élément dépendant de el car si, par exemple,
// un ematrice avait des termes dépendants d'une dérivée partielle qui vient d'être recalculée
// par metAJourObjetsDependantDe ses termes doivent tous être recalculés
// list.positionneDependantsDe(false, app.dimf, el) // Il faut rappeler positionneDependantsDe pour par exemple recalculer les dérivées
list.positionneDependantsDe(false, dimf, this, true) // Il faut rappeler positionneDependantsDe pour par exemple recalculer les dérivées
list.setDependantLatexToBeUpdated(this) // Le deuxième positionnement des CLaTeX dépendant de el a mis leur membre isToBeUpdated
// à false et il faut le remettre à true pour qu'ils soient réaffichés
// car ces dérivées n'ont été mises à jour que dans metAJourObjetsDependantDe
list.updateDependants(this, svg, doc.couleurFond, true)
this.updateDisplay()
}
/**
* Fonction appelée quand l'utilisateur clique sur le bouton = du dialoge associé
* à la variable (si this.dialogueAssocie est true).
* Fait apparaître une boîte de dialogue permettant de modifier la valeur actuelle
* de la variable. La valeur entrée n'est affectée à valeurActuelle que si elle est
* comprise entre les valeurs mini et maxi.
* Est redéfini dans CVariableBorneeAdd pour la version mtgloader
* @returns {void}
*/
CVariableBornee.prototype.onClickEgal = CVariableBornee.prototype.onClickEgalBase
/**
* Fonction remettant à jour l'affichage en bas et à droite de la figure du dialogue associé
* à la variable (ne peut être appelé que si this.dialogueAssocie est true).
* @returns {void}
*/
CVariableBornee.prototype.updateDisplay = function () {
if (this.dialogueAssocie) this.editeur.value = this.nomCalcul + ' = ' + this.rendChaineValeur(12)
}
/*
CVariableBornee.prototype.deleteComponent = function (svg) {
try {
var par = svg.parentNode
// Modification version WepPack. Dans cette version le parent peut être un svg
if (par.tagName.toLowerCase() !== 'div') par = par.parentNode
par.removeChild(this.div)
} catch (ex) {}
}
*/
/**
* Fonction détruisant le div représentant le dialogue affiché en bas et à droite de la figure et associé
* à la variable, avec les bouton +, - et = associés.
* Ne peut être appelée que si this.dialogeAssocie est true.
* @returns {void}
*/
CVariableBornee.prototype.deleteComponent = function () {
try {
if (this.foreignElt) {
this.foreignElt.parentNode.removeChild(this.foreignElt)
this.foreignElt = null
}
} catch (ex) { /* on ignore volontairement */ }
}
/**
* Cette fonction renvoie true pour les objets qui implémentent dans la figure une élément "externe"
* sous la forme d'un foreign Objet foreignElt
*/
CVariableBornee.prototype.hasComponent = function () {
return this.dialogueAssocie
}