diff --git a/colorpicker.js b/colorpicker.js
index e13892c..d93c87c 100644
--- a/colorpicker.js
+++ b/colorpicker.js
@@ -9,9 +9,9 @@
// This HTML snippet is inserted into the innerHTML property of the passed color picker element
// when the no-hassle call to ColorPicker() is used, i.e. ColorPicker(function(hex, hsv, rgb) { ... });
-
+
var colorpickerHTMLSnippet = [
-
+
'
',
'
',
'
',
@@ -20,24 +20,37 @@
'
',
'
',
'
'
-
+
].join('');
/**
- * Return mouse position relative to the element el.
+ * Return mouse position relative to the element el, optionally
+ * clipping the coordinates to be inside the element
*/
- function mousePosition(evt) {
- // IE:
- if (window.event && window.event.contentOverflow !== undefined) {
- return { x: window.event.offsetX, y: window.event.offsetY };
+ function mousePosition(evt, el, clip) {
+ var posx = 0, posy = 0, result,
+ bounds = el.getBoundingClientRect();
+ // https://www.quirksmode.org/js/events_properties.html#position
+ evt = evt || window.event;
+ if (evt.pageX || evt.pageY) {
+ posx = evt.pageX;
+ posy = evt.pageY;
}
- // Webkit:
- if (evt.offsetX !== undefined && evt.offsetY !== undefined) {
- return { x: evt.offsetX, y: evt.offsetY };
+ else if (evt.clientX || evt.clientY) {
+ posx = evt.clientX + document.body.scrollLeft
+ + document.documentElement.scrollLeft;
+ posy = evt.clientY + document.body.scrollTop
+ + document.documentElement.scrollTop;
}
- // Firefox:
- var wrapper = evt.target.parentNode.parentNode;
- return { x: evt.layerX - wrapper.offsetLeft, y: evt.layerY - wrapper.offsetTop };
+ result = {
+ x: posx - bounds.left,
+ y: posy - bounds.top
+ };
+ if (clip) {
+ result.x = Math.max(0, Math.min(el.offsetWidth, result.x));
+ result.y = Math.max(0, Math.min(el.offsetHeight, result.y));
+ }
+ return result;
}
/**
@@ -122,7 +135,7 @@
'',
''
].join('');
-
+
if (!document.namespaces['v'])
document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
}
@@ -134,7 +147,7 @@
function hsv2rgb(hsv) {
var R, G, B, X, C;
var h = (hsv.h % 360) / 60;
-
+
C = hsv.v * hsv.s;
X = C * (1 - Math.abs(h % 2 - 1));
R = G = B = hsv.v - C;
@@ -160,7 +173,7 @@
var r = rgb.r;
var g = rgb.g;
var b = rgb.b;
-
+
if (rgb.r > 1 || rgb.g > 1 || rgb.b > 1) {
r /= 255;
g /= 255;
@@ -182,11 +195,11 @@
/**
* Return click event handler for the slider.
* Sets picker background color and calls ctx.callback if provided.
- */
+ */
function slideListener(ctx, slideElement, pickerElement) {
return function(evt) {
evt = evt || window.event;
- var mouse = mousePosition(evt);
+ var mouse = mousePosition(evt, slideElement, true);
ctx.h = mouse.y / slideElement.offsetHeight * 360 + hueOffset;
var pickerColor = hsv2rgb({ h: ctx.h, s: 1, v: 1 });
var c = hsv2rgb({ h: ctx.h, s: ctx.s, v: ctx.v });
@@ -198,12 +211,12 @@
/**
* Return click event handler for the picker.
* Calls ctx.callback if provided.
- */
+ */
function pickerListener(ctx, pickerElement) {
return function(evt) {
evt = evt || window.event;
- var mouse = mousePosition(evt),
- width = pickerElement.offsetWidth,
+ var mouse = mousePosition(evt, pickerElement, true),
+ width = pickerElement.offsetWidth,
height = pickerElement.offsetHeight;
ctx.s = mouse.x / width;
@@ -214,7 +227,7 @@
};
var uniqID = 0;
-
+
/**
* ColorPicker.
* @param {DOMElement} slideElement HSV slide element.
@@ -222,7 +235,7 @@
* @param {Function} callback Called whenever the color is changed provided chosen color in RGB HEX format as the only argument.
*/
function ColorPicker(slideElement, pickerElement, callback) {
-
+
if (!(this instanceof ColorPicker)) return new ColorPicker(slideElement, pickerElement, callback);
this.h = 0;
@@ -234,23 +247,23 @@
var element = slideElement;
element.innerHTML = colorpickerHTMLSnippet;
-
+
this.slideElement = element.getElementsByClassName('slide')[0];
this.pickerElement = element.getElementsByClassName('picker')[0];
var slideIndicator = element.getElementsByClassName('slide-indicator')[0];
var pickerIndicator = element.getElementsByClassName('picker-indicator')[0];
-
+
ColorPicker.fixIndicators(slideIndicator, pickerIndicator);
this.callback = function(hex, hsv, rgb, pickerCoordinate, slideCoordinate) {
ColorPicker.positionIndicators(slideIndicator, pickerIndicator, slideCoordinate, pickerCoordinate);
-
+
pickerElement(hex, hsv, rgb);
};
-
+
} else {
-
+
this.callback = callback;
this.pickerElement = pickerElement;
this.slideElement = slideElement;
@@ -263,20 +276,20 @@
var slideClone = slide.cloneNode(true);
var pickerClone = picker.cloneNode(true);
-
+
var hsvGradient = slideClone.getElementsByTagName('linearGradient')[0];
-
+
var hsvRect = slideClone.getElementsByTagName('rect')[0];
-
+
hsvGradient.id = 'gradient-hsv-' + uniqID;
hsvRect.setAttribute('fill', 'url(#' + hsvGradient.id + ')');
var blackAndWhiteGradients = [pickerClone.getElementsByTagName('linearGradient')[0], pickerClone.getElementsByTagName('linearGradient')[1]];
var whiteAndBlackRects = pickerClone.getElementsByTagName('rect');
-
+
blackAndWhiteGradients[0].id = 'gradient-black-' + uniqID;
blackAndWhiteGradients[1].id = 'gradient-white-' + uniqID;
-
+
whiteAndBlackRects[0].setAttribute('fill', 'url(#' + blackAndWhiteGradients[1].id + ')');
whiteAndBlackRects[1].setAttribute('fill', 'url(#' + blackAndWhiteGradients[0].id + ')');
@@ -284,11 +297,11 @@
this.pickerElement.appendChild(pickerClone);
uniqID++;
-
+
} else {
-
+
this.slideElement.innerHTML = slide;
- this.pickerElement.innerHTML = picker;
+ this.pickerElement.innerHTML = picker;
}
addEventListener(this.slideElement, 'click', slideListener(this, this.slideElement, this.pickerElement));
@@ -301,15 +314,27 @@
function addEventListener(element, event, listener) {
if (element.attachEvent) {
-
+
element.attachEvent('on' + event, listener);
-
+
} else if (element.addEventListener) {
element.addEventListener(event, listener, false);
}
}
+ function removeEventListener(element, event, listener) {
+
+ if (element.detachEvent) {
+
+ element.detachEvent('on' + event, listener);
+
+ } else if (element.removeEventListener) {
+
+ element.removeEventListener(event, listener, false);
+ }
+ }
+
/**
* Enable drag&drop color selection.
* @param {object} ctx ColorPicker instance.
@@ -317,19 +342,29 @@
* @param {Function} listener Function that will be called whenever mouse is dragged over the element with event object as argument.
*/
function enableDragging(ctx, element, listener) {
-
- var mousedown = false;
- addEventListener(element, 'mousedown', function(evt) { mousedown = true; });
- addEventListener(element, 'mouseup', function(evt) { mousedown = false; });
- addEventListener(element, 'mouseout', function(evt) { mousedown = false; });
- addEventListener(element, 'mousemove', function(evt) {
+ var start = function() {
+ removeEventListener(element, 'mousedown', start);
+ addEventListener(document.body, 'mouseup', stop);
+ addEventListener(document.body, 'mousemove', listener);
+ };
+
+ var stop = function() {
+ addEventListener(element, 'mousedown', start);
+ removeEventListener(document.body, 'mouseup', stop);
+ removeEventListener(document.body, 'mousemove', listener);
+ };
- if (mousedown) {
-
- listener(evt);
+ addEventListener(element, 'mousedown', start);
+ addEventListener(element, 'dragstart', function(evt) {
+ evt = evt || window.event;
+ if (evt.preventDefault) {
+ evt.preventDefault();
+ } else {
+ evt.returnValue = false;
}
});
+
}
@@ -338,21 +373,21 @@
delete rgbHex.hex;
return rgbHex;
};
-
+
ColorPicker.hsv2hex = function(hsv) {
return hsv2rgb(hsv).hex;
};
-
+
ColorPicker.rgb2hsv = rgb2hsv;
ColorPicker.rgb2hex = function(rgb) {
return hsv2rgb(rgb2hsv(rgb)).hex;
};
-
+
ColorPicker.hex2hsv = function(hex) {
return rgb2hsv(ColorPicker.hex2rgb(hex));
};
-
+
ColorPicker.hex2rgb = function(hex) {
return { r: parseInt(hex.substr(1, 2), 16), g: parseInt(hex.substr(3, 2), 16), b: parseInt(hex.substr(5, 2), 16) };
};
@@ -368,24 +403,24 @@
ctx.h = hsv.h % 360;
ctx.s = hsv.s;
ctx.v = hsv.v;
-
+
var c = hsv2rgb(ctx);
-
+
var mouseSlide = {
y: (ctx.h * ctx.slideElement.offsetHeight) / 360,
x: 0 // not important
};
-
+
var pickerHeight = ctx.pickerElement.offsetHeight;
-
+
var mousePicker = {
x: ctx.s * ctx.pickerElement.offsetWidth,
y: pickerHeight - ctx.v * pickerHeight
};
-
+
ctx.pickerElement.style.backgroundColor = hsv2rgb({ h: ctx.h, s: 1, v: 1 }).hex;
ctx.callback && ctx.callback(hex || c.hex, { h: ctx.h, s: ctx.s, v: ctx.v }, rgb || { r: c.r, g: c.g, b: c.b }, mousePicker, mouseSlide);
-
+
return ctx;
};
@@ -396,7 +431,7 @@
ColorPicker.prototype.setHsv = function(hsv) {
return setColor(this, hsv);
};
-
+
/**
* Sets color of the picker in rgb format.
* @param {object} rgb Object of the form: { r: , g: , b: }.
@@ -421,14 +456,14 @@
* @param {object} mousePicker Coordinates of the mouse cursor in the picker area.
*/
ColorPicker.positionIndicators = function(slideIndicator, pickerIndicator, mouseSlide, mousePicker) {
-
+
if (mouseSlide) {
slideIndicator.style.top = (mouseSlide.y - slideIndicator.offsetHeight/2) + 'px';
}
if (mousePicker) {
pickerIndicator.style.top = (mousePicker.y - pickerIndicator.offsetHeight/2) + 'px';
pickerIndicator.style.left = (mousePicker.x - pickerIndicator.offsetWidth/2) + 'px';
- }
+ }
};
/**