/* global MathQuill */
import $ from 'jquery'
import { j3pAddElt, j3pIsHtmlElement, j3pValeurde } from 'src/legacy/core/functions'
import { getZoneParente } from 'src/lib/core/domHelpers'
import Button from 'src/lib/widgets/mqVirtualKeyboard/Button'
import ButtonArrow from 'src/lib/widgets/mqVirtualKeyboard/ButtonArrow'
import ButtonBackSpace, { doBackSpaceAction } from 'src/lib/widgets/mqVirtualKeyboard/ButtonBackSpace'
import ButtonEnter, { doEnterAction } from 'src/lib/widgets/mqVirtualKeyboard/ButtonEnter'
import ButtonMq, { doCharAction } from 'src/lib/widgets/mqVirtualKeyboard/ButtonMq'
import ButtonMqCommand from 'src/lib/widgets/mqVirtualKeyboard/ButtonMqCommand'
import './mqVirtualKeyboard.scss'
const decalageSousInputMq = 4
// la marge qu’on se réserve dans le boundingContainer dans lequel on essaie de rester
const boundingMargin = 20 // en dessous de 20 ça déborde sous l’éventuel scrollbar verticale
// const logEvent = (event, ...args) => {
// console.log('event', event.type, 'on', event.target.id || Array.from(event.target.classList).join(' ') || event.target)
// if (args.length) console.log.apply(console, args)
// }
// Pour gérer une touche Tab ou Shift-Tab, on regarde la position du curseur Mathquill, on envoie
// la commande MathQuill correspondante et on regarde la position finale du curseur
// Si ces deux positions ne sont pas égales, on ne fait pas remonter l’événenemnt
// Si elles sont égales c’est que la position du curseur n’a pas changé et on transmet
// la commande Tab ou Shift-Tab
// A noter qu’il faut gérer les touches ici mais pas dans keyup car dans FireFox
// keyup n’est pas généré si on tape par exemple sur la touche /
const keydownListener = (event) => {
// on écoute rien si :
if (
// y’a pas de clavier
!currentVirtualKeyboard ||
// il est masqué
!currentVirtualKeyboard.isOpen ||
// on a marqué l’événement clavier comme devant être ignoré
event.isFake ||
// Yves : si l’éditeur associé est caché ou enfant d’une élément du DOM caché
// cf https://developer.mozilla.org/fr/docs/Web/API/HTMLElement/offsetParent
// ici currentVirtualKeyboard est bien positionné, donc s’il est masqué alors son parent positionné le plus proche sera null
!currentVirtualKeyboard.inputMq.offsetParent
) {
return
}
const activeElt = document.activeElement
if (
activeElt && // y’a un élément actif (dans le cas aucun, y’a eu un blur, on écoute quand même)
activeElt !== document.body && // c’est pas le body (car avec le clavier virtuel qui simule le focus activeElt peut être le body)
!activeElt.classList.contains('mq-editable') && // pas un inputMq
!activeElt.classList.contains('mqBtn') // pas un bouton créé par j3pPaletteMathquill
) {
return
}
if (event.shiftKey && event.key === 'Shift') return
const { inputMq } = currentVirtualKeyboard
// if (event.code.indexOf('Shift') !== -1) return
let code = ''
if (event.key === 'Tab') {
if (event.shiftKey) code = 'Shift-Tab'
else code = 'Tab'
const MQ = MathQuill.getInterface(2)
const mathField = MQ.MathField(inputMq)
const cursor = mathField.__controller.cursor
const leftInit = cursor['-1']
const rightinit = cursor['1']
$(inputMq).mathquill('keystroke', code)
const left = cursor['-1']
const right = cursor['1']
if ((leftInit !== left) || (rightinit !== right)) {
event.preventDefault()
}
} else {
switch (event.code) {
case 'Backspace':
doBackSpaceAction(inputMq)
break
// pour enter ça déconne, le premier caractère tapé après dépliement du clavier se retrouve doublé avec la touche entrée
case 'Enter':
doEnterAction(inputMq)
break
case 'ArrowLeft':
$(inputMq).mathquill('keystroke', 'Left')
break
case 'ArrowRight':
$(inputMq).mathquill('keystroke', 'Right')
break
case 'ArrowDown':
$(inputMq).mathquill('keystroke', 'Down')
break
case 'ArrowUp':
$(inputMq).mathquill('keystroke', 'Up')
break
case 'Delete':
$(inputMq).mathquill('keystroke', 'Del')
break
default:
// si ça correspond à un truc d’un seul caractère, qui est autorisé sur ce clavier virtuel, on l’ajoute
if (event.key && event.key.length === 1 && currentVirtualKeyboard.restriction.test(event.key)) {
doCharAction(inputMq, event.key)
event.preventDefault()
// event.stopPropagation() // Nécessaire pour FireFox
}
// et sinon on fait rien
}
}
}
const pasteListener = (event) => {
if (!currentVirtualKeyboard) return
const activeElt = document.activeElement
// Dans le cas où on un un éditeur Mathquill avec clavier virtuel qui simule le focus, activeElt peut être le body
// et sinon si l’élement qui a le fovus n’est pas un éditeur MathQuill il ne faut rien faire
if (activeElt && activeElt !== document.body && (activeElt.classList.length === 0 || !activeElt.classList.contains('mq-editable'))) return
const clip = event.clipboardData || window.clipboardData
if (clip) {
const { inputMq } = currentVirtualKeyboard
const data = clip.getData('text')
$(inputMq).mathquill('latex', data)
}
}
const copyListener = (event) => {
if (!currentVirtualKeyboard) return
const activeElt = document.activeElement
// Dans le cas où on un un éditeur Mathquill avec clavier virtuel qui simule le focus, activeElt peut être le body
// et sinon si l’élement qui a le fovus n’est pas un éditeur MathQuill il ne faut rien faire
if (activeElt && activeElt !== document.body && (activeElt.classList.length === 0 || !activeElt.classList.contains('mq-editable'))) return
const cb = event.clipboardData
if (cb) {
const { inputMq } = currentVirtualKeyboard
cb.setData('text/plain', j3pValeurde(inputMq))
event.preventDefault()
}
}
/**
* Fonction chargée de refaire aparaître le curseur clignotant de MathQuill sans redonner le focus
* au champ MathQuill
* @param inputMq
* @param bshow
* @private
*/
export const showMqCursor = (inputMq, bshow) => {
const MQ = MathQuill.getInterface(2)
const mathField = MQ.MathField(inputMq)
const controller = mathField.__controller
const cursor = controller.cursor
if (bshow) cursor.show()
else cursor.hide()
}
// un listener unique pour écouter keyup sur body, pour répercuter la frappe clavier physique dans l’input concerné
// par un clavier virtuel ouvert, mais qui n’a pas le focus (pour éviter le clavier virtuel de l’OS)
const enableKeyListeners = () => {
if (hasBodyListener) return
// on veut récupérer les événement du clavier physique pour répercuter la frappe dans notre input qui n’a plus le focus
// document.body.addEventListener('keyup', keyupListener)
document.body.addEventListener('keydown', keydownListener)
document.body.addEventListener('paste', pasteListener)
document.body.addEventListener('copy', copyListener)
hasBodyListener = true
}
// faut le désactiver dès que l’input a vraiment le focus
const disableKeyListeners = () => {
if (!hasBodyListener) return
// document.body.removeEventListener('keyup', keyupListener)
document.body.removeEventListener('keydown', keydownListener)
document.body.removeEventListener('paste', pasteListener)
document.body.removeEventListener('copy', copyListener)
hasBodyListener = false
}
const focusListener = (inputMq, event) => {
// logEvent(event, 'avec isFocusedByTouch', inputMq.isFocusedByTouch)
// Si le clavier n’est pas actif on ne fait rien (il peut avoir été désactivé par j3pDesactive)
if (!inputMq.virtualKeyboard.isActive) return
/** @type MqVirtualKeyboard */
const vk = inputMq.virtualKeyboard
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0
// const isTouchDevice = false
if (isTouchDevice) {
// si on est dans un contexte "touch" faut virer le focus dès qu’on le reçoit
// pour que le clavier virtuel de l’OS ne se montre pas (sinon c’est pas la peine ça change rien)
enableKeyListeners()
// on l’ouvre même si y’en a pas d’autres ouvert, parce qu’on a eu le focus avec du touch
if (vk.isOpen) fakeMqFocus(inputMq)
else vk.show() // il fera le fakeMqFocus
// il faut virer le focus pour faire disparaître le clavier virtuel de la tablette
} else {
disableKeyListeners()
// on regarde si on doit ouvrir ce clavier virtuel, si c’est déjà le cas y’a rien à faire,
// sinon désactiver le focus pour que ça fonctionne comme sur tablette
if (vk.isOpen) {
fakeMqFocus(inputMq)
} else {
// si y’a un autre clavier ouvert on s’affiche à sa place
const allKeyboards = Array.from(document.querySelectorAll('.virtualKeyboard'))
// on a eu un cas avec un div qui n'avait pas de prop virtualKeyboard (https://app.bugsnag.com/sesamath/j3p/errors/68343525d383ca45786b2d5f?filters[event.since]=30d&filters[error.status]=open&filters[error.assigned_to][type]=ne&filters[error.assigned_to][value]=anyone)
if (allKeyboards.some(div => div.virtualKeyboard?.isOpen)) vk.show()
// et sinon on laisse le focus clavier à cet inputMq sans déplier ce clavier virtuel
}
}
}
const onResize = () => {
if (!currentVirtualKeyboard || !currentVirtualKeyboard.isOpen || !currentVirtualKeyboard.boundingContainer || !currentVirtualKeyboard.element) return
// on regarde s’il faut décaler notre clavier virtuel à gauche ou à droite (on change rien en hauteur car on veut rester sous l’inputMq)
// on translate à gauche si on est trop près du bord droit
const vk = currentVirtualKeyboard
if (!vk.element._lastTop) return // le clavier a été déplacé manuellement => on bouge rien
const { x: bX, right: bRight } = vk.boundingContainer.getBoundingClientRect()
// on translate à gauche si on est trop près du bord droit
const { x, right } = vk.element.getBoundingClientRect()
if (right + boundingMargin > bRight) {
// ça déborde à droite, on décale à gauche autant qu’on peut (sans déborder à gauche)
// right-bright : pour caler à droite du conteneur
// x - bX : pour caler à gauche du conteneur
// => on prend les opposés (faut décaler vers la gauche) et le max (donc valeur absolue minimale pour décaler le moins possible)
vk.translatePosition(Math.max(bRight - right - boundingMargin, bX - x + boundingMargin / 2))
} else {
// si c’est décalé à gauche de l’input, on regarde si on peut le recaler sur sa position par défaut (left: 0)
// (ça pourrait aussi déborder à gauche, si on avait décalé à gauche précédemment et que l’input est passé à la ligne depuis),
const left = parseInt(vk.element.style.left, 10)
if (left < 0) {
// on décale vers la droite autant que possible pour aligner le coin supérieur gauche à celui du l’inputMq
// pour se simplifier la vie (et éviter un éventuel bug qui risquerait de passer inaperçu) on remet à 0 et relance le calcul ci-dessus
vk.element.style.left = 0
setTimeout(onResize, 0)
}
}
}
/**
* Met le curseur clignotant dans l’inputMq et le liseret bleu, mais sans lui donner le focus
* (pour ne pas avoir le clavier virtuel iOS/Android)
* @private
* @param inputMq
*/
const fakeMqFocus = (inputMq) => {
const $inputMq = $(inputMq)
$inputMq.mathquill('blur')
inputMq.classList.add('fakeMqFocus')
// et lancer qqchose pour remettre le curseur clignotant dans l’input, avec un setTimeout pour passer après la propagation du blur
// (sinon on a pas le curseur clignotant)
setTimeout(() => {
showMqCursor(inputMq, true)
})
}
// on pourrait détecter en statique les capacités touch avec ça
// cf https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/
// const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0
// mais on préfère gérer chaque cas, input qui a reçu le focus avec un touch (on ouvre automatiquement)
// ou avec un clic (on ouvre si y’en avait un autre ouvert)
let hasBodyListener = false
/**
* @type {MqVirtualKeyboard}
* @private
*/
let currentVirtualKeyboard
/**
* @typedef MqVirtualKeyboardOptions
* @property {string[]} [commandes] Une liste éventuelle de commandes mathquill
* @property {HTMLElement} [boundingContainer] Un parent dans lequel il faut essayer de rester (que le clavier ne déborde pas)
*/
/**
* À priori pas besoin d’instancier directement cette classe, l’appel de {@link module:mqFunctions.mqRestriction mqRestriction} devrait suffire
* Cf également le tuto {@tutorial MqVirtualKeyboard} pour migrer de {@link module:j3pFunctions.j3pRestriction} à {@link module:lib/mathquill/functions.mqRestriction mqRestriction}
*/
class MqVirtualKeyboard {
/**
* Instancie un clavier virtuel et ses éléments mais ne l’insère pas dans le dom (insérer sa propriété element où on voudra ensuite)
* @param {HTMLElement} inputMq
* @param {RegExp} restriction
* @param {MqVirtualKeyboardOptions} [options]
*/
constructor (inputMq, restriction, options = {}) {
if (!restriction) restriction = /./ // on autorise tout, ce sera limité plus loin par ce qu’on gère dans un clavier virtuel
if (!(restriction instanceof RegExp)) throw Error('restriction invalide')
// si on nous passe une restriction qui vérifie depuis le début de la chaîne, on vire cette restriction
// (elle s’appliquera quand même dans restrict mais si on la laissait ici on n’aurait que le 1er caractère autorisé comme bouton)
const regSrc = restriction.source
if (regSrc.startsWith('^')) {
restriction = new RegExp(regSrc.substring(1), restriction.flags)
} else {
// on la clone, pour que notre usage ne modifie pas son pointeur interne lastIndex, au cas où qqun s’en servirait
restriction = new RegExp(regSrc, restriction.flags)
}
/**
* Sera mis à false quand on veut le désactiver par exemple dans j3PDesactive (le boutont pour déplier devient alors inactif)
* @type {boolean}
*/
this.isActive = true
/**
* L’input mathquill
* @type {HTMLElement}
*/
this.inputMq = inputMq
/**
* La liste des touches du clavier virtuel
* @type {RegExp}
*/
this.restriction = restriction
if (options.boundingContainer) {
/**
* Le parent dans lequel le clavier se repositionne pour rester visible
* @type {HTMLElement}
*/
this.boundingContainer = options.boundingContainer
} else {
// on cherche le premier parent divZone
const zone = getZoneParente(inputMq, true)
if (zone) this.boundingContainer = zone
}
// on s’attache à l’inputMq, pour que d’autres puissent nous retrouver via le dom
// (c’est utilisé par ButtonEnter pour masquer le clavier virtuel par ex)
inputMq.virtualKeyboard = this
// on ajoute une méthode fakeFocus à cet inputMq, que j3pFocus utilisera si ça existe
inputMq.fakeFocus = fakeMqFocus.bind(null, inputMq)
const triggerBtn = new Button({
className: 'triggerButton',
value: '⇓',
onClick: () => this.toggle()
})
/**
* le bouton déclencheur
* @type {HTMLElement}
*/
this.triggerElement = triggerBtn.element
// La chaîne contenant tous les caractères éventuels (qui pourraient être filtrés par la regExp restriction)
// @todo if (restriction.flag.includes('i')) => retirer les majuscules de cette liste et ajouter un bouton Maj pour les gérer
// on crée des listes pour passer à la ligne entre chaque liste
const allChars = ['0123456789,.²', '+-*/=^', ';()[]<>|', 'aàâbcçdeéèêëfghiïjklmnoôpqrstuùûvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']
const allowedChars = allChars.map(list => Array.from(list).filter(char => restriction.test(char))).filter(l => l.length)
const div = document.createElement('div')
div.classList.add('virtualKeyboard')
div.style.display = 'none'
/**
* Le div contenant le clavier virtuel
* On lui ajoutera une propriété virtualKeyboard contenant cette instance de MqVirtualKeyboard
* @type {HTMLDivElement}
*/
this.element = div
/**
* @type {boolean}
*/
this.isOpen = false
// on s’ajoute aussi à cet elt (c’est lui qu’on récupère avec du querySelectorAll('.virtualKeyboard')
this.element.virtualKeyboard = this
// //////////////////////
// comportements
// //////////////////////
// ordre des event
// - focusin sur inputMq
// - touchstart sur inputMq
// - touchend sur inputMq
// - focus sur inputMq.querySelector('textarea')
// - click sur inputMq
// => on lance onFocus sur les deux derniers
// La ligne suivante doit être présente pour que cela fonctionne beien avec le clavier
inputMq.querySelector('textarea').addEventListener('focus', focusListener.bind(null, inputMq))
// Le addEventListener suivant améliore le problème du curseur fantôme non clignotant sur tablette.
// Finalement plus utilisé depuis qu ela chasse au curseur parasite a été délégué à mathquill
// dans la fonction blink
// faut remettre le listener si on perd le focus en gardant le clavier ouvert
inputMq.addEventListener('focusout', (event) => {
if (this.isOpen) enableKeyListeners()
})
if (this.boundingContainer) {
// ça marche aussi sur le changement d’orientation sur iOS et android
window.addEventListener('resize', onResize)
// et ça tombe bien car ce qui suit n’est jamais appelé au changement d’orientation
// const eventTarget = (typeof screen === 'object' && typeof screen.addEventListener === 'function') ? screen : window
// eventTarget.addEventListener('deviceorientation', onResize)
}
/**
* Retourne la position relative du touch ou du clic par rapport à sa cible (position dans event.target)
* @param {TouchEvent|MouseEvent} event
* @return {number[]} [x, y]
* @private
*/
const getPosition = (event) => {
const rect = div.getBoundingClientRect()
const ref = (event.targetTouches && event.targetTouches[0]) || event
return [ref.clientX - rect.x, ref.clientY - rect.y]
}
// div draggable (pour utiliser le drag&drop natif) marche pas au touch, et ça marche plus non plus quand on se retrouve dans un mq-math-mode :-/
// on gère tout ensemble
const onDragStart = (event) => {
// sur iPad faut un preventDefault sinon toute la page bouge, sauf sur les <button> sinon ça déclenche plus le click
// => on ignore le drag sur un bouton et on fera un preventDefault dans les autres cas
if (event.target.tagName.toLowerCase() !== 'div') return // on fait rien sur les <button> ou les <span> qu’ils contiennent
div.isDragging = true
const [x, y] = getPosition(event)
div._dragStartX = x
div._dragStartY = y
div.addEventListener('touchmove', onDragMove)
// pour le mousemove faut le mettre sur body, sinon la souris sort trop facilement du div dès qu’on bouge un peu trop vite
document.body.addEventListener('mousemove', onDragMove)
if (event.cancelable) event.preventDefault()
}
const onDragMove = (event) => {
if (div.isDragging) {
const [x, y] = getPosition(event)
const decalX = x - div._dragStartX
const decalY = y - div._dragStartY
this.translatePosition(decalX, decalY)
div._lastTop = null
}
}
const onDragEnd = () => {
if (!div.isDragging) return
div.isDragging = false
div.removeEventListener('touchmove', onDragMove)
document.body.removeEventListener('mousemove', onDragMove)
}
// pour drag&drop au touch ou à la souris
div.addEventListener('mousedown', onDragStart)
// faut préciser passive: false pour indiquer au navigateur qu’on va utilise preventDefault (si on le dit pas ça marche quand même mais chrome met un warning en console)
div.addEventListener('touchstart', onDragStart, { passive: false })
document.body.addEventListener('mouseup', onDragEnd)
div.addEventListener('touchend', onDragEnd)
// faut repositionner le clavier à chaque fois que l’input change (s’il devient plus haut avec des
// fractions faut se décaler pour pas le recouvrir, idem si ça diminue)
// init avec la taille actuelle de l’input
const { height } = inputMq.getBoundingClientRect()
div._lastTop = height
div.style.top = (height + decalageSousInputMq) + 'px'
// keyup fonctionne pour les saisies du clavier physique mais aussi pour les clics sur les boutons du clavier virtule,
// on l’écoute pour décaler notre div si besoin (si l’inputMq a changé de hauteur après un clic sur fraction par ex)
inputMq.addEventListener('keyup', () => {
if (!div._lastTop) return // le clavier a été déplacé manuellement => on bouge rien
const { height } = inputMq.getBoundingClientRect()
if (div._lastTop === height) return
// ça a changé
div._lastTop = height
div.style.top = (height + decalageSousInputMq) + 'px'
})
// ///////////////////////////////
// ajout du contenu du clavier
// ///////////////////////////////
// A gauche un bloc contenant les boutons pour les caractères
const blockChars = j3pAddElt(div, 'div')
allowedChars.forEach((chars, i) => {
if (i) j3pAddElt(blockChars, 'br')
chars.forEach(char => ButtonMq.addInto(blockChars, inputMq, { value: char }))
})
/**
* Le bloc des boutons de commande
* @type {HTMLDivElement}
*/
this.commandsContainer = j3pAddElt(blockChars, 'div', '', { className: 'mqCommandes' })
if (Array.isArray(options.commandes)) {
options.commandes.forEach(commande => this.addCommand(commande))
}
// un block à droite pour les boutons "meta"
const blockMeta = j3pAddElt(div, 'div')
// qui contient deux block, un pour les flèches
const blockArrows = j3pAddElt(blockMeta, 'div')
const arrows = ['Left', 'Right', 'Up', 'Down']
arrows.forEach(arrow => ButtonArrow.addInto(blockArrows, inputMq, arrow))
// et un autre pour backspace et enter
const blockDelEnter = j3pAddElt(blockMeta, 'div')
ButtonBackSpace.addInto(blockDelEnter, inputMq)
ButtonEnter.addInto(blockDelEnter, inputMq)
} // constructor
/**
* Crée un MqVirtualKeyboard attaché à un inputMq (découple l’instanciation de l’objet et la manipulation du dom)
* @param {HTMLElement|string} inputMq
* @param {RegExp|null} restriction
* @param {MqVirtualKeyboardOptions} [options]
*/
static create (inputMq, restriction, options) {
if (!j3pIsHtmlElement(inputMq) || !inputMq.classList.contains('mq-editable-field')) throw Error('input mathquill invalide')
if (inputMq.virtualKeyboard) {
// cet input a déjà un clavier, on pourrait virer ses éléments, mais on peut pas virer les listeners sur inputMq
// par ailleurs, on voit pas très bien dans quel cas on pourrait vouloir créer un 2e clavier virtuel différent sur le même inputMq
throw Error('Cet input mathquill a déjà un clavier virtuel qui lui est attaché')
}
const parent = inputMq.parentNode
// On crée un div qui va englober le span contenant l’éditeur MathQuill
const div = document.createElement('div')
// On impose la position relative à ce div pour que le clavier virtuel puisse se positionner en absolute par rapport à lui
div.classList.add('kbdContainer')
// on le positionne juste avant l’input
parent.insertBefore(div, inputMq)
// et on déplace l’inputMq à l’intérieur de ce div
div.appendChild(inputMq)
// on instancie un clavier virtuel
const virtualKeyboard = new MqVirtualKeyboard(inputMq, restriction, options)
// on ajoute un bouton pour faire apparaître le clavier
div.appendChild(virtualKeyboard.triggerElement)
// puis le clavier
div.appendChild(virtualKeyboard.element)
}
/**
* Ajoute un bouton de commande mathquill (cf commands dans src/lib/mathquill/functions.js)
* @param {string} command
*/
addCommand (command) {
ButtonMqCommand.addInto(this.commandsContainer, this.inputMq, { command })
}
/**
* Décale le div du clavier virtuel
* @param decalX Le décalage en abscisse
* @param decalY Le décalage en ordonnée
*/
translatePosition (decalX, decalY) {
const div = this.element
if (!div) return // ça peut arriver si le clavier vient d’etre détruit
const { left, top } = getComputedStyle(div)
div.style.left = (parseInt(left, 10) + decalX) + 'px'
div.style.top = (parseInt(top, 10) + decalY) + 'px'
}
show () {
for (const vkElement of document.querySelectorAll('.virtualKeyboard')) {
if (!vkElement.virtualKeyboard) {
console.error(Error('élément .virtualKeyboard sans instance de virtualKeyboard'), vkElement)
continue
}
if (vkElement.virtualKeyboard.isOpen) {
vkElement.virtualKeyboard.hide()
}
}
this.element.style.display = 'inline-block'
this.triggerElement.innerText = '⇑'
fakeMqFocus(this.inputMq)
this.isOpen = true
currentVirtualKeyboard = this
enableKeyListeners()
if (this.boundingContainer) {
// il faut un setTimeout pour passer après un éventuel retour à la ligne de l’inputMq
// (s’il est à ras du bord droit, ouvrir le clavier peut renvoyer l’input au début de la ligne suivante
// et dans ce cas getBoundingClientRect retourne les anciennes positions)
setTimeout(onResize, 10)
}
}
hide () {
if (!this.isOpen) return
this.element.style.display = 'none'
this.triggerElement.innerText = '⇓'
// avec du `this.inputMq.blur()` ça déconne (casse le show suivant)
$(this.inputMq).mathquill('blur')
this.isOpen = false
this.inputMq.classList.remove('fakeMqFocus')
currentVirtualKeyboard = null
}
toggle () {
if (this.isActive) {
if (this.element.style.display === 'none') this.show()
else this.hide()
}
}
/**
* Fonction appelée pour rendre le clavier virtuel actif ou inactif suivant la valeur de bActive
* @param {boolean} bActive
*/
setActive (bActive) {
this.isActive = bActive
const sel = this.inputMq.parentNode.querySelector('.triggerButton')
if (bActive) {
sel.style.display = 'inline-block'
} else { // Si on désactive le clavier virtuel et qu’il est visible on le masque
if (this.isOpen) this.hide()
sel.style.display = 'none'
}
}
}
export default MqVirtualKeyboard