added treeTable for channels

restructed ad bit
This commit is contained in:
Steffen Vogel 2010-09-29 21:59:49 +02:00
parent e6bf6952d9
commit 261fa6f768
81 changed files with 276 additions and 255 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -3,24 +3,26 @@
<head>
<title>volkszaehler.org - web frontend</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="javascript/jquery-ui-1.8.5.min.js"></script>
<script type="text/javascript" src="javascript/jquery-extensions.js"></script>
<script type="text/javascript" src="javascripts/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="javascripts/jquery-ui-1.8.5.min.js"></script>
<script type="text/javascript" src="javascripts/jquery-treeTable.min.js"></script>
<script type="text/javascript" src="javascripts/jquery-extensions.js"></script>
<script type="text/javascript" src="javascript/jqplot/jquery.jqplot.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.canvasTextRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.cursor.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.barRenderer.min.js"></script>
<script type="text/javascript" src="javascript/jqplot/plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/jquery.jqplot.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.canvasTextRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.cursor.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.barRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="javascript/functions.js"></script>
<script type="text/javascript" src="javascript/script.js"></script>
<script type="text/javascript" src="javascripts/functions.js"></script>
<script type="text/javascript" src="javascripts/script.js"></script>
<link rel="stylesheet" type="text/css" href="javascript/jqplot/jquery.jqplot.css">
<link rel="stylesheet" type="text/css" href="stylesheets/jquery.jqplot.min.css">
<link rel="stylesheet" type="text/css" href="stylesheets/jquery.treeTable.css">
<link rel="stylesheet" type="text/css" href="stylesheets/ui-lightness/jquery-ui-1.8.4.css" rel="Stylesheet" />
<link rel="stylesheet" type="text/css" href="stylesheets/style.css">
</head>
@ -40,7 +42,17 @@
<div id="accordion">
<h3>Kanäle</h3>
<table id="entities"></table>
<table id="entities">
<thead>
<tr>
<th>UUID</th>
<th>Title</th>
<th>Typ</th>
<th>Operationen</th>
</tr>
</thead>
<tbody></tbody>
</table>
<h3>Optionen</h3>
<div id="options">
<table>

View file

@ -1,167 +0,0 @@
/*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/
.jqplot-target {
position: relative;
color: #666666;
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
font-size: 1em;
/* height: 300px;
width: 400px;*/
}
/*rules applied to all axes*/
.jqplot-axis {
font-size: 0.75em;
}
.jqplot-xaxis {
margin-top: 10px;
}
.jqplot-x2axis {
margin-bottom: 10px;
}
.jqplot-yaxis {
margin-right: 10px;
}
.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis {
margin-left: 10px;
margin-right: 10px;
}
/*rules applied to all axis tick divs*/
.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
position: absolute;
}
.jqplot-xaxis-tick {
top: 0px;
/* initial position untill tick is drawn in proper place */
left: 15px;
/* padding-top: 10px;*/
vertical-align: top;
}
.jqplot-x2axis-tick {
bottom: 0px;
/* initial position untill tick is drawn in proper place */
left: 15px;
/* padding-bottom: 10px;*/
vertical-align: bottom;
}
.jqplot-yaxis-tick {
right: 0px;
/* initial position untill tick is drawn in proper place */
top: 15px;
/* padding-right: 10px;*/
text-align: right;
}
.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
left: 0px;
/* initial position untill tick is drawn in proper place */
top: 15px;
/* padding-left: 10px;*/
/* padding-right: 15px;*/
text-align: left;
}
.jqplot-xaxis-label {
margin-top: 10px;
font-size: 11pt;
position: absolute;
}
.jqplot-x2axis-label {
margin-bottom: 10px;
font-size: 11pt;
position: absolute;
}
.jqplot-yaxis-label {
margin-right: 10px;
/* text-align: center;*/
font-size: 11pt;
position: absolute;
}
.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label {
/* text-align: center;*/
font-size: 11pt;
position: absolute;
}
table.jqplot-table-legend, table.jqplot-cursor-legend {
background-color: rgba(255,255,255,0.6);
border: 1px solid #cccccc;
position: absolute;
font-size: 0.75em;
}
td.jqplot-table-legend {
vertical-align:middle;
}
td.jqplot-table-legend > div {
border:1px solid #cccccc;
padding:0.2em;
}
div.jqplot-table-legend-swatch {
width:0px;
height:0px;
border-top-width: 0.35em;
border-bottom-width: 0.35em;
border-left-width: 0.6em;
border-right-width: 0.6em;
border-top-style: solid;
border-bottom-style: solid;
border-left-style: solid;
border-right-style: solid;
}
.jqplot-title {
top: 0px;
left: 0px;
padding-bottom: 0.5em;
font-size: 1.2em;
}
table.jqplot-cursor-tooltip {
border: 1px solid #cccccc;
font-size: 0.75em;
}
.jqplot-cursor-tooltip {
border: 1px solid #cccccc;
font-size: 0.75em;
white-space: nowrap;
background: rgba(208,208,208,0.5);
padding: 1px;
}
.jqplot-highlighter-tooltip {
border: 1px solid #cccccc;
font-size: 0.75em;
white-space: nowrap;
background: rgba(208,208,208,0.5);
padding: 1px;
}
.jqplot-point-label {
font-size: 0.75em;
}
td.jqplot-cursor-legend-swatch {
vertical-align:middle;
text-align:center;
}
div.jqplot-cursor-legend-swatch {
width:1.2em;
height:0.7em;
}

View file

@ -24,22 +24,20 @@
* volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Frontend related functions
*/
/**
* Refresh plot with new data
*/
function refresh() {
if ($('[name=refresh]').attr('checked')) {
getData();
}
}
function loadEntities() {
$('#entities').empty();
$.each(uuids, function(index, value) {
$.getJSON(backendUrl + '/entity/' + value + '.json', function(json) {
var entity = (json.group) ? json.group : json.channel;
$('#entities').append('<tr><td><input type="checkbox" /></td><td>' + entity.uuid + '</td><td>' + entity.title + '</td><td>' + entity.type + '</td></tr>');
});
});
}
function moveWindow(mode) {
delta = myWindowEnd - myWindowStart;
@ -58,10 +56,9 @@ function moveWindow(mode) {
getData();
}
function getData() {
// load json data with given time window
$.getJSON(backendUrl + '/data/' + myUUID + '.json', {from: myWindowStart, to: myWindowEnd, tuples: 500}, function(data){
$.getJSON(backendUrl + '/data/' + myUUID + '.json', { from: myWindowStart, to: myWindowEnd, tuples: 500 }, function(data){
json = data;
showChart();
});
@ -82,4 +79,91 @@ function showChart() {
clear: true,
resetAxes: true
});
}
/*
* Entity list related functions
*/
/**
* Get all entity infomration from backend
*/
function loadEntities() {
$.each(uuids, function(index, value) {
$.getJSON(backendUrl + '/entity/' + value + '.json', ajaxWait(showEntities, 'enities'));
});
}
/**
* Create nested entity list
* @param data
*/
function showEntities(data) {
$('#entities tbody').empty();
$.each(data, function(index, value) {
var entity = (value.group) ? value.group : value.channel;
showEntity(entity);
});
$('#entities').treeTable();
}
/**
* Create nested entity list (recursive)
* @param entity
* @param parent
*/
function showEntity(entity, parent) {
$('#entities tbody').append(
$('<tr>')
.attr('class', (parent) ? 'child-of-entity-' + parent.uuid : '')
.attr('id', 'entity-' + entity.uuid)
.append($('<td>').text(entity.uuid))
.append($('<td>').text(entity.title))
.append($('<td>').text(entity.type))
);
var entities = new Array();
if (entity.channels) {
$.merge(entities, entity.channels);
}
if (entity.groups) {
$.merge(entities, entity.groups);
}
$.each(entities, function(index, value) {
showEntity(value, entity);
});
}
/*
* General helper functions
*/
function ajaxWait(callback, identifier) {
if (!identifier) {
var identifier = 0;
}
if (!ajaxWait.counter || !ajaxWait.data) {
ajaxWait.counter = new Array();
ajaxWait.data = new Array();
}
if (!ajaxWait.counter[identifier] || !ajaxWait.data[identifier]) {
ajaxWait.counter[identifier] = 0;
ajaxWait.data[identifier] = new Array;
}
ajaxWait.counter[identifier]++;
return function (data, textStatus) {
ajaxWait.data[identifier].push(data);
if (!--ajaxWait.counter[identifier]) {
callback(ajaxWait.data[identifier]);
}
};
}

View file

@ -26,14 +26,14 @@
*/
/**
*
* Get URL parameters
*/
$.extend( {
getUrlVars : function() {
var vars = [], hash;
var hashes = window.location.href.slice(
window.location.href.indexOf('?') + 1).split('&');
for ( var i = 0; i < hashes.length; i++) {
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
@ -57,49 +57,53 @@ $.extend( {
* http://www.gnu.org/licenses/gpl.html
*/
$.extend({
cookie: function(name, value, options) {
if (typeof value != 'undefined') { // name and value given, set cookie
options = options || {};
if (value === null) {
value = '';
options.expires = -1;
}
var expires = '';
if (options.expires
&& (typeof options.expires == 'number' || options.expires.toUTCString)) {
var date;
if (typeof options.expires == 'number') {
date = new Date();
date.setTime(date.getTime()
+ (options.expires * 24 * 60 * 60 * 1000));
} else {
date = options.expires;
}
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
}
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
// in the packed version for some reason...
var path = options.path ? '; path=' + (options.path) : '';
var domain = options.domain ? '; domain=' + (options.domain) : '';
var secure = options.secure ? '; secure' : '';
document.cookie = [ name, '=', encodeURIComponent(value), expires,
path, domain, secure ].join('');
} else { // only name given, get cookie
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for ( var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie
.substring(name.length + 1));
break;
}
}
}
return cookieValue;
setCookie: function(name, value, options) {
options = options || {};
if (value === null) {
value = '';
options.expires = -1;
}
var expires = '';
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
var date;
if (typeof options.expires == 'number') {
date = new Date();
date.setTime(date.getTime()
+ (options.expires * 24 * 60 * 60 * 1000));
} else {
date = options.expires;
}
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
}
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
// in the packed version for some reason...
var path = options.path ? '; path=' + (options.path) : '';
var domain = options.domain ? '; domain=' + (options.domain) : '';
var secure = options.secure ? '; secure' : '';
document.cookie = [ name, '=', encodeURIComponent(value), expires, path, domain, secure ].join('');
},
getCookie: function(name) {
var value = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = $.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
value = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return value;
}
});

View file

@ -0,0 +1,19 @@
/*
* jQuery treeTable Plugin 2.3.0
* http://ludo.cubicphuse.nl/jquery-plugins/treeTable/
*
* Copyright 2010, Ludo van den Boom
* Dual licensed under the MIT or GPL Version 2 licenses.
*/
(function($){var options;var defaultPaddingLeft;$.fn.treeTable=function(opts){options=$.extend({},$.fn.treeTable.defaults,opts);return this.each(function(){$(this).addClass("treeTable").find("tbody tr").each(function(){if(!options.expandable||$(this)[0].className.search(options.childPrefix)==-1){if(isNaN(defaultPaddingLeft)){defaultPaddingLeft=parseInt($($(this).children("td")[options.treeColumn]).css('padding-left'),10);}
initialize($(this));}else if(options.initialState=="collapsed"){this.style.display="none";}});});};$.fn.treeTable.defaults={childPrefix:"child-of-",clickableNodeNames:false,expandable:true,indent:19,initialState:"collapsed",treeColumn:0};$.fn.collapse=function(){$(this).addClass("collapsed");childrenOf($(this)).each(function(){if(!$(this).hasClass("collapsed")){$(this).collapse();}
this.style.display="none";});return this;};$.fn.expand=function(){$(this).removeClass("collapsed").addClass("expanded");childrenOf($(this)).each(function(){initialize($(this));if($(this).is(".expanded.parent")){$(this).expand();}
$(this).show();});return this;};$.fn.reveal=function(){$(ancestorsOf($(this)).reverse()).each(function(){initialize($(this));$(this).expand().show();});return this;};$.fn.appendBranchTo=function(destination){var node=$(this);var parent=parentOf(node);var ancestorNames=$.map(ancestorsOf($(destination)),function(a){return a.id;});if($.inArray(node[0].id,ancestorNames)==-1&&(!parent||(destination.id!=parent[0].id))&&destination.id!=node[0].id){indent(node,ancestorsOf(node).length*options.indent*-1);if(parent){node.removeClass(options.childPrefix+parent[0].id);}
node.addClass(options.childPrefix+destination.id);move(node,destination);indent(node,ancestorsOf(node).length*options.indent);}
return this;};$.fn.reverse=function(){return this.pushStack(this.get().reverse(),arguments);};$.fn.toggleBranch=function(){if($(this).hasClass("collapsed")){$(this).expand();}else{$(this).removeClass("expanded").collapse();}
return this;};function ancestorsOf(node){var ancestors=[];while(node=parentOf(node)){ancestors[ancestors.length]=node[0];}
return ancestors;};function childrenOf(node){return $("table.treeTable tbody tr."+options.childPrefix+node[0].id);};function getPaddingLeft(node){var paddingLeft=parseInt(node[0].style.paddingLeft,10);return(isNaN(paddingLeft))?defaultPaddingLeft:paddingLeft;}
function indent(node,value){var cell=$(node.children("td")[options.treeColumn]);cell[0].style.paddingLeft=getPaddingLeft(cell)+value+"px";childrenOf(node).each(function(){indent($(this),value);});};function initialize(node){if(!node.hasClass("initialized")){node.addClass("initialized");var childNodes=childrenOf(node);if(!node.hasClass("parent")&&childNodes.length>0){node.addClass("parent");}
if(node.hasClass("parent")){var cell=$(node.children("td")[options.treeColumn]);var padding=getPaddingLeft(cell)+options.indent;childNodes.each(function(){$(this).children("td")[options.treeColumn].style.paddingLeft=padding+"px";});if(options.expandable){cell.prepend('<span style="margin-left: -'+options.indent+'px; padding-left: '+options.indent+'px" class="expander"></span>');$(cell[0].firstChild).click(function(){node.toggleBranch();});if(options.clickableNodeNames){cell[0].style.cursor="pointer";$(cell).click(function(e){if(e.target.className!='expander'){node.toggleBranch();}});}
if(!(node.hasClass("expanded")||node.hasClass("collapsed"))){node.addClass(options.initialState);}
if(node.hasClass("expanded")){node.expand();}}}}};function move(node,destination){node.insertAfter(destination);childrenOf(node).reverse().each(function(){move($(this),node[0]);});};function parentOf(node){var classNames=node[0].className.split(' ');for(key in classNames){if(classNames[key].match(options.childPrefix)){return $("#"+classNames[key].substring(9));}}};})(jQuery);

View file

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 331 B

View file

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 43 B

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 43 B

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 132 B

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -39,7 +39,12 @@ const jqOptions = {
},
seriesDefaults: {
lineWidth: 1,
showMarker: false
showMarker: true,
showLine: false,
markerOptions: {
style: 'dash',
size: 2
}
},
axes: {
yaxis: {
@ -68,21 +73,27 @@ const jqOptions = {
}
};
/*
* Variables
*/
// uuids
var myUUID = '';
var uuids = $.parseJSON($.cookie('uuids'));
var uuids = new Array;
if ($.getCookie('uuids')) {
var uuids = $.parseJSON($.getCookie('uuids'));
}
if($.getUrlVar('uuid')) {
myUUID = $.getUrlVar('uuid');
uuids.push($.getUrlVar('uuid'));
}
if (uuids.length == 0) {
alert('Error: No UUIDs given!')
}
// storing json data
var json;
//windowEnd parameter for json server
// windowEnd parameter for json server
var myWindowEnd = new Date().getTime();
// windowStart parameter for json server
@ -91,15 +102,6 @@ var myWindowStart = myWindowEnd - 24*60*60*1000;
// executed on document loaded complete
// this is where it all starts...
$(document).ready(function() {
// initialization of user interface
$('#accordion h3').click(function() {
$(this).next().toggle('fast');
return false;
}).next().hide();
$('#refreshInterval').slider();
// resize chart area for low resolution displays
// works fine with HTC hero
// perhaps you have to reload after display rotation
@ -113,8 +115,17 @@ $(document).ready(function() {
// load all entity information
loadEntities();
// start auto refresh timer
window.setInterval(refresh, 5000);
// initialization of user interface
$('#accordion h3').click(function() {
$(this).next().toggle('fast');
return false;
}).next().hide();
$('#refreshInterval').slider();
// load data and show plot
getData();
});

View file

@ -0,0 +1,43 @@
/* jQuery TreeTable Core 2.0 stylesheet
*
* This file contains styles that are used to display the tree table. Each tree
* table is assigned the +treeTable+ class.
* ========================================================================= */
/* jquery.treeTable.collapsible
* ------------------------------------------------------------------------- */
.treeTable tr td .expander {
background-position: left center;
background-repeat: no-repeat;
cursor: pointer;
padding: 0;
zoom: 1; /* IE7 Hack */
}
.treeTable tr.collapsed td .expander {
background-image: url(../images/toggle-expand-dark.png);
}
.treeTable tr.expanded td .expander {
background-image: url(../images/toggle-collapse-dark.png);
}
/* jquery.treeTable.sortable
* ------------------------------------------------------------------------- */
.treeTable tr.selected, .treeTable tr.accept {
background-color: #3875d7;
color: #fff;
}
.treeTable tr.collapsed.selected td .expander, .treeTable tr.collapsed.accept td .expander {
background-image: url(../images/toggle-expand-light.png);
}
.treeTable tr.expanded.selected td .expander, .treeTable tr.expanded.accept td .expander {
background-image: url(../images/toggle-collapse-light.png);
}
.treeTable .ui-draggable-dragging {
color: #000;
z-index: 1;
}

View file

@ -26,4 +26,19 @@ body {
margin: 2px;
-moz-border-radius: 4px;
font-size: 0.9em;
}
/* treeTable for entities */
#entities {
border-collapse: collapse;
}
#entities thead tr th {
padding: 0.3em 1.67em;
text-align: left;
}
#entities tbody tr td {
padding: 0.3em 1.5em;
white-space: nowrap;
}