Subversion Repositories Integrator Subversion

Rev

Blame | Last modification | View Log | Download | RSS feed

/* =========================================================
 * bootstrap-colorpicker.js
 * http://www.eyecon.ro/bootstrap-colorpicker
 * =========================================================
 * Copyright 2012 Stefan Petre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */

 
!function( $ ) {
       
        // Color object
       
        var Color = function(val) {
                this.value = {
                        h: 1,
                        s: 1,
                        b: 1,
                        a: 1
                };
                this.setColor(val);
        };
       
        Color.prototype = {
                constructor: Color,
               
                //parse a string to HSB
                setColor: function(val){
                        val = val.toLowerCase();
                        var that = this;
                        $.each( CPGlobal.stringParsers, function( i, parser ) {
                                var match = parser.re.exec( val ),
                                        values = match && parser.parse( match ),
                                        space = parser.space||'rgba';
                                if ( values ) {
                                        if (space === 'hsla') {
                                                that.value = CPGlobal.RGBtoHSB.apply(null, CPGlobal.HSLtoRGB.apply(null, values));
                                        } else {
                                                that.value = CPGlobal.RGBtoHSB.apply(null, values);
                                        }
                                        return false;
                                }
                        });
                },
               
                setHue: function(h) {
                        this.value.h = 1- h;
                },
               
                setSaturation: function(s) {
                        this.value.s = s;
                },
               
                setLightness: function(b) {
                        this.value.b = 1- b;
                },
               
                setAlpha: function(a) {
                        this.value.a = parseInt((1 - a)*100, 10)/100;
                },
               
                // HSBtoRGB from RaphaelJS
                // https://github.com/DmitryBaranovskiy/raphael/
                toRGB: function(h, s, b, a) {
                        if (!h) {
                                h = this.value.h;
                                s = this.value.s;
                                b = this.value.b;
                        }
                        h *= 360;
                        var R, G, B, X, C;
                        h = (h % 360) / 60;
                        C = b * s;
                        X = C * (1 - Math.abs(h % 2 - 1));
                        R = G = B = b - C;

                        h = ~~h;
                        R += [C, X, 0, 0, X, C][h];
                        G += [X, C, C, X, 0, 0][h];
                        B += [0, 0, X, C, C, X][h];
                        return {
                                r: Math.round(R*255),
                                g: Math.round(G*255),
                                b: Math.round(B*255),
                                a: a||this.value.a
                        };
                },
               
                toHex: function(h, s, b, a){
                        var rgb = this.toRGB(h, s, b, a);
                        return '#'+((1 << 24) | (parseInt(rgb.r) << 16) | (parseInt(rgb.g) << 8) | parseInt(rgb.b)).toString(16).substr(1);
                },
               
                toHSL: function(h, s, b, a){
                        if (!h) {
                                h = this.value.h;
                                s = this.value.s;
                                b = this.value.b;
                        }
                        var H = h,
                                L = (2 - s) * b,
                                S = s * b;
                        if (L > 0 && L <= 1) {
                                S /= L;
                        } else {
                                S /= 2 - L;
                        }
                        L /= 2;
                        if (S > 1) {
                                S = 1;
                        }
                        return {
                                h: H,
                                s: S,
                                l: L,
                                a: a||this.value.a
                        };
                }
        };
       
        // Picker object
       
        var Colorpicker = function(element, options){
                this.element = $(element);
                var format = options.format||this.element.data('color-format')||'hex';
                this.format = CPGlobal.translateFormats[format];
                this.isInput = this.element.is('input');
                this.component = this.element.is('.color') ? this.element.find('.input-group-btn') : false; // modified by keenthemes for Bootstrap 3.0 support
               
                this.picker = $(CPGlobal.template)
                                                        .appendTo('body')
                                                        .on('mousedown', $.proxy(this.mousedown, this));
               
                if (this.isInput) {
                        this.element.on({
                                'focus': $.proxy(this.show, this),
                                'keyup': $.proxy(this.update, this)
                        });
                } else if (this.component){
                        this.component.on({
                                'click': $.proxy(this.show, this)
                        });
                } else {
                        this.element.on({
                                'click': $.proxy(this.show, this)
                        });
                }
                if (format === 'rgba' || format === 'hsla') {
                        this.picker.addClass('alpha');
                        this.alpha = this.picker.find('.colorpicker-alpha')[0].style;
                }
               
                if (this.component){
                        this.picker.find('.colorpicker-color').hide();
                        this.preview = this.element.find('i')[0].style;
                } else {
                        this.preview = this.picker.find('div:last')[0].style;
                }
               
                this.base = this.picker.find('div:first')[0].style;
                this.update();
        };
       
        Colorpicker.prototype = {
                constructor: Colorpicker,
               
                show: function(e) {
                        this.picker.show();
                        this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
                        this.place();
                        $(window).on('resize', $.proxy(this.place, this));
                        if (!this.isInput) {
                                if (e) {
                                        e.stopPropagation();
                                        e.preventDefault();
                                }
                        }
                        $(document).on({
                                'mousedown': $.proxy(this.hide, this)
                        });
                        this.element.trigger({
                                type: 'show',
                                color: this.color
                        });
                },
               
                update: function(){
                        this.color = new Color(this.isInput ? this.element.prop('value') : this.element.data('color'));
                        this.picker.find('i')
                                .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end()
                                .eq(1).css('top', 100 * (1 - this.color.value.h)).end()
                                .eq(2).css('top', 100 * (1 - this.color.value.a));
                        this.previewColor();
                },
               
                setValue: function(newColor) {
                        this.color = new Color(newColor);
                        this.picker.find('i')
                                .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end()
                                .eq(1).css('top', 100 * (1 - this.color.value.h)).end()
                                .eq(2).css('top', 100 * (1 - this.color.value.a));
                        this.previewColor();
                        this.element.trigger({
                                type: 'changeColor',
                                color: this.color
                        });
                },
               
                hide: function(){
                        this.picker.hide();
                        $(window).off('resize', this.place);
                        if (!this.isInput) {
                                $(document).off({
                                        'mousedown': this.hide
                                });
                                if (this.component){
                                        this.element.find('input').prop('value', this.format.call(this));
                                }
                                this.element.data('color', this.format.call(this));
                        } else {
                                this.element.prop('value', this.format.call(this));
                        }
                        this.element.trigger({
                                type: 'hide',
                                color: this.color
                        });
                },
               
                place: function(){
                        var offset = this.component ? this.component.offset() : this.element.offset();
                        this.picker.css({
                                top: offset.top + this.height,
                                left: offset.left
                        });
                },
               
                //preview color change
                previewColor: function(){
                        try {
                                this.preview.backgroundColor = this.format.call(this);
                        } catch(e) {
                                this.preview.backgroundColor = this.color.toHex();
                        }
                        //set the color for brightness/saturation slider
                        this.base.backgroundColor = this.color.toHex(this.color.value.h, 1, 1, 1);
                        //set te color for alpha slider
                        if (this.alpha) {
                                this.alpha.backgroundColor = this.color.toHex();
                        }
                },
               
                pointer: null,
               
                slider: null,
               
                mousedown: function(e){
                        e.stopPropagation();
                        e.preventDefault();
                       
                        var target = $(e.target);
                       
                        //detect the slider and set the limits and callbacks
                        var zone = target.closest('div');
                        if (!zone.is('.colorpicker')) {
                                if (zone.is('.colorpicker-saturation')) {
                                        this.slider = $.extend({}, CPGlobal.sliders.saturation);
                                }
                                else if (zone.is('.colorpicker-hue')) {
                                        this.slider = $.extend({}, CPGlobal.sliders.hue);
                                }
                                else if (zone.is('.colorpicker-alpha')) {
                                        this.slider = $.extend({}, CPGlobal.sliders.alpha);
                                } else {
                                        return false;
                                }
                                var offset = zone.offset();
                                //reference to knob's style
                                this.slider.knob = zone.find('i')[0].style;
                                this.slider.left = e.pageX - offset.left;
                                this.slider.top = e.pageY - offset.top;
                                this.pointer = {
                                        left: e.pageX,
                                        top: e.pageY
                                };
                                //trigger mousemove to move the knob to the current position
                                $(document).on({
                                        mousemove: $.proxy(this.mousemove, this),
                                        mouseup: $.proxy(this.mouseup, this)
                                }).trigger('mousemove');
                        }
                        return false;
                },
               
                mousemove: function(e){
                        e.stopPropagation();
                        e.preventDefault();
                        var left = Math.max(
                                0,
                                Math.min(
                                        this.slider.maxLeft,
                                        this.slider.left + ((e.pageX||this.pointer.left) - this.pointer.left)
                                )
                        );
                        var top = Math.max(
                                0,
                                Math.min(
                                        this.slider.maxTop,
                                        this.slider.top + ((e.pageY||this.pointer.top) - this.pointer.top)
                                )
                        );
                        this.slider.knob.left = left + 'px';
                        this.slider.knob.top = top + 'px';
                        if (this.slider.callLeft) {
                                this.color[this.slider.callLeft].call(this.color, left/100);
                        }
                        if (this.slider.callTop) {
                                this.color[this.slider.callTop].call(this.color, top/100);
                        }
                        this.previewColor();
                        this.element.trigger({
                                type: 'changeColor',
                                color: this.color
                        });
                        return false;
                },
               
                mouseup: function(e){
                        e.stopPropagation();
                        e.preventDefault();
                        $(document).off({
                                mousemove: this.mousemove,
                                mouseup: this.mouseup
                        });
                        return false;
                }
        }

        $.fn.colorpicker = function ( option, val ) {
                return this.each(function () {
                        var $this = $(this),
                                data = $this.data('colorpicker'),
                                options = typeof option === 'object' && option;
                        if (!data) {
                                $this.data('colorpicker', (data = new Colorpicker(this, $.extend({}, $.fn.colorpicker.defaults,options))));
                        }
                        if (typeof option === 'string') data[option](val);
                });
        };

        $.fn.colorpicker.defaults = {
        };
       
        $.fn.colorpicker.Constructor = Colorpicker;
       
        var CPGlobal = {
       
                // translate a format from Color object to a string
                translateFormats: {
                        'rgb': function(){
                                var rgb = this.color.toRGB();
                                return 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';
                        },
                       
                        'rgba': function(){
                                var rgb = this.color.toRGB();
                                return 'rgba('+rgb.r+','+rgb.g+','+rgb.b+','+rgb.a+')';
                        },
                       
                        'hsl': function(){
                                var hsl = this.color.toHSL();
                                return 'hsl('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%)';
                        },
                       
                        'hsla': function(){
                                var hsl = this.color.toHSL();
                                return 'hsla('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%,'+hsl.a+')';
                        },
                       
                        'hex': function(){
                                return  this.color.toHex();
                        }
                },
               
                sliders: {
                        saturation: {
                                maxLeft: 100,
                                maxTop: 100,
                                callLeft: 'setSaturation',
                                callTop: 'setLightness'
                        },
                       
                        hue: {
                                maxLeft: 0,
                                maxTop: 100,
                                callLeft: false,
                                callTop: 'setHue'
                        },
                       
                        alpha: {
                                maxLeft: 0,
                                maxTop: 100,
                                callLeft: false,
                                callTop: 'setAlpha'
                        }
                },
               
                // HSBtoRGB from RaphaelJS
                // https://github.com/DmitryBaranovskiy/raphael/
                RGBtoHSB: function (r, g, b, a){
                        r /= 255;
                        g /= 255;
                        b /= 255;

                        var H, S, V, C;
                        V = Math.max(r, g, b);
                        C = V - Math.min(r, g, b);
                        H = (C === 0 ? null :
                                V == r ? (g - b) / C :
                                V == g ? (b - r) / C + 2 :
                                        (r - g) / C + 4
                                );
                        H = ((H + 360) % 6) * 60 / 360;
                        S = C === 0 ? 0 : C / V;
                        return {h: H||1, s: S, b: V, a: a||1};
                },
               
                HueToRGB: function (p, q, h) {
                        if (h < 0)
                                h += 1;
                        else if (h > 1)
                                h -= 1;

                        if ((h * 6) < 1)
                                return p + (q - p) * h * 6;
                        else if ((h * 2) < 1)
                                return q;
                        else if ((h * 3) < 2)
                                return p + (q - p) * ((2 / 3) - h) * 6;
                        else
                                return p;
                },
       
                HSLtoRGB: function (h, s, l, a)
                {
                        if (s < 0) {
                                s = 0;
                        }
                        var q;
                        if (l <= 0.5) {
                                q = l * (1 + s);
                        } else {
                                q = l + s - (l * s);
                        }
                       
                        var p = 2 * l - q;

                        var tr = h + (1 / 3);
                        var tg = h;
                        var tb = h - (1 / 3);

                        var r = Math.round(CPGlobal.HueToRGB(p, q, tr) * 255);
                        var g = Math.round(CPGlobal.HueToRGB(p, q, tg) * 255);
                        var b = Math.round(CPGlobal.HueToRGB(p, q, tb) * 255);
                        return [r, g, b, a||1];
                },
               
                // a set of RE's that can match strings and generate color tuples.
                // from John Resig color plugin
                // https://github.com/jquery/jquery-color/
                stringParsers: [
                        {
                                re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
                                parse: function( execResult ) {
                                        return [
                                                execResult[ 1 ],
                                                execResult[ 2 ],
                                                execResult[ 3 ],
                                                execResult[ 4 ]
                                        ];
                                }
                        }, {
                                re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
                                parse: function( execResult ) {
                                        return [
                                                2.55 * execResult[1],
                                                2.55 * execResult[2],
                                                2.55 * execResult[3],
                                                execResult[ 4 ]
                                        ];
                                }
                        }, {
                                re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
                                parse: function( execResult ) {
                                        return [
                                                parseInt( execResult[ 1 ], 16 ),
                                                parseInt( execResult[ 2 ], 16 ),
                                                parseInt( execResult[ 3 ], 16 )
                                        ];
                                }
                        }, {
                                re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
                                parse: function( execResult ) {
                                        return [
                                                parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
                                                parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
                                                parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
                                        ];
                                }
                        }, {
                                re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
                                space: 'hsla',
                                parse: function( execResult ) {
                                        return [
                                                execResult[1]/360,
                                                execResult[2] / 100,
                                                execResult[3] / 100,
                                                execResult[4]
                                        ];
                                }
                        }
                ],
                template: '<div class="colorpicker dropdown-menu">'+
                                                        '<div class="colorpicker-saturation"><i><b></b></i></div>'+
                                                        '<div class="colorpicker-hue"><i></i></div>'+
                                                        '<div class="colorpicker-alpha"><i></i></div>'+
                                                        '<div class="colorpicker-color"><div /></div>'+
                                                '</div>'
        };

}( window.jQuery )