nouilles/DOM.js

265 lines
7.3 KiB
JavaScript

import { EventojLancilo } from './EventojLancilo.js'
EventojLancilo( EventTarget ) // same event handler for everybody
String.merge = (ss,...pp)=> [].concat(ss).map( (s,i)=> s+(i in pp?pp[i]:'') ).join('')
const logNpass = o=> console.log(o)||o
// const ss_pp = (ss,...pp)=> [].concat(ss).map( (s,i)=> s+(i in pp?pp[i]:'') ).join('')
// const $ = (ss,...pp)=> document.querySelector( ss_pp(ss,...pp) )
// const $$ = (ss,...pp)=> [...document.querySelectorAll( ss_pp(ss,...pp) )]
const DOM = (ss,...pp)=> {
let t = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'template' )
t.innerHTML = String.merge(ss,...pp)
return t.content
}
const SVG = (ss,...pp)=> DOM`<svg xmlns="http://www.w3.org/2000/svg">${String.merge(ss,...pp)}</svg>`
.children[0].childNodes
const CSS = (ss,...pp)=> {
let styles = new CSSStyleSheet()
styles.replaceSync( String.merge(ss,...pp) )
return styles
}
const $ = (ss,...pp)=> document.$( ss,...pp )
const $$ = (ss,...pp)=> document.$$( ss,...pp )
Node.prototype.$ = function(ss,...pp)
{
return this.querySelector( String.merge(ss,...pp) )
}
Node.prototype.$$ = function(ss,...pp)
{
return [...this.querySelectorAll( String.merge(ss,...pp) )]
}
Node.prototype.add = function( ...args )
{
this.append( ...args )
return this
}
// Node.prototype.on = function(ss,...pp)
// {
// return (...args)=> ( this.addEventListener( ss_pp(ss,...pp), ...args ), this )
// }
Object.defineProperties( SVGRect.prototype, {
center: {
get(){ return {
x: this.x + ( this.width/2 )
, y: this.y + ( this.height/2 )
}}
, set( v ){}
}
})
/** <line> **/
// @see https://cp-algorithms.com/geometry/segment-to-line.html
// SVGLineElement.prototype.getCoef = function line()
// {
// let P = { x: this.x1.baseVal.value, y: this.y1.baseVal.value }
// let Q = { x: this.x2.baseVal.value, y: this.y2.baseVal.value }
// //console.log(P,Q)
// let A = P.y - Q.y
// let B = Q.x - P.x
// let C = ( -A * P.x ) - ( B * P.y )
// return [A,B,C]
// }
// // @see https://cp-algorithms.com/geometry/lines-intersection.html
// SVGLineElement.prototype.intersect = function intersect( line )
// {
// let [ a1,b1,c1 ] = this.getCoef()
// // console.log(this.getCoef())
// let [ a2,b2,c2 ] = line.getCoef()
// // console.log(line.getCoef())
// let x = - ((c1*b2)-(c2*b1))/((a1*b2)-(a2*b1))
// let y = - ((a1*c2)-(a2*c1))/((a1*b2)-(a2*b1))
// return { x, y }
// }
//PointProperty('_p1','x1','y1')
const PointProperty = ( _, x, y )=> ({
get(){ return this[_] = this[_] || new DOMPoint(
this[x].baseVal.value,
this[y].baseVal.value
)}
, set( obj ){
if( 'x' in obj && 'y' in obj )
{
const ev = e=> {
this.setAttribute( x, obj.x )
this.setAttribute( y, obj.y )
this.dispatchEvent( new Event('move') )
}
this[_]
&& this[_].removeEventListener
&& this[_].removeEventListener('move', ev)
this[_] = obj
obj.addEventListener && obj.addEventListener('move', ev)
ev()
this.dispatchEvent( new Event('pointchange') )
}
}
})
Object.defineProperties(SVGLineElement.prototype, {
p1: PointProperty('_p1','x1','y1')
// {
// get(){ return this._p1 = this._p1 || new DOMPoint(
// this.x1.baseVal.value,
// this.y1.baseVal.value
// )
// }
// , set( obj ){
// if( 'x' in obj && 'y' in obj )
// {
// const ev = e=> {
// this.setAttribute('x1', obj.x)
// this.setAttribute('y1', obj.y)
// this.dispatchEvent( new Event('move') )
// }
// this._p1
// && this._p1.removeEventListener
// && this._p1.removeEventListener('move', ev)
// this._p1 = obj
// obj.addEventListener && obj.addEventListener('move', ev)
// ev()
// this.dispatchEvent( new Event('pointchange') )
// }
// }
// }
, p2: PointProperty('_p2','x2','y2')
// {
// get(){ return this._p2 = this._p2 || new DOMPoint(
// this.x2.baseVal.value,
// this.y2.baseVal.value
// )}
// , set( obj ){
// if( 'x' in obj && 'y' in obj )
// {
// const ev = e=> {
// this.setAttribute('x2', obj.x)
// this.setAttribute('y2', obj.y)
// this.dispatchEvent( new Event('move') )
// }
// this._p2
// && this._p2.removeEventListener
// && this._p2.removeEventListener('move', ev)
// this._p2 = obj
// obj.addEventListener && obj.addEventListener('move', ev)
// ev()
// this.dispatchEvent( new Event('pointchange') )
// }
// }
// }
})
//@TODO Debounce move event fired twice when changing x and y
//@TODO Adapt which attribute is set ( x, cx, x1, transform ), depending on localName ( <rect>, <circle>, <g>, ... )
Object.defineProperties(SVGElement.prototype, {
x: {
get(){ return parseFloat((this.attributes.x||this.attributes.cx||this.attributes.x1||{value:0}).value) }
, set( v ){
if( !isFinite(v) ) return v
if( this.localName == 'g' )
this.setAttribute('transform',`translate(${v},${this.y})`)
this.setAttribute('x',v)
this.setAttribute('cx',v)
this.setAttribute('x1',v)
}
}
, y: {
get(){ return parseFloat((this.attributes.y||this.attributes.cy||this.attributes.y1||{value:0}).value) }
, set( v ){
if( !isFinite(v) ) return v
if( this.localName == 'g' )
this.setAttribute('transform',`translate(${this.x},${v})`)
this.setAttribute('y',v)
this.setAttribute('cy',v)
this.setAttribute('y1',v)
}
}
, moveTo: {
value: function({ x, y }){
this.x = x
this.y = y
this.dispatchEvent( new Event('move') )
return this
}
}
, moveBy: {
value: function({ x, y }){
this.x += x
this.y += y
this.dispatchEvent( new Event('move') )
return this
}
}
})
Object.defineProperties(SVGSVGElement.prototype, {
zoom: {
get(){ return parseFloat((this.attributes.x||this.attributes.cx||this.attributes.x1||{value:0}).value) }
, set( v ){
if( !isFinite(v) ) return v
if( this.localName == 'g' )
this.setAttribute('transform',`translate(${v},${this.y})`)
this.setAttribute('x',v)
this.setAttribute('cx',v)
this.setAttribute('x1',v)
}
}
, pan: //PointProperty('_pan','pan-x','pan-y')
{
get(){ return this._pan = this._pan || new DOMPoint(
this.x2.baseVal.value,
this.y2.baseVal.value
)}
, set( obj ){
if( 'x' in obj && 'y' in obj )
{
const ev = e=> {
this.setAttribute('x2', obj.x)
this.setAttribute('y2', obj.y)
this.dispatchEvent( new Event('move') )
}
this._p2
&& this._p2.removeEventListener
&& this._p2.removeEventListener('move', ev)
this._p2 = obj
obj.addEventListener && obj.addEventListener('move', ev)
ev()
this.dispatchEvent( new Event('pointchange') )
}
}
}
, panAndZoom: {
value( x, y, z )
{
let { x:vx, y:vy, width:vw, height:vh } = this.viewBox.baseVal
, vz
vz = Math.min( vw, vh ) / 2
vx = vx + ( vw / 2 )
vy = vy + ( vh / 2 )
requestAnimationFrame( ()=> {
// this.setAttribute('viewBox', `${typeof x == 'number' ? x-(z||vz) : vx} ${typeof y == 'number' ? y-(z||vz) : vy} ${z ? z*2 : vw} ${z ? z*2 : vh}`)
this.setAttribute('viewBox', `${(x||vx)-(z||vz)} ${(y||vy)-(z||vz)} ${(z||vz)*2} ${(z||vz)*2}`)
console.log('viewBox', `${(x||vx)-(z||vz)} ${(y||vy)-(z||vz)} ${(z||vz)*2} ${(z||vz)*2}`)
// this.setAttribute('pan', `${x} ${y}`)
// this.setAttribute('zoom', `${z}`)
})
}
}
})
export {
logNpass
, DOM
, SVG
, CSS
, $
, $$
}