505 lines
18 KiB
JavaScript
505 lines
18 KiB
JavaScript
var FwColourPicker = function(callback, options, colour, onClose) {
|
|
if(typeof callback !== 'function')
|
|
return;
|
|
if(typeof colour !== 'number')
|
|
colour = parseInt(colour || 0);
|
|
if(typeof options !== 'object')
|
|
options = {};
|
|
|
|
var readThres = 168,
|
|
lumiRed = .299,
|
|
lumiGreen = .587,
|
|
lumiBlue = .114;
|
|
|
|
var extractRGB = function(raw) {
|
|
return [
|
|
(raw >> 16) & 0xFF,
|
|
(raw >> 8) & 0xFF,
|
|
raw & 0xFF,
|
|
];
|
|
};
|
|
|
|
var calcLumi = function(raw) {
|
|
var rgb = extractRGB(raw);
|
|
return rgb[0] * lumiRed
|
|
+ rgb[1] * lumiGreen
|
|
+ rgb[2] * lumiBlue;
|
|
};
|
|
|
|
var textColour = function(raw) {
|
|
return calcLumi(raw) > readThres ? 0 : 0xFFFFFF;
|
|
};
|
|
|
|
var weightNum = function(n1, n2, w) {
|
|
w = Math.min(1, Math.max(0, w));
|
|
return Math.round((n1 * w) + (n2 * (1 - w)));
|
|
};
|
|
|
|
var weightColour = function(c1, c2, w) {
|
|
c1 = extractRGB(c1);
|
|
c2 = extractRGB(c2);
|
|
return (weightNum(c1[0], c2[0], w) << 16)
|
|
| (weightNum(c1[1], c2[1], w) << 8)
|
|
| weightNum(c1[2], c2[2], w);
|
|
};
|
|
|
|
var shadeColour = function(raw, offset) {
|
|
if(offset == 0)
|
|
return raw;
|
|
|
|
var dir = 0xFFFFFF;
|
|
if(offset < 0) {
|
|
dir = 0;
|
|
offset *= -1;
|
|
}
|
|
|
|
return weightColour(dir, raw, offset);
|
|
};
|
|
|
|
var hexFormat = FwColourPicker.hexFormat;
|
|
|
|
var verifyOption = function(name, type, def) {
|
|
if(typeof options[name] !== type)
|
|
options[name] = def;
|
|
};
|
|
|
|
verifyOption('posX', 'number', -1);
|
|
verifyOption('posY', 'number', -1);
|
|
verifyOption('presets', 'object', []);
|
|
verifyOption('showPresetsTab', 'boolean', options.presets.length > 0);
|
|
verifyOption('showGridTab', 'boolean', true);
|
|
verifyOption('showSlidersTab', 'boolean', true);
|
|
verifyOption('showHexValue', 'boolean', true);
|
|
verifyOption('showRawValue', 'boolean', true);
|
|
verifyOption('autoClose', 'boolean', true);
|
|
|
|
var onColourChange = [];
|
|
var runOnColourChange = function() {
|
|
var text = textColour(colour);
|
|
for(var i = 0; i < onColourChange.length; ++i)
|
|
onColourChange[i](colour, text);
|
|
};
|
|
|
|
var setColour = function(raw) {
|
|
colour = parseInt(raw || 0) & 0xFFFFFF;
|
|
runOnColourChange();
|
|
};
|
|
|
|
var apply = function() {
|
|
callback(pub, colour);
|
|
if(options.autoClose)
|
|
close();
|
|
};
|
|
|
|
var cancel = function() {
|
|
callback(pub, null);
|
|
if(options.autoClose)
|
|
close();
|
|
};
|
|
|
|
var close = function() {
|
|
if(onClose && onClose())
|
|
return;
|
|
container.parentNode.removeChild(container);
|
|
};
|
|
|
|
var height = 96;
|
|
|
|
var container = document.createElement('div');
|
|
container.className = 'fw-colour-picker';
|
|
onColourChange.push(function(colour) {
|
|
container.style.borderColor = hexFormat(colour);
|
|
});
|
|
|
|
var form = document.createElement('form');
|
|
form.className = 'fw-colour-picker-form';
|
|
container.appendChild(form);
|
|
|
|
form.onsubmit = function(ev) {
|
|
ev.preventDefault();
|
|
apply();
|
|
return false;
|
|
};
|
|
|
|
var tabs = {};
|
|
var activeTab = undefined;
|
|
onColourChange.push(function(colour, text) {
|
|
if(activeTab) {
|
|
activeTab.b.style.background = hexFormat(colour);
|
|
activeTab.b.style.borderColor = hexFormat(colour);
|
|
activeTab.b.style.color = hexFormat(text);
|
|
}
|
|
});
|
|
|
|
var tabbed = document.createElement('div');
|
|
tabbed.className = 'fw-colour-picker-tabbed';
|
|
|
|
var tabbedContainer = document.createElement('div');
|
|
tabbedContainer.className = 'fw-colour-picker-tabbed-container';
|
|
tabbed.appendChild(tabbedContainer);
|
|
onColourChange.push(function(colour) {
|
|
tabbedContainer.style.borderColor = hexFormat(colour);
|
|
});
|
|
|
|
var tabbedList = document.createElement('div');
|
|
tabbedList.className = 'fw-colour-picker-tabbed-list';
|
|
tabbed.appendChild(tabbedList);
|
|
|
|
var switchTab = function(id) {
|
|
if(activeTab == tabs[id])
|
|
return;
|
|
|
|
if(activeTab) {
|
|
activeTab.c.classList.remove('fw-colour-picker-tab-container-active');
|
|
activeTab.b.classList.remove('fw-colour-picker-tab-button-active');
|
|
activeTab.b.style.background = '';
|
|
activeTab.b.style.borderColor = '';
|
|
activeTab.b.style.color = '';
|
|
}
|
|
|
|
activeTab = tabs[id] || undefined;
|
|
|
|
if(activeTab) {
|
|
activeTab.c.classList.add('fw-colour-picker-tab-container-active');
|
|
activeTab.b.classList.add('fw-colour-picker-tab-button-active');
|
|
activeTab.b.style.background = hexFormat(colour);
|
|
activeTab.b.style.borderColor = hexFormat(colour);
|
|
activeTab.b.style.color = hexFormat(textColour(colour));
|
|
}
|
|
};
|
|
|
|
var createTab = function(id, name, construct) {
|
|
var tabContainer = construct();
|
|
tabContainer.className = 'fw-colour-picker-tab-container fw-colour-picker-tab-' + id + '-container';
|
|
tabbedContainer.appendChild(tabContainer);
|
|
|
|
var tabButton = document.createElement('input');
|
|
tabButton.type = 'button';
|
|
tabButton.value = name;
|
|
tabButton.className = 'fw-colour-picker-tab-button fw-colour-picker-tab-' + id + '-button';
|
|
tabButton.onclick = function() { switchTab(id); };
|
|
tabbedList.appendChild(tabButton);
|
|
|
|
tabs[id] = { c: tabContainer, b: tabButton };
|
|
|
|
if(activeTab === undefined) {
|
|
activeTab = tabs[id];
|
|
tabContainer.className += ' fw-colour-picker-tab-container-active';
|
|
tabButton.className += ' fw-colour-picker-tab-button-active';
|
|
}
|
|
};
|
|
|
|
if(options.showPresetsTab)
|
|
createTab('presets', 'Presets', function() {
|
|
var presets = options.presets;
|
|
var cont = document.createElement('div');
|
|
|
|
for(var i = 0; i < presets.length; ++i)
|
|
(function(preset) {
|
|
var option = document.createElement('a');
|
|
option.href = 'javascript:void(0);';
|
|
option.className = 'fw-colour-picker-presets-option';
|
|
option.style.background = hexFormat(preset.c);
|
|
option.title = preset.n;
|
|
option.onclick = function() {
|
|
setColour(preset.c);
|
|
};
|
|
onColourChange.push(function(value) {
|
|
option.classList[(value === preset.c ? 'add' : 'remove')]('fw-colour-picker-presets-option-active');
|
|
});
|
|
cont.appendChild(option);
|
|
})(presets[i]);
|
|
|
|
return cont;
|
|
});
|
|
|
|
if(options.showGridTab)
|
|
createTab('grid', 'Grid', function() {
|
|
var greys = [0xFFFFFF, 0xEBEBEB, 0xD6D6D6, 0xC2C2C2, 0xADADAD, 0x999999, 0x858585, 0x707070, 0x5C5C5C, 0x474747, 0x333333, 0];
|
|
var colours = [0x00A1D8, 0x0061FE, 0x4D22B2, 0x982ABC, 0xB92D5D, 0xFF4015, 0xFF6A00, 0xFFAB01, 0xFDC700, 0xFEFB41, 0xD9EC37, 0x76BB40];
|
|
var shades = [-.675, -.499, -.345, -.134, 0, .134, .345, .499, .675];
|
|
|
|
var cont = document.createElement('div');
|
|
|
|
for(var i = 0; i < greys.length; ++i)
|
|
(function(grey) {
|
|
var option = document.createElement('a');
|
|
option.href = 'javascript:void(0);';
|
|
option.className = 'fw-colour-picker-grid-option';
|
|
option.style.background = hexFormat(grey);
|
|
option.onclick = function() {
|
|
setColour(grey);
|
|
};
|
|
onColourChange.push(function(value) {
|
|
option.classList[(value === grey ? 'add' : 'remove')]('fw-colour-picker-grid-option-active');
|
|
});
|
|
cont.appendChild(option);
|
|
})(greys[i]);
|
|
|
|
for(var i = 0; i < shades.length; ++i)
|
|
for(var j = 0; j < colours.length; ++j)
|
|
(function(colour) {
|
|
var option = document.createElement('a');
|
|
option.href = 'javascript:void(0);';
|
|
option.className = 'fw-colour-picker-grid-option';
|
|
option.style.background = hexFormat(colour);
|
|
option.onclick = function() {
|
|
setColour(colour);
|
|
};
|
|
onColourChange.push(function(value) {
|
|
option.classList[(value === colour ? 'add' : 'remove')]('fw-colour-picker-grid-option-active');
|
|
});
|
|
cont.appendChild(option);
|
|
})(shadeColour(colours[j], shades[i]));
|
|
|
|
return cont;
|
|
});
|
|
|
|
if(options.showSlidersTab)
|
|
createTab('sliders', 'Sliders', function() {
|
|
var cont = document.createElement('div');
|
|
|
|
var addSlider = function(id, name, update, apply) {
|
|
var sCont = document.createElement('div');
|
|
sCont.className = 'fw-colour-picker-slider fw-colour-picker-slider-' + id;
|
|
cont.appendChild(sCont);
|
|
|
|
var sName = document.createElement('div');
|
|
sName.className = 'fw-colour-picker-slider-name';
|
|
sName.textContent = name;
|
|
sCont.appendChild(sName);
|
|
|
|
var sValue = document.createElement('div');
|
|
sValue.className = 'fw-colour-picker-slider-value';
|
|
sCont.appendChild(sValue);
|
|
|
|
var sValueSliderCont = document.createElement('div');
|
|
sValueSliderCont.className = 'fw-colour-picker-slider-value-slider-container';
|
|
sValue.appendChild(sValueSliderCont);
|
|
|
|
var sGradient = document.createElement('div');
|
|
sGradient.className = 'fw-colour-picker-slider-gradient';
|
|
sValueSliderCont.appendChild(sGradient);
|
|
|
|
var sValueSlider = document.createElement('input');
|
|
sValueSlider.type = 'range';
|
|
if(sValueSlider.type === 'range') {
|
|
sValueSlider.className = 'fw-colour-picker-slider-value-slider';
|
|
sValueSlider.min = '0';
|
|
sValueSlider.max = '255';
|
|
sValueSlider.onchange = function() {
|
|
setColour(apply(colour, sValueSlider.value));
|
|
};
|
|
sValueSliderCont.appendChild(sValueSlider);
|
|
} else
|
|
sValueSlider = undefined;
|
|
|
|
var sValueInputContainer = document.createElement('div');
|
|
sValueInputContainer.className = 'fw-colour-picker-slider-value-input-container';
|
|
sValue.appendChild(sValueInputContainer);
|
|
|
|
var sValueInput = document.createElement('input');
|
|
sValueInput.className = 'fw-colour-picker-slider-value-input';
|
|
sValueInput.type = 'number';
|
|
sValueInput.min = '0';
|
|
sValueInput.max = '255';
|
|
sValueInput.onchange = function() {
|
|
setColour(apply(colour, sValueInput.value));
|
|
};
|
|
sValueInputContainer.appendChild(sValueInput);
|
|
|
|
sGradient.onmousedown = function(ev) {
|
|
if(ev.button === 0)
|
|
setColour(apply(colour, Math.floor(255 * (Math.max(0, Math.min(200, (ev.layerX - 5))) / 200))));
|
|
};
|
|
|
|
onColourChange.push(function(colour) {
|
|
if(sValueSlider)
|
|
sValueSlider.value = update(colour);
|
|
sValueInput.value = update(colour);
|
|
|
|
var gradient = 'linear-gradient(to right, ' + hexFormat(apply(colour, 0)) + ', ' + hexFormat(apply(colour, 0xFF)) + ')';
|
|
sGradient.style.background = '';
|
|
sGradient.style.background = gradient;
|
|
if(!sGradient.style.background)
|
|
sGradient.style.background = '-moz-' + gradient;
|
|
if(!sGradient.style.background)
|
|
sGradient.style.background = '-webkit-' + gradient;
|
|
});
|
|
};
|
|
|
|
addSlider('red', 'Red',
|
|
function(value) { return (value >> 16) & 0xFF; },
|
|
function(colour, value) { return (colour & 0xFFFF) | (Math.min(255, Math.max(0, value)) << 16); }
|
|
);
|
|
addSlider('green', 'Green',
|
|
function(value) { return (value >> 8) & 0xFF; },
|
|
function(colour, value) { return (colour & 0xFF00FF) | (Math.min(255, Math.max(0, value)) << 8); }
|
|
);
|
|
addSlider('blue', 'Blue',
|
|
function(value) { return value & 0xFF; },
|
|
function(colour, value) { return (colour & 0xFFFF00) | Math.min(255, Math.max(0, value)); }
|
|
);
|
|
|
|
return cont;
|
|
});
|
|
|
|
if(activeTab) {
|
|
height += 261;
|
|
form.appendChild(tabbed);
|
|
}
|
|
|
|
var middleRow = document.createElement('div');
|
|
middleRow.className = 'fw-colour-picker-middle-row';
|
|
form.appendChild(middleRow);
|
|
|
|
var colourPreviewContainer = document.createElement('div');
|
|
colourPreviewContainer.className = 'fw-colour-picker-colour-preview-container';
|
|
middleRow.appendChild(colourPreviewContainer);
|
|
|
|
var colourPreview = document.createElement('div');
|
|
colourPreview.className = 'fw-colour-picker-colour-preview';
|
|
colourPreviewContainer.appendChild(colourPreview);
|
|
onColourChange.push(function(colour) {
|
|
colourPreview.style.background = hexFormat(colour);
|
|
});
|
|
|
|
var values = {};
|
|
var valuesContainer = document.createElement('div');
|
|
valuesContainer.className = 'fw-colour-picker-values-container';
|
|
middleRow.appendChild(valuesContainer);
|
|
|
|
var addValue = function(id, name, type, format, change) {
|
|
var valueContainer = document.createElement('label');
|
|
valueContainer.className = 'fw-colour-picker-values-child fw-colour-picker-' + id + '-value';
|
|
valuesContainer.appendChild(valueContainer);
|
|
|
|
var valueLabel = document.createElement('div');
|
|
valueLabel.textContent = name;
|
|
valueLabel.className = 'fw-colour-picker-values-child-label fw-colour-picker-' + id + '-value-label';
|
|
valueContainer.appendChild(valueLabel);
|
|
|
|
var valueInput = document.createElement('input');
|
|
valueInput.type = type;
|
|
valueInput.value = '0';
|
|
valueInput.className = 'fw-colour-picker-values-child-input fw-colour-picker-' + id + '-value-input';
|
|
valueInput.onchange = function() {
|
|
change(valueInput.value);
|
|
};
|
|
valueContainer.appendChild(valueInput);
|
|
|
|
onColourChange.push(function(colour) {
|
|
valueInput.value = format(colour);
|
|
});
|
|
|
|
values[id] = { c: valueContainer, l: valueLabel, i: valueInput };
|
|
};
|
|
|
|
if(options.showHexValue)
|
|
addValue('hex', 'Hex', 'text', function(value) {
|
|
return hexFormat(value);
|
|
}, function(value) {
|
|
while(value.substring(0, 1) === '#')
|
|
value = value.substring(1);
|
|
value = value.substring(0, 6);
|
|
|
|
if(value.length === 3)
|
|
value = value.substring(0, 1) + value.substring(0, 1)
|
|
+ value.substring(1, 2) + value.substring(1, 2)
|
|
+ value.substring(2, 3) + value.substring(2, 3);
|
|
|
|
if(value.length === 6)
|
|
setColour(parseInt(value, 16));
|
|
});
|
|
|
|
if(options.showRawValue)
|
|
addValue('raw', 'Raw', 'number', function(value) {
|
|
return value;
|
|
}, function(value) {
|
|
setColour(Math.min(0xFFFFFF, Math.max(0, parseInt(value))));
|
|
});
|
|
|
|
var buttons = {};
|
|
var buttonsContainer = document.createElement('div');
|
|
buttonsContainer.className = 'fw-colour-picker-buttons-container';
|
|
form.appendChild(buttonsContainer);
|
|
|
|
var buttonsContainerInner = document.createElement('div');
|
|
buttonsContainerInner.className = 'fw-colour-picker-buttons-container-inner';
|
|
buttonsContainer.appendChild(buttonsContainerInner);
|
|
|
|
var addButton = function(id, name, action) {
|
|
var button = document.createElement('input');
|
|
button.className = 'fw-colour-picker-buttons-button fw-colour-picker-buttons-' + id + '-button';
|
|
if(action === null) {
|
|
button.className += ' fw-colour-picker-buttons-button-submit';
|
|
onColourChange.push(function(colour, text) {
|
|
button.style.background = hexFormat(colour);
|
|
button.style.color = hexFormat(text);
|
|
});
|
|
button.type = 'submit';
|
|
} else {
|
|
button.onclick = function() { action(); };
|
|
button.type = 'button';
|
|
}
|
|
button.value = name;
|
|
buttonsContainerInner.appendChild(button);
|
|
buttons[id] = { b: button };
|
|
};
|
|
|
|
addButton('cancel', 'Cancel', cancel);
|
|
addButton('apply', 'Apply', null);
|
|
|
|
var setPosition = function(x, y) {
|
|
container.style.top = y >= 0 ? (y.toString() + 'px') : '';
|
|
container.style.left = x >= 0 ? (x.toString() + 'px') : '';
|
|
};
|
|
|
|
setPosition(options.posX, options.posY);
|
|
runOnColourChange();
|
|
|
|
var appendTo = function(parent) {
|
|
parent.appendChild(container);
|
|
};
|
|
|
|
var pub = {
|
|
getWidth: function() { return 290; },
|
|
getHeight: function() { return height; },
|
|
getColour: function() { return colour; },
|
|
getContainer: function() { return container; },
|
|
setColour: setColour,
|
|
appendTo: appendTo,
|
|
setPosition: setPosition,
|
|
switchTab: switchTab,
|
|
close: close,
|
|
suggestPosition: function(mouseEvent) {
|
|
var x = 10, y = 10;
|
|
|
|
if(document.body.clientWidth > 340) {
|
|
x = mouseEvent.clientX;
|
|
y = mouseEvent.clientY;
|
|
|
|
var height = pub.getHeight(),
|
|
width = pub.getWidth();
|
|
|
|
if(y > height + 20)
|
|
y -= height;
|
|
if(x > document.body.clientWidth - width - 20)
|
|
x -= width;
|
|
}
|
|
|
|
return {
|
|
x: x,
|
|
y: y,
|
|
};
|
|
},
|
|
};
|
|
|
|
return pub;
|
|
};
|
|
FwColourPicker.hexFormat = function(raw) {
|
|
var str = raw.toString(16).substring(0, 6);
|
|
if(str.length < 6)
|
|
str = '000000'.substring(str.length) + str;
|
|
return '#' + str;
|
|
};
|