/** * @license * Copyright 2010-2021 Uil.js Authors * SPDX-License-Identifier: MIT */ /** * @author lth / https://github.com/lo-th */ // INTENAL FUNCTION const R = { ui: [], dom:null, ID: null, lock:false, wlock:false, current:-1, needReZone: true, isEventsInit: false, isLeave:false, downTime:0, prevTime:0, prevDefault: ['contextmenu', 'wheel'], pointerEvent: ['pointerdown', 'pointermove', 'pointerup'], eventOut: ['pointercancel', 'pointerout', 'pointerleave'], xmlserializer: null, tmpTime: null, tmpImage: null, oldCursor:'auto', input: null, parent: null, firstImput: true, hiddenImput:null, hiddenSizer:null, hasFocus:false, startInput:false, inputRange : [0,0], cursorId : 0, str:'', pos:0, startX:-1, moveX:-1, debugInput:false, isLoop: false, listens: [], e:{ type:null, clientX:0, clientY:0, keyCode:NaN, key:null, delta:0, }, isMobile: false, now: null, getTime: function() { return ( self.performance && self.performance.now ) ? self.performance.now.bind( performance ) : Date.now; }, add: function ( o ) { R.ui.push( o ); R.getZone( o ); if( !R.isEventsInit ) R.initEvents(); }, testMobile: function () { let n = navigator.userAgent; if (n.match(/Android/i) || n.match(/webOS/i) || n.match(/iPhone/i) || n.match(/iPad/i) || n.match(/iPod/i) || n.match(/BlackBerry/i) || n.match(/Windows Phone/i)) return true; else return false; }, remove: function ( o ) { let i = R.ui.indexOf( o ); if ( i !== -1 ) { R.removeListen( o ); R.ui.splice( i, 1 ); } if( R.ui.length === 0 ){ R.removeEvents(); } }, // ---------------------- // EVENTS // ---------------------- initEvents: function () { if( R.isEventsInit ) return; let dom = document.body; R.isMobile = R.testMobile(); R.now = R.getTime(); if(!R.isMobile){ dom.addEventListener( 'wheel', R, { passive: false } ); } else { dom.style.touchAction = 'none'; } dom.addEventListener( 'pointercancel', R ); dom.addEventListener( 'pointerleave', R ); //dom.addEventListener( 'pointerout', R ) dom.addEventListener( 'pointermove', R ); dom.addEventListener( 'pointerdown', R ); dom.addEventListener( 'pointerup', R ); dom.addEventListener( 'keydown', R, false ); dom.addEventListener( 'keyup', R, false ); window.addEventListener( 'resize', R.resize , false ); //window.onblur = R.out; //window.onfocus = R.in; R.isEventsInit = true; R.dom = dom; }, removeEvents: function () { if( !R.isEventsInit ) return; let dom = document.body; if(!R.isMobile){ dom.removeEventListener( 'wheel', R ); } dom.removeEventListener( 'pointercancel', R ); dom.removeEventListener( 'pointerleave', R ); //dom.removeEventListener( 'pointerout', R ); dom.removeEventListener( 'pointermove', R ); dom.removeEventListener( 'pointerdown', R ); dom.removeEventListener( 'pointerup', R ); dom.removeEventListener( 'keydown', R ); dom.removeEventListener( 'keyup', R ); window.removeEventListener( 'resize', R.resize ); R.isEventsInit = false; }, resize: function () { R.needReZone = true; let i = R.ui.length, u; while( i-- ){ u = R.ui[i]; if( u.isGui && !u.isCanvasOnly && u.autoResize ) u.calc(); } }, out: function () { console.log('im am out'); R.clearOldID(); }, in: function () { console.log('im am in'); // R.clearOldID(); }, // ---------------------- // HANDLE EVENTS // ---------------------- fakeUp: function(){ this.handleEvent( {type:'pointerup'} ); }, handleEvent: function ( event ) { //if(!event.type) return; if( R.prevDefault.indexOf( event.type ) !== -1 ) event.preventDefault(); R.findZone(); let e = R.e; let leave = false; if( event.type === 'keydown') R.keydown( event ); if( event.type === 'keyup') R.keyup( event ); if( event.type === 'wheel' ) e.delta = event.deltaY > 0 ? 1 : -1; else e.delta = 0; let ptype = event.pointerType; // mouse, pen, touch e.clientX = ( ptype === 'touch' ? event.pageX : event.clientX ) || 0; e.clientY = ( ptype === 'touch' ? event.pageY : event.clientY ) || 0; e.type = event.type; if( R.eventOut.indexOf( event.type ) !== -1 ){ leave = true; e.type = 'mouseup'; } if( event.type === 'pointerleave'){ R.isLeave = true; } if( event.type === 'pointerdown') e.type = 'mousedown'; if( event.type === 'pointerup') e.type = 'mouseup'; if( event.type === 'pointermove'){ if( R.isLeave ){ // if user resize outside this document R.isLeave = false; R.resize(); } e.type = 'mousemove'; } // double click test if( e.type === 'mousedown' ) { R.downTime = R.now(); let time = R.downTime - R.prevTime; // double click on imput if( time < 200 ) { R.selectAll(); return false } R.prevTime = R.downTime; } // for imput if( e.type === 'mousedown' ) R.clearInput(); // mouse lock if( e.type === 'mousedown' ) R.lock = true; if( e.type === 'mouseup' ) R.lock = false; //if( R.current !== null && R.current.neverlock ) R.lock = false; /*if( e.type === 'mousedown' && event.button === 1){ R.cursor() e.preventDefault(); e.stopPropagation(); }*/ if( R.isMobile && e.type === 'mousedown' ) R.findID( e ); if( e.type === 'mousemove' && !R.lock ) R.findID( e ); if( R.ID !== null ){ if( R.ID.isCanvasOnly ) { e.clientX = R.ID.mouse.x; e.clientY = R.ID.mouse.y; } R.ID.handleEvent( e ); } if( R.isMobile && e.type === 'mouseup' ) R.clearOldID(); if( leave ) R.clearOldID(); }, // ---------------------- // ID // ---------------------- findID: function ( e ) { let i = R.ui.length, next = -1, u, x, y; while( i-- ){ u = R.ui[i]; if( u.isCanvasOnly ) { x = u.mouse.x; y = u.mouse.y; } else { x = e.clientX; y = e.clientY; } if( R.onZone( u, x, y ) ){ next = i; if( next !== R.current ){ R.clearOldID(); R.current = next; R.ID = u; } break; } } if( next === -1 ) R.clearOldID(); }, clearOldID: function () { if( !R.ID ) return; R.current = -1; R.ID.reset(); R.ID = null; R.cursor(); }, // ---------------------- // GUI / GROUP FUNCTION // ---------------------- calcUis: function ( uis, zone, py ) { //console.log('calc_uis') let i = uis.length, u, px = 0, n = 0, tw; let height = 0; let m = 1; while( i-- ){ u = uis[n]; n++; if( u.isGroup ) u.calcUis(); u.zone.w = u.w; u.zone.h = u.h; m = u.margin; if( !u.autoWidth ){ if( px===0 ){ height += u.h + m; } u.zone.x = zone.x + px; u.zone.y = py; tw = R.getWidth(u); if( tw ) u.zone.w = u.w = tw; // focrce width if content is canvas else if( u.fw ) u.zone.w = u.w = u.fw; //console.log( u.name, u.zone.w, u.w, zone, tw ) //console.log( tw ) px += u.zone.w; if( px >= zone.w ) { py += u.h + m; px = 0; } } else { px = 0; u.zone.x = zone.x; u.zone.y = py; py += u.h + m; height += u.h + m; } } return height }, findTarget: function ( uis, e ) { let i = uis.length; while( i-- ){ if( R.onZone( uis[i], e.clientX, e.clientY ) ) return i } return -1; }, // ---------------------- // ZONE // ---------------------- findZone: function ( force ) { if( !R.needReZone && !force ) return; var i = R.ui.length, u; while( i-- ){ u = R.ui[i]; R.getZone( u ); if( u.isGui ) u.calcUis(); } R.needReZone = false; }, onZone: function ( o, x, y ) { if( x === undefined || y === undefined ) return false; let z = o.zone; let mx = x - z.x; let my = y - z.y; let over = ( mx >= 0 ) && ( my >= 0 ) && ( mx <= z.w ) && ( my <= z.h ); if( over ) o.local.set( mx, my ); else o.local.neg(); return over; }, getWidth: function ( o ) { //return o.getDom().offsetWidth return o.getDom().clientWidth //let r = o.getDom().getBoundingClientRect(); //return (r.width) //return Math.floor(r.width) }, getZone: function ( o ) { if( o.isCanvasOnly ) return; let r = o.getDom().getBoundingClientRect(); //if( !r.width ) return //o.zone = { x:Math.floor(r.left), y:Math.floor(r.top), w:Math.floor(r.width), h:Math.floor(r.height) }; //o.zone = { x:Math.round(r.left), y:Math.round(r.top), w:Math.round(r.width), h:Math.round(r.height) }; o.zone = { x:r.left, y:r.top, w:r.width, h:r.height }; //console.log(o.name, o.zone) }, // ---------------------- // CURSOR // ---------------------- cursor: function ( name ) { name = name ? name : 'auto'; if( name !== R.oldCursor ){ document.body.style.cursor = name; R.oldCursor = name; } }, // ---------------------- // CANVAS // ---------------------- toCanvas: function ( o, w, h, force ) { if( !R.xmlserializer ) R.xmlserializer = new XMLSerializer(); // prevent exesive redraw if( force && R.tmpTime !== null ) { clearTimeout(R.tmpTime); R.tmpTime = null; } if( R.tmpTime !== null ) return; if( R.lock ) R.tmpTime = setTimeout( function(){ R.tmpTime = null; }, 10 ); /// let isNewSize = false; if( w !== o.canvas.width || h !== o.canvas.height ) isNewSize = true; if( R.tmpImage === null ) R.tmpImage = new Image(); let img = R.tmpImage; //new Image(); let htmlString = R.xmlserializer.serializeToString( o.content ); let svg = ''+ htmlString +''; img.onload = function() { let ctx = o.canvas.getContext("2d"); if( isNewSize ){ o.canvas.width = w; o.canvas.height = h; }else { ctx.clearRect( 0, 0, w, h ); } ctx.drawImage( this, 0, 0 ); o.onDraw(); }; img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg); //img.src = 'data:image/svg+xml;base64,'+ window.btoa( svg ); img.crossOrigin = ''; }, // ---------------------- // INPUT // ---------------------- setHidden: function () { if( R.hiddenImput === null ){ //let css = R.parent.css.txtselect + 'padding:0; width:auto; height:auto; ' //let css = R.parent.css.txt + 'padding:0; width:auto; height:auto; text-shadow:none;' //css += 'left:10px; top:auto; border:none; color:#FFF; background:#000;' + hide; R.hiddenImput = document.createElement('input'); R.hiddenImput.type = 'text'; //R.hiddenImput.style.cssText = css + 'bottom:30px;' + (R.debugInput ? '' : 'transform:scale(0);'); R.hiddenSizer = document.createElement('div'); //R.hiddenSizer.style.cssText = css + 'bottom:60px;'; document.body.appendChild( R.hiddenImput ); document.body.appendChild( R.hiddenSizer ); } let hide = R.debugInput ? '' : 'opacity:0; zIndex:0;'; let css = R.parent.css.txtselect + 'padding:0; width:auto; height:auto; left:10px; top:auto; color:#FFF; background:#000;'+ hide; R.hiddenImput.style.cssText = css + 'bottom:10px;' + (R.debugInput ? '' : 'transform:scale(0);'); R.hiddenSizer.style.cssText = css + 'bottom:40px;'; R.hiddenImput.style.width = R.input.clientWidth + 'px'; R.hiddenImput.value = R.str; R.hiddenSizer.innerHTML = R.str; R.hasFocus = true; }, clearHidden: function ( p ) { if( R.hiddenImput === null ) return; R.hasFocus = false; }, clickPos: function( x ){ let i = R.str.length, l = 0, n = 0; while( i-- ){ l += R.textWidth( R.str[n] ); if( l >= x ) break; n++; } return n; }, upInput: function ( x, down ) { if( R.parent === null ) return false; let up = false; if( down ){ let id = R.clickPos( x ); R.moveX = id; if( R.startX === -1 ){ R.startX = id; R.cursorId = id; R.inputRange = [ R.startX, R.startX ]; } else { let isSelection = R.moveX !== R.startX; if( isSelection ){ if( R.startX > R.moveX ) R.inputRange = [ R.moveX, R.startX ]; else R.inputRange = [ R.startX, R.moveX ]; } } up = true; } else { if( R.startX !== -1 ){ R.hasFocus = true; R.hiddenImput.focus(); R.hiddenImput.selectionStart = R.inputRange[0]; R.hiddenImput.selectionEnd = R.inputRange[1]; R.startX = -1; up = true; } } if( up ) R.selectParent(); return up; }, selectAll: function (){ if(!R.parent) return R.str = R.input.textContent; R.inputRange = [0, R.str.length ]; R.hasFocus = true; R.hiddenImput.focus(); R.hiddenImput.selectionStart = R.inputRange[0]; R.hiddenImput.selectionEnd = R.inputRange[1]; R.cursorId = R.inputRange[1]; R.selectParent(); }, selectParent: function (){ var c = R.textWidth( R.str.substring( 0, R.cursorId )); var e = R.textWidth( R.str.substring( 0, R.inputRange[0] )); var s = R.textWidth( R.str.substring( R.inputRange[0], R.inputRange[1] )); R.parent.select( c, e, s, R.hiddenSizer.innerHTML ); }, textWidth: function ( text ){ if( R.hiddenSizer === null ) return 0; text = text.replace(/ /g, ' '); R.hiddenSizer.innerHTML = text; return R.hiddenSizer.clientWidth; }, clearInput: function () { if( R.parent === null ) return; if( !R.firstImput ) R.parent.validate( true ); R.clearHidden(); R.parent.unselect(); //R.input.style.background = 'none'; R.input.style.background = R.parent.colors.back; R.input.style.borderColor = R.parent.colors.border; //R.input.style.color = R.parent.colors.text; R.parent.isEdit = false; R.input = null; R.parent = null; R.str = '', R.firstImput = true; }, setInput: function ( Input, parent ) { R.clearInput(); R.input = Input; R.parent = parent; R.input.style.background = R.parent.colors.backoff; R.input.style.borderColor = R.parent.colors.select; //R.input.style.color = R.parent.colors.textSelect; R.str = R.input.textContent; R.setHidden(); }, keydown: function ( e ) { if( R.parent === null ) return; let keyCode = e.which; e.shiftKey; //console.log( keyCode ) R.firstImput = false; if (R.hasFocus) { // hack to fix touch event bug in iOS Safari window.focus(); R.hiddenImput.focus(); } R.parent.isEdit = true; // e.preventDefault(); // add support for Ctrl/Cmd+A selection //if ( keyCode === 65 && (e.ctrlKey || e.metaKey )) { //R.selectText(); //e.preventDefault(); //return self.render(); //} if( keyCode === 13 ){ //enter R.clearInput(); //} else if( keyCode === 9 ){ //tab key // R.input.textContent = ''; } else { if( R.input.isNum ){ if ( ((e.keyCode > 47) && (e.keyCode < 58)) || ((e.keyCode > 95) && (e.keyCode < 106)) || e.keyCode === 190 || e.keyCode === 110 || e.keyCode === 8 || e.keyCode === 109 ){ R.hiddenImput.readOnly = false; } else { R.hiddenImput.readOnly = true; } } else { R.hiddenImput.readOnly = false; } } }, keyup: function ( e ) { if( R.parent === null ) return; R.str = R.hiddenImput.value; if( R.parent.allEqual ) R.parent.sameStr( R.str );// numeric samùe value else R.input.textContent = R.str; R.cursorId = R.hiddenImput.selectionStart; R.inputRange = [ R.hiddenImput.selectionStart, R.hiddenImput.selectionEnd ]; R.selectParent(); //if( R.parent.allway ) R.parent.validate(); }, // ---------------------- // // LISTENING // // ---------------------- loop: function () { if( R.isLoop ) requestAnimationFrame( R.loop ); R.update(); }, update: function () { let i = R.listens.length; while( i-- ) R.listens[i].listening(); }, removeListen: function ( proto ) { let id = R.listens.indexOf( proto ); if( id !== -1 ) R.listens.splice(id, 1); if( R.listens.length === 0 ) R.isLoop = false; }, addListen: function ( proto ) { let id = R.listens.indexOf( proto ); if( id !== -1 ) return false; R.listens.push( proto ); if( !R.isLoop ){ R.isLoop = true; R.loop(); } return true; }, }; const Roots = R; /** * @author lth / https://github.com/lo-th */ const T = { transition: 0.2, frag: document.createDocumentFragment(), colorRing: null, joystick_0: null, joystick_1: null, circular: null, knob: null, pad2d: null, svgns: "http://www.w3.org/2000/svg", links: "http://www.w3.org/1999/xlink", htmls: "http://www.w3.org/1999/xhtml", DOM_SIZE: [ 'height', 'width', 'top', 'left', 'bottom', 'right', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom'], SVG_TYPE_D: [ 'pattern', 'defs', 'transform', 'stop', 'animate', 'radialGradient', 'linearGradient', 'animateMotion', 'use', 'filter', 'feColorMatrix' ], SVG_TYPE_G: [ 'svg', 'rect', 'circle', 'path', 'polygon', 'text', 'g', 'line', 'foreignObject' ], PI: Math.PI, TwoPI: Math.PI*2, pi90: Math.PI * 0.5, pi60: Math.PI/3, torad: Math.PI / 180, todeg: 180 / Math.PI, clamp: function (v, min, max) { v = v < min ? min : v; v = v > max ? max : v; return v; }, size: { w: 240, h: 20, p: 30, s: 8 }, // ---------------------- // COLOR // ---------------------- defineColor: function( o, cc = T.colors ) { let color = { ...cc }; let textChange = ['fontFamily', 'fontWeight', 'fontShadow', 'fontSize' ]; let changeText = false; if( o.font ) o.fontFamily = o.font; if( o.shadow ) o.fontShadow = o.shadow; if( o.weight ) o.fontWeight = o.weight; if( o.fontColor ) o.text = o.fontColor; if( o.color ) o.text = o.color; if( o.text ){ color.text = o.text; if( !o.fontColor && !o.color ){ color.title = T.ColorLuma( o.text, -0.25 ); color.titleoff = T.ColorLuma( o.text, -0.5 ); } color.textOver = T.ColorLuma( o.text, 0.25 ); color.textSelect = T.ColorLuma( o.text, 0.5 ); } if( o.button ){ color.button = o.button; color.border = T.ColorLuma( o.button, 0.1 ); color.overoff = T.ColorLuma( o.button, 0.2 ); } if( o.select ){ color.select = o.select; color.over = T.ColorLuma( o.select, -0.1 ); } if( o.itemBg ) o.back = o.itemBg; if( o.back ){ color.back = o.back; color.backoff = T.ColorLuma( o.back, -0.1 ); } if( o.fontSelect ) color.textSelect = o.fontSelect; if( o.groupBorder ) color.gborder = o.groupBorder; if( o.transparent ) o.bg = 'none'; if( o.bg ) color.background = color.backgroundOver = o.bg; if( o.bgOver ) color.backgroundOver = o.bgOver; for( let m in color ){ if(o[m]) color[m] = o[m]; } for( let m in o ){ if( textChange.indexOf(m) !== -1 ) changeText = true; } if( changeText ) T.defineText( color ); return color }, colors: { content:'none', background: 'rgba(50,50,50,0.3)', backgroundOver: 'rgba(50,50,50,0.4)', title : '#CCC', titleoff : '#BBB', text : '#DDD', textOver : '#EEE', textSelect : '#FFF', //inputBg: 'rgba(0,0,0,0.25)', //itemBg:'rgba(0,0,0,0.25)', back:'rgba(0,0,0,0.2)', backoff:'rgba(0,0,0,0.3)', //inputOver: 'rgba(0,0,0,0.2)', // input and button border border : '#4c4c4c', borderSize : 1, gborder : 'none', button : '#3c3c3c', overoff : '#5c5c5c', over : '#024699', select : '#308AFF', action: '#FF3300', //fontFamily: 'Tahoma', fontFamily: 'Consolas,monaco,monospace', fontWeight: 'normal', fontShadow: '#000', fontSize:12, radius:4, hide: 'rgba(0,0,0,0)', }, // style css css : { //unselect: '-o-user-select:none; -ms-user-select:none; -khtml-user-select:none; -webkit-user-select:none; -moz-user-select:none;', basic: 'position:absolute; pointer-events:none; box-sizing:border-box; margin:0; padding:0; overflow:hidden; ' + '-o-user-select:none; -ms-user-select:none; -khtml-user-select:none; -webkit-user-select:none; -moz-user-select:none;', button:'display:flex; justify-content:center; align-items:center; text-align:center;', /*txt: T.css.basic + 'font-family:'+ T.colors.fontFamily +'; font-size:'+T.colors.fontSize+'px; color:'+T.colors.text+'; padding:2px 10px; left:0; top:2px; height:16px; width:100px; overflow:hidden; white-space: nowrap;', txtselect: T.css.txt + 'display:flex; justify-content:left; align-items:center; text-align:left;' +'padding:2px 5px; border:1px dashed ' + T.colors.border + '; background:'+ T.colors.txtselectbg+';', item: T.css.txt + 'position:relative; background:rgba(0,0,0,0.2); margin-bottom:1px;',*/ }, // svg path svgs: { group:'M 7 7 L 7 8 8 8 8 7 7 7 M 5 7 L 5 8 6 8 6 7 5 7 M 3 7 L 3 8 4 8 4 7 3 7 M 7 5 L 7 6 8 6 8 5 7 5 M 6 6 L 6 5 5 5 5 6 6 6 M 7 3 L 7 4 8 4 8 3 7 3 M 6 4 L 6 3 5 3 5 4 6 4 M 3 5 L 3 6 4 6 4 5 3 5 M 3 3 L 3 4 4 4 4 3 3 3 Z', arrow:'M 3 8 L 8 5 3 2 3 8 Z', arrowDown:'M 5 8 L 8 3 2 3 5 8 Z', arrowUp:'M 5 2 L 2 7 8 7 5 2 Z', solid:'M 13 10 L 13 1 4 1 1 4 1 13 10 13 13 10 M 11 3 L 11 9 9 11 3 11 3 5 5 3 11 3 Z', body:'M 13 10 L 13 1 4 1 1 4 1 13 10 13 13 10 M 11 3 L 11 9 9 11 3 11 3 5 5 3 11 3 M 5 4 L 4 5 4 10 9 10 10 9 10 4 5 4 Z', vehicle:'M 13 6 L 11 1 3 1 1 6 1 13 3 13 3 11 11 11 11 13 13 13 13 6 M 2.4 6 L 4 2 10 2 11.6 6 2.4 6 M 12 8 L 12 10 10 10 10 8 12 8 M 4 8 L 4 10 2 10 2 8 4 8 Z', articulation:'M 13 9 L 12 9 9 2 9 1 5 1 5 2 2 9 1 9 1 13 5 13 5 9 4 9 6 5 8 5 10 9 9 9 9 13 13 13 13 9 Z', character:'M 13 4 L 12 3 9 4 5 4 2 3 1 4 5 6 5 8 4 13 6 13 7 9 8 13 10 13 9 8 9 6 13 4 M 6 1 L 6 3 8 3 8 1 6 1 Z', terrain:'M 13 8 L 12 7 Q 9.06 -3.67 5.95 4.85 4.04 3.27 2 7 L 1 8 7 13 13 8 M 3 8 Q 3.78 5.420 5.4 6.6 5.20 7.25 5 8 L 7 8 Q 8.39 -0.16 11 8 L 7 11 3 8 Z', joint:'M 7.7 7.7 Q 8 7.45 8 7 8 6.6 7.7 6.3 7.45 6 7 6 6.6 6 6.3 6.3 6 6.6 6 7 6 7.45 6.3 7.7 6.6 8 7 8 7.45 8 7.7 7.7 M 3.35 8.65 L 1 11 3 13 5.35 10.65 Q 6.1 11 7 11 8.28 11 9.25 10.25 L 7.8 8.8 Q 7.45 9 7 9 6.15 9 5.55 8.4 5 7.85 5 7 5 6.54 5.15 6.15 L 3.7 4.7 Q 3 5.712 3 7 3 7.9 3.35 8.65 M 10.25 9.25 Q 11 8.28 11 7 11 6.1 10.65 5.35 L 13 3 11 1 8.65 3.35 Q 7.9 3 7 3 5.7 3 4.7 3.7 L 6.15 5.15 Q 6.54 5 7 5 7.85 5 8.4 5.55 9 6.15 9 7 9 7.45 8.8 7.8 L 10.25 9.25 Z', ray:'M 9 11 L 5 11 5 12 9 12 9 11 M 12 5 L 11 5 11 9 12 9 12 5 M 11.5 10 Q 10.9 10 10.45 10.45 10 10.9 10 11.5 10 12.2 10.45 12.55 10.9 13 11.5 13 12.2 13 12.55 12.55 13 12.2 13 11.5 13 10.9 12.55 10.45 12.2 10 11.5 10 M 9 10 L 10 9 2 1 1 2 9 10 Z', collision:'M 11 12 L 13 10 10 7 13 4 11 2 7.5 5.5 9 7 7.5 8.5 11 12 M 3 2 L 1 4 4 7 1 10 3 12 8 7 3 2 Z', map:'M 13 1 L 1 1 1 13 13 13 13 1 M 12 2 L 12 7 7 7 7 12 2 12 2 7 7 7 7 2 12 2 Z', material:'M 13 1 L 1 1 1 13 13 13 13 1 M 12 2 L 12 7 7 7 7 12 2 12 2 7 7 7 7 2 12 2 Z', texture:'M 13 4 L 13 1 1 1 1 4 5 4 5 13 9 13 9 4 13 4 Z', object:'M 10 1 L 7 4 4 1 1 1 1 13 4 13 4 5 7 8 10 5 10 13 13 13 13 1 10 1 Z', none:'M 9 5 L 5 5 5 9 9 9 9 5 Z', cursor:'M 4 7 L 1 10 1 12 2 13 4 13 7 10 9 14 14 0 0 5 4 7 Z', }, getImput: function(){ return Roots.input ? true : false }, setStyle : function ( data ){ for ( var o in data ){ if( T.colors[o] ) T.colors[o] = data[o]; } T.setText(); }, // ---------------------- // custom text // ---------------------- defineText: function( o ){ T.setText( o.fontSize, o.text, o.fontFamily, o.fontShadow, o.fontWeight ); }, setText: function( size, color, font, shadow, weight ){ let cc = T.colors; if( font === undefined ) font = cc.fontFamily; if( size === undefined ) size = cc.fontSize; if( shadow === undefined ) shadow = cc.fontShadow; if( weight === undefined ) weight = cc.fontWeight; if( color === undefined ) color = cc.text; let align = 'display:flex; justify-content:left; align-items:center; text-align:left;'; T.css.txt = T.css.basic + align + 'font-family:'+ font +'; font-weight:'+weight+'; font-size:'+size+'px; color:'+cc.text+'; padding:0px 10px; left:0; top:2px; height:16px; width:100px; overflow:hidden; white-space: nowrap;'; if( shadow !== 'none' ) T.css.txt += ' text-shadow: 1px 1px 1px '+shadow+';'; T.css.txtselect = T.css.txt + 'padding:0px 4px; border:1px dashed ' + cc.border + ';'; //T.css.item = T.css.txt + ' position:relative; margin-bottom:1px; '//display:block; padding:4px 4px;';// T.css.item = T.css.txt + ' position:relative; margin-bottom:1px; display:block; padding:2px 4px;';// }, // note //https://developer.mozilla.org/fr/docs/Web/CSS/css_flexible_box_layout/aligning_items_in_a_flex_container /*cloneColor: function () { let cc = Object.assign({}, T.colors ); return cc; },*/ // intern function cloneCss: function () { //let cc = Object.assign({}, T.css ); return { ...T.css }; }, clone: function ( o ) { return o.cloneNode( true ); }, setSvg: function( dom, type, value, id, id2 ){ if( id === -1 ) dom.setAttributeNS( null, type, value ); else if( id2 !== undefined ) dom.childNodes[ id || 0 ].childNodes[ id2 || 0 ].setAttributeNS( null, type, value ); else dom.childNodes[ id || 0 ].setAttributeNS( null, type, value ); }, setCss: function( dom, css ){ for( let r in css ){ if( T.DOM_SIZE.indexOf(r) !== -1 ) dom.style[r] = css[r] + 'px'; else dom.style[r] = css[r]; } }, set: function( g, o ){ for( let att in o ){ if( att === 'txt' ) g.textContent = o[ att ]; if( att === 'link' ) g.setAttributeNS( T.links, 'xlink:href', o[ att ] ); else g.setAttributeNS( null, att, o[ att ] ); } }, get: function( dom, id ){ if( id === undefined ) return dom; // root else if( !isNaN( id ) ) return dom.childNodes[ id ]; // first child else if( id instanceof Array ){ if(id.length === 2) return dom.childNodes[ id[0] ].childNodes[ id[1] ]; if(id.length === 3) return dom.childNodes[ id[0] ].childNodes[ id[1] ].childNodes[ id[2] ]; } }, dom : function ( type, css, obj, dom, id ) { type = type || 'div'; if( T.SVG_TYPE_D.indexOf(type) !== -1 || T.SVG_TYPE_G.indexOf(type) !== -1 ){ // is svg element if( type ==='svg' ){ dom = document.createElementNS( T.svgns, 'svg' ); T.set( dom, obj ); /* } else if ( type === 'use' ) { dom = document.createElementNS( T.svgns, 'use' ); T.set( dom, obj ); */ } else { // create new svg if not def if( dom === undefined ) dom = document.createElementNS( T.svgns, 'svg' ); T.addAttributes( dom, type, obj, id ); } } else { // is html element if( dom === undefined ) dom = document.createElementNS( T.htmls, type ); else dom = dom.appendChild( document.createElementNS( T.htmls, type ) ); } if( css ) dom.style.cssText = css; if( id === undefined ) return dom; else return dom.childNodes[ id || 0 ]; }, addAttributes : function( dom, type, o, id ){ let g = document.createElementNS( T.svgns, type ); T.set( g, o ); T.get( dom, id ).appendChild( g ); if( T.SVG_TYPE_G.indexOf(type) !== -1 ) g.style.pointerEvents = 'none'; return g; }, clear : function( dom ){ T.purge( dom ); while (dom.firstChild) { if ( dom.firstChild.firstChild ) T.clear( dom.firstChild ); dom.removeChild( dom.firstChild ); } }, purge : function ( dom ) { let a = dom.attributes, i, n; if (a) { i = a.length; while(i--){ n = a[i].name; if (typeof dom[n] === 'function') dom[n] = null; } } a = dom.childNodes; if (a) { i = a.length; while(i--){ T.purge( dom.childNodes[i] ); } } }, // ---------------------- // SVG Effects function // ---------------------- addSVGGlowEffect: function () { if ( document.getElementById( 'UILGlow') !== null ) return; let svgFilter = T.initUILEffects(); let filter = T.addAttributes( svgFilter, 'filter', { id: 'UILGlow', x: '-20%', y: '-20%', width: '140%', height: '140%' } ); T.addAttributes( filter, 'feGaussianBlur', { in: 'SourceGraphic', stdDeviation: '3', result: 'uilBlur' } ); let feMerge = T.addAttributes( filter, 'feMerge', { } ); for( let i = 0; i <= 3; i++ ) { T.addAttributes( feMerge, 'feMergeNode', { in: 'uilBlur' } ); } T.addAttributes( feMerge, 'feMergeNode', { in: 'SourceGraphic' } ); }, initUILEffects: function () { let svgFilter = document.getElementById( 'UILSVGEffects'); if ( svgFilter === null ) { svgFilter = T.dom( 'svg', undefined , { id: 'UILSVGEffects', width: '0', height: '0' } ); document.body.appendChild( svgFilter ); } return svgFilter; }, // ---------------------- // Color function // ---------------------- ColorLuma : function ( hex, l ) { if( hex === 'n' ) hex = '#000'; // validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, ''); if (hex.length < 6) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; } l = l || 0; // convert to decimal and change luminosity let rgb = "#", c, i; for (i = 0; i < 3; i++) { c = parseInt(hex.substr(i*2,2), 16); c = Math.round(Math.min(Math.max(0, c + (c * l)), 255)).toString(16); rgb += ("00"+c).substr(c.length); } return rgb; }, findDeepInver: function ( c ) { return (c[0] * 0.3 + c[1] * .59 + c[2] * .11) <= 0.6; }, lerpColor: function( c1, c2, factor ) { let newColor = {}; for ( let i = 0; i < 3; i++ ) { newColor[i] = c1[ i ] + ( c2[ i ] - c1[ i ] ) * factor; } return newColor; }, hexToHtml: function ( v ) { v = v === undefined ? 0x000000 : v; return "#" + ("000000" + v.toString(16)).substr(-6); }, htmlToHex: function ( v ) { return v.toUpperCase().replace("#", "0x"); }, u255: function (c, i) { return parseInt(c.substring(i, i + 2), 16) / 255; }, u16: function ( c, i ) { return parseInt(c.substring(i, i + 1), 16) / 15; }, unpack: function( c ){ if (c.length == 7) return [ T.u255(c, 1), T.u255(c, 3), T.u255(c, 5) ]; else if (c.length == 4) return [ T.u16(c,1), T.u16(c,2), T.u16(c,3) ]; }, p255: function ( c ) { let h = Math.round( ( c * 255 ) ).toString( 16 ); if ( h.length < 2 ) h = '0' + h; return h; }, pack: function ( c ) { return '#' + T.p255( c[ 0 ] ) + T.p255( c[ 1 ] ) + T.p255( c[ 2 ] ); }, htmlRgb: function( c ){ return 'rgb(' + Math.round(c[0] * 255) + ','+ Math.round(c[1] * 255) + ','+ Math.round(c[2] * 255) + ')'; }, pad: function( n ){ if(n.length == 1)n = '0' + n; return n; }, rgbToHex : function( c ){ let r = Math.round(c[0] * 255).toString(16); let g = Math.round(c[1] * 255).toString(16); let b = Math.round(c[2] * 255).toString(16); return '#' + T.pad(r) + T.pad(g) + T.pad(b); // return '#' + ( '000000' + ( ( c[0] * 255 ) << 16 ^ ( c[1] * 255 ) << 8 ^ ( c[2] * 255 ) << 0 ).toString( 16 ) ).slice( - 6 ); }, hueToRgb: function( p, q, t ){ if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; }, rgbToHsl: function ( c ) { let r = c[0], g = c[1], b = c[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h = 0, s = 0, l = (min + max) / 2; if (l > 0 && l < 1) s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l)); if (delta > 0) { if (max == r && max != g) h += (g - b) / delta; if (max == g && max != b) h += (2 + (b - r) / delta); if (max == b && max != r) h += (4 + (r - g) / delta); h /= 6; } return [ h, s, l ]; }, hslToRgb: function ( c ) { let p, q, h = c[0], s = c[1], l = c[2]; if ( s === 0 ) return [ l, l, l ]; else { q = l <= 0.5 ? l * (s + 1) : l + s - ( l * s ); p = l * 2 - q; return [ T.hueToRgb(p, q, h + 0.33333), T.hueToRgb(p, q, h), T.hueToRgb(p, q, h - 0.33333) ]; } }, // ---------------------- // SVG MODEL // ---------------------- makeGradiant: function ( type, settings, parent, colors ) { T.dom( type, null, settings, parent, 0 ); let n = parent.childNodes[0].childNodes.length - 1, c; for( let i = 0; i < colors.length; i++ ){ c = colors[i]; //T.dom( 'stop', null, { offset:c[0]+'%', style:'stop-color:'+c[1]+'; stop-opacity:'+c[2]+';' }, parent, [0,n] ); T.dom( 'stop', null, { offset:c[0]+'%', 'stop-color':c[1], 'stop-opacity':c[2] }, parent, [0,n] ); } }, /*makeGraph: function () { let w = 128; let radius = 34; let svg = T.dom( 'svg', T.css.basic , { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'path', '', { d:'', stroke:T.colors.text, 'stroke-width':4, fill:'none', 'stroke-linecap':'butt' }, svg );//0 //T.dom( 'rect', '', { x:10, y:10, width:108, height:108, stroke:'rgba(0,0,0,0.3)', 'stroke-width':2 , fill:'none'}, svg );//1 //T.dom( 'circle', '', { cx:64, cy:64, r:radius, fill:T.colors.button, stroke:'rgba(0,0,0,0.3)', 'stroke-width':8 }, svg );//0 //T.dom( 'circle', '', { cx:64, cy:64, r:radius+7, stroke:'rgba(0,0,0,0.3)', 'stroke-width':7 , fill:'none'}, svg );//2 //T.dom( 'path', '', { d:'', stroke:'rgba(255,255,255,0.3)', 'stroke-width':2, fill:'none', 'stroke-linecap':'round', 'stroke-opacity':0.5 }, svg );//3 T.graph = svg; },*/ makePad: function ( model ) { let ww = 256; let svg = T.dom( 'svg', T.css.basic + 'position:relative;', { viewBox:'0 0 '+ww+' '+ww, width:ww, height:ww, preserveAspectRatio:'none' } ); let w = 200; let d = (ww-w)*0.5, m = 20; Tools.dom( 'rect', '', { x: d, y: d, width: w, height: w, fill:T.colors.back }, svg ); // 0 Tools.dom( 'rect', '', { x: d+m*0.5, y: d+m*0.5, width: w - m , height: w - m, fill:T.colors.button }, svg ); // 1 // Pointer Tools.dom( 'line', '', { x1: d+(m*0.5), y1: ww *0.5, x2: d+(w-m*0.5), y2: ww * 0.5, stroke:T.colors.back, 'stroke-width': 2 }, svg ); // 2 Tools.dom( 'line', '', { x1: ww * 0.5, x2: ww * 0.5, y1: d+(m*0.5), y2: d+(w-m*0.5), stroke:T.colors.back, 'stroke-width': 2 }, svg ); // 3 Tools.dom( 'circle', '', { cx: ww * 0.5, cy: ww * 0.5, r:5, stroke: T.colors.text, 'stroke-width': 5, fill:'none' }, svg ); // 4 T.pad2d = svg; }, makeKnob: function ( model ) { let w = 128; let radius = 34; let svg = T.dom( 'svg', T.css.basic + 'position:relative;', { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'circle', '', { cx:64, cy:64, r:radius, fill:T.colors.button, stroke:'rgba(0,0,0,0.3)', 'stroke-width':8 }, svg );//0 T.dom( 'path', '', { d:'', stroke:T.colors.text, 'stroke-width':4, fill:'none', 'stroke-linecap':'round' }, svg );//1 T.dom( 'circle', '', { cx:64, cy:64, r:radius+7, stroke:'rgba(0,0,0,0.1)', 'stroke-width':7 , fill:'none'}, svg );//2 T.dom( 'path', '', { d:'', stroke:'rgba(255,255,255,0.3)', 'stroke-width':2, fill:'none', 'stroke-linecap':'round', 'stroke-opacity':0.5 }, svg );//3 T.knob = svg; }, makeCircular: function ( model ) { let w = 128; let radius = 40; let svg = T.dom( 'svg', T.css.basic + 'position:relative;', { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'circle', '', { cx:64, cy:64, r:radius, stroke:'rgba(0,0,0,0.1)', 'stroke-width':10, fill:'none' }, svg );//0 T.dom( 'path', '', { d:'', stroke:T.colors.text, 'stroke-width':7, fill:'none', 'stroke-linecap':'butt' }, svg );//1 T.circular = svg; }, makeJoystick: function ( model ) { //+' background:#f00;' let w = 128, ccc; let radius = Math.floor((w-30)*0.5); let innerRadius = Math.floor(radius*0.6); let svg = T.dom( 'svg', T.css.basic + 'position:relative;', { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'defs', null, {}, svg ); T.dom( 'g', null, {}, svg ); if( model === 0 ){ // gradian background ccc = [ [40, 'rgb(0,0,0)', 0.3], [80, 'rgb(0,0,0)', 0], [90, 'rgb(50,50,50)', 0.4], [100, 'rgb(50,50,50)', 0] ]; T.makeGradiant( 'radialGradient', { id:'grad', cx:'50%', cy:'50%', r:'50%', fx:'50%', fy:'50%' }, svg, ccc ); // gradian shadow ccc = [ [60, 'rgb(0,0,0)', 0.5], [100, 'rgb(0,0,0)', 0] ]; T.makeGradiant( 'radialGradient', { id:'gradS', cx:'50%', cy:'50%', r:'50%', fx:'50%', fy:'50%' }, svg, ccc ); // gradian stick let cc0 = ['rgb(40,40,40)', 'rgb(48,48,48)', 'rgb(30,30,30)']; let cc1 = ['rgb(1,90,197)', 'rgb(3,95,207)', 'rgb(0,65,167)']; ccc = [ [30, cc0[0], 1], [60, cc0[1], 1], [80, cc0[1], 1], [100, cc0[2], 1] ]; T.makeGradiant( 'radialGradient', { id:'gradIn', cx:'50%', cy:'50%', r:'50%', fx:'50%', fy:'50%' }, svg, ccc ); ccc = [ [30, cc1[0], 1], [60, cc1[1], 1], [80, cc1[1], 1], [100, cc1[2], 1] ]; T.makeGradiant( 'radialGradient', { id:'gradIn2', cx:'50%', cy:'50%', r:'50%', fx:'50%', fy:'50%' }, svg, ccc ); // graph T.dom( 'circle', '', { cx:64, cy:64, r:radius, fill:'url(#grad)' }, svg );//2 T.dom( 'circle', '', { cx:64+5, cy:64+10, r:innerRadius+10, fill:'url(#gradS)' }, svg );//3 T.dom( 'circle', '', { cx:64, cy:64, r:innerRadius, fill:'url(#gradIn)' }, svg );//4 T.joystick_0 = svg; } else { // gradian shadow ccc = [ [69, 'rgb(0,0,0)', 0],[70, 'rgb(0,0,0)', 0.3], [100, 'rgb(0,0,0)', 0] ]; T.makeGradiant( 'radialGradient', { id:'gradX', cx:'50%', cy:'50%', r:'50%', fx:'50%', fy:'50%' }, svg, ccc ); T.dom( 'circle', '', { cx:64, cy:64, r:radius, fill:'none', stroke:'rgba(100,100,100,0.25)', 'stroke-width':'4' }, svg );//2 T.dom( 'circle', '', { cx:64, cy:64, r:innerRadius+14, fill:'url(#gradX)' }, svg );//3 T.dom( 'circle', '', { cx:64, cy:64, r:innerRadius, fill:'none', stroke:'rgb(100,100,100)', 'stroke-width':'4' }, svg );//4 T.joystick_1 = svg; } }, makeColorRing: function () { let w = 256; let svg = T.dom( 'svg', T.css.basic + 'position:relative;', { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'defs', null, {}, svg ); T.dom( 'g', null, {}, svg ); let s = 30;//stroke let r =( w-s )*0.5; let mid = w*0.5; let n = 24, nudge = 8 / r / n * Math.PI, a1 = 0; let am, tan, d2, a2, ar, i, j, path, ccc; let color = []; for ( i = 0; i <= n; ++i) { d2 = i / n; a2 = d2 * T.TwoPI; am = (a1 + a2) * 0.5; tan = 1 / Math.cos((a2 - a1) * 0.5); ar = [ Math.sin(a1), -Math.cos(a1), Math.sin(am) * tan, -Math.cos(am) * tan, Math.sin(a2), -Math.cos(a2) ]; color[1] = T.rgbToHex( T.hslToRgb([d2, 1, 0.5]) ); if (i > 0) { j = 6; while(j--){ ar[j] = ((ar[j]*r)+mid).toFixed(2); } path = ' M' + ar[0] + ' ' + ar[1] + ' Q' + ar[2] + ' ' + ar[3] + ' ' + ar[4] + ' ' + ar[5]; ccc = [ [0,color[0],1], [100,color[1],1] ]; T.makeGradiant( 'linearGradient', { id:'G'+i, x1:ar[0], y1:ar[1], x2:ar[4], y2:ar[5], gradientUnits:"userSpaceOnUse" }, svg, ccc ); T.dom( 'path', '', { d:path, 'stroke-width':s, stroke:'url(#G'+i+')', 'stroke-linecap':"butt" }, svg, 1 ); } a1 = a2 - nudge; color[0] = color[1]; } let tw = 84.90; // black / white ccc = [ [0, '#FFFFFF', 1], [50, '#FFFFFF', 0], [50, '#000000', 0], [100, '#000000', 1] ]; T.makeGradiant( 'linearGradient', { id:'GL0', x1:0, y1:mid-tw, x2:0, y2:mid+tw, gradientUnits:"userSpaceOnUse" }, svg, ccc ); ccc = [ [0, '#7f7f7f', 1], [50, '#7f7f7f', 0.5], [100, '#7f7f7f', 0] ]; T.makeGradiant( 'linearGradient', { id:'GL1', x1:mid-49.05, y1:0, x2:mid+98, y2:0, gradientUnits:"userSpaceOnUse" }, svg, ccc ); T.dom( 'g', null, { 'transform-origin': '128px 128px', 'transform':'rotate(0)' }, svg );//2 T.dom( 'polygon', '', { points:'78.95 43.1 78.95 212.85 226 128', fill:'red' }, svg, 2 );// 2,0 T.dom( 'polygon', '', { points:'78.95 43.1 78.95 212.85 226 128', fill:'url(#GL1)','stroke-width':1, stroke:'url(#GL1)' }, svg, 2 );//2,1 T.dom( 'polygon', '', { points:'78.95 43.1 78.95 212.85 226 128', fill:'url(#GL0)','stroke-width':1, stroke:'url(#GL0)' }, svg, 2 );//2,2 T.dom( 'path', '', { d:'M 255.75 136.5 Q 256 132.3 256 128 256 123.7 255.75 119.5 L 241 128 255.75 136.5 Z', fill:'none','stroke-width':2, stroke:'#000' }, svg, 2 );//2,3 //T.dom( 'circle', '', { cx:128+113, cy:128, r:6, 'stroke-width':3, stroke:'#000', fill:'none' }, svg, 2 );//2.3 T.dom( 'circle', '', { cx:128, cy:128, r:6, 'stroke-width':2, stroke:'#000', fill:'none' }, svg );//3 T.colorRing = svg; }, icon: function ( type, color, w ){ w = w || 40; //color = color || '#DEDEDE'; let viewBox = '0 0 256 256'; //let viewBox = '0 0 '+ w +' '+ w; let t = [""]; switch(type){ case 'logo': t[1]=""; break; case 'donate': t[1]=""; break; case 'neo': t[1]=""; break; case 'github': t[1]=""; break; case 'save': t[1]=""; break; } t[2] = ""; return t.join("\n"); }, logoFill_d:` M 171 150.75 L 171 33.25 155.5 33.25 155.5 150.75 Q 155.5 162.2 147.45 170.2 139.45 178.25 128 178.25 116.6 178.25 108.55 170.2 100.5 162.2 100.5 150.75 L 100.5 33.25 85 33.25 85 150.75 Q 85 168.65 97.55 181.15 110.15 193.75 128 193.75 145.9 193.75 158.4 181.15 171 168.65 171 150.75 M 200 33.25 L 184 33.25 184 150.8 Q 184 174.1 167.6 190.4 151.3 206.8 128 206.8 104.75 206.8 88.3 190.4 72 174.1 72 150.8 L 72 33.25 56 33.25 56 150.75 Q 56 180.55 77.05 201.6 98.2 222.75 128 222.75 157.8 222.75 178.9 201.6 200 180.55 200 150.75 L 200 33.25 Z `, logo_github:` M 180.5 70 Q 186.3 82.4 181.55 96.55 196.5 111.5 189.7 140.65 183.65 168.35 146 172.7 152.5 178.7 152.55 185.9 L 152.55 218.15 Q 152.84 224.56 159.15 223.3 159.21 223.3 159.25 223.3 181.14 216.25 198.7 198.7 228 169.4 228 128 228 86.6 198.7 57.3 169.4 28 128 28 86.6 28 57.3 57.3 28 86.6 28 128 28 169.4 57.3 198.7 74.85 216.25 96.75 223.3 96.78 223.3 96.8 223.3 103.16 224.54 103.45 218.15 L 103.45 200 Q 82.97 203.1 75.1 196.35 69.85 191.65 68.4 185.45 64.27 177.055 59.4 174.15 49.20 166.87 60.8 167.8 69.85 169.61 75.7 180 81.13 188.09 90 188.55 98.18 188.86 103.45 185.9 103.49 178.67 110 172.7 72.33 168.33 66.3 140.65 59.48 111.49 74.45 96.55 69.7 82.41 75.5 70 84.87 68.74 103.15 80 115.125 76.635 128 76.85 140.85 76.65 152.85 80 171.1 68.75 180.5 70 Z `, logo_neo:` M 219 52 L 206 52 206 166 Q 206 183.4 193.75 195.65 181.4 208 164 208 146.6 208 134.35 195.65 122 183.4 122 166 L 122 90 Q 122 77.6 113.15 68.85 104.4 60 92 60 79.55 60 70.75 68.85 62 77.6 62 90 L 62 204 75 204 75 90 Q 75 83 79.95 78 84.95 73 92 73 99 73 104 78 109 83 109 90 L 109 166 Q 109 188.8 125.15 204.85 141.2 221 164 221 186.75 221 202.95 204.85 219 188.8 219 166 L 219 52 M 194 52 L 181 52 181 166 Q 181 173 176.05 178 171.05 183 164 183 157 183 152 178 147 173 147 166 L 147 90 Q 147 67.2 130.85 51.15 114.8 35 92 35 69.25 35 53.05 51.15 37 67.2 37 90 L 37 204 50 204 50 90 Q 50 72.6 62.25 60.35 74.6 48 92 48 109.4 48 121.65 60.35 134 72.6 134 90 L 134 166 Q 134 178.4 142.85 187.15 151.6 196 164 196 176.45 196 185.25 187.15 194 178.4 194 166 L 194 52 Z `, logo_donate:` M 171.3 80.3 Q 179.5 62.15 171.3 45.8 164.1 32.5 141.35 30.1 L 94.35 30.1 Q 89.35 30.4 88.3 35.15 L 70.5 148.05 Q 70.2 152.5 73.7 152.6 L 100.95 152.6 107 111.6 Q 108.75 106.55 112.6 106.45 130.45 108.05 145.3 103.9 163.35 98.75 171.3 80.3 M 179.8 71.5 Q 178.6 79.75 174.9 87.85 168.45 102.9 151.9 109.15 140.65 113.95 117.55 113 113.15 112.75 111 117.45 L 102.7 169.95 Q 102.45 173.8 105.5 173.85 L 128.95 173.85 Q 132.2 174.2 133.35 169.65 L 138.3 139.95 Q 139.75 135.6 143.1 135.5 146.6 135.75 150.6 135.65 154.55 135.5 157.35 135.1 160.15 134.7 166.75 132.35 181.35 127.4 187.9 111.2 194.25 95.75 189.5 81.95 186.75 74.85 179.8 71.5 M 103.5 209.9 Q 103.5 202.85 99.7 198.85 95.95 194.75 89.4 194.75 82.8 194.75 79.05 198.85 75.3 202.9 75.3 209.9 75.3 216.85 79.05 220.95 82.8 225.05 89.4 225.05 95.95 225.05 99.7 221 103.5 216.95 103.5 209.9 M 95.45 205.5 Q 95.95 207.3 95.95 209.9 95.95 212.65 95.45 214.35 94.95 216 94 217.3 93.1 218.45 91.9 219 90.7 219.55 89.4 219.55 88.15 219.55 86.95 219.05 85.75 218.55 84.8 217.3 83.9 216.15 83.4 214.35 82.85 212.6 82.85 209.9 82.85 207.3 83.4 205.45 83.95 203.55 84.85 202.45 85.9 201.2 86.95 200.75 88.05 200.25 89.4 200.25 90.7 200.25 91.85 200.8 93.05 201.3 94 202.5 94.9 203.65 95.45 205.5 M 153.3 195.35 L 145.3 195.35 135.5 224.45 142.8 224.45 144.6 218.5 153.75 218.5 155.6 224.45 163.1 224.45 153.3 195.35 M 152.15 213.25 L 146.25 213.25 149.2 203.65 152.15 213.25 M 116.75 195.35 L 107.8 195.35 107.8 224.45 114.5 224.45 114.5 204.2 125.7 224.45 132.75 224.45 132.75 195.35 126.05 195.35 126.05 212.05 116.75 195.35 M 66.5 197.65 Q 64.15 196.15 61.45 195.75 58.8 195.35 55.75 195.35 L 46.7 195.35 46.7 224.45 55.8 224.45 Q 58.8 224.45 61.5 224.05 64.15 223.6 66.4 222.15 69.15 220.45 70.9 217.2 72.7 214 72.7 209.95 72.7 205.7 71 202.6 69.35 199.5 66.5 197.65 M 64.2 205 Q 65.2 207 65.2 209.9 65.2 212.75 64.25 214.75 63.3 216.75 61.5 217.85 60 218.85 58.3 218.9 56.6 219 54.15 219 L 54 219 54 200.8 54.15 200.8 Q 56.4 200.8 58.05 200.9 59.7 200.95 61.15 201.75 63.2 202.95 64.2 205 M 210.2 195.35 L 190.5 195.35 190.5 224.45 210.2 224.45 210.2 218.9 197.75 218.9 197.75 211.55 209.2 211.55 209.2 206 197.75 206 197.75 200.9 210.2 200.9 210.2 195.35 M 187.5 195.35 L 163 195.35 163 200.9 171.6 200.9 171.6 224.45 178.9 224.45 178.9 200.9 187.5 200.9 187.5 195.35 Z `, }; T.setText(); const Tools = T; ///https://wicg.github.io/file-system-access/#api-filesystemfilehandle-getfile class Files { //----------------------------- // FILE TYPE //----------------------------- static autoTypes( type ) { let t = []; switch( type ){ case 'json': t = [ { accept: { 'image/svg+xml': '.svg'} }, ]; break; case 'text': t = [ { description: 'Text Files', accept: { 'text/plain': ['.txt', '.text'], 'text/html': ['.html', '.htm'] } }, ]; break; case 'json': t = [ { description: 'JSON Files', accept: { 'text/plain': ['.json'] } }, ]; break; case 'image': t = [ { description: 'Images', accept: { 'image/*': ['.png', '.gif', '.jpeg', '.jpg'] } }, ]; break; } return t } //----------------------------- // LOAD //----------------------------- static async load( o = {} ) { if (typeof window.showOpenFilePicker !== 'function') { window.showOpenFilePicker = this.showOpenFilePickerPolyfill; } try { let type = o.type || ''; const options = { excludeAcceptAllOption: type ? true : false, multiple: false, //startIn:'./assets' }; options.types = this.autoTypes( type ); // create a new handle const handle = await window.showOpenFilePicker( options ); const file = await handle[0].getFile(); //let content = await file.text() if( !file ) return null let fname = file.name; let ftype = fname.substring( fname.lastIndexOf('.')+1, fname.length ); const dataUrl = [ 'png', 'jpg', 'jpeg', 'mp4', 'webm', 'ogg', 'mp3' ]; const dataBuf = [ 'sea', 'z', 'hex', 'bvh', 'BVH', 'glb', 'gltf' ]; const reader = new FileReader(); if( dataUrl.indexOf( ftype ) !== -1 ) reader.readAsDataURL( file ); else if( dataBuf.indexOf( ftype ) !== -1 ) reader.readAsArrayBuffer( file ); else reader.readAsText( file ); reader.onload = function(e) { let content = e.target.result; if( type === 'image' ){ var img = new Image; img.onload = function() { if( o.callback ) o.callback( img, fname ); }; img.src = content; } else { if( o.callback ) o.callback( content, fname ); } }; } catch(e) { console.log(e); } } static showOpenFilePickerPolyfill( options ) { return new Promise((resolve) => { const input = document.createElement("input"); input.type = "file"; input.multiple = options.multiple; input.accept = options.types .map((type) => type.accept) .flatMap((inst) => Object.keys(inst).flatMap((key) => inst[key])) .join(","); input.addEventListener("change", () => { resolve( [...input.files].map((file) => { return { getFile: async () => new Promise((resolve) => { resolve(file); }), }; }) ); }); input.click(); }) } //----------------------------- // SAVE //----------------------------- static async save( o = {} ) { this.usePoly = false; if (typeof window.showSaveFilePicker !== 'function') { window.showSaveFilePicker = this.showSaveFilePickerPolyfill; this.usePoly = true; } try { let type = o.type || ''; const options = { suggestedName: o.name || 'hello', data: o.data || '' }; options.types = this.autoTypes( type ); options.finalType = Object.keys(options.types[0].accept )[0]; options.suggestedName += options.types[0].accept[options.finalType][0]; // create a new handle const handle = await window.showSaveFilePicker( options ); if( this.usePoly ) return // create a FileSystemWritableFileStream to write to const file = await handle.createWritable(); let blob = new Blob([ options.data ], { type: option.finalType }); // write our file await file.write(blob); // close the file and write the contents to disk. await file.close(); } catch(e) { console.log(e); } } static showSaveFilePickerPolyfill( options ) { return new Promise((resolve) => { const a = document.createElement("a"); a.download = options.suggestedName || "my-file.txt"; let blob = new Blob([ options.data ], { type:options.finalType }); a.href = URL.createObjectURL( blob ); a.addEventListener("click", () => { resolve( setTimeout( () => URL.revokeObjectURL(a.href), 1000 ) ); }); a.click(); }) } //----------------------------- // FOLDER not possible in poly //----------------------------- static async getFolder() { try { const handle = await window.showDirectoryPicker(); const files = []; for await (const entry of handle.values()) { const file = await entry.getFile(); files.push(file); } console.log(files); return files; } catch(e) { console.log(e); } } } class V2 { constructor( x = 0, y = 0 ) { this.x = x; this.y = y; } set ( x, y ) { this.x = x; this.y = y; return this; } divide ( v ) { this.x /= v.x; this.y /= v.y; return this; } multiply ( v ) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar ( scalar ) { this.x *= scalar; this.y *= scalar; return this; } divideScalar ( scalar ) { return this.multiplyScalar( 1 / scalar ); } length () { return Math.sqrt( this.x * this.x + this.y * this.y ); } angle () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; } addScalar ( s ) { this.x += s; this.y += s; return this; } negate () { this.x *= -1; this.y *= -1; return this; } neg () { this.x = -1; this.y = -1; return this; } isZero () { return ( this.x === 0 && this.y === 0 ); } copy ( v ) { this.x = v.x; this.y = v.y; return this; } equals ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); } nearEquals ( v, n ) { return ( ( v.x.toFixed(n) === this.x.toFixed(n) ) && ( v.y.toFixed(n) === this.y.toFixed(n) ) ); } lerp ( v, alpha ) { if( v === null ){ this.x -= this.x * alpha; this.y -= this.y * alpha; } else { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; } return this; } } /** * @author lth / https://github.com/lo-th */ class Proto { constructor( o = {} ) { // disable mouse controle this.lock = o.lock || false; // for button this.neverlock = false; // only simple space this.isSpace = o.isSpace || false; // if is on gui or group this.main = o.main || null; this.isUI = o.isUI || false; this.group = o.group || null; this.isListen = false; this.isSelectable = o.selectable !== undefined ? o.selectable : false; this.unselectable = o.unselect !== undefined ? o.unselect : this.isSelectable; this.ontop = o.ontop ? o.ontop : false; // 'beforebegin' 'afterbegin' 'beforeend' 'afterend' this.css = this.main ? this.main.css : Tools.css; this.colors = Tools.defineColor( o, this.main ? ( this.group ? this.group.colors : this.main.colors ) : Tools.colors ); this.svgs = Tools.svgs; this.zone = { x:0, y:0, w:0, h:0 }; this.local = new V2().neg(); this.isCanvasOnly = false; this.isSelect = false; // percent of title this.p = o.p !== undefined ? o.p : Tools.size.p; this.w = this.isUI ? this.main.size.w : Tools.size.w; if( o.w !== undefined ) this.w = o.w; this.h = this.isUI ? this.main.size.h : Tools.size.h; if( o.h !== undefined ) this.h = o.h; if( !this.isSpace ) this.h = this.h < 11 ? 11 : this.h; else this.lock = true; // decale for canvas only this.fw = o.fw || 0; this.autoWidth = o.auto || true;// auto width or flex this.isOpen = false;// open statu // radius for toolbox this.radius = o.radius || this.colors.radius; this.transition = o.transition || Tools.transition; // only for number this.isNumber = false; this.noNeg = o.noNeg || false; this.allEqual = o.allEqual || false; // only most simple this.mono = false; // stop listening for edit slide text this.isEdit = false; // no title this.simple = o.simple || false; if( this.simple ) this.sa = 0; // define obj size this.setSize( this.w ); // title size if( o.sa !== undefined ) this.sa = o.sa; if( o.sb !== undefined ) this.sb = o.sb; if( this.simple ) this.sb = this.w - this.sa; // last number size for slide this.sc = o.sc === undefined ? 47 : o.sc; // for listening object this.objectLink = null; this.isSend = false; this.val = null; this.txt = o.name || ''; this.name = o.rename || this.txt; this.target = o.target || null; // callback this.callback = o.callback === undefined ? null : o.callback; this.endCallback = null; this.openCallback = o.openCallback === undefined ? null : o.openCallback; this.closeCallback = o.closeCallback === undefined ? null : o.closeCallback; // if no callback take one from group or gui if( this.callback === null && this.isUI && this.main.callback !== null ){ this.callback = this.group ? this.group.callback : this.main.callback; } // elements this.c = []; // style this.s = []; this.useFlex = this.isUI ? this.main.useFlex : false; let flexible = this.useFlex ? 'display:flex; justify-content:center; align-items:center; text-align:center; flex: 1 100%;' : 'float:left;'; this.c[0] = Tools.dom( 'div', this.css.basic + flexible + 'position:relative; height:20px;'); this.s[0] = this.c[0].style; // bottom margin this.margin = o.margin || 1; if( this.isUI && this.margin ){ this.s[0].boxSizing = 'content-box'; //this.s[0].marginBottom = this.margin + 'px'; if( this.margin*0.5===Math.floor(this.margin*0.5) ){ this.s[0].borderTop = (this.margin*0.5) + 'px solid transparent'; this.s[0].borderBottom = (this.margin*0.5) + 'px solid transparent'; } else { this.s[0].borderBottom = this.margin + 'px solid transparent'; } } // with title if( !this.simple ){ this.c[1] = Tools.dom( 'div', this.css.txt ); this.s[1] = this.c[1].style; this.c[1].textContent = this.name; this.s[1].color = this.lock ? this.colors.titleoff : this.colors.title; } if( o.pos ){ this.s[0].position = 'absolute'; for(let p in o.pos){ this.s[0][p] = o.pos[p]; } this.mono = true; } if( o.css ) this.s[0].cssText = o.css; } // ---------------------- // make the node // ---------------------- init() { this.zone.h = this.h; this.zone.w = this.w; let s = this.s; // style cache let c = this.c; // div cach s[0].height = this.h + 'px'; if( this.isUI ) s[0].background = this.colors.background; if(!this.autoWidth && this.useFlex ){ s[0].flex = '1 0 auto'; s[0].minWidth = this.minw+'px'; s[0].textAlign = 'center'; } else { if( this.isUI ) s[0].width = '100%'; } //if( this.autoHeight ) s[0].transition = 'height 0.01s ease-out'; if( c[1] !== undefined && this.autoWidth ){ s[1] = c[1].style; s[1].height = (this.h-4) + 'px'; s[1].lineHeight = (this.h-8) + 'px'; } let frag = Tools.frag; for( let i = 1, lng = c.length; i !== lng; i++ ){ if( c[i] !== undefined ) { frag.appendChild( c[i] ); s[i] = c[i].style; } } let pp = this.target !== null ? this.target : ( this.isUI ? this.main.inner : document.body ); if( this.ontop ) pp.insertAdjacentElement( 'afterbegin', c[0] ); else pp.appendChild( c[0] ); c[0].appendChild( frag ); this.rSize(); // ! solo proto if( !this.isUI ){ this.c[0].style.pointerEvents = 'auto'; Roots.add( this ); } if( this.baseH && this.transition && this.isUI ){ this.c[0].style.transition = 'height '+this.transition+'s ease-out'; } } // from Tools dom( type, css, obj, dom, id ) { return Tools.dom( type, css, obj, dom, id ); } setSvg( dom, type, value, id, id2 ) { Tools.setSvg( dom, type, value, id, id2 ); } setCss( dom, css ) { Tools.setCss( dom, css ); } clamp( value, min, max ) { return Tools.clamp( value, min, max ); } getColorRing() { if( !Tools.colorRing ) Tools.makeColorRing(); return Tools.clone( Tools.colorRing ); } getJoystick( model ) { if( !Tools[ 'joystick_'+ model ] ) Tools.makeJoystick( model ); return Tools.clone( Tools[ 'joystick_'+ model ] ) } getCircular( model ) { if( !Tools.circular ) Tools.makeCircular( model ); return Tools.clone( Tools.circular ) } getKnob( model ) { if( !Tools.knob ) Tools.makeKnob( model ); return Tools.clone( Tools.knob ) } getPad2d( model ) { if( !Tools.pad2d ) Tools.makePad( model ); return Tools.clone( Tools.pad2d ) } // from Roots cursor( name ) { Roots.cursor( name ); } ///////// update() {} reset() {} ///////// getDom() { return this.c[0] } uiout() { if( this.lock ) return; if(this.s) this.s[0].background = this.colors.background; } uiover() { if( this.lock ) return; if(this.s) this.s[0].background = this.colors.backgroundOver; } rename( s ) { if( this.c[1] !== undefined) this.c[1].textContent = s; } listen() { this.isListen = Roots.addListen( this ); return this; } listening() { if( this.objectLink === null ) return; if( this.isSend ) return; if( this.isEdit ) return; this.setValue( this.objectLink[ this.val ] ); } setValue( v ) { if( this.isNumber ) this.value = this.numValue( v ); //else if( v instanceof Array && v.length === 1 ) v = v[0]; else this.value = v; this.update(); } // ---------------------- // update every change // ---------------------- onChange( f ) { if( this.isSpace ) return this.callback = f || null; return this } // ---------------------- // update only on end // ---------------------- onFinishChange( f ) { if( this.isSpace ) return; this.callback = null; this.endCallback = f; return this } // ---------------------- // event on open close // ---------------------- onOpen( f ) { this.openCallback = f; return this } onClose( f ) { this.closeCallback = f; return this } // ---------------------- // send back value // ---------------------- send( v ) { v = v || this.value; if( v instanceof Array && v.length === 1 ) v = v[0]; this.isSend = true; if( this.objectLink !== null ) this.objectLink[ this.val ] = v; if( this.callback ) this.callback( v, this.val ); this.isSend = false; } sendEnd( v ) { v = v || this.value; if( v instanceof Array && v.length === 1 ) v = v[0]; if( this.endCallback ) this.endCallback( v ); if( this.objectLink !== null ) this.objectLink[ this.val ] = v; } // ---------------------- // clear node // ---------------------- dispose(){ if( this.isListen ) Roots.removeListen( this ); Tools.clear( this.c[0] ); if( this.target !== null ){ if( this.group !== null ) this.group.clearOne( this ); else this.target.removeChild( this.c[0] ); } else { if( this.isUI ) this.main.clearOne( this ); else document.body.removeChild( this.c[0] ); } if( !this.isUI ) Roots.remove( this ); this.c = null; this.s = null; this.callback = null; this.target = null; this.isListen = false; } clear() { } // ---------------------- // change size // ---------------------- getWidth() { let nw = Roots.getWidth( this ); if(nw) this.w = nw; } setSize( sx ) { if( !this.autoWidth ) return; this.w = sx; if( this.simple ){ this.sb = this.w - this.sa; } else { let pp = this.w * ( this.p / 100 ); this.sa = Math.floor( pp + 10 ); this.sb = Math.floor( this.w - pp - 20 ); } } rSize() { if( !this.autoWidth ) return; if( !this.isUI ) this.s[0].width = this.w + 'px'; if( !this.simple ) this.s[1].width = this.sa + 'px'; } // ---------------------- // for numeric value // ---------------------- setTypeNumber( o ) { this.isNumber = true; this.value = 0; if( o.value !== undefined ){ if( typeof o.value === 'string' ) this.value = o.value * 1; else this.value = o.value; } this.min = o.min === undefined ? -Infinity : o.min; this.max = o.max === undefined ? Infinity : o.max; this.precision = o.precision === undefined ? 2 : o.precision; let s; switch(this.precision){ case 0: s = 1; break; case 1: s = 0.1; break; case 2: s = 0.01; break; case 3: s = 0.001; break; case 4: s = 0.0001; break; case 5: s = 0.00001; break; } this.step = o.step === undefined ? s : o.step; this.range = this.max - this.min; this.value = this.numValue( this.value ); } numValue( n ) { if( this.noNeg ) n = Math.abs( n ); return Math.min( this.max, Math.max( this.min, n ) ).toFixed( this.precision ) * 1; } // ---------------------- // EVENTS DEFAULT // ---------------------- handleEvent( e ) { if( this.lock ) return if( this.neverlock ) Roots.lock = false; if( !this[e.type] ) return console.error(e.type, 'this type of event no existe !') return this[e.type](e) } wheel( e ) { return false; } mousedown( e ) { return false; } mousemove( e ) { return false; } mouseup( e ) { return false; } keydown( e ) { return false; } keyup( e ) { return false; } // ---------------------- // object referency // ---------------------- setReferency( obj, val ) { this.objectLink = obj; this.val = val; } display( v = false ) { this.s[0].visibility = v ? 'visible' : 'hidden'; } // ---------------------- // resize height // ---------------------- open () { if( this.isOpen ) return; this.isOpen = true; if( this.openCallback ) this.openCallback(); } close () { if( !this.isOpen ) return this.isOpen = false; if( this.closeCallback ) this.closeCallback(); } needZone() { Roots.needReZone = true; } rezone() { Roots.needReZone = true; } // ---------------------- // INPUT // ---------------------- select() { } unselect() { } setInput( Input ) { Roots.setInput( Input, this ); } upInput( x, down ) { return Roots.upInput( x, down ); } // ---------------------- // special item // ---------------------- selected( b ){ this.isSelect = b || false; } } class Bool extends Proto { constructor( o = {} ) { super( o ); this.value = o.value || false; this.model = o.mode !== undefined ? o.mode : 0; this.onName = o.rename || this.txt; if( o.onName ) o.onname = o.onName; if( o.onname ) this.onName = o.onname; this.inh = o.inh || Math.floor( this.h*0.8 ); this.inw = o.inw || 36; let cc = this.colors; if( this.model === 0 ){ let t = Math.floor(this.h*0.5)-((this.inh-2)*0.5); this.c[2] = this.dom( 'div', this.css.basic + 'background:'+ cc.inputBg +'; height:'+(this.inh-2)+'px; width:'+this.inw+'px; top:'+t+'px; border-radius:10px; border:2px solid '+ cc.back ); this.c[3] = this.dom( 'div', this.css.basic + 'height:'+(this.inh-6)+'px; width:16px; top:'+(t+2)+'px; border-radius:10px; background:'+ cc.button+';' ); } else { this.p = 0; if( this.c[1] !== undefined ) this.c[1].textContent = ''; this.c[2] = this.dom( 'div', this.css.txt + this.css.button + 'top:1px; background:'+cc.button+'; height:'+(this.h-2)+'px; border:1px solid '+cc.border+'; border-radius:'+this.radius+'px;' ); } this.stat = -1; this.init(); this.update(); } // ---------------------- // EVENTS // ---------------------- mousedown ( e ) { this.value = !this.value; this.update( true ); return this.mousemove( e ) } mousemove ( e ) { this.cursor('pointer'); return this.mode( true ) } reset () { this.cursor(); return this.mode() } // ---------------------- // MODE // ---------------------- mode ( over ) { let change = false; let cc = this.colors, s, s2, n, v = this.value; if( over ) n = v ? 4 : 3; else n = v ? 2 : 1; if( this.stat !== n ){ this.stat = n; if( this.model !== 0 ){ s = this.s[2]; switch( n ){ case 1: s.color = cc.text; s.background = cc.button; break; case 2: s.color = cc.textSelect; s.background = cc.select; break; case 3: s.color = cc.textOver; s.background = cc.overoff; break; case 4: s.color = cc.textOver; s.background = cc.over; break; } this.c[2].innerHTML = v ? this.onName : this.name; } else { s = this.s[2]; s2 = this.s[3]; switch( n ){ case 1: s.background = s.borderColor = cc.back; s2.background = cc.button; break; case 2: s.background = s.borderColor = cc.select; s2.background = cc.button; break; case 3: s.background = s.borderColor = cc.back; s2.background = cc.overoff; break; case 4: s.background = s.borderColor = cc.select; s2.background = cc.over; break; } this.s[3].marginLeft = v ? '17px' : '2px'; this.c[1].textContent = v ? this.onName : this.name; } change = true; } return change } // ---------------------- update ( up ) { this.mode(); if( up ) this.send(); } rSize () { super.rSize(); let s = this.s; let w = (this.w - 10 ) - this.inw; if( this.model === 0 ){ s[2].left = w + 'px'; s[3].left = w + 'px'; } else { s[2].left = this.sa + 'px'; s[2].width = (this.w- 20) + 'px'; } } } class Button extends Proto { constructor( o = {} ) { super( o ); this.value = o.value || ''; this.values = o.value || this.txt; if( o.values ) this.values = o.values; this.onName = o.onName || null; this.on = false; // force button width this.bw = o.forceWidth || 0; if(o.bw) this.bw = o.bw; this.space = o.space || 3; if( typeof this.values === 'string' ) this.values = [ this.values ]; this.isDown = false; this.neverlock = true; this.res = 0; this.lng = this.values.length; this.tmp = []; this.stat = []; let sel, cc = this.colors; for( let i = 0; i < this.lng; i++ ){ sel = false; if( this.values[i] === this.value && this.isSelectable ) sel = true; this.c[i+2] = this.dom( 'div', this.css.txt + this.css.button + 'top:1px; height:'+(this.h-2)+'px; border:'+cc.borderSize+'px solid '+cc.border+'; border-radius:'+this.radius+'px;' ); this.c[i+2].style.background = sel ? cc.select : cc.button; this.c[i+2].style.color = sel ? cc.textSelect : cc.text; this.c[i+2].innerHTML = this.values[i]; this.stat[i] = sel ? 3:1; } if( !o.value && !o.values ){ if( this.c[1] !== undefined ) { this.c[1].textContent = ''; this.txt = ''; } } if( !this.txt ) this.p = 0; this.init(); } onOff() { this.on = !this.on; this.label( this.on ? this.onName : this.txt ); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return -1 let i = this.lng; let t = this.tmp; while( i-- ){ if( l.x>t[i][0] && l.x 0 ? Tools.pack( Tools.lerpColor( Tools.unpack( Tools.ColorLuma( cc.text, -0.75) ), Tools.unpack( cc.text ), this.percent ) ) : cc.text; this.setSvg( this.c[3], 'stroke', color, 1 ); break; case 1: // down this.s[2].color = cc.textOver; this.setSvg( this.c[3], 'stroke', cc.backoff, 0); color = this.model > 0 ? Tools.pack( Tools.lerpColor( Tools.unpack( Tools.ColorLuma( cc.text, -0.75) ), Tools.unpack( cc.text ), this.percent ) ) : cc.textOver; this.setSvg( this.c[3], 'stroke', color, 1 ); break; } this.cmode = mode; return true; } reset () { this.isDown = false; } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( l.y <= this.c[ 1 ].offsetHeight ) return 'title'; else if ( l.y > this.h - this.c[ 2 ].offsetHeight ) return 'text'; else return 'circular'; } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { this.isDown = false; this.sendEnd(); return this.mode(0); } mousedown ( e ) { this.isDown = true; this.old = this.value; this.oldr = null; this.mousemove( e ); return this.mode(1); } mousemove ( e ) { if( !this.isDown ) return; //console.log('over') let off = this.offset; off.x = (this.w*0.5) - ( e.clientX - this.zone.x ); off.y = (this.diam*0.5) - ( e.clientY - this.zone.y - this.top ); this.r = off.angle() - this.pi90; this.r = (((this.r%this.twoPi)+this.twoPi)%this.twoPi); if( this.oldr !== null ){ let dif = this.r - this.oldr; this.r = Math.abs(dif) > Math.PI ? this.oldr : this.r; if( dif > 6 ) this.r = 0; if( dif < -6 ) this.r = this.twoPi; } let steps = 1 / this.twoPi; let value = this.r * steps; let n = ( ( this.range * value ) + this.min ) - this.old; if(n >= this.step || n <= this.step){ n = ~~ ( n / this.step ); this.value = this.numValue( this.old + ( n * this.step ) ); this.update( true ); this.old = this.value; this.oldr = this.r; } } wheel ( e ) { let name = this.testZone( e ); if( name === 'circular' ) { let v = this.value - this.step * e.delta; if ( v > this.max ) { v = this.isCyclic ? this.min : this.max; } else if ( v < this.min ) { v = this.isCyclic ? this.max : this.min; } this.setValue( v ); this.old = v; this.update( true ); return true; } return false; } // ---------------------- makePath () { let r = 40; let d = 24; let a = this.percent * this.twoPi - 0.001; let x2 = (r + r * Math.sin(a)) + d; let y2 = (r - r * Math.cos(a)) + d; let big = a > Math.PI ? 1 : 0; return "M " + (r+d) + "," + d + " A " + r + "," + r + " 0 " + big + " 1 " + x2 + "," + y2; } update ( up ) { this.c[2].textContent = this.value; this.percent = ( this.value - this.min ) / this.range; this.setSvg( this.c[3], 'd', this.makePath(), 1 ); if ( this.model > 0 ) { let cc = this.colors; let color = Tools.pack( Tools.lerpColor( Tools.unpack( Tools.ColorLuma( cc.text, -0.75) ), Tools.unpack( cc.text ), this.percent ) ); this.setSvg( this.c[3], 'stroke', color, 1 ); } if( up ) this.send(); } } class Color extends Proto { constructor( o = {} ) { super( o ); //this.autoHeight = true; this.ctype = o.ctype || 'hex'; this.wfixe = 256; this.cw = this.sb > 256 ? 256 : this.sb; if(o.cw != undefined ) this.cw = o.cw; // color up or down this.side = o.side || 'down'; this.up = this.side === 'down' ? 0 : 1; this.baseH = this.h; this.offset = new V2(); this.decal = new V2(); this.pp = new V2(); this.c[2] = this.dom( 'div', this.css.txt + 'height:'+(this.h-4)+'px;' + 'border-radius:'+this.radius+'px; line-height:'+(this.h-8)+'px;' ); this.s[2] = this.c[2].style; this.s[2].textShadow = 'none'; if( this.up ){ this.s[2].top = 'auto'; this.s[2].bottom = '2px'; } //this.c[0].style.textAlign = 'center'; //this.c[0].style.flex = '1 0 auto' this.c[3] = this.getColorRing(); this.c[3].style.visibility = 'hidden'; this.hsl = null; this.value = '#ffffff'; if( o.value !== undefined ){ if( o.value instanceof Array ) this.value = Tools.rgbToHex( o.value ); else if(!isNaN(o.value)) this.value = Tools.hexToHtml( o.value ); else this.value = o.value; } this.bcolor = null; this.isDown = false; this.fistDown = false; this.notext = o.notext || false; this.tr = 98; this.tsl = Math.sqrt(3) * this.tr; this.hue = 0; this.d = 256; this.setColor( this.value ); this.init(); if( o.open !== undefined ) this.open(); } testZone ( mx, my ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( this.up && this.isOpen ){ if( l.y > this.wfixe ) return 'title'; else return 'color'; } else { if( l.y < this.baseH+2 ) return 'title'; else if( this.isOpen ) return 'color'; } } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { this.isDown = false; this.d = 256; } mousedown ( e ) { let name = this.testZone( e.clientX, e.clientY ); //if( !name ) return; if(name === 'title'){ if( !this.isOpen ) this.open(); else this.close(); return true; } if( name === 'color' ){ this.isDown = true; this.fistDown = true; this.mousemove( e ); } } mousemove ( e ) { let name = this.testZone( e.clientX, e.clientY ); let off, d, hue, sat, lum, rad, x, y, rr, T = Tools; if( name === 'title' ) this.cursor('pointer'); if( name === 'color' ){ off = this.offset; off.x = e.clientX - ( this.zone.x + this.decal.x + this.mid ); off.y = e.clientY - ( this.zone.y + this.decal.y + this.mid ); d = off.length() * this.ratio; rr = off.angle(); if(rr < 0) rr += 2 * T.PI; if ( d < 128 ) this.cursor('crosshair'); else if( !this.isDown ) this.cursor(); if( this.isDown ){ if( this.fistDown ){ this.d = d; this.fistDown = false; } if ( this.d < 128 ) { if ( this.d > this.tr ) { // outside hue hue = ( rr + T.pi90 ) / T.TwoPI; this.hue = (hue + 1) % 1; this.setHSL([(hue + 1) % 1, this.hsl[1], this.hsl[2]]); } else { // triangle x = off.x * this.ratio; y = off.y * this.ratio; let rr = (this.hue * T.TwoPI) + T.PI; if(rr < 0) rr += 2 * T.PI; rad = Math.atan2(-y, x); if(rad < 0) rad += 2 * T.PI; let rad0 = ( rad + T.pi90 + T.TwoPI + rr ) % (T.TwoPI), rad1 = rad0 % ((2/3) * T.PI) - (T.pi60), a = 0.5 * this.tr, b = Math.tan(rad1) * a, r = Math.sqrt(x*x + y*y), maxR = Math.sqrt(a*a + b*b); if( r > maxR ) { let dx = Math.tan(rad1) * r; let rad2 = Math.atan(dx / maxR); if(rad2 > T.pi60) rad2 = T.pi60; else if( rad2 < -T.pi60 ) rad2 = -T.pi60; rad += rad2 - rad1; rad0 = (rad + T.pi90 + T.TwoPI + rr) % (T.TwoPI), rad1 = rad0 % ((2/3) * T.PI) - (T.pi60); b = Math.tan(rad1) * a; r = maxR = Math.sqrt(a*a + b*b); } lum = ((Math.sin(rad0) * r) / this.tsl) + 0.5; let w = 1 - (Math.abs(lum - 0.5) * 2); sat = (((Math.cos(rad0) * r) + (this.tr / 2)) / (1.5 * this.tr)) / w; sat = T.clamp( sat, 0, 1 ); this.setHSL([this.hsl[0], sat, lum]); } } } } } // ---------------------- setHeight () { this.h = this.isOpen ? this.wfixe + this.baseH + 5 : this.baseH; this.s[0].height = this.h + 'px'; this.zone.h = this.h; } parentHeight ( t ) { if ( this.group !== null ) this.group.calc( t ); else if ( this.isUI ) this.main.calc( t ); } open () { super.open(); this.setHeight(); if( this.up ) this.zone.y -= this.wfixe + 5; let t = this.h - this.baseH; this.s[3].visibility = 'visible'; //this.s[3].display = 'block'; this.parentHeight( t ); } close () { super.close(); if( this.up ) this.zone.y += this.wfixe + 5; let t = this.h - this.baseH; this.setHeight(); this.s[3].visibility = 'hidden'; //this.s[3].display = 'none'; this.parentHeight( -t ); } update ( up ) { let cc = Tools.rgbToHex( Tools.hslToRgb([ this.hsl[0], 1, 0.5 ]) ); this.moveMarkers(); this.value = this.bcolor; this.setSvg( this.c[3], 'fill', cc, 2, 0 ); this.s[2].background = this.bcolor; if(!this.notext) this.c[2].textContent = Tools.htmlToHex( this.bcolor ); this.invert = Tools.findDeepInver( this.rgb ); this.s[2].color = this.invert ? '#fff' : '#000'; if(!up) return; if( this.ctype === 'array' ) this.send( this.rgb ); if( this.ctype === 'rgb' ) this.send( Tools.htmlRgb( this.rgb ) ); if( this.ctype === 'hex' ) this.send( Tools.htmlToHex( this.value ) ); if( this.ctype === 'html' ) this.send(); } setValue ( v ){ if( v instanceof Array ) this.value = Tools.rgbToHex( v ); else if(!isNaN(v)) this.value = Tools.hexToHtml( v ); else this.value = v; this.setColor( this.value ); this.update(); } setColor ( color ) { let unpack = Tools.unpack(color); if (this.bcolor !== color && unpack) { this.bcolor = color; this.rgb = unpack; this.hsl = Tools.rgbToHsl( this.rgb ); this.hue = this.hsl[0]; this.update(); } return this; } setHSL ( hsl ) { this.hsl = hsl; this.rgb = Tools.hslToRgb( hsl ); this.bcolor = Tools.rgbToHex( this.rgb ); this.update( true ); return this; } moveMarkers () { let p = this.pp; let T = Tools; this.invert ? '#fff' : '#000'; let a = this.hsl[0] * T.TwoPI; let third = (2/3) * T.PI; let r = this.tr; let h = this.hsl[0]; let s = this.hsl[1]; let l = this.hsl[2]; let angle = ( a - T.pi90 ) * T.todeg; h = - a + T.pi90; let hx = Math.cos(h) * r; let hy = -Math.sin(h) * r; let sx = Math.cos(h - third) * r; let sy = -Math.sin(h - third) * r; let vx = Math.cos(h + third) * r; let vy = -Math.sin(h + third) * r; let mx = (sx + vx) / 2, my = (sy + vy) / 2; a = (1 - 2 * Math.abs(l - .5)) * s; let x = sx + (vx - sx) * l + (hx - mx) * a; let y = sy + (vy - sy) * l + (hy - my) * a; p.set( x, y ).addScalar(128); //let ff = (1-l)*255; // this.setSvg( this.c[3], 'stroke', 'rgb('+ff+','+ff+','+ff+')', 3 ); this.setSvg( this.c[3], 'transform', 'rotate('+angle+' )', 2 ); this.setSvg( this.c[3], 'cx', p.x, 3 ); this.setSvg( this.c[3], 'cy', p.y, 3 ); this.setSvg( this.c[3], 'stroke', this.invert ? '#fff' : '#000', 2, 3 ); this.setSvg( this.c[3], 'stroke', this.invert ? '#fff' : '#000', 3 ); this.setSvg( this.c[3], 'fill',this.bcolor, 3 ); } rSize () { //Proto.prototype.rSize.call( this ); super.rSize(); let s = this.s; s[2].width = this.sb + 'px'; s[2].left = this.sa + 'px'; this.rSizeColor( this.cw ); this.decal.x = Math.floor((this.w - this.wfixe) * 0.5); //s[3].left = this.decal.x + 'px'; } rSizeColor ( w ) { if( w === this.wfixe ) return; this.wfixe = w; let s = this.s; //this.decal.x = Math.floor((this.w - this.wfixe) * 0.5); this.decal.y = this.side === 'up' ? 2 : this.baseH + 2; this.mid = Math.floor( this.wfixe * 0.5 ); this.setSvg( this.c[3], 'viewBox', '0 0 '+ this.wfixe + ' '+ this.wfixe ); s[3].width = this.wfixe + 'px'; s[3].height = this.wfixe + 'px'; //s[3].left = this.decal.x + 'px'; s[3].top = this.decal.y + 'px'; this.ratio = 256 / this.wfixe; this.square = 1 / (60*(this.wfixe/256)); this.setHeight(); } } class Fps extends Proto { constructor( o = {} ) { super( o ); this.round = Math.round; //this.autoHeight = true; this.baseH = this.h; this.hplus = o.hplus || 50; this.res = o.res || 40; this.l = 1; this.precision = o.precision || 0; this.custom = o.custom || false; this.names = o.names || ['FPS', 'MS']; let cc = o.cc || ['220,220,220', '255,255,0']; // this.divid = [ 100, 100, 100 ]; // this.multy = [ 30, 30, 30 ]; this.adding = o.adding || false; this.range = o.range || [ 165, 100, 100 ]; this.alpha = o.alpha || 0.25; this.values = []; this.points = []; this.textDisplay = []; if(!this.custom){ this.now = Roots.getTime(); this.startTime = 0;//this.now() this.prevTime = 0;//this.startTime; this.frames = 0; this.ms = 0; this.fps = 0; this.mem = 0; this.mm = 0; this.isMem = ( self.performance && self.performance.memory ) ? true : false; // this.divid = [ 100, 200, 1 ]; // this.multy = [ 30, 30, 30 ]; if( this.isMem ){ this.names.push('MEM'); cc.push('0,255,255'); } this.txt = o.name || 'Fps'; } let fltop = Math.floor(this.h*0.5)-6; this.c[1].textContent = this.txt; this.c[0].style.cursor = 'pointer'; this.c[0].style.pointerEvents = 'auto'; let panelCss = 'display:none; left:10px; top:'+ this.h + 'px; height:'+(this.hplus - 8)+'px; box-sizing:border-box; background: rgba(0, 0, 0, 0.2); border:1px solid '+ this.colors.border +';'; if( this.radius !== 0 ) panelCss += 'border-radius:' + this.radius+'px;'; this.c[2] = this.dom( 'path', this.css.basic + panelCss , {} ); this.c[2].setAttribute('viewBox', '0 0 '+this.res+' 50' ); this.c[2].setAttribute('height', '100%' ); this.c[2].setAttribute('width', '100%' ); this.c[2].setAttribute('preserveAspectRatio', 'none' ); //this.dom( 'path', null, { fill:'rgba(255,255,0,0.3)', 'stroke-width':1, stroke:'#FF0', 'vector-effect':'non-scaling-stroke' }, this.c[2] ); //this.dom( 'path', null, { fill:'rgba(0,255,255,0.3)', 'stroke-width':1, stroke:'#0FF', 'vector-effect':'non-scaling-stroke' }, this.c[2] ); // arrow this.c[3] = this.dom( 'path', this.css.basic + 'position:absolute; width:10px; height:10px; left:4px; top:'+fltop+'px;', { d:this.svgs.arrow, fill:this.colors.text, stroke:'none'}); // result test this.c[4] = this.dom( 'div', this.css.txt + 'position:absolute; left:10px; top:'+(this.h+2) +'px; display:none; width:100%; text-align:center;' ); // bottom line if( o.bottomLine ) this.c[4] = this.dom( 'div', this.css.basic + 'width:100%; bottom:0px; height:1px; background: rgba(255, 255, 255, 0.2);'); this.isShow = false; let s = this.s; s[1].marginLeft = '10px'; s[1].lineHeight = this.h-4; s[1].color = this.colors.text; s[1].fontWeight = 'bold'; if( this.radius !== 0 ) s[0].borderRadius = this.radius+'px'; if( this.colors.gborder!=='none') s[0].border = '1px solid ' + this.colors.gborder; let j = 0; for( j=0; j " + this.names[j] +" "); } j = this.names.length; while(j--){ this.dom( 'path', null, { fill:'rgba('+cc[j]+','+this.alpha+')', 'stroke-width':1, stroke:'rgba('+cc[j]+',1)', 'vector-effect':'non-scaling-stroke' }, this.c[2] ); } this.init(); //if( this.isShow ) this.show(); } // ---------------------- // EVENTS // ---------------------- mousedown ( e ) { if( this.isShow ) this.close(); else this.open(); } // ---------------------- /*mode: function ( mode ) { let s = this.s; switch(mode){ case 0: // base s[1].color = this.colors.text; //s[1].background = 'none'; break; case 1: // over s[1].color = '#FFF'; //s[1].background = UIL.SELECT; break; case 2: // edit / down s[1].color = this.colors.text; //s[1].background = UIL.SELECTDOWN; break; } },*/ tick ( v ) { this.values = v; if( !this.isShow ) return; this.drawGraph(); this.upText(); } makePath ( point ) { let p = ''; p += 'M ' + (-1) + ' ' + 50; for ( let i = 0; i < this.res + 1; i ++ ) { p += ' L ' + i + ' ' + point[i]; } p += ' L ' + (this.res + 1) + ' ' + 50; return p; } upText ( val ) { let v = val || this.values, t = ''; for( let j=0, lng =this.names.length; j'; this.c[4].innerHTML = t; } drawGraph () { let svg = this.c[2]; let i = this.names.length, v, old = 0, n = 0; while( i-- ){ if( this.adding ) v = (this.values[n]+old) * this.range[n]; else v = (this.values[n] * this.range[n]); this.points[n].shift(); this.points[n].push( 50 - v ); this.setSvg( svg, 'd', this.makePath( this.points[n] ), i+1 ); old += this.values[n]; n++; } } open () { super.open(); this.h = this.hplus + this.baseH; this.setSvg( this.c[3], 'd', this.svgs.arrowDown ); if( this.group !== null ){ this.group.calc( this.hplus );} else if( this.isUI ) this.main.calc( this.hplus ); this.s[0].height = this.h +'px'; this.s[2].display = 'block'; this.s[4].display = 'block'; this.isShow = true; if( !this.custom ) Roots.addListen( this ); } close () { super.close(); this.h = this.baseH; this.setSvg( this.c[3], 'd', this.svgs.arrow ); if( this.group !== null ){ this.group.calc( -this.hplus );} else if( this.isUI ) this.main.calc( -this.hplus ); this.s[0].height = this.h +'px'; this.s[2].display = 'none'; this.s[4].display = 'none'; this.isShow = false; if( !this.custom ) Roots.removeListen( this ); this.c[4].innerHTML = ''; } ///// AUTO FPS ////// begin () { this.startTime = this.now(); } end () { let time = this.now(); this.ms = time - this.startTime; this.frames ++; if ( time > this.prevTime + 1000 ) { this.fps = this.round( ( this.frames * 1000 ) / ( time - this.prevTime ) ); this.prevTime = time; this.frames = 0; if ( this.isMem ) { let heapSize = performance.memory.usedJSHeapSize; let heapSizeLimit = performance.memory.jsHeapSizeLimit; this.mem = this.round( heapSize * 0.000000954 ); this.mm = heapSize / heapSizeLimit; } } this.values = [ this.fps, this.ms , this.mm ]; this.drawGraph(); this.upText( [ this.fps, this.ms, this.mem ] ); return time; } listening () { if( !this.custom ) this.startTime = this.end(); } rSize () { let s = this.s; let w = this.w; s[0].width = w + 'px'; s[1].width = w + 'px'; s[2].left = 10 + 'px'; s[2].width = (w-20) + 'px'; s[4].width = (w-20) + 'px'; } } class Graph extends Proto { constructor( o = {} ) { super( o ); this.value = o.value !== undefined ? o.value : [0,0,0]; this.lng = this.value.length; this.precision = o.precision !== undefined ? o.precision : 2; this.multiplicator = o.multiplicator || 1; this.neg = o.neg || false; this.line = o.line !== undefined ? o.line : true; //if(this.neg)this.multiplicator*=2; this.autoWidth = o.autoWidth !== undefined ? o.autoWidth : true; this.isNumber = false; this.isDown = false; this.h = o.h || 128 + 10; this.rh = this.h - 10; this.top = 0; this.c[0].style.width = this.w +'px'; if( this.c[1] !== undefined ) { // with title this.c[1].style.width = this.w +'px'; if(!this.autoWidth){ this.c[1].style.width = '100%'; this.c[1].style.justifyContent = 'center'; } //this.c[1].style.background = '#ff0000'; //this.c[1].style.textAlign = 'center'; this.top = 10; this.h += 10; } this.gh = this.rh - 28; this.gw = this.w - 28; //this.c[2] = this.dom( 'div', this.css.txt + 'justify-content:center; text-align: justify; column-count:'+this.lng+'; top:'+(this.h-20)+'px; width:100%; color:'+ this.colors.text ); //let colum = 'column-count:'+this.lng+'; column:'+this.lng+'; break-inside: column; top:' this.c[2] = this.dom( 'div', this.css.txt + 'display:block; text-align:center; padding:0px 0px; top:'+(this.h-20)+'px; left:14px; width:'+this.gw+'px; color:'+ this.colors.text ); //this.c[2].textContent = this.value; this.c[2].innerHTML = this.valueToHtml(); let svg = this.dom( 'svg', this.css.basic , { viewBox:'0 0 '+this.w+' '+this.rh, width:this.w, height:this.rh, preserveAspectRatio:'none' } ); this.setCss( svg, { width:this.w, height:this.rh, left:0, top:this.top }); this.dom( 'path', '', { d:'', stroke:this.colors.text, 'stroke-width':2, fill:'none', 'stroke-linecap':'butt' }, svg ); this.dom( 'rect', '', { x:10, y:10, width:this.gw+8, height:this.gh+8, stroke:'rgba(0,0,0,0.3)', 'stroke-width':1 , fill:'none'}, svg ); this.iw = ((this.gw-(4*(this.lng-1)))/this.lng); let t = []; this.cMode = []; this.v = []; for( let i = 0; i < this.lng; i++ ){ t[i] = [ 14 + (i*this.iw) + (i*4), this.iw ]; t[i][2] = t[i][0] + t[i][1]; this.cMode[i] = 0; if( this.neg ) this.v[i] = ((1+(this.value[i] / this.multiplicator))*0.5); else this.v[i] = this.value[i] / this.multiplicator; this.dom( 'rect', '', { x:t[i][0], y:14, width:t[i][1], height:1, fill:this.colors.text, 'fill-opacity':0.3 }, svg ); } this.tmp = t; this.c[3] = svg; //console.log(this.w) this.init(); if( this.c[1] !== undefined ){ this.c[1].style.top = 0 +'px'; this.c[1].style.height = 20 +'px'; this.s[1].lineHeight = (20-5)+'px'; } this.update( false ); } setValue ( value ) { this.value = value; this.lng = this.value.length; for (var i = 0; i < this.lng; i++) { if (this.neg) this.v[i] = (1 + value[i] / this.multiplicator) * 0.5; else this.v[i] = value[i] / this.multiplicator; } this.update(); } valueToHtml() { let i = this.lng, n=0, r = ''; let w = 100 / this.lng; let style = 'width:'+ w +'%;';//' text-align:center;' while(i--){ if(n===this.lng-1) r += '
' + this.value[n] + '
'; else r += '' + this.value[n] + ''; n++; } return r } updateSVG () { if( this.line ) this.setSvg( this.c[3], 'd', this.makePath(), 0 ); for(let i = 0; ithis.top && l.yt[i][0] && l.x this.distance ) { let angle = Math.atan2(this.tmp.x, this.tmp.y); this.tmp.x = Math.sin( angle ) * this.distance; this.tmp.y = Math.cos( angle ) * this.distance; } this.pos.copy( this.tmp ).divideScalar( this.distance ).negate(); this.update(); } setValue ( v ) { if(v===undefined) v=[0,0]; this.pos.set( v[0] || 0, v[1] || 0 ); this.updateSVG(); } update ( up ) { if( up === undefined ) up = true; if( this.interval !== null ){ if( !this.isDown ){ this.pos.lerp( null, 0.3 ); this.pos.x = Math.abs( this.pos.x ) < 0.01 ? 0 : this.pos.x; this.pos.y = Math.abs( this.pos.y ) < 0.01 ? 0 : this.pos.y; if( this.isUI && this.main.isCanvas ) this.main.draw(); } } this.updateSVG(); if( up ) this.send(); if( this.pos.isZero() ) this.stopInterval(); } updateSVG () { //let x = this.radius - ( -this.pos.x * this.distance ); //let y = this.radius - ( -this.pos.y * this.distance ); let x = (this.diam*0.5) - ( -this.pos.x * this.distance ); let y = (this.diam*0.5) - ( -this.pos.y * this.distance ); if(this.model === 0){ let sx = x + ((this.pos.x)*5) + 5; let sy = y + ((this.pos.y)*5) + 10; this.setSvg( this.c[3], 'cx', sx*this.ratio, 3 ); this.setSvg( this.c[3], 'cy', sy*this.ratio, 3 ); } else { this.setSvg( this.c[3], 'cx', x*this.ratio, 3 ); this.setSvg( this.c[3], 'cy', y*this.ratio, 3 ); } this.setSvg( this.c[3], 'cx', x*this.ratio, 4 ); this.setSvg( this.c[3], 'cy', y*this.ratio, 4 ); this.value[0] = ( this.pos.x * this.multiplicator ).toFixed( this.precision ) * 1; this.value[1] = ( this.pos.y * this.multiplicator ).toFixed( this.precision ) * 1; this.c[2].textContent = this.value; } clear () { this.stopInterval(); super.clear(); } } class Knob extends Proto { constructor( o = {} ) { super( o ); this.isCyclic = o.cyclic || false; this.model = o.stype || 0; if( o.mode !== undefined ) this.model = o.mode; this.autoWidth = false; this.setTypeNumber( o ); this.minw = this.w; this.diam = o.diam || this.w; this.mPI = Math.PI * 0.8; this.toDeg = 180 / Math.PI; this.cirRange = this.mPI * 2; this.offset = new V2(); this.h = o.h || this.w + 10; this.top = 0; this.c[0].style.width = this.w +'px'; if(this.c[1] !== undefined) { this.c[1].style.width = '100%'; this.c[1].style.justifyContent = 'center'; this.top = 10; this.h += 10; } this.percent = 0; this.cmode = 0; let cc = this.colors; this.c[2] = this.dom( 'div', this.css.txt + 'justify-content:center; top:'+(this.h-20)+'px; width:100%; color:'+ cc.text ); this.c[3] = this.getKnob(); this.setSvg( this.c[3], 'fill', cc.button, 0 ); this.setSvg( this.c[3], 'stroke', cc.text, 1 ); this.setSvg( this.c[3], 'stroke', cc.text, 3 ); this.setSvg( this.c[3], 'd', this.makeGrad(), 3 ); this.setSvg( this.c[3], 'viewBox', '0 0 ' + this.diam + ' ' + this.diam ); this.setCss( this.c[3], { width:this.diam, height:this.diam, left:0, top:this.top }); if ( this.model > 0 ) { Tools.dom( 'path', '', { d: '', stroke:cc.text, 'stroke-width': 2, fill: 'none', 'stroke-linecap': 'round' }, this.c[3] ); //4 if ( this.model == 2) { Tools.addSVGGlowEffect(); this.setSvg( this.c[3], 'style', 'filter: url("#UILGlow");', 4 ); } } this.r = 0; this.init(); this.update(); } mode ( mode ) { let cc = this.colors; if( this.cmode === mode ) return false; switch( mode ) { case 0: // base this.s[2].color = cc.text; this.setSvg( this.c[3], 'fill', cc.button, 0); //this.setSvg( this.c[3], 'stroke','rgba(255,0,0,0.2)', 2); this.setSvg( this.c[3], 'stroke', cc.text, 1 ); break; case 1: // down this.s[2].color = cc.textOver; this.setSvg( this.c[3], 'fill', cc.select, 0); //this.setSvg( this.c[3], 'stroke','rgba(0,0,0,0.6)', 2); this.setSvg( this.c[3], 'stroke', cc.textOver, 1 ); break; } this.cmode = mode; return true; } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( l.y <= this.c[ 1 ].offsetHeight ) return 'title'; else if ( l.y > this.h - this.c[ 2 ].offsetHeight ) return 'text'; else return 'knob'; } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { this.isDown = false; this.sendEnd(); return this.mode(0) } mousedown ( e ) { this.isDown = true; this.old = this.value; this.oldr = null; this.mousemove( e ); return this.mode(1) } mousemove ( e ) { if( !this.isDown ) return; let off = this.offset; //off.x = this.radius - ( e.clientX - this.zone.x ); //off.y = this.radius - ( e.clientY - this.zone.y - this.top ); off.x = (this.w*0.5) - ( e.clientX - this.zone.x ); off.y = (this.diam*0.5) - ( e.clientY - this.zone.y - this.top ); this.r = - Math.atan2( off.x, off.y ); if( this.oldr !== null ) this.r = Math.abs(this.r - this.oldr) > Math.PI ? this.oldr : this.r; this.r = this.r > this.mPI ? this.mPI : this.r; this.r = this.r < -this.mPI ? -this.mPI : this.r; let steps = 1 / this.cirRange; let value = (this.r + this.mPI) * steps; let n = ( ( this.range * value ) + this.min ) - this.old; if(n >= this.step || n <= this.step){ n = Math.floor( n / this.step ); this.value = this.numValue( this.old + ( n * this.step ) ); this.update( true ); this.old = this.value; this.oldr = this.r; } } wheel ( e ) { let name = this.testZone( e ); if( name === 'knob' ) { let v = this.value - this.step * e.delta; if ( v > this.max ) { v = this.isCyclic ? this.min : this.max; } else if ( v < this.min ) { v = this.isCyclic ? this.max : this.min; } this.setValue( v ); this.old = v; this.update( true ); return true; } return false; } makeGrad () { let d = '', step, range, a, x, y, x2, y2, r = 64; let startangle = Math.PI + this.mPI; let endangle = Math.PI - this.mPI; //let step = this.step>5 ? this.step : 1; if(this.step>5){ range = this.range / this.step; step = ( startangle - endangle ) / range; } else { step = (( startangle - endangle ) / r)*2; range = r*0.5; } for ( let i = 0; i <= range; ++i ) { a = startangle - ( step * i ); x = r + Math.sin( a ) * ( r - 20 ); y = r + Math.cos( a ) * ( r - 20 ); x2 = r + Math.sin( a ) * ( r - 24 ); y2 = r + Math.cos( a ) * ( r - 24 ); d += 'M' + x + ' ' + y + ' L' + x2 + ' '+y2 + ' '; } return d; } update ( up ) { this.c[2].textContent = this.value; this.percent = (this.value - this.min) / this.range; let sa = Math.PI + this.mPI; let ea = ( ( this.percent * this.cirRange ) - ( this.mPI ) ); let sin = Math.sin( ea ); let cos = Math.cos( ea ); let x1 = ( 25 * sin ) + 64; let y1 = -( 25 * cos ) + 64; let x2 = ( 20 * sin ) + 64; let y2 = -( 20 * cos ) + 64; this.setSvg( this.c[3], 'd', 'M ' + x1 +' ' + y1 + ' L ' + x2 +' ' + y2, 1 ); if ( this.model > 0 ) { let x1 = 36 * Math.sin( sa ) + 64; let y1 = 36 * Math.cos( sa ) + 64; let x2 = 36 * sin + 64; let y2 = -36 * cos + 64; let big = ea <= Math.PI - this.mPI ? 0 : 1; this.setSvg( this.c[3], 'd', 'M ' + x1 + ',' + y1 + ' A ' + 36 + ',' + 36 + ' 1 ' + big + ' 1 ' + x2 + ',' + y2, 4 ); let color = Tools.pack( Tools.lerpColor( Tools.unpack( Tools.ColorLuma( this.colors.text, -0.75) ), Tools.unpack( this.colors.text ), this.percent ) ); this.setSvg( this.c[3], 'stroke', color, 4 ); } if( up ) this.send(); } } class List extends Proto { constructor( o = {} ) { super( o ); // images this.path = o.path || ''; this.format = o.format || ''; this.isWithImage = this.path !== '' ? true:false; this.preLoadComplete = false; this.tmpImage = {}; this.tmpUrl = []; //this.autoHeight = false; let align = o.align || 'center'; // scroll size let ss = o.scrollSize || 10; this.ss = ss+1; this.sMode = 0; this.tMode = 0; this.listOnly = o.listOnly || false; this.staticTop = o.staticTop || false; this.isSelectable = this.listOnly; if( o.select !== undefined ) o.selectable = o.select; if( o.selectable !== undefined ) this.isSelectable = o.selectable; if( this.txt === '' ) this.p = 0; let fltop = Math.floor(this.h*0.5)-5; let cc = this.colors; this.c[2] = this.dom( 'div', this.css.basic + 'top:0; display:none; border-radius:'+this.radius+'px;' ); this.c[3] = this.dom( 'div', this.css.item + 'position:absolute; text-align:'+align+'; line-height:'+(this.h-4)+'px; top:1px; background:'+cc.button+'; height:'+(this.h-2)+'px; border:1px solid '+cc.border+'; border-radius:'+this.radius+'px;' ); this.c[4] = this.dom( 'path', this.css.basic + 'position:absolute; width:10px; height:10px; top:'+fltop+'px;', { d:this.svgs.arrow, fill:cc.text, stroke:'none'}); this.scrollerBack = this.dom( 'div', this.css.basic + 'right:0px; width:'+ss+'px; background:'+cc.back+'; display:none;'); this.scroller = this.dom( 'div', this.css.basic + 'right:'+((ss-(ss*0.25))*0.5)+'px; width:'+(ss*0.25)+'px; background:'+cc.text+'; display:none; '); this.c[3].style.color = cc.text; this.list = []; this.refObject = null; if(o.list){ if( o.list instanceof Array ){ this.list = o.list; } else { this.refObject = o.list; for( let g in this.refObject ) this.list.push(g); } } this.items = []; this.prevName = ''; this.baseH = this.h; this.itemHeight = o.itemHeight || (this.h-3); // force full list this.full = o.full || false; this.py = 0; this.ww = this.sb; this.scroll = false; this.isDown = false; this.current = null; // list up or down this.side = o.side || 'down'; this.up = this.side === 'down' ? 0 : 1; if( this.up ){ this.c[2].style.top = 'auto'; this.c[3].style.top = 'auto'; this.c[4].style.top = 'auto'; //this.c[5].style.top = 'auto'; this.c[2].style.bottom = this.h-2 + 'px'; this.c[3].style.bottom = '1px'; this.c[4].style.bottom = fltop + 'px'; } else { this.c[2].style.top = this.baseH + 'px'; } this.listIn = this.dom( 'div', this.css.basic + 'left:0; top:0; width:100%; background:none;'); this.listIn.name = 'list'; this.topList = 0; this.c[2].appendChild( this.listIn ); this.c[2].appendChild( this.scrollerBack ); this.c[2].appendChild( this.scroller ); if( o.value !== undefined ){ if(!isNaN(o.value)) this.value = this.list[ o.value ]; else this.value = o.value; }else { this.value = this.list[0]; } this.isOpenOnStart = o.open || false; if( this.listOnly ){ this.baseH = 5; this.c[3].style.display = 'none'; this.c[4].style.display = 'none'; this.c[2].style.top = this.baseH+'px'; this.isOpenOnStart = true; } this.miniCanvas = o.miniCanvas || false; this.canvasBg = o.canvasBg || 'rgba(0,0,0,0)'; this.imageSize = o.imageSize || [20,20]; // dragout function this.drag = o.drag || false; this.dragout = o.dragout || false; this.dragstart = o.dragstart || null; this.dragend = o.dragend || null; //this.c[0].style.background = '#FF0000' if( this.isWithImage ) this.preloadImage(); // } else { // populate list this.setList( this.list ); this.init(); if( this.isOpenOnStart ) this.open( true ); // } } /*send ( v ) { super.send( v ); //Proto.prototype.send.call( this, v ); }*/ // image list preloadImage () { this.preLoadComplete = false; this.tmpImage = {}; for( let i=0; i this.h - this.baseH ) return 'title'; else { if( this.scroll && ( l.x > (this.sa+this.sb-this.ss)) ) return 'scroll'; if(l.x > this.sa) return this.testItems( l.y-this.baseH ); } } else { if( l.y < this.baseH+2 ) return 'title'; else { if( this.isOpen ){ if( this.scroll && ( l.x > (this.sa+this.sb-this.ss)) ) return 'scroll'; if(l.x > this.sa) return this.testItems( l.y-this.baseH ); } } } return ''; } testItems ( y ) { let name = ''; let i = this.items.length, item, a, b; while(i--){ item = this.items[i]; a = item.posy + this.topList; b = item.posy + this.itemHeight + 1 + this.topList; if( y >= a && y <= b ){ name = 'item' + i; this.modeItem(0); this.current = item; this.modeItem(1); return name; } } return name; } modeItem ( mode ) { if( !this.current ) return if( this.current.select && mode===0) mode = 2; let cc = this.colors; switch( mode ){ case 0: // base this.current.style.background = cc.back; this.current.style.color = cc.text; break; case 1: // over this.current.style.background = cc.over; this.current.style.color = cc.textOver; break; case 2: // edit / down this.current.style.background = cc.select; this.current.style.color = cc.textSelect; break; } } unSelected() { if( !this.current ) return this.modeItem(0); this.current = null; } selected() { if( !this.current ) return this.resetItems(); this.modeItem(2); this.current.select = true; } resetItems() { let i = this.items.length; while(i--){ this.items[i].select = false; this.items[i].style.background = this.colors.back; this.items[i].style.color = this.colors.text; } } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { this.isDown = false; } mousedown ( e ) { let name = this.testZone( e ); if( !name ) return false; if( name === 'scroll' ){ this.isDown = true; this.mousemove( e ); } else if( name === 'title' ){ this.modeTitle(2); if( !this.listOnly ){ if( !this.isOpen ) this.open(); else this.close(); } } else { // is item if( this.current ){ this.value = this.list[ this.current.id ]; if( this.isSelectable ) this.selected(); //this.value = this.refObject !== null ? this.refObject[this.list[this.current.id]] : this.list[this.current.id] //this.value = this.current.textContent; this.send( this.refObject !== null ? this.refObject[this.list[this.current.id]] : this.value ); if( !this.listOnly ) { this.close(); this.setTopItem(); } } } return true; } mousemove ( e ) { let nup = false; let name = this.testZone( e ); if( !name ) return nup; if( name === 'title' ){ this.unSelected(); this.modeTitle(1); this.cursor('pointer'); } else if( name === 'scroll' ){ this.cursor('s-resize'); this.modeScroll(1); if( this.isDown ){ this.modeScroll(2); let top = this.zone.y+this.baseH-2; this.update( ( e.clientY - top ) - ( this.sh*0.5 ) ); } //if(this.isDown) this.listmove(e); } else { // is item this.modeTitle(0); this.modeScroll(0); this.cursor('pointer'); } if( name !== this.prevName ) nup = true; this.prevName = name; return nup; } wheel ( e ) { let name = this.testZone( e ); if( name === 'title' ) return false; this.py += e.delta*10; this.update(this.py); return true; } // ---------------------- reset () { this.prevName = ''; this.unSelected(); this.modeTitle(0); this.modeScroll(0); //console.log('this is reset') } modeScroll ( mode ) { if( mode === this.sMode ) return; let s = this.scroller.style; let cc = this.colors; switch(mode){ case 0: // base s.background = cc.text; break; case 1: // over s.background = cc.select; break; case 2: // edit / down s.background = cc.select; break; } this.sMode = mode; } modeTitle ( mode ) { if( mode === this.tMode ) return; let s = this.s; let cc = this.colors; switch(mode){ case 0: // base s[3].color = cc.text; s[3].background = cc.button; break; case 1: // over s[3].color = cc.textOver; s[3].background = cc.overoff; break; case 2: // edit / down s[3].color = cc.textSelect; s[3].background = cc.overoff; break; } this.tMode = mode; } clearList () { while ( this.listIn.children.length ) this.listIn.removeChild( this.listIn.lastChild ); this.items = []; } setList ( list ) { this.clearList(); this.list = list; this.length = this.list.length; this.maxItem = this.full ? this.length : 5; this.maxItem = this.length < this.maxItem ? this.length : this.maxItem; this.maxHeight = this.maxItem * (this.itemHeight+1) + 2; this.max = this.length * (this.itemHeight+1) + 2; this.ratio = this.maxHeight / this.max; this.sh = this.maxHeight * this.ratio; this.range = this.maxHeight - this.sh; this.c[2].style.height = this.maxHeight + 'px'; this.scrollerBack.style.height = this.maxHeight + 'px'; this.scroller.style.height = this.sh + 'px'; if( this.max > this.maxHeight ){ this.ww = this.sb - this.ss; this.scroll = true; } if( this.miniCanvas ) { this.tmpCanvas = document.createElement('canvas'); this.tmpCanvas.width = this.imageSize[0]; this.tmpCanvas.height = this.imageSize[1]; this.tmpCtx = this.tmpCanvas.getContext("2d"); this.tmpCtx.fillStyle = this.canvasBg; this.tmpCtx.fillRect(0, 0, this.imageSize[0], this.imageSize[1]); } let item, n;//, l = this.sb; for( let i=0; i this.range ? this.range : y; this.topList = -Math.floor( y / this.ratio ); this.listIn.style.top = this.topList+'px'; this.scroller.style.top = Math.floor( y ) + 'px'; this.py = y; } parentHeight ( t ) { if ( this.group !== null ) this.group.calc( t ); else if ( this.isUI ) this.main.calc( t ); } open ( first ) { super.open(); this.update( 0 ); this.h = this.maxHeight + this.baseH + 5; if( !this.scroll ){ this.topList = 0; this.h = this.baseH + 5 + this.max; this.scroller.style.display = 'none'; this.scrollerBack.style.display = 'none'; } else { this.scroller.style.display = 'block'; this.scrollerBack.style.display = 'block'; } this.s[0].height = this.h + 'px'; this.s[2].display = 'block'; if( this.up ){ this.zone.y -= this.h - (this.baseH-10); this.setSvg( this.c[4], 'd', this.svgs.arrowUp ); } else { this.setSvg( this.c[4], 'd', this.svgs.arrowDown ); } this.rSizeContent(); let t = this.h - this.baseH; this.zone.h = this.h; if(!first) this.parentHeight( t ); } close () { super.close(); if( this.up ) this.zone.y += this.h - (this.baseH-10); let t = this.h - this.baseH; this.h = this.baseH; this.s[0].height = this.h + 'px'; this.s[2].display = 'none'; this.setSvg( this.c[4], 'd', this.svgs.arrow ); this.zone.h = this.h; this.parentHeight( -t ); } // ----- text ( txt ) { this.c[3].textContent = txt; } rSizeContent () { let i = this.length; while(i--) this.listIn.children[i].style.width = this.ww + 'px'; } rSize () { super.rSize(); //Proto.prototype.rSize.call( this ); let s = this.s; let w = this.sb; let d = this.sa; if(s[2]=== undefined) return; s[2].width = w + 'px'; s[2].left = d +'px'; s[3].width = w + 'px'; s[3].left = d + 'px'; s[4].left = d + w - 17 + 'px'; this.ww = w; if( this.max > this.maxHeight ) this.ww = w-this.ss; if(this.isOpen) this.rSizeContent(); } } class Numeric extends Proto { constructor( o = {} ) { super( o ); this.setTypeNumber( o ); this.allway = o.allway || false; this.isDown = false; this.value = [0]; this.multy = 1; this.invmulty = 1; this.isSingle = true; this.isAngle = false; this.isVector = false; if( o.isAngle ){ this.isAngle = true; this.multy = Tools.torad; this.invmulty = Tools.todeg; } this.isDrag = o.drag || false; if( o.value !== undefined ){ if(!isNaN(o.value)){ this.value = [o.value]; } else if( o.value instanceof Array ){ this.value = o.value; this.isSingle = false; } else if( o.value instanceof Object ){ this.value = []; if( o.value.x !== undefined ) this.value[0] = o.value.x; if( o.value.y !== undefined ) this.value[1] = o.value.y; if( o.value.z !== undefined ) this.value[2] = o.value.z; if( o.value.w !== undefined ) this.value[3] = o.value.w; this.isVector = true; this.isSingle = false; } } this.lng = this.value.length; this.tmp = []; this.current = -1; this.prev = { x:0, y:0, d:0, v:0 }; let cc = this.colors; // bg this.c[2] = this.dom( 'div', this.css.basic + ' background:' + cc.select + '; top:4px; width:0px; height:' + (this.h-8) + 'px;' ); this.cMode = []; let i = this.lng; while(i--){ if(this.isAngle) this.value[i] = (this.value[i] * 180 / Math.PI).toFixed( this.precision ); this.c[3+i] = this.dom( 'div', this.css.txtselect + ' height:'+(this.h-4)+'px; color:' + cc.text + '; background:' + cc.back + '; borderColor:' + cc.border+'; border-radius:'+this.radius+'px;'); if(o.center) this.c[2+i].style.textAlign = 'center'; this.c[3+i].textContent = this.value[i]; this.c[3+i].style.color = this.colors.text; this.c[3+i].isNum = true; this.cMode[i] = 0; } // selection this.selectId = 3 + this.lng; this.c[this.selectId] = this.dom( 'div', this.css.txtselect + 'position:absolute; top:4px; height:' + (this.h-8) + 'px; padding:0px 0px; width:0px; color:' + cc.textSelect + '; background:' + cc.select + '; border:none; border-radius:0px;'); // cursor this.cursorId = 4 + this.lng; this.c[ this.cursorId ] = this.dom( 'div', this.css.basic + 'top:4px; height:' + (this.h-8) + 'px; width:0px; background:'+cc.text+';' ); this.init(); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; let i = this.lng; let t = this.tmp; while( i-- ){ if( l.x>t[i][0] && l.x= this.txl ) return 'text'; else if( l.x >= this.sa ) return 'scroll'; else return ''; } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { if( this.isDown ) this.isDown = false; } mousedown ( e ) { let name = this.testZone( e ); if( !name ) return false; if( name === 'scroll' ){ this.isDown = true; this.old = this.value; this.mousemove( e ); } /*if( name === 'text' ){ this.setInput( this.c[2], function(){ this.validate() }.bind(this) ); }*/ return true; } mousemove ( e ) { let nup = false; let name = this.testZone( e ); if( name === 'scroll' ) { this.mode(1); this.cursor('w-resize'); //} else if(name === 'text'){ //this.cursor('pointer'); } else { this.cursor(); } if( this.isDown ){ let n = ((( e.clientX - (this.zone.x+this.sa) - 3 ) / this.ww ) * this.range + this.min ) - this.old; if(n >= this.step || n <= this.step){ n = Math.floor( n / this.step ); this.value = this.numValue( this.old + ( n * this.step ) ); this.update( true ); this.old = this.value; } nup = true; } return nup; } wheel ( e ) { let name = this.testZone( e ); if( name === 'scroll' ) { let v = this.value - this.step * e.delta; if ( v > this.max ) { v = this.isCyclic ? this.min : this.max; } else if ( v < this.min ) { v = this.isCyclic ? this.max : this.min; } this.setValue(v); this.old = v; this.update( true ); return true; } return false; } //keydown: function ( e ) { return true; }, // ---------------------- validate () { let n = this.c[2].textContent; if(!isNaN( n )){ this.value = this.numValue( n ); this.update(true); } else this.c[2].textContent = this.value + (this.isDeg ? '°':''); } reset () { //this.clearInput(); this.isDown = false; this.mode(0); } mode ( mode ) { let s = this.s; let cc = this.colors; switch(mode){ case 0: // base // s[2].border = '1px solid ' + this.colors.hide; s[2].color = cc.text; s[4].background = cc.back; s[5].background = cc.text; break; case 1: // scroll over //s[2].border = '1px dashed ' + this.colors.hide; s[2].color = cc.textOver; s[4].background = cc.back; s[5].background = cc.textOver; break; /* case 2: s[2].border = '1px solid ' + this.colors.borderSelect; break; case 3: s[2].border = '1px dashed ' + this.colors.text;//this.colors.borderSelect; break; case 4: s[2].border = '1px dashed ' + this.colors.hide; break;*/ } } update ( up ) { let ww = Math.floor( this.ww * (( this.value - this.min ) / this.range )); if(this.model !== 3) this.s[5].width = ww + 'px'; if(this.s[6]) this.s[6].left = ( this.sa + ww + 3 ) + 'px'; this.c[2].textContent = this.value + (this.isDeg ? '°':''); if( up ) this.send(); } rSize () { super.rSize(); let w = this.sb - this.sc; this.ww = w - 6; let tx = this.sc; if(this.isUI || !this.simple) tx = this.sc+10; this.txl = this.w - tx + 2; //let ty = Math.floor(this.h * 0.5) - 8; let s = this.s; s[2].width = (this.sc -6 )+ 'px'; s[2].left = (this.txl +4) + 'px'; //s[2].top = ty + 'px'; s[3].left = this.sa + 'px'; s[3].width = w + 'px'; s[4].left = this.sa + 'px'; s[4].width = w + 'px'; s[5].left = (this.sa + 3) + 'px'; this.update(); } } class TextInput extends Proto { constructor( o = {} ) { super( o ); this.cmode = 0; this.value = o.value || ''; this.placeHolder = o.placeHolder || ''; this.allway = o.allway || false; this.editable = o.edit !== undefined ? o.edit : true; this.isDown = false; let cc = this.colors; // text this.c[2] = this.dom( 'div', this.css.txtselect + 'height:' + (this.h-4) + 'px; color:' + cc.text + '; background:' + cc.back + '; borderColor:' + cc.border+'; border-radius:'+this.radius+'px;' ); this.c[2].textContent = this.value; // selection this.c[3] = this.dom( 'div', this.css.txtselect + 'position:absolute; top:4px; height:' + (this.h-8) + 'px; padding:0px 0px; width:0px; color:' + cc.textSelect + '; background:' + cc.select + '; border:none; border-radius:0px;'); // cursor this.c[4] = this.dom( 'div', this.css.basic + 'top:4px; height:' + (this.h-8) + 'px; width:0px; background:'+cc.text+';' ); // fake this.c[5] = this.dom( 'div', this.css.txtselect + 'height:' + (this.h-4) + 'px; justify-content: center; font-style: italic; color:'+cc.border+';' ); if( this.value === '' ) this.c[5].textContent = this.placeHolder; this.init(); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( l.x >= this.sa ) return 'text'; return ''; } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { if(!this.editable) return; if( this.isDown ){ this.isDown = false; return this.mousemove( e ); } return false; } mousedown ( e ) { if(!this.editable) return; let name = this.testZone( e ); if( !this.isDown ){ this.isDown = true; if( name === 'text' ) this.setInput( this.c[2] ); return this.mousemove( e ); } return false; } mousemove ( e ) { if(!this.editable) return; let name = this.testZone( e ); //let l = this.local; //if( l.x === -1 && l.y === -1 ){ return;} //if( l.x >= this.sa ) this.cursor('text'); //else this.cursor(); let x = 0; if( name === 'text' ) this.cursor('text'); else this.cursor(); if( this.isDown ) x = e.clientX - this.zone.x; return this.upInput( x - this.sa -3, this.isDown ); } update ( ) { this.c[2].textContent = this.value; } // ---------------------- reset () { this.cursor(); } // ---------------------- // INPUT // ---------------------- select ( c, e, w, t ) { let s = this.s; let d = this.sa + 5; s[4].width = '1px'; s[4].left = ( d + e ) + 'px'; s[3].left = ( d + e ) + 'px'; s[3].width = w + 'px'; this.c[3].innerHTML = t; } unselect () { let s = this.s; if(!s) return; s[3].width = 0 + 'px'; this.c[3].innerHTML = 't'; s[4].width = 0 + 'px'; } validate ( force ) { if( this.allway ) force = true; this.value = this.c[2].textContent; if(this.value !== '') this.c[5].textContent = ''; else this.c[5].textContent = this.placeHolder; if( !force ) return; this.send(); } // ---------------------- // REZISE // ---------------------- rSize () { super.rSize(); let s = this.s; s[2].left = this.sa + 'px'; s[2].width = this.sb + 'px'; s[5].left = this.sa + 'px'; s[5].width = this.sb + 'px'; } } class Title extends Proto { constructor( o = {} ) { super( o ); let prefix = o.prefix || ''; this.c[2] = this.dom( 'div', this.css.txt + 'justify-content:right; width:60px; line-height:'+ (this.h-8) + 'px; color:' + this.colors.text ); if( this.h === 31 ){ this.s[0].height = this.h + 'px'; this.s[1].top = 8 + 'px'; this.c[2].style.top = 8 + 'px'; } let s = this.s; s[1].justifyContent = o.align || 'left'; //s[1].textAlign = o.align || 'left'; s[1].fontWeight = o.fontWeight || 'bold'; this.c[1].textContent = this.txt.substring(0,1).toUpperCase() + this.txt.substring(1).replace("-", " "); this.c[2].textContent = prefix; this.init(); } text( txt ) { this.c[1].textContent = txt; } text2( txt ) { this.c[2].textContent = txt; } rSize() { super.rSize(); this.s[1].width = this.w + 'px'; //- 50 + 'px'; this.s[2].left = this.w + 'px';//- ( 50 + 26 ) + 'px'; } setColor( c ) { this.s[1].color = c; this.s[2].color = c; } } class Select extends Proto { constructor( o = {} ) { super( o ); this.value = o.value || ''; this.isDown = false; this.onActif = o.onActif || function(){}; o.prefix || ''; this.c[2] = this.dom( 'div', this.css.txt + this.css.button + ' top:1px; background:'+this.colors.button+'; height:'+(this.h-2)+'px; border:'+this.colors.buttonBorder+'; border-radius:15px; width:30px; left:10px;' ); this.c[2].style.color = this.fontColor; this.c[3] = this.dom( 'div', this.css.txtselect + 'height:' + (this.h-4) + 'px; background:' + this.colors.inputBg + '; borderColor:' + this.colors.inputBorder+'; border-radius:'+this.radius+'px;' ); this.c[3].textContent = this.value; let fltop = Math.floor(this.h*0.5)-7; this.c[4] = this.dom( 'path', this.css.basic + 'position:absolute; width:14px; height:14px; left:5px; top:'+fltop+'px;', { d:this.svgs[ 'cursor' ], fill:this.fontColor, stroke:'none'}); this.stat = 1; this.isActif = false; this.init(); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( l.x > this.sa && l.x < this.sa+30 ) return 'over'; return '0' } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { if( this.isDown ){ //this.value = false; this.isDown = false; //this.send(); return this.mousemove( e ); } return false; } mousedown ( e ) { let name = this.testZone( e ); if( !name ) return false; this.isDown = true; //this.value = this.values[ name-2 ]; //this.send(); return this.mousemove( e ); } mousemove ( e ) { let up = false; let name = this.testZone( e ); //let sel = false; //console.log(name) if( name === 'over' ){ this.cursor('pointer'); up = this.mode( this.isDown ? 3 : 2 ); } else { up = this.reset(); } return up; } // ---------------------- apply ( v ) { v = v || ''; if( v !== this.value ) { this.value = v; this.c[3].textContent = this.value; this.send(); } this.mode(1); } update () { this.mode( 3 ); } mode ( n ) { let change = false; let cc = this.colors; if( this.stat !== n ){ if( n===1 ) this.isActif = false; if( n===3 ){ if( !this.isActif ){ this.isActif = true; n=4; this.onActif( this ); } else { this.isActif = false; } } if( n===2 && this.isActif ) n = 4; this.stat = n; switch( n ){ case 1: this.s[ 2 ].color = cc.text; this.s[ 2 ].background = cc.button; break; // base case 2: this.s[ 2 ].color = cc.textOver; this.s[ 2 ].background = cc.over; break; // over case 3: this.s[ 2 ].color = cc.textSelect; this.s[ 2 ].background = cc.select; break; // down case 4: this.s[ 2 ].color = cc.textSelect; this.s[ 2 ].background = cc.action; break; // actif } change = true; } return change; } reset () { this.cursor(); return this.mode( this.isActif ? 4 : 1 ); } text ( txt ) { this.c[3].textContent = txt; } rSize () { super.rSize(); let s = this.s; s[2].left = this.sa + 'px'; s[3].left = (this.sa + 40) + 'px'; s[3].width = (this.sb - 40) + 'px'; s[4].left = (this.sa+8) + 'px'; } } //import { Proto } from '../core/Proto.js'; class Selector extends Button { constructor( o = {} ) { if( o.selectable === undefined ) o.selectable = true; super( o ); } } class Empty extends Proto { constructor( o = {} ) { o.isSpace = true; o.margin = 0; if(!o.h) o.h = 10; super( o ); this.init(); } } class Item extends Proto { constructor( o = {} ) { super( o ); this.p = 100; this.value = this.txt; this.status = 1; this.itype = o.itype || 'none'; this.val = this.itype; this.graph = this.svgs[ this.itype ]; let fltop = Math.floor(this.h*0.5)-7; this.c[2] = this.dom( 'path', this.css.basic + 'position:absolute; width:14px; height:14px; left:5px; top:'+fltop+'px;', { d:this.graph, fill:this.colors.text, stroke:'none'}); this.s[1].marginLeft = 20 + 'px'; this.init(); } // ---------------------- // EVENTS // ---------------------- mousemove ( e ) { this.cursor('pointer'); //up = this.modes( this.isDown ? 3 : 2, name ); } mousedown ( e ) { if( this.isUI ) this.main.resetItem(); this.selected( true ); this.send(); return true; } uiout () { if( this.isSelect ) this.mode(3); else this.mode(1); } uiover () { if( this.isSelect ) this.mode(4); else this.mode(2); } update () { } /*rSize () { super.rSize(); }*/ mode ( n ) { let change = false; if( this.status !== n ){ this.status = n; let s = this.s, cc = this.colors; switch( n ){ case 1: this.status = 1; s[1].color = cc.text; s[0].background = 'none'; break; case 2: this.status = 2; s[1].color = cc.textOver; s[0].background = cc.back; break; case 3: this.status = 3; s[1].color = cc.textSelect; s[0].background = cc.select; break; case 4: this.status = 4; s[1].color = cc.textOver; s[0].background = cc.over; break; } change = true; } return change; } reset () { this.cursor(); // return this.mode( 1 ); } selected ( b ){ if( this.isSelect ) this.mode(1); this.isSelect = b || false; if( this.isSelect ) this.mode(3); } } class Grid extends Proto { constructor( o = {} ) { super( o ); this.values = o.values || []; if( typeof this.values === 'string' ) this.values = [ this.values ]; this.lng = this.values.length; this.value = o.value || null; this.isSelectable = o.selectable || false; this.spaces = o.spaces || [5,3]; this.bsize = o.bsize || [90,20]; if(o.h) this.bsize[1] = o.h; this.bsizeMax = this.bsize[0]; this.tmp = []; this.stat = []; this.grid = [ 2, Math.round( this.lng * 0.5 ) ]; this.h = this.grid[1] * ( this.bsize[1] + this.spaces[1] ) + this.spaces[1]; this.c[1].textContent = ''; this.c[2] = this.dom( 'table', this.css.basic + 'width:100%; top:'+(this.spaces[1]-2)+'px; height:auto; border-collapse:separate; border:none; border-spacing: '+(this.spaces[0]-2)+'px '+(this.spaces[1]-2)+'px;' ); let n = 0, b, td, tr, sel; this.res = -1; this.isDown = false; this.neverlock = true; this.buttons = []; this.stat = []; this.tmpX = []; this.tmpY = []; let cc = this.colors; for( let i = 0; i < this.grid[1]; i++ ){ tr = this.c[2].insertRow(); tr.style.cssText = 'pointer-events:none;'; for( let j = 0; j < this.grid[0]; j++ ){ td = tr.insertCell(); td.style.cssText = 'pointer-events:none;'; if( this.values[n] ){ sel = false; if( this.values[n] === this.value && this.isSelectable ) sel = true; b = document.createElement( 'div' ); b.style.cssText = this.css.txt + this.css.button + 'position:static; width:'+this.bsize[0]+'px; height:'+this.bsize[1]+'px; border:'+cc.borderSize+'px solid '+cc.border+'; left:auto; right:auto; border-radius:'+this.radius+'px;'; b.style.background = sel ? cc.select : cc.button; b.style.color = sel ? cc.textSelect : cc.text; b.innerHTML = this.values[n]; td.appendChild( b ); this.buttons.push(b); this.stat.push(1); } else { b = document.createElement( 'div' ); b.style.cssText = this.css.txt + 'position:static; width:'+this.bsize[0]+'px; height:'+this.bsize[1]+'px; text-align:center; left:auto; right:auto; background:none;'; td.appendChild( b ); } if(j===0) b.style.cssText += 'float:right;'; else b.style.cssText += 'float:left;'; n++; } } this.init(); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return -1; let tx = this.tmpX; let ty = this.tmpY; let id = -1; let c = -1; let line = -1; let i = this.grid[0]; while( i-- ){ if( l.x > tx[i][0] && l.x < tx[i][1] ) c = i; } i = this.grid[1]; while( i-- ){ if( l.y > ty[i][0] && l.y < ty[i][1] ) line = i; } if(c!==-1 && line!==-1){ id = c + (line*2); if(id>this.lng-1) id = -1; } return id; } // ---------------------- // EVENTS // ---------------------- mouseup ( e ) { if( !this.isDown ) return false this.isDown = false; if( this.res !== -1 ){ this.value = this.values[this.res]; this.send(); } return this.mousemove( e ) } mousedown ( e ) { if( this.isDown ) return false this.isDown = true; return this.mousemove( e ) } mousemove ( e ) { let up = false; this.res = this.testZone( e ); if( this.res !== -1 ){ this.cursor('pointer'); up = this.modes( this.isDown ? 3 : 2, this.res ); } else { up = this.reset(); } return up; } // ---------------------- // MODE // ----------------------- modes ( N = 1, id = -1 ) { let i = this.lng, w, n, r = false; while( i-- ){ n = N; w = this.isSelectable ? this.values[ i ] === this.value : false; if( i === id ){ if( w && n === 2 ) n = 3; } else { n = 1; if( w ) n = 4; } if( this.mode( n, i ) ) r = true; } return r } mode ( n, id ) { let change = false; let cc = this.colors, s = this.buttons; let i = id; if( this.stat[id] !== n ){ this.stat[id] = n; switch( n ){ case 1: s[i].style.color = cc.text; s[i].style.background = cc.button; break; case 2: s[i].style.color = cc.textOver; s[i].style.background = cc.overoff; break; case 3: s[i].style.color = cc.textOver; s[i].style.background = cc.over; break; case 4: s[i].style.color = cc.textSelect; s[i].style.background = cc.select; break; } change = true; } return change; } // ---------------------- reset () { this.res = -1; this.cursor(); return this.modes() } label ( string, n ) { this.buttons[n].textContent = string; } icon ( string, y, n ) { this.buttons[n].style.padding = ( y || 0 ) +'px 0px'; this.buttons[n].innerHTML = string; } testW () { let vw = this.spaces[0]*3 + this.bsizeMax*2, rz = false; if( vw > this.w ) { this.bsize[0] = ( this.w-(this.spaces[0]*3) ) * 0.5; rz = true; } else { if( this.bsize[0] !== this.bsizeMax ) { this.bsize[0] = this.bsizeMax; rz = true; } } if( !rz ) return; let i = this.buttons.length; while(i--) this.buttons[i].style.width = this.bsize[0] + 'px'; } rSize () { super.rSize(); this.testW(); let mid; this.tmpX = []; this.tmpY = []; for( let j = 0; j < this.grid[0]; j++ ){ if(j===0){ mid = ( this.w*0.5 ) - ( this.spaces[0]*0.5 ); this.tmpX.push( [ mid-this.bsize[0], mid ] ); } else { mid = ( this.w*0.5 ) + ( this.spaces[0]*0.5 ); this.tmpX.push( [ mid, mid+this.bsize[0] ] ); } } mid = this.spaces[1]; for( let i = 0; i < this.grid[1]; i++ ){ this.tmpY.push( [ mid, mid + this.bsize[1] ] ); mid += this.bsize[1] + this.spaces[1]; } } } class Pad2D extends Proto { constructor( o = {} ) { super( o ); this.autoWidth = false; this.minw = this.w; this.diam = o.diam || this.w; //this.margin = 15; this.pos = new V2(0,0); this.maxPos = 90; this.model = o.stype || 0; if( o.mode !== undefined ) this.model = o.mode; this.min = o.min === undefined ? -1 : o.min; this.max = o.max === undefined ? 1 : o.max; this.range = (this.max - this.min)*0.5; this.cmode = 0; //console.log(this.range) this.precision = o.precision === undefined ? 2 : o.precision; /*this.bounds = {}; this.bounds.x1 = o.x1 || -1; this.bounds.x2 = o.x2 || 1; this.bounds.y1 = o.y1 || -1; this.bounds.y2 = o.y2 || 1; this.lerpX = this.lerp( this.margin, this.w - this.margin , this.bounds.x1, this.bounds.x2 ); this.lerpY = this.lerp( this.margin, this.w - this.margin , this.bounds.y1, this.bounds.y2 ); this.alerpX = this.lerp( this.bounds.x1, this.bounds.x2, this.margin, this.w - this.margin ); this.alerpY = this.lerp( this.bounds.y1, this.bounds.y2, this.margin, this.w - this.margin );*/ this.value = ( Array.isArray( o.value ) && o.value.length == 2 ) ? o.value : [ 0, 0 ]; this.h = o.h || this.w + 10; this.top = 0; this.c[0].style.width = this.w + 'px'; // Title if( this.c[1] !== undefined ) { // with title this.c[1].style.width = '100%'; this.c[1].style.justifyContent = 'center'; this.top = 10; this.h += 10; } let cc = this.colors; // Value this.c[2] = this.dom( 'div', this.css.txt + 'justify-content:center; top:'+ ( this.h - 20 ) + 'px; width:100%; color:' + cc.text ); this.c[2].textContent = this.value; // Pad let pad = this.getPad2d(); this.setSvg( pad, 'fill', cc.back, 0 ); this.setSvg( pad, 'fill', cc.button, 1 ); this.setSvg( pad, 'stroke', cc.back, 2 ); this.setSvg( pad, 'stroke', cc.back, 3 ); this.setSvg( pad, 'stroke', cc.text, 4 ); this.setSvg( pad, 'viewBox', '0 0 '+this.diam+' '+this.diam ); this.setCss( pad, { width:this.diam, height:this.diam, left:0, top:this.top }); this.c[3] = pad; this.init(); this.setValue(); } testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; if( l.y <= this.c[ 1 ].offsetHeight ) return 'title'; else if ( l.y > this.h - this.c[ 2 ].offsetHeight ) return 'text'; else return 'pad'; /*if( ( l.x >= this.margin ) && ( l.x <= this.w - this.margin ) && ( l.y >= this.top + this.margin ) && ( l.y <= this.top + this.w - this.margin ) ) { return 'pad'; }*/ //return ''; } mouseup ( e ) { this.isDown = false; return this.mode(0); } mousedown ( e ) { if ( this.testZone(e) === 'pad' ) { this.isDown = true; this.mousemove( e ); return this.mode(1); } } mousemove ( e ) { if( !this.isDown ) return; let x = (this.w*0.5) - ( e.clientX - this.zone.x ); let y = (this.diam*0.5) - ( e.clientY - this.zone.y - this.top ); let r = 256 / this.diam; x = -(x*r); y = -(y*r); x = Tools.clamp( x, -this.maxPos, this.maxPos ); y = Tools.clamp( y, -this.maxPos, this.maxPos ); //let x = e.clientX - this.zone.x; //let y = e.clientY - this.zone.y - this.top; /*if( x < this.margin ) x = this.margin; if( x > this.w - this.margin ) x = this.w - this.margin; if( y < this.margin ) y = this.margin; if( y > this.w - this.margin ) y = this.w - this.margin;*/ //console.log(x,y) this.setPos( [ x , y ] ); this.update( true ); } mode ( mode ) { if( this.cmode === mode ) return false; let cc = this.colors; switch( mode ){ case 0: // base this.s[2].color = cc.text; this.setSvg( this.c[3], 'fill', cc.back, 0); this.setSvg( this.c[3], 'fill', cc.button, 1); this.setSvg( this.c[3], 'stroke', cc.back, 2); this.setSvg( this.c[3], 'stroke', cc.back, 3); this.setSvg( this.c[3], 'stroke', cc.text, 4 ); break; case 1: // down this.s[2].color = cc.textSelect; this.setSvg( this.c[3], 'fill', cc.backoff, 0); this.setSvg( this.c[3], 'fill', cc.overoff, 1); this.setSvg( this.c[3], 'stroke', cc.backoff, 2); this.setSvg( this.c[3], 'stroke', cc.backoff, 3); this.setSvg( this.c[3], 'stroke', cc.textSelect, 4 ); break; } this.cmode = mode; return true; } update ( up ) { //if( up === undefined ) up = true; this.c[2].textContent = this.value; this.updateSVG(); if( up ) this.send(); } updateSVG() { if ( this.model == 1 ) { this.setSvg( this.c[3], 'y1', this.pos.y, 2 ); this.setSvg( this.c[3], 'y2', this.pos.y, 2 ); this.setSvg( this.c[3], 'x1', this.pos.x, 3 ); this.setSvg( this.c[3], 'x2', this.pos.x, 3 ); } this.setSvg( this.c[3], 'cx', this.pos.x, 4 ); this.setSvg( this.c[3], 'cy', this.pos.y, 4 ); } setPos ( p ) { //if( p === undefined ) p = [ this.w / 2, this.w / 2 ]; this.pos.set( p[0]+128 , p[1]+128 ); let r = 1/this.maxPos; this.value[0] = ((p[0]*r)*this.range).toFixed( this.precision ); this.value[1] = ((p[1]*r)*this.range).toFixed( this.precision ); } setValue ( v, up = false ) { if( v === undefined ) v = this.value; /*if ( v[0] < this.bounds.x1 ) v[0] = this.bounds.x1; if ( v[0] > this.bounds.x2 ) v[0] = this.bounds.x2; if ( v[1] < this.bounds.y1 ) v[1] = this.bounds.y1; if ( v[1] > this.bounds.y2 ) v[1] = this.bounds.y2;*/ this.value[0] = Math.min( this.max, Math.max( this.min, v[0] ) ).toFixed( this.precision ) * 1; this.value[1] = Math.min( this.max, Math.max( this.min, v[1] ) ).toFixed( this.precision ) * 1; this.pos.set( ((this.value[0]/this.range)*this.maxPos)+128 , ((this.value[1]/this.range)*this.maxPos)+128 ); //console.log(this.pos) this.update( up ); } /*lerp( s1, s2, d1, d2, c = true ) { let s = ( d2 - d1 ) / ( s2 - s1 ); return c ? ( v ) => { return ( ( v < s1 ? s1 : v > s2 ? s2 : v ) - s1 ) * s + d1 } : ( v ) => { return ( v - s1 ) * s + d1 } }*/ } const add = function () { let a = arguments; let type, o, ref = false, n = null; if( typeof a[0] === 'string' ){ type = a[0]; o = a[1] || {}; } else if ( typeof a[0] === 'object' ){ // like dat gui ref = true; if( a[2] === undefined ) [].push.call(a, {}); type = a[2].type ? a[2].type : autoType( a[0][a[1]], a[2] ); o = a[2]; o.name = a[1]; if( type === 'list' ){ o.list = a[0][a[1]]; } else o.value = a[0][a[1]]; } let name = type.toLowerCase(); if( name === 'group' ) o.add = add; switch( name ){ case 'bool': case 'boolean': n = new Bool(o); break; case 'button': n = new Button(o); break; case 'circular': n = new Circular(o); break; case 'color': n = new Color(o); break; case 'fps': n = new Fps(o); break; case 'graph': n = new Graph(o); break; case 'group': n = new Group(o); break; case 'joystick': n = new Joystick(o); break; case 'knob': n = new Knob(o); break; case 'list': n = new List(o); break; case 'numeric': case 'number': n = new Numeric(o); break; case 'slide': n = new Slide(o); break; case 'textInput': case 'string': n = new TextInput(o); break; case 'title': case 'text': n = new Title(o); break; case 'select': n = new Select(o); break; case 'selector': n = new Selector(o); break; case 'empty': case 'space': n = new Empty(o); break; case 'item': n = new Item(o); break; case 'grid': n = new Grid(o); break; case 'pad2d': case 'pad': n = new Pad2D(o); break; } if( n !== null ){ if( ref ) n.setReferency( a[0], a[1] ); return n; } }; const autoType = function ( v, o ) { let type = 'slide'; if( typeof v === 'boolean' ) type = 'bool'; else if( typeof v === 'string' ){ if( v.substring(0,1) === '#' ) type = 'color'; else type = 'string'; } else if( typeof v === 'number' ){ if( o.ctype ) type = 'color'; else type = 'slide'; } else if( typeof v === 'array' && v instanceof Array ){ if( typeof v[0] === 'number' ) type = 'number'; else if( typeof v[0] === 'string' ) type = 'list'; } else if( typeof v === 'object' && v instanceof Object ){ if( v.x !== undefined ) type = 'number'; else type = 'list'; } return type }; /** * @author lth / https://github.com/lo-th */ class Gui { constructor( o = {} ) { this.isGui = true; this.name = 'gui'; // for 3d this.canvas = null; this.screen = null; this.plane = o.plane || null; // color if( o.config ) o.colors = o.config; if ( o.colors ) this.setConfig( o.colors ); else this.colors = Tools.defineColor( o ); // style this.css = Tools.cloneCss(); this.isReset = true; this.tmpAdd = null; //this.tmpH = 0 this.isCanvas = o.isCanvas || false; this.isCanvasOnly = false; this.callback = o.callback === undefined ? null : o.callback; this.forceHeight = o.maxHeight || 0; this.lockHeight = o.lockHeight || false; this.isItemMode = o.itemMode !== undefined ? o.itemMode : false; this.cn = ''; // size define this.size = Tools.size; if( o.p !== undefined ) this.size.p = o.p; if( o.w !== undefined ) this.size.w = o.w; if( o.h !== undefined ) this.size.h = o.h; if( o.s !== undefined ) this.size.s = o.s; this.size.h = this.size.h < 11 ? 11 : this.size.h; // local mouse and zone this.local = new V2().neg(); this.zone = { x:0, y:0, w:this.size.w, h:0 }; // virtual mouse this.mouse = new V2().neg(); this.h = 0; //this.prevY = -1; this.sw = 0; // bottom and close height this.isWithClose = o.close !== undefined ? o.close : true; this.bh = !this.isWithClose ? 0 : this.size.h; this.autoResize = o.autoResize === undefined ? true : o.autoResize; // default position this.isCenter = o.center || false; this.cssGui = o.css !== undefined ? o.css : (this.isCenter ? '' : 'right:10px;'); this.isOpen = o.open !== undefined ? o.open : true; this.isDown = false; this.isScroll = false; this.uis = []; this.current = -1; this.proto = null; this.isEmpty = true; this.decal = 0; this.ratio = 1; this.oy = 0; this.isNewTarget = false; let cc = this.colors; this.content = Tools.dom( 'div', this.css.basic + ' width:0px; height:auto; top:0px; background:'+cc.content+'; ' + this.cssGui ); this.innerContent = Tools.dom( 'div', this.css.basic + 'width:100%; top:0; left:0; height:auto; overflow:hidden;'); //this.innerContent = Tools.dom( 'div', this.css.basic + this.css.button + 'width:100%; top:0; left:0; height:auto; overflow:hidden;'); this.content.appendChild( this.innerContent ); //this.inner = Tools.dom( 'div', this.css.basic + 'width:100%; left:0; ') this.useFlex = true; let flexible = this.useFlex ? 'display:flex; flex-flow: row wrap;' : ''; //' display:flex; justify-content:start; align-items:start;flex-direction: column; justify-content: center; align-items: center;'; this.inner = Tools.dom( 'div', this.css.basic + flexible + 'width:100%; left:0; '); this.innerContent.appendChild(this.inner); // scroll this.scrollBG = Tools.dom( 'div', this.css.basic + 'right:0; top:0; width:'+ (this.size.s - 1) +'px; height:10px; display:none; background:'+cc.background+';'); this.content.appendChild( this.scrollBG ); this.scroll = Tools.dom( 'div', this.css.basic + 'background:'+cc.button+'; right:2px; top:0; width:'+(this.size.s-4)+'px; height:10px;'); this.scrollBG.appendChild( this.scroll ); // bottom button this.bottomText = o.bottomText || ['open', 'close']; let r = cc.radius; this.bottom = Tools.dom( 'div', this.css.txt + 'width:100%; top:auto; bottom:0; left:0; border-bottom-right-radius:'+r+'px; border-bottom-left-radius:'+r+'px; justify-content:center; height:'+this.bh+'px; line-height:'+(this.bh-5)+'px; color:' + cc.text+';' );// border-top:1px solid '+Tools.colors.stroke+';'); this.content.appendChild( this.bottom ); this.bottom.textContent = this.isOpen ? this.bottomText[1] : this.bottomText[0]; this.bottom.style.background = cc.background; // this.parent = o.parent !== undefined ? o.parent : null; this.parent = o.target !== undefined ? o.target : this.parent; if( this.parent === null && !this.isCanvas ){ this.parent = document.body; } if( this.parent !== null ) this.parent.appendChild( this.content ); if( this.isCanvas && this.parent === null ) this.isCanvasOnly = true; if( !this.isCanvasOnly ){ this.content.style.pointerEvents = 'auto'; } else { this.content.style.left = '0px'; this.content.style.right = 'auto'; o.transition = 0; } // height transition this.transition = o.transition || Tools.transition; if( this.transition ) setTimeout( this.addTransition.bind( this ), 0 ); this.setWidth(); if( this.isCanvas ) this.makeCanvas(); Roots.add( this ); } setTop( t, h ) { this.content.style.top = t + 'px'; if( h !== undefined ) this.forceHeight = h; this.calc(); Roots.needReZone = true; } addTransition(){ if( this.transition && !this.isCanvas ){ this.innerContent.style.transition = 'height '+this.transition+'s ease-out'; this.content.style.transition = 'height '+this.transition+'s ease-out'; this.bottom.style.transition = 'top '+this.transition+'s ease-out'; //this.bottom.addEventListener("transitionend", Roots.resize, true); } } // ---------------------- // CANVAS // ---------------------- onDraw () {} makeCanvas () { this.canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', "canvas" ); this.canvas.width = this.zone.w; this.canvas.height = this.forceHeight ? this.forceHeight : this.zone.h; //console.log( this.canvas.width, this.canvas.height ) } draw ( force ) { if( this.canvas === null ) return; let w = this.zone.w; let h = this.forceHeight ? this.forceHeight : this.zone.h; Roots.toCanvas( this, w, h, force ); } ////// getDom () { return this.content; } noMouse () { this.mouse.neg(); } setMouse ( uv, flip = true ) { if(flip) this.mouse.set( Math.round( uv.x * this.canvas.width ), this.canvas.height - Math.round( uv.y * this.canvas.height ) ); else this.mouse.set( Math.round( uv.x * this.canvas.width ), Math.round( uv.y * this.canvas.height ) ); //this.mouse.set( m.x, m.y ); } setConfig ( o ) { // reset to default text Tools.setText(); this.colors = Tools.defineColor( o ); } setColors ( o ) { for( let c in o ){ if( this.colors[c] ) this.colors[c] = o[c]; } } setText ( size, color, font, shadow ) { Tools.setText( size, color, font, shadow ); } hide ( b ) { this.content.style.visibility = b ? 'hidden' : 'visible'; } display( v = false ) { this.content.style.visibility = v ? 'visible' : 'hidden'; } onChange ( f ) { this.callback = f || null; return this; } // ---------------------- // STYLES // ---------------------- mode ( n ) { let needChange = false; let cc = this.colors; if( n !== this.cn ){ this.cn = n; switch( n ){ case 'def': Roots.cursor(); this.scroll.style.background = cc.button; this.bottom.style.background = cc.background; this.bottom.style.color = cc.text; break; //case 'scrollDef': this.scroll.style.background = this.colors.scroll; break; case 'scrollOver': Roots.cursor('ns-resize'); this.scroll.style.background = cc.select; break; case 'scrollDown': this.scroll.style.background = cc.select; break; //case 'bottomDef': this.bottom.style.background = this.colors.background; break; case 'bottomOver': Roots.cursor('pointer'); this.bottom.style.background = cc.backgroundOver; this.bottom.style.color = cc.textOver; break; //case 'bottomDown': this.bottom.style.background = this.colors.select; this.bottom.style.color = '#000'; break; } needChange = true; } return needChange; } // ---------------------- // TARGET // ---------------------- clearTarget () { if( this.current === -1 ) return false; if( this.proto.s ){ // if no s target is delete !! this.proto.uiout(); this.proto.reset(); } this.proto = null; this.current = -1; ///console.log(this.isDown)//if(this.isDown)Roots.clearInput(); Roots.cursor(); return true; } // ---------------------- // ZONE TEST // ---------------------- testZone ( e ) { let l = this.local; if( l.x === -1 && l.y === -1 ) return ''; this.isReset = false; let name = ''; let s = this.isScroll ? this.zone.w - this.size.s : this.zone.w; if( l.y > this.zone.h - this.bh && l.y < this.zone.h ) name = 'bottom'; else name = l.x > s ? 'scroll' : 'content'; return name; } // ---------------------- // EVENTS // ---------------------- handleEvent ( e ) { let type = e.type; let change = false; let protoChange = false; let name = this.testZone( e ); if( type === 'mouseup' && this.isDown ) this.isDown = false; if( type === 'mousedown' && !this.isDown ) this.isDown = true; if( this.isDown && this.isNewTarget ){ Roots.clearInput(); this.isNewTarget=false; } if( !name ) return; switch( name ){ case 'content': e.clientY = this.isScroll ? e.clientY + this.decal : e.clientY; if( Roots.isMobile && type === 'mousedown' ) this.getNext( e, change ); if( this.proto ) protoChange = this.proto.handleEvent( e ); if( type === 'mousemove' ) change = this.mode('def'); if( type === 'wheel' && !protoChange && this.isScroll ) change = this.onWheel( e ); if( !Roots.lock ) { this.getNext( e, change ); } break; case 'bottom': this.clearTarget(); if( type === 'mousemove' ) change = this.mode('bottomOver'); if( type === 'mousedown' ) { this.isOpen = this.isOpen ? false : true; this.bottom.textContent = this.isOpen ? this.bottomText[1] : this.bottomText[0]; //this.setHeight(); this.calc(); this.mode('def'); change = true; } break; case 'scroll': this.clearTarget(); if( type === 'mousemove' ) change = this.mode('scrollOver'); if( type === 'mousedown' ) change = this.mode('scrollDown'); if( type === 'wheel' ) change = this.onWheel( e ); if( this.isDown ) this.update( (e.clientY-this.zone.y)-(this.sh*0.5) ); break; } if( this.isDown ) change = true; if( protoChange ) change = true; if( type === 'keyup' ) change = true; if( type === 'keydown' ) change = true; if( change ) this.draw(); } getNext ( e, change ) { let next = Roots.findTarget( this.uis, e ); if( next !== this.current ){ this.clearTarget(); this.current = next; this.isNewTarget = true; } if( next !== -1 ){ this.proto = this.uis[ this.current ]; this.proto.uiover(); } } onWheel ( e ) { this.oy += 20*e.delta; this.update( this.oy ); return true; } // ---------------------- // RESET // ---------------------- reset ( force ) { if( this.isReset ) return; //this.resetItem(); this.mouse.neg(); this.isDown = false; //Roots.clearInput(); let r = this.mode('def'); let r2 = this.clearTarget(); if( r || r2 ) this.draw( true ); this.isReset = true; //Roots.lock = false; } // ---------------------- // ADD NODE // ---------------------- add () { let a = arguments; let ontop = false; if( typeof a[1] === 'object' ){ a[1].isUI = true; a[1].main = this; ontop = a[1].ontop ? a[1].ontop : false; } else if( typeof a[1] === 'string' ){ if( a[2] === undefined ) [].push.call(a, { isUI:true, main:this }); else { a[2].isUI = true; a[2].main = this; //ontop = a[1].ontop ? a[1].ontop : false; ontop = a[2].ontop ? a[2].ontop : false; } } let u = add.apply( this, a ); if( u === null ) return; if( ontop ) this.uis.unshift( u ); else this.uis.push( u ); this.calc(); this.isEmpty = false; return u } // remove one node remove ( n ) { if( n.dispose ) n.dispose(); } // call after uis clear clearOne ( n ) { let id = this.uis.indexOf( n ); if ( id !== -1 ) { //this.calc( - (this.uis[ id ].h + 1 ) ); this.inner.removeChild( this.uis[ id ].c[0] ); this.uis.splice( id, 1 ); this.calc(); } } // clear all gui empty() { //this.close(); let i = this.uis.length, item; while( i-- ){ item = this.uis.pop(); this.inner.removeChild( item.c[0] ); item.dispose(); } this.uis = []; this.isEmpty = true; this.calc(); } clear() { this.empty(); } dispose() { this.clear(); if( this.parent !== null ) this.parent.removeChild( this.content ); Roots.remove( this ); } // ---------------------- // ITEMS SPECIAL // ---------------------- resetItem () { if( !this.isItemMode ) return; let i = this.uis.length; while(i--) this.uis[i].selected(); } setItem ( name ) { if( !this.isItemMode ) return; name = name || ''; this.resetItem(); if( !name ){ this.update(0); return } let i = this.uis.length; while(i--){ if( this.uis[i].value === name ){ this.uis[i].selected( true ); if( this.isScroll ) this.update( ( i*(this.uis[i].h+1) )*this.ratio ); } } } // ---------------------- // SCROLL // ---------------------- upScroll ( b ) { this.sw = b ? this.size.s : 0; this.oy = b ? this.oy : 0; this.scrollBG.style.display = b ? 'block' : 'none'; if( b ){ this.total = this.h; this.maxView = this.maxHeight; this.ratio = this.maxView / this.total; this.sh = this.maxView * this.ratio; this.range = this.maxView - this.sh; this.oy = Tools.clamp( this.oy, 0, this.range ); this.scrollBG.style.height = this.maxView + 'px'; this.scroll.style.height = this.sh + 'px'; } this.setItemWidth( this.zone.w - this.sw ); this.update( this.oy ); } update ( y ) { y = Tools.clamp( y, 0, this.range ); this.decal = Math.floor( y / this.ratio ); this.inner.style.top = - this.decal + 'px'; this.scroll.style.top = Math.floor( y ) + 'px'; this.oy = y; } // ---------------------- // RESIZE FUNCTION // ---------------------- calcUis() { return Roots.calcUis( this.uis, this.zone, this.zone.y ) } calc() { clearTimeout( this.tmp ); this.tmp = setTimeout( this.setHeight.bind( this ), 10 ); } setHeight() { if( this.tmp ) clearTimeout( this.tmp ); this.zone.h = this.bh; this.isScroll = false; if( this.isOpen ){ this.h = this.calcUis(); let hhh = this.forceHeight ? this.forceHeight + this.zone.y : window.innerHeight; this.maxHeight = hhh - this.zone.y - this.bh; let diff = this.h - this.maxHeight; if( diff > 1 ){ this.isScroll = true; this.zone.h = this.maxHeight + this.bh; } else { this.zone.h = this.h + this.bh; } } this.upScroll( this.isScroll ); this.innerContent.style.height = this.zone.h - this.bh + 'px'; this.content.style.height = this.zone.h + 'px'; this.bottom.style.top = this.zone.h - this.bh + 'px'; if( this.forceHeight && this.lockHeight ) this.content.style.height = this.forceHeight + 'px'; if( this.isCanvas ) this.draw( true ); } rezone () { Roots.needReZone = true; } setWidth ( w ) { if( w ) this.zone.w = w; this.zone.w = Math.floor( this.zone.w ); this.content.style.width = this.zone.w + 'px'; if( this.isCenter ) this.content.style.marginLeft = -(Math.floor(this.zone.w*0.5)) + 'px'; this.setItemWidth( this.zone.w - this.sw ); } setItemWidth ( w ) { let i = this.uis.length; while(i--){ this.uis[i].setSize( w ); this.uis[i].rSize(); } } } const REVISION = '4.0.8'; export { Files, Gui, Proto, REVISION, Tools, add };