switching/stats.html

4389 lines
745 KiB
HTML
Raw Normal View History

2020-09-16 18:57:40 +00:00
<!DOCTYPE html><html><head><meta charset='UTF-8' /><meta http-equiv='X-UA-Compatible' content='IE=edge'><meta name='viewport' content='width=device-width, initial-scale=1'><meta name='robots' content='noindex, nofollow' /><title>Server&nbsp;Statistics</title><style>/*!* Font Awesome Free 5.0.10 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license(Icons:CC BY 4.0,Fonts:SIL OFL 1.1,Code:MIT License) */ .fa,.fas,.far,.fal,.fab{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fas.fa-pull-left,.far.fa-pull-left,.fal.fa-pull-left,.fab.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fas.fa-pull-right,.far.fa-pull-right,.fal.fa-pull-right,.fab.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1)";-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1)";-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-horizontal.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1)";-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before,.fa-wheelchair-alt:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-address-book:before,.fa-address-book-o:before{content:"\f2b9"}.fa-address-card:before,.fa-vcard:before,.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-america
html,
body {
overflow-x: hidden;
background: #f0f0f0;
}
h1 {
font-weight: bold;
letter-spacing: -3px;
}
h3 {
font-size: 21px;
letter-spacing: -1px;
}
.page-header {
position: relative;
margin: 25px 0 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
}
.page-header h1 {
margin: 0;
}
.pagination {
margin: 5px 0;
}
.clickable,
.expandable>td {
cursor: pointer;
}
.spinner {
color: #999;
left: 50%;
top: 50%;
position: absolute;
}
.powered {
bottom: 170px;
color: #9E9E9E;
font-size: smaller;
position: absolute;
right: 20px;
transform-origin: 100% 0;
transform: rotate(-90deg);
}
.powered a {
color: #636363;
}
.dropdown-header {
color: #007bc3;
padding: 3px 25px;
text-transform: uppercase;
}
.gheader {
letter-spacing: -1px;
text-transform: uppercase;
}
h5.gheader {
letter-spacing: 0;
}
.panel-header h4.gheader {
margin-top: 20px;
}
.panel-header .gheader small {
font-size: 69%;
}
/* NAVIGATION */
nav {
-webkit-transition: left .7s;
background: #1C1C1C;
border-right: 3px solid #5bc0de;
height: 100%;
left: -236px;
position: fixed;
top: 0;
transition: left .7s;
width: 300px;
z-index: 2;
overflow: hidden;
}
nav .nav-list {
height: 100%;
overflow-y: auto;
width: 350px;
}
nav header {
margin: 40px 20px 30px;
}
nav header a {
font-size: 2.7em;
font-weight: 300;
text-transform: uppercase;
color: rgba(240,240,240,.7);
}
nav header a:hover {
color: #eee;
}
nav.active {
display: block !important;
left: 0;
}
nav:hover ~ #content {
opacity: .3;
}
nav.active .nav-bars,
nav.active .nav-gears,
nav.active .nav-ws-status {
opacity: 0;
}
nav .nav-bars,
nav .nav-gears,
nav .nav-ws-status {
-webkit-transition: opacity .7s;
color: #9E9E9E;
cursor: pointer;
float: right;
font-size: 36px;
height: 32px;
left: 13px;
line-height: 32px;
position: fixed;
text-align: center;
top: 30px;
transition: opacity .7s;
width: 32px;
}
nav .nav-gears {
top: 100px;
opacity: 0.6;
}
nav .nav-ws-status,
.nav-ws-status.mini {
color: #6A6A6A;
cursor: help;
display: none;
font-size: 12px;
}
nav .nav-ws-status {
left: 25px;
top: 125px;
}
.nav-ws-status.mini {
top: 14px;
left: 50px;
position: absolute;
}
.nav-ws-status.connected {
color: #5DB56A;
}
nav li a {
border-left: 3px solid transparent;
color: rgba(200,200,200,.5);
display: block;
font-size: smaller;
max-width: 235px;
opacity: 0;
overflow: hidden;
padding: 9px 20px;
text-overflow: ellipsis;
text-transform: uppercase;
transition: opacity .7s;
white-space: nowrap;
}
nav.active li a {
max-width: 100%;
opacity: 1;
}
nav li a:hover,
nav li.active a {
background: rgba(0,0,0,.1);
border-color: #5BC0DE;
color: #eee;
}
nav ul {
padding-left: 0;
list-style: none;
}
/* Navigation -- Icon */
nav a,
nav a:hover {
text-decoration: none;
}
nav h3 {
color: #FFF !important;
font-size: medium;
font-weight: bold;
margin: 20px 25px 10px;
text-transform: uppercase;
}
/* CONTAINER */
@media screen and (max-width: 767px) {
.row-offcanvas {
-webkit-transition: all .25s ease-out;
-o-transition: all .25s ease-out;
position: relative;
transition: all .25s ease-out;
}
.row-offcanvas-right {
right: 0;
}
.row-offcanvas-left {
left: 0;
}
.row-offcanvas-right
.sidebar-offcanvas {
right: -50%;
}
.row-offcanvas-left
.sidebar-offcanvas {
left: -50%;
}
.row-offcanvas-right.active {
right: 50%;
}
.row-offcanvas-left.active {
left: 50%;
}
.sidebar-offcanvas {
position: absolute;
top: 0;
width: 50%;
};
}
@media (min-width: 768px) {
.container {
width: 750px;
};
}
@media (max-width: 480px) {
.wrap-general h5,
.wrap-panel h5 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wrap-general h5 {
width: 100%
}
.wrap-panel h5 {
width: 70%
}
}
.container-fluid {
margin-left: 75px;
}
@media (min-width: 1120px) {
.container {
width: 970px;
};
}
@media (min-width: 1320px) {
.container {
width: 1170px;
};
}
@media (max-width: 992px) {
.container-fluid {
margin-left: auto;
};
}
@media (max-width: 768px) {
.container-fluid {
padding-left: 5px;
padding-right: 5px;
}
.page-header {
padding: 0 10px;
}
}
/* PANEL STYLES */
.wrap-panel .panel-header {
position: relative;
}
div.wrap-panel > div {
background: #FFF;
margin-top: 10px;
padding: 0 10px;
border-top: 1px solid rgba(0, 0, 0, 0.15);
}
/* PANEL TABLES */
.wrap-panel table.table-borderless tbody tr td,
.wrap-panel table.table-borderless tbody tr th,
.wrap-panel table.table-borderless thead tr th {
border: none;
}
.wrap-panel table thead tr th {
text-align: right;
border-bottom-width: 1px;
}
.wrap-panel table .string,
.wrap-panel table .date {
text-align: left;
}
.wrap-panel table .percent {
color: #898989;
}
.wrap-panel table td,
.wrap-panel table th {
white-space: nowrap;
overflow: hidden;
}
.wrap-panel table th.sortable {
cursor: pointer;
}
/* thead meta */
.wrap-panel table tbody.tbody-meta {
border-top: 1px solid #C7C7C7;
border-bottom: 1px solid #C7C7C7;
}
.wrap-panel table tbody.tbody-meta tr {
background-color: #F1F1F1;
color: #222;
}
.wrap-panel table tbody.tbody-meta small {
font-size: 65%;
}
/* thead data */
.wrap-panel table tbody.tbody-data tr td {
border-right: 1px solid #F1F1F1;
font-size: smaller;
}
.wrap-panel table tbody.tbody-data td:last-child {
border-right: none;
}
.wrap-panel table tbody.tbody-data td.row-idx {
color: #898989;
}
.wrap-panel table>tbody+tbody {
border-top-width: 1px;
}
.wrap-panel table tbody.tbody-data tr.shaded {
background-color: #F7F7F7;
}
.wrap-panel table tbody.tbody-data tr. {
background-color: #F7F7F7;
}
.wrap-panel table tbody.tbody-data tr.child td:nth-child(1),
.wrap-panel table tbody.tbody-data tr.child td:nth-child(2) {
border-right: none;
}
.wrap-panel table.table-hover>tbody>tr:hover {
background-color: #EEE;
}
/* GENERAL */
.wrap-general {
position: relative;
}
.report-title {
background: #FFF;
border-radius: 4px;
bottom: -10px;
color: #9E9E9E;
font-size: small;
padding: 0 10px;
position: absolute;
right: 0;
z-index: 1;
}
.panel-plot-wrap {
position: absolute;
right: 0;
top: 18px;
}
.col-title {
font-size: 85%;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 1px 1px 0 #FFF;
white-space: nowrap;
width: 100%;
}
.grid-module {
background: #FFF;
color: rgb(36, 36, 36);
font-weight: normal;
margin-top: 5px;
padding: 7px;
}
.grid-module h3 {
font-size: 25px;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.grid-module.black {
border-top: 4px solid #0F1214;
}
.grid-module.gray {
border-top: 4px solid #9E9E9E;
}
.grid-module.red {
border-top: 4px solid #FF303E;
}
.grid-module.blue{
border-top: 4px solid #00D4E1;
}
.grid-module.green {
border-top: 4px solid #5DB56A;
}
@media (max-width: 767px) {
.panel-plot-wrap {
top: 10px;
}
.powered {
bottom: 10px;
left: 25px;
transform: initial;
}
}
/* CHARTS */
.chart-wrap {
margin-bottom: 15px;
position: relative;
}
svg {
background-color: #fff;
display: block;
}
.axis path {
fill: transparent;
stroke: black;
shape-rendering: crispEdges;
stroke-width: 1;
}
.grid.y .tick line,
.grid.x .tick line {
shape-rendering: crispEdges;
stroke: #999;
stroke-dasharray: 3 3;
stroke-width: 1;
}
.axis.x .tick line,
.axis.y0 .tick line,
.axis.y1 .tick line,
.grid.y .tick:first-child line {
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
.bars rect.bar {
shape-rendering: crispEdges;
}
.rects rect {
fill: transparent;
}
.area {
opacity: 0.2;
}
.points {
stroke: transparent;
}
line.indicator {
fill: transparent;
pointer-events: none;
shape-rendering: crispEdges;
stroke: #999;
stroke-width: 1;
}
.area0,
.bars.y0 .bar,
.points.y0,
rect.legend.y0 {
fill: #447FB3;
}
.area1,
.bars.y1 .bar,
.points.y1,
rect.legend.y1 {
fill: #FF6854;
}
.line0,
.line1 {
fill: transparent;
stroke-width: 1;
}
.line0 {
stroke: #007BC3;
}
.line1 {
stroke: #FF303E;
}
.axis text,
.axis-label,
text.legend {
font: 10px sans-serif;
}
.axis-label.y0,
.axis-label.y1 {
text-anchor: end;
}
rect.legend {
height: 10px;
width: 10px;
}
.legend {
cursor: pointer;
}
.wrap-text text {
text-anchor: start!important;
}
/* CHART TOOLTIP */
.chart-tooltip-wrap {
left: 0;
pointer-events: none;
position: absolute;
top: 10px;
z-index: 10;
}
.chart-tooltip {
-moz-box-shadow: 7px 7px 12px -9px #777777;
-webkit-box-shadow: 7px 7px 12px -9px #777777;
background-color: #fff;
border-collapse: collapse;
border-spacing: 0;
box-shadow: 7px 7px 12px -9px #777777;
empty-cells: show;
opacity: 0.9;
}
.chart-tooltip tr {
border: 1px solid #CCC;
}
.chart-tooltip th {
background-color: #aaa;
color: #FFF;
font-size: 14px;
max-width: 380px;
overflow: hidden;
padding: 2px 5px;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
}
.chart-tooltip td {
border-left: 1px dotted #999;
font-size: 13px;
padding: 3px 6px;
}
.chart-tooltip td > span {
display: inline-block;
height: 10px;
margin-right: 6px;
width: 10px;
}
.chart-tooltip td.value {
text-align: right;
}
.chart-tooltip .blue {
background-color: #007BC3;
}
.chart-tooltip .red {
background-color: #FF303E;
}
/* DARK THEME */
.dark h1 {
color: rgba(255, 255, 255, 0.6);
}
.dark h3,
.dark h4,
.dark h5 {
color: rgba(255,255,255,0.4);
}
.dark .table-responsive {
border: none;
}
.dark .wrap-panel > div > table {
color: #D2D2D2;
}
.dark .wrap-panel table tbody.tbody-meta tr {
background-color: transparent;
color: #F7F7F7;
}
.dark .wrap-panel table tbody.tbody-data tr td {
border-right: none;
}
.dark .wrap-panel table.table-hover>tbody.tbody-data>tr:hover {
background-color: rgba(255, 255, 255, 0.08) !important;
}
.dark .col-title {
color: #9e9e9e;
text-shadow:none;
}
.dark .grid-module h3 {
color: #FFF;
}
.dark .dropdown-menu>li>a {
color: #FFF;
}
.dark div.wrap-panel > div {
color: #EEE;
margin-top: 10px;
padding: 0 10px;
border-top: 1px solid rgba(255, 255, 255, 0.15);
}
/* DARK BLUE THEME */
html.dark.blue,
.dark.blue body {
background: #252B30;
}
.dark.blue .container {
background: #252B30;
}
.dark.blue .page-header {
border-bottom: 1px solid #3B444C;
}
.dark.blue .label-info {
background-color: #252B30;
}
.dark.blue nav {
border-right: 1px solid #181B1F;
background: #1F2328;
}
.dark.blue div.wrap-panel > div {
background: #1F2328;
}
.dark.blue .wrap-panel table tbody.tbody-meta {
border-top: 1px solid #3B444C;
border-bottom: 1px solid #3B444C;
}
.dark.blue .wrap-panel table tbody.tbody-data tr.shaded {
background-color: #181B1F;
}
.dark.blue .gray {
border-top: 4px solid #3B444C;
}
.dark.blue .grid-module {
background: #1F2328;
}
.dark.blue .btn-default {
color: #9E9E9E;
background-color: #1F2328;
border-color: #3B444C;
}
.dark.blue .btn-default:active,
.dark.blue .btn-default:hover,
.dark.blue .btn-default.active,
.dark.blue .open>.dropdown-toggle.btn-default {
color: #3B444C;
background-color: #1F2328;
border-color: #0F1214;
}
.dark.blue .pagination>.disabled>a,
.dark.blue .pagination>.disabled>a:hover,
.dark.blue .pagination>.disabled>a:focus {
color: #777;
}
.dark.blue .pagination>li>a {
background-color: #1F2328;
border: 1px solid #3B444C;
}
.dark.blue .pagination>li>a:hover,
.dark.blue .pagination>li>a:active,
.dark.blue .pagination>li>a:focus {
color: #0370B0;
background-color: #1F2328;
border-color: #3B444C;
}
.dark.blue .dropdown-menu>li>a:hover,
.dark.blue .dropdown-menu>li>a:focus {
color: #FFF;
background-color: #3B444C;
}
.dark.blue .dropdown-menu {
background-color: #252B30;
}
.dark.blue::-webkit-scrollbar-track,
.dark.blue .table-responsive::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #9E9E9E;
}
.dark.blue::-webkit-scrollbar,
.dark.blue .table-responsive::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: #9E9E9E;
}
.dark.blue::-webkit-scrollbar-thumb,
.dark.blue .table-responsive::-webkit-scrollbar-thumb {
background-color: #3B444C;
}
.dark.blue .chart-tooltip {
background-color: #252B30;
}
.dark.blue .report-title {
background: #1F2328;
}
/* DARK GREY THEME */
html.dark.gray,
.dark.gray body {
background: #212121;
}
.dark.gray .container {
background: #212121;
}
.dark.gray .page-header {
border-bottom: 1px solid #303030;
}
.dark.gray .label-info {
background-color: #303030;
}
.dark.gray nav {
border-right: 1px solid #363737;
background: #1C1C1C;
}
.dark.gray div.wrap-panel > div {
background: #1C1C1C;
}
.dark.gray .wrap-panel table tbody.tbody-meta {
border-top: 1px solid #363737;
border-bottom: 1px solid #363737;
}
.dark.gray .wrap-panel table tbody.tbody-data tr.shaded {
background-color: rgba(48, 48, 48, 0.48);
}
.dark.gray .gray {
border-top: 4px solid #303030;
}
.dark.gray .grid-module {
background: #1C1C1C;
}
.dark.gray .btn-default {
color: #9E9E9E;
background-color: #212121;
border-color: #303030;
}
.dark.gray .btn-default:active,
.dark.gray .btn-default:hover,
.dark.gray .btn-default.active,
.dark.gray .open>.dropdown-toggle.btn-default {
color: #363737;
background-color: #1C1C1C;
border-color: #0F1214;
}
.dark.gray .pagination>.disabled>a,
.dark.gray .pagination>.disabled>a:hover,
.dark.gray .pagination>.disabled>a:focus {
color: #777;
}
.dark.gray .pagination>li>a {
background-color: #212121;
border: 1px solid #303030;
}
.dark.gray .pagination>li>a:hover,
.dark.gray .pagination>li>a:active,
.dark.gray .pagination>li>a:focus {
color: #0370B0;
background-color: #212121;
border-color: #303030;
}
.dark.gray .dropdown-menu>li>a {
color: #FFF;
}
.dark.gray .dropdown-menu>li>a:hover,
.dark.gray .dropdown-menu>li>a:focus {
color: #FFF;
background-color: #303030;
}
.dark.gray .dropdown-menu {
background-color: #212121;
}
.dark.gray::-webkit-scrollbar-track,
.dark.gray .table-responsive::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #9E9E9E;
}
.dark.gray::-webkit-scrollbar,
.dark.gray .table-responsive::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: #9E9E9E;
}
.dark.gray::-webkit-scrollbar-thumb,
.dark.gray .table-responsive::-webkit-scrollbar-thumb {
background-color: #303030;
}
.dark.gray .chart-tooltip {
background-color: #303030;
}
.dark.gray .report-title {
background: #303030;
}
/* DARK CHARTS */
.dark svg {
background-color: transparent;
}
.dark .area {
opacity: 0.1;
}
.dark .line0,
.dark .line1 {
stroke-width: 2;
}
.dark .area0,
.dark .bars.y0 .bar,
.dark rect.legend.y0 {
fill: #007BC3;
}
.dark .area1,
.dark .bars.y1 .bar,
.dark .points.y1,
.dark rect.legend.y1 {
fill: #FF303E;
}
.dark .points.y0 {
fill: #00D4E1;
}
.dark .line0 {
stroke: #007BC3;
}
.dark .line1 {
stroke: #FF303E;
}
.dark .grid.y .tick line,
.dark .grid.x .tick line {
stroke: #44474B;
stroke-dasharray: 1 1;
}
.dark .axis text,
.dark .axis-label,
.dark text.legend {
fill: #9E9E9E;
}
.dark .axis path {
stroke: #999999;
}
.dark .axis.x .tick line,
.dark .axis.y0 .tick line,
.dark .axis.y1 .tick line,
.dark .grid.y .tick:first-child line {
stroke: #3B444C;
}
.dark .chart-tooltip th {
background-color: #1c1c1c;
}
.dark .chart-tooltip tr {
border: 1px solid #363737;
}
</style></head><body><nav class='hidden-xs hidden-sm hide'></nav><i class='spinner fa fa-circle-o-notch fa-spin fa-3x fa-fw'></i><div class='container hide'><div class='wrap-header'><div class='row row-offcanvas row-offcanvas-right'><div class='col-md-12'><div class='page-header clearfix'><div class='pull-right'><h4><span class='label label-info' style='display:block'><span class='hidden-xs'>Last Updated: </span><span class='last-updated'>2020-09-16 15:46:22 +0200</span></span></h4></div><h1><span class='hidden-xs hidden-sm'><i class='fa fa-tachometer'></i> Dashboard</span><span class='visible-xs visible-sm'><i class='fa fa-bars nav-minibars'></i><i class='fa fa-circle nav-ws-status mini'></i></span></h1><div class='report-title'></div></div><div class='wrap-general'></div></div></div></div><div class='wrap-panels'></div></div><!-- TPL General -->
<script id="tpl-general" type="text/template">
<h4 class="hidden-xs gheader">{{head}}<span class="pull-right"><span class="from"></span> &#8212; <span class="to"></span></span></h4>
<h5 class="visible-xs hidden-sm hidden-md hidden-lg gheader">{{head}}&nbsp;&nbsp;<span class="from"></span> &#8212; <span class="to"></span></span></h5>
<div class="wrap-general-items"></div>
</script>
<!-- TPL General Items -->
<script id="tpl-general-items" type="text/template">
<div class="col-md-2">
<div class="grid-module {{#className}}{{className}}{{/className}}{{^className}}gray{{/className}}">
<div class="col-title">
<i class="fa fa-bar-chart"></i> {{#label}}{{label}}{{/label}}
</div>
<h3 id="{{id}}" style="padding-top: 0;">{{value}}</h3>
</div>
</div>
</script>
<!-- TPL Panel Table -->
<script id="tpl-table-row" type="text/template">
{{#rows}}
<tr class="{{#className}}{{className}}{{/className}} {{#hasSubItems}}{{#items}}expandable{{/items}}{{/hasSubItems}}" {{#idx}}data-pid="{{idx}}"{{/idx}} data-panel="{{panel}}" {{#key}}data-key="{{key}}"{{/key}}>
{{#hasSubItems}}
<td class="row-expandable text-center {{#items}}clickable{{/items}}">
{{#items}}<i class="fa {{#expanded}}fa-caret-down{{/expanded}}{{^expanded}}fa fa-caret-right{{/expanded}}"></i>{{/items}}
{{^items}}<i></i>{{/items}}
</td>
{{/hasSubItems}}
<td class="row-idx text-right">
{{#idx}}{{idx}}{{/idx}}
</td>
{{#cells}}
<td class="{{className}}" {{#colspan}}colspan="{{colspan}}"{{/colspan}}>
<span class="value">{{{value}}}</span>{{#percent}}<span class="percent"> ({{percent}})</span>{{/percent}}
</td>
{{/cells}}
</tr>
{{/rows}}
</script>
<!-- TPL Panel Table Meta -->
<script id="tpl-table-row-meta" type="text/template">
{{#row}}
<tr>
{{#hasSubItems}}
<td class=""></td>
{{/hasSubItems}}
<td class=""></td>
{{#cells}}
<td class="{{className}}" {{#colspan}}colspan="{{colspan}}"{{/colspan}}>
{{#value}}
<h4 class="value"><span title="{{title}}">{{value}}</span>{{#label}}<small> {{label}}</small>{{/label}}{{#max}}<br><small>Max: {{max}}</small>{{/max}}{{#min}}<br><small>Min: {{min}}</small>{{/min}}</h4>
{{/value}}
</td>
{{/cells}}
</tr>
{{/row}}
</script>
<!-- TPL Table thead -->
<script id="tpl-table-thead" type="text/template">
<tr>
{{#hasSubItems}}
<th></th>
{{/hasSubItems}}
<th>#</th>
{{#items}}
<th class="{{dataType}} {{#key}}sortable{{/key}}" data-key="{{key}}" {{#sort}}data-order="{{#asc}}asc{{/asc}}{{^asc}}desc{{/asc}}"{{/sort}}>
{{label}} <i class="fa fa-{{^sort}}sort{{/sort}}{{#sort}}{{#asc}}caret-up{{/asc}}{{^asc}}caret-down{{/asc}}{{/sort}}"></i>
</th>
{{/items}}
</tr>
</script>
<!-- TPL Panel Options DropDown -->
<script id="tpl-panel-opts" type="text/template">
{{#plot.length}}
<li class="dropdown-header">Chart Type</li>
<li><a href="javascript:void(0);" data-panel="{{id}}" data-chart-type="area-spline"><i class="fa fa-circle{{^area-spline}}-o{{/area-spline}}"></i> Area Spline</a></li>
<li><a href="javascript:void(0);" data-panel="{{id}}" data-chart-type="bar"><i class="fa fa-circle{{^bar}}-o{{/bar}}"></i> Bar</a></li>
<li class="dropdown-header">Plot Metric</li>
{{#plot}}
<li><a href="javascript:void(0);" data-panel="{{id}}" data-plot="{{className}}" class="panel-plot-{{className}}"><i class="fa fa-circle{{^selected}}-o{{/selected}}"></i> {{label}}</a></li>
{{/plot}}
{{/plot.length}}
<li class="dropdown-header">Table Columns</li>
{{#items}}
<li><a href="javascript:void(0);" data-panel="{{id}}" data-metric="{{key}}"><i class="fa fa-{{^hide}}check-{{/hide}}square-o"></i> {{label}}</a></li>
{{/items}}
</script>
<!-- TPL Table colgroup -->
<script id="tpl-table-colgroup" type="text/template">
{{#hasSubItems}}
<col style="width: 2%;"> <!-- right-caret -->
{{/hasSubItems}}
<col style="width: 3%;"> <!-- row # -->
{{#items}}
<col style="width:{{colWidth}}">
{{/items}}
</script>
<!-- TPL Panel -->
<script id="tpl-panel" type="text/template">
<div class="row">
<div class="col-md-12">
<div class="form-group clearfix panel-header">
<h4 class="pull-left hidden-xs gheader" id="{{id}}">{{head}}<br><small>{{desc}}</small></h4>
<h5 class="pull-left visible-xs hidden-sm hidden-md hidden-lg gheader" id="{{id}}">{{head}}<br><small>{{desc}}</small></h5>
<div class="panel-plot-wrap">
<div class="dropdown">
<button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" data-panel="{{id}}">
<i class="fa fa-gear"></i> Panel Options <span class="fa fa-caret-down"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right panel-opts-{{id}}">
</ul>
</div>
</div>
</div>
</div>
</div>
{{#plot.length}}
<div class="row">
<div class="col-md-12">
<div id="chart-{{id}}" class="chart-wrap"></div>
</div>
</div>
{{/plot.length}}
{{#table}}
<div class="row clearfix table-wrapper {{#autoHideTables}}hidden-xs{{/autoHideTables}}">
<div class="col-md-12">
<div class="table-responsive">
<table data-panel="{{id}}" class="table table-borderless table-hover table-{{id}}">
<colgroup>
</colgroup>
<thead>
</thead>
<tbody class="tbody-meta">
</tbody>
<tbody class="tbody-data">
</tbody>
</table>
</div>
<ul class="pagination pagination-sm pull-left">
<li class="disabled">
<a class="panel-prev" href="javascript:void(0);" aria-label="Previous" data-panel="{{id}}">
<i class="fa fa-chevron-left"></i>
</a>
</li>
<li>
<a class="panel-next" href="javascript:void(0);" aria-label="Next" data-panel="{{id}}">
<i class="fa fa-chevron-right"></i>
</a>
</li>
</ul>
</div>
</div>
{{/table}}
</script>
<script id="tpl-nav-wrap" type="text/template">
<div class="nav-bars fa fa-bars"></div>
<div class="nav-gears fa fa-cog"></div>
<div class="nav-ws-status fa fa-circle"></div>
<div class="nav-list"></div>
<div class="powered hidden-xs hidden-sm">by <a href="https://goaccess.io/">GoAccess</a> and <a href="http://gwsocket.io/">GWSocket</a></div>
</script>
<script id="tpl-nav-menu" type="text/template">
<h3>Panels</h3>
<ul>
<li {{#overall}}class="active"{{/overall}}><a href="#"><i class="fa fa-bar-chart"></i> Overall Analyzed Requests</a></li>
{{#nav}}
<li {{#current}}class="active"{{/current}}><a href="#{{key}}"><i class="fa fa-{{icon}}"></i> {{head}}</a></li>
{{/nav}}
</ul>
</script>
<script id="tpl-nav-opts" type="text/template">
<h3><i class="fa fa-hashtag"></i> Theme</h3>
<ul>
<li {{#darkGray}}class="active"{{/darkGray}}>
<a href="javascript:void(0);" class="theme-dark-gray"><i class="fa fa-circle{{^darkGray}}-o{{/darkGray}}"></i> Dark Gray</a>
</li>
<li {{#bright}}class="active"{{/bright}}>
<a href="javascript:void(0);" class="theme-bright"><i class="fa fa-circle{{^bright}}-o{{/bright}}"></i> Bright</a>
</li>
<li {{#darkBlue}}class="active"{{/darkBlue}}>
<a href="javascript:void(0);" class="theme-dark-blue"><i class="fa fa-circle{{^darkBlue}}-o{{/darkBlue}}"></i> Dark Blue</a>
</li>
</ul>
<h3><i class="fa fa-list-alt"></i> Panels</h3>
<ul class="perpage-wrap">
<li class="dropdown-header"><i class="fa fa-list"></i> Items Per Page</li>
<li {{#perPage5}}class="active"{{/perPage5}}>
<a href="javascript:void(0);" data-perpage="5"><i class="fa fa-circle{{^perPage5}}-o{{/perPage5}}"></i> 5</a>
</li>
<li {{#perPage7}}class="active"{{/perPage7}}>
<a href="javascript:void(0);" data-perpage="7"><i class="fa fa-circle{{^perPage7}}-o{{/perPage7}}"></i> 7</a>
</li>
<li {{#perPage10}}class="active"{{/perPage10}}>
<a href="javascript:void(0);" data-perpage="10"><i class="fa fa-circle{{^perPage10}}-o{{/perPage10}}"></i> 10</a>
</li>
<li {{#perPage15}}class="active"{{/perPage15}}>
<a href="javascript:void(0);" data-perpage="15"><i class="fa fa-circle{{^perPage15}}-o{{/perPage15}}"></i> 15</a>
</li>
<li {{#perPage20}}class="active"{{/perPage20}}>
<a href="javascript:void(0);" data-perpage="20"><i class="fa fa-circle{{^perPage20}}-o{{/perPage20}}"></i> 20</a>
</li>
<li class="dropdown-header"><i class="fa fa-table"></i> Tables</li>
<li {{#showTables}}class="active"{{/showTables}}>
<a href="javascript:void(0);" data-show-tables="1"><i class="fa fa-{{#showTables}}check-{{/showTables}}square-o"></i> Display Tables</a>
</li>
<li {{#autoHideTables}}class="active"{{/autoHideTables}}>
<a href="javascript:void(0);" data-autohide-tables="1" title="Automatically hide tables on small screen devices">
<i class="fa fa-{{#autoHideTables}}check-{{/autoHideTables}}square-o"></i> Auto-hide on small devices
</a>
</li>
</ul>
<h3><i class="fa fa-th-large"></i> Layout</h3>
<ul>
<li {{#horizontal}}class="active"{{/horizontal}}>
<a href="javascript:void(0);" class="layout-horizontal"><i class="fa fa-circle{{^horizontal}}-o{{/horizontal}}"></i> Horizontal</a>
</li>
<li {{#vertical}}class="active"{{/vertical}}>
<a href="javascript:void(0);" class="layout-vertical"><i class="fa fa-circle{{^vertical}}-o{{/vertical}}"></i> Vertical</a>
</li>
</ul>
<h3><i class="fa fa-cog"></i> File Options</h3>
<ul>
<li><a href="javascript:void(0);" class="export-json"><i class="fa fa-code"></i> Export as JSON</a></li>
</ul>
</script>
<script id="tpl-chart-tooltip" type="text/template">
<table class="chart-tooltip">
<tbody>
<tr>
<th colspan="2">{{data.0}}</th>
</tr>
<tr>
<td class="name"><span class="blue"></span>hits</td>
<td class="value">{{data.1}}</td>
</tr>
{{#data.2}}
<tr>
<td class="name"><span class="red"></span>visitors</td>
<td class="value">{{data.2}}</td>
</tr>
{{/data.2}}
</tbody>
</table>
</script>
<script type='text/javascript'>var html_prefs={};var user_interface={"general": {"head": "Overall Analyzed Requests","desc": "","items": {"total_requests": {"className": "black","dataType": "numeric","label": "Total Requests"},"valid_requests": {"className": "green","dataType": "numeric","label": "Valid Requests"},"failed_requests": {"className": "red","dataType": "numeric","label": "Failed Requests"},"generation_time": {"className": "gray","dataType": "secs","label": "Init. Proc. Time"},"unique_visitors": {"className": "blue","dataType": "numeric","label": "Unique Visitors"},"unique_files": {"dataType": "numeric","label": "Unique Files"},"excluded_hits": {"dataType": "numeric","label": "Excl. IP Hits"},"unique_referrers": {"dataType": "numeric","label": "Referrers"},"unique_not_found": {"dataType": "numeric","label": "Unique 404"},"unique_static_files": {"dataType": "numeric","label": "Static Files"},"log_size": {"dataType": "bytes","label": "Log Size"},"bandwidth": {"dataType": "bytes","label": "Bandwidth"}}},"visitors": {"head": "Unique visitors per day - Including spiders","desc": "Hits having the same IP, date and agent are a unique visit.","id": "visitors","table": 1,"sort": {"field": "data","order": "DESC"},"plot": [{"className": "hits-visitors","label": "Hits/Visitors","chartType": "area-spline","chartReverse": 1,"redrawOnExpand": 1,"d3": {"y0": {"key": "hits","label": "Hits"},"y1": {"key": "visitors","label": "Visitors"}}},{"className": "bandwidth","label": "Bandwidth","chartType": "area-spline","chartReverse": 1,"redrawOnExpand": 1,"d3": {"y0": {"key": "bytes","label": "Bandwidth","format": "bytes"}}}],"items": [{"colWidth": "12%","meta": "count","dataType": "numeric","key": "hits","label": "Hits"},{"colWidth": "12%","meta": "count","dataType": "numeric","key": "visitors","label": "Visitors"},{"colWidth": "12%","meta": "count","dataType": "bytes","key": "bytes","label": "Bandwidth"},{"className": "trunc","colWidth": "100%","meta": "unique","metaType": "numeric","metaLabel": "Total","dataType": "date","key": "data","label": "Data"}]},"requests": {"head": "Requested Files (URLs)","desc": "Top requests sorted by hits [, avgts, cumts, maxts, mthd, proto]","id": "requests","table": 1,"sort": {"field": "hits","order": "DESC"},"plot": [{"className": "hits-visitors","label": "Hits/Visitors","chartType": "bar","chartReverse": 0,"redrawOnExpand": 0,"d3": {"x": {"key": ["method", "data", "protocol"]},"y0": {"key": "hits","label": "Hits"},"y1": {"key": "visitors","label": "Visitors"}}},{"className": "bandwidth","label": "Bandwidth","chartType": "bar","chartReverse": 0,"redrawOnExpand": 0,"d3": {"x": {"key": ["method", "protocol", "data"]},"y0": {"key": "bytes","label": "Bandwidth","format": "bytes"}}}],"items": [{"colWidth": "12%","meta": "count","dataType": "numeric","key": "hits","label": "Hits"},{"colWidth": "12%","meta": "count","dataType": "numeric","key": "visitors","label": "Visitors"},{"colWidth": "12%","meta": "count","dataType": "bytes","key": "bytes","label": "Bandwidth"},{"colWidth": "6%","dataType": "string","key": "method","label": "Method"},{"colWidth": "7%","dataType": "string","key": "protocol","label": "Protocol"},{"className": "trunc","colWidth": "100%","meta": "unique","metaType": "numeric","metaLabel": "Total","dataType": "string","key": "data","label": "Data"}]},"static_requests": {"head": "Static Requests","desc": "Top static requests sorted by hits [, avgts, cumts, maxts, mthd, proto]","id": "static_requests","table": 1,"sort": {"field": "hits","order": "DESC"},"plot": [{"className": "hits-visitors","label": "Hits/Visitors","chartType": "bar","chartReverse": 0,"redrawOnExpand": 0,"d3": {"x": {"key": ["method", "data", "protocol"]},"y0": {"key": "hits","label": "Hits"},"y1": {"key": "visitors","label": "Visitors"}}},{"className": "bandwidth","label": "Bandwidth","chartType": "bar","chartReverse": 0,"redrawOnExpand": 0,"d3": {"x": {"key": ["method", "protocol", "data"]},"y0": {"key": "bytes","label": "Bandwidth","format": "bytes"}}}],"items": [{"colWidth": "12%","meta": "count","dataType": "numeric",
S=(r1-r0)/ρ;i=function(t){var s=t*S,coshr0=d3_cosh(r0),u=w0/(ρ2*d1)*(coshr0*d3_tanh(ρ*s+r0)-d3_sinh(r0));return[ux0+u*dx,uy0+u*dy,w0*coshr0/d3_cosh(ρ*s+r0)]}}i.duration=S*1e3;return i};d3.behavior.zoom=function(){var view={x:0,y:0,k:1},translate0,center0,center,size=[960,500],scaleExtent=d3_behavior_zoomInfinity,duration=250,zooming=0,mousedown="mousedown.zoom",mousemove="mousemove.zoom",mouseup="mouseup.zoom",mousewheelTimer,touchstart="touchstart.zoom",touchtime,event=d3_eventDispatch(zoom,"zoomstart","zoom","zoomend"),x0,x1,y0,y1;if(!d3_behavior_zoomWheel){d3_behavior_zoomWheel="onwheel"in d3_document?(d3_behavior_zoomDelta=function(){return-d3.event.deltaY*(d3.event.deltaMode?120:1)},"wheel"):"onmousewheel"in d3_document?(d3_behavior_zoomDelta=function(){return d3.event.wheelDelta},"mousewheel"):(d3_behavior_zoomDelta=function(){return-d3.event.detail},"MozMousePixelScroll")}function zoom(g){g.on(mousedown,mousedowned).on(d3_behavior_zoomWheel+".zoom",mousewheeled).on("dblclick.zoom",dblclicked).on(touchstart,touchstarted)}zoom.event=function(g){g.each(function(){var dispatch=event.of(this,arguments),view1=view;if(d3_transitionInheritId){d3.select(this).transition().each("start.zoom",function(){view=this.__chart__||{x:0,y:0,k:1};zoomstarted(dispatch)}).tween("zoom:zoom",function(){var dx=size[0],dy=size[1],cx=center0?center0[0]:dx/2,cy=center0?center0[1]:dy/2,i=d3.interpolateZoom([(cx-view.x)/view.k,(cy-view.y)/view.k,dx/view.k],[(cx-view1.x)/view1.k,(cy-view1.y)/view1.k,dx/view1.k]);return function(t){var l=i(t),k=dx/l[2];this.__chart__=view={x:cx-l[0]*k,y:cy-l[1]*k,k:k};zoomed(dispatch)}}).each("interrupt.zoom",function(){zoomended(dispatch)}).each("end.zoom",function(){zoomended(dispatch)})}else{this.__chart__=view;zoomstarted(dispatch);zoomed(dispatch);zoomended(dispatch)}})};zoom.translate=function(_){if(!arguments.length)return[view.x,view.y];view={x:+_[0],y:+_[1],k:view.k};rescale();return zoom};zoom.scale=function(_){if(!arguments.length)return view.k;view={x:view.x,y:view.y,k:null};scaleTo(+_);rescale();return zoom};zoom.scaleExtent=function(_){if(!arguments.length)return scaleExtent;scaleExtent=_==null?d3_behavior_zoomInfinity:[+_[0],+_[1]];return zoom};zoom.center=function(_){if(!arguments.length)return center;center=_&&[+_[0],+_[1]];return zoom};zoom.size=function(_){if(!arguments.length)return size;size=_&&[+_[0],+_[1]];return zoom};zoom.duration=function(_){if(!arguments.length)return duration;duration=+_;return zoom};zoom.x=function(z){if(!arguments.length)return x1;x1=z;x0=z.copy();view={x:0,y:0,k:1};return zoom};zoom.y=function(z){if(!arguments.length)return y1;y1=z;y0=z.copy();view={x:0,y:0,k:1};return zoom};function location(p){return[(p[0]-view.x)/view.k,(p[1]-view.y)/view.k]}function point(l){return[l[0]*view.k+view.x,l[1]*view.k+view.y]}function scaleTo(s){view.k=Math.max(scaleExtent[0],Math.min(scaleExtent[1],s))}function translateTo(p,l){l=point(l);view.x+=p[0]-l[0];view.y+=p[1]-l[1]}function zoomTo(that,p,l,k){that.__chart__={x:view.x,y:view.y,k:view.k};scaleTo(Math.pow(2,k));translateTo(center0=p,l);that=d3.select(that);if(duration>0)that=that.transition().duration(duration);that.call(zoom.event)}function rescale(){if(x1)x1.domain(x0.range().map(function(x){return(x-view.x)/view.k}).map(x0.invert));if(y1)y1.domain(y0.range().map(function(y){return(y-view.y)/view.k}).map(y0.invert))}function zoomstarted(dispatch){if(!zooming++)dispatch({type:"zoomstart"})}function zoomed(dispatch){rescale();dispatch({type:"zoom",scale:view.k,translate:[view.x,view.y]})}function zoomended(dispatch){if(!--zooming)dispatch({type:"zoomend"}),center0=null}function mousedowned(){var that=this,dispatch=event.of(that,arguments),dragged=0,subject=d3.select(d3_window(that)).on(mousemove,moved).on(mouseup,ended),location0=location(d3.mouse(that)),dragRestore=d3_event_dragSuppress(that);d3_selection_interrupt.call(that);zoomstarted(dispatch);function moved(){dragged=1;translateTo(d3.mouse(that),location0);zoomed(dispatch)}function ended(){subject.on(mousemove,null).on(mouseup,null);dragRestore(dragged);zoomended(dis
locale_periods.forEach(function(p,i){d3_time_periodLookup.set(p.toLowerCase(),i)});var d3_time_formats={a:function(d){return locale_shortDays[d.getDay()]},A:function(d){return locale_days[d.getDay()]},b:function(d){return locale_shortMonths[d.getMonth()]},B:function(d){return locale_months[d.getMonth()]},c:d3_time_format(locale_dateTime),d:function(d,p){return d3_time_formatPad(d.getDate(),p,2)},e:function(d,p){return d3_time_formatPad(d.getDate(),p,2)},H:function(d,p){return d3_time_formatPad(d.getHours(),p,2)},I:function(d,p){return d3_time_formatPad(d.getHours()%12||12,p,2)},j:function(d,p){return d3_time_formatPad(1+d3_time.dayOfYear(d),p,3)},L:function(d,p){return d3_time_formatPad(d.getMilliseconds(),p,3)},m:function(d,p){return d3_time_formatPad(d.getMonth()+1,p,2)},M:function(d,p){return d3_time_formatPad(d.getMinutes(),p,2)},p:function(d){return locale_periods[+(d.getHours()>=12)]},S:function(d,p){return d3_time_formatPad(d.getSeconds(),p,2)},U:function(d,p){return d3_time_formatPad(d3_time.sundayOfYear(d),p,2)},w:function(d){return d.getDay()},W:function(d,p){return d3_time_formatPad(d3_time.mondayOfYear(d),p,2)},x:d3_time_format(locale_date),X:d3_time_format(locale_time),y:function(d,p){return d3_time_formatPad(d.getFullYear()%100,p,2)},Y:function(d,p){return d3_time_formatPad(d.getFullYear()%1e4,p,4)},Z:d3_time_zone,"%":function(){return"%"}};var d3_time_parsers={a:d3_time_parseWeekdayAbbrev,A:d3_time_parseWeekday,b:d3_time_parseMonthAbbrev,B:d3_time_parseMonth,c:d3_time_parseLocaleFull,d:d3_time_parseDay,e:d3_time_parseDay,H:d3_time_parseHour24,I:d3_time_parseHour24,j:d3_time_parseDayOfYear,L:d3_time_parseMilliseconds,m:d3_time_parseMonthNumber,M:d3_time_parseMinutes,p:d3_time_parseAmPm,S:d3_time_parseSeconds,U:d3_time_parseWeekNumberSunday,w:d3_time_parseWeekdayNumber,W:d3_time_parseWeekNumberMonday,x:d3_time_parseLocaleDate,X:d3_time_parseLocaleTime,y:d3_time_parseYear,Y:d3_time_parseFullYear,Z:d3_time_parseZone,"%":d3_time_parseLiteralPercent};function d3_time_parseWeekdayAbbrev(date,string,i){d3_time_dayAbbrevRe.lastIndex=0;var n=d3_time_dayAbbrevRe.exec(string.slice(i));return n?(date.w=d3_time_dayAbbrevLookup.get(n[0].toLowerCase()),i+n[0].length):-1}function d3_time_parseWeekday(date,string,i){d3_time_dayRe.lastIndex=0;var n=d3_time_dayRe.exec(string.slice(i));return n?(date.w=d3_time_dayLookup.get(n[0].toLowerCase()),i+n[0].length):-1}function d3_time_parseMonthAbbrev(date,string,i){d3_time_monthAbbrevRe.lastIndex=0;var n=d3_time_monthAbbrevRe.exec(string.slice(i));return n?(date.m=d3_time_monthAbbrevLookup.get(n[0].toLowerCase()),i+n[0].length):-1}function d3_time_parseMonth(date,string,i){d3_time_monthRe.lastIndex=0;var n=d3_time_monthRe.exec(string.slice(i));return n?(date.m=d3_time_monthLookup.get(n[0].toLowerCase()),i+n[0].length):-1}function d3_time_parseLocaleFull(date,string,i){return d3_time_parse(date,d3_time_formats.c.toString(),string,i)}function d3_time_parseLocaleDate(date,string,i){return d3_time_parse(date,d3_time_formats.x.toString(),string,i)}function d3_time_parseLocaleTime(date,string,i){return d3_time_parse(date,d3_time_formats.X.toString(),string,i)}function d3_time_parseAmPm(date,string,i){var n=d3_time_periodLookup.get(string.slice(i,i+=2).toLowerCase());return n==null?-1:(date.p=n,i)}return d3_time_format}var d3_time_formatPads={"-":"",_:" ",0:"0"},d3_time_numberRe=/^\s*\d+/,d3_time_percentRe=/^%/;function d3_time_formatPad(value,fill,width){var sign=value<0?"-":"",string=(sign?-value:value)+"",length=string.length;return sign+(length<width?new Array(width-length+1).join(fill)+string:string)}function d3_time_formatRe(names){return new RegExp("^(?:"+names.map(d3.requote).join("|")+")","i")}function d3_time_formatLookup(names){var map=new d3_Map,i=-1,n=names.length;while(++i<n)map.set(names[i].toLowerCase(),i);return map}function d3_time_parseWeekdayNumber(date,string,i){d3_time_numberRe.lastIndex=0;var n=d3_time_numberRe.exec(string.slice(i,i+1));return n?(date.w=+n[0],i+n[0].length):-1}function d3_time_parseWeekNumberSunday(date,string,i){d3_time_numberRe.lastIndex=0;var n=d
};d3.geo.albersUsa=function(){var lower48=d3.geo.albers();var alaska=d3.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]);var hawaii=d3.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]);var point,pointStream={point:function(x,y){point=[x,y]}},lower48Point,alaskaPoint,hawaiiPoint;function albersUsa(coordinates){var x=coordinates[0],y=coordinates[1];point=null;(lower48Point(x,y),point)||(alaskaPoint(x,y),point)||hawaiiPoint(x,y);return point}albersUsa.invert=function(coordinates){var k=lower48.scale(),t=lower48.translate(),x=(coordinates[0]-t[0])/k,y=(coordinates[1]-t[1])/k;return(y>=.12&&y<.234&&x>=-.425&&x<-.214?alaska:y>=.166&&y<.234&&x>=-.214&&x<-.115?hawaii:lower48).invert(coordinates)};albersUsa.stream=function(stream){var lower48Stream=lower48.stream(stream),alaskaStream=alaska.stream(stream),hawaiiStream=hawaii.stream(stream);return{point:function(x,y){lower48Stream.point(x,y);alaskaStream.point(x,y);hawaiiStream.point(x,y)},sphere:function(){lower48Stream.sphere();alaskaStream.sphere();hawaiiStream.sphere()},lineStart:function(){lower48Stream.lineStart();alaskaStream.lineStart();hawaiiStream.lineStart()},lineEnd:function(){lower48Stream.lineEnd();alaskaStream.lineEnd();hawaiiStream.lineEnd()},polygonStart:function(){lower48Stream.polygonStart();alaskaStream.polygonStart();hawaiiStream.polygonStart()},polygonEnd:function(){lower48Stream.polygonEnd();alaskaStream.polygonEnd();hawaiiStream.polygonEnd()}}};albersUsa.precision=function(_){if(!arguments.length)return lower48.precision();lower48.precision(_);alaska.precision(_);hawaii.precision(_);return albersUsa};albersUsa.scale=function(_){if(!arguments.length)return lower48.scale();lower48.scale(_);alaska.scale(_*.35);hawaii.scale(_);return albersUsa.translate(lower48.translate())};albersUsa.translate=function(_){if(!arguments.length)return lower48.translate();var k=lower48.scale(),x=+_[0],y=+_[1];lower48Point=lower48.translate(_).clipExtent([[x-.455*k,y-.238*k],[x+.455*k,y+.238*k]]).stream(pointStream).point;alaskaPoint=alaska.translate([x-.307*k,y+.201*k]).clipExtent([[x-.425*k+ε,y+.12*k+ε],[x-.214*k-ε,y+.234*k-ε]]).stream(pointStream).point;hawaiiPoint=hawaii.translate([x-.205*k,y+.212*k]).clipExtent([[x-.214*k+ε,y+.166*k+ε],[x-.115*k-ε,y+.234*k-ε]]).stream(pointStream).point;return albersUsa};return albersUsa.scale(1070)};var d3_geo_pathAreaSum,d3_geo_pathAreaPolygon,d3_geo_pathArea={point:d3_noop,lineStart:d3_noop,lineEnd:d3_noop,polygonStart:function(){d3_geo_pathAreaPolygon=0;d3_geo_pathArea.lineStart=d3_geo_pathAreaRingStart},polygonEnd:function(){d3_geo_pathArea.lineStart=d3_geo_pathArea.lineEnd=d3_geo_pathArea.point=d3_noop;d3_geo_pathAreaSum+=abs(d3_geo_pathAreaPolygon/2)}};function d3_geo_pathAreaRingStart(){var x00,y00,x0,y0;d3_geo_pathArea.point=function(x,y){d3_geo_pathArea.point=nextPoint;x00=x0=x,y00=y0=y};function nextPoint(x,y){d3_geo_pathAreaPolygon+=y0*x-x0*y;x0=x,y0=y}d3_geo_pathArea.lineEnd=function(){nextPoint(x00,y00)}}var d3_geo_pathBoundsX0,d3_geo_pathBoundsY0,d3_geo_pathBoundsX1,d3_geo_pathBoundsY1;var d3_geo_pathBounds={point:d3_geo_pathBoundsPoint,lineStart:d3_noop,lineEnd:d3_noop,polygonStart:d3_noop,polygonEnd:d3_noop};function d3_geo_pathBoundsPoint(x,y){if(x<d3_geo_pathBoundsX0)d3_geo_pathBoundsX0=x;if(x>d3_geo_pathBoundsX1)d3_geo_pathBoundsX1=x;if(y<d3_geo_pathBoundsY0)d3_geo_pathBoundsY0=y;if(y>d3_geo_pathBoundsY1)d3_geo_pathBoundsY1=y}function d3_geo_pathBuffer(){var pointCircle=d3_geo_pathBufferCircle(4.5),buffer=[];var stream={point:point,lineStart:function(){stream.point=pointLineStart},lineEnd:lineEnd,polygonStart:function(){stream.lineEnd=lineEndPolygon},polygonEnd:function(){stream.lineEnd=lineEnd;stream.point=point},pointRadius:function(_){pointCircle=d3_geo_pathBufferCircle(_);return stream},result:function(){if(buffer.length){var result=buffer.join("");buffer=[];return result}}};function point(x,y){buffer.push("M",x,",",y,pointCircle)}function pointLineStart(x,y){buffer.push("M",x,",",y);stream.point=pointLine}function pointLine(x,y){buffer.push("L",x,",",y)}function
return(rfocx+lfocx)/2}function d3_geom_voronoiRightBreakPoint(arc,directrix){var rArc=arc.N;if(rArc)return d3_geom_voronoiLeftBreakPoint(rArc,directrix);var site=arc.site;return site.y===directrix?site.x:Infinity}function d3_geom_voronoiCell(site){this.site=site;this.edges=[]}d3_geom_voronoiCell.prototype.prepare=function(){var halfEdges=this.edges,iHalfEdge=halfEdges.length,edge;while(iHalfEdge--){edge=halfEdges[iHalfEdge].edge;if(!edge.b||!edge.a)halfEdges.splice(iHalfEdge,1)}halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);return halfEdges.length};function d3_geom_voronoiCloseCells(extent){var x0=extent[0][0],x1=extent[1][0],y0=extent[0][1],y1=extent[1][1],x2,y2,x3,y3,cells=d3_geom_voronoiCells,iCell=cells.length,cell,iHalfEdge,halfEdges,nHalfEdges,start,end;while(iCell--){cell=cells[iCell];if(!cell||!cell.prepare())continue;halfEdges=cell.edges;nHalfEdges=halfEdges.length;iHalfEdge=0;while(iHalfEdge<nHalfEdges){end=halfEdges[iHalfEdge].end(),x3=end.x,y3=end.y;start=halfEdges[++iHalfEdge%nHalfEdges].start(),x2=start.x,y2=start.y;if(abs(x3-x2)>ε||abs(y3-y2)>ε){halfEdges.splice(iHalfEdge,0,new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site,end,abs(x3-x0)<ε&&y1-y3>ε?{x:x0,y:abs(x2-x0)<ε?y2:y1}:abs(y3-y1)<ε&&x1-x3>ε?{x:abs(y2-y1)<ε?x2:x1,y:y1}:abs(x3-x1)<ε&&y3-y0>ε?{x:x1,y:abs(x2-x1)<ε?y2:y0}:abs(y3-y0)<ε&&x3-x0>ε?{x:abs(y2-y0)<ε?x2:x0,y:y0}:null),cell.site,null));++nHalfEdges}}}}function d3_geom_voronoiHalfEdgeOrder(a,b){return b.angle-a.angle}function d3_geom_voronoiCircle(){d3_geom_voronoiRedBlackNode(this);this.x=this.y=this.arc=this.site=this.cy=null}function d3_geom_voronoiAttachCircle(arc){var lArc=arc.P,rArc=arc.N;if(!lArc||!rArc)return;var lSite=lArc.site,cSite=arc.site,rSite=rArc.site;if(lSite===rSite)return;var bx=cSite.x,by=cSite.y,ax=lSite.x-bx,ay=lSite.y-by,cx=rSite.x-bx,cy=rSite.y-by;var d=2*(ax*cy-ay*cx);if(d>=-ε2)return;var ha=ax*ax+ay*ay,hc=cx*cx+cy*cy,x=(cy*ha-ay*hc)/d,y=(ax*hc-cx*ha)/d,cy=y+by;var circle=d3_geom_voronoiCirclePool.pop()||new d3_geom_voronoiCircle;circle.arc=arc;circle.site=cSite;circle.x=x+bx;circle.y=cy+Math.sqrt(x*x+y*y);circle.cy=cy;arc.circle=circle;var before=null,node=d3_geom_voronoiCircles._;while(node){if(circle.y<node.y||circle.y===node.y&&circle.x<=node.x){if(node.L)node=node.L;else{before=node.P;break}}else{if(node.R)node=node.R;else{before=node;break}}}d3_geom_voronoiCircles.insert(before,circle);if(!before)d3_geom_voronoiFirstCircle=circle}function d3_geom_voronoiDetachCircle(arc){var circle=arc.circle;if(circle){if(!circle.P)d3_geom_voronoiFirstCircle=circle.N;d3_geom_voronoiCircles.remove(circle);d3_geom_voronoiCirclePool.push(circle);d3_geom_voronoiRedBlackNode(circle);arc.circle=null}}function d3_geom_voronoiClipEdges(extent){var edges=d3_geom_voronoiEdges,clip=d3_geom_clipLine(extent[0][0],extent[0][1],extent[1][0],extent[1][1]),i=edges.length,e;while(i--){e=edges[i];if(!d3_geom_voronoiConnectEdge(e,extent)||!clip(e)||abs(e.a.x-e.b.x)<ε&&abs(e.a.y-e.b.y)<ε){e.a=e.b=null;edges.splice(i,1)}}}function d3_geom_voronoiConnectEdge(edge,extent){var vb=edge.b;if(vb)return true;var va=edge.a,x0=extent[0][0],x1=extent[1][0],y0=extent[0][1],y1=extent[1][1],lSite=edge.l,rSite=edge.r,lx=lSite.x,ly=lSite.y,rx=rSite.x,ry=rSite.y,fx=(lx+rx)/2,fy=(ly+ry)/2,fm,fb;if(ry===ly){if(fx<x0||fx>=x1)return;if(lx>rx){if(!va)va={x:fx,y:y0};else if(va.y>=y1)return;vb={x:fx,y:y1}}else{if(!va)va={x:fx,y:y1};else if(va.y<y0)return;vb={x:fx,y:y0}}}else{fm=(lx-rx)/(ry-ly);fb=fy-fm*fx;if(fm<-1||fm>1){if(lx>rx){if(!va)va={x:(y0-fb)/fm,y:y0};else if(va.y>=y1)return;vb={x:(y1-fb)/fm,y:y1}}else{if(!va)va={x:(y1-fb)/fm,y:y1};else if(va.y<y0)return;vb={x:(y0-fb)/fm,y:y0}}}else{if(ly<ry){if(!va)va={x:x0,y:fm*x0+fb};else if(va.x>=x1)return;vb={x:x1,y:fm*x1+fb}}else{if(!va)va={x:x1,y:fm*x1+fb};else if(va.x<x0)return;vb={x:x0,y:fm*x0+fb}}}}edge.a=va;edge.b=vb;return true}function d3_geom_voronoiEdge(lSite,rSite){this.l=lSite;this.r=rSite;this.a=this.b=null}function d3_geom_voronoiCreateEdge(lSite,rSite,va,vb){var edge=new d3_geom_voronoiEdge(lSite,rSite);d3_geom_voronoiEdges.push(
delete node.children}}d3_layout_hierarchyVisitAfter(root,function(node){var childs,parent;if(sort&&(childs=node.children))childs.sort(sort);if(value&&(parent=node.parent))parent.value+=node.value});return nodes}hierarchy.sort=function(x){if(!arguments.length)return sort;sort=x;return hierarchy};hierarchy.children=function(x){if(!arguments.length)return children;children=x;return hierarchy};hierarchy.value=function(x){if(!arguments.length)return value;value=x;return hierarchy};hierarchy.revalue=function(root){if(value){d3_layout_hierarchyVisitBefore(root,function(node){if(node.children)node.value=0});d3_layout_hierarchyVisitAfter(root,function(node){var parent;if(!node.children)node.value=+value.call(hierarchy,node,node.depth)||0;if(parent=node.parent)parent.value+=node.value})}return root};return hierarchy};function d3_layout_hierarchyRebind(object,hierarchy){d3.rebind(object,hierarchy,"sort","children","value");object.nodes=object;object.links=d3_layout_hierarchyLinks;return object}function d3_layout_hierarchyVisitBefore(node,callback){var nodes=[node];while((node=nodes.pop())!=null){callback(node);if((children=node.children)&&(n=children.length)){var n,children;while(--n>=0)nodes.push(children[n])}}}function d3_layout_hierarchyVisitAfter(node,callback){var nodes=[node],nodes2=[];while((node=nodes.pop())!=null){nodes2.push(node);if((children=node.children)&&(n=children.length)){var i=-1,n,children;while(++i<n)nodes.push(children[i])}}while((node=nodes2.pop())!=null){callback(node)}}function d3_layout_hierarchyChildren(d){return d.children}function d3_layout_hierarchyValue(d){return d.value}function d3_layout_hierarchySort(a,b){return b.value-a.value}function d3_layout_hierarchyLinks(nodes){return d3.merge(nodes.map(function(parent){return(parent.children||[]).map(function(child){return{source:parent,target:child}})}))}d3.layout.partition=function(){var hierarchy=d3.layout.hierarchy(),size=[1,1];function position(node,x,dx,dy){var children=node.children;node.x=x;node.y=node.depth*dy;node.dx=dx;node.dy=dy;if(children&&(n=children.length)){var i=-1,n,c,d;dx=node.value?dx/node.value:0;while(++i<n){position(c=children[i],x,d=c.value*dx,dy);x+=d}}}function depth(node){var children=node.children,d=0;if(children&&(n=children.length)){var i=-1,n;while(++i<n)d=Math.max(d,depth(children[i]))}return 1+d}function partition(d,i){var nodes=hierarchy.call(this,d,i);position(nodes[0],0,size[0],size[1]/depth(nodes[0]));return nodes}partition.size=function(x){if(!arguments.length)return size;size=x;return partition};return d3_layout_hierarchyRebind(partition,hierarchy)};d3.layout.pie=function(){var value=Number,sort=d3_layout_pieSortByValue,startAngle=0,endAngle=τ,padAngle=0;function pie(data){var n=data.length,values=data.map(function(d,i){return+value.call(pie,d,i)}),a=+(typeof startAngle==="function"?startAngle.apply(this,arguments):startAngle),da=(typeof endAngle==="function"?endAngle.apply(this,arguments):endAngle)-a,p=Math.min(Math.abs(da)/n,+(typeof padAngle==="function"?padAngle.apply(this,arguments):padAngle)),pa=p*(da<0?-1:1),sum=d3.sum(values),k=sum?(da-n*pa)/sum:0,index=d3.range(n),arcs=[],v;if(sort!=null)index.sort(sort===d3_layout_pieSortByValue?function(i,j){return values[j]-values[i]}:function(i,j){return sort(data[i],data[j])});index.forEach(function(i){arcs[i]={data:data[i],value:v=values[i],startAngle:a,endAngle:a+=v*k+pa,padAngle:p}});return arcs}pie.value=function(_){if(!arguments.length)return value;value=_;return pie};pie.sort=function(_){if(!arguments.length)return sort;sort=_;return pie};pie.startAngle=function(_){if(!arguments.length)return startAngle;startAngle=_;return pie};pie.endAngle=function(_){if(!arguments.length)return endAngle;endAngle=_;return pie};pie.padAngle=function(_){if(!arguments.length)return padAngle;padAngle=_;return pie};return pie};var d3_layout_pieSortByValue={};d3.layout.stack=function(){var values=d3_identity,order=d3_layout_stackOrderDefault,offset=d3_layout_stackOffsetZero,out=d3_layout_stackOut,x=d3_layout_stackX,y=d3_layout_stackY;function stack(data,index){if(!(n=data.length))retu
d3.scale.quantile=function(){return d3_scale_quantile([],[])};function d3_scale_quantile(domain,range){var thresholds;function rescale(){var k=0,q=range.length;thresholds=[];while(++k<q)thresholds[k-1]=d3.quantile(domain,k/q);return scale}function scale(x){if(!isNaN(x=+x))return range[d3.bisect(thresholds,x)]}scale.domain=function(x){if(!arguments.length)return domain;domain=x.map(d3_number).filter(d3_numeric).sort(d3_ascending);return rescale()};scale.range=function(x){if(!arguments.length)return range;range=x;return rescale()};scale.quantiles=function(){return thresholds};scale.invertExtent=function(y){y=range.indexOf(y);return y<0?[NaN,NaN]:[y>0?thresholds[y-1]:domain[0],y<thresholds.length?thresholds[y]:domain[domain.length-1]]};scale.copy=function(){return d3_scale_quantile(domain,range)};return rescale()}d3.scale.quantize=function(){return d3_scale_quantize(0,1,[0,1])};function d3_scale_quantize(x0,x1,range){var kx,i;function scale(x){return range[Math.max(0,Math.min(i,Math.floor(kx*(x-x0))))]}function rescale(){kx=range.length/(x1-x0);i=range.length-1;return scale}scale.domain=function(x){if(!arguments.length)return[x0,x1];x0=+x[0];x1=+x[x.length-1];return rescale()};scale.range=function(x){if(!arguments.length)return range;range=x;return rescale()};scale.invertExtent=function(y){y=range.indexOf(y);y=y<0?NaN:y/kx+x0;return[y,y+1/kx]};scale.copy=function(){return d3_scale_quantize(x0,x1,range)};return rescale()}d3.scale.threshold=function(){return d3_scale_threshold([.5],[0,1])};function d3_scale_threshold(domain,range){function scale(x){if(x<=x)return range[d3.bisect(domain,x)]}scale.domain=function(_){if(!arguments.length)return domain;domain=_;return scale};scale.range=function(_){if(!arguments.length)return range;range=_;return scale};scale.invertExtent=function(y){y=range.indexOf(y);return[domain[y-1],domain[y]]};scale.copy=function(){return d3_scale_threshold(domain,range)};return scale}d3.scale.identity=function(){return d3_scale_identity([0,1])};function d3_scale_identity(domain){function identity(x){return+x}identity.invert=identity;identity.domain=identity.range=function(x){if(!arguments.length)return domain;domain=x.map(identity);return identity};identity.ticks=function(m){return d3_scale_linearTicks(domain,m)};identity.tickFormat=function(m,format){return d3_scale_linearTickFormat(domain,m,format)};identity.copy=function(){return d3_scale_identity(domain)};return identity}d3.svg={};function d3_zero(){return 0}d3.svg.arc=function(){var innerRadius=d3_svg_arcInnerRadius,outerRadius=d3_svg_arcOuterRadius,cornerRadius=d3_zero,padRadius=d3_svg_arcAuto,startAngle=d3_svg_arcStartAngle,endAngle=d3_svg_arcEndAngle,padAngle=d3_svg_arcPadAngle;function arc(){var r0=Math.max(0,+innerRadius.apply(this,arguments)),r1=Math.max(0,+outerRadius.apply(this,arguments)),a0=startAngle.apply(this,arguments)-halfπ,a1=endAngle.apply(this,arguments)-halfπ,da=Math.abs(a1-a0),cw=a0>a1?0:1;if(r1<r0)rc=r1,r1=r0,r0=rc;if(da>=τε)return circleSegment(r1,cw)+(r0?circleSegment(r0,1-cw):"")+"Z";var rc,cr,rp,ap,p0=0,p1=0,x0,y0,x1,y1,x2,y2,x3,y3,path=[];if(ap=(+padAngle.apply(this,arguments)||0)/2){rp=padRadius===d3_svg_arcAuto?Math.sqrt(r0*r0+r1*r1):+padRadius.apply(this,arguments);if(!cw)p1*=-1;if(r1)p1=d3_asin(rp/r1*Math.sin(ap));if(r0)p0=d3_asin(rp/r0*Math.sin(ap))}if(r1){x0=r1*Math.cos(a0+p1);y0=r1*Math.sin(a0+p1);x1=r1*Math.cos(a1-p1);y1=r1*Math.sin(a1-p1);var l1=Math.abs(a1-a0-2*p1)<=π?0:1;if(p1&&d3_svg_arcSweep(x0,y0,x1,y1)===cw^l1){var h1=(a0+a1)/2;x0=r1*Math.cos(h1);y0=r1*Math.sin(h1);x1=y1=null}}else{x0=y0=0}if(r0){x2=r0*Math.cos(a1-p0);y2=r0*Math.sin(a1-p0);x3=r0*Math.cos(a0+p0);y3=r0*Math.sin(a0+p0);var l0=Math.abs(a0-a1+2*p0)<=π?0:1;if(p0&&d3_svg_arcSweep(x2,y2,x3,y3)===1-cw^l0){var h0=(a0+a1)/2;x2=r0*Math.cos(h0);y2=r0*Math.sin(h0);x3=y3=null}}else{x2=y2=0}if(da>ε&&(rc=Math.min(Math.abs(r1-r0)/2,+cornerRadius.apply(this,arguments)))>.001){cr=r0<r1^cw?0:1;var rc1=rc,rc0=rc;if(da<π){var oc=x3==null?[x2,y2]:x1==null?[x0,y0]:d3_geom_polygonIntersect([x0,y0],[x3,y3],[x1,y1],[x2,y2]),ax=x0-oc[0],ay=y0-oc[1],bx=x1-oc[0],by=
var lineEnter=tickEnter.select("line"),lineUpdate=tickUpdate.select("line"),text=tick.select("text").text(tickFormat),textEnter=tickEnter.select("text"),textUpdate=tickUpdate.select("text"),sign=orient==="top"||orient==="left"?-1:1,x1,x2,y1,y2;if(orient==="bottom"||orient==="top"){tickTransform=d3_svg_axisX,x1="x",y1="y",x2="x2",y2="y2";text.attr("dy",sign<0?"0em":".71em").style("text-anchor","middle");pathUpdate.attr("d","M"+range[0]+","+sign*outerTickSize+"V0H"+range[1]+"V"+sign*outerTickSize)}else{tickTransform=d3_svg_axisY,x1="y",y1="x",x2="y2",y2="x2";text.attr("dy",".32em").style("text-anchor",sign<0?"end":"start");pathUpdate.attr("d","M"+sign*outerTickSize+","+range[0]+"H0V"+range[1]+"H"+sign*outerTickSize)}lineEnter.attr(y2,sign*innerTickSize);textEnter.attr(y1,sign*tickSpacing);lineUpdate.attr(x2,0).attr(y2,sign*innerTickSize);textUpdate.attr(x1,0).attr(y1,sign*tickSpacing);if(scale1.rangeBand){var x=scale1,dx=x.rangeBand()/2;scale0=scale1=function(d){return x(d)+dx}}else if(scale0.rangeBand){scale0=scale1}else{tickExit.call(tickTransform,scale1,scale0)}tickEnter.call(tickTransform,scale0,scale1);tickUpdate.call(tickTransform,scale1,scale1)})}axis.scale=function(x){if(!arguments.length)return scale;scale=x;return axis};axis.orient=function(x){if(!arguments.length)return orient;orient=x in d3_svg_axisOrients?x+"":d3_svg_axisDefaultOrient;return axis};axis.ticks=function(){if(!arguments.length)return tickArguments_;tickArguments_=d3_array(arguments);return axis};axis.tickValues=function(x){if(!arguments.length)return tickValues;tickValues=x;return axis};axis.tickFormat=function(x){if(!arguments.length)return tickFormat_;tickFormat_=x;return axis};axis.tickSize=function(x){var n=arguments.length;if(!n)return innerTickSize;innerTickSize=+x;outerTickSize=+arguments[n-1];return axis};axis.innerTickSize=function(x){if(!arguments.length)return innerTickSize;innerTickSize=+x;return axis};axis.outerTickSize=function(x){if(!arguments.length)return outerTickSize;outerTickSize=+x;return axis};axis.tickPadding=function(x){if(!arguments.length)return tickPadding;tickPadding=+x;return axis};axis.tickSubdivide=function(){return arguments.length&&axis};return axis};var d3_svg_axisDefaultOrient="bottom",d3_svg_axisOrients={top:1,right:1,bottom:1,left:1};function d3_svg_axisX(selection,x0,x1){selection.attr("transform",function(d){var v0=x0(d);return"translate("+(isFinite(v0)?v0:x1(d))+",0)"})}function d3_svg_axisY(selection,y0,y1){selection.attr("transform",function(d){var v0=y0(d);return"translate(0,"+(isFinite(v0)?v0:y1(d))+")"})}d3.svg.brush=function(){var event=d3_eventDispatch(brush,"brushstart","brush","brushend"),x=null,y=null,xExtent=[0,0],yExtent=[0,0],xExtentDomain,yExtentDomain,xClamp=true,yClamp=true,resizes=d3_svg_brushResizes[0];function brush(g){g.each(function(){var g=d3.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",brushstart).on("touchstart.brush",brushstart);var background=g.selectAll(".background").data([0]);background.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair");g.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var resize=g.selectAll(".resize").data(resizes,d3_identity);resize.exit().remove();resize.enter().append("g").attr("class",function(d){return"resize "+d}).style("cursor",function(d){return d3_svg_brushCursor[d]}).append("rect").attr("x",function(d){return/[ew]$/.test(d)?-3:null}).attr("y",function(d){return/^[ns]/.test(d)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden");resize.style("display",brush.empty()?"none":null);var gUpdate=d3.transition(g),backgroundUpdate=d3.transition(background),range;if(x){range=d3_scaleRange(x);backgroundUpdate.attr("x",range[0]).attr("width",range[1]-range[0]);redrawX(gUpdate)}if(y){range=d3_scaleRange(y);backgroundUpdate.attr("y",range[0]).attr("height",range[1]-range[0]);redrawY(gUpdate)}redraw(gUpdate)})}brush.event=function(g){g.each(function(){var event_=event.of
</script><script>/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Hogan = {};
(function (Hogan, useArrayBuffer) {
Hogan.Template = function (renderFunc, text, compiler, options) {
this.r = renderFunc || this.r;
this.c = compiler;
this.options = options;
this.text = text || '';
this.buf = (useArrayBuffer) ? [] : '';
}
Hogan.Template.prototype = {
// render: replaced by generated code.
r: function (context, partials, indent) { return ''; },
// variable escaping
v: hoganEscape,
// triple stache
t: coerceToString,
render: function render(context, partials, indent) {
return this.ri([context], partials || {}, indent);
},
// render internal -- a hook for overrides that catches partials too
ri: function (context, partials, indent) {
return this.r(context, partials, indent);
},
// tries to find a partial in the curent scope and render it
rp: function(name, context, partials, indent) {
var partial = partials[name];
if (!partial) {
return '';
}
if (this.c && typeof partial == 'string') {
partial = this.c.compile(partial, this.options);
}
return partial.ri(context, partials, indent);
},
// render a section
rs: function(context, partials, section) {
var tail = context[context.length - 1];
if (!isArray(tail)) {
section(context, partials, this);
return;
}
for (var i = 0; i < tail.length; i++) {
context.push(tail[i]);
section(context, partials, this);
context.pop();
}
},
// maybe start a section
s: function(val, ctx, partials, inverted, start, end, tags) {
var pass;
if (isArray(val) && val.length === 0) {
return false;
}
if (typeof val == 'function') {
val = this.ls(val, ctx, partials, inverted, start, end, tags);
}
pass = (val === '') || !!val;
if (!inverted && pass && ctx) {
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
}
return pass;
},
// find values with dotted names
d: function(key, ctx, partials, returnFound) {
var names = key.split('.'),
val = this.f(names[0], ctx, partials, returnFound),
cx = null;
if (key === '.' && isArray(ctx[ctx.length - 2])) {
return ctx[ctx.length - 1];
}
for (var i = 1; i < names.length; i++) {
if (val && typeof val == 'object' && names[i] in val) {
cx = val;
val = val[names[i]];
} else {
val = '';
}
}
if (returnFound && !val) {
return false;
}
if (!returnFound && typeof val == 'function') {
ctx.push(cx);
val = this.lv(val, ctx, partials);
ctx.pop();
}
return val;
},
// find values with normal names
f: function(key, ctx, partials, returnFound) {
var val = false,
v = null,
found = false;
for (var i = ctx.length - 1; i >= 0; i--) {
v = ctx[i];
if (v && typeof v == 'object' && key in v) {
val = v[key];
found = true;
break;
}
}
if (!found) {
return (returnFound) ? false : "";
}
if (!returnFound && typeof val == 'function') {
val = this.lv(val, ctx, partials);
}
return val;
},
// higher order templates
ho: function(val, cx, partials, text, tags) {
var compiler = this.c;
var options = this.options;
options.delimiters = tags;
var text = val.call(cx, text);
text = (text == null) ? String(text) : text.toString();
this.b(compiler.compile(text, options).render(cx, partials));
return false;
},
// template result buffering
b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
function(s) { this.buf += s; },
fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
function() { var r = this.buf; this.buf = ''; return r; },
// lambda replace section
ls: function(val, ctx, partials, inverted, start, end, tags) {
var cx = ctx[ctx.length - 1],
t = null;
if (!inverted && this.c && val.length > 0) {
return this.ho(val, cx, partials, this.text.substring(start, end), tags);
}
t = val.call(cx);
if (typeof t == 'function') {
if (inverted) {
return true;
} else if (this.c) {
return this.ho(t, cx, partials, this.text.substring(start, end), tags);
}
}
return t;
},
// lambda replace variable
lv: function(val, ctx, partials) {
var cx = ctx[ctx.length - 1];
var result = val.call(cx);
if (typeof result == 'function') {
result = coerceToString(result.call(cx));
if (this.c && ~result.indexOf("{\u007B")) {
return this.c.compile(result, this.options).render(cx, partials);
}
}
return coerceToString(result);
}
};
var rAmp = /&/g,
rLt = /</g,
rGt = />/g,
rApos =/\'/g,
rQuot = /\"/g,
hChars =/[&<>\"\']/;
function coerceToString(val) {
return String((val === null || val === undefined) ? '' : val);
}
function hoganEscape(str) {
str = coerceToString(str);
return hChars.test(str) ?
str
.replace(rAmp,'&amp;')
.replace(rLt,'&lt;')
.replace(rGt,'&gt;')
.replace(rApos,'&#39;')
.replace(rQuot, '&quot;') :
str;
}
var isArray = Array.isArray || function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
};
})(typeof exports !== 'undefined' ? exports : Hogan);
(function (Hogan) {
// Setup regex assignments
// remove whitespace according to Mustache spec
var rIsWhitespace = /\S/,
rQuot = /\"/g,
rNewline = /\n/g,
rCr = /\r/g,
rSlash = /\\/g,
tagTypes = {
'#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
'<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
};
Hogan.scan = function scan(text, delimiters) {
var len = text.length,
IN_TEXT = 0,
IN_TAG_TYPE = 1,
IN_TAG = 2,
state = IN_TEXT,
tagType = null,
tag = null,
buf = '',
tokens = [],
seenTag = false,
i = 0,
lineStart = 0,
otag = '{{',
ctag = '}}';
function addBuf() {
if (buf.length > 0) {
tokens.push(new String(buf));
buf = '';
}
}
function lineIsWhitespace() {
var isAllWhitespace = true;
for (var j = lineStart; j < tokens.length; j++) {
isAllWhitespace =
(tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
(!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
if (!isAllWhitespace) {
return false;
}
}
return isAllWhitespace;
}
function filterLine(haveSeenTag, noNewLine) {
addBuf();
if (haveSeenTag && lineIsWhitespace()) {
for (var j = lineStart, next; j < tokens.length; j++) {
if (!tokens[j].tag) {
if ((next = tokens[j+1]) && next.tag == '>') {
// set indent to token value
next.indent = tokens[j].toString()
}
tokens.splice(j, 1);
}
}
} else if (!noNewLine) {
tokens.push({tag:'\n'});
}
seenTag = false;
lineStart = tokens.length;
}
function changeDelimiters(text, index) {
var close = '=' + ctag,
closeIndex = text.indexOf(close, index),
delimiters = trim(
text.substring(text.indexOf('=', index) + 1, closeIndex)
).split(' ');
otag = delimiters[0];
ctag = delimiters[1];
return closeIndex + close.length - 1;
}
if (delimiters) {
delimiters = delimiters.split(' ');
otag = delimiters[0];
ctag = delimiters[1];
}
for (i = 0; i < len; i++) {
if (state == IN_TEXT) {
if (tagChange(otag, text, i)) {
--i;
addBuf();
state = IN_TAG_TYPE;
} else {
if (text.charAt(i) == '\n') {
filterLine(seenTag);
} else {
buf += text.charAt(i);
}
}
} else if (state == IN_TAG_TYPE) {
i += otag.length - 1;
tag = tagTypes[text.charAt(i + 1)];
tagType = tag ? text.charAt(i + 1) : '_v';
if (tagType == '=') {
i = changeDelimiters(text, i);
state = IN_TEXT;
} else {
if (tag) {
i++;
}
state = IN_TAG;
}
seenTag = i;
} else {
if (tagChange(ctag, text, i)) {
tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
i: (tagType == '/') ? seenTag - ctag.length : i + otag.length});
buf = '';
i += ctag.length - 1;
state = IN_TEXT;
if (tagType == '{') {
if (ctag == '}}') {
i++;
} else {
cleanTripleStache(tokens[tokens.length - 1]);
}
}
} else {
buf += text.charAt(i);
}
}
}
filterLine(seenTag, true);
return tokens;
}
function cleanTripleStache(token) {
if (token.n.substr(token.n.length - 1) === '}') {
token.n = token.n.substring(0, token.n.length - 1);
}
}
function trim(s) {
if (s.trim) {
return s.trim();
}
return s.replace(/^\s*|\s*$/g, '');
}
function tagChange(tag, text, index) {
if (text.charAt(index) != tag.charAt(0)) {
return false;
}
for (var i = 1, l = tag.length; i < l; i++) {
if (text.charAt(index + i) != tag.charAt(i)) {
return false;
}
}
return true;
}
function buildTree(tokens, kind, stack, customTags) {
var instructions = [],
opener = null,
token = null;
while (tokens.length > 0) {
token = tokens.shift();
if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) {
stack.push(token);
token.nodes = buildTree(tokens, token.tag, stack, customTags);
instructions.push(token);
} else if (token.tag == '/') {
if (stack.length === 0) {
throw new Error('Closing tag without opener: /' + token.n);
}
opener = stack.pop();
if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
}
opener.end = token.i;
return instructions;
} else {
instructions.push(token);
}
}
if (stack.length > 0) {
throw new Error('missing closing tag: ' + stack.pop().n);
}
return instructions;
}
function isOpener(token, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].o == token.n) {
token.tag = '#';
return true;
}
}
}
function isCloser(close, open, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].c == close && tags[i].o == open) {
return true;
}
}
}
Hogan.generate = function (tree, text, options) {
var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl();';
if (options.asString) {
return 'function(c,p,i){' + code + ';}';
}
return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options);
}
function esc(s) {
return s.replace(rSlash, '\\\\')
.replace(rQuot, '\\\"')
.replace(rNewline, '\\n')
.replace(rCr, '\\r');
}
function chooseMethod(s) {
return (~s.indexOf('.')) ? 'd' : 'f';
}
function walk(tree) {
var code = '';
for (var i = 0, l = tree.length; i < l; i++) {
var tag = tree[i].tag;
if (tag == '#') {
code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag);
} else if (tag == '^') {
code += invertedSection(tree[i].nodes, tree[i].n,
chooseMethod(tree[i].n));
} else if (tag == '<' || tag == '>') {
code += partial(tree[i]);
} else if (tag == '{' || tag == '&') {
code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
} else if (tag == '\n') {
code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i'));
} else if (tag == '_v') {
code += variable(tree[i].n, chooseMethod(tree[i].n));
} else if (tag === undefined) {
code += text('"' + esc(tree[i]) + '"');
}
}
return code;
}
function section(nodes, id, method, start, end, tags) {
return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
'c,p,0,' + start + ',' + end + ',"' + tags + '")){' +
'_.rs(c,p,' +
'function(c,p,_){' +
walk(nodes) +
'});c.pop();}';
}
function invertedSection(nodes, id, method) {
return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' +
walk(nodes) +
'};';
}
function partial(tok) {
return '_.b(_.rp("' + esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));';
}
function tripleStache(id, method) {
return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));';
}
function variable(id, method) {
return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
}
function text(id) {
return '_.b(' + id + ');';
}
Hogan.parse = function(tokens, text, options) {
options = options || {};
return buildTree(tokens, '', [], options.sectionTags || []);
},
Hogan.cache = {};
Hogan.compile = function(text, options) {
// options
//
// asString: false (default)
//
// sectionTags: [{o: '_foo', c: 'foo'}]
// An array of object with o and c fields that indicate names for custom
// section tags. The example above allows parsing of {{_foo}}{{/foo}}.
//
// delimiters: A string that overrides the default delimiters.
// Example: "<% %>"
//
options = options || {};
var key = text + '||' + !!options.asString;
var t = this.cache[key];
if (t) {
return t;
}
t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
return this.cache[key] = t;
};
})(typeof exports !== 'undefined' ? exports : Hogan);
</script><script>'use strict';
// Syntactic sugar
function $(selector) {
return document.querySelector(selector);
}
// Syntactic sugar & execute callback
function $$(selector, callback) {
var elems = document.querySelectorAll(selector);
for (var i = 0; i < elems.length; ++i) {
if (callback && typeof callback == 'function')
callback.call(this, elems[i]);
}
}
var debounce = function (func, wait, now) {
var timeout;
return function debounced () {
var that = this, args = arguments;
function delayed() {
if (!now)
func.apply(that, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (now)
func.apply(obj, args);
timeout = setTimeout(delayed, wait || 250);
};
};
// global namespace
window.GoAccess = window.GoAccess || {
initialize: function (options) {
this.opts = options;
this.AppState = {}; // current state app key-value store
this.AppTpls = {}; // precompiled templates
this.AppCharts = {}; // holds all rendered charts
this.AppUIData = (this.opts || {}).uiData || {}; // holds panel definitions
this.AppData = (this.opts || {}).panelData || {}; // hold raw data
this.AppWSConn = (this.opts || {}).wsConnection || {}; // WebSocket connection
this.AppPrefs = {
'theme': 'darkBlue',
'perPage': 7,
'layout': 'horizontal',
'autoHideTables': true,
};
this.AppPrefs = GoAccess.Util.merge(this.AppPrefs, this.opts.prefs);
if (GoAccess.Util.hasLocalStorage()) {
var ls = JSON.parse(localStorage.getItem('AppPrefs'));
this.AppPrefs = GoAccess.Util.merge(this.AppPrefs, ls);
}
if (Object.keys(this.AppWSConn).length)
this.setWebSocket(this.AppWSConn);
},
getPanelUI: function (panel) {
return panel ? this.AppUIData[panel] : this.AppUIData;
},
getPrefs: function (panel) {
return panel ? this.AppPrefs[panel] : this.AppPrefs;
},
setPrefs: function () {
if (GoAccess.Util.hasLocalStorage()) {
localStorage.setItem('AppPrefs', JSON.stringify(GoAccess.getPrefs()));
}
},
getPanelData: function (panel) {
return panel ? this.AppData[panel] : this.AppData;
},
setWebSocket: function (wsConn) {
var host = null, host = wsConn.url ? wsConn.url : window.location.hostname ? window.location.hostname : "localhost";
var str = /^(wss?:\/\/)?[^\/]+:[0-9]{1,5}\//.test(host + "/") ? host : String(host + ':' + wsConn.port);
str = !/^wss?:\/\//i.test(str) ? 'ws://' + str : str;
var socket = new WebSocket(str);
socket.onopen = function (event) {
GoAccess.Nav.WSOpen();
}.bind(this);
socket.onmessage = function (event) {
this.AppState['updated'] = true;
this.AppData = JSON.parse(event.data);
this.App.renderData();
}.bind(this);
socket.onclose = function (event) {
GoAccess.Nav.WSClose();
}.bind(this);
},
};
// HELPERS
GoAccess.Util = {
months: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul","Aug", "Sep", "Oct", "Nov", "Dec"],
// Add all attributes of n to o
merge: function (o, n) {
var obj = {}, i = 0, il = arguments.length, key;
for (; i < il; i++) {
for (key in arguments[i]) {
if (arguments[i].hasOwnProperty(key)) {
obj[key] = arguments[i][key];
}
}
}
return obj;
},
// hash a string
hashCode: function (s) {
return (s.split('').reduce(function (a, b) {
a = ((a << 5) - a) + b.charCodeAt(0);
return a&a;
}, 0) >>> 0).toString(16);
},
// Format bytes to human readable
formatBytes: function (bytes, decimals, numOnly) {
if (bytes == 0)
return numOnly ? 0 : '0 Byte';
var k = 1024;
var dm = decimals + 1 || 2;
var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + (numOnly ? '' : (' ' + sizes[i]));
},
// Validate number
isNumeric: function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
},
// Format microseconds to human readable
utime2str: function (usec) {
if (usec >= 864E8)
return ((usec) / 864E8).toFixed(2) + ' d';
else if (usec >= 36E8)
return ((usec) / 36E8).toFixed(2) + ' h';
else if (usec >= 6E7)
return ((usec) / 6E7).toFixed(2) + ' m';
else if (usec >= 1E6)
return ((usec) / 1E6).toFixed(2) + ' s';
else if (usec >= 1E3)
return ((usec) / 1E3).toFixed(2) + ' ms';
return (usec).toFixed(2) + ' us';
},
// Format date from 20120124 to 24/Jan/2012
formatDate: function (str) {
var y = str.substr(0,4), m = str.substr(4,2) - 1, d = str.substr(6,2),
h = str.substr(8,2) || 0, i = str.substr(10, 2) || 0, s = str.substr(12, 2) || 0;
var date = new Date(y,m,d,h,i,s);
var out = ('0' + date.getDate()).slice(-2) + '/' + this.months[date.getMonth()] + '/' + date.getFullYear();
10 <= str.length && (out += ":" + h);
12 <= str.length && (out += ":" + i);
14 <= str.length && (out += ":" + s);
return out;
},
// Format field value to human readable
fmtValue: function (value, dataType) {
var val = 0;
if (!dataType)
val = value;
switch (dataType) {
case 'utime':
val = this.utime2str(value);
break;
case 'date':
val = this.formatDate(value);
break;
case 'numeric':
if (this.isNumeric(value))
val = value.toLocaleString();
break;
case 'bytes':
val = this.formatBytes(value);
break;
case 'percent':
val = value.toFixed(2) + '%';
break;
case 'time':
if (this.isNumeric(value))
val = value.toLocaleString();
break;
case 'secs':
val = value + ' secs';
break;
default:
val = value;
};
return value == 0 ? String(val) : val;
},
isPanelValid: function (panel) {
var data = GoAccess.getPanelData(), ui = GoAccess.getPanelUI();
return (!ui.hasOwnProperty(panel) || !data.hasOwnProperty(panel) || !ui[panel].id);
},
// Attempts to extract the count from either an object or a scalar.
// e.g., item = Object {count: 14351, percent: 5.79} OR item = 4824825140
getCount: function (item) {
if (this.isObject(item) && 'count' in item)
return item.count;
return item;
},
getPercent: function (item) {
if (this.isObject(item) && 'percent' in item)
return this.fmtValue(item.percent, 'percent');
return null;
},
isObject: function (o) {
return o === Object(o);
},
setProp: function (o, s, v) {
var schema = o;
var a = s.split('.');
for (var i = 0, n = a.length; i < n-1; ++i) {
var k = a[i];
if (!schema[k]) schema[k] = {}
schema = schema[k];
}
schema[a[n-1]] = v;
},
getProp: function (o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1');
s = s.replace(/^\./, '');
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (this.isObject(o) && k in o) {
o = o[k];
} else {
return;
}
}
return o;
},
hasLocalStorage: function () {
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
return true;
} catch(e) {
return false;
}
},
isWithinViewPort: function (el) {
var elemTop = el.getBoundingClientRect().top;
var elemBottom = el.getBoundingClientRect().bottom;
return elemTop < window.innerHeight && elemBottom >= 0;
},
};
// OVERALL STATS
GoAccess.OverallStats = {
// Render general section wrapper
renderWrapper: function (ui) {
$('.wrap-general').innerHTML = GoAccess.AppTpls.General.wrap.render(ui);
},
// Render each overall stats box
renderBox: function (data, ui, row, x, idx) {
var wrap = $('.wrap-general-items');
// create a new bootstrap row every 6 elements
if (idx % 6 == 0) {
row = document.createElement('div');
row.setAttribute('class', 'row');
wrap.appendChild(row);
}
var box = document.createElement('div');
box.innerHTML = GoAccess.AppTpls.General.items.render({
'id': x,
'className': ui.items[x].className,
'label': ui.items[x].label,
'value': GoAccess.Util.fmtValue(data[x], ui.items[x].dataType),
});
row.appendChild(box);
return row;
},
// Render overall stats
renderData: function (data, ui) {
var idx = 0, row = null;
$('.last-updated').innerHTML = data.date_time;
$$('span.from', function (item) {
item.innerHTML = data.start_date
});
$$('span.to', function (item) {
item.innerHTML = data.end_date
});
// Iterate over general data object
for (var x in data) {
if (!data.hasOwnProperty(x) || !ui.items.hasOwnProperty(x))
continue;
row = this.renderBox(data, ui, row, x, idx);
idx++;
}
},
// Render general/overall analyzed requests.
initialize: function () {
var ui = GoAccess.getPanelUI('general');
var data = GoAccess.getPanelData('general'), i = 0;
this.renderWrapper(ui);
this.renderData(data, ui);
}
};
// RENDER PANELS
GoAccess.Nav = {
events: function () {
$('.nav-bars').onclick = function (e) {
e.stopPropagation();
this.renderMenu(e);
}.bind(this);
$('.nav-gears').onclick = function (e) {
e.stopPropagation();
this.renderOpts(e);
}.bind(this);
$('.nav-minibars').onclick = function (e) {
e.stopPropagation();
this.renderOpts(e);
}.bind(this);
$('body').onclick = function (e) {
$('nav').classList.remove('active');
}.bind(this);
$$('.export-json', function (item) {
item.onclick = function (e) {
this.downloadJSON(e);
}.bind(this);
}.bind(this));
$$('.theme-bright', function (item) {
item.onclick = function (e) {
this.setTheme('bright');
}.bind(this);
}.bind(this));
$$('.theme-dark-blue', function (item) {
item.onclick = function (e) {
this.setTheme('darkBlue');
}.bind(this);
}.bind(this));
$$('.theme-dark-gray', function (item) {
item.onclick = function (e) {
this.setTheme('darkGray');
}.bind(this);
}.bind(this));
$$('.layout-horizontal', function (item) {
item.onclick = function (e) {
this.setLayout('horizontal');
}.bind(this);
}.bind(this));
$$('.layout-vertical', function (item) {
item.onclick = function (e) {
this.setLayout('vertical');
}.bind(this);
}.bind(this));
$$('[data-perpage]', function (item) {
item.onclick = function (e) {
this.setPerPage(e);
}.bind(this);
}.bind(this));
$$('[data-show-tables]', function (item) {
item.onclick = function (e) {
this.toggleTables();
}.bind(this);
}.bind(this));
$$('[data-autohide-tables]', function (item) {
item.onclick = function (e) {
this.toggleAutoHideTables();
}.bind(this);
}.bind(this));
},
downloadJSON: function (e) {
var targ = e.currentTarget;
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(GoAccess.getPanelData()));
targ.href = 'data:' + data;
targ.download = 'goaccess-' + +new Date() + '.json';
},
setLayout: function (layout) {
if ('horizontal' == layout) {
$('.container').classList.add('container-fluid');
$('.container').classList.remove('container');
} else if ('vertical' == layout) {
$('.container-fluid').classList.add('container');
$('.container').classList.remove('container-fluid');
}
GoAccess.AppPrefs['layout'] = layout;
GoAccess.setPrefs();
GoAccess.Panels.initialize();
GoAccess.Charts.initialize();
GoAccess.Tables.initialize();
},
toggleAutoHideTables: function (e) {
var autoHideTables = GoAccess.Tables.autoHideTables();
$$('.table-wrapper', function (item) {
if (autoHideTables)
item.classList.remove('hidden-xs');
else
item.classList.add('hidden-xs');
}.bind(this));
GoAccess.AppPrefs['autoHideTables'] = !autoHideTables;
GoAccess.setPrefs();
},
toggleTables: function () {
var ui = GoAccess.getPanelUI();
var showTables = GoAccess.Tables.showTables();
Object.keys(ui).forEach(function (panel, idx) {
if (!GoAccess.Util.isPanelValid(panel))
ui[panel]['table'] = !showTables;
}.bind(this));
GoAccess.AppPrefs['showTables'] = !showTables;
GoAccess.setPrefs();
GoAccess.Panels.initialize();
GoAccess.Charts.initialize();
GoAccess.Tables.initialize();
},
setTheme: function (theme) {
if (!theme)
return;
$('html').className = '';
switch(theme) {
case 'darkGray':
$('html').classList.add('dark');
$('html').classList.add('gray');
break;
case 'darkBlue':
$('html').classList.add('dark');
$('html').classList.add('blue');
break;
}
GoAccess.AppPrefs['theme'] = theme;
GoAccess.setPrefs();
},
getIcon: function (key) {
switch(key) {
case 'visitors' : return 'users';
case 'requests' : return 'file';
case 'static_requests' : return 'file-text';
case 'not_found' : return 'file-o';
case 'hosts' : return 'user';
case 'os' : return 'desktop';
case 'browsers' : return 'chrome';
case 'visit_time' : return 'clock-o';
case 'vhosts' : return 'th-list';
case 'referrers' : return 'external-link';
case 'referring_sites' : return 'external-link';
case 'keyphrases' : return 'google';
case 'status_codes' : return 'warning';
case 'remote_user' : return 'users';
case 'geolocation' : return 'map-marker';
default : return 'pie-chart';
}
},
getItems: function () {
var ui = GoAccess.getPanelUI(), menu = [];
for (var panel in ui) {
if (GoAccess.Util.isPanelValid(panel))
continue;
// Push valid panels to our navigation array
menu.push({
'current': window.location.hash.substr(1) == panel,
'head': ui[panel].head,
'key': panel,
'icon': this.getIcon(panel),
});
}
return menu;
},
setPerPage: function (e) {
GoAccess.AppPrefs['perPage'] = +e.currentTarget.getAttribute('data-perpage');
GoAccess.App.renderData();
GoAccess.setPrefs();
},
getTheme: function () {
return GoAccess.AppPrefs.theme || 'darkGray';
},
getLayout: function () {
return GoAccess.AppPrefs.layout || 'horizontal';
},
getPerPage: function () {
return GoAccess.AppPrefs.perPage || 7;
},
// Render left-hand side navigation options.
renderOpts: function () {
var o = {};
o[this.getLayout()] = true;
o[this.getTheme()] = true;
o['perPage' + this.getPerPage()] = true;
o['autoHideTables'] = GoAccess.Tables.autoHideTables();
o['showTables'] = GoAccess.Tables.showTables();
$('.nav-list').innerHTML = GoAccess.AppTpls.Nav.opts.render(o);
$('nav').classList.toggle('active');
this.events();
},
// Render left-hand side navigation given the available panels.
renderMenu: function (e) {
$('.nav-list').innerHTML = GoAccess.AppTpls.Nav.menu.render({
'nav': this.getItems(),
'overall': window.location.hash.substr(1) == '',
});
$('nav').classList.toggle('active');
this.events();
},
WSStatus: function () {
if (Object.keys(GoAccess.AppWSConn).length)
$$('.nav-ws-status', function (item) { item.style.display = 'block' });
},
WSClose: function () {
$$('.nav-ws-status', function (item) {
item.classList.remove('connected');
item.setAttribute('title', 'Disconnected');
});
},
WSOpen: function () {
$$('.nav-ws-status', function (item) {
item.classList.add('connected');
item.setAttribute('title', 'Connected to ' + GoAccess.AppWSConn.url);
});
},
// Render left-hand side navigation given the available panels.
renderWrap: function (nav) {
$('nav').innerHTML = GoAccess.AppTpls.Nav.wrap.render({});
},
// Iterate over all available panels and render each.
initialize: function () {
this.setTheme(GoAccess.AppPrefs.theme);
this.renderWrap();
this.WSStatus();
this.events();
}
};
// RENDER PANELS
GoAccess.Panels = {
events: function () {
$$('[data-toggle=dropdown]', function (item) {
item.onclick = function (e) {
this.openOpts(e.currentTarget);
}.bind(this);
item.onblur = function (e) {
this.closeOpts(e);
}.bind(this);
}.bind(this));
$$('[data-plot]', function (item) {
item.onclick = function (e) {
GoAccess.Charts.redrawChart(e.currentTarget);
}.bind(this);
}.bind(this));
$$('[data-chart-type]', function (item) {
item.onclick = function (e) {
GoAccess.Charts.setChartType(e.currentTarget);
}.bind(this);
}.bind(this));
$$('[data-metric]', function (item) {
item.onclick = function (e) {
GoAccess.Tables.toggleColumn(e.currentTarget);
}.bind(this);
}.bind(this));
},
openOpts: function (targ) {
var panel = targ.getAttribute('data-panel');
targ.parentElement.classList.toggle('open');
this.renderOpts(panel);
},
closeOpts: function (e) {
e.currentTarget.parentElement.classList.remove('open');
// Trigger the click event on the target if not opening another menu
if (e.relatedTarget && e.relatedTarget.getAttribute('data-toggle') !== 'dropdown')
e.relatedTarget.click();
},
setPlotSelection: function (ui, prefs) {
var chartType = ((prefs || {}).plot || {}).chartType || ui.plot[0].chartType;
var metric = ((prefs || {}).plot || {}).metric || ui.plot[0].className;
ui[chartType] = true;
for (var i = 0, len = ui.plot.length; i < len; ++i)
if (ui.plot[i].className == metric)
ui.plot[i]['selected'] = true;
},
setColSelection: function (items, prefs) {
var columns = (prefs || {}).columns || {};
for (var i = 0, len = items.length; i < len; ++i)
if ((items[i].key in columns) && columns[items[i].key]['hide'])
items[i]['hide'] = true;
},
setOpts: function (panel) {
var ui = JSON.parse(JSON.stringify(GoAccess.getPanelUI(panel))), prefs = GoAccess.getPrefs(panel);
this.setPlotSelection(ui, prefs);
this.setColSelection(ui.items, prefs);
return ui;
},
renderOpts: function (panel) {
$('.panel-opts-' + panel).innerHTML = GoAccess.AppTpls.Panels.opts.render(this.setOpts(panel));
this.events();
},
enablePrev: function (panel) {
var $pagination = $('#panel-' + panel + ' .pagination a.panel-prev');
if ($pagination)
$pagination.parentNode.classList.remove('disabled');
},
disablePrev: function (panel) {
var $pagination = $('#panel-' + panel + ' .pagination a.panel-prev');
if ($pagination)
$pagination.parentNode.classList.add('disabled');
},
enableNext: function (panel) {
var $pagination = $('#panel-' + panel + ' .pagination a.panel-next');
if ($pagination)
$pagination.parentNode.classList.remove('disabled');
},
disableNext: function (panel) {
var $pagination = $('#panel-' + panel + ' .pagination a.panel-next');
if ($pagination)
$pagination.parentNode.classList.add('disabled');
},
enablePagination: function (panel) {
this.enablePrev(panel);
this.enableNext(panel);
},
disablePagination: function (panel) {
this.disablePrev(panel);
this.disableNext(panel);
},
hasSubItems: function (ui, data) {
for (var i = 0, len = data.length; i < len; ++i) {
if (!data[i].items)
return (ui['hasSubItems'] = false);
if (data[i].items.length) {
return (ui['hasSubItems'] = true);
}
}
return false;
},
// Render the given panel given a user interface definition.
renderPanel: function (panel, ui, row, idx) {
var wrap = $('.wrap-panels');
var every = GoAccess.AppPrefs['layout'] == 'horizontal' ? 2 : 1;
var perRow = GoAccess.AppPrefs['layout'] == 'horizontal' ? 6 : 12;
// create a new bootstrap row every one or two elements depending on
// the layout
if (idx % every == 0) {
row = document.createElement('div');
row.setAttribute('class', 'row' + (every == 2 ? ' equal' : ''));
wrap.appendChild(row);
}
var data = GoAccess.getPanelData(panel);
this.hasSubItems(ui, data.data);
GoAccess.Tables.hasTables(ui);
// set the number of columns based on current layout
var col = document.createElement('div');
col.setAttribute('class', 'col-md-' + perRow + ' wrap-panel');
row.appendChild(col);
// per panel wrapper
var box = document.createElement('div');
box.id = 'panel-' + panel;
box.innerHTML = GoAccess.AppTpls.Panels.wrap.render(ui);
col.appendChild(box);
// Remove pagination if not enough data for the given panel
if (data.data.length <= GoAccess.getPrefs().perPage)
this.disablePagination(panel);
GoAccess.Tables.renderThead(panel, ui);
return row;
},
// Iterate over all available panels and render each panel
// structure.
renderPanels: function () {
var ui = GoAccess.getPanelUI(), idx = 0, row = null;
$('.wrap-panels').innerHTML = '';
for (var panel in ui) {
if (GoAccess.Util.isPanelValid(panel))
continue;
// Render panel given a user interface definition
row = this.renderPanel(panel, ui[panel], row, idx++);
}
},
initialize: function () {
this.renderPanels();
this.events();
}
};
// RENDER CHARTS
GoAccess.Charts = {
iter: function (callback) {
Object.keys(GoAccess.AppCharts).forEach(function (panel) {
// redraw chart only if it's within the viewport
if (!GoAccess.Util.isWithinViewPort($('#panel-' + panel)))
return;
if (callback && typeof callback === 'function')
callback.call(this, GoAccess.AppCharts[panel], panel);
});
},
getMetricKeys: function (panel, key) {
return GoAccess.getPanelUI(panel)['items'].map(function (a) { return a[key]; });
},
getPanelData: function (panel, data) {
// Grab ui plot data for the selected panel
var plot = GoAccess.Util.getProp(GoAccess.AppState, panel + '.plot');
// Grab the data for the selected panel
data = data || this.processChartData(GoAccess.getPanelData(panel).data);
return plot.chartReverse ? data.reverse() : data;
},
renderChart: function (panel, chart, data) {
// remove popup
d3.select('#chart-' + panel + '>.chart-tooltip-wrap')
.remove();
// remove svg
d3.select('#chart-' + panel).select('svg')
.remove();
// add chart to the document
d3.select("#chart-" + panel)
.datum(data)
.call(chart)
.append("div").attr("class", "chart-tooltip-wrap");
},
drawPlot: function (panel, plotUI, data) {
var chart = this.getChart(panel, plotUI, data);
if (!chart)
return;
this.renderChart(panel, chart, data);
GoAccess.AppCharts[panel] = null;
GoAccess.AppCharts[panel] = chart;
},
setChartType: function (targ) {
var panel = targ.getAttribute('data-panel');
var type = targ.getAttribute('data-chart-type');
GoAccess.Util.setProp(GoAccess.AppPrefs, panel + '.plot.chartType', type);
GoAccess.setPrefs();
var plotUI = GoAccess.Util.getProp(GoAccess.AppState, panel + '.plot');
// Extract data for the selected panel and process it
this.drawPlot(panel, plotUI, this.getPanelData(panel));
},
// Redraw a chart upon selecting a metric.
redrawChart: function (targ) {
var plot = targ.getAttribute('data-plot');
var panel = targ.getAttribute('data-panel');
var ui = GoAccess.getPanelUI(panel);
var plotUI = ui.plot;
GoAccess.Util.setProp(GoAccess.AppPrefs, panel + '.plot.metric', plot);
GoAccess.setPrefs();
// Iterate over plot user interface definition
for (var x in plotUI) {
if (!plotUI.hasOwnProperty(x) || plotUI[x].className != plot)
continue;
GoAccess.Util.setProp(GoAccess.AppState, panel + '.plot', plotUI[x]);
// Extract data for the selected panel and process it
this.drawPlot(panel, plotUI[x], this.getPanelData(panel));
break;
}
},
// Iterate over the item properties and and extract the count value.
extractCount: function (item) {
var o = {};
for (var prop in item)
o[prop] = GoAccess.Util.getCount(item[prop]);
return o;
},
// Extract an array of objects that D3 can consume to process the chart.
// e.g., o = Object {hits: 37402, visitors: 6949, bytes:
// 505881789, avgts: 118609, cumts: 4436224010…}
processChartData: function (data) {
var out = [];
for (var i = 0; i < data.length; ++i)
out.push(this.extractCount(data[i]));
return out;
},
findUIItem: function (panel, key) {
var items = GoAccess.getPanelUI(panel).items, o = {};
for (var i = 0; i < items.length; ++i) {
if (items[i].key == key)
return items[i];
}
return null;
},
getXKey: function (datum, key) {
var arr = [];
if (typeof key === 'string')
return datum[key];
for (var prop in key)
arr.push(datum[key[prop]]);
return arr.join(' ');
},
getAreaSpline: function (panel, plotUI, data) {
var dualYaxis = plotUI['d3']['y1'];
var chart = AreaChart(dualYaxis)
.labels({
y0: plotUI['d3']['y0'].label,
y1: dualYaxis ? plotUI['d3']['y1'].label : ''
})
.x(function (d) {
if ((((plotUI || {}).d3 || {}).x || {}).key)
return this.getXKey(d, plotUI['d3']['x']['key']);
return d.data;;
}.bind(this))
.y0(function (d) {
return +d[plotUI['d3']['y0']['key']];
})
.width($("#chart-" + panel).getBoundingClientRect().width)
.height(175)
.format({
x: (this.findUIItem(panel, 'data') || {}).dataType || null,
y0: ((plotUI.d3 || {}).y0 || {}).format,
y1: ((plotUI.d3 || {}).y1 || {}).format,
})
.opts(plotUI);
dualYaxis && chart.y1(function (d) {
return +d[plotUI['d3']['y1']['key']];
});
return chart;
},
getVBar: function (panel, plotUI, data) {
var dualYaxis = plotUI['d3']['y1'];
var chart = BarChart(dualYaxis)
.labels({
y0: plotUI['d3']['y0'].label,
y1: dualYaxis ? plotUI['d3']['y1'].label : ''
})
.x(function (d) {
if ((((plotUI || {}).d3 || {}).x || {}).key)
return this.getXKey(d, plotUI['d3']['x']['key']);
return d.data;;
}.bind(this))
.y0(function (d) {
return +d[plotUI['d3']['y0']['key']];
})
.width($("#chart-" + panel).getBoundingClientRect().width)
.height(175)
.format({
x: (this.findUIItem(panel, 'data') || {}).dataType || null,
y0: ((plotUI.d3 || {}).y0 || {}).format,
y1: ((plotUI.d3 || {}).y1 || {}).format,
})
.opts(plotUI);
dualYaxis && chart.y1(function (d) {
return +d[plotUI['d3']['y1']['key']];
});
return chart;
},
getChartType: function (panel) {
var ui = GoAccess.getPanelUI(panel);
if (!ui.plot.length)
return '';
return GoAccess.Util.getProp(GoAccess.getPrefs(), panel + '.plot.chartType') || ui.plot[0].chartType;
},
getPlotUI: function (panel, ui) {
var metric = GoAccess.Util.getProp(GoAccess.getPrefs(), panel + '.plot.metric');
if (!metric)
return ui.plot[0];
return ui.plot.filter(function (v) {
return v.className == metric;
})[0];
},
getChart: function (panel, plotUI, data) {
var chart = null;
// Render given its type
switch (this.getChartType(panel)) {
case 'area-spline':
chart = this.getAreaSpline(panel, plotUI, data);
break;
case 'bar':
chart = this.getVBar(panel, plotUI, data);
break;
};
return chart;
},
// Render all charts for the applicable panels.
renderCharts: function (ui) {
var plotUI = null, chart = null;
for (var panel in ui) {
if (!ui.hasOwnProperty(panel))
continue;
// Ensure it has a plot definitions
if (!ui[panel].plot || !ui[panel].plot.length)
continue;
plotUI = this.getPlotUI(panel, ui[panel]);
// set ui plot data
GoAccess.Util.setProp(GoAccess.AppState, panel + '.plot', plotUI);
// Grab the data for the selected panel
var data = this.getPanelData(panel);
if (!(chart = this.getChart(panel, plotUI, data)))
continue;
this.renderChart(panel, chart, data);
GoAccess.AppCharts[panel] = chart;
}
},
// Reload the given chart data
reloadChart: function (chart, panel) {
var subItems = GoAccess.Tables.getSubItemsData(panel);
var data = (subItems.length ? subItems : GoAccess.getPanelData(panel).data).slice(0);
d3.select("#chart-" + panel)
.datum(this.processChartData(this.getPanelData(panel, data)))
.call(chart.width($("#chart-" + panel).offsetWidth));
},
reloadCharts: function () {
this.iter(function (chart, panel) {
this.reloadChart(chart, panel);
}.bind(this));
GoAccess.AppState.updated = false;
},
// Only redraw charts with current data
redrawCharts: function () {
this.iter(function (chart, panel) {
d3.select("#chart-" + panel).call(chart.width($("#chart-" + panel).offsetWidth));
});
},
initialize: function () {
this.renderCharts(GoAccess.getPanelUI());
// reload on scroll & redraw on resize
d3.select(window).on('scroll.charts', debounce(function () {
this.reloadCharts();
}, 250, false).bind(this)).on('resize.charts', function () {
this.redrawCharts();
}.bind(this));
}
};
// RENDER TABLES
GoAccess.Tables = {
chartData: {}, // holds all panel sub items data that feeds the chart
events: function () {
$$('.panel-next', function (item) {
item.onclick = function (e) {
var panel = e.currentTarget.getAttribute('data-panel');
this.renderTable(panel, this.nextPage(panel))
}.bind(this);
}.bind(this));
$$('.panel-prev', function (item) {
item.onclick = function (e) {
var panel = e.currentTarget.getAttribute('data-panel');
this.renderTable(panel, this.prevPage(panel))
}.bind(this);
}.bind(this));
$$('.expandable>td', function (item) {
item.onclick = function (e) {
if (!window.getSelection().toString())
this.toggleRow(e.currentTarget);
}.bind(this);
}.bind(this));
$$('.row-expandable.clickable', function (item) {
item.onclick = function (e) {
this.toggleRow(e.currentTarget);
}.bind(this);
}.bind(this));
$$('.sortable', function (item) {
item.onclick = function (e) {
this.sortColumn(e.currentTarget);
}.bind(this);
}.bind(this));
},
toggleColumn: function (targ) {
var panel = targ.getAttribute('data-panel');
var metric = targ.getAttribute('data-metric');
var columns = (GoAccess.getPrefs(panel) || {}).columns || {};
if (metric in columns)
delete columns[metric];
else
GoAccess.Util.setProp(columns, metric + '.hide', true);
GoAccess.Util.setProp(GoAccess.AppPrefs, panel + '.columns', columns);
GoAccess.setPrefs();
GoAccess.Tables.renderThead(panel, GoAccess.getPanelUI(panel));
GoAccess.Tables.renderFullTable(panel);
},
sortColumn: function (ele) {
var field = ele.getAttribute('data-key');
var order = ele.getAttribute('data-order');
var panel = ele.parentElement.parentElement.parentElement.getAttribute('data-panel');
order = order ? 'asc' == order ? 'desc' : 'asc' : 'asc';
GoAccess.App.sortData(panel, field, order)
GoAccess.Util.setProp(GoAccess.AppState, panel + '.sort', {
'field': field,
'order': order,
});
this.renderThead(panel, GoAccess.getPanelUI(panel));
this.renderTable(panel, this.getCurPage(panel));
GoAccess.Charts.reloadChart(GoAccess.AppCharts[panel], panel);
},
getDataByKey: function (panel, key) {
var data = GoAccess.getPanelData(panel).data;
for (var i = 0, n = data.length; i < n; ++i) {
if (GoAccess.Util.hashCode(data[i].data) == key)
return data[i];
}
return null;
},
getSubItemsData: function (panel) {
var out = [], items = this.chartData[panel];
for (var x in items) {
if (!items.hasOwnProperty(x))
continue;
out = out.concat(items[x]);
}
return out;
},
addChartData: function (panel, key) {
var data = this.getDataByKey(panel, key);
var path = panel + '.' + key;
if (!data || !data.items)
return [];
GoAccess.Util.setProp(this.chartData, path, data.items);
return this.getSubItemsData(panel);
},
removeChartData: function (panel, key) {
if (GoAccess.Util.getProp(this.chartData, panel + '.' + key))
delete this.chartData[panel][key];
if (!this.chartData[panel] || Object.keys(this.chartData[panel]).length == 0)
return GoAccess.getPanelData(panel).data;
return this.getSubItemsData(panel);
},
isExpanded: function (panel, key) {
var path = panel + '.expanded.' + key;
return GoAccess.Util.getProp(GoAccess.AppState, path);
},
toggleExpanded: function (panel, key) {
var path = panel + '.expanded.' + key, ret = true;
if (this.isExpanded(panel, key))
delete GoAccess.AppState[panel]['expanded'][key];
else
GoAccess.Util.setProp(GoAccess.AppState, path, true), ret = false;
return ret;
},
// Toggle children rows
toggleRow: function (ele) {
var hide = false, data = [];
var row = ele.parentNode;
var panel = row.getAttribute('data-panel'), key = row.getAttribute('data-key');
var plotUI = GoAccess.AppCharts[panel].opts();
hide = this.toggleExpanded(panel, key);
this.renderTable(panel, this.getCurPage(panel));
if (!plotUI.redrawOnExpand)
return;
if (!hide)
data = GoAccess.Charts.processChartData(this.addChartData(panel, key));
else
data = GoAccess.Charts.processChartData(this.removeChartData(panel, key));
GoAccess.Charts.drawPlot(panel, plotUI, data);
},
// Get current panel page
getCurPage: function (panel) {
return GoAccess.Util.getProp(GoAccess.AppState, panel + '.curPage') || 0;
},
// Page offset.
// e.g., Return Value: 11, curPage: 2
pageOffSet: function (panel) {
return ((this.getCurPage(panel) - 1) * GoAccess.getPrefs().perPage);
},
// Get total number of pages given the number of items on array
getTotalPages: function (dataItems) {
return Math.ceil(dataItems.length / GoAccess.getPrefs().perPage);
},
// Get a shallow copy of a portion of the given data array and the
// current page.
getPage: function (panel, dataItems, page) {
var totalPages = this.getTotalPages(dataItems);
if (page < 1)
page = 1;
if (page > totalPages)
page = totalPages;
GoAccess.Util.setProp(GoAccess.AppState, panel + '.curPage', page);
var start = this.pageOffSet(panel);
var end = start + GoAccess.getPrefs().perPage;
return dataItems.slice(start, end);
},
// Get previous page
prevPage: function (panel) {
return this.getCurPage(panel) - 1;
},
// Get next page
nextPage: function (panel) {
return this.getCurPage(panel) + 1;
},
getMetaValue: function (ui, value) {
if ('meta' in ui)
return value[ui.meta];
return null;
},
getMetaCell: function (ui, value) {
var val = this.getMetaValue(ui, value);
var max = (value || {}).max;
var min = (value || {}).min;
// use metaType if exist else fallback to dataType
var vtype = ui.metaType || ui.dataType;
var className = ui.className || '';
className += ui.dataType != 'string' ? 'text-right' : '';
return {
'className': className,
'max' : max != undefined ? GoAccess.Util.fmtValue(max, vtype) : null,
'min' : min != undefined ? GoAccess.Util.fmtValue(min, vtype) : null,
'value' : val != undefined ? GoAccess.Util.fmtValue(val, vtype) : null,
'title' : ui.meta,
'label' : ui.metaLabel || null,
}
},
hideColumn: function (panel, col) {
var columns = (GoAccess.getPrefs(panel) || {}).columns || {};
return ((col in columns) && columns[col]['hide']);
},
showTables: function () {
return ('showTables' in GoAccess.getPrefs()) ? GoAccess.getPrefs().showTables : true;
},
autoHideTables: function () {
return ('autoHideTables' in GoAccess.getPrefs()) ? GoAccess.getPrefs().autoHideTables : true;
},
hasTables: function (ui) {
ui['table'] = this.showTables();
ui['autoHideTables'] = this.autoHideTables();
},
renderMetaRow: function (panel, ui) {
// find the table to set
var table = $('.table-' + panel + ' tbody.tbody-meta');
if (!table)
return;
var cells = [], uiItems = ui.items;
var data = GoAccess.getPanelData(panel).metadata;
for (var i = 0; i < uiItems.length; ++i) {
var item = uiItems[i];
if (this.hideColumn(panel, item.key))
continue;
var value = data[item.key];
cells.push(this.getMetaCell(item, value))
}
table.innerHTML = GoAccess.AppTpls.Tables.meta.render({
row: [{
'hasSubItems': ui.hasSubItems,
'cells': cells
}]
});
},
// Iterate over user interface definition properties
iterUIItems: function (panel, uiItems, dataItems, callback) {
var out = [];
for (var i = 0; i < uiItems.length; ++i) {
var uiItem = uiItems[i];
if (this.hideColumn(panel, uiItem.key))
continue;
// Data for the current user interface property.
// e.g., dataItem = Object {count: 13949, percent: 5.63}
var dataItem = dataItems[uiItem.key];
// Apply the callback and push return data to output array
if (callback && typeof callback == 'function') {
var ret = callback.call(this, panel, uiItem, dataItem);
if (ret) out.push(ret);
}
}
return out;
},
// Return an object that can be consumed by the table template given a user
// interface definition and a cell value object.
// e.g., value = Object {count: 14351, percent: 5.79}
getObjectCell: function (panel, ui, value) {
var className = ui.className || '';
className += ui.dataType != 'string' ? 'text-right' : '';
return {
'className': className,
'percent': GoAccess.Util.getPercent(value),
'value': GoAccess.Util.fmtValue(GoAccess.Util.getCount(value), ui.dataType)
};
},
// Given a data item object, set all the row cells and return a
// table row that the template can consume.
renderRow: function (panel, callback, ui, dataItem, idx, subItem, parentId, expanded) {
var shadeParent = ((!subItem && idx % 2 != 0) ? 'shaded' : '');
var shadeChild = ((parentId % 2 != 0) ? 'shaded' : '');
return {
'panel' : panel,
'idx' : !subItem && (String((idx + 1) + this.pageOffSet(panel))),
'key' : !subItem ? GoAccess.Util.hashCode(dataItem.data) : '',
'expanded' : !subItem && expanded,
'parentId' : subItem ? String(parentId) : '',
'className' : subItem ? 'child ' + shadeChild : 'parent ' + shadeParent,
'hasSubItems' : ui.hasSubItems,
'items' : dataItem.items ? dataItem.items.length : 0,
'cells' : callback.call(this),
};
},
renderRows: function (rows, panel, ui, dataItems, subItem, parentId) {
subItem = subItem || false;
// no data rows
if (dataItems.length == 0 && ui.items.length) {
rows.push({
cells: [{
className: 'text-center',
colspan: ui.items.length + 1,
value: 'No data on this panel.'
}]
});
}
// Iterate over all data items for the given panel and
// generate a table row per date item.
var cellcb = null;
for (var i = 0; i < dataItems.length; ++i) {
var dataItem = dataItems[i], data = null, expanded = false;
switch(typeof dataItem) {
case 'string':
data = dataItem;
cellcb = function () {
return {
'colspan': ui.items.length,
'value': data
}
};
break;
default:
data = dataItem.data;
cellcb = this.iterUIItems.bind(this, panel, ui.items, dataItem, this.getObjectCell.bind(this));
}
expanded = this.isExpanded(panel, GoAccess.Util.hashCode(data));
rows.push(this.renderRow(panel, cellcb, ui, dataItem, i, subItem, parentId, expanded));
if (dataItem.items && dataItem.items.length && expanded) {
this.renderRows(rows, panel, ui, dataItem.items, true, i, expanded);
}
}
},
// Entry point to render all data rows into the table
renderDataRows: function (panel, ui, dataItems, page) {
// find the table to set
var table = $('.table-' + panel + ' tbody.tbody-data');
if (!table)
return;
var dataItems = this.getPage(panel, dataItems, page);
var rows = [];
this.renderRows(rows, panel, ui, dataItems);
if (rows.length == 0)
return;
table.innerHTML = GoAccess.AppTpls.Tables.data.render({
rows: rows
});
},
togglePagination: function (panel, page, dataItems) {
GoAccess.Panels.enablePagination(panel);
// Diable pagination next button if last page is reached
if (page >= this.getTotalPages(dataItems))
GoAccess.Panels.disableNext(panel);
if (page <= 1)
GoAccess.Panels.disablePrev(panel);
},
renderTable: function (panel, page) {
var dataItems = GoAccess.getPanelData(panel).data;
var ui = GoAccess.getPanelUI(panel);
this.togglePagination(panel, page, dataItems);
// Render data rows
this.renderDataRows(panel, ui, dataItems, page);
this.events();
},
renderFullTable: function (panel) {
var ui = GoAccess.getPanelUI(panel), page = 0;
// panel's data
var data = GoAccess.getPanelData(panel);
// render meta data
if (data.hasOwnProperty('metadata'))
this.renderMetaRow(panel, ui);
// render actual data
if (data.hasOwnProperty('data')) {
page = this.getCurPage(panel);
this.togglePagination(panel, page, data.data);
this.renderDataRows(panel, ui, data.data, page);
}
},
// Iterate over all panels and determine which ones should contain
// a data table.
renderTables: function (force) {
var ui = GoAccess.getPanelUI();
for (var panel in ui) {
if (GoAccess.Util.isPanelValid(panel) || !this.showTables())
continue;
if (force || GoAccess.Util.isWithinViewPort($('#panel-' + panel)))
this.renderFullTable(panel);
}
},
// Given a UI panel definition, make a copy of it and assign the sort
// fields to the template object to render
sort2Tpl: function (panel, ui) {
var uiClone = JSON.parse(JSON.stringify(ui)), out = [];
var sort = GoAccess.Util.getProp(GoAccess.AppState, panel + '.sort');
for (var i = 0, len = uiClone.items.length; i < len; ++i) {
var item = uiClone.items[i];
if (this.hideColumn(panel, item.key))
continue;
item['sort'] = false;
if (item.key == sort.field && sort.order) {
item['sort'] = true;
item[sort.order.toLowerCase()] = true;
}
out.push(item);
}
uiClone.items = out;
return uiClone;
},
renderThead: function (panel, ui) {
var $thead = $('.table-' + panel + '>thead'), $colgroup = $('.table-' + panel + '>colgroup');
if ($thead && $colgroup && this.showTables()) {
ui = this.sort2Tpl(panel, ui);
$thead.innerHTML = GoAccess.AppTpls.Tables.head.render(ui);
$colgroup.innerHTML = GoAccess.AppTpls.Tables.colgroup.render(ui);
}
},
reloadTables: function () {
this.renderTables(false);
this.events();
},
initialize: function () {
this.renderTables(true);
this.events();
// redraw on scroll
d3.select(window).on('scroll.tables', debounce(function () {
this.reloadTables();
}, 250, false).bind(this));
},
};
// Main App
GoAccess.App = {
tpl: function (tpl) {
return Hogan.compile(tpl);
},
setTpls: function () {
GoAccess.AppTpls = {
'Nav': {
'wrap': this.tpl($('#tpl-nav-wrap').innerHTML),
'menu': this.tpl($('#tpl-nav-menu').innerHTML),
'opts': this.tpl($('#tpl-nav-opts').innerHTML),
},
'Panels': {
'wrap': this.tpl($('#tpl-panel').innerHTML),
'opts': this.tpl($('#tpl-panel-opts').innerHTML),
},
'General': {
'wrap': this.tpl($('#tpl-general').innerHTML),
'items': this.tpl($('#tpl-general-items').innerHTML),
},
'Tables': {
'colgroup': this.tpl($('#tpl-table-colgroup').innerHTML),
'head': this.tpl($('#tpl-table-thead').innerHTML),
'meta': this.tpl($('#tpl-table-row-meta').innerHTML),
'data': this.tpl($('#tpl-table-row').innerHTML),
},
}
},
sortField: function (o, field) {
var f = o[field];
if (GoAccess.Util.isObject(f) && (f !== null))
f = o[field].count;
return f;
},
sortData: function (panel, field, order) {
// panel's data
var panelData = GoAccess.getPanelData(panel).data;
panelData.sort(function (a, b) {
var a = this.sortField(a, field);
var b = this.sortField(b, field);
if (typeof a === 'string' && typeof b === 'string')
return 'asc' == order ? a.localeCompare(b) : b.localeCompare(a);
return 'asc' == order ? a - b : b - a;
}.bind(this));
},
setInitSort: function () {
var ui = GoAccess.getPanelUI();
for (var panel in ui) {
if (GoAccess.Util.isPanelValid(panel))
continue;
GoAccess.Util.setProp(GoAccess.AppState, panel + '.sort', ui[panel].sort);
}
},
// Verify if we need to sort panels upon data re-entry
verifySort: function () {
var ui = GoAccess.getPanelUI();
for (var panel in ui) {
if (GoAccess.Util.isPanelValid(panel))
continue;
var sort = GoAccess.Util.getProp(GoAccess.AppState, panel + '.sort');
// do not sort panels if they still hold the same sort properties
if (JSON.stringify(sort) === JSON.stringify(ui[panel].sort))
continue;
this.sortData(panel, sort.field, sort.order);
}
},
initDom: function () {
$('nav').classList.remove('hide');
$('.container').classList.remove('hide');
$('.spinner').classList.add('hide');
if (GoAccess.AppPrefs['layout'] == 'horizontal') {
$('.container').classList.add('container-fluid');
$('.container-fluid').classList.remove('container');
}
},
renderData: function () {
this.verifySort();
GoAccess.OverallStats.initialize();
// do not rerender tables/charts if data hasn't changed
if (!GoAccess.AppState.updated)
return;
GoAccess.Charts.reloadCharts();
GoAccess.Tables.reloadTables();
},
initialize: function () {
this.setInitSort();
this.setTpls();
GoAccess.Nav.initialize();
this.initDom();
GoAccess.OverallStats.initialize();
GoAccess.Panels.initialize();
GoAccess.Charts.initialize();
GoAccess.Tables.initialize();
},
};
// Init app
window.onload = function () {
GoAccess.initialize({
'uiData': window.user_interface,
'panelData': window.json_data,
'wsConnection': window.connection || null,
'prefs': window.html_prefs || {},
});
GoAccess.App.initialize();
};
</script><script>/**
* ______ ___
* / ____/___ / | _____________ __________
* / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/
* / /_/ / /_/ / ___ / /__/ /__/ __(__ |__ )
* \____/\____/_/ |_\___/\___/\___/____/____/
*
* The MIT License (MIT)
* Copyright (c) 2009-2017 Gerardo Orellana <hello @ goaccess.io>
*/
'use strict';
// This is faster than calculating the exact length of each label.
// e.g., getComputedTextLength(), slice()...
function truncate(text, width) {
text.each(function () {
var parent = this.parentNode, $d3parent = d3.select(parent);
var gw = $d3parent.node().getBBox();
var x = (Math.min(gw.width, width) / 2) * -1;
// adjust wrapper <svg> width
if ('svg' == parent.nodeName)
$d3parent.attr('width', width).attr('x', x);
// wrap <text> within an svg
else {
$d3parent.insert('svg', function () {
return this;
}.bind(this))
.attr('class', 'wrap-text')
.attr('width', width)
.attr('x', x)
.append(function () {
return this;
}.bind(this));
}
});
}
function AreaChart(dualYaxis) {
var opts = {};
var margin = {
top : 20,
right : 50,
bottom : 40,
left : 50
},
height = 170,
nTicks = 10,
padding = 10,
width = 760;
var labels = { x: 'Unnamed', y0: 'Unnamed', y1: 'Unnamed' };
var format = { x: null, y0: null, y1: null};
var xValue = function (d) {
return d[0];
},
yValue0 = function (d) {
return d[1];
},
yValue1 = function (d) {
return d[2];
};
var xScale = d3.scale.ordinal();
var yScale0 = d3.scale.linear().nice();
var yScale1 = d3.scale.linear().nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickFormat(function (d) {
if (format.x)
return GoAccess.Util.fmtValue(d, format.x);
return d;
});
var yAxis0 = d3.svg.axis()
.scale(yScale0)
.orient('left')
.tickFormat(function (d) {
if (format.y0)
return GoAccess.Util.fmtValue(d, format.y0);
return d3.format('.2s')(d);
});
var yAxis1 = d3.svg.axis()
.scale(yScale1)
.orient('right')
.tickFormat(function (d) {
if (format.y1)
return GoAccess.Util.fmtValue(d, format.y1);
return d3.format('.2s')(d);
});
var xGrid = d3.svg.axis()
.scale(xScale)
.orient('bottom');
var yGrid = d3.svg.axis()
.scale(yScale0)
.orient('left');
var area0 = d3.svg.area()
.interpolate('cardinal')
.x(X)
.y(Y0);
var area1 = d3.svg.area()
.interpolate('cardinal')
.x(X)
.y(Y1);
var line0 = d3.svg.line()
.interpolate('cardinal')
.x(X)
.y(Y0);
var line1 = d3.svg.line()
.interpolate('cardinal')
.x(X)
.y(Y1);
// The x-accessor for the path generator; xScale ∘ xValue.
function X(d) {
return xScale(d[0]);
}
// The x-accessor for the path generator; yScale0 yValue0.
function Y0(d) {
return yScale0(d[1]);
}
// The x-accessor for the path generator; yScale0 yValue0.
function Y1(d) {
return yScale1(d[2]);
}
function innerW() {
return width - margin.left - margin.right;
}
function innerH() {
return height - margin.top - margin.bottom;
}
function getXTicks(data) {
if (data.length < nTicks)
return xScale.domain();
return d3.range(0, data.length, Math.ceil(data.length / nTicks)).map(function (d) {
return xScale.domain()[d];
});
}
function getYTicks(scale) {
var domain = scale.domain();
return d3.range(domain[0], domain[1], Math.ceil(domain[1] / nTicks));
}
// Convert data to standard representation greedily;
// this is needed for nondeterministic accessors.
function mapData(data) {
var _datum = function (d, i) {
var datum = [xValue.call(data, d, i), yValue0.call(data, d, i)];
dualYaxis && datum.push(yValue1.call(data, d, i));
return datum;
};
return data.map(function (d, i) {
return _datum(d, i);
});
}
function updateScales(data) {
// Update the x-scale.
xScale.domain(data.map(function (d) {
return d[0];
}))
.rangePoints([0, innerW()], 1);
// Update the y-scale.
yScale0.domain([0, d3.max(data, function (d) {
return d[1];
})])
.range([innerH(), 0]);
// Update the y-scale.
dualYaxis && yScale1.domain([0, d3.max(data, function (d) {
return d[2];
})])
.range([innerH(), 0]);
}
function toggleOpacity(ele, op) {
d3.select(ele.parentNode).selectAll('.' + (ele.getAttribute('data-yaxis') == 'y0' ? 'y1' : 'y0')).attr('style', op);
}
function setLegendLabels(svg) {
// Legend Color
var rect = svg.selectAll('rect.legend.y0').data([null]);
rect.enter().append('rect')
.attr('class', 'legend y0')
.attr('data-yaxis', 'y0')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 15));
rect
.attr('x', (width / 2) - 100);
// Legend Labels
var text = svg.selectAll('text.legend.y0').data([null]);
text.enter().append('text')
.attr('class', 'legend y0')
.attr('data-yaxis', 'y0')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 6));
text
.attr('x', (width / 2) - 85)
.text(labels.y0);
if (!dualYaxis)
return;
// Legend Labels
rect = svg.selectAll('rect.legend.y1').data([null]);
rect.enter().append('rect')
.attr('class', 'legend y1')
.attr('data-yaxis', 'y1')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 15));
rect
.attr('x', (width / 2));
// Legend Labels
text = svg.selectAll('text.legend.y1').data([null]);
text.enter().append('text')
.attr('class', 'legend y1')
.attr('data-yaxis', 'y1')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 6));
text
.attr('x', (width / 2) + 15)
.text(labels.y1);
}
function setAxisLabels(svg) {
// Labels
svg.selectAll('text.axis-label.y0').data([null])
.enter().append('text')
.attr('class', 'axis-label y0')
.attr('y', 10)
.attr('x', 50)
.text(labels.y0);
if (!dualYaxis)
return;
// Labels
var tEnter = svg.selectAll('text.axis-label.y1').data([null]);
tEnter.enter().append('text')
.attr('class', 'axis-label y1')
.attr('y', 10)
.text(labels.y1);
dualYaxis && tEnter
.attr('x', width - 25)
}
function createSkeleton(svg) {
// Otherwise, create the skeletal chart.
var gEnter = svg.enter().append('svg').append('g');
// Lines
gEnter.append('g')
.attr('class', 'line line0 y0');
dualYaxis && gEnter.append('g')
.attr('class', 'line line1 y1');
// Areas
gEnter.append('g')
.attr('class', 'area area0 y0');
dualYaxis && gEnter.append('g')
.attr('class', 'area area1 y1');
// Points
gEnter.append('g')
.attr('class', 'points y0');
dualYaxis && gEnter.append('g')
.attr('class', 'points y1');
// Grid
gEnter.append('g')
.attr('class', 'x grid');
gEnter.append('g')
.attr('class', 'y grid');
// Axis
gEnter.append('g')
.attr('class', 'x axis');
gEnter.append('g')
.attr('class', 'y0 axis');
dualYaxis && gEnter.append('g')
.attr('class', 'y1 axis');
// Rects
gEnter.append('g')
.attr('class', 'rects');
setAxisLabels(svg);
setLegendLabels(svg);
// Mouseover line
gEnter.append('line')
.attr('y2', innerH())
.attr('y1', 0)
.attr('class', 'indicator');
}
function pathLen(d) {
return d.node().getTotalLength();
}
function addLine(g, data, line, cName) {
// Update the line path.
var path = g.select('g.' + cName).selectAll('path.' + cName)
.data([data]);
// enter
path
.enter()
.append('svg:path')
.attr('d', line)
.attr('class', cName)
.attr('stroke-dasharray', function (d) {
var pl = pathLen(d3.select(this));
return pl + ' ' + pl;
})
.attr('stroke-dashoffset', function (d) {
return pathLen(d3.select(this))
});
// update
path
.attr('d', line)
.transition()
.attr('stroke-dasharray', function (d) {
var pl = pathLen(d3.select(this));
return pl + ' ' + pl;
})
.duration(2000)
.attr('stroke-dashoffset', 0);
// remove elements
path.exit().remove();
}
function addArea(g, data, cb, cName) {
// Update the area path.
var area = g.select('g.' + cName).selectAll('path.' + cName)
.data([data]);
area
.enter()
.append('svg:path')
.attr('class', cName);
area
.attr('d', cb);
// remove elements
area.exit().remove();
}
// Update the area path and lines.
function addAreaLines(g, data) {
// Update the area path.
addArea(g, data, area0.y0(yScale0.range()[0]), 'area0');
// Update the line path.
addLine(g, data, line0, 'line0');
// Update the area path.
addArea(g, data, area1.y1(yScale1.range()[0]), 'area1');
// Update the line path.
addLine(g, data, line1, 'line1');
}
// Update chart points
function addPoints(g, data) {
var radius = data.length > 100 ? 1 : 2.5;
var points = g.select('g.points.y0').selectAll('circle.point')
.data(data);
points
.enter()
.append('svg:circle')
.attr('r', radius)
.attr('class', 'point');
points
.attr('cx', function (d) { return xScale(d[0]) })
.attr('cy', function (d) { return yScale0(d[1]) })
// remove elements
points.exit().remove();
if (!dualYaxis)
return;
points = g.select('g.points.y1').selectAll('circle.point')
.data(data);
points
.enter()
.append('svg:circle')
.attr('r', radius)
.attr('class', 'point');
points
.attr('cx', function (d) { return xScale(d[0]) })
.attr('cy', function (d) { return yScale1(d[2]) })
// remove elements
points.exit().remove();
}
function addAxis(g, data) {
var xTicks = getXTicks(data);
var tickDistance = xTicks.length > 1 ? (xScale(xTicks[1]) - xScale(xTicks[0])) : innerW();
var labelW = tickDistance - padding;
// Update the x-axis.
g.select('.x.axis')
.attr('transform', 'translate(0,' + yScale0.range()[0] + ')')
.call(xAxis.tickValues(xTicks))
.selectAll(".tick text")
.call(truncate, labelW > 0 ? labelW : innerW());
// Update the y0-axis.
g.select('.y0.axis')
.call(yAxis0.tickValues(getYTicks(yScale0)) );
if (!dualYaxis)
return;
// Update the y1-axis.
g.select('.y1.axis')
.attr('transform', 'translate(' + innerW() + ', 0)')
.call(yAxis1.tickValues(getYTicks(yScale1)));
}
// Update the X-Y grid.
function addGrid(g, data) {
g.select('.x.grid')
.attr('transform', 'translate(0,' + yScale0.range()[0] + ')')
.call(xGrid
.tickValues(getXTicks(data))
.tickSize(-innerH(), 0, 0)
.tickFormat('')
);
g.select('.y.grid')
.call(yGrid
.tickValues(getYTicks(yScale0))
.tickSize(-innerW(), 0, 0)
.tickFormat('')
);
}
function formatTooltip(data, i) {
var d = data.slice(0);
d[0] = (format.x) ? GoAccess.Util.fmtValue(d[0], format.x) : d[0];
d[1] = (format.y0) ? GoAccess.Util.fmtValue(d[1], format.y0) : d3.format(',')(d[1]);
dualYaxis && (d[2] = (format.y1) ? GoAccess.Util.fmtValue(d[2], format.y1) : d3.format(',')(d[2]));
var template = d3.select('#tpl-chart-tooltip').html();
return Hogan.compile(template).render({
'data': d
});
}
function mouseover(_self, selection, data, idx) {
var tooltip = selection.select('.chart-tooltip-wrap');
tooltip.html(formatTooltip(data, idx))
.style('left', (xScale(data[0])) + 'px')
.style('top', (d3.mouse(_self)[1] + 10) + 'px')
.style('display', 'block');
selection.select('line.indicator')
.style('display', 'block')
.attr('transform', 'translate(' + xScale(data[0]) + ',' + 0 + ')');
}
function mouseout(selection, g) {
var tooltip = selection.select('.chart-tooltip-wrap');
tooltip.style('display', 'none');
g.select('line.indicator').style('display', 'none');
}
function addRects(selection, g, data) {
var w = (innerW() / data.length);
var rects = g.select('g.rects').selectAll('rect')
.data(data);
rects
.enter()
.append('svg:rect')
.attr('height', innerH())
.attr('class', 'point');
rects
.attr('width', d3.functor(w))
.attr('x', function (d, i) { return (w * i); })
.attr('y', 0)
.on('mousemove', function (d, i) {
mouseover(this, selection, d, i);
})
.on('mouseleave', function (d, i) {
mouseout(selection, g);
});
// remove elements
rects.exit().remove();
}
function chart(selection) {
selection.each(function (data) {
// normalize data
data = mapData(data);
// updates X-Y scales
updateScales(data);
// Select the svg element, if it exists.
var svg = d3.select(this).selectAll('svg').data([data]);
createSkeleton(svg);
// Update the outer dimensions.
svg.attr({
'width': width,
'height': height
});
// Update the inner dimensions.
var g = svg.select('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Add grid
addGrid(g, data);
// Add chart lines and areas
addAreaLines(g, data);
// Add chart points
addPoints(g, data);
// Add axis
addAxis(g, data);
// Add rects
addRects(selection, g, data);
});
}
chart.opts = function (_) {
if (!arguments.length) return opts;
opts = _;
return chart;
};
chart.format = function (_) {
if (!arguments.length) return format;
format = _;
return chart;
};
chart.labels = function (_) {
if (!arguments.length) return labels;
labels = _;
return chart;
};
chart.margin = function (_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function (_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function (_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.x = function (_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y0 = function (_) {
if (!arguments.length) return yValue0;
yValue0 = _;
return chart;
};
chart.y1 = function (_) {
if (!arguments.length) return yValue1;
yValue1 = _;
return chart;
};
return chart;
}
function BarChart(dualYaxis) {
var opts = {};
var margin = {
top : 20,
right : 50,
bottom : 40,
left : 50
},
height = 170,
nTicks = 10,
padding = 10,
width = 760;
var labels = { x: 'Unnamed', y0: 'Unnamed', y1: 'Unnamed' };
var format = { x: null, y0: null, y1: null};
var xValue = function (d) {
return d[0];
},
yValue0 = function (d) {
return d[1];
},
yValue1 = function (d) {
return d[2];
};
var xScale = d3.scale.ordinal();
var yScale0 = d3.scale.linear().nice();
var yScale1 = d3.scale.linear().nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickFormat(function (d) {
if (format.x)
return GoAccess.Util.fmtValue(d, format.x);
return d;
});
var yAxis0 = d3.svg.axis()
.scale(yScale0)
.orient('left')
.tickFormat(function (d) {
if (format.y1)
return GoAccess.Util.fmtValue(d, format.y1);
return d3.format('.2s')(d);
});
var yAxis1 = d3.svg.axis()
.scale(yScale1)
.orient('right')
.tickFormat(function (d) {
if (format.y1)
return GoAccess.Util.fmtValue(d, format.y1);
return d3.format('.2s')(d);
});
var xGrid = d3.svg.axis()
.scale(xScale)
.orient('bottom');
var yGrid = d3.svg.axis()
.scale(yScale0)
.orient('left');
function innerW() {
return width - margin.left - margin.right;
}
function innerH() {
return height - margin.top - margin.bottom;
}
function getXTicks(data) {
if (data.length < nTicks)
return xScale.domain();
return d3.range(0, data.length, Math.ceil(data.length / nTicks)).map(function (d) {
return xScale.domain()[d];
});
}
function getYTicks(scale) {
var domain = scale.domain();
return d3.range(domain[0], domain[1], Math.ceil(domain[1] / nTicks));
}
// Convert data to standard representation greedily;
// this is needed for nondeterministic accessors.
function mapData(data) {
var _datum = function (d, i) {
var datum = [xValue.call(data, d, i), yValue0.call(data, d, i)];
dualYaxis && datum.push(yValue1.call(data, d, i));
return datum;
};
return data.map(function (d, i) {
return _datum(d, i);
});
}
function updateScales(data) {
// Update the x-scale.
xScale.domain(data.map(function (d) {
return d[0];
}))
.rangeBands([0, innerW()], .1);
// Update the y-scale.
yScale0.domain([0, d3.max(data, function (d) {
return d[1];
})])
.range([innerH(), 0]);
// Update the y-scale.
dualYaxis && yScale1.domain([0, d3.max(data, function (d) {
return d[2];
})])
.range([innerH(), 0]);
}
function toggleOpacity(ele, op) {
d3.select(ele.parentNode).selectAll('.' + (ele.getAttribute('data-yaxis') == 'y0' ? 'y1' : 'y0')).attr('style', op);
}
function setLegendLabels(svg) {
// Legend Color
var rect = svg.selectAll('rect.legend.y0').data([null]);
rect.enter().append('rect')
.attr('class', 'legend y0')
.attr('data-yaxis', 'y0')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 15));
rect
.attr('x', (width / 2) - 100);
// Legend Labels
var text = svg.selectAll('text.legend.y0').data([null]);
text.enter().append('text')
.attr('class', 'legend y0')
.attr('data-yaxis', 'y0')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 6));
text
.attr('x', (width / 2) - 85)
.text(labels.y0);
if (!dualYaxis)
return;
// Legend Labels
rect = svg.selectAll('rect.legend.y1').data([null]);
rect.enter().append('rect')
.attr('class', 'legend y1')
.attr('data-yaxis', 'y1')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 15));
rect
.attr('x', (width / 2));
// Legend Labels
text = svg.selectAll('text.legend.y1').data([null]);
text.enter().append('text')
.attr('class', 'legend y1')
.attr('data-yaxis', 'y1')
.on('mousemove', function (d, i) { toggleOpacity(this, 'opacity:0.1'); })
.on('mouseleave', function (d, i) { toggleOpacity(this, null); })
.attr('y', (height - 6));
text
.attr('x', (width / 2) + 15)
.text(labels.y1);
}
function setAxisLabels(svg) {
// Labels
svg.selectAll('text.axis-label.y0').data([null])
.enter().append('text')
.attr('class', 'axis-label y0')
.attr('y', 10)
.attr('x', 50)
.text(labels.y0);
if (!dualYaxis)
return;
// Labels
var tEnter = svg.selectAll('text.axis-label.y1').data([null]);
tEnter.enter().append('text')
.attr('class', 'axis-label y1')
.attr('y', 10)
.text(labels.y1);
dualYaxis && tEnter
.attr('x', width - 25)
}
function createSkeleton(svg) {
// Otherwise, create the skeletal chart.
var gEnter = svg.enter().append('svg').append('g');
// Grid
gEnter.append('g')
.attr('class', 'x grid');
gEnter.append('g')
.attr('class', 'y grid');
// Axis
gEnter.append('g')
.attr('class', 'x axis');
gEnter.append('g')
.attr('class', 'y0 axis');
dualYaxis && gEnter.append('g')
.attr('class', 'y1 axis');
// Bars
gEnter.append('g')
.attr('class', 'bars y0');
dualYaxis && gEnter.append('g')
.attr('class', 'bars y1');
// Rects
gEnter.append('g')
.attr('class', 'rects');
setAxisLabels(svg);
setLegendLabels(svg);
// Mouseover line
gEnter.append('line')
.attr('y2', innerH())
.attr('y1', 0)
.attr('class', 'indicator');
}
// Update the area path and lines.
function addBars(g, data) {
var bars = g.select('g.bars.y0').selectAll('rect.bar')
.data(data);
// enter
bars
.enter()
.append('svg:rect')
.attr('class', 'bar')
.attr('height', 0)
.attr('width', function (d, i) { return xScale.rangeBand() / 2; })
.attr('x', function (d, i) { return xScale(d[0]); })
.attr('y', function (d, i) { return innerH(); });
// update
bars
.attr('width', xScale.rangeBand() / 2)
.attr('x', function (d) { return xScale(d[0]) })
.transition()
.delay(function (d, i) { return i / data.length * 1000; })
.duration(500)
.attr('height', function (d, i) { return innerH() - yScale0(d[1]); })
.attr('y', function (d, i) { return yScale0(d[1]); });
// remove elements
bars.exit().remove();
if (!dualYaxis)
return;
var bars = g.select('g.bars.y1').selectAll('rect.bar')
.data(data);
// enter
bars
.enter()
.append('svg:rect')
.attr('class', 'bar')
.attr('height', 0)
.attr('width', function (d, i) { return xScale.rangeBand() / 2; })
.attr('x', function (d) { return (xScale(d[0]) + xScale.rangeBand() / 2) })
.attr('y', function (d, i) { return innerH(); });
// update
bars
.attr('width', xScale.rangeBand() / 2)
.attr('x', function (d) { return (xScale(d[0]) + xScale.rangeBand() / 2) })
.transition()
.delay(function (d, i) { return i / data.length * 1000; })
.duration(500)
.attr('height', function (d, i) { return innerH() - yScale1(d[2]); })
.attr('y', function (d, i) { return yScale1(d[2]); });
// remove elements
bars.exit().remove();
}
function addAxis(g, data) {
var xTicks = getXTicks(data);
var tickDistance = xTicks.length > 1 ? (xScale(xTicks[1]) - xScale(xTicks[0])) : innerW();
var labelW = tickDistance - padding;
// Update the x-axis.
g.select('.x.axis')
.attr('transform', 'translate(0,' + yScale0.range()[0] + ')')
.call(xAxis.tickValues(xTicks))
.selectAll(".tick text")
.call(truncate, labelW > 0 ? labelW : innerW());
// Update the y0-axis.
g.select('.y0.axis')
.call(yAxis0.tickValues(getYTicks(yScale0)));
if (!dualYaxis)
return;
// Update the y1-axis.
g.select('.y1.axis')
.attr('transform', 'translate(' + innerW() + ', 0)')
.call(yAxis1.tickValues(getYTicks(yScale1)));
}
// Update the X-Y grid.
function addGrid(g, data) {
g.select('.x.grid')
.attr('transform', 'translate(0,' + yScale0.range()[0] + ')')
.call(xGrid
.tickValues(getXTicks(data))
.tickSize(-innerH(), 0, 0)
.tickFormat('')
);
g.select('.y.grid')
.call(yGrid
.tickValues(getYTicks(yScale0))
.tickSize(-innerW(), 0, 0)
.tickFormat('')
);
}
function formatTooltip(data, i) {
var d = data.slice(0);
d[0] = (format.x) ? GoAccess.Util.fmtValue(d[0], format.x) : d[0];
d[1] = (format.y0) ? GoAccess.Util.fmtValue(d[1], format.y0) : d3.format(',')(d[1]);
dualYaxis && (d[2] = (format.y1) ? GoAccess.Util.fmtValue(d[2], format.y1) : d3.format(',')(d[2]));
var template = d3.select('#tpl-chart-tooltip').html();
return Hogan.compile(template).render({
'data': d
});
}
function mouseover(_self, selection, data, idx) {
var left = xScale(data[0]) + (xScale.rangeBand() / 2);
var tooltip = selection.select('.chart-tooltip-wrap');
tooltip.html(formatTooltip(data, idx))
.style('left', left + 'px')
.style('top', (d3.mouse(_self)[1] + 10) + 'px')
.style('display', 'block');
selection.select('line.indicator')
.style('display', 'block')
.attr('transform', 'translate(' + left + ',' + 0 + ')');
}
function mouseout(selection, g) {
var tooltip = selection.select('.chart-tooltip-wrap');
tooltip.style('display', 'none');
g.select('line.indicator').style('display', 'none');
}
function addRects(selection, g, data) {
var w = (innerW() / data.length);
var rects = g.select('g.rects').selectAll('rect')
.data(data);
rects
.enter()
.append('svg:rect')
.attr('height', innerH())
.attr('class', 'point');
rects
.attr('width', d3.functor(w))
.attr('x', function (d, i) { return (w * i); })
.attr('y', 0)
.on('mousemove', function (d, i) {
mouseover(this, selection, d, i);
})
.on('mouseleave', function (d, i) {
mouseout(selection, g);
});
// remove elements
rects.exit().remove();
}
function chart(selection) {
selection.each(function (data) {
// normalize data
data = mapData(data);
// updates X-Y scales
updateScales(data);
// Select the svg element, if it exists.
var svg = d3.select(this).selectAll('svg').data([data]);
createSkeleton(svg);
// Update the outer dimensions.
svg.attr({
'width': width,
'height': height
});
// Update the inner dimensions.
var g = svg.select('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Add grid
addGrid(g, data);
// Add axis
addAxis(g, data);
// Add chart lines and areas
addBars(g, data);
// Add rects
addRects(selection, g, data);
});
}
chart.opts = function (_) {
if (!arguments.length) return opts;
opts = _;
return chart;
};
chart.format = function (_) {
if (!arguments.length) return format;
format = _;
return chart;
};
chart.labels = function (_) {
if (!arguments.length) return labels;
labels = _;
return chart;
};
chart.width = function (_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function (_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.x = function (_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y0 = function (_) {
if (!arguments.length) return yValue0;
yValue0 = _;
return chart;
};
chart.y1 = function (_) {
if (!arguments.length) return yValue1;
yValue1 = _;
return chart;
};
return chart;
}
</script></body></html>