/**
 *
 * Color picker
 * Author: Stefan Petre www.eyecon.ro
 * 
 * Dual licensed under the MIT and GPL licenses
 * 
 */
(function ($) {
        var ColorPicker = function () {
                var
                        ids = {},
                        inAction,
                        charMin = 65,
                        visible,
                        tpl = '<div class="colorpicker"><div class="colorpicker_color"><div><div></div></div></div><div class="colorpicker_hue"><div></div></div><div class="colorpicker_new_color"></div><div class="colorpicker_current_color"></div><div class="colorpicker_hex"><input type="text" maxlength="6" size="6" /></div><div class="colorpicker_rgb_r colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_g colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_h colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_s colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_submit"></div></div>',
                        defaults = {
                                eventName: 'click',
                                onShow: function () {},
                                onBeforeShow: function(){},
                                onHide: function () {},
                                onChange: function () {},
                                onSubmit: function () {},
                                color: 'ff0000',
                                livePreview: true,
                                flat: false
                        },
                        fillRGBFields = function  (hsb, cal) {
                                var rgb = HSBToRGB(hsb);
                                $(cal).data('colorpicker').fields
                                        .eq(1).val(rgb.r).end()
                                        .eq(2).val(rgb.g).end()
                                        .eq(3).val(rgb.b).end();
                        },
                        fillHSBFields = function  (hsb, cal) {
                                $(cal).data('colorpicker').fields
                                        .eq(4).val(hsb.h).end()
                                        .eq(5).val(hsb.s).end()
                                        .eq(6).val(hsb.b).end();
                        },
                        fillHexFields = function (hsb, cal) {
                                $(cal).data('colorpicker').fields
                                        .eq(0).val(HSBToHex(hsb)).end();
                        },
                        setSelector = function (hsb, cal) {
                                $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100}));
                                $(cal).data('colorpicker').selectorIndic.css({
                                        left: parseInt(150 * hsb.s/100, 10),
                                        top: parseInt(150 * (100-hsb.b)/100, 10)
                                });
                        },
                        setHue = function (hsb, cal) {
                                $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10));
                        },
                        setCurrentColor = function (hsb, cal) {
                                $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb));
                        },
                        setNewColor = function (hsb, cal) {
                                $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb));
                        },
                        keyDown = function (ev) {
                                var pressedKey = ev.charCode || ev.keyCode || -1;
                                if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) {
                                        return false;
                                }
                                var cal = $(this).parent().parent();
                                if (cal.data('colorpicker').livePreview === true) {
                                        change.apply(this);
                                }
                        },
                        change = function (ev) {
                                var cal = $(this).parent().parent(), col;
                                if (this.parentNode.className.indexOf('_hex') > 0) {
                                        cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value));
                                } else if (this.parentNode.className.indexOf('_hsb') > 0) {
                                        cal.data('colorpicker').color = col = fixHSB({
                                                h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10),
                                                s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10),
                                                b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10)
                                        });
                                } else {
                                        cal.data('colorpicker').color = col = RGBToHSB(fixRGB({
                                                r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10),
                                                g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10),
                                                b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10)
                                        }));
                                }
                                if (ev) {
                                        fillRGBFields(col, cal.get(0));
                                        fillHexFields(col, cal.get(0));
                                        fillHSBFields(col, cal.get(0));
                                }
                                setSelector(col, cal.get(0));
                                setHue(col, cal.get(0));
                                setNewColor(col, cal.get(0));
                                cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]);
                        },
                        blur = function (ev) {
                                var cal = $(this).parent().parent();
                                cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus');
                        },
                        focus = function () {
                                charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65;
                                $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus');
                                $(this).parent().addClass('colorpicker_focus');
                        },
                        downIncrement = function (ev) {
                                var field = $(this).parent().find('input').focus();
                                var current = {
                                        el: $(this).parent().addClass('colorpicker_slider'),
                                        max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255),
                                        y: ev.pageY,
                                        field: field,
                                        val: parseInt(field.val(), 10),
                                        preview: $(this).parent().parent().data('colorpicker').livePreview                                      
                                };
                                $(document).bind('mouseup', current, upIncrement);
                                $(document).bind('mousemove', current, moveIncrement);
                        },
                        moveIncrement = function (ev) {
                                ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10))));
                                if (ev.data.preview) {
                                        change.apply(ev.data.field.get(0), [true]);
                                }
                                return false;
                        },
                        upIncrement = function (ev) {
                                change.apply(ev.data.field.get(0), [true]);
                                ev.data.el.removeClass('colorpicker_slider').find('input').focus();
                                $(document).unbind('mouseup', upIncrement);
                                $(document).unbind('mousemove', moveIncrement);
                                return false;
                        },
                        downHue = function (ev) {
                                var current = {
                                        cal: $(this).parent(),
                                        y: $(this).offset().top
                                };
                                current.preview = current.cal.data('colorpicker').livePreview;
                                $(document).bind('mouseup', current, upHue);
                                $(document).bind('mousemove', current, moveHue);
                        },
                        moveHue = function (ev) {
                                change.apply(
                                        ev.data.cal.data('colorpicker')
                                                .fields
                                                .eq(4)
                                                .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10))
                                                .get(0),
                                        [ev.data.preview]
                                );
                                return false;
                        },
                        upHue = function (ev) {
                                fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
                                fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
                                $(document).unbind('mouseup', upHue);
                                $(document).unbind('mousemove', moveHue);
                                return false;
                        },
                        downSelector = function (ev) {
                                var current = {
                                        cal: $(this).parent(),
                                        pos: $(this).offset()
                                };
                                current.preview = current.cal.data('colorpicker').livePreview;
                                $(document).bind('mouseup', current, upSelector);
                                $(document).bind('mousemove', current, moveSelector);
                        },
                        moveSelector = function (ev) {
                                change.apply(
                                        ev.data.cal.data('colorpicker')
                                                .fields
                                                .eq(6)
                                                .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10))
                                                .end()
                                                .eq(5)
                                                .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10))
                                                .get(0),
                                        [ev.data.preview]
                                );
                                return false;
                        },
                        upSelector = function (ev) {
                                fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
                                fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0));
                                $(document).unbind('mouseup', upSelector);
                                $(document).unbind('mousemove', moveSelector);
                                return false;
                        },
                        enterSubmit = function (ev) {
                                $(this).addClass('colorpicker_focus');
                        },
                        leaveSubmit = function (ev) {
                                $(this).removeClass('colorpicker_focus');
                        },
                        clickSubmit = function (ev) {
                                var cal = $(this).parent();
                                var col = cal.data('colorpicker').color;
                                cal.data('colorpicker').origColor = col;
                                setCurrentColor(col, cal.get(0));
                                cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el);
                        },
                        show = function (ev) {
                                var cal = $('#' + $(this).data('colorpickerId'));
                                cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]);
                                var pos = $(this).offset();
                                var viewPort = getViewport();
                                var top = pos.top + this.offsetHeight;
                                var left = pos.left;
                                if (top + 176 > viewPort.t + viewPort.h) {
                                        top -= this.offsetHeight + 176;
                                }
                                if (left + 356 > viewPort.l + viewPort.w) {
                                        left -= 356;
                                }
                                cal.css({left: left + 'px', top: top + 'px'});
                                if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) {
                                        cal.show();
                                }
                                $(document).bind('mousedown', {cal: cal}, hide);
                                return false;
                        },
                        hide = function (ev) {
                                if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) {
                                        if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
                                                ev.data.cal.hide();
                                        }
                                        $(document).unbind('mousedown', hide);
                                }
                        },
                        isChildOf = function(parentEl, el, container) {
                                if (parentEl == el) {
                                        return true;
                                }
                                if (parentEl.contains) {
                                        return parentEl.contains(el);
                                }
                                if ( parentEl.compareDocumentPosition ) {
                                        return !!(parentEl.compareDocumentPosition(el) & 16);
                                }
                                var prEl = el.parentNode;
                                while(prEl && prEl != container) {
                                        if (prEl == parentEl)
                                                return true;
                                        prEl = prEl.parentNode;
                                }
                                return false;
                        },
                        getViewport = function () {
                                var m = document.compatMode == 'CSS1Compat';
                                return {
                                        l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
                                        t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop),
                                        w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth),
                                        h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight)
                                };
                        },
                        fixHSB = function (hsb) {
                                return {
                                        h: Math.min(360, Math.max(0, hsb.h)),
                                        s: Math.min(100, Math.max(0, hsb.s)),
                                        b: Math.min(100, Math.max(0, hsb.b))
                                };
                        }, 
                        fixRGB = function (rgb) {
                                return {
                                        r: Math.min(255, Math.max(0, rgb.r)),
                                        g: Math.min(255, Math.max(0, rgb.g)),
                                        b: Math.min(255, Math.max(0, rgb.b))
                                };
                        },
                        fixHex = function (hex) {
                                var len = 6 - hex.length;
                                if (len > 0) {
                                        var o = [];
                                        for (var i=0; i<len; i++) {
                                                o.push('0');
                                        }
                                        o.push(hex);
                                        hex = o.join('');
                                }
                                return hex;
                        }, 
                        HexToRGB = function (hex) {
                                var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
                                return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)};
                        },
                        HexToHSB = function (hex) {
                                return RGBToHSB(HexToRGB(hex));
                        },
                        RGBToHSB = function (rgb) {
                                var hsb = {
                                        h: 0,
                                        s: 0,
                                        b: 0
                                };
                                var min = Math.min(rgb.r, rgb.g, rgb.b);
                                var max = Math.max(rgb.r, rgb.g, rgb.b);
                                var delta = max - min;
                                hsb.b = max;
                                if (max != 0) {
                                        
                                }
                                hsb.s = max != 0 ? 255 * delta / max : 0;
                                if (hsb.s != 0) {
                                        if (rgb.r == max) {
                                                hsb.h = (rgb.g - rgb.b) / delta;
                                        } else if (rgb.g == max) {
                                                hsb.h = 2 + (rgb.b - rgb.r) / delta;
                                        } else {
                                                hsb.h = 4 + (rgb.r - rgb.g) / delta;
                                        }
                                } else {
                                        hsb.h = -1;
                                }
                                hsb.h *= 60;
                                if (hsb.h < 0) {
                                        hsb.h += 360;
                                }
                                hsb.s *= 100/255;
                                hsb.b *= 100/255;
                                return hsb;
                        },
                        HSBToRGB = function (hsb) {
                                var rgb = {};
                                var h = Math.round(hsb.h);
                                var s = Math.round(hsb.s*255/100);
                                var v = Math.round(hsb.b*255/100);
                                if(s == 0) {
                                        rgb.r = rgb.g = rgb.b = v;
                                } else {
                                        var t1 = v;
                                        var t2 = (255-s)*v/255;
                                        var t3 = (t1-t2)*(h%60)/60;
                                        if(h==360) h = 0;
                                        if(h<60) {rgb.r=t1;     rgb.b=t2; rgb.g=t2+t3}
                                        else if(h<120) {rgb.g=t1; rgb.b=t2;     rgb.r=t1-t3}
                                        else if(h<180) {rgb.g=t1; rgb.r=t2;     rgb.b=t2+t3}
                                        else if(h<240) {rgb.b=t1; rgb.r=t2;     rgb.g=t1-t3}
                                        else if(h<300) {rgb.b=t1; rgb.g=t2;     rgb.r=t2+t3}
                                        else if(h<360) {rgb.r=t1; rgb.g=t2;     rgb.b=t1-t3}
                                        else {rgb.r=0; rgb.g=0; rgb.b=0}
                                }
                                return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)};
                        },
                        RGBToHex = function (rgb) {
                                var hex = [
                                        rgb.r.toString(16),
                                        rgb.g.toString(16),
                                        rgb.b.toString(16)
                                ];
                                $.each(hex, function (nr, val) {
                                        if (val.length == 1) {
                                                hex[nr] = '0' + val;
                                        }
                                });
                                return hex.join('');
                        },
                        HSBToHex = function (hsb) {
                                return RGBToHex(HSBToRGB(hsb));
                        },
                        restoreOriginal = function () {
                                var cal = $(this).parent();
                                var col = cal.data('colorpicker').origColor;
                                cal.data('colorpicker').color = col;
                                fillRGBFields(col, cal.get(0));
                                fillHexFields(col, cal.get(0));
                                fillHSBFields(col, cal.get(0));
                                setSelector(col, cal.get(0));
                                setHue(col, cal.get(0));
                                setNewColor(col, cal.get(0));
                        };
                return {
                        init: function (opt) {
                                opt = $.extend({}, defaults, opt||{});
                                if (typeof opt.color == 'string') {
                                        opt.color = HexToHSB(opt.color);
                                } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) {
                                        opt.color = RGBToHSB(opt.color);
                                } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) {
                                        opt.color = fixHSB(opt.color);
                                } else {
                                        return this;
                                }
                                return this.each(function () {
                                        if (!$(this).data('colorpickerId')) {
                                                var options = $.extend({}, opt);
                                                options.origColor = opt.color;
                                                var id = 'collorpicker_' + parseInt(Math.random() * 1000);
                                                $(this).data('colorpickerId', id);
                                                var cal = $(tpl).attr('id', id);
                                                if (options.flat) {
                                                        cal.appendTo(this).show();
                                                } else {
                                                        cal.appendTo(document.body);
                                                }
                                                options.fields = cal
                                                                                        .find('input')
                                                                                                .bind('keyup', keyDown)
                                                                                                .bind('change', change)
                                                                                                .bind('blur', blur)
                                                                                                .bind('focus', focus);
                                                cal
                                                        .find('span').bind('mousedown', downIncrement).end()
                                                        .find('>div.colorpicker_current_color').bind('click', restoreOriginal);
                                                options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector);
                                                options.selectorIndic = options.selector.find('div div');
                                                options.el = this;
                                                options.hue = cal.find('div.colorpicker_hue div');
                                                cal.find('div.colorpicker_hue').bind('mousedown', downHue);
                                                options.newColor = cal.find('div.colorpicker_new_color');
                                                options.currentColor = cal.find('div.colorpicker_current_color');
                                                cal.data('colorpicker', options);
                                                cal.find('div.colorpicker_submit')
                                                        .bind('mouseenter', enterSubmit)
                                                        .bind('mouseleave', leaveSubmit)
                                                        .bind('click', clickSubmit);
                                                fillRGBFields(options.color, cal.get(0));
                                                fillHSBFields(options.color, cal.get(0));
                                                fillHexFields(options.color, cal.get(0));
                                                setHue(options.color, cal.get(0));
                                                setSelector(options.color, cal.get(0));
                                                setCurrentColor(options.color, cal.get(0));
                                                setNewColor(options.color, cal.get(0));
                                                if (options.flat) {
                                                        cal.css({
                                                                position: 'relative',
                                                                display: 'block'
                                                        });
                                                } else {
                                                        $(this).bind(options.eventName, show);
                                                }
                                        }
                                });
                        },
                        showPicker: function() {
                                return this.each( function () {
                                        if ($(this).data('colorpickerId')) {
                                                show.apply(this);
                                        }
                                });
                        },
                        hidePicker: function() {
                                return this.each( function () {
                                        if ($(this).data('colorpickerId')) {
                                                $('#' + $(this).data('colorpickerId')).hide();
                                        }
                                });
                        },
                        setColor: function(col) {
                                if (typeof col == 'string') {
                                        col = HexToHSB(col);
                                } else if (col.r != undefined && col.g != undefined && col.b != undefined) {
                                        col = RGBToHSB(col);
                                } else if (col.h != undefined && col.s != undefined && col.b != undefined) {
                                        col = fixHSB(col);
                                } else {
                                        return this;
                                }
                                return this.each(function(){
                                        if ($(this).data('colorpickerId')) {
                                                var cal = $('#' + $(this).data('colorpickerId'));
                                                cal.data('colorpicker').color = col;
                                                cal.data('colorpicker').origColor = col;
                                                fillRGBFields(col, cal.get(0));
                                                fillHSBFields(col, cal.get(0));
                                                fillHexFields(col, cal.get(0));
                                                setHue(col, cal.get(0));
                                                setSelector(col, cal.get(0));
                                                setCurrentColor(col, cal.get(0));
                                                setNewColor(col, cal.get(0));
                                        }
                                });
                        }
                };
        }();
        $.fn.extend({
                ColorPicker: ColorPicker.init,
                ColorPickerHide: ColorPicker.hidePicker,
                ColorPickerShow: ColorPicker.showPicker,
                ColorPickerSetColor: ColorPicker.setColor
        });
})(jQuery);

