interface/NameEditor.js

/*
 * Created by yvesb on 19/11/2016.
 */
/*
 * MathGraph32 Javascript : Software for animating online dynamic mathematics figures
 * https://www.mathgraph32.org/
 * @Author Yves Biton (yves.biton@sesamath.net)
 * @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
 */

import NatObj from '../types/NatObj'
import Fonte from '../types/Fonte'
import { ce, cens, preventDefault } from '../kernel/kernel'
import AvertDlg from '../dialogs/AvertDlg'
// import $ from 'jquery'
import $ from 'jquery'

export default NameEditor

/**
 *
 * @param {MtgApp} app
 * @constructor
 */
function NameEditor (app) {
  this.app = app
  this.eltAssocie = null // Pointera sur un point ou une droite dont on peut entrer le nom
  this.isVisible = false
  this.editeur = ce('input', {
    type: 'text',
    class: 'mtgnameinput',
    size: 4
  })
  // Pour que le foreign Object soit visible il faut connaître ses dimensions.
  // Pour cela on l'affiche d'abord dans le div parent de l'appli ou du lecteur
  const parentDiv = app.svg.containerDiv
  parentDiv.appendChild(this.editeur)
  // On mémorise les dimensions de l'éditeur
  const width = this.editeur.clientWidth + 4
  const height = this.editeur.clientHeight + 6
  parentDiv.removeChild(this.editeur)  // this.editeur.size = 2;
  this.editeur.owner = this
  this.foreigntElt = cens('foreignObject', {
    x: 0,
    y: 0,
    width,
    height
  })

  // this.editeur.onblur = this.onblur;
  this.editeur.onkeypress = this.onkeypress
  this.editeur.onkeyup = this.onkeyup
  this.foreigntElt.appendChild(this.editeur)
  this.editeur.style.fontSize = '16px'
  // this.editeur.style.fontFamily = "TNR"; // Modifié version 6.3.5
  this.editeur.style.fontFamily = 'Roboto' // Il faut utiliser une fonte monospace pour que size soit respecté
  this.foreigntElt.style.visibility = 'hidden'
  app.svgGlob.appendChild(this.foreigntElt)
}

NameEditor.prototype.associeA = function (el) {
  this.montre(false) // Pour que si l'utilisateur a enté un nom sans appiuyer sur OK ça marche aussi.
  this.montre(true)
  this.eltAssocie = el
  $(this.editeur).val('')
  this.associeAPoint = el.estDeNature(NatObj.NTtPoint)
}

NameEditor.prototype.focus = function () {
  const self = this
  // Précaution pour les périphériques mobiles
  setTimeout(function () {
    self.editeur.focus()
  }, 200)
}

NameEditor.prototype.montre = function (b) {
  this.isVisible = b
  this.foreigntElt.style.visibility = b ? 'visible' : 'hidden'
  if (!b) {
    // On regarde si l'éditeur contient une chaîne non vide alors que l'élement associé  aun nom vide
    // C'est alors que l'utilisateur a oiublié d'appuyer sur la touche Entrée.
    const el = this.eltAssocie
    const text = this.editeur.value
    const list = this.app.listePourConst
    if ((el !== null) && (el.nom === '')) {
      if ((this.associeAPoint ? Fonte.validationNomPoint(text) : Fonte.validationNomDroite(text)) &&
        !list.existePointOuDroiteMemeNom(null, text)) {
        el.nom = text
        el.updateName(this.app.svgFigure)
      }
    }
    this.eltAssocie = null
  }
}

NameEditor.prototype.onkeyup = function () {
  this.owner.demarquePourErreur()
}

NameEditor.prototype.onkeypress = function (evt) {
  let text
  const self = this
  if (evt.keyCode === 13) { // On a pressé la touche Entrée
    const owner = this.owner
    if (owner.elementAssocie !== null) {
      // Modification version 6.3.5 : Pour les macs ou Ipad on remplace les apostrophes courbes qui
      // sont générées par le clavier Français par des apostrophes droites
      text = this.value.replace(/’/g, "'")
      /*
      if (text.length === 0) {
        self.owner.montre(false);
        return;
      }
      */
      const app = owner.app
      const nomValide = (text === '') || (owner.associeAPoint ? Fonte.validationNomPoint(text) : Fonte.validationNomDroite(text))
      if (nomValide) {
        if ((text !== '') && app.listePourConst.existePointOuDroiteMemeNom(null, text)) {
          new AvertDlg(app, 'ExisteMemeNom', function () {
            self.focus()
          })
          preventDefault(evt)
        } else {
          if (owner.eltAssocie) { // Test rajouté suite à un rapport BusNag
            owner.eltAssocie.donneNom(text)
            // owner.eltAssocie.afficheNom(app.svgFigure);
            owner.eltAssocie.updateName()
            app.gestionnaire.enregistreFigureEnCours('Nommer')
            this.value = ''
            preventDefault(evt)
            this.blur() // Ajouté pour que sur péripérique mobile le clavier se replie
            // On retourne en haut de la page (important pour périphériques mobiles)
            // $('html,body').scrollTop(0); Abandonné
            // Si on est en train de créer l'intersection d'une droite et un cercle ou de deux cercles
            // On passe au deuxième point d'intersection.
            if (owner.eltAssocie.className === 'CPointLieBipoint') {
              if (owner.eltAssocie !== owner.pointAssociePre) {
                const bp = owner.eltAssocie.ptBipoint
                const plb = bp.pointReelDansListe((owner.eltAssocie.indiceDansBipoint === 1) ? 2 : 1)
                // plb peut être nul par exemple avec l'outil de création d'un rectangle direct
                owner.pointAssociePre = plb
                if ((plb !== null) && plb.existe && (plb.nom === '') && plb.dansFenetre) {
                  // Modification version 6.1.0 : S'il existe un point déjà nommé presque confondu avec le point d'intersection
                  // on ne propose pas d'éditer son nom.
                  if (app.listePr.premierPointNommeProcheDe(plb) === null) {
                    owner.associeA(plb)
                    owner.setPosition()
                  } else owner.montre(false)
                } else owner.montre(false)
              } else owner.montre(false)
            } else {
              // On regarde si le point dont on édite le nom n'est pas le dernier objet de la figure
              //  S'il ne l'est pas et si on trouve à partir de la fin un point rajouté on édite son nom
              const list = app.listePr
              const index = owner.eltAssocie.index
              if (index !== list.longueur() - 1) {
                let trouve = false
                let last
                for (let i = list.longueur() - 1; i > index; i--) {
                  last = list.get(i)
                  if (!last.estElementIntermediaire() && last.estDeNature(NatObj.NTtPoint) && !last.masque) {
                    trouve = true
                    break
                  }
                }
                if (trouve && last.dansFenetre) {
                  owner.associeA(last)
                  owner.setPosition()
                } else owner.montre(false)
              } else owner.montre(false)
            }
          }
        }
      } else {
        self.owner.marquePourErreur()
        new AvertDlg(app, 'NomInvalide', function () {
          self.focus()
        })
        preventDefault(evt)
      }
    }
  }
}

NameEditor.prototype.setPosition = function () {
  this.demarquePourErreur()
  let x, y
  const app = this.app
  if (this.eltAssocie === null) return
  const pointAssocie = this.associeAPoint ? this.eltAssocie : null
  const droiteAssociee = this.associeAPoint ? null : this.eltAssocie
  const width = this.editeur.clientWidth
  const height = this.editeur.clientHeight
  const decalagevert = height + 3
  const decalagehor = width / 2
  const xmax = app.dimf.x
  const ymax = app.dimf.y
  x = (pointAssocie !== null) ? pointAssocie.x - decalagehor : droiteAssociee.xNom - droiteAssociee.decX - decalagehor
  if (x + width > xmax) x = x - decalagehor
  else if (x < 0) x = x + decalagehor
  y = (pointAssocie !== null) ? pointAssocie.y + 15 : droiteAssociee.yNom - droiteAssociee.decY
  if (y > ymax - decalagevert) y -= (pointAssocie !== null) ? 2 * decalagevert : decalagevert
  $(this.foreigntElt).attr('x', x)
  $(this.foreigntElt).attr('y', y)
  const horsEcran = (pointAssocie !== null) ? !pointAssocie.dansFenetre : droiteAssociee.horsFenetre
  if (horsEcran) this.montre(false)
  else {
    this.isVisible = true
    if (app.doc.type !== 'touch') this.focus()
  }
}

NameEditor.prototype.marquePourErreur = function () {
  $(this.editeur).css('background-color', '#F5a9BC')
  this.isRed = true
}
NameEditor.prototype.demarquePourErreur = function () {
  if (this.isRed) $(this.editeur).css('background-color', '#FFFFFF')
  this.isRed = false
}