(function($) {

  $.fn.colorfield = function() {

    return this.each(function() {

      var field = $(this);

      // avoid multiple inits
      if(field.data('colorfield')) {
        return true;
      } else {
        field.data('colorfield', true);
      }

      // MINICOLORS PLUGIN CODE
      //
      // 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="field-icon"><i class="icon fa fa-paint-brush"></i></div>' +
            '<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);
          });
      }));
      // end of MINICOLORS PLUGIN CODE 


      // FIELD CONFIGURATION CODE
      $("input[data-field='colorfield']").each(function(){
        $(this).minicolors('settings',{
            format: $(this).attr('data-format'),
            opacity: $(this).attr('data-opacity'),
            control: $(this).attr('data-control'),
            swatches: $(this).attr('data-swatches') ? $(this).attr('data-swatches').split('|') : []
          });
      });
      // end of FIELD CONFIGURATION CODE

    });

  };

})(jQuery);