├── .github └── FUNDING.yml ├── examples ├── test_attach_to_input.html ├── index.html ├── test_destroy.html ├── common.css ├── test_popup.html ├── test_create_and_destroy.html ├── test_one_colorpicer_and_multiple_inputs.html ├── test_colorsavers.html └── test_resize_onchange_event.html ├── README.md ├── html5kellycolorpicker.min.js ├── LICENSE └── html5kellycolorpicker.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: nradiowave 2 | -------------------------------------------------------------------------------- /examples/test_attach_to_input.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

14 | Back | KellyC Color Picker | List of projects by nradiowave 15 |

16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 |
26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KellyC-Color-Picker 2 | 3 | 4 | 5 | 6 | 7 | A scaleable color picker (color wheel). Attaches to "input" DOM element 8 | 9 | - HTML5 canvas 10 | - Scalable 11 | - HSV color model 12 | - Tested on mobile devices 13 | - Don't require any addition libraries 14 | - Correct turn off if browser not support HTML5 15 | - Two styles for set saturation and volume (display as hsv quad block or as hsv triangle) 16 | - Optional transparency slider (>= v0.9) 17 | - Optional popup window if attach color picker to an input (>= v1.02) 18 | 19 | Example : 20 | 21 | 22 | 23 | 30 | 31 | See [Wiki](//github.com/NC22/HTML5-Color-Picker/wiki/) for full documentation 32 | 33 | ## Demo | Examples 34 | 35 | List of examples 36 | 37 | ## License 38 | 39 | [GNU General Public License v3](http://www.gnu.org/licenses/gpl.html) 40 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KellyC Color Picker 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

14 | KellyC Color Picker | List of projects by nradiowave 15 |

16 | 17 |
18 |

List of expamples

19 | 29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /examples/test_destroy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 |
18 | 19 |

20 | Back | KellyC Color Picker | List of projects by nradiowave 21 |

22 | 23 |
24 | 25 | 26 | 27 | 28 |

increase size right |  29 | decrease size right

30 | 31 |
32 | 33 |
34 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /examples/common.css: -------------------------------------------------------------------------------- 1 | /* this file is just describe DEMO pages base style, its not required for correct work of extension */ 2 | 3 | html, body { 4 | width: 100%; 5 | margin: 0; 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | border: 0; 12 | outline: 0; 13 | box-sizing: border-box; 14 | } 15 | 16 | body { 17 | background-color: #8d8d8d; 18 | font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; 19 | font-weight: 400; 20 | line-height: 1.5; 21 | font-size: 14px; 22 | color: #cdcdcd; 23 | text-align: left; 24 | } 25 | 26 | a { 27 | text-decoration : none; 28 | color: #dadada; 29 | } 30 | 31 | input, button { 32 | border: 0px; 33 | height: 32px; 34 | text-align: center; 35 | height: 40px; 36 | } 37 | 38 | .example-page { 39 | max-width : 800px; 40 | margin : 0 auto; 41 | background: #5c5c5c; 42 | padding: 15px; 43 | margin-top: 16px; 44 | } 45 | 46 | .example-wrap p { 47 | padding : 6px; 48 | font-size : 16px; 49 | } 50 | 51 | .example-wrap { 52 | background: #7f7f7f; 53 | border-radius: 3px; 54 | -moz-box-shadow: 1px 2px 10px 0px rgba(0,0,0,0.75); 55 | margin-top: 60px; 56 | text-align: center; 57 | padding: 6px; 58 | display: inline-block; 59 | } 60 | 61 | .example-wrap-simple { 62 | width : 98%; 63 | height : 98%; 64 | padding-bottom : 64px; 65 | } 66 | 67 | .example-top a { 68 | font-size: 16px; 69 | font-weight: bold; 70 | text-decoration : underline; 71 | } 72 | 73 | .example-top { 74 | background: #8d8d8d; 75 | padding: 6px; 76 | display: block; 77 | } 78 | 79 | .examples-list { 80 | max-width : 640px; 81 | text-align : left; 82 | padding : 24px; 83 | box-sizing: border-box; 84 | } 85 | 86 | .examples-list ul { 87 | text-align : left; 88 | margin : 0px; 89 | padding : 0px; 90 | margin-left: 19px; 91 | } 92 | 93 | .examples-list li { 94 | list-style-type: square; 95 | padding: 0; 96 | padding-left: -1px; 97 | } 98 | 99 | .examples-list li a { 100 | font-size: 16px; 101 | color: #f9f9f9; 102 | font-weight: bold; 103 | } 104 | 105 | .multi-input { 106 | border: 2px solid rgba(70, 70, 70, 0.18); 107 | box-sizing: border-box; 108 | text-align : center; 109 | } 110 | 111 | .multi-input.selected { 112 | 113 | border: 2px solid #000; 114 | } 115 | 116 | .example-wrap-test-6, .example-wrap-test-4 { 117 | width : 100%; 118 | padding : 40px; 119 | margin-top: 162px; 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /examples/test_popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 27 | 28 | 29 |
30 | 31 |

32 | Back | KellyC Color Picker | List of projects by nradiowave 33 |

34 | 35 |
36 | 37 |
38 |

Press on any input

39 | 40 | 41 | 42 | 43 |

Custom popup class

44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |
52 | 53 | 67 | 68 | -------------------------------------------------------------------------------- /examples/test_create_and_destroy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 |
16 | 17 |

18 | Back | KellyC Color Picker | List of projects by nradiowave 19 |

20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 | 28 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /examples/test_one_colorpicer_and_multiple_inputs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 10 | 21 | 22 | 23 |
24 | 25 |

26 | Back | KellyC Color Picker | List of projects by nradiowave 27 |

28 | 29 |
30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
44 | 45 |
46 | 47 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /examples/test_colorsavers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 35 | 36 | 37 | 38 |
39 | 40 |

41 | Back | KellyC Color Picker | List of projects by nradiowave 42 |

43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 |

Resizable (redraw canvas on window.resize event)

51 |

Current selected color button :

52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 60 | 61 |
62 | 63 |
64 | 65 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /examples/test_resize_onchange_event.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kelly Color Picker 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 |
21 | 22 |

23 | Back | KellyC Color Picker | List of projects by nradiowave 24 |

25 | 26 |
27 | 28 |
29 |
30 |
31 |
32 |

33 | 34 | 35 | 36 | 37 |

38 |
39 | 40 |
41 | 42 |
43 | 44 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /html5kellycolorpicker.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @category html5 widgets 3 | * @package Kelly 4 | * @author Rubchuk Vladimir 5 | * @copyright 2015-2022 Rubchuk Vladimir 6 | * @license GPLv3 7 | * @version 1.22 8 | * 9 | * Usage example : 10 | * 11 | * new KellyColorPicker({place : 'color-picker'}); 12 | * 13 | * ToDo : 14 | * 15 | * Add switch color in colorsavers button (analog of X button in Photoshop) 16 | * updateConfig method 17 | * 18 | **/ /** 19 | * Create color picker 20 | * @param {Array} cfg 21 | * @returns {KellyColorPicker} 22 | */ function KellyColorPicker(f){var h,i,j,k,l,m=Math.PI,n=!0,g=new Object;g.radius=4;var o=!1,p=!1,q="quad",r=!1,s=!1,t=!0,u=new Array,v=new Array,w=document.createElement("canvas"),x=!1,y=!1,z=null,A=!1,B=!0,C="mixed",e=new Object;e.tag=!1,e.margin=6;var D=!1,E=this,F=200,G="#000000",H=1,I=!1,J=!1,K=new Array,L=!1,M=new Array;function N(c,b,d){b=!!b;var a=new Object;a.width,a.widthPercentage=22,a.imageData=null,a.align=c,a.selected=b,a.color="#ffffff",a.position,a.paddingY=-4,a.paddingX=4,a.lineWidth=1,a.selectSize=4,"right"==c&&(a.paddingX=-1*a.paddingX),a.selected&&(a.color=G),d&&(a.color=d),a.updateSize=function(){this.width=parseInt(F-F/100*(100-this.widthPercentage)),"left"==this.align?this.position={x:0,y:F-this.width}:"right"==this.align&&(this.position={x:F-this.width,y:F-this.width})},a.calcS=function(a){return Math.abs((a[1].x-a[0].x)*(a[2].y-a[0].y)-(a[2].x-a[0].x)*(a[1].y-a[0].y))/2},a.isDotIn=function(e){var a=new Array;"left"==this.align?(a[0]={x:this.position.x,y:this.position.y},a[1]={x:this.position.x,y:this.position.y+this.width},a[2]={x:this.position.x+this.width,y:this.position.y+this.width}):(a[0]={x:this.position.x+this.width,y:this.position.y},a[1]={x:a[0].x,y:a[0].y+this.width},a[2]={x:a[0].x-this.width,y:this.position.y+this.width});for(var c=0;c<=a.length-1;++c)a[c].x+=this.paddingX,a[c].y+=this.paddingY;var f=this.calcS(a),b=[{x:a[0].x,y:a[0].y},{x:a[1].x,y:a[1].y},{x:e.x,y:e.y}],d=this.calcS(b);return b[1]={x:a[2].x,y:a[2].y},d+=this.calcS(b),b[0]={x:a[1].x,y:a[1].y},d+=this.calcS(b),Math.ceil(d)==Math.ceil(f)},a.draw=function(){w.width=this.width,w.height=this.width,x.clearRect(0,0,this.width,this.width),x.beginPath(),"left"==this.align&&(x.moveTo(this.lineWidth/2,this.width-this.lineWidth),x.lineTo(this.width,this.width-this.lineWidth),x.lineTo(this.lineWidth,this.lineWidth),x.lineTo(this.lineWidth,this.width-this.lineWidth)),"right"==this.align&&(x.moveTo(this.lineWidth/2,this.width-this.lineWidth),x.lineTo(this.width-this.lineWidth,this.width-this.lineWidth),x.lineTo(this.width-this.lineWidth,this.lineWidth),x.lineTo(this.lineWidth,this.width-this.lineWidth)),this.selected&&(x.fillStyle="rgba(255,255,255, 1)",x.fill(),x.strokeStyle="rgba(0, 0, 0, 1)",x.stroke(),x.closePath(),x.beginPath(),x.lineWidth=this.lineWidth,"left"==this.align&&(x.moveTo(this.selectSize,this.width-this.selectSize),x.lineTo(this.width-2*this.selectSize,this.width-this.selectSize),x.lineTo(this.selectSize,2*this.selectSize),x.lineTo(this.selectSize,this.width-this.selectSize)),"right"==this.align&&(x.moveTo(2*this.selectSize,this.width-this.selectSize),x.lineTo(this.width-this.selectSize,this.width-this.selectSize),x.lineTo(this.width-this.selectSize,2*this.selectSize),x.lineTo(2*this.selectSize,this.width-this.selectSize)));var a=W(this.color);x.fillStyle="rgba("+a.r+","+a.g+","+a.b+", 1)",x.fill(),x.strokeStyle="rgba(0, 0, 0, 1)",x.stroke(),this.imageData=x.getImageData(0,0,this.width,this.width),p.drawImage(w,this.position.x+this.paddingX,this.position.y+this.paddingY)};var e=K.length;K[e]=a}var b=new Object;b.width=18,b.imageData=null,b.innerRadius,b.startAngle=0,b.outerRadius,b.outerStrokeStyle="rgba(0,0,0,0.2)",b.innerStrokeStyle="rgba(0,0,0,0.2)",b.pos,b.draw=function(){if(this.imageData)w.width=F,w.height=F,x.putImageData(this.imageData,0,0),p.drawImage(w,0,0);else{for(var b=this.startAngle,a=0;a<=360;a++){var d=Y(a-2),e=Y(a);p.beginPath(),p.moveTo(j,j),p.arc(j,j,this.outerRadius,d,e,!1),p.closePath();var c=U(b/360,1,1);p.fillStyle="rgb("+c.r+", "+c.g+", "+c.b+")",p.fill(),++b>=360&&(b=0)}p.globalCompositeOperation="destination-out",p.beginPath(),p.arc(j,j,this.innerRadius,0,2*m),p.fill(),p.globalCompositeOperation="source-over",p.strokeStyle=this.innerStrokeStyle,p.lineWidth=2,p.stroke(),p.closePath(),p.beginPath(),p.arc(j,j,this.outerRadius,0,2*m),p.strokeStyle=this.outerStrokeStyle,p.lineWidth=2,p.stroke(),p.closePath(),this.imageData=p.getImageData(0,0,F,F)}},b.isDotIn=function(a){return!!(Math.pow(this.pos.x-a.x,2)+Math.pow(this.pos.y-a.y,2)Math.pow(this.innerRadius,2))};var d=new Object;d.lineWeight=2,d.height=4,d.paddingX=2,d.path;var a=new Object;a.width=18,a.padding=4,a.outerStrokeStyle="rgba(0,0,0,0.2)",a.innerStrokeStyle="rgba(0,0,0,0.2)",a.height,a.pos,a.updateSize=function(){this.pos={x:F+a.padding,y:a.padding},this.height=F-2*a.padding},a.draw=function(){var b=p.createLinearGradient(0,0,0,this.height),a=U(k.h,1,1);b.addColorStop(0,"rgba("+a.r+","+a.g+","+a.b+",1)"),b.addColorStop(1,"rgba("+a.r+","+a.g+","+a.b+",0)"),p.beginPath(),p.rect(this.pos.x,this.pos.y,this.width,this.height),p.fillStyle="white",p.fill(),p.fillStyle=b,p.fill(),p.strokeStyle="rgba(0,0,0, 0.2)",p.lineWidth=2,p.stroke(),p.closePath()},a.dotToAlpha=function(a){return 1-Math.abs(this.pos.y-a.y)/this.height},a.alphaToDot=function(a){return{x:0,y:this.height-this.height*a}},a.limitDotPosition=function(b){var a=b.y;return athis.pos.y+this.height&&(a=this.pos.y+this.height),{x:this.pos.x,y:a}},a.isDotIn=function(b){return!(b.xthis.pos.x+a.width)&&!(b.ythis.pos.y+this.height)};var c=new Object;function O(d){var b=d.getBoundingClientRect(),c=0,e=0;return r&&(e=a.width+2*a.padding),d===o?b.width<=b.height?c=b.height:b.heightb.height?c=b.height:b.height>=b.width&&(c=b.width),c=parseInt(c),r&&(c-=e),!(c<=0)&&c}function P(a,i){var c=1,b=!1;if("string"==typeof a){if(-1==(a=a.trim(a)).indexOf("(")){if("#"==a.charAt(0)&&(a=a.slice(1)),(a=a.substr(0,8)).length>=3){if(a.length>6&&a.length<8&&(a=a.substr(0,6)),a.length>3&&a.length<6&&(a=a.substr(0,3)),b=a,a.length>=3&&a.length<=4){b="";for(let d=0;d=3){switch(a.substring(0,3)){case"rgb":vals[0]=vals[0].replace("rgba(",""),vals[0]=vals[0].replace("rgb(","");var e={r:parseInt(vals[0]),g:parseInt(vals[1]),b:parseInt(vals[2])};e.r<=255&&e.g<=255&&e.b<=255&&(b=X(e));break;case"hsl":vals[0]=vals[0].replace("hsl(",""),vals[0]=vals[0].replace("hsla(","");var f=parseFloat(vals[0])/360,g=parseFloat(vals[1])/100,h=parseFloat(vals[2])/100;f>=0&&g<=1&&h<=1&&(b=X(U(f,g,h)))}4==vals.length&&((c=parseFloat(vals[3]))&&!(c<0)||(c=0),c>1&&(c=1))}}else"object"==typeof a&&(void 0!==a.r&& void 0!==a.g&& void 0!==a.b?b=X(a):void 0!==a.h&& void 0!==a.s&& void 0!==a.l&&(a.h=parseFloat(a.h)/360,a.s=parseFloat(a.s)/100,a.l=parseFloat(a.l)/100,a.h>=0&&a.s<=1&&a.l<=1&&(b=X(U(a.h,a.s,a.l)))),void 0!==a.a&&(c=a.a));return(!1!==b||!i)&&(!1===b&&(b="000000"),{h:b="#"!=b.charAt(0)?"#"+(b=b.substr(0,6)):b.substr(0,7),a:c})}function Q(){if(M.quad)return M.quad;var a=new Object;return a.size,a.padding=2,a.path,a.imageData=null,a.dotToSv=function(a){return{s:Math.abs(this.path[3].x-a.x)/this.size,v:Math.abs(this.path[3].y-a.y)/this.size}},a.svToDot=function(d){var g=this.path[0].x,h=this.path[0].y,a=.02;F<150?a=.07:F<100&&(a=.16);for(var b=0;bthis.path[0].x+this.size&&(a=this.path[0].x+this.size),bthis.path[0].y+this.size&&(b=this.path[0].y+this.size),{x:a,y:b}},a.draw=function(){this.imageData||(this.imageData=p.createImageData(this.size,this.size));for(var a=0,e=this.path[0].x,f=this.path[0].y,b=0;bthis.path[0].x+this.size)&&!(a.ythis.path[0].y+this.size)},M.quad=a,a}function R(){if(M.triangle)return M.triangle;var a=new Object;return a.size,a.padding=2,a.path,a.imageData=null,a.followWheel=!0,a.s,a.sOnTop=!1,a.outerRadius,a.limitDotPosition=function(e){var f=e.x,g=e.y,i=this.path[0].x,h=this.path[2].x,a=f,c=g;a=Math.min(Math.max(h,a),i);var d=(this.path[0].y-this.path[1].y)/(this.path[0].x-this.path[1].x),j=Math.ceil(this.path[1].y+d*(a-this.path[1].x));d=(this.path[0].y-this.path[2].y)/(this.path[0].x-this.path[2].x);var k=Math.floor(this.path[2].y+d*(a-this.path[2].x));return fthis.h-1&&(a=this.h);var e=a/this.h,b=Math.abs(aa(c,this.sSide));return b<30&&(b=30),b-=30,b=60-b,{s:b/=60,v:e}},a.isDotIn=function(c){var a=[{x:this.path[0].x,y:this.path[0].y},{x:this.path[1].x,y:this.path[1].y},{x:c.x,y:c.y}],b=this.calcS(a);return a[1]={x:this.path[2].x,y:this.path[2].y},b+=this.calcS(a),a[0]={x:this.path[1].x,y:this.path[1].y},b+=this.calcS(a),Math.ceil(b)==Math.ceil(this.s)},a.updateSize=function(){this.outerRadius=b.innerRadius-d.paddingX-this.padding,this.size=Math.floor(2*this.outerRadius*Math.sin(Y(60)));var e=Math.sqrt(3)/2*this.size;this.h=Math.sqrt(3)/2*this.size,this.path=new Array,this.path[0]={x:this.outerRadius,y:0},this.path[1]={x:this.path[0].x-e,y:-1*(this.size/2)},this.path[2]={x:this.path[1].x,y:this.size/2},this.path[3]={x:this.path[0].x,y:this.path[0].y};for(var c=0;c<=this.path.length-1;++c)this.path[c].x+=b.pos.x,this.path[c].y+=b.pos.y;if(this.vol=new Array,this.s=this.calcS(this.path),this.sOnTop){var a=$(this.path[0],this.path[2]);this.vol[0]={x:this.path[1].x,y:this.path[1].y},this.vol[1]={x:a.x,y:a.y},this.sSide=this.path[1]}else{var a=$(this.path[0],this.path[1]);this.vol[0]={x:this.path[2].x,y:this.path[2].y},this.vol[1]={x:a.x,y:a.y},this.sSide=this.path[2]}},M.triangle=a,a}function S(a,b,d,c){return"object"!=typeof a&&(a=document.getElementById(a)),!!a&&(c||(c=""),u[c+b]=d,a.addEventListener?a.addEventListener(b,u[c+b]):a.attachEvent("on"+b,u[c+b]),!0)}function T(a,b,c){return"object"!=typeof a&&(a=document.getElementById(a)),!!a&&(c||(c=""),!!u[c+b]&&(a.removeEventListener?a.removeEventListener(b,u[c+b]):a.detachEvent("on"+b,u[c+b]),u[c+b]=null,!0))}function U(b,g,a){var c,d,e,j,k,f,h,i;switch(b&& void 0===g&& void 0===a&&(g=b.s,a=b.v,b=b.h),j=Math.floor(6*b),k=6*b-j,f=a*(1-g),h=a*(1-k*g),i=a*(1-(1-k)*g),j%6){case 0:c=a,d=i,e=f;break;case 1:c=h,d=a,e=f;break;case 2:c=f,d=a,e=i;break;case 3:c=f,d=h,e=a;break;case 4:c=i,d=f,e=a;break;case 5:c=a,d=f,e=h}return{r:Math.floor(255*c),g:Math.floor(255*d),b:Math.floor(255*e)}}function V(a,b,c){a&& void 0===b&& void 0===c&&(b=a.g,c=a.b,a=a.r);var d,g,e=Math.max(a/=255,b/=255,c/=255),h=Math.min(a,b,c),f=e-h;if(g=0==e?0:f/e,e==h)d=0;else{switch(e){case a:d=(b-c)/f+(b>16,g:b>>8&255,b:255&b}}function X(a){var b=function(b){var a=b.toString(16);return 1===a.length?"0"+a:a};return"#"+b(a.r)+b(a.g)+b(a.b)}function Y(a){return a*(m/180)}function Z(a,b){return Math.sqrt(Math.pow(a.x-b.x,2)+Math.pow(a.y-b.y,2))}function $(a,b){return{x:(a.x+b.x)/2,y:(a.y+b.y)/2}}function _(d,a){var e=(a[0].x-a[1].x)*(a[0].x-a[1].x)+(a[0].y-a[1].y)*(a[0].y-a[1].y),f=(d.x-a[0].x)*(a[1].x-a[0].x)+(d.y-a[0].y)*(a[1].y-a[0].y),c=!0,b=f/e;return b<0&&(b=0,c=!1),b>1&&(b=1,c=!1),{x:a[0].x+b*(a[1].x-a[0].x),y:a[0].y+b*(a[1].y-a[0].y),pt:c}}function aa(c,a,d){a||(a={x:0,y:0});var e=c.x-a.x,f=c.y-a.y,b=180*Math.atan2(f,e)/m;return d&&b<0&&(b=360+b),b}function ab(){i=2+d.paddingX,y=!1,b.imageData=null,j=F/2,b.pos={x:j,y:j},b.outerRadius=j-i,b.innerRadius=b.outerRadius-b.width,d.path=[{x:b.innerRadius-d.paddingX,y:-1*d.height},{x:b.outerRadius+d.paddingX,y:-1*d.height},{x:b.outerRadius+d.paddingX,y:d.height},{x:b.innerRadius-d.paddingX,y:d.height},{x:b.innerRadius-d.paddingX,y:-((d.height+d.lineWeight/2)*1)}];var c=F;r&&(c+=a.width+2*a.padding),"canvas"!=D.tagName.toLowerCase()&&(D.style.width=c+"px",D.style.height=F+"px"),o.width=c,o.height=F,I!=o&&(o.style.width=c+"px",o.style.height=F+"px");for(var e=0;e<=K.length-1;++e)K[e].updateSize();L&&(L.imageData.triangle=null,L.imageData.quad=null,L.updateSize()),h.updateSize(),r&&a.updateSize()}function ac(b){if(!A||v.updateinput&&!(0,v.updateinput)(E,A,b))return;let c=H.toFixed(2),a="rgba("+l.r+", "+l.g+", "+l.b+", "+c+")";if(!b)switch(C){case"mixed":H<1?A.value=a:A.value=G;break;case"hex":A.value=G;break;case"hsla":A.value="hsla("+(360*k.h).toFixed(2)+", "+(100*k.s).toFixed(2)+"%, "+(100*k.v).toFixed(2)+"%, "+c+")";break;default:A.value=a}B&&(k.v<.5?A.style.color="#FFF":A.style.color="#000",A.style.background=a)}function ad(){S(o,"mousedown",function(a){E.mouseDownEvent(a)},"wait_action_"),S(o,"touchstart",function(a){E.mouseDownEvent(a)},"wait_action_"),S(o,"mouseout",function(a){E.mouseOutEvent(a)},"wait_action_"),S(window,"touchmove",function(a){E.touchMoveEvent(a)},"wait_action_"),S(o,"mousemove",function(a){E.mouseMoveRest(a)},"wait_action_")}function ae(){T(o,"mousedown","wait_action_"),T(o,"touchstart","wait_action_"),T(o,"mouseout","wait_action_"),T(window,"touchmove","wait_action_"),T(o,"mousemove","wait_action_")}function af(a){a=a||window.event;var b,c,d=document.body.scrollLeft+document.documentElement.scrollLeft,e=document.body.scrollTop+document.documentElement.scrollTop;"touchend"==a.type?(b=a.changedTouches[0].clientX+d,c=a.changedTouches[0].clientY+e):"touchmove"==a.type||a.touches?(b=a.touches[0].clientX+d,c=a.touches[0].clientY+e):(b=a.clientX+d,c=a.clientY+e);var f=o.getBoundingClientRect();return b-=f.left+d,c-=f.top+e,{x:b,y:c}}function ag(d){for(var c=!1,a=0;a<=K.length-1;++a)K[a].selected&&(c=a),K[a].selected=!1;for(var b=!1,a=0;a<=K.length-1;++a)if(a==d){K[a].selected=!0,E.setColorByHex(K[a].color),b=!0;break}return b&&v.selectcolorsaver&&(0,v.selectcolorsaver)(E,K[d]),b|| !1===c||(K[c].selected=!0),b}function ah(){for(var a=0;a<=K.length-1;++a)K[a].selected&&(K[a].color=G)}function ai(){if(K.length)for(var a=0;a<=K.length-1;++a)K[a].draw()}function aj(){if(!(p&&((p.clearRect(0,0,o.width,o.height),y)?(p.putImageData(z,0,0),ai(),!0):(b.draw(),h.draw(),r&&a.draw(),ai(),L&&L.draw(),s||(z=p.getImageData(0,0,o.width,o.height),y=!0),!0))))return!1;var j=360*k.h-b.startAngle;if(r){p.beginPath();var f=2,i=2,l=a.height*(1-H);p.rect(a.pos.x-i,a.padding+l-f/2,a.width+2*i,f),p.strokeStyle="rgba(0,0,0, 0.8)",p.lineWidth=2,p.stroke(),p.closePath()}p.beginPath();for(var e=function(c,b){b=Y(b);for(var d=new Array,a=0;a<=c.length-1;++a)d[a]={x:c[a].x*Math.cos(b)-c[a].y*Math.sin(b),y:c[a].x*Math.sin(b)+c[a].y*Math.cos(b)};return d}(d.path,j,{x:b.pos.x,y:b.pos.y}),c=0;c<=e.length-1;++c)e[c].x+=b.pos.x,e[c].y+=b.pos.y,0==c?p.moveTo(e[c].x,e[c].y):p.lineTo(e[c].x,e[c].y);return p.strokeStyle="rgba(0,0,0,0.8)",p.lineWidth=d.lineWeight,p.stroke(),p.closePath(),k.v>.5&&k.s<.5?p.strokeStyle="rgba(0, 0, 0, 1)":p.strokeStyle="rgba(255, 255, 255, 1)",p.beginPath(),p.lineWidth=2,p.arc(k.x,k.y,g.radius,0,2*m),p.stroke(),p.closePath(),!1}c.svCursorData=null,c.stCursor=null,c.curType=0,c.size=16,c.cEl=document.body,c.initSvCursor=function(){if(!o)return!1;if(this.curType=1,this.stCursor||(this.stCursor=window.getComputedStyle(this.cEl).cursor,this.stCursor||(this.stCursor="auto")),this.svCursorData)return this.cEl.style.cursor=this.svCursorData,!0;if(!w)return!1;var a=this.size+2;w.width=a,w.height=a,x.clearRect(0,0,this.size,this.size),x.strokeStyle="rgba(255, 255, 255, 1)",x.beginPath(),x.lineWidth=2,x.arc(a/2,a/2,this.size/2,0,2*m),x.stroke(),x.closePath();var b=a,c=w.toDataURL();return this.svCursorData="url("+c+") "+b/2+" "+b/2+", auto",!!this.svCursorData&&(this.cEl.style.cursor=this.svCursorData,-1===this.cEl.style.cursor.indexOf(c)&&(this.svCursorData="crosshair",this.cEl.style.cursor="crosshair"),!0)},c.initStandartCursor=function(){this.stCursor&&(c.curType=0,this.cEl.style.cursor=this.stCursor)},c.updateCursor=function(a){n&&(KellyColorPicker.cursorLock||(h.isDotIn(a)?c.initSvCursor():c.initStandartCursor()))},this.popUpClose=function(a){if(!1!==e.tag){if(a&&(a.target==A||a.target==o||a.target==e.tag))return!1;(!v.popupclose||v.popupclose(E,a))&&(e.tag.style.display="none",KellyColorPicker.activePopUp==E&&(KellyColorPicker.activePopUp=!1))}},this.popUpShow=function(g){if(!1!==e.tag&&(!v.popupshow||v.popupshow(E,g))){KellyColorPicker.popupEventsInclude||(S(document,"click",function(a){return!!KellyColorPicker.activePopUp&&KellyColorPicker.activePopUp.popUpClose(a)},"popup_close_"),S(window,"resize",function(a){if(KellyColorPicker.activePopUp)return KellyColorPicker.activePopUp.popUpShow(a)},"popup_resize_"),KellyColorPicker.popupEventsInclude=!0),KellyColorPicker.activePopUp&&KellyColorPicker.activePopUp.popUpClose(!1);var c=E.getCanvas().width,a=E.getAlphaFig();a&&(c-=a.width+a.padding);var d=window.getComputedStyle(e.tag),b=parseInt(d.paddingBottom)+parseInt(d.paddingTop);b<=0&&(b=0);var f=A.getBoundingClientRect(),h=f.top+(window.scrollY||window.pageYOffset||document.body.scrollTop)-b,i=f.left+(window.scrollX||window.pageXOffset||document.body.scrollLeft);return e.tag.style.top=h-c-e.margin+"px",e.tag.style.left=i+"px",e.tag.style.display="block",KellyColorPicker.activePopUp=E,!1}},this.setHueByDot=function(c){var a=aa(c,b.pos)+b.startAngle;a<0&&(a=360+a),k.h=a/360,l=U(k.h,k.s,k.v),G=X(l),ah(),v.change&&(0,v.change)(E),ac(),y=!1,aj()},this.setColorForColorSaver=function(a,d){var b=P(a,!0);if(b){var c=E.getColorSaver(d);return c.selected?this.setColorByHex(a,!1):(c.color=b.h,aj()),!0}},this.setColor=function(a,b){E.setColorByHex(a,b)},this.setColorByHex=function(a,c){c||(c=!1);var b=H;if(!1!==a){if(!a||!a.length)return;var d=P(a,!0);if(!d)return;a=d.h,r&&(b=d.a)}else a=G;if(r&&a==G&&y&&b!=H){H=b,aj();return}if(!G||a!=G||!y){H=b,l=W(a),G=a,k=V(l);var e=h.svToDot(k);k.x=e.x,k.y=e.y,y=!1,ah(),aj(),v.change&&(0,v.change)(E),ac(c)}},this.setAlphaByDot=function(b){H=a.dotToAlpha(b),v.change&&(0,v.change)(E),ac(),aj()},this.setAlpha=function(a){H=a,ac(),aj()},this.setColorByDot=function(a){var b=h.dotToSv(a);k.s=b.s,k.v=b.v,k.x=a.x,k.y=a.y,k.s>1&&(k.s=1),k.s<0&&(k.s=0),k.v>1&&(k.v=1),k.v<0&&(k.v=0),l=U(k.h,k.s,k.v),G=X(l),ah(),v.change&&(0,v.change)(E),ac(),aj()},this.mouseOutEvent=function(a){c.curType>0&&!KellyColorPicker.cursorLock&&c.initStandartCursor()},this.mouseMoveRest=function(a){if(!s&&t){t=!1;var b=af(a);c.updateCursor(b),requestAnimationFrame(function(){t=!0}),v.mousemoverest&&(0,v.mousemoverest)(a,E,b)}},this.touchMoveEvent=function(a){s&&event.preventDefault()},this.mouseDownEvent=function(g){g.preventDefault();var d,e=!1,c=af(g);if(b.isDotIn(c))s="wheel",E.setHueByDot(c),d=function(a){E.wheelMouseMove(a,c)},e=function(a){KellyColorPicker.cursorLock=!1,E.wheelMouseUp(a,c)};else if(h.isDotIn(c))s="sv",E.setColorByDot(c),d=function(a){E.svMouseMove(a,c)},e=function(a){KellyColorPicker.cursorLock=!1,E.svMouseUp(a,c)};else if(r&&a.isDotIn(c))s="alpha",E.setAlphaByDot(c),d=function(a){E.alphaMouseMove(a,c)},e=function(a){KellyColorPicker.cursorLock=!1,E.alphaMouseUp(a,c)};else if(L&&L.isDotIn(c))E.setMethod();else if(K.length){for(var f=0;f<=K.length-1;++f)if(K[f].isDotIn(c)){ag(f);break}}d&&e&&(ae(),KellyColorPicker.cursorLock=E,S(document,"mouseup",e,"action_process_"),S(document,"mousemove",d,"action_process_"),S(document,"touchend",e,"action_process_"),S(document,"touchmove",d,"action_process_"))},this.wheelMouseMove=function(a,c){if(a.preventDefault(),s&&t){t=!1;var b=af(a);requestAnimationFrame(function(){t=!0}),E.setHueByDot(b),v.mousemoveh&&(0,v.mousemoveh)(a,E,b)}},this.wheelMouseUp=function(a,d){if(a.preventDefault(),s){T(document,"mouseup","action_process_"),T(document,"mousemove","action_process_"),T(document,"touchend","action_process_"),T(document,"touchmove","action_process_"),ad(),s=!1,y=!1,aj();var b=af(a);c.updateCursor(b),v.mouseuph&&(0,v.mouseuph)(a,E,b)}},this.alphaMouseMove=function(c,d){if(c.preventDefault(),s&&t){t=!1;var b=af(c);b=a.limitDotPosition(b),requestAnimationFrame(function(){t=!0}),E.setAlphaByDot(b),v.mousemovealpha&&(0,v.mousemovealpha)(c,E,b)}},this.alphaMouseUp=function(a,d){if(a.preventDefault(),s){T(document,"mouseup","action_process_"),T(document,"mousemove","action_process_"),T(document,"touchend","action_process_"),T(document,"touchmove","action_process_"),ad(),s=!1;var b=af(a);c.updateCursor(b),v.mouseupalpha&&(0,v.mouseupalpha)(a,E,b)}},this.svMouseMove=function(b,c){if(b.preventDefault(),s&&t){t=!1;var a=af(b);a=h.limitDotPosition(a),requestAnimationFrame(function(){t=!0}),E.setColorByDot(a),v.mousemovesv&&(0,v.mousemovesv)(b,E,a)}},this.svMouseUp=function(a,d){if(a.preventDefault(),s){T(document,"mouseup","action_process_"),T(document,"mousemove","action_process_"),T(document,"touchend","action_process_"),T(document,"touchmove","action_process_"),ad(),s=!1;var b=af(a);c.updateCursor(b),r&&(y=!1,aj()),v.mouseupsv&&(0,v.mouseupsv)(a,E,b)}},this.addUserEvent=function(a,b){return v[a]=b,!0},this.removeUserEvent=function(a){return!!v[a]&&(v[a]=null,!0)},this.getCanvas=function(){return!!p&&o},this.getCtx=function(){return!!p&&p},this.getInput=function(){return A},this.getSvFig=function(){return h},this.getSvFigCursor=function(){return g},this.getWheel=function(){return b},this.getWheelCursor=function(){return d},this.getCurColorHsv=function(){return k},this.getCurColorRgb=function(){return l},this.getCurColorHex=function(){return G},this.getCurColorRgba=function(){return{r:l.r,g:l.g,b:l.b,a:H}},this.getCurAlpha=function(){return H},this.getAlphaFig=function(){return!!r&&a},this.getPopup=function(){return e},this.getSize=function(){return F},this.getColorSaver=function(b){for(var a=0;a<=K.length-1;++a)if(!b&&K[a].selected||K[a].align==b)return K[a].rgb=W(K[a].color),K[a].hsv=V(K[a].rgb.r,K[a].rgb.g,K[a].rgb.b),K[a]},this.setColorSaver=function(b){if(!b)return!1;for(var a=0;a<=K.length-1;++a)if(K[a].align==b)return ag(a),K[a]},this.updateView=function(a){return!!p&&(a&&(b.imageData=null,h.imageData=null,z=null),y=!1,ab(),aj(),!0)},this.resize=function(a,c){return!!p&&(a==F&&!c||(y=!1,b.imageData=null,h.imageData=null,z=null,F=a,ab(),E.setColorByHex(!1),!1))},this.syncSize=function(b){if(!I)return!1;var a=O(I);return a&&E.resize(a),!1},this.setMethod=function(a){return a||(a="triangle","triangle"!=q||(a="quad")),a!=q&&("quad"==q||"triangle"==q)&&("quad"==(q=a)&&(h=Q()),"triangle"==q&&(h=R()),E.resize(F,!0),v.setmethod&&(0,v.setmethod)(E,q),!0)},this.destroy=function(){if(!E)return!1;c.curType>0&&(KellyColorPicker.cursorLock=!1,c.initStandartCursor()),s&&(T(document,"mouseup","action_process_"),T(document,"mousemove","action_process_"),T(document,"touchend","action_process_"),T(document,"touchmove","action_process_"),s=!1),e.tag&&T(A,"click","popup_"),A&&(T(A,"click","input_edit_"),T(A,"change","input_edit_"),T(A,"keyup","input_edit_"),T(A,"keypress","input_edit_")),KellyColorPicker.popupEventsInclude&&u.popup_close_click&&(KellyColorPicker.activePopUp&&KellyColorPicker.activePopUp.popUpClose(!1),T(document,"click","popup_close_"),T(window,"resize","popup_resize_"),KellyColorPicker.popupEventsInclude=!1),b.imageData=null,h.imageData=null,z=null,w=null,D&&D.parentNode&&D.parentNode.removeChild(D),I&&T(window,"resize","canvas_"),ae(),E=null},function(a){var c="",f="";(void 0!==a.alpha_slider&&(a.alphaSlider=a.alpha_slider),void 0!==a.input_color&&(a.inputColor=a.input_color),void 0!==a.input_format&&(a.inputFormat=a.input_format),a.input&&"object"!=typeof a.input?(a.input=document.getElementById(a.input),A=a.input):"object"==typeof a.input&&(A=a.input),void 0!==a.changeCursor&&(n=a.changeCursor),void 0!==a.alpha&&(H=a.alpha),void 0!==a.alphaSlider&&(r=a.alphaSlider),void 0!==a.inputColor&&(B=a.inputColor),void 0!==a.inputFormat&&(C=a.inputFormat),a.userEvents&&(v=a.userEvents),a.place&&"object"!=typeof a.place&&(f=a.place,a.place=document.getElementById(a.place)),a.place)?D=a.place:A?(e.tag=document.createElement("div"),e.tag.className="popup-kelly-color",a.popupClass?e.tag.className=a.popupClass:(e.tag.className="popup-kelly-color",e.tag.style.position="absolute",e.tag.style.bottom="0px",e.tag.style.left="0px",e.tag.style.display="none",e.tag.style.backgroundColor="#e1e1e1",e.tag.style.border="1px solid #bfbfbf",e.tag.style.boxShadow="7px 7px 14px -3px rgba(0,0,0,0.24)",e.tag.style.borderTopLeftRadius="4px",e.tag.style.borderTopRightRadius="4px",e.tag.style.borderBottomLeftRadius="4px",e.tag.style.borderBottomRightRadius="4px",e.tag.style.padding="12px",e.tag.style.boxSizing="content-box"),D=e.tag,document.getElementsByTagName("body")[0].appendChild(e.tag),S(A,"click",function(a){return E.popUpShow(a)},"popup_")):c+='| "place" ('+f+") not not found";var b=!1;if(a.color?b=P(a.color):A&&A.value&&(b=P(A.value)),b&&(G=b.h,r&&(H=b.a)),a.method&&("triangle"==a.method||"quad"==a.method)&&(q=a.method),D&&("CANVAS"!=D.tagName?(o=document.createElement("CANVAS"),D.appendChild(o)):o=D,void 0!==window.G_vmlCanvasManager&&(o=window.G_vmlCanvasManager.initElement(o),w=window.G_vmlCanvasManager.initElement(w)),o.getContext&&o.getContext("2d")&&(p=o.getContext("2d"),x=w.getContext("2d"),1))||(c+=" | cant init canvas context"),a.resizeWith&&("object"!=typeof a.resizeWith&&"boolean"!=typeof a.resizeWith&&(a.resizeWith=document.getElementById(a.resizeWith)),I=!0===a.resizeWith?o:a.resizeWith,a.resizeSide&&(J=a.resizeSide),I)&&(O(I)&&(a.size=O(I)),S(window,"resize",function(a){return E.syncSize(a)},"canvas_")),a.size&&a.size>0&&(F=a.size),c){"undefined"!=typeof console&&console.log("KellyColorPicker : "+c);return}if("quad"==q&&(h=Q()),"triangle"==q&&(h=R()),A){var d=function(a){var a=a||window.event;a.target||(a.target=a.srcElement),E.setColorByHex(a.target.value,!0)};S(A,"click",d,"input_edit_"),S(A,"change",d,"input_edit_"),S(A,"keyup",d,"input_edit_"),S(A,"keypress",d,"input_edit_")}a.colorSaver&&(N("left",!0),N("right")),a.methodSwitch&&((L=new Object).size,L.sizePercentage=10,L.position,L.paddingY=4,L.paddingX=4,L.imageData=new Array,L.lineWidth=2,L.color="#c1ebf5",L.updateSize=function(){this.size=parseInt(F-F/100*(100-this.sizePercentage)),this.size<16&&(this.size=16),this.position={x:this.paddingX,y:this.paddingY}},L.draw=function(){if(this.imageData[q]){p.putImageData(this.imageData[q],this.position.x,this.position.y);return}var b,e=W(this.color);w.width=this.size,w.height=this.size,x.clearRect(0,0,this.size,this.size),x.beginPath();var f="triangle";if("triangle"==q&&(f="quad"),x.beginPath(),this.size<35)var d=w.width/2,c=d;else{var d=w.width/2-this.lineWidth;x.arc(this.size/2,this.size/2,d,0,2*m),x.strokeStyle="rgba(0, 0, 0, 0.4)",x.lineWidth=this.lineWidth,x.stroke();var c=d-6;x.closePath(),x.beginPath(),x.arc(this.size/2,this.size/2,c,0,2*m),x.strokeStyle="rgba(0, 0, 0, 0.4)",x.lineWidth=this.lineWidth,x.stroke(),x.closePath()}if(x.beginPath(),"quad"==f){b=Math.floor((2*c-4)/Math.sqrt(2));var g=(this.size-b)/2,a={x:g+b,y:g+b/2};a.y=a.y-b/2,x.moveTo(a.x,a.y),x.lineTo(a.x-b,a.y),x.lineTo(a.x-b,a.y+b),x.lineTo(a.x,a.y+b)}else{b=Math.floor((2*c-4)*Math.sin(Y(60)));var a={x:2*c+(d-c),y:this.size/2},h=Math.sqrt(3)/2*b;x.moveTo(a.x,a.y),x.lineTo(a.x-h,a.y-b/2),x.lineTo(a.x-h,a.y+b/2),x.lineTo(a.x,a.y)}x.lineTo(a.x,a.y),x.fillStyle="rgba("+e.r+","+e.g+","+e.b+", 1)",x.fill(),x.lineWidth=this.lineWidth,x.strokeStyle="rgba(0, 0, 0, 0.6)",x.stroke(),x.closePath(),this.imageData[q]=x.getImageData(0,0,w.width,w.width),p.drawImage(w,this.position.x,this.position.y)},L.isDotIn=function(a){return a.x>=this.position.x&&a.x<=this.position.x+this.size&&a.y>=this.position.y&&a.y<=this.position.y+this.size}),ad(),ab(),E.setColorByHex(!1)}(f)}KellyColorPicker.cursorLock=!1,KellyColorPicker.activePopUp=!1,KellyColorPicker.popupEventsInclude=!1,KellyColorPicker.attachToInputByClass=function(e,a){for(var d=new Array,c=document.getElementsByClassName(e),b=0;b 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /html5kellycolorpicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @category html5 widgets 3 | * @package Kelly 4 | * @author Rubchuk Vladimir 5 | * @copyright 2015-2022 Rubchuk Vladimir 6 | * @license GPLv3 7 | * @version 1.22 8 | * 9 | * Usage example : 10 | * 11 | * new KellyColorPicker({place : 'color-picker'}); 12 | * 13 | * ToDo : 14 | * 15 | * Add switch color in colorsavers button (analog of X button in Photoshop) 16 | * updateConfig method 17 | * 18 | **/ 19 | 20 | /** 21 | * Create color picker 22 | * @param {Array} cfg 23 | * @returns {KellyColorPicker} 24 | */ 25 | 26 | function KellyColorPicker(cfg) { 27 | var PI = Math.PI; 28 | 29 | var svFig; // current method SV figure object 30 | 31 | var changeCursor = true; 32 | 33 | var svCursor = new Object; 34 | svCursor.radius = 4; 35 | 36 | var canvas = false; 37 | var ctx = false; 38 | 39 | var method = 'quad'; 40 | var alpha = false; // is alpha slider enabled 41 | var drag = false; 42 | var cursorAnimReady = true; // sets by requestAnimationFrame to limit FPS on events like mousemove etc. when draging 43 | 44 | var events = new Array(); 45 | var userEvents = new Array(); 46 | 47 | var canvasHelper = document.createElement("canvas"); 48 | var canvasHelperCtx = false; // used if needed to copy image data throw ctx.drawImage for save alpha channel 49 | var rendered = false; // is colorpicker rendered (without side alpha bar and cursors, rendered image stores in canvasHelperData 50 | var canvasHelperData = null; // rendered interface without cursors and without alpha slider [wheelBlockSize x wheelBlockSize] 51 | 52 | var input = false; 53 | 54 | // used by updateInput() function if not overloaded by user event 55 | var inputColor = true; // update input color according to picker 56 | var inputFormat = 'mixed'; // text format of colorpicker color displayed in input element | values : mixed | hex | rgba 57 | 58 | var popup = new Object; // popup block for input 59 | popup.tag = false; // Dom element if popup is enabled 60 | popup.margin = 6; // margin from input in pixels 61 | 62 | // container, or canvas element 63 | var place = false; 64 | var handler = this; 65 | 66 | var basePadding = 2; 67 | 68 | var padding; 69 | var wheelBlockSize = 200; 70 | var center; 71 | 72 | // current color 73 | var hsv; 74 | var rgb; 75 | var hex = '#000000'; 76 | var a = 1; 77 | 78 | var resizeWith = false; 79 | var resizeSide = false; 80 | 81 | var colorSavers = new Array(); 82 | 83 | var styleSwitch = false; // change method from square to triangle 84 | var svFigsPool = new Array(); // if we have button for switch method, better store already created figure object to buffer 85 | 86 | // style switch from triange to quad and backwards 87 | function initStyleSwitch() { 88 | 89 | styleSwitch = new Object; 90 | styleSwitch.size; 91 | styleSwitch.sizePercentage = 10; 92 | styleSwitch.position; 93 | styleSwitch.paddingY = 4; 94 | styleSwitch.paddingX = 4; 95 | styleSwitch.imageData = new Array(); 96 | styleSwitch.lineWidth = 2; 97 | styleSwitch.color = '#c1ebf5'; 98 | 99 | styleSwitch.updateSize = function () { 100 | this.size = parseInt(wheelBlockSize - (wheelBlockSize / 100) * (100 - this.sizePercentage)); 101 | 102 | if (this.size < 16) 103 | this.size = 16; 104 | 105 | this.position = {x: this.paddingX, y: this.paddingY}; 106 | } 107 | 108 | styleSwitch.draw = function () { 109 | 110 | if (this.imageData[method]) { 111 | ctx.putImageData(this.imageData[method], this.position.x, this.position.y); 112 | return; 113 | } 114 | 115 | var rgb = hexToRgb(this.color); 116 | 117 | canvasHelper.width = this.size; 118 | canvasHelper.height = this.size; 119 | 120 | canvasHelperCtx.clearRect(0, 0, this.size, this.size); 121 | canvasHelperCtx.beginPath(); 122 | 123 | var switchFig = 'triangle'; 124 | if (method == 'triangle') 125 | switchFig = 'quad'; 126 | 127 | canvasHelperCtx.beginPath(); 128 | 129 | if (this.size < 35) { 130 | var circleRadiusMain = canvasHelper.width / 2; 131 | var circleRadius = circleRadiusMain; 132 | } else { 133 | 134 | var circleRadiusMain = (canvasHelper.width / 2) - this.lineWidth; 135 | 136 | canvasHelperCtx.arc(this.size / 2, this.size / 2, circleRadiusMain, 0, PI * 2); 137 | canvasHelperCtx.strokeStyle = 'rgba(0, 0, 0, 0.4)'; 138 | canvasHelperCtx.lineWidth = this.lineWidth; 139 | canvasHelperCtx.stroke(); 140 | 141 | var circleRadius = circleRadiusMain - 6; 142 | canvasHelperCtx.closePath(); 143 | canvasHelperCtx.beginPath(); 144 | canvasHelperCtx.arc(this.size / 2, this.size / 2, circleRadius, 0, PI * 2); 145 | canvasHelperCtx.strokeStyle = 'rgba(0, 0, 0, 0.4)'; 146 | canvasHelperCtx.lineWidth = this.lineWidth; 147 | canvasHelperCtx.stroke(); 148 | canvasHelperCtx.closePath(); 149 | } 150 | 151 | canvasHelperCtx.beginPath(); 152 | var svmSize; 153 | 154 | if (switchFig == 'quad') { 155 | var workDiametr = (circleRadius * 2) - 4; // may be some paddings here 156 | svmSize = Math.floor(workDiametr / Math.sqrt(2)); 157 | var padding = (this.size - svmSize) / 2; 158 | var svmPos = {x: padding + svmSize, y: padding + svmSize / 2}; // start middle point 159 | svmPos.y = svmPos.y - (svmSize / 2); 160 | canvasHelperCtx.moveTo(svmPos.x, svmPos.y); // right top 161 | canvasHelperCtx.lineTo(svmPos.x - svmSize, svmPos.y); // left tp 162 | canvasHelperCtx.lineTo(svmPos.x - svmSize, svmPos.y + svmSize); // left bottom 163 | canvasHelperCtx.lineTo(svmPos.x, svmPos.y + svmSize); // right bottom 164 | 165 | } else { 166 | svmSize = Math.floor((2 * circleRadius - 4) * Math.sin(toRadians(60))); // side size 167 | var svmPos = {x: circleRadius * 2 + (circleRadiusMain - circleRadius), y: this.size / 2}; // start middle point 168 | var h = ((Math.sqrt(3) / 2) * svmSize); 169 | canvasHelperCtx.moveTo(svmPos.x, svmPos.y); 170 | canvasHelperCtx.lineTo(svmPos.x - h, svmPos.y - (svmSize / 2)); // top 171 | canvasHelperCtx.lineTo(svmPos.x - h, svmPos.y + (svmSize / 2)); // bottom 172 | canvasHelperCtx.lineTo(svmPos.x, svmPos.y); 173 | } 174 | 175 | canvasHelperCtx.lineTo(svmPos.x, svmPos.y); 176 | 177 | 178 | canvasHelperCtx.fillStyle = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ', 1)'; 179 | canvasHelperCtx.fill(); 180 | canvasHelperCtx.lineWidth = this.lineWidth; 181 | canvasHelperCtx.strokeStyle = 'rgba(0, 0, 0, 0.6)'; 182 | canvasHelperCtx.stroke(); 183 | canvasHelperCtx.closePath(); 184 | 185 | 186 | this.imageData[method] = canvasHelperCtx.getImageData(0, 0, canvasHelper.width, canvasHelper.width); 187 | ctx.drawImage(canvasHelper, this.position.x, this.position.y); 188 | 189 | } 190 | 191 | styleSwitch.isDotIn = function (dot) { 192 | if ( 193 | dot.x >= this.position.x && dot.x <= this.position.x + this.size && 194 | dot.y >= this.position.y && dot.y <= this.position.y + this.size 195 | ) { 196 | return true; 197 | } 198 | 199 | //if (Math.pow(this.position.x - dot.x, 2) + Math.pow(this.position.y - dot.y, 2) < Math.pow(this.outerRadius, 2)) { 200 | // return true; 201 | //} 202 | 203 | return false; 204 | } 205 | } 206 | 207 | // triangle colorsavers for left and right side 208 | function initColorSaver(align, selected, color) { 209 | 210 | if (!selected) 211 | selected = false; 212 | else 213 | selected = true; 214 | 215 | var colorSaver = new Object; 216 | colorSaver.width; // size of side of triangle 217 | colorSaver.widthPercentage = 22; 218 | 219 | colorSaver.imageData = null; // last rendered colorsaver image 220 | colorSaver.align = align; 221 | colorSaver.selected = selected; // current color 222 | colorSaver.color = '#ffffff'; // hex color 223 | colorSaver.position; // top point of triangle 224 | colorSaver.paddingY = -4; 225 | colorSaver.paddingX = 4; 226 | colorSaver.lineWidth = 1; 227 | colorSaver.selectSize = 4; 228 | 229 | if (align == 'right') { 230 | colorSaver.paddingX = colorSaver.paddingX * -1; 231 | } 232 | 233 | if (colorSaver.selected) { 234 | colorSaver.color = hex; 235 | } 236 | 237 | if (color) { 238 | colorSaver.color = color; 239 | } 240 | 241 | colorSaver.updateSize = function () { 242 | this.width = parseInt(wheelBlockSize - (wheelBlockSize / 100) * (100 - this.widthPercentage)); 243 | 244 | // start render point in global canvas coords 245 | if (this.align == 'left') { 246 | this.position = {x: 0, y: wheelBlockSize - this.width}; 247 | } else if (this.align == 'right') { 248 | this.position = {x: wheelBlockSize - this.width, y: wheelBlockSize - this.width}; 249 | } 250 | } 251 | 252 | // calc triangle area (same method as for triangle sv figure) 253 | colorSaver.calcS = function (p) { 254 | return Math.abs((p[1].x - p[0].x) * (p[2].y - p[0].y) - (p[2].x - p[0].x) * (p[1].y - p[0].y)) / 2; 255 | } 256 | 257 | colorSaver.isDotIn = function (dot) { 258 | 259 | var path = new Array(); 260 | 261 | if (this.align == 'left') { 262 | path[0] = {x: this.position.x, y: this.position.y}; // top 263 | path[1] = {x: this.position.x, y: this.position.y + this.width}; // bottom left 264 | path[2] = {x: this.position.x + this.width, y: this.position.y + this.width}; // bottom right 265 | } else { 266 | path[0] = {x: this.position.x + this.width, y: this.position.y}; // top 267 | path[1] = {x: path[0].x, y: path[0].y + this.width}; // bottom right 268 | path[2] = {x: path[0].x - this.width, y: this.position.y + this.width}; // bottom left 269 | } 270 | 271 | for (var i = 0; i <= path.length - 1; ++i) 272 | { 273 | path[i].x += this.paddingX; 274 | path[i].y += this.paddingY; 275 | } 276 | 277 | var selfS = this.calcS(path); 278 | 279 | var t = [ 280 | {x: path[0].x, y: path[0].y}, 281 | {x: path[1].x, y: path[1].y}, 282 | {x: dot.x, y: dot.y} 283 | ]; 284 | 285 | var s = this.calcS(t); 286 | t[1] = {x: path[2].x, y: path[2].y}; 287 | s += this.calcS(t); 288 | t[0] = {x: path[1].x, y: path[1].y}; 289 | s += this.calcS(t); 290 | 291 | if (Math.ceil(s) == Math.ceil(selfS)) 292 | return true; 293 | else 294 | return false; 295 | } 296 | 297 | colorSaver.draw = function () { 298 | 299 | canvasHelper.width = this.width; 300 | canvasHelper.height = this.width; 301 | 302 | canvasHelperCtx.clearRect(0, 0, this.width, this.width); 303 | canvasHelperCtx.beginPath(); 304 | 305 | if (this.align == 'left') { 306 | canvasHelperCtx.moveTo(this.lineWidth / 2, this.width - this.lineWidth); 307 | canvasHelperCtx.lineTo(this.width, this.width - this.lineWidth); 308 | canvasHelperCtx.lineTo(this.lineWidth, this.lineWidth); 309 | canvasHelperCtx.lineTo(this.lineWidth, this.width - this.lineWidth); 310 | } 311 | 312 | if (this.align == 'right') { 313 | canvasHelperCtx.moveTo(this.lineWidth / 2, this.width - this.lineWidth); 314 | canvasHelperCtx.lineTo(this.width - this.lineWidth, this.width - this.lineWidth); 315 | canvasHelperCtx.lineTo(this.width - this.lineWidth, this.lineWidth); 316 | canvasHelperCtx.lineTo(this.lineWidth, this.width - this.lineWidth); 317 | } 318 | 319 | if (this.selected) { 320 | 321 | // start draw addition inner figure 322 | 323 | canvasHelperCtx.fillStyle = 'rgba(255,255,255, 1)'; 324 | canvasHelperCtx.fill(); 325 | 326 | canvasHelperCtx.strokeStyle = 'rgba(0, 0, 0, 1)'; 327 | canvasHelperCtx.stroke(); 328 | canvasHelperCtx.closePath(); 329 | canvasHelperCtx.beginPath(); 330 | 331 | canvasHelperCtx.lineWidth = this.lineWidth; 332 | 333 | if (this.align == 'left') { 334 | canvasHelperCtx.moveTo(this.selectSize, this.width - this.selectSize); 335 | canvasHelperCtx.lineTo(this.width - this.selectSize * 2, this.width - this.selectSize); 336 | canvasHelperCtx.lineTo(this.selectSize, this.selectSize * 2); 337 | canvasHelperCtx.lineTo(this.selectSize, this.width - this.selectSize); 338 | } 339 | 340 | if (this.align == 'right') { 341 | 342 | canvasHelperCtx.moveTo(this.selectSize * 2, this.width - this.selectSize); 343 | canvasHelperCtx.lineTo(this.width - this.selectSize, this.width - this.selectSize); 344 | canvasHelperCtx.lineTo(this.width - this.selectSize, this.selectSize * 2); 345 | canvasHelperCtx.lineTo(this.selectSize * 2, this.width - this.selectSize); 346 | } 347 | } 348 | 349 | var rgb = hexToRgb(this.color); 350 | canvasHelperCtx.fillStyle = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ', 1)'; 351 | canvasHelperCtx.fill(); 352 | canvasHelperCtx.strokeStyle = 'rgba(0, 0, 0, 1)'; 353 | canvasHelperCtx.stroke(); 354 | 355 | this.imageData = canvasHelperCtx.getImageData(0, 0, this.width, this.width); 356 | ctx.drawImage(canvasHelper, this.position.x + this.paddingX, this.position.y + this.paddingY); 357 | 358 | } 359 | 360 | var colorSaverKey = colorSavers.length; 361 | colorSavers[colorSaverKey] = colorSaver; 362 | } 363 | 364 | var wheel = new Object; 365 | wheel.width = 18; 366 | wheel.imageData = null; // rendered wheel image data 367 | wheel.innerRadius; 368 | wheel.startAngle = 0; // 150 369 | wheel.outerRadius; 370 | wheel.outerStrokeStyle = 'rgba(0,0,0,0.2)'; 371 | wheel.innerStrokeStyle = 'rgba(0,0,0,0.2)'; 372 | wheel.pos; // updates in updateSize() | center point; wheel cursor \ hsv quad \ hsv triangle positioned relative that point 373 | wheel.draw = function () { 374 | 375 | // put rendered data 376 | 377 | if (this.imageData) { 378 | 379 | canvasHelper.width = wheelBlockSize; 380 | canvasHelper.height = wheelBlockSize; 381 | canvasHelperCtx.putImageData(this.imageData, 0, 0); // use helper only, put image data directly to ctx cause render bug if combine with draw image ontop after (ex. in triangle.draw) in modern chrome versions 382 | ctx.drawImage(canvasHelper, 0, 0); // draw with save overlaps transparent things , not direct putImageData that rewrite all pixels 383 | 384 | } else { 385 | var hAngle = this.startAngle; 386 | for (var angle = 0; angle <= 360; angle++) { 387 | 388 | var startAngle = toRadians(angle - 2); 389 | var endAngle = toRadians(angle); 390 | 391 | ctx.beginPath(); 392 | ctx.moveTo(center, center); 393 | ctx.arc(center, center, this.outerRadius, startAngle, endAngle, false); 394 | ctx.closePath(); 395 | 396 | var targetRgb = hsvToRgb(hAngle / 360, 1, 1); 397 | ctx.fillStyle = 'rgb(' + targetRgb.r + ', ' + targetRgb.g + ', ' + targetRgb.b + ')'; 398 | //ctx.fillStyle = 'hsl('+hAngle+', 100%, 50%)'; 399 | ctx.fill(); 400 | 401 | hAngle++; 402 | if (hAngle >= 360) 403 | hAngle = 0; 404 | } 405 | 406 | ctx.globalCompositeOperation = "destination-out"; // cut out color wheel inside by circle next 407 | ctx.beginPath(); 408 | ctx.arc(center, center, this.innerRadius, 0, PI * 2); 409 | 410 | ctx.fill(); 411 | 412 | ctx.globalCompositeOperation = "source-over"; 413 | ctx.strokeStyle = this.innerStrokeStyle; // 'rgba(0,0,0,0.2)'; 414 | ctx.lineWidth = 2; 415 | ctx.stroke(); 416 | ctx.closePath(); 417 | 418 | // wheel border 419 | ctx.beginPath(); 420 | ctx.arc(center, center, this.outerRadius, 0, PI * 2); 421 | ctx.strokeStyle = this.outerStrokeStyle; 422 | ctx.lineWidth = 2; 423 | ctx.stroke(); 424 | ctx.closePath(); 425 | 426 | this.imageData = ctx.getImageData(0, 0, wheelBlockSize, wheelBlockSize); 427 | } 428 | 429 | }; 430 | 431 | wheel.isDotIn = function (dot) { 432 | // is dot in circle 433 | if (Math.pow(this.pos.x - dot.x, 2) + Math.pow(this.pos.y - dot.y, 2) < Math.pow(this.outerRadius, 2)) { 434 | if (Math.pow(this.pos.x - dot.x, 2) + Math.pow(this.pos.y - dot.y, 2) > Math.pow(this.innerRadius, 2)) { 435 | return true; 436 | } 437 | } 438 | return false; 439 | }; 440 | 441 | var wheelCursor = new Object; 442 | wheelCursor.lineWeight = 2; 443 | wheelCursor.height = 4; 444 | wheelCursor.paddingX = 2; // padding from sides of wheel 445 | wheelCursor.path; // rotatePath2 --- поворот по старой функции, в фигуре не приплюсован центр 446 | 447 | var alphaSlider = new Object; 448 | alphaSlider.width = 18; 449 | alphaSlider.padding = 4; 450 | alphaSlider.outerStrokeStyle = 'rgba(0,0,0,0.2)'; 451 | alphaSlider.innerStrokeStyle = 'rgba(0,0,0,0.2)'; 452 | alphaSlider.height; 453 | alphaSlider.pos; // left top corner position 454 | alphaSlider.updateSize = function () { 455 | this.pos = {x: wheelBlockSize + alphaSlider.padding, y: alphaSlider.padding}; 456 | this.height = wheelBlockSize - alphaSlider.padding * 2; 457 | }; 458 | 459 | alphaSlider.draw = function () { 460 | var alphaGrd = ctx.createLinearGradient(0, 0, 0, this.height); 461 | 462 | var aRgb = hsvToRgb(hsv.h, 1, 1); 463 | 464 | alphaGrd.addColorStop(0, 'rgba(' + aRgb.r + ',' + aRgb.g + ',' + aRgb.b + ',1)'); 465 | alphaGrd.addColorStop(1, 'rgba(' + aRgb.r + ',' + aRgb.g + ',' + aRgb.b + ',0)'); 466 | 467 | ctx.beginPath(); 468 | ctx.rect(this.pos.x, this.pos.y, this.width, this.height); 469 | ctx.fillStyle = "white"; 470 | ctx.fill(); 471 | ctx.fillStyle = alphaGrd; 472 | ctx.fill(); 473 | 474 | ctx.strokeStyle = 'rgba(0,0,0, 0.2)'; 475 | ctx.lineWidth = 2; 476 | 477 | ctx.stroke(); 478 | ctx.closePath(); 479 | }; 480 | 481 | alphaSlider.dotToAlpha = function (dot) { 482 | return 1 - Math.abs(this.pos.y - dot.y) / this.height; 483 | }; 484 | 485 | alphaSlider.alphaToDot = function (alpha) { 486 | return { 487 | x: 0, 488 | y: this.height - (this.height * alpha) 489 | }; 490 | }; 491 | 492 | alphaSlider.limitDotPosition = function (dot) { 493 | var y = dot.y; 494 | 495 | if (y < this.pos.y) { 496 | y = this.pos.y; 497 | } 498 | 499 | if (y > this.pos.y + this.height) { 500 | y = this.pos.y + this.height; 501 | } 502 | 503 | return {x: this.pos.x, y: y}; 504 | }; 505 | 506 | alphaSlider.isDotIn = function (dot) { 507 | if (dot.x < this.pos.x || 508 | dot.x > this.pos.x + alphaSlider.width || 509 | dot.y < this.pos.y || 510 | dot.y > this.pos.y + this.height) { 511 | return false; 512 | } 513 | return true; 514 | }; 515 | 516 | // svCursorMouse - для устройств с мышкой, генератор указателя в зависимости от активной области 517 | // todo on very very small sv when set by hex, cursor may be go out of bounds 518 | var svCursorMouse = new Object; 519 | 520 | svCursorMouse.svCursorData = null; 521 | svCursorMouse.stCursor = null; // cursor before replace 522 | svCursorMouse.curType = 0; // if > 0 cursor switched by KellyColorPicker to custom 523 | svCursorMouse.size = 16; 524 | svCursorMouse.cEl = document.body; 525 | 526 | svCursorMouse.initSvCursor = function () { 527 | if (!canvas) 528 | return false; 529 | 530 | this.curType = 1; 531 | 532 | if (!this.stCursor) { 533 | 534 | this.stCursor = window.getComputedStyle(this.cEl).cursor; 535 | 536 | if (!this.stCursor) { 537 | this.stCursor = 'auto'; 538 | } 539 | } 540 | 541 | if (this.svCursorData) { 542 | this.cEl.style.cursor = this.svCursorData; 543 | return true; 544 | } 545 | 546 | if (!canvasHelper) 547 | return false; 548 | 549 | // create canvas on 2 pixels bigger for Opera that cut image 550 | var canvasSize = this.size + 2; 551 | 552 | canvasHelper.width = canvasSize; 553 | canvasHelper.height = canvasSize; 554 | 555 | canvasHelperCtx.clearRect(0, 0, this.size, this.size); 556 | canvasHelperCtx.strokeStyle = 'rgba(255, 255, 255, 1)'; 557 | 558 | canvasHelperCtx.beginPath(); 559 | canvasHelperCtx.lineWidth = 2; 560 | canvasHelperCtx.arc(canvasSize / 2, canvasSize / 2, this.size / 2, 0, PI * 2); 561 | 562 | canvasHelperCtx.stroke(); 563 | canvasHelperCtx.closePath(); 564 | 565 | var offset = canvasSize; //if (input.value.indexOf(curImageData) !== -1) 566 | var curImageData = canvasHelper.toDataURL(); 567 | 568 | this.svCursorData = 'url(' + curImageData + ') ' + offset / 2 + ' ' + offset / 2 + ', auto'; 569 | 570 | if (!this.svCursorData) 571 | return false; 572 | 573 | this.cEl.style.cursor = this.svCursorData; 574 | if (this.cEl.style.cursor.indexOf(curImageData) === -1) { // for autist IE (Edge also), that not support data-uri for cursor -_- 575 | this.svCursorData = 'crosshair'; 576 | this.cEl.style.cursor = 'crosshair'; 577 | } 578 | return true; 579 | }; 580 | 581 | svCursorMouse.initStandartCursor = function () { 582 | if (!this.stCursor) 583 | return; 584 | 585 | svCursorMouse.curType = 0; 586 | this.cEl.style.cursor = this.stCursor; 587 | }; 588 | 589 | svCursorMouse.updateCursor = function (newDot) { 590 | if (!changeCursor) 591 | return; 592 | 593 | if (KellyColorPicker.cursorLock) 594 | return; 595 | 596 | if (svFig.isDotIn(newDot)) { 597 | svCursorMouse.initSvCursor(); 598 | } else { 599 | svCursorMouse.initStandartCursor(); 600 | } 601 | }; 602 | 603 | // updateinput 604 | 605 | function constructor(cfg) { 606 | var criticalError = '', placeName = ''; 607 | 608 | // save non-camelased old style options compatibility 609 | 610 | if (cfg.alpha_slider !== undefined) { 611 | cfg.alphaSlider = cfg.alpha_slider; 612 | } 613 | 614 | if (cfg.input_color !== undefined) { 615 | cfg.inputColor = cfg.input_color; 616 | } 617 | 618 | if (cfg.input_format !== undefined) { 619 | cfg.inputFormat = cfg.input_format; 620 | } 621 | 622 | // config apply 623 | 624 | if (cfg.input && typeof cfg.input !== 'object') { 625 | cfg.input = document.getElementById(cfg.input); 626 | input = cfg.input; 627 | // if (!cfg.input) log += '| "input" (' + inputName + ') not not found'; 628 | } else if (typeof cfg.input === 'object') { 629 | input = cfg.input; 630 | } 631 | 632 | if (cfg.changeCursor !== undefined) { 633 | changeCursor = cfg.changeCursor; 634 | } 635 | 636 | if (cfg.alpha !== undefined) { 637 | a = cfg.alpha; 638 | } 639 | 640 | if (cfg.alphaSlider !== undefined) { 641 | alpha = cfg.alphaSlider; 642 | } 643 | 644 | if (cfg.inputColor !== undefined) { 645 | inputColor = cfg.inputColor; 646 | } 647 | 648 | if (cfg.inputFormat !== undefined) { 649 | inputFormat = cfg.inputFormat; 650 | } 651 | 652 | if (cfg.userEvents) 653 | userEvents = cfg.userEvents; 654 | 655 | if (cfg.place && typeof cfg.place !== 'object') { 656 | placeName = cfg.place; 657 | cfg.place = document.getElementById(cfg.place); 658 | } 659 | 660 | if (cfg.place) { 661 | place = cfg.place; 662 | } else if (input) { 663 | 664 | popup.tag = document.createElement('div'); 665 | popup.tag.className = "popup-kelly-color"; 666 | 667 | if (!cfg.popupClass) { 668 | 669 | popup.tag.className = "popup-kelly-color"; 670 | 671 | popup.tag.style.position = 'absolute'; 672 | popup.tag.style.bottom = '0px'; 673 | popup.tag.style.left = '0px'; 674 | popup.tag.style.display = 'none'; 675 | popup.tag.style.backgroundColor = '#e1e1e1'; 676 | popup.tag.style.border = "1px solid #bfbfbf"; 677 | popup.tag.style.boxShadow = "7px 7px 14px -3px rgba(0,0,0,0.24)"; 678 | popup.tag.style.borderTopLeftRadius = '4px'; 679 | popup.tag.style.borderTopRightRadius = '4px'; 680 | popup.tag.style.borderBottomLeftRadius = '4px'; 681 | popup.tag.style.borderBottomRightRadius = '4px'; 682 | popup.tag.style.padding = "12px"; 683 | popup.tag.style.boxSizing = "content-box"; 684 | 685 | } else { 686 | popup.tag.className = cfg.popupClass; 687 | } 688 | 689 | place = popup.tag; 690 | 691 | var body = document.getElementsByTagName('body')[0]; 692 | body.appendChild(popup.tag); 693 | 694 | addEventListner(input, "click", function (e) { 695 | return handler.popUpShow(e); 696 | }, 'popup_'); 697 | 698 | } // attach directly to input by popup 699 | else 700 | criticalError += '| "place" (' + placeName + ') not not found'; 701 | 702 | // hex default #000000 703 | var colorData = false; 704 | 705 | if (cfg.color) { 706 | colorData = readColorData(cfg.color); 707 | } else if (input && input.value) { 708 | colorData = readColorData(input.value); 709 | } 710 | 711 | if (colorData) { 712 | hex = colorData.h; 713 | if (alpha) 714 | a = colorData.a; 715 | } 716 | 717 | //if (hex.charAt(0) == '#') hex = hex.slice(1); 718 | //if (hex.length == 3) hex = hex + hex; 719 | //if (hex.length !== 6) hex = '#000000'; 720 | 721 | if (cfg.method && (cfg.method == 'triangle' || cfg.method == 'quad')) 722 | method = cfg.method; 723 | 724 | if (!initCanvas()) { 725 | criticalError += ' | cant init canvas context'; 726 | } 727 | 728 | // size of elments init 729 | 730 | if (cfg.resizeWith) { 731 | 732 | if (typeof cfg.resizeWith !== 'object' && typeof cfg.resizeWith !== 'boolean') 733 | cfg.resizeWith = document.getElementById(cfg.resizeWith); 734 | 735 | if (cfg.resizeWith === true) { 736 | resizeWith = canvas; 737 | } else { 738 | resizeWith = cfg.resizeWith; 739 | } 740 | 741 | if (cfg.resizeSide) 742 | resizeSide = cfg.resizeSide; 743 | 744 | if (resizeWith) { 745 | var newSize = getSizeByElement(resizeWith); 746 | if (newSize) 747 | cfg.size = getSizeByElement(resizeWith); 748 | 749 | addEventListner(window, "resize", function (e) { 750 | return handler.syncSize(e); 751 | }, 'canvas_'); 752 | } 753 | } 754 | 755 | if (cfg.size && cfg.size > 0) { 756 | wheelBlockSize = cfg.size; 757 | } 758 | 759 | // size init end 760 | 761 | if (criticalError) { 762 | if (typeof console !== 'undefined') 763 | console.log('KellyColorPicker : ' + criticalError); 764 | return; 765 | } 766 | 767 | if (method == 'quad') 768 | svFig = getSvFigureQuad(); 769 | if (method == 'triangle') 770 | svFig = getSvFigureTriangle(); 771 | 772 | if (input) { 773 | var inputEdit = function (e) { 774 | var e = e || window.event; 775 | if (!e.target) { 776 | e.target = e.srcElement; 777 | } 778 | handler.setColorByHex(e.target.value, true); 779 | }; 780 | 781 | addEventListner(input, "click", inputEdit, 'input_edit_'); 782 | addEventListner(input, "change", inputEdit, 'input_edit_'); 783 | addEventListner(input, "keyup", inputEdit, 'input_edit_'); 784 | addEventListner(input, "keypress", inputEdit, 'input_edit_'); 785 | } 786 | 787 | if (cfg.colorSaver) { 788 | initColorSaver('left', true); 789 | initColorSaver('right'); 790 | } 791 | 792 | if (cfg.methodSwitch) { 793 | initStyleSwitch(); 794 | } 795 | 796 | enableEvents(); 797 | 798 | updateSize(); 799 | handler.setColorByHex(false); // update color info and first draw 800 | } 801 | 802 | // may be zero in some cases / check before applay 803 | 804 | function getSizeByElement(el) { 805 | 806 | var sizeInfo = el.getBoundingClientRect(); 807 | var size = 0; 808 | var sizeReduse = 0; 809 | if (alpha) { 810 | sizeReduse = alphaSlider.width + alphaSlider.padding * 2; 811 | } 812 | 813 | if (el === canvas) { 814 | if (sizeInfo.width <= sizeInfo.height) 815 | size = sizeInfo.height; 816 | else if (sizeInfo.height < sizeInfo.width) 817 | size = sizeInfo.width; 818 | } else { 819 | 820 | if (resizeSide) { 821 | if (resizeSide == 'height') 822 | size = sizeInfo.height; 823 | else if (resizeSide == 'width') 824 | size = sizeInfo.width; 825 | } else { 826 | if (sizeInfo.width > sizeInfo.height) 827 | size = sizeInfo.height; 828 | else if (sizeInfo.height >= sizeInfo.width) 829 | size = sizeInfo.width; 830 | } 831 | } 832 | 833 | size = parseInt(size); 834 | 835 | if (alpha) { 836 | 837 | size -= sizeReduse; 838 | } 839 | 840 | if (size <= 0) { 841 | return false; 842 | } 843 | 844 | return size; 845 | } 846 | 847 | // Read color value from string cString in rgb \ rgba \ hex \ hsl \ hsla format or from object ex. rgb {r : 255, g : 255, b : 255, a : 1}, hsv {h : 1, s: 1, l : 1, a : 1} 848 | // return array {h : color in rgb hex format (string #000000), a : alpha (float value from 0 to 1) } 849 | // falseOnFail = false - return default array on fail {h : '#000000', a : 1} or return false on fail if true 850 | 851 | function readColorData(cString, falseOnFail) { 852 | 853 | var alpha = 1; 854 | var h = false; 855 | 856 | if (typeof cString == 'string') { 857 | 858 | cString = cString.trim(cString); 859 | 860 | if (cString.indexOf("(") == -1) { // hex color 861 | 862 | if (cString.charAt(0) == '#') 863 | cString = cString.slice(1); 864 | 865 | cString = cString.substr(0, 8); 866 | 867 | if (cString.length >= 3) { 868 | 869 | if (cString.length > 6 && cString.length < 8) { 870 | cString = cString.substr(0, 6); // bad alpha data 871 | } 872 | 873 | if (cString.length > 3 && cString.length < 6) { 874 | cString = cString.substr(0, 3); // bad full format 875 | } 876 | 877 | h = cString; 878 | 879 | // complite full format, by replicating the R, G, and B values 880 | 881 | if (cString.length >= 3 && cString.length <= 4) { 882 | 883 | h = ""; 884 | 885 | for (let i = 0; i < cString.length; i++) { 886 | h += cString[i] + cString[i]; 887 | } 888 | } 889 | 890 | if (h.length == 8) 891 | alpha = (parseInt(h, 16) & 255) / 255; 892 | 893 | } 894 | 895 | } else { 896 | 897 | vals = cString.split(","); 898 | 899 | if (vals.length >= 3) { 900 | 901 | switch (cString.substring(0, 3)) { 902 | 903 | case 'rgb': 904 | 905 | vals[0] = vals[0].replace("rgba(", ""); 906 | vals[0] = vals[0].replace("rgb(", ""); 907 | 908 | var rgb = {r: parseInt(vals[0]), g: parseInt(vals[1]), b: parseInt(vals[2])}; 909 | 910 | if (rgb.r <= 255 && rgb.g <= 255 && rgb.b <= 255) { 911 | h = rgbToHex(rgb); 912 | } 913 | 914 | break; 915 | 916 | case 'hsl': 917 | 918 | vals[0] = vals[0].replace("hsl(", ""); 919 | vals[0] = vals[0].replace("hsla(", ""); 920 | 921 | var hue = parseFloat(vals[0]) / 360.0; 922 | var s = parseFloat(vals[1]) / 100.0; //js will ignore % in the end 923 | var l = parseFloat(vals[2]) / 100.0; 924 | 925 | if (hue >= 0 && s <= 1 && l <= 1) { 926 | h = rgbToHex(hsvToRgb(hue, s, l)); 927 | } 928 | 929 | break; 930 | } 931 | 932 | if (vals.length == 4) { 933 | 934 | alpha = parseFloat(vals[3]); 935 | 936 | if (!alpha || alpha < 0) 937 | alpha = 0; 938 | 939 | if (alpha > 1) 940 | alpha = 1; 941 | } 942 | } 943 | } 944 | 945 | } else if (typeof cString == 'object') { 946 | 947 | if (typeof cString.r != 'undefined' && 948 | typeof cString.g != 'undefined' && 949 | typeof cString.b != 'undefined') { // rgb input 950 | 951 | h = rgbToHex(cString); 952 | 953 | } else if ( 954 | typeof cString.h != 'undefined' && 955 | typeof cString.s != 'undefined' && 956 | typeof cString.l != 'undefined') { 957 | 958 | cString.h = parseFloat(cString.h) / 360.0; 959 | cString.s = parseFloat(cString.s) / 100.0; 960 | cString.l = parseFloat(cString.l) / 100.0; 961 | 962 | if (cString.h >= 0 && cString.s <= 1 && cString.l <= 1) { 963 | h = rgbToHex(hsvToRgb(cString.h, cString.s, cString.l)); 964 | } 965 | } 966 | 967 | if (typeof cString.a != 'undefined') { 968 | 969 | alpha = cString.a; 970 | } 971 | } 972 | 973 | if (h === false && falseOnFail) { 974 | 975 | return false; 976 | } 977 | 978 | if (h === false) { 979 | 980 | h = '000000'; 981 | } 982 | 983 | if (h.charAt(0) != '#') { 984 | 985 | h = h.substr(0, 6); 986 | h = '#' + h; 987 | 988 | } else { 989 | 990 | h = h.substr(0, 7); // for private purposes must contain only rgb part 991 | } 992 | 993 | return {h: h, a: alpha}; 994 | } 995 | 996 | function getSvFigureQuad() { 997 | 998 | if (svFigsPool['quad']) 999 | return svFigsPool['quad']; 1000 | 1001 | var quad = new Object; 1002 | quad.size; 1003 | quad.padding = 2; 1004 | quad.path; // крайние точки фигуры на координатной плоскости 1005 | quad.imageData = null; // rendered quad image data 1006 | // перезаписывается существующий, чтобы не вызывать утечек памяти, обнуляя прошлый 1007 | // тк UInt8ClampedArray генерируемый createImageData стандартными способами не 1008 | // во всех браузерах выгружается сразу 1009 | 1010 | quad.dotToSv = function (dot) { 1011 | return { 1012 | s: Math.abs(this.path[3].x - dot.x) / this.size, 1013 | v: Math.abs(this.path[3].y - dot.y) / this.size 1014 | }; 1015 | }; 1016 | 1017 | quad.svToDot = function (sv) { 1018 | var quadX = this.path[0].x; 1019 | var quadY = this.path[0].y; 1020 | 1021 | var svError = 0.02; 1022 | if (wheelBlockSize < 150) { 1023 | svError = 0.07; 1024 | } else if (wheelBlockSize < 100) { 1025 | svError = 0.16; 1026 | } 1027 | 1028 | for (var y = 0; y < this.size; y++) { 1029 | for (var x = 0; x < this.size; x++) { 1030 | var dot = {x: x + quadX, y: y + quadY}; 1031 | var targetSv = this.dotToSv(dot); 1032 | var es = Math.abs(targetSv.s - sv.s), ev = Math.abs(targetSv.v - sv.v); 1033 | 1034 | if (es < svError && ev < svError) { 1035 | return dot; 1036 | } 1037 | } 1038 | } 1039 | 1040 | return {x: 0, y: 0}; 1041 | }; 1042 | 1043 | quad.limitDotPosition = function (dot) { 1044 | var x = dot.x; 1045 | var y = dot.y; 1046 | 1047 | if (x < this.path[0].x) { 1048 | x = this.path[0].x; 1049 | } 1050 | 1051 | if (x > this.path[0].x + this.size) { 1052 | x = this.path[0].x + this.size; 1053 | } 1054 | 1055 | if (y < this.path[0].y) { 1056 | y = this.path[0].y; 1057 | } 1058 | 1059 | if (y > this.path[0].y + this.size) { 1060 | y = this.path[0].y + this.size; 1061 | } 1062 | 1063 | return {x: x, y: y}; 1064 | }; 1065 | 1066 | quad.draw = function () { 1067 | if (!this.imageData) 1068 | this.imageData = ctx.createImageData(this.size, this.size); 1069 | var i = 0; 1070 | 1071 | var quadX = this.path[0].x; 1072 | var quadY = this.path[0].y; 1073 | 1074 | for (var y = 0; y < this.size; y++) { 1075 | for (var x = 0; x < this.size; x++) { 1076 | var dot = {x: x + quadX, y: y + quadY}; 1077 | 1078 | var sv = this.dotToSv(dot); 1079 | var targetRgb = hsvToRgb(hsv.h, sv.s, sv.v); 1080 | this.imageData.data[i + 0] = targetRgb.r; 1081 | this.imageData.data[i + 1] = targetRgb.g; 1082 | this.imageData.data[i + 2] = targetRgb.b; 1083 | this.imageData.data[i + 3] = 255; 1084 | i += 4; 1085 | } 1086 | } 1087 | 1088 | ctx.putImageData(this.imageData, quadX, quadY); 1089 | 1090 | ctx.beginPath(); 1091 | ctx.strokeStyle = 'rgba(0,0,0, 0.2)'; 1092 | ctx.lineWidth = 2; 1093 | for (var i = 0; i <= this.path.length - 1; ++i) 1094 | { 1095 | if (i == 0) 1096 | ctx.moveTo(this.path[i].x, this.path[i].y); 1097 | else 1098 | ctx.lineTo(this.path[i].x, this.path[i].y); 1099 | } 1100 | 1101 | ctx.stroke(); 1102 | 1103 | ctx.closePath(); 1104 | }; 1105 | 1106 | quad.updateSize = function () { 1107 | var workD = (wheel.innerRadius * 2) - wheelCursor.paddingX * 2 - this.padding * 2; 1108 | 1109 | // исходя из формулы диагонали квадрата, узнаем длинну стороны на основании доступного диаметра 1110 | this.size = Math.floor(workD / Math.sqrt(2)); 1111 | 1112 | this.path = new Array(); 1113 | 1114 | // находим верхнюю левую точку и от нее задаем остальные координаты 1115 | this.path[0] = {x: -1 * (this.size / 2), y: -1 * (this.size / 2)}; 1116 | this.path[1] = {x: this.path[0].x + this.size, y: this.path[0].y}; 1117 | this.path[2] = {x: this.path[1].x, y: this.path[1].y + this.size}; 1118 | this.path[3] = {x: this.path[2].x - this.size, y: this.path[2].y}; 1119 | this.path[4] = {x: this.path[0].x, y: this.path[0].y}; 1120 | 1121 | for (var i = 0; i <= this.path.length - 1; ++i) { 1122 | this.path[i].x += wheel.pos.x; 1123 | this.path[i].y += wheel.pos.y; 1124 | } 1125 | } 1126 | 1127 | quad.isDotIn = function (dot) { 1128 | if (dot.x < this.path[0].x || 1129 | dot.x > this.path[0].x + this.size || 1130 | dot.y < this.path[0].y || 1131 | dot.y > this.path[0].y + this.size) { 1132 | return false; 1133 | } 1134 | return true; 1135 | }; 1136 | 1137 | svFigsPool['quad'] = quad; 1138 | return quad; 1139 | } 1140 | 1141 | function getSvFigureTriangle() { 1142 | 1143 | if (svFigsPool['triangle']) 1144 | return svFigsPool['triangle']; 1145 | 1146 | var triangle = new Object; 1147 | triangle.size; // сторона равностороннего треугольника 1148 | triangle.padding = 2; 1149 | triangle.path; 1150 | triangle.imageData = null; // rendered triangle image data 1151 | triangle.followWheel = true; 1152 | triangle.s; 1153 | triangle.sOnTop = false; 1154 | triangle.outerRadius; 1155 | 1156 | triangle.limitDotPosition = function (dot) { 1157 | var x = dot.x; 1158 | var y = dot.y; 1159 | 1160 | var slopeToCtr; 1161 | var maxX = this.path[0].x; 1162 | var minX = this.path[2].x; 1163 | var finalX = x; 1164 | var finalY = y; 1165 | 1166 | finalX = Math.min(Math.max(minX, finalX), maxX); 1167 | var slope = ((this.path[0].y - this.path[1].y) / (this.path[0].x - this.path[1].x)); 1168 | var minY = Math.ceil((this.path[1].y + (slope * (finalX - this.path[1].x)))); 1169 | slope = ((this.path[0].y - this.path[2].y) / (this.path[0].x - this.path[2].x)); 1170 | var maxY = Math.floor((this.path[2].y + (slope * (finalX - this.path[2].x)))); 1171 | 1172 | if (x < minX) { 1173 | slopeToCtr = ((wheel.pos.y - y) / (wheel.pos.x - x)); 1174 | finalY = y; 1175 | } 1176 | 1177 | finalY = Math.min(Math.max(minY, finalY), maxY); 1178 | return {x: finalX, y: finalY}; 1179 | }; 1180 | 1181 | triangle.svToDot = function (sv) { 1182 | var svError = 0.02; 1183 | if (wheelBlockSize < 150) { 1184 | svError = 0.07; 1185 | } else if (wheelBlockSize < 100) { 1186 | svError = 0.16; 1187 | } 1188 | 1189 | for (var y = 0; y < this.size; y++) { 1190 | for (var x = 0; x < this.size; x++) { 1191 | var dot = {x: this.path[1].x + x, y: this.path[1].y + y}; 1192 | if (svFig.isDotIn(dot)) { 1193 | var targetSv = this.dotToSv(dot); 1194 | var es = Math.abs(targetSv.s - sv.s), ev = Math.abs(targetSv.v - sv.v); 1195 | 1196 | if (es < svError && ev < svError) { 1197 | return dot; 1198 | } 1199 | } 1200 | } 1201 | } 1202 | 1203 | return { 1204 | x: 0, 1205 | y: 0 1206 | }; 1207 | }; 1208 | 1209 | triangle.draw = function () { 1210 | // no buffer 1211 | 1212 | if (!this.imageData) 1213 | this.imageData = canvasHelperCtx.createImageData(this.size, this.size); 1214 | 1215 | canvasHelper.width = this.size; 1216 | canvasHelper.height = this.size; 1217 | 1218 | var trX = this.path[1].x; 1219 | var trY = this.path[1].y; 1220 | var i = 0; 1221 | for (var y = 0; y < this.size; y++) { 1222 | for (var x = 0; x < this.size; x++) { 1223 | var dot = {x: this.path[1].x + x, y: this.path[1].y + y}; 1224 | if (!svFig.isDotIn(dot)) { 1225 | this.imageData.data[i + 0] = 0; 1226 | this.imageData.data[i + 1] = 0; 1227 | this.imageData.data[i + 2] = 0; 1228 | this.imageData.data[i + 3] = 0; 1229 | } else { 1230 | var sv = this.dotToSv(dot); 1231 | var targetRgb = hsvToRgb(hsv.h, sv.s, sv.v); 1232 | 1233 | this.imageData.data[i + 0] = targetRgb.r; 1234 | this.imageData.data[i + 1] = targetRgb.g; 1235 | this.imageData.data[i + 2] = targetRgb.b; 1236 | this.imageData.data[i + 3] = 255; 1237 | } 1238 | 1239 | i += 4; 1240 | } 1241 | } 1242 | 1243 | canvasHelperCtx.putImageData(this.imageData, 0, 0); 1244 | ctx.drawImage(canvasHelper, trX, trY); // draw with save overlaps transparent things , not direct putImageData that rewrite all pixels 1245 | 1246 | ctx.beginPath(); 1247 | ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)'; 1248 | ctx.lineWidth = 2; 1249 | var trianglePath = this.path; //rotatePath(triangle.path, hsv.h * 360); 1250 | for (var i = 0; i <= trianglePath.length - 1; ++i) 1251 | { 1252 | if (i == 0) 1253 | ctx.moveTo(trianglePath[i].x, trianglePath[i].y); 1254 | else 1255 | ctx.lineTo(trianglePath[i].x, trianglePath[i].y); 1256 | } 1257 | 1258 | ctx.stroke(); 1259 | ctx.closePath(); 1260 | }; 1261 | 1262 | triangle.calcS = function (p) { 1263 | return Math.abs((p[1].x - p[0].x) * (p[2].y - p[0].y) - (p[2].x - p[0].x) * (p[1].y - p[0].y)) / 2; 1264 | }; 1265 | 1266 | triangle.dotToSv = function (dot) { 1267 | var p = getP({x: dot.x, y: dot.y}, this.vol); 1268 | var len = getLen(p, this.vol[0]); 1269 | 1270 | // dirty tricks? replace output to interpolation and lerp in future 1271 | if (len < 1) 1272 | len = Math.floor(len); 1273 | if (len > this.h - 1) 1274 | len = this.h; 1275 | 1276 | var vol = len / (this.h); 1277 | 1278 | var angle = Math.abs(getAngle(dot, this.sSide)); 1279 | if (angle < 30) 1280 | angle = 30; 1281 | angle -= 30; 1282 | angle = 60 - angle; 1283 | angle = angle / 60; // - saturation from one angle 1284 | 1285 | return {s: angle, v: vol}; 1286 | }; 1287 | 1288 | triangle.isDotIn = function (dot) { 1289 | var t = [ 1290 | {x: this.path[0].x, y: this.path[0].y}, 1291 | {x: this.path[1].x, y: this.path[1].y}, 1292 | {x: dot.x, y: dot.y} 1293 | ]; 1294 | 1295 | var s = this.calcS(t); 1296 | t[1] = {x: this.path[2].x, y: this.path[2].y}; 1297 | s += this.calcS(t); 1298 | t[0] = {x: this.path[1].x, y: this.path[1].y}; 1299 | s += this.calcS(t); 1300 | 1301 | if (Math.ceil(s) == Math.ceil(this.s)) 1302 | return true; 1303 | else 1304 | return false; 1305 | }; 1306 | 1307 | triangle.updateSize = function () { 1308 | // из формулы высоты равностороннего треугольника 1309 | this.outerRadius = wheel.innerRadius - wheelCursor.paddingX - this.padding; 1310 | // из теоремы синусов треугольника 1311 | this.size = Math.floor((2 * this.outerRadius) * Math.sin(toRadians(60))); 1312 | 1313 | var h = ((Math.sqrt(3) / 2) * this.size); 1314 | this.h = ((Math.sqrt(3) / 2) * this.size); 1315 | 1316 | this.path = new Array(); 1317 | this.path[0] = {x: this.outerRadius, y: 0}; // middle point - h 1318 | this.path[1] = {x: this.path[0].x - h, y: -1 * (this.size / 2)}; // upper - s 1319 | this.path[2] = {x: this.path[1].x, y: this.size / 2}; // bottom - v 1320 | this.path[3] = {x: this.path[0].x, y: this.path[0].y}; // to begin 1321 | 1322 | for (var i = 0; i <= this.path.length - 1; ++i) { 1323 | this.path[i].x += wheel.pos.x; 1324 | this.path[i].y += wheel.pos.y; 1325 | } 1326 | 1327 | this.vol = new Array(); 1328 | 1329 | 1330 | this.s = this.calcS(this.path); 1331 | if (this.sOnTop) { 1332 | var middle = getMiddlePoint(this.path[0], this.path[2]); 1333 | 1334 | this.vol[0] = {x: this.path[1].x, y: this.path[1].y}; 1335 | this.vol[1] = {x: middle.x, y: middle.y}; 1336 | 1337 | this.sSide = this.path[1]; 1338 | } else { 1339 | var middle = getMiddlePoint(this.path[0], this.path[1]); 1340 | 1341 | this.vol[0] = {x: this.path[2].x, y: this.path[2].y}; 1342 | this.vol[1] = {x: middle.x, y: middle.y}; 1343 | 1344 | this.sSide = this.path[2]; 1345 | } 1346 | }; 1347 | 1348 | svFigsPool['triangle'] = triangle; 1349 | return triangle; 1350 | } 1351 | 1352 | // prefix - for multiple event functions for one object 1353 | function addEventListner(object, event, callback, prefix) { 1354 | if (typeof object !== 'object') { 1355 | object = document.getElementById(object); 1356 | } 1357 | 1358 | if (!object) 1359 | return false; 1360 | if (!prefix) 1361 | prefix = ''; 1362 | 1363 | events[prefix + event] = callback; 1364 | 1365 | if (!object.addEventListener) { 1366 | object.attachEvent('on' + event, events[prefix + event]); 1367 | } else { 1368 | object.addEventListener(event, events[prefix + event]); 1369 | } 1370 | 1371 | return true; 1372 | } 1373 | 1374 | function removeEventListener(object, event, prefix) { 1375 | if (typeof object !== 'object') { 1376 | object = document.getElementById(object); 1377 | } 1378 | 1379 | // console.log('remove : : ' + Object.keys(events).length); 1380 | if (!object) 1381 | return false; 1382 | if (!prefix) 1383 | prefix = ''; 1384 | 1385 | if (!events[prefix + event]) 1386 | return false; 1387 | 1388 | if (!object.removeEventListener) { 1389 | object.detachEvent('on' + event, events[prefix + event]); 1390 | } else { 1391 | object.removeEventListener(event, events[prefix + event]); 1392 | } 1393 | 1394 | events[prefix + event] = null; 1395 | return true; 1396 | } 1397 | 1398 | // [converters] 1399 | // Read more about HSV color model : 1400 | // https://ru.wikipedia.org/wiki/HSV_%28%F6%E2%E5%F2%EE%E2%E0%FF_%EC%EE%E4%E5%EB%FC%29 1401 | // source of converter hsv functions 1402 | // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c 1403 | 1404 | function hsvToRgb(h, s, v) { 1405 | var r, g, b, i, f, p, q, t; 1406 | 1407 | if (h && s === undefined && v === undefined) { 1408 | s = h.s, v = h.v, h = h.h; 1409 | } 1410 | 1411 | i = Math.floor(h * 6); 1412 | f = h * 6 - i; 1413 | p = v * (1 - s); 1414 | q = v * (1 - f * s); 1415 | t = v * (1 - (1 - f) * s); 1416 | 1417 | switch (i % 6) { 1418 | case 0: 1419 | r = v, g = t, b = p; 1420 | break; 1421 | case 1: 1422 | r = q, g = v, b = p; 1423 | break; 1424 | case 2: 1425 | r = p, g = v, b = t; 1426 | break; 1427 | case 3: 1428 | r = p, g = q, b = v; 1429 | break; 1430 | case 4: 1431 | r = t, g = p, b = v; 1432 | break; 1433 | case 5: 1434 | r = v, g = p, b = q; 1435 | break; 1436 | } 1437 | 1438 | return { 1439 | r: Math.floor(r * 255), 1440 | g: Math.floor(g * 255), 1441 | b: Math.floor(b * 255) 1442 | }; 1443 | } 1444 | 1445 | function rgbToHsv(r, g, b) { 1446 | if (r && g === undefined && b === undefined) { 1447 | g = r.g, b = r.b, r = r.r; 1448 | } 1449 | 1450 | r = r / 255, g = g / 255, b = b / 255; 1451 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 1452 | var h, s, v = max; 1453 | 1454 | var d = max - min; 1455 | s = max == 0 ? 0 : d / max; 1456 | 1457 | if (max == min) { 1458 | h = 0; // achromatic 1459 | } else { 1460 | switch (max) { 1461 | case r: 1462 | h = (g - b) / d + (g < b ? 6 : 0); 1463 | break; 1464 | case g: 1465 | h = (b - r) / d + 2; 1466 | break; 1467 | case b: 1468 | h = (r - g) / d + 4; 1469 | break; 1470 | } 1471 | h /= 6; 1472 | } 1473 | 1474 | return {h: h, s: s, v: v}; 1475 | } 1476 | 1477 | function hexToRgb(hex) { 1478 | var dec = parseInt(hex.charAt(0) == '#' ? hex.slice(1) : hex, 16); 1479 | return {r: dec >> 16, g: dec >> 8 & 255, b: dec & 255}; 1480 | } 1481 | 1482 | function rgbToHex(color) { 1483 | var componentToHex = function (c) { 1484 | var hex = c.toString(16); 1485 | return hex.length === 1 ? "0" + hex : hex; 1486 | }; 1487 | 1488 | return "#" + componentToHex(color.r) + componentToHex(color.g) + componentToHex(color.b); 1489 | } 1490 | 1491 | function toRadians(i) { 1492 | return i * (PI / 180); 1493 | } 1494 | 1495 | // [converters - end] 1496 | 1497 | function getLen(point1, point2) { 1498 | return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)); 1499 | } 1500 | 1501 | function getMiddlePoint(point1, point2) { 1502 | return {x: (point1.x + point2.x) / 2, y: (point1.y + point2.y) / 2}; 1503 | } 1504 | 1505 | // перпендикуляр от точки 1506 | 1507 | function getP(point1, line1) { 1508 | var l = (line1[0].x - line1[1].x) * (line1[0].x - line1[1].x) + (line1[0].y - line1[1].y) * (line1[0].y - line1[1].y); 1509 | var pr = (point1.x - line1[0].x) * (line1[1].x - line1[0].x) + (point1.y - line1[0].y) * (line1[1].y - line1[0].y); 1510 | var pt = true; 1511 | var cf = pr / l; 1512 | 1513 | if (cf < 0) { 1514 | cf = 0; 1515 | pt = false; 1516 | } 1517 | if (cf > 1) { 1518 | cf = 1; 1519 | pt = false; 1520 | } 1521 | 1522 | return { 1523 | x: line1[0].x + cf * (line1[1].x - line1[0].x), 1524 | y: line1[0].y + cf * (line1[1].y - line1[0].y), 1525 | pt: pt 1526 | }; 1527 | } 1528 | 1529 | // translate360 = true 270 1530 | // 180 --- from.x.y --- 0 1531 | // 90 1532 | 1533 | function getAngle(point, from, translate360) { 1534 | if (!from) 1535 | from = {x: 0, y: 0}; 1536 | 1537 | var distX = point.x - from.x; 1538 | var distY = point.y - from.y; 1539 | 1540 | var a = Math.atan2(distY, distX) * 180 / (PI); 1541 | if (translate360 && a < 0) 1542 | a = 360 + a; 1543 | 1544 | return a; 1545 | } 1546 | 1547 | // поворот фигуры относительно точки 1548 | function rotatePath2(points, angle) { 1549 | angle = toRadians(angle); 1550 | var newPoints = new Array(); 1551 | 1552 | for (var i = 0; i <= points.length - 1; ++i) 1553 | { 1554 | newPoints[i] = { 1555 | x: points[i].x * Math.cos(angle) - points[i].y * Math.sin(angle), 1556 | y: points[i].x * Math.sin(angle) + points[i].y * Math.cos(angle) 1557 | }; 1558 | } 1559 | 1560 | return newPoints; 1561 | } 1562 | 1563 | function updateSize() { 1564 | padding = basePadding + wheelCursor.paddingX; 1565 | 1566 | rendered = false; 1567 | wheel.imageData = null; 1568 | 1569 | center = wheelBlockSize / 2; 1570 | wheel.pos = {x: center, y: center}; 1571 | 1572 | wheel.outerRadius = center - padding; 1573 | wheel.innerRadius = wheel.outerRadius - wheel.width; 1574 | 1575 | // объект относительно начала координат 1576 | wheelCursor.path = [ 1577 | {x: wheel.innerRadius - wheelCursor.paddingX, y: wheelCursor.height * -1}, 1578 | {x: wheel.outerRadius + wheelCursor.paddingX, y: wheelCursor.height * -1}, 1579 | {x: wheel.outerRadius + wheelCursor.paddingX, y: wheelCursor.height}, 1580 | {x: wheel.innerRadius - wheelCursor.paddingX, y: wheelCursor.height}, 1581 | {x: wheel.innerRadius - wheelCursor.paddingX, y: (wheelCursor.height + (wheelCursor.lineWeight / 2)) * -1} 1582 | ]; 1583 | 1584 | var width = wheelBlockSize; 1585 | if (alpha) 1586 | width += alphaSlider.width + alphaSlider.padding * 2; 1587 | 1588 | if (place.tagName.toLowerCase() != 'canvas') { 1589 | place.style.width = width + 'px'; 1590 | place.style.height = wheelBlockSize + 'px'; 1591 | } 1592 | 1593 | canvas.width = width; 1594 | canvas.height = wheelBlockSize; 1595 | 1596 | if (resizeWith != canvas) { 1597 | canvas.style.width = width + 'px'; 1598 | canvas.style.height = wheelBlockSize + 'px'; 1599 | } 1600 | 1601 | for (var i = 0; i <= colorSavers.length - 1; ++i) 1602 | { 1603 | colorSavers[i].updateSize(); 1604 | } 1605 | 1606 | if (styleSwitch) { 1607 | 1608 | styleSwitch.imageData['triangle'] = null; 1609 | styleSwitch.imageData['quad'] = null; 1610 | 1611 | styleSwitch.updateSize(); 1612 | } 1613 | 1614 | svFig.updateSize(); 1615 | if (alpha) 1616 | alphaSlider.updateSize(); 1617 | } 1618 | 1619 | // updates input after color changes (manualEnter = true if value entered from input, not from widget) 1620 | // if manualEnter = true - save original text in input, else set input value in configurated format 1621 | // if user event 'updateinput' is setted and return false - prevent default updateInput behavior 1622 | 1623 | function updateInput(manualEnter) { 1624 | if (!input) 1625 | return; 1626 | 1627 | if (userEvents["updateinput"]) { 1628 | var callback = userEvents["updateinput"]; 1629 | if (!callback(handler, input, manualEnter)) 1630 | return; 1631 | } 1632 | 1633 | let aStr = a.toFixed(2); 1634 | let rgba = 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + aStr + ')'; 1635 | 1636 | if (!manualEnter) { 1637 | switch (inputFormat) { 1638 | case 'mixed': 1639 | if (a < 1) 1640 | input.value = rgba; 1641 | else 1642 | input.value = hex; 1643 | break; 1644 | case 'hex': 1645 | input.value = hex; 1646 | break; 1647 | case 'hsla': 1648 | input.value = 'hsla(' + (hsv.h * 360).toFixed(2) + ', ' + (hsv.s * 100).toFixed(2) + '%, ' + (hsv.v * 100).toFixed(2) + '%, ' + aStr + ')'; 1649 | break; 1650 | default: 1651 | input.value = rgba; 1652 | break; 1653 | } 1654 | } 1655 | 1656 | if (inputColor) { 1657 | if (hsv.v < 0.5) { 1658 | input.style.color = "#FFF"; 1659 | } else { 1660 | input.style.color = "#000"; 1661 | } 1662 | 1663 | input.style.background = rgba; 1664 | } 1665 | } 1666 | 1667 | function initCanvas() { 1668 | if (!place) 1669 | return false; 1670 | if (place.tagName != 'CANVAS') { 1671 | canvas = document.createElement('CANVAS'); 1672 | place.appendChild(canvas); 1673 | } else { 1674 | canvas = place; 1675 | } 1676 | 1677 | // code for IE browsers 1678 | if (typeof window.G_vmlCanvasManager != 'undefined') { 1679 | canvas = window.G_vmlCanvasManager.initElement(canvas); 1680 | canvasHelper = window.G_vmlCanvasManager.initElement(canvasHelper); 1681 | } 1682 | 1683 | if (!!(canvas.getContext && canvas.getContext('2d'))) { 1684 | ctx = canvas.getContext("2d"); 1685 | canvasHelperCtx = canvasHelper.getContext("2d"); 1686 | return true; 1687 | } else 1688 | return false; 1689 | } 1690 | 1691 | // temp events until wait mouse click or touch 1692 | function enableEvents() { 1693 | addEventListner(canvas, "mousedown", function (e) { 1694 | handler.mouseDownEvent(e); 1695 | }, 'wait_action_'); 1696 | addEventListner(canvas, "touchstart", function (e) { 1697 | handler.mouseDownEvent(e); 1698 | }, 'wait_action_'); 1699 | addEventListner(canvas, "mouseout", function (e) { 1700 | handler.mouseOutEvent(e); 1701 | }, 'wait_action_'); 1702 | addEventListner(window, "touchmove", function (e) { 1703 | handler.touchMoveEvent(e); 1704 | }, 'wait_action_'); 1705 | addEventListner(canvas, "mousemove", function (e) { 1706 | handler.mouseMoveRest(e); 1707 | }, 'wait_action_'); 1708 | } 1709 | 1710 | // mouse detect canvas events 1711 | 1712 | function disableEvents() { 1713 | removeEventListener(canvas, "mousedown", 'wait_action_'); 1714 | removeEventListener(canvas, "touchstart", 'wait_action_'); 1715 | removeEventListener(canvas, "mouseout", 'wait_action_'); 1716 | removeEventListener(window, "touchmove", 'wait_action_'); 1717 | removeEventListener(canvas, "mousemove", 'wait_action_'); 1718 | } 1719 | 1720 | function getEventDot(e) { 1721 | 1722 | e = e || window.event; 1723 | var x, y; 1724 | var scrollX = document.body.scrollLeft + document.documentElement.scrollLeft; 1725 | var scrollY = document.body.scrollTop + document.documentElement.scrollTop; 1726 | 1727 | if (e.type == 'touchend') { 1728 | 1729 | x = e.changedTouches[0].clientX + scrollX; 1730 | y = e.changedTouches[0].clientY + scrollY; 1731 | 1732 | } else if (e.type == 'touchmove' || e.touches) { 1733 | 1734 | x = e.touches[0].clientX + scrollX; 1735 | y = e.touches[0].clientY + scrollY; 1736 | 1737 | } else { 1738 | // e.pageX e.pageY e.x e.y bad for cross-browser 1739 | x = e.clientX + scrollX; 1740 | y = e.clientY + scrollY; 1741 | } 1742 | 1743 | // set point to local coordinates 1744 | 1745 | var rect = canvas.getBoundingClientRect(); 1746 | x -= rect.left + scrollX; 1747 | y -= rect.top + scrollY; 1748 | 1749 | return {x: x, y: y}; 1750 | } 1751 | 1752 | function selectColorSaver(key) { 1753 | 1754 | // disable current selection 1755 | var previouseSelect = false; 1756 | for (var i = 0; i <= colorSavers.length - 1; ++i) 1757 | { 1758 | if (colorSavers[i].selected) 1759 | previouseSelect = i; 1760 | colorSavers[i].selected = false; 1761 | } 1762 | 1763 | // select new 1764 | var select = false; 1765 | for (var i = 0; i <= colorSavers.length - 1; ++i) 1766 | { 1767 | if (i == key) { 1768 | colorSavers[i].selected = true; 1769 | handler.setColorByHex(colorSavers[i].color); 1770 | select = true; 1771 | break; 1772 | } 1773 | } 1774 | 1775 | if (select && userEvents["selectcolorsaver"]) { 1776 | var callback = userEvents["selectcolorsaver"]; 1777 | callback(handler, colorSavers[key]); 1778 | } 1779 | 1780 | if (!select && previouseSelect !== false) { 1781 | colorSavers[previouseSelect].selected = true; 1782 | } 1783 | 1784 | return select; 1785 | } 1786 | 1787 | function updateColorSavers() { 1788 | 1789 | for (var i = 0; i <= colorSavers.length - 1; ++i) 1790 | { 1791 | if (colorSavers[i].selected) 1792 | colorSavers[i].color = hex; 1793 | } 1794 | 1795 | } 1796 | 1797 | function drawColorSavers() { 1798 | if (colorSavers.length) { 1799 | for (var i = 0; i <= colorSavers.length - 1; ++i) 1800 | { 1801 | colorSavers[i].draw(); 1802 | } 1803 | } 1804 | } 1805 | 1806 | // вывод интерфейса без курсоров 1807 | // поддерживается буферизация todo добавить буферизацию color saver элементов 1808 | // вынести буфер альфа слайдера отдельно от колеса и sv блока 1809 | 1810 | function drawColorPicker() { 1811 | if (!ctx) 1812 | return false; 1813 | 1814 | ctx.clearRect(0, 0, canvas.width, canvas.height); 1815 | 1816 | // put buffered data 1817 | if (rendered) { 1818 | ctx.putImageData(canvasHelperData, 0, 0); 1819 | drawColorSavers(); 1820 | return true; 1821 | } 1822 | 1823 | // форма кольца может измениться только при изменении размеров виджета 1824 | wheel.draw(); 1825 | svFig.draw(); 1826 | 1827 | if (alpha) 1828 | alphaSlider.draw(); 1829 | 1830 | drawColorSavers(); 1831 | if (styleSwitch) 1832 | styleSwitch.draw(); 1833 | 1834 | // поместить текущее отрисованное изображение кольца + sv селектора в буфер 1835 | // notice : 1836 | // при перемещении курсора кольца сохранять буфер все изображение бессмысленно - sv блок постоянно обновляется, поэтому 1837 | // сохраняем уже на событии выхода из процесса перемещения 1838 | 1839 | if (!drag) { 1840 | //wheelBlockSize 1841 | canvasHelperData = ctx.getImageData(0, 0, canvas.width, canvas.height); 1842 | rendered = true; 1843 | } 1844 | return true; 1845 | } 1846 | 1847 | function draw() { 1848 | if (!drawColorPicker()) { 1849 | return false; 1850 | } 1851 | 1852 | var curAngle = hsv.h * 360 - wheel.startAngle; 1853 | 1854 | // cursors 1855 | 1856 | if (alpha) { 1857 | ctx.beginPath(); 1858 | var cursorHeight = 2; 1859 | var cursorPaddingX = 2; 1860 | var pointY = alphaSlider.height * (1 - a); 1861 | ctx.rect(alphaSlider.pos.x - cursorPaddingX, alphaSlider.padding + pointY - cursorHeight / 2, alphaSlider.width + cursorPaddingX * 2, cursorHeight); 1862 | ctx.strokeStyle = 'rgba(0,0,0, 0.8)'; 1863 | ctx.lineWidth = 2; 1864 | 1865 | ctx.stroke(); 1866 | ctx.closePath(); 1867 | } 1868 | 1869 | ctx.beginPath(); 1870 | 1871 | var wheelCursorPath = rotatePath2(wheelCursor.path, curAngle, {x: wheel.pos.x, y: wheel.pos.y}); 1872 | for (var i = 0; i <= wheelCursorPath.length - 1; ++i) 1873 | { 1874 | wheelCursorPath[i].x += wheel.pos.x; 1875 | wheelCursorPath[i].y += wheel.pos.y; 1876 | if (i == 0) 1877 | ctx.moveTo(wheelCursorPath[i].x, wheelCursorPath[i].y); 1878 | else 1879 | ctx.lineTo(wheelCursorPath[i].x, wheelCursorPath[i].y); 1880 | } 1881 | 1882 | ctx.strokeStyle = 'rgba(0,0,0,0.8)'; 1883 | ctx.lineWidth = wheelCursor.lineWeight; 1884 | ctx.stroke(); 1885 | ctx.closePath(); 1886 | 1887 | // sv cursor 1888 | if (hsv.v > 0.5 && hsv.s < 0.5) 1889 | ctx.strokeStyle = 'rgba(0, 0, 0, 1)'; 1890 | else 1891 | ctx.strokeStyle = 'rgba(255, 255, 255, 1)'; 1892 | //ctx.strokeStyle='rgba(255,255, 255, 1)'; 1893 | 1894 | //document.getElementById('test3').value = 'h' + hsv.h.toFixed(2) + ' s' + hsv.s.toFixed(2) + ' v' + hsv.v.toFixed(2) 1895 | 1896 | ctx.beginPath(); 1897 | ctx.lineWidth = 2; 1898 | ctx.arc(hsv.x, hsv.y, svCursor.radius, 0, PI * 2); 1899 | 1900 | 1901 | ctx.stroke(); 1902 | ctx.closePath(); 1903 | 1904 | return false; 1905 | } 1906 | 1907 | this.popUpClose = function (e) { 1908 | 1909 | if (popup.tag === false) 1910 | return; 1911 | 1912 | if (e) { 1913 | // todo check when select color and then unpress button out of bounds 1914 | if (e.target == input || e.target == canvas) 1915 | return false; 1916 | if (e.target == popup.tag) 1917 | return false; 1918 | } 1919 | 1920 | if (userEvents["popupclose"] && !userEvents["popupclose"](handler, e)) { 1921 | return; 1922 | } 1923 | 1924 | popup.tag.style.display = 'none'; 1925 | 1926 | if (KellyColorPicker.activePopUp == handler) 1927 | KellyColorPicker.activePopUp = false; 1928 | } 1929 | 1930 | // if 'popupshow' user event is setted and return false - prevent show popup default behavior 1931 | 1932 | this.popUpShow = function (e) { 1933 | if (popup.tag === false) 1934 | return; 1935 | 1936 | if (userEvents["popupshow"] && !userEvents["popupshow"](handler, e)) { 1937 | return; 1938 | } 1939 | 1940 | // include once 1941 | if (!KellyColorPicker.popupEventsInclude) { 1942 | addEventListner(document, "click", function (e) { 1943 | if (KellyColorPicker.activePopUp) 1944 | return KellyColorPicker.activePopUp.popUpClose(e); 1945 | else 1946 | return false; 1947 | }, 'popup_close_'); 1948 | addEventListner(window, "resize", function (e) { 1949 | if (KellyColorPicker.activePopUp) 1950 | return KellyColorPicker.activePopUp.popUpShow(e); 1951 | }, 'popup_resize_'); 1952 | KellyColorPicker.popupEventsInclude = true; 1953 | } 1954 | 1955 | if (KellyColorPicker.activePopUp) { 1956 | KellyColorPicker.activePopUp.popUpClose(false); 1957 | } 1958 | 1959 | var topMargin = handler.getCanvas().width; 1960 | 1961 | var alpha = handler.getAlphaFig(); 1962 | if (alpha) { 1963 | topMargin -= alpha.width + alpha.padding; 1964 | } 1965 | 1966 | var popupStyle = window.getComputedStyle(popup.tag); 1967 | 1968 | var paddingPopup = parseInt(popupStyle.paddingBottom) + parseInt(popupStyle.paddingTop); 1969 | if (paddingPopup <= 0) { 1970 | paddingPopup = 0; 1971 | } 1972 | 1973 | var viewportOffset = input.getBoundingClientRect(); 1974 | var top = viewportOffset.top + (window.scrollY || window.pageYOffset || document.body.scrollTop) - paddingPopup; 1975 | var left = viewportOffset.left + (window.scrollX || window.pageXOffset || document.body.scrollLeft); 1976 | var padding = 6; 1977 | 1978 | popup.tag.style.top = (top - topMargin - popup.margin) + 'px'; 1979 | popup.tag.style.left = left + 'px'; 1980 | popup.tag.style.display = 'block'; 1981 | 1982 | KellyColorPicker.activePopUp = handler; 1983 | return false; 1984 | } 1985 | 1986 | this.setHueByDot = function (dot) { 1987 | var angle = getAngle(dot, wheel.pos) + wheel.startAngle; 1988 | if (angle < 0) 1989 | angle = 360 + angle; 1990 | 1991 | hsv.h = angle / 360; 1992 | 1993 | rgb = hsvToRgb(hsv.h, hsv.s, hsv.v); 1994 | hex = rgbToHex(rgb); 1995 | 1996 | updateColorSavers(); 1997 | 1998 | if (userEvents["change"]) { 1999 | var callback = userEvents["change"]; 2000 | callback(handler); 2001 | } 2002 | 2003 | updateInput(); 2004 | 2005 | rendered = false; 2006 | draw(); 2007 | }; 2008 | 2009 | this.setColorForColorSaver = function (cString, align) { 2010 | 2011 | var colorData = readColorData(cString, true); 2012 | if (!colorData) 2013 | return; 2014 | 2015 | var colorSaver = handler.getColorSaver(align); 2016 | if (colorSaver.selected) { 2017 | this.setColorByHex(cString, false); 2018 | } else { 2019 | colorSaver.color = colorData.h; 2020 | draw(); 2021 | } 2022 | 2023 | return true; 2024 | }; 2025 | 2026 | this.setColor = function(inputColor, manualEnter) { 2027 | 2028 | // synonym, since setColorByHex already accept color in different formats, not only in hex 2029 | 2030 | handler.setColorByHex(inputColor, manualEnter); 2031 | 2032 | } 2033 | 2034 | // update color with redraw canvas and update input hex value 2035 | // now support rgba \ rgb string format input 2036 | // and also hsla \ hsl 2037 | 2038 | this.setColorByHex = function (inputHex, manualEnter) { 2039 | 2040 | if (!manualEnter) 2041 | manualEnter = false; 2042 | var inputAlpha = a; 2043 | 2044 | if (inputHex !== false) { 2045 | 2046 | if (!inputHex || !inputHex.length) 2047 | return; 2048 | 2049 | var colorData = readColorData(inputHex, true); 2050 | if (!colorData) 2051 | return; 2052 | 2053 | inputHex = colorData.h; 2054 | if (alpha) 2055 | inputAlpha = colorData.a; 2056 | 2057 | } else 2058 | inputHex = hex; 2059 | 2060 | if (alpha && inputHex == hex && rendered && inputAlpha != a) { 2061 | a = inputAlpha; 2062 | 2063 | draw(); // slider always redraws in current even if part of canvas buffered 2064 | return; 2065 | } 2066 | 2067 | if (hex && inputHex == hex && rendered) 2068 | return; 2069 | 2070 | // set and redraw all 2071 | 2072 | a = inputAlpha; 2073 | rgb = hexToRgb(inputHex); 2074 | hex = inputHex; 2075 | hsv = rgbToHsv(rgb); 2076 | 2077 | var dot = svFig.svToDot(hsv); 2078 | hsv.x = dot.x; 2079 | hsv.y = dot.y; 2080 | 2081 | rendered = false; 2082 | updateColorSavers(); 2083 | draw(); 2084 | 2085 | if (userEvents["change"]) { 2086 | var callback = userEvents["change"]; 2087 | callback(handler); 2088 | } 2089 | 2090 | updateInput(manualEnter); 2091 | }; 2092 | 2093 | this.setAlphaByDot = function (dot) { 2094 | a = alphaSlider.dotToAlpha(dot); 2095 | 2096 | if (userEvents["change"]) { 2097 | var callback = userEvents["change"]; 2098 | callback(handler); 2099 | } 2100 | 2101 | updateInput(); 2102 | draw(); 2103 | }; 2104 | 2105 | this.setAlpha = function (alpha) { 2106 | a = alpha; 2107 | updateInput(); 2108 | draw(); 2109 | }; 2110 | 2111 | this.setColorByDot = function (dot) { 2112 | var sv = svFig.dotToSv(dot); 2113 | 2114 | hsv.s = sv.s; 2115 | hsv.v = sv.v; 2116 | hsv.x = dot.x; 2117 | hsv.y = dot.y; 2118 | 2119 | if (hsv.s > 1) 2120 | hsv.s = 1; 2121 | if (hsv.s < 0) 2122 | hsv.s = 0; 2123 | if (hsv.v > 1) 2124 | hsv.v = 1; 2125 | if (hsv.v < 0) 2126 | hsv.v = 0; 2127 | 2128 | rgb = hsvToRgb(hsv.h, hsv.s, hsv.v); 2129 | hex = rgbToHex(rgb); 2130 | 2131 | updateColorSavers(); 2132 | 2133 | if (userEvents["change"]) { 2134 | var callback = userEvents["change"]; 2135 | callback(handler); 2136 | } 2137 | 2138 | updateInput(); 2139 | draw(); 2140 | }; 2141 | 2142 | this.mouseOutEvent = function (e) { 2143 | if (svCursorMouse.curType > 0 && !KellyColorPicker.cursorLock) { 2144 | svCursorMouse.initStandartCursor(); 2145 | } 2146 | }; 2147 | 2148 | // перемещение указателя по canvas в режиме покоя 2149 | this.mouseMoveRest = function (e) { 2150 | if (drag) 2151 | return; 2152 | 2153 | if (!cursorAnimReady) { 2154 | return; 2155 | } 2156 | 2157 | cursorAnimReady = false; 2158 | var newDot = getEventDot(e); 2159 | svCursorMouse.updateCursor(newDot); 2160 | requestAnimationFrame(function () { 2161 | cursorAnimReady = true; 2162 | }); 2163 | 2164 | if (userEvents["mousemoverest"]) { 2165 | var callback = userEvents["mousemoverest"]; 2166 | callback(e, handler, newDot); 2167 | } 2168 | }; 2169 | 2170 | // to prevent scroll by touches while change color 2171 | // в FireFox под андройд есть "фича" которая скрывает или раскрывает тулбар адресной строки при движении пальцем 2172 | // отключить её можно только через опцию about:config browser.chrome.dynamictoolbar 2173 | 2174 | this.touchMoveEvent = function (e) { 2175 | if (drag) { // todo check number of touches to ignore zoom action 2176 | event.preventDefault(); 2177 | } 2178 | }; 2179 | 2180 | // маршрутизатор событий нажатий на элементы 2181 | this.mouseDownEvent = function (event) { 2182 | event.preventDefault(); 2183 | 2184 | var move, up = false; 2185 | var newDot = getEventDot(event); 2186 | // console.log('mouseDownEvent : cur : ' + newDot.x + ' | ' + newDot.y); 2187 | 2188 | if (wheel.isDotIn(newDot)) { 2189 | drag = 'wheel'; 2190 | handler.setHueByDot(newDot); 2191 | 2192 | move = function (e) { 2193 | handler.wheelMouseMove(e, newDot); 2194 | }; 2195 | up = function (e) { 2196 | KellyColorPicker.cursorLock = false; 2197 | handler.wheelMouseUp(e, newDot); 2198 | }; 2199 | 2200 | } else if (svFig.isDotIn(newDot)) { 2201 | drag = 'sv'; 2202 | handler.setColorByDot(newDot); 2203 | 2204 | move = function (e) { 2205 | handler.svMouseMove(e, newDot); 2206 | }; 2207 | up = function (e) { 2208 | KellyColorPicker.cursorLock = false; 2209 | handler.svMouseUp(e, newDot); 2210 | }; 2211 | } else if (alpha && alphaSlider.isDotIn(newDot)) { 2212 | drag = 'alpha'; 2213 | handler.setAlphaByDot(newDot); 2214 | 2215 | move = function (e) { 2216 | handler.alphaMouseMove(e, newDot); 2217 | }; 2218 | up = function (e) { 2219 | KellyColorPicker.cursorLock = false; 2220 | handler.alphaMouseUp(e, newDot); 2221 | }; 2222 | } else if (styleSwitch && styleSwitch.isDotIn(newDot)) { 2223 | handler.setMethod(); 2224 | } else if (colorSavers.length) { // here all items with post check of dot in 2225 | 2226 | for (var i = 0; i <= colorSavers.length - 1; ++i) 2227 | { 2228 | if (colorSavers[i].isDotIn(newDot)) { 2229 | selectColorSaver(i); 2230 | break; 2231 | } 2232 | } 2233 | } 2234 | 2235 | if (move && up) { 2236 | disableEvents(); 2237 | KellyColorPicker.cursorLock = handler; 2238 | addEventListner(document, "mouseup", up, 'action_process_'); 2239 | addEventListner(document, "mousemove", move, 'action_process_'); 2240 | addEventListner(document, "touchend", up, 'action_process_'); 2241 | addEventListner(document, "touchmove", move, 'action_process_'); 2242 | } 2243 | }; 2244 | 2245 | this.wheelMouseMove = function (event, dot) { 2246 | event.preventDefault(); 2247 | 2248 | if (!drag) 2249 | return; 2250 | 2251 | if (!cursorAnimReady) { 2252 | return; 2253 | } 2254 | cursorAnimReady = false; 2255 | var newDot = getEventDot(event); 2256 | 2257 | // console.log('wheelMouseMove : start : ' + dot.x + ' | ' + dot.y + ' cur : ' + newDot.x + ' | ' + newDot.y); 2258 | requestAnimationFrame(function () { 2259 | cursorAnimReady = true; 2260 | }); 2261 | //setTimeout(function() {cursorAnimReady = true;}, 1000/30); 2262 | 2263 | handler.setHueByDot(newDot); 2264 | 2265 | if (userEvents["mousemoveh"]) { 2266 | var callback = userEvents["mousemoveh"]; 2267 | callback(event, handler, newDot); 2268 | } 2269 | }; 2270 | 2271 | this.wheelMouseUp = function (event, dot) { 2272 | event.preventDefault(); 2273 | if (!drag) 2274 | return; 2275 | //console.log('wheelMouseUp : start : ' + dot.x + ' | ' + dot.y); 2276 | 2277 | removeEventListener(document, "mouseup", 'action_process_'); 2278 | removeEventListener(document, "mousemove", 'action_process_'); 2279 | removeEventListener(document, "touchend", 'action_process_'); 2280 | removeEventListener(document, "touchmove", 'action_process_'); 2281 | 2282 | enableEvents(); 2283 | drag = false; 2284 | 2285 | rendered = false; 2286 | draw(); 2287 | 2288 | var newDot = getEventDot(event); 2289 | svCursorMouse.updateCursor(newDot); 2290 | 2291 | if (userEvents["mouseuph"]) { 2292 | var callback = userEvents["mouseuph"]; 2293 | callback(event, handler, newDot); 2294 | } 2295 | }; 2296 | 2297 | this.alphaMouseMove = function (event, dot) { 2298 | event.preventDefault(); 2299 | if (!drag) 2300 | return; 2301 | 2302 | if (!cursorAnimReady) { 2303 | return; 2304 | } 2305 | 2306 | cursorAnimReady = false; 2307 | var newDot = getEventDot(event); 2308 | 2309 | // console.log('svMouseMove : start : ' + dot.x + ' | ' + dot.y + ' cur : ' + newDot.x + ' | ' + newDot.y); 2310 | 2311 | newDot = alphaSlider.limitDotPosition(newDot); 2312 | 2313 | requestAnimationFrame(function () { 2314 | cursorAnimReady = true; 2315 | }); 2316 | //setTimeout(function() {cursorAnimReady = true;}, 1000/30); 2317 | 2318 | handler.setAlphaByDot(newDot); 2319 | 2320 | if (userEvents["mousemovealpha"]) { 2321 | var callback = userEvents["mousemovealpha"]; 2322 | callback(event, handler, newDot); 2323 | } 2324 | }; 2325 | 2326 | this.alphaMouseUp = function (event, dot) { 2327 | event.preventDefault(); 2328 | if (!drag) 2329 | return; 2330 | 2331 | removeEventListener(document, "mouseup", 'action_process_'); 2332 | removeEventListener(document, "mousemove", 'action_process_'); 2333 | removeEventListener(document, "touchend", 'action_process_'); 2334 | removeEventListener(document, "touchmove", 'action_process_'); 2335 | 2336 | enableEvents(); 2337 | drag = false; 2338 | 2339 | var newDot = getEventDot(event); 2340 | svCursorMouse.updateCursor(newDot); 2341 | 2342 | if (userEvents["mouseupalpha"]) { 2343 | var callback = userEvents["mouseupalpha"]; 2344 | callback(event, handler, newDot); 2345 | } 2346 | }; 2347 | 2348 | this.svMouseMove = function (event, dot) { 2349 | event.preventDefault(); 2350 | if (!drag) 2351 | return; 2352 | 2353 | if (!cursorAnimReady) { 2354 | return; 2355 | } 2356 | 2357 | cursorAnimReady = false; 2358 | var newDot = getEventDot(event); 2359 | 2360 | // console.log('svMouseMove : start : ' + dot.x + ' | ' + dot.y + ' cur : ' + newDot.x + ' | ' + newDot.y); 2361 | 2362 | newDot = svFig.limitDotPosition(newDot); 2363 | 2364 | requestAnimationFrame(function () { 2365 | cursorAnimReady = true; 2366 | }); 2367 | //setTimeout(function() {cursorAnimReady = true;}, 1000/30); 2368 | 2369 | handler.setColorByDot(newDot); 2370 | 2371 | if (userEvents["mousemovesv"]) { 2372 | var callback = userEvents["mousemovesv"]; 2373 | callback(event, handler, newDot); 2374 | } 2375 | }; 2376 | 2377 | this.svMouseUp = function (event, dot) { 2378 | event.preventDefault(); 2379 | if (!drag) 2380 | return; 2381 | 2382 | // console.log('svMouseUp : start : ' + dot.x + ' | ' + dot.y); 2383 | 2384 | removeEventListener(document, "mouseup", 'action_process_'); 2385 | removeEventListener(document, "mousemove", 'action_process_'); 2386 | removeEventListener(document, "touchend", 'action_process_'); 2387 | removeEventListener(document, "touchmove", 'action_process_'); 2388 | 2389 | enableEvents(); 2390 | drag = false; 2391 | 2392 | var newDot = getEventDot(event); 2393 | svCursorMouse.updateCursor(newDot); 2394 | 2395 | // todo 2396 | // split cached data for sv + h wheel and slider, so we can redraw alpha slider without performanse lost in svMouseMove 2397 | 2398 | if (alpha) { 2399 | rendered = false; 2400 | draw(); 2401 | } 2402 | 2403 | if (userEvents["mouseupsv"]) { 2404 | var callback = userEvents["mouseupsv"]; 2405 | callback(event, handler, newDot); 2406 | } 2407 | }; 2408 | 2409 | this.addUserEvent = function (event, callback) { 2410 | userEvents[event] = callback; 2411 | return true; 2412 | }; 2413 | 2414 | this.removeUserEvent = function (event) { 2415 | if (!userEvents[event]) 2416 | return false; 2417 | userEvents[event] = null; 2418 | return true; 2419 | }; 2420 | 2421 | // для кастомизации отображения элементов виджета 2422 | 2423 | this.getCanvas = function () { 2424 | if (!ctx) 2425 | return false; 2426 | return canvas; 2427 | }; 2428 | 2429 | this.getCtx = function () { 2430 | if (!ctx) 2431 | return false; 2432 | return ctx; 2433 | }; 2434 | 2435 | this.getInput = function () { 2436 | return input; 2437 | }; 2438 | 2439 | this.getSvFig = function () { 2440 | return svFig; 2441 | }; 2442 | 2443 | this.getSvFigCursor = function () { 2444 | return svCursor; 2445 | }; 2446 | 2447 | this.getWheel = function () { 2448 | return wheel; 2449 | }; 2450 | 2451 | this.getWheelCursor = function () { 2452 | return wheelCursor; 2453 | }; 2454 | 2455 | this.getCurColorHsv = function () { 2456 | return hsv; 2457 | }; 2458 | 2459 | this.getCurColorRgb = function () { 2460 | return rgb; 2461 | }; 2462 | 2463 | this.getCurColorHex = function () { 2464 | return hex; 2465 | }; 2466 | 2467 | this.getCurColorRgba = function () { 2468 | return {r: rgb.r, g: rgb.g, b: rgb.b, a: a}; 2469 | }; 2470 | 2471 | this.getCurAlpha = function () { 2472 | return a; 2473 | }; 2474 | 2475 | this.getAlphaFig = function () { 2476 | if (alpha) 2477 | return alphaSlider; 2478 | else 2479 | return false; 2480 | } 2481 | 2482 | this.getPopup = function () { 2483 | return popup; 2484 | }; 2485 | 2486 | this.getSize = function () { 2487 | return wheelBlockSize; 2488 | }; 2489 | 2490 | // if align not setted get selected 2491 | this.getColorSaver = function (align) { 2492 | for (var i = 0; i <= colorSavers.length - 1; ++i) 2493 | { 2494 | if ((!align && colorSavers[i].selected) || colorSavers[i].align == align) { 2495 | colorSavers[i].rgb = hexToRgb(colorSavers[i].color); 2496 | colorSavers[i].hsv = rgbToHsv(colorSavers[i].rgb.r, colorSavers[i].rgb.g, colorSavers[i].rgb.b); 2497 | return colorSavers[i]; 2498 | } 2499 | } 2500 | }; 2501 | 2502 | this.setColorSaver = function (align) { 2503 | 2504 | if (!align) 2505 | return false; 2506 | 2507 | for (var i = 0; i <= colorSavers.length - 1; ++i) 2508 | { 2509 | if (colorSavers[i].align == align) { 2510 | selectColorSaver(i); 2511 | return colorSavers[i]; 2512 | } 2513 | } 2514 | } 2515 | 2516 | this.updateView = function (dropBuffer) { 2517 | if (!ctx) 2518 | return false; 2519 | 2520 | if (dropBuffer) { 2521 | wheel.imageData = null; 2522 | svFig.imageData = null; 2523 | canvasHelperData = null; 2524 | } 2525 | 2526 | rendered = false; 2527 | updateSize(); 2528 | draw(); 2529 | return true; 2530 | }; 2531 | 2532 | // resize canvas, with all data \ full refresh view 2533 | // if size same as current and refresh variable setted to true - refresh current view anyway 2534 | // othervise exit with return true 2535 | 2536 | this.resize = function (size, refresh) { 2537 | if (!ctx) 2538 | return false; 2539 | if (size == wheelBlockSize && !refresh) 2540 | return true; 2541 | 2542 | rendered = false; 2543 | wheel.imageData = null; 2544 | svFig.imageData = null; 2545 | canvasHelperData = null; 2546 | wheelBlockSize = size; 2547 | updateSize(); 2548 | 2549 | handler.setColorByHex(false); 2550 | return false; 2551 | }; 2552 | 2553 | this.syncSize = function (e) { 2554 | 2555 | if (!resizeWith) 2556 | return false; 2557 | 2558 | var newSize = getSizeByElement(resizeWith); 2559 | if (newSize) 2560 | handler.resize(newSize); 2561 | return false; 2562 | } 2563 | 2564 | this.setMethod = function (newMethod) { 2565 | if (!newMethod) { 2566 | newMethod = 'triangle'; 2567 | if (method == 'triangle') 2568 | newMethod = 'quad'; 2569 | } 2570 | 2571 | if (newMethod == method) 2572 | return false; 2573 | if (method != 'quad' && method != 'triangle') 2574 | return false; 2575 | 2576 | method = newMethod; 2577 | 2578 | if (method == 'quad') 2579 | svFig = getSvFigureQuad(); 2580 | if (method == 'triangle') 2581 | svFig = getSvFigureTriangle(); 2582 | 2583 | handler.resize(wheelBlockSize, true); 2584 | 2585 | if (userEvents["setmethod"]) { 2586 | var callback = userEvents["setmethod"]; 2587 | callback(handler, method); 2588 | } 2589 | 2590 | return true; 2591 | } 2592 | 2593 | // restore color of input ? 2594 | 2595 | this.destroy = function () { 2596 | if (!handler) { 2597 | return false; 2598 | } 2599 | 2600 | if (svCursorMouse.curType > 0) { 2601 | KellyColorPicker.cursorLock = false; 2602 | svCursorMouse.initStandartCursor(); 2603 | } 2604 | 2605 | if (drag) { 2606 | removeEventListener(document, "mouseup", 'action_process_'); 2607 | removeEventListener(document, "mousemove", 'action_process_'); 2608 | removeEventListener(document, "touchend", 'action_process_'); 2609 | removeEventListener(document, "touchmove", 'action_process_'); 2610 | 2611 | drag = false; 2612 | } 2613 | 2614 | if (popup.tag) { 2615 | removeEventListener(input, "click", "popup_"); 2616 | } 2617 | 2618 | if (input) { 2619 | removeEventListener(input, "click", 'input_edit_'); 2620 | removeEventListener(input, "change", 'input_edit_'); 2621 | removeEventListener(input, "keyup", 'input_edit_'); 2622 | removeEventListener(input, "keypress", 'input_edit_'); 2623 | } 2624 | 2625 | // remove popup close and resize events if this picker include them erlier 2626 | if (KellyColorPicker.popupEventsInclude && events['popup_close_click']) { 2627 | if (KellyColorPicker.activePopUp) 2628 | KellyColorPicker.activePopUp.popUpClose(false); 2629 | 2630 | removeEventListener(document, "click", 'popup_close_'); 2631 | removeEventListener(window, "resize", 'popup_resize_'); 2632 | 2633 | KellyColorPicker.popupEventsInclude = false; 2634 | } 2635 | 2636 | wheel.imageData = null; 2637 | svFig.imageData = null; 2638 | canvasHelperData = null; 2639 | canvasHelper = null; 2640 | 2641 | if (place && place.parentNode) { 2642 | place.parentNode.removeChild(place); 2643 | } 2644 | 2645 | if (resizeWith) { 2646 | removeEventListener(window, "resize", 'canvas_'); 2647 | } 2648 | 2649 | disableEvents(); // remove canvas events 2650 | 2651 | // debug test for check is all events removed 2652 | // for (var key in events) { 2653 | // console.log('key : ' + key + ' data ' + events[key]); 2654 | // } 2655 | 2656 | handler = null; 2657 | }; 2658 | 2659 | constructor(cfg); 2660 | } 2661 | 2662 | /* static methods */ 2663 | 2664 | /** 2665 | * Тригер для объектов KellyColorPicker, чтобы не сбрасывали стиль курсора при наведении если уже идет выбор цвета 2666 | * Notice : при выходе курсора за границы текущего canvas, событие неизвестного объекта всегда может сбросить изображение курсора 2667 | */ 2668 | 2669 | KellyColorPicker.cursorLock = false; // можно указывать handler объекта 2670 | KellyColorPicker.activePopUp = false; 2671 | KellyColorPicker.popupEventsInclude = false; // include events for document and window once for all elements 2672 | 2673 | KellyColorPicker.attachToInputByClass = function (className, cfg) { 2674 | 2675 | var colorPickers = new Array(); 2676 | var inputs = document.getElementsByClassName(className); 2677 | 2678 | 2679 | for (var i = 0; i < inputs.length; i++) { 2680 | 2681 | if (cfg) 2682 | cfg.input = inputs[i]; 2683 | else 2684 | cfg = {input: inputs[i], size: 150}; 2685 | 2686 | colorPickers.push(new KellyColorPicker(cfg)); 2687 | } 2688 | 2689 | return colorPickers; 2690 | }; 2691 | 2692 | // KellyColorPicker.dragTrigger = false; 2693 | --------------------------------------------------------------------------------