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