/** * jQuery Wheel Color Picker * * http://www.jar2.net/projects/jquery-wheelcolorpicker * * Author : Fajar Chandra * Date : 2015.10.16 * * Copyright © 2011-2015 Fajar Chandra. All rights reserved. * Released under MIT License. * http://www.opensource.org/licenses/mit-license.php */ (function($) { // Contains public methods var methods = {}; // Contains private methods var private = {}; // Holds the color picker popup widget for use globally var g_Popup = null; // Holds the overlay element wrapped in jQuery var g_Overlay = null; // Coordinate of the top left page (mobile chrome workaround) var g_Origin = { left: 0, top: 0 }; // Various workaround flags var BUG_RELATIVE_PAGE_ORIGIN = false; // top left of the page is not on (0,0), making window.scrollX/Y and offset() useless /** * Function: wheelColorPicker * * The wheelColorPicker function wrapper. Firing all functions and * setting/getting all options in this plugin should be called via * this function. * * This function will look for the options parameter passed in, and * try to do something as specified in this order: * 1. If no argument is passed, then initialize the plugin * 2. If object is passed, then call setOptions() * 3. If string is passed, then try to fire a method with that name * 4. If string is passed and no method matches the name, then try * to set/get an option with that name */ $.fn.wheelColorPicker = function () { // Initialize if( arguments.length == 0 ) { return methods.setOptions.call( this, {} ); } // Set options and initialize if( typeof arguments[0] === "object" ) { return methods.setOptions.call( this, arguments[0] ); } // Call a method else if( $.isFunction( methods[ arguments[0] ] ) ) { var shift = [].shift; var firstArg = shift.apply(arguments); return methods[ firstArg ].apply( this, arguments ); } // Set option value else if( arguments.length == 2 ) { return methods.setOption.call( this, arguments[0], arguments[1] ); } // Get option value else if( arguments.length == 1) { return methods.getOption.call( this, arguments[0] ); } else { $.error( 'Method/option named ' + arguments[0] + ' does not exist on jQuery.wheelColorPicker' ); } }; /** * Object: defaults * * Contains default options for the wheelColorPicker plugin. * * Member properties: * * format - String Color naming style. See colorToRgb for all * available formats. * live - Boolean Enable dynamic slider gradients. * preview - Boolean Enable live color preview on input field * userinput - (Deprecated) Boolean Enable picking color by typing directly * validate - (Deprecated) Boolean When userinput is enabled, always convert * the input value to a specified format. This option is * deprecated. use autoConvert instead. * autoConvert - Boolean Automatically convert inputted value to * specified format. * color - Mixed Initial value in any of supported color * value format or as an object. Setting this value will * override the current input value. * alpha - (Deprecated) Boolean Force the color picker to use alpha value * despite its selected color format. This option is * deprecated. Use sliders = "a" instead. * inverseLabel - (deprecated) Boolean use inverse color for * input label instead of black/white color. * preserveWheel - Boolean preserve color wheel shade when slider * position changes. If set to true, changing * color wheel from black will reset selectedColor.val * (shade) to 1. * interactive - Boolean enable interactive sliders where slider bar * gradients change dynamically as user drag a slider * handle. Set to false if this affect performance. * See also 'quality' option if you wish to keep * interactive slider but with reduced quality. * cssClass - Object CSS Classes to be added to the color picker. * width - Mixed Color picker width, either in px or 'stretch' to * fit the input width. If null then the default CSS size * will be used. * height - Integer Color picker height in px. If null then the default * CSS size will be used. * layout - String [block|popup] Layout mode. * animDuration - Number Duration for transitions such as fade-in * and fade-out. * quality - Rendering details quality. The normal quality is 1. * Setting less than 0.1 may make the sliders ugly, * while setting the value too high might affect performance. * sliders - String combination of sliders. If null then the color * picker will show default values, which is "wvp" for * normal color or "wvap" for color with alpha value. * Possible combinations are "whsvrgbap". Note that the * order of letters affects the slider positions. * showSliderLabel - Boolean Show labels for each slider. * showSliderValue - Boolean Show numeric value of each slider. * hideKeyboard - Boolean Keep input blurred to avoid on screen keyboard appearing. * If this is set to true, avoid assigning handler to blur event. * rounding - Round the alpha value to N decimal digits. Default is 2. * Set -1 to disable rounding. * mobile - Display mobile-friendly layout when opened in mobile device. * mobileAutoScroll - Automatically scroll the page if focused input element * gets obstructed by color picker dialog. * htmlOptions - Load options from HTML attributes. * To set options using HTML attributes, * prefix these options with 'data-wcp-' as attribute names. */ $.fn.wheelColorPicker.defaults = { format: 'hex', /* 1.x */ preview: false, /* 1.x */ live: true, /* 2.0 */ userinput: true, /* DEPRECATED 1.x */ validate: false, /* DEPRECATED 1.x */ /* See autoConvert */ autoConvert: true, /* 2.0 */ /* NOT IMPLEMENTED */ color: null, /* DEPRECATED 1.x */ /* Init-time usage only */ alpha: null, /* DEPRECATED 1.x */ /* See methods.alpha */ preserveWheel: false, /* DEPRECATED 1.x */ /* Use interactive */ interactive: true, /* 3.0 */ /* NOT IMPLEMENTED */ cssClass: '', /* 2.0 */ layout: 'popup', /* 2.0 */ animDuration: 200, /* 2.0 */ quality: 1, /* 2.0 */ sliders: null, /* 2.0 */ showSliderLabel: true, /* 2.0 */ showSliderValue: false, /* 2.0 */ rounding: 2, /* 2.3 */ mobile: true, /* 3.0 */ /* NOT IMPLEMENTED */ hideKeyboard: false, /* 2.4 */ htmlOptions: true /* 2.3 */ }; $.fn.wheelColorPicker.hasInit = false; /** * Function: colorToStr * * Introduced in 2.0 * * Convert color object to string in specified format * * Available formats: * - hex * - css * - rgb * - rgb% * - rgba * - rgba% * - hsv * - hsv% * - hsva * - hsva% * - hsb * - hsb% * - hsba * - hsba% */ $.fn.wheelColorPicker.colorToStr = function( color, format ) { var result = ""; switch( format ) { case 'css': result = "#"; case 'hex': var r = Math.round(color.r * 255).toString(16); if( r.length == 1) { r = "0" + r; } var g = Math.round(color.g * 255).toString(16); if( g.length == 1) { g = "0" + g; } var b = Math.round(color.b * 255).toString(16); if( b.length == 1) { b = "0" + b; } result += r + g + b; break; case 'rgb': result = "rgb(" + Math.round(color.r * 255) + "," + Math.round(color.g * 255) + "," + Math.round(color.b * 255) + ")"; break; case 'rgb%': result = "rgb(" + (color.r * 100) + "%," + (color.g * 100) + "%," + (color.b * 100) + "%)"; break; case 'rgba': result = "rgba(" + Math.round(color.r * 255) + "," + Math.round(color.g * 255) + "," + Math.round(color.b * 255) + "," + color.a + ")"; break; case 'rgba%': result = "rgba(" + (color.r * 100) + "%," + (color.g * 100) + "%," + (color.b * 100) + "%," + (color.a * 100) + "%)"; break; case 'hsv': result = "hsv(" + (color.h * 360) + "," + color.s + "," + color.v + ")"; break; case 'hsv%': result = "hsv(" + (color.h * 100) + "%," + (color.s * 100) + "%," + (color.v * 100) + "%)"; break; case 'hsva': result = "hsva(" + (color.h * 360) + "," + color.s + "," + color.v + "," + color.a + ")"; break; case 'hsva%': result = "hsva(" + (color.h * 100) + "%," + (color.s * 100) + "%," + (color.v * 100) + "%," + (color.a * 100) + "%)"; break; case 'hsb': result = "hsb(" + color.h + "," + color.s + "," + color.v + ")"; break; case 'hsb%': result = "hsb(" + (color.h * 100) + "%," + (color.s * 100) + "%," + (color.v * 100) + "%)"; break; case 'hsba': result = "hsba(" + color.h + "," + color.s + "," + color.v + "," + color.a + ")"; break; case 'hsba%': result = "hsba(" + (color.h * 100) + "%," + (color.s * 100) + "%," + (color.v * 100) + "%," + (color.a * 100) + "%)"; break; } return result; }; /** * Function: strToColor * * Introduced in 2.0 * * Convert string to color object. * Please note that if RGB color is supplied, the returned value * will only contain RGB. * * If invalid string is supplied, FALSE will be returned. */ $.fn.wheelColorPicker.strToColor = function( val ) { var color = { a: 1 }; var tmp; var hasAlpha; // #ffffff if(val.match(/^#[0-9a-f]{6}$/i) != null) { if( isNaN( color.r = parseInt(val.substr(1, 2), 16) / 255 ) ) { return false; } if( isNaN( color.g = parseInt(val.substr(3, 2), 16) / 255 ) ) { return false; } if( isNaN( color.b = parseInt(val.substr(5, 2), 16) / 255 ) ) { return false; } } // ffffff else if(val.match(/^[0-9a-f]{6}$/i) != null) { if( isNaN( color.r = parseInt(val.substr(0, 2), 16) / 255 ) ) { return false; } if( isNaN( color.g = parseInt(val.substr(2, 2), 16) / 255 ) ) { return false; } if( isNaN( color.b = parseInt(val.substr(4, 2), 16) / 255 ) ) { return false; } } // rgb(100%,100%,100%) // rgba(100%,100%,100%,100%) // rgba(255,255,255,1) // rgba(100%,1, 0.5,.2) else if( val.match(/^rgba\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null || val.match(/^rgb\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ) { if(val.match(/a/i) != null) { hasAlpha = true; } else { hasAlpha = false; } tmp = val.substring(val.indexOf('(')+1, val.indexOf(',')); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.r = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.r = parseInt(tmp) / 255 ) ) { return false; } } tmp = val.substring(val.indexOf(',')+1, val.indexOf(',', val.indexOf(',')+1)); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.g = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.g = parseInt(tmp) / 255 ) ) { return false; } } if(hasAlpha) { tmp = val.substring(val.indexOf(',', val.indexOf(',')+1)+1, val.lastIndexOf(',')); } else { tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')')); } if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.b = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.b = parseInt(tmp) / 255 ) ) { return false; } } if(hasAlpha) { tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')')); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.a = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.a = parseFloat(tmp) ) ) { return false; } } } } // hsv(100%,100%,100%) // hsva(100%,100%,100%,100%) // hsv(360,1,1,1) // hsva(360,1, 0.5,.2) // hsb(100%,100%,100%) // hsba(100%,100%,100%,100%) // hsb(360,1,1,1) // hsba(360,1, 0.5,.2) else if( val.match(/^hsva\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null || val.match(/^hsv\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null || val.match(/^hsba\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null || val.match(/^hsb\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ) { if(val.match(/a/i) != null) { hasAlpha = true; } else { hasAlpha = false; } tmp = val.substring(val.indexOf('(')+1, val.indexOf(',')); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.h = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.h = parseFloat(tmp) / 360 ) ) { return false; } } tmp = val.substring(val.indexOf(',')+1, val.indexOf(',', val.indexOf(',')+1)); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.s = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.s = parseFloat(tmp) ) ) { return false; } } if(hasAlpha) { tmp = val.substring(val.indexOf(',', val.indexOf(',')+1)+1, val.lastIndexOf(',')); } else { tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')')); } if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.v = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.v = parseFloat(tmp) ) ) { return false; } } if(hasAlpha) { tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')')); if( tmp.charAt( tmp.length-1 ) == '%') { if( isNaN( color.a = parseFloat(tmp) / 100 ) ) { return false; } } else { if( isNaN( color.a = parseFloat(tmp) ) ) { return false; } } } } else { return false; } return color; }; /** * Function: hsvToRgb * * Introduced in 2.0 * * Perform HSV to RGB conversion */ $.fn.wheelColorPicker.hsvToRgb = function( h, s, v ) { // Calculate RGB from hue (1st phase) var cr = (h <= (1/6) || h >= (5/6)) ? 1 : (h < (1/3) ? 1 - ((h - (1/6)) * 6) : (h > (4/6) ? (h - (4/6)) * 6 : 0)); var cg = (h >= (1/6) && h <= (3/6)) ? 1 : (h < (1/6) ? h * 6 : (h < (4/6) ? 1 - ((h - (3/6)) * 6) : 0)); var cb = (h >= (3/6) && h <= (5/6)) ? 1 : (h > (2/6) && h < (3/6) ? (h - (2/6)) * 6 : (h > (5/6) ? 1 - ((h - (5/6)) * 6) : 0)); //~ console.log(cr + ' ' + cg + ' ' + cb); // Calculate RGB with saturation & value applied var r = (cr + (1-cr)*(1-s)) * v; var g = (cg + (1-cg)*(1-s)) * v; var b = (cb + (1-cb)*(1-s)) * v; //~ console.log(r + ' ' + g + ' ' + b + ' ' + v); return { r: r, g: g, b: b }; }; /** * Function: rgbToHsv * * Introduced in 2.0 * * Perform RGB to HSV conversion */ $.fn.wheelColorPicker.rgbToHsv = function( r, g, b ) { var h; var s; var v; var maxColor = Math.max(r, g, b); var minColor = Math.min(r, g, b); var delta = maxColor - minColor; // Calculate saturation if(maxColor != 0) { s = delta / maxColor; } else { s = 0; } // Calculate hue // To simplify the formula, we use 0-6 range. if(delta == 0) { h = 0; } else if(r == maxColor) { h = (6 + (g - b) / delta) % 6; } else if(g == maxColor) { h = 2 + (b - r) / delta; } else if(b == maxColor) { h = 4 + (r - g) / delta; } else { h = 0; } // Then adjust the range to be 0-1 h = h/6; // Calculate value v = maxColor; //~ console.log(h + ' ' + s + ' ' + v); return { h: h, s: s, v: v }; }; /* * DEVELOPER's NOTE * * While the way to retrieve references differs, the following * local variable names are reused to reference the same object * across methods. * * input - input DOM element * $input - input element wrapped in jquery * widget - color picker DOM element (div.jQWCP-wWidget) * $widget - color picker widget wrapped in jquery (div.jQWCP-wWidget) * settings - reference to settings object */ /** * Function: staticInit * * Introduced in 2.0 * * Initialize wheel color picker globally */ methods.staticInit = function() { if($.fn.wheelColorPicker.hasInit) return; // This must only occur once $.fn.wheelColorPicker.hasInit = true; // Insert overlay element to handle popup closing // when hideKeyboard is true, hence input is always blurred g_Overlay = $('
'); $('body').append(g_Overlay); g_Overlay.on('click', private.onOverlayClick); // Attach events $('body').on('mouseup.wheelColorPicker', private.onBodyMouseUp); $('body').on('touchend.wheelColorPicker', private.onBodyMouseUp); $('body').on('mousemove.wheelColorPicker', private.onBodyMouseMove); $('body').on('touchmove.wheelColorPicker', private.onBodyMouseMove); }; /** * Function: init * * Initialize wheel color picker */ methods.init = function() { methods.staticInit(); return this.each(function() { var $this = $(this); var settings = $this.data('jQWCP.settings'); var $widget = null; // Initialization must only occur once if(settings.hasInit) return; // Set hasInit flag to true // Notice: remember that settings was assigned by reference, // so we can directly set the members of settings object. settings.hasInit = true; // Setup selected color container var color = { h: 0, s: 0, v: 1, r: 1, g: 1, b: 1, a: 1 }; $this.data('jQWCP.color', color); // Check sliders option if(settings.sliders == null) settings.sliders = 'wvp' + (settings.format.indexOf('a') >= 0 ? 'a' : ''); /// LAYOUT & BINDINGS /// // Setup block mode layout if( settings.layout == 'block' ) { $widget = private.initWidget.call( $this ); //private.adjustWidget( $widget.get(0), settings ); $widget.addClass(settings.cssClass); // Store DOM element reference $this.data('jQWCP.widget', $widget.get(0)); $widget.data('jQWCP.inputElm', this); // Wrap widget around the input elm and put the input // elm inside widget $widget.addClass('jQWCP-block'); $this.after($widget); if($this.css('display') == "inline") { $widget.css('display', "inline-block"); } else { $widget.css('display', $this.css('display')); } $widget.append($this); private.adjustWidget( $widget.get(0), settings ); $this.hide(); // Add tabindex attribute to make the widget focusable if($this.attr('tabindex') != undefined) { $widget.attr('tabindex', $this.attr('tabindex')); } else { $widget.attr('tabindex', 0); } // Draw shading methods.redrawSliders.call( $this, null, true ); methods.updateSliders.call( $this ); // Bind widget element events $widget.on('focus.wheelColorPicker', private.onWidgetFocus); $widget.on('blur.wheelColorPicker', private.onWidgetBlur); } // Setup popup mode layout else { if(g_Popup == null) { $widget = private.initWidget.call( $this ); // Store DOM element reference $this.data('jQWCP.widget', $widget.get(0)); // Assign widget to global g_Popup = $widget; g_Popup.hide(); $('body').append($widget); // Bind popup events $widget.on('mousedown.wheelColorPicker', private.onPopupDlgMouseDown); $widget.on('mouseup.wheelColorPicker', private.onPopupDlgMouseUp); } else { $this.data('jQWCP.widget', g_Popup); } // Bind input element events $this.on('focus.wheelColorPicker', methods.show); //$this.on('blur.wheelColorPicker', methods.hide); $this.on('blur.wheelColorPicker', private.onInputBlur); } // Bind input events $this.on('keyup.wheelColorPicker', private.onInputKeyup); $this.on('change.wheelColorPicker', private.onInputChange); // Set color value if(settings.color == null) { methods.setValue.call( $this, $this.val() ); } else if(typeof(settings.color) == "object") { methods.setColor.call( $this, settings.color ); settings.color = undefined; } else { methods.setValue.call( $this, settings.color ); settings.color = undefined; } // Set readonly mode /* DEPRECATED */ if(settings.userinput) { $this.removeAttr('readonly'); } else { $this.attr('readonly', true); } }); }; /** * Function: setOptions * * Introduced in 2.0 * * Set options to the color picker */ methods.setOptions = function( options ) { var optionsParam = options; this.each(function() { var $this = $(this); // Refers to input elm var input = this; options = $.extend({}, optionsParam); // Reset options for each iteration // Load options from HTML attributes if(typeof options.htmlOptions == 'undefined') { // Since defaults is not yet loaded, specifically lookup // from defaults.htmlOptions if not htmlOptions is not specified. options.htmlOptions = $.fn.wheelColorPicker.defaults.htmlOptions; } if(options.htmlOptions) { $.each($.fn.wheelColorPicker.defaults, function(key, val) { if(input.hasAttribute('data-wcp-'+key)) { options[key] = $this.attr('data-wcp-'+key); } }); } // Override options var settings = $.extend( true, {}, $.fn.wheelColorPicker.defaults, options ); $this.data('jQWCP.settings', settings); }); return methods.init.call( this ); }; /** * Function: destroy * * Destroy the color picker and return it to normal element. */ methods.destroy = function() { return this.each(function() { var $this = $(this); var settings = $this.data('jQWCP.settings'); var $widget = null; // Destroy must only occur when it's initialized if(!settings) return; // Reset layout if(settings.layout == 'block') { $widget = $( $this.data('jQWCP.widget') ); $widget.before($this); $widget.remove(); $this.show(); } else { $widget = g_Popup; } // Unbind events $this.off('focus.wheelColorPicker'); $this.off('blur.wheelColorPicker'); $this.off('keyup.wheelColorPicker'); $this.off('change.wheelColorPicker'); // Remove data $this.data('jQWCP.settings', null); $this.data('jQWCP.widget', null); }); }; /** * Function: show * * Show the color picker dialog. This function is only applicable to * popup mode color picker layout. * * Parameter: * e - Event object */ methods.show = function( e ) { var $this = $(this); // Refers to input elm var settings = $this.data('jQWCP.settings'); var $widget = g_Popup; // Don't do anything if not using popup layout if( settings.layout != "popup" ) return; // Don't do anything if the popup is already shown and attached // to the correct input elm if( this == $this.data('jQWCP.inputElm') ) return; // Terminate ongoing transitions $widget.stop( true, true ); // Reposition the popup window $widget.css({ top: ($this.offset().top + $this.outerHeight()) + 'px', left: $this.offset().left + 'px' }); // BUG_RELATIVE_PAGE_ORIGIN workaround if(BUG_RELATIVE_PAGE_ORIGIN) { $widget.css({ top: ($this.get(0).getBoundingClientRect().top - g_Origin.top + $this.outerHeight()) + 'px', left: ($this.get(0).getBoundingClientRect().left - g_Origin.left) + 'px' }); } // Set the input element the popup is attached to $widget.data('jQWCP.inputElm', this); // Assign custom css class $widget.attr( 'class', 'jQWCP-wWidget' ); $widget.addClass( settings.cssClass ); // Adjust layout private.adjustWidget( $widget.get(0), settings ); // Redraw sliders methods.redrawSliders.call( $this, null, true ); methods.updateSliders.call( $this ); // Store last textfield value settings.lastValue = $this.val(); $widget.fadeIn( settings.animDuration ); // If hideKeyboard is true, force to hide soft keyboard if(settings.hideKeyboard) { $this.blur(); g_Overlay.show(); } }; /** * Function: hide * * Hide the color picker dialog. This function is only applicable to * popup mode color picker layout. * * Parameter: * e - Event object */ methods.hide = function( e ) { var $this = $(this); // Refers to input elm var settings = $this.data('jQWCP.settings'); g_Popup.fadeOut( settings.animDuration ); }; /** * Function: redrawSliders * * Introduced in 2.0 * * Redraw slider gradients. * * Parameter: * sliders - String combination of sliders to be redrawn. If not * specified then redraw all sliders. Possible combinations * are "hsvrgba". * force - Boolean force redraw. */ methods.redrawSliders = function( sliders, force ) { return this.each(function() { var $this = $(this); // Refers to input elm var settings = $this.data('jQWCP.settings'); var $widget = $( $this.data('jQWCP.widget') ); var color = $this.data('jQWCP.color'); // No need to redraw sliders on global popup widget if not // attached to the input elm in current iteration if(this != $widget.data('jQWCP.inputElm')) return; var w = 1; var h = settings.quality * 50; var A = 1; var R = 0; var G = 0; var B = 0; var H = 0; var S = 0; var V = 1; // Dynamic colors if(settings.live) { A = color.a; R = Math.round(color.r * 255); G = Math.round(color.g * 255); B = Math.round(color.b * 255); H = color.h; S = color.s; V = color.v; } /// PREVIEW /// // Preview box must always be redrawn var $previewBox = $widget.find('.jQWCP-wPreviewBox'); var previewBoxCtx = $previewBox.get(0).getContext('2d'); previewBoxCtx.fillStyle = "rgba(" + R + "," + G + "," + B + "," + A + ")"; previewBoxCtx.clearRect(0, 0, 1, 1); previewBoxCtx.fillRect(0, 0, 1, 1); /// SLIDERS /// if(!settings.live && !force) return; /// ALPHA /// // The top color is (R, G, B, 1) // The bottom color is (R, G, B, 0) var $alphaSlider = $widget.find('.jQWCP-wAlphaSlider'); var alphaSliderCtx = $alphaSlider.get(0).getContext('2d'); var alphaGradient = alphaSliderCtx.createLinearGradient(0, 0, 0, h); alphaGradient.addColorStop(0, "rgba("+R+","+G+","+B+",1)"); alphaGradient.addColorStop(1, "rgba("+R+","+G+","+B+",0)"); alphaSliderCtx.fillStyle = alphaGradient; alphaSliderCtx.clearRect(0, 0, w, h); alphaSliderCtx.fillRect(0, 0, w, h); /// RED /// // The top color is (255, G, B) // The bottom color is (0, G, B) var $redSlider = $widget.find('.jQWCP-wRedSlider'); var redSliderCtx = $redSlider.get(0).getContext('2d'); var redGradient = redSliderCtx.createLinearGradient(0, 0, 0, h); redGradient.addColorStop(0, "rgb(255,"+G+","+B+")"); redGradient.addColorStop(1, "rgb(0,"+G+","+B+")"); redSliderCtx.fillStyle = redGradient; redSliderCtx.fillRect(0, 0, w, h); /// GREEN /// // The top color is (R, 255, B) // The bottom color is (R, 0, B) var $greenSlider = $widget.find('.jQWCP-wGreenSlider'); var greenSliderCtx = $greenSlider.get(0).getContext('2d'); var greenGradient = greenSliderCtx.createLinearGradient(0, 0, 0, h); greenGradient.addColorStop(0, "rgb("+R+",255,"+B+")"); greenGradient.addColorStop(1, "rgb("+R+",0,"+B+")"); greenSliderCtx.fillStyle = greenGradient; greenSliderCtx.fillRect(0, 0, w, h); /// BLUE /// // The top color is (R, G, 255) // The bottom color is (R, G, 0) var $blueSlider = $widget.find('.jQWCP-wBlueSlider'); var blueSliderCtx = $blueSlider.get(0).getContext('2d'); var blueGradient = blueSliderCtx.createLinearGradient(0, 0, 0, h); blueGradient.addColorStop(0, "rgb("+R+","+G+",255)"); blueGradient.addColorStop(1, "rgb("+R+","+G+",0)"); blueSliderCtx.fillStyle = blueGradient; blueSliderCtx.fillRect(0, 0, w, h); /// HUE /// // The hue slider is static. var $hueSlider = $widget.find('.jQWCP-wHueSlider'); var hueSliderCtx = $hueSlider.get(0).getContext('2d'); var hueGradient = hueSliderCtx.createLinearGradient(0, 0, 0, h); hueGradient.addColorStop(0, "#f00"); hueGradient.addColorStop(0.166666667, "#ff0"); hueGradient.addColorStop(0.333333333, "#0f0"); hueGradient.addColorStop(0.5, "#0ff"); hueGradient.addColorStop(0.666666667, "#00f"); hueGradient.addColorStop(0.833333333, "#f0f"); hueGradient.addColorStop(1, "#f00"); hueSliderCtx.fillStyle = hueGradient; hueSliderCtx.fillRect(0, 0, w, h); /// SAT /// // The top color is hsv(h, 1, v) // The bottom color is hsv(0, 0, v) var satTopRgb = $.fn.wheelColorPicker.hsvToRgb(H, 1, V); satTopRgb.r = Math.round(satTopRgb.r * 255); satTopRgb.g = Math.round(satTopRgb.g * 255); satTopRgb.b = Math.round(satTopRgb.b * 255); var $satSlider = $widget.find('.jQWCP-wSatSlider'); var satSliderCtx = $satSlider.get(0).getContext('2d'); var satGradient = satSliderCtx.createLinearGradient(0, 0, 0, h); satGradient.addColorStop(0, "rgb("+satTopRgb.r+","+satTopRgb.g+","+satTopRgb.b+")"); satGradient.addColorStop(1, "rgb("+Math.round(V*255)+","+Math.round(V*255)+","+Math.round(V*255)+")"); satSliderCtx.fillStyle = satGradient; satSliderCtx.fillRect(0, 0, w, h); /// VAL /// // The top color is hsv(h, s, 1) // The bottom color is always black. var valTopRgb = $.fn.wheelColorPicker.hsvToRgb(H, S, 1); valTopRgb.r = Math.round(valTopRgb.r * 255); valTopRgb.g = Math.round(valTopRgb.g * 255); valTopRgb.b = Math.round(valTopRgb.b * 255); var $valSlider = $widget.find('.jQWCP-wValSlider'); var valSliderCtx = $valSlider.get(0).getContext('2d'); var valGradient = valSliderCtx.createLinearGradient(0, 0, 0, h); valGradient.addColorStop(0, "rgb("+valTopRgb.r+","+valTopRgb.g+","+valTopRgb.b+")"); valGradient.addColorStop(1, "#000"); valSliderCtx.fillStyle = valGradient; valSliderCtx.fillRect(0, 0, w, h); }); }; /** * Function: updateSliders * * Introduced in 2.0 * * Update slider positions. */ methods.updateSliders = function() { return this.each(function() { var $this = $(this); // Refers to input elm var settings = $this.data('jQWCP.settings'); var $widget = $( $this.data('jQWCP.widget') ); var color = $this.data('jQWCP.color'); // No need to redraw sliders on global popup widget if not // attached to the input elm in current iteration if(this != $widget.data('jQWCP.inputElm')) return; //~ console.log(color); // Wheel var $wheel = $widget.find('.jQWCP-wWheel'); var $wheelCursor = $widget.find('.jQWCP-wWheelCursor'); var $wheelOverlay = $widget.find('.jQWCP-wWheelOverlay'); var wheelX = Math.cos(2 * Math.PI * color.h) * color.s; var wheelY = Math.sin(2 * Math.PI * color.h) * color.s; var wheelOffsetX = $wheel.width() / 2; var wheelOffsetY = $wheel.height() / 2; $wheelCursor.css('left', (wheelOffsetX + (wheelX * $wheel.width() / 2)) + 'px'); $wheelCursor.css('top', (wheelOffsetY - (wheelY * $wheel.height() / 2)) + 'px'); if(settings.preserveWheel) { $wheelOverlay.css('opacity', 0); } else { $wheelOverlay.css('opacity', 1 - (color.v < 0.2 ? 0.2 : color.v)); } // Hue var $hueSlider = $widget.find('.jQWCP-wHueSlider'); var $hueCursor = $widget.find('.jQWCP-wHueCursor'); $hueCursor.css('top', (color.h * $hueSlider.height()) + 'px'); // Saturation var $satSlider = $widget.find('.jQWCP-wSatSlider'); var $satCursor = $widget.find('.jQWCP-wSatCursor'); $satCursor.css('top', ((1 - color.s) * $satSlider.height()) + 'px'); // Value var $valSlider = $widget.find('.jQWCP-wValSlider'); var $valCursor = $widget.find('.jQWCP-wValCursor'); $valCursor.css('top', ((1 - color.v) * $valSlider.height()) + 'px'); // Red var $redSlider = $widget.find('.jQWCP-wRedSlider'); var $redCursor = $widget.find('.jQWCP-wRedCursor'); $redCursor.css('top', ((1 - color.r) * $redSlider.height()) + 'px'); // Green var $greenSlider = $widget.find('.jQWCP-wGreenSlider'); var $greenCursor = $widget.find('.jQWCP-wGreenCursor'); $greenCursor.css('top', ((1 - color.g) * $greenSlider.height()) + 'px'); // Blue var $blueSlider = $widget.find('.jQWCP-wBlueSlider'); var $blueCursor = $widget.find('.jQWCP-wBlueCursor'); $blueCursor.css('top', ((1 - color.b) * $blueSlider.height()) + 'px'); // Alpha var $alphaSlider = $widget.find('.jQWCP-wAlphaSlider'); var $alphaCursor = $widget.find('.jQWCP-wAlphaCursor'); $alphaCursor.css('top', ((1 - color.a) * $alphaSlider.height()) + 'px'); }); }; /** * Function: updateSelection * * DEPRECATED in 2.0 * * Update color dialog selection to match current selectedColor value. */ methods.updateSelection = function() { methods.redrawSliders.call( $this ); return methods.updateSliders.call( $this ); }; /** * Function: setRgba * * Introduced in 2.0 * * Set color using RGBA combination. */ methods.setRgba = function( r, g, b, a ) { this.each(function() { var $this = $(this); // Refers to input elm var color = $this.data('jQWCP.color'); color.r = r; color.g = g; color.b = b; if(a != null) { color.a = a; } var hsv = $.fn.wheelColorPicker.rgbToHsv(r, g, b); color.h = hsv.h; color.s = hsv.s; color.v = hsv.v; //~ console.log(color); }); methods.updateSliders.call( this, "hsv" ); return methods.redrawSliders.call( this, "svrgba" ); }; /** * Function: setRgb * * Introduced in 2.0 * * Set color using RGB combination. */ methods.setRgb = function( r, g, b ) { return methods.setRgba.call( this, r, g, b, null ); }; /** * Function: setHsva * * Introduced in 2.0 * * Set color using HSVA combination. */ methods.setHsva = function( h, s, v, a ) { this.each(function() { var $this = $(this); // Refers to input elm var color = $this.data('jQWCP.color'); color.h = h; color.s = s; color.v = v; if(a != null) { color.a = a; } var rgb = $.fn.wheelColorPicker.hsvToRgb(h, s, v); color.r = rgb.r; color.g = rgb.g; color.b = rgb.b; //~ console.log(color); }); methods.updateSliders.call( this, "rgb" ); return methods.redrawSliders.call( this, "svrgba" ); }; /** * Function: setHsv * * Introduced in 2.0 * * Set color using HSV combination. */ methods.setHsv = function( h, s, v ) { return methods.setHsva.call( this, h, s, v, null ); }; /** * Function: setAlpha * * Introduced in 2.0 * * Set alpha value. */ methods.setAlpha = function( value ) { this.each(function() { var $this = $(this); // Refers to input elm var color = $this.data('jQWCP.color'); color.a = value; }); return methods.redrawSliders.call( this, "" ); }; /** * Function: alpha * * Introduced in 2.0 * DEPRECATED in 2.0 * * This function is made to maintain compatibility with deprecated * alpha option. */ methods.alpha = function( value ) { var settings = this.data('jQWCP.settings'); if( value == null ) { return settings.sliders.indexOf('a') == -1 ? false : true; } else if( value == true ) { if(settings.sliders.indexOf('a') == -1) return this.each(function() { var $this = $(this); // Refers to input control var settings = $this.data('jQWCP.settings'); settings.sliders += 'a'; }); } else if( value == false ) { return this.each(function() { var $this = $(this); // Refers to input control var settings = $this.data('jQWCP.settings'); settings.sliders.replace('a', ''); }); } }; /** * Function: color * * DEPRECATED in 2.0 * * Gets/sets color */ methods.color = function( value ) { if(value == null) { return methods.getValue.call( this ); } else { return methods.setValue.call( this, value ); } }; /** * Function: getColor * * Introduced in 2.0 * * Return color components as an object. The object consists of: * { * r: red * g: green * b: blue * h: hue * s: saturation * v: value * a: alpha * } */ methods.getColor = function() { return this.data('jQWCP.color'); }; /** * Function: setColor * * Introduced in 2.0 * * Set color by passing an object consisting of: * { r, g, b, a } or * { h, s, v, a } */ methods.setColor = function( color ) { if(color.r != null) { return methods.setRgba.call( this, color.r, color.g, color.b, color.a ); } else if(color.h != null) { return methods.setHsva.call( this, color.h, color.s, color.v, color.a ); } else if(color.a != null) { return methods.setAlpha.call( this, color.a ); } return this; }; /** * Function: getValue * * Get the color value as string. */ methods.getValue = function( format ) { var settings = this.data('jQWCP.settings'); var color = this.data('jQWCP.color'); if( format == null ) { format = settings.format; } // If settings.rounding is TRUE, round alpha value to N decimal digits if(settings.rounding >= 0) { color.a = Math.round(color.a * Math.pow(10, settings.rounding)) / Math.pow(10, settings.rounding); } return $.fn.wheelColorPicker.colorToStr( color, format ); }; /** * Function: setValue * * Set the color value as string. */ methods.setValue = function( value ) { var color = $.fn.wheelColorPicker.strToColor( value ); if(!color) return this; return methods.setColor.call( this, color ); } /** * Function: initWidget * * Initialize widget elements and layout */ private.initWidget = function( widget ) { var settings = this.data('jQWCP.settings'); var sCanvasSize = settings.quality * 50; /// WIDGET /// // Notice: We won't use canvas to draw the color wheel since // it may takes time and cause performance issue. var $widget = $( "