diff --git a/include/ajax.reports.php b/include/ajax.reports.php
deleted file mode 100644
index 81eb83b8d802fd60a199780fa583ff77ec49cee7..0000000000000000000000000000000000000000
--- a/include/ajax.reports.php
+++ /dev/null
@@ -1,243 +0,0 @@
-<?php
-/*********************************************************************
-    ajax.reports.php
-
-    AJAX interface for reports -- both plot and tabular data are retrievable
-    in JSON format from this utility. Please put plumbing in /scp/ajax.php
-    pattern rules.
-
-    Jared Hancock <jared@osticket.com>
-    Copyright (c)  2006-2013 osTicket
-    http://www.osticket.com
-
-    Released under the GNU General Public License WITHOUT ANY WARRANTY.
-    See LICENSE.TXT for details.
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-
-if(!defined('INCLUDE_DIR')) die('403');
-
-include_once(INCLUDE_DIR.'class.ticket.php');
-include_once(INCLUDE_DIR.'class.report.php');
-
-/**
- * Overview Report
- *
- * The overview report allows for the display of basic ticket statistics in
- * both graphical and tabular formats.
- */
-class OverviewReportAjaxAPI extends AjaxController {
-    function enumTabularGroups() {
-        return $this->encode(array("dept"=>__("Department"), "topic"=>__("Topics"),
-            # XXX: This will be relative to permissions based on the
-            # logged-in-staff. For basic staff, this will be 'My Stats'
-            "staff"=>__("Agent")));
-    }
-
-    function getData() {
-        global $thisstaff;
-
-        list($start, $stop) = $this->_getDateRange();
-
-        $groups = array(
-            "dept" => array(
-                "table" => DEPT_TABLE,
-                "pk" => "id",
-                "fpk" => "dept_id",
-                "sort" => 'T1.name',
-                "fields" => 'T1.name',
-                "headers" => array(__('Department')),
-                "filter" => ('T1.id IN ('.implode(',', db_input($thisstaff->getDepts())).')')
-            ),
-            "topic" => array(
-                "table" => TOPIC_TABLE,
-                "pk" => "topic_id",
-                "sort" => 'name',
-                "fields" => "CONCAT_WS(' / ',"
-                    ."(SELECT P.topic FROM ".TOPIC_TABLE." P WHERE P.topic_id = T1.topic_pid),"
-                    ."T1.topic) as name ",
-                "headers" => array(__('Help Topic')),
-                "filter" => '1'
-            ),
-            "staff" => array(
-                "table" => STAFF_TABLE,
-                "pk" => 'staff_id',
-                "sort" => 'name',
-                "fields" => "CONCAT_WS(' ', T1.firstname, T1.lastname) as name",
-                "headers" => array(__('Agent')),
-                "filter" =>
-                    ('T1.staff_id=S1.staff_id
-                      AND
-                      (T1.staff_id='.db_input($thisstaff->getId())
-                        .(($depts=$thisstaff->getManagedDepartments())?
-                            (' OR T1.dept_id IN('.implode(',', db_input($depts)).')'):'')
-                        .($thisstaff->hasPerm(ReportModel::PERM_AGENTS)?
-                            (' OR T1.dept_id IN('.implode(',', db_input($thisstaff->getDepts())).')'):'')
-                     .')'
-                     )
-            )
-        );
-        $group = $this->get('group', 'dept');
-        $info = isset($groups[$group])?$groups[$group]:$groups['dept'];
-
-        # XXX: Die if $group not in $groups
-        $info['fpk'] = $info['fpk'] ?: $info['pk'];
-        $queries=array(
-            array(5, 'SELECT '.$info['fields'].',
-                COUNT(*)-COUNT(NULLIF(A1.state, "created")) AS Opened,
-                COUNT(*)-COUNT(NULLIF(A1.state, "assigned")) AS Assigned,
-                COUNT(*)-COUNT(NULLIF(A1.state, "overdue")) AS Overdue,
-                COUNT(*)-COUNT(NULLIF(A1.state, "closed")) AS Closed,
-                COUNT(*)-COUNT(NULLIF(A1.state, "reopened")) AS Reopened
-            FROM '.$info['table'].' T1
-                LEFT JOIN '.THREAD_EVENT_TABLE.' A1
-                    ON (A1.'.$info['fpk'].'=T1.'.$info['pk'].'
-                         AND NOT annulled
-                         AND (A1.timestamp BETWEEN '.$start.' AND '.$stop.'))
-                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=A1.staff_id)
-            WHERE '.$info['filter'].'
-            GROUP BY T1.'.$info['pk'].'
-            ORDER BY '.$info['sort']),
-
-            array(1, 'SELECT '.$info['fields'].',
-                FORMAT(AVG(DATEDIFF(T2.closed, T2.created)),1) AS ServiceTime
-            FROM '.$info['table'].' T1
-                LEFT JOIN '.TICKET_TABLE.' T2 ON (T2.'.$info['fpk'].'=T1.'.$info['pk'].')
-                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=T2.staff_id)
-            WHERE '.$info['filter'].' AND T2.closed BETWEEN '.$start.' AND '.$stop.'
-            GROUP BY T1.'.$info['pk'].'
-            ORDER BY '.$info['sort']),
-
-            array(1, 'SELECT '.$info['fields'].',
-                FORMAT(AVG(DATEDIFF(B2.created, B1.created)),1) AS ResponseTime
-            FROM '.$info['table'].' T1
-                LEFT JOIN '.TICKET_TABLE.' T2 ON (T2.'.$info['fpk'].'=T1.'.$info['pk'].')
-                LEFT JOIN '.THREAD_TABLE.' B0 ON (B0.object_id=T2.ticket_id
-                    AND B0.object_type="T")
-                LEFT JOIN '.THREAD_ENTRY_TABLE.' B2 ON (B2.thread_id = B0.id
-                    AND B2.`type`="R")
-                LEFT JOIN '.THREAD_ENTRY_TABLE.' B1 ON (B2.pid = B1.id)
-                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=B2.staff_id)
-            WHERE '.$info['filter'].' AND B1.created BETWEEN '.$start.' AND '.$stop.'
-            GROUP BY T1.'.$info['pk'].'
-            ORDER BY '.$info['sort'])
-        );
-        $rows = array();
-        $cols = 1;
-        foreach ($queries as $q) {
-            list($c, $sql) = $q;
-            $res = db_query($sql);
-            $cols += $c;
-            while ($row = db_fetch_row($res)) {
-                $found = false;
-                foreach ($rows as &$r) {
-                    if ($r[0] == $row[0]) {
-                        $r = array_merge($r, array_slice($row, -$c));
-                        $found = true;
-                        break;
-                    }
-                }
-                if (!$found)
-                    $rows[] = array_merge(array($row[0]), array_slice($row, -$c));
-            }
-            # Make sure each row has the same number of items
-            foreach ($rows as &$r)
-                while (count($r) < $cols)
-                    $r[] = null;
-        }
-        return array("columns" => array_merge($info['headers'],
-                        array(__('Opened'),__('Assigned'),__('Overdue'),__('Closed'),__('Reopened'),
-                              __('Service Time'),__('Response Time'))),
-                     "data" => $rows);
-    }
-
-    function getTabularData() {
-        return $this->encode($this->getData());
-    }
-
-    function downloadTabularData() {
-        $data = $this->getData();
-        $csv = '"' . implode('","',$data['columns']) . '"';
-        foreach ($data['data'] as $row)
-            $csv .= "\n" . '"' . implode('","', $row) . '"';
-        Http::download(
-            sprintf('%s-report.csv', $this->get('group', __('Department'))),
-            'text/csv', $csv);
-    }
-
-    function _getDateRange() {
-        global $cfg;
-
-        if(($start = $this->get('start', 'last month'))) {
-            $stop = $this->get('period', 'now');
-        } else {
-            $start = 'last month';
-            $stop = $this->get('period', 'now');
-        }
-
-       $start = strtotime($start);
-
-        if (substr($stop, 0, 1) == '+')
-            $stop = strftime('%Y-%m-%d ', $start) . $stop;
-
-        $start = 'FROM_UNIXTIME('.$start.')';
-        $stop = 'FROM_UNIXTIME('.strtotime($stop).')';
-
-        return array($start, $stop);
-    }
-
-    function getPlotData() {
-        list($start, $stop) = $this->_getDateRange();
-
-        # Fetch all types of events over the timeframe
-        $res = db_query('SELECT DISTINCT(state) FROM '.THREAD_EVENT_TABLE
-            .' WHERE timestamp BETWEEN '.$start.' AND '.$stop
-            .' AND state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
-            .' ORDER BY 1');
-        $events = array();
-        while ($row = db_fetch_row($res)) $events[] = $row[0];
-
-        # TODO: Handle user => db timezone offset
-        # XXX: Implement annulled column from the %ticket_event table
-        $res = db_query('SELECT state, DATE_FORMAT(timestamp, \'%Y-%m-%d\'), '
-                .'COUNT(DISTINCT T.id)'
-            .' FROM '.THREAD_EVENT_TABLE. ' E '
-            .' JOIN '.THREAD_TABLE. ' T
-                ON (T.id = E.thread_id AND T.object_type = "T") '
-            .' WHERE E.timestamp BETWEEN '.$start.' AND '.$stop
-            .' AND NOT annulled'
-            .' AND E.state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
-            .' GROUP BY E.state, DATE_FORMAT(E.timestamp, \'%Y-%m-%d\')'
-            .' ORDER BY 2, 1');
-        # Initialize array of plot values
-        $plots = array();
-        foreach ($events as $e) { $plots[$e] = array(); }
-
-        $time = null; $times = array();
-        # Iterate over result set, adding zeros for missing ticket events
-        $slots = array();
-        while ($row = db_fetch_row($res)) {
-            $row_time = strtotime($row[1]);
-            if ($time != $row_time) {
-                # New time (and not the first), figure out which events did
-                # not have any tickets associated for this time slot
-                if ($time !== null) {
-                    # Not the first record -- add zeros all the arrays that
-                    # did not have at least one entry for the timeframe
-                    foreach (array_diff($events, $slots) as $slot)
-                        $plots[$slot][] = 0;
-                }
-                $slots = array();
-                $times[] = $time = $row_time;
-            }
-            # Keep track of states for this timeframe
-            $slots[] = $row[0];
-            $plots[$row[0]][] = (int)$row[2];
-        }
-        foreach (array_diff($events, $slots) as $slot)
-            $plots[$slot][] = 0;
-        return $this->encode(array("times" => $times, "plots" => $plots,
-            "events"=>$events));
-    }
-}
diff --git a/include/class.dept.php b/include/class.dept.php
index d33b1bb1289ea917043017c3e18e8eba97857a3d..502c34742ccf9610968faaf833c619e3d6b6ac1c 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -112,6 +112,9 @@ implements TemplateVariable {
         $T = CustomDataTranslation::translate($tag);
         return $T != $tag ? $T : $default;
     }
+    static function getLocalNameById($id, $default) {
+        return static::getLocalById($id, 'name', $default);
+    }
 
     function getTranslateTag($subtag='name') {
         return _H(sprintf('dept.%s.%s', $subtag, $this->getId()));
diff --git a/include/class.orm.php b/include/class.orm.php
index a774fc65ea3a51b6ce6d80753f49b6fea7d1dd62..44d292f664038f825478fc01bc6e2e848e78e4aa 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -1806,7 +1806,7 @@ class SqlCompiler {
     static function splitCriteria($criteria) {
         static $operators = array(
             'exact' => 1, 'isnull' => 1,
-            'gt' => 1, 'lt' => 1, 'gte' => 1, 'lte' => 1,
+            'gt' => 1, 'lt' => 1, 'gte' => 1, 'lte' => 1, 'range' => 1,
             'contains' => 1, 'like' => 1, 'startswith' => 1, 'endswith' => 1, 'regex' => 1,
             'in' => 1, 'intersect' => 1,
             'hasbit' => 1,
@@ -2199,6 +2199,7 @@ class MySqlCompiler extends SqlCompiler {
         'lt' => '%1$s < %2$s',
         'gte' => '%1$s >= %2$s',
         'lte' => '%1$s <= %2$s',
+        'range' => array('self', '__range'),
         'isnull' => array('self', '__isnull'),
         'like' => '%1$s LIKE %2$s',
         'hasbit' => '%1$s & %2$s != 0',
@@ -2273,6 +2274,11 @@ class MySqlCompiler extends SqlCompiler {
         return sprintf('%s REGEXP %s', $a, $this->input($b));
     }
 
+    function __range($a, $b) {
+        // XXX: Crash if $b is not array of two items
+        return sprintf('%s BETWEEN %s AND %s', $a, $b[0], $b[1]);
+    }
+
     function compileJoin($tip, $model, $alias, $info, $extra=false) {
         $constraints = array();
         $join = ' JOIN ';
diff --git a/include/class.report.php b/include/class.report.php
index 059325b3f44a69c1905582ba1826a8594ad93dc1..9913df318f10df9e0bfc700201a85d3b9f094dee 100644
--- a/include/class.report.php
+++ b/include/class.report.php
@@ -19,4 +19,203 @@ class ReportModel {
 }
 
 RolePermission::register(/* @trans */ 'Miscellaneous', ReportModel::getPermissions());
-?>
+
+class OverviewReport {
+    var $start;
+    var $end;
+
+    function __construct($start, $end='now') {
+        $this->start = $start;
+        $this->end = $end;
+    }
+
+    function getDateRange() {
+        global $cfg;
+
+        $start = $this->start ?: 'last month';
+        $stop = $this->end ?: 'now';
+
+        $start = strtotime($start);
+
+        if (substr($stop, 0, 1) == '+')
+            $stop = strftime('%Y-%m-%d ', $start) . $stop;
+
+        $start = 'FROM_UNIXTIME('.$start.')';
+        $stop = 'FROM_UNIXTIME('.strtotime($stop).')';
+
+        return array($start, $stop);
+    }
+
+    function getPlotData() {
+        list($start, $stop) = $this->getDateRange();
+
+        # Fetch all types of events over the timeframe
+        $res = db_query('SELECT DISTINCT(state) FROM '.THREAD_EVENT_TABLE
+            .' WHERE timestamp BETWEEN '.$start.' AND '.$stop
+            .' AND state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
+            .' ORDER BY 1');
+        $events = array();
+        while ($row = db_fetch_row($res)) $events[] = $row[0];
+
+        # TODO: Handle user => db timezone offset
+        # XXX: Implement annulled column from the %ticket_event table
+        $res = db_query('SELECT state, DATE_FORMAT(timestamp, \'%Y-%m-%d\'), '
+                .'COUNT(DISTINCT T.id)'
+            .' FROM '.THREAD_EVENT_TABLE. ' E '
+            .' JOIN '.THREAD_TABLE. ' T
+                ON (T.id = E.thread_id AND T.object_type = "T") '
+            .' WHERE E.timestamp BETWEEN '.$start.' AND '.$stop
+            .' AND NOT annulled'
+            .' AND E.state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
+            .' GROUP BY E.state, DATE_FORMAT(E.timestamp, \'%Y-%m-%d\')'
+            .' ORDER BY 2, 1');
+        # Initialize array of plot values
+        $plots = array();
+        foreach ($events as $e) { $plots[$e] = array(); }
+
+        $time = null; $times = array();
+        # Iterate over result set, adding zeros for missing ticket events
+        $slots = array();
+        while ($row = db_fetch_row($res)) {
+            $row_time = strtotime($row[1]);
+            if ($time != $row_time) {
+                # New time (and not the first), figure out which events did
+                # not have any tickets associated for this time slot
+                if ($time !== null) {
+                    # Not the first record -- add zeros all the arrays that
+                    # did not have at least one entry for the timeframe
+                    foreach (array_diff($events, $slots) as $slot)
+                        $plots[$slot][] = 0;
+                }
+                $slots = array();
+                $times[] = $time = $row_time;
+            }
+            # Keep track of states for this timeframe
+            $slots[] = $row[0];
+            $plots[$row[0]][] = (int)$row[2];
+        }
+        foreach (array_diff($events, $slots) as $slot)
+            $plots[$slot][] = 0;
+
+        return array("times" => $times, "plots" => $plots, "events" => $events);
+    }
+
+    function enumTabularGroups() {
+        return array("dept"=>__("Department"), "topic"=>__("Topics"),
+            # XXX: This will be relative to permissions based on the
+            # logged-in-staff. For basic staff, this will be 'My Stats'
+            "staff"=>__("Agent"));
+    }
+
+    function getTabularData($group='dept') {
+        global $thisstaff;
+
+        list($start, $stop) = $this->getDateRange();
+        $times = Ticket::objects()
+            ->constrain(array(
+                'thread__entries' => array(
+                    'thread__entries__type' => 'R'
+                ),
+            ))
+            ->aggregate(array(
+                'ServiceTime' => SqlAggregate::AVG(SqlFunction::DATEDIFF(
+                    new SqlField('closed'), new SqlField('created')
+                )),
+                'ResponseTime' => SqlAggregate::AVG(SqlFunction::DATEDIFF(
+                    new SqlField('thread__entries__created'), new SqlField('thread__entries__parent__created')
+                )),
+            ));
+
+        $stats = Ticket::objects()
+            ->constrain(array(
+                'thread__events' => array(
+                    'thread__events__annulled' => 0,
+                    'thread__events__timestamp__range' => array($start, $stop),
+                ),
+            ))
+            ->aggregate(array(
+                'Opened' => SqlAggregate::COUNT(
+                    SqlCase::N()
+                        ->when(new Q(array('thread__events__state' => 'created')), 1)
+                ),
+                'Assigned' => SqlAggregate::COUNT(
+                    SqlCase::N()
+                        ->when(new Q(array('thread__events__state' => 'assigned')), 1)
+                ),
+                'Overdue' => SqlAggregate::COUNT(
+                    SqlCase::N()
+                        ->when(new Q(array('thread__events__state' => 'overdue')), 1)
+                ),
+                'Closed' => SqlAggregate::COUNT(
+                    SqlCase::N()
+                        ->when(new Q(array('thread__events__state' => 'closed')), 1)
+                ),
+                'Reopened' => SqlAggregate::COUNT(
+                    SqlCase::N()
+                        ->when(new Q(array('thread__events__state' => 'reopened')), 1)
+                ),
+            ));
+
+        switch ($group) {
+        case 'dept':
+            $headers = array(__('Department'));
+            $header = function($row) { return Dept::getLocalNameById($row['dept_id'], $row['dept__name']); };
+            $pk = 'dept_id';
+            $stats = $stats
+                ->filter(array('dept_id__in' => $thisstaff->getDepts()))
+                ->values('dept__id', 'dept__name');
+            $times = $times
+                ->filter(array('dept_id__in' => $thisstaff->getDepts()))
+                ->values('dept__id');
+            break;
+        case 'topic':
+            $headers = array(__('Help Topic'));
+            $header = function($row) { return Topic::getLocalNameById($row['topic_id'], $row['topic__topic']); };
+            $pk = 'topic_id';
+            $stats = $stats
+                ->values('topic_id', 'topic__topic')
+                ->filter(array('topic_id__gt' => 0));
+            $times = $times
+                ->values('topic_id')
+                ->filter(array('topic_id__gt' => 0));
+            break;
+        case 'staff':
+            $headers = array(__('Agent'));
+            $header = function($row) { return new AgentsName(array(
+                'first' => $row['staff__firstname'], 'last' => $row['staff__lastname'])); };
+            $pk = 'staff_id';
+            $stats = $stats->values('staff_id', 'staff__firstname', 'staff__lastname');
+            $times = $times->values('staff_id');
+            $depts = $thisstaff->getManagedDepartments();
+            if ($thisstaff->hasPerm(ReportModel::PERM_AGENTS))
+                $depts = array_merge($depts, $thisstaff->getDepts());
+            $Q = Q::any(array(
+                'staff_id' => $thisstaff->getId(),
+                'dept_id__in' => $depts
+            ));
+            $stats = $stats->filter(array('staff_id__gt'=>0))->filter($Q);
+            $times = $times->filter(array('staff_id__gt'=>0))->filter($Q);
+            break;
+        default:
+            # XXX: Die if $group not in $groups
+        }
+
+        $timings = array();
+        foreach ($times as $T) {
+            $timings[$T[$pk]] = $T;
+        }
+
+        $rows = array();
+        foreach ($stats as $R) {
+            $T = $timings[$R[$pk]];
+            $rows[] = array($header($R), $R['Opened'], $R['Assigned'],
+                $R['Overdue'], $R['Closed'], $R['Reopened'],
+                number_format($T['ServiceTime'], 1),
+                number_format($T['ResponseTime'], 1));
+        }
+        return array("columns" => array_merge($headers,
+                        array(__('Opened'),__('Assigned'),__('Overdue'),__('Closed'),__('Reopened'),
+                              __('Service Time'),__('Response Time'))),
+                     "data" => $rows);
+    }
+}
diff --git a/include/class.topic.php b/include/class.topic.php
index 557daf533311caf66da3d51b269bb04c7dd2e1d3..d448ea7ec10279dfef915674ee253d50e785efa5 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -371,6 +371,11 @@ implements TemplateVariable {
         return self::getHelpTopics(false, true, $localize);
     }
 
+    static function getLocalNameById($id) {
+        $topics = static::getHelpTopics(false, true);
+        return $topics[$id];
+    }
+
     static function getIdByName($name, $pid=0) {
         $list = self::objects()->filter(array(
             'topic'=>$name,
diff --git a/include/staff/dashboard.inc.php b/include/staff/dashboard.inc.php
index 1aba46122842c9fa4edd7d60d20c33a41a1b3b0e..76551ee75996122c598f0a2745d7279c9ae63116 100644
--- a/include/staff/dashboard.inc.php
+++ b/include/staff/dashboard.inc.php
@@ -1,19 +1,25 @@
+<?php
+$report = new OverviewReport($_POST['start'], $_POST['period']);
+$plots = $report->getPlotData();
+
+?>
 <script type="text/javascript" src="js/raphael-min.js"></script>
 <script type="text/javascript" src="js/g.raphael.js"></script>
 <script type="text/javascript" src="js/g.line-min.js"></script>
 <script type="text/javascript" src="js/g.dot-min.js"></script>
 <script type="text/javascript" src="js/dashboard.inc.js"></script>
 
-<!--<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>-->
 <link rel="stylesheet" type="text/css" href="css/dashboard.css"/>
 
 <div id="basic_search">
     <div style="min-height:25px;">
         <!--<p><?php //echo __('Select the starting time and period for the system activity graph');?></p>-->
-        <form class="form-inline" id="timeframe-form">
+        <form method="post" action="dashboard.php">
+            <?php echo csrf_token(); ?>
             <label>
                 <?php echo __( 'Report timeframe'); ?>:
-                <input type="text" class="dp input-medium search-query" name="start" placeholder="<?php echo __('Last month');?>" />
+                <input type="text" class="dp input-medium search-query" name="start" placeholder="<?php echo __('Last month');?>"i
+                    value="<?php echo Format::htmlchars($_POST['start']); ?>" />
             </label>
             <label>
                 <?php echo __( 'period');?>:
@@ -59,8 +65,54 @@
 <hr/>
 <h2><?php echo __('Statistics'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#statistics"></i></h2>
 <p><?php echo __('Statistics of tickets organized by department, help topic, and agent.');?></p>
-<ul id="tabular-navigation" class="tabs">
+
+<ul class="clean tabs">
+<?php
+$first = true;
+$groups = $report->enumTabularGroups();
+foreach ($groups as $g=>$desc) { ?>
+    <li class="<?php echo $first ? 'active' : ''; ?>"><a href="#<?php echo Format::slugify($g); ?>"
+        ><?php echo Format::htmlchars($desc); ?></a></li>
+<?php
+    $first = false;
+} ?>
 </ul>
-<!--<div id="table-here"></div>-->
 
 <?php
+$first = true;
+foreach ($groups as $g=>$desc) {
+    $data = $report->getTabularData($g); ?>
+    <div class="tab_content <?php echo (!$first) ? 'hidden' : ''; ?>" id="<?php echo Format::slugify($g); ?>">
+    <table class="table"><tbody><tr>
+<?php
+    foreach ($data['columns'] as $c) { ?>
+        <th><?php echo Format::htmlchars($c); ?></th>
+<?php
+    } ?>
+    </tr></tbody>
+    <tbody>
+<?php
+    foreach ($data['data'] as $i=>$row) {
+        echo '<tr>';
+        foreach ($row as $j=>$td) {
+            if ($j === 0) { ?>
+                <th><?php echo Format::htmlchars($td); ?></th>
+<?php       }
+            else { ?>
+                <td><?php echo Format::htmlchars($td);
+                if ($td) { // TODO Add head map
+                }
+                echo '</td>';
+            }
+        }
+        echo '</tr>';
+    }
+    $first = false; ?>
+    </tbody></table>
+    </div>
+<?php
+}
+?>
+<script>
+    $.drawPlots(<?php echo JsonDataEncoder::encode($report->getPlotData()); ?>);
+</script>
diff --git a/scp/css/bootstrap.css b/scp/css/bootstrap.css
deleted file mode 100644
index 7a02fb748814898548e67f60e34185991712de67..0000000000000000000000000000000000000000
--- a/scp/css/bootstrap.css
+++ /dev/null
@@ -1,1589 +0,0 @@
-/*!
- * Bootstrap v2.0.4
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-.clearfix {
-  *zoom: 1;
-}
-.clearfix:before,
-.clearfix:after {
-  display: table;
-  content: "";
-}
-.clearfix:after {
-  clear: both;
-}
-.hide-text {
-  font: 0/0 a;
-  color: transparent;
-  text-shadow: none;
-  background-color: transparent;
-  border: 0;
-}
-.input-block-level {
-  display: block;
-  width: 100%;
-  min-height: 28px;
-  -webkit-box-sizing: border-box;
-  -moz-box-sizing: border-box;
-  -ms-box-sizing: border-box;
-  box-sizing: border-box;
-}
-p {
-  margin: 0 0 9px;
-}
-p small {
-  font-size: 11px;
-  color: #999999;
-}
-.lead {
-  margin-bottom: 18px;
-  font-size: 20px;
-  font-weight: 200;
-  line-height: 27px;
-}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-  margin: 0;
-  font-family: inherit;
-  font-weight: bold;
-  color: inherit;
-  text-rendering: optimizelegibility;
-}
-h1 small,
-h2 small,
-h3 small,
-h4 small,
-h5 small,
-h6 small {
-  font-weight: normal;
-  color: #999999;
-}
-h1 {
-  font-size: 30px;
-  line-height: 36px;
-}
-h1 small {
-  font-size: 18px;
-}
-h2 {
-  font-size: 24px;
-  line-height: 36px;
-}
-h2 small {
-  font-size: 18px;
-}
-h3 {
-  font-size: 18px;
-  line-height: 27px;
-}
-h3 small {
-  font-size: 14px;
-}
-h4,
-h5,
-h6 {
-  line-height: 18px;
-}
-h4 {
-  font-size: 14px;
-}
-h4 small {
-  font-size: 12px;
-}
-h5 {
-  font-size: 12px;
-}
-h6 {
-  font-size: 11px;
-  color: #999999;
-  text-transform: uppercase;
-}
-.page-header {
-  padding-bottom: 17px;
-  margin: 18px 0;
-  border-bottom: 1px solid #eeeeee;
-}
-.page-header h1 {
-  line-height: 1;
-}
-ul,
-ol {
-  padding: 0;
-  margin: 0 0 9px 25px;
-}
-ul ul,
-ul ol,
-ol ol,
-ol ul {
-  margin-bottom: 0;
-}
-ul {
-  list-style: disc;
-}
-ol {
-  list-style: decimal;
-}
-/*li {
-  line-height: 18px;
-}*/
-ul.unstyled,
-ol.unstyled {
-  margin-left: 0;
-  list-style: none;
-}
-dl {
-  margin-bottom: 18px;
-}
-dt,
-dd {
-  line-height: 18px;
-}
-dt {
-  font-weight: bold;
-  line-height: 17px;
-}
-dd {
-  margin-left: 9px;
-}
-.dl-horizontal dt {
-  float: left;
-  width: 120px;
-  clear: left;
-  text-align: right;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.dl-horizontal dd {
-  margin-left: 130px;
-}
-hr {
-  margin: 18px 0;
-  border: 0;
-  border-top: 1px solid #eeeeee;
-  border-bottom: 1px solid #ffffff;
-}
-strong {
-  font-weight: bold;
-}
-em {
-  font-style: italic;
-}
-.muted {
-  color: #999999;
-}
-abbr[title] {
-  cursor: help;
-  border-bottom: 1px dotted #999999;
-}
-abbr.initialism {
-  font-size: 90%;
-  text-transform: uppercase;
-}
-blockquote {
-  padding: 0 0 0 15px;
-  margin: 0 0 18px;
-  border-left: 5px solid #eeeeee;
-}
-blockquote p {
-  margin-bottom: 0;
-  font-size: 16px;
-  font-weight: 300;
-  line-height: 22.5px;
-}
-blockquote small {
-  display: block;
-  line-height: 18px;
-  color: #999999;
-}
-blockquote small:before {
-  content: '\2014 \00A0';
-}
-blockquote.pull-right {
-  float: right;
-  padding-right: 15px;
-  padding-left: 0;
-  border-right: 5px solid #eeeeee;
-  border-left: 0;
-}
-blockquote.pull-right p,
-blockquote.pull-right small {
-  text-align: right;
-}
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
-  content: "";
-}
-address {
-  display: block;
-  margin-bottom: 18px;
-  font-style: normal;
-  line-height: 18px;
-}
-small {
-  font-size: 100%;
-}
-cite {
-  font-style: normal;
-}
-.label,
-.badge {
-  font-size: 10.998px;
-  font-weight: bold;
-  line-height: 14px;
-  color: #ffffff;
-  vertical-align: baseline;
-  white-space: nowrap;
-  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-  background-color: #999999;
-}
-.label {
-  padding: 1px 4px 2px;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-}
-.badge {
-  padding: 1px 9px 2px;
-  -webkit-border-radius: 9px;
-  -moz-border-radius: 9px;
-  border-radius: 9px;
-}
-a.label:hover,
-a.badge:hover {
-  color: #ffffff;
-  text-decoration: none;
-  cursor: pointer;
-}
-.label-important,
-.badge-important {
-  background-color: #b94a48;
-}
-.label-important[href],
-.badge-important[href] {
-  background-color: #953b39;
-}
-.label-warning,
-.badge-warning {
-  background-color: #f89406;
-}
-.label-warning[href],
-.badge-warning[href] {
-  background-color: #c67605;
-}
-.label-success,
-.badge-success {
-  background-color: #468847;
-}
-.label-success[href],
-.badge-success[href] {
-  background-color: #356635;
-}
-.label-info,
-.badge-info {
-  background-color: #3a87ad;
-}
-.label-info[href],
-.badge-info[href] {
-  background-color: #2d6987;
-}
-.label-inverse,
-.badge-inverse {
-  background-color: #333333;
-}
-.label-inverse[href],
-.badge-inverse[href] {
-  background-color: #1a1a1a;
-}
-table {
-  max-width: 100%;
-  background-color: transparent;
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-.table {
-  width: 100%;
-  margin-bottom: 18px;
-}
-.table th,
-.table td {
-  padding: 8px;
-  line-height: 18px;
-  text-align: left;
-  vertical-align: top;
-  border-top: 1px solid #dddddd;
-}
-.table th {
-  font-weight: bold;
-}
-.table thead th {
-  vertical-align: bottom;
-}
-.table caption + thead tr:first-child th,
-.table caption + thead tr:first-child td,
-.table colgroup + thead tr:first-child th,
-.table colgroup + thead tr:first-child td,
-.table thead:first-child tr:first-child th,
-.table thead:first-child tr:first-child td {
-  border-top: 0;
-}
-.table tbody + tbody {
-  border-top: 2px solid #dddddd;
-}
-.table-condensed th,
-.table-condensed td {
-  padding: 4px 5px;
-}
-.table-bordered {
-  border: 1px solid #dddddd;
-  border-collapse: separate;
-  *border-collapse: collapsed;
-  border-left: 0;
-  -webkit-border-radius: 4px;
-  -moz-border-radius: 4px;
-  border-radius: 4px;
-}
-.table-bordered th,
-.table-bordered td {
-  border-left: 1px solid #dddddd;
-}
-.table-bordered caption + thead tr:first-child th,
-.table-bordered caption + tbody tr:first-child th,
-.table-bordered caption + tbody tr:first-child td,
-.table-bordered colgroup + thead tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child td,
-.table-bordered thead:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child td {
-  border-top: 0;
-}
-.table-bordered thead:first-child tr:first-child th:first-child,
-.table-bordered tbody:first-child tr:first-child td:first-child {
-  -webkit-border-top-left-radius: 4px;
-  border-top-left-radius: 4px;
-  -moz-border-radius-topleft: 4px;
-}
-.table-bordered thead:first-child tr:first-child th:last-child,
-.table-bordered tbody:first-child tr:first-child td:last-child {
-  -webkit-border-top-right-radius: 4px;
-  border-top-right-radius: 4px;
-  -moz-border-radius-topright: 4px;
-}
-.table-bordered thead:last-child tr:last-child th:first-child,
-.table-bordered tbody:last-child tr:last-child td:first-child {
-  -webkit-border-radius: 0 0 0 4px;
-  -moz-border-radius: 0 0 0 4px;
-  border-radius: 0 0 0 4px;
-  -webkit-border-bottom-left-radius: 4px;
-  border-bottom-left-radius: 4px;
-  -moz-border-radius-bottomleft: 4px;
-}
-.table-bordered thead:last-child tr:last-child th:last-child,
-.table-bordered tbody:last-child tr:last-child td:last-child {
-  -webkit-border-bottom-right-radius: 4px;
-  border-bottom-right-radius: 4px;
-  -moz-border-radius-bottomright: 4px;
-}
-.table-striped tbody tr:nth-child(odd) td,
-.table-striped tbody tr:nth-child(odd) th {
-  background-color: #f9f9f9;
-}
-.table tbody tr:hover td,
-.table tbody tr:hover th {
-  background-color: #f5f5f5;
-}
-table .span1 {
-  float: none;
-  width: 44px;
-  margin-left: 0;
-}
-table .span2 {
-  float: none;
-  width: 124px;
-  margin-left: 0;
-}
-table .span3 {
-  float: none;
-  width: 204px;
-  margin-left: 0;
-}
-table .span4 {
-  float: none;
-  width: 284px;
-  margin-left: 0;
-}
-table .span5 {
-  float: none;
-  width: 364px;
-  margin-left: 0;
-}
-table .span6 {
-  float: none;
-  width: 444px;
-  margin-left: 0;
-}
-table .span7 {
-  float: none;
-  width: 524px;
-  margin-left: 0;
-}
-table .span8 {
-  float: none;
-  width: 604px;
-  margin-left: 0;
-}
-table .span9 {
-  float: none;
-  width: 684px;
-  margin-left: 0;
-}
-table .span10 {
-  float: none;
-  width: 764px;
-  margin-left: 0;
-}
-table .span11 {
-  float: none;
-  width: 844px;
-  margin-left: 0;
-}
-table .span12 {
-  float: none;
-  width: 924px;
-  margin-left: 0;
-}
-table .span13 {
-  float: none;
-  width: 1004px;
-  margin-left: 0;
-}
-table .span14 {
-  float: none;
-  width: 1084px;
-  margin-left: 0;
-}
-table .span15 {
-  float: none;
-  width: 1164px;
-  margin-left: 0;
-}
-table .span16 {
-  float: none;
-  width: 1244px;
-  margin-left: 0;
-}
-table .span17 {
-  float: none;
-  width: 1324px;
-  margin-left: 0;
-}
-table .span18 {
-  float: none;
-  width: 1404px;
-  margin-left: 0;
-}
-table .span19 {
-  float: none;
-  width: 1484px;
-  margin-left: 0;
-}
-table .span20 {
-  float: none;
-  width: 1564px;
-  margin-left: 0;
-}
-table .span21 {
-  float: none;
-  width: 1644px;
-  margin-left: 0;
-}
-table .span22 {
-  float: none;
-  width: 1724px;
-  margin-left: 0;
-}
-table .span23 {
-  float: none;
-  width: 1804px;
-  margin-left: 0;
-}
-table .span24 {
-  float: none;
-  width: 1884px;
-  margin-left: 0;
-}
-form {
-  margin: 0 0 18px;
-}
-fieldset {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-legend {
-  display: block;
-  width: 100%;
-  padding: 0;
-  margin-bottom: 27px;
-  font-size: 19.5px;
-  line-height: 36px;
-  color: #333333;
-  border: 0;
-  border-bottom: 1px solid #e5e5e5;
-}
-legend small {
-  font-size: 13.5px;
-  color: #999999;
-}
-label,
-input,
-button,
-select,
-textarea {
-  font-size: 13px;
-  font-weight: normal;
-  line-height: 18px;
-}
-input,
-button,
-select,
-textarea {
-  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-}
-label {
-  display: block;
-  margin-bottom: 5px;
-}
-select,
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
-  display: inline-block;
-  height: 18px;
-  padding: 4px;
-  margin-bottom: 9px;
-  font-size: 13px;
-  line-height: 18px;
-  color: #555555;
-}
-input,
-textarea {
-  width: 210px;
-}
-textarea {
-  height: auto;
-}
-textarea,
-input[type="text"],
-input[type="password"],
-input[type="datetime"],
-input[type="datetime-local"],
-input[type="date"],
-input[type="month"],
-input[type="time"],
-input[type="week"],
-input[type="number"],
-input[type="email"],
-input[type="url"],
-input[type="search"],
-input[type="tel"],
-input[type="color"],
-.uneditable-input {
-  background-color: #ffffff;
-  border: 1px solid #cccccc;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-  -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-  -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
-  -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
-  -ms-transition: border linear 0.2s, box-shadow linear 0.2s;
-  -o-transition: border linear 0.2s, box-shadow linear 0.2s;
-  transition: border linear 0.2s, box-shadow linear 0.2s;
-}
-textarea:focus,
-input[type="text"]:focus,
-input[type="password"]:focus,
-input[type="datetime"]:focus,
-input[type="datetime-local"]:focus,
-input[type="date"]:focus,
-input[type="month"]:focus,
-input[type="time"]:focus,
-input[type="week"]:focus,
-input[type="number"]:focus,
-input[type="email"]:focus,
-input[type="url"]:focus,
-input[type="search"]:focus,
-input[type="tel"]:focus,
-input[type="color"]:focus,
-.uneditable-input:focus {
-  border-color: rgba(82, 168, 236, 0.8);
-  outline: 0;
-  outline: thin dotted \9;
-  /* IE6-9 */
-
-  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
-  -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
-  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
-}
-input[type="radio"],
-input[type="checkbox"] {
-  margin: 3px 0;
-  *margin-top: 0;
-  /* IE7 */
-
-  line-height: normal;
-  cursor: pointer;
-}
-input[type="submit"],
-input[type="reset"],
-input[type="button"],
-input[type="radio"],
-input[type="checkbox"] {
-  width: auto;
-}
-.uneditable-textarea {
-  width: auto;
-  height: auto;
-}
-select,
-input[type="file"] {
-  height: 28px;
-  /* In IE7, the height of the select element cannot be changed by height, only font-size */
-
-  *margin-top: 4px;
-  /* For IE7, add top margin to align select with labels */
-
-  line-height: 28px;
-}
-select {
-  width: 220px;
-  border: 1px solid #bbb;
-}
-select[multiple],
-select[size] {
-  height: auto;
-}
-select:focus,
-input[type="file"]:focus,
-input[type="radio"]:focus,
-input[type="checkbox"]:focus {
-  outline: thin dotted #333;
-  outline: 5px auto -webkit-focus-ring-color;
-  outline-offset: -2px;
-}
-.radio,
-.checkbox {
-  min-height: 18px;
-  padding-left: 18px;
-}
-.radio input[type="radio"],
-.checkbox input[type="checkbox"] {
-  float: left;
-  margin-left: -18px;
-}
-.controls > .radio:first-child,
-.controls > .checkbox:first-child {
-  padding-top: 5px;
-}
-.radio.inline,
-.checkbox.inline {
-  display: inline-block;
-  padding-top: 5px;
-  margin-bottom: 0;
-  vertical-align: middle;
-}
-.radio.inline + .radio.inline,
-.checkbox.inline + .checkbox.inline {
-  margin-left: 10px;
-}
-.input-mini {
-  width: 60px;
-}
-.input-small {
-  width: 90px;
-}
-.input-medium {
-  width: 150px;
-}
-.input-large {
-  width: 210px;
-}
-.input-xlarge {
-  width: 270px;
-}
-.input-xxlarge {
-  width: 530px;
-}
-input[class*="span"],
-select[class*="span"],
-textarea[class*="span"],
-.uneditable-input[class*="span"],
-.row-fluid input[class*="span"],
-.row-fluid select[class*="span"],
-.row-fluid textarea[class*="span"],
-.row-fluid .uneditable-input[class*="span"] {
-  float: none;
-  margin-left: 0;
-}
-.input-append input[class*="span"],
-.input-append .uneditable-input[class*="span"],
-.input-prepend input[class*="span"],
-.input-prepend .uneditable-input[class*="span"],
-.row-fluid .input-prepend [class*="span"],
-.row-fluid .input-append [class*="span"] {
-  display: inline-block;
-}
-input,
-textarea,
-.uneditable-input {
-  margin-left: 0;
-}
-input.span12, textarea.span12, .uneditable-input.span12 {
-  width: 930px;
-}
-input.span11, textarea.span11, .uneditable-input.span11 {
-  width: 850px;
-}
-input.span10, textarea.span10, .uneditable-input.span10 {
-  width: 770px;
-}
-input.span9, textarea.span9, .uneditable-input.span9 {
-  width: 690px;
-}
-input.span8, textarea.span8, .uneditable-input.span8 {
-  width: 610px;
-}
-input.span7, textarea.span7, .uneditable-input.span7 {
-  width: 530px;
-}
-input.span6, textarea.span6, .uneditable-input.span6 {
-  width: 450px;
-}
-input.span5, textarea.span5, .uneditable-input.span5 {
-  width: 370px;
-}
-input.span4, textarea.span4, .uneditable-input.span4 {
-  width: 290px;
-}
-input.span3, textarea.span3, .uneditable-input.span3 {
-  width: 210px;
-}
-input.span2, textarea.span2, .uneditable-input.span2 {
-  width: 130px;
-}
-input.span1, textarea.span1, .uneditable-input.span1 {
-  width: 50px;
-}
-input[disabled],
-select[disabled],
-textarea[disabled],
-input[readonly],
-select[readonly],
-textarea[readonly] {
-  cursor: not-allowed;
-  background-color: #eeeeee;
-  border-color: #ddd;
-}
-input[type="radio"][disabled],
-input[type="checkbox"][disabled],
-input[type="radio"][readonly],
-input[type="checkbox"][readonly] {
-  background-color: transparent;
-}
-.control-group.warning > label,
-.control-group.warning .help-block,
-.control-group.warning .help-inline {
-  color: #c09853;
-}
-.control-group.warning .checkbox,
-.control-group.warning .radio,
-.control-group.warning input,
-.control-group.warning select,
-.control-group.warning textarea {
-  color: #c09853;
-  border-color: #c09853;
-}
-.control-group.warning .checkbox:focus,
-.control-group.warning .radio:focus,
-.control-group.warning input:focus,
-.control-group.warning select:focus,
-.control-group.warning textarea:focus {
-  border-color: #a47e3c;
-  -webkit-box-shadow: 0 0 6px #dbc59e;
-  -moz-box-shadow: 0 0 6px #dbc59e;
-  box-shadow: 0 0 6px #dbc59e;
-}
-.control-group.warning .input-prepend .add-on,
-.control-group.warning .input-append .add-on {
-  color: #c09853;
-  background-color: #fcf8e3;
-  border-color: #c09853;
-}
-.control-group.error > label,
-.control-group.error .help-block,
-.control-group.error .help-inline {
-  color: #b94a48;
-}
-.control-group.error .checkbox,
-.control-group.error .radio,
-.control-group.error input,
-.control-group.error select,
-.control-group.error textarea {
-  color: #b94a48;
-  border-color: #b94a48;
-}
-.control-group.error .checkbox:focus,
-.control-group.error .radio:focus,
-.control-group.error input:focus,
-.control-group.error select:focus,
-.control-group.error textarea:focus {
-  border-color: #953b39;
-  -webkit-box-shadow: 0 0 6px #d59392;
-  -moz-box-shadow: 0 0 6px #d59392;
-  box-shadow: 0 0 6px #d59392;
-}
-.control-group.error .input-prepend .add-on,
-.control-group.error .input-append .add-on {
-  color: #b94a48;
-  background-color: #f2dede;
-  border-color: #b94a48;
-}
-.control-group.success > label,
-.control-group.success .help-block,
-.control-group.success .help-inline {
-  color: #468847;
-}
-.control-group.success .checkbox,
-.control-group.success .radio,
-.control-group.success input,
-.control-group.success select,
-.control-group.success textarea {
-  color: #468847;
-  border-color: #468847;
-}
-.control-group.success .checkbox:focus,
-.control-group.success .radio:focus,
-.control-group.success input:focus,
-.control-group.success select:focus,
-.control-group.success textarea:focus {
-  border-color: #356635;
-  -webkit-box-shadow: 0 0 6px #7aba7b;
-  -moz-box-shadow: 0 0 6px #7aba7b;
-  box-shadow: 0 0 6px #7aba7b;
-}
-.control-group.success .input-prepend .add-on,
-.control-group.success .input-append .add-on {
-  color: #468847;
-  background-color: #dff0d8;
-  border-color: #468847;
-}
-input:focus:required:invalid,
-textarea:focus:required:invalid,
-select:focus:required:invalid {
-  color: #b94a48;
-  border-color: #ee5f5b;
-}
-input:focus:required:invalid:focus,
-textarea:focus:required:invalid:focus,
-select:focus:required:invalid:focus {
-  border-color: #e9322d;
-  -webkit-box-shadow: 0 0 6px #f8b9b7;
-  -moz-box-shadow: 0 0 6px #f8b9b7;
-  box-shadow: 0 0 6px #f8b9b7;
-}
-.form-actions {
-  padding: 17px 20px 18px;
-  margin-top: 18px;
-  margin-bottom: 18px;
-  background-color: #f5f5f5;
-  border-top: 1px solid #e5e5e5;
-  *zoom: 1;
-}
-.form-actions:before,
-.form-actions:after {
-  display: table;
-  content: "";
-}
-.form-actions:after {
-  clear: both;
-}
-.uneditable-input {
-  overflow: hidden;
-  white-space: nowrap;
-  cursor: not-allowed;
-  background-color: #ffffff;
-  border-color: #eee;
-  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
-  -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
-  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
-}
-:-moz-placeholder {
-  color: #999999;
-}
-:-ms-input-placeholder {
-  color: #999999;
-}
-::-webkit-input-placeholder {
-  color: #999999;
-}
-.help-block,
-.help-inline {
-  color: #555555;
-}
-.help-block {
-  display: block;
-  margin-bottom: 9px;
-}
-.help-inline {
-  display: inline-block;
-  *display: inline;
-  /* IE7 inline-block hack */
-
-  *zoom: 1;
-  vertical-align: middle;
-  padding-left: 5px;
-}
-.input-prepend,
-.input-append {
-  margin-bottom: 5px;
-}
-.input-prepend input,
-.input-append input,
-.input-prepend select,
-.input-append select,
-.input-prepend .uneditable-input,
-.input-append .uneditable-input {
-  position: relative;
-  margin-bottom: 0;
-  *margin-left: 0;
-  vertical-align: middle;
-  -webkit-border-radius: 0 3px 3px 0;
-  -moz-border-radius: 0 3px 3px 0;
-  border-radius: 0 3px 3px 0;
-}
-.input-prepend input:focus,
-.input-append input:focus,
-.input-prepend select:focus,
-.input-append select:focus,
-.input-prepend .uneditable-input:focus,
-.input-append .uneditable-input:focus {
-  z-index: 2;
-}
-.input-prepend .uneditable-input,
-.input-append .uneditable-input {
-  border-left-color: #ccc;
-}
-.input-prepend .add-on,
-.input-append .add-on {
-  display: inline-block;
-  width: auto;
-  height: 18px;
-  min-width: 16px;
-  padding: 4px 5px;
-  font-weight: normal;
-  line-height: 18px;
-  text-align: center;
-  text-shadow: 0 1px 0 #ffffff;
-  vertical-align: middle;
-  background-color: #eeeeee;
-  border: 1px solid #ccc;
-}
-.input-prepend .add-on,
-.input-append .add-on,
-.input-prepend .btn,
-.input-append .btn {
-  margin-left: -1px;
-  -webkit-border-radius: 0;
-  -moz-border-radius: 0;
-  border-radius: 0;
-}
-.input-prepend .active,
-.input-append .active {
-  background-color: #a9dba9;
-  border-color: #46a546;
-}
-.input-prepend .add-on,
-.input-prepend .btn {
-  margin-right: -1px;
-}
-.input-prepend .add-on:first-child,
-.input-prepend .btn:first-child {
-  -webkit-border-radius: 3px 0 0 3px;
-  -moz-border-radius: 3px 0 0 3px;
-  border-radius: 3px 0 0 3px;
-}
-.input-append input,
-.input-append select,
-.input-append .uneditable-input {
-  -webkit-border-radius: 3px 0 0 3px;
-  -moz-border-radius: 3px 0 0 3px;
-  border-radius: 3px 0 0 3px;
-}
-.input-append .uneditable-input {
-  border-right-color: #ccc;
-  border-left-color: #eee;
-}
-.input-append .add-on:last-child,
-.input-append .btn:last-child {
-  -webkit-border-radius: 0 3px 3px 0;
-  -moz-border-radius: 0 3px 3px 0;
-  border-radius: 0 3px 3px 0;
-}
-.input-prepend.input-append input,
-.input-prepend.input-append select,
-.input-prepend.input-append .uneditable-input {
-  -webkit-border-radius: 0;
-  -moz-border-radius: 0;
-  border-radius: 0;
-}
-.input-prepend.input-append .add-on:first-child,
-.input-prepend.input-append .btn:first-child {
-  margin-right: -1px;
-  -webkit-border-radius: 3px 0 0 3px;
-  -moz-border-radius: 3px 0 0 3px;
-  border-radius: 3px 0 0 3px;
-}
-.input-prepend.input-append .add-on:last-child,
-.input-prepend.input-append .btn:last-child {
-  margin-left: -1px;
-  -webkit-border-radius: 0 3px 3px 0;
-  -moz-border-radius: 0 3px 3px 0;
-  border-radius: 0 3px 3px 0;
-}
-.search-query {
-  padding-right: 14px;
-  padding-right: 4px \9;
-  padding-left: 14px;
-  padding-left: 4px \9;
-  /* IE7-8 doesn't have border-radius, so don't indent the padding */
-
-  margin-bottom: 0;
-  -webkit-border-radius: 14px;
-  -moz-border-radius: 14px;
-  border-radius: 14px;
-}
-.form-search input,
-.form-inline input,
-.form-horizontal input,
-.form-search textarea,
-.form-inline textarea,
-.form-horizontal textarea,
-.form-search select,
-.form-inline select,
-.form-horizontal select,
-.form-search .help-inline,
-.form-inline .help-inline,
-.form-horizontal .help-inline,
-.form-search .uneditable-input,
-.form-inline .uneditable-input,
-.form-horizontal .uneditable-input,
-.form-search .input-prepend,
-.form-inline .input-prepend,
-.form-horizontal .input-prepend,
-.form-search .input-append,
-.form-inline .input-append,
-.form-horizontal .input-append {
-  display: inline-block;
-  *display: inline;
-  /* IE7 inline-block hack */
-
-  *zoom: 1;
-  margin-bottom: 0;
-}
-.form-search .hide,
-.form-inline .hide,
-.form-horizontal .hide {
-  display: none;
-}
-.form-search label,
-.form-inline label {
-  display: inline-block;
-}
-.form-search .input-append,
-.form-inline .input-append,
-.form-search .input-prepend,
-.form-inline .input-prepend {
-  margin-bottom: 0;
-}
-.form-search .radio,
-.form-search .checkbox,
-.form-inline .radio,
-.form-inline .checkbox {
-  padding-left: 0;
-  margin-bottom: 0;
-  vertical-align: middle;
-}
-.form-search .radio input[type="radio"],
-.form-search .checkbox input[type="checkbox"],
-.form-inline .radio input[type="radio"],
-.form-inline .checkbox input[type="checkbox"] {
-  float: left;
-  margin-right: 3px;
-  margin-left: 0;
-}
-.control-group {
-  margin-bottom: 9px;
-}
-legend + .control-group {
-  margin-top: 18px;
-  -webkit-margin-top-collapse: separate;
-}
-.form-horizontal .control-group {
-  margin-bottom: 18px;
-  *zoom: 1;
-}
-.form-horizontal .control-group:before,
-.form-horizontal .control-group:after {
-  display: table;
-  content: "";
-}
-.form-horizontal .control-group:after {
-  clear: both;
-}
-.form-horizontal .control-label {
-  float: left;
-  width: 140px;
-  padding-top: 5px;
-  text-align: right;
-}
-.form-horizontal .controls {
-  *display: inline-block;
-  *padding-left: 20px;
-  margin-left: 160px;
-  *margin-left: 0;
-}
-.form-horizontal .controls:first-child {
-  *padding-left: 160px;
-}
-.form-horizontal .help-block {
-  margin-top: 9px;
-  margin-bottom: 0;
-}
-.form-horizontal .form-actions {
-  padding-left: 160px;
-}
-.nav {
-  margin-left: 0;
-  margin-bottom: 18px;
-  list-style: none;
-}
-.nav > li > a {
-  display: block;
-}
-.nav > li > a:hover {
-  text-decoration: none;
-  background-color: #eeeeee;
-}
-.nav > .pull-right {
-  float: right;
-}
-.nav .nav-header {
-  display: block;
-  padding: 3px 15px;
-  font-size: 11px;
-  font-weight: bold;
-  line-height: 18px;
-  color: #999999;
-  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-  text-transform: uppercase;
-}
-.nav li + .nav-header {
-  margin-top: 9px;
-}
-.nav-list {
-  padding-left: 15px;
-  padding-right: 15px;
-  margin-bottom: 0;
-}
-.nav-list > li > a,
-.nav-list .nav-header {
-  margin-left: -15px;
-  margin-right: -15px;
-  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-}
-.nav-list > li > a {
-  padding: 3px 15px;
-}
-.nav-list > .active > a,
-.nav-list > .active > a:hover {
-  color: #ffffff;
-  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-  background-color: #0088cc;
-}
-.nav-list [class^="icon-"] {
-  margin-right: 2px;
-}
-.nav-list .divider {
-  *width: 100%;
-  height: 1px;
-  margin: 8px 1px;
-  *margin: -5px 0 5px;
-  overflow: hidden;
-  background-color: #e5e5e5;
-  border-bottom: 1px solid #ffffff;
-}
-.nav-tabs,
-.nav-pills {
-  *zoom: 1;
-}
-.nav-tabs:before,
-.nav-pills:before,
-.nav-tabs:after,
-.nav-pills:after {
-  display: table;
-  content: "";
-}
-.nav-tabs:after,
-.nav-pills:after {
-  clear: both;
-}
-.nav-tabs > li,
-.nav-pills > li {
-  display: inline-block;
-}
-.nav-tabs > li > a,
-.nav-pills > li > a {
-  padding-right: 12px;
-  padding-left: 12px;
-  margin-right: 2px;
-  line-height: 14px;
-}
-.nav-tabs {
-  border-bottom: 1px solid #ddd;
-}
-.nav-tabs > li {
-  margin-bottom: -1px;
-}
-.nav-tabs > li > a {
-  padding-top: 8px;
-  padding-bottom: 8px;
-  line-height: 18px;
-  border: 1px solid transparent;
-  -webkit-border-radius: 4px 4px 0 0;
-  -moz-border-radius: 4px 4px 0 0;
-  border-radius: 4px 4px 0 0;
-}
-.nav-tabs > li > a:hover {
-  border-color: #eeeeee #eeeeee #dddddd;
-}
-.nav-tabs > .active > a,
-.nav-tabs > .active > a:hover {
-  color: #555555;
-  background-color: #ffffff;
-  border: 1px solid #ddd;
-  border-bottom-color: transparent;
-  cursor: default;
-}
-.nav-pills > li > a {
-  padding-top: 8px;
-  padding-bottom: 8px;
-  margin-top: 2px;
-  margin-bottom: 2px;
-  -webkit-border-radius: 5px;
-  -moz-border-radius: 5px;
-  border-radius: 5px;
-}
-.nav-pills > .active > a,
-.nav-pills > .active > a:hover {
-  color: #ffffff;
-  background-color: #0088cc;
-}
-.nav-stacked > li {
-  float: none;
-}
-.nav-stacked > li > a {
-  margin-right: 0;
-}
-.nav-tabs.nav-stacked {
-  border-bottom: 0;
-}
-.nav-tabs.nav-stacked > li > a {
-  border: 1px solid #ddd;
-  -webkit-border-radius: 0;
-  -moz-border-radius: 0;
-  border-radius: 0;
-}
-.nav-tabs.nav-stacked > li:first-child > a {
-  -webkit-border-radius: 4px 4px 0 0;
-  -moz-border-radius: 4px 4px 0 0;
-  border-radius: 4px 4px 0 0;
-}
-.nav-tabs.nav-stacked > li:last-child > a {
-  -webkit-border-radius: 0 0 4px 4px;
-  -moz-border-radius: 0 0 4px 4px;
-  border-radius: 0 0 4px 4px;
-}
-.nav-tabs.nav-stacked > li > a:hover {
-  border-color: #ddd;
-  z-index: 2;
-}
-.nav-pills.nav-stacked > li > a {
-  margin-bottom: 3px;
-}
-.nav-pills.nav-stacked > li:last-child > a {
-  margin-bottom: 1px;
-}
-.nav-tabs .dropdown-menu {
-  -webkit-border-radius: 0 0 5px 5px;
-  -moz-border-radius: 0 0 5px 5px;
-  border-radius: 0 0 5px 5px;
-}
-.nav-pills .dropdown-menu {
-  -webkit-border-radius: 4px;
-  -moz-border-radius: 4px;
-  border-radius: 4px;
-}
-.nav-tabs .dropdown-toggle .caret,
-.nav-pills .dropdown-toggle .caret {
-  border-top-color: #0088cc;
-  border-bottom-color: #0088cc;
-  margin-top: 6px;
-}
-.nav-tabs .dropdown-toggle:hover .caret,
-.nav-pills .dropdown-toggle:hover .caret {
-  border-top-color: #005580;
-  border-bottom-color: #005580;
-}
-.nav-tabs .active .dropdown-toggle .caret,
-.nav-pills .active .dropdown-toggle .caret {
-  border-top-color: #333333;
-  border-bottom-color: #333333;
-}
-.nav > .dropdown.active > a:hover {
-  color: #000000;
-  cursor: pointer;
-}
-.nav-tabs .open .dropdown-toggle,
-.nav-pills .open .dropdown-toggle,
-.nav > li.dropdown.open.active > a:hover {
-  color: #ffffff;
-  background-color: #999999;
-  border-color: #999999;
-}
-.nav li.dropdown.open .caret,
-.nav li.dropdown.open.active .caret,
-.nav li.dropdown.open a:hover .caret {
-  border-top-color: #ffffff;
-  border-bottom-color: #ffffff;
-  opacity: 1;
-  filter: alpha(opacity=100);
-}
-.tabs-stacked .open > a:hover {
-  border-color: #999999;
-}
-.tabbable {
-  *zoom: 1;
-}
-.tabbable:before,
-.tabbable:after {
-  display: table;
-  content: "";
-}
-.tabbable:after {
-  clear: both;
-}
-.tab-content {
-  overflow: auto;
-}
-.tabs-below > .nav-tabs,
-.tabs-right > .nav-tabs,
-.tabs-left > .nav-tabs {
-  border-bottom: 0;
-}
-.tab-content > .tab-pane,
-.pill-content > .pill-pane {
-  display: none;
-}
-.tab-content > .active,
-.pill-content > .active {
-  display: block;
-}
-.tabs-below > .nav-tabs {
-  border-top: 1px solid #ddd;
-}
-.tabs-below > .nav-tabs > li {
-  margin-top: -1px;
-  margin-bottom: 0;
-}
-.tabs-below > .nav-tabs > li > a {
-  -webkit-border-radius: 0 0 4px 4px;
-  -moz-border-radius: 0 0 4px 4px;
-  border-radius: 0 0 4px 4px;
-}
-.tabs-below > .nav-tabs > li > a:hover {
-  border-bottom-color: transparent;
-  border-top-color: #ddd;
-}
-.tabs-below > .nav-tabs > .active > a,
-.tabs-below > .nav-tabs > .active > a:hover {
-  border-color: transparent #ddd #ddd #ddd;
-}
-.tabs-left > .nav-tabs > li,
-.tabs-right > .nav-tabs > li {
-  float: none;
-}
-.tabs-left > .nav-tabs > li > a,
-.tabs-right > .nav-tabs > li > a {
-  min-width: 74px;
-  margin-right: 0;
-  margin-bottom: 3px;
-}
-.tabs-left > .nav-tabs {
-  float: left;
-  margin-right: 19px;
-  border-right: 1px solid #ddd;
-}
-.tabs-left > .nav-tabs > li > a {
-  margin-right: -1px;
-  -webkit-border-radius: 4px 0 0 4px;
-  -moz-border-radius: 4px 0 0 4px;
-  border-radius: 4px 0 0 4px;
-}
-.tabs-left > .nav-tabs > li > a:hover {
-  border-color: #eeeeee #dddddd #eeeeee #eeeeee;
-}
-.tabs-left > .nav-tabs .active > a,
-.tabs-left > .nav-tabs .active > a:hover {
-  border-color: #ddd transparent #ddd #ddd;
-  *border-right-color: #ffffff;
-}
-.tabs-right > .nav-tabs {
-  float: right;
-  margin-left: 19px;
-  border-left: 1px solid #ddd;
-}
-.tabs-right > .nav-tabs > li > a {
-  margin-left: -1px;
-  -webkit-border-radius: 0 4px 4px 0;
-  -moz-border-radius: 0 4px 4px 0;
-  border-radius: 0 4px 4px 0;
-}
-.tabs-right > .nav-tabs > li > a:hover {
-  border-color: #eeeeee #eeeeee #eeeeee #dddddd;
-}
-.tabs-right > .nav-tabs .active > a,
-.tabs-right > .nav-tabs .active > a:hover {
-  border-color: #ddd #ddd #ddd transparent;
-  *border-left-color: #ffffff;
-}
-.pagination {
-  height: 36px;
-  margin: 18px 0;
-}
-.pagination ul {
-  display: inline-block;
-  *display: inline;
-  /* IE7 inline-block hack */
-
-  *zoom: 1;
-  margin-left: 0;
-  margin-bottom: 0;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-  -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-.pagination li {
-  display: inline;
-}
-.pagination a {
-  float: left;
-  padding: 0 14px;
-  line-height: 34px;
-  text-decoration: none;
-  border: 1px solid #ddd;
-  border-left-width: 0;
-}
-.pagination a:hover,
-.pagination .active a {
-  background-color: #f5f5f5;
-}
-.pagination .active a {
-  color: #999999;
-  cursor: default;
-}
-.pagination .disabled span,
-.pagination .disabled a,
-.pagination .disabled a:hover {
-  color: #999999;
-  background-color: transparent;
-  cursor: default;
-}
-.pagination li:first-child a {
-  border-left-width: 1px;
-  -webkit-border-radius: 3px 0 0 3px;
-  -moz-border-radius: 3px 0 0 3px;
-  border-radius: 3px 0 0 3px;
-}
-.pagination li:last-child a {
-  -webkit-border-radius: 0 3px 3px 0;
-  -moz-border-radius: 0 3px 3px 0;
-  border-radius: 0 3px 3px 0;
-}
-.pagination-centered {
-  text-align: center;
-}
-.pagination-right {
-  text-align: right;
-}
-.well {
-  min-height: 20px;
-  padding: 19px;
-  margin-bottom: 20px;
-  background-color: #f5f5f5;
-  border: 1px solid #eee;
-  border: 1px solid rgba(0, 0, 0, 0.05);
-  -webkit-border-radius: 4px;
-  -moz-border-radius: 4px;
-  border-radius: 4px;
-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
-  -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
-  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
-}
-.well blockquote {
-  border-color: #ddd;
-  border-color: rgba(0, 0, 0, 0.15);
-}
-.well-large {
-  padding: 24px;
-  -webkit-border-radius: 6px;
-  -moz-border-radius: 6px;
-  border-radius: 6px;
-}
-.well-small {
-  padding: 9px;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-}
diff --git a/scp/css/dashboard.css b/scp/css/dashboard.css
index 43ec338890ac6bcce9d7647f85841657d362fe88..249c43597aa05e73bcd7a3888f651313cfc05d08 100644
--- a/scp/css/dashboard.css
+++ b/scp/css/dashboard.css
@@ -27,20 +27,3 @@ span.label.disabled {
 span.label {
     cursor: pointer;
 }
-#table-here tr :not(:first-child) {
-    text-align: right;
-    padding-right: 2.3em;
-    width: 12%;
-}
-#table-here tr :not(:first-child) div {
-    position: relative;
-    margin-right: -1em;
-}
-#table-here tr :not(:first-child) div div {
-    position: absolute;
-    -moz-border-radius: 1em;
-    -webkit-border-radius: 1em;
-    border-radius: 1em;
-}
-
-
diff --git a/scp/css/scp.css b/scp/css/scp.css
index aa424efb2440ef8cf4bfdce48eab879a0ab376ca..69fa5d08d7960645fc69045d36de71640c357df4 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -2248,12 +2248,12 @@ tr.disabled th {
     float: right;
     margin-bottom: 4px;
     font-size: 11px;
-    padding: 0px 4px;
+    padding: 0px 7px;
     -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
     border-radius: 3px;
     font-weight: bold;
-    line-height: 24px;
+    line-height: 18px;
     color: #ffffff;
     vertical-align: baseline;
     white-space: nowrap;
diff --git a/scp/dashboard.php b/scp/dashboard.php
index 2d6b20dfedbdf71fed3aa18bd2d910e3247003cb..4a0cd3eef5fead042ee955acb288e6d21a1c5553 100644
--- a/scp/dashboard.php
+++ b/scp/dashboard.php
@@ -14,9 +14,13 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require('staff.inc.php');
+
+require_once INCLUDE_DIR . 'class.report.php';
+
 $nav->setTabActive('dashboard');
 $ost->addExtraHeader('<meta name="tip-namespace" content="dashboard.dashboard" />',
     "$('#content').data('tipNamespace', 'dashboard.dashboard');");
+
 require(STAFFINC_DIR.'header.inc.php');
 require_once(STAFFINC_DIR.'dashboard.inc.php');
 include(STAFFINC_DIR.'footer.inc.php');
diff --git a/scp/js/dashboard.inc.js b/scp/js/dashboard.inc.js
index 2110f0d69618630d41b3b799e3bafdacbc77e4b1..dd850da7b6c4b57edfa58030c330e2922aa92e08 100644
--- a/scp/js/dashboard.inc.js
+++ b/scp/js/dashboard.inc.js
@@ -1,273 +1,99 @@
 (function ($) {
-    var current_tab = null;
-    function refresh(e) {
+    $.drawPlots = function(json) {
         $('#line-chart-here').empty();
         $('#line-chart-legend').empty();
         var r = new Raphael('line-chart-here'),
             width = $('#line-chart-here').width(),
             height = $('#line-chart-here').height();
-        $.ajax({
-            method:     'GET',
-            url:        'ajax.php/report/overview/graph',
-            data:       $(this).serialize(),
-            dataType:   'json',
-            success:    function(json) {
-                var times = [],
-                    smtimes = Array.prototype.concat.apply([], json.times),
-                    plots = [],
-                    max = 0;
+        var times = [],
+            smtimes = Array.prototype.concat.apply([], json.times),
+            plots = [],
+            max = 0;
 
-                // Convert the timestamp to number of whole days after the
-                // unix epoch.
-                for (key in smtimes) {
-                    smtimes[key] = Math.floor(smtimes[key] / 86400);
-                }
-                for (key in json.events) {
-                    e = json.events[key];
-                    if (json.plots[e] === undefined) continue;
-                    $('<span>').append(e)
-                        .attr({'class':'label','style':'margin-left:0.5em'})
-                        .appendTo($('#line-chart-legend'));
-                    $('<br>').appendTo('#line-chart-legend');
-                    times.push(smtimes);
-                    plots.push(json.plots[e]);
-                    // Keep track of max value from any plot
-                    max = Math.max(max, Math.max.apply(Math, json.plots[e]));
-                }
-                m = r.linechart(20, 0, width - 70, height,
-                    times, plots, { 
-                    gutter: 20,
-                    width: 1.6,
-                    nostroke: false, 
-                    shade: false,
-                    axis: "0 0 1 1",
-                    axisxstep: 8,
-                    axisystep: Math.min(12, max),
-                    symbol: "circle",
-                    smooth: false
-                }).hoverColumn(function () {
-                    this.tags = r.set();
-                    var slots = [];
+        // Convert the timestamp to number of whole days after the
+        // unix epoch.
+        for (key in smtimes) {
+            smtimes[key] = Math.floor(smtimes[key] / 86400);
+        }
+        for (key in json.events) {
+            e = json.events[key];
+            if (json.plots[e] === undefined) continue;
+            $('<span>').append(e)
+                .attr({'class':'label','style':'margin-left:0.5em'})
+                .appendTo($('#line-chart-legend'));
+            $('<br>').appendTo('#line-chart-legend');
+            times.push(smtimes);
+            plots.push(json.plots[e]);
+            // Keep track of max value from any plot
+            max = Math.max(max, Math.max.apply(Math, json.plots[e]));
+        }
+        m = r.linechart(20, 0, width - 70, height,
+            times, plots, {
+            gutter: 20,
+            width: 1.6,
+            nostroke: false,
+            shade: false,
+            axis: "0 0 1 1",
+            axisxstep: 8,
+            axisystep: Math.min(12, max),
+            symbol: "circle",
+            smooth: false
+        }).hoverColumn(function () {
+            this.tags = r.set();
+            var slots = [];
 
-                    for (var i = 0, ii = this.y.length; i < ii; i++) {
-                        if (this.values[i] === 0) continue;
-                        if (this.symbols[i].node.style.display == "none") continue;
-                        var angle = 160;
-                        for (var j = 0, jj = slots.length; j < jj; j++) {
-                            if (slots[j][0] == this.x
-                                    && Math.abs(slots[j][1] - this.y[i]) < 20) {
-                                angle = 20;
-                                break;
-                            }
-                        }
-                        slots.push([this.x, this.y[i]]);
-                        this.tags.push(r.tag(this.x, this.y[i],
-                            this.values[i], angle,
-                            10).insertBefore(this).attr([
-                                { fill: '#eee' },
-                                { fill: this.symbols[i].attr('fill') }]));
+            for (var i = 0, ii = this.y.length; i < ii; i++) {
+                if (this.values[i] === 0) continue;
+                if (this.symbols[i].node.style.display == "none") continue;
+                var angle = 160;
+                for (var j = 0, jj = slots.length; j < jj; j++) {
+                    if (slots[j][0] == this.x
+                            && Math.abs(slots[j][1] - this.y[i]) < 20) {
+                        angle = 20;
+                        break;
                     }
-                }, function () {
-                    this.tags && this.tags.remove();
-                });
-                // Change axis labels from Unix epoch
-                $('tspan', $('#line-chart-here')).each(function(e) {
-                    var text = this.firstChild.textContent;
-                    if (parseInt(text) > 10000)
-                        this.firstChild.textContent =
-                            $.datepicker.formatDate('mm-dd-yy',
-                            new Date(parseInt(text) * 86400000));
-                });
-                $('span.label').each(function(i, e) {
-                    e = $(e);
-                    e.click(function() {
-                        e.toggleClass('disabled');
-                        if (e.hasClass('disabled')) {
-                            m.symbols[i].hide();
-                            m.lines[i].hide();
-                        } else {
-                            m.symbols[i].show();
-                            m.lines[i].show();
-                        }
-                    });
-                });
-                // Dear aspiring API writers, please consider making [easy]
-                // things simpler than this...
-                $('span.label', '#line-chart-legend').css(
-                    'background-color', function(i) {
-                        return Raphael.color(m.symbols[i][0].attr('fill')).hex; 
-                });
-            }
-        });
-        if (this.start) build_table.apply(this);
-        return false;
-    }
-
-    // Add tabs for the tabular display
-    $(function() {
-        $.ajax({
-            url:        'ajax.php/report/overview/table/groups',
-            dataType:   'json',
-            success:    function(json) {
-                var first=true;
-                for (key in json) {
-                    $('#tabular-navigation')
-                        .append($('<li>').attr(first ? {'class':'active'} : {})
-                        .append($('<a>')
-                            .click(build_table)
-                            .attr({'href':'#'+key})
-                            .append(json[key]))
-                        )
-                        .after($('<div class="tab_content"></div>')
-                            .addClass(first ? 'hidden' : undefined))
-
-                    first=false;
                 }
-                build_table.apply($('#tabular-navigation li:first-child a')[0])
+                slots.push([this.x, this.y[i]]);
+                this.tags.push(r.tag(this.x, this.y[i],
+                    this.values[i], angle,
+                    10).insertBefore(this).attr([
+                        { fill: '#eee' },
+                        { fill: this.symbols[i].attr('fill') }]));
             }
+        }, function () {
+            this.tags && this.tags.remove();
         });
-    });
-
-    var start, stop;
-    function build_table() {
-        start = this.start ? this.start.value : 'last month';
-        stop = this.period ? this.period.value : 'now';
-
-        if (!current_tab)
-            current_tab = $('#tabular-navigation li:first-child a');
-
-        var group = current_tab.attr('table-group');
-        var pagesize = 25;
-        getConfig().then(function(c) { if (c.page_size) pagesize = c.page_size; });
-        $.ajax({
-            method:     'GET',
-            dataType:   'json',
-            url:        'ajax.php/report/overview/table',
-            data:       {group: group, start: start, period: stop},
-            success:    function(json) {
-                var q = $('<table>'),
-                    h = $('<tr>').appendTo($('<thead>').appendTo(q)),
-                    max = [];
-                for (var c in json.columns) {
-                    h.append($('<th>').append(json.columns[c]));
-                    max.push(0);
-                }
-                for (y in json.data) {
-                    row = json.data[y];
-                    for (x in row) {
-                        max[x] = Math.max(max[x], parseFloat(row[x]||0));
-                    }
-                }
-                for (var i in json.data) {
-                    if (i % pagesize === 0)
-                        b = $('<tbody>').attr({'page':i/pagesize+1}).addClass('hidden').appendTo(q);
-                    row = json.data[i];
-                    tr = $('<tr>').appendTo(b);
-                    for (var j in row) {
-                        if (j == 0) 
-                            tr.append($('<th>').append(row[j]));
-                        else {
-                            val = parseFloat(row[j])||0;
-                            color = 'black';
-                            size = 0;
-                            if (val && max[j] && json.data.length > 1) {
-                                scale = val / max[j];
-                                color = Raphael.hsb(
-                                    Math.min((1 - scale) * .4, 1),
-                                    .75, .75);
-                                size = 16 * scale;
-                            }
-                            tr.append($('<td>')
-                                .append($('<div>').append(
-                                    $('<div>').css(val ? {
-                                        'background-color': color,
-                                        'width': size,
-                                        'height': size,
-                                        'top': 9 - (size / 2),
-                                        'right': 10 - (size / 2)
-                                    } : {})
-                                    .append("&nbsp;")))
-                                .append(row[j]));
-                        }
-                    }
-                }
-                if (json.data.length == 0) {
-                    $('<tbody>').attr('page','1').append($('<tr>').append(
-                        $('<td>').attr('colspan','8').append(
-                            'No data for this timeframe found'))).appendTo(q);
+        // Change axis labels from Unix epoch
+        var qq = setInterval(function() {
+            if ($.datepicker === undefined)
+                return;
+            clearInterval(qq);
+            $('tspan', $('#line-chart-here')).each(function(e) {
+                var text = this.firstChild.textContent;
+                if (parseInt(text) > 10000)
+                    this.firstChild.textContent =
+                        $.datepicker.formatDate('mm-dd-yy',
+                        new Date(parseInt(text) * 86400000));
+            });
+        }, 50);
+        $('span.label').each(function(i, e) {
+            e = $(e);
+            e.click(function() {
+                e.toggleClass('disabled');
+                if (e.hasClass('disabled')) {
+                    m.symbols[i].hide();
+                    m.lines[i].hide();
+                } else {
+                    m.symbols[i].show();
+                    m.lines[i].show();
                 }
-                $('tbody[page=1]', q).removeClass('hidden');
-                $('#table-here').empty().append(q);
-
-                // ----------------------> Pagination <---------------------
-                function goabs(e) {
-                    $('tbody', q).addClass('hidden');
-                    if (e.target) {
-                        page = e.target.text;
-                        $('tbody[page='+page+']', q).removeClass('hidden');
-                    } else {
-                        e.removeClass('hidden');
-                        page = e.attr('page')
-                    }
-                    return enable_next_prev(page);
-                }
-                function goprev() {
-                    current = $('tbody:not(.hidden)', q).attr('page');
-                    page = Math.max(1, parseInt(current) - 1);
-                    return goabs($('tbody[page='+page+']', q));
-                }
-                function gonext() {
-                    current = $('tbody:not(.hidden)', q).attr('page');
-                    page = Math.min(Math.floor(json.data.length / pagesize) + 1,
-                        parseInt(current) + 1);
-                    return goabs($('tbody[page='+page+']', q));
-                }
-                function enable_next_prev(page) {
-                    $('#table-here div.pagination li[page]').removeClass('active');
-                    $('#table-here div.pagination li[page='+page+']').addClass('active');
-
-                    if (page == 1)  $('#report-page-prev').addClass('disabled');
-                    else            $('#report-page-prev').removeClass('disabled');
-
-                    if (page == Math.floor(json.data.length / pagesize) + 1)
-                                    $('#report-page-next').addClass('disabled');
-                    else            $('#report-page-next').removeClass('disabled');
-                    return false;
-                }
-
-                var p = $('<ul>')
-                    .appendTo($('<div>').attr({'class':'pagination'})
-                    .appendTo($('#table-here')));
-                $('<a>').click(goprev).attr({'href':'#'})
-                    .append('&laquo;').appendTo($('<li>').attr({'id':'report-page-prev'})
-                    .appendTo(p));
-                $('tbody', q).each(function() {
-                    page = $(this).attr('page');
-                    $('<a>').click(goabs).attr({'href':'#'}).append(page)
-                        .appendTo($('<li>').attr({'page':page})
-                        .appendTo(p));
-                });
-                $('<a>').click(gonext).attr({'href':'#'})
-                    .append('&raquo;').appendTo($('<li>').attr({'id':'report-page-next'})
-                    .appendTo(p));
-
-                // ------------------------> Export <-----------------------
-                $('<a>').attr({'href':'ajax.php/report/overview/table/export?group='
-                        +group+'&start='+start+'&stop='+stop}).append('Export')
-                    .addClass('no-pjax')
-                    .appendTo($('<li>')
-                    .appendTo(p));
-
-                goprev();
-            }
+            });
         });
-        return false;
-    }
-
-    $(function() {
-        var form = $('#timeframe-form');
-        form.submit(refresh);
-        //Trigger submit now...init.
-        form.submit();
-    });
+        // Dear aspiring API writers, please consider making [easy]
+        // things simpler than this...
+        $('span.label', '#line-chart-legend').css(
+            'background-color', function(i) {
+                return Raphael.color(m.symbols[i][0].attr('fill')).hex;
+        });
+    };
 })(window.jQuery);