UNPKG

ngx-color-picker

Version:
1 lines 130 kB
{"version":3,"file":"ngx-color-picker.mjs","sources":["../../../projects/lib/src/lib/formats.ts","../../../projects/lib/src/lib/helpers.ts","../../../projects/lib/src/lib/color-picker.service.ts","../../../projects/lib/src/lib/color-picker.component.ts","../../../projects/lib/src/lib/color-picker.component.html","../../../projects/lib/src/lib/color-picker.directive.ts","../../../projects/lib/src/ngx-color-picker.ts"],"sourcesContent":["export enum ColorFormats {\n HEX,\n RGBA,\n HSLA,\n CMYK\n}\n\nexport class Rgba {\n constructor(public r: number, public g: number, public b: number, public a: number) {}\n}\n\nexport class Hsva {\n constructor(public h: number, public s: number, public v: number, public a: number) {}\n}\n\nexport class Hsla {\n constructor(public h: number, public s: number, public l: number, public a: number) {}\n}\n\nexport class Cmyk {\n constructor(public c: number, public m: number, public y: number, public k: number, public a: number = 1) {}\n}\n","import { Directive, Input, Output, EventEmitter, HostListener, ElementRef } from '@angular/core';\n\nexport type ColorMode = 'color' | 'c' | '1' |\n 'grayscale' | 'g' | '2' | 'presets' | 'p' | '3';\n\nexport type AlphaChannel = 'enabled' | 'disabled' | 'always' | 'forced';\n\nexport type BoundingRectangle = {\n top: number;\n bottom: number;\n left: number;\n right: number;\n height: number;\n width: number;\n};\n\nexport type OutputFormat = 'auto' | 'hex' | 'rgba' | 'hsla';\n\nexport function calculateAutoPositioning(elBounds: BoundingRectangle, triggerElBounds: BoundingRectangle): string {\n // Defaults\n let usePositionX = 'right';\n let usePositionY = 'bottom';\n // Calculate collisions\n const { height, width } = elBounds;\n const { top, left } = triggerElBounds;\n const bottom = top + triggerElBounds.height;\n const right = left + triggerElBounds.width;\n\n const collisionTop = top - height < 0;\n const collisionBottom = bottom + height > (window.innerHeight || document.documentElement.clientHeight);\n const collisionLeft = left - width < 0;\n const collisionRight = right + width > (window.innerWidth || document.documentElement.clientWidth);\n const collisionAll = collisionTop && collisionBottom && collisionLeft && collisionRight;\n\n // Generate X & Y position values\n if (collisionBottom) {\n usePositionY = 'top';\n }\n\n if (collisionTop) {\n usePositionY = 'bottom';\n }\n\n if (collisionLeft) {\n usePositionX = 'right';\n }\n\n if (collisionRight) {\n usePositionX = 'left';\n }\n\n\n // Choose the largest gap available\n if (collisionAll) {\n const postions = ['left', 'right', 'top', 'bottom'];\n return postions.reduce((prev, next) => elBounds[prev] > elBounds[next] ? prev : next);\n }\n\n if ((collisionLeft && collisionRight)) {\n if (collisionTop) { return 'bottom'; }\n if (collisionBottom) { return 'top'; }\n return top > bottom ? 'top' : 'bottom';\n }\n\n if ((collisionTop && collisionBottom)) {\n if (collisionLeft) { return 'right'; }\n if (collisionRight) { return 'left'; }\n return left > right ? 'left' : 'right';\n }\n\n return `${usePositionY}-${usePositionX}`;\n}\n\nexport function detectIE(): boolean | number {\n let ua = '';\n\n if (typeof navigator !== 'undefined') {\n ua = navigator.userAgent.toLowerCase();\n }\n\n const msie = ua.indexOf('msie ');\n\n if (msie > 0) {\n // IE 10 or older => return version number\n return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);\n }\n\n // Other browser\n return false;\n}\n\n@Directive({\n selector: '[text]'\n})\nexport class TextDirective {\n @Input() rg: number;\n @Input() text: any;\n\n @Output() newValue = new EventEmitter<any>();\n\n @HostListener('input', ['$event']) inputChange(event: any): void {\n const value = event.target.value;\n\n if (this.rg === undefined) {\n this.newValue.emit(value);\n } else {\n const numeric = parseFloat(value);\n\n this.newValue.emit({ v: numeric, rg: this.rg });\n }\n }\n}\n\n@Directive({\n selector: '[slider]'\n})\nexport class SliderDirective {\n private listenerMove: any;\n private listenerStop: any;\n\n @Input() rgX: number;\n @Input() rgY: number;\n\n @Input() slider: string;\n\n @Output() dragEnd = new EventEmitter();\n @Output() dragStart = new EventEmitter();\n\n @Output() newValue = new EventEmitter<any>();\n\n @HostListener('mousedown', ['$event']) mouseDown(event: any): void {\n this.start(event);\n }\n\n @HostListener('touchstart', ['$event']) touchStart(event: any): void {\n this.start(event);\n }\n\n constructor(private elRef: ElementRef) {\n this.listenerMove = (event: any) => this.move(event);\n\n this.listenerStop = () => this.stop();\n }\n\n private move(event: any): void {\n event.preventDefault();\n\n this.setCursor(event);\n }\n\n private start(event: any): void {\n this.setCursor(event);\n\n event.stopPropagation();\n\n document.addEventListener('mouseup', this.listenerStop);\n document.addEventListener('touchend', this.listenerStop);\n document.addEventListener('mousemove', this.listenerMove);\n document.addEventListener('touchmove', this.listenerMove);\n\n this.dragStart.emit();\n }\n\n private stop(): void {\n document.removeEventListener('mouseup', this.listenerStop);\n document.removeEventListener('touchend', this.listenerStop);\n document.removeEventListener('mousemove', this.listenerMove);\n document.removeEventListener('touchmove', this.listenerMove);\n\n this.dragEnd.emit();\n }\n\n private getX(event: any): number {\n const position = this.elRef.nativeElement.getBoundingClientRect();\n\n const pageX = (event.pageX !== undefined) ? event.pageX : event.touches[0].pageX;\n\n return pageX - position.left - window.pageXOffset;\n }\n\n private getY(event: any): number {\n const position = this.elRef.nativeElement.getBoundingClientRect();\n\n const pageY = (event.pageY !== undefined) ? event.pageY : event.touches[0].pageY;\n\n return pageY - position.top - window.pageYOffset;\n }\n\n private setCursor(event: any): void {\n const width = this.elRef.nativeElement.offsetWidth;\n const height = this.elRef.nativeElement.offsetHeight;\n\n const x = Math.max(0, Math.min(this.getX(event), width));\n const y = Math.max(0, Math.min(this.getY(event), height));\n\n if (this.rgX !== undefined && this.rgY !== undefined) {\n this.newValue.emit({ s: x / width, v: (1 - y / height), rgX: this.rgX, rgY: this.rgY });\n } else if (this.rgX === undefined && this.rgY !== undefined) {\n this.newValue.emit({ v: y / height, rgY: this.rgY });\n } else if (this.rgX !== undefined && this.rgY === undefined) {\n this.newValue.emit({ v: x / width, rgX: this.rgX });\n }\n }\n}\n\nexport class SliderPosition {\n constructor(public h: number, public s: number, public v: number, public a: number) {}\n}\n\nexport class SliderDimension {\n constructor(public h: number, public s: number, public v: number, public a: number) {}\n}\n","import { Injectable } from '@angular/core';\n\nimport { Cmyk, Rgba, Hsla, Hsva } from './formats';\n\nimport { ColorPickerComponent } from './color-picker.component';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ColorPickerService {\n private active: ColorPickerComponent | null = null;\n\n public setActive(active: ColorPickerComponent | null): void {\n if (this.active && this.active !== active && this.active.cpDialogDisplay !== 'inline') {\n this.active.closeDialog();\n }\n\n this.active = active;\n }\n\n public hsva2hsla(hsva: Hsva): Hsla {\n const h = hsva.h, s = hsva.s, v = hsva.v, a = hsva.a;\n\n if (v === 0) {\n return new Hsla(h, 0, 0, a);\n } else if (s === 0 && v === 1) {\n return new Hsla(h, 1, 1, a);\n } else {\n const l = v * (2 - s) / 2;\n\n return new Hsla(h, v * s / (1 - Math.abs(2 * l - 1)), l, a);\n }\n }\n\n public hsla2hsva(hsla: Hsla): Hsva {\n const h = Math.min(hsla.h, 1), s = Math.min(hsla.s, 1);\n const l = Math.min(hsla.l, 1), a = Math.min(hsla.a, 1);\n\n if (l === 0) {\n return new Hsva(h, 0, 0, a);\n } else {\n const v = l + s * (1 - Math.abs(2 * l - 1)) / 2;\n\n return new Hsva(h, 2 * (v - l) / v, v, a);\n }\n }\n\n public hsvaToRgba(hsva: Hsva): Rgba {\n let r: number, g: number, b: number;\n\n const h = hsva.h, s = hsva.s, v = hsva.v, a = hsva.a;\n\n const i = Math.floor(h * 6);\n const f = h * 6 - i;\n const p = v * (1 - s);\n const q = v * (1 - f * s);\n const t = v * (1 - (1 - f) * s);\n\n switch (i % 6) {\n case 0:\n r = v, g = t, b = p;\n break;\n case 1:\n r = q, g = v, b = p;\n break;\n case 2:\n r = p, g = v, b = t;\n break;\n case 3:\n r = p, g = q, b = v;\n break;\n case 4:\n r = t, g = p, b = v;\n break;\n case 5:\n r = v, g = p, b = q;\n break;\n default:\n r = 0, g = 0, b = 0;\n }\n\n return new Rgba(r, g, b, a);\n }\n\n public cmykToRgb(cmyk: Cmyk): Rgba {\n const r = ( 1 - cmyk.c ) * (1 - cmyk.k);\n const g = ( 1 - cmyk.m ) * (1 - cmyk.k);\n const b = ( 1 - cmyk.y ) * (1 - cmyk.k);\n\n return new Rgba(r, g, b, cmyk.a);\n }\n\n public rgbaToCmyk(rgba: Rgba): Cmyk {\n const k: number = 1 - Math.max(rgba.r, rgba.g, rgba.b);\n\n if (k === 1) {\n return new Cmyk(0, 0, 0, 1, rgba.a);\n } else {\n const c = (1 - rgba.r - k) / (1 - k);\n const m = (1 - rgba.g - k) / (1 - k);\n const y = (1 - rgba.b - k) / (1 - k);\n\n return new Cmyk(c, m, y, k, rgba.a);\n }\n }\n\n public rgbaToHsva(rgba: Rgba): Hsva {\n let h: number, s: number;\n\n const r = Math.min(rgba.r, 1), g = Math.min(rgba.g, 1);\n const b = Math.min(rgba.b, 1), a = Math.min(rgba.a, 1);\n\n const max = Math.max(r, g, b), min = Math.min(r, g, b);\n\n const v: number = max, d = max - min;\n\n s = (max === 0) ? 0 : d / max;\n\n if (max === min) {\n h = 0;\n } else {\n switch (max) {\n case r:\n h = (g - b) / d + (g < b ? 6 : 0);\n break;\n case g:\n h = (b - r) / d + 2;\n break;\n case b:\n h = (r - g) / d + 4;\n break;\n default:\n h = 0;\n }\n\n h /= 6;\n }\n\n return new Hsva(h, s, v, a);\n }\n\n public rgbaToHex(rgba: Rgba, allowHex8?: boolean): string {\n /* eslint-disable no-bitwise */\n let hex = '#' + ((1 << 24) | (rgba.r << 16) | (rgba.g << 8) | rgba.b).toString(16).substr(1);\n\n if (allowHex8) {\n hex += ((1 << 8) | Math.round(rgba.a * 255)).toString(16).substr(1);\n }\n /* eslint-enable no-bitwise */\n\n return hex;\n }\n\n public normalizeCMYK(cmyk: Cmyk): Cmyk {\n return new Cmyk(cmyk.c / 100, cmyk.m / 100, cmyk.y / 100, cmyk.k / 100, cmyk.a);\n }\n\n public denormalizeCMYK(cmyk: Cmyk): Cmyk {\n return new Cmyk(Math.floor(cmyk.c * 100), Math.floor(cmyk.m * 100), Math.floor(cmyk.y * 100),\n Math.floor(cmyk.k * 100), cmyk.a);\n }\n\n public denormalizeRGBA(rgba: Rgba): Rgba {\n return new Rgba(Math.round(rgba.r * 255), Math.round(rgba.g * 255), Math.round(rgba.b * 255), rgba.a);\n }\n\n public stringToHsva(colorString: string = '', allowHex8: boolean = false): Hsva | null {\n let hsva: Hsva | null = null;\n\n colorString = (colorString || '').toLowerCase();\n\n const stringParsers = [\n {\n re: /(rgb)a?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*%?,\\s*(\\d{1,3})\\s*%?(?:,\\s*(\\d+(?:\\.\\d+)?)\\s*)?\\)/,\n parse: function(execResult: any) {\n return new Rgba(parseInt(execResult[2], 10) / 255,\n parseInt(execResult[3], 10) / 255,\n parseInt(execResult[4], 10) / 255,\n isNaN(parseFloat(execResult[5])) ? 1 : parseFloat(execResult[5]));\n }\n }, {\n re: /(hsl)a?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})%\\s*,\\s*(\\d{1,3})%\\s*(?:,\\s*(\\d+(?:\\.\\d+)?)\\s*)?\\)/,\n parse: function(execResult: any) {\n return new Hsla(parseInt(execResult[2], 10) / 360,\n parseInt(execResult[3], 10) / 100,\n parseInt(execResult[4], 10) / 100,\n isNaN(parseFloat(execResult[5])) ? 1 : parseFloat(execResult[5]));\n }\n }\n ];\n\n if (allowHex8) {\n stringParsers.push({\n re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})?$/,\n parse: function(execResult: any) {\n return new Rgba(parseInt(execResult[1], 16) / 255,\n parseInt(execResult[2], 16) / 255,\n parseInt(execResult[3], 16) / 255,\n parseInt(execResult[4] || 'FF', 16) / 255);\n }\n });\n } else {\n stringParsers.push({\n re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/,\n parse: function(execResult: any) {\n return new Rgba(parseInt(execResult[1], 16) / 255,\n parseInt(execResult[2], 16) / 255,\n parseInt(execResult[3], 16) / 255,\n 1);\n }\n });\n }\n\n stringParsers.push({\n re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/,\n parse: function(execResult: any) {\n return new Rgba(parseInt(execResult[1] + execResult[1], 16) / 255,\n parseInt(execResult[2] + execResult[2], 16) / 255,\n parseInt(execResult[3] + execResult[3], 16) / 255,\n 1);\n }\n });\n\n for (const key in stringParsers) {\n if (stringParsers.hasOwnProperty(key)) {\n const parser = stringParsers[key];\n\n const match = parser.re.exec(colorString), color: any = match && parser.parse(match);\n\n if (color) {\n if (color instanceof Rgba) {\n hsva = this.rgbaToHsva(color);\n } else if (color instanceof Hsla) {\n hsva = this.hsla2hsva(color);\n }\n\n return hsva;\n }\n }\n }\n\n return hsva;\n }\n\n public outputFormat(hsva: Hsva, outputFormat: string, alphaChannel: string | null): string {\n if (outputFormat === 'auto') {\n outputFormat = hsva.a < 1 ? 'rgba' : 'hex';\n }\n\n switch (outputFormat) {\n case 'hsla':\n const hsla = this.hsva2hsla(hsva);\n\n const hslaText = new Hsla(Math.round((hsla.h) * 360), Math.round(hsla.s * 100),\n Math.round(hsla.l * 100), Math.round(hsla.a * 100) / 100);\n\n if (hsva.a < 1 || alphaChannel === 'always') {\n return 'hsla(' + hslaText.h + ',' + hslaText.s + '%,' + hslaText.l + '%,' +\n hslaText.a + ')';\n } else {\n return 'hsl(' + hslaText.h + ',' + hslaText.s + '%,' + hslaText.l + '%)';\n }\n\n case 'rgba':\n const rgba = this.denormalizeRGBA(this.hsvaToRgba(hsva));\n\n if (hsva.a < 1 || alphaChannel === 'always') {\n return 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' +\n Math.round(rgba.a * 100) / 100 + ')';\n } else {\n return 'rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')';\n }\n\n default:\n const allowHex8 = (alphaChannel === 'always' || alphaChannel === 'forced');\n\n return this.rgbaToHex(this.denormalizeRGBA(this.hsvaToRgba(hsva)), allowHex8);\n }\n }\n}\n","import {\n Component,\n OnInit,\n OnDestroy,\n AfterViewInit,\n ViewChild,\n HostListener,\n ViewEncapsulation,\n ElementRef,\n ChangeDetectorRef,\n TemplateRef,\n NgZone,\n Inject,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport { CommonModule, DOCUMENT, isPlatformBrowser } from '@angular/common';\n\nimport { detectIE, calculateAutoPositioning, SliderDirective, TextDirective } from './helpers';\n\nimport { ColorFormats, Cmyk, Hsla, Hsva, Rgba } from './formats';\nimport { AlphaChannel, OutputFormat, SliderDimension, SliderPosition } from './helpers';\n\nimport { ColorPickerService } from './color-picker.service';\n\n// Do not store that on the class instance since the condition will be run\n// every time the class is created.\nconst SUPPORTS_TOUCH = typeof window !== 'undefined' && 'ontouchstart' in window;\n\n@Component({\n selector: 'color-picker',\n templateUrl: './color-picker.component.html',\n styleUrls: [ './color-picker.component.css' ],\n encapsulation: ViewEncapsulation.None,\n imports: [\n SliderDirective,\n TextDirective,\n CommonModule\n ]\n})\nexport class ColorPickerComponent implements OnInit, OnDestroy, AfterViewInit {\n private isIE10: boolean = false;\n\n private cmyk: Cmyk;\n private hsva: Hsva;\n\n private width: number;\n private height: number;\n\n private cmykColor: string;\n private outputColor: string;\n private initialColor: string;\n private fallbackColor: string;\n\n private listenerResize: any;\n private listenerMouseDown: EventListener;\n\n private directiveInstance: any;\n\n private sliderH: number;\n private sliderDimMax: SliderDimension;\n private directiveElementRef: ElementRef;\n\n private dialogArrowSize: number = 10;\n private dialogArrowOffset: number = 15;\n\n private dialogInputFields: ColorFormats[] = [\n ColorFormats.HEX,\n ColorFormats.RGBA,\n ColorFormats.HSLA,\n ColorFormats.CMYK\n ];\n\n private useRootViewContainer: boolean = false;\n\n public show: boolean;\n public hidden: boolean;\n\n public top: number;\n public left: number;\n public position: string;\n\n public format: ColorFormats;\n public slider: SliderPosition;\n\n public hexText: string;\n public hexAlpha: number;\n\n public cmykText: Cmyk;\n public hslaText: Hsla;\n public rgbaText: Rgba;\n\n public arrowTop: number;\n\n public selectedColor: string;\n public hueSliderColor: string;\n public alphaSliderColor: string;\n\n public cpWidth: number;\n public cpHeight: number;\n\n public cpColorMode: number;\n\n public cpCmykEnabled: boolean;\n\n public cpAlphaChannel: AlphaChannel;\n public cpOutputFormat: OutputFormat;\n\n public cpDisableInput: boolean;\n public cpDialogDisplay: string;\n\n public cpIgnoredElements: any;\n\n public cpSaveClickOutside: boolean;\n public cpCloseClickOutside: boolean;\n\n public cpPosition: string;\n public cpUsePosition: string;\n public cpPositionOffset: number;\n\n public cpOKButton: boolean;\n public cpOKButtonText: string;\n public cpOKButtonClass: string;\n\n public cpCancelButton: boolean;\n public cpCancelButtonText: string;\n public cpCancelButtonClass: string;\n\n public cpEyeDropper: boolean;\n public eyeDropperSupported: boolean;\n\n public cpPresetLabel: string;\n public cpPresetColors: string[];\n public cpPresetColorsClass: string;\n public cpMaxPresetColorsLength: number;\n\n public cpPresetEmptyMessage: string;\n public cpPresetEmptyMessageClass: string;\n\n public cpAddColorButton: boolean;\n public cpAddColorButtonText: string;\n public cpAddColorButtonClass: string;\n public cpRemoveColorButtonClass: string;\n public cpArrowPosition: number;\n\n public cpTriggerElement: ElementRef;\n\n public cpExtraTemplate: TemplateRef<any>;\n\n @ViewChild('dialogPopup', { static: true }) dialogElement: ElementRef;\n\n @ViewChild('hueSlider', { static: true }) hueSlider: ElementRef;\n @ViewChild('alphaSlider', { static: true }) alphaSlider: ElementRef;\n\n @HostListener('document:keyup.esc', ['$event']) handleEsc(event: any): void {\n if (this.show && this.cpDialogDisplay === 'popup') {\n this.onCancelColor(event);\n }\n }\n\n @HostListener('document:keyup.enter', ['$event']) handleEnter(event: any): void {\n if (this.show && this.cpDialogDisplay === 'popup') {\n this.onAcceptColor(event);\n }\n }\n\n constructor(\n private ngZone: NgZone,\n private elRef: ElementRef,\n private cdRef: ChangeDetectorRef,\n @Inject(DOCUMENT) private document: Document,\n @Inject(PLATFORM_ID) private platformId: string,\n private service: ColorPickerService\n ) {\n this.eyeDropperSupported = isPlatformBrowser(this.platformId) && 'EyeDropper' in this.document.defaultView;\n }\n\n ngOnInit(): void {\n this.slider = new SliderPosition(0, 0, 0, 0);\n\n const hueWidth = this.hueSlider.nativeElement.offsetWidth || 140;\n const alphaWidth = this.alphaSlider.nativeElement.offsetWidth || 140;\n\n this.sliderDimMax = new SliderDimension(hueWidth, this.cpWidth, 130, alphaWidth);\n\n if (this.cpCmykEnabled) {\n this.format = ColorFormats.CMYK;\n } else if (this.cpOutputFormat === 'rgba') {\n this.format = ColorFormats.RGBA;\n } else if (this.cpOutputFormat === 'hsla') {\n this.format = ColorFormats.HSLA;\n } else {\n this.format = ColorFormats.HEX;\n }\n\n this.listenerMouseDown = (event: MouseEvent) => { this.onMouseDown(event); };\n this.listenerResize = () => { this.onResize(); };\n\n this.openDialog(this.initialColor, false);\n }\n\n ngOnDestroy(): void {\n this.closeDialog();\n }\n\n ngAfterViewInit(): void {\n if (this.cpWidth !== 230 || this.cpDialogDisplay === 'inline') {\n const hueWidth = this.hueSlider.nativeElement.offsetWidth || 140;\n const alphaWidth = this.alphaSlider.nativeElement.offsetWidth || 140;\n\n this.sliderDimMax = new SliderDimension(hueWidth, this.cpWidth, 130, alphaWidth);\n\n this.updateColorPicker(false);\n\n this.cdRef.detectChanges();\n }\n }\n\n public openDialog(color: any, emit: boolean = true): void {\n this.service.setActive(this);\n\n if (!this.width) {\n this.cpWidth = this.directiveElementRef.nativeElement.offsetWidth;\n }\n\n if (!this.height) {\n this.height = 320;\n }\n\n this.setInitialColor(color);\n\n this.setColorFromString(color, emit);\n\n this.openColorPicker();\n }\n\n public closeDialog(): void {\n this.closeColorPicker();\n }\n\n public setupDialog(instance: any, elementRef: ElementRef, color: any,\n cpWidth: string, cpHeight: string, cpDialogDisplay: string, cpFallbackColor: string,\n cpColorMode: string, cpCmykEnabled: boolean, cpAlphaChannel: AlphaChannel,\n cpOutputFormat: OutputFormat, cpDisableInput: boolean, cpIgnoredElements: any,\n cpSaveClickOutside: boolean, cpCloseClickOutside: boolean, cpUseRootViewContainer: boolean,\n cpPosition: string, cpPositionOffset: string, cpPositionRelativeToArrow: boolean,\n cpPresetLabel: string, cpPresetColors: string[], cpPresetColorsClass: string,\n cpMaxPresetColorsLength: number, cpPresetEmptyMessage: string,\n cpPresetEmptyMessageClass: string, cpOKButton: boolean, cpOKButtonClass: string,\n cpOKButtonText: string, cpCancelButton: boolean, cpCancelButtonClass: string,\n cpCancelButtonText: string, cpAddColorButton: boolean, cpAddColorButtonClass: string,\n cpAddColorButtonText: string, cpRemoveColorButtonClass: string, cpEyeDropper: boolean,\n cpTriggerElement: ElementRef, cpExtraTemplate: TemplateRef<any>): void\n {\n this.setInitialColor(color);\n\n this.setColorMode(cpColorMode);\n\n this.isIE10 = (detectIE() === 10);\n\n this.directiveInstance = instance;\n this.directiveElementRef = elementRef;\n\n this.cpDisableInput = cpDisableInput;\n\n this.cpCmykEnabled = cpCmykEnabled;\n this.cpAlphaChannel = cpAlphaChannel;\n this.cpOutputFormat = cpOutputFormat;\n\n this.cpDialogDisplay = cpDialogDisplay;\n\n this.cpIgnoredElements = cpIgnoredElements;\n\n this.cpSaveClickOutside = cpSaveClickOutside;\n this.cpCloseClickOutside = cpCloseClickOutside;\n\n this.useRootViewContainer = cpUseRootViewContainer;\n\n this.width = this.cpWidth = parseInt(cpWidth, 10);\n this.height = this.cpHeight = parseInt(cpHeight, 10);\n\n this.cpPosition = cpPosition;\n this.cpPositionOffset = parseInt(cpPositionOffset, 10);\n\n this.cpOKButton = cpOKButton;\n this.cpOKButtonText = cpOKButtonText;\n this.cpOKButtonClass = cpOKButtonClass;\n\n this.cpCancelButton = cpCancelButton;\n this.cpCancelButtonText = cpCancelButtonText;\n this.cpCancelButtonClass = cpCancelButtonClass;\n\n this.cpEyeDropper = cpEyeDropper;\n\n this.fallbackColor = cpFallbackColor || '#fff';\n\n this.setPresetConfig(cpPresetLabel, cpPresetColors);\n\n this.cpPresetColorsClass = cpPresetColorsClass;\n this.cpMaxPresetColorsLength = cpMaxPresetColorsLength;\n this.cpPresetEmptyMessage = cpPresetEmptyMessage;\n this.cpPresetEmptyMessageClass = cpPresetEmptyMessageClass;\n\n this.cpAddColorButton = cpAddColorButton;\n this.cpAddColorButtonText = cpAddColorButtonText;\n this.cpAddColorButtonClass = cpAddColorButtonClass;\n this.cpRemoveColorButtonClass = cpRemoveColorButtonClass;\n\n this.cpTriggerElement = cpTriggerElement;\n this.cpExtraTemplate = cpExtraTemplate;\n\n if (!cpPositionRelativeToArrow) {\n this.dialogArrowOffset = 0;\n }\n\n if (cpDialogDisplay === 'inline') {\n this.dialogArrowSize = 0;\n this.dialogArrowOffset = 0;\n }\n\n if (cpOutputFormat === 'hex' &&\n cpAlphaChannel !== 'always' && cpAlphaChannel !== 'forced')\n {\n this.cpAlphaChannel = 'disabled';\n }\n }\n\n public setColorMode(mode: string): void {\n switch (mode.toString().toUpperCase()) {\n case '1':\n case 'C':\n case 'COLOR':\n this.cpColorMode = 1;\n break;\n case '2':\n case 'G':\n case 'GRAYSCALE':\n this.cpColorMode = 2;\n break;\n case '3':\n case 'P':\n case 'PRESETS':\n this.cpColorMode = 3;\n break;\n default:\n this.cpColorMode = 1;\n }\n }\n\n public setInitialColor(color: any): void {\n this.initialColor = color;\n }\n\n public setPresetConfig(cpPresetLabel: string, cpPresetColors: string[]): void {\n this.cpPresetLabel = cpPresetLabel;\n this.cpPresetColors = cpPresetColors;\n }\n\n public setColorFromString(value: string, emit: boolean = true, update: boolean = true): void {\n let hsva: Hsva | null;\n\n if (this.cpAlphaChannel === 'always' || this.cpAlphaChannel === 'forced') {\n hsva = this.service.stringToHsva(value, true);\n\n if (!hsva && !this.hsva) {\n hsva = this.service.stringToHsva(value, false);\n }\n } else {\n hsva = this.service.stringToHsva(value, false);\n }\n\n if (!hsva && !this.hsva) {\n hsva = this.service.stringToHsva(this.fallbackColor, false);\n }\n\n if (hsva) {\n this.hsva = hsva;\n\n this.sliderH = this.hsva.h;\n\n if (this.cpOutputFormat === 'hex' && this.cpAlphaChannel === 'disabled') {\n this.hsva.a = 1;\n }\n\n this.updateColorPicker(emit, update);\n }\n }\n\n public onResize(): void {\n if (this.position === 'fixed') {\n this.setDialogPosition();\n } else if (this.cpDialogDisplay !== 'inline') {\n this.closeColorPicker();\n }\n }\n\n public onDragEnd(slider: string): void {\n this.directiveInstance.sliderDragEnd({ slider: slider, color: this.outputColor });\n }\n\n public onDragStart(slider: string): void {\n this.directiveInstance.sliderDragStart({ slider: slider, color: this.outputColor });\n }\n\n public onMouseDown(event: MouseEvent): void {\n if (\n this.show &&\n !this.isIE10 &&\n this.cpDialogDisplay === 'popup' &&\n event.target !== this.directiveElementRef.nativeElement &&\n !this.isDescendant(this.elRef.nativeElement, event.target) &&\n !this.isDescendant(this.directiveElementRef.nativeElement, event.target) &&\n this.cpIgnoredElements.filter((item: any) => item === event.target).length === 0\n ) {\n this.ngZone.run(() => {\n if (this.cpSaveClickOutside) {\n this.directiveInstance.colorSelected(this.outputColor);\n } else {\n this.hsva = null;\n\n this.setColorFromString(this.initialColor, false);\n\n if (this.cpCmykEnabled) {\n this.directiveInstance.cmykChanged(this.cmykColor);\n }\n\n this.directiveInstance.colorChanged(this.initialColor);\n\n this.directiveInstance.colorCanceled();\n }\n\n if (this.cpCloseClickOutside) {\n this.closeColorPicker();\n }\n });\n }\n }\n\n public onAcceptColor(event: Event): void {\n event.stopPropagation();\n\n if (this.outputColor) {\n this.directiveInstance.colorSelected(this.outputColor);\n }\n\n if (this.cpDialogDisplay === 'popup') {\n this.closeColorPicker();\n }\n }\n\n public onCancelColor(event: Event): void {\n this.hsva = null;\n\n event.stopPropagation();\n\n this.directiveInstance.colorCanceled();\n\n this.setColorFromString(this.initialColor, true);\n\n if (this.cpDialogDisplay === 'popup') {\n if (this.cpCmykEnabled) {\n this.directiveInstance.cmykChanged(this.cmykColor);\n }\n\n this.directiveInstance.colorChanged(this.initialColor, true);\n\n this.closeColorPicker();\n }\n }\n\n public onEyeDropper(): void {\n if (!this.eyeDropperSupported) return;\n const eyeDropper = new (window as any).EyeDropper();\n eyeDropper.open().then((eyeDropperResult: {\n sRGBHex: string;\n }) => {\n this.setColorFromString(eyeDropperResult.sRGBHex, true);\n });\n }\n\n public onFormatToggle(change: number): void {\n const availableFormats = this.dialogInputFields.length -\n (this.cpCmykEnabled ? 0 : 1);\n\n const nextFormat = (((this.dialogInputFields.indexOf(this.format) + change) %\n availableFormats) + availableFormats) % availableFormats;\n\n this.format = this.dialogInputFields[nextFormat];\n }\n\n public onColorChange(value: { s: number, v: number, rgX: number, rgY: number }): void {\n this.hsva.s = value.s / value.rgX;\n this.hsva.v = value.v / value.rgY;\n\n this.updateColorPicker();\n\n this.directiveInstance.sliderChanged({\n slider: 'lightness',\n value: this.hsva.v,\n color: this.outputColor\n });\n\n this.directiveInstance.sliderChanged({\n slider: 'saturation',\n value: this.hsva.s,\n color: this.outputColor\n });\n }\n\n public onHueChange(value: { v: number, rgX: number }): void {\n this.hsva.h = value.v / value.rgX;\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n\n this.directiveInstance.sliderChanged({\n slider: 'hue',\n value: this.hsva.h,\n color: this.outputColor\n });\n }\n\n public onValueChange(value: { v: number, rgX: number }): void {\n this.hsva.v = value.v / value.rgX;\n\n this.updateColorPicker();\n\n this.directiveInstance.sliderChanged({\n slider: 'value',\n value: this.hsva.v,\n color: this.outputColor\n });\n }\n\n public onAlphaChange(value: { v: number, rgX: number }): void {\n this.hsva.a = value.v / value.rgX;\n\n this.updateColorPicker();\n\n this.directiveInstance.sliderChanged({\n slider: 'alpha',\n value: this.hsva.a,\n color: this.outputColor\n });\n }\n\n public onHexInput(value: string | null): void {\n if (value === null) {\n this.updateColorPicker();\n } else {\n if (value && value[0] !== '#') {\n value = '#' + value;\n }\n\n let validHex = /^#([a-f0-9]{3}|[a-f0-9]{6})$/gi;\n\n if (this.cpAlphaChannel === 'always') {\n validHex = /^#([a-f0-9]{3}|[a-f0-9]{6}|[a-f0-9]{8})$/gi;\n }\n\n const valid = validHex.test(value);\n\n if (valid) {\n if (value.length < 5) {\n value = '#' + value.substring(1)\n .split('')\n .map(c => c + c)\n .join('');\n }\n\n if (this.cpAlphaChannel === 'forced') {\n value += Math.round(this.hsva.a * 255).toString(16);\n }\n\n this.setColorFromString(value, true, false);\n }\n\n this.directiveInstance.inputChanged({\n input: 'hex',\n valid: valid,\n value: value,\n color: this.outputColor\n });\n }\n }\n\n public onRedInput(value: { v: number, rg: number }): void {\n const rgba = this.service.hsvaToRgba(this.hsva);\n\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n rgba.r = value.v / value.rg;\n\n this.hsva = this.service.rgbaToHsva(rgba);\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'red',\n valid: valid,\n value: rgba.r,\n color: this.outputColor\n });\n }\n\n public onBlueInput(value: { v: number, rg: number }): void {\n const rgba = this.service.hsvaToRgba(this.hsva);\n\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n rgba.b = value.v / value.rg;\n\n this.hsva = this.service.rgbaToHsva(rgba);\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'blue',\n valid: valid,\n value: rgba.b,\n color: this.outputColor\n });\n }\n\n public onGreenInput(value: { v: number, rg: number }): void {\n const rgba = this.service.hsvaToRgba(this.hsva);\n\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n rgba.g = value.v / value.rg;\n\n this.hsva = this.service.rgbaToHsva(rgba);\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'green',\n valid: valid,\n value: rgba.g,\n color: this.outputColor\n });\n }\n\n public onHueInput(value: { v: number, rg: number }) {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.hsva.h = value.v / value.rg;\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'hue',\n valid: valid,\n value: this.hsva.h,\n color: this.outputColor\n });\n }\n\n public onValueInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.hsva.v = value.v / value.rg;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'value',\n valid: valid,\n value: this.hsva.v,\n color: this.outputColor\n });\n }\n\n public onAlphaInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.hsva.a = value.v / value.rg;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'alpha',\n valid: valid,\n value: this.hsva.a,\n color: this.outputColor\n });\n }\n\n public onLightnessInput(value: { v: number, rg: number }): void {\n const hsla = this.service.hsva2hsla(this.hsva);\n\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n hsla.l = value.v / value.rg;\n\n this.hsva = this.service.hsla2hsva(hsla);\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'lightness',\n valid: valid,\n value: hsla.l,\n color: this.outputColor\n });\n }\n\n public onSaturationInput(value: { v: number, rg: number }): void {\n const hsla = this.service.hsva2hsla(this.hsva);\n\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n hsla.s = value.v / value.rg;\n\n this.hsva = this.service.hsla2hsva(hsla);\n\n this.sliderH = this.hsva.h;\n\n this.updateColorPicker();\n }\n\n this.directiveInstance.inputChanged({\n input: 'saturation',\n valid: valid,\n value: hsla.s,\n color: this.outputColor\n });\n }\n\n public onCyanInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.cmyk.c = value.v;\n\n this.updateColorPicker(false, true, true);\n }\n\n this.directiveInstance.inputChanged({\n input: 'cyan',\n valid: true,\n value: this.cmyk.c,\n color: this.outputColor\n });\n }\n\n public onMagentaInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.cmyk.m = value.v;\n\n this.updateColorPicker(false, true, true);\n }\n\n this.directiveInstance.inputChanged({\n input: 'magenta',\n valid: true,\n value: this.cmyk.m,\n color: this.outputColor\n });\n }\n\n public onYellowInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.cmyk.y = value.v;\n\n this.updateColorPicker(false, true, true);\n }\n\n this.directiveInstance.inputChanged({\n input: 'yellow',\n valid: true,\n value: this.cmyk.y,\n color: this.outputColor\n });\n }\n\n public onBlackInput(value: { v: number, rg: number }): void {\n const valid = !isNaN(value.v) && value.v >= 0 && value.v <= value.rg;\n\n if (valid) {\n this.cmyk.k = value.v;\n\n this.updateColorPicker(false, true, true);\n }\n\n this.directiveInstance.inputChanged({\n input: 'black',\n valid: true,\n value: this.cmyk.k,\n color: this.outputColor\n });\n }\n\n public onAddPresetColor(event: any, value: string): void {\n event.stopPropagation();\n\n if (!this.cpPresetColors.filter((color) => (color === value)).length) {\n this.cpPresetColors = this.cpPresetColors.concat(value);\n\n this.directiveInstance.presetColorsChanged(this.cpPresetColors);\n }\n }\n\n public onRemovePresetColor(event: any, value: string): void {\n event.stopPropagation();\n\n this.cpPresetColors = this.cpPresetColors.filter((color) => (color !== value));\n\n this.directiveInstance.presetColorsChanged(this.cpPresetColors);\n }\n\n // Private helper functions for the color picker dialog status\n\n private openColorPicker(): void {\n if (!this.show) {\n this.show = true;\n this.hidden = true;\n\n setTimeout(() => {\n this.hidden = false;\n\n this.setDialogPosition();\n\n this.cdRef.detectChanges();\n }, 0);\n\n this.directiveInstance.stateChanged(true);\n\n if (!this.isIE10) {\n // The change detection should be run on `mousedown` event only when the condition\n // is met within the `onMouseDown` method.\n this.ngZone.runOutsideAngular(() => {\n // There's no sense to add both event listeners on touch devices since the `touchstart`\n // event is handled earlier than `mousedown`, so we'll get 2 change detections and the\n // second one will be unnecessary.\n if (SUPPORTS_TOUCH) {\n document.addEventListener('touchstart', this.listenerMouseDown);\n } else {\n document.addEventListener('mousedown', this.listenerMouseDown);\n }\n });\n }\n\n window.addEventListener('resize', this.listenerResize);\n }\n }\n\n private closeColorPicker(): void {\n if (this.show) {\n this.show = false;\n\n this.directiveInstance.stateChanged(false);\n\n if (!this.isIE10) {\n if (SUPPORTS_TOUCH) {\n document.removeEventListener('touchstart', this.listenerMouseDown);\n } else {\n document.removeEventListener('mousedown', this.listenerMouseDown);\n }\n }\n\n window.removeEventListener('resize', this.listenerResize);\n\n if (!this.cdRef['destroyed']) {\n this.cdRef.detectChanges();\n }\n }\n }\n\n private updateColorPicker(emit: boolean = true, update: boolean = true, cmykInput: boolean = false): void {\n if (this.sliderDimMax) {\n if (this.cpColorMode === 2) {\n this.hsva.s = 0;\n }\n\n let hue: Rgba, hsla: Hsla, rgba: Rgba;\n\n const lastOutput = this.outputColor;\n\n hsla = this.service.hsva2hsla(this.hsva);\n\n if (!this.cpCmykEnabled) {\n rgba = this.service.denormalizeRGBA(this.service.hsvaToRgba(this.hsva));\n } else {\n if (!cmykInput) {\n rgba = this.service.hsvaToRgba(this.hsva);\n\n this.cmyk = this.service.denormalizeCMYK(this.service.rgbaToCmyk(rgba));\n } else {\n rgba = this.service.cmykToRgb(this.service.normalizeCMYK(this.cmyk));\n\n this.hsva = this.service.rgbaToHsva(rgba);\n }\n\n rgba = this.service.denormalizeRGBA(rgba);\n\n this.sliderH = this.hsva.h;\n }\n\n hue = this.service.denormalizeRGBA(this.service.hsvaToRgba(new Hsva(this.sliderH || this.hsva.h, 1, 1, 1)));\n\n if (update) {\n this.hslaText = new Hsla(Math.round((hsla.h) * 360), Math.round(hsla.s * 100), Math.round(hsla.l * 100),\n Math.round(hsla.a * 100) / 100);\n\n this.rgbaText = new Rgba(rgba.r, rgba.g, rgba.b, Math.round(rgba.a * 100) / 100);\n\n if (this.cpCmykEnabled) {\n this.cmykText = new Cmyk(this.cmyk.c, this.cmyk.m, this.cmyk.y, this.cmyk.k,\n Math.round(this.cmyk.a * 100) / 100);\n }\n\n const allowHex8 = this.cpAlphaChannel === 'always';\n\n this.hexText = this.service.rgbaToHex(rgba, allowHex8);\n this.hexAlpha = this.rgbaText.a;\n }\n\n if (this.cpOutputFormat === 'auto') {\n if (this.format !== ColorFormats.RGBA && this.format !== ColorFormats.CMYK && this.format !== ColorFormats.HSLA) {\n if (this.hsva.a < 1) {\n this.format = this.hsva.a < 1 ? ColorFormats.RGBA : ColorFormats.HEX;\n }\n }\n }\n\n this.hueSliderColor = 'rgb(' + hue.r + ',' + hue.g + ',' + hue.b + ')';\n this.alphaSliderColor = 'rgb(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ')';\n\n this.outputColor = this.service.outputFormat(this.hsva, this.cpOutputFormat, this.cpAlphaChannel);\n this.selectedColor = this.service.outputFormat(this.hsva, 'rgba', null);\n\n if (this.format !== ColorFormats.CMYK) {\n this.cmykColor = '';\n } else {\n if (this.cpAlphaChannel === 'always' || this.cpAlphaChannel === 'enabled' ||\n this.cpAlphaChannel === 'forced')\n {\n const alpha = Math.round(this.cmyk.a * 100) / 100;\n\n this.cmykColor = `cmyka(${this.cmyk.c},${this.cmyk.m},${this.cmyk.y},${this.cmyk.k},${alpha})`;\n } else {\n this.cmykColor = `cmyk(${this.cmyk.c},${this.cmyk.m},${this.cmyk.y},${this.cmyk.k})`;\n }\n }\n\n this.slider = new SliderPosition(\n (this.sliderH || this.hsva.h) * this.sliderDimMax.h - 8,\n this.hsva.s * this.sliderDimMax.s - 8,\n (1 - this.hsva.v) * this.sliderDimMax.v - 8,\n this.hsva.a * this.sliderDimMax.a - 8\n );\n\n if (emit && lastOutput !== this.outputColor) {\n if (this.cpCmykEnabled) {\n this.directiveInstance.cmykChanged(this.cmykColor);\n }\n\n this.directiveInstance.colorChanged(this.outputColor);\n }\n }\n }\n\n // Private helper functions for the color picker dialog positioning\n\n private setDialogPosition(): void {\n if (this.cpDialogDisplay === 'inline') {\n this.position = 'relative';\n } else {\n let position = 'static', transform = '', style;\n\n let parentNode: any = null, transformNode: any = null;\n\n let node = this.directiveElementRef.nativeElement.parentNode;\n\n const dialogHeight = this.dialogElement.nativeElement.offsetHeight;\n\n while (node !== null && node.tagName !== 'HTML') {\n style = window.getComputedStyle(node);\n position = style.getPropertyValue('position');\n transform = style.getPropertyValue('transform');\n\n if (position !== 'static' && parentNode === null) {\n parentNode = node;\n }\n\n if (transform && transform !== 'none' && transformNode === null) {\n transformNode = node;\n }\n\n if (position === 'fixed') {\n parentNode = transformNode;\n\n break;\n }\n\n node = node.parentNode;\n }\n\n const boxDirective = this.createDialogBox(this.directiveElementRef.nativeElement, (position !== 'fixed'));\n\n if (this.useRootViewContainer || (position === 'fixed' &&\n (!parentNode || parentNode instanceof HTMLUnknownElement)))\n {\n this.top = boxDirective.top;\n this.left = boxDirective.left;\n } else {\n if (parentNode === null) {\n parentNode = node;\n }\n\n const boxParent = this.createDialogBox(parentNode, (position !== 'fixed'));\n\n this.top = boxDirective.top - boxParent.top;\n this.left = boxDirective.left - boxParent.left;\n }\n\n if (position === 'fixed') {\n this.position = 'fixed';\n }\n\n let usePosition = this.cpPosition;\n\n const dialogBounds = this.dialogElement.nativeElement.getBoundingClientRect();\n if (this.cpPosition === 'auto') {\n const triggerBounds = this.cpTriggerElement.nativeElement.getBoundingClientRect();\n usePosition = calculateAutoPositioning(dialogBounds, triggerBounds);\n }\n\n this.arrowTop = usePosition === 'top'\n ? dialogHeight - 1\n : undefined;\n this.cpArrowPosition = undefined;\n\n switch (usePosition) {\n case 'top':\n this.top -= dialogHeight + this.dialogArrowSize;\n this.left += this.cpPositionOffset / 100 * boxDirective.width - this.dialogArrowOffset;\n break;\n case 'bottom':\n this.top += boxDirective.height + this.dialogArrowSize;\n this.left += this.cpPositionOffset / 100 * boxDirective.width - this.dialogArrowOffset;\n break;\n case 'top-left':\n case 'left-top':\n this.top -= dialogHeight - boxDirective.height + boxDirective.height * this.cpPositionOffset / 100;\n this.left -= this.cpWidth + this.dialogArrowSize - 2 - this.dialogArrowOffset;\n break;\n case 'top-right':\n case 'right-top':\n this.top -= dialogHeight - boxDirective.height + boxDirective.height * this.cpPositionOffset / 100;\n this.left += boxDirective.width + this.dialogArrowSize - 2 - this.dialogArrowOffset;\n break;\n case 'left':\n case 'bottom-left':\n case 'left-bottom':\n this.top += boxDirective.height * this.cpPositionOffset / 100 - this.dialogArrowOffset;\n this.left -= this.cpWidth + this.dialogArrowSize - 2;\n break;\n case 'right':\n case 'bottom-right':\n case 'right-bottom':\n default:\n this.top += boxDirective.height * this.cpPositionOffset / 100 - this.dialogArrowOffset;\n this.left += boxDirective.width + this.dialogArrowSize - 2;\n break;\n }\n\n const windowInnerHeight = window.innerHeight;\n const windowInnerWidth = window.innerWidth;\n const elRefClientRect = this.elRef.nativeElement.getBoundingClientRect();\n const bottom = this.top + dialogBounds.height;\n if (bottom > windowInnerHeight) {\n this.top = windowInnerHeight - dialogBounds.height;\n this.cpArrowPosition = elRefClientRect.x / 2 - 20;\n }\n const right = this.left + dialogBounds.width;\n if (right > windowInnerWidth) {\n this.left = windowInnerWidth - dialogBounds.width;\n this.cpArrowPosition = elRefClientRect.x / 2 - 20;\n }\n\n this.cpUsePosition = usePosition;\n }\n }\n\n // Private helper functions for the color picker dialog positioning and opening\n\n private isDescendant(parent: any, child: any): boolean {\n let node: any = child.parentNode;\n\n while (node !== null) {\n if (node === parent) {\n return true;\n }\n\n node = node.parentNode;\n }\n\n return false;\n }\n\n private createDialogBox(element: any, offset: boolean): any {\n const { top, left } = element.getBoundingClientRect();\n return {\n top: top + (offset ? window.pageYOffset : 0),\n left: left + (offset ? window.pageXOffset : 0),\n width: element.offsetWidth,\n height: element.offsetHeight\n };\n }\n}\n","<div #dialogPopup class=\"color-picker\" [class.open]=\"show\" [style.display]=\"!show ? 'none' : 'block'\" [style.visibility]=\"hidden ? 'hidden' : 'visible'\" [style.top.px]=\"top\" [style.left.px]=\"left\" [style.position]=\"position\" [style.height.px]=\"cpHeight\" [style.width.px]=\"cpWidth\" (click)=\"$event.stopPropagation()\">\n <div *ngIf=\"cpDialogDisplay === 'popup'\" [style.left]=\"cpArrowPosition\" class=\"arrow arrow-{{cpUsePosition}}\" [style.top.px]=\"arrowTop\"></div>\n\n <div *ngIf=\"(cpColorMode || 1) === 1\" class=\"saturation-lightness\" [slider] [rgX]=\"1\" [rgY]=\"1\" [style.background-color]=\"hueSliderColor\" (newValue)=\"onColorChange($event)\" (dragStart)=\"onDragStart('saturation-lightness')\" (dragEnd)=\"onDragEnd('saturation-lightness')\">\n <div class=\"cursor\" [style.top.px]=\"slider?.v\" [style.left.px]=\"slider?.s\"></div>\n </div>\n\n <div class=\"hue-alpha box\">\n <div class=\"left\">\n <div class=\"selected-color-background\"></div>\n\n <div class=\"selected-color\" [style.background-color]=\"selectedColor\" [style.cursor]=\"eyeDropperSupported && cpEyeDropper ? 'pointer' : null\" (click)=\"eyeDropperSupported && cpEyeDropper && onEyeDropper()\">\n <svg *ngIf=\"eyeDropperSupported && cpEyeDropper\" class=\"eyedropper-icon\" xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBo