Color picker tool

ColorPicker tool

HTML

    <div id="container">
        <div id="palette" class="block">
            <div id="color-palette"></div>
            <div id="color-info">
                <div class="title"> CSS Color </div>
            </div>
        </div>

        <div id="picker" class="block">
            <div class="ui-color-picker" data-topic="picker" data-mode="HSL"></div>
            <div id="picker-samples" sample-id="master"></div>
            <div id="controls">
                <div id="delete">
                    <div id="trash-can"></div>
                </div>
                <div id="void-sample" class="icon"></div>
            </div>
        </div>

        <div id="canvas" data-tutorial="drop">
            <div id="zindex" class="ui-input-slider" data-topic="z-index" data-info="z-index"
                data-max="20" data-sensivity="10"></div>
        </div>
    </div>

CSS

/*
 * COLOR PICKER TOOL
 */

.ui-color-picker {
	width: 420px;
	margin: 0;
	border: 1px solid #DDD;
	background-color: #FFF;
	display: table;

	-moz-user-select: none;
	-webkit-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

.ui-color-picker .picking-area {
	width: 198px;
	height: 198px;
	margin: 5px;
	border: 1px solid #DDD;
	position: relative;
	float: left;
	display: table;
}

.ui-color-picker .picking-area:hover {
	cursor: default;
}

/* HSV format - Hue-Saturation-Value(Brightness) */
.ui-color-picker .picking-area {
	background: url('https://mdn.mozillademos.org/files/5707/picker_mask_200.png') center center;

	background: -moz-linear-gradient(bottom, #000 0%, rgba(0, 0, 0, 0) 100%),
				-moz-linear-gradient(left, #FFF 0%, rgba(255, 255, 255, 0) 100%);
	background: -webkit-linear-gradient(bottom, #000 0%, rgba(0, 0, 0, 0) 100%),
				-webkit-linear-gradient(left, #FFF 0%, rgba(255, 255, 255, 0) 100%);
	background: -ms-linear-gradient(bottom, #000 0%, rgba(0, 0, 0, 0) 100%),
				-ms-linear-gradient(left, #FFF 0%, rgba(255, 255, 255, 0) 100%);
	background: -o-linear-gradient(bottom, #000 0%, rgba(0, 0, 0, 0) 100%),
				-o-linear-gradient(left, #FFF 0%, rgba(255, 255, 255, 0) 100%);

	background-color: #F00;
}

/* HSL format - Hue-Saturation-Lightness */
.ui-color-picker[data-mode='HSL'] .picking-area {
	background: -moz-linear-gradient(top, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%,
									hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%),
				-moz-linear-gradient(left, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%);
	background: -webkit-linear-gradient(top, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%,
									hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%),
				-webkit-linear-gradient(left, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%);
	background: -ms-linear-gradient(top, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%,
									hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%),
				-ms-linear-gradient(left, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%);
	background: -o-linear-gradient(top, hsl(0, 0%, 100%) 0%, hsla(0, 0%, 100%, 0) 50%,
									hsla(0, 0%, 0%, 0) 50%, hsl(0, 0%, 0%) 100%),
				-o-linear-gradient(left, hsl(0, 0%, 50%) 0%, hsla(0, 0%, 50%, 0) 100%);
	background-color: #F00;
}

.ui-color-picker .picker {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	border: 1px solid #FFF;
	position: absolute;
	top: 45%;
	left: 45%;
}

.ui-color-picker .picker:before {
	width: 8px;
	height: 8px;
	content: "";
	position: absolute;
	border: 1px solid #999;
	border-radius: 50%;
}

.ui-color-picker .hue,
.ui-color-picker .alpha {
	width: 198px;
	height: 28px;
	margin: 5px;
	border: 1px solid #FFF;
	float: left;
}

.ui-color-picker .hue {
	background: url("https://mdn.mozillademos.org/files/5701/hue.png") center;
	background: -moz-linear-gradient(left, #F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%,
				#00F 66.66%, #F0F 83.33%, #F00 100%);
	background: -webkit-linear-gradient(left, #F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%,
				#00F 66.66%, #F0F 83.33%, #F00 100%);
	background: -ms-linear-gradient(left, #F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%,
				#00F 66.66%, #F0F 83.33%, #F00 100%);
	background: -o-linear-gradient(left, #F00 0%, #FF0 16.66%, #0F0 33.33%, #0FF 50%,
				#00F 66.66%, #F0F 83.33%, #F00 100%);
}

.ui-color-picker .alpha {
	border: 1px solid #CCC;
	background: url("https://mdn.mozillademos.org/files/5705/alpha.png");
}

.ui-color-picker .alpha-mask {
	width: 100%;
	height: 100%;
	background: url("https://mdn.mozillademos.org/files/6089/alpha_mask.png");
}

.ui-color-picker .slider-picker {
	width: 2px;
	height: 100%;
	border: 1px solid #777;
	background-color: #FFF;
	position: relative;
	top: -1px;
}

/* input HSV and RGB */

.ui-color-picker .info {
	width: 200px;
	margin: 5px;
	float: left;
}

.ui-color-picker .info * {
	float: left;
}

.ui-color-picker .input {
	width: 64px;
	margin: 5px 2px;
	float: left;
}

.ui-color-picker .input .name {
	height: 20px;
	width: 30px;
	text-align: center;
	font-size: 14px;
	line-height: 18px;
	float: left;
}

.ui-color-picker .input input {
	width: 30px;
	height: 18px;
	margin: 0;
	padding: 0;
	border: 1px solid #DDD;
	text-align: center;
	float: right;

	-moz-user-select: text;
	-webkit-user-select: text;
	-ms-user-select: text;
}

.ui-color-picker .input[data-topic="lightness"] {
	display: none;
}

.ui-color-picker[data-mode='HSL'] .input[data-topic="value"] {
	display: none;
}

.ui-color-picker[data-mode='HSL'] .input[data-topic="lightness"] {
	display: block;
}

.ui-color-picker .input[data-topic="alpha"] {
	margin-top: 10px;
	width: 93px;
}

.ui-color-picker .input[data-topic="alpha"] > .name {
	width: 60px;
}

.ui-color-picker .input[data-topic="alpha"] > input {
	float: right;
}

.ui-color-picker .input[data-topic="hexa"] {
	width: auto;
	float: right;
	margin: 6px 8px 0 0;
}

.ui-color-picker .input[data-topic="hexa"] > .name {
	display: none;
}

.ui-color-picker .input[data-topic="hexa"] > input {
	width: 90px;
	height: 24px;
	padding: 2px 0;
	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
}

/* Preview color */
.ui-color-picker .preview {
	width: 95px;
	height: 53px;
	margin: 5px;
	margin-top: 10px;
	border: 1px solid #DDD;
	background-image: url("https://mdn.mozillademos.org/files/5705/alpha.png");
	float: left;
	position: relative;
}

.ui-color-picker .preview:before {
	height: 100%;
	width: 50%;
	left: 50%;
	top: 0;
	content: "";
	background: #FFF;
	position: absolute;
	z-index: 1;
}

.ui-color-picker .preview-color {
	width: 100%;
	height: 100%;
	background-color: rgba(255, 0, 0, 0.5);
	position: absolute;
	z-index: 1;
}

.ui-color-picker .switch_mode {
	width: 10px;
	height: 20px;
	position: relative;
	border-radius: 5px 0 0 5px;
	border: 1px solid #DDD;
	background-color: #EEE;
	left: -12px;
	top: -1px;
	z-index: 1;
	transition: all 0.5s;
}

.ui-color-picker .switch_mode:hover {
	background-color: #CCC;
	cursor: pointer;
}

/*
 * UI Component
 */

.ui-input-slider {
	height: 20px;
	font-family: "Segoe UI", Arial, Helvetica, sans-serif;
	-moz-user-select: none;
	user-select: none;
}

.ui-input-slider * {
	float: left;
	height: 100%;
	line-height: 100%;
}

/* Input Slider */

.ui-input-slider > input {
	margin: 0;
	padding: 0;
	width: 50px;
	text-align: center;

	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
}

.ui-input-slider-info {
	width: 90px;
	padding: 0px 10px 0px 0px;
	text-align: right;
	text-transform: lowercase;
}

.ui-input-slider-left, .ui-input-slider-right {
	width: 16px;
	cursor: pointer;
	background: url("https://mdn.mozillademos.org/files/5679/arrows.png") center left no-repeat;
}

.ui-input-slider-right {
	background: url("https://mdn.mozillademos.org/files/5679/arrows.png") center right no-repeat;
}

.ui-input-slider-name {
	width: 90px;
	padding: 0 10px 0 0;
	text-align: right;
	text-transform: lowercase;
}

.ui-input-slider-btn-set {
	width: 25px;
	background-color: #2C9FC9;
	border-radius: 5px;
	color: #FFF;
	font-weight: bold;
	line-height: 14px;
	text-align: center;
}

.ui-input-slider-btn-set:hover {
	background-color: #379B4A;
	cursor: pointer;
}

/*
 * COLOR PICKER TOOL
 */

body {
	max-width: 1000px;
	margin: 0 auto;

	font-family: "Segoe UI", Arial, Helvetica, sans-serif;

	box-shadow: 0 0 5px 0 #999;

	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;

	-moz-user-select: none;
	-webkit-user-select: none;
	-ms-user-select: none;
	user-select: none;

}

/**
 * Resize Handle
 */
.resize-handle {
	width: 10px;
	height: 10px;
	background: url('https://mdn.mozillademos.org/files/6083/resize.png') center center no-repeat;
	position: absolute;
	bottom: 0;
	right: 0;
}

[data-resize='both']:hover {
	cursor: nw-resize !important;
}

[data-resize='width']:hover {
	cursor: w-resize !important;
}

[data-resize='height']:hover {
	cursor: n-resize !important;
}

[data-hidden='true'] {
	display: none;
}

[data-collapsed='true'] {
	height: 0 !important;
}

.block {
	display: table;
}


/**
 * 	Container
 */
#container {
	width: 100%;

	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;

	display: table;
}

/**
 * 	Picker Zone
 */

#picker {
	padding: 10px;
	width: 980px;
}

.ui-color-picker {
	padding: 3px 5px;
	float: left;
	border-color: #FFF;
}

.ui-color-picker .switch_mode {
	display: none;
}

.ui-color-picker .preview-color:hover {
	cursor: move;
}

/**
 * Picker Container
 */

#picker-samples {
	width: 375px;
	height: 114px;
	max-height: 218px;
	margin: 0 10px 0 30px;
	overflow: hidden;
	position: relative;
	float: left;

	transition: all 0.2s;
}

#picker-samples .sample {
	width: 40px;
	height: 40px;
	margin: 5px;
	border: 1px solid #DDD;
	position: absolute;
	float: left;
	transition: all 0.2s;
}

#picker-samples .sample:hover {
	cursor: pointer;
	border-color: #BBB;
	transform: scale(1.15);
	border-radius: 3px;
}

#picker-samples .sample[data-active='true'] {
	border-color: #999;
}

#picker-samples .sample[data-active='true']:after {
	content: "";
	position: absolute;
	background: url('https://mdn.mozillademos.org/files/6065/arrow.png') center no-repeat;
	width: 100%;
	height: 12px;
	top: -12px;
	z-index: 2;
}

#picker-samples #add-icon {
	width: 100%;
	height: 100%;
	position: relative;
	box-shadow: inset 0px 0px 2px 0px #DDD;
}

#picker-samples #add-icon:hover {
	cursor: pointer;
	border-color: #DDD;
	box-shadow: inset 0px 0px 5px 0px #CCC;
}

#picker-samples #add-icon:before,
#picker-samples #add-icon:after {
	content: "";
	position: absolute;
	background-color: #EEE;
	box-shadow: 0 0 1px 0 #EEE;
}

#picker-samples #add-icon:before {
	width: 70%;
	height: 16%;
	top: 42%;
	left: 15%;
}

#picker-samples #add-icon:after {
	width: 16%;
	height: 70%;
	top: 15%;
	left: 42%;
}

#picker-samples #add-icon:hover:before,
#picker-samples #add-icon:hover:after {
	background-color: #DDD;
	box-shadow: 0 0 1px 0 #DDD;
}

/**
 * 	Controls
 */

#controls {
	width: 110px;
	padding: 10px;
	float: right;
}

#controls #picker-switch {
	text-align: center;
	float: left;
}

#controls .icon {
	width: 48px;
	height: 48px;
	margin: 10px 0;
	background-repeat: no-repeat;
	background-position: center;
	border: 1px solid #DDD;
	display: table;
	float: left;
}

#controls .icon:hover {
	cursor: pointer;
}

#controls .picker-icon {
	background-image: url('https://mdn.mozillademos.org/files/6081/picker.png');
}

#controls #void-sample {
	margin-right: 10px;
	background-image: url('https://mdn.mozillademos.org/files/6087/void.png');
	background-position: center left;
}

#controls #void-sample[data-active='true'] {
	border-color: #CCC;
	background-position: center right;
}

#controls .switch {
	width: 106px;
	padding: 1px;
	border: 1px solid #CCC;
	font-size: 14px;
	text-align: center;
	line-height: 24px;
	overflow: hidden;
	float: left;
}

#controls .switch:hover {
	cursor: pointer;
}

#controls .switch > * {
	width: 50%;
	padding: 2px 0;
	background-color: #EEE;
	float: left;
}

#controls .switch [data-active='true'] {
	color: #FFF;
	background-image: url('https://mdn.mozillademos.org/files/6025/grain.png');
	background-color: #777;
}

/**
 * 	Trash Can
 */

#delete {
	width: 100%;
	height: 94px;
	background-color: #DDD;
	background-image: url('https://mdn.mozillademos.org/files/6025/grain.png');
	background-repeat: repeat;

	text-align: center;
	color: #777;

	position: relative;
	float: right;
}

#delete #trash-can {
	width: 80%;
	height: 80%;
	border: 2px dashed #FFF;
	border-radius: 5px;
	background: url('https://mdn.mozillademos.org/files/6085/trash-can.png') no-repeat center;

	position: absolute;
	top: 10%;
	left: 10%;

	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;

	transition: all 0.2s;
}

#delete[drag-state='enter'] {
	background-color: #999;
}

/**
 * 	Color Theme
 */

#color-theme {
	margin: 0 8px 0 0;
	border: 1px solid #EEE;
	display: inline-block;
	float: right;
}

#color-theme .box {
	width: 80px;
	height: 92px;
	float: left;
}

/**
 * Color info box
 */
#color-info {
	width: 360px;
	float: left;
}

#color-info .title {
	width: 100%;
	padding: 15px;
	font-size: 18px;
	text-align: center;
	background-image: url('https://mdn.mozillademos.org/files/6071/color-wheel.png');
	background-repeat:no-repeat;
	background-position: center left 30%;
}

#color-info .copy-container {
	position: absolute;
	top: -100%;
}

#color-info .property {
	min-width: 280px;
	height: 30px;
	margin: 10px 0;
	text-align: center;
	line-height: 30px;
}

#color-info .property > * {
	float: left;
}

#color-info .property .type {
	width: 60px;
	height: 100%;
	padding: 0 16px 0 0;
	text-align: right;
}

#color-info .property .value {
	width: 200px;
	height: 100%;
	padding: 0 10px;
	font-family: "Segoe UI", Arial, Helvetica, sans-serif;
	font-size: 16px;
	color: #777;
	text-align: center;
	background-color: #FFF;
	border: none;
}

#color-info .property .value:hover {
	color: #37994A;
}

#color-info .property .value:hover + .copy {
	background-position: center right;
}

#color-info .property .copy {
	width: 24px;
	height: 100%;
	padding: 0 5px;
	background-color: #FFF;
	background-image: url('https://mdn.mozillademos.org/files/6073/copy.png');
	background-repeat: no-repeat;
	background-position: center left;
	border-left: 1px solid #EEE;
	text-align: right;
	float: left;
}

#color-info .property .copy:hover {
	background-position: center right;
}


/**
 * 	Color Palette
 */

#palette {
	width: 1000px;
	padding: 10px 0;
	background-image: url('https://mdn.mozillademos.org/files/6025/grain.png');
	background-repeat: repeat;
	background-color: #EEE;
	color: #777;

	-moz-box-sizing: border-box;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
}

#color-palette {
	width: 640px;
	font-family: Arial, Helvetica, sans-serif;
	color: #777;
	float: left;
}

#color-palette .container {
	width: 100%;
	height: 50px;
	line-height: 50px;
	overflow: hidden;
	float: left;
	transition: all 0.5s;
}

#color-palette .container > * {
	float: left;
}

#color-palette .title {
	width: 100px;
	padding: 0 10px;
	text-align: right;
	line-height: inherit;
}

#color-palette .palette {
	width: 456px;
	height: 38px;
	margin: 3px;
	padding: 3px;
	display: table;
	background-color: #FFF;
}

#color-palette .palette .sample {
	width: 30px;
	height: 30px;
	margin: 3px;
	position: relative;
	border: 1px solid #DDD;
	float: left;
	transition: all 0.2s;
}

#color-palette .palette .sample:hover {
	cursor: pointer;
	border-color: #BBB;
	transform: scale(1.15);
	border-radius: 3px;
}

#color-palette .controls {
}

#color-palette .controls > * {
	float: left;
}

#color-palette .controls > *:hover {
	cursor: pointer;
}

#color-palette .controls .lock {
	width: 24px;
	height: 24px;
	margin: 10px;
	padding: 3px;
	background-image: url('https://mdn.mozillademos.org/files/6077/lock.png');
	background-repeat: no-repeat;
	background-position: bottom right;
}

#color-palette .controls .lock:hover {
	/*background-image: url('images/unlocked-hover.png');*/
	background-position: bottom left;
}

#color-palette .controls .lock[locked-state='true'] {
	/*background-image: url('images/locked.png');*/
	background-position: top left ;
}

#color-palette .controls .lock[locked-state='true']:hover {
	/*background-image: url('images/lock-hover.png');*/
	background-position: top right;
}

/**
 * Canvas
 */

#canvas {
	width: 100%;
	height: 300px;
	min-height: 250px;
	border-top: 1px solid #DDD;
	background-image: url('https://mdn.mozillademos.org/files/6025/grain.png');
	background-repeat: repeat;
	position: relative;
	float: left;
}

#canvas[data-tutorial='drop'] {
	text-align: center;
	font-size: 30px;
	color: #777;
}

#canvas[data-tutorial='drop']:before {
	content: "Drop colors here to compare";
	width: 40%;
	padding: 30px 9% 70px 11%;

	background-image: url('https://mdn.mozillademos.org/files/6075/drop.png');
	background-repeat: no-repeat;
	background-position: left 35px top 60%;

	text-align: right;

	border: 3px dashed rgb(221, 221, 221);
	border-radius: 15px;

	position: absolute;
	top: 50px;
	left: 20%;
}

#canvas[data-tutorial='drop']:after {
	content: "adjust, change or modify";
	width: 40%;
	font-size: 24px;
	position: absolute;
	top: 130px;
	left: 32%;
	z-index: 2;
}

#canvas [data-tutorial='dblclick'] {
	background-color: #999 !important;
}

#canvas [data-tutorial='dblclick']:before {
	content: "double click to activate";
	width: 80px;
	color: #FFF;
	position: absolute;
	top: 10%;
	left: 20%;
	z-index: 2;
}

#canvas .sample {
	width: 100px;
	height: 100px;
	min-width: 20px;
	min-height: 20px;
	position: absolute;
	border: 1px solid rgba(255, 255, 255, 0.3);
}

#canvas .sample:hover {
	cursor: move;
}

#canvas .sample[data-active='true']:after {
	content: "";
	position: absolute;
	background: url('https://mdn.mozillademos.org/files/6065/arrow.png') center no-repeat;
	width: 100%;
	height: 12px;
	top: -12px;
	z-index: 2;
}

#canvas .sample:hover > * {
	cursor: pointer;
	display: block !important;
}

#canvas .sample .resize-handle {
	display: none;
}

#canvas .sample .pick {
	width: 10px;
	height: 10px;
	margin: 5px;
	background: url('https://mdn.mozillademos.org/files/6079/pick.png') center no-repeat;
	position: absolute;
	top: 0;
	left: 0;
	display: none;
}

#canvas .sample .delete {
	width: 10px;
	height: 10px;
	margin: 5px;
	background: url('https://mdn.mozillademos.org/files/6069/close.png') center no-repeat;
	position: absolute;
	top: 0;
	right: 0;
	display: none;
}


/**
 * Canvas controls
 */

#canvas .toggle-bg {
	width: 16px;
	height: 16px;
	margin: 5px;
	background: url("images/canvas-controls.png") center left no-repeat;
	position: absolute;
	top: 0;
	right: 0;
}

#canvas .toggle-bg:hover {
	cursor: pointer;
}

#canvas[data-bg='true'] {
	background: none;
}

#canvas[data-bg='true'] .toggle-bg {
	background: url('https://mdn.mozillademos.org/files/6067/canvas-controls.png') center right no-repeat;
}

#zindex {
	height: 20px;
	margin: 5px;
	font-size: 16px;
	position: absolute;
	opacity: 0;
	top: -10000px;
	left: 0;
	color: #777;
	float: left;
	transition: opacity 1s;
}

#zindex input {
	border: 1px solid #DDD;
	font-size: 16px;
	color: #777;
}

#zindex .ui-input-slider-info {
	width: 60px;
}

#zindex[data-active='true'] {
	top: 0;
	opacity: 1;
}

JavaScript Content

'use strict';

var UIColorPicker = (function UIColorPicker() {

	function getElemById(id) {
		return document.getElementById(id);
	}

	var subscribers = [];
	var pickers = [];

	/**
	 * RGBA Color class
	 *
	 * HSV/HSB and HSL (hue, saturation, value / brightness, lightness)
	 * @param hue			0-360
	 * @param saturation	0-100
	 * @param value 		0-100
	 * @param lightness		0-100
	 */

	function Color(color) {

		if(color instanceof Color === true) {
			this.copy(color);
			return;
		}

		this.r = 0;
		this.g = 0;
		this.b = 0;
		this.a = 1;
		this.hue = 0;
		this.saturation = 0;
		this.value = 0;
		this.lightness = 0;
		this.format = 'HSV';
	}

	function RGBColor(r, g, b) {
		var color = new Color();
		color.setRGBA(r, g, b, 1);
		return color;
	}

	function RGBAColor(r, g, b, a) {
		var color = new Color();
		color.setRGBA(r, g, b, a);
		return color;
	}

	function HSVColor(h, s, v) {
		var color = new Color();
		color.setHSV(h, s, v);
		return color;
	}

	function HSVAColor(h, s, v, a) {
		var color = new Color();
		color.setHSV(h, s, v);
		color.a = a;
		return color;
	}

	function HSLColor(h, s, l) {
		var color = new Color();
		color.setHSL(h, s, l);
		return color;
	}

	function HSLAColor(h, s, l, a) {
		var color = new Color();
		color.setHSL(h, s, l);
		color.a = a;
		return color;
	}

	Color.prototype.copy = function copy(obj) {
		if(obj instanceof Color !== true) {
			console.log('Typeof parameter not Color');
			return;
		}

		this.r = obj.r;
		this.g = obj.g;
		this.b = obj.b;
		this.a = obj.a;
		this.hue = obj.hue;
		this.saturation = obj.saturation;
		this.value = obj.value;
		this.format = '' + obj.format;
		this.lightness = obj.lightness;
	};

	Color.prototype.setFormat = function setFormat(format) {
		if (format === 'HSV')
			this.format = 'HSV';
		if (format === 'HSL')
			this.format = 'HSL';
	};

	/*========== Methods to set Color Properties ==========*/

	Color.prototype.isValidRGBValue = function isValidRGBValue(value) {
		return (typeof(value) === 'number' && isNaN(value) === false &&
			value >= 0 && value <= 255);
	};

	Color.prototype.setRGBA = function setRGBA(red, green, blue, alpha) {
		if (this.isValidRGBValue(red) === false ||
			this.isValidRGBValue(green) === false ||
			this.isValidRGBValue(blue) === false)
			return;

			this.r = red | 0;
			this.g = green | 0;
			this.b = blue | 0;

		if (this.isValidRGBValue(alpha) === true)
			this.a = alpha | 0;
	};

	Color.prototype.setByName = function setByName(name, value) {
		if (name === 'r' || name === 'g' || name === 'b') {
			if(this.isValidRGBValue(value) === false)
				return;

			this[name] = value;
			this.updateHSX();
		}
	};

	Color.prototype.setHSV = function setHSV(hue, saturation, value) {
		this.hue = hue;
		this.saturation = saturation;
		this.value = value;
		this.HSVtoRGB();
	};

	Color.prototype.setHSL = function setHSL(hue, saturation, lightness) {
		this.hue = hue;
		this.saturation = saturation;
		this.lightness = lightness;
		this.HSLtoRGB();
	};

	Color.prototype.setHue = function setHue(value) {
		if (typeof(value) !== 'number' || isNaN(value) === true ||
			value < 0 || value > 359)
			return;
		this.hue = value;
		this.updateRGB();
	};

	Color.prototype.setSaturation = function setSaturation(value) {
		if (typeof(value) !== 'number' || isNaN(value) === true ||
			value < 0 || value > 100)
			return;
		this.saturation = value;
		this.updateRGB();
	};

	Color.prototype.setValue = function setValue(value) {
		if (typeof(value) !== 'number' || isNaN(value) === true ||
			value < 0 || value > 100)
			return;
		this.value = value;
		this.HSVtoRGB();
	};

	Color.prototype.setLightness = function setLightness(value) {
		if (typeof(value) !== 'number' || isNaN(value) === true ||
			value < 0 || value > 100)
			return;
		this.lightness = value;
		this.HSLtoRGB();
	};

	Color.prototype.setHexa = function setHexa(value) {
		var valid  = /(^#{0,1}[0-9A-F]{6}$)|(^#{0,1}[0-9A-F]{3}$)/i.test(value);

		if (valid !== true)
			return;

		if (value[0] === '#')
			value = value.slice(1, value.length);

		if (value.length === 3)
			value = value.replace(/([0-9A-F])([0-9A-F])([0-9A-F])/i,'$1$1$2$2$3$3');

		this.r = parseInt(value.substr(0, 2), 16);
		this.g = parseInt(value.substr(2, 2), 16);
		this.b = parseInt(value.substr(4, 2), 16);

		this.alpha	= 1;
		this.RGBtoHSV();
	};

	/*========== Conversion Methods ==========*/

	Color.prototype.convertToHSL = function convertToHSL() {
		if (this.format === 'HSL')
			return;

		this.setFormat('HSL');
		this.RGBtoHSL();
	};

	Color.prototype.convertToHSV = function convertToHSV() {
		if (this.format === 'HSV')
			return;

		this.setFormat('HSV');
		this.RGBtoHSV();
	};

	/*========== Update Methods ==========*/

	Color.prototype.updateRGB = function updateRGB() {
		if (this.format === 'HSV') {
			this.HSVtoRGB();
			return;
		}

		if (this.format === 'HSL') {
			this.HSLtoRGB();
			return;
		}
	};

	Color.prototype.updateHSX = function updateHSX() {
		if (this.format === 'HSV') {
			this.RGBtoHSV();
			return;
		}

		if (this.format === 'HSL') {
			this.RGBtoHSL();
			return;
		}
	};

	Color.prototype.HSVtoRGB = function HSVtoRGB() {
		var sat = this.saturation / 100;
		var value = this.value / 100;
		var C = sat * value;
		var H = this.hue / 60;
		var X = C * (1 - Math.abs(H % 2 - 1));
		var m = value - C;
		var precision = 255;

		C = (C + m) * precision | 0;
		X = (X + m) * precision | 0;
		m = m * precision | 0;

		if (H >= 0 && H < 1) {	this.setRGBA(C, X, m);	return; }
		if (H >= 1 && H < 2) {	this.setRGBA(X, C, m);	return; }
		if (H >= 2 && H < 3) {	this.setRGBA(m, C, X);	return; }
		if (H >= 3 && H < 4) {	this.setRGBA(m, X, C);	return; }
		if (H >= 4 && H < 5) {	this.setRGBA(X, m, C);	return; }
		if (H >= 5 && H < 6) {	this.setRGBA(C, m, X);	return; }
	};

	Color.prototype.HSLtoRGB = function HSLtoRGB() {
		var sat = this.saturation / 100;
		var light = this.lightness / 100;
		var C = sat * (1 - Math.abs(2 * light - 1));
		var H = this.hue / 60;
		var X = C * (1 - Math.abs(H % 2 - 1));
		var m = light - C/2;
		var precision = 255;

		C = (C + m) * precision | 0;
		X = (X + m) * precision | 0;
		m = m * precision | 0;

		if (H >= 0 && H < 1) {	this.setRGBA(C, X, m);	return; }
		if (H >= 1 && H < 2) {	this.setRGBA(X, C, m);	return; }
		if (H >= 2 && H < 3) {	this.setRGBA(m, C, X);	return; }
		if (H >= 3 && H < 4) {	this.setRGBA(m, X, C);	return; }
		if (H >= 4 && H < 5) {	this.setRGBA(X, m, C);	return; }
		if (H >= 5 && H < 6) {	this.setRGBA(C, m, X);	return; }
	};

	Color.prototype.RGBtoHSV = function RGBtoHSV() {
		var red		= this.r / 255;
		var green	= this.g / 255;
		var blue	= this.b / 255;

		var cmax = Math.max(red, green, blue);
		var cmin = Math.min(red, green, blue);
		var delta = cmax - cmin;
		var hue = 0;
		var saturation = 0;

		if (delta) {
			if (cmax === red ) { hue = ((green - blue) / delta); }
			if (cmax === green ) { hue = 2 + (blue - red) / delta; }
			if (cmax === blue ) { hue = 4 + (red - green) / delta; }
			if (cmax) saturation = delta / cmax;
		}

		this.hue = 60 * hue | 0;
		if (this.hue < 0) this.hue += 360;
		this.saturation = (saturation * 100) | 0;
		this.value = (cmax * 100) | 0;
	};

	Color.prototype.RGBtoHSL = function RGBtoHSL() {
		var red		= this.r / 255;
		var green	= this.g / 255;
		var blue	= this.b / 255;

		var cmax = Math.max(red, green, blue);
		var cmin = Math.min(red, green, blue);
		var delta = cmax - cmin;
		var hue = 0;
		var saturation = 0;
		var lightness = (cmax + cmin) / 2;
		var X = (1 - Math.abs(2 * lightness - 1));

		if (delta) {
			if (cmax === red ) { hue = ((green - blue) / delta); }
			if (cmax === green ) { hue = 2 + (blue - red) / delta; }
			if (cmax === blue ) { hue = 4 + (red - green) / delta; }
			if (cmax) saturation = delta / X;
		}

		this.hue = 60 * hue | 0;
		if (this.hue < 0) this.hue += 360;
		this.saturation = (saturation * 100) | 0;
		this.lightness = (lightness * 100) | 0;
	};

	/*========== Get Methods ==========*/

	Color.prototype.getHexa = function getHexa() {
		var r = this.r.toString(16);
		var g = this.g.toString(16);
		var b = this.b.toString(16);
		if (this.r < 16) r = '0' + r;
		if (this.g < 16) g = '0' + g;
		if (this.b < 16) b = '0' + b;
		var value = '#' + r + g + b;
		return value.toUpperCase();
	};

	Color.prototype.getRGBA = function getRGBA() {

		var rgb = '(' + this.r + ', ' + this.g + ', ' + this.b;
		var a = '';
		var v = '';
		var x = parseFloat(this.a);
		if (x !== 1) {
			a = 'a';
			v = ', ' + x;
		}

		var value = 'rgb' + a + rgb + v + ')';
		return value;
	};

	Color.prototype.getHSLA = function getHSLA() {
		if (this.format === 'HSV') {
			var color = new Color(this);
			color.setFormat('HSL');
			color.updateHSX();
			return color.getHSLA();
		}

		var a = '';
		var v = '';
		var hsl = '(' + this.hue + ', ' + this.saturation + '%, ' + this.lightness +'%';
		var x = parseFloat(this.a);
		if (x !== 1) {
			a = 'a';
			v = ', ' + x;
		}

		var value = 'hsl' + a + hsl + v + ')';
		return value;
	};

	Color.prototype.getColor = function getColor() {
		if (this.a | 0 === 1)
			return this.getHexa();
		return this.getRGBA();
	};

	/*=======================================================================*/
	/*=======================================================================*/

	/*========== Capture Mouse Movement ==========*/

	var setMouseTracking = function setMouseTracking(elem, callback) {
		elem.addEventListener('mousedown', function(e) {
			callback(e);
			document.addEventListener('mousemove', callback);
		});

		document.addEventListener('mouseup', function(e) {
			document.removeEventListener('mousemove', callback);
		});
	};

	/*====================*/
	// Color Picker Class
	/*====================*/

	function ColorPicker(node) {
		this.color = new Color();
		this.node = node;
		this.subscribers = [];

		var type = this.node.getAttribute('data-mode');
		var topic = this.node.getAttribute('data-topic');

		this.topic = topic;
		this.picker_mode = (type === 'HSL') ? 'HSL' : 'HSV';
		this.color.setFormat(this.picker_mode);

		this.createPickingArea();
		this.createHueArea();

		this.newInputComponent('H', 'hue', this.inputChangeHue.bind(this));
		this.newInputComponent('S', 'saturation', this.inputChangeSaturation.bind(this));
		this.newInputComponent('V', 'value', this.inputChangeValue.bind(this));
		this.newInputComponent('L', 'lightness', this.inputChangeLightness.bind(this));

		this.createAlphaArea();

		this.newInputComponent('R', 'red', this.inputChangeRed.bind(this));
		this.newInputComponent('G', 'green', this.inputChangeGreen.bind(this));
		this.newInputComponent('B', 'blue', this.inputChangeBlue.bind(this));

		this.createPreviewBox();
		this.createChangeModeButton();

		this.newInputComponent('alpha', 'alpha', this.inputChangeAlpha.bind(this));
		this.newInputComponent('hexa', 'hexa', this.inputChangeHexa.bind(this));

		this.setColor(this.color);
		pickers[topic] = this;
	}

	/*************************************************************************/
	//				Function for generating the color-picker
	/*************************************************************************/

	ColorPicker.prototype.createPickingArea = function createPickingArea() {
		var area = document.createElement('div');
		var picker = document.createElement('div');

		area.className = 'picking-area';
		picker.className = 'picker';

		this.picking_area = area;
		this.color_picker = picker;
		setMouseTracking(area, this.updateColor.bind(this));

		area.appendChild(picker);
		this.node.appendChild(area);
	};

	ColorPicker.prototype.createHueArea = function createHueArea() {
		var area = document.createElement('div');
		var picker = document.createElement('div');

		area.className = 'hue';
		picker.className ='slider-picker';

		this.hue_area = area;
		this.hue_picker = picker;
		setMouseTracking(area, this.updateHueSlider.bind(this));

		area.appendChild(picker);
		this.node.appendChild(area);
	};

	ColorPicker.prototype.createAlphaArea = function createAlphaArea() {
		var area = document.createElement('div');
		var mask = document.createElement('div');
		var picker = document.createElement('div');

		area.className = 'alpha';
		mask.className = 'alpha-mask';
		picker.className = 'slider-picker';

		this.alpha_area = area;
		this.alpha_mask = mask;
		this.alpha_picker = picker;
		setMouseTracking(area, this.updateAlphaSlider.bind(this));

		area.appendChild(mask);
		mask.appendChild(picker);
		this.node.appendChild(area);
	};

	ColorPicker.prototype.createPreviewBox = function createPreviewBox(e) {
		var preview_box = document.createElement('div');
		var preview_color = document.createElement('div');

		preview_box.className = 'preview';
		preview_color.className = 'preview-color';

		this.preview_color = preview_color;

		preview_box.appendChild(preview_color);
		this.node.appendChild(preview_box);
	};

	ColorPicker.prototype.newInputComponent = function newInputComponent(title, topic, onChangeFunc) {
		var wrapper = document.createElement('div');
		var input = document.createElement('input');
		var info = document.createElement('span');

		wrapper.className = 'input';
		wrapper.setAttribute('data-topic', topic);
		info.textContent = title;
		info.className = 'name';
		input.setAttribute('type', 'text');

		wrapper.appendChild(info);
		wrapper.appendChild(input);
		this.node.appendChild(wrapper);

		input.addEventListener('change', onChangeFunc);
		input.addEventListener('click', function() {
			this.select();
		});

		this.subscribe(topic, function(value) {
			input.value = value;
		});
	};

	ColorPicker.prototype.createChangeModeButton = function createChangeModeButton() {

		var button = document.createElement('div');
		button.className = 'switch_mode';
		button.addEventListener('click', function() {
			if (this.picker_mode === 'HSV')
				this.setPickerMode('HSL');
			else
				this.setPickerMode('HSV');

		}.bind(this));

		this.node.appendChild(button);
	};

	/*************************************************************************/
	//					Updates properties of UI elements
	/*************************************************************************/

	ColorPicker.prototype.updateColor = function updateColor(e) {
		var x = e.pageX - this.picking_area.offsetLeft;
		var y = e.pageY - this.picking_area.offsetTop;
		var picker_offset = 5;

		// width and height should be the same
		var size = this.picking_area.clientWidth;

		if (x > size) x = size;
		if (y > size) y = size;
		if (x < 0) x = 0;
		if (y < 0) y = 0;

		var value = 100 - (y * 100 / size) | 0;
		var saturation = x * 100 / size | 0;

		if (this.picker_mode === 'HSV')
			this.color.setHSV(this.color.hue, saturation, value);
		if (this.picker_mode === 'HSL')
			this.color.setHSL(this.color.hue, saturation, value);

		this.color_picker.style.left = x - picker_offset + 'px';
		this.color_picker.style.top = y - picker_offset + 'px';

		this.updateAlphaGradient();
		this.updatePreviewColor();

		this.notify('value', value);
		this.notify('lightness', value);
		this.notify('saturation', saturation);

		this.notify('red', this.color.r);
		this.notify('green', this.color.g);
		this.notify('blue', this.color.b);
		this.notify('hexa', this.color.getHexa());

		notify(this.topic, this.color);
	};

	ColorPicker.prototype.updateHueSlider = function updateHueSlider(e) {
		var x = e.pageX - this.hue_area.offsetLeft;
		var width = this.hue_area.clientWidth;

		if (x < 0) x = 0;
		if (x > width) x = width;

		// TODO 360 => 359
		var hue = ((359 * x) / width) | 0;
		// if (hue === 360) hue = 359;

		this.updateSliderPosition(this.hue_picker, x);
		this.setHue(hue);
	};

	ColorPicker.prototype.updateAlphaSlider = function updateAlphaSlider(e) {
		var x = e.pageX - this.alpha_area.offsetLeft;
		var width = this.alpha_area.clientWidth;

		if (x < 0) x = 0;
		if (x > width) x = width;

		this.color.a = (x / width).toFixed(2);

		this.updateSliderPosition(this.alpha_picker, x);
		this.updatePreviewColor();

		this.notify('alpha', this.color.a);
		notify(this.topic, this.color);
	};

	ColorPicker.prototype.setHue = function setHue(value) {
		this.color.setHue(value);

		this.updatePickerBackground();
		this.updateAlphaGradient();
		this.updatePreviewColor();

		this.notify('red', this.color.r);
		this.notify('green', this.color.g);
		this.notify('blue', this.color.b);
		this.notify('hexa', this.color.getHexa());
		this.notify('hue', this.color.hue);

		notify(this.topic, this.color);
	};

	// Updates when one of Saturation/Value/Lightness changes
	ColorPicker.prototype.updateSLV = function updateSLV() {
		this.updatePickerPosition();
		this.updateAlphaGradient();
		this.updatePreviewColor();

		this.notify('red', this.color.r);
		this.notify('green', this.color.g);
		this.notify('blue', this.color.b);
		this.notify('hexa', this.color.getHexa());

		notify(this.topic, this.color);
	};

	/*************************************************************************/
	//				Update positions of various UI elements
	/*************************************************************************/

	ColorPicker.prototype.updatePickerPosition = function updatePickerPosition() {
		var size = this.picking_area.clientWidth;
		var value = 0;
		var offset = 5;

		if (this.picker_mode === 'HSV')
			value = this.color.value;
		if (this.picker_mode === 'HSL')
			value = this.color.lightness;

		var x = (this.color.saturation * size / 100) | 0;
		var y = size - (value * size / 100) | 0;

		this.color_picker.style.left = x - offset + 'px';
		this.color_picker.style.top = y - offset + 'px';
	};

	ColorPicker.prototype.updateSliderPosition = function updateSliderPosition(elem, pos) {
		elem.style.left = Math.max(pos - 3, -2) + 'px';
	};

	ColorPicker.prototype.updateHuePicker = function updateHuePicker() {
		var size = this.hue_area.clientWidth;
		var offset = 1;
		var pos = (this.color.hue * size / 360 ) | 0;
		this.hue_picker.style.left = pos - offset + 'px';
	};

	ColorPicker.prototype.updateAlphaPicker = function updateAlphaPicker() {
		var size = this.alpha_area.clientWidth;
		var offset = 1;
		var pos = (this.color.a * size) | 0;
		this.alpha_picker.style.left = pos - offset + 'px';
	};

	/*************************************************************************/
	//						Update background colors
	/*************************************************************************/

	ColorPicker.prototype.updatePickerBackground = function updatePickerBackground() {
		var nc = new Color(this.color);
		nc.setHSV(nc.hue, 100, 100);
		this.picking_area.style.backgroundColor = nc.getHexa();
	};

	ColorPicker.prototype.updateAlphaGradient = function updateAlphaGradient() {
		this.alpha_mask.style.backgroundColor = this.color.getHexa();
	};

	ColorPicker.prototype.updatePreviewColor = function updatePreviewColor() {
		this.preview_color.style.backgroundColor = this.color.getColor();
	};

	/*************************************************************************/
	//						Update input elements
	/*************************************************************************/

	ColorPicker.prototype.inputChangeHue = function inputChangeHue(e) {
		var value = parseInt(e.target.value);
		this.setHue(value);
		this.updateHuePicker();
	};

	ColorPicker.prototype.inputChangeSaturation = function inputChangeSaturation(e) {
		var value = parseInt(e.target.value);
		this.color.setSaturation(value);
		e.target.value = this.color.saturation;
		this.updateSLV();
	};

	ColorPicker.prototype.inputChangeValue = function inputChangeValue(e) {
		var value = parseInt(e.target.value);
		this.color.setValue(value);
		e.target.value = this.color.value;
		this.updateSLV();
	};

	ColorPicker.prototype.inputChangeLightness = function inputChangeLightness(e) {
		var value = parseInt(e.target.value);
		this.color.setLightness(value);
		e.target.value = this.color.lightness;
		this.updateSLV();
	};

	ColorPicker.prototype.inputChangeRed = function inputChangeRed(e) {
		var value = parseInt(e.target.value);
		this.color.setByName('r', value);
		e.target.value = this.color.r;
		this.setColor(this.color);
	};

	ColorPicker.prototype.inputChangeGreen = function inputChangeGreen(e) {
		var value = parseInt(e.target.value);
		this.color.setByName('g', value);
		e.target.value = this.color.g;
		this.setColor(this.color);
	};

	ColorPicker.prototype.inputChangeBlue = function inputChangeBlue(e) {
		var value = parseInt(e.target.value);
		this.color.setByName('b', value);
		e.target.value = this.color.b;
		this.setColor(this.color);
	};

	ColorPicker.prototype.inputChangeAlpha = function inputChangeAlpha(e) {
		var value = parseFloat(e.target.value);

		if (typeof value === 'number' && isNaN(value) === false &&
			value >= 0 && value <= 1)
			this.color.a = value.toFixed(2);

		e.target.value = this.color.a;
		this.updateAlphaPicker();
	};

	ColorPicker.prototype.inputChangeHexa = function inputChangeHexa(e) {
		var value = e.target.value;
		this.color.setHexa(value);
		this.setColor(this.color);
	};

	/*************************************************************************/
	//							Internal Pub/Sub
	/*************************************************************************/

	ColorPicker.prototype.subscribe = function subscribe(topic, callback) {
		this.subscribers[topic] = callback;
	};

	ColorPicker.prototype.notify = function notify(topic, value) {
		if (this.subscribers[topic])
			this.subscribers[topic](value);
	};

	/*************************************************************************/
	//							Set Picker Properties
	/*************************************************************************/

	ColorPicker.prototype.setColor = function setColor(color) {
		if(color instanceof Color !== true) {
			console.log('Typeof parameter not Color');
			return;
		}

		if (color.format !== this.picker_mode) {
			color.setFormat(this.picker_mode);
			color.updateHSX();
		}

		this.color.copy(color);
		this.updateHuePicker();
		this.updatePickerPosition();
		this.updatePickerBackground();
		this.updateAlphaPicker();
		this.updateAlphaGradient();
		this.updatePreviewColor();

		this.notify('red', this.color.r);
		this.notify('green', this.color.g);
		this.notify('blue', this.color.b);

		this.notify('hue', this.color.hue);
		this.notify('saturation', this.color.saturation);
		this.notify('value', this.color.value);
		this.notify('lightness', this.color.lightness);

		this.notify('alpha', this.color.a);
		this.notify('hexa', this.color.getHexa());
		notify(this.topic, this.color);
	};

	ColorPicker.prototype.setPickerMode = function setPickerMode(mode) {
		if (mode !== 'HSV' && mode !== 'HSL')
			return;

		this.picker_mode = mode;
		this.node.setAttribute('data-mode', this.picker_mode);
		this.setColor(this.color);
	};

	/*************************************************************************/
	//								UNUSED
	/*************************************************************************/

	var setPickerMode = function setPickerMode(topic, mode) {
		if (pickers[topic])
			pickers[topic].setPickerMode(mode);
	};

	var setColor = function setColor(topic, color) {
		if (pickers[topic])
			pickers[topic].setColor(color);
	};

	var getColor = function getColor(topic) {
		if (pickers[topic])
			return new Color(pickers[topic].color);
	};

	var subscribe = function subscribe(topic, callback) {
		if (subscribers[topic] === undefined)
			subscribers[topic] = [];

		subscribers[topic].push(callback);
	};

	var unsubscribe = function unsubscribe(callback) {
		subscribers.indexOf(callback);
		subscribers.splice(index, 1);
	};

	var notify = function notify(topic, value) {
		if (subscribers[topic] === undefined || subscribers[topic].length === 0)
			return;

		var color = new Color(value);
		for (var i in subscribers[topic])
			subscribers[topic][i](color);
	};

	var init = function init() {
		var elem = document.querySelectorAll('.ui-color-picker');
		var size = elem.length;
		for (var i = 0; i < size; i++)
			new ColorPicker(elem[i]);
	};

	return {
		init : init,
		Color : Color,
		RGBColor : RGBColor,
		RGBAColor : RGBAColor,
		HSVColor : HSVColor,
		HSVAColor : HSVAColor,
		HSLColor : HSLColor,
		HSLAColor : HSLAColor,
		setColor : setColor,
		getColor : getColor,
		subscribe : subscribe,
		unsubscribe : unsubscribe,
		setPickerMode : setPickerMode
	};

})();



/**
 * UI-SlidersManager
 */

var InputSliderManager = (function InputSliderManager() {

	var subscribers = {};
	var sliders = [];

	var InputComponent = function InputComponent(obj) {
		var input = document.createElement('input');
		input.setAttribute('type', 'text');
		input.style.width = 50 + obj.precision * 10 + 'px';

		input.addEventListener('click', function(e) {
			this.select();
		});

		input.addEventListener('change', function(e) {
			var value = parseFloat(e.target.value);

			if (isNaN(value) === true)
				setValue(obj.topic, obj.value);
			else
				setValue(obj.topic, value);
		});

		return input;
	};

	var SliderComponent = function SliderComponent(obj, sign) {
		var slider = document.createElement('div');
		var startX = null;
		var start_value = 0;

		slider.addEventListener("click", function(e) {
			document.removeEventListener("mousemove", sliderMotion);
			setValue(obj.topic, obj.value + obj.step * sign);
		});

		slider.addEventListener("mousedown", function(e) {
			startX = e.clientX;
			start_value = obj.value;
			document.body.style.cursor = "e-resize";

			document.addEventListener("mouseup", slideEnd);
			document.addEventListener("mousemove", sliderMotion);
		});

		var slideEnd = function slideEnd(e) {
			document.removeEventListener("mousemove", sliderMotion);
			document.body.style.cursor = "auto";
			slider.style.cursor = "pointer";
		};

		var sliderMotion = function sliderMotion(e) {
			slider.style.cursor = "e-resize";
			var delta = (e.clientX - startX) / obj.sensivity | 0;
			var value = delta * obj.step + start_value;
			setValue(obj.topic, value);
		};

		return slider;
	};

	var InputSlider = function(node) {
		var min		= parseFloat(node.getAttribute('data-min'));
		var max		= parseFloat(node.getAttribute('data-max'));
		var step	= parseFloat(node.getAttribute('data-step'));
		var value	= parseFloat(node.getAttribute('data-value'));
		var topic	= node.getAttribute('data-topic');
		var unit	= node.getAttribute('data-unit');
		var name 	= node.getAttribute('data-info');
		var sensivity = node.getAttribute('data-sensivity') | 0;
		var precision = node.getAttribute('data-precision') | 0;

		this.min = isNaN(min) ? 0 : min;
		this.max = isNaN(max) ? 100 : max;
		this.precision = precision >= 0 ? precision : 0;
		this.step = step < 0 || isNaN(step) ? 1 : step.toFixed(precision);
		this.topic = topic;
		this.node = node;
		this.unit = unit === null ? '' : unit;
		this.sensivity = sensivity > 0 ? sensivity : 5;
		value = isNaN(value) ? this.min : value;

		var input = new InputComponent(this);
		var slider_left  = new SliderComponent(this, -1);
		var slider_right = new SliderComponent(this,  1);

		slider_left.className = 'ui-input-slider-left';
		slider_right.className = 'ui-input-slider-right';

		if (name) {
			var info = document.createElement('span');
			info.className = 'ui-input-slider-info';
			info.textContent = name;
			node.appendChild(info);
		}

		node.appendChild(slider_left);
		node.appendChild(input);
		node.appendChild(slider_right);

		this.input = input;
		sliders[topic] = this;
		setValue(topic, value);
	};

	InputSlider.prototype.setInputValue = function setInputValue() {
		this.input.value = this.value.toFixed(this.precision) + this.unit;
	};

	var setValue = function setValue(topic, value, send_notify) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		value = parseFloat(value.toFixed(slider.precision));

		if (value > slider.max) value = slider.max;
		if (value < slider.min)	value = slider.min;

		slider.value = value;
		slider.node.setAttribute('data-value', value);

		slider.setInputValue();

		if (send_notify === false)
			return;

		notify.call(slider);
	};

	var setMax = function setMax(topic, value) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		slider.max = value;
		setValue(topic, slider.value);
	};

	var setMin = function setMin(topic, value) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		slider.min = value;
		setValue(topic, slider.value);
	};

	var setUnit = function setUnit(topic, unit) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		slider.unit = unit;
		setValue(topic, slider.value);
	};

	var setStep = function setStep(topic, value) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		slider.step = parseFloat(value);
		setValue(topic, slider.value);
	};

	var setPrecision = function setPrecision(topic, value) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		value = value | 0;
		slider.precision = value;

		var step = parseFloat(slider.step.toFixed(value));
		if (step === 0)
			slider.step = 1 / Math.pow(10, value);

		setValue(topic, slider.value);
	};

	var setSensivity = function setSensivity(topic, value) {
		var slider = sliders[topic];
		if (slider === undefined)
			return;

		value = value | 0;

		slider.sensivity = value > 0 ? value : 5;
	};

	var getNode =  function getNode(topic) {
		return sliders[topic].node;
	};

	var getPrecision =  function getPrecision(topic) {
		return sliders[topic].precision;
	};

	var getStep =  function getStep(topic) {
		return sliders[topic].step;
	};

	var subscribe = function subscribe(topic, callback) {
		if (subscribers[topic] === undefined)
			subscribers[topic] = [];
		subscribers[topic].push(callback);
	};

	var unsubscribe = function unsubscribe(topic, callback) {
		subscribers[topic].indexOf(callback);
		subscribers[topic].splice(index, 1);
	};

	var notify = function notify() {
		if (subscribers[this.topic] === undefined)
			return;
		for (var i = 0; i < subscribers[this.topic].length; i++)
			subscribers[this.topic][i](this.value);
	};

	var createSlider = function createSlider(topic, label) {
		var slider = document.createElement('div');
		slider.className = 'ui-input-slider';
		slider.setAttribute('data-topic', topic);

		if (label !== undefined)
			slider.setAttribute('data-info', label);

		new InputSlider(slider);
		return slider;
	};

	var init = function init() {
		var elem = document.querySelectorAll('.ui-input-slider');
		var size = elem.length;
		for (var i = 0; i < size; i++)
			new InputSlider(elem[i]);
	};

	return {
		init : init,
		setMax : setMax,
		setMin : setMin,
		setUnit : setUnit,
		setStep : setStep,
		getNode : getNode,
		getStep : getStep,
		setValue : setValue,
		subscribe : subscribe,
		unsubscribe : unsubscribe,
		setPrecision : setPrecision,
		setSensivity : setSensivity,
		getPrecision : getPrecision,
		createSlider : createSlider,
	};

})();


'use strict';

window.addEventListener("load", function() {
	ColorPickerTool.init();
});

var ColorPickerTool = (function ColorPickerTool() {

	/*========== Get DOM Element By ID ==========*/

	function getElemById(id) {
		return document.getElementById(id);
	}

	function allowDropEvent(e) {
		e.preventDefault();
	}

	/*========== Make an element resizable relative to it's parent ==========*/

	var UIComponent = (function UIComponent() {

		function makeResizable(elem, axis) {
			var valueX = 0;
			var valueY = 0;
			var action = 0;

			var resizeStart = function resizeStart(e) {
				e.stopPropagation();
				e.preventDefault();
				if (e.button !== 0)
					return;

				valueX = e.clientX - elem.clientWidth;
				valueY = e.clientY - elem.clientHeight;

				document.body.setAttribute('data-resize', axis);
				document.addEventListener('mousemove', mouseMove);
				document.addEventListener('mouseup', resizeEnd);
			};

			var mouseMove = function mouseMove(e) {
				if (action >= 0)
					elem.style.width = e.clientX - valueX + 'px';
				if (action <= 0)
					elem.style.height = e.clientY - valueY + 'px';
			};

			var resizeEnd = function resizeEnd(e) {
				if (e.button !== 0)
					return;

				document.body.removeAttribute('data-resize', axis);
				document.removeEventListener('mousemove', mouseMove);
				document.removeEventListener('mouseup', resizeEnd);
			};

			var handle = document.createElement('div');
			handle.className = 'resize-handle';

			if (axis === 'width') action = 1;
			else if (axis === 'height') action = -1;
			else axis = 'both';

			handle.className = 'resize-handle';
			handle.setAttribute('data-resize', axis);
			handle.addEventListener('mousedown', resizeStart);
			elem.appendChild(handle);
		};

		/*========== Make an element draggable relative to it's parent ==========*/

		var makeDraggable = function makeDraggable(elem, endFunction) {

			var offsetTop;
			var offsetLeft;

			elem.setAttribute('data-draggable', 'true');

			var dragStart = function dragStart(e) {
				e.preventDefault();
				e.stopPropagation();

				if (e.target.getAttribute('data-draggable') !== 'true' ||
					e.target !== elem || e.button !== 0)
					return;

				offsetLeft = e.clientX - elem.offsetLeft;
				offsetTop = e.clientY - elem.offsetTop;

				document.addEventListener('mousemove', mouseDrag);
				document.addEventListener('mouseup', dragEnd);
			};

			var dragEnd = function dragEnd(e) {
				if (e.button !== 0)
					return;

				document.removeEventListener('mousemove', mouseDrag);
				document.removeEventListener('mouseup', dragEnd);
			};

			var mouseDrag = function mouseDrag(e) {
				elem.style.left = e.clientX - offsetLeft + 'px';
				elem.style.top = e.clientY - offsetTop + 'px';
			};

			elem.addEventListener('mousedown', dragStart, false);
		};

		return {
			makeResizable : makeResizable,
			makeDraggable : makeDraggable
		};

	})();

	/*========== Color Class ==========*/

	var Color = UIColorPicker.Color;
	var HSLColor = UIColorPicker.HSLColor;

	/**
	 * ColorPalette
	 */
	var ColorPalette = (function ColorPalette() {

		var samples = [];
		var color_palette;
		var complementary;

		var hideNode = function(node) {
			node.setAttribute('data-hidden', 'true');
		};

		var ColorSample = function ColorSample(id) {
			var node = document.createElement('div');
			node.className = 'sample';

			this.uid = samples.length;
			this.node = node;
			this.color = new Color();

			node.setAttribute('sample-id', this.uid);
			node.setAttribute('draggable', 'true');
			node.addEventListener('dragstart', this.dragStart.bind(this));
			node.addEventListener('click', this.pickColor.bind(this));

			samples.push(this);
		};

		ColorSample.prototype.updateBgColor = function updateBgColor() {
			this.node.style.backgroundColor = this.color.getColor();
		};

		ColorSample.prototype.updateColor = function updateColor(color) {
			this.color.copy(color);
			this.updateBgColor();
		};

		ColorSample.prototype.updateHue = function updateHue(color, degree, steps) {
			this.color.copy(color);
			var hue = (steps * degree + this.color.hue) % 360;
			if (hue < 0) hue += 360;
			this.color.setHue(hue);
			this.updateBgColor();
		};

		ColorSample.prototype.updateSaturation = function updateSaturation(color, value, steps) {
			var saturation = color.saturation + value * steps;
			if (saturation <= 0) {
				this.node.setAttribute('data-hidden', 'true');
				return;
			}

			this.node.removeAttribute('data-hidden');
			this.color.copy(color);
			this.color.setSaturation(saturation);
			this.updateBgColor();
		};

		ColorSample.prototype.updateLightness = function updateLightness(color, value, steps) {
			var lightness = color.lightness + value * steps;
			if (lightness <= 0) {
				this.node.setAttribute('data-hidden', 'true');
				return;
			}
			this.node.removeAttribute('data-hidden');
			this.color.copy(color);
			this.color.setLightness(lightness);
			this.updateBgColor();
		};

		ColorSample.prototype.updateBrightness = function updateBrightness(color, value, steps) {
			var brightness = color.value + value * steps;
			if (brightness <= 0) {
				this.node.setAttribute('data-hidden', 'true');
				return;
			}
			this.node.removeAttribute('data-hidden');
			this.color.copy(color);
			this.color.setValue(brightness);
			this.updateBgColor();
		};

		ColorSample.prototype.updateAlpha = function updateAlpha(color, value, steps) {
			var alpha = parseFloat(color.a) + value * steps;
			if (alpha <= 0) {
				this.node.setAttribute('data-hidden', 'true');
				return;
			}
			this.node.removeAttribute('data-hidden');
			this.color.copy(color);
			this.color.a = parseFloat(alpha.toFixed(2));
			this.updateBgColor();
		};

		ColorSample.prototype.pickColor = function pickColor() {
			UIColorPicker.setColor('picker', this.color);
		};

		ColorSample.prototype.dragStart = function dragStart(e) {
			e.dataTransfer.setData('sampleID', this.uid);
			e.dataTransfer.setData('location', 'palette-samples');
		};

		var Palette = function Palette(text, size) {
			this.samples = [];
			this.locked = false;

			var palette = document.createElement('div');
			var title = document.createElement('div');
			var controls = document.createElement('div');
			var container = document.createElement('div');
			var lock = document.createElement('div');

			container.className = 'container';
			title.className = 'title';
			palette.className = 'palette';
			controls.className = 'controls';
			lock.className = 'lock';
			title.textContent = text;

			controls.appendChild(lock);
			container.appendChild(title);
			container.appendChild(controls);
			container.appendChild(palette);

			lock.addEventListener('click', function () {
				this.locked = !this.locked;
				lock.setAttribute('locked-state', this.locked);
			}.bind(this));

			for(var i = 0; i < size; i++) {
				var sample = new ColorSample();
				this.samples.push(sample);
				palette.appendChild(sample.node);
			}

			this.container = container;
			this.title = title;
		};

		var createHuePalette = function createHuePalette() {
			var palette = new Palette('Hue', 12);

			UIColorPicker.subscribe('picker', function(color) {
				if (palette.locked === true)
					return;

				for(var i = 0; i < 12; i++) {
					palette.samples[i].updateHue(color, 30, i);
				}
			});

			color_palette.appendChild(palette.container);
		};

		var createSaturationPalette = function createSaturationPalette() {
			var palette = new Palette('Saturation', 11);

			UIColorPicker.subscribe('picker', function(color) {
				if (palette.locked === true)
					return;

				for(var i = 0; i < 11; i++) {
					palette.samples[i].updateSaturation(color, -10, i);
				}
			});

			color_palette.appendChild(palette.container);
		};

		/* Brightness or Lightness - depends on the picker mode */
		var createVLPalette = function createSaturationPalette() {
			var palette = new Palette('Lightness', 11);

			UIColorPicker.subscribe('picker', function(color) {
				if (palette.locked === true)
					return;

				if(color.format === 'HSL') {
					palette.title.textContent = 'Lightness';
					for(var i = 0; i < 11; i++)
						palette.samples[i].updateLightness(color, -10, i);
				}
				else {
					palette.title.textContent = 'Value';
					for(var i = 0; i < 11; i++)
						palette.samples[i].updateBrightness(color, -10, i);
				}
			});

			color_palette.appendChild(palette.container);
		};

		var isBlankPalette = function isBlankPalette(container, value) {
			if (value === 0) {
				container.setAttribute('data-collapsed', 'true');
				return true;
			}

			container.removeAttribute('data-collapsed');
			return false;
		};

		var createAlphaPalette = function createAlphaPalette() {
			var palette = new Palette('Alpha', 10);

			UIColorPicker.subscribe('picker', function(color) {
				if (palette.locked === true)
					return;

				for(var i = 0; i < 10; i++) {
					palette.samples[i].updateAlpha(color, -0.1, i);
				}
			});

			color_palette.appendChild(palette.container);
		};

		var getSampleColor = function getSampleColor(id) {
			if (samples[id] !== undefined && samples[id]!== null)
				return new Color(samples[id].color);
		};

		var init = function init() {
			color_palette = getElemById('color-palette');

			createHuePalette();
			createSaturationPalette();
			createVLPalette();
			createAlphaPalette();

		};

		return {
			init : init,
			getSampleColor : getSampleColor
		};

	})();

	/**
	 * ColorInfo
	 */
	var ColorInfo = (function ColorInfo() {

		var info_box;
		var select;
		var RGBA;
		var HEXA;
		var HSLA;

		var updateInfo = function updateInfo(color) {
			if (color.a | 0 === 1) {
				RGBA.info.textContent = 'RGB';
				HSLA.info.textContent = 'HSL';
			}
			else {
				RGBA.info.textContent = 'RGBA';
				HSLA.info.textContent = 'HSLA';
			}

			RGBA.value.value = color.getRGBA();
			HSLA.value.value = color.getHSLA();
			HEXA.value.value = color.getHexa();
		};

		var InfoProperty = function InfoProperty(info) {

			var node = document.createElement('div');
			var title = document.createElement('div');
			var value = document.createElement('input');
			var copy = document.createElement('div');

			node.className = 'property';
			title.className = 'type';
			value.className = 'value';
			copy.className = 'copy';

			title.textContent = info;
			value.setAttribute('type', 'text');

			copy.addEventListener('click', function() {
				value.select();
			});

			node.appendChild(title);
			node.appendChild(value);
			node.appendChild(copy);

			this.node = node;
			this.value = value;
			this.info = title;

			info_box.appendChild(node);
		};

		var init = function init() {

			info_box = getElemById('color-info');

			RGBA = new InfoProperty('RGBA');
			HSLA = new InfoProperty('HSLA');
			HEXA = new InfoProperty('HEXA');

			UIColorPicker.subscribe('picker', updateInfo);

		};

		return {
			init: init
		};

	})();

	/**
	 * ColorPicker Samples
	 */
	var ColorPickerSamples = (function ColorPickerSamples() {

		var samples = [];
		var nr_samples = 0;
		var active = null;
		var container = null;
		var	samples_per_line = 10;
		var trash_can = null;
		var base_color = new HSLColor(0, 50, 100);
		var add_btn;
		var add_btn_pos;

		var ColorSample = function ColorSample() {
			var node = document.createElement('div');
			node.className = 'sample';

			this.uid = samples.length;
			this.index = nr_samples++;
			this.node = node;
			this.color = new Color(base_color);

			node.setAttribute('sample-id', this.uid);
			node.setAttribute('draggable', 'true');

			node.addEventListener('dragstart', this.dragStart.bind(this));
			node.addEventListener('dragover' , allowDropEvent);
			node.addEventListener('drop'     , this.dragDrop.bind(this));

			this.updatePosition(this.index);
			this.updateBgColor();
			samples.push(this);
		};

		ColorSample.prototype.updateBgColor = function updateBgColor() {
			this.node.style.backgroundColor = this.color.getColor();
		};

		ColorSample.prototype.updatePosition = function updatePosition(index) {
			this.index = index;
			this.posY = 5 + ((index / samples_per_line) | 0) * 52;
			this.posX = 5 + ((index % samples_per_line) | 0) * 52;
			this.node.style.top  = this.posY + 'px';
			this.node.style.left = this.posX + 'px';
		};

		ColorSample.prototype.updateColor = function updateColor(color) {
			this.color.copy(color);
			this.updateBgColor();
		};

		ColorSample.prototype.activate = function activate() {
			UIColorPicker.setColor('picker', this.color);
			this.node.setAttribute('data-active', 'true');
		};

		ColorSample.prototype.deactivate = function deactivate() {
			this.node.removeAttribute('data-active');
		};

		ColorSample.prototype.dragStart = function dragStart(e) {
			e.dataTransfer.setData('sampleID', this.uid);
			e.dataTransfer.setData('location', 'picker-samples');
		};

		ColorSample.prototype.dragDrop = function dragDrop(e) {
			e.stopPropagation();
			this.color = Tool.getSampleColorFrom(e);
			this.updateBgColor();
		};

		ColorSample.prototype.deleteSample = function deleteSample() {
			container.removeChild(this.node);
			samples[this.uid] = null;
			nr_samples--;
		};

		var updateUI = function updateUI() {
			updateContainerProp();

			var index = 0;
			var nr = samples.length;
			for (var i=0; i < nr; i++)
				if (samples[i] !== null) {
					samples[i].updatePosition(index);
					index++;
				}

			AddSampleButton.updatePosition(index);
		};

		var deleteSample = function deleteSample(e) {
			trash_can.parentElement.setAttribute('drag-state', 'none');

			var location = e.dataTransfer.getData('location');
			if (location !== 'picker-samples')
				return;

			var sampleID = e.dataTransfer.getData('sampleID');
			samples[sampleID].deleteSample();
			console.log(samples);

			updateUI();
		};

		var createDropSample = function createDropSample() {
			var sample = document.createElement('div');
			sample.id = 'drop-effect-sample';
			sample.className = 'sample';
			container.appendChild(sample);
		};

		var setActivateSample = function setActivateSample(e) {
			if (e.target.className !== 'sample')
				return;

			unsetActiveSample(active);
			Tool.unsetVoidSample();
			CanvasSamples.unsetActiveSample();
			active = samples[e.target.getAttribute('sample-id')];
			active.activate();
		};

		var unsetActiveSample = function unsetActiveSample() {
			if (active)
				active.deactivate();
			active = null;
		};

		var getSampleColor = function getSampleColor(id) {
			if (samples[id] !== undefined && samples[id]!== null)
				return new Color(samples[id].color);
		};

		var updateContainerProp = function updateContainerProp() {
			samples_per_line = ((container.clientWidth - 5) / 52) | 0;
			var height = 52 * (1 + (nr_samples / samples_per_line) | 0);
			container.style.height = height + 10 + 'px';
		};

		var AddSampleButton = (function AddSampleButton() {
			var node;
			var _index = 0;
			var _posX;
			var _posY;

			var updatePosition = function updatePosition(index) {
				_index = index;
				_posY = 5 + ((index / samples_per_line) | 0) * 52;
				_posX = 5 + ((index % samples_per_line) | 0) * 52;

				node.style.top  = _posY + 'px';
				node.style.left = _posX + 'px';
			};

			var addButtonClick = function addButtonClick() {
				var sample = new ColorSample();
				container.appendChild(sample.node);
				updatePosition(_index + 1);
				updateUI();
			};

			var init = function init() {
				node = document.createElement('div');
				var icon = document.createElement('div');

				node.className = 'sample';
				icon.id = 'add-icon';
				node.appendChild(icon);
				node.addEventListener('click', addButtonClick);

				updatePosition(0);
				container.appendChild(node);
			};

			return {
				init : init,
				updatePosition : updatePosition
			};
		})();

		var init = function init() {
			container = getElemById('picker-samples');
			trash_can = getElemById('trash-can');

			AddSampleButton.init();

			for (var i=0; i<16; i++) {
				var sample = new ColorSample();
				container.appendChild(sample.node);
			}

			AddSampleButton.updatePosition(samples.length);
			updateUI();

			active = samples[0];
			active.activate();

			container.addEventListener('click', setActivateSample);

			trash_can.addEventListener('dragover', allowDropEvent);
			trash_can.addEventListener('dragenter', function() {
				this.parentElement.setAttribute('drag-state', 'enter');
			});
			trash_can.addEventListener('dragleave', function(e) {
				this.parentElement.setAttribute('drag-state', 'none');
			});
			trash_can.addEventListener('drop', deleteSample);

			UIColorPicker.subscribe('picker', function(color) {
				if (active)
					active.updateColor(color);
			});

		};

		return {
			init : init,
			getSampleColor : getSampleColor,
			unsetActiveSample : unsetActiveSample
		};

	})();

	/**
	 * Canvas Samples
	 */
	var CanvasSamples = (function CanvasSamples() {

		var active = null;
		var canvas = null;
		var samples = [];
		var zindex = null;
		var tutorial = true;

		var CanvasSample = function CanvasSample(color, posX, posY) {

			var node = document.createElement('div');
			var pick = document.createElement('div');
			var delete_btn = document.createElement('div');
			node.className = 'sample';
			pick.className = 'pick';
			delete_btn.className = 'delete';

			this.uid = samples.length;
			this.node = node;
			this.color = color;
			this.updateBgColor();
			this.zIndex = 1;

			node.style.top = posY - 50 + 'px';
			node.style.left = posX - 50 + 'px';
			node.setAttribute('sample-id', this.uid);

			node.appendChild(pick);
			node.appendChild(delete_btn);

			var activate = function activate() {
				setActiveSample(this);
			}.bind(this);

			node.addEventListener('dblclick', activate);
			pick.addEventListener('click', activate);
			delete_btn.addEventListener('click', this.deleteSample.bind(this));

			UIComponent.makeDraggable(node);
			UIComponent.makeResizable(node);

			samples.push(this);
			canvas.appendChild(node);
			return this;
		};

		CanvasSample.prototype.updateBgColor = function updateBgColor() {
			this.node.style.backgroundColor = this.color.getColor();
		};

		CanvasSample.prototype.updateColor = function updateColor(color) {
			this.color.copy(color);
			this.updateBgColor();
		};

		CanvasSample.prototype.updateZIndex = function updateZIndex(value) {
			this.zIndex = value;
			this.node.style.zIndex = value;
		};

		CanvasSample.prototype.activate = function activate() {
			this.node.setAttribute('data-active', 'true');
			zindex.setAttribute('data-active', 'true');

			UIColorPicker.setColor('picker', this.color);
			InputSliderManager.setValue('z-index', this.zIndex);
		};

		CanvasSample.prototype.deactivate = function deactivate() {
			this.node.removeAttribute('data-active');
			zindex.removeAttribute('data-active');
		};

		CanvasSample.prototype.deleteSample = function deleteSample() {
			if (active === this)
				unsetActiveSample();
			canvas.removeChild(this.node);
			samples[this.uid] = null;
		};

		CanvasSample.prototype.updatePosition = function updatePosition(posX, posY) {
			this.node.style.top = posY - this.startY + 'px';
			this.node.style.left = posX - this.startX + 'px';
		};

		var canvasDropEvent = function canvasDropEvent(e) {
			var color = Tool.getSampleColorFrom(e);

			if (color) {
				var offsetX = e.pageX - canvas.offsetLeft;
				var offsetY = e.pageY - canvas.offsetTop;
				var sample = new CanvasSample(color, offsetX, offsetY);
				if (tutorial) {
					tutorial = false;
					canvas.removeAttribute('data-tutorial');
					var info = new CanvasSample(new Color(), 100, 100);
					info.node.setAttribute('data-tutorial', 'dblclick');
				}
			}

		};

		var setActiveSample = function setActiveSample(sample) {
			ColorPickerSamples.unsetActiveSample();
			Tool.unsetVoidSample();
			unsetActiveSample();
			active = sample;
			active.activate();
		};

		var unsetActiveSample = function unsetActiveSample() {
			if (active)
				active.deactivate();
			active = null;
		};

		var createToggleBgButton = function createToggleBgButton() {
			var button = document.createElement('div');
			var state = false;
			button.className = 'toggle-bg';
			canvas.appendChild(button);

			button.addEventListener('click', function() {
				console.log(state);
				state = !state;
				canvas.setAttribute('data-bg', state);
			});
		};

		var init = function init() {
			canvas = getElemById('canvas');
			zindex = getElemById('zindex');

			canvas.addEventListener('dragover', allowDropEvent);
			canvas.addEventListener('drop', canvasDropEvent);

			createToggleBgButton();

			UIColorPicker.subscribe('picker', function(color) {
				if (active)	active.updateColor(color);
			});

			InputSliderManager.subscribe('z-index', function (value) {
				if (active)	active.updateZIndex(value);
			});

			UIComponent.makeResizable(canvas, 'height');
		};

		return {
			init : init,
			unsetActiveSample : unsetActiveSample
		};

	})();

	var StateButton = function StateButton(node, state) {
		this.state = false;
		this.callback = null;

		node.addEventListener('click', function() {
			this.state = !this.state;
			if (typeof this.callback === "function")
				this.callback(this.state);
		}.bind(this));
	};

	StateButton.prototype.set = function set() {
		this.state = true;
		if (typeof this.callback === "function")
			this.callback(this.state);
	};

	StateButton.prototype.unset = function unset() {
		this.state = false;
		if (typeof this.callback === "function")
			this.callback(this.state);
	};

	StateButton.prototype.subscribe = function subscribe(func) {
		this.callback = func;
	};


	/**
	 * Tool
	 */
	var Tool = (function Tool() {

		var samples = [];
		var controls = null;
		var void_sw;

		var createPickerModeSwitch = function createPickerModeSwitch() {
			var parent = getElemById('controls');
			var icon = document.createElement('div');
			var button = document.createElement('div');
			var hsv = document.createElement('div');
			var hsl = document.createElement('div');
			var active = null;

			icon.className = 'icon picker-icon';
			button.className = 'switch';
			button.appendChild(hsv);
			button.appendChild(hsl);

			hsv.textContent = 'HSV';
			hsl.textContent = 'HSL';

			active = hsl;
			active.setAttribute('data-active', 'true');

			function switchPickingModeTo(elem) {
				active.removeAttribute('data-active');
				active = elem;
				active.setAttribute('data-active', 'true');
				UIColorPicker.setPickerMode('picker', active.textContent);
			};

			var picker_sw = new StateButton(icon);
			picker_sw.subscribe(function() {
				if (active === hsv)
					switchPickingModeTo(hsl);
				else
					switchPickingModeTo(hsv);
			});

			hsv.addEventListener('click', function() {
				switchPickingModeTo(hsv);
			});
			hsl.addEventListener('click', function() {
				switchPickingModeTo(hsl);
			});

			parent.appendChild(icon);
			parent.appendChild(button);
		};

		var setPickerDragAndDrop = function setPickerDragAndDrop() {
			var preview = document.querySelector('#picker .preview-color');
			var picking_area = document.querySelector('#picker .picking-area');

			preview.setAttribute('draggable', 'true');
			preview.addEventListener('drop', drop);
			preview.addEventListener('dragstart', dragStart);
			preview.addEventListener('dragover', allowDropEvent);

			picking_area.addEventListener('drop', drop);
			picking_area.addEventListener('dragover', allowDropEvent);

			function drop(e) {
				var color = getSampleColorFrom(e);
				UIColorPicker.setColor('picker', color);
			};

			function dragStart(e) {
				e.dataTransfer.setData('sampleID', 'picker');
				e.dataTransfer.setData('location', 'picker');
			};
		};

		var getSampleColorFrom = function getSampleColorFrom(e) {
			var sampleID = e.dataTransfer.getData('sampleID');
			var location = e.dataTransfer.getData('location');

			if (location === 'picker')
				return UIColorPicker.getColor(sampleID);
			if (location === 'picker-samples')
				return ColorPickerSamples.getSampleColor(sampleID);
			if (location === 'palette-samples')
				return ColorPalette.getSampleColor(sampleID);
		};

		var setVoidSwitch = function setVoidSwitch() {
			var void_sample = getElemById('void-sample');
			void_sw = new StateButton(void_sample);
			void_sw.subscribe( function (state) {
				void_sample.setAttribute('data-active', state);
				if (state === true) {
					ColorPickerSamples.unsetActiveSample();
					CanvasSamples.unsetActiveSample();
				}
			});
		};

		var unsetVoidSample = function unsetVoidSample() {
			void_sw.unset();
		};

		var init = function init() {
			controls = getElemById('controls');

			var color = new Color();
			color.setHSL(0, 51, 51);
			UIColorPicker.setColor('picker', color);

			setPickerDragAndDrop();
			createPickerModeSwitch();
			setVoidSwitch();
		};

		return {
			init : init,
			unsetVoidSample : unsetVoidSample,
			getSampleColorFrom : getSampleColorFrom
		};

	})();

	var init = function init() {
		UIColorPicker.init();
		InputSliderManager.init();
		ColorInfo.init();
		ColorPalette.init();
		ColorPickerSamples.init();
		CanvasSamples.init();
		Tool.init();
	};

	return {
		init : init
	};

})();

This tool makes it easy to create, adjust, and experiment with custom colors for the web. It also makes it easy to convert between various color formats supported by CSS, including: HEXA colors, RGB (Red/Green/Blue) and HSL (Hue/Saturation/Lightness). Control over the alpha channel is also supported on RGB (rgba) and HSL (hsla) formats.

As you adjust the parameters that define the color, it gets displayed in all three standard Web CSS formats. In addition, based on the currently-selected color, a palette for HSL and HSV, as well as alpha, is generated. The "eyedropper" style color picker box can be toggled between HSL or HSV format. You can also test colors and how they overlap one another by dragging them into the box at the bottom of the tool and moving them over one another. Adjust their relative Z index values to move them forward and behind one another.

This tool will help you identify the perfect CSS colors to apply to your HTML.

The generated colors you create above can be used anywhere color is used in CSS and HTML, unless otherwise noted.

See also

For more on using color, see: