/** * Custom color picker for Beaver Builder based on Iris by Matt Wiebe. * * Iris Color Picker - v1.0.7 - 2014-11-28 * https://github.com/Automattic/Iris * Copyright (c) 2014 Matt Wiebe; Licensed GPLv2 */ /** * Global variable for referencing the color picker. */ var FLBuilderColorPicker; (function( $, undef ) { var FLBuilderColorPresets = [], UA = navigator.userAgent.toLowerCase(), isIE = navigator.appName === 'Microsoft Internet Explorer', IEVersion = isIE ? parseFloat( UA.match( /msie ([0-9]{1,}[\.0-9]{0,})/ )[1] ) : 0, nonGradientIE = ( isIE && IEVersion < 10 ), gradientType = false, vendorPrefixes = [ '-moz-', '-webkit-', '-o-', '-ms-' ]; /** * Run some tests to check if the current browser supports CSS3 gradients. * Sets gradientType accordingly. * * @since 1.6.4 * @method testGradientType */ function testGradientType() { var el, base, bgImageString = 'backgroundImage'; // check current browser is an IE version that doesn't support CSS3 Gradients if ( nonGradientIE ) { // if yes, set gradientType to filter gradientType = 'filter'; } else { // if no, runs a quick test to check if the browser supports modern gradient syntax el = $( '
' ); base = 'linear-gradient(top,#fff,#000)'; $.each( vendorPrefixes, function( i, val ){ el.css( bgImageString, val + base ); if ( el.css( bgImageString ).match( 'gradient' ) ) { gradientType = i; return false; } }); // check for legacy webkit gradient syntax if ( gradientType === false ) { el.css( 'background', '-webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#000))' ); if ( el.css( this.bgImageString ).match( 'gradient' ) ) { gradientType = 'webkit'; } } el.remove(); } } /** * Only for CSS3 gradients. oldIE will use a separate function. * * Accepts as many color stops as necessary from 2nd arg on, or 2nd * arg can be an array of color stops * * @since 1.6.4 * @method createGradient * @param {String} origin Gradient origin - top or left, defaults to left. * @return {String} Appropriate CSS3 gradient string for use in */ function createGradient( origin, stops ) { origin = ( origin === 'top' ) ? 'top' : 'left'; stops = Array.isArray( stops ) ? stops : Array.prototype.slice.call( arguments, 1 ); if ( gradientType === 'webkit' ) { return legacyWebkitGradient( origin, stops ); } else { return vendorPrefixes[ gradientType ] + 'linear-gradient(' + origin + ', ' + stops.join(', ') + ')'; } } /** * Gradients for stupid IE. * * @since 1.6.4 * @method stupidIEGradient * @param {String} origin * @return {Array} stops */ function stupidIEGradient( origin, stops ) { var type, self, lastIndex, filter, startPosProp, endPosProp, dimensionProp, template, html; origin = ( origin === 'top' ) ? 'top' : 'left'; stops = Array.isArray( stops ) ? stops : Array.prototype.slice.call( arguments, 1 ); // 8 hex: AARRGGBB // GradientType: 0 vertical, 1 horizontal type = ( origin === 'top' ) ? 0 : 1; self = $( this ); lastIndex = stops.length - 1; filter = 'filter'; startPosProp = ( type === 1 ) ? 'left' : 'top'; endPosProp = ( type === 1 ) ? 'right' : 'bottom'; dimensionProp = ( type === 1 ) ? 'height' : 'width'; template = ''; html = ''; // need a positioning context if ( self.css('position') === 'static' ) { self.css( {position: 'relative' } ); } stops = fillColorStops( stops ); $.each(stops, function( i, startColor ) { var endColor, endStop, filterVal; // we want two at a time. if we're on the last pair, bail. if ( i === lastIndex ) { return false; } endColor = stops[ i + 1 ]; //if our pairs are at the same color stop, moving along. if ( startColor.stop === endColor.stop ) { return; } endStop = 100 - parseFloat( endColor.stop ) + '%'; startColor.octoHex = new FLBuilderColor( startColor.color ).toIEOctoHex(); endColor.octoHex = new FLBuilderColor( endColor.color ).toIEOctoHex(); filterVal = 'progid:DXImageTransform.Microsoft.Gradient(GradientType=' + type + ', StartColorStr=\'' + startColor.octoHex + '\', EndColorStr=\'' + endColor.octoHex + '\')'; html += template.replace( '%start%', startColor.stop ).replace( '%end%', endStop ).replace( '%filter%', filterVal ); }); self.find( '.iris-ie-gradient-shim' ).remove(); $( html ).prependTo( self ); } /** * Builds CSS syntax for legacy webkit browsers. * * @see fillColorStops * @since 1.6.4 * @method legacyWebkitGradient * @param {String} origin Where the gradient starts. * @param {Array} colorList * @return {String} The correct CSS gradient syntax. */ function legacyWebkitGradient( origin, colorList ) { var stops = []; origin = ( origin === 'top' ) ? '0% 0%,0% 100%,' : '0% 100%,100% 100%,'; colorList = fillColorStops( colorList ); $.each( colorList, function( i, val ){ stops.push( 'color-stop(' + ( parseFloat( val.stop ) / 100 ) + ', ' + val.color + ')' ); }); return '-webkit-gradient(linear,' + origin + stops.join(',') + ')'; } /** * @since 1.6.4 * @method fillColorStops * @param {Array} colorList * @return {Array} */ function fillColorStops( colorList ) { var colors = [], percs = [], newColorList = [], lastIndex = colorList.length - 1; $.each( colorList, function( index, val ) { var color = val, perc = false, match = val.match( /1?[0-9]{1,2}%$/ ); if ( match ) { color = val.replace( /\s?1?[0-9]{1,2}%$/, '' ); perc = match.shift(); } colors.push( color ); percs.push( perc ); }); // back fill first and last if ( percs[0] === false ) { percs[0] = '0%'; } if ( percs[lastIndex] === false ) { percs[lastIndex] = '100%'; } percs = backFillColorStops( percs ); $.each( percs, function( i ){ newColorList[i] = { color: colors[i], stop: percs[i] }; }); return newColorList; } /** * @since 1.6.4 * @method backFillColorStops * @param {Array} stops * @return {Array} */ function backFillColorStops( stops ) { var first = 0, last = stops.length - 1, i = 0, foundFirst = false, incr, steps, step, firstVal; if ( stops.length <= 2 || $.inArray( false, stops ) < 0 ) { return stops; } while ( i < stops.length - 1 ) { if ( ! foundFirst && stops[i] === false ) { first = i - 1; foundFirst = true; } else if ( foundFirst && stops[i] !== false ) { last = i; i = stops.length; } i++; } steps = last - first; firstVal = parseInt( stops[first].replace('%'), 10 ); incr = ( parseFloat( stops[last].replace('%') ) - firstVal ) / steps; i = first + 1; step = 1; while ( i < last ) { stops[i] = ( firstVal + ( step * incr ) ) + '%'; step++; i++; } return backFillColorStops( stops ); } /** * @since 1.8.4 * @method flBuilderParseColorValue * @return {Array} */ flBuilderParseColorValue = function( val ) { var value = val.replace(/\s+/g, ''), rgba = ( value.indexOf('rgba') !== -1 ) ? true : false, alpha = rgba ? parseFloat( value.replace(/^.*,(.+)\)/, '$1') * 100 ) : 100; return { value: value, alpha: alpha, rgba: rgba }; } /** * @since 1.6.4 * @method $.fn.flBuilderColorPickerGradient * @return {Array} */ $.fn.flBuilderColorPickerGradient = function() { var args = arguments; return this.each( function() { // this'll be oldishIE if ( nonGradientIE ) { stupidIEGradient.apply( this, args ); } else { // new hotness $( this ).css( 'backgroundImage', createGradient.apply( this, args ) ); } }); }; /** * @since 1.6.4 * @method $.fn.flBuilderColorPickerRaninbowGradient * @return {Array} */ $.fn.flBuilderColorPickerRaninbowGradient = function( origin, args ) { var opts, template, i, steps; origin = origin || 'top'; opts = $.extend( {}, { s: 100, l: 50 }, args ); template = 'hsl(%h%,' + opts.s + '%,' + opts.l + '%)'; i = 0; steps = []; while ( i <= 360 ) { steps.push( template.replace('%h%', i) ); i += 30; } return this.each(function() { $(this).flBuilderColorPickerGradient( origin, steps ); }); }; /** * Main color picker class for Beaver Builder's custom implementation. * * @class FLBuilderColorPicker * @param {Object} settings * @since 1.6.4 */ FLBuilderColorPicker = function( settings ) { this._html = ''; // default settings var defaults = { elements : null, color : '', mode : 'hsl', controls : { horiz : 's', // horizontal defaults to saturation vert : 'l', // vertical defaults to lightness strip : 'h' // right strip defaults to hue }, target : false, // a DOM element / jQuery selector that the element will be appended within. Only used when called on an input. width : 200, // the width of the collection of UI elements presets: [], labels : { colorPresets : 'Color Presets', colorPicker : 'Color Picker', placeholder : 'Paste color here...', removePresetConfirm : 'Are you sure?', noneColorSelected : 'None color selected.', alreadySaved : '%s is already a saved preset.', noPresets : 'Add a color preset first.', presetAdded : '%s added to presets!', } }; // setting plugin options this.options = $.extend( {}, defaults, settings ); // Bail for IE <= 7 if ( nonGradientIE === false || nonGradientIE === true && IEVersion > 7 ) { // initialize the color picker single instance this._init(); } }; /** * Prototype for new instances. * * @since 1.6.4 * @property {Object} prototype */ FLBuilderColorPicker.prototype = { /** * Initial markup for the color picker. * * @since 1.6.4 * @property {String} _html */ _html : '', /** * Current set color for the color picker. * * @since 1.6.4 * @property {String} _color */ _color : '', /** * A reference to the current picker setting element. * * @since 1.6.4 * @property {Object} _currentElement */ _currentElement : '', /** * Whether the picker has been initialized or not. * * @since 1.6.4 * @property {Boolean} _inited */ _inited : false, /** * Defaults for the HSL controls. * * @since 1.6.4 * @property {Object} _defaultHSLControls */ _defaultHSLControls : { horiz : 's', vert : 'l', strip : 'h' }, /** * Defaults for the HSV controls. * * @since 1.6.4 * @property {Object} _defaultHSVControls */ _defaultHSVControls : { horiz : 'h', vert : 'v', strip : 's' }, /** * @since 1.6.4 * @property {Object} _scale */ _scale : { h: 360, s: 100, l: 100, v: 100 }, /** * Initializes this instance. * * @since 1.6.4 * @method _init */ _init: function(){ var self = this, el = $( self.options.elements, window.parent.document ); // Just prep the color inputs and bail early if the color picker // markup has already been initialized in the DOM. if( $( 'html', window.parent.document ).hasClass( 'fl-color-picker-init' ) ){ this._prepareColorFields(); return; } this._color = new FLBuilderColor( '#ff0000' ).setHSpace( self.options.mode ); // Set picker color presets FLBuilderColorPresets = this.options.presets; if ( gradientType === false ) { testGradientType(); } // appends color picker markup to the body // check if there's already a color picker instance self.picker = $( this._html ).appendTo( $( 'body', window.parent.document ) ); // Browsers / Versions // Feature detection doesn't work for these, and $.browser is deprecated if ( isIE ) { if ( IEVersion === 9 ) { self.picker.addClass( 'iris-ie-9' ); } else if ( IEVersion <= 8 ) { self.picker.addClass( 'iris-ie-lt9' ); } } else if ( UA.indexOf('compatible') < 0 && UA.indexOf('khtml') < 0 && UA.match( /mozilla/ ) ) { self.picker.addClass( 'iris-mozilla' ); } // prep 'em for re-use self.controls = { square : self.picker.find( '.iris-square' ), squareDrag : self.picker.find( '.iris-square-value' ), horiz : self.picker.find( '.iris-square-horiz' ), vert : self.picker.find( '.iris-square-vert' ), strip : self.picker.find( '.iris-strip' ), stripSlider : self.picker.find( '.iris-strip .iris-slider-offset' ) }; // small sanity check - if we chose hsv, change default controls away from hsl if ( self.options.mode === 'hsv' && self._has('l', self.options.controls) ) { self.options.controls = self._defaultHSVControls; } else if ( self.options.mode === 'hsl' && self._has('v', self.options.controls) ) { self.options.controls = self._defaultHSLControls; } // store it. HSL gets squirrely self.hue = self._color.h(); this._setTemplates(); // COLOR PRESETS UI -------------------------------------// // cache reference to the picker wrapper this._ui = $( '.fl-color-picker-ui', window.parent.document ); this._iris = $( '.iris-picker', window.parent.document ); this._wrapper = $( 'body', window.parent.document ); this._ui .prepend( this._hexHtml ) .append( this._presetsHtml ); self.element = this._ui.find( '.fl-color-picker-input' ); self._initControls(); self.active = 'external'; //self._dimensions(); self._change(); // binds listeners to all color picker instances self._addInputListeners( self.element ); // build the presets UI this._buildUI(); // adds needed markup and bind functions to all color fields this._prepareColorFields(); // bind picker control events this._pickerControls(); // bind presets control events this._presetsControls(); // adds opacity/alpha support this._buildAlphaUI(); // now we know that the picker is already added to the body $( 'html', window.parent.document ).addClass( 'fl-color-picker-init' ); }, /** * @since 1.6.4 * @method _prepareColorFields */ _prepareColorFields: function(){ var self = this; // append presets initial html and trigger that toggles the picker $( '.fl-color-picker-value', window.parent.document ).each( function(){ var $this = $( this ), $colorValue = $this.val(), $colorTrigger = $this.parent().find( '.fl-color-picker-color' ), $parsedValue = flBuilderParseColorValue( $colorValue ), $bgColor = ''; if( $colorValue ){ // set initial color, check for alpha support if ( $colorTrigger.hasClass('fl-color-picker-alpha-enabled') && $parsedValue.rgba ) { $bgColor = $this.val().toString(); } else if ( !$colorTrigger.hasClass('fl-color-picker-alpha-enabled') && $parsedValue.rgba ) { var $newColorValue = $colorValue.replace('rgba', 'rgb') $newColorValue = $newColorValue.substr(0, $newColorValue.lastIndexOf(",")) + ')'; self._color._alpha = 1; $bgColor = $newColorValue; $this.val($newColorValue); } else { $bgColor = '#' + $this.val().toString(); } $colorTrigger.css({ backgroundColor: $bgColor }); } }); }, /** * Sets templates to build the color picker markup. * * @since 1.6.4 * @method _setTemplates */ _setTemplates: function(){ this._alphaHtml = '