legacy/outils/scratch/Scratch.js

/*
 * Ce fichier contient la classe Scratch, qui reprend tout le code de l’ancien
 * tomblok.js pour le mettre dans un objet avec des méthodes.
 * Attention, l’objet Blockly de départ n’est pas exactement le même, on utilise ici celui du paquet scratch-blocks
 * alors que l’ancien tomblok utilisait un Blockly figé mis en dur dans nos outils externes (cf src/lib/outils/scratch/legacy.js avant 2023-01-09)
 */

import $ from 'jquery'

import { j3pAddContent, j3pAddElt, j3pAjouteBouton, j3pArrondi, j3pClone, j3pDetruit, j3pEmpty, j3pFreezeElt, j3pGetNewId, j3pModale, j3pShuffle, j3pStyle } from 'src/legacy/core/functions'
import { j3pCreeSVG, j3pSvgAppend } from 'src/legacy/core/functionsSvg'
import { legacyStyles } from 'src/legacy/core/StylesJ3p'
import BulleAide from 'src/legacy/outils/bulleAide/BulleAide'
import MenuContextuel from 'src/legacy/outils/menuContextuel'
import Zoneclick from 'src/legacy/outils/zoneclick/Zoneclick'
import { addDefaultTable } from 'src/legacy/themes/table'

import { j3pBaseUrl } from 'src/lib/core/constantes'
import { j3pAffiche } from 'src/lib/mathquill/functions'

import ajoutsBlockly from './ajoutsBlockly'

// nos images
// lignes suivantes générées avec cette commande (lancée dans ce dossier)
// ls -1 images/*.svg images/*.gif images/*.png images/*.jpg|while read f; do n=${f%.*}; echo "import ${n}Img from './$f'"; done
// import arc3pImg from './images/arc3p.png'
// import arcmilImg from './images/arcmil.png'
import barreboutonImg from './images/barrebouton.png'
import blackholeImg from './images/blackhole.svg'
import bras2Img from './images/bras2.png'
import cercleptImg from './images/cerclept.png'
import cerclerayImg from './images/cercleray.png'
import chatImg from './images/chat.png'
import cosmoImg from './images/cosmo2.png'
import croixImg from './images/croix.png'
import demidroiteptImg from './images/demidroitept.png'
import dessinImg from './images/dessin.png'
import dessinImg2 from './images/dessin2.png'
import dessinleveImg from './images/dessinleve.png'
import dessinleveImg2 from './images/dessinleve2.png'
import drapeauImg from './images/drapeau.gif'
import drapeauokImg from './images/drapeauok.gif'
import drapeauImgVite from './images/drapeauvite.gif'
import droiteparaImg from './images/droitepara.png'
import triangleImg from './images/triangle.png'
import pointlibreImg from './images/pointlibre.png'
import pointinterImg from './images/pointinter.png'
import mediatriceImg from './images/mediatrice.png'
import diametreImg from './images/diametre.png'
import anglImg from './images/angle.png'
import arcmilIMG from './images/arcmil.png'
import droiteperpImg from './images/droiteperp.png'
import droiteptImg from './images/droitept.png'
import effacerImg from './images/effacer.png'
import enregistre from './images/enre.jpg'
import fondetoileImg from './images/fondetoile2.png'
import fourcheImg from './images/fourche.gif'
// import fondvarImg from './images/fondvar.jpg'
import fuseeImg from './images/fusee.svg'
import importer from './images/importer.png'
import murImg from './images/mur.jpg'
import num1Img from './images/num1.png'
import num2Img from './images/num2.png'
import num3Img from './images/num3.png'
import num4Img from './images/num4.png'
import obj1Img from './images/obj1.gif'
import obj2Img from './images/obj2.gif'
// import pinceImg from './images/pince.png'
import planetrougeImg from './images/planetrouge.svg'
// import pointinterImg from './images/pointinter.png'
// import pointlibreImg from './images/pointlibre.png'
// import pointmilieuImg from './images/pointmilieu.png'
// import pointsurImg from './images/pointsur.png'
import radarImg from './images/radar.png'
import retourImg from './images/retour.jpg'
// import segmentlongImg from './images/segmentlong.png'
import segmentImg from './images/segment.png'
import stopImg from './images/stop.gif'
import tapisImg from './images/tapis.gif'
import tapis2Img from './images/tapis2.gif'
import terreImg from './images/terre.svg'
import tuxImg from './images/tux.png'
import ScratchBase from './ScratchBase'
// import valgetusImg from './images/valgetus.png'
// import work1Img from './images/work1.png'
// import work2Img from './images/work2.png'
// import work3Img from './images/work3.png'
// import work4Img from './images/work4.png'

// options à passer à addElt pour le drapeau vert (utilisé plusieurs fois dans ce fichier)
const drapeauOpts = { src: drapeauImg, alt: 'Tester le programme' } // alt utilisé par testBrowser pour trouver ce bouton

const listLL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ¶©®™♠♣♥♦↑↓↔'

const sortOnLengthProp = (a, b) => b.length - a.length

/**
 * Construit un gros objet plein de méthodes pour les sections scratch
 * @todo découper cette classe en plusieurs classes différentes (toutes héritées de ScratchBase) suivant les besoins (dessin, mtg, etc.)
 */
class Scratch extends ScratchBase {
  constructor (Blockly) {
    // on étend ScratchBase, donc on doit appeler son constructeur en premier
    super(Blockly)
    // ici on a donc les propriétés et méthodes de ScratchBase, on ajoute ce qui est spécifique à cette classe Scratch
    this.params = {
      imageLutin: [],
      AffVarRect1: [],
      AffVarRect2: [],
      AffVarText1: [],
      AffVarText2: [],
      figIm: [],
      NomsVariableAutorise: [], /// les noms que l’eleve peut donner aux variables
      AutoriseSupprimeVariable: true,
      AutoriseRenomeVariable: true,
      nbEssais: 1,
      BoutPasSuiv: [],
      figureAfaire: [],
      yamodel: false,
      yafusee: false,
      yabras: false,
      validation: [],
      blokmenulimite: [],
      // ces propriétés seront affectées dans initParams
      forcealeatoire: false,
      ModeSequence: true, // en mode sequence c’est a peu pres 1 sec par block
      ProposePasaPas: true,
      NbEssaiIllimite: true,
      nbEssaisbase: 1,
      correctionCachee: false,
      me: null, // @todo parcours serait plus adapté comme nom de propriété
      scratchProg: {
        pause: false,
        flag: [], // liste des objectifs
        // this.testFlag à surcharger dans la section
        variables: [],
        workspace: {},
        progcolo: false,
        LesVariablesCo: [],
        tabcodeX: [],
        lesvarbonnes: [],
        reponse: 'vide',
        resultatAttenduDire: [],
        Lutins: [{
          coxbase: 90,
          coybase: 100,
          anglebase: 0,
          cox: 100,
          coy: 100,
          img: chatImg,
          angle: 0,
          id: 'chat',
          larg: 120,
          haut: 100,
          hauteur: 0,
          hauteurbase: 0
        }],
        progdeb: [],
        menu: [],
        menuLimite: {},
        Valeursdebaseautorisees: [],
        Valeursautorisees: [],
        Valeursutilisees: [],
        said: [],
        lesflag: [],
        valeursaleatoiressorties: [],
        lignetracees: [],
        lesprogs: [],
        Progused: [],
        resetflag: true,
        squizzVar: false
      },
      listeDef: {},
      lesBlocPerso: [],
      aiPrevenuNomArc: false,
      yaFauxClavier: false
    } // params
    this.Bouton = {}
    this.lesdivs = {}
    this.maxIdCo = 0
    // il faut stocker ça en propriété pour pouvoir le mettre dans une méthode et le retirer dans une autre (ça doit être la même)
    this._decodeListener = this.decode.bind(this)
    this._scratchVerifRunListener = this.scratchVerifRun.bind(this)

    // et on ajoute nos personnalisations à Blockly
    ajoutsBlockly(this.Blockly, this)
  }

  scratchPetitScratchAjouteColoeur (s) {
    const lacoul = this.fileCouleur(s)
    const lx = (this.params.scratchProgDonnees.Lutins[0].cox / 50)
    const ly = (this.params.scratchProgDonnees.Lutins[0].coy / 50)
    this.params.scratchProgDonnees.carteCases[lx][ly] = s
    if (this.params.modeCompare) return
    if (this.params.cacheTest) return
    j3pSvgAppend(this.params.svg, 'rect', {
      x: this.params.scratchProgDonnees.Lutins[0].cox,
      y: this.params.scratchProgDonnees.Lutins[0].coy,
      width: 50,
      height: 50
    }, {
      style: 'fill:' + lacoul + ';stroke:black;stroke-width:4;fill-opacity:1;stroke-opacity:1'
    })
    this.scratchPetitScratch()
  }

  fileCouleur (s) {
    switch (s) {
      case 'jaune': return '#f2ff1f'
      case 'bleue': return '#003cff'
      case 'rouge': return '#ff0000'
      case 'verte': return '#43c700'
      case 'noire': return '#000000'
      case 'violette': return '#a900d9'
      case 'orange': return '#ff500e'
      case 'rose': return '#ff41dc'
    }
  }

  scratchCreeMenu (tab, deb) {
    let aaj = ''
    let aajfin = ''
    let bufaj, bufcus
    if (deb === true) {
      aaj = '<?xml version="1.0"?><xml id="toolbox" >'
      aajfin = '</xml>'
    }

    for (let i = 0; i < tab.length; i++) {
      let bufi = ''
      if (tab[i].movable !== undefined) bufi += ' movable="' + tab[i].movable + '" '
      if (tab[i].deletable !== undefined) bufi += ' deletable="' + tab[i].deletable + '" '
      if (tab[i].editable !== undefined) bufi += ' editable="' + tab[i].editable + '" '
      if (tab[i].disabled === true) continue
      if (tab[i].id !== undefined) bufi += ' id="' + tab[i].id + '" '
      else if (tab[i].quoi !== undefined) bufi += ' id="' + tab[i].quoi + i + '" '
      else bufi += ' id="' + tab[i].nom + i + '" '

      bufcus = ''
      if (tab[i].type === 'catégorie') {
        if (tab[i].custom !== undefined) bufcus = ' custom="' + tab[i].custom + '" '
        aaj += '<category  ' + bufi + '  name="' + tab[i].nom + '"  colour="' + tab[i].coul1 + '" secondaryColour="' + tab[i].coul2 + '"  ' + bufcus + '  >' + this.scratchCreeMenu(tab[i].contenu, false) + '</category>'
      } else {
        bufaj = this.scratchFaisBlokXml(tab[i])
        aaj += bufaj.aaj + bufaj.aajmil + bufaj.aajfin
      }
    }

    aaj += aajfin
    return aaj
  }

  afficheModale (texte, titre, fonc) {
    if (this.params.isEditing) {
      j3pEmpty(this.params.mtgEditoText)
      j3pAffiche(this.params.mtgEditoText, null, texte)
      return
    }
    const uu = j3pModale({ titre, contenu: '' })
    uu.style.left = '20px'
    uu.style.top = '20px'
    const tab = addDefaultTable(uu, 2, 1)
    const tabu = addDefaultTable(tab[0][0], 1, 2)
    const tab2 = addDefaultTable(tab[1][0], 1, 3)
    j3pAddElt(tabu[0][0], 'img', '', { src: tuxImg, alt: 'Charlie', height: '20px', width: '20px' })
    // j’ajoute cette classe pour retrouver le feedback en cas d’erreur lors des tests (Jean-Claude Lhote)
    tabu[0][1].classList.add('ScratchFeedback')
    j3pAffiche(tabu[0][1], null, texte)
    if (titre === 'Erreur') tabu[0][1].classList.add('errormes')
    const boubout = j3pAjouteBouton(tab2[0][0], this.scratchFerme1.bind(this), { className: 'MepBoutons2', value: 'Fermer' })
    if (titre !== 'Message') {
      if (texte.indexOf('Objectif atteint') === -1) {
        tab2[0][1].style.width = '100%'
        j3pAjouteBouton(tab2[0][2], this.scratchFerme2.bind(this), {
          className: 'MepBoutons2',
          value: 'Fermer et Réinitialiser'
        })
      } else if ((this.params.scratchProgDonnees.Verifutilisateur || this.params.scratchProgDonnees.Verifaleatoire) && !this.params.yaFauxClavier) {
        tab2[0][1].style.width = '100%'
        j3pAjouteBouton(tab2[0][2], this.scratchFerme3.bind(this), {
          className: 'MepBoutons2',
          value: 'Fermer et Valider'
        })
      } else if (!this.params.yaFauxClavier) {
        if (!this.params.squizzEssai) {
          tab2[0][1].style.width = '100%'
          j3pAjouteBouton(tab2[0][2], this.scratchFerme5.bind(this), {
            className: 'MepBoutons2',
            value: 'Fermer et Valider'
          })
        }
      } else {
        tab2[0][1].style.width = '100%'
        j3pAjouteBouton(tab2[0][2], this.scratchFerme2.bind(this), {
          className: 'MepBoutons2',
          value: 'Fermer et Réinitialiser'
        })
      }
    } else {
      if (typeof fonc === 'function') {
        boubout.addEventListener('click', fonc)
      }
    }
    const elem = $('.croix')[0]
    j3pDetruit(elem)
    return uu
  }

  scratchResetFlag () {
    for (let i = 0; i < this.params.scratchProgDonnees.lesflag.length; i++) {
      this.params.scratchProgDonnees.lesflag[i].ok = false
    }
  }

  boutonPasApas () {
    this.bouAaPaPa.classList.remove('bouturg')

    for (let i = this.params.BoutPasSuiv.length - 1; i > -1; i--) {
      this.params.BoutPasSuiv[i]()
      this.params.BoutPasSuiv.splice(i, 1)
    }
  }

  copieDe (tabloo) {
    const tabret = []
    if (tabloo) {
      for (let i = 0; i < tabloo.length; i++) {
        tabret[i] = tabloo[i]
      }
    }
    return tabret
  }

  corrige (xmlDoc2, xmlText) {
    if (this.isMtg && !this.params.squizzEssai) {
      this.params.MtgBoutonFerm.style.display = ''
    }
    this.params.scratchProgDonnees.fostop = false
    this.params.scratchProgDonnees.fostopErreur = false
    this.params.grosStop = false
    this.lesdivs.PASAPAS.style.display = 'none'
    let msg = ''
    if (Array.isArray(this.params.scratchProgDonnees.avertissements)) {
      msg = this.params.scratchProgDonnees.avertissements[0]
      for (let i = 1; i < this.params.scratchProgDonnees.avertissements.length; i++) {
        msg += '<BR>' + this.params.scratchProgDonnees.avertissements[i]
      }
    }
    if (this.params.correctionCachee) msg = ''
    const rrr = addDefaultTable(this.lesdivs.explications, 1, 3)
    rrr[0][0].classList.add('explique')
    j3pAffiche(rrr[0][0], null, msg)
    rrr[0][1].style.width = '100%'
    rrr[0][1].style.textAlign = 'center'
    j3pDetruit(this.lesdivs.BoutonsSuite)
    this.lesdivs.BoutonsSuite = rrr[0][1]
    j3pEmpty(this.lesdivs.divBlocPerso)
    j3pEmpty(this.tabPourCoADiv)
    if (this.isMtg) {
      this.lesdivs.contBlockly.style.border = ''
    }
    if (this.lesdivs.leAvireentre) {
      j3pEmpty(this.lesdivs.leAvireentre)
      this.lesdivs.leAvireentre.style.width = '0%'
      this.lesdivs.leAvireentre.style.border = '1px solid green'
    }
    this.lesdivs.contBlockly.style.width = '100%'
    this.lesdivs.contBlockly.style.borderRight = 0
    this.lesdivs.contBlockly.style.verticalAlign = 'Top'
    const divBlS1 = addDefaultTable(this.tabPourCoADiv, 1, 2)
    const divBlS = addDefaultTable(this.lesdivs.contBlockly, 1, 4)
    divBlS[0][3].style.borderRight = '4px solid black'
    for (let j = 0; j < 3; j++) divBlS[0][j].style.padding = 0
    for (let j = 0; j < 2; j++) {
      divBlS1[0][j].style.padding = 0
      divBlS1[0][j].style.minWidth = '310px'
    }
    divBlS[0][0].parentNode.style.borderBottom = '4px solid black'
    this.lesdivs.zoneCo = rrr[0][2]
    this.lesdivs.zoneCo.classList.add('ScratchFeedback')
    this.lesdivs.drap1 = divBlS1[0][0]
    divBlS[0][1].style.width = '10px'
    this.lesdivs.drap2 = divBlS1[0][1]
    this.fb = j3pAddElt(this.lesdivs.drap2, 'span')
    j3pAddContent(this.fb, '⏷')
    this.lesdivs.drap2.style.textAlign = 'center'
    this.fb.classList.add('flechBasX')
    this.fb.addEventListener('click', this.metActifCo.bind(this))
    this.fb2 = j3pAddElt(this.lesdivs.drap1, 'span')
    j3pAddContent(this.fb2, '⏷')
    this.lesdivs.drap1.style.textAlign = 'center'
    this.fb2.classList.add('flechBasX')
    this.fb2.addEventListener('click', this.metActifRep.bind(this))
    this.corActif = 'Co'
    this.fb.style.color = '#fc0000'
    this.fb2.style.color = '#aaaaaa'
    this.lesdivs.divBlockly = divBlS[0][0]
    this.lesdivs.divBlockly.style.verticalAlign = 'Top'
    this.lesdivs.divBlocklyCo = divBlS[0][2]
    this.lesdivs.divBlocklyCo.style.verticalAlign = 'Top'
    this.lesdivs.zoneCo.style.color = legacyStyles.cfaux
    this.lesdivs.zoneCo.innerHTML += 'C’est faux !'
    this.lesdivs.divBlockly.style.border = '10px solid grey'
    this.lesdivs.divBlocklyCo.style.border = '10px solid red'
    this.scratchBloklyCo(xmlDoc2)
    this.scratchBloklyCoCo(xmlDoc2)
    this.lesdivs.drapeau.style.display = 'none'
    if (this.lesdivs.sortie4) this.lesdivs.sortie4.style.display = 'none'
    this.lesdivs.stop.style.display = 'none'
    let progret
    if (!this.params.MohaMode) {
      if (Array.isArray(this.params.scratchProgDonnees.lesordres[0])) {
        progret = this.scratchCreeProgCo(this.params.scratchProgDonnees.lesordres[0], { kedeb: true })
        const dedec = !(this.params.scratchProgDonnees.empDec === false)
        progret += this.scratchCreeProgCo(this.params.scratchProgDonnees.lesordres[1], { kefin: true, ajoutedebut: true, dedec })
      } else {
        progret = this.scratchCreeProgCo(this.params.scratchProgDonnees.lesordres, { ajoutedebut: true })
      }
    } else {
      progret = j3pClone(this.params.CoMOha)
    }
    this.scratchAfficheDansBloklyCo(xmlText)
    this.scratchAfficheDansBloklyCoco(progret)
    this.scratchCacheCo(this.lesdivs.divBlockly, false)
    this.scratchCacheCo(this.lesdivs.divBlocklyCo, true)
    this.params.scratchProgDonnees.runable = true
    j3pFreezeElt(this.lesdivs.divBlockly)
    // on pourrait aussi geler la solution
    // j3pFreezeElt(this.lesdivs.divBlocklyCo)
    // mais ça supprime le highlight de l'étape courante si on rejoue la solution
    // (parce que les pointeurs sur les sous-éléments ne pointent plus sur les bons qui ont été clonés et remis dans le dom, justement pour supprimer tous les listeners)
    this.montreBoutonReinit()
  }

  metActifCo () {
    this.fb.style.color = '#fc0000'
    this.fb2.style.color = '#aaaaaa'
    this.corActif = 'Co'
    this.lesdivs.divBlocklyCo.style.border = '10px solid red'
    this.lesdivs.divBlockly.style.border = '10px solid grey'
  }

  metActifRep () {
    this.fb2.style.color = '#fc0000'
    this.fb.style.color = '#aaaaaa'
    this.corActif = 'Rep'
    this.lesdivs.divBlocklyCo.style.border = '10px solid grey'
    this.lesdivs.divBlockly.style.border = '10px solid red'
  }

  corrigeGood (xmlDoc2, xmlText) {
    if (this.isMtg && !this.params.squizzEssai) {
      this.params.MtgBoutonFerm.style.display = ''
    }
    this.params.scratchProgDonnees.fostop = false
    this.params.scratchProgDonnees.fostopErreur = false
    this.params.grosStop = false
    this.lesdivs.PASAPAS.style.display = 'none'
    this.lesdivs.Compteur.style.color = legacyStyles.cbien
    if (this.lesdivs.retIm) this.lesdivs.retIm.style.display = 'none'
    this.lesdivs.Compteur.classList.add('ScratchFeedback')
    this.lesdivs.Compteur.innerHTML = 'C’est bien !&nbsp;'
    j3pEmpty(this.lesdivs.divBlocPerso)
    this.lesdivs.divBlockly = j3pAddElt(this.lesdivs.contBlockly, 'div')
    this.scratchBloklyCo(xmlDoc2)
    this.scratchAfficheDansBloklyCo(xmlText)
    this.scratchCacheCo(this.lesdivs.divBlockly, true, true)
    this.lesdivs.stop.style.display = 'none'
    this.montreBoutonReinit()
    this.cacheDrapeaux = () => {
      if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = 'none'
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = 'none'
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = 'none'
    }
    this.montreDrapeaux = () => {
      if (this.params.NbEssaiIllimite || this.params.yaFauxClavier) {
        if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = ''
      }
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = ''
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = ''
      if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = 'none'
    }
  }

  // mis comme listener souris ou en setInterval
  decode () {
    if (this.params.scratchProgDonnees.workspace === undefined) return
    if (this.params.scratchProgDonnees.workspace === null) return
    if (!this.params.scratchProgDonnees.runable) return
    this.params.scratchProgDonnees.runable = false

    this.params.scratchProgDonnees.runable = true
    const leCompte = this.scratchCompteBloc(this.params.workspaceEl)
    if (leCompte.insertion) leCompte.cmpt -= 2
    if (this.params.verifDeb && !leCompte.insertion) {
      const acomp = this.scratchRunBlockly(this.params.workspaceEl)
      for (let i = 0; i < this.params.acomp.length; i++) {
        const liComp = this.ligneComp(this.params.acomp[i][0].id, acomp)
        if (liComp.length === 0) {
          this.fautRaz()
          return
        }
        if (this.caABouge(this.params.acomp[i], liComp)) {
          this.fautRaz()
          return
        }
      }
      this.params.xmlACons = this.Blockly.Xml.workspaceToDom(this.params.workspaceEl)
    }

    if (this.lesdivs.Compteur && this.params.me.etat === 'correction') {
      if (this.params.me) {
        const laWidth = (this.isMtg) ? this.params.me.zonesElts.MG.offsetWidth + 290 : this.params.me.zonesElts.MG.offsetWidth
        if (this.laWidth !== laWidth) {
          this.laWidth = laWidth
          this.lesdivs.divBlockly.style.width = Math.max(600, laWidth - 353) + 'px'
          this.Blockly.svgResize(this.params.workspaceEl)
        }
      }
      if (this.lesdivs.Compteur.childNodes.length) {
        // màj essais restants et nb de blocs
        const elts = this.lesdivs.Compteur.querySelectorAll('span')
        if (elts.length === 2) {
          const [essais, blocs] = elts
          essais.innerHTML = this.params.nbEssais > 1 ? `<u>Essais restants</u>&nbsp;:${this.params.nbEssais}` : '<b>Dernier Essai</b>'
          j3pEmpty(blocs)
          j3pAddContent(blocs, ' ' + leCompte.cmpt + '&nbsp;&nbsp;')
        } else {
          const [blocs] = elts
          j3pEmpty(blocs)
          j3pAddContent(blocs, ' ' + leCompte.cmpt + '&nbsp;&nbsp;')
        }
      } else {
        // 1er appel
        if (!this.params.NbEssaiIllimite && !this.params.squizzEssai) {
          if (this.params.nbEssais > 1) {
            this.lesdivs.Compteur.innerHTML = `&nbsp;<span><u>Essais restants</u>&nbsp;:${this.params.nbEssais}</span> `
          } else {
            this.lesdivs.Compteur.innerHTML = '<u><span><b>Dernier Essai</b></span></u>'
          }
        }
        this.lesdivs.Compteur.innerHTML += `&nbsp;<u>Nombre de Blocs</u>&nbsp;: <span>${leCompte.cmpt}&nbsp;&nbsp;</span>`
        j3pEmpty(this.lesdivs.retIm)
        if (!this.params.scratchProgDonnees.run) {
          this.lesdivs.retIm.style.minWidth = '30px'
          this.lesdivs.retIm2 = j3pAddElt(this.lesdivs.retIm, 'img', { src: retourImg })
          this.lesdivs.retIm2.classList.add('scratchReset')
          this.lesdivs.retIm2.style.cursor = 'pointer'
          this.lesdivs.retIm.style.cursor = 'pointer'
          const clickListener = this.scratchRAZ.bind(this)
          this.lesdivs.retIm2.id = 'scratchReset'
          this.lesdivs.retIm2.addEventListener('click', clickListener, false)
        }
      }
      this.params.scratchProgDonnees.nbdeblocs = leCompte.cmpt
    }

    for (let i = 0; i < this.params.blokmenulimite.length; i++) {
      let oktype = false
      for (let j = 0; j < leCompte.tabtype.length; j++) {
        if (leCompte.tabtype[j].type === this.params.blokmenulimite[i].type) {
          oktype = true
          if (leCompte.tabtype[j].nb >= this.params.blokmenulimite[i].nb) {
            this.desactiveDuMenu(leCompte.tabtype[j].type)
          } else {
            this.activeDuMenu(leCompte.tabtype[j].type)
          }
        }
      }
      if (!oktype) {
        this.activeDuMenu(this.params.blokmenulimite[i].type)
      }
    }

    return leCompte.cmpt
  } // decode

  caABouge (tab1, tab2) {
    for (let i = 0; i < tab1.length; i++) {
      if (tab1[i] === '') return false
      if (tab1[i].id !== tab2[i].id) return true
    }
    return false
  }

  fautRaz () {
    this.params.workspaceEl.clear()
    this.Blockly.Xml.domToWorkspace(this.params.xmlACons, this.params.workspaceEl)
    this.params.acomp = this.scratchRunBlockly(this.params.workspaceEl)
  }

  ligneComp (id, tab) {
    const aret = []
    for (let i = 0; i < tab.length; i++) {
      if (tab[i][0].id === id) {
        for (let j = 0; j < tab[i].length; j++) {
          aret.push(tab[i][j])
        }
        return aret
      }
    }
    return aret
  }

  scratchCompteBloc (workspace) {
    const mmm = Object.values(workspace.blockDB_)
    let cmpt = 0
    const tabtype = []
    let insertion = false
    for (const mm of mmm) {
      insertion = insertion || mm.isInsertionMarker_
      switch (mm.type) {
        case 'Drapo':
        case 'FLECHEG':
        case 'FLECHED':
        case 'FLECHEA':
        case 'FLECHEH':
        case 'FLECHEB':
        case 'EVENTSou':
        case 'operator_add':
        case 'operator_subtract':
        case 'operator_multiply':
        case 'operator_random':
        case 'operator_divide':
        case 'operator_add2':
        case 'operator_subtract2':
        case 'operator_multiply2':
        case 'operator_divide2':
        case 'dire':
        case 'data_setvariableto':
        case 'data_variable':
        case 'data_changevariableby':
        case 'data_showvariable':
        case 'data_hidevariable':
        case 'motion_movesteps':
        case 'motion_turnright':
        case 'motion_turnleft':
        case 'levestylo':
        case 'poserstylo':
        case 'metcouleur':
        case 'Get_us':
        case 'control_repeat':
        case 'control_repeat_until':
        case 'avance_fusee':
        case 'droite_fusee':
        case 'gauche_fusee':
        case 'atteri_fusee':
        case 'decolle_fusee':
        case 'monter_bras':
        case 'descendre_bras':
        case 'tourner_bras':
        case 'agrandir_bras':
        case 'reduire_bras':
        case 'prendre_bras':
        case 'deposer_bras':
        case 'reponse':
        case 'control_if':
        case 'control_if_else':
        case 'pi':
        case 'operator_fusee_planet':
        case 'operator_fusee_cosmo':
        case 'recuperer_fusee':
        case 'Get_pomme':
        case 'Giv_pomme':
        case 'vaCharlie':
        case 'vaLlyn':
        case 'perso':
        case 'MAkePerso':
        case 'pointV':
        case 'pointB':
        case 'varz':
        case 'vary':
        case 'varx':
        case 'mtgEffacer':
        case 'arccentremoins':
        case 'arccentreplus':
        case 'arc1PointPasse':
        case 'angleplus':
        case 'anglemoins':
        case 'cercleCentreRayon':
        case 'cercleCentrePoint':
        case 'demidroitept':
        case 'droiteperp':
        case 'droitepara':
        case 'droitept':
        case 'segmentlongA':
        case 'segmentAB':
        case 'pointMilieu':
        case 'pointSur':
        case 'pointIntersection':
        case 'pointLibreAvecNom':
        case 'NomAleatoire':
        case 'vaFelix':
        case 'motion_pointindirection':
        case 'avancer':
        case 'poser_stylo':
        case 'lever_stylo':
        case 'tourne_gauche':
        case 'tourne_droite':
        case 'aller_a':
        case 'ajouter_x':
        case 'ajouter_y':
        case 'direction':
        case 'abscisse_x':
        case 'ordonnee_y':
        case 'mettre_x':
        case 'mettre_y':
        case 'caseAvancer':
        case 'caseReculer':
        case 'caseTournerDroite':
        case 'caseTournerGauche':
        case 'caseIsJaune':
        case 'caseIsBleue':
        case 'caseIsRouge':
        case 'caseIsVerte':
        case 'caseIsOrange':
        case 'caseIsRose':
        case 'caseIsViolette':
        case 'caseIsColore':
        case 'caseIsNotColore':
        case 'caseDevantNoire':
        case 'caseColoreJaune':
        case 'caseColoreBleue':
        case 'caseColoreRouge':
        case 'caseColoreVerte':
        case 'caseColoreOrange':
        case 'operator_divide3':
        case 'operator_subtract3':
        case 'caseColoreRose':
        case 'caseColoreViolette':
        case 'operator_reste':
        case 'operator_quotient':
        case 'FLECHEESP':
        case 'pointLong':
        case 'operator_equals':
        case 'operator_gt':
        case 'operator_lt':
        case 'dire2':
        case 'dire3':
        case 'mtgPointille':
          cmpt++
          break
        case 'text':
        case 'fauxx':
        case 'math_number':
          break
        default: {
          const typeMod = mm.type.replace('b', '')
          if (this.params.lesBlocPerso.includes(typeMod)) {
            cmpt++
          } else {
            console.error('bloc non reconnu ' + mm.type + ' pour comptage')
          }
        }
      }
      for (const itemType of tabtype) {
        let oktype = false
        if (itemType.type === mm.type) {
          itemType.nb++
          oktype = true
        }
        if (!oktype) tabtype.push({ type: mm.type, nb: 1 })
      }
    }
    return { cmpt, tabtype, insertion }
  }

  scratchRAZ () {
    this.loadSampletotal({ squizzVar: this.params.scratchProgDonnees.squizzVar })
    this.params.acomp = this.scratchRunBlockly(this.params.workspaceEl)
  }

  getDisabledDansMenu (qui, tab) {
    qui = this.scratchTraduireOrdre(qui)
    if (tab === undefined) {
      tab = this.params.scratchProgDonnees.menu
    }
    for (let i = 0; i < tab.length; i++) {
      if (tab[i].type === 'catégorie') {
        const ret = this.getDisabledDansMenu(qui, tab[i].contenu)
        if ((ret === true) || (ret === false)) {
          return ret
        }
      }
      if (tab[i].type === qui) {
        return tab[i].disabled
      }
    }
    return undefined
  }

  modif (tab) {
    for (let i = 0; i < tab.length; i++) {
      for (let j = 0; j < tab[i].length; j++) {
        tab[i][j].style.padding = '0px'
      }
    }
  }

  enonce (elem, modprog) {
    if (modprog === 'libre') {
      this.params.dirigeProg = {
        sequence: true, pasApas: false, cacheTest: false
      }
      this.params.modeCorrection = {
        TypeCorrection: 'Sans',
        aVerif: []
      }
    } else {
      this.params.modeCorrection = modprog.modeCorrection
      this.params.dirigeProg = modprog.dirigeProg
    }
    this.params.oldNumBlok = undefined
    this.corActif = 'Rep'
    this.lesdivs = {}
    this.idTop = j3pGetNewId('top')
    this.lesdivs.top = j3pAddElt(this.params.me.zonesElts.MG, 'div', { id: this.idTop })
    this.lesdivs.conteneur = j3pAddElt(this.params.me.zonesElts.MG, 'div', {
      style: this.params.me.styles.etendre('toutpetit.enonce', { padding: '10px' })
    })
    this.params.svg = undefined

    let tabEvent
    if (this.isMtg) {
      const tt = addDefaultTable(this.lesdivs.conteneur, 3, 1)
      tt[0][0].parentNode.parentNode.style.borderCollapse = 'separate'
      tt[0][0].style.padding = 0
      tt[1][0].style.padding = 0
      tt[2][0].style.padding = 0
      this.lesdivs.enonce = tt[0][0]
      this.lesdivs.explications = tt[1][0]
      this.lesdivs.explications.style.color = legacyStyles.cfaux

      const yy2 = addDefaultTable(tt[2][0], 5, 2)
      this.lesdivs.BoutonsSuite = yy2[2][1]
      yy2[2][1].style.verticalAlign = 'top'
      const yuioi = addDefaultTable(yy2[0][0], 1, 3)
      this.tabPourCoADiv = yy2[0][0]
      this.lesdivs.compteurX = yuioi[0][0]
      this.lesdivs.sortie1 = yuioi[0][2]
      yuioi[0][1].style.width = '100%'

      this.lesdivs.divBlocPerso = yuioi[0][1]
      yuioi[0][1].style.textAlign = 'center'

      this.lesdivs.contBlockly = yy2[2][0]
      this.lesdivs.contBlockly.style.padding = 0
      this.lesdivs.contBlockly.style.verticalAlign = 'Top'
      this.lesdivs.sortie1.style.textAlign = 'left'
      this.lesdivs.sortie1.style.height = '30px'
      this.lesdivs.sortie1.style.width = '200px'
      this.lesdivs.sortie1.style.backgroundImage = barreboutonImg
      this.lesdivs.sortie1.style.backgroundRepeat = 'repeat x'
      this.lesdivs.sortie1.style.backgroundWidth = '100%'

      this.lesdivs.compteurX.style.textAlign = 'right'
      this.lesdivs.compteurX.style.height = '30px'
      this.lesdivs.compteurX.style.backgroundImage = barreboutonImg
      this.lesdivs.compteurX.style.backgroundRepeat = 'repeat x'
      this.lesdivs.compteurX.style.backgroundWidth = '100%'

      yuioi[0][1].style.backgroundImage = barreboutonImg
      yuioi[0][1].style.backgroundRepeat = 'repeat x'
      yuioi[0][1].style.backgroundWidth = '100%'

      if (modprog === 'libre') {
        this.params.MtgSortie = yy2[3][0]
        this.params.MtgSortie.style.border = '4px solid black'
        this.params.pourBuf = yuioi[0][1]
      }
      const ff = addDefaultTable(this.params.MtgSortie, 2, 1)
      this.lesdivs.sortieEl = ff[0][0]
      this.lesdivs.sortie3 = ff[1][0]
      ff[0][0].style.padding = '0px'
      ff[1][0].style.padding = '0px'

      this.lesdivs.compteurX.style.borderLeft = '4px solid grey'
      this.lesdivs.compteurX.style.borderTop = '4px solid grey'
      this.lesdivs.compteurX.style.borderBottom = '4px solid grey'
      this.lesdivs.sortie1.style.border = '4px solid grey'
      yuioi[0][1].style.borderTop = '4px solid grey'
      yuioi[0][1].style.borderBottom = '4px solid grey'
      this.lesdivs.contBlockly.style.border = '4px solid black'
      this.lesdivs.contBlockly.style.verticalAlign = 'Top'
      this.lesdivs.sortieEl.style.background = '#ffffff'
      this.lesdivs.sortieEl.style.padding = 0
      this.lesdivs.sortieEl.style.height = '300px'

      const ml = addDefaultTable(this.lesdivs.compteurX, 1, 5)
      ml[0][0].style.padding = 0
      ml[0][1].style.padding = 0
      ml[0][2].style.padding = 0
      ml[0][3].style.padding = 0
      ml[0][4].style.padding = 0
      this.lesdivs.PASAPAS = ml[0][0]
      this.lesdivs.PASAPASBOOT = ml[0][1]
      ml[0][2].style.width = '100%'
      this.lesdivs.Compteur = ml[0][3]
      this.lesdivs.retIm = ml[0][4]
      this.lesdivs.divBlockly = j3pAddElt(this.lesdivs.contBlockly, 'div')
      this.lesdivs.output = this.lesdivs.sortieEl
      this.lesdivs.explications.style.color = legacyStyles.cfaux
      if (modprog === 'libre') {
        j3pDetruit(this.lesdivs.explications)
        j3pDetruit(this.lesdivs.correction)
        j3pDetruit(this.lesdivs.enonce)
        this.params.squizzEssai = true
        this.params.MtgSortieD = yy2[4][0]
        this.params.MtgSortieD.show2 = () => {
          window.scrollTo(0, 9000)
        }
      }
      this.tabPourCoADiv = yuioi[0][0]
    } else {
      const tt = addDefaultTable(this.lesdivs.conteneur, 3, 1)
      tt[0][0].parentNode.parentNode.style.borderCollapse = 'separate'
      tt[0][0].style.padding = 0
      tt[1][0].style.padding = 0
      tt[2][0].style.padding = 0
      this.lesdivs.enonce = tt[0][0]
      this.lesdivs.explications = tt[1][0]
      this.lesdivs.explications.style.color = legacyStyles.cfaux

      const yy2 = addDefaultTable(tt[2][0], 4, 4)
      this.lesdivs.BoutonsSuite = yy2[2][3]
      this.lesdivs.BoutonsSuite.style.verticalAlign = 'top'
      this.lesdivs.compteurX = yy2[0][0]
      this.tabPourCoADiv = yy2[0][0]
      this.lesdivs.sortie1 = yy2[0][2]
      this.lesdivs.divBlocPerso = yy2[0][1]
      this.lesdivs.divBlocPerso.style.textAlign = 'center'
      this.lesdivs.contBlockly = yy2[2][0]
      this.lesdivs.contBlockly.style.padding = 0
      this.lesdivs.contBlockly.style.verticalAlign = 'Top'
      this.lesdivs.sortie1.style.textAlign = 'right'
      this.lesdivs.sortie1.style.height = '30px'
      this.lesdivs.sortie1.style.width = '306px'
      this.lesdivs.sortie1.style.backgroundImage = barreboutonImg
      this.lesdivs.sortie1.style.backgroundRepeat = 'repeat x'
      this.lesdivs.sortie1.style.backgroundWidth = '100%'

      this.lesdivs.compteurX.style.textAlign = 'right'
      this.lesdivs.compteurX.style.height = '30px'
      this.lesdivs.compteurX.style.backgroundImage = barreboutonImg
      this.lesdivs.compteurX.style.backgroundRepeat = 'repeat x'
      this.lesdivs.compteurX.style.backgroundWidth = '100%'

      yy2[0][1].style.backgroundImage = barreboutonImg
      yy2[0][1].style.backgroundRepeat = 'repeat x'
      yy2[0][1].style.backgroundWidth = '100%'

      const ff = addDefaultTable(yy2[2][2], 5, 1)
      this.lesdivs.sortieEl = ff[0][0]
      this.lesdivs.sortieEl.style.minWidth = '300px'
      this.lesdivs.sortie3 = ff[1][0]
      ff[0][0].style.padding = 0
      ff[1][0].style.padding = 0
      this.lesdivs.sortie4 = ff[2][0]
      this.lesdivs.sortie4.style.display = (this.params.yaFauxClavier) ? '' : 'none'
      if (this.params.yaFauxClavier) {
        // dessine un clavier
        tabEvent = addDefaultTable(this.lesdivs.sortie4, 3, 1)
        tabEvent[0][0].style.textAlign = 'center'
        tabEvent[1][0].style.padding = '10px'
        j3pAddContent(tabEvent[0][0], '<u>Evénements</u>')
        tabEvent[2][0].style.border = '1px solid black'
        tabEvent[2][0].style.borderRadius = '5px'
        tabEvent[2][0].style.background = '#79d3c4'
        tabEvent[2][0].style.width = '300px'
        const divClav = j3pAddElt(tabEvent[2][0], 'div')
        divClav.style.padding = '10px'
        divClav.style.display = 'grid'
        divClav.style.gridTemplateRows = 'auto'
        divClav.style.gridTemplateColumns = 'auto auto auto auto auto auto auto auto auto auto auto auto auto'
        const listClav = ['◄', '►', '▲', '▼', 'ª', '▂']
        for (let i = 0; i < 6; i++) {
          const td1 = j3pAddElt(divClav, 'div')
          td1.style.gridColumn = String(i * 2 + 2)
          td1.style.gridRow = '1'
          td1.style.display = 'flex'
          td1.style.alignItems = 'center'
          td1.style.justifyContent = 'center'
          td1.style.textAlign = 'center'
          td1.style.border = '1px solid black'
          td1.style.borderRadius = '3px'
          j3pAddContent(td1, (listClav[i] === 'ª') ? 'a' : listClav[i])
          td1.classList.add('cKlav')
          td1.addEventListener('click', () => { this.lanceEvent(listClav[i]) })
        }
        this.lesdivs.conteneurLanceur = this.lesdivs.sortie4
      }

      this.lesdivs.compteurX.style.borderLeft = '4px solid grey'
      this.lesdivs.compteurX.style.borderTop = '4px solid grey'
      this.lesdivs.compteurX.style.borderBottom = '4px solid grey'
      this.lesdivs.sortie1.style.border = '4px solid grey'
      yy2[2][2].style.border = '4px solid black'
      yy2[2][2].style.verticalAlign = 'top'
      yy2[0][1].style.borderTop = '4px solid grey'
      yy2[0][1].style.borderBottom = '4px solid grey'
      this.lesdivs.leAvireentre = yy2[0][1]
      yy2[1][1].style.width = '100%'
      this.lesdivs.contBlockly.style.border = '4px solid black'
      this.lesdivs.contBlockly.style.verticalAlign = 'Top'
      this.lesdivs.sortieEl.style.borderBottom = '4px solid black'
      this.lesdivs.sortieEl.style.background = '#ffffff'
      this.lesdivs.sortieEl.style.padding = 0
      this.lesdivs.sortieEl.style.height = '300px'
      this.lesdivs.sortieMoha = ff[3][0]
      this.lesdivs.sortieMohasVG = ff[4][0]

      const ml = addDefaultTable(this.lesdivs.compteurX, 1, 5)
      ml[0][0].style.padding = 0
      ml[0][1].style.padding = 0
      ml[0][2].style.padding = 0
      ml[0][3].style.padding = 0
      ml[0][4].style.padding = 0
      this.lesdivs.PASAPAS = ml[0][0]
      this.lesdivs.PASAPASBOOT = ml[0][1]
      ml[0][2].style.width = '100%'
      this.lesdivs.Compteur = ml[0][3]
      this.lesdivs.retIm = ml[0][4]
      this.lesdivs.divBlockly = j3pAddElt(this.lesdivs.contBlockly, 'div')
      this.lesdivs.output = this.lesdivs.sortieEl
      this.lesdivs.explications.style.color = legacyStyles.cfaux
    }

    const untatab = addDefaultTable(this.lesdivs.sortie1, 1, 4)
    untatab[0][0].style.margin = '0px'
    untatab[0][0].style.padding = '0px'
    untatab[0][1].style.margin = '0px'
    untatab[0][1].style.padding = '0px'
    untatab[0][2].style.margin = '0px'
    untatab[0][2].style.padding = '0px'
    untatab[0][3].style.margin = '0px'
    untatab[0][3].style.padding = '0px'
    let sp
    let Rinit = j3pAjouteBouton(untatab[0][2], this.scratchReinit.bind(this), { value: 'Réinitialiser ' })
    if (this.params.yaFauxClavier) {
      sp = j3pAddElt(untatab[0][0], 'span')
      this.lesdivs.drapeauok = j3pAddElt(sp, 'img', { src: drapeauokImg })
      const sp2 = j3pAddElt(tabEvent[1][0], 'span')
      this.lesdivs.drapeau = j3pAddElt(sp2, 'img', drapeauOpts)
    } else {
      sp = j3pAddElt(untatab[0][0], 'span')
      this.lesdivs.drapeauok = j3pAddElt(sp, 'img', { src: drapeauokImg })
      const sp2 = j3pAddElt(untatab[0][1], 'span')
      this.lesdivs.drapeau = j3pAddElt(sp2, 'img', drapeauOpts)
      this.lesdivs.conteneurLanceur = untatab[0][1]
    }
    const sp3 = j3pAddElt(untatab[0][3], 'span')
    this.lesdivs.stop = j3pAddElt(sp3, 'img', { src: stopImg })
    this.lesdivs.stop.style.display = 'none'
    if (modprog === 'libre') {
      this.lesdivs.conteneur.style.background = '#aaa'
      this.lesdivs.sortie1.style.background = '#fff'
      this.lesdivs.compteurX.style.background = '#fff'
      this.lesdivs.divBlocPerso.style.background = '#fff'
      j3pEmpty(this.lesdivs.sortie1)
      const spp = addDefaultTable(this.lesdivs.sortie1, 1, 2)
      spp[0][0].style.padding = '0px'
      spp[0][1].style.padding = '0px'
      spp[0][0].style.margin = '0px'
      spp[0][1].style.margin = '0px'
      const sp12 = addDefaultTable(spp[0][0], 1, 5)
      for (let i = 0; i < 5; i++) {
        // j3pAddContent(sp12[0][i], '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
        sp12[0][i].style.minWidth = '30px'
        sp12[0][i].style.minHeight = '30px'
      }
      const sp2 = j3pAddElt(sp12[0][0], 'span')
      this.lesdivs.drapeau = j3pAddElt(sp2, 'img', drapeauOpts)
      this.lesdivs.drapeauvite = j3pAddElt(sp12[0][1], 'img', { src: drapeauImgVite })
      this.lesdivs.drapeauvite.addEventListener('click', () => { this.lanceEvent('drapeauVite') }, false)
      j3pStyle(this.lesdivs.drapeauvite, { cursor: 'pointer' })
      const sp4 = j3pAddElt(sp12[0][3], 'span')
      this.lesdivs.export = j3pAddElt(sp4, 'img', { src: enregistre })
      const sp5 = j3pAddElt(sp12[0][4], 'span')
      this.lesdivs.import = j3pAddElt(sp5, 'img', { src: importer })
      this.lesdivs.import.addEventListener('click', this.addImporter.bind(this), false)
      j3pStyle(this.lesdivs.import, { cursor: 'pointer' })
      this.lesdivs.export.addEventListener('click', this.addExporter.bind(this), false)
      j3pStyle(this.lesdivs.export, { cursor: 'pointer' })
      sp.style.display = 'none'
      this.lesdivs.conteneurLanceur = spp[0][0]
      Rinit = j3pAjouteBouton(spp[0][1], this.scratchReinit.bind(this), { value: 'Réinitialiser ' })
    }
    this.lesdivs.drapeauok.classList.add('ScratchDrapeauOk')
    this.lesdivs.drapeau.classList.add('ScratchDrapeau')
    this.lesdivs.stop.classList.add('ScratchStop')
    this.lesdivs.sortieEl.classList.add('ScratchSortie')
    this.lesdivs.contBlockly.classList.add('ScratchBlockly')
    this.lesdivs.compteurX.classList.add('ScratchBandeau')
    this.lesdivs.contBlockly.style.verticalAlign = 'Top'

    this.lesdivs.sortie1.style.minWidth = '200px'
    if (this.params.NbEssaiIllimite || this.params.yaFauxClavier) {
      this.lesdivs.drapeauok.style.cursor = 'pointer'
    } else {
      this.lesdivs.drapeauok.style.display = 'none'
    }
    document.addEventListener('mouseup', this._decodeListener, false)
    document.addEventListener('keyup', this._decodeListener, false)
    this.lesdivs.drapeauok.addEventListener('click', () => { this.lanceEvent('drapeauOK') }, false)
    j3pStyle(this.lesdivs.drapeauok, { cursor: 'pointer' })
    this.lesdivs.drapeau.addEventListener('click', () => { this.lanceEvent('drapeau') }, false)
    j3pStyle(this.lesdivs.drapeau, { cursor: 'pointer' })
    this.lesdivs.stop.addEventListener('click', this.stopTout.bind(this), false)
    j3pStyle(this.lesdivs.stop, { cursor: 'pointer' })

    if (this.params.ProposePasaPas) {
      this.pasapas = j3pAddElt(this.lesdivs.PASAPAS, 'input', { type: 'checkbox' })
      j3pAffiche(this.lesdivs.PASAPAS, null, 'Pas à pas&nbsp;')
    }
    this.lesdivs.PASAPASBOOT.style.display = 'none'
    this.bouAaPaPa = j3pAjouteBouton(this.lesdivs.PASAPASBOOT, this.boutonPasApas.bind(this), { value: 'Bloc suivant' })

    this.params.scratchProgDonnees.mypre = this.lesdivs.output
    this.params.scratchProgDonnees.run = false
    this.params.scratchProgDonnees.runable = true
    this.params.scratchProgDonnees.fostop = false
    this.params.chrono = []
    this.params.scratchProgDonnees.progencours = []
    this.params.scratchProgDonnees.lesidprog = []
    this.params.scratchProgDonnees.progcolo = false
    this.params.scratchProgDonnees.lesflag = []
    this.params.scratchProgDonnees.reponse = 0
    this.params.scratchProgDonnees.variables = []
    this.params.nbEssais = this.params.nbEssaisbase
    this.params.scratchProgDonnees.Progused = []

    const iop = addDefaultTable(this.lesdivs.enonce, 1, 3)
    iop[0][1].style.width = '100%'
    this.lesdivs.enonceG = iop[0][0]
    this.lesdivs.enonceG.style.whiteSpace = 'normal'
    this.lesdivs.enonce.classList.add('enonce')
    this.lesdivs.enonce.style.border = '4px blue ridge'
    this.lesdivs.enonce.style.borderRadius = '10px'
    this.lesdivs.enonce.style.padding = '0px 5px 0px 5px'
    iop[0][2].style.borderLeft = '1px solid blue'
    iop[0][2].style.fontSize = '90%'
    iop[0][2].style.padding = '3px'
    this.loadMenustotal(modprog.pourInject)

    if (!this.params.NbEssaiIllimite && !this.params.yaFauxClavier) {
      j3pDetruit(iop[0][2])
      this.lesdivs.enonceG.style.width = '100%'
    } else {
      let consinfo = 'Clique sur <img tabindex="0" src="' + drapeauImg + '" /> pour tester. <BR> Clique sur  <img tabindex="0" src="' + drapeauokImg + '" /> pour valider.'
      if (this.params.yaFauxClavier) consinfo = 'Déclenche des événements ( en bas à droite ) pour tester. <BR> Clique sur  <img tabindex="0" src="' + drapeauokImg + '" /> pour valider.'
      this.lllla = new BulleAide(iop[0][2], consinfo, { gg: true })
    }
    if (this.isMtg) {
      const tabenoc = addDefaultTable(this.lesdivs.enonceG, 2, 1)
      j3pAffiche(tabenoc[0][0], null, '<b>Ecris un programme de construction</b> pour obtenir la figure modèle à partir de la figure de départ.')
      this.modif(tabenoc)

      const tabenoc2 = addDefaultTable(tabenoc[0][0], 2, 7)
      this.modif(tabenoc2)

      j3pAffiche(tabenoc2[0][0], null, '<i>(Clique sur le bouton <b>modèle à reproduire</b> pour voir la figure attendue)</i>')
      this.params.BouModel = tabenoc2[0][2]
      tabenoc2[0][1].style.width = '80px'

      j3pAffiche(tabenoc2[1][0], null, '<i>(Clique sur le bouton <b>figure de départ</b> pour voir la figure de départ)</i>')
      this.params.BouModelBase = tabenoc2[1][2]
      tabenoc2[1][4].style.width = '80px'
      tabenoc2[1][6].style.width = '80px'
      this.params.TextModel = this.params.me.donneesSection.textModel
      this.params.TextModelBase = this.params.me.donneesSection.textBase
      this.params.BouMaFig = tabenoc2[1][5]

      if (this.params.MtgProg.FoEfCo) {
        const tabenoc7 = addDefaultTable(this.lesdivs.enonceG, 1, 1)
        j3pAffiche(tabenoc7[0][0], null, '<i>Dans cet exercice, il <b>faut effacer</b> tous les <u>traits de construction</u>.</i>')
        tabenoc7[0][0].style.color = '#f30dbc'
      }
    }

    this.cacheDrapeaux = () => {
      if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = 'none'
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = 'none'
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = 'none'
      if (this.lesdivs.retIm2) this.lesdivs.retIm2.style.display = 'none'
    }
    Rinit.style.display = 'none'
    this.montreBoutonReinit = () => {
      Rinit.style.display = ''
    }
    this.montreDrapeaux = () => {
      if (this.params.NbEssaiIllimite || this.params.yaFauxClavier) {
        if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = ''
      }
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = ''
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = ''
      if (this.lesdivs.retIm2) this.lesdivs.retIm2.style.display = ''
    }
    this.cacheBoutonReinit = () => {
      Rinit.style.display = 'none'
    }

    this.scratchReinitBase(true)
    this.params.chronodecode = setInterval(this._decodeListener, 1000)
    this.params.me.zonesElts.MG.classList.add('fond')
    this.loadSample2total(elem)
    this.params.acomp = this.scratchRunBlockly(this.params.workspaceEl)

    // this.fautReinit = false
  }

  addImporter () {
    this.avireYY = j3pModale({ titre: 'Import' })
    const uu = addDefaultTable(this.avireYY, 3, 1)
    const tt = addDefaultTable(uu[0][0], 1, 3)
    j3pAjouteBouton(tt[0][0], this.importer.bind(this), { value: 'Importer' })
    this.params.inputImporter = j3pAddElt(tt[0][2], 'input', { size: 16 })
    j3pAddContent(tt[0][1], '&nbsp;')
    j3pAddContent(uu[1][0], '&nbsp;')
    uu[2][0].style.textAlign = 'center'
    j3pAjouteBouton(uu[2][0], () => {
      j3pDetruit(this.avireYY)
      j3pDetruit('j3pmasque')
    }, { value: 'Quitter' })
    this.params.inputImporter.focus()
  }

  importer () {
    try {
      const tt = JSON.parse(this.params.inputImporter.value)
      if (!tt.Prog && !tt.blocs) {
        this.params.inputImporter.value = 'JSON Invalide'
        return
      } else {
        if (tt.blocs) {
          this.params.BlocsAAj = j3pClone(tt.blocs)
          this.refaisMenu()
        }
        if (tt.Prog) {
          const xmlDoc = $.parseXML(tt.Prog)
          let x = xmlDoc.firstChild
          while (x.nodeType !== 1) {
            x = x.nextSibling
          }
          this.params.scratchProgDonnees.workspace.clear()
          this.Blockly.Xml.domToWorkspace(x, this.params.scratchProgDonnees.workspace)
        }
        this.params.inputImporter.value = 'import OK'
      }
      j3pDetruit(this.avireYY)
      j3pDetruit('j3pmasque')
    } catch (error) {
      this.params.inputImporter.value = 'JSON Invalide'
    }
  }

  addExporter () {
    const yy = j3pModale({ titre: 'Export' })
    const uu = addDefaultTable(yy, 7, 1)

    yy.style.top = '20px'
    yy.style.left = '20px'
    const tt = addDefaultTable(uu[0][0], 1, 3)
    j3pAjouteBouton(tt[0][0], this.exporterBlocs.bind(this), { value: 'Exporter blocs' })
    this.params.inputExportBlocsaa = j3pAddElt(tt[0][2], 'input', { size: 16 })
    j3pAddContent(tt[0][1], '&nbsp;')
    j3pAddContent(uu[1][0], '&nbsp;')

    const tt2 = addDefaultTable(uu[2][0], 1, 3)
    j3pAjouteBouton(tt2[0][0], this.exporterProg.bind(this), { value: 'Exporter programme' })
    this.params.inputExportProgsaa = j3pAddElt(tt2[0][2], 'input', { size: 16 })
    j3pAddContent(tt2[0][1], '&nbsp;')
    j3pAddContent(uu[3][0], '&nbsp;')

    const tt3 = addDefaultTable(uu[4][0], 1, 3)
    j3pAjouteBouton(tt3[0][0], this.exporterTout.bind(this), { value: 'Exporter tout' })
    this.params.inputExportToutsaa = j3pAddElt(tt3[0][2], 'input', { size: 16 })
    j3pAddContent(tt3[0][1], '&nbsp;')
    j3pAddContent(uu[5][0], '&nbsp;')

    uu[6][0].style.textAlign = 'center'
    j3pAjouteBouton(uu[6][0], () => {
      j3pDetruit(yy)
      j3pDetruit('j3pmasque')
    }, { value: 'Quitter' })
  }

  exporterBlocs () {
    this.params.inputExportBlocsaa.value = JSON.stringify({ blocs: this.params.listeDef })
  }

  exporterProg () {
    const lxml = this.Blockly.Xml.workspaceToDom(this.params.scratchProgDonnees.workspace)
    this.params.inputExportProgsaa.value = JSON.stringify({ Prog: this.Blockly.Xml.domToText(lxml) })
  }

  exporterTout () {
    const lxml = this.Blockly.Xml.workspaceToDom(this.params.scratchProgDonnees.workspace)
    this.params.inputExportToutsaa.value = JSON.stringify({
      Prog: this.Blockly.Xml.domToText(lxml),
      blocs: this.params.listeDef
    })
  }

  initFusee (ncase, x, y, h, a, elements) {
    let img, lx, ly
    this.params.scratchProgDonnees.Lutins[0].img = fuseeImg
    this.params.scratchProgDonnees.Lutins[0].larg = 300 / ncase
    this.params.scratchProgDonnees.Lutins[0].haut = 300 / ncase
    this.params.scratchProgDonnees.Lutins[0].cox = x
    this.params.scratchProgDonnees.Lutins[0].coxbase = x
    this.params.scratchProgDonnees.Lutins[0].coybase = y
    this.params.scratchProgDonnees.Lutins[0].coy = y
    this.params.scratchProgDonnees.Lutins[0].angle = a
    this.params.scratchProgDonnees.Lutins[0].anglebase = a
    this.params.scratchProgDonnees.Lutins[0].hauteurbase = h
    this.params.scratchProgDonnees.Lutins[0].hauteur = h
    this.params.scratchProgDonnees.nbcasefusee = ncase
    this.params.scratchProgDonnees.cartefusee = []
    for (let i = 0; i < ncase; i++) {
      this.params.scratchProgDonnees.cartefusee[i] = []
      for (let j = 0; j < ncase; j++) {
        this.params.scratchProgDonnees.cartefusee[i][j] = 0
      }
    }
    this.params.figureAfaire = []
    this.params.figureAfaire.push({
      type: 'image',
      x: 0,
      y: -150,
      width: 450,
      height: 600,
      url: fondetoileImg
    })
    for (let i = 1; i < ncase; i++) {
      this.params.figureAfaire.push({ type: 'line', x1: 0, y1: 300 / ncase * i, x2: 300, y2: 300 / ncase * i })
      this.params.figureAfaire.push({ type: 'line', x1: 300 / ncase * i, y1: 0, x2: 300 / ncase * i, y2: 300 })
    }
    for (let i = 0; i < elements.length; i++) {
      switch (elements[i].quoi) {
        case 'terre':
          this.params.scratchProgDonnees.cartefusee[elements[i].x][elements[i].y] = 1
          img = terreImg
          break
        case 'trou':
          this.params.scratchProgDonnees.cartefusee[elements[i].x][elements[i].y] = 2
          img = blackholeImg
          break
        case 'cosmo':
          this.params.scratchProgDonnees.cartefusee[elements[i].x][elements[i].y] = 4
          img = cosmoImg
          break
        case 'mars':
          this.params.scratchProgDonnees.cartefusee[elements[i].x][elements[i].y] = 3
          img = planetrougeImg
          break
        case 'radar': {
          const listel = ['', 'terre', 'trou', 'mars', 'cosmo']
          this.params.scratchProgDonnees.cartefusee[elements[i].x][elements[i].y] = listel.indexOf(elements[i].cache)
          img = radarImg
        }
      }
      lx = elements[i].x * this.params.scratchProgDonnees.Lutins[0].larg
      ly = elements[i].y * this.params.scratchProgDonnees.Lutins[0].larg
      this.params.figureAfaire.push({
        type: 'image',
        x: lx,
        y: ly,
        width: this.params.scratchProgDonnees.Lutins[0].larg,
        height: this.params.scratchProgDonnees.Lutins[0].larg,
        url: img,
        id: elements[i].x + elements[i].quoi + elements[i].y,
        fx: elements[i].x,
        fy: elements[i].y
      })
    }
    this.params.yafusee = true
  } // initFusee

  initBras (hauteur, sens, taille, elements) {
    this.params.scratchProgDonnees.Lutins[0].img = bras2Img
    this.params.scratchProgDonnees.Lutins[0].larg = 60
    this.params.scratchProgDonnees.Lutins[0].haut = 22
    this.params.scratchProgDonnees.Lutins[0].cox = sens
    this.params.scratchProgDonnees.Lutins[0].coxbase = sens
    this.params.scratchProgDonnees.Lutins[0].coybase = hauteur
    this.params.scratchProgDonnees.Lutins[0].coy = hauteur
    this.params.scratchProgDonnees.Lutins[0].angle = taille
    this.params.scratchProgDonnees.Lutins[0].anglebase = taille
    this.params.scratchProgDonnees.Lutins[0].id = 'BRAS'

    this.params.figureAfaire = []
    this.params.figureAfaire.push({
      type: 'image',
      x: 0,
      y: -20,
      width: 320,
      height: 340,
      url: murImg
    })
    /// ajout des  tapis
    this.params.figureAfaire.push({
      type: 'image',
      x: -50,
      y: 200,
      width: 150,
      height: 100,
      url: tapisImg
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: -50,
      y: 100,
      width: 150,
      height: 100,
      url: tapisImg
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: 200,
      y: 200,
      width: 150,
      height: 100,
      url: tapis2Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: 200,
      y: 100,
      width: 150,
      height: 100,
      url: tapis2Img
    })
    // num sur tapis
    this.params.figureAfaire.push({
      type: 'image',
      x: 10,
      y: 253,
      width: 18,
      height: 18,
      url: num1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: 10,
      y: 153,
      width: 18,
      height: 18,
      url: num2Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: 270,
      y: 253,
      width: 18,
      height: 18,
      url: num3Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      x: 270,
      y: 153,
      width: 18,
      height: 18,
      url: num4Img
    })
    /// les objets
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj11',
      x: 22,
      y: 232,
      width: 15,
      height: 15,
      url: obj1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj12',
      x: 22,
      y: 132,
      width: 15,
      height: 18,
      url: obj1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj13',
      x: 263,
      y: 232,
      width: 15,
      height: 15,
      url: obj1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj14',
      x: 263,
      y: 132,
      width: 15,
      height: 15,
      url: obj1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj21',
      x: 22,
      y: 232,
      width: 15,
      height: 15,
      url: obj2Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj22',
      x: 22,
      y: 132,
      width: 15,
      height: 18,
      url: obj2Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj23',
      x: 263,
      y: 232,
      width: 15,
      height: 15,
      url: obj2Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj24',
      x: 263,
      y: 132,
      width: 15,
      height: 15,
      url: obj2Img
    })

    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj1',
      x: 263,
      y: 132,
      width: 15,
      height: 15,
      url: obj1Img
    })
    this.params.figureAfaire.push({
      type: 'image',
      cachee: true,
      id: 'obj2',
      x: 22,
      y: 232,
      width: 15,
      height: 15,
      url: obj2Img
    })

    // rectvertical bouge jamais
    this.params.figureAfaire.push({
      type: 'rectangle',
      x: 100,
      y: 100,
      width: 50,
      height: 1,
      id: 'rec1',
      style: 'fill:black;stroke:black;stroke-width:5;fill-opacity:1;stroke-opacity:1'
    })
    this.params.figureAfaire.push({
      type: 'rectangle',
      x: 150,
      y: 130,
      width: 1,
      height: 250,
      id: 'rec2',
      style: 'fill:black;stroke:black;stroke-width:5;fill-opacity:1;stroke-opacity:1'
    })
    // fourche
    this.params.figureAfaire.push({
      type: 'image',
      id: 'FOURCHE',
      x: 120.5,
      y: 120,
      width: 60,
      height: 58,
      url: fourcheImg
    })

    this.params.scratchProgDonnees.donneesbras = { tapis1: 0, tapis2: 0, tapis3: 0, tapis4: 0, bras: 0 }
    this.params.scratchProgDonnees.donneesbrasbase = { tapis1: 0, tapis2: 0, tapis3: 0, tapis4: 0, bras: 0 }
    for (let i = 0; i < elements.length; i++) {
      switch (elements[i].ou) {
        case 'tapis1':
          this.params.scratchProgDonnees.donneesbras.tapis1 = elements[i].obj
          this.params.scratchProgDonnees.donneesbrasbase.tapis1 = elements[i].obj
          break
        case 'tapis2':
          this.params.scratchProgDonnees.donneesbras.tapis2 = elements[i].obj
          this.params.scratchProgDonnees.donneesbrasbase.tapis2 = elements[i].obj
          break
        case 'tapis3':
          this.params.scratchProgDonnees.donneesbras.tapis3 = elements[i].obj
          this.params.scratchProgDonnees.donneesbrasbase.tapis3 = elements[i].obj
          break
        case 'tapis4':
          this.params.scratchProgDonnees.donneesbras.tapis4 = elements[i].obj
          this.params.scratchProgDonnees.donneesbrasbase.tapis4 = elements[i].obj
          break
        case 'bras':
          this.params.scratchProgDonnees.donneesbras.bras = elements[i].obj
          this.params.scratchProgDonnees.donneesbrasbase.bras = elements[i].obj
          break
      }
    }

    this.params.yabras = true
  }

  /**
   * Initialise la propriété params d’après parcours.donneesSection
   * @param {Parcours} parcours
   * @param {boolean} [isDessin]
   */
  initParams (parcours, isDessin, options = {}) {
    // en édition on a pas d’instance de Parcours
    if (!options.edit) {
      if (!parcours?.donneesSection) return console.error(Error('Il faut passer le parcours courant en paramètre'))
      this.params.NbEssaiIllimite = (Number(parcours.donneesSection.nbchances) <= 0)
      this.params.ProposePasaPas = parcours.donneesSection.PasAPas
      this.params.ModeSequence = parcours.donneesSection.Sequence
      this.params.nbEssaisbase = Number(parcours.donneesSection.nbchances)
      this.params.correctionCachee = parcours.donneesSection.CacheTest
      this.params.me = parcours
      this.params.ds = parcours.donneesSection
    } else {
      this.params.isEditing = true
    }
    this.params.enreG = []
    this.params.yayayayeu = false
    this.params.scratchProgDonnees = {
      pause: false,
      flag: [], // liste des objectifs
      // this.testFlag à surcharger dans la section
      variables: [],
      workspace: {},
      progcolo: false,
      LesVariablesCo: [],
      tabcodeX: [],
      lesvarbonnes: [],
      reponse: 'vide',
      resultatAttenduDire: [],
      Lutins: [{
        coxbase: 90,
        coybase: 100,
        anglebase: 0,
        cox: 100,
        coy: 100,
        img: chatImg,
        angle: 0,
        id: 'chat',
        larg: 120,
        haut: 100,
        hauteur: 0,
        hauteurbase: 0,
        StyloCol: '#000000'
      }],
      progdeb: [],
      menu: [],
      menuLimite: {},
      Valeursdebaseautorisees: [],
      Valeursautorisees: [],
      Valeursutilisees: [],
      said: [],
      lesflag: [],
      valeursaleatoiressorties: [],
      lignetracees: [],
      lesprogs: [],
      Progused: [],
      resetflag: true,
      squizzVar: false,
      alelabase: ['¶', '©', '®', '™', '♠', '♣', '♥', '♦', '↑', '↓', '↔', '¶1', '©1', '®1', '™1', '♠1', '♣1', '♥1', '♦1', '↑1', '↓1', '↔1', '¶2', '©2', '®2', '™2', '♠2', '♣2', '♥2', '♦2', '↑2', '↓2', '↔2', '¶3', '©3', '®3', '™3', '♠3', '♣3', '♥3', '♦3', '↑3', '↓3', '↔3'],
      allllela: ['¶', '©', '®', '™', '♠', '♣', '♥', '♦', '↑', '↓', '↔', '¶1', '©1', '®1', '™1', '♠1', '♣1', '♥1', '♦1', '↑1', '↓1', '↔1', '¶2', '©2', '®2', '™2', '♠2', '♣2', '♥2', '♦2', '↑2', '↓2', '↔2', '¶3', '©3', '®3', '™3', '♠3', '♣3', '♥3', '♦3', '↑3', '↓3', '↔3']
    }
    this.params.valAleoOblige = []
    this.ajZone = () => {
    }
    this.testFlag = () => {
    }
    this.testFlagFin = () => {
    }
    this.scratchEditorMetPasOk = () => {
    }
    this.finishCreaPerso = () => {
    }
    this.cacheDrapeauxDemo = () => {
    }
    this.montreBoutonReinitDemo = () => {
    }
    this.montreDrapeauxDemo = () => {
    }
    this.cahceBoutonReinitDemo = () => {
    }
    this.cacheDrapeaux = () => {

    }
    this.montreBoutonReinit = () => {

    }
    this.montreDrapeaux = () => {

    }
    this.cahceBoutonReinit = () => {

    }
    if (isDessin) {
      this.params.scratchProgDonnees.Lutins[0].img = dessinleveImg
      this.params.scratchProgDonnees.Lutins[0].id = 'main'
      this.params.scratchProgDonnees.Lutins[0].larg = 50
      this.params.scratchProgDonnees.Lutins[0].haut = 50
      this.params.scratchProgDonnees.Lutins[0].StyloCol = 'Black'
      this.params.scratchProgDonnees.Lutins[0].StyloPose = false
      this.params.scratchProgDonnees.Traces = []
      this.params.yamodel = true
      this.testFlagFin = () => {
        // cree deux canvas , dessine dedans puis trans en base 64
        const cnv1 = j3pAddElt(this.params.me.zonesElts.MG, 'div')
        const canvas1 = j3pAddElt(cnv1, 'canvas', { style: { display: 'none', width: '300px', height: '150px' } })
        const cnv2 = j3pAddElt(this.params.me.zonesElts.MG, 'div')
        const canvas2 = j3pAddElt(cnv2, 'canvas', { style: { display: 'none', width: '300px', height: '150px' } })

        const ctx1 = canvas1.getContext('2d')
        const ctx2 = canvas2.getContext('2d')
        let i, obi
        // dans model
        if (this.params.figureAfaire2) {
          const xx = Number(this.params.scratchProgDonnees.variables[0].val)
          if (xx === 0) {
            return { ok: false, mess: 'La variable <i>x</i> ne s\'est pas bien initialisée !' }
          }
          let yy = 0
          if (this.params.scratchProgDonnees.variables.length > 1) {
            yy = Number(this.params.scratchProgDonnees.variables[1].val)
            if (yy === 0) {
              return { ok: false, mess: 'La variable <i>y</i> ne s\'est pas bien initialisée !' }
            }
          }
          let zz = 0
          if (this.params.scratchProgDonnees.variables.length > 2) {
            zz = Number(this.params.scratchProgDonnees.variables[2].val)
            if (zz === 0) {
              return { ok: false, mess: 'La variable <i>z</i> ne s\'est pas bien initialisée !' }
            }
          }
          for (i = 0; i < this.params.figureAfaire2.length; i++) {
            obi = this.params.figureAfaire2[i]
            this.scratchDrawLine(obi.x1(xx, yy, zz), obi.y1(xx, yy, zz), obi.x2(xx, yy, zz), obi.y2(xx, yy, zz), 2, 'black', ctx1)
          }
        } else {
          for (i = 0; i < this.params.figureAfaire.length; i++) {
            switch (this.params.figureAfaire[i].type) {
              case 'line' :
                obi = this.params.figureAfaire[i]
                this.scratchDrawLine(obi.x1, obi.y1, obi.x2, obi.y2, 2, 'black', ctx1)
                break
            }
          }
        }
        for (i = 0; i < this.params.scratchProgDonnees.lignetracees.length; i++) {
          obi = this.params.scratchProgDonnees.lignetracees[i]
          this.scratchDrawLine(obi.x1, obi.y1 - 150, obi.x2, obi.y2 - 150, 2, 'black', ctx2)
        }

        let png1 = canvas1.toDataURL()
        let png2 = canvas2.toDataURL()

        png1 = png1.replace('data:image/png;base64,', '')
        png2 = png2.replace('data:image/png;base64,', '')

        this.params.scratchProgDonnees.Lutins[0].StyloPose = false
        this.params.scratchProgDonnees.Lutins[0].img = dessinleveImg
        if (png1 === png2) {
          return { ok: true, mess: '' }
        }
        return { ok: false, mess: 'La figure obtenue ne correspond pas au modèle !' }
      }
    }
  }

  enonceDessin (specialcons, el) {
    this.params.dirigeProg = el.dirigeProg
    this.params.modeCorrection = el.modeCorrection

    this.corActif = 'Rep'
    this.lesdivs = {}
    this.lesdivs.conteneur = j3pAddElt(this.params.me.zonesElts.MG, 'div', {
      style: this.params.me.styles.etendre('toutpetit.enonce', { padding: '10px' })
    })
    this.params.svg = undefined

    const tt = addDefaultTable(this.lesdivs.conteneur, 3, 1)
    tt[0][0].parentNode.parentNode.style.borderCollapse = 'separate'
    tt[0][0].style.padding = 0
    tt[1][0].style.padding = 0
    tt[2][0].style.padding = 0
    this.lesdivs.enonce = tt[0][0]
    this.lesdivs.explications = tt[1][0]
    this.lesdivs.explications.style.color = legacyStyles.cfaux

    const yy2 = addDefaultTable(tt[2][0], 4, 4)
    this.lesdivs.BoutonsSuite = yy2[2][3]
    this.lesdivs.BoutonsSuite.style.verticalAlign = 'top'
    this.lesdivs.compteurX = yy2[0][0]
    this.tabPourCoADiv = yy2[0][0]
    this.lesdivs.divBlocPerso = yy2[0][1]
    this.lesdivs.sortie1 = yy2[0][2]

    this.lesdivs.contBlockly = yy2[2][0]
    this.lesdivs.contBlockly.style.padding = 0
    this.lesdivs.sortie1.style.textAlign = 'right'
    this.lesdivs.sortie1.style.height = '30px'
    this.lesdivs.sortie1.style.width = '306px'
    this.lesdivs.sortie1.style.backgroundImage = barreboutonImg
    this.lesdivs.sortie1.style.backgroundRepeat = 'repeat x'
    this.lesdivs.sortie1.style.backgroundWidth = '100%'

    this.lesdivs.compteurX.style.textAlign = 'right'
    this.lesdivs.compteurX.style.height = '30px'
    this.lesdivs.compteurX.style.backgroundImage = barreboutonImg
    this.lesdivs.compteurX.style.backgroundRepeat = 'repeat x'
    this.lesdivs.compteurX.style.backgroundWidth = '100%'

    yy2[0][1].style.backgroundImage = barreboutonImg
    yy2[0][1].style.backgroundRepeat = 'repeat x'
    yy2[0][1].style.backgroundWidth = '50px'

    const ff = addDefaultTable(yy2[2][2], 2, 1)
    this.lesdivs.sortieEl = ff[0][0]
    this.lesdivs.sortie3 = ff[1][0]
    ff[0][0].style.padding = 0
    ff[1][0].style.padding = 0

    this.lesdivs.compteurX.style.borderLeft = '4px solid grey'
    this.lesdivs.compteurX.style.borderTop = '4px solid grey'
    this.lesdivs.compteurX.style.borderBottom = '4px solid grey'
    this.lesdivs.sortie1.style.border = '4px solid grey'
    yy2[2][2].style.border = '4px solid black'
    yy2[2][2].style.verticalAlign = 'top'
    yy2[0][1].style.borderTop = '4px solid grey'
    yy2[0][1].style.borderBottom = '4px solid grey'
    this.lesdivs.leAvireentre = yy2[0][1]
    this.lesdivs.contBlockly.style.border = '4px solid black'
    this.lesdivs.contBlockly.style.verticalAlign = 'Top'
    this.lesdivs.sortieEl.style.borderBottom = '4px solid black'
    this.lesdivs.sortieEl.style.background = '#ffffff'
    this.lesdivs.sortieEl.style.padding = 0
    this.lesdivs.sortieEl.style.height = '300px'

    const ml = addDefaultTable(this.lesdivs.compteurX, 1, 5)
    ml[0][0].style.padding = 0
    ml[0][1].style.padding = 0
    ml[0][2].style.padding = 0
    ml[0][3].style.padding = 0
    ml[0][4].style.padding = 0
    this.lesdivs.PASAPAS = ml[0][0]
    this.lesdivs.PASAPASBOOT = ml[0][1]
    ml[0][2].style.width = '100%'
    this.lesdivs.Compteur = ml[0][3]
    this.lesdivs.retIm = ml[0][4]
    this.lesdivs.divBlockly = j3pAddElt(this.lesdivs.contBlockly, 'div')
    this.lesdivs.output = this.lesdivs.sortieEl
    this.params.scratchProgDonnees.mypre = this.lesdivs.output
    this.lesdivs.explications.style.color = legacyStyles.cfaux

    const untatab = addDefaultTable(this.lesdivs.sortie1, 1, 3)
    untatab[0][0].style.margin = '0px'
    untatab[0][0].style.padding = '0px'
    untatab[0][1].style.margin = '0px'
    untatab[0][1].style.padding = '0px'
    const sp = j3pAddElt(untatab[0][0], 'span')
    this.lesdivs.drapeauok = j3pAddElt(sp, 'img', { src: drapeauokImg })
    const sp2 = j3pAddElt(untatab[0][1], 'span')
    this.lesdivs.drapeau = j3pAddElt(sp2, 'img', drapeauOpts)
    const sp3 = j3pAddElt(untatab[0][1], 'span')
    this.lesdivs.stop = j3pAddElt(sp3, 'img', { src: stopImg })
    this.lesdivs.stop.style.display = 'none'

    this.lesdivs.drapeauok.classList.add('ScratchDrapeauOk')
    this.lesdivs.drapeau.classList.add('ScratchDrapeau')
    this.lesdivs.stop.classList.add('ScratchStop')
    this.lesdivs.sortieEl.classList.add('ScratchSortie')
    this.lesdivs.contBlockly.classList.add('ScratchBlockly')
    this.lesdivs.contBlockly.style.verticalAlign = 'Top'

    if (this.params.NbEssaiIllimite) {
      this.lesdivs.drapeauok.style.cursor = 'pointer'
    } else {
      this.lesdivs.drapeauok.style.display = 'none'
    }
    document.addEventListener('mouseup', this._decodeListener, false)
    document.addEventListener('keyup', this._decodeListener, false)
    this.lesdivs.drapeauok.addEventListener('click', () => { this.lanceEvent('drapeauOK') }, false)
    j3pStyle(this.lesdivs.drapeauok, { cursor: 'pointer' })
    this.lesdivs.drapeau.addEventListener('click', () => { this.lanceEvent('drapeau') }, false)
    j3pStyle(this.lesdivs.drapeau, { cursor: 'pointer' })
    this.lesdivs.stop.addEventListener('click', this.stopTout.bind(this), false)
    j3pStyle(this.lesdivs.stop, { cursor: 'pointer' })

    if (this.params.ProposePasaPas) {
      this.pasapas = j3pAddElt(this.lesdivs.PASAPAS, 'input', { type: 'checkbox' })
      j3pAffiche(this.lesdivs.PASAPAS, null, 'Pas à pas')
    }
    this.lesdivs.PASAPASBOOT.style.display = 'none'
    this.bouAaPaPa = j3pAjouteBouton(this.lesdivs.PASAPASBOOT, this.boutonPasApas.bind(this), { value: 'Bloc suivant' })

    this.params.scratchProgDonnees.run = false
    this.params.scratchProgDonnees.runable = true
    this.params.scratchProgDonnees.fostop = false
    this.params.chrono = []
    this.params.scratchProgDonnees.progencours = []
    this.params.scratchProgDonnees.lesidprog = []
    this.params.scratchProgDonnees.progcolo = false
    this.params.scratchProgDonnees.lesflag = []
    this.params.scratchProgDonnees.reponse = 0
    this.params.scratchProgDonnees.variables = []
    this.params.nbEssais = this.params.nbEssaisbase

    const iop = addDefaultTable(this.lesdivs.enonce, 1, 3)
    this.lesdivs.enonceG = iop[0][0]
    this.lesdivs.enonce.classList.add('enonce')
    this.lesdivs.enonce.style.border = '4px blue ridge'
    this.lesdivs.enonce.style.borderRadius = '10px'
    this.lesdivs.enonce.style.padding = '0px 5px 0px 5px'
    iop[0][1].style.width = '100%'
    iop[0][2].style.borderLeft = '1px solid blue'
    iop[0][2].style.padding = '3px'

    if (!this.params.NbEssaiIllimite) {
      j3pDetruit(iop[0][2])
      this.lesdivs.enonceG.style.width = '100%'
    } else {
      const consinfo = 'Clique sur <img tabindex="0" src="' + drapeauImg + '" /> pour tester. <BR> Clique sur  <img tabindex="0" src="' + drapeauokImg + '" /> pour valider.'
      this.lllla = new BulleAide(iop[0][2], consinfo, { gg: true })
    }
    const Rinit = j3pAjouteBouton(untatab[0][2], this.scratchReinit.bind(this), { value: 'Réinitialiser ' })

    this.cacheDrapeaux = () => {
      if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = 'none'
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = 'none'
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = 'none'
      if (this.lesdivs.retIm2) this.lesdivs.retIm2.style.display = 'none'
    }
    Rinit.style.display = 'none'
    this.montreBoutonReinit = () => {
      Rinit.style.display = ''
    }
    this.montreDrapeaux = () => {
      if (this.params.NbEssaiIllimite || this.params.yaFauxClavier) {
        if (this.lesdivs.drapeauok) this.lesdivs.drapeauok.style.display = ''
      }
      if (this.lesdivs.drapeau) this.lesdivs.drapeau.style.display = ''
      if (this.lesdivs.conteneurLanceur) this.lesdivs.conteneurLanceur.style.display = ''
      if (this.lesdivs.retIm2) this.lesdivs.retIm2.style.display = ''
    }
    this.cacheBoutonReinit = () => {
      Rinit.style.display = 'none'
    }

    this.loadMenustotal(el.pourInject)

    this.params.chronodecode = setInterval(this._decodeListener, 1000)
    const consi = specialcons || '<b> Ecris le programme </b> pour que le lutin reproduise la figure du modèle.'
    j3pAffiche(this.lesdivs.enonceG, null, consi)

    this.scratchReinitBase()
    this.loadSample2total()
    this.params.acomp = this.scratchRunBlockly(this.params.workspaceEl)
  }

  lanceEvent (koi) {
    if (!this.params.scratchProgDonnees.runable) return
    this.cacheBoutonReinit()
    this.cacheDrapeauxDemo()
    this.cacheDrapeaux()
    this.cahceBoutonReinitDemo()
    this.params.chrono = { qui: koi, lechrono: setTimeout(() => { this.scratchVerifEvent(koi) }, 50) }
  }

  detListEvt (tab) {
    const aret = []
    for (let i = 0; i < tab.length; i++) {
      if (tab[i].length > 0) {
        if (tab[i][0].type === 'event') {
          const apush = tab[i][0].eventtype
          if (aret.indexOf(apush) === -1) aret.push(apush)
        }
      }
    }
    return aret
  }

  loadMenustotal (optmod) {
    let Controll
    let Scroll
    let StartScale

    if (optmod !== undefined) {
      if (optmod.scroll !== undefined) {
        Scroll = optmod.scroll
      } else {
        Scroll = true
      }
      if (optmod.StartScale !== undefined) {
        StartScale = optmod.StartScale
      } else {
        StartScale = 0.75
      }
      if (optmod.Controll !== undefined) {
        Controll = optmod.Controll
      } else {
        Controll = true
      }
    } else {
      Scroll = Controll = true
      StartScale = 0.75
    }

    // création de l’espace de travail (en particulier du menu)
    const x = this.scratchCreeMenu(this.params.scratchProgDonnees.menu, true)
    const xmlDoc = $.parseXML(x)
    let xml = xmlDoc.firstChild
    while (xml.nodeType !== 1) {
      xml = xml.nextSibling
    }

    const side = 'start'
    this.params.workspaceEl = this.Blockly.inject(this.lesdivs.divBlockly, {
      comments: true,
      disable: false,
      collapse: false,
      media: j3pBaseUrl + 'externals/blockly/media/',
      readOnly: false,
      rtl: null,
      scrollbars: Scroll,
      // toolbox: $(xml).find("#toolbox")[0],
      toolbox: xml,
      maxInstances: this.params.scratchProgDonnees.menuLimite,
      toolboxPosition: side === 'top' || side === 'start' ? 'start' : 'end',
      horizontalLayout: side === 'top' || side === 'bottom',
      sounds: false,
      zoom: {
        controls: Controll,
        wheel: true,
        startScale: StartScale,
        maxScale: 2,
        minScale: 0.25,
        scaleSpeed: 1.1
      },
      colours: {
        fieldShadow: 'rgba(100, 100, 100, 0.3)',
        dragShadowOpacity: 0.6
      }
    })
    this.lesdivs.divBlockly.style.width = (this.isMtg) ? '980px' : '600px'
    this.lesdivs.divBlockly.style.height = (this.isMtg) ? '500px' : '600px'
    this.Blockly.svgResize(this.params.workspaceEl)
    this.params.scratchProgDonnees.workspace = this.params.workspaceEl
    this.scratchSetLocale('fr')
  }

  loadSample2total (elem) {
    if (this.Blockly.getMainWorkspace()) {
      // this.Blockly.updateToolbox();
      this.loadSampletotal(elem)
      this.Blockly.BlockSvg.START_HAT = true
    } else {
      setTimeout(this.loadSample2total.bind(this, elem), 20)
    }
  }

  prepCorrec () {
    clearTimeout(this.params.chronodecode)
    document.removeEventListener('mouseup', this._decodeListener, false)
    document.removeEventListener('keyup', this._decodeListener, false)
    document.removeEventListener('keydown', this._lanceToucheListener, false)
    j3pDetruit(this.lesdivs.drapeauok)
    this.lesdivs.Compteur.innerHTML = ''

    // recupere prog eleve
    const xml = this.Blockly.Xml.workspaceToDom(this.params.workspaceEl)
    const xmlText = this.Blockly.Xml.domToText(xml)
      .replace('movable="false"', '')
      .replace('y="150"', 'y="10"')

    // efface worspace et remets en un en readonly
    const xmlText2 = '<?xml version="1.0"?><xml id="toolbox" style="display: none"></xml>'
    const xmlDoc2 = $.parseXML(xmlText2)
    this.lesdivs.contBlockly.innerHTML = ''
    return { xmlDoc2, xmlText }
  }

  /**
   * fonction à surcharger pour les sections avec initialisation aléatoire
   */
  remiseAzero () {
  }

  said () {
    return this.params.scratchProgDonnees.said
  }

  stopTout () {
    this.params.scratchProgDonnees.fostop = true
    if (this.params.chrono.lechrono) clearTimeout(this.params.chrono.lechrono)
    this.params.chrono = { }
    this.params.scratchProgDonnees.progencours = []
  }

  testFlag () {
    console.error('Il faut surcharger cette fonction dans la section')
  }

  testFlagFin () {
    console.error('Il faut surcharger cette fonction dans la section')
  }

  traceSortieBase (options = {}) {
    this.params.scratchProgDonnees.carteCases = [
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide']
    ]
    if (options.demoMode === true) return this.traceSortieBaseDemo()

    for (const variable of this.params.scratchProgDonnees.variables) variable.val = 0

    j3pEmpty(options.sortie)
    if (this.params.yaFond) {
      const fondfond = j3pAddElt(options.sortie, 'img', '', {
        src: this.params.lefond,
        width: 300,
        height: 300
      })
      fondfond.style.width = '300px'
      fondfond.style.height = '300px'
      fondfond.style.position = 'absolute'
    }
    const id = j3pGetNewId('Sb')
    this.params.svg = j3pCreeSVG(options.sortie, { width: 300, height: 300, viewBox: '0 0 300 300', id })
    this.params.svg.style.position = 'relative'

    j3pEmpty(this.params.svg)

    this.params.figIm = []
    this.params.idIm = []
    if (this.params.yamodel) {
      j3pSvgAppend(this.params.svg, 'rect', { x: 0, y: 0, width: 300, height: 300 }, {
        style: {
          strokeWidth: '0',
          fill: 'rgb(255,255,255)'
        }
      })
      j3pSvgAppend(this.params.svg, 'line', { x1: 0, y1: 150, x2: 300, y2: 150 }, {
        style: {
          stroke: '#000',
          strokeWidth: '1px'
        }
      })
      // DC: C’est quoi ce Comic Sans MS ? C’est prohibé depuis la fin du 20e siècle :-D
      // TB: C’est une prof de français qui m’a dit que c’est une police pratique pour les dyslexiques. J’ai pas vérifié ses sources
      // DC: ok => https://www.extensis.com/fr-fr/blog/quelle-est-la-meilleure-police-pour-les-personnes-atteintes-de-dyslexie
      // et il est d’accord avec https://culturedys.com/quelle-est-la-meilleure-police-pour-les-dyslexiques/
      // pour dire que la police https://www.dafont.com/fr/open-dyslexic.font n’apporte rien de plus qu’une police sans serif classique
      // (idem pour comic sans), le coté monospace améliorerait un peu les choses.
      // @todo inclure une police pour dys dans nos css (car rien ne dit que comic sans soit installé sur la machine cible)
      //  Roboto pourrait être un bon candidat (déjà dans la bibli), sinon une sans serif monospace
      j3pSvgAppend(this.params.svg, 'text', { x: 230, y: 18 }, {
        style: {
          fill: '#aaaaaa',
          stroke: '#aaaaaa',
          fontSize: '20px',
          fontFace: 'Comic Sans MS'
        },
        text: 'Modèle'
      })
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 0,
        y: 150,
        width: 300,
        height: 150,
        style: 'stroke-width:0;fill:rgb(240,240,240)'
      })
    }
    const dejaMarqAxeX = []
    const dejaMarqAxeY = []
    const aMarqAxe = []
    for (const figure of this.params.figureAfaire) {
      switch (figure.type) {
        case 'image': {
          const { x, y, width, height, url, cachee } = figure
          const iim = j3pSvgAppend(this.params.svg, 'image', { x, y, width, height, xlink: url })
          if (cachee === true) {
            iim.style.display = 'none'
          }
          if (figure.id) {
            if (figure.id.indexOf('radar') !== -1) {
              this.params.MonRadar = iim
              this.params.MonRadarX = figure.fx
              this.params.MonRadarY = figure.fy
            }
            this.params.idIm.push({ id: figure.id, el: iim })
          }
          this.params.figIm.push(iim)
          break
        }

        case 'line' : {
          const ep = figure.epaisseur || 2
          const coul = figure.couleur || 'grey'
          const iim = j3pSvgAppend(this.params.svg, 'line', {
            x1: figure.x1,
            y1: figure.y1,
            x2: figure.x2,
            y2: figure.y2
          }, { style: { strokeWidth: ep + 'px', stroke: coul } })
          if (figure.affichelong) {
            const tt = figure.spe ? figure.spe : Math.round((Math.sqrt((figure.x2 - figure.x1) * (figure.x2 - figure.x1) + (figure.y2 - figure.y1) * (figure.y2 - figure.y1)))) + ''
            const ll = this.textWidth(tt, 'Comic Sans MS', 15)
            j3pSvgAppend(this.params.svg, 'text', {
              x: (figure.x1 + figure.x2 - ll) / 2,
              y: (figure.y1 + figure.y2) / 2 + 6
            }, {
              style: { stroke: '#000', fontSize: '15px', fontFace: 'Comic Sans MS' },
              text: tt,
              bgColor: 'rgba(236,230,233,0.5)',
              bgPadding: 3
            })
          }
          this.params.figIm.push(iim)
          break
        }

        case 'rectangle' : {
          const iim = j3pSvgAppend(this.params.svg, 'rect', {
            x: figure.x,
            y: figure.y,
            width: figure.width,
            height: figure.height
          }, { style: figure.style })
          this.params.figIm.push(iim)
          break
        }
        case 'Cases' : {
          const lx = j3pShuffle(figure.x).pop()
          const ly = j3pShuffle(figure.y).pop()
          const coul = j3pShuffle(figure.coul).pop()
          this.params.scratchProgDonnees.carteCases[lx - 1][ly - 1] = this.recupeCoul(coul)
          const iim = j3pSvgAppend(this.params.svg, 'rect', {
            x: (lx - 1) * 50,
            y: (ly - 1) * 50,
            width: 50,
            height: 50
          }, {
            style: 'fill:' + coul + ';stroke:black;stroke-width:4;fill-opacity:1;stroke-opacity:1'
          })
          this.params.figIm.push(iim)
          break
        }

        case 'pointdepart' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'red' } })
          this.params.scratchProgDonnees.Lutins[0].coxbase = figure.x1 - this.params.scratchProgDonnees.Lutins[0].larg + 1
          this.params.scratchProgDonnees.Lutins[0].coybase = figure.y1 + 116
          this.params.scratchProgDonnees.Lutins[0].cox = this.params.scratchProgDonnees.Lutins[0].coxbase
          this.params.scratchProgDonnees.Lutins[0].coy = this.params.scratchProgDonnees.Lutins[0].coybase
          this.params.figIm.push(iim)
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:red;fill:red'
          })
          break
        }
        case 'pointdepart2' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'green' } })
          this.params.figIm.push(iim)
          this.params.pointVx = figure.x1
          this.params.pointVy = figure.y1 + 150
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:green;fill:green'
          })
          break
        }
        case 'pointdepart3' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'blue' } })
          this.params.figIm.push(iim)
          this.params.pointBx = figure.x1
          this.params.pointBy = figure.y1 + 150
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:blue;fill:blue'
          })
          break
        }

        case 'angle' : {
          /// calcul l’angle
          const angle = Math.acos(((figure.x1 - figure.x2) * (figure.x3 - figure.x2) + (figure.y1 - figure.y2) * (figure.y3 - figure.y2)) / (Math.sqrt((figure.x1 - figure.x2) * (figure.x1 - figure.x2) + (figure.y1 - figure.y2) * (figure.y1 - figure.y2)) * Math.sqrt((figure.x3 - figure.x2) * (figure.x3 - figure.x2) + (figure.y3 - figure.y2) * (figure.y3 - figure.y2))))

          let angle1 = Math.atan((figure.y1 - figure.y2) / (figure.x1 - figure.x2))
          if (figure.x1 > figure.x2) {
            angle1 += Math.PI
          }
          if (angle1 < 0) {
            angle1 += 2 * Math.PI
          }
          if (angle1 >= 2 * Math.PI) {
            angle1 -= 2 * Math.PI
          }
          if (figure.x1 === figure.x2) {
            if (figure.y1 > figure.y2) {
              angle1 = 270 * Math.PI / 180
            } else {
              angle1 = 90 * Math.PI / 180
            }
          }

          let angle2 = Math.atan((figure.y3 - figure.y2) / (figure.x3 - figure.x2))
          if (figure.x3 > figure.x2) {
            angle2 += Math.PI
          }
          if (angle2 < 0) {
            angle2 += 2 * Math.PI
          }
          if (angle2 >= 2 * Math.PI) {
            angle2 -= 2 * Math.PI
          }
          if (figure.x3 === figure.x2) {
            if (figure.y3 > figure.y2) {
              angle2 = 270 * Math.PI / 180
            } else {
              angle2 = 90 * Math.PI / 180
            }
          }

          let anglef = (angle1 + angle2) / 2
          if (Math.abs(angle2 - angle1) > Math.PI) {
            anglef += Math.PI
          }

          const rayon = 10
          const centreX = figure.x2
          const centreY = figure.y2

          let ll = (angle2 > angle1) ? 1 : 0
          if (Math.abs(angle2 - angle1) > Math.PI) ll = 1 - ll

          const X0 = centreX - rayon * Math.cos(angle1)
          const Y0 = centreY - rayon * Math.sin(angle1)
          const X1 = centreX - rayon * Math.cos(angle2)
          const Y1 = centreY - rayon * Math.sin(angle2)
          const R = rayon
          // la string pour l’attribut d
          const ch = 'M ' + X0 + ' ' + Y0 + ' ' + ' A ' + R + ' ' + R + '  0 0 ' + ll + ' ' + X1 + ' ' + Y1

          const iim = j3pSvgAppend(this.params.svg, 'path', { d: ch, style: 'fill-opacity:0;stroke-width:2;stroke:#00f' })
          const tt = (Math.round(angle * 180 / Math.PI) + '°')
          ll = this.textWidth(tt, 'Comic Sans MS', 12)
          j3pSvgAppend(this.params.svg, 'text', {
            x: centreX - (rayon + 12) * Math.cos(anglef) - ll / 2,
            y: centreY - (rayon + 10) * Math.sin(anglef) + 6
          }, { style: { stroke: '#00f', fontSize: '12px', fontFace: 'Comic Sans MS' }, text: tt })
          this.params.figIm.push(iim)
        }
          break

        case 'angledirect' : {
          /// calcul l’angle
          const x1 = 2 * figure.x2 - figure.x1
          const y1 = 2 * figure.y2 - figure.y1
          const angle = Math.acos(((x1 - figure.x2) * (figure.x3 - figure.x2) + (y1 - figure.y2) * (figure.y3 - figure.y2)) / (Math.sqrt((x1 - figure.x2) * (x1 - figure.x2) + (y1 - figure.y2) * (y1 - figure.y2)) * Math.sqrt((figure.x3 - figure.x2) * (figure.x3 - figure.x2) + (figure.y3 - figure.y2) * (figure.y3 - figure.y2))))

          let angle1 = Math.atan((figure.y1 - figure.y2) / (figure.x1 - figure.x2))
          if (figure.x1 > figure.x2) {
            angle1 += Math.PI
          }
          angle1 += Math.PI
          if (angle1 < 0) {
            angle1 += 2 * Math.PI
          }
          if (angle1 >= 2 * Math.PI) {
            angle1 -= 2 * Math.PI
          }

          let angle2 = Math.atan((figure.y3 - figure.y2) / (figure.x3 - figure.x2))
          if (figure.x3 > figure.x2) {
            angle2 += Math.PI
          }
          if (angle2 < 0) {
            angle2 += 2 * Math.PI
          }
          if (angle2 >= 2 * Math.PI) {
            angle2 -= 2 * Math.PI
          }

          let anglef = (angle1 + angle2) / 2
          if (Math.abs(angle2 - angle1) > Math.PI) {
            anglef += Math.PI
          }

          const rayon = 10
          const centreX = figure.x2
          const centreY = figure.y2

          let ll = angle2 > angle1 ? 1 : 0
          if (Math.abs(angle2 - angle1) > Math.PI) {
            ll = 1 - ll
          }

          const X0 = centreX - rayon * Math.cos(angle1)
          const Y0 = centreY - rayon * Math.sin(angle1)
          const X1 = centreX - rayon * Math.cos(angle2)
          const Y1 = centreY - rayon * Math.sin(angle2)
          const R = rayon
          // la string pour l’attribut d
          const ch = 'M ' + X0 + ' ' + Y0 + ' ' + ' A ' + R + ' ' + R + '  0 0 ' + ll + ' ' + X1 + ' ' + Y1

          const iim = j3pSvgAppend(this.params.svg, 'path', { d: ch, style: 'fill-opacity:0;stroke-width:2;stroke:#00f' })
          const tt = (Math.round(angle * 180 / Math.PI) + '°')
          ll = this.textWidth(tt, 'Comic Sans MS', 12)
          j3pSvgAppend(this.params.svg, 'text', {
            x: centreX - (rayon + 12) * Math.cos(anglef) - ll / 2,
            y: centreY - (rayon + 10) * Math.sin(anglef) + 6
          }, { style: { stroke: '#00f', fontSize: '12px', fontFace: 'Comic Sans MS' }, text: tt, bgColor: '#ccccff' })
          j3pSvgAppend(this.params.svg, 'line', {
            x1,
            y1,
            x2: figure.x2,
            y2: figure.y2,
            style: 'stroke-width:1px;stroke:#00F;stroke-dasharray:4'
          })
          this.params.figIm.push(iim)
          break
        }
        case 'croix': {
          const po = { x: 150, y: 150 }
          const modifx = 145 / 240
          const modify = 146.5 / 240
          const dejx = dejaMarqAxeX.indexOf(Number(figure.x)) !== -1
          const dejy = dejaMarqAxeY.indexOf(Number(figure.y)) !== -1
          if (Number(figure.x) !== 0) {
            const iim3 = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: -Number(figure.y) * modify + po.y,
              x2: Number(figure.x) * modifx + po.x,
              y2: po.y
            }, { style: { strokeWidth: '1px', stroke: '#bbbbbb' } })
            this.params.figIm.push(iim3)
          }
          if (Number(figure.y) !== 0) {
            const iim3 = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: -Number(figure.y) * modify + po.y,
              x2: po.x,
              y2: -Number(figure.y) * modify + po.y
            }, { style: { strokeWidth: '1px', stroke: '#bbbbbb' } })
            this.params.figIm.push(iim3)
          }
          if (Number(figure.y) !== 0) {
            const iim = j3pSvgAppend(this.params.svg, 'line', {
              x1: po.x + 5,
              y1: -Number(figure.y) * modify + po.y,
              x2: po.x - 5,
              y2: -Number(figure.y) * modify + po.y
            }, { style: { strokeWidth: '2px', stroke: '#1346c4' } })
            this.params.figIm.push(iim)
          }
          if (Number(figure.x) !== 0) {
            const iim = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: po.y + 5,
              x2: Number(figure.x) * modifx + po.x,
              y2: po.y - 5
            }, { style: { strokeWidth: '2px', stroke: '#1346c4' } })
            this.params.figIm.push(iim)
          }
          if (!dejx) {
            if (Number(figure.x) !== 0 && Number(figure.x) !== 100 && Number(figure.x) !== -100) {
              aMarqAxe.push({
                x: Number(figure.x) * modifx + po.x - 9,
                y: (!figure.sens2) ? (po.y + 18) : (po.y - 10),
                text: '' + figure.x
              })
            }
          }
          if (!dejy) {
            if (Number(figure.y) !== 0 && Number(figure.y) !== 100 && Number(figure.y) !== -100) {
              aMarqAxe.push({
                x: (!figure.sens1) ? (po.x - 27) : (po.x + 6),
                y: -Number(figure.y) * modify + po.y + 4,
                text: '' + figure.y
              })
            }
          }
          dejaMarqAxeX.push(Number(figure.x))
          dejaMarqAxeY.push(Number(figure.y))
        }
      }
    }
    if (this.params.scratchProgDonnees.randomCases) {
      for (let i = 0; i < this.params.scratchProgDonnees.randomCases.length; i++) {
        const figure = this.params.scratchProgDonnees.randomCases[i]
        const lx = figure.cox - 1
        const ly = figure.coy - 1
        const coul = figure.coul
        this.params.scratchProgDonnees.carteCases[lx][ly] = this.recupeCoul(coul)
        const iim = j3pSvgAppend(this.params.svg, 'rect', {
          x: (lx) * 50,
          y: (ly) * 50,
          width: 50,
          height: 50
        }, {
          style: 'fill:' + coul + ';stroke:black;stroke-width:4;fill-opacity:1;stroke-opacity:1'
        })
        this.params.figIm.push(iim)
      }
    }
    for (let i = 0; i < aMarqAxe.length; i++) {
      j3pSvgAppend(this.params.svg, 'text', { x: aMarqAxe[i].x, y: aMarqAxe[i].y }, {
        style: {
          stroke: '#000',
          fontSize: '10px',
          fontFace: 'Comic Sans MS'
        },
        text: aMarqAxe[i].text,
        bgColor: '#ffffff'
      })
    }
    this.params.imageLutin[0] = j3pSvgAppend(this.params.svg, 'image', {
      x: this.params.scratchProgDonnees.Lutins[0].cox,
      y: this.params.scratchProgDonnees.Lutins[0].coy,
      width: this.params.scratchProgDonnees.Lutins[0].larg,
      height: this.params.scratchProgDonnees.Lutins[0].haut,
      xlink: this.params.scratchProgDonnees.Lutins[0].img
    })
    if (this.params.yaFauxClavier) this.params.imageLutin[0].addEventListener('click', () => { this.lanceEvent('sou') })
    this.params.zerozero = j3pSvgAppend(this.params.svg, 'rect', {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      style: 'stroke-width:1px;fill:rgb(200,100,100);stroke:rgb(200,200,200)'
    })

    const haut = 30
    for (let i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      this.params.AffVarRect1[i] = j3pSvgAppend(this.params.svg, 'rect', {
        x: 5,
        y: 10 + (i * haut),
        rx: 3,
        ry: 3,
        width: 20,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(200,200,255);stroke:rgb(100,100,150)'
      })
      this.params.AffVarRect2[i] = j3pSvgAppend(this.params.svg, 'rect', {
        x: 40,
        y: 10 + (i * haut),
        rx: 2,
        ry: 2,
        width: 2,
        height: 2,
        style: 'stroke-width:0;fill:rgb(229,83,0);stroke:rgb(200,200,200)'
      })
      this.params.AffVarText1[i] = j3pSvgAppend(this.params.svg, 'text', {
        x: 9,
        y: 20 + (i * haut)
      }, { style: { fontSize: '20px' }, text: String(this.params.scratchProgDonnees.variables[i].name) })
      this.params.AffVarText2[i] = j3pSvgAppend(this.params.svg, 'text', {
        x: 25,
        y: 20 + (i * haut)
      }, {
        style: { fontSize: '15px', stroke: '#ffffff', fill: '#ffffff' },
        text: String(this.params.scratchProgDonnees.variables[i].val)
      })
    }

    if (this.params.yainit) {
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 95,
        y: 77,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toFelix = j3pSvgAppend(this.params.svg, 'text', { x: 104, y: 92 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[1])
      })
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 100,
        y: 220,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toLlyn = j3pSvgAppend(this.params.svg, 'text', { x: 109, y: 235 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[0])
      })
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 220,
        y: 215,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toCharlie = j3pSvgAppend(this.params.svg, 'text', { x: 229, y: 230 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[2])
      })
    }
    this.params.maMain = this.params.imageLutin[0]
    this.scratchReplaceVar()
    if (this.params.bug) {
      this.params.cmptbug++
    } else {
      this.params.bug = true
      this.params.cmptbug = 0
    }
    if (this.params.yafusee) {
      this.scratchFusee()
    }
    if (this.params.yabras) {
      this.scratchBras()
    }
  } // traceSortieBase

  recupeCoul (s) {
    switch (s) {
      case '#f2ff1f': return 'Jaune'
      case '#003cff': return 'Bleue'
      case '#ff0000': return 'Rouge'
      case '#43c700': return 'Verte'
      case '#000000': return 'Noire'
      case '#a900d9': return 'Violette'
      case '#ff500e': return 'Orange'
      case '#ff41dc': return 'Rose'
    }
  }

  traceSortieBaseDemo () {
    this.params.scratchProgDonnees.carteCases = [
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide'],
      ['vide', 'vide', 'vide', 'vide', 'vide', 'vide']
    ]
    for (let i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      this.params.scratchProgDonnees.variables[i].val = 0
    }
    this.params.scratchProgDonnees.reponse = 0

    j3pEmpty(this.params.scratchProgDonnees.mypreDemo)
    if (this.params.yaFond) {
      const fondfond = j3pAddElt(this.params.scratchProgDonnees.mypreDemo, 'img', '', {
        src: this.params.lefond,
        width: 300,
        height: 300
      })
      fondfond.style.width = '300px'
      fondfond.style.height = '300px'
      fondfond.style.position = 'absolute'
    }
    this.params.scratchProgDonnees.mypreDemo.style.background = '#eee'
    this.params.svg = j3pCreeSVG(this.params.scratchProgDonnees.mypreDemo, { width: 300, height: 300, viewBox: '0 0 300 300' })
    this.params.svg.style.position = 'relative'
    j3pEmpty(this.params.svg)

    this.params.figIm = []
    this.params.idIm = []
    const dejaMarqAxeX = []
    const dejaMarqAxeY = []
    const aMarqAxe = []
    for (const figure of this.params.figureAfaire) {
      switch (figure.type) {
        case 'image': {
          const { x, y, width, height, url, cachee } = figure
          const iim = j3pSvgAppend(this.params.svg, 'image', { x, y, width, height, xlink: url })
          if (cachee === true) {
            iim.style.display = 'none'
          }
          if (figure.id) {
            if (figure.id.indexOf('radar') !== -1) {
              this.params.MonRadar = iim
              this.params.MonRadarX = figure.fx
              this.params.MonRadarY = figure.fy
            }
            this.params.idIm.push({ id: figure.id, el: iim })
          }
          this.params.figIm.push(iim)
          break
        }

        case 'line' : {
          const ep = figure.epaisseur || 2
          const coul = figure.couleur || 'grey'
          const iim = j3pSvgAppend(this.params.svg, 'line', {
            x1: figure.x1,
            y1: figure.y1,
            x2: figure.x2,
            y2: figure.y2
          }, { style: { strokeWidth: ep + 'px', stroke: coul } })
          if (figure.affichelong) {
            const tt = figure.spe ? figure.spe : Math.round((Math.sqrt((figure.x2 - figure.x1) * (figure.x2 - figure.x1) + (figure.y2 - figure.y1) * (figure.y2 - figure.y1)))) + ''
            const ll = this.textWidth(tt, 'Comic Sans MS', 15)
            j3pSvgAppend(this.params.svg, 'text', {
              x: (figure.x1 + figure.x2 - ll) / 2,
              y: (figure.y1 + figure.y2) / 2 + 6
            }, {
              style: { stroke: '#000', fontSize: '15px', fontFace: 'Comic Sans MS' },
              text: tt,
              bgColor: 'rgba(236,230,233,0.5)',
              bgPadding: 3
            })
          }
          this.params.figIm.push(iim)
          break
        }

        case 'rectangle' : {
          const iim = j3pSvgAppend(this.params.svg, 'rect', {
            x: figure.x,
            y: figure.y,
            width: figure.width,
            height: figure.height
          }, { style: figure.style })
          this.params.figIm.push(iim)
          break
        }
        case 'Cases' : {
          const lx = j3pShuffle(figure.x).pop()
          const ly = j3pShuffle(figure.y).pop()
          const coul = j3pShuffle(figure.coul).pop()
          this.params.scratchProgDonnees.carteCases[lx - 1][ly - 1] = this.recupeCoul(coul)
          const iim = j3pSvgAppend(this.params.svg, 'rect', {
            x: (lx - 1) * 50,
            y: (ly - 1) * 50,
            width: 50,
            height: 50
          }, {
            style: 'fill:' + coul + ';stroke:black;stroke-width:4;fill-opacity:1;stroke-opacity:1'
          })
          this.params.figIm.push(iim)
          break
        }
        case 'pointdepart' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'red' } })
          this.params.scratchProgDonnees.Lutins[0].coxbase = figure.x1 - this.params.scratchProgDonnees.Lutins[0].larg + 1
          this.params.scratchProgDonnees.Lutins[0].coybase = figure.y1 + 116
          this.params.scratchProgDonnees.Lutins[0].cox = this.params.scratchProgDonnees.Lutins[0].coxbase
          this.params.scratchProgDonnees.Lutins[0].coy = this.params.scratchProgDonnees.Lutins[0].coybase
          this.params.figIm.push(iim)
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:red;fill:red'
          })
          break
        }
        case 'pointdepart2' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'green' } })
          this.params.figIm.push(iim)
          this.params.pointVx = figure.x1
          this.params.pointVy = figure.y1 + 150
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:green;fill:green'
          })
          break
        }
        case 'pointdepart3' : {
          const iim = j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1,
            r: 4
          }, { style: { fill: 'blue' } })
          this.params.figIm.push(iim)
          this.params.pointBx = figure.x1
          this.params.pointBy = figure.y1 + 150
          j3pSvgAppend(this.params.svg, 'circle', {
            cx: figure.x1,
            cy: figure.y1 + 150,
            r: 4,
            style: 'stroke:blue;fill:blue'
          })
          break
        }

        case 'angle' : {
          /// calcul l’angle
          const angle = Math.acos(((figure.x1 - figure.x2) * (figure.x3 - figure.x2) + (figure.y1 - figure.y2) * (figure.y3 - figure.y2)) / (Math.sqrt((figure.x1 - figure.x2) * (figure.x1 - figure.x2) + (figure.y1 - figure.y2) * (figure.y1 - figure.y2)) * Math.sqrt((figure.x3 - figure.x2) * (figure.x3 - figure.x2) + (figure.y3 - figure.y2) * (figure.y3 - figure.y2))))

          let angle1 = Math.atan((figure.y1 - figure.y2) / (figure.x1 - figure.x2))
          if (figure.x1 > figure.x2) {
            angle1 += Math.PI
          }
          if (angle1 < 0) {
            angle1 += 2 * Math.PI
          }
          if (angle1 >= 2 * Math.PI) {
            angle1 -= 2 * Math.PI
          }
          if (figure.x1 === figure.x2) {
            if (figure.y1 > figure.y2) {
              angle1 = 270 * Math.PI / 180
            } else {
              angle1 = 90 * Math.PI / 180
            }
          }

          let angle2 = Math.atan((figure.y3 - figure.y2) / (figure.x3 - figure.x2))
          if (figure.x3 > figure.x2) {
            angle2 += Math.PI
          }
          if (angle2 < 0) {
            angle2 += 2 * Math.PI
          }
          if (angle2 >= 2 * Math.PI) {
            angle2 -= 2 * Math.PI
          }
          if (figure.x3 === figure.x2) {
            if (figure.y3 > figure.y2) {
              angle2 = 270 * Math.PI / 180
            } else {
              angle2 = 90 * Math.PI / 180
            }
          }

          let anglef = (angle1 + angle2) / 2
          if (Math.abs(angle2 - angle1) > Math.PI) {
            anglef += Math.PI
          }

          const rayon = 10
          const centreX = figure.x2
          const centreY = figure.y2

          let ll = (angle2 > angle1) ? 1 : 0
          if (Math.abs(angle2 - angle1) > Math.PI) ll = 1 - ll

          const X0 = centreX - rayon * Math.cos(angle1)
          const Y0 = centreY - rayon * Math.sin(angle1)
          const X1 = centreX - rayon * Math.cos(angle2)
          const Y1 = centreY - rayon * Math.sin(angle2)
          const R = rayon
          // la string pour l’attribut d
          const ch = 'M ' + X0 + ' ' + Y0 + ' ' + ' A ' + R + ' ' + R + '  0 0 ' + ll + ' ' + X1 + ' ' + Y1

          const iim = j3pSvgAppend(this.params.svg, 'path', { d: ch, style: 'fill-opacity:0;stroke-width:2;stroke:#00f' })
          const tt = (Math.round(angle * 180 / Math.PI) + '°')
          ll = this.textWidth(tt, 'Comic Sans MS', 12)
          j3pSvgAppend(this.params.svg, 'text', {
            x: centreX - (rayon + 12) * Math.cos(anglef) - ll / 2,
            y: centreY - (rayon + 10) * Math.sin(anglef) + 6
          }, { style: { stroke: '#00f', fontSize: '12px', fontFace: 'Comic Sans MS' }, text: tt })
          this.params.figIm.push(iim)
        }
          break

        case 'angledirect' : {
          /// calcul l’angle
          const x1 = 2 * figure.x2 - figure.x1
          const y1 = 2 * figure.y2 - figure.y1
          const angle = Math.acos(((x1 - figure.x2) * (figure.x3 - figure.x2) + (y1 - figure.y2) * (figure.y3 - figure.y2)) / (Math.sqrt((x1 - figure.x2) * (x1 - figure.x2) + (y1 - figure.y2) * (y1 - figure.y2)) * Math.sqrt((figure.x3 - figure.x2) * (figure.x3 - figure.x2) + (figure.y3 - figure.y2) * (figure.y3 - figure.y2))))

          let angle1 = Math.atan((figure.y1 - figure.y2) / (figure.x1 - figure.x2))
          if (figure.x1 > figure.x2) {
            angle1 += Math.PI
          }
          angle1 += Math.PI
          if (angle1 < 0) {
            angle1 += 2 * Math.PI
          }
          if (angle1 >= 2 * Math.PI) {
            angle1 -= 2 * Math.PI
          }

          let angle2 = Math.atan((figure.y3 - figure.y2) / (figure.x3 - figure.x2))
          if (figure.x3 > figure.x2) {
            angle2 += Math.PI
          }
          if (angle2 < 0) {
            angle2 += 2 * Math.PI
          }
          if (angle2 >= 2 * Math.PI) {
            angle2 -= 2 * Math.PI
          }

          let anglef = (angle1 + angle2) / 2
          if (Math.abs(angle2 - angle1) > Math.PI) {
            anglef += Math.PI
          }

          const rayon = 10
          const centreX = figure.x2
          const centreY = figure.y2

          let ll = angle2 > angle1 ? 1 : 0
          if (Math.abs(angle2 - angle1) > Math.PI) {
            ll = 1 - ll
          }

          const X0 = centreX - rayon * Math.cos(angle1)
          const Y0 = centreY - rayon * Math.sin(angle1)
          const X1 = centreX - rayon * Math.cos(angle2)
          const Y1 = centreY - rayon * Math.sin(angle2)
          const R = rayon
          // la string pour l’attribut d
          const ch = 'M ' + X0 + ' ' + Y0 + ' ' + ' A ' + R + ' ' + R + '  0 0 ' + ll + ' ' + X1 + ' ' + Y1

          const iim = j3pSvgAppend(this.params.svg, 'path', { d: ch, style: 'fill-opacity:0;stroke-width:2;stroke:#00f' })
          const tt = (Math.round(angle * 180 / Math.PI) + '°')
          ll = this.textWidth(tt, 'Comic Sans MS', 12)
          j3pSvgAppend(this.params.svg, 'text', {
            x: centreX - (rayon + 12) * Math.cos(anglef) - ll / 2,
            y: centreY - (rayon + 10) * Math.sin(anglef) + 6
          }, { style: { stroke: '#00f', fontSize: '12px', fontFace: 'Comic Sans MS' }, text: tt, bgColor: '#ccccff' })
          j3pSvgAppend(this.params.svg, 'line', {
            x1,
            y1,
            x2: figure.x2,
            y2: figure.y2,
            style: 'stroke-width:1px;stroke:#00F;stroke-dasharray:4'
          })
          this.params.figIm.push(iim)
          break
        }
        case 'croix': {
          const po = { x: 150, y: 150 }
          const modifx = 145 / 240
          const modify = 148 / 240
          const dejx = dejaMarqAxeX.indexOf(Number(figure.x)) !== -1
          const dejy = dejaMarqAxeY.indexOf(Number(figure.y)) !== -1
          if (Number(figure.x) !== 0) {
            const iim3 = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: -Number(figure.y) * modify + po.y,
              x2: Number(figure.x) * modifx + po.x,
              y2: po.y
            }, { style: { strokeWidth: '1px', stroke: '#bbbbbb' } })
            this.params.figIm.push(iim3)
          }
          if (Number(figure.y) !== 0) {
            const iim3 = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: -Number(figure.y) * modify + po.y,
              x2: po.x,
              y2: -Number(figure.y) * modify + po.y
            }, { style: { strokeWidth: '1px', stroke: '#bbbbbb' } })
            this.params.figIm.push(iim3)
          }
          if (Number(figure.y) !== 0) {
            const iim = j3pSvgAppend(this.params.svg, 'line', {
              x1: po.x + 5,
              y1: -Number(figure.y) * modify + po.y,
              x2: po.x - 5,
              y2: -Number(figure.y) * modify + po.y
            }, { style: { strokeWidth: '2px', stroke: '#1346c4' } })
            this.params.figIm.push(iim)
          }
          if (Number(figure.x) !== 0) {
            const iim = j3pSvgAppend(this.params.svg, 'line', {
              x1: Number(figure.x) * modifx + po.x,
              y1: po.y + 5,
              x2: Number(figure.x) * modifx + po.x,
              y2: po.y - 5
            }, { style: { strokeWidth: '2px', stroke: '#1346c4' } })
            this.params.figIm.push(iim)
          }
          if (!dejx) {
            if (Number(figure.x) !== 0 && Number(figure.x) !== 100 && Number(figure.x) !== -100) {
              aMarqAxe.push({
                x: Number(figure.x) * modifx + po.x - 9,
                y: (!figure.sens2) ? (po.y + 18) : (po.y - 10),
                text: '' + figure.x
              })
            }
          }
          if (!dejy) {
            if (Number(figure.y) !== 0 && Number(figure.y) !== 100 && Number(figure.y) !== -100) {
              aMarqAxe.push({
                x: (!figure.sens1) ? (po.x - 27) : (po.x + 6),
                y: -Number(figure.y) * modify + po.y + 4,
                text: '' + figure.y
              })
            }
          }
          dejaMarqAxeX.push(Number(figure.x))
          dejaMarqAxeY.push(Number(figure.y))
        }
      }
    }
    for (let i = 0; i < aMarqAxe.length; i++) {
      j3pSvgAppend(this.params.svg, 'text', { x: aMarqAxe[i].x, y: aMarqAxe[i].y }, {
        style: {
          stroke: '#000',
          fontSize: '10px',
          fontFace: 'Comic Sans MS'
        },
        text: aMarqAxe[i].text,
        bgColor: '#ffffff'
      })
    }
    this.params.imageLutin[0] = j3pSvgAppend(this.params.svg, 'image', {
      x: this.params.scratchProgDonnees.Lutins[0].cox,
      y: this.params.scratchProgDonnees.Lutins[0].coy,
      width: this.params.scratchProgDonnees.Lutins[0].larg,
      height: this.params.scratchProgDonnees.Lutins[0].haut,
      xlink: this.params.scratchProgDonnees.Lutins[0].img
    })
    this.params.zerozero = j3pSvgAppend(this.params.svg, 'rect', {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      style: 'stroke-width:1px;fill:rgb(200,100,100);stroke:rgb(200,200,200)'
    })
    const haut = 30
    for (let i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      this.params.AffVarRect1[i] = j3pSvgAppend(this.params.svg, 'rect', {
        x: 5,
        y: 10 + (i * haut),
        rx: 3,
        ry: 3,
        width: 20,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(200,200,255);stroke:rgb(100,100,150)'
      })
      this.params.AffVarRect2[i] = j3pSvgAppend(this.params.svg, 'rect', {
        x: 40,
        y: 10 + (i * haut),
        rx: 2,
        ry: 2,
        width: 2,
        height: 2,
        style: 'stroke-width:0;fill:rgb(229,83,0);stroke:rgb(200,200,200)'
      })
      this.params.AffVarText1[i] = j3pSvgAppend(this.params.svg, 'text', {
        x: 9,
        y: 20 + (i * haut)
      }, { style: { fontSize: '20px' }, text: String(this.params.scratchProgDonnees.variables[i].name) })
      this.params.AffVarText2[i] = j3pSvgAppend(this.params.svg, 'text', {
        x: 25,
        y: 20 + (i * haut)
      }, {
        style: { fontSize: '15px', stroke: '#ffffff', fill: '#ffffff' },
        text: String(this.params.scratchProgDonnees.variables[i].val)
      })
    }
    if (this.params.yainit) {
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 95,
        y: 77,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toFelix = j3pSvgAppend(this.params.svg, 'text', { x: 104, y: 92 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[1])
      })
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 100,
        y: 220,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toLlyn = j3pSvgAppend(this.params.svg, 'text', { x: 109, y: 235 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[0])
      })
      j3pSvgAppend(this.params.svg, 'rect', {
        x: 220,
        y: 215,
        rx: 3,
        ry: 3,
        width: 30,
        height: 20,
        style: 'stroke-width:1px;fill:rgb(255,255,255);stroke:rgb(100,100,150)'
      })
      this.params.toCharlie = j3pSvgAppend(this.params.svg, 'text', { x: 229, y: 230 }, {
        style: {
          fontSize: '15px',
          stroke: '#000000',
          fill: '#000000'
        },
        text: String(this.params.scratchProgDonnees.mem[2])
      })
    }
    this.params.maMain = this.params.imageLutin[0]
    this.scratchReplaceVar()
  } // traceSortieBaseDemo

  // FIXME pourquoi créer une deuxième méthode strictement identique à la première ???
  scratchCreeProgCo2 (a, b) {
    return this.scratchCreeProgCo(a, b)
  }

  execProg2 (letabeduprog) {
    this.params.scratchProgDonnees.variables = this.params.scratchProgDonnees.workspace.getAllVariables()
    for (let i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      this.params.scratchProgDonnees.variables[i].visible = true
    }
    const progAtest =
      {
        paramDeScratchprog: {
          vite: false,
          PasAPas: false,
          cacheTest: false,
          workspace: this.params.scratchProgDonnees.workspace,
          tabcodeX: letabeduprog,
          sortie: this.params.scratchProgDonnees.mypre,
          valAlea: [],
          valGetUs: [],
          valRadar: undefined,
          cKi: 'eleve'
        }
      }
    this.lanceProg(progAtest, 0)
  }

  scratchMenuPerso () {
    if (this.params.menuPerso) this.params.menuPerso.destroy()
    j3pEmpty(this.lesdivs.divBlocPerso)
    if (this.params.lesBlocPerso.length === 0) return
    const yy = addDefaultTable(this.lesdivs.divBlocPerso, 1, 3)
    yy[0][0].style.padding = '0px'
    yy[0][1].style.padding = '0px'
    yy[0][2].style.padding = '0px'
    yy[0][0].style.width = '100px'
    yy[0][2].style.width = '100px'
    if (!this.params.vireperso) j3pAddContent(yy[0][1], 'Définitions')
    yy[0][1].style.color = '#0000FF'
    yy[0][1].style.cursor = 'pointer'
    const listeMenu = []
    for (let i = 0; i < this.params.lesBlocPerso.length; i++) {
      listeMenu.push({
        name: 'men' + i,
        label: this.params.lesBlocPerso[i],
        callback: (choice, i) => {
          this.scratchDemandeMenu(i)
        }
      })
    }
    if (!this.params.vireperso) this.params.menuPerso = new MenuContextuel(yy[0][1], listeMenu)
  }

  scratchDemandeMenu (i) {
    if (!this.params.isEditing) {
      this.params.ModaleDefi = j3pModale({ titre: 'Définition de ' + i.label })
      const elem = $('.croix')[0]
      j3pDetruit(elem)
    } else {
      this.params.ModaleDefi = this.params.ModaleCreaisEditing
    }
    const tttab = addDefaultTable(this.params.ModaleDefi, 2, 1)
    let letext = this.params.listeDef[i.label].txt
    let toRemp = ['%1', '%2', '%3']
    if (this.params.listeDef[i.label].yaicone) {
      letext = letext.replace('%1 %2', '')
      toRemp = ['%3', '%4', '%5']
    }
    letext = letext.replace(toRemp[0], ' <span style="border:1px solid blue;background:cornflowerblue;border-radius: 10px;color: white">&nbsp;x&nbsp;</span> ')
    letext = letext.replace(toRemp[1], ' <span style="border:1px solid blue;background:cornflowerblue;border-radius: 10px;color: white">&nbsp;y&nbsp;</span> ')
    letext = letext.replace(toRemp[2], ' <span style="border:1px solid blue;background:cornflowerblue;border-radius: 10px;color: white">&nbsp;z&nbsp;</span> ')
    const tttab2 = addDefaultTable(tttab[0][0], 1, 5)
    j3pAffiche(tttab2[0][0], null, '<u><b>Text</b></u>: ' + letext)
    tttab2[0][1].style.width = '100px'
    tttab2[0][3].style.width = '100px'
    j3pAjouteBouton(tttab2[0][2], () => {
      this.scratchValideDef(i.label)
    }, { value: 'Valider' })
    j3pAjouteBouton(tttab2[0][4], this.scratchAnnuleDef.bind(this), { value: 'Annuler' })
    tttab[0][0].style.borderBottom = '1px solid black'
    const xmlDoc = $.parseXML(this.params.listeDef[i.label].xmlText)
    let x = xmlDoc.firstChild
    while (x.nodeType !== 1) {
      x = x.nextSibling
    }

    let MMMMenu = j3pClone(this.params.scratchProgDonnees.menu)
    MMMMenu.pop()
    for (let j = MMMMenu[MMMMenu.length - 1].contenu.length - 1; j > -1; j--) {
      if (MMMMenu[MMMMenu.length - 1].contenu[j].type === i.label + 'b') {
        MMMMenu[MMMMenu.length - 1].contenu.splice(j, 1000)
        break
      }
    }
    // if (MMMMenu[MMMMenu.length - 1].contenu.length === 0) MMMMenu.pop()
    if (this.cmtglibre) {
      MMMMenu[MMMMenu.length - 2].contenu.push({ type: 'NomAleatoire' })
      MMMMenu[MMMMenu.length - 2].contenu.push({
        type: 'Regroupe',
        value: [{ nom: 'A', contenu: { valeur: ' ' } }, { nom: 'B', contenu: { valeur: ' ' } }]
      })
      MMMMenu[MMMMenu.length - 2].contenu.push({ type: 'ecriSeg', value: [{ nom: 'A', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 2].contenu.push({ type: 'ecriDroite', value: [{ nom: 'A', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 2].contenu.push({ type: 'ecriDDroite', value: [{ nom: 'A', contenu: { valeur: ' ' } }] })
    }
    if (this.isMtg && this.params.isEditing) {
      MMMMenu = []

      MMMMenu.push({ type: 'catégorie', nom: 'POINTS', coul1: '#3230B8', coul2: '#000000', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'pointLibreAvecNom', value: [{ nom: 'NOM', contenu: { valeur: 'A' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'pointIntersection', value: [{ nom: 'NOM', contenu: { valeur: 'A' } }, { nom: 'CONT1', contenu: { valeur: ' ' } }, { nom: 'CONT2', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'pointSur', value: [{ nom: 'NOM', contenu: { valeur: 'A' } }, { nom: 'CONT', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'pointMilieu', value: [{ nom: 'NOM', contenu: { valeur: 'I' } }, { nom: 'CONT', contenu: { valeur: 'AB' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'pointLong', value: [{ nom: 'EXT1', contenu: { valeur: 'A' } }, { nom: 'LONG', contenu: { valeur: ' ' } }, { nom: 'P1', contenu: { valeur: ' ' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'SEGMENTS', coul1: '#ff0000', coul2: '#000000', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'segmentAB', value: [{ nom: 'EXT1', contenu: { valeur: 'AB' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'segmentlongA', value: [{ nom: 'EXT1', contenu: { valeur: 'AB' } }, { nom: 'LONG', contenu: { valeur: '3' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'DEMI-DROITES', coul1: '#e11ac3', coul2: '#000000', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'demidroitept', value: [{ nom: 'ORIGINE', contenu: { valeur: 'AB' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'angleplus', value: [{ nom: 'NOM', contenu: { valeur: 'ABC' } }, { nom: 'MESURE', contenu: { valeur: '45' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'anglemoins', value: [{ nom: 'NOM', contenu: { valeur: 'ABC' } }, { nom: 'MESURE', contenu: { valeur: '45' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'DROITES', coul1: '#7900c5', coul2: '#000000', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'droitept', value: [{ nom: 'POINT1', contenu: { valeur: 'AB' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'droitepara', value: [{ nom: 'NOM', contenu: { valeur: '(d1)' } }, { nom: 'PARA', contenu: { valeur: '(AB)' } }, { nom: 'POINT', contenu: { valeur: 'C' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'droiteperp', value: [{ nom: 'NOM', contenu: { valeur: '(d1)' } }, { nom: 'PERP', contenu: { valeur: '(AB)' } }, { nom: 'POINT', contenu: { valeur: 'C' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'CERCLES', coul1: '#E55B16', coul2: '#090505', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'cercleCentrePoint', value: [{ nom: 'CENTRE', contenu: { valeur: 'O' } }, { nom: 'POINT', contenu: { valeur: 'A' } }, { nom: 'NOM', contenu: { valeur: 'C1' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'cercleCentreRayon', value: [{ nom: 'CENTRE', contenu: { valeur: 'O' } }, { nom: 'RAYON', contenu: { valeur: '3' } }, { nom: 'NOM', contenu: { valeur: 'C1' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'ARC DE CERCLES', coul1: '#E57E16', coul2: '#010202', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'arc1PointPasse', value: [{ nom: 'POINT', contenu: { valeur: 'M' } }, { nom: 'NOM', contenu: { valeur: 'BC' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'arccentreplus', value: [{ nom: 'CENTRE', contenu: { valeur: 'O' } }, { nom: 'NOM', contenu: { valeur: 'AB' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'arccentremoins', value: [{ nom: 'CENTRE', contenu: { valeur: 'O' } }, { nom: 'NOM', contenu: { valeur: 'AB' } }] })

      MMMMenu.push({ type: 'catégorie', nom: 'UTILITAIRES', coul1: '#B5E61D', coul2: '#3373CC', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'mtgEffacer', value: [{ nom: 'NOM', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'mtgPointille', value: [{ nom: 'NOM', contenu: { valeur: '[AB]' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'Renommer', value: [{ nom: 'NOM', contenu: { valeur: ' ' } }, { nom: 'NOM2', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'Regroupe', value: [{ nom: 'A', contenu: { valeur: ' ' } }, { nom: 'B', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'ecriSeg', value: [{ nom: 'A', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'ecriDroite', value: [{ nom: 'A', contenu: { valeur: ' ' } }, { nom: 'B', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'ecriDDroite', value: [{ nom: 'A', contenu: { valeur: ' ' } }, { nom: 'B', contenu: { valeur: ' ' } }] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'NomAleatoire' })

      if (this.params.MtgProg.blocPerso) {
        for (let i = 0; i < this.params.MtgProg.blocPerso.length; i++) {
          if (i === 0) {
            MMMMenu.push({ type: 'catégorie', nom: 'MES BLOCS', coul1: '#D14C7D', coul2: '#B8436E', contenu: [] })
          }

          const nwBlo = this.params.MtgProg.blocPerso[i]
          const value = []
          if (nwBlo.nbVar > 0) {
            value.push({ nom: 'X', contenu: { valeur: ' ' } })
          }
          if (nwBlo.nbVar > 1) {
            value.push({ nom: 'Y', contenu: { valeur: ' ' } })
          }
          if (nwBlo.nbVar > 2) {
            value.push({ nom: 'Z', contenu: { valeur: ' ' } })
          }
          MMMMenu[MMMMenu.length - 1].contenu.push({ type: nwBlo.nom + 'b', value })
        }
      }
    }

    if (MMMMenu[MMMMenu.length - 1].contenu.length === 0) {
      MMMMenu.splice(MMMMenu.length - 1, 1)
    }
    if (this.params.listeDef[i.label].nbVar > 0) {
      MMMMenu.push({ type: 'catégorie', nom: 'Variables internes', coul1: '#B5E61D', coul2: '#3373CC', contenu: [] })
      MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'varx' })
      if (this.params.listeDef[i.label].nbVar > 1) MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'vary' })
      if (this.params.listeDef[i.label].nbVar > 2) MMMMenu[MMMMenu.length - 1].contenu.push({ type: 'varz' })
    }
    MMMMenu.push({
      type: 'catégorie',
      nom: 'VARIABLES',
      coul1: '#FF8C1A',
      coul2: '#DB6E00',
      custom: 'VARIABLE',
      contenu: []
    })

    const x2 = this.scratchCreeMenu(MMMMenu, true)
    const xmlDoc2 = $.parseXML(x2)
    let xml2 = xmlDoc2.firstChild
    while (xml2.nodeType !== 1) {
      xml2 = xml2.nextSibling
    }

    const side = 'start'
    this.params.scratchProgDonnees.workspaceDef = this.Blockly.inject(tttab[1][0], {
      comments: true,
      disable: false,
      collapse: false,
      media: j3pBaseUrl + '/externals/blockly/media/',
      readOnly: false,
      rtl: null,
      scrollbars: true,
      toolbox: xml2,
      toolboxPosition: side === 'top' || side === 'start' ? 'start' : 'end',
      horizontalLayout: side === 'top' || side === 'bottom',
      sounds: false,
      zoom: {
        controls: true,
        wheel: true,
        startScale: 0.6,
        maxScale: 2,
        minScale: 0.25,
        scaleSpeed: 1.1
      },
      colours: {
        fieldShadow: 'rgba(100, 100, 100, 0.3)',
        dragShadowOpacity: 0.6
      }
    })
    this.params.ModaleDefi.style.top = '0px'
    this.params.ModaleDefi.style.left = '0px'
    tttab[1][0].style.height = '600px'
    tttab[1][0].style.width = '1200px'
    this.Blockly.svgResize(this.params.scratchProgDonnees.workspaceDef)
    // this.params.scratchProgDonnees.workspaceDef.clear()
    this.Blockly.Xml.domToWorkspace(x, this.params.scratchProgDonnees.workspaceDef)
  }

  scratchAnnuleDef () {
    if (!this.params.isEditing) {
      j3pDetruit(this.params.ModaleDefi, 'j3pmasque')
    }
    this.finishCreaPerso()
  }

  scratchValideDef (label) {
    const lxml = this.Blockly.Xml.workspaceToDom(this.params.scratchProgDonnees.workspaceDef)
    this.params.listeDef[label].xmlText = this.Blockly.Xml.domToText(lxml)

    window.LoopTrap = 10000
    this.Blockly.JavaScript.INFINITE_LOOP_TRAP =
      'if (--window.LoopTrap == 0) throw "Infinite loop.";\n'
    //
    const ladef = this.Blockly.JavaScript.workspaceToCode(this.params.scratchProgDonnees.workspaceDef)
    ladef[0].splice(0, 1)
    this.params.listeDef[label].def = ladef[0]
    if (!this.params.isEditing) {
      j3pDetruit(this.params.ModaleDefi, 'j3pmasque')
    }
    this.finishCreaPerso()
  }

  scratchfautStop (t) {
    this.params.scratchProgDonnees.pause = false
    this.params.scratchProgDonnees.fostopErreur = true
    this.params.scratchProgDonnees.fostopErreurText = t
  }

  scratchYaPLusieurs (obj) {
    const donnesMtg = this.params.donnesMtg
    let cmpt = 0
    for (let i = 0; i < donnesMtg.length; i++) {
      let ok = true
      ok = ok && (!obj.nom || (donnesMtg[i].noms.indexOf(obj.nom) !== -1))
      ok = ok && (!obj.centre || (donnesMtg[i].centre === obj.centre))
      ok = ok && (!obj.point || (donnesMtg[i].point === obj.point))
      ok = ok && (!obj.rayon || (donnesMtg[i].point === obj.rayon))
      ok = ok && (!obj.type || (donnesMtg[i].type === obj.type))
      if (ok) cmpt++
    }
    return cmpt > 1
  }

  scratchLigneContient (tab, type, retour) {
    for (let j = 0; j < this.params.donnesMtg.length; j++) {
      const ob = this.params.donnesMtg[j]
      if (this.params.donnesMtg[j].efface) continue
      if (ob.contient) {
        if (type !== 'arc' && type !== 'cercle') {
          if (ob.type !== 'cercle' && ob.type !== 'arc') {
            if (type === 'droite' && ob.type !== 'droite') continue
            if (type === 'demidroite') {
              if (ob.type === 'segment') continue
              if (ob.type === 'demidroite') continue
            }
            let ok = true
            for (let i = 0; i < tab.length; i++) {
              let acomp = tab[i]
              while (acomp.indexOf('\\\'') !== -1) {
                acomp = acomp.replace('\\\'', '\'')
              }
              while (acomp.indexOf('\\"') !== -1) {
                acomp = acomp.replace('\\"', '"')
              }
              ok = ok && ob.contient.indexOf(acomp) !== -1
            }
            if (ok) {
              if (!retour) return true
              return ob.noms[0]
            }
          }
        } else {
          if (ob.type !== 'segment' && ob.type.indexOf('droite') === -1) {
            let ok = true
            for (let i = 0; i < tab.length; i++) {
              let acomp = tab[i]
              while (acomp.indexOf('\\\'') !== -1) {
                acomp = acomp.replace('\\\'', '\'')
              }
              while (acomp.indexOf('\\"') !== -1) {
                acomp = acomp.replace('\\"', '"')
              }
              ok = ok && ob.contient.indexOf(acomp) !== -1
            }
            if (ok) {
              if (!retour) return true
              return ob
            }
          }
        }
      }
    }
    return false
  }

  scratchAjouteContient (num, ki, koi) {
    let donnesMtg
    switch (num) {
      case 1:
        donnesMtg = this.params.donnesMtg
        break
      case 2:
        donnesMtg = this.params.donnesMtg2
        break
      case 3:
        donnesMtg = this.params.donnesMtg3
        break
    }
    for (let i = 0; i < donnesMtg.length; i++) {
      if (donnesMtg[i].noms.indexOf(ki) !== -1) {
        if (donnesMtg[i].contient) {
          donnesMtg[i].contient.push(koi)
        } else {
          donnesMtg[i].contient = [koi]
        }
        return
      }
    }
  }

  scratchIsNomCorecct (nom, type) {
    while (nom.indexOf('\\\'') !== -1) {
      nom = nom.replace('\\\'', '\'')
    }
    while (nom.indexOf('\\"') !== -1) {
      nom = nom.replace('\\"', '"')
    }
    switch (type) {
      case 'point':
        return nom !== '' && /^[A-Z][0-9]?['"]?$/.test(nom)
      case 'droite':
        return nom !== '' && (/^\([a-zA-Z][0-9]['"]?\)$/.test(nom) || /^[a-zA-Z][0-9]['"]?$/.test(nom))
      case 'cercle':
        return nom !== '' && (/^\([A-Za-z][0-9]?\)$/.test(nom) || /^[A-Za-z][0-9]?$/.test(nom))
    }
  }

  scratchDecomposeEnPoints (nom) {
    function nomPourCode (code) {
      let aret = ''
      ldnp.forEach(element => {
        if (element.code === code) aret = element.nom
      })
      return aret
    }

    const listeDesNomDePOint = this.scratchFaitListeNomPoints()
    listeDesNomDePOint.sort(sortOnLengthProp)
    const ldnp = []
    listeDesNomDePOint.forEach(element => {
      if (element !== '') ldnp.push({ nom: element, code: '#' + ldnp.length })
    })
    while (nom.indexOf('\\\'') !== -1) {
      nom = nom.replace('\\\'', '\'')
    }
    while (nom.indexOf('\\"') !== -1) {
      nom = nom.replace('\\"', '"')
    }
    ldnp.forEach(element => {
      while (nom.indexOf(element.nom) !== -1) {
        nom = nom.replace(element.nom, element.code)
      }
    })
    const aret = []
    const words = nom.split('#')
    if (words.length === 0) return []
    if (words[0] !== '') return []
    words.splice(0, 1)
    let fovire = false
    words.forEach(element => {
      const nn = nomPourCode('#' + element)
      if (nn) {
        aret.push(nn)
      } else {
        fovire = true
      }
    })
    if (fovire) return []
    return aret
  }

  scratchFaitListeNomPoints () {
    const aret = []
    for (let i = 0; i < listLL.length; i++) {
      aret.push(listLL[i], listLL[i] + '\'', listLL[i] + '"')
      for (let j = 0; j < 10; j++) {
        aret.push(listLL[i] + j, listLL[i] + j + '\'', listLL[i] + j + '"')
      }
    }
    return aret
  }

  scratchReinitBase (b) {
    this.params.scratchProgDonnees.runable = true
    this.params.scratchProgDonnees.Lutins[0].cox = this.params.scratchProgDonnees.Lutins[0].coxbase
    this.params.scratchProgDonnees.Lutins[0].coy = this.params.scratchProgDonnees.Lutins[0].coybase
    this.params.scratchProgDonnees.Lutins[0].angle = this.params.scratchProgDonnees.Lutins[0].anglebase
    this.params.scratchProgDonnees.Lutins[0].StyloPose = false
    if (!b) this.params.scratchProgDonnees.Lutins[0].img = dessinleveImg
    this.traceSortieBase({ sortie: this.lesdivs.sortieEl })
  }

  scratchSontAligne (pa, pb, pc) {
    this.params.mtgApplecteur.setPointPosition(this.params.svgId, '#clonedroite1p1', pa.x, pa.y, true)
    this.params.mtgApplecteur.setPointPosition(this.params.svgId, '#clonedroite1p2', pc.x, pc.y, true)
    this.params.mtgApplecteur.setPointPosition(this.params.svgId, '#PS', pb.x, pb.y, true)
    this.params.mtgApplecteur.updateFigure(this.params.svgId)
    const rez = this.params.mtgApplecteur.valueOf(this.params.svgId, 'distptdroite')
    return rez < 1
  }

  scratchTrace (a, b, c, d) {
    this.params.scratchProgDonnees.lignetracees.push({
      x1: a,
      x2: a + c,
      y1: b,
      y2: b + d,
      coul: this.params.scratchProgDonnees.Lutins[0].StyloCol
    })
    if (this.params.cacheTest) return
    const svg = this.params.svg
    const ns = document.createElementNS('http://www.w3.org/2000/svg', 'line')
    ns.setAttribute('x1', a)
    ns.setAttribute('x2', a + c)
    ns.setAttribute('y1', b)
    ns.setAttribute('y2', b + d)
    svg.appendChild(ns)
    ns.setAttribute('style', 'stroke:' + this.params.scratchProgDonnees.Lutins[0].StyloCol + ';stroke-width:3')
  }

  scratchMain () {
    if (this.params.cacheTest) return
    j3pDetruit(this.params.maMain)
    const svg = this.params.svg
    this.params.maMain = document.createElementNS('http://www.w3.org/2000/svg', 'image')
    this.params.maMain.setAttribute('x', this.params.scratchProgDonnees.Lutins[0].cox)
    this.params.maMain.setAttribute('y', this.params.scratchProgDonnees.Lutins[0].coy)
    this.params.maMain.setAttribute('width', this.params.scratchProgDonnees.Lutins[0].larg)
    this.params.maMain.setAttribute('height', this.params.scratchProgDonnees.Lutins[0].haut)
    this.params.maMain.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.params.scratchProgDonnees.Lutins[0].img)
    this.params.maMain.setAttribute('transform', 'rotate(' + (this.params.scratchProgDonnees.Lutins[0].angle) + ',' + (this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg - 1) + ',' + (this.params.scratchProgDonnees.Lutins[0].coy + 34) + ')')
    svg.appendChild(this.params.maMain)
  }

  scratchPetitScratch () {
    if (this.params.cacheTest) return
    j3pDetruit(this.params.maMain)
    const svg = this.params.svg
    this.params.maMain = document.createElementNS('http://www.w3.org/2000/svg', 'image')
    this.params.maMain.setAttribute('x', this.params.scratchProgDonnees.Lutins[0].cox)
    this.params.maMain.setAttribute('y', this.params.scratchProgDonnees.Lutins[0].coy)
    this.params.maMain.setAttribute('width', this.params.scratchProgDonnees.Lutins[0].larg)
    this.params.maMain.setAttribute('height', this.params.scratchProgDonnees.Lutins[0].haut)
    this.params.maMain.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.params.scratchProgDonnees.Lutins[0].img)
    this.params.maMain.setAttribute('transform', 'rotate(' + (this.params.scratchProgDonnees.Lutins[0].angle) + ',' + (this.params.scratchProgDonnees.Lutins[0].cox + 25 + ',' + (this.params.scratchProgDonnees.Lutins[0].coy + 25) + ')'))
    svg.appendChild(this.params.maMain)
  }

  scratchDrawLine (x, y, z, u, wi, col, ctx) {
    ctx.beginPath()
    ctx.lineWidth = wi
    ctx.moveTo(j3pArrondi(x, 5), j3pArrondi(y, 5))
    ctx.lineTo(j3pArrondi(z, 5), j3pArrondi(u, 5))
    ctx.stroke()
  }

  scratchMove (cb) {
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      this.scratchTrace(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg - 1, this.params.scratchProgDonnees.Lutins[0].coy + 34, cb * Math.cos((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180), cb * Math.sin((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180))
    }
    this.params.scratchProgDonnees.Lutins[0].cox += cb * Math.cos((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180)
    this.params.scratchProgDonnees.Lutins[0].coy += cb * Math.sin((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180)
    this.scratchMain()
  }

  scratchTurnRight (cb) {
    this.params.scratchProgDonnees.Lutins[0].angle += parseInt(cb)
    this.scratchMain()
  }

  scratchTurnLeft (cb) {
    this.params.scratchProgDonnees.Lutins[0].angle -= parseInt(cb)
    this.scratchMain()
  }

  vireGuillemets (m) {
    m = m + ''
    // chaine vide => on la retourne
    if (!m.length) return m
    // encadrée par des ' => on les vire
    if (m[0] === '\'' && m[m.length - 1] === '\'') return m.substring(1, m.length - 1)
    // on retourne tel quel
    return m
  }

  /**
   * affecte une valeur a une variable de this.params.scratchProgDonnees.variables
   * @param val string
   * @param name string
   * @returns {string}
   */
  setVal (val, name) {
    let i // on en a besoin en sortie de boucle
    for (i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      if (this.params.scratchProgDonnees.variables[i].name === name) {
        break
      }
    }
    if (i === this.params.scratchProgDonnees.variables.length) {
      if (this.params.osef) return
      this.params.scratchProgDonnees.variables.push({ name, val })
    }
    this.params.scratchProgDonnees.Valeursutilisees.push(val)
    this.params.scratchProgDonnees.variables[i].val = val
    return 'ordre'
  }

  ajVal (b, x) {
    let i // on en a besoin en sortie de boucle
    for (i = 0; i < this.params.scratchProgDonnees.variables.length; i++) {
      if (this.params.scratchProgDonnees.variables[i].name === x) {
        break
      }
    }
    if (i === this.params.scratchProgDonnees.variables.length) {
      this.params.scratchProgDonnees.variables.push({ name: x, val: 0 })
    }
    this.params.scratchProgDonnees.variables[i].val = parseFloat(this.params.scratchProgDonnees.variables[i].val) + parseFloat(b)
    return 'ordre'
  }

  scratchListeDesArcs (untabdeprog) {
    let prz = []
    for (let j = 0; j < untabdeprog.length; j++) {
      for (let i = 0; i < untabdeprog[j].length; i++) {
        if (!untabdeprog[j][i]) continue
        if (untabdeprog[j][i].nom === 'arc1PointPasse') {
          prz.push(untabdeprog[j][i])
        }
        if (untabdeprog[j][i].type === 'boucle') {
          prz = prz.concat(this.scratchListeDesArcs(untabdeprog[j][i].boucle))
        }
        if (untabdeprog[j][i].type === 'si') {
          prz = prz.concat(this.scratchListeDesArcs(untabdeprog[j][i].sioui))
          prz = prz.concat(this.scratchListeDesArcs(untabdeprog[j][i].sinon))
        }
      }
    }
    return prz
  }

  videInputs () {
    for (let i = 0; i < 7; i++) j3pEmpty(this.params.divzoneTxtCre[0][i])
  }

  scratchModfiNbCRea () {
    const deco = {
      style: {
        border: '1px solid blue',
        background: 'cornflowerblue',
        borderRadius: '10px',
        color: 'white'
      }
    }
    const acons = this.params.zoneTxtCre.map(nomPris => nomPris.value)
    acons.push('text')
    switch (this.params.nbVarCrea) {
      case 0:
        this.videInputs()
        this.params.zoneTxtCre = []
        this.params.zoneTxtCre[0] = j3pAddElt(this.params.divzoneTxtCre[0][0], 'input', '', {
          value: acons[0],
          maxLength: 30,
          minLength: 1,
          size: 40
        })
        break
      case 1:
        this.videInputs()
        this.params.zoneTxtCre = []
        this.params.zoneTxtCre[0] = j3pAddElt(this.params.divzoneTxtCre[0][0], 'input', '', {
          value: acons[0],
          maxLength: 25,
          minLength: 1,
          size: 25
        })
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][1], 'span', '&nbsp;X&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        this.params.zoneTxtCre[1] = j3pAddElt(this.params.divzoneTxtCre[0][2], 'input', '', {
          value: acons[1],
          maxLength: 25,
          minLength: 0,
          size: 25
        })
        break
      case 2:
        this.videInputs()
        this.params.zoneTxtCre = []
        this.params.zoneTxtCre[0] = j3pAddElt(this.params.divzoneTxtCre[0][0], 'input', '', {
          value: acons[0],
          maxLength: 20,
          minLength: 1,
          size: 20
        })
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][1], 'span', '&nbsp;X&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        this.params.zoneTxtCre[1] = j3pAddElt(this.params.divzoneTxtCre[0][2], 'input', '', {
          value: acons[1],
          maxLength: 20,
          minLength: 0,
          size: 20
        })
        j3pAddContent(this.params.divzoneTxtCre[0][3], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][3], 'span', '&nbsp;Y&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][3], '&nbsp;')
        this.params.zoneTxtCre[2] = j3pAddElt(this.params.divzoneTxtCre[0][4], 'input', '', {
          value: acons[2],
          maxLength: 20,
          minLength: 0,
          size: 20
        })
        break
      case 3:
        this.videInputs()
        this.params.zoneTxtCre = []
        this.params.zoneTxtCre[0] = j3pAddElt(this.params.divzoneTxtCre[0][0], 'input', '', {
          value: acons[0],
          maxLength: 18,
          minLength: 1,
          size: 18
        })
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][1], 'span', '&nbsp;X&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][1], '&nbsp;')
        this.params.zoneTxtCre[1] = j3pAddElt(this.params.divzoneTxtCre[0][2], 'input', '', {
          value: acons[1],
          maxLength: 18,
          minLength: 0,
          size: 18
        })
        j3pAddContent(this.params.divzoneTxtCre[0][3], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][3], 'span', '&nbsp;Y&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][3], '&nbsp;')
        this.params.zoneTxtCre[2] = j3pAddElt(this.params.divzoneTxtCre[0][4], 'input', '', {
          value: acons[2],
          maxLength: 18,
          minLength: 0,
          size: 18
        })
        j3pAddContent(this.params.divzoneTxtCre[0][5], '&nbsp;')
        j3pAddElt(this.params.divzoneTxtCre[0][5], 'span', '&nbsp;Z&nbsp;', deco)
        j3pAddContent(this.params.divzoneTxtCre[0][5], '&nbsp;')
        this.params.zoneTxtCre[3] = j3pAddElt(this.params.divzoneTxtCre[0][6], 'input', '', {
          value: acons[3],
          maxLength: 18,
          minLength: 0,
          size: 18
        })
        break
    }
  }

  scratchAnnuleCrea () {
    j3pDetruit(this.params.ModaleCrea)
    j3pDetruit('j3pmasque')
  }

  scratchValideCrea () {
    const newid = this.params.zoneNomBloc.value
    const newNewid = this.params.zoneNomBloc.value + 'b'

    if (this.params.lesBlocPerso.indexOf(newid) !== -1) {
      this.params.zoneNomBloc.style.backgroundColor = '#ff0000'
      setTimeout(() => {
        this.params.zoneNomBloc.style.backgroundColor = '#ffffff'
      }, 100)
      return
    }
    this.params.lesBlocPerso.push(newid)
    let txt = ''
    let yaicone = false
    const obdep = {
      message0: txt,
      args0: [],
      category: this.Blockly.Categories.motion,
      colour: Math.round(this.params.valColor),
      extensions: ['shape_statement']
    }
    const value = []
    let funX = () => {
      return ''
    }
    let funY = () => {
      return ''
    }
    let funZ = () => {
      return ''
    }
    if (this.params.nbVarCrea > 0) {
      obdep.args0.push({ type: 'input_value', name: 'X', default: '' })
      funX = (block) => {
        return this.Blockly.JavaScript.valueToCode(block, 'X', this.Blockly.JavaScript.ORDER_ATOMIC).replace(/'/g, '')
      }
      value.push({ nom: 'X', contenu: { valeur: ' ' } })
    }
    if (this.params.nbVarCrea > 1) {
      obdep.args0.push({ type: 'input_value', name: 'Y', default: '' })
      funY = (block) => {
        return this.Blockly.JavaScript.valueToCode(block, 'Y', this.Blockly.JavaScript.ORDER_ATOMIC).replace(/'/g, '')
      }
      value.push({ nom: 'Y', contenu: { valeur: ' ' } })
    }
    if (this.params.nbVarCrea > 2) {
      obdep.args0.push({ type: 'input_value', name: 'Z', default: '' })
      funZ = (block) => {
        return this.Blockly.JavaScript.valueToCode(block, 'Z', this.Blockly.JavaScript.ORDER_ATOMIC).replace(/'/g, '')
      }
      value.push({ nom: 'Z', contenu: { valeur: ' ' } })
    }
    for (let i = 0; i < this.params.zoneTxtCre.length; i++) {
      if (i === 1) txt += '%1'
      if (i === 2) txt += '%2'
      if (i === 3) txt += '%3'
      txt += this.params.zoneTxtCre[i].value
    }

    obdep.message0 = txt

    if (this.params.creaIconeChoice) {
      txt = txt.replace('%3', '%5')
      txt = txt.replace('%2', '%4')
      txt = txt.replace('%1', '%3')
      obdep.message0 = '%1 %2 ' + txt
      obdep.args0.splice(0, 0, { type: 'field_vertical_separator' })
      obdep.args0.splice(0, 0, {
        type: 'field_image',
        src: this.params.creaIconeChoice,
        width: 30,
        height: 30
      })
      yaicone = true
    }

    // eslint-disable-next-line no-lone-blocks
    {
      this.Blockly.Blocks[newid] = {
        init: function () {
          this.jsonInit({
            type: 'make',
            message0: 'Définir ' + newid,
            inputsInline: false,
            nextStatement: null,
            colour: 330,
            tooltip: '',
            helpUrl: ''
          })
        }
      }
      this.Blockly.JavaScript[newid] = function () {
        return {
          type: 'make',
          eventtype: 'make',
          nom: newid
        }
      }
    }
    // eslint-disable-next-line no-lone-blocks
    {
      this.Blockly.Blocks[newNewid] = {
        init: function () {
          this.jsonInit(obdep)
        }
      }
      this.Blockly.JavaScript[newNewid] = function (block) {
        const retValx = function () {
          return funX(block)
        }
        const retvaly = function () {
          return funY(block)
        }
        const retvalz = function () {
          return funZ(block)
        }
        return { type: 'Mb', nom: newid + 'b', valx: retValx(), valy: retvaly(), valz: retvalz() }
      }
    }

    if (!this.params.isEditing) {
      if (this.params.scratchProgDonnees.menu[this.params.scratchProgDonnees.menu.length - 2].nom !== 'MES BLOCS') {
        this.params.scratchProgDonnees.menu.splice(this.params.scratchProgDonnees.menu.length - 1, 0, {
          type: 'catégorie',
          nom: 'MES BLOCS',
          coul1: '#D14C7D',
          coul2: '#B8436E',
          contenu: []
        })
      }
      this.params.scratchProgDonnees.menu[this.params.scratchProgDonnees.menu.length - 2].contenu.push({ type: newid + 'b', value })
      const x2 = this.scratchCreeMenu(this.params.scratchProgDonnees.menu, true)
      const xmlDoc2 = $.parseXML(x2)
      let xml2 = xmlDoc2.firstChild
      while (xml2.nodeType !== 1) {
        xml2 = xml2.nextSibling
      }
      this.params.scratchProgDonnees.workspace.updateToolbox(xml2)
      j3pDetruit(this.params.ModaleCrea, 'j3pmasque')
      j3pDetruit('j3pmasque')
      this.scratchMenuPerso()
    }

    let aaj = '<xml xmlns="http://www.w3.org/1999/xhtml"><variables>'
    let aajfin = '</xml>'
    //  uu = [{ n: 'X', id: 'aaaaaa' }, { n: 'Y', id: 'bbbbbb' }, { n: 'Z', id: 'cccccc' }]
    // for (let i = 0; i < this.params.nbVarCrea; i++) {
    //  this.params.scratchProgDonnees.LesVariablesCo.push({ name: uu[i].n, id: uu[i].id, val: 'vide' })
    //  aaj += '<variable type="" id="' + uu[i].id + '" islocal="false" iscloud="false">' + uu[i].n + '</variable>'
    // }
    aaj += '</variables>'
    aaj += '<block type="' + newid + '" id="?q:ukn@XHskl#oIIl+"  x="20" y="30" deletable = "false"><next>'
    aajfin = '</next></block>' + aajfin

    this.params.listeDef[newid] = {
      nom: newid,
      nbVar: this.params.nbVarCrea,
      xmlText: aaj + aajfin,
      txt,
      yaicone,
      def: [],
      value,
      colour: this.params.valColor
    }

    if (yaicone) this.params.listeDef[newid].icone = this.params.creaIconeChoice
    this.finishCreaPerso()
  }

  faisPlus () {
    this.params.nbVarCrea = Math.min(3, this.params.nbVarCrea + 1)
    j3pEmpty(this.params.nbVarCreaDiv)
    j3pAddContent(this.params.nbVarCreaDiv, String(this.params.nbVarCrea))
    this.scratchModfiNbCRea()
  }

  faisMoins () {
    this.params.nbVarCrea = Math.max(0, this.params.nbVarCrea - 1)
    j3pEmpty(this.params.nbVarCreaDiv)
    j3pAddContent(this.params.nbVarCreaDiv, String(this.params.nbVarCrea))
    this.scratchModfiNbCRea()
  }

  HSVtoRGB (h, s, v) {
    let r, g, b
    const i = Math.floor(h * 6)
    const f = h * 6 - i
    const p = v * (1 - s)
    const q = v * (1 - f * s)
    const t = v * (1 - (1 - f) * s)
    switch (i % 6) {
      case 0:
        r = v
        g = t
        b = p
        break
      case 1:
        r = q
        g = v
        b = p
        break
      case 2:
        r = p
        g = v
        b = t
        break
      case 3:
        r = p
        g = q
        b = v
        break
      case 4:
        r = t
        g = p
        b = v
        break
      case 5:
        r = v
        g = p
        b = q
        break
    }
    return {
      r: Math.round(r * 255),
      g: Math.round(g * 255),
      b: Math.round(b * 255)
    }
  }

  scratchColorCrea (val) {
    const jj = this.HSVtoRGB(val / 360, 0.9, 0.9)
    this.params.coolCrea.style.background = 'rgb(' + jj.r + ',' + jj.g + ',' + jj.b + ')'
    this.params.valColor = val
  }

  scratchChoixIcinePerso () {
    this.params.tabIcineCrea.style.display = ''
    this.params.tabIcineCrea2.style.display = 'none'
    this.params.listeCrea.select(0)
  }

  scratchPropIcineCrea () {
    if (this.params.listeCrea.reponse === 'Choisir') return
    let imagesList
    switch (this.params.listeCrea.reponse) {
      case 'Mathgraph':
        imagesList = [segmentImg, cercleptImg, cerclerayImg, diametreImg, demidroiteptImg, droiteptImg, droiteparaImg, droiteperpImg, mediatriceImg, arcmilIMG, anglImg, pointlibreImg, pointinterImg, triangleImg, effacerImg]
        break
      case 'fusée':
        break
      case 'scratch':
        imagesList = [chatImg, croixImg, dessinImg, dessinleveImg]
        break
      case 'bras':
        imagesList = [bras2Img, fourcheImg, num1Img, num2Img, num3Img, num4Img]
        break
    }
    j3pEmpty(this.params.creaDivClic)
    this.params.zonrClicCrea = new Zoneclick(this.params.creaDivClic, imagesList, {
      afaire: this.scratchAchoisiIconeCrea.bind(this),
      dispo: 'fixe',
      image: true,
      ligne: 3,
      colonne: 5
    })
    for (let i = 0; i < imagesList.length; i++) {
      const img = document.createElement('img')
      img.src = imagesList[i]
      img.stretch = true
      img.style.width = '30px'
      img.style.height = '35px'
      this.params.zonrClicCrea.jegarde[i][3].appendChild(img)
    }
  }

  scratchAchoisiIconeCrea (n) {
    j3pEmpty(this.params.dd1)
    this.params.zonrClicCrea.disable()
    j3pEmpty(this.params.creaDivClic)
    this.params.creaIconeChoice = n
    this.params.tabIcineCrea.style.display = 'none'
    this.params.tabIcineCrea2.style.display = ''

    const img = document.createElement('img')
    img.src = n
    img.stretch = true
    img.style.width = '30px'
    img.style.height = '35px'
    this.params.dd1.appendChild(img)
    j3pAjouteBouton(this.params.dd2, this.scratchSupIcinePerso.bind(this), { value: 'Supprimer' })
  }

  scratchSupIcinePerso () {
    this.params.creaIconeChoice = null
    j3pEmpty(this.params.dd1)
    j3pEmpty(this.params.dd2)
    j3pAjouteBouton(this.params.dd1, this.scratchChoixIcinePerso.bind(this), { value: 'Ajouter' })
  }

  scratchTourneMoha (cb) {
    this.params.scratchProgDonnees.Lutins[0].angle = cb - 90
    this.scratchMainMoha()
  }

  scratchMoveMoha (cb) {
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      this.scratchTraceMoha(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2, this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2, 3 / 5 * cb * Math.cos((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180), 3 / 5 * cb * Math.sin((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180))
    }
    this.params.scratchProgDonnees.Lutins[0].cox += 3 / 5 * cb * Math.cos((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180)
    this.params.scratchProgDonnees.Lutins[0].coy += 3 / 5 * cb * Math.sin((this.params.scratchProgDonnees.Lutins[0].angle) * Math.PI / 180)
    this.scratchMainMoha()
  }

  scratchAJoutexMoha (cb) {
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      this.scratchTraceMoha(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2, this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2, 3 / 5 * cb, 0)
    }
    this.params.scratchProgDonnees.Lutins[0].cox += 3 / 5 * cb
    this.scratchMainMoha()
  }

  scratchAJouteyMoha (cb) {
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      this.scratchTraceMoha(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2, this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2, 0, -3 / 5 * cb)
    }
    this.params.scratchProgDonnees.Lutins[0].coy -= 3 / 5 * cb
    this.scratchMainMoha()
  }

  scratchMetsxMoha (cb) {
    const newx = 3 / 5 * cb + 90
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      const dec = newx - this.params.scratchProgDonnees.Lutins[0].cox
      this.scratchTraceMoha(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2, this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2, dec, 0)
    }
    this.params.scratchProgDonnees.Lutins[0].cox = newx
    this.scratchMainMoha()
  }

  scratchMetsyMoha (cb) {
    const newy = 100 - 3 / 5 * cb
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      const dec = newy - this.params.scratchProgDonnees.Lutins[0].coy
      this.scratchTraceMoha(this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2, this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2, 0, dec)
    }
    this.params.scratchProgDonnees.Lutins[0].coy = newy
    this.scratchMainMoha()
  }

  scratchAllerAMoha (x, y) {
    const nx = 3 / 5 * x + 100
    const ny = 100 - 3 / 5 * y
    if (this.params.scratchProgDonnees.Lutins[0].StyloPose) {
      const oldx = this.params.scratchProgDonnees.Lutins[0].cox
      const oldy = this.params.scratchProgDonnees.Lutins[0].coy
      this.scratchTraceMoha(oldx + this.params.scratchProgDonnees.Lutins[0].larg / 2, oldy + this.params.scratchProgDonnees.Lutins[0].haut / 2, nx - oldx, ny - oldy)
    }
    this.params.scratchProgDonnees.Lutins[0].cox = nx
    this.params.scratchProgDonnees.Lutins[0].coy = ny
    this.scratchMainMoha()
  }

  scratchMoveMohaDroite (cb) {
    this.params.scratchProgDonnees.Lutins[0].angle += parseInt(cb)
    this.scratchMainMoha()
  }

  scratchMoveMohaGauche (cb) {
    this.params.scratchProgDonnees.Lutins[0].angle -= parseInt(cb)
    this.scratchMainMoha()
  }

  scratchMainMoha () {
    if (this.params.cacheTest) return
    this.params.maMain.setAttribute('x', this.params.scratchProgDonnees.Lutins[0].cox)
    this.params.maMain.setAttribute('y', this.params.scratchProgDonnees.Lutins[0].coy)
    this.params.maMain.setAttribute('width', this.params.scratchProgDonnees.Lutins[0].larg)
    this.params.maMain.setAttribute('height', this.params.scratchProgDonnees.Lutins[0].haut)
    if (this.params.scratchProgDonnees.Lutins[0].img.indexOf('chat') !== -1) {
      this.params.maMain.setAttributeNS('http://www.w3.org/1999/xlink', 'href', chatImg)
    } else {
      if (!this.params.scratchProgDonnees.Lutins[0].StyloPose) {
        this.params.maMain.setAttributeNS('http://www.w3.org/1999/xlink', 'href', dessinleveImg2)
      } else {
        this.params.maMain.setAttributeNS('http://www.w3.org/1999/xlink', 'href', dessinImg2)
      }
    }
    this.params.maMain.setAttribute('transform', 'rotate(' + (this.params.scratchProgDonnees.Lutins[0].angle) + ',' + (this.params.scratchProgDonnees.Lutins[0].cox + this.params.scratchProgDonnees.Lutins[0].larg / 2) + ',' + (this.params.scratchProgDonnees.Lutins[0].coy + this.params.scratchProgDonnees.Lutins[0].haut / 2) + ')')
  }

  scratchTraceMoha (a, b, c, d) {
    const svg = this.params.svg
    const ns = document.createElementNS('http://www.w3.org/2000/svg', 'line')
    ns.setAttribute('x1', a)
    ns.setAttribute('x2', a + c)
    ns.setAttribute('y1', b)
    ns.setAttribute('y2', b + d)
    svg.appendChild(ns)
    ns.setAttribute('style', 'stroke:' + this.params.scratchProgDonnees.Lutins[0].StyloCol + ';stroke-width:3')
    this.params.scratchProgDonnees.lignetracees.push({
      x1: a,
      x2: a + c,
      y1: b,
      y2: b + d,
      coul: this.params.scratchProgDonnees.Lutins[0].StyloCol
    })
  }

  scratchReplaceDemoVar () {

  }

  dist (p1, p2) {
    return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))
  }

  /**
   * Appelé par une méthode définie dans ScratchProg
   * @param isMoha
   */
  mohaRegistreTout (isMoha) {
    if (!isMoha) {
      this.params.Aconserve.ok = false
      this.params.Aconserve.el = {
        lutin: j3pClone(this.params.scratchProgDonnees.Lutins),
        said: j3pClone(this.params.scratchProgDonnees.said),
        lignes: j3pClone(this.params.scratchProgDonnees.lignetracees),
        variables: j3pClone(this.params.scratchProgDonnees.variables),
        valGetDon: j3pClone(this.params.scratchProgDonnees.valGetDon),
        tabCase: j3pClone(this.params.scratchProgDonnees.carteCases)
      }
    } else {
      this.params.Aconserve.moha = {
        lutin: j3pClone(this.params.scratchProgDonnees.Lutins),
        said: j3pClone(this.params.scratchProgDonnees.said),
        lignes: j3pClone(this.params.scratchProgDonnees.lignetracees),
        variables: j3pClone(this.params.scratchProgDonnees.variables),
        tabCase: j3pClone(this.params.scratchProgDonnees.carteCases)
      }
      this.params.Aconserve.ok = true
    }
  }

  remetApresDemo (b) {
    if (this.params.DemoText) this.params.DemoText.style.display = ''
    if (this.Bouton.Rinit) {
      this.Bouton.Rinit.style.display = ''
    } else {
      this.lesdivs.drapeau.style.display = ''
      if (this.lesdivs.sortie4) this.lesdivs.sortie4.style.display = ''
      if (this.lesdivs.drapeauok && (this.params.NbEssaiIllimite)) {
        this.lesdivs.drapeauok.style.display = ''
      }
    }
    if (!b) this.params.reinitDemo.style.display = ''
    this.params.scratchProgDonnees.runable = true
    this.params.foraz = true
    j3pDetruit('inputdivtot') // pas de pb si ça existe pas
    j3pDetruit(this.lesdivs.avire)
    this.lesdivs.stop.style.display = 'none'
    this.lesdivs.PASAPAS.style.display = ''
    this.lesdivs.PASAPASBOOT.style.display = 'none'
    // mettre un display vide les rends visible (block ou inline suivant ce que c’est)
    this.params.scratchProgDonnees.Lutins[0].cox = this.params.scratchProgDonnees.Lutins[0].coxbase
    this.params.scratchProgDonnees.Lutins[0].coy = this.params.scratchProgDonnees.Lutins[0].coybase
    this.params.scratchProgDonnees.Lutins[0].angle = this.params.scratchProgDonnees.Lutins[0].anglebase
    this.params.scratchProgDonnees.Lutins[0].hauteur = this.params.scratchProgDonnees.Lutins[0].hauteurbase
    this.params.scratchProgDonnees.Lutins[0].StyloPose = false
    this.params.scratchProgDonnees.progencours = []
  }

  reinitDemo = () => {
    this.params.scratchProgDonnees.Lutins[0].cox = this.params.scratchProgDonnees.Lutins[0].coxbase
    this.params.scratchProgDonnees.Lutins[0].coy = this.params.scratchProgDonnees.Lutins[0].coybase
    this.params.scratchProgDonnees.Lutins[0].angle = this.params.scratchProgDonnees.Lutins[0].anglebase
    this.params.scratchProgDonnees.Lutins[0].hauteur = this.params.scratchProgDonnees.Lutins[0].hauteurbase
    this.params.scratchProgDonnees.Lutins[0].StyloPose = false
    if (this.params.yabras) {
      this.params.scratchProgDonnees.donneesbras.tapis1 = this.params.scratchProgDonnees.donneesbrasbase.tapis1
      this.params.scratchProgDonnees.donneesbras.tapis2 = this.params.scratchProgDonnees.donneesbrasbase.tapis2
      this.params.scratchProgDonnees.donneesbras.tapis3 = this.params.scratchProgDonnees.donneesbrasbase.tapis3
      this.params.scratchProgDonnees.donneesbras.tapis4 = this.params.scratchProgDonnees.donneesbrasbase.tapis4
      this.params.scratchProgDonnees.donneesbras.bras = this.params.scratchProgDonnees.donneesbrasbase.bras
    }
    this.scratchResetFlag()
    this.traceSortieBaseDemo()
    this.cahceBoutonReinitDemo()
    this.montreDrapeauxDemo()
    this.fautRinitDemo = false
  }
}

export default Scratch