/*
* Created by yvesb on 05/10/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 { cens, getStr, preventDefault } from '../kernel/kernel'
import constantes from '../kernel/constantes'
// import $ from 'jquery'
import $ from 'jquery'
export default Button
/**
*
* @param {MtgApp} app
* @param {string} fileName
* @param {string} tip
* @param {number} w
* @param {number} h
* @param {boolean} [bframe=true]
* @param {boolean} [bNoZoomFactor=false]
* @constructor
*/
function Button (app, fileName, tip, w, h, bframe = true, bNoZoomFactor = false) {
if (arguments.length === 0) return
this.app = app
this.fileName = fileName
const zf = bNoZoomFactor ? 1 : app.zoomFactor
this.w = w * zf
this.h = h * zf
this.tip = getStr(tip)
this.bframe = bframe
this.clicks = 0
this.isArrow = this.fileName.indexOf('arrow') === 0
}
/**
* Foonction donnant au bouton l'apparence activé ou non suibant la valeur de bActivate
* @param {boolean} bActivate
*/
Button.prototype.activate = function (bActivate) {
$(this.mask).attr('fill-opacity', '0')
$(this.frameRect).attr('fill', (bActivate)
? 'url(#buttonGradAct)'
: constantes.buttonBackGroundColor)
$(this.mask).attr('stroke', (bActivate)
? constantes.buttonActivatedBackGroundColor
: constantes.buttonBackGroundColor)
this.isActivated = bActivate
}
Button.prototype.build = function () {
const self = this
const w = this.w
const h = this.h
const g = cens('g')
// g.setAttribute("id",this.tip);
this.container = g
const frameRect = cens('rect', {
width: w,
height: h,
x: '0',
y: '0',
stroke: constantes.buttonStroke,
fill: constantes.buttonFill,
'fill-opacity': '1'
})
if (!this.bframe) $(frameRect).css('visibility', 'hidden')
this.frameRect = frameRect
g.appendChild(frameRect)
const mask = cens('rect', {
width: w,
height: h,
x: '0',
y: '0',
stroke: constantes.buttonStroke,
fill: 'url(#buttonGrad)',
'fill-opacity': '0' // Sera passé à un quand la souris passe dessus
})
this.mask = mask
if (!this.bframe) {
$(mask).css('visibility', 'hidden').css('pointer-events', 'all')
}
g.appendChild(mask)
if (this.isArrow) {
const points = '3,1 3,' + String(h - 1) + ' ' + String(w - 2) + ',' + String(h / 2)
const poly = cens('polygon', {
points,
fill: 'url(#arrowGrad)',
'fill-opacity': '0.5',
style: 'stroke:#A9A9F5;stroke-width:1',
'pointer-events': 'none'
})
this.poly = poly
g.appendChild(poly)
g.addEventListener('mouseover', function () {
self.poly.setAttribute('fill-opacity', '1')
})
g.addEventListener('mouseout', function () {
self.poly.setAttribute('fill-opacity', '0.5')
})
} else {
if (this.fileName.length !== 0) { // fileName est "" pour les OneColorButton
const gImage = cens('g', {
transform: 'translate(1,1)',
'pointer-events': 'none'// ,
// opacity: "0.8"
})
g.appendChild(gImage)
this.image = cens('image', {
width: String(w - 2),
height: String(h - 2),
x: '0',
y: '0'
})
gImage.appendChild(this.image)
// Chargement en lazy-loading de l'image
import(`src/images/${this.fileName}.png`)
.then(({ default: img }) => {
self.image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', img)
})
.catch(error => console.error(`impossible de charger src/images/${this.fileName}.png`, error))
}
}
mask.addEventListener('mouseover', function () {
if (!self.isActivated) self.mask.setAttribute('fill-opacity', '1')
})
mask.addEventListener('mouseout', function () { self.mask.setAttribute('fill-opacity', '0') })
// Initialement les boutons de la barre de gauche ne sont pas visibles.
// Ils sont rendus visibles quand on crée les expandableBar
g.setAttribute('visibility', 'hidden') // Sera changé pour les descendants
const downListener = this.devicedown.bind(this)
const moveListener = this.deviceMove.bind(this)
const activeOpts = { capture: false, passive: false }
g.addEventListener('mousedown', downListener, activeOpts)
g.addEventListener('mousemove', moveListener, activeOpts)
// faut préciser l'option passive sur du touch pour éviter les warnings de chrome,
// ici à false car justement devicedown fait du preventDefault pour empêcher un démarrage de scroll
g.addEventListener('touchstart', downListener, activeOpts)
g.addEventListener('touchmove', moveListener, activeOpts)
}
Button.prototype.devicedown = function (evt) {
// var app = this.app;
// var doc = app.doc;
// Sur les périphériques mobiles il peut y avoir deux événements générés quand on touche l'écran : onmousedown et ontouchstart
// if (doc.type && (doc.type != type)) return;
// doc.type = type;
this.clicks++
if (this.clicks >= 2) {
this.doubleClickAction()
this.clicks = 0 // Ajout version 6.1.0
} else {
const self = this
setTimeout(function () {
switch (self.clicks) {
case 1 :
self.singleClickAction()
self.clicks = 0 // Ajout version 6.1.0
break
case 2 :
self.doubleClickAction()
self.clicks = 0 // Ajout version 6.1.0
}
}, 300)
}
preventDefault(evt)
evt.stopPropagation()
}
Button.prototype.deviceMove = function (evt) {
// Si le bouton n'a pas de cadre(ToolBarArrow) pas de tip
if (!this.bframe || self.tipDisplayed || self.tip === '') return
const app = this.app
// var doc = app.doc;
// Sur les périphériques mobiles il peut y avoir deux événements générés quand on touche l'écran : onmousedown et ontouchstart
// if (doc.type && (doc.type != type)) return;
// doc.type = type;
if (!this.tipDisplayed && this.tip !== '') {
app.cacheTip() // Sans argument pour effacer l'ancien tip quel qu'il soit
this.tipDisplayed = false
app.setTip(this)
const self = this
setTimeout(function () {
self.app.cacheTip(self)
}, 2000)
}
preventDefault(evt)
evt.stopPropagation()
}
/**
* Fonction appelée lors d'un simple clic sur la fonction
* A redéfinir pour les descendants
*/
Button.prototype.singleClickAction = function () {
}
/**
* Fonction appelée lors d'un double clic sur la fonction
* A redéfinir pour les descendants si double click implémenté
*/
Button.prototype.doubleClickAction = function () {
this.singleClickAction()
}