brand new javascript frontend with a lot of fancy features!

This commit is contained in:
Steffen Vogel 2013-02-19 21:55:34 +00:00
parent c650c322ab
commit 52cde395e9
24 changed files with 771 additions and 597 deletions

View file

@ -1,51 +0,0 @@
/**
* Farbtastic Color Picker 1.2
* © 2008 Steven Wittens
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.farbtastic {
position: relative;
}
.farbtastic * {
position: absolute;
cursor: crosshair;
}
.farbtastic, .farbtastic .wheel {
width: 195px;
height: 195px;
}
.farbtastic .color, .farbtastic .overlay {
top: 47px;
left: 47px;
width: 101px;
height: 101px;
}
.farbtastic .wheel {
background: url(../img/wheel.png) no-repeat;
width: 195px;
height: 195px;
}
.farbtastic .overlay {
background: url(../img/mask.png) no-repeat;
}
.farbtastic .marker {
width: 17px;
height: 17px;
margin: -8px 0 0 -8px;
overflow: hidden;
background: url(../img/marker.png) no-repeat;
}

View file

@ -1,34 +0,0 @@
body {
font-family: sans-serif;
}
#wrapper {
width: 295px;
text-align: center;
margin: 40px auto;
}
#picker {
padding-left: 50px;
}
#details {
border: 1px solid grey;
padding: 3px;
font-size: 0.85em;
}
#details input {
border: 1px solid grey;
margin: 1px;
}
#mask input {
margin: 2px;
}
#show_details {
font-weight: bold;
margin: 30px 0 4px;
cursor: pointer;
}

View file

@ -1,29 +0,0 @@
<?php
$count = 10;
$params = array(
'color' => (isset($_REQUEST['color'])) ? substr($_REQUEST['color'], 1) : 'ffffff',
'delay' => (isset($_REQUEST['delay'])) ? $_REQUEST['delay'] : 0,
'step' => (isset($_REQUEST['step'])) ? $_REQUEST['step'] : 255,
'mask' => (isset($_REQUEST['mask'])) ? $_REQUEST['mask'] : str_repeat('1', $count),
'port' => '/dev/ttyUSB0'
);
$cmd = '../../src/fnctl fade';
foreach ($params as $param => $value) {
$cmd .= ' --' . $param . '=' . $value;
}
$return;
$output = passthru($cmd, $return);
$json = array(
'code' => $return,
'output' => $output,
'cmd' => $cmd,
'params' => $params
);
header('Content-type: application/json');
echo json_encode($json);
?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,134 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>fnordlichts</title>
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="js/farbtastic.js"></script>
<link rel="stylesheet" href="css/farbtastic.css" type="text/css" />
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" charset="utf-8">
var locked = false;
var count; /* count of lamps */
var color; /* last color */
function stop() {
if (!locked) {
locked = true;
$.ajax({
type: 'POST',
url: 'stop',
success: function(data) {
locked = false;
}
});
}
}
function start(script) {
var mask = get_mask();
var data = {
script : script
};
if (mask.indexOf('0') != -1) {
data.mask = mask;
}
if (!locked) {
locked = true;
$.ajax({
type: 'POST',
url: 'start?' + $.param(data),
success: function(data) {
locked = false;
}
});
}
}
function get_mask() {
var mask = new String;
for (var i=0; i < count; i++) {
mask += ($('#lamp_' + i).attr('checked')) ? '1' : '0';
}
return mask;
}
function fade(hex) {
color = (hex == undefined) ? $.farbtastic('#picker').color.substr(1) : hex.substr(1);
var mask = get_mask();
var data = {
color: color,
step: $('#step').val(),
delay: $('#delay').val(),
}
if (mask.indexOf('0') != -1) {
data.mask = mask;
}
if (!locked) {
locked = true;
$.ajax({
type: 'POST',
url: 'fade?' + $.param(data),
success: function(data) {
locked = false;
}
});
}
}
$(document).ready(function() {
/* draw mask boxes */
$.get('count', function(data) {
count = parseInt(data);
for (var i=0; i < count; i++) {
$('#mask').append(
$('<input>')
.attr('type', 'checkbox')
.attr('id', 'lamp_' + i)
.attr('checked', true)
);
}
});
/* get current color */
$.get('color', function(data) {
color = data;
$.farbtastic('#picker').setColor('#' + color);
});
$('#picker').farbtastic(fade);
//$('input').bind('click change keypress', fade);
$('#details').hide();
$('#show_details').click(function() { $('#details').slideToggle(); });
});
</script>
</head>
<body>
<div id="wrapper">
<div id="picker"></div>
<p id="show_details">more..</p>
<div id="details">
<table>
<tr><td><label>Verz&ouml;gerung</label></td><td><input type="text" id="delay" value="3"/></td></tr>
<tr><td><label>Schrittweite</label></td><td><input type="text" id="step" value="20" /></td></tr>
<tr><td><label>Lampen</label></td><td><div id="mask" /></td></tr>
<tr><td></td><td>
<input type="button" onclick="stop()" value="Stop" />
<input type="button" onclick="start(1)" value="Party" />
<input type="button" onclick="start(0)" value="Farbrad" />
</td></tr>
</table>
</div>
</div>
</form>
</body>
</html>

View file

@ -1,345 +0,0 @@
/**
* Farbtastic Color Picker 1.2
* © 2008 Steven Wittens
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
jQuery.fn.farbtastic = function (callback) {
$.farbtastic(this, callback);
return this;
};
jQuery.farbtastic = function (container, callback) {
var container = $(container).get(0);
return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback));
}
jQuery._farbtastic = function (container, callback) {
// Store farbtastic object
var fb = this;
// Insert markup
$(container).html('<div class="farbtastic"><div class="color"></div><div class="wheel"></div><div class="overlay"></div><div class="h-marker marker"></div><div class="sl-marker marker"></div></div>');
var e = $('.farbtastic', container);
fb.wheel = $('.wheel', container).get(0);
// Dimensions
fb.radius = 84;
fb.square = 100;
fb.width = 194;
// Fix background PNGs in IE6
if (navigator.appVersion.match(/MSIE [0-6]\./)) {
$('*', e).each(function () {
if (this.currentStyle.backgroundImage != 'none') {
var image = this.currentStyle.backgroundImage;
image = this.currentStyle.backgroundImage.substring(5, image.length - 2);
$(this).css({
'backgroundImage': 'none',
'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
});
}
});
}
/**
* Link to the given element(s) or callback.
*/
fb.linkTo = function (callback) {
// Unbind previous nodes
if (typeof fb.callback == 'object') {
$(fb.callback).unbind('keyup', fb.updateValue);
}
// Reset color
fb.color = null;
// Bind callback or elements
if (typeof callback == 'function') {
fb.callback = callback;
}
else if (typeof callback == 'object' || typeof callback == 'string') {
fb.callback = $(callback);
fb.callback.bind('keyup', fb.updateValue);
if (fb.callback.get(0).value) {
fb.setColor(fb.callback.get(0).value);
}
}
return this;
}
fb.updateValue = function (event) {
if (this.value && this.value != fb.color) {
fb.setColor(this.value);
}
}
/**
* Change color with HTML syntax #123456
*/
fb.setColor = function (color) {
var unpack = fb.unpack(color);
if (fb.color != color && unpack) {
fb.color = color;
fb.rgb = unpack;
fb.hsl = fb.RGBToHSL(fb.rgb);
fb.updateDisplay();
}
return this;
}
/**
* Change color with HSL triplet [0..1, 0..1, 0..1]
*/
fb.setHSL = function (hsl) {
fb.hsl = hsl;
fb.rgb = fb.HSLToRGB(hsl);
fb.color = fb.pack(fb.rgb);
fb.updateDisplay();
return this;
}
/////////////////////////////////////////////////////
/**
* Retrieve the coordinates of the given event relative to the center
* of the widget.
*/
fb.widgetCoords = function (event) {
var x, y;
var el = event.target || event.srcElement;
var reference = fb.wheel;
if (typeof event.offsetX != 'undefined') {
// Use offset coordinates and find common offsetParent
var pos = { x: event.offsetX, y: event.offsetY };
// Send the coordinates upwards through the offsetParent chain.
var e = el;
while (e) {
e.mouseX = pos.x;
e.mouseY = pos.y;
pos.x += e.offsetLeft;
pos.y += e.offsetTop;
e = e.offsetParent;
}
// Look for the coordinates starting from the wheel widget.
var e = reference;
var offset = { x: 0, y: 0 }
while (e) {
if (typeof e.mouseX != 'undefined') {
x = e.mouseX - offset.x;
y = e.mouseY - offset.y;
break;
}
offset.x += e.offsetLeft;
offset.y += e.offsetTop;
e = e.offsetParent;
}
// Reset stored coordinates
e = el;
while (e) {
e.mouseX = undefined;
e.mouseY = undefined;
e = e.offsetParent;
}
}
else {
// Use absolute coordinates
var pos = fb.absolutePosition(reference);
x = (event.pageX || 0*(event.clientX + $('html').get(0).scrollLeft)) - pos.x;
y = (event.pageY || 0*(event.clientY + $('html').get(0).scrollTop)) - pos.y;
}
// Subtract distance to middle
return { x: x - fb.width / 2, y: y - fb.width / 2 };
}
/**
* Mousedown handler
*/
fb.mousedown = function (event) {
// Capture mouse
if (!document.dragging) {
$(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup);
document.dragging = true;
}
// Check which area is being dragged
var pos = fb.widgetCoords(event);
fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square;
// Process
fb.mousemove(event);
return false;
}
/**
* Mousemove handler
*/
fb.mousemove = function (event) {
// Get coordinates relative to color picker center
var pos = fb.widgetCoords(event);
// Set new HSL parameters
if (fb.circleDrag) {
var hue = Math.atan2(pos.x, -pos.y) / 6.28;
if (hue < 0) hue += 1;
fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]);
}
else {
var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5));
var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5));
fb.setHSL([fb.hsl[0], sat, lum]);
}
return false;
}
/**
* Mouseup handler
*/
fb.mouseup = function () {
// Uncapture mouse
$(document).unbind('mousemove', fb.mousemove);
$(document).unbind('mouseup', fb.mouseup);
document.dragging = false;
}
/**
* Update the markers and styles
*/
fb.updateDisplay = function () {
// Markers
var angle = fb.hsl[0] * 6.28;
$('.h-marker', e).css({
left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px',
top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px'
});
$('.sl-marker', e).css({
left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px',
top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px'
});
// Saturation/Luminance gradient
$('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5])));
// Linked elements or callback
if (typeof fb.callback == 'object') {
// Set background/foreground color
$(fb.callback).css({
backgroundColor: fb.color,
color: fb.hsl[2] > 0.5 ? '#000' : '#fff'
});
// Change linked value
$(fb.callback).each(function() {
if (this.value && this.value != fb.color) {
this.value = fb.color;
}
});
}
else if (typeof fb.callback == 'function') {
fb.callback.call(fb, fb.color);
}
}
/**
* Get absolute position of element
*/
fb.absolutePosition = function (el) {
var r = { x: el.offsetLeft, y: el.offsetTop };
// Resolve relative to offsetParent
if (el.offsetParent) {
var tmp = fb.absolutePosition(el.offsetParent);
r.x += tmp.x;
r.y += tmp.y;
}
return r;
};
/* Various color utility functions */
fb.pack = function (rgb) {
var r = Math.round(rgb[0] * 255);
var g = Math.round(rgb[1] * 255);
var b = Math.round(rgb[2] * 255);
return '#' + (r < 16 ? '0' : '') + r.toString(16) +
(g < 16 ? '0' : '') + g.toString(16) +
(b < 16 ? '0' : '') + b.toString(16);
}
fb.unpack = function (color) {
if (color.length == 7) {
return [parseInt('0x' + color.substring(1, 3)) / 255,
parseInt('0x' + color.substring(3, 5)) / 255,
parseInt('0x' + color.substring(5, 7)) / 255];
}
else if (color.length == 4) {
return [parseInt('0x' + color.substring(1, 2)) / 15,
parseInt('0x' + color.substring(2, 3)) / 15,
parseInt('0x' + color.substring(3, 4)) / 15];
}
}
fb.HSLToRGB = function (hsl) {
var m1, m2, r, g, b;
var h = hsl[0], s = hsl[1], l = hsl[2];
m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s;
m1 = l * 2 - m2;
return [this.hueToRGB(m1, m2, h+0.33333),
this.hueToRGB(m1, m2, h),
this.hueToRGB(m1, m2, h-0.33333)];
}
fb.hueToRGB = function (m1, m2, h) {
h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
if (h * 2 < 1) return m2;
if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
return m1;
}
fb.RGBToHSL = function (rgb) {
var min, max, delta, h, s, l;
var r = rgb[0], g = rgb[1], b = rgb[2];
min = Math.min(r, Math.min(g, b));
max = Math.max(r, Math.max(g, b));
delta = max - min;
l = (min + max) / 2;
s = 0;
if (l > 0 && l < 1) {
s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
}
h = 0;
if (delta > 0) {
if (max == r && max != g) h += (g - b) / delta;
if (max == g && max != b) h += (2 + (b - r) / delta);
if (max == b && max != r) h += (4 + (r - g) / delta);
h /= 6;
}
return [h, s, l];
}
// Install mousedown handler (the others are set on the document on-demand)
$('*', e).mousedown(fb.mousedown);
// Init color
fb.setColor('#000000');
// Set linked elements/callback
if (callback) {
fb.linkTo(callback);
}
}

File diff suppressed because one or more lines are too long

103
web/css/style.css Normal file
View file

@ -0,0 +1,103 @@
/**
* fnordlicht web control frontend
*
* simple ajax colorwheel frontend to control fnordlicht's
*
* @copyright 2013 Steffen Vogel
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <post@steffenvogel.de>
* @link http://www.steffenvogel.de
*/
/*
* This file is part of libfn
*
* libfn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* libfn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libfn. If not, see <http://www.gnu.org/licenses/>.
*/
body {
font-family: sans-serif;
text-align: center;
margin: 40px;
background-color: black;
}
#wrapper {
width: 600px;
margin: 0 auto;
}
#details {
padding: 10px;
margin: 0 50px;
font-size: 0.95em;
display: none;
text-align: left;
-moz-border-radius: 15px;
border-radius: 15px;
}
#details input {
-moz-border-radius: 4px;
border-radius: 4px;
border: 1px solid #727272;
padding: 3px;
text-align: center;
}
#details > table {
margin: 4px auto;
width: 100%;
border-bottom: 1px solid #727272;
}
#details > p {
margin: 0;
}
#details label {
font-weight: bold;
}
img.icon {
margin: 3px 3px;
width: 32px;
height: 32px;
}
#show_details {
font-weight: bold;
color: #C0C0C0;
margin: 30px 0 4px;
cursor: pointer;
}
#wheel {
text-align: left;
margin: 50px;
}
.alpha60 {
/* Fallback for web browsers that doesn't support RGBa */
background: rgb(255, 255, 255);
/* RGBa with 0.6 opacity */
background: rgba(255, 255, 255, 0.6);
/* For IE 5.5 - 7*/
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99ffffff, endColorstr=#99ffffff);
/* For IE 8*/
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99ffffff, endColorstr=#99ffffff)";
}

BIN
web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web/img/bulb-off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
web/img/bulb-on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
web/img/lego1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
web/img/lego2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
web/img/lego3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
web/img/lego4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
web/img/lego5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
web/img/lego6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

45
web/index.html Normal file
View file

@ -0,0 +1,45 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>fnordlichts</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="icon" href="favicon.png" type="image/png">
<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="js/raphael-2.1.0.min.js"></script>
<script type="text/javascript" src="js/colorwheel.js"></script>
<script type="text/javascript" src="js/frontend.js"></script>
<link rel="stylesheet" href="css/style.css" type="text/css" />
</head>
<body>
<div id="wrapper">
<div id="wheel"></div>
<p id="show_details">more...</p>
<div id="details" class="alpha60">
<table>
<tr><td><label>Lampen</label></td><td><div id="mask" /></td></tr>
<tr><td><label>Benutzer</label></td><td><div id="users" /></td></tr>
</table>
<table id="options">
<tr><td><label>Verz&ouml;gerung</label></td><td><input type="text" id="delay" value="5" size="3" /> (0-255, in 10ms Schritten)</td></tr>
<tr><td><label>Schrittweite</label></td><td><input type="text" id="step" value="8" size="3" maxlength="3" /> (0-255)</td></tr>
<tr><td><label>Pause</label></td><td><input type="text" id="sleep" value="10" size="3" maxlength="3" /> (0-255, in 1s Schritten)</td></tr>
<tr><td><label>Helligkeit</label></td><td><input type="text" id="value" value="255" size="3" maxlength="3" /> (0-255)</td></tr>
<tr><td><label>S&auml;ttigung</label></td><td><input type="text" id="saturation" value="255" size="3" maxlength="3" /> (0-255)</td></tr>
<tr><td><label>Benutze Adresse</label></td><td><input type="checkbox" id="use_address" checked="checked" /></td></tr>
<tr><td><label>Warte auf Fade</label></td><td><input type="checkbox" id="wait_for_fade" value="1" checked="checked" /></td></tr>
<tr><td><label>Stoppe vor Fade</label></td><td><input type="checkbox" id="stop_fade" value="1" /></td></tr>
</table>
<p>
<input type="button" onclick="fnFade(Raphael.getRGB('#000000'))" value="Aus" />
<input type="button" onclick="fnFade(Raphael.getRGB('#ffffff'))" value="An" />
<input type="button" onclick="fnStop()" value="Stop" />
<input type="button" onclick="fnStart(1)" value="Party" />
<input type="button" onclick="fnStart(0)" value="Farbrad" />
</p>
</div>
</div>
</form>
</body>
</html>

317
web/js/colorwheel.js Normal file
View file

@ -0,0 +1,317 @@
/*
* Colorwheel
* Copyright (c) 2010 John Weir (http://famedriver.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*
* requires jQuery & Raphael
* http://jquery.com http://raphaeljs.com
*
* see http://jweir.github.com/colorwheel for Usage
*
*/
Raphael.colorwheel = function(target, color_wheel_size, no_segments){
var canvas,
current_color,
size,
segments = no_segments || 60,
bs_square = {},
hue_ring = {},
tri_size,
cursor = {},
drag_target,
input_target,
center,
parent,
change_callback,
drag_callbacks = [function(){}, function(){}],
offset,
padding = 2,
sdim; // holds the dimensions for the saturation square
function point(x, y){ return {x:x, y:y};}
function radians(a){ return a * (Math.PI/180);}
function angle(x,y){
var q = x > 0 ? 0 : 180;
return q+Math.atan((0 - y)/(0 - x))*180/(Math.PI);
}
function create(target, color_wheel_size){
size = color_wheel_size;
tri_size = size/20;
center = size/2;
parent = $(target);
canvas = Raphael(parent[0],size, size);
canvas.safari();
create_bs_square();
create_hue_ring();
hue_ring.cursor = cursor_create(tri_size);
bs_square.cursor = cursor_create(tri_size*0.5);
events_setup();
parent.css({height:size+"px", width:size+"px"});
disable_select(parent);
return public_methods();
}
function disable_select(target){
$(target).css({"unselectable": "on","-moz-user-select": "none","-webkit-user-select": "none"});
}
function public_methods(){
return {
input: input,
onchange: onchange,
ondrag : ondrag,
color : public_set_color
};
}
// Sets a textfield for user input of hex color values
// TODO don't clear the change callback
// TODO allow a null target to unbind the input
function input(target){
change_callback = null;
input_target = target;
$(target).keyup(function(){
if(this.value.match(/^#([0-9A-F]){3}$|^#([0-9A-F]){6}$/img)){
set_color(this.value);
update_color(true);
run_onchange_event();
}
});
set_color(target.value);
update_color(true);
return public_methods();
}
function onchange(callback){
change_callback = callback;
update_color(false);
return public_methods();
}
function ondrag(start_callback, end_callback){
drag_callbacks = [start_callback || function(){}, end_callback || function(){}];
return public_methods();
}
function drag(e){
var x, y, page;
e.preventDefault(); // prevents scrolling on touch
page = e.originalEvent.touches ? e.originalEvent.touches[0] : e;
x = page.pageX - (parent.offset().left + center);
y = page.pageY - (parent.offset().top + center);
if(drag_target == hue_ring){
set_hue_cursor(x,y);
update_color();
run_onchange_event();
return true;
}
if(drag_target == bs_square){
set_bs_cursor(x,y);
update_color();
run_onchange_event();
return true;
}
}
function start_drag(event, target){
event.preventDefault(); // prevents scrolling on touch
$(document).on('mouseup touchend',stop_drag);
$(document).on('mousemove touchmove',drag);
drag_target = target;
drag(event);
drag_callbacks[0](current_color);
}
function stop_drag(event){
event.preventDefault(); // prevents scrolling on touch
$(document).off("mouseup touchend",stop_drag);
$(document).off("mousemove touchmove",drag);
drag_callbacks[1](current_color);
run_onchange_event();
}
function events_setup(){
$([hue_ring.event.node,hue_ring.cursor[0].node]).on("mousedown touchstart",
function(e){start_drag(e,hue_ring);});
$([bs_square.b.node, bs_square.cursor[0].node]).on("mousedown touchstart",
function(e){start_drag(e,bs_square);});
}
function cursor_create(size){
var set = canvas.set().push(
canvas.circle(0, 0, size).attr({"stroke-width":4, stroke:"#333"}),
canvas.circle(0, 0, size+2).attr({"stroke-width":1, stroke:"#FFF", opacity:0.5})
);
set[0].node.style.cursor = "crosshair";
return set;
}
function set_bs_cursor(x,y){
x = x+center;
y = y+center;
if(x < sdim.x){x = sdim.x}
if(x > sdim.x+sdim.l){x = sdim.x+sdim.l}
if(y < sdim.y){y = sdim.y}
if(y > sdim.y+sdim.l){y = sdim.y + sdim.l}
bs_square.cursor.attr({cx:x, cy:y}).transform("t0,0");
}
function set_hue(color){
var hex = Raphael.getRGB(color).hex;
bs_square.h.attr("fill", hex);
}
function hue(){
return Raphael.rgb2hsb(bs_square.h.attr("fill")).h;
}
function public_set_color(value){
var ret = set_color(value);
update_color(false);
return ret;
}
function set_color(value){
if(value === undefined){ return current_color; }
var temp = canvas.rect(1,1,1,1).attr({fill:value}),
hsb = canvas.raphael.rgb2hsb(temp.attr("fill"));
set_bs_cursor(
(0-sdim.l/2) + (sdim.l*hsb.s),
sdim.l/2 - (sdim.l*hsb.b));
set_hue_cursor((360*(hsb.h))-90);
temp.remove();
return public_methods();
}
// Could optimize this method
function update_color(dont_replace_input_value){
var x = bs_square.cursor.items[0].attr("cx"),
y = bs_square.cursor.items[0].attr("cy"),
hsb = {
b: 1-(y-sdim.y)/sdim.l,
s: (x-sdim.x)/sdim.l,
h: hue()
};
current_color = Raphael.hsb2rgb(hsb.h, hsb.s,hsb.b);
if(input_target){
var c = current_color.hex;
if(dont_replace_input_value !== true) { input_target.value = c;}
if(hsb.b < 0.5){
$(input_target).css("color", "#FFF");
} else {
$(input_target).css("color", "#000");
}
input_target.style.background = c;
}
}
// accepts either x,y or d (degrees)
function set_hue_cursor(mixed_args){
var d;
if(arguments.length == 2){
d = angle(arguments[0],arguments[1]);
} else {
d = arguments[0];
}
var x = Math.cos(radians(d)) * (center-tri_size-padding);
var y = Math.sin(radians(d)) * (center-tri_size-padding);
hue_ring.cursor.attr({cx:x+center, cy:y+center}).transform("t0,0");
set_hue("hsb("+(d+90)/360+",1,1)");
}
function bs_square_dim(){
if(sdim){ return sdim;}
var s = size - (tri_size * 4);
sdim = {
x:(s/6)+tri_size*2+padding,
y:(s/6)+tri_size*2+padding,
l:(s * 2/3)-padding*2
};
return sdim;
}
function create_bs_square(){
bs_square_dim();
box = [sdim.x, sdim.y, sdim.l, sdim.l];
bs_square.h = canvas.rect.apply(canvas, box).attr({
stroke:"#EEE", gradient: "0-#FFF-#000", opacity:1});
bs_square.s = canvas.rect.apply(canvas, box).attr({
stroke:null, gradient: "0-#FFF-#FFF", opacity:0});
bs_square.b = canvas.rect.apply(canvas, box).attr({
stroke:null, gradient: "90-#000-#FFF", opacity:0});
bs_square.b.node.style.cursor = "crosshair";
}
function hue_segement_shape(){
var path = "M -@W 0 L @W 0 L @W @H L -@W @H z";
return path.replace(/@H/img, tri_size*2).replace(/@W/img,tri_size);
}
function copy_segment(r, d, k){
var n = r.clone();
var hue = d*(255/k);
var s = size/2,
t = tri_size,
p = padding;
n.transform("t"+s+","+(s-t)+"r"+(360/k)*d+"t0,-"+(s-t-p)+"");
n.attr({"stroke-width":0, fill:"hsb("+d*(1/k)+", 1, 0.85)"});
hue_ring.hues.push(n);
}
function create_hue_ring(){
var s = hue_segement_shape(),
tri = canvas.path(s).attr({stroke:"rgba(0,0,0,0)"}).transform("t"+(size/2)+","+padding),
k = segments; // # of segments to use to generate the hues
hue_ring.hues = canvas.set();
for(n=0; n<k; n++){ copy_segment(tri, n, k); }
// IE needs a slight opacity to assign events
hue_ring.event = canvas.circle(
center,
center,
center-tri_size-padding).attr({"stroke-width":tri_size*2, opacity:0.01});
hue_ring.outline = canvas.circle(
center,
center,
center-tri_size-padding).attr({"stroke":"#000", "stroke-width":(tri_size*2)+3, opacity:0.1});
hue_ring.outline.toBack();
hue_ring.event.node.style.cursor = "crosshair";
}
function run_onchange_event(){
if (change_callback !== undefined){
change_callback(current_color);
}
}
return create(target, color_wheel_size);
};

294
web/js/frontend.js Normal file
View file

@ -0,0 +1,294 @@
/**
* fnordlicht web control frontend
*
* simple ajax colorwheel frontend to control fnordlicht's
*
* @copyright 2013 Steffen Vogel
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <post@steffenvogel.de>
* @link http://www.steffenvogel.de
*/
/*
* This file is part of libfn
*
* libfn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* libfn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libfn. If not, see <http://www.gnu.org/licenses/>.
*/
var wheel; /* the colorwheel object */
var state = { };
var fade = { step : { } };
var seed = Math.random();
var listener;
Math.sgn = function(x) {
return (x == 0) ? 0 : (x < 0) ? -1 : 1;
}
function computeFade(current, target, step) {
/* search for max distance */
var max = 'r';
var dist = 0;
for (var i in target) {
if (['r', 'g', 'b'].indexOf(i) == -1) continue;
var d = Math.abs(target[i] - current[i]);
if (d > dist) {
max = i;
dist = d;
}
}
/* adjust fading speeds, relative to max distance */
fade.step[max] = Math.sgn(target[max] - current[max]) * step;
fade.steps = dist / step;
for (var i in current) {
if (['r', 'g', 'b'].indexOf(i) == -1 || i == max) continue;
if (dist > 0) {
var d = target[i] - current[i];
var ratio = d / dist;
fade.step[i] = ratio * step;
}
else {
fade.step[i] = 0;
}
}
}
function wheelFade(current, target, step, delay) {
if (current == target) return;
window.clearInterval(fade.interval);
computeFade(current, target, step, delay);
fade.interval = window.setInterval(function() {
fade.steps--;
current.r += fade.step.r;
current.g += fade.step.g;
current.b += fade.step.b;
current.hex = Raphael.rgb(current.r, current.g, current.b);
if (fade.steps <= 0) {
current = target;
window.clearInterval(fade.interval);
}
state.color = current;
setColor(current);
}, delay * 10);
}
function getUrlParams() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
function fnStop() {
$.ajax({
type: 'POST',
url: 'stop',
async: false
});
}
function fnStart(script) {
var data = {
script: script,
step: state.step,
delay: state.delay,
sleep: state.sleep,
value: state.value,
saturation: state.saturation,
use_address: (state.use_address) ? 1 : 0,
wait_for_fade: (state.wait_for_fade) ? 1 : 0
};
$.ajax({
type: 'POST',
url: 'start?' + $.param(data)
});
}
function fnFade(color) {
if (fade.running) return;
fade.running = true;
var data = {
color: color.hex,
step: (fade.drag) ? 255 : state.step,
delay: (fade.drag) ? 0 : state.delay
};
if (state.mask.indexOf('0') >= 0) {
data.mask = state.mask;
}
if ($('#stop_fade').attr('checked')) {
fnStop();
}
$.ajax({
type: 'POST',
url: 'fade?' + $.param(data),
success: function() {
fade.running = false;
}
});
}
function setColor(color) {
$(document.body).css('background-color', color.hex);
wheel.color(color.hex);
}
function listenCallback(data) {
wheelFade(state.color, data.color, data.step, data.delay);
state.count = data.count;
state.users = data.users;
drawUsers(data.users);
/* restart listener */
listener = $.get('status', { comet: 10 }, listenCallback);
}
function drawLamps(count) {
state.mask = new String;
$('#mask').empty();
for (var i = 0; i < count; i++) {
$('#mask').append(
$('<img>')
.addClass('icon').css('cursor', 'pointer')
.attr('src', 'img/bulb-on.png').attr('alt', i)
.data('index', i)
.toggle(function() {
$(this).attr('src', 'img/bulb-off.png');
var idx = $(this).data('index');
state.mask = state.mask.substr(0, idx) + '0' + state.mask.substr(idx+1);
}, function() {
$(this).attr('src', 'img/bulb-on.png');
var idx = $(this).data('index');
state.mask = state.mask.substr(0, i) + '1' + state.mask.substr(i+1);
})
);
state.mask += '1';
}
}
function drawUsers(count) {
$('#users').empty();
for (var i = 0; i <= count; i++) {
$('#users').append(
$('<img>')
.addClass('icon').attr('alt', i)
.attr('src', 'img/lego' + (Math.ceil(seed * 100 + i) % 6 + 1) + '.png')
);
}
}
$(document).ready(function() {
/* initialize wheel */
wheel = Raphael.colorwheel($('#wheel')[0], 500, 200);
wheel.ondrag(function() {
fade.dragTimeout = window.setTimeout(function() {
fade.drag = true;
listener.abort();
}, 200);
}, function(color) {
if (fade.drag) {
state.color = color;
listener = $.get('status', { comet: 10 }, listenCallback);
}
else
fnFade(color);
fade.drag = false;
window.clearTimeout(fade.dragTimeout);
});
wheel.onchange(function(color) {
if (fade.drag) {
$(document.body).css('background-color', color.hex);
fnFade(color);
}
});
/* show details */
$('#show_details').click(function() {
$('#details').toggle();
$('#show_details').toggle();
});
$('#options input')
.change(function() {
var elm = $(this);
var key = elm.attr('id');
var val = elm.val();
switch (elm.attr('type')) {
case 'text':
val = parseInt(val);
break;
case 'checkbox':
val = (elm.attr('checked') == 'checked');
break;
}
state[key] = val;
})
.each(function(idx, elm) {
$(elm).change();
});
/* start listener for first time*/
listener = $.get('status', function(data) {
state.color = data.color;
setColor(state.color);
/* draw lamps */
drawLamps(data.count);
/* parse url parameters */
window.setTimeout(function() {
var params = getUrlParams();
if ('party' in params) {
fnStart(('script' in params) ? parseInt(params['script']) : 1);
}
else if ('fade' in params) {
if ('step' in params) state.step = parseInt(params['step']);
if ('delay' in params) state.delay = parseInt(params['delay']);
fnFade(Raphael.getRGB(params['fade']));
}
}, 100);
listenCallback(data);
});
});

2
web/js/jquery-1.8.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

10
web/js/raphael-2.1.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long