legacy/outils/etiquette/Etiquettes.js

import { j3pAddContent, j3pAddElt, j3pDetruit, j3pDiv, j3pElement, j3pGetNewId, j3pSpan, j3pStyle } from 'src/legacy/core/functions'
import { j3pCreeSVG } from 'src/legacy/core/functionsSvg'
import ecouteImg from 'src/legacy/sections/college/etikMake/ecoute.png'

import { addDefaultTable } from 'src/legacy/themes/table'
import { getJ3pConteneur } from 'src/lib/core/domHelpers'
import { j3pAffiche } from 'src/lib/mathquill/functions'
import BulleAide from 'src/legacy/outils/bulleAide/BulleAide'

import './etiquette.css'

/**
 * À décrire
 * @param {string} iddivconteneur
 * @param {string} manouvellediv
 * @param {string[]} montab
 * @param {Object} parametres
 * @param {boolean[]} parametres.mathquill à décrire
 * @param {function} [parametres.afaire] à décrire
 * @param {number} [parametres.width=0] à décrire
 * @param {boolean} [parametres.centre] à décrire
 * @param {boolean} [parametres.cacheAide] à décrire
 * @param {boolean} [parametres.reutilisable] à décrire
 * @param {string} [parametres.dispo=carre] à décrire (doit valoir fixe|lignes|colonnes|carre)
 * @param {number} [parametres.colonne] à décrire
 * @param {number} [parametres.ligne] à décrire
 * @param {string} [parametres.couleur=#C0C0C0] à décrire
 * @param {number} [parametres.limite=15] à décrire
 * @param {objet} [parametres.mtgAppLecteur] mtgAppLEcteur ( si yen a besoin )
 * @param {objet} [parametres.mtgAppLecteur] codePlace ( Si y’a besoin de distinguer les places entre elles )
 * @constructor
 */
function Etiquettes (iddivconteneur, manouvellediv, montab, parametres) {
  this.cooplace = []
  this.montab = montab
  this.ongoingTouches = []
  this.disabled = false
  this.direct = parametres.direct

  /// si on veut déclencher une fonction sur un changement
  // mettre etiq pour recuperer le num de l’étiquette
  //       contenu pour recupere le contenu
  //       place pour recup le num de la place
  if (parametres.afaire === undefined) {
    this.afaire = function () {}
  } else {
    if (typeof parametres.afaire !== 'function') throw Error('afaire doit être une fonction')
    this.afaire = parametres.afaire
  }
  this.mtgAppLecteur = parametres.mtgAppLecteur ?? undefined
  this.codePlace = parametres.codePlace ?? undefined
  if (typeof parametres.width === 'number') {
    this.tailledonnee = parametres.width
  } else {
    this.tailledonnee = 0
  }
  // pour des trucs centré
  const uncentre = parametres.centre
  // FIXME un booléen devrait pas admettre plusieurs valeurs…
  this.centre = ((uncentre === true) || (uncentre === 'true') || (uncentre === 'oui'))

  // pour des etiquettes a usage unique
  // @todo mettre le meme nom pour le paramètre et la propriété de l’objet (choisir entre reutilisable et unique)
  const unique = parametres.reutilisable
  // FIXME un booléen devrait pas admettre plusieurs valeurs…
  this.unique = ((unique === false) || (unique === 'false') || (unique === 'non'))
  /// dispo des etiquettes
  const dispo = parametres.dispo
  const nbcol = parametres.colonne
  const nbligne = parametres.ligne
  let ya = false
  if (Number.isInteger(nbcol)) {
    this.col = nbcol
    ya = true
  } else { this.col = 0 }
  if (Number.isInteger(nbligne)) {
    this.lig = nbligne
    ya = true
  } else {
    this.lig = 0
  }
  // @todo réduire le nb de cas folkloriques ;-), à faire au moment de l’affectation avec par ex
  // this.dispo = ['fixe', 'lignes', 'colonnes', 'carre'].includes(parametres.dispo) ? parametres.dispo : 'carre'
  if ((dispo === 'Fixe') || (dispo === 'Fixé') || (dispo === 'fixe') || (dispo === 'fixé')) { if (ya) { this.dispo = 'fixe' } }
  if ((dispo === 'ligne') || (dispo === 'lignes') || (dispo === 'line') || (dispo === 'lines')) { this.dispo = 'lignes' }
  if ((dispo === 'colonnes') || (dispo === 'colonne') || (dispo === 'row') || (dispo === 'rows')) { this.dispo = 'colonnes' }
  if ((this.dispo !== 'lignes') && (this.dispo !== 'colonnes') && (this.dispo !== 'fixe')) { this.dispo = 'carre' }
  this.couleur = parametres.couleur || '#C0C0C0'
  this.limite = parametres.limite || 15
  /// /fin des parametres

  this.iddivconteneur = iddivconteneur
  if (typeof iddivconteneur === 'string') this.iddivconteneur = j3pElement(iddivconteneur)

  try {
    this.parent = getJ3pConteneur(this.iddivconteneur)
  } catch (e) {
    this.parent = document.body
  }
  try {
    this.mepact = getJ3pConteneur(this.parent)
  } catch (e) {
    this.mepact = document.body
  }
  if (!this.parent) {
    this.parent = document.body
  } else if (this.parent.URL) this.parent = document.body
  if (!this.mepact) {
    this.mepact = document.body
  } else if (this.mepact.URL) this.mepact = document.body

  const yui = addDefaultTable(iddivconteneur, 1, 3)
  yui[0][0].style.padding = '0px'
  yui[0][1].style.padding = '0px'
  yui[0][1].style.width = '5px'
  yui[0][2].style.padding = '0px'
  this.spanContenant = yui[0][0]
  if (!parametres.cacheAide) this.llaide = new BulleAide(yui[0][2], '1.Clique pour attraper. <br> 2.Clique pour déposer.', {})
  this.spanContenant.classList.add('zonelistetexte')
  this.spantexte = j3pSpan(this.spanContenant, {
    className: 'zonelistetexte'
  })
  this.nb = this.montab.length

  switch (this.dispo) {
    case 'lignes' :
      this.ligne = 1
      this.colonne = this.nb
      break
    case 'colonnes' :
      this.ligne = this.nb
      this.colonne = 1
      break
    case 'carre' :
      this.ligne = Math.floor(Math.sqrt(this.nb))
      this.colonne = Math.ceil(this.nb / this.ligne)
      break
    case 'fixe' :
      if (this.col !== 0) {
        this.colonne = this.col
        this.ligne = Math.ceil(this.nb / this.col)
      } else {
        this.ligne = this.lig
        this.colonne = Math.ceil(this.nb / this.lig)
      }
  }

  // crea tab des bases etiquettes
  this.whEtkl = []
  this.tabEtikDou = addDefaultTable(this.spantexte, this.ligne, this.colonne * 2)
  for (let i = 0; i < this.tabEtikDou.length; i++) {
    for (let j = 0; j < this.tabEtikDou[i].length - 1; j += 2) {
      this.tabEtikDou[i][j + 1].style.width = '10px'
    }
  }
  this.listdivchoix = -1
  this.etiqok = []
  this.divARet = []

  for (let i = 0; i < montab.length; i++) {
    const io = Math.ceil((i + 1) / this.colonne) - 1
    const j = ((i + 1) - 1) % (this.colonne)
    let buf = this.montab[i]
    if (Array.isArray(buf)) {
      // en fait ca peut etre un tableau de tableau si y’a des accords a prévoir (s au pluriel)
      buf = buf[0]
    }
    for (let mm = 0; mm < this.montab.length; mm++) {
      const div2 = j3pDiv(this.tabEtikDou[io][j * 2])
      div2.setAttribute('class', 'etiquetteCache')
      let buf2 = this.montab[mm]
      if (Array.isArray(buf2)) {
        // en fait ca peut etre un tableau de tableau si y’a des accords a prévoir (s au pluriel)
        buf2 = buf2[0]
      }
      if (typeof buf2 === 'string') {
        j3pAffiche(div2, null, buf2)
      } else {
        const el = buf2
        switch (el.type) {
          case undefined:
          case 'mathgraph': {
            const ui = addDefaultTable(div2, 1, 1)[0][0]
            const newId = j3pGetNewId('mtgetih')
            const svg = j3pCreeSVG(ui, { id: newId, width: 37 + el.content.width * 340, height: 54 + el.content.height * 253 })
            this.mtgAppLecteur.addDoc(newId, el.content.txtFigure, true)
            svg.style.position = 'relative'
          }
            break
          case 'image': {
            const limm = j3pAddElt(div2, 'img', '', { src: el.content.txtFigure, width: el.content.width, height: el.content.height })
            limm.style.width = el.content.width + 'px'
            limm.style.height = el.content.height + 'px'
            limm.style.marginTop = '2px'
            limm.style.marginBottom = '2px'
          }
            break
          case 'son': {
            const ui2 = addDefaultTable(div2, 1, 1)[0][0]
            const limm = j3pAddElt(ui2, 'img', '', { src: ecouteImg, width: '20px', height: '20px' })
            limm.style.width = '20px'
            limm.style.height = '20px'
            j3pAddContent(ui2, 'n° ' + mm)
          }
        }
      }
    }
    const div = j3pDiv(this.tabEtikDou[io][j * 2])
    const divSon = j3pDiv(this.tabEtikDou[io][j * 2 + 1])
    this.whEtkl[i] = this.tabEtikDou[io][j * 2]
    div.setAttribute('class', 'etiquette2')
    this.etiqok[i] = true
    if (typeof buf === 'string') {
      const hjkl = j3pAffiche(div, null, buf)
      for (let uu = 0; uu < hjkl.mqList.length; uu++) {
        if (hjkl.mqList[uu] !== null) hjkl.mqList[uu].style.cursor = 'pointer'
      }
    }
    div.style.background = this.couleur
    div.addEventListener('click', (event) => this.create(i, event), false)
    this.divARet.push({ princ: div, divSon })
  }

  this.place2Listener = this.place2.bind(this)
  this.mepact.addEventListener('mousemove', this.place2Listener, false)
} // Etiquettes

Etiquettes.prototype.rac = function rac () {
  j3pDetruit(this.listdivchoix)
  const uni = this.listdivchoix.num
  this.listdivchoix = -1
  for (let i = 0; i < this.cooplace.length; i++) {
    if (this.cooplace[i][2].etiq === -1) {
      this.cooplace[i][2].contenu = this.montab[uni]
      this.cooplace[i][2].etiq = uni
      this.cooplace[i][2].ecris()
      if (this.unique) {
        this.whEtkl[uni].style.visibility = 'hidden'
        this.etiqok[uni] = false
      }
      return
    }
  }
  if (this.unique) {
    this.whEtkl[uni].style.visibility = undefined
    this.etiqok[uni] = true
  }
}

Etiquettes.prototype.create = function create (indexEtiquette, event) {
  if (this.disabled) return
  this.verifCoPla()
  if (!this.etiqok[indexEtiquette]) return
  let buf = this.montab[indexEtiquette]
  if (Array.isArray(buf)) { buf = buf[0] }
  this.listdivchoix = j3pDiv(this.mepact)
  this.listdivchoix.num = indexEtiquette
  for (let mm = 0; mm < this.montab.length; mm++) {
    const div2 = j3pDiv(this.listdivchoix)
    div2.setAttribute('class', 'etiquetteCache')
    div2.style.fontSize = '20px'
    let buf2 = this.montab[mm]
    if (Array.isArray(buf2)) {
      // en fait ca peut etre un tableau de tableau si y’a des accords a prévoir (s au pluriel)
      buf2 = buf2[0]
    }
    if (typeof buf2 === 'string') {
      j3pAffiche(div2, null, buf2)
    } else {
      const el = buf2
      switch (el.type) {
        case undefined:
        case 'mathgraph': {
          const ui = addDefaultTable(div2, 1, 1)[0][0]
          const newId = j3pGetNewId('mtgetih')
          const svg = j3pCreeSVG(ui, { id: newId, width: 37 + el.content.width * 340, height: 54 + el.content.height * 253 })
          this.mtgAppLecteur.addDoc(newId, el.content.txtFigure, true)
          svg.style.position = 'relative'
        }
          break
        case 'image': {
          const limm = j3pAddElt(div2, 'img', '', { src: el.content.txtFigure, width: el.content.width, height: el.content.height })
          limm.style.width = el.content.width + 'px'
          limm.style.height = el.content.height + 'px'
          limm.style.marginTop = '2px'
          limm.style.marginBottom = '2px'
        }
          break
        case 'son': {
          const ui2 = addDefaultTable(div2, 1, 1)[0][0]
          const limm = j3pAddElt(ui2, 'img', '', { src: ecouteImg, width: '20px', height: '20px' })
          limm.style.width = '20px'
          limm.style.height = '20px'
          j3pAddContent(ui2, 'n° ' + mm)
        }
      }
    }
  }
  this.listdivchoix.setAttribute('class', 'etiquette3')
  this.listdivchoix.style.fontSize = '20px'
  this.listdivchoix.style.fontFamily = 'Roboto, "Noto Emoji", sans-serif'
  this.listdivchoix.style.background = this.couleur
  if (typeof buf === 'string') {
    const hjkl = j3pAffiche(this.listdivchoix, null, buf)
    for (let uu = 0; uu < hjkl.mqList.length; uu++) {
      if (hjkl.mqList[uu] !== null) hjkl.mqList[uu].style.cursor = 'pointer'
    }
  } else {
    const el = buf
    switch (el.type) {
      case undefined:
      case 'mathgraph': {
        const ui = addDefaultTable(this.listdivchoix, 1, 1)[0][0]
        const newId = j3pGetNewId('mtgetih')
        j3pCreeSVG(ui, { id: newId, width: 37 + el.content.width * 340, height: 54 + el.content.height * 253 })
        this.mtgAppLecteur.addDoc(newId, el.content.txtFigure, true)
        this.mtgAppLecteur.calculate(newId)
        this.mtgAppLecteur.display(newId)
      }
        break
      case 'image': {
        const limm = j3pAddElt(this.listdivchoix, 'img', '', { src: el.content.txtFigure, width: el.content.width, height: el.content.height })
        limm.style.width = el.content.width + 'px'
        limm.style.height = el.content.height + 'px'
        limm.style.marginTop = '2px'
        limm.style.marginBottom = '2px'
      }
        break
      case 'son': {
        const ui2 = addDefaultTable(this.listdivchoix, 1, 1)[0][0]
        const limm = j3pAddElt(ui2, 'img', '', { src: ecouteImg, width: '20px', height: '20px' })
        limm.style.width = '20px'
        limm.style.height = '20px'
        j3pAddContent(ui2, 'n° ' + indexEtiquette)
      }
    }
  }
  this.listdivchoix.bloque = false

  const { left, top } = this.mepact.getBoundingClientRect()
  this.listdivchoix.style.left = (event.clientX - left - this.listdivchoix.offsetWidth / 2) + 'px'
  this.listdivchoix.style.top = (event.clientY - top - this.listdivchoix.offsetHeight / 2) + 'px'

  this.listdivchoix.addEventListener('click', (event) => this.place(event), false)

  this.listdivchoix.addEventListener('touchstart', this._handleStart.bind(this), { capture: false, passive: false })
  this.listdivchoix.addEventListener('touchend', this._handleEnd.bind(this), false)
  this.listdivchoix.addEventListener('touchcancel', this._handleCancel.bind(this), false)
  this.listdivchoix.addEventListener('touchleave', this._handleLeave.bind(this), false)
  this.listdivchoix.addEventListener('touchmove', this._handleMove.bind(this), { capture: false, passive: false })

  // FIXME mafoncez n’existe pas
  if (this.unique) {
    this.whEtkl[indexEtiquette].style.visibility = 'hidden'
    this.etiqok[indexEtiquette] = false
  }
  if (event.ctrlKey || this.direct) { this.rac() }
}

Etiquettes.prototype.place = function place (event) {
  if (event.ctrlKey || this.direct) {
    this.rac()
    return
  }
  if (this.disabled) { return }

  if (this.listdivchoix.bloque) {
    if (this.listdivchoix.place.etiq !== -1) {
      if (!this.unique) {
        try {
          this.whEtkl[this.listdivchoix.num].style.visibility = undefined
          this.etiqok[this.listdivchoix.num] = true
        } catch (e) {
          return
        }
      } else {
        try {
          this.whEtkl[this.listdivchoix.place.etiq].style.visibility = 'visible'
          this.etiqok[this.listdivchoix.place.etiq] = true
        } catch (e) {
          return
        }
      }
    }
    this.listdivchoix.place.contenu = this.montab[this.listdivchoix.num]
    this.listdivchoix.place.etiq = this.listdivchoix.num
    this.listdivchoix.place.ecris()
    if (!this.unique) {
      this.whEtkl[this.listdivchoix.num].style.visibility = undefined
      this.etiqok[this.listdivchoix.num] = true
    }
  } else {
    if (!this.unique) {
      this.whEtkl[this.listdivchoix.num].style.visibility = undefined
      this.etiqok[this.listdivchoix.num] = true
    } else {
      if (!this.whEtkl[this.listdivchoix.num]) {
        return
      }
      this.whEtkl[this.listdivchoix.num].style.visibility = 'visible'
      this.etiqok[this.listdivchoix.num] = true
    }
  }
  j3pDetruit(this.listdivchoix)
  this.listdivchoix = -1
}

Etiquettes.prototype.place2 = function place2 (event) {
  if (this.disabled || (this.listdivchoix === -1)) return
  j3pStyle(this.listdivchoix, { zIndex: 700 })

  const { left, top } = this.mepact.getBoundingClientRect()
  const x = event.clientX - left
  const y = event.clientY - top

  let bloque = false
  const lim = this.limite

  let nub
  for (let i = 0; i < this.cooplace.length; i++) {
    if ((Math.abs(x - this.cooplace[i][0]) < lim) && (Math.abs(y - this.cooplace[i][1]) < lim)) {
      bloque = true
      nub = i
      break
    }
  }

  this.listdivchoix.bloque = bloque
  if (bloque) {
    this.listdivchoix.place = this.cooplace[nub][2]
    this.listdivchoix.style.left = (this.cooplace[nub][0] - this.listdivchoix.offsetWidth / 2) + 'px'
    this.listdivchoix.style.top = (this.cooplace[nub][1] - this.listdivchoix.offsetHeight / 2) + 'px'
    this.listdivchoix.style.border = '1px solid red'
  } else {
    this.listdivchoix.style.border = '1px solid black'
    this.listdivchoix.style.left = (x - this.listdivchoix.offsetWidth / 2) + 'px'
    this.listdivchoix.style.top = (y - this.listdivchoix.offsetHeight / 2) + 'px'
  }
}

Etiquettes.prototype.supprimemoi = function supprimemoi (obj) {
  for (let i = 0; i < this.cooplace.length; i++) {
    if ((this.cooplace[i][2].iddivconteneur === obj.iddivconteneur) && (this.cooplace[i][2].manouvellediv === obj.manouvellediv)) {
      this.cooplace.splice(i, 1)
    }
  }
}

Etiquettes.prototype.envoieCoo = function envoieCoo (x, y, z) {
  this.cooplace.push([x, y, z])
}

Etiquettes.prototype.verifCoPla = function verifCoPla () {
  const coolplabuf = this.cooplace
  this.cooplace = []
  for (let i = 0; i < coolplabuf.length; i++) {
    coolplabuf[i][2].returnco()
  }
}

Etiquettes.prototype.disable = function disable () {
  this.parent.removeEventListener('mousemove', this.place2Listener, false)
  this.disabled = true
  for (let i = 0; i < this.cooplace.length; i++) {
    this.cooplace[i][2].disable()
  }
}

Etiquettes.prototype.detruit = function detruit () {
  this.disable()
  j3pDetruit(this.spanContenant)
}

Etiquettes.prototype._handleStart = function _handleStart (evt) {
  evt.preventDefault()
  const touches = evt.changedTouches
  for (let i = 0; i < touches.length; i++) {
    this.ongoingTouches.push(touches[i])
  }
}

Etiquettes.prototype._handleEnd = function _handleEnd (evt) {
  this.ongoingTouches = []
  evt.preventDefault()
  this.place({ ctrlKey: false })
}

Etiquettes.prototype._handleCancel = function _handleCancel (evt) {
  evt.preventDefault()
  this.ongoingTouches = []
}

Etiquettes.prototype._handleLeave = function _handleLeave (evt) {
  this.ongoingTouches = []
  evt.preventDefault()
}

Etiquettes.prototype._handleMove = function _handleMove (evt) {
  if (this.disabled || (this.listdivchoix === -1)) return
  const ongoingTouchIndexById = (idToFind) => {
    for (let i = 0; i < this.ongoingTouches.length; i++) {
      const id = this.ongoingTouches[i].identifier
      if (id === idToFind) return i
    }
    return -1 // toucher non trouvé
  }

  evt.preventDefault()
  j3pStyle(this.listdivchoix, { zIndex: 1 })

  const touches = evt.changedTouches
  for (let i = 0; i < touches.length; i++) {
    const idx = ongoingTouchIndexById(touches[i].identifier)
    if (idx === -1) continue
    this.place2({ clientX: this.ongoingTouches[idx].pageX, clientY: this.ongoingTouches[idx].pageY })
    this.ongoingTouches.splice(idx, 1, touches[i]) // mettre à jour la liste des touchers
  }
}

export default Etiquettes