]> defiant.homedns.org Git - ros_wild_thumper.git/blobdiff - www/assets/javascripts/jquery.minicolors.js
www: Added minicolors colorpicker for lights
[ros_wild_thumper.git] / www / assets / javascripts / jquery.minicolors.js
diff --git a/www/assets/javascripts/jquery.minicolors.js b/www/assets/javascripts/jquery.minicolors.js
new file mode 100644 (file)
index 0000000..9b1690b
--- /dev/null
@@ -0,0 +1,1108 @@
+//
+// jQuery MiniColors: A tiny color picker built on jQuery
+//
+// Developed by Cory LaViska for A Beautiful Site, LLC
+//
+// Licensed under the MIT license: http://opensource.org/licenses/MIT
+//
+(function (factory) {
+  if(typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module.
+    define(['jquery'], factory);
+  } else if(typeof exports === 'object') {
+    // Node/CommonJS
+    module.exports = factory(require('jquery'));
+  } else {
+    // Browser globals
+    factory(jQuery);
+  }
+}(function ($) {
+  'use strict';
+
+  // Defaults
+  $.minicolors = {
+    defaults: {
+      animationSpeed: 50,
+      animationEasing: 'swing',
+      change: null,
+      changeDelay: 0,
+      control: 'hue',
+      defaultValue: '',
+      format: 'hex',
+      hide: null,
+      hideSpeed: 100,
+      inline: false,
+      keywords: '',
+      letterCase: 'lowercase',
+      opacity: false,
+      position: 'bottom left',
+      show: null,
+      showSpeed: 100,
+      theme: 'default',
+      swatches: []
+    }
+  };
+
+  // Public methods
+  $.extend($.fn, {
+    minicolors: function(method, data) {
+
+      switch(method) {
+      // Destroy the control
+      case 'destroy':
+        $(this).each(function() {
+          destroy($(this));
+        });
+        return $(this);
+
+      // Hide the color picker
+      case 'hide':
+        hide();
+        return $(this);
+
+      // Get/set opacity
+      case 'opacity':
+        // Getter
+        if(data === undefined) {
+          // Getter
+          return $(this).attr('data-opacity');
+        } else {
+          // Setter
+          $(this).each(function() {
+            updateFromInput($(this).attr('data-opacity', data));
+          });
+        }
+        return $(this);
+
+      // Get an RGB(A) object based on the current color/opacity
+      case 'rgbObject':
+        return rgbObject($(this), method === 'rgbaObject');
+
+      // Get an RGB(A) string based on the current color/opacity
+      case 'rgbString':
+      case 'rgbaString':
+        return rgbString($(this), method === 'rgbaString');
+
+      // Get/set settings on the fly
+      case 'settings':
+        if(data === undefined) {
+          return $(this).data('minicolors-settings');
+        } else {
+          // Setter
+          $(this).each(function() {
+            var settings = $(this).data('minicolors-settings') || {};
+            destroy($(this));
+            $(this).minicolors($.extend(true, settings, data));
+          });
+        }
+        return $(this);
+
+      // Show the color picker
+      case 'show':
+        show($(this).eq(0));
+        return $(this);
+
+      // Get/set the hex color value
+      case 'value':
+        if(data === undefined) {
+          // Getter
+          return $(this).val();
+        } else {
+          // Setter
+          $(this).each(function() {
+            if(typeof(data) === 'object' && data !== null) {
+              if(data.opacity) {
+                $(this).attr('data-opacity', keepWithin(data.opacity, 0, 1));
+              }
+              if(data.color) {
+                $(this).val(data.color);
+              }
+            } else {
+              $(this).val(data);
+            }
+            updateFromInput($(this));
+          });
+        }
+        return $(this);
+
+      // Initializes the control
+      default:
+        if(method !== 'create') data = method;
+        $(this).each(function() {
+          init($(this), data);
+        });
+        return $(this);
+
+      }
+
+    }
+  });
+
+  // Initialize input elements
+  function init(input, settings) {
+    var minicolors = $('<div class="minicolors" />');
+    var defaults = $.minicolors.defaults;
+    var size;
+    var swatches;
+    var swatch;
+    var panel;
+    var i;
+
+    // Do nothing if already initialized
+    if(input.data('minicolors-initialized')) return;
+
+    // Handle settings
+    settings = $.extend(true, {}, defaults, settings);
+
+    // The wrapper
+    minicolors
+    .addClass('minicolors-theme-' + settings.theme)
+    .toggleClass('minicolors-with-opacity', settings.opacity);
+
+    // Custom positioning
+    if(settings.position !== undefined) {
+      $.each(settings.position.split(' '), function() {
+        minicolors.addClass('minicolors-position-' + this);
+      });
+    }
+
+    // Input size
+    if(settings.format === 'rgb') {
+      size = settings.opacity ? '25' : '20';
+    } else {
+      size = settings.keywords ? '11' : '7';
+    }
+
+    // The input
+    input
+    .addClass('minicolors-input')
+    .data('minicolors-initialized', false)
+    .data('minicolors-settings', settings)
+    .prop('size', size)
+    .wrap(minicolors)
+    .after(
+      '<div class="minicolors-panel minicolors-slider-' + settings.control + '">' +
+      '<div class="minicolors-slider minicolors-sprite">' +
+      '<div class="minicolors-picker"></div>' +
+      '</div>' +
+      '<div class="minicolors-opacity-slider minicolors-sprite">' +
+      '<div class="minicolors-picker"></div>' +
+      '</div>' +
+      '<div class="minicolors-grid minicolors-sprite">' +
+      '<div class="minicolors-grid-inner"></div>' +
+      '<div class="minicolors-picker"><div></div></div>' +
+      '</div>' +
+      '</div>'
+    );
+
+    // The swatch
+    if(!settings.inline) {
+      input.after('<span class="minicolors-swatch minicolors-sprite minicolors-input-swatch"><span class="minicolors-swatch-color"></span></span>');
+      input.next('.minicolors-input-swatch').on('click', function(event) {
+        event.preventDefault();
+        input.focus();
+      });
+    }
+
+    // Prevent text selection in IE
+    panel = input.parent().find('.minicolors-panel');
+    panel.on('selectstart', function() { return false; }).end();
+
+    // Swatches
+    if(settings.swatches && settings.swatches.length !== 0) {
+      panel.addClass('minicolors-with-swatches');
+      swatches = $('<ul class="minicolors-swatches"></ul>')
+      .appendTo(panel);
+      for(i = 0; i < settings.swatches.length; ++i) {
+        swatch = settings.swatches[i];
+        swatch = isRgb(swatch) ? parseRgb(swatch, true) : hex2rgb(parseHex(swatch, true));
+        $('<li class="minicolors-swatch minicolors-sprite"><span class="minicolors-swatch-color"></span></li>')
+        .appendTo(swatches)
+        .data('swatch-color', settings.swatches[i])
+        .find('.minicolors-swatch-color')
+        .css({
+          backgroundColor: rgb2hex(swatch),
+          opacity: swatch.a
+        });
+        settings.swatches[i] = swatch;
+      }
+    }
+
+    // Inline controls
+    if(settings.inline) input.parent().addClass('minicolors-inline');
+
+    updateFromInput(input, false);
+
+    input.data('minicolors-initialized', true);
+  }
+
+  // Returns the input back to its original state
+  function destroy(input) {
+    var minicolors = input.parent();
+
+    // Revert the input element
+    input
+      .removeData('minicolors-initialized')
+      .removeData('minicolors-settings')
+      .removeProp('size')
+      .removeClass('minicolors-input');
+
+    // Remove the wrap and destroy whatever remains
+    minicolors.before(input).remove();
+  }
+
+  // Shows the specified dropdown panel
+  function show(input) {
+    var minicolors = input.parent();
+    var panel = minicolors.find('.minicolors-panel');
+    var settings = input.data('minicolors-settings');
+
+    // Do nothing if uninitialized, disabled, inline, or already open
+    if(
+      !input.data('minicolors-initialized') ||
+      input.prop('disabled') ||
+      minicolors.hasClass('minicolors-inline') ||
+      minicolors.hasClass('minicolors-focus')
+    ) return;
+
+    hide();
+
+    minicolors.addClass('minicolors-focus');
+    panel
+    .stop(true, true)
+    .fadeIn(settings.showSpeed, function() {
+      if(settings.show) settings.show.call(input.get(0));
+    });
+  }
+
+  // Hides all dropdown panels
+  function hide() {
+    $('.minicolors-focus').each(function() {
+      var minicolors = $(this);
+      var input = minicolors.find('.minicolors-input');
+      var panel = minicolors.find('.minicolors-panel');
+      var settings = input.data('minicolors-settings');
+
+      panel.fadeOut(settings.hideSpeed, function() {
+        if(settings.hide) settings.hide.call(input.get(0));
+        minicolors.removeClass('minicolors-focus');
+      });
+
+    });
+  }
+
+  // Moves the selected picker
+  function move(target, event, animate) {
+    var input = target.parents('.minicolors').find('.minicolors-input');
+    var settings = input.data('minicolors-settings');
+    var picker = target.find('[class$=-picker]');
+    var offsetX = target.offset().left;
+    var offsetY = target.offset().top;
+    var x = Math.round(event.pageX - offsetX);
+    var y = Math.round(event.pageY - offsetY);
+    var duration = animate ? settings.animationSpeed : 0;
+    var wx, wy, r, phi;
+
+    // Touch support
+    if(event.originalEvent.changedTouches) {
+      x = event.originalEvent.changedTouches[0].pageX - offsetX;
+      y = event.originalEvent.changedTouches[0].pageY - offsetY;
+    }
+
+    // Constrain picker to its container
+    if(x < 0) x = 0;
+    if(y < 0) y = 0;
+    if(x > target.width()) x = target.width();
+    if(y > target.height()) y = target.height();
+
+    // Constrain color wheel values to the wheel
+    if(target.parent().is('.minicolors-slider-wheel') && picker.parent().is('.minicolors-grid')) {
+      wx = 75 - x;
+      wy = 75 - y;
+      r = Math.sqrt(wx * wx + wy * wy);
+      phi = Math.atan2(wy, wx);
+      if(phi < 0) phi += Math.PI * 2;
+      if(r > 75) {
+        r = 75;
+        x = 75 - (75 * Math.cos(phi));
+        y = 75 - (75 * Math.sin(phi));
+      }
+      x = Math.round(x);
+      y = Math.round(y);
+    }
+
+    // Move the picker
+    if(target.is('.minicolors-grid')) {
+      picker
+      .stop(true)
+      .animate({
+        top: y + 'px',
+        left: x + 'px'
+      }, duration, settings.animationEasing, function() {
+        updateFromControl(input, target);
+      });
+    } else {
+      picker
+      .stop(true)
+      .animate({
+        top: y + 'px'
+      }, duration, settings.animationEasing, function() {
+        updateFromControl(input, target);
+      });
+    }
+  }
+
+  // Sets the input based on the color picker values
+  function updateFromControl(input, target) {
+
+    function getCoords(picker, container) {
+      var left, top;
+      if(!picker.length || !container) return null;
+      left = picker.offset().left;
+      top = picker.offset().top;
+
+      return {
+        x: left - container.offset().left + (picker.outerWidth() / 2),
+        y: top - container.offset().top + (picker.outerHeight() / 2)
+      };
+    }
+
+    var hue, saturation, brightness, x, y, r, phi;
+    var hex = input.val();
+    var opacity = input.attr('data-opacity');
+
+    // Helpful references
+    var minicolors = input.parent();
+    var settings = input.data('minicolors-settings');
+    var swatch = minicolors.find('.minicolors-input-swatch');
+
+    // Panel objects
+    var grid = minicolors.find('.minicolors-grid');
+    var slider = minicolors.find('.minicolors-slider');
+    var opacitySlider = minicolors.find('.minicolors-opacity-slider');
+
+    // Picker objects
+    var gridPicker = grid.find('[class$=-picker]');
+    var sliderPicker = slider.find('[class$=-picker]');
+    var opacityPicker = opacitySlider.find('[class$=-picker]');
+
+    // Picker positions
+    var gridPos = getCoords(gridPicker, grid);
+    var sliderPos = getCoords(sliderPicker, slider);
+    var opacityPos = getCoords(opacityPicker, opacitySlider);
+
+    // Handle colors
+    if(target.is('.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider')) {
+
+      // Determine HSB values
+      switch(settings.control) {
+      case 'wheel':
+        // Calculate hue, saturation, and brightness
+        x = (grid.width() / 2) - gridPos.x;
+        y = (grid.height() / 2) - gridPos.y;
+        r = Math.sqrt(x * x + y * y);
+        phi = Math.atan2(y, x);
+        if(phi < 0) phi += Math.PI * 2;
+        if(r > 75) {
+          r = 75;
+          gridPos.x = 69 - (75 * Math.cos(phi));
+          gridPos.y = 69 - (75 * Math.sin(phi));
+        }
+        saturation = keepWithin(r / 0.75, 0, 100);
+        hue = keepWithin(phi * 180 / Math.PI, 0, 360);
+        brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
+        hex = hsb2hex({
+          h: hue,
+          s: saturation,
+          b: brightness
+        });
+
+        // Update UI
+        slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
+        break;
+
+      case 'saturation':
+        // Calculate hue, saturation, and brightness
+        hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
+        saturation = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
+        brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
+        hex = hsb2hex({
+          h: hue,
+          s: saturation,
+          b: brightness
+        });
+
+        // Update UI
+        slider.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: brightness }));
+        minicolors.find('.minicolors-grid-inner').css('opacity', saturation / 100);
+        break;
+
+      case 'brightness':
+        // Calculate hue, saturation, and brightness
+        hue = keepWithin(parseInt(gridPos.x * (360 / grid.width()), 10), 0, 360);
+        saturation = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
+        brightness = keepWithin(100 - Math.floor(sliderPos.y * (100 / slider.height())), 0, 100);
+        hex = hsb2hex({
+          h: hue,
+          s: saturation,
+          b: brightness
+        });
+
+        // Update UI
+        slider.css('backgroundColor', hsb2hex({ h: hue, s: saturation, b: 100 }));
+        minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (brightness / 100));
+        break;
+
+      default:
+        // Calculate hue, saturation, and brightness
+        hue = keepWithin(360 - parseInt(sliderPos.y * (360 / slider.height()), 10), 0, 360);
+        saturation = keepWithin(Math.floor(gridPos.x * (100 / grid.width())), 0, 100);
+        brightness = keepWithin(100 - Math.floor(gridPos.y * (100 / grid.height())), 0, 100);
+        hex = hsb2hex({
+          h: hue,
+          s: saturation,
+          b: brightness
+        });
+
+        // Update UI
+        grid.css('backgroundColor', hsb2hex({ h: hue, s: 100, b: 100 }));
+        break;
+      }
+
+      // Handle opacity
+      if(settings.opacity) {
+        opacity = parseFloat(1 - (opacityPos.y / opacitySlider.height())).toFixed(2);
+      } else {
+        opacity = 1;
+      }
+
+      updateInput(input, hex, opacity);
+    }
+    else {
+      // Set swatch color
+      swatch.find('span').css({
+        backgroundColor: hex,
+        opacity: opacity
+      });
+
+      // Handle change event
+      doChange(input, hex, opacity);
+    }
+  }
+
+  // Sets the value of the input and does the appropriate conversions
+  // to respect settings, also updates the swatch
+  function updateInput(input, value, opacity) {
+    var rgb;
+
+    // Helpful references
+    var minicolors = input.parent();
+    var settings = input.data('minicolors-settings');
+    var swatch = minicolors.find('.minicolors-input-swatch');
+
+    if(settings.opacity) input.attr('data-opacity', opacity);
+
+    // Set color string
+    if(settings.format === 'rgb') {
+      // Returns RGB(A) string
+
+      // Checks for input format and does the conversion
+      if(isRgb(value)) {
+        rgb = parseRgb(value, true);
+      }
+      else {
+        rgb = hex2rgb(parseHex(value, true));
+      }
+
+      opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
+      if(isNaN(opacity) || !settings.opacity) opacity = 1;
+
+      if(input.minicolors('rgbObject').a <= 1 && rgb && settings.opacity) {
+        // Set RGBA string if alpha
+        value = 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
+      } else {
+        // Set RGB string (alpha = 1)
+        value = 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
+      }
+    } else {
+      // Returns hex color
+
+      // Checks for input format and does the conversion
+      if(isRgb(value)) {
+        value = rgbString2hex(value);
+      }
+
+      value = convertCase(value, settings.letterCase);
+    }
+
+    // Update value from picker
+    input.val(value);
+
+    // Set swatch color
+    swatch.find('span').css({
+      backgroundColor: value,
+      opacity: opacity
+    });
+
+    // Handle change event
+    doChange(input, value, opacity);
+  }
+
+  // Sets the color picker values from the input
+  function updateFromInput(input, preserveInputValue) {
+    var hex, hsb, opacity, keywords, alpha, value, x, y, r, phi;
+
+    // Helpful references
+    var minicolors = input.parent();
+    var settings = input.data('minicolors-settings');
+    var swatch = minicolors.find('.minicolors-input-swatch');
+
+    // Panel objects
+    var grid = minicolors.find('.minicolors-grid');
+    var slider = minicolors.find('.minicolors-slider');
+    var opacitySlider = minicolors.find('.minicolors-opacity-slider');
+
+    // Picker objects
+    var gridPicker = grid.find('[class$=-picker]');
+    var sliderPicker = slider.find('[class$=-picker]');
+    var opacityPicker = opacitySlider.find('[class$=-picker]');
+
+    // Determine hex/HSB values
+    if(isRgb(input.val())) {
+      // If input value is a rgb(a) string, convert it to hex color and update opacity
+      hex = rgbString2hex(input.val());
+      alpha = keepWithin(parseFloat(getAlpha(input.val())).toFixed(2), 0, 1);
+      if(alpha) {
+        input.attr('data-opacity', alpha);
+      }
+    } else {
+      hex = convertCase(parseHex(input.val(), true), settings.letterCase);
+    }
+
+    if(!hex){
+      hex = convertCase(parseInput(settings.defaultValue, true), settings.letterCase);
+    }
+    hsb = hex2hsb(hex);
+
+    // Get array of lowercase keywords
+    keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
+      return $.trim(a.toLowerCase());
+    });
+
+    // Set color string
+    if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) {
+      value = convertCase(input.val());
+    } else {
+      value = isRgb(input.val()) ? parseRgb(input.val()) : hex;
+    }
+
+    // Update input value
+    if(!preserveInputValue) input.val(value);
+
+    // Determine opacity value
+    if(settings.opacity) {
+      // Get from data-opacity attribute and keep within 0-1 range
+      opacity = input.attr('data-opacity') === '' ? 1 : keepWithin(parseFloat(input.attr('data-opacity')).toFixed(2), 0, 1);
+      if(isNaN(opacity)) opacity = 1;
+      input.attr('data-opacity', opacity);
+      swatch.find('span').css('opacity', opacity);
+
+      // Set opacity picker position
+      y = keepWithin(opacitySlider.height() - (opacitySlider.height() * opacity), 0, opacitySlider.height());
+      opacityPicker.css('top', y + 'px');
+    }
+
+    // Set opacity to zero if input value is transparent
+    if(input.val().toLowerCase() === 'transparent') {
+      swatch.find('span').css('opacity', 0);
+    }
+
+    // Update swatch
+    swatch.find('span').css('backgroundColor', hex);
+
+    // Determine picker locations
+    switch(settings.control) {
+    case 'wheel':
+      // Set grid position
+      r = keepWithin(Math.ceil(hsb.s * 0.75), 0, grid.height() / 2);
+      phi = hsb.h * Math.PI / 180;
+      x = keepWithin(75 - Math.cos(phi) * r, 0, grid.width());
+      y = keepWithin(75 - Math.sin(phi) * r, 0, grid.height());
+      gridPicker.css({
+        top: y + 'px',
+        left: x + 'px'
+      });
+
+      // Set slider position
+      y = 150 - (hsb.b / (100 / grid.height()));
+      if(hex === '') y = 0;
+      sliderPicker.css('top', y + 'px');
+
+      // Update panel color
+      slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
+      break;
+
+    case 'saturation':
+      // Set grid position
+      x = keepWithin((5 * hsb.h) / 12, 0, 150);
+      y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
+      gridPicker.css({
+        top: y + 'px',
+        left: x + 'px'
+      });
+
+      // Set slider position
+      y = keepWithin(slider.height() - (hsb.s * (slider.height() / 100)), 0, slider.height());
+      sliderPicker.css('top', y + 'px');
+
+      // Update UI
+      slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: hsb.b }));
+      minicolors.find('.minicolors-grid-inner').css('opacity', hsb.s / 100);
+      break;
+
+    case 'brightness':
+      // Set grid position
+      x = keepWithin((5 * hsb.h) / 12, 0, 150);
+      y = keepWithin(grid.height() - Math.ceil(hsb.s / (100 / grid.height())), 0, grid.height());
+      gridPicker.css({
+        top: y + 'px',
+        left: x + 'px'
+      });
+
+      // Set slider position
+      y = keepWithin(slider.height() - (hsb.b * (slider.height() / 100)), 0, slider.height());
+      sliderPicker.css('top', y + 'px');
+
+      // Update UI
+      slider.css('backgroundColor', hsb2hex({ h: hsb.h, s: hsb.s, b: 100 }));
+      minicolors.find('.minicolors-grid-inner').css('opacity', 1 - (hsb.b / 100));
+      break;
+
+    default:
+      // Set grid position
+      x = keepWithin(Math.ceil(hsb.s / (100 / grid.width())), 0, grid.width());
+      y = keepWithin(grid.height() - Math.ceil(hsb.b / (100 / grid.height())), 0, grid.height());
+      gridPicker.css({
+        top: y + 'px',
+        left: x + 'px'
+      });
+
+      // Set slider position
+      y = keepWithin(slider.height() - (hsb.h / (360 / slider.height())), 0, slider.height());
+      sliderPicker.css('top', y + 'px');
+
+      // Update panel color
+      grid.css('backgroundColor', hsb2hex({ h: hsb.h, s: 100, b: 100 }));
+      break;
+    }
+
+    // Fire change event, but only if minicolors is fully initialized
+    if(input.data('minicolors-initialized')) {
+      doChange(input, value, opacity);
+    }
+  }
+
+  // Runs the change and changeDelay callbacks
+  function doChange(input, value, opacity) {
+    var settings = input.data('minicolors-settings');
+    var lastChange = input.data('minicolors-lastChange');
+    var obj, sel, i;
+
+    // Only run if it actually changed
+    if(!lastChange || lastChange.value !== value || lastChange.opacity !== opacity) {
+
+      // Remember last-changed value
+      input.data('minicolors-lastChange', {
+        value: value,
+        opacity: opacity
+      });
+
+      // Check and select applicable swatch
+      if(settings.swatches && settings.swatches.length !== 0) {
+        if(!isRgb(value)) {
+          obj = hex2rgb(value);
+        }
+        else {
+          obj = parseRgb(value, true);
+        }
+        sel = -1;
+        for(i = 0; i < settings.swatches.length; ++i) {
+          if(obj.r === settings.swatches[i].r && obj.g === settings.swatches[i].g && obj.b === settings.swatches[i].b && obj.a === settings.swatches[i].a) {
+            sel = i;
+            break;
+          }
+        }
+
+        input.parent().find('.minicolors-swatches .minicolors-swatch').removeClass('selected');
+        if(sel !== -1) {
+          input.parent().find('.minicolors-swatches .minicolors-swatch').eq(i).addClass('selected');
+        }
+      }
+
+      // Fire change event
+      if(settings.change) {
+        if(settings.changeDelay) {
+          // Call after a delay
+          clearTimeout(input.data('minicolors-changeTimeout'));
+          input.data('minicolors-changeTimeout', setTimeout(function() {
+            settings.change.call(input.get(0), value, opacity);
+          }, settings.changeDelay));
+        } else {
+          // Call immediately
+          settings.change.call(input.get(0), value, opacity);
+        }
+      }
+      input.trigger('change').trigger('input');
+    }
+  }
+
+  // Generates an RGB(A) object based on the input's value
+  function rgbObject(input) {
+    var rgb,
+      opacity = $(input).attr('data-opacity');
+    if( isRgb($(input).val()) ) {
+      rgb = parseRgb($(input).val(), true);
+    } else {
+      var hex = parseHex($(input).val(), true);
+      rgb = hex2rgb(hex);
+    }
+    if( !rgb ) return null;
+    if( opacity !== undefined ) $.extend(rgb, { a: parseFloat(opacity) });
+    return rgb;
+  }
+
+  // Generates an RGB(A) string based on the input's value
+  function rgbString(input, alpha) {
+    var rgb,
+      opacity = $(input).attr('data-opacity');
+    if( isRgb($(input).val()) ) {
+      rgb = parseRgb($(input).val(), true);
+    } else {
+      var hex = parseHex($(input).val(), true);
+      rgb = hex2rgb(hex);
+    }
+    if( !rgb ) return null;
+    if( opacity === undefined ) opacity = 1;
+    if( alpha ) {
+      return 'rgba(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ', ' + parseFloat(opacity) + ')';
+    } else {
+      return 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')';
+    }
+  }
+
+  // Converts to the letter case specified in settings
+  function convertCase(string, letterCase) {
+    return letterCase === 'uppercase' ? string.toUpperCase() : string.toLowerCase();
+  }
+
+  // Parses a string and returns a valid hex string when possible
+  function parseHex(string, expand) {
+    string = string.replace(/^#/g, '');
+    if(!string.match(/^[A-F0-9]{3,6}/ig)) return '';
+    if(string.length !== 3 && string.length !== 6) return '';
+    if(string.length === 3 && expand) {
+      string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2];
+    }
+    return '#' + string;
+  }
+
+  // Parses a string and returns a valid RGB(A) string when possible
+  function parseRgb(string, obj) {
+    var values = string.replace(/[^\d,.]/g, '');
+    var rgba = values.split(',');
+
+    rgba[0] = keepWithin(parseInt(rgba[0], 10), 0, 255);
+    rgba[1] = keepWithin(parseInt(rgba[1], 10), 0, 255);
+    rgba[2] = keepWithin(parseInt(rgba[2], 10), 0, 255);
+    if(rgba[3]) {
+      rgba[3] = keepWithin(parseFloat(rgba[3], 10), 0, 1);
+    }
+
+    // Return RGBA object
+    if( obj ) {
+      if (rgba[3]) {
+        return {
+          r: rgba[0],
+          g: rgba[1],
+          b: rgba[2],
+          a: rgba[3]
+        };
+      } else {
+        return {
+          r: rgba[0],
+          g: rgba[1],
+          b: rgba[2]
+        };
+      }
+    }
+
+    // Return RGBA string
+    if(typeof(rgba[3]) !== 'undefined' && rgba[3] <= 1) {
+      return 'rgba(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ', ' + rgba[3] + ')';
+    } else {
+      return 'rgb(' + rgba[0] + ', ' + rgba[1] + ', ' + rgba[2] + ')';
+    }
+
+  }
+
+  // Parses a string and returns a valid color string when possible
+  function parseInput(string, expand) {
+    if(isRgb(string)) {
+      // Returns a valid rgb(a) string
+      return parseRgb(string);
+    } else {
+      return parseHex(string, expand);
+    }
+  }
+
+  // Keeps value within min and max
+  function keepWithin(value, min, max) {
+    if(value < min) value = min;
+    if(value > max) value = max;
+    return value;
+  }
+
+  // Checks if a string is a valid RGB(A) string
+  function isRgb(string) {
+    var rgb = string.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
+    return (rgb && rgb.length === 4) ? true : false;
+  }
+
+  // Function to get alpha from a RGB(A) string
+  function getAlpha(rgba) {
+    rgba = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.\d{1,2})?|\.\d{1,2})[\s+]?/i);
+    return (rgba && rgba.length === 6) ? rgba[4] : '1';
+  }
+
+  // Converts an HSB object to an RGB object
+  function hsb2rgb(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)
+    };
+  }
+
+  // Converts an RGB string to a hex string
+  function rgbString2hex(rgb){
+    rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
+    return (rgb && rgb.length === 4) ? '#' +
+    ('0' + parseInt(rgb[1],10).toString(16)).slice(-2) +
+    ('0' + parseInt(rgb[2],10).toString(16)).slice(-2) +
+    ('0' + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
+  }
+
+  // Converts an RGB object to a hex string
+  function rgb2hex(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('');
+  }
+
+  // Converts an HSB object to a hex string
+  function hsb2hex(hsb) {
+    return rgb2hex(hsb2rgb(hsb));
+  }
+
+  // Converts a hex string to an HSB object
+  function hex2hsb(hex) {
+    var hsb = rgb2hsb(hex2rgb(hex));
+    if(hsb.s === 0) hsb.h = 360;
+    return hsb;
+  }
+
+  // Converts an RGB object to an HSB object
+  function rgb2hsb(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;
+    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;
+  }
+
+  // Converts a hex string to an RGB object
+  function hex2rgb(hex) {
+    hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
+    return {
+      r: hex >> 16,
+      g: (hex & 0x00FF00) >> 8,
+      b: (hex & 0x0000FF)
+    };
+  }
+
+  // Handle events
+  $([document])
+    // Hide on clicks outside of the control
+    .on('mousedown.minicolors touchstart.minicolors', function(event) {
+      if(!$(event.target).parents().add(event.target).hasClass('minicolors')) {
+        hide();
+      }
+    })
+    // Start moving
+    .on('mousedown.minicolors touchstart.minicolors', '.minicolors-grid, .minicolors-slider, .minicolors-opacity-slider', function(event) {
+      var target = $(this);
+      event.preventDefault();
+      $(event.delegateTarget).data('minicolors-target', target);
+      move(target, event, true);
+    })
+    // Move pickers
+    .on('mousemove.minicolors touchmove.minicolors', function(event) {
+      var target = $(event.delegateTarget).data('minicolors-target');
+      if(target) move(target, event);
+    })
+    // Stop moving
+    .on('mouseup.minicolors touchend.minicolors', function() {
+      $(this).removeData('minicolors-target');
+    })
+    // Selected a swatch
+    .on('click.minicolors', '.minicolors-swatches li', function(event) {
+      event.preventDefault();
+      var target = $(this), input = target.parents('.minicolors').find('.minicolors-input'), color = target.data('swatch-color');
+      updateInput(input, color, getAlpha(color));
+      updateFromInput(input);
+    })
+    // Show panel when swatch is clicked
+    .on('mousedown.minicolors touchstart.minicolors', '.minicolors-input-swatch', function(event) {
+      var input = $(this).parent().find('.minicolors-input');
+      event.preventDefault();
+      show(input);
+    })
+    // Show on focus
+    .on('focus.minicolors', '.minicolors-input', function() {
+      var input = $(this);
+      if(!input.data('minicolors-initialized')) return;
+      show(input);
+    })
+    // Update value on blur
+    .on('blur.minicolors', '.minicolors-input', function() {
+      var input = $(this);
+      var settings = input.data('minicolors-settings');
+      var keywords;
+      var hex;
+      var rgba;
+      var swatchOpacity;
+      var value;
+
+      if(!input.data('minicolors-initialized')) return;
+
+      // Get array of lowercase keywords
+      keywords = !settings.keywords ? [] : $.map(settings.keywords.split(','), function(a) {
+        return $.trim(a.toLowerCase());
+      });
+
+      // Set color string
+      if(input.val() !== '' && $.inArray(input.val().toLowerCase(), keywords) > -1) {
+        value = input.val();
+      } else {
+        // Get RGBA values for easy conversion
+        if(isRgb(input.val())) {
+          rgba = parseRgb(input.val(), true);
+        } else {
+          hex = parseHex(input.val(), true);
+          rgba = hex ? hex2rgb(hex) : null;
+        }
+
+        // Convert to format
+        if(rgba === null) {
+          value = settings.defaultValue;
+        } else if(settings.format === 'rgb') {
+          value = settings.opacity ?
+          parseRgb('rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + input.attr('data-opacity') + ')') :
+          parseRgb('rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')');
+        } else {
+          value = rgb2hex(rgba);
+        }
+      }
+
+      // Update swatch opacity
+      swatchOpacity = settings.opacity ? input.attr('data-opacity') : 1;
+      if(value.toLowerCase() === 'transparent') swatchOpacity = 0;
+      input
+      .closest('.minicolors')
+      .find('.minicolors-input-swatch > span')
+      .css('opacity', swatchOpacity);
+
+      // Set input value
+      input.val(value);
+
+      // Is it blank?
+      if(input.val() === '') input.val(parseInput(settings.defaultValue, true));
+
+      // Adjust case
+      input.val(convertCase(input.val(), settings.letterCase));
+
+    })
+    // Handle keypresses
+    .on('keydown.minicolors', '.minicolors-input', function(event) {
+      var input = $(this);
+      if(!input.data('minicolors-initialized')) return;
+      switch(event.keyCode) {
+      case 9: // tab
+        hide();
+        break;
+      case 13: // enter
+      case 27: // esc
+        hide();
+        input.blur();
+        break;
+      }
+    })
+    // Update on keyup
+    .on('keyup.minicolors', '.minicolors-input', function() {
+      var input = $(this);
+      if(!input.data('minicolors-initialized')) return;
+      updateFromInput(input, true);
+    })
+    // Update on paste
+    .on('paste.minicolors', '.minicolors-input', function() {
+      var input = $(this);
+      if(!input.data('minicolors-initialized')) return;
+      setTimeout(function() {
+        updateFromInput(input, true);
+      }, 1);
+    });
+}));