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'); ?> <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(" "))) - .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('«').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('»').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);