Administrator
2023-04-21 195945efc5db921a4c9eb8cf9421c172273293f5
提交 | 用户 | 时间
58d006 1 /* =========================================================
A 2  * bootstrap-colorpicker.js 
3  * http://www.eyecon.ro/bootstrap-colorpicker
4  * =========================================================
5  * Copyright 2012 Stefan Petre
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================================================= */
19  
20 !function( $ ) {
21     
22     // Color object
23     
24     var Color = function(val) {
25         this.value = {
26             h: 1,
27             s: 1,
28             b: 1,
29             a: 1
30         };
31         this.setColor(val);
32     };
33     
34     Color.prototype = {
35         constructor: Color,
36         
37         //parse a string to HSB
38         setColor: function(val){
39             val = val.toLowerCase();
40             var that = this;
41             $.each( CPGlobal.stringParsers, function( i, parser ) {
42                 var match = parser.re.exec( val ),
43                     values = match && parser.parse( match ),
44                     space = parser.space||'rgba';
45                 if ( values ) {
46                     if (space === 'hsla') {
47                         that.value = CPGlobal.RGBtoHSB.apply(null, CPGlobal.HSLtoRGB.apply(null, values));
48                     } else {
49                         that.value = CPGlobal.RGBtoHSB.apply(null, values);
50                     }
51                     return false;
52                 }
53             });
54         },
55         
56         setHue: function(h) {
57             this.value.h = 1- h;
58         },
59         
60         setSaturation: function(s) {
61             this.value.s = s;
62         },
63         
64         setLightness: function(b) {
65             this.value.b = 1- b;
66         },
67         
68         setAlpha: function(a) {
69             this.value.a = parseInt((1 - a)*100, 10)/100;
70         },
71         
72         // HSBtoRGB from RaphaelJS
73         // https://github.com/DmitryBaranovskiy/raphael/
74         toRGB: function(h, s, b, a) {
75             if (!h) {
76                 h = this.value.h;
77                 s = this.value.s;
78                 b = this.value.b;
79             }
80             h *= 360;
81             var R, G, B, X, C;
82             h = (h % 360) / 60;
83             C = b * s;
84             X = C * (1 - Math.abs(h % 2 - 1));
85             R = G = B = b - C;
86
87             h = ~~h;
88             R += [C, X, 0, 0, X, C][h];
89             G += [X, C, C, X, 0, 0][h];
90             B += [0, 0, X, C, C, X][h];
91             return {
92                 r: Math.round(R*255),
93                 g: Math.round(G*255),
94                 b: Math.round(B*255),
95                 a: a||this.value.a
96             };
97         },
98         
99         toHex: function(h, s, b, a){
100             var rgb = this.toRGB(h, s, b, a);
101             return '#'+((1 << 24) | (parseInt(rgb.r) << 16) | (parseInt(rgb.g) << 8) | parseInt(rgb.b)).toString(16).substr(1);
102         },
103         
104         toHSL: function(h, s, b, a){
105             if (!h) {
106                 h = this.value.h;
107                 s = this.value.s;
108                 b = this.value.b;
109             }
110             var H = h,
111                 L = (2 - s) * b,
112                 S = s * b;
113             if (L > 0 && L <= 1) {
114                 S /= L;
115             } else {
116                 S /= 2 - L;
117             }
118             L /= 2;
119             if (S > 1) {
120                 S = 1;
121             }
122             return {
123                 h: H,
124                 s: S,
125                 l: L,
126                 a: a||this.value.a
127             };
128         }
129     };
130     
131     // Picker object
132     
133     var Colorpicker = function(element, options){
134         this.element = $(element);
135         var format = options.format||this.element.data('color-format')||'hex';
136         this.format = CPGlobal.translateFormats[format];
137         this.isInput = this.element.is('input');
138         this.component = this.element.is('.color') ? this.element.find('.add-on') : false;
139         
140         this.picker = $(CPGlobal.template)
141                             .appendTo('body')
142                             .on('mousedown', $.proxy(this.mousedown, this));
143         
144         if (this.isInput) {
145             this.element.on({
146                 'focus': $.proxy(this.show, this),
147                 'keyup': $.proxy(this.update, this)
148             });
149         } else if (this.component){
150             this.component.on({
151                 'click': $.proxy(this.show, this)
152             });
153         } else {
154             this.element.on({
155                 'click': $.proxy(this.show, this)
156             });
157         }
158         if (format === 'rgba' || format === 'hsla') {
159             this.picker.addClass('alpha');
160             this.alpha = this.picker.find('.colorpicker-alpha')[0].style;
161         }
162         
163         if (this.component){
164             this.picker.find('.colorpicker-color').hide();
165             this.preview = this.element.find('i')[0].style;
166         } else {
167             this.preview = this.picker.find('div:last')[0].style;
168         }
169         
170         this.base = this.picker.find('div:first')[0].style;
171         this.update();
172     };
173     
174     Colorpicker.prototype = {
175         constructor: Colorpicker,
176         
177         show: function(e) {
178             this.picker.show();
179             this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
180             this.place();
181             $(window).on('resize', $.proxy(this.place, this));
182             if (!this.isInput) {
183                 if (e) {
184                     e.stopPropagation();
185                     e.preventDefault();
186                 }
187             }
188             $(document).on({
189                 'mousedown': $.proxy(this.hide, this)
190             });
191             this.element.trigger({
192                 type: 'show',
193                 color: this.color
194             });
195         },
196         
197         update: function(){
198             this.color = new Color(this.isInput ? this.element.prop('value') : this.element.data('color'));
199             this.picker.find('i')
200                 .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end()
201                 .eq(1).css('top', 100 * (1 - this.color.value.h)).end()
202                 .eq(2).css('top', 100 * (1 - this.color.value.a));
203             this.previewColor();
204         },
205         
206         setValue: function(newColor) {
207             this.color = new Color(newColor);
208             this.picker.find('i')
209                 .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end()
210                 .eq(1).css('top', 100 * (1 - this.color.value.h)).end()
211                 .eq(2).css('top', 100 * (1 - this.color.value.a));
212             this.previewColor();
213             this.element.trigger({
214                 type: 'changeColor',
215                 color: this.color
216             });
217         },
218         
219         hide: function(){
220             this.picker.hide();
221             $(window).off('resize', this.place);
222             if (!this.isInput) {
223                 $(document).off({
224                     'mousedown': this.hide
225                 });
226                 if (this.component){
227                     this.element.find('input').prop('value', this.format.call(this));
228                 }
229                 this.element.data('color', this.format.call(this));
230             } else {
231                 this.element.prop('value', this.format.call(this));
232             }
233             this.element.trigger({
234                 type: 'hide',
235                 color: this.color
236             });
237         },
238         
239         place: function(){
240             var offset = this.component ? this.component.offset() : this.element.offset();
241             this.picker.css({
242                 top: offset.top + this.height,
243                 left: offset.left
244             });
245         },
246         
247         //preview color change
248         previewColor: function(){
249             try {
250                 this.preview.backgroundColor = this.format.call(this);
251             } catch(e) {
252                 this.preview.backgroundColor = this.color.toHex();
253             }
254             //set the color for brightness/saturation slider
255             this.base.backgroundColor = this.color.toHex(this.color.value.h, 1, 1, 1);
256             //set te color for alpha slider
257             if (this.alpha) {
258                 this.alpha.backgroundColor = this.color.toHex();
259             }
260         },
261         
262         pointer: null,
263         
264         slider: null,
265         
266         mousedown: function(e){
267             e.stopPropagation();
268             e.preventDefault();
269             
270             var target = $(e.target);
271             
272             //detect the slider and set the limits and callbacks
273             var zone = target.closest('div');
274             if (!zone.is('.colorpicker')) {
275                 if (zone.is('.colorpicker-saturation')) {
276                     this.slider = $.extend({}, CPGlobal.sliders.saturation);
277                 } 
278                 else if (zone.is('.colorpicker-hue')) {
279                     this.slider = $.extend({}, CPGlobal.sliders.hue);
280                 }
281                 else if (zone.is('.colorpicker-alpha')) {
282                     this.slider = $.extend({}, CPGlobal.sliders.alpha);
283                 } else {
284                     return false;
285                 }
286                 var offset = zone.offset();
287                 //reference to knob's style
288                 this.slider.knob = zone.find('i')[0].style;
289                 this.slider.left = e.pageX - offset.left;
290                 this.slider.top = e.pageY - offset.top;
291                 this.pointer = {
292                     left: e.pageX,
293                     top: e.pageY
294                 };
295                 //trigger mousemove to move the knob to the current position
296                 $(document).on({
297                     mousemove: $.proxy(this.mousemove, this),
298                     mouseup: $.proxy(this.mouseup, this)
299                 }).trigger('mousemove');
300             }
301             return false;
302         },
303         
304         mousemove: function(e){
305             e.stopPropagation();
306             e.preventDefault();
307             var left = Math.max(
308                 0,
309                 Math.min(
310                     this.slider.maxLeft,
311                     this.slider.left + ((e.pageX||this.pointer.left) - this.pointer.left)
312                 )
313             );
314             var top = Math.max(
315                 0,
316                 Math.min(
317                     this.slider.maxTop,
318                     this.slider.top + ((e.pageY||this.pointer.top) - this.pointer.top)
319                 )
320             );
321             this.slider.knob.left = left + 'px';
322             this.slider.knob.top = top + 'px';
323             if (this.slider.callLeft) {
324                 this.color[this.slider.callLeft].call(this.color, left/100);
325             }
326             if (this.slider.callTop) {
327                 this.color[this.slider.callTop].call(this.color, top/100);
328             }
329             this.previewColor();
330             this.element.trigger({
331                 type: 'changeColor',
332                 color: this.color
333             });
334             return false;
335         },
336         
337         mouseup: function(e){
338             e.stopPropagation();
339             e.preventDefault();
340             $(document).off({
341                 mousemove: this.mousemove,
342                 mouseup: this.mouseup
343             });
344             return false;
345         }
346     }
347
348     $.fn.colorpicker = function ( option, val ) {
349         return this.each(function () {
350             var $this = $(this),
351                 data = $this.data('colorpicker'),
352                 options = typeof option === 'object' && option;
353             if (!data) {
354                 $this.data('colorpicker', (data = new Colorpicker(this, $.extend({}, $.fn.colorpicker.defaults,options))));
355             }
356             if (typeof option === 'string') data[option](val);
357         });
358     };
359
360     $.fn.colorpicker.defaults = {
361     };
362     
363     $.fn.colorpicker.Constructor = Colorpicker;
364     
365     var CPGlobal = {
366     
367         // translate a format from Color object to a string
368         translateFormats: {
369             'rgb': function(){
370                 var rgb = this.color.toRGB();
371                 return 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';
372             },
373             
374             'rgba': function(){
375                 var rgb = this.color.toRGB();
376                 return 'rgba('+rgb.r+','+rgb.g+','+rgb.b+','+rgb.a+')';
377             },
378             
379             'hsl': function(){
380                 var hsl = this.color.toHSL();
381                 return 'hsl('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%)';
382             },
383             
384             'hsla': function(){
385                 var hsl = this.color.toHSL();
386                 return 'hsla('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%,'+hsl.a+')';
387             },
388             
389             'hex': function(){
390                 return  this.color.toHex();
391             }
392         },
393         
394         sliders: {
395             saturation: {
396                 maxLeft: 100,
397                 maxTop: 100,
398                 callLeft: 'setSaturation',
399                 callTop: 'setLightness'
400             },
401             
402             hue: {
403                 maxLeft: 0,
404                 maxTop: 100,
405                 callLeft: false,
406                 callTop: 'setHue'
407             },
408             
409             alpha: {
410                 maxLeft: 0,
411                 maxTop: 100,
412                 callLeft: false,
413                 callTop: 'setAlpha'
414             }
415         },
416         
417         // HSBtoRGB from RaphaelJS
418         // https://github.com/DmitryBaranovskiy/raphael/
419         RGBtoHSB: function (r, g, b, a){
420             r /= 255;
421             g /= 255;
422             b /= 255;
423
424             var H, S, V, C;
425             V = Math.max(r, g, b);
426             C = V - Math.min(r, g, b);
427             H = (C === 0 ? null :
428                 V == r ? (g - b) / C :
429                 V == g ? (b - r) / C + 2 :
430                     (r - g) / C + 4
431                 );
432             H = ((H + 360) % 6) * 60 / 360;
433             S = C === 0 ? 0 : C / V;
434             return {h: H||1, s: S, b: V, a: a||1};
435         },
436         
437         HueToRGB: function (p, q, h) {
438             if (h < 0)
439                 h += 1;
440             else if (h > 1)
441                 h -= 1;
442
443             if ((h * 6) < 1)
444                 return p + (q - p) * h * 6;
445             else if ((h * 2) < 1)
446                 return q;
447             else if ((h * 3) < 2)
448                 return p + (q - p) * ((2 / 3) - h) * 6;
449             else
450                 return p;
451         },
452     
453         HSLtoRGB: function (h, s, l, a)
454         {
455             if (s < 0) {
456                 s = 0;
457             }
458             var q;
459             if (l <= 0.5) {
460                 q = l * (1 + s);
461             } else {
462                 q = l + s - (l * s);
463             }
464             
465             var p = 2 * l - q;
466
467             var tr = h + (1 / 3);
468             var tg = h;
469             var tb = h - (1 / 3);
470
471             var r = Math.round(CPGlobal.HueToRGB(p, q, tr) * 255);
472             var g = Math.round(CPGlobal.HueToRGB(p, q, tg) * 255);
473             var b = Math.round(CPGlobal.HueToRGB(p, q, tb) * 255);
474             return [r, g, b, a||1];
475         },
476         
477         // a set of RE's that can match strings and generate color tuples.
478         // from John Resig color plugin
479         // https://github.com/jquery/jquery-color/
480         stringParsers: [
481             {
482                 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
483                 parse: function( execResult ) {
484                     return [
485                         execResult[ 1 ],
486                         execResult[ 2 ],
487                         execResult[ 3 ],
488                         execResult[ 4 ]
489                     ];
490                 }
491             }, {
492                 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
493                 parse: function( execResult ) {
494                     return [
495                         2.55 * execResult[1],
496                         2.55 * execResult[2],
497                         2.55 * execResult[3],
498                         execResult[ 4 ]
499                     ];
500                 }
501             }, {
502                 re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
503                 parse: function( execResult ) {
504                     return [
505                         parseInt( execResult[ 1 ], 16 ),
506                         parseInt( execResult[ 2 ], 16 ),
507                         parseInt( execResult[ 3 ], 16 )
508                     ];
509                 }
510             }, {
511                 re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
512                 parse: function( execResult ) {
513                     return [
514                         parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
515                         parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
516                         parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
517                     ];
518                 }
519             }, {
520                 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
521                 space: 'hsla',
522                 parse: function( execResult ) {
523                     return [
524                         execResult[1]/360,
525                         execResult[2] / 100,
526                         execResult[3] / 100,
527                         execResult[4]
528                     ];
529                 }
530             }
531         ],
532         template: '<div class="colorpicker dropdown-menu">'+
533                             '<div class="colorpicker-saturation"><i><b></b></i></div>'+
534                             '<div class="colorpicker-hue"><i></i></div>'+
535                             '<div class="colorpicker-alpha"><i></i></div>'+
536                             '<div class="colorpicker-color"><div /></div>'+
537                         '</div>'
538     };
539
540 }( window.jQuery )