raumsteuerung/js/colorjoe.min.js

770 lines
17 KiB
JavaScript
Raw Normal View History

2013-12-10 20:26:47 +00:00
(function(root, factory) {
if(typeof exports === 'object') {
module.exports = factory(require('onecolor'));
}
else if(typeof define === 'function' && define.amd) {
define(['onecolor'], factory);
}
else {
root.colorjoe = factory(root.one.color);
}
}(this, function(onecolor) {
/*! colorjoe - v0.9.2 - Juho Vepsalainen <bebraw@gmail.com> - MIT
https://bebraw.github.com/colorjoe - 2013-09-24 */
/*! dragjs - v0.4.0 - Juho Vepsalainen <bebraw@gmail.com> - MIT
https://bebraw.github.com/dragjs - 2013-07-17 */
var drag = (function() {
function drag(elem, cbs) {
if(!elem) {
console.warn('drag is missing elem!');
return;
}
if(isTouch()) dragTemplate(elem, cbs, 'touchstart', 'touchmove', 'touchend');
else dragTemplate(elem, cbs, 'mousedown', 'mousemove', 'mouseup');
}
function xyslider(o) {
var twod = div(o['class'] || '', o.parent);
var pointer = div('pointer', twod);
div('shape shape1', pointer);
div('shape shape2', pointer);
div('bg bg1', twod);
div('bg bg2', twod);
drag(twod, attachPointer(o.cbs, pointer));
return {
background: twod,
pointer: pointer
};
}
function slider(o) {
var oned = div(o['class'], o.parent);
var pointer = div('pointer', oned);
div('shape', pointer);
div('bg', oned);
drag(oned, attachPointer(o.cbs, pointer));
return {
background: oned,
pointer: pointer
};
}
drag.xyslider = xyslider;
drag.slider = slider;
return drag;
function attachPointer(cbs, pointer) {
var ret = {};
for(var n in cbs) ret[n] = wrap(cbs[n]);
function wrap(fn) {
return function(p) {
p.pointer = pointer;
fn(p);
};
}
return ret;
}
// move to elemutils lib?
function div(klass, p) {
return e('div', klass, p);
}
function e(type, klass, p) {
var elem = document.createElement(type);
if(klass) elem.className = klass;
p.appendChild(elem);
return elem;
}
// http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
function isTouch() {
return typeof(window.ontouchstart) != 'undefined';
}
function dragTemplate(elem, cbs, down, move, up) {
var dragging = false;
cbs = getCbs(cbs);
var beginCb = cbs.begin;
var changeCb = cbs.change;
var endCb = cbs.end;
on(elem, down, function(e) {
dragging = true;
var moveHandler = partial(callCb, changeCb, elem);
function upHandler() {
dragging = false;
off(document, move, moveHandler);
off(document, up, upHandler);
callCb(endCb, elem, e);
}
on(document, move, moveHandler);
on(document, up, upHandler);
callCb(beginCb, elem, e);
});
}
function on(elem, evt, handler) {
if(elem.addEventListener)
elem.addEventListener(evt, handler, false);
else if(elem.attachEvent)
elem.attachEvent('on' + evt, handler);
}
function off(elem, evt, handler) {
if(elem.removeEventListener)
elem.removeEventListener(evt, handler, false);
else if(elem.detachEvent)
elem.detachEvent('on' + evt, handler);
}
function getCbs(cbs) {
if(!cbs) {
var initialOffset;
var initialPos;
return {
begin: function(c) {
initialOffset = {x: c.elem.offsetLeft, y: c.elem.offsetTop};
initialPos = c.cursor;
},
change: function(c) {
style(c.elem, 'left', (initialOffset.x + c.cursor.x - initialPos.x) + 'px');
style(c.elem, 'top', (initialOffset.y + c.cursor.y - initialPos.y) + 'px');
},
end: empty
};
}
else {
return {
begin: cbs.begin || empty,
change: cbs.change || empty,
end: cbs.end || empty
};
}
}
// TODO: set draggable class (handy for fx)
function style(e, prop, value) {
e.style[prop] = value;
}
function empty() {}
function callCb(cb, elem, e) {
e.preventDefault();
var offset = findPos(elem);
var width = elem.clientWidth;
var height = elem.clientHeight;
var cursor = {
x: cursorX(elem, e),
y: cursorY(elem, e)
};
var x = (cursor.x - offset.x) / width;
var y = (cursor.y - offset.y) / height;
cb({
x: isNaN(x)? 0: x,
y: isNaN(y)? 0: y,
cursor: cursor,
elem: elem,
e: e
});
}
// http://stackoverflow.com/questions/4394747/javascript-curry-function
function partial(fn) {
var slice = Array.prototype.slice;
var args = slice.apply(arguments, [1]);
return function() {
return fn.apply(null, args.concat(slice.apply(arguments)));
};
}
// http://www.quirksmode.org/js/findpos.html
function findPos(e) {
var x = 0;
var y = 0;
if(e.offsetParent) {
do {
x += e.offsetLeft;
y += e.offsetTop;
} while (e = e.offsetParent);
}
return {x: x, y: y};
}
// http://javascript.about.com/library/blmousepos.htm
function cursorX(elem, evt) {
if(isFixed(elem)) {
var bodyLeft = parseInt(getStyle(document.body, 'marginLeft'), 10) -
calc(elem, 'scrollLeft') + window.pageXOffset +
elem.style.marginLeft;
return evt.clientX - bodyLeft;
}
if(evt.pageX) return evt.pageX;
else if(evt.clientX)
return evt.clientX + document.body.scrollLeft;
}
function cursorY(elem, evt) {
if(isFixed(elem)) {
var bodyTop = parseInt(getStyle(document.body, 'marginTop'), 10) -
calc(elem, 'scrollTop') + window.pageYOffset +
elem.style.marginTop;
return evt.clientY - bodyTop;
}
if(evt.pageY) return evt.pageY;
else if(evt.clientY)
return evt.clientY + document.body.scrollTop;
}
function calc(element, prop) {
var ret = 0;
while (element.nodeName != "HTML") {
ret += element[prop];
element = element.parentNode;
}
return ret;
}
// http://www.velocityreviews.com/forums/t942580-mouse-position-in-both-fixed-and-relative-positioning.html
function isFixed(element) {
// While not at the top of the document tree, or not fixed, keep
// searching upwards.
while (element.nodeName != "HTML" && usedStyle(element,
"position") != "fixed")
element = element.parentNode;
if(element.nodeName == "HTML") return false;
else return true;
}
// http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
function getStyle(el, cssprop){
if (el.currentStyle) // IE
return el.currentStyle[cssprop];
if(document.defaultView && document.defaultView.getComputedStyle)
return document.defaultView.getComputedStyle(el, "")[cssprop];
//try and get inline style
return el.style[cssprop];
}
// Used style is to get around browsers' different methods of getting
// the currently used (e.g. inline, class, etc) style for an element
function usedStyle(element, property) {
var s;
// getComputedStyle is the standard way but some ie versions don't
// support it
if(window.getComputedStyle)
s = window.getComputedStyle(element, null);
else s = element.currentStyle;
return s[property];
}
})();
var div = partial(e, 'div');
function e(type, klass, p) {
var elem = document.createElement(type);
elem.className = klass;
p.appendChild(elem);
return elem;
}
// http://stackoverflow.com/questions/4394747/javascript-curry-function
function partial(fn) {
var slice = Array.prototype.slice;
var args = slice.apply(arguments, [1]);
return function() {
return fn.apply(null, args.concat(slice.apply(arguments)));
};
}
function labelInput(klass, n, p, maxLen) {
var d = div(klass, p);
var l = label(n, d);
var i = input('text', d, maxLen);
return {label: l, input: i};
}
function label(c, p) {
var elem = e('label', '', p);
elem.innerHTML = c;
return elem;
}
function input(t, p, maxLen) {
var elem = e('input', '', p);
elem.type = t;
if(maxLen) elem.maxLength = maxLen;
return elem;
}
function X(p, a) {p.style.left = clamp(a * 100, 0, 100) + '%';}
function Y(p, a) {p.style.top = clamp(a * 100, 0, 100) + '%';}
function BG(e, c) {e.style.background = c;}
function clamp(a, minValue, maxValue) {
return Math.min(Math.max(a, minValue), maxValue);
}
var utils = {
clamp: clamp,
e: e,
div: div,
partial: partial,
labelInput: labelInput,
X: X,
Y: Y,
BG: BG
};
function currentColor(p) {
var e1 = utils.div('currentColorContainer', p);
var e = utils.div('currentColor', e1);
return {
change: function(col) {
utils.BG(e, col.cssa());
}
};
}
function fields(p, joe, o) {
var cs = o.space;
var fac = o.limit || 255;
var fix = o.fix >= 0? o.fix: 0;
var inputLen = ('' + fac).length + fix;
inputLen = fix? inputLen + 1: inputLen;
var initials = cs.split('');
var useAlpha = cs[cs.length - 1] == 'A';
cs = useAlpha? cs.slice(0, -1): cs;
if(['RGB', 'HSL', 'HSV', 'CMYK'].indexOf(cs) < 0)
return console.warn('Invalid field names', cs);
var c = utils.div('colorFields', p);
var elems = initials.map(function(n, i) {
n = n.toLowerCase();
var e = utils.labelInput('color ' + n, n, c, inputLen);
e.input.onkeyup = update;
return {name: n, e: e};
});
function update() {
var col = [cs];
elems.forEach(function(o) {col.push(o.e.input.value / fac);});
if(!useAlpha) col.push(joe.getAlpha());
joe.set(col);
}
return {
change: function(col) {
elems.forEach(function(o) {
o.e.input.value = (col[o.name]() * fac).toFixed(fix);
});
}
};
}
function alpha(p, joe) {
var e = drag.slider({
parent: p,
'class': 'oned alpha',
cbs: {
begin: change,
change: change
}
});
function change(p) {
var val = utils.clamp(p.y, 0, 1);
utils.Y(p.pointer, val);
joe.setAlpha(1 - val);
}
return {
change: function(col) {
utils.Y(e.pointer, 1 - col.alpha());
}
};
}
function hex(p, joe, o) {
var e = utils.labelInput('hex', o.label || '', p, 7);
e.input.value = '#';
e.input.onkeyup = function(elem) {
var key = elem.keyCode || elem.which;
var val = elem.target.value;
val = val[0] == '#'? val: '#' + val;
val = pad(val, 7, '0');
if(key == 13) joe.set(val);
};
e.input.onblur = function(elem) {
joe.set(elem.target.value);
};
return {
change: function(col) {
e.input.value = e.input.value[0] == '#'? '#': '';
e.input.value += col.hex().slice(1);
}
};
}
function close(p, joe, o) {
var elem = utils.e('a', o['class'] || 'close', p);
elem.href = '#';
elem.innerHTML = o.label || 'Close';
elem.onclick = function(e) {
e.preventDefault();
joe.hide();
};
}
function pad(a, n, c) {
var ret = a;
for(var i = a.length, len = n; i < n; i++) ret += c;
return ret;
}
var extras = {
currentColor: currentColor,
fields: fields,
hex: hex,
alpha: alpha,
close: close
};
var colorjoe = function(cbs) {
if(!all(isFunction, [cbs.init, cbs.xy, cbs.z]))
return console.warn('colorjoe: missing cb');
return function(element, initialColor, extras) {
return setup({
e: element,
color: initialColor,
cbs: cbs,
extras: extras
});
};
};
/* pickers */
colorjoe.rgb = colorjoe({
init: function(col, xy, z) {
var ret = onecolor(col).hsl();
this.xy(ret, {x: ret.saturation(), y: 1 - ret.value()}, xy, z);
this.z(ret, ret.hue(), xy, z);
return ret;
},
xy: function(col, p, xy, z) {
utils.X(xy.pointer, p.x);
utils.Y(xy.pointer, p.y);
return col.saturation(p.x).value(1 - p.y);
},
z: function(col, v, xy, z) {
utils.Y(z.pointer, v);
RGB_BG(xy.background, v);
return col.hue(v);
}
});
colorjoe.hsl = colorjoe({
init: function(col, xy, z) {
var ret = onecolor(col).hsl();
this.xy(ret, {x: ret.hue(), y: 1 - ret.saturation()}, xy, z);
this.z(ret, 1 - ret.lightness(), xy, z);
return ret;
},
xy: function(col, p, xy, z) {
utils.X(xy.pointer, p.x);
utils.Y(xy.pointer, p.y);
RGB_BG(z.background, p.x);
return col.hue(p.x).saturation(1 - p.y);
},
z: function(col, v, xy, z) {
utils.Y(z.pointer, v);
return col.lightness(1 - v);
}
});
colorjoe._extras = {};
colorjoe.registerExtra = function(name, fn) {
if(name in colorjoe._extras)
console.warn('Extra "' + name + '"has been registered already!');
colorjoe._extras[name] = fn;
};
for(var k in extras) {
colorjoe.registerExtra(k, extras[k]);
}
function RGB_BG(e, h) {
utils.BG(e, new onecolor.HSV(h, 1, 1).cssa());
}
function setup(o) {
if(!o.e) return console.warn('colorjoe: missing element');
var e = isString(o.e)? document.getElementById(o.e): o.e;
e.className = 'colorPicker';
var cbs = o.cbs;
var xy = drag.xyslider({
parent: e,
'class': 'twod',
cbs: {
begin: changeXY,
change: changeXY,
end: done
}
});
function changeXY(p) {
col = cbs.xy(col, {
x: utils.clamp(p.x, 0, 1),
y: utils.clamp(p.y, 0, 1)
}, xy, z);
changed();
}
var z = drag.slider({
parent: e,
'class': 'oned',
cbs: {
begin: changeZ,
change: changeZ,
end: done
}
});
function changeZ(p) {
col = cbs.z(col, utils.clamp(p.y, 0, 1), xy, z);
changed();
}
// Initial color
var previous = getColor(o.color);
var col = cbs.init(previous, xy, z);
var listeners = {change: [], done: []};
function changed(skip) {
skip = isArray(skip)? skip: [];
var li = listeners.change;
var v;
for(var i = 0, len = li.length; i < len; i++) {
v = li[i];
if(skip.indexOf(v.name) == -1) v.fn(col);
}
}
function done() {
// Do not call done callback if the color did not change
if (previous.equals(col)) return;
for(var i = 0, len = listeners.done.length; i < len; i++) {
listeners.done[i].fn(col);
}
previous = col;
}
var ob = {
e: e,
update: function(skip) {
changed(skip);
return this;
},
hide: function() {
e.style.display = 'none';
return this;
},
show: function() {
e.style.display = '';
return this;
},
get: function() {
return col;
},
set: function(c) {
var oldCol = this.get();
col = cbs.init(getColor(c), xy, z);
if(!oldCol.equals(col)) this.update();
return this;
},
getAlpha: function() {
return col.alpha();
},
setAlpha: function(v) {
col = col.alpha(v);
this.update();
return this;
},
on: function(evt, cb, name) {
if(evt == 'change' || evt == 'done') {
listeners[evt].push({name: name, fn: cb});
}
else console.warn('Passed invalid evt name "' + evt + '" to colorjoe.on');
return this;
},
removeAllListeners: function(evt) {
if (evt) {
delete listeners[evt];
}
else {
for(var key in listeners) {
delete listeners[key];
}
}
return this;
}
};
setupExtras(e, ob, o.extras);
changed();
return ob;
}
function getColor(c) {
if(!isDefined(c)) return onecolor('#000');
if(c.isColor) return c;
var ret = onecolor(c);
if(ret) return ret;
if(isDefined(c)) console.warn('Passed invalid color to colorjoe, using black instead');
return onecolor('#000');
}
function setupExtras(p, joe, extras) {
if(!extras) return;
var c = utils.div('extras', p);
var cbs;
var name;
var params;
extras.forEach(function(e, i) {
if(isArray(e)) {
name = e[0];
params = e.length > 1? e[1]: {};
}
else {
name = e;
params = {};
}
extra = name in colorjoe._extras? colorjoe._extras[name]: null;
if(extra) {
cbs = extra(c, extraProxy(joe, name + i), params);
for(var k in cbs) joe.on(k, cbs[k], name);
}
});
}
function extraProxy(joe, name) {
var ret = copy(joe);
ret.update = function() {
joe.update([name]);
};
return ret;
}
function copy(o) {
// returns a shallow copy
var ret = {};
for(var k in o) {
ret[k] = o[k];
}
return ret;
}
function all(cb, a) {return a.map(cb).filter(id).length == a.length;}
function isArray(o) {
return Object.prototype.toString.call(o) === "[object Array]";
}
function isString(o) {return typeof(o) === 'string';}
function isDefined(input) {return typeof input !== "undefined";}
function isFunction(input) {return typeof input === "function";}
function id(a) {return a;}
return colorjoe;
}));