/*
* This file is part of Toolkit.
*
* Toolkit is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Toolkit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
"use strict";
(function (w, TK) {
var color_options = [ "rgb", "hsl", "hex", "hue", "saturation", "lightness", "red", "green", "blue" ];
var checkinput = function (e) {
var I = this.hex._input;
if (e.keyCode && e.keyCode == 13) {
apply.call(this);
return;
}
if (e.keyCode && e.keyCode == 27) {
cancel.call(this);
return;
}
if (I.value.substr(0, 1) == "#")
I.value = I.value.substr(1);
if (e.type == "paste" && I.value.length == 3) {
I.value = I.value[0] + I.value[0] +
I.value[1] + I.value[1] +
I.value[2] + I.value[2];
}
if (I.value.length == 6) {
this.set("hex", I.value);
}
}
var cancel = function () {
/**
* Is fired whenever the cancel button gets clicked or ESC is hit on input.
*
* @event TK.ColorPicker#cancel
*/
fevent.call(this, "cancel");
}
var apply = function () {
/**
* Is fired whenever the apply button gets clicked or return is hit on input.
*
* @event TK.ColorPicker#apply
* @param {object} colors - Object containing all color objects: `rgb`, `hsl`, `hex`, `hue`, `saturation`, `lightness`, `red`, `green`, `blue`
*/
fevent.call(this, "apply", true);
}
var fevent = function (e, useraction) {
var O = this.options;
if (useraction) {
this.fire_event("userset", "rgb", O.rgb);
this.fire_event("userset", "hsl", O.hsl);
this.fire_event("userset", "hex", O.hex);
this.fire_event("userset", "hue", O.hue);
this.fire_event("userset", "saturation", O.saturation);
this.fire_event("userset", "lightness", O.lightness);
this.fire_event("userset", "red", O.red);
this.fire_event("userset", "green", O.green);
this.fire_event("userset", "blue", O.blue);
}
this.fire_event(e, {
rgb: O.rgb,
hsl: O.hsl,
hex: O.hex,
hue: O.hue,
saturation: O.saturation,
lightness: O.lightness,
red: O.red,
green: O.green,
blue: O.blue,
});
}
var color_atoms = { "hue":"hsl", "saturation":"hsl", "lightness":"hsl", "red":"rgb", "green":"rgb", "blue":"rgb" };
function set_atoms (key, value) {
var O = this.options;
var atoms = Object.keys(color_atoms);
for ( var i = 0; i < atoms.length; i++) {
var atom = atoms[i];
if (key !== atom) {
O[atom] = O[color_atoms[atom]][atom.substr(0,1)]
this[atom].set("value", O[atom]);
}
}
if (key !== "hex")
O.hex = this.rgb2hex(O.rgb);
}
/**
* TK.ColorPicker provides a collection of widgets to select a color in
* RGB or HSL color space.
*
* @class TK.ColorPicker
*
* @extends TK.Container
*
* @implements TK.Colors
*
* @param {Object} [options={ }] - An object containing initial options.
*
* @property {object} [hsl={h:0, s:0.5, l:0}] - An object containing members `h`ue, `s`aturation and `l`ightness as numerical values.
* @property {object} [rgb={r:0, r:0, b:0}] - An object containing members `r`ed, `g`reen and `b`lue as numerical values.
* @property {string} [hex=000000] - A HEX color value, either with or without leading `#`.
* @property {number} [hue=0] - A numerical value 0..1 for the hue.
* @property {number} [saturation=0] - A numerical value 0..1 for the saturation.
* @property {number} [lightness=0] - A numerical value 0..1 for the lightness.
* @property {number} [red=0] - A numerical value 0..255 for the amount of red.
* @property {number} [green=0] - A numerical value 0..255 for the amount of green.
* @property {number} [blue=0] - A numerical value 0..255 for the amount of blue.
* @property {boolean} [show_hue=true] - Set to `false` to hide the {@link TK.ValueKnob} for hue.
* @property {boolean} [show_saturation=true] - Set to `false` to hide the {@link TK.ValueKnob} for saturation.
* @property {boolean} [show_lightness=true] - Set to `false` to hide the {@link TK.ValueKnob} for lightness.
* @property {boolean} [show_red=true] - Set to `false` to hide the {@link TK.ValueKnob} for red.
* @property {boolean} [show_green=true] - Set to `false` to hide the {@link TK.ValueKnob} for green.
* @property {boolean} [show_blue=true] - Set to `false` to hide the {@link TK.ValueKnob} for blue.
* @property {boolean} [show_hex=true] - Set to `false` to hide the {@link TK.Value} for the HEX color.
* @property {boolean} [show_apply=true] - Set to `false` to hide the {@link TK.Button} to apply.
* @property {boolean} [show_cancel=true] - Set to `false` to hide the {@link TK.Button} to cancel.
* @property {boolean} [show_canvas=true] - Set to `false` to hide the color canvas.
* @property {boolean} [show_grayscale=true] - Set to `false` to hide the grayscale.
* @property {boolean} [show_indicator=true] - Set to `false` to hide the color indicator.
*/
TK.ColorPicker = TK.class({
_class: "ColorPicker",
Extends: TK.Container,
Implements: [TK.Colors],
_options: Object.assign(Object.create(TK.Container.prototype._options), {
hsl: "object",
rgb: "object",
hex: "string",
hue: "number",
saturation: "number",
lightness: "number",
red: "number",
green: "number",
blue: "number",
}),
options: {
hsl: {h:0, s:0.5, l:0},
rgb: {r:0, g:0, b:0},
hex: "000000",
hue: 0,
saturation: 0.5,
lightness: 0,
red: 0,
green: 0,
blue: 0,
},
initialize: function (options) {
TK.Container.prototype.initialize.call(this, options);
var E = this.element;
/** @member {HTMLDivElement} TK.ColorPicker#element - The main DIV container.
* Has class <code>toolkit-color-picker</code>.
*/
TK.add_class(E, "toolkit-color-picker");
/**
* @member {TK.Range} TK.ColorPicker#range_x - The {@link TK.Range} for the x axis.
*/
this.range_x = new TK.Range({
min: 0,
max: 1,
});
/**
* @member {TK.Range} TK.ColorPicker#range_y - The {@link TK.Range} for the y axis.
*/
this.range_y = new TK.Range({
min: 0,
max: 1,
reverse: true,
});
/**
* @member {TK.Range} TK.ColorPicker#drag_x - The {@link TK.DragValue} for the x axis.
*/
this.drag_x = new TK.DragValue(this, {
range: (function () { return this.range_x; }).bind(this),
get: function () { return this.parent.options.hue; },
set: function (v) { this.parent.userset("hue", this.parent.range_x.snap(v)); },
direction: "horizontal",
onstartcapture: function (e) {
if (e.start.target.classList.contains("toolkit-indicator")) return;
var ev = e.stouch ? e.stouch : e.start;
var x = ev.clientX - this.parent._canvas.getBoundingClientRect().left;
this.parent.set("hue", this.options.range().px2val(x));
}
});
/**
* @member {TK.Range} TK.ColorPicker#drag_y - The {@link TK.DragValue} for the y axis.
*/
this.drag_y = new TK.DragValue(this, {
range: (function () { return this.range_y; }).bind(this),
get: function () { return this.parent.options.lightness; },
set: function (v) { this.parent.userset("lightness", this.parent.range_y.snap(v)); },
direction: "vertical",
onstartcapture: function (e) {
if (e.start.target.classList.contains("toolkit-indicator")) return;
var ev = e.stouch ? e.stouch : e.start;
var y = ev.clientY - this.parent._canvas.getBoundingClientRect().top;
this.parent.set("lightness", 1 - this.options.range().px2val(y));
}
});
if (options.rgb)
this.set("rgb", options.rgb);
if (options.hex)
this.set("hex", options.hex);
if (options.hsl)
this.set("hsl", options.hsl);
},
resize: function () {
var rect = this._canvas.getBoundingClientRect();
this.range_x.set("basis", rect.width);
this.range_y.set("basis", rect.height);
},
redraw: function () {
TK.Container.prototype.redraw.call(this);
var I = this.invalid;
var O = this.options;
var E = this.element;
if (I.validate("rgb", "hsl", "hex", "hue", "saturation", "lightness", "red", "green", "blue")) {
var bw = this.rgb2bw(O.rgb);
var bg = "rgb("+parseInt(O.red)+","+parseInt(O.green)+","+parseInt(O.blue)+")";
this.hex._input.style.backgroundColor = bg;
this.hex._input.style.color = bw;
this.hex.set("value", O.hex);
this._indicator.style.left = (O.hue * 100) + "%";
this._indicator.style.top = (O.lightness * 100) + "%";
this._indicator.style.backgroundColor = bg;
this._indicator.style.color = bw;
this._grayscale.style.opacity = 1 - O.saturation;
}
},
set: function (key, value) {
var O = this.options;
if (color_options.indexOf(key) > -1) {
switch (key) {
case "rgb":
O.hsl = this.rgb2hsl(value);
break;
case "hsl":
O.rgb = this.hsl2rgb(value);
break;
case "hex":
O.rgb = this.hex2rgb(value);
O.hsl = this.rgb2hsl(O.rgb);
break;
case "hue":
O.hsl = {h:Math.min(1,Math.max(0,value)), s:O.saturation, l:O.lightness};
O.rgb = this.hsl2rgb(O.hsl);
break;
case "saturation":
O.hsl = {h:O.hue, s:Math.min(1,Math.max(0,value)), l:O.lightness};
O.rgb = this.hsl2rgb(O.hsl);
break;
case "lightness":
O.hsl = {h:O.hue, s:O.saturation, l:Math.min(1,Math.max(0,value))};
O.rgb = this.hsl2rgb(O.hsl);
break;
case "red":
O.rgb = {r:Math.min(255,Math.max(0,value)), g:O.green, b:O.blue};
O.hsl = this.rgb2hsl(O.rgb);
break;
case "green":
O.rgb = {r:O.red, g:Math.min(255,Math.max(0,value)), b:O.blue};
O.hsl = this.rgb2hsl(O.rgb);
break;
case "blue":
O.rgb = {r:O.red, g:O.green, b:Math.min(255,Math.max(0,value))};
O.hsl = this.rgb2hsl(O.rgb);
break;
}
set_atoms.call(this, key, value);
}
return TK.Container.prototype.set.call(this, key, value);
}
});
/**
* @member {HTMLDivElement} TK.ColorPicker#canvas - The color background.
* Has class `toolkit-canvas`,
*/
TK.ChildElement(TK.ColorPicker, "canvas", {
show: true,
append: function () {
this.element.appendChild(this._canvas);
this.drag_x.set("node", this._canvas);
this.drag_y.set("node", this._canvas);
},
});
/**
* @member {HTMLDivElement} TK.ColorPicker#grayscale - The grayscale background.
* Has class `toolkit-grayscale`,
*/
TK.ChildElement(TK.ColorPicker, "grayscale", {
show: true,
append: function () {
this._canvas.appendChild(this._grayscale);
},
});
/**
* @member {HTMLDivElement} TK.ColorPicker#indicator - The indicator element.
* Has class `toolkit-indicator`,
*/
TK.ChildElement(TK.ColorPicker, "indicator", {
show: true,
append: function () {
this._canvas.appendChild(this._indicator);
},
});
/**
* @member {TK.Value} TK.ColorPicker#hex - The {@link TK.Value} for the HEX color.
* Has class `toolkit-hex`,
*/
TK.ChildWidget(TK.ColorPicker, "hex", {
create: TK.Value,
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("hex", val);
},
"keyup": function (e) { checkinput.call(this.parent, e); },
"paste": function (e) { checkinput.call(this.parent, e); },
},
default_options: {
format: TK.FORMAT("%s"),
"class": "toolkit-hex",
set: function (v) {
var p=0, tmp;
if (v[0] == "#")
v = v.substring(1);
while (v.length < 6) {
tmp = v.slice(0, p+1);
tmp += v[p];
tmp += v.slice(p+1);
v = tmp;
p+=2;
}
return v;
},
size: 7,
maxlength: 7,
},
map_options: {
"hex" : "value"
},
inherit_options: true,
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#hue - The {@link TK.ValueKnob} for the hue.
* Has class `toolkit-hue`,
*/
TK.ChildWidget(TK.ColorPicker, "hue", {
create: TK.ValueKnob,
option: "show_hsl",
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("hue", val);
},
},
default_options: {
title: "Hue",
min: 0,
max: 1,
"class": "toolkit-hue",
},
map_options: {
"hue" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#saturation - The {@link TK.ValueKnob} for the saturation.
* Has class `toolkit-saturation`,
*/
TK.ChildWidget(TK.ColorPicker, "saturation", {
create: TK.ValueKnob,
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("saturation", val);
},
},
default_options: {
title: "Saturation",
min: 0,
max: 1,
"class": "toolkit-saturation",
},
map_options: {
"saturation" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#lightness - The {@link TK.ValueKnob} for the lightness.
* Has class `toolkit-lightness`,
*/
TK.ChildWidget(TK.ColorPicker, "lightness", {
create: TK.ValueKnob,
option: "show_hsl",
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("lightness", val);
},
},
default_options: {
title: "Lightness",
min: 0,
max: 1,
"class": "toolkit-lightness",
},
map_options: {
"lightness" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#red - The {@link TK.ValueKnob} for the red color.
* Has class `toolkit-red`,
*/
TK.ChildWidget(TK.ColorPicker, "red", {
create: TK.ValueKnob,
option: "show_rgb",
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("red", val);
},
},
default_options: {
title: "Red",
min: 0,
max: 255,
snap: 1,
value_format: function (v) { return parseInt(v); },
set: function (v) { return Math.round(v); },
"class": "toolkit-red",
},
map_options: {
"red" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#green - The {@link TK.ValueKnob} for the green color.
* Has class `toolkit-green`,
*/
TK.ChildWidget(TK.ColorPicker, "green", {
create: TK.ValueKnob,
option: "show_rgb",
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("green", val);
},
},
default_options: {
title: "Green",
min: 0,
max: 255,
snap: 1,
value_format: function (v) { return parseInt(v); },
set: function (v) { return Math.round(v); },
"class": "toolkit-green",
},
map_options: {
"green" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.ValueKnob} TK.ColorPicker#blue - The {@link TK.ValueKnob} for the blue color.
* Has class `toolkit-blue`,
*/
TK.ChildWidget(TK.ColorPicker, "blue", {
create: TK.ValueKnob,
option: "show_rgb",
show: true,
static_events: {
"userset": function (key, val) {
if (key == "value") this.parent.userset("blue", val);
},
},
default_options: {
title: "Blue",
min: 0,
max: 255,
snap: 1,
value_format: function (v) { return parseInt(v); },
set: function (v) { return Math.round(v); },
"class": "toolkit-blue",
},
map_options: {
"blue" : "value"
},
inherit_options: true,
blacklist_options: ["x", "y", "value"],
});
/**
* @member {TK.Button} TK.ColorPicker#apply - The {@link TK.Button} to apply.
* Has class `toolkit-apply`,
*/
TK.ChildWidget(TK.ColorPicker, "apply", {
create: TK.Button,
show: true,
static_events: {
"click": function () { apply.call(this.parent); },
},
default_options: {
"label" : "Apply",
"class": "toolkit-apply",
},
});
/**
* @member {TK.Button} TK.ColorPicker#cancel - The {@link TK.Button} to cancel.
* Has class `toolkit-cancel`,
*/
TK.ChildWidget(TK.ColorPicker, "cancel", {
create: TK.Button,
show: true,
static_events: {
"click": function () { cancel.call(this.parent); },
},
default_options: {
"label" : "Cancel",
"class" : "toolkit-cancel",
},
});
})(this, this.TK);