|
|
|
/*
|
|
|
|
* Copyright (c) 2010 Tim Baumann
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// NOTE: Converted to ES6 by MightyPork (2017)
|
|
|
|
// Modified for ESPTerm
|
|
|
|
|
|
|
|
const EventEmitter = require('events')
|
|
|
|
const {
|
|
|
|
rgb2hex,
|
|
|
|
hex2rgb,
|
|
|
|
hsl2rgb,
|
|
|
|
rgb2hsl
|
|
|
|
} = require('./color_utils')
|
|
|
|
|
|
|
|
const win = window
|
|
|
|
const doc = document
|
|
|
|
const M = Math
|
|
|
|
const TAU = 2 * M.PI
|
|
|
|
|
|
|
|
function times (i, fn) {
|
|
|
|
for (let j = 0; j < i; j++) {
|
|
|
|
fn(j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function each (obj, fn) {
|
|
|
|
if (obj.length) {
|
|
|
|
times(obj.length, function (i) {
|
|
|
|
fn(obj[i], i)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
for (let key in obj) {
|
|
|
|
if (obj.hasOwnProperty(key)) {
|
|
|
|
fn(obj[key], key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = class ColorTriangle extends EventEmitter {
|
|
|
|
/****************
|
|
|
|
* ColorTriangle *
|
|
|
|
****************/
|
|
|
|
|
|
|
|
// Constructor function:
|
|
|
|
constructor (color, options) {
|
|
|
|
super()
|
|
|
|
|
|
|
|
this.options = {
|
|
|
|
size: 150,
|
|
|
|
padding: 8,
|
|
|
|
triangleSize: 0.8,
|
|
|
|
wheelPointerColor1: '#444',
|
|
|
|
wheelPointerColor2: '#eee',
|
|
|
|
trianglePointerSize: 16,
|
|
|
|
// wheelPointerSize: 16,
|
|
|
|
trianglePointerColor1: '#eee',
|
|
|
|
trianglePointerColor2: '#444',
|
|
|
|
background: 'transparent'
|
|
|
|
}
|
|
|
|
|
|
|
|
this.pixelRatio = window.devicePixelRatio
|
|
|
|
|
|
|
|
this.setOptions(options)
|
|
|
|
this.calculateProperties()
|
|
|
|
|
|
|
|
this.createContainer()
|
|
|
|
this.createTriangle()
|
|
|
|
this.createWheel()
|
|
|
|
this.createWheelPointer()
|
|
|
|
this.createTrianglePointer()
|
|
|
|
this.attachEvents()
|
|
|
|
|
|
|
|
color = color || '#f00'
|
|
|
|
if (typeof color == 'string') {
|
|
|
|
this.setHEX(color)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
calculateProperties () {
|
|
|
|
let opts = this.options
|
|
|
|
|
|
|
|
this.padding = opts.padding
|
|
|
|
this.innerSize = opts.size - opts.padding * 2
|
|
|
|
this.triangleSize = opts.triangleSize * this.innerSize
|
|
|
|
this.wheelThickness = (this.innerSize - this.triangleSize) / 2
|
|
|
|
this.wheelPointerSize = opts.wheelPointerSize || this.wheelThickness
|
|
|
|
|
|
|
|
this.wheelRadius = (this.innerSize) / 2
|
|
|
|
this.triangleRadius = (this.triangleSize) / 2
|
|
|
|
this.triangleSideLength = M.sqrt(3) * this.triangleRadius
|
|
|
|
}
|
|
|
|
|
|
|
|
calculatePositions () {
|
|
|
|
const r = this.triangleRadius
|
|
|
|
const hue = this.hue
|
|
|
|
const third = TAU / 3
|
|
|
|
const s = this.saturation
|
|
|
|
const l = this.lightness
|
|
|
|
|
|
|
|
// Colored point
|
|
|
|
const hx = this.hx = M.cos(hue) * r
|
|
|
|
const hy = this.hy = -M.sin(hue) * r
|
|
|
|
// Black point
|
|
|
|
const sx = this.sx = M.cos(hue - third) * r
|
|
|
|
const sy = this.sy = -M.sin(hue - third) * r
|
|
|
|
// White point
|
|
|
|
const vx = this.vx = M.cos(hue + third) * r
|
|
|
|
const vy = this.vy = -M.sin(hue + third) * r
|
|
|
|
// Current point
|
|
|
|
const mx = (sx + vx) / 2
|
|
|
|
const my = (sy + vy) / 2
|
|
|
|
const a = (1 - 2 * M.abs(l - 0.5)) * s
|
|
|
|
this.x = sx + (vx - sx) * l + (hx - mx) * a
|
|
|
|
this.y = sy + (vy - sy) * l + (hy - my) * a
|
|
|
|
}
|
|
|
|
|
|
|
|
createContainer () {
|
|
|
|
let c = this.container = doc.createElement('div')
|
|
|
|
c.className = 'color-triangle'
|
|
|
|
|
|
|
|
c.style.display = 'block'
|
|
|
|
c.style.padding = `${this.padding}px`
|
|
|
|
c.style.position = 'relative'
|
|
|
|
c.style.boxShadow = '0 1px 10px black'
|
|
|
|
c.style.borderRadius = '5px'
|
|
|
|
c.style.width = c.style.height = `${this.innerSize + 2 * this.padding}px`
|
|
|
|
c.style.background = this.options.background
|
|
|
|
}
|
|
|
|
|
|
|
|
createWheel () {
|
|
|
|
let c = this.wheel = doc.createElement('canvas')
|
|
|
|
c.width = c.height = this.innerSize * this.pixelRatio
|
|
|
|
c.style.width = c.style.height = `${this.innerSize}px`
|
|
|
|
c.style.position = 'absolute'
|
|
|
|
c.style.margin = c.style.padding = '0'
|
|
|
|
c.style.left = c.style.top = `${this.padding}px`
|
|
|
|
|
|
|
|
this.drawWheel(c.getContext('2d'))
|
|
|
|
this.container.appendChild(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
drawWheel (ctx) {
|
|
|
|
let s, i
|
|
|
|
|
|
|
|
ctx.save()
|
|
|
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0)
|
|
|
|
ctx.translate(this.wheelRadius, this.wheelRadius)
|
|
|
|
s = this.wheelRadius - this.triangleRadius
|
|
|
|
// Draw a circle for every color
|
|
|
|
for (i = 0; i < 360; i++) {
|
|
|
|
ctx.rotate(TAU / -360) // rotate one degree
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.fillStyle = 'hsl(' + i + ', 100%, 50%)'
|
|
|
|
ctx.arc(this.wheelRadius - (s / 2), 0, s / 2, 0, TAU, true)
|
|
|
|
ctx.fill()
|
|
|
|
}
|
|
|
|
ctx.restore()
|
|
|
|
}
|
|
|
|
|
|
|
|
createTriangle () {
|
|
|
|
let c = this.triangle = doc.createElement('canvas')
|
|
|
|
|
|
|
|
c.width = c.height = this.innerSize * this.pixelRatio
|
|
|
|
c.style.width = c.style.height = `${this.innerSize}px`
|
|
|
|
c.style.position = 'absolute'
|
|
|
|
c.style.margin = c.style.padding = '0'
|
|
|
|
c.style.left = c.style.top = this.padding + 'px'
|
|
|
|
|
|
|
|
this.triangleCtx = c.getContext('2d')
|
|
|
|
|
|
|
|
this.container.appendChild(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
drawTriangle () {
|
|
|
|
const hx = this.hx
|
|
|
|
const hy = this.hy
|
|
|
|
const sx = this.sx
|
|
|
|
const sy = this.sy
|
|
|
|
const vx = this.vx
|
|
|
|
const vy = this.vy
|
|
|
|
const size = this.innerSize
|
|
|
|
|
|
|
|
let ctx = this.triangleCtx
|
|
|
|
|
|
|
|
// clear
|
|
|
|
ctx.clearRect(0, 0, size * this.pixelRatio, size * this.pixelRatio)
|
|
|
|
|
|
|
|
ctx.save()
|
|
|
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0)
|
|
|
|
ctx.translate(this.wheelRadius, this.wheelRadius)
|
|
|
|
|
|
|
|
// make a triangle
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.moveTo(hx, hy)
|
|
|
|
ctx.lineTo(sx, sy)
|
|
|
|
ctx.lineTo(vx, vy)
|
|
|
|
ctx.closePath()
|
|
|
|
ctx.clip()
|
|
|
|
|
|
|
|
ctx.fillStyle = '#000'
|
|
|
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
|
|
|
|
// => black triangle
|
|
|
|
|
|
|
|
// create gradient from hsl(hue, 1, 1) to transparent
|
|
|
|
let grad0 = ctx.createLinearGradient(hx, hy, (sx + vx) / 2, (sy + vy) / 2)
|
|
|
|
const hsla = 'hsla(' + M.round(this.hue * (360 / TAU)) + ', 100%, 50%, '
|
|
|
|
grad0.addColorStop(0, hsla + '1)')
|
|
|
|
grad0.addColorStop(1, hsla + '0)')
|
|
|
|
ctx.fillStyle = grad0
|
|
|
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
|
|
|
|
// => gradient: one side of the triangle is black, the opponent angle is $color
|
|
|
|
|
|
|
|
// create color gradient from white to transparent
|
|
|
|
let grad1 = ctx.createLinearGradient(vx, vy, (hx + sx) / 2, (hy + sy) / 2)
|
|
|
|
grad1.addColorStop(0, '#fff')
|
|
|
|
grad1.addColorStop(1, 'rgba(255, 255, 255, 0)')
|
|
|
|
ctx.globalCompositeOperation = 'lighter'
|
|
|
|
ctx.fillStyle = grad1
|
|
|
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
|
|
|
|
// => white angle
|
|
|
|
|
|
|
|
ctx.restore()
|
|
|
|
}
|
|
|
|
|
|
|
|
// The two pointers
|
|
|
|
createWheelPointer () {
|
|
|
|
let c = this.wheelPointer = doc.createElement('canvas')
|
|
|
|
const size = this.wheelPointerSize
|
|
|
|
c.width = c.height = size * this.pixelRatio
|
|
|
|
c.style.width = c.style.height = `${size}px`
|
|
|
|
c.style.position = 'absolute'
|
|
|
|
c.style.margin = c.style.padding = '0'
|
|
|
|
this.drawPointer(c.getContext('2d'), size / 2, this.options.wheelPointerColor1, this.options.wheelPointerColor2)
|
|
|
|
this.container.appendChild(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
moveWheelPointer () {
|
|
|
|
const r = this.wheelPointerSize / 2
|
|
|
|
const s = this.wheelPointer.style
|
|
|
|
s.top = this.padding + this.wheelRadius - M.sin(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px'
|
|
|
|
s.left = this.padding + this.wheelRadius + M.cos(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px'
|
|
|
|
}
|
|
|
|
|
|
|
|
createTrianglePointer () { // create pointer in the triangle
|
|
|
|
let c = this.trianglePointer = doc.createElement('canvas')
|
|
|
|
const size = this.options.trianglePointerSize
|
|
|
|
|
|
|
|
c.width = c.height = size * this.pixelRatio
|
|
|
|
c.style.width = c.style.height = `${size}px`
|
|
|
|
c.style.position = 'absolute'
|
|
|
|
c.style.margin = c.style.padding = '0'
|
|
|
|
this.drawPointer(c.getContext('2d'), size / 2, this.options.trianglePointerColor1, this.options.trianglePointerColor2)
|
|
|
|
this.container.appendChild(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
moveTrianglePointer (x, y) {
|
|
|
|
const s = this.trianglePointer.style
|
|
|
|
const r = this.options.trianglePointerSize / 2
|
|
|
|
s.top = (this.y + this.wheelRadius + this.padding - r) + 'px'
|
|
|
|
s.left = (this.x + this.wheelRadius + this.padding - r) + 'px'
|
|
|
|
}
|
|
|
|
|
|
|
|
drawPointer (ctx, r, color1, color2) {
|
|
|
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0)
|
|
|
|
ctx.fillStyle = color2
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.arc(r, r, r, 0, TAU, true)
|
|
|
|
ctx.fill() // => black circle
|
|
|
|
ctx.fillStyle = color1
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.arc(r, r, r - 2, 0, TAU, true)
|
|
|
|
ctx.fill() // => white circle with 1px black border
|
|
|
|
ctx.fillStyle = color2
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.arc(r, r, r / 4 + 2, 0, TAU, true)
|
|
|
|
ctx.fill() // => black circle with big white border and a small black border
|
|
|
|
ctx.globalCompositeOperation = 'destination-out'
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.arc(r, r, r / 4, 0, TAU, true)
|
|
|
|
ctx.fill() // => transparent center
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Element and the DOM
|
|
|
|
inject (parent) {
|
|
|
|
parent.appendChild(this.container)
|
|
|
|
}
|
|
|
|
|
|
|
|
getRelativeCoordinates (evt) {
|
|
|
|
let elem = this.triangle
|
|
|
|
let rect = elem.getBoundingClientRect()
|
|
|
|
|
|
|
|
return {
|
|
|
|
x: evt.clientX - rect.x,
|
|
|
|
y: evt.clientY - rect.y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dispose () {
|
|
|
|
let parent = this.container.parentNode
|
|
|
|
if (parent) {
|
|
|
|
parent.removeChild(this.container)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getElement () {
|
|
|
|
return this.container
|
|
|
|
}
|
|
|
|
|
|
|
|
// Color accessors
|
|
|
|
getCSS () {
|
|
|
|
const h = Math.round(this.hue * (360 / TAU))
|
|
|
|
const s = Math.round(this.saturation * 100)
|
|
|
|
const l = Math.round(this.lightness * 100)
|
|
|
|
|
|
|
|
return `hsl(${h}, ${s}%, ${l}%)`
|
|
|
|
}
|
|
|
|
|
|
|
|
getHEX () {
|
|
|
|
return rgb2hex(...this.getRGB())
|
|
|
|
}
|
|
|
|
|
|
|
|
setHEX (hex) {
|
|
|
|
this.setRGB(...hex2rgb(hex))
|
|
|
|
}
|
|
|
|
|
|
|
|
getRGB () {
|
|
|
|
return hsl2rgb(...this.getHSL())
|
|
|
|
}
|
|
|
|
|
|
|
|
setRGB (r, g, b) {
|
|
|
|
this.setHSL(...rgb2hsl(r, g, b))
|
|
|
|
}
|
|
|
|
|
|
|
|
getHSL () {
|
|
|
|
return [this.hue, this.saturation, this.lightness]
|
|
|
|
}
|
|
|
|
|
|
|
|
setHSL (h, s, l) {
|
|
|
|
this.hue = h
|
|
|
|
this.saturation = s
|
|
|
|
this.lightness = l
|
|
|
|
|
|
|
|
this.initColor()
|
|
|
|
}
|
|
|
|
|
|
|
|
initColor () {
|
|
|
|
this.calculatePositions()
|
|
|
|
this.moveWheelPointer()
|
|
|
|
this.drawTriangle()
|
|
|
|
this.moveTrianglePointer()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse event handling
|
|
|
|
attachEvents () {
|
|
|
|
this.down = null
|
|
|
|
|
|
|
|
let mousedown = (evt) => {
|
|
|
|
evt.stopPropagation()
|
|
|
|
evt.preventDefault()
|
|
|
|
|
|
|
|
doc.body.addEventListener('mousemove', mousemove, false)
|
|
|
|
doc.body.addEventListener('mouseup', mouseup, false)
|
|
|
|
|
|
|
|
let xy = this.getRelativeCoordinates(evt)
|
|
|
|
this.map(xy.x, xy.y)
|
|
|
|
}
|
|
|
|
|
|
|
|
let mousemove = (evt) => {
|
|
|
|
let xy = this.getRelativeCoordinates(evt)
|
|
|
|
this.move(xy.x, xy.y)
|
|
|
|
}
|
|
|
|
|
|
|
|
let mouseup = (evt) => {
|
|
|
|
if (this.down) {
|
|
|
|
this.down = null
|
|
|
|
this.emit('dragend')
|
|
|
|
}
|
|
|
|
doc.body.removeEventListener('mousemove', mousemove, false)
|
|
|
|
doc.body.removeEventListener('mouseup', mouseup, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.container.addEventListener('mousedown', mousedown, false)
|
|
|
|
this.container.addEventListener('mousemove', mousemove, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
map (x, y) {
|
|
|
|
let x0 = x
|
|
|
|
let y0 = y
|
|
|
|
x -= this.wheelRadius
|
|
|
|
y -= this.wheelRadius
|
|
|
|
|
|
|
|
const r = M.sqrt(x * x + y * y) // Pythagoras
|
|
|
|
if (r > this.triangleRadius && r < this.wheelRadius) {
|
|
|
|
// Wheel
|
|
|
|
this.down = 'wheel'
|
|
|
|
this.emit('dragstart')
|
|
|
|
this.move(x0, y0)
|
|
|
|
} else if (r < this.triangleRadius) {
|
|
|
|
// Inner circle
|
|
|
|
this.down = 'triangle'
|
|
|
|
this.emit('dragstart')
|
|
|
|
this.move(x0, y0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
move (x, y) {
|
|
|
|
if (!this.down) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
x -= this.wheelRadius
|
|
|
|
y -= this.wheelRadius
|
|
|
|
|
|
|
|
let rad = M.atan2(-y, x)
|
|
|
|
if (rad < 0) {
|
|
|
|
rad += TAU
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.down === 'wheel') {
|
|
|
|
this.hue = rad
|
|
|
|
this.initColor()
|
|
|
|
this.emit('drag')
|
|
|
|
} else if (this.down === 'triangle') {
|
|
|
|
// get radius and max radius
|
|
|
|
let rad0 = (rad + TAU - this.hue) % TAU
|
|
|
|
let rad1 = rad0 % (TAU / 3) - (TAU / 6)
|
|
|
|
let a = 0.5 * this.triangleRadius
|
|
|
|
let b = M.tan(rad1) * a
|
|
|
|
let r = M.sqrt(x * x + y * y) // Pythagoras
|
|
|
|
let maxR = M.sqrt(a * a + b * b) // Pythagoras
|
|
|
|
|
|
|
|
if (r > maxR) {
|
|
|
|
const dx = M.tan(rad1) * r
|
|
|
|
let rad2 = M.atan(dx / maxR)
|
|
|
|
if (rad2 > TAU / 6) {
|
|
|
|
rad2 = TAU / 6
|
|
|
|
} else if (rad2 < -TAU / 6) {
|
|
|
|
rad2 = -TAU / 6
|
|
|
|
}
|
|
|
|
rad += rad2 - rad1
|
|
|
|
|
|
|
|
rad0 = (rad + TAU - this.hue) % TAU
|
|
|
|
rad1 = rad0 % (TAU / 3) - (TAU / 6)
|
|
|
|
b = M.tan(rad1) * a
|
|
|
|
r = maxR = M.sqrt(a * a + b * b) // Pythagoras
|
|
|
|
}
|
|
|
|
|
|
|
|
x = M.round(M.cos(rad) * r)
|
|
|
|
y = M.round(-M.sin(rad) * r)
|
|
|
|
|
|
|
|
const l = this.lightness = ((M.sin(rad0) * r) / this.triangleSideLength) + 0.5
|
|
|
|
|
|
|
|
const widthShare = 1 - (M.abs(l - 0.5) * 2)
|
|
|
|
let s = this.saturation = (((M.cos(rad0) * r) + (this.triangleRadius / 2)) / (1.5 * this.triangleRadius)) / widthShare
|
|
|
|
s = M.max(0, s) // cannot be lower than 0
|
|
|
|
s = M.min(1, s) // cannot be greater than 1
|
|
|
|
|
|
|
|
this.lightness = l
|
|
|
|
this.saturation = s
|
|
|
|
|
|
|
|
this.x = x
|
|
|
|
this.y = y
|
|
|
|
this.moveTrianglePointer()
|
|
|
|
|
|
|
|
this.emit('drag')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************
|
|
|
|
* Init helpers *
|
|
|
|
***************/
|
|
|
|
|
|
|
|
static initInput (input, options) {
|
|
|
|
options = options || {}
|
|
|
|
|
|
|
|
let ct
|
|
|
|
let openColorTriangle = function () {
|
|
|
|
let hex = input.value
|
|
|
|
if (options.parseColor) hex = options.parseColor(hex)
|
|
|
|
if (!ct) {
|
|
|
|
options.size = options.size || input.offsetWidth
|
|
|
|
options.background = win.getComputedStyle(input, null).backgroundColor
|
|
|
|
options.margin = options.margin || 10
|
|
|
|
options.event = options.event || 'dragend'
|
|
|
|
|
|
|
|
ct = new ColorTriangle(hex, options)
|
|
|
|
ct.on(options.event, () => {
|
|
|
|
const hex = ct.getHEX()
|
|
|
|
input.value = options.uppercase ? hex.toUpperCase() : hex
|
|
|
|
fireChangeEvent()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
ct.setHEX(hex)
|
|
|
|
}
|
|
|
|
|
|
|
|
let top = input.offsetTop
|
|
|
|
if (win.innerHeight - input.getBoundingClientRect().top > input.offsetHeight + options.margin + options.size) {
|
|
|
|
top += input.offsetHeight + options.margin // below
|
|
|
|
} else {
|
|
|
|
top -= options.margin + options.size // above
|
|
|
|
}
|
|
|
|
|
|
|
|
let el = ct.getElement()
|
|
|
|
el.style.position = 'absolute'
|
|
|
|
el.style.left = input.offsetLeft + 'px'
|
|
|
|
el.style.top = top + 'px'
|
|
|
|
el.style.zIndex = '1338' // above everything
|
|
|
|
|
|
|
|
ct.inject(input.parentNode)
|
|
|
|
}
|
|
|
|
|
|
|
|
let closeColorTriangle = () => {
|
|
|
|
if (ct) {
|
|
|
|
ct.dispose()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let fireChangeEvent = () => {
|
|
|
|
let evt = doc.createEvent('HTMLEvents')
|
|
|
|
evt.initEvent('input', true, false) // bubbles = true, cancable = false
|
|
|
|
input.dispatchEvent(evt) // fire event
|
|
|
|
}
|
|
|
|
|
|
|
|
input.addEventListener('focus', openColorTriangle, false)
|
|
|
|
input.addEventListener('blur', closeColorTriangle, false)
|
|
|
|
input.addEventListener('keyup', () => {
|
|
|
|
const val = input.value
|
|
|
|
if (val.match(/^#((?:[0-9A-Fa-f]{3})|(?:[0-9A-Fa-f]{6}))$/)) {
|
|
|
|
openColorTriangle()
|
|
|
|
fireChangeEvent()
|
|
|
|
} else {
|
|
|
|
closeColorTriangle()
|
|
|
|
}
|
|
|
|
}, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************
|
|
|
|
* Helper functions *
|
|
|
|
*******************/
|
|
|
|
|
|
|
|
setOptions (opts) {
|
|
|
|
opts = opts || {}
|
|
|
|
let dflt = this.options
|
|
|
|
let options = this.options = {}
|
|
|
|
|
|
|
|
each(dflt, function (val, key) {
|
|
|
|
options[key] = (opts.hasOwnProperty(key))
|
|
|
|
? opts[key]
|
|
|
|
: val
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|