364 lines
8.8 KiB
CoffeeScript
364 lines
8.8 KiB
CoffeeScript
JSColor =
|
|
css: ->
|
|
"""<%= grunt.file.read('src/General/css/jscolor.css').replace(/\s+/g, ' ').trim() %>"""
|
|
|
|
bind: (el) ->
|
|
el.color = new JSColor.color(el) if not el.color
|
|
|
|
fetchElement: (mixed) ->
|
|
if typeof mixed is "string" then $.id mixed else mixed
|
|
|
|
fireEvent: (el, event) ->
|
|
return unless el
|
|
|
|
$.event event, null, el
|
|
|
|
getRelMousePos: (e) ->
|
|
e or= window.event
|
|
x = 0
|
|
y = 0
|
|
if typeof e.offsetX is 'number'
|
|
x = e.offsetX
|
|
y = e.offsetY
|
|
else if typeof e.layerX is 'number'
|
|
x = e.layerX
|
|
y = e.layerY
|
|
x: x
|
|
y: y
|
|
|
|
color: (target) ->
|
|
# Read Only
|
|
@hsv = [0, 0, 1] # 0-6, 0-1, 0-1
|
|
@rgb = [1, 1, 1] # 0-1, 0-1, 0-1
|
|
|
|
# Writable.
|
|
# Value holder / Where to reflect current color
|
|
@valueElement = @styleElement = target
|
|
|
|
# Blur / Drag trackers
|
|
abortBlur = holdPad = holdSld = false
|
|
|
|
@hidePicker = ->
|
|
if isPickerOwner() then removePicker()
|
|
|
|
@showPicker = ->
|
|
unless isPickerOwner() then drawPicker()
|
|
|
|
@importColor = ->
|
|
unless valueElement
|
|
@exportColor()
|
|
else
|
|
unless @fromString valueElement.value, leaveValue
|
|
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor
|
|
@exportColor leaveValue | leaveStyle
|
|
|
|
@exportColor = (flags) ->
|
|
if !(flags & leaveValue) and valueElement
|
|
value = '#' + @toString()
|
|
valueElement.value = value
|
|
valueElement.previousSibling.value = value
|
|
editTheme[valueElement.previousSibling.name] = value
|
|
|
|
setTimeout -> Style.setTheme editTheme
|
|
|
|
if not (flags & leaveStyle) and styleElement
|
|
styleElement.style.backgroundColor = '#' + @toString()
|
|
|
|
if not (flags & leavePad) and isPickerOwner()
|
|
redrawPad()
|
|
|
|
if not (flags & leaveSld) and isPickerOwner()
|
|
redrawSld()
|
|
|
|
@fromHSV = (h, s, v, flags) -> # null = don't change
|
|
@hsv = [
|
|
h =
|
|
if h
|
|
$.minmax h, 0.0, 6.0
|
|
else
|
|
@hsv[0]
|
|
s =
|
|
if s
|
|
$.minmax s, 0.0, 1.0
|
|
else
|
|
@hsv[1]
|
|
v =
|
|
if v
|
|
$.minmax v, 0.0, 1.0
|
|
else
|
|
@hsv[2]
|
|
]
|
|
|
|
@rgb = HSV_RGB(h, s, v)
|
|
|
|
@exportColor flags
|
|
|
|
@fromRGB = (r, g, b, flags) -> # null = don't change
|
|
r =
|
|
if r?
|
|
$.minmax r, 0.0, 1.0
|
|
else
|
|
@rgb[0]
|
|
g =
|
|
if g?
|
|
$.minmax g, 0.0, 1.0
|
|
else
|
|
@rgb[1]
|
|
b =
|
|
if b?
|
|
$.minmax b, 0.0, 1.0
|
|
else
|
|
@rgb[2]
|
|
|
|
hsv = RGB_HSV(r, g, b)
|
|
|
|
if hsv[0]?
|
|
@hsv[0] = $.minmax hsv[0], 0.0, 6.0
|
|
|
|
if hsv[2] isnt 0
|
|
@hsv[1] =
|
|
unless hsv[1]?
|
|
null
|
|
else
|
|
$.minmax hsv[1], 0.0, 1.0
|
|
|
|
@hsv[2] =
|
|
unless hsv[2]?
|
|
null
|
|
else
|
|
$.minmax hsv[2], 0.0, 1.0
|
|
|
|
# update RGB according to final HSV, as some values might be trimmed
|
|
@rgb = HSV_RGB @hsv[0], @hsv[1], @hsv[2]
|
|
|
|
@exportColor flags
|
|
|
|
@fromString = (number, flags) ->
|
|
m = number.match /^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i
|
|
unless m
|
|
return false
|
|
else
|
|
if m[1].length is 6 # 6-char notation
|
|
@fromRGB(
|
|
parseInt(m[1].substr(0, 2), 16) / 255
|
|
parseInt(m[1].substr(2, 2), 16) / 255
|
|
parseInt(m[1].substr(4, 2), 16) / 255
|
|
flags
|
|
)
|
|
else # 3-char notation
|
|
@fromRGB(
|
|
# Double-up each character to fake 6-char notation.
|
|
parseInt((val = m[1].charAt 0) + val, 16) / 255
|
|
parseInt((val = m[1].charAt 1) + val, 16) / 255
|
|
parseInt((val = m[1].charAt 2) + val, 16) / 255
|
|
flags
|
|
)
|
|
true
|
|
|
|
@toString = ->
|
|
(0x100 | Math.round(255 * @rgb[0])).toString(16).substr(1) +
|
|
(0x100 | Math.round(255 * @rgb[1])).toString(16).substr(1) +
|
|
(0x100 | Math.round(255 * @rgb[2])).toString(16).substr(1)
|
|
|
|
RGB_HSV = (r, g, b) ->
|
|
n = if (n = if r < g then r else g) < b then n else b
|
|
v = if (v = if r > g then r else g) > b then v else b
|
|
m = v - n
|
|
|
|
return [ null, 0, v ] if m is 0
|
|
|
|
h =
|
|
if r is n
|
|
3 + (b - g) / m
|
|
else
|
|
if g is n
|
|
5 + (r - b) / m
|
|
else
|
|
1 + (g - r) / m
|
|
[
|
|
if h is 6 then 0 else h
|
|
m / v
|
|
v
|
|
]
|
|
|
|
HSV_RGB = (h, s, v) ->
|
|
|
|
return [ v, v, v ] unless h?
|
|
|
|
i = Math.floor(h)
|
|
f =
|
|
if i % 2
|
|
h - i
|
|
else
|
|
1 - (h - i)
|
|
m = v * (1 - s)
|
|
n = v * (1 - s * f)
|
|
|
|
switch i
|
|
when 6, 0
|
|
[v,n,m]
|
|
when 1
|
|
[n,v,m]
|
|
when 2
|
|
[m,v,n]
|
|
when 3
|
|
[m,n,v]
|
|
when 4
|
|
[n,m,v]
|
|
when 5
|
|
[v,m,n]
|
|
|
|
removePicker = ->
|
|
delete JSColor.picker.owner
|
|
$.rm JSColor.picker.boxB
|
|
|
|
drawPicker = (x, y) ->
|
|
unless p = JSColor.picker
|
|
elements = ['box', 'boxB', 'pad', 'padB', 'padM', 'sld', 'sldB', 'sldM', 'btn']
|
|
p = {}
|
|
for item in elements
|
|
p[item] = $.el 'div', {className: "jsc#{item.charAt(0).toUpperCase() + item.slice 1}"}
|
|
|
|
p.btnS = $.el 'span', {className: 'jscBtnS'}
|
|
p.btnT = $.tn 'Close'
|
|
|
|
JSColor.picker = p
|
|
|
|
$.add p.box, [p.sldB, p.sldM, p.padB, p.padM, p.btn]
|
|
$.add p.sldB, p.sld
|
|
$.add p.padB, p.pad
|
|
$.add p.btnS, p.btnT
|
|
$.add p.btn, p.btnS
|
|
$.add p.boxB, p.box
|
|
|
|
# controls interaction
|
|
{box, boxB, btn, btnS, pad, padB, padM, sld, sldB, sldM} = p
|
|
box.onmouseup =
|
|
box.onmouseout = -> target.focus()
|
|
box.onmousedown = -> abortBlur=true
|
|
box.onmousemove = (e) ->
|
|
if holdPad or holdSld
|
|
holdPad and setPad e
|
|
holdSld and setSld e
|
|
|
|
if d.selection
|
|
d.selection.empty()
|
|
else if window.getSelection
|
|
window.getSelection().removeAllRanges()
|
|
|
|
padM.onmouseup =
|
|
padM.onmouseout = -> if holdPad
|
|
holdPad = false
|
|
JSColor.fireEvent valueElement, 'change'
|
|
padM.onmousedown = (e) ->
|
|
# If the slider is at the bottom, move it up
|
|
|
|
if THIS.hsv[2] is 0
|
|
THIS.fromHSV null, null, 1.0
|
|
|
|
holdPad = true
|
|
setPad e
|
|
|
|
sldM.onmouseup =
|
|
sldM.onmouseout = -> if holdSld
|
|
holdSld = false
|
|
JSColor.fireEvent valueElement, 'change'
|
|
sldM.onmousedown = (e) ->
|
|
holdSld = true
|
|
setSld e
|
|
|
|
btn.onmousedown = ->
|
|
THIS.hidePicker()
|
|
|
|
# place pointers
|
|
redrawPad()
|
|
redrawSld()
|
|
|
|
JSColor.picker.owner = THIS
|
|
$.add ThemeTools.dialog, p.boxB
|
|
|
|
# redraw the pad pointer
|
|
redrawPad = ->
|
|
# The X and Y positions of the picker crosshair, based on the hsv Hue and Saturation values as percentages and the picker's dimensions.
|
|
JSColor.picker.padM.style.backgroundPosition =
|
|
"#{4 + Math.round (THIS.hsv[0] / 6) * 180}px #{4 + Math.round (1 - THIS.hsv[1]) * 100}px"
|
|
|
|
rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1)
|
|
JSColor.picker.sld.style.backgroundColor = "rgb(#{rgb[0] * 100}%, #{rgb[1] * 100}%, #{rgb[2] * 100}%)"
|
|
|
|
return
|
|
|
|
redrawSld = ->
|
|
# redraw the slider pointer. X will always be 0, Y will always be a percentage of the HSV 'Value' value.
|
|
JSColor.picker.sldM.style.backgroundPosition =
|
|
"0 #{6 + Math.round (1 - THIS.hsv[2]) * 100}px"
|
|
|
|
|
|
isPickerOwner = ->
|
|
return JSColor.picker and JSColor.picker.owner is THIS
|
|
|
|
blurTarget = ->
|
|
if valueElement is target
|
|
THIS.importColor()
|
|
|
|
blurValue = ->
|
|
if valueElement isnt target
|
|
THIS.importColor()
|
|
|
|
setPad = (e) ->
|
|
mpos = JSColor.getRelMousePos e
|
|
x = mpos.x - 11
|
|
y = mpos.y - 11
|
|
THIS.fromHSV(
|
|
x * (1 / 30)
|
|
1 - y / 100
|
|
null
|
|
leaveSld
|
|
)
|
|
|
|
setSld = (e) ->
|
|
mpos = JSColor.getRelMousePos e
|
|
y = mpos.y - 9
|
|
THIS.fromHSV(
|
|
null
|
|
null
|
|
1 - y / 100
|
|
leavePad
|
|
)
|
|
|
|
THIS = @
|
|
valueElement = JSColor.fetchElement @valueElement
|
|
styleElement = JSColor.fetchElement @styleElement
|
|
leaveValue = 1 << 0
|
|
leaveStyle = 1 << 1
|
|
leavePad = 1 << 2
|
|
leaveSld = 1 << 3
|
|
|
|
# target
|
|
$.on target, 'focus', ->
|
|
THIS.showPicker()
|
|
|
|
$.on target, 'blur', ->
|
|
unless abortBlur
|
|
window.setTimeout(->
|
|
abortBlur or blurTarget()
|
|
abortBlur = false
|
|
)
|
|
else
|
|
abortBlur = false
|
|
|
|
# valueElement
|
|
if valueElement
|
|
$.on valueElement, 'keyup input', ->
|
|
THIS.fromString valueElement.value, leaveValue
|
|
|
|
$.on valueElement, 'blur', blurValue
|
|
|
|
valueElement.setAttribute 'autocomplete', 'off'
|
|
|
|
# styleElement
|
|
if styleElement
|
|
styleElement.jscStyle =
|
|
backgroundColor: styleElement.style.backgroundColor
|
|
|
|
@importColor() |