diff --git a/.gitignore b/.gitignore index 3d8272d..25d7dee 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ glsl/* config.json ignore.txt .env +.venv .DS_Store *.egg-info *.bak diff --git a/README.md b/README.md index 91b753b..6e78885 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,13 @@ ComfyUI Nodes for procedural masking, live composition and video manipulation -

+

COMFYUI Nodes for procedural masking, live composition and video manipulation +

+ +

+JOVIMETRIX IS ONLY GAURENTEED TO SUPPORT COMFYUI 0.0.7+ and FRONTEND 1.2.27+
+IF YOU NEED AN OLDER VERSION, PLEASE DO NOT UPDATE.

diff --git a/node_list.json b/node_list.json index b4b214d..29e3687 100644 --- a/node_list.json +++ b/node_list.json @@ -1,36 +1,6 @@ { - "ADJUST (JOV) \ud83d\udd78\ufe0f": "Enhance and modify images with various effects such as blurring, sharpening, color tweaks, and edge detection", - "AKASHIC (JOV) \ud83d\udcd3": "Visualize data", - "ARRAY (JOV) \ud83d\udcda": "Processes a batch of data based on the selected mode, such as merging, picking, slicing, random selection, or indexing", - "BLEND (JOV) \u2697\ufe0f": "Combine two input images using various blending modes, such as normal, screen, multiply, overlay, etc", - "COLOR BLIND (JOV) \ud83d\udc41\u200d\ud83d\udde8": "Simulate color blindness effects on images", - "COLOR MATCH (JOV) \ud83d\udc9e": "Adjust the color scheme of one image to match another with the Color Match Node", - "COLOR THEORY (JOV) \ud83d\udede": "Generate a color harmony based on the selected scheme", "COMPARISON (JOV) \ud83d\udd75\ud83c\udffd": "Evaluates two inputs (A and B) with a specified comparison operators and optional values for successful and failed comparisons", - "CONSTANT (JOV) \ud83d\udfea": "Generate a constant image or mask of a specified size and color", - "CROP (JOV) \u2702\ufe0f": "Extract a portion of an input image or resize it", "DELAY (JOV) \u270b\ud83c\udffd": "Introduce pauses in the workflow that accept an optional input to pass through and a timer parameter to specify the duration of the delay", - "EXPORT (JOV) \ud83d\udcfd": "Responsible for saving images or animations to disk", - "FILTER MASK (JOV) \ud83e\udd3f": "Create masks based on specific color ranges within an image", - "FLATTEN (JOV) \u2b07\ufe0f": "Combine multiple input images into a single image by summing their pixel values", - "GLSL (JOV) \ud83c\udf69": "Execute custom GLSL (OpenGL Shading Language) fragment shaders to generate images or apply effects", - "GLSL BLEND LINEAR (JOV) \ud83e\uddd9\ud83c\udffd": "Simple linear blend between two images", - "GLSL GRAYSCALE (JOV) \ud83e\uddd9\ud83c\udffd": "Convert input to grayscale", - "GLSL HSV-2-LAB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert HSV image into LAB color space", - "GLSL HSV-2-RGB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert HSV image into RGB color space", - "GLSL LAB-2-HSV (JOV) \ud83e\uddd9\ud83c\udffd": "Convert LAB image into HSV color space", - "GLSL LAB-2-RGB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert LAB image into RGB color space", - "GLSL LAB-2-XYZ (JOV) \ud83e\uddd9\ud83c\udffd": "Convert LAB image into XYZ color space", - "GLSL NORMAL (JOV) \ud83e\uddd9\ud83c\udffd": "Convert input into a Normal map", - "GLSL NORMAL BLEND (JOV) \ud83e\uddd9\ud83c\udffd": "Blend two Normal maps", - "GLSL RGB-2-HSV (JOV) \ud83e\uddd9\ud83c\udffd": "Convert RGB(A) input into HSV color space", - "GLSL RGB-2-LAB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert RGB(A) image into LAB color space", - "GLSL RGB-2-XYZ (JOV) \ud83e\uddd9\ud83c\udffd": "Convert RGB(A) input into XYZ color space", - "GLSL XYZ-2-LAB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert XYZ(W) image into LAB color space", - "GLSL XYZ-2-RGB (JOV) \ud83e\uddd9\ud83c\udffd": "Convert XYZ(W) image into RGB color space", - "GRADIENT MAP (JOV) \ud83c\uddf2\ud83c\uddfa": "Remaps an input image using a gradient lookup table (LUT)", - "GRAPH (JOV) \ud83d\udcc8": "Visualize a series of data points over time", - "IMAGE INFO (JOV) \ud83d\udcda": "Exports and Displays immediate information about images", "LERP (JOV) \ud83d\udd30": "Calculate linear interpolation between two values or vectors based on a blending factor (alpha)", "MIDI FILTER (JOV) \u2733\ufe0f": "Provides advanced filtering capabilities for MIDI messages based on various criteria, including MIDI mode (such as note on or note off), MIDI channel, control number, note number, value, and normalized value", "MIDI FILTER EZ (JOV) \u2747\ufe0f": "Filter MIDI messages based on various criteria, including MIDI mode (such as note on or note off), MIDI channel, control number, note number, value, and normalized value", @@ -38,25 +8,8 @@ "MIDI READER (JOV) \ud83c\udfb9": "Captures MIDI messages from an external MIDI device or controller", "OP BINARY (JOV) \ud83c\udf1f": "Execute binary operations like addition, subtraction, multiplication, division, and bitwise operations on input values, supporting various data types and vector sizes", "OP UNARY (JOV) \ud83c\udfb2": "Perform single function operations like absolute value, mean, median, mode, magnitude, normalization, maximum, or minimum on input values", - "PIXEL MERGE (JOV) \ud83e\udec2": "Combines individual color channels (red, green, blue) along with an optional mask channel to create a composite image", - "PIXEL SPLIT (JOV) \ud83d\udc94": "Takes an input image and splits it into its individual color channels (red, green, blue), along with a mask channel", - "PIXEL SWAP (JOV) \ud83d\udd03": "Swap pixel values between two input images based on specified channel swizzle operations", - "QUEUE (JOV) \ud83d\uddc3": "Manage a queue of items, such as file paths or data", - "ROUTE (JOV) \ud83d\ude8c": "Routes the input data from the optional input ports to the output port, preserving the order of inputs", - "SAVE OUTPUT (JOV) \ud83d\udcbe": "Save the output image along with its metadata to the specified path", - "SHAPE GEN (JOV) \u2728": "Create n-sided polygons", - "SPOUT WRITER (JOV) \ud83c\udfa5": "Sends frames to a specified Spout receiver application for real-time video sharing", - "STACK (JOV) \u2795": "Merge multiple input images into a single composite image by stacking them along a specified axis", - "STEREOGRAM (JOV) \ud83d\udcfb": "Generates false perception 3D images from 2D input", - "STEREOSCOPIC (JOV) \ud83d\udd76\ufe0f": "Simulates depth perception in images by generating stereoscopic views", - "STREAM READER (JOV) \ud83d\udcfa": "Capture frames from various sources such as URLs, cameras, monitors, windows, or Spout streams", - "STREAM WRITER (JOV) \ud83c\udf9e\ufe0f": "Sends frames to a specified route, typically for live streaming or recording purposes", "SWIZZLE (JOV) \ud83d\ude35": "Swap components between two vectors based on specified swizzle patterns and values", - "TEXT GEN (JOV) \ud83d\udcdd": "Generates images containing text based on parameters such as font, size, alignment, color, and position", - "THRESHOLD (JOV) \ud83d\udcc9": "Define a range and apply it to an image for segmentation and feature extraction", "TICK (JOV) \u23f1": "A timer and frame counter, emitting pulses or signals based on time intervals", - "TRANSFORM (JOV) \ud83c\udfdd\ufe0f": "Apply various geometric transformations to images, including translation, rotation, scaling, mirroring, tiling and perspective projection", "VALUE (JOV) \ud83e\uddec": "Supplies raw or default values for various data types, supporting vector input with components for X, Y, Z, and W", - "WAVE GEN (JOV) \ud83c\udf0a": "Produce waveforms like sine, square, or sawtooth with adjustable frequency, amplitude, phase, and offset", - "WAVE GRAPH (JOV) \u25b6 \u0131l\u0131\u0131l\u0131": "The Wave Graph node visualizes audio waveforms as bars" + "WAVE GEN (JOV) \ud83c\udf0a": "Produce waveforms like sine, square, or sawtooth with adjustable frequency, amplitude, phase, and offset" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9f08526..60a0ca6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "jovimetrix" description = "Integrates Webcam, MIDI, Spout and GLSL shader support. Animation via tick. Parameter manipulation with wave generator. Math operations with Unary and Binary support. Value conversion for all major types (int, string, list, dict, Image, Mask). Shape mask generation, image stacking and channel ops, batch splitting, merging and randomizing, load images and video from anywhere, dynamic bus routing with a single node, export support for GIPHY, save output anywhere! flatten, crop, transform; check colorblindness, make stereogram or stereoscopic images, or liner interpolate values and more." -version = "1.2.27" +version = "1.2.28" license = { file = "LICENSE" } dependencies = [ "aenum>=3.1.15,<4", diff --git a/reqire.txt b/reqire.txt new file mode 100644 index 0000000..9c8dac1 Binary files /dev/null and b/reqire.txt differ diff --git a/requirements.txt b/requirements.txt index 7b14477..5dc2828 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ aenum>=3.1.15,<4 blendmodes>=2024.1.1 +cupy-cuda12x>=13.3.0 daltonlens>=0.1.5 glfw>=2.7.0 loguru>=0.7.2 diff --git a/web/core/core_color.js b/web/core/core_color.js index f9f70a3..81af124 100644 --- a/web/core/core_color.js +++ b/web/core/core_color.js @@ -7,8 +7,9 @@ import { app } from '../../../scripts/app.js' import { $el } from '../../../scripts/ui.js' import { apiPost } from '../util/util_api.js' +import { colorContrast } from '../util/util.js' import * as util_config from '../util/util_config.js' -import '../extern/jsColorPicker.js' +import { colorPicker } from '../extern/jsColorPicker.js' // gets the CONFIG entry for name function nodeColorGet(node) { @@ -75,14 +76,6 @@ function nodeColorAll() { app.canvas.setDirty(true); } -/* -const colorClear = (name) => { - apiPost("/jovimetrix/config/clear", { name }); - delete util_config.CONFIG_THEME[name]; - if (util_config.CONFIG_COLOR.overwrite) nodeColorAll(); -}; -*/ - class JovimetrixPanelColorize { constructor() { @@ -199,10 +192,10 @@ class JovimetrixPanelColorize { } }); } else if (retries > 0) { - console.warn(`jsColorPicker not available, retrying... (${retries} attempts left)`); + console.warn(`colorPicker not available, retrying... (${retries} attempts left)`); setTimeout(() => this.initializeColorPicker(retries - 1), 1000); } else { - console.error('jsColorPicker function is not available after multiple attempts'); + console.error('colorPicker function is not available after multiple attempts'); } } @@ -432,4 +425,96 @@ app.extensionManager.registerSidebarTab({ el.appendChild(content); } } -}); \ No newline at end of file +}); + +app.registerExtension({ + name: "jovimetrix.color", + async setup(app) { + // Option for user to contrast text for better readability + const original_color = LiteGraph.NODE_TEXT_COLOR; + + util_config.setting_make('color 🎨.contrast', 'Auto-Contrast Text', 'boolean', 'Auto-contrast the title text for all nodes for better readability', true); + + const drawNodeShape = LGraphCanvas.prototype.drawNodeShape; + LGraphCanvas.prototype.drawNodeShape = function() { + const contrast = localStorage["Comfy.Settings.jov.user.default.color.contrast"] || false; + if (contrast == true) { + var color = this.color || LiteGraph.NODE_TITLE_COLOR; + var bgcolor = this.bgcolor || LiteGraph.WIDGET_BGCOLOR; + this.node_title_color = colorContrast(color) ? "#000" : "#FFF"; + LiteGraph.NODE_TEXT_COLOR = colorContrast(bgcolor) ? "#000" : "#FFF"; + } else { + this.node_title_color = original_color + LiteGraph.NODE_TEXT_COLOR = original_color; + } + drawNodeShape.apply(this, arguments); + }; + + if (util_config.CONFIG_USER.color.overwrite) { + nodeColorAll(); + } + }, + async beforeRegisterNodeDef(nodeType) { + const onNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = async function () { + const me = onNodeCreated?.apply(this, arguments); + if (this) { + nodeColorReset(this, false); + } + return me; + } + } +}) + +function initializeColorPicker() { + const elements = document.querySelectorAll('input.jov-color'); + if (elements.length) { + colorPicker(elements, { + readOnly: true, + size: 2, + multipleInstances: false, + appendTo: document, + noAlpha: false, + init: function(elm, rgb) { + elm.style.backgroundColor = elm.color || LiteGraph.WIDGET_BGCOLOR; + elm.style.color = rgb.RGBLuminance > 0.22 ? '#222' : '#ddd' + }, + convertCallback: function(data) { + let AHEX = this.patch.attributes.color; + if (!AHEX) return; + + let name = this.patch.attributes.name.value; + const parts = name.split('.'); + const part = parts.pop(); + name = parts[0]; + let api_packet = {}; + + if (parts.length > 1) { + const idx = parts[1]; + let data = util_config.CONFIG_REGEX[idx]; + data[part] = AHEX.value; + util_config.CONFIG_REGEX[idx] = data; + + api_packet = { + id: `${util_config.USER}.color.regex`, + v: util_config.CONFIG_REGEX + }; + } else { + const themeConfig = util_config.CONFIG_THEME[name] || (util_config.CONFIG_THEME[name] = {}); + themeConfig[part] = AHEX.value; + + api_packet = { + id: `${util_config.USER}.color.theme.${name}`, + v: themeConfig + }; + } + apiPost("/jovimetrix/config", api_packet); + if (util_config.CONFIG_COLOR.overwrite) { + nodeColorAll(); + } + } + }); + } +} + +window.addEventListener('DOMContentLoaded', initializeColorPicker); \ No newline at end of file diff --git a/web/core/core_cozy_tips.js b/web/core/core_cozy_tips.js index 5b7f31d..b18a7ba 100644 --- a/web/core/core_cozy_tips.js +++ b/web/core/core_cozy_tips.js @@ -150,7 +150,7 @@ app.registerExtension({ if (!tip) { return hideTooltip(); } - showTooltip(tip, app.canvas.mouse[0], app.canvas.mouse[1] - 18); + showTooltip(tip, app.canvas.mouse[0], app.canvas.mouse[1] - 26); }.bind(app.canvas); app.ui.settings.addSetting({ diff --git a/web/extern/jsColorPicker.js b/web/extern/jsColorPicker.js index 444b6fb..25caa03 100644 --- a/web/extern/jsColorPicker.js +++ b/web/extern/jsColorPicker.js @@ -1,256 +1,174 @@ -// jsColorpicker.js - -import { colorRGB2Hex } from '../util/util.js'; -import { local_set, local_get } from '../util/util_config.js'; - -(function (global) { - if (typeof global.ColorPicker === 'undefined') { - global.ColorPicker = {}; - } - - // Define docCookies if it's not already defined - if (typeof window.ColorPicker.docCookies === 'undefined') { - window.ColorPicker.docCookies = { - getItem: function (key, def) { - const data = local_get(key, def); - return data; - }, - setItem: function (key, value) { - local_set(key, value); - } +const ColorPicker = { + colorPickers: window.jsColorPicker?.colorPickers || [], + docCookies: { + getItem: (key, def) => { + const data = local_get(key, def); + return data; + }, + setItem: (key, value) => { + local_set(key, value); + } + }, + + createInstance: (elm, config) => { + const initConfig = { + input: elm, + patch: elm, + isIE8: !!document.all && !document.addEventListener, + margin: { left: -1, top: 2 }, + customBG: '#FFFFFF', + color: ColorPicker.extractValue(elm), + initStyle: 'display: none', + mode: ColorPicker.docCookies('colorPickerMode') || 'hsv-h', + memoryColors: ColorPicker.docCookies('colorPickerMemos' + ((config || {}).noAlpha ? 'NoAlpha' : '')), + size: ColorPicker.docCookies('colorPickerSize') || 1, + renderCallback: ColorPicker.renderCallback, + actionCallback: ColorPicker.actionCallback }; - } - - global.jsColorPicker = function(selectors, config) { - var renderCallback = function(colors, mode) { - var options = this, - input = options.input, - patch = options.patch, - RGB = colors.RND.rgb; - const rgb = Object.values(RGB).reverse(); - const AHEX = !colors.HEX.includes("NAN") ? colorRGB2Hex(rgb) : LiteGraph.NODE_DEFAULT_BGCOLOR // LiteGraph.NODE_DEFAULT_COLOR; // LiteGraph.NODE_DEFAULT_BGCOLOR - - patch.style.cssText = - 'color:' + (colors.rgbaMixCustom.luminance > 0.22 ? '#222' : '#ddd') + ';' + // Black...??? - 'background-color: ' + AHEX + ';' + - 'filter:'; - input.setAttribute("color", AHEX); - if (options.displayCallback) { - options.displayCallback(colors, mode, options); - } - }, - extractValue = function(elm) { - const val = elm.getAttribute('color') || elm.style.backgroundColor || '#353535FF'; - if (val.includes("NAN")) { - return "#353535FF"; - } - return val; - }, - actionCallback = function(event, action) { - var options = this, - colorPicker = colorPickers.current; + for (const n in config) { + initConfig[n] = config[n]; + } - if (action === 'toMemory') { - var memos = colorPicker.nodes.memos, - backgroundColor = '', - opacity = 0, - cookieTXT = []; + return new ColorPicker(initConfig); + }, - for (var n = 0, m = memos.length; n < m; n++) { - backgroundColor = memos[n].style.backgroundColor; - opacity = memos[n].style.opacity; - opacity = Math.round((opacity === '' ? 1 : opacity) * 100) / 100; - cookieTXT.push(backgroundColor. - replace(/, /g, ','). - replace('rgb(', 'rgba('). - replace(')', ',' + opacity + ')') - ); - } - cookieTXT = '\'' + cookieTXT.join('\',\'') + '\''; - ColorPicker.docCookies('colorPickerMemos' + (options.noAlpha ? 'NoAlpha' : ''), cookieTXT); - } else if (action === 'resizeApp') { - ColorPicker.docCookies('colorPickerSize', colorPicker.color.options.currentSize); - } else if (action === 'modeChange') { - var mode = colorPicker.color.options.mode; - ColorPicker.docCookies('colorPickerMode', mode.type + '-' + mode.z); - } - }, - createInstance = function(elm, config) { - var initConfig = { - klass: window.ColorPicker, - input: elm, - patch: elm, - isIE8: !!document.all && !document.addEventListener, // Opera??? - // *** animationSpeed: 200, - // *** draggable: true, - margin: {left: -1, top: 2}, - customBG: '#FFFFFF', - // displayCallback: displayCallback, - /* --- regular colorPicker options from this point --- */ - color: extractValue(elm), - initStyle: 'display: none', - mode: ColorPicker.docCookies('colorPickerMode') || 'hsv-h', - // memoryColors: (function(colors, config) { - // return config.noAlpha ? - // colors.replace(/\,\d*\.*\d*\)/g, ',1)') : colors; - // })($.docCookies('colorPickerMemos'), config || {}), - memoryColors: ColorPicker.docCookies('colorPickerMemos' + - ((config || {}).noAlpha ? 'NoAlpha' : '')), - size: ColorPicker.docCookies('colorPickerSize') || 1, - renderCallback: renderCallback, - actionCallback: actionCallback - }; + renderCallback: (colors, mode) => { + const rgb = Object.values(colors.RND.rgb).reverse(); + const AHEX = !colors.HEX.includes("NAN") ? colorRGB2Hex(rgb) : "#353535FF"; - for (var n in config) { - initConfig[n] = config[n]; - } - return new initConfig.klass(initConfig); - }, - doEventListeners = function(elm, multiple, off) { - var onOff = off ? 'removeEventListener' : 'addEventListener', - focusListener = function() { - var input = this, - position = window.ColorPicker.getOrigin(input), - index = multiple ? Array.prototype.indexOf.call(elms, this) : 0, - colorPicker = colorPickers[index] || - (colorPickers[index] = createInstance(this, config)), - options = colorPicker.color.options, - colorPickerUI = colorPicker.nodes.colorPicker, - appendTo = (options.appendTo || document.body), - isStatic = /static/.test(window.getComputedStyle(appendTo).position), - atrect = isStatic ? {left: 0, top: 0} : appendTo.getBoundingClientRect(), - waitTimer = 0; + this.patch.style.cssText = + 'color:' + (colors.rgbaMixCustom.luminance > 0.22 ? '#222' : '#ddd') + ';' + + 'background-color: ' + AHEX + ';'; - options.color = extractValue(elm); // brings color to default on reset - colorPickerUI.style.cssText = - 'position: absolute;' + (!colorPickers[index].cssIsReady ? 'display: none;' : '') + - 'left:' + (position.left + options.margin.left - atrect.left) + 'px;' + - 'top:' + (position.top + +input.offsetHeight + options.margin.top - atrect.top) + 'px;'; + this.input.setAttribute("color", AHEX); - if (!multiple) { - options.input = elm; - options.patch = elm; // check again??? - colorPicker.setColor(extractValue(elm), undefined, undefined, true); - colorPicker.saveAsBackground(); - } - colorPickers.current = colorPickers[index]; - appendTo.appendChild(colorPickerUI); - waitTimer = setInterval(function() { - // compensating late style on onload in colorPicker - if (colorPickers.current.cssIsReady) { - waitTimer = clearInterval(waitTimer); - colorPickerUI.style.display = 'block'; - } - }, 10); - }, - mousDownListener = function(e) { - var colorPicker = colorPickers.current, - colorPickerUI = (colorPicker ? colorPicker.nodes.colorPicker : undefined), - // animationSpeed = colorPicker ? colorPicker.color.options.animationSpeed : 0 - isColorPicker = colorPicker && (function(elm) { - while (elm) { - if ((elm.className || '').indexOf('cp-app') !== -1) return elm; - elm = elm.parentNode; - } - return false; - })(e.target), - inputIndex = Array.prototype.indexOf.call(elms, e.target); + if (this.displayCallback) { + this.displayCallback(colors, mode, this); + } + }, + + extractValue: (elm) => { + const val = elm.getAttribute('color') || elm.style.backgroundColor || '#353535FF'; + return val.includes("NAN") ? "#353535FF" : val; + }, + + actionCallback: (event, action) => { + if (action === 'toMemory') { + const memos = this.nodes.memos; + const cookieTXT = []; + + for (let n = 0, m = memos.length; n < m; n++) { + let backgroundColor = memos[n].style.backgroundColor; + let opacity = memos[n].style.opacity; + opacity = Math.round((opacity === '' ? 1 : opacity) * 100) / 100; + cookieTXT.push(backgroundColor.replace(/, /g, ',').replace('rgb(', 'rgba(').replace(')', ',' + opacity + ')')); + } - if (isColorPicker && Array.prototype.indexOf.call(colorPickers, isColorPicker)) { - if (e.target === colorPicker.nodes.exit) { - colorPickerUI.style.display = 'none'; - document.activeElement.blur(); - } else { - // ... - } - } else if (inputIndex !== -1) { - // ... - } else if (colorPickerUI) { - colorPickerUI.style.display = 'none'; - } - }; + ColorPicker.docCookies('colorPickerMemos' + (this.noAlpha ? 'NoAlpha' : ''), "'" + cookieTXT.join("','") + "'"); + } else if (action === 'resizeApp') { + ColorPicker.docCookies('colorPickerSize', this.color.options.currentSize); + } else if (action === 'modeChange') { + const mode = this.color.options.mode; + ColorPicker.docCookies('colorPickerMode', mode.type + '-' + mode.z); + } + }, + + doEventListeners: (elm, multiple, off, elms) => { + const onOff = off ? 'removeEventListener' : 'addEventListener'; + const focusListener = () => { + const position = ColorPicker.getOrigin(this); + const index = multiple ? Array.prototype.indexOf.call(elms, this) : 0; + const colorPicker = ColorPicker.colorPickers[index] || (ColorPicker.colorPickers[index] = ColorPicker.createInstance(this, config)); + const options = colorPicker.color.options; + const colorPickerUI = colorPicker.nodes.colorPicker; + const appendTo = (options.appendTo || document.body); + const isStatic = /static/.test(window.getComputedStyle(appendTo).position); + const atrect = isStatic ? { left: 0, top: 0 } : appendTo.getBoundingClientRect(); + + options.color = ColorPicker.extractValue(elm); + colorPickerUI.style.cssText = + 'position: absolute;' + (!ColorPicker.colorPickers[index].cssIsReady ? 'display: none;' : '') + + 'left:' + (position.left + options.margin.left - atrect.left) + 'px;' + + 'top:' + (position.top + +this.offsetHeight + options.margin.top - atrect.top) + 'px;'; + + if (!multiple) { + options.input = elm; + options.patch = elm; + colorPicker.setColor(ColorPicker.extractValue(elm), undefined, undefined, true); + colorPicker.saveAsBackground(); + } - elm[onOff]('focus', focusListener); + ColorPicker.colorPickers.current = ColorPicker.colorPickers[index]; + appendTo.appendChild(colorPickerUI); - if (!colorPickers.evt || off) { - // prevent new eventListener for window - colorPickers.evt = true; - window[onOff]('mousedown', mousDownListener); + let waitTimer = setInterval(function() { + if (ColorPicker.colorPickers.current.cssIsReady) { + waitTimer = clearInterval(waitTimer); + colorPickerUI.style.display = 'block'; } - }, - // this is a way to prevent data binding on HTMLElements - colorPickers = window.jsColorPicker.colorPickers || [], - elms = document.querySelectorAll(selectors), - testColors = new window.Colors({customBG: config.customBG, allMixDetails: true}); - - window.jsColorPicker.colorPickers = colorPickers; - - for (var n = 0, m = elms.length; n < m; n++) { - var elm = elms[n]; + }, 10); + }; - if (config === 'destroy') { - doEventListeners(elm, (config && config.multipleInstances), true); - if (colorPickers[n]) { - colorPickers[n].destroyAll(); - } - } else { - var color = extractValue(elm); - var value = color.split('('); + elm[onOff]('focus', focusListener); + + if (!ColorPicker.colorPickers.evt || off) { + ColorPicker.colorPickers.evt = true; + window[onOff]('mousedown', (e) => { + const colorPicker = ColorPicker.colorPickers.current; + const colorPickerUI = (colorPicker ? colorPicker.nodes.colorPicker : undefined); + const isColorPicker = colorPicker && (function(elm) { + while (elm) { + if ((elm.className || '').indexOf('cp-app') !== -1) return elm; + elm = elm.parentNode; + } + return false; + })(e.target); - testColors.setColor(color); - if (config && config.init) { - config.init(elm, testColors.colors); - } - elm.setAttribute('data-colorMode', value[1] ? value[0].substr(0, 3) : 'HEX'); - doEventListeners(elm, (config && config.multipleInstances), false); - if (config && config.readOnly) { - elm.readOnly = true; + if (isColorPicker && Array.prototype.indexOf.call(ColorPicker.colorPickers, isColorPicker)) { + if (e.target === colorPicker.nodes.exit) { + colorPickerUI.style.display = 'none'; + document.activeElement.blur(); + } + } else if (Array.prototype.indexOf.call(elms, e.target) !== -1) { + } else if (colorPickerUI) { + colorPickerUI.style.display = 'none'; } - } - }; + }); + } + } +}; - return window.jsColorPicker.colorPickers; - }; +export function colorPicker(selectors, config, callback, elms) { + const testColors = new window.Colors({ customBG: config.customBG, allMixDetails: true }); - global.ColorPicker.docCookies = function(key, val, options) { - var encode = encodeURIComponent, decode = decodeURIComponent, - cookies, n, tmp, cache = {}, - days; + for (let n = 0, m = elms.length; n < m; n++) { + const elm = elms[n]; - if (val === undefined) { // all about reading cookies - cookies = document.cookie.split(/;\s*/) || []; - for (n = cookies.length; n--; ) { - tmp = cookies[n].split('='); - if (tmp[0]) cache[decode(tmp.shift())] = decode(tmp.join('=')); // there might be '='s in the value... + if (config === 'destroy') { + ColorPicker.doEventListeners(elm, (config && config.multipleInstances), true, elms); + if (ColorPicker.colorPickers[n]) { + ColorPicker.colorPickers[n].destroyAll(); } + } else { + const color = ColorPicker.extractValue(elm); + const value = color.split('('); - if (!key) return cache; // return Json for easy access to all cookies - else return cache[key]; // easy access to cookies from here - } else { // write/delete cookie - options = options || {}; - - if (val === '' || options.expires < 0) { // prepare deleteing the cookie - options.expires = -1; - // options.path = options.domain = options.secure = undefined; // to make shure the cookie gets deleted... + testColors.setColor(color); + if (config && config.init) { + config.init(elm, testColors.colors); } - - if (options.expires !== undefined) { // prepare date if any - days = new Date(); - days.setDate(days.getDate() + options.expires); + elm.setAttribute('data-colorMode', value[1] ? value[0].substr(0, 3) : 'HEX'); + ColorPicker.doEventListeners(elm, (config && config.multipleInstances), false, elms); + if (config && config.readOnly) { + elm.readOnly = true; } - - document.cookie = encode(key) + '=' + encode(val) + - (days ? '; expires=' + days.toUTCString() : '') + - (options.path ? '; path=' + options.path : '') + - (options.domain ? '; domain=' + options.domain : '') + - (options.secure ? '; secure' : ''); } - }; -})(typeof window !== 'undefined' ? window : this); + } + + if (callback) { + callback(ColorPicker.colorPickers); + } -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - module.exports = jsColorPicker; -} else { - window.jsColorPicker = jsColorPicker; -} \ No newline at end of file + return ColorPicker.colorPickers; +}