This file covers the colors demo. Most of it is pretty straightforward.
The most interesting thing is adding the analogs which requires very little code, even though it is automatically wired up with all of the dynamic events, because it is wired to the data model.
run when the document is ready
$(function() {
main data structure that all of the ui is based on we have three concrete values, and three derrived values
var color = {
r: propagate(160),
g: propagate(90),
b: propagate(45),
h: propagate(function() {
return hsvByte(0);
}),
s: propagate(function() {
return hsvByte(1);
}),
v: propagate(function() {
return hsvByte(2);
})
};
activate HSV propagation!
$('hsv'.split('')).each(function(i, c) {
color[c]();
});
convienence for returning a 0-255 normalized version of HSV (0.0 - 1.0)
function hsvByte(index) {
return Math.floor($.Color(colorString()).toHSV()[index] * 255);
}
takes a byte and makes sure it is between 0 - 255 (no overages or <0)
function byteConstrain(input) {
return Math.max(Math.min(input, 255), 0);
}
return a 00-FF hex representation of a byte
function toOctet(n) {
var str = (n % 256).toString(16);
if (str.length < 2) {
str = "0" + str;
}
return str;
}
return the RGB hex code of a color.
function colorString() {
return "#" + toOctet(color.r()) + toOctet(color.g()) + toOctet(color.b());
}
function that will keep the main color panel up to date
var updateColor = propagate(function() {
$('#combined').css({
backgroundColor: colorString()
});
$('#combined-hex').html(colorString());
});
note: you probably don't want to go creating your HTML like this =) generate some buttons for user input
function createControl(space, channel, num, valence) {
return $('<input class="controller" type="submit" data-channel="' + num + '" data-valence="' + valence + '" value="' + channel + ': ' + valence + '16" data-space="' + space + '"/>');
}
generate live controls for rgb, hsv
$(['RGB', 'HSV']).each(function(index, space) {
var spacediv = $('<div>' + space + '</div>');
$(space.split('')).each(function(num, channel) {
var plus = createControl(space, channel, num, '+');
var minus = createControl(space, channel, num, '-');
var div = $('<div/>');
div.append(plus).append(minus).append($('<span> current ' + channel + ' value: <span class="' + channel.toLowerCase() + '-int"/></span></span>'));
spacediv.append(div);
});
$('#controls').append(spacediv);
});
set up click handlers for the controls
$('#controls .controller').live('click', function() {
retrieve values from the dom
var space = $(this).attr('data-space');
var chan = $(this).attr('data-channel');
var valence = $(this).attr('data-valence');
var delta = parseInt(valence + (16).toString(), 10);
initials the vars that both RGB and HSV use
var current, channel, fn, newValue, modificationArray;
console.log("Color space %s, channel %s, modifying %s8", space, chan, valence);
we have different approaches for updating if we are in RGB or HSV
if (space == "RGB") {
convert from index to name
channel = 'rgb'.charAt(chan);
get the accessor
fn = color[channel];
get the current value
current = fn();
newValue = byteConstrain(current + delta);
call the setter with the new val,
fn(newValue);
} else {
We have to deal with HSV
hsv = $.Color(colorString()).toHSV();
current = 255.0 * hsv[chan];
newValue = current + delta;
modificationArray = [null, null, null];
modificationArray[chan] = byteConstrain(newValue) / 255.0;
hsv = hsv.modify(modificationArray);
color.r(hsv.red());
color.g(hsv.green());
color.b(hsv.blue());
}
cancel default click handler
return false;
});
make it so that our labels will update automagically! i can add these in whatever module I'd like no worrying about callback registration or anything.
$('rgbhsv'.split('')).each(function(num, channel) {
propagate(function() {
var myInt = color[channel]();
$('.' + channel + '-int').html(myInt);
})();
});
stylize color swatches for rgb
construct hex that has the info for just the current color channel and 00 for the other channels
$('rgb'.split('')).each(function(num, channel) {
propagate(function() {
var bg = '#';
var myInt = color[channel]();
var myHex = toOctet(myInt);
$([0, 1, 2]).each(function(index, value) {
if (num == value) {
bg = bg + myHex;
} else {
bg = bg + "00";
}
});
$('#' + channel).css({
backgroundColor: bg
});
})();
});
updateColor();
function that will keep a complementary color up to date
var updateComplementary = propagate(function() {
var complementary = $.Color(colorString()).complementary().toHEX();
$('#complementary-hex').html(complementary);
$('#complementary').css({
backgroundColor: complementary
});
});
updateComplementary();
note that it doesnt have to worry about registering for rgb callbacks (that dependency is detected from colorString()) also note that the callback for the event handler just sets values this way you just code the relationships between events and data and then the relationships between data and the view you keep these two things separate so it is nice and clean. this also lets you add other parts to the view without changing the event handling code
var analogOffsets = {
1: propagate(30 / 360.0),
2: propagate(330 / 360.0)
};
set the swatch and the label appropriately
var updateAnalogous = propagate(function() {
$([1, 2]).each(function(i, value) {
var currentValue = parseFloat(analogOffsets[value]());
var analog = $.Color(colorString()).analogous(currentValue).toHEX();
$('#analog' + value.toString() + '-hex').html(analog);
$('#analog' + value.toString()).css({
backgroundColor: analog
});
});
});
updateAnalogous();
set up analog binding to selectors
function updateAnalogOffsetsFromSelects() {
analogOffsets[1]($('#analog1-selector').val());
analogOffsets[2]($('#analog2-selector').val());
}
$('.analog-selector').live('change', updateAnalogOffsetsFromSelects);
updateAnalogOffsetsFromSelects();
and we're done!
});