UNPKG

uniforms-gui

Version:

Small utility to transform all uniforms of selected program to GUI controls.

2,087 lines (1,309 loc) 140 kB
// Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( Function.prototype.name === undefined ) { // Missing in IE9-11. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, 'name', { get: function () { return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( 'Cannot convert undefined or null to object' ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * @author lth / https://github.com/lo-th */ var T = { frag: document.createDocumentFragment(), colorRing: null, joystick_0: null, joystick_1: null, circular: null, knob: null, //graph: null, svgns: "http://www.w3.org/2000/svg", 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' ], SVG_TYPE_G: [ 'svg', 'rect', 'circle', 'path', 'polygon', 'text', 'g', 'line', 'foreignObject' ], TwoPI: 6.283185307179586, size: { w: 240, h: 20, p: 30, s: 20 }, // ---------------------- // COLOR // ---------------------- colors: { text : '#C0C0C0', textOver : '#FFFFFF', background: 'rgba(44,44,44,0.3)', backgroundOver: 'rgba(11,11,11,0.5)', input: '#005AAA', border : '#454545', borderOver : '#5050AA', borderSelect : '#308AFF', scrollback:'rgba(44,44,44,0.2)', scrollbackover:'rgba(44,44,44,0.5)', button : '#404040', boolbg : '#181818', select : '#308AFF', moving : '#03afff', down : '#024699', stroke: 'rgba(11,11,11,0.5)', scroll: '#333333', 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;', }, // 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', }, // custom text setText : function( size, color, font ){ size = size || 11; color = color || '#CCC'; font = font || 'Monospace';//'"Consolas", "Lucida Console", Monaco, monospace'; T.colors.text = color; T.css.txt = T.css.basic + 'font-family:'+font+'; font-size:'+size+'px; color:'+color+'; padding:2px 10px; left:0; top:2px; height:16px; width:100px; overflow:hidden; white-space: nowrap;'; T.css.txtselect = T.css.txt + 'padding:2px 5px; border:1px dashed ' + T.colors.border+';'; T.css.item = T.css.txt + 'position:relative; background:rgba(0,0,0,0.2); margin-bottom:1px;'; }, clone: function ( o ) { return o.cloneNode( true ); }, setSvg: function( dom, type, value, id ){ if( id === -1 ) dom.setAttributeNS( null, type, value ); else dom.childNodes[ id || 0 ].setAttributeNS( null, type, value ); }, setCss: function( dom, css ){ for( var 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( var att in o ){ if( att === 'txt' ) g.textContent = o[ att ]; 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 { // 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 ){ var 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 ) { var 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] ); } } }, clamp: function ( value, min, max ) { //return value <= min ? min : value >= max ? max : value; return value < min ? min : value > max ? max : value; //return Math.max( min, Math.min( max, value ) ); }, // ---------------------- // Color function // ---------------------- ColorLuma : function ( hex, l ) { // 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 var 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; }, 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) ]; }, htmlRgb: function( c ){ return 'rgb(' + Math.round(c[0] * 255) + ','+ Math.round(c[1] * 255) + ','+ Math.round(c[2] * 255) + ')'; }, rgbToHex : function( c ){ 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 ) { var 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 ) { var 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 ); var n = parent.childNodes[0].childNodes.length - 1, c; for( var 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] ); } }, /*makeGraph: function () { var w = 128; var radius = 34; var 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; },*/ makeKnob: function ( model ) { var w = 128; var radius = 34; var svg = T.dom( 'svg', T.css.basic , { 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 ) { var w = 128; var radius = 40; var svg = T.dom( 'svg', T.css.basic , { 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;' var w = 128; var radius = Math.floor((w-30)*0.5); var innerRadius = Math.floor(radius*0.6); var svg = T.dom( 'svg', T.css.basic , { 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 var 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 var cc0 = ['rgb(40,40,40)', 'rgb(48,48,48)', 'rgb(30,30,30)']; var 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 () { var w = 256; var svg = T.dom( 'svg', T.css.basic , { viewBox:'0 0 '+w+' '+w, width:w, height:w, preserveAspectRatio:'none' } ); T.dom( 'defs', null, {}, svg ); T.dom( 'g', null, {}, svg ); var s = 40;//stroke var r =( w-s )*0.5; var mid = w*0.5; var n = 24, nudge = 8 / r / n * Math.PI, a1 = 0; var am, tan, d2, a2, ar, i, j, path, ccc; var 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]; } var br = (128 - s ) + 2; var bw = 60; // black / white ccc = [ [0, '#FFFFFF', 1], [50, '#FFFFFF', 0], [50, '#000000', 0], [100, '#000000', 1] ]; T.makeGradiant( 'linearGradient', { id:'GL1', x1:mid-bw, y1:mid-bw, x2:mid-bw, y2:mid+bw, gradientUnits:"userSpaceOnUse" }, svg, ccc ); // saturation ccc = [ [0, '#7f7f7f', 0], [50, '#7f7f7f', 0.5], [100, '#7f7f7f', 1] ]; T.makeGradiant( 'linearGradient', { id:'GL2', x1:mid-bw, y1:mid-bw, x2:mid+bw, y2:mid-bw, gradientUnits:"userSpaceOnUse" }, svg, ccc ); T.dom( 'circle', '', { cx:128, cy:128, r:br, fill:'red' }, svg );//2 T.dom( 'circle', '', { cx:128, cy:128, r:br, fill:'url(#GL2)' }, svg );//3 T.dom( 'circle', '', { cx:128, cy:128, r:br, fill:'url(#GL1)' }, svg );//4 T.dom( 'circle', '', { cx:0, cy:0, r:6, 'stroke-width':3, stroke:'#FFF', fill:'none' }, svg );//5 T.dom( 'circle', '', { cx:0, cy:0, r:6, 'stroke-width':3, stroke:'#000', fill:'none' }, svg );//6 T.colorRing = svg; }, icon: function ( type, color, w ){ w = w || 40; color = color || '#DEDEDE'; var viewBox = '0 0 256 256'; var t = ["<svg xmlns='"+T.svgns+"' version='1.1' xmlns:xlink='"+T.htmls+"' style='pointer-events:none;' preserveAspectRatio='xMinYMax meet' x='0px' y='0px' width='"+w+"px' height='"+w+"px' viewBox='"+viewBox+"'><g>"]; switch(type){ case 'logo': t[1]="<path id='logoin' stroke='"+color+"' stroke-width='16' stroke-linejoin='round' stroke-linecap='square' fill='none' d='M 192 44 L 192 148 Q 192 174.5 173.3 193.25 154.55 212 128 212 101.5 212 82.75 193.25 64 174.5 64 148 L 64 44 M 160 44 L 160 148 Q 160 161.25 150.65 170.65 141.25 180 128 180 114.75 180 105.35 170.65 96 161.25 96 148 L 96 44'/>"; break; case 'save': t[1]="<path stroke='"+color+"' stroke-width='4' stroke-linejoin='round' stroke-linecap='round' fill='none' d='M 26.125 17 L 20 22.95 14.05 17 M 20 9.95 L 20 22.95'/><path stroke='"+color+"' stroke-width='2.5' stroke-linejoin='round' stroke-linecap='round' fill='none' d='M 32.6 23 L 32.6 25.5 Q 32.6 28.5 29.6 28.5 L 10.6 28.5 Q 7.6 28.5 7.6 25.5 L 7.6 23'/>"; break; } t[2] = "</g></svg>"; return t.join("\n"); }, }; T.setText(); var Tools = T; /** * @author lth / https://github.com/lo-th */ // INTENAL FUNCTION var R = { ui: [], ID: null, lock:false, wlock:false, current:-1, needReZone: true, isEventsInit: false, prevDefault: ['contextmenu', 'mousedown', 'mousemove', 'mouseup'], xmlserializer: new XMLSerializer(), tmpTime: null, tmpImage: null, oldCursor:'auto', input: null, firstImput: true, callbackImput: null, isLoop: false, listens: [], e:{ type:null, clientX:0, clientY:0, keyCode:NaN, key:null, delta:0, }, isMobile: false, add: function ( o ) { R.ui.push( o ); R.getZone( o ); if( !R.isEventsInit ) R.initEvents(); }, testMobile: function () { var 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 ) { var 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; var domElement = document.body; R.isMobile = R.testMobile(); if( R.isMobile ){ domElement.addEventListener( 'touchstart', R, false ); domElement.addEventListener( 'touchend', R, false ); domElement.addEventListener( 'touchmove', R, false ); }else{ domElement.addEventListener( 'mousedown', R, false ); domElement.addEventListener( 'contextmenu', R, false ); domElement.addEventListener( 'wheel', R, false ); document.addEventListener( 'mousemove', R, false ); document.addEventListener( 'mouseup', R, false ); } window.addEventListener( 'keydown', R, false ); window.addEventListener( 'resize', R.resize , false ); R.isEventsInit = true; }, removeEvents: function () { if( !R.isEventsInit ) return; var domElement = document.body; if( R.isMobile ){ domElement.removeEventListener( 'touchstart', R, false ); domElement.removeEventListener( 'touchend', R, false ); domElement.removeEventListener( 'touchmove', R, false ); }else{ domElement.removeEventListener( 'mousedown', R, false ); domElement.removeEventListener( 'contextmenu', R, false ); domElement.removeEventListener( 'wheel', R, false ); document.removeEventListener( 'mousemove', R, false ); document.removeEventListener( 'mouseup', R, false ); } window.removeEventListener( 'keydown', R ); window.removeEventListener( 'resize', R.resize ); R.isEventsInit = false; }, resize: function () { R.needReZone = true; var i = R.ui.length, u; while( i-- ){ u = R.ui[i]; if( u.isGui && !u.isCanvasOnly && u.autoResize ) u.setHeight(); } }, // ---------------------- // HANDLE EVENTS // ---------------------- handleEvent: function ( event ) { //if(!event.type) return; // console.log( event.type ) if( event.type.indexOf( R.prevDefault ) !== -1 ) event.preventDefault(); if( event.type === 'contextmenu' ) return; //if( event.type === 'keydown'){ R.editText( event ); return;} //if( event.type !== 'keydown' && event.type !== 'wheel' ) event.preventDefault(); //event.stopPropagation(); R.findZone(); var e = R.e; if( event.type === 'keydown') R.editText( event ); if( event.type === 'wheel' ) e.delta = event.deltaY > 0 ? 1 : -1; else e.delta = 0; e.clientX = event.clientX || 0; e.clientY = event.clientY || 0; e.type = event.type; // mobile if( R.isMobile ){ if( event.touches && event.touches.length > 0 ){ e.clientX = event.touches[ 0 ].clientX || 0; e.clientY = event.touches[ 0 ].clientY || 0; } if( event.type === 'touchstart') e.type = 'mousedown'; if( event.type === 'touchend') e.type = 'mouseup'; if( event.type === 'touchmove') e.type = 'mousemove'; } /* if( event.type === 'touchstart'){ e.type = 'mousedown'; R.findID( e ); } if( event.type === 'touchend'){ e.type = 'mouseup'; if( R.ID !== null ) R.ID.handleEvent( e ); R.clearOldID(); } if( event.type === 'touchmove'){ e.type = 'mousemove'; } */ if( e.type === 'mousedown' ) R.lock = true; if( e.type === 'mouseup' ) R.lock = false; 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(); }, // ---------------------- // ID // ---------------------- findID: function ( e ) { var 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 ) { var lng = uis.length, u, i, px = 0, my = 0; for( i = 0; i < lng; i++ ){ u = uis[i]; u.zone.w = u.w; u.zone.h = u.h; if( !u.autoWidth ){ if( px === 0 ) py += u.h + 1; u.zone.x = zone.x + px; u.zone.y = px === 0 ? py - u.h : my; my = u.zone.y; px += u.w; if( px + u.w > zone.w ) px = 0; } else { u.zone.x = zone.x; u.zone.y = py; py += u.h + 1; } if( u.isGroup ) u.calcUis(); } }, findTarget: function ( uis, e ) { var 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; var z = o.zone; var mx = x - z.x; var my = y - z.y; var over = ( mx >= 0 ) && ( my >= 0 ) && ( mx <= z.w ) && ( my <= z.h ); if( over ) o.local.set( mx, my ); else o.local.neg(); return over; }, getZone: function ( o ) { if( o.isCanvasOnly ) return; var r = o.getDom().getBoundingClientRect(); o.zone = { x:r.left, y:r.top, w:r.width, h:r.height }; }, // ---------------------- // 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 ) { // 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 ); /// var isNewSize = false; if( w !== o.canvas.width || h !== o.canvas.height ) isNewSize = true; if( R.tmpImage === null ) R.tmpImage = new Image(); var img = R.tmpImage; //new Image(); var htmlString = R.xmlserializer.serializeToString( o.content ); var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="'+w+'" height="'+h+'"><foreignObject style="pointer-events: none; left:0;" width="100%" height="100%">'+ htmlString +'</foreignObject></svg>'; img.onload = function() { var 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 // ---------------------- clearInput: function () { if( R.input === null ) return; if( !R.firstImput ) R.callbackImput(); R.input.style.background = 'none'; R.callbackImput = null; R.input = null; R.firstImput = true; }, setInput: function ( Input, Callback, color ) { R.clearInput(); R.callbackImput = Callback; R.input = Input; R.input.style.background = color; }, select: function () { document.execCommand( "selectall", null, false ); }, editText: function ( e ) { if( R.input === null ) return; if( e.keyCode === 13 ){ //enter R.callbackImput(); R.clearInput(); } else { if( R.input.isNum ){ if ( ((e.keyCode > 95) && (e.keyCode < 106)) || e.keyCode === 110 || e.keyCode === 109 ){ if( R.firstImput ){ R.input.textContent = e.key; R.firstImput = false; } else R.input.textContent += e.key; } } else { if( R.firstImput ){ R.input.textContent = e.key; R.firstImput = false; } else R.input.textContent += e.key; } } }, // ---------------------- // LISTENING // ---------------------- loop: function () { if( R.isLoop ) requestAnimationFrame( R.loop ); R.update(); }, update: function () { var i = R.listens.length; while(i--) R.listens[i].listening(); }, removeListen: function ( proto ) { var id = R.listens.indexOf( proto ); if( id !== -1 ) R.listens.splice(id, 1); if( R.listens.length === 0 ) R.isLoop = false; }, addListen: function ( proto ) { var id = R.listens.indexOf( proto ); if( id !== -1 ) return; R.listens.push( proto ); if( !R.isLoop ){ R.isLoop = true; R.loop(); } }, }; var Roots = R; // minimal vector 2 function V2 ( x, y ){ this.x = x || 0; this.y = y || 0; } Object.assign( V2.prototype, { set: function ( x, y ) { this.x = x; this.y = y; return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, angle: function () { // 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: function ( s ) { this.x += s; this.y += s; return this; }, negate: function () { this.x *= -1; this.y *= -1; return this; }, neg: function () { this.x = -1; this.y = -1; return this; }, isZero: function () { return ( this.x === 0 && this.y === 0 ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, nearEquals: function ( v, n ) { return ( ( v.x.toFixed(n) === this.x.toFixed(n) ) && ( v.y.toFixed(n) === this.y.toFixed(n) ) ); }, lerp: function ( 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 */ function Proto ( o ) { o = o || {}; this.css = Tools.css; this.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; // if is on gui or group this.main = o.main || null; this.isUI = o.isUI || false; this.parentGroup = null; // 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; this.h = this.h < 11 ? 11 : this.h; // if need resize width this.autoWidth = o.auto || true; // open statu this.isOpen = false; // radius for toolbox this.radius = o.radius || 0; // only for number this.isNumber = 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; // Background this.bg = this.isUI ? this.main.bg : Tools.colors.background; this.bgOver = Tools.colors.backgroundOver; if( o.bg !== undefined ){ this.bg = o.bg; this.bgOver = o.bg; } if( o.bgOver !== undefined ){ this.bgOver = o.bgOver; } // Font Color; this.titleColor = o.titleColor || Tools.colors.text; this.fontColor = o.fontColor || Tools.colors.text; if( o.color !== undefined ){ if( !isNaN(o.color) ) this.fontColor = Tools.hexToHtml(o.color); else this.fontColor = o.color; this.titleColor = this.fontColor; } this.colorPlus = Tools.ColorLuma( this.fontColor, 0.3 ); this.txt = o.name || 'Proto'; this.rename = o.rename || ''; this.target = o.target || null; this.callback = o.callback === undefined ? null : o.callback; this.endCallback = null; if( this.callback === null && this.isUI && this.main.callback !== null ) this.callback = this.main.callback; // elements this.c = []; // style this.s = []; this.c[0] = Tools.dom( 'div', Tools.css.basic + 'position:relative; height:20px; float:left; overflow:hidden;'); this.s[0] = this.c[0].style; if( this.isUI ) this.s[0].marginBottom = '1px'; // with title if( !this.simple ){ this.c[1] = Tools.dom( 'div', Tools.css.txt ); this.s[1] = this.c[1].style; this.c[1].textContent = this.rename === '' ? this.txt : this.rename; this.s[1].color = this.titleColor; } if( o.pos ){ this.s[0].position = 'absolute'; for(var p in o.pos){ this.s[0][p] = o.pos[p]; } this.mono = true; } if( o.css ) this.s[0].cssText = o.css; } Object.assign( Proto.prototype, { constructor: Proto, // ---------------------- // make de node // ---------------------- init: function () { var s = this.s; // style cache var c = this.c; // div cache s[0].height = this.h + 'px'; this.zone.h = this.h; if( this.isUI ) s[0].background = this.bg; //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'; } var frag = Tools.frag; for( var i = 1, lng = c.length; i !== lng; i++ ){ if( c[i] !== undefined ) { frag.appendChild( c[i] ); s[i] = c[i].style; } } if( this.target !== null ){ this.target.appendChild( c[0] ); } else { if( this.isUI ) this.main.inner.appendChild( c[0] ); else document.body.appendChild( c[0] ); } c[0].appendChild( frag ); this.rSize(); // ! solo proto if( !this.isUI ){ this.c[0].style.pointerEvents = 'auto'; Roots.add( this ); } }, // TRANS FUNCTIONS from Tools dom: function ( type, css, obj, dom, id ) { return Tools.dom( type, css, obj, dom, id ); }, setSvg: function ( dom, type, value, id ) { Tools.setSvg( dom, type, value, id ); }, setCss: function ( dom, css ) { Tools.setCss( dom, css ); }, clamp: function ( value, min, max ) { return Tools.clamp( value, min, max ); }, getColorRing: function () { if( !Tools.colorRing ) Tools.makeColorRing(); return Tools.clone( Tools.colorRing ); }, getJoystick: function ( model ) { if( !Tools[ 'joystick_'+ model ] ) Tools.makeJoystick( model ); return Tools.clone( Tools[ 'joystick_'+ model ] ); }, getCircular: function ( model ) { if( !Tools.circular ) Tools.makeCircular( model ); return Tools.clone( Tools.circular ); }, getKnob: function ( model ) { if( !Tools.knob ) Tools.makeKnob( model ); return Tools.clone( Tools.knob ); }, /*getGraph: function () { if( !Tools.graph ) Tools.makeGraph(); return Tools.clone( Tools.graph ); },*/ // TRANS FUNCTIONS from Roots cursor: function ( name ) { Roots.cursor( name ); }, setInput: function ( Input, Callback ) { Roots.setInput( Input, Callback, Tools.colors.input ); }, ///////// update: function () {}, reset: function () {}, ///////// getDom: function () { return this.c[0]; }, uiout: function () { this.s[0].background = this.bg; }, uiover: function () { this.s[0].background = this.bgOver; }, rename: function ( s ) { if( this.c[1] !== undefined) this.c[1].textContent = s; }, listen: function () { Roots.addListen( this ); //Roots.listens.push( this ); return this; }, listening: function () { if( this.objectLink === null ) return; if( this.isSend ) return; if( this.isEdit ) return; this.setValue( this.objectLink[ this.val ] ); }, setValue: function ( v ) { if( this.isNumber ) this.value = this.numValue( v ); else this.value = v; this.update(); }, // ---------------------- // update every change // ---------------------- onChange: function ( f ) { this.callback = f; return this; }, // ---------------------- // update only on end // ---------------------- onFinishChange: function ( f ) { this.callback = null; this.endCallback = f; return this; }, send: function ( v ) { this.isSend = true; if( this.objectLink !== null ) this.objectLink[ this.val ] = v || this.value; if( this.callback ) this.callback( v || this.value ); this.isSend = false; }, sendEnd: function ( v ) { if( this.endCallback ) this.endCallback( v || this.value ); if( this.objectLink !== null ) this.objectLink[ this.val ] = v || this.value; }, // ---------------------- // clear node // ---------------------- clear: function () { Tools.clear( this.c[0] ); if( this.target !== null ){ 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; }, // ---------------------- // change size // ---------------------- setSize: function ( sx ) { if( !this.autoWidth ) return; this.w = sx; if( this.simple ){ //this.sa = 0; this.sb = this.w - this.sa; } else { var pp = this.w * ( this.p / 100 ); this.sa = Math.floor( pp + 10 ); this.sb = Math.floor( this.w - pp - 20 ); } }, rSize: function () { if( !this.autoWidth ) return; this.s[0].width = this.w + 'px'; if( !this.simple ) this.s[1].width = this.sa + 'px'; }, // ---------------------- // for numeric value // ---------------------- setTypeNumber: function ( 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; var 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; } this.step = o.step === undefined ? s : o.step; this.range = this.max - this.min; this.value = this.numValue( this.value ); }, numValue: function ( n ) { return Math.min( this.max, Math.max( this.min, n ) ).toFixed( this.precision ) * 1; }, // ---------------------- // EVENTS DEFAULT // ---------------------- handleEvent: function ( e ){ return this[e.type](e); }, wheel: function ( e ) { return false; }, mousedown: function( e ) { return false; }, mousemove: function( e ) { return false; }, mouseup: function( e ) { return false; }, keydown: function( e ) { return false; }, // ---------------------- // object referency // ---------------------- setReferency: function ( obj, val ) { this.objectLink = obj; this.val = val; }, display: function ( v ) { v = v || false; this.s[0].display = v ? 'block' : 'none'; //this.isReady = v ? false : true; }, // ---------------------- // resize height // ---------------------- open: function () { if( this.isOpen ) return; this.isOpen = true; }, close: function () { if( !this.isOpen ) return; this.isOpen = false; }, needZone: function () { Roots.needReZone = true; }, } ); function Bool ( o ){ Proto.call( this, o ); this.value = o.value || false; this.buttonColor = o.bColor || this.colors.button; this.inh = o.inh || this.h; var t = Math.floor(this.h*0.5)-((this.inh-2)*0.5); this.c[2] = this.dom( 'div', this.css.basic + 'background:'+ this.colors.boolbg +'; height:'+(this.inh-2)+'px; width:36px; top:'+t+'px; border-radius:10px; border:2px solid '+this.boolbg ); this.c[3] = this.dom( 'div', this.css.basic + 'height:'+(this.inh-6)+'px; width:16px; top:'+(t+2)+'px; border-radius:10px; background:'+this.buttonColor+';' ); this.init(); this.update(); } Bool.prototype = Object.assign( Object.create( Proto.prototype ), { constructor: Bool, // ---------------------- // EVENTS // ---------------------- mousemove: function ( e ) { this.cursor('pointer'); }, mousedown: function ( e ) { this.value = this.value ? false : true; this.update(); this.send(); return true; }, update: function () { var s = this.s; if( this.value ){ s[2].background = this.fontColor; s[2].borderColor = this.fontColor; s[3].marginLeft = '17px'; } else { s[2].background = this.colors.boolbg; s[2].borderColor = this.colors.boolbg; s[3].marginLeft = '2px'; } }, rSize: function () { Proto.prototype.rSize.call( this ); var s = this.s; s[2].left = this.sa + 'px'; s[3].left = this.sa+1+ 'px'; } } ); function Button ( o ) { Proto.call( this, o ); this.value = false; this.values = o.value || [this.txt]; //this.selected = null; this.isDown = false; this.buttonColor = o.bColor || this.colors.button; this.isLoadButton = o.loader || false; this.isDragButton = o.drag || false; if( this.isDragButton ) this.isLoadButton = true; this.lng = this.values.length; this.tmp = []; this.stat = []; for(var i = 0; i < this.lng; i++){ this.c[i+2] = this.dom( 'div', this.css.txt + 'text-align:center; top:1px; background:'+this.buttonColor+'; height:'+(this.h-2)+'px; border-radius:'+this.radius+'px; line-height:'+(this.h-4)+'px;' ); this.c[i+2].style.color = this.fontColor; this.c[i+2].innerHTML = this.values[i]; this.stat[i] = 1; } if( this.c[1] !== undefined ) this.c[1].textContent = ''; if( this.isLoadButton ) this.initLoader(); if( this.isDragButton ){ this.lng ++; this.initDrager(); } this.init(); } Button.prototype = Object.assign( Object.create( Proto.prototype ), { constructor: Button, testZone: function ( e ) { var l = this.local; if( l.x === -1 && l.y === -1 ) return ''; var i = this.lng; var t = this.tmp; while( i-- ){ if( l.x>t[i][0] && l.x<t[i][2] ) return i+2; } return '' }, // ---------------------- // EVENTS // ---------------------- mouseup: function ( e ) { if( this.isDown ){ this.value = false; this.isDown = false; //this.send(); return this.mousemove( e ); } return false; }, mousedown: function ( e ) { var name = this.testZone( e ); if( !name ) return false; this.isDown = true; this.value = this.values[name-2]; if( !this.isLoadButton ) this.send(); //else this.fileSelect( e.target.files[0] ); return this.mousemove( e ); // true; }, mousemove: function ( e ) { var up = false; var name = this.testZone( e ); // console.log(name) if( name !== '' ){ this.cursor('pointer'); up = this.modes( this.isDown ? 3 : 2, name ); } else { up = this.reset(); } //console.log(up) return up; }, // ---------------------- modes: function ( n, name ) { var v, r = false; for( var i = 0; i < this.lng; i++ ){ if( i === name-2 ) v = this.mode( n, i+2 ); else v = this.mode( 1, i+2 ); if(v) r = true; } return r; }, mode: function ( n, name ) { var change = false; var i = name - 2; if( this.stat[i] !== n ){ switch( n ){ case 1: this.stat[i] = 1; this.s[ i+2 ].color = this.fontColor; this.s[ i+2 ].background = this.buttonColor; break; case 2: this.stat[i] = 2; this.s[ i+2 ].color = '#FFF'; this.s[ i+2 ].background = this.colors.select; break; case 3: this.stat[i] = 3; this.s[ i+2 ].color = '#FFF'; this.s[ i+2 ].background = this.colors.down; break; } change = true; } return change; }, // ---------------------- reset: function () { this.cursor(); /*var v, r = false; for( var i = 0; i < this.lng; i++ ){ v = this.mode( 1, i+2 ); if(v) r = true; }*/ return this.modes( 1 , 2 ); /*if( this.selected ){ this.s[ this.selected ].color = this.fontColor;