uniforms-gui
Version:
Small utility to transform all uniforms of selected program to GUI controls.
1,909 lines (1,201 loc) • 151 kB
JavaScript
var uniformsgui = (function () {
'use strict';
// 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 = t