#167 JS development of hashchange-dependant tabbing mechanism, and of Rickshaw graphs with separate legends.
#167 JS development of hashchange-dependant tabbing mechanism, and of Rickshaw graphs with separate legends.

.table-condensed td.sparkline-cell { .table-condensed td.sparkline-cell {
padding: 1px 0 0 0; padding: 1px 0 0 0;
width: 108px; width: 108px;
text-align: center; text-align: center;
} }
.rickshaw_chart_container { .rickshaw_chart_container {
position: relative; position: relative;
height: 350px; height: 350px;
margin: 0 auto 20px auto; margin: 0 auto 20px auto;
} }
.rickshaw_chart { .rickshaw_chart {
position: absolute; position: absolute;
left: 40px; left: 40px;
width: 500px; width: 500px;
top: 0; top: 0;
bottom: 0; bottom: 0;
} }
.rickshaw_legend { .rickshaw_legend {
position: absolute;  
right: 0;  
top: 0;  
margin-left: 15px;  
padding: 0 5px;  
background: transparent; background: transparent;
max-width: 150px; width: 100%;
overflow: hidden;  
background: rgba(0,0,0,0.05);  
border-radius:5px;  
} }
.rickshaw_y_axis { .rickshaw_y_axis {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 40px; width: 40px;
} }
.rickshaw_legend .label { .rickshaw_legend .label {
background: transparent !important; background: transparent !important;
color: #000000 !important; color: #000000 !important;
font-weight: normal !important; font-weight: normal !important;
} }
   
   
var CKAN = CKAN || {}; var CKAN = CKAN || {};
CKAN.GA_Reports = {}; CKAN.GA_Reports = {};
   
CKAN.GA_Reports.render_rickshaw = function( css_name, data, mode, colorscheme ) { CKAN.GA_Reports.render_rickshaw = function( css_name, data, mode, colorscheme ) {
  var graphLegends = $('#graph-legend-container');
  var myLegend = $('<div id="legend_'+css_name+'"/>').appendTo(graphLegends);
   
var palette = new Rickshaw.Color.Palette( { scheme: colorscheme } ); var palette = new Rickshaw.Color.Palette( { scheme: colorscheme } );
$.each(data, function(i, object) { $.each(data, function(i, object) {
object['color'] = palette.color(); object['color'] = palette.color();
}); });
   
var graphElement = document.querySelector("#chart_"+css_name); var graphElement = document.querySelector("#chart_"+css_name);
   
var graph = new Rickshaw.Graph( { var graph = new Rickshaw.Graph( {
element: document.querySelector("#chart_"+css_name), element: document.querySelector("#chart_"+css_name),
renderer: mode, renderer: mode,
series: data , series: data ,
height: 328 height: 328
}); });
var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } ); var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } );
var y_axis = new Rickshaw.Graph.Axis.Y( { var y_axis = new Rickshaw.Graph.Axis.Y( {
graph: graph, graph: graph,
orientation: 'left', orientation: 'left',
tickFormat: Rickshaw.Fixtures.Number.formatKMBT, tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
element: document.getElementById('y_axis_'+css_name), element: document.getElementById('y_axis_'+css_name),
} ); } );
var legend = new Rickshaw.Graph.Legend( { var legend = new Rickshaw.Graph.Legend( {
element: document.querySelector('#legend_'+css_name), element: document.querySelector('#legend_'+css_name),
graph: graph graph: graph
} ); } );
var hoverDetail = new Rickshaw.Graph.HoverDetail( { var hoverDetail = new Rickshaw.Graph.HoverDetail( {
graph: graph, graph: graph,
formatter: function(series, x, y) { formatter: function(series, x, y) {
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>'; var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>'; var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
var content = swatch + series.name + ": " + parseInt(y) + '<br>' + date; var content = swatch + series.name + ": " + parseInt(y) + '<br>' + date;
return content; return content;
} }
} ); } );
graph.render(); graph.render();
}; };
   
  CKAN.GA_Reports.bind_sparklines = function() {
  /*
  * Bind to the 'totals' tab being on screen, when the
  * Sparkline graphs should be drawn.
  * Note that they cannot be drawn sooner.
  */
  $('a[href="#totals"]').on(
  'shown',
  function() {
  var sparkOptions = {
  enableTagOptions: true,
  type: 'line',
  width: 100,
  height: 26,
  chartRangeMin: 0,
  spotColor: '',
  maxSpotColor: '',
  minSpotColor: '',
  highlightSpotColor: '000000',
  lineColor: '3F8E6D',
  fillColor: 'B7E66B'
  };
  $('.sparkline').sparkline('html',sparkOptions);
  }
  );
  };
   
  CKAN.GA_Reports.bind_sidebar = function() {
  /*
  * Bind to changes in the tab behaviour:
  * Show the correct rickshaw graph in the sidebar.
  * Not to be called before all graphs load.
  */
  $('a[data-toggle="hashchange"]').on(
  'shown',
  function(e) {
  var href = $(e.target).attr('href');
  var pane = $(href);
  if (!pane.length) { console.err('bad href',href); return; }
  var legend_name = "none";
  var graph = pane.find('.rickshaw_chart');
  if (graph.length) {
  legend_name = graph.attr('id').replace('chart_','');
  }
  legend_name = '#legend_'+legend_name;
  $('#graph-legend-container > *').hide();
  $(legend_name).show();
  }
  );
  };
   
  /*
  * Custom bootstrap plugin for handling data-toggle="hashchange".
  * Behaves like data-toggle="tab" but I respond to the hashchange.
  * Page state is memo-ized in the URL this way. Why doesn't Bootstrap do this?
  */
  $(function() {
  var mapping = {};
  $('a[data-toggle="hashchange"]').each(
  function(i,link) {
  link = $(link);
  mapping[link.attr('href')] = link;
  }
  );
  $(window).hashchange(function() {
  var link = mapping[window.location.hash];
  if (link) { link.tab('show'); }
  });
  });
   
<html <html
xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n" xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/" xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="" py:strip=""
> >
   
<select name="month" py:def="month_selector(current_month, months, day)"> <select name="month" py:def="month_selector(current_month, months, day)">
<option value='' py:attrs="{'selected': 'selected' if not current_month else None}">All months</option> <option value='' py:attrs="{'selected': 'selected' if not current_month else None}">All months</option>
<py:for each="i, (val,desc) in enumerate(months)"> <py:for each="i, (val,desc) in enumerate(months)">
<option value='${val}' py:attrs="{'selected': 'selected' if current_month == val else None}">${desc}<py:if test="i == 0 and day"> (up to ${day})</py:if></option> <option value='${val}' py:attrs="{'selected': 'selected' if current_month == val else None}">${desc}<py:if test="i == 0 and day"> (up to ${day})</py:if></option>
</py:for> </py:for>
</select> </select>
   
   
<table py:def="social_table(items, with_source=False)" class="table table-condensed table-bordered table-striped"> <table py:def="social_table(items, with_source=False)" class="table table-condensed table-bordered table-striped">
<tr> <tr>
<th>Name</th> <th>Name</th>
<th py:if="with_source">Source</th> <th py:if="with_source">Source</th>
<th>Visits</th> <th>Visits</th>
</tr> </tr>
<py:for each="name, url, source, count in items"> <py:for each="name, url, source, count in items">
<tr> <tr>
<td><a href="${url}">${name}</a></td> <td><a href="${url}">${name}</a></td>
<td py:if="with_source">${source}</td> <td py:if="with_source">${source}</td>
<td>${count}</td> <td>${count}</td>
</tr> </tr>
</py:for> </py:for>
</table> </table>
   
   
<div py:def="rickshaw_graph(items_json,id,mode='line',colorscheme='munin')"> <div py:def="rickshaw_graph(items_json,id,mode='line',colorscheme='munin')">
<div id="chart_container_$id" class="rickshaw_chart_container"> <div id="chart_container_$id" class="rickshaw_chart_container">
<div id="y_axis_$id" class="rickshaw_y_axis"></div> <div id="y_axis_$id" class="rickshaw_y_axis"></div>
<div id="chart_$id" class="rickshaw_chart"></div> <div id="chart_$id" class="rickshaw_chart"></div>
<div id="legend_$id" class="rickshaw_legend"></div>  
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
CKAN.GA_Reports.render_rickshaw('$id', $items_json, '$mode', '$colorscheme'); CKAN.GA_Reports.render_rickshaw('$id', $items_json, '$mode', '$colorscheme');
}); });
</script> </script>
</div> </div>
</div> </div>
   
   
<table py:def="stat_table(items, title='Views')" class="table table-condensed table-bordered table-striped"> <table py:def="stat_table(items, title='Views')" class="table table-condensed table-bordered table-striped">
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>% ${title}</th> <th>% ${title}</th>
</tr> </tr>
<py:for each="name, value in items"> <py:for each="name, value in items">
<tr> <tr>
<td>${name}</td> <td>${name}</td>
<td>${value}</td> <td>${value}</td>
</tr> </tr>
</py:for> </py:for>
</table> </table>
   
   
<div py:def="usage_nav(active_name)" id="minornavigation"> <div py:def="usage_nav(active_name)" id="minornavigation">
<div id="minornavigation-bg-left"> <div id="minornavigation-bg-left">
<div id="minornavigation-bg-right"> <div id="minornavigation-bg-right">
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li py:attrs="{'class': 'active' if active_name=='Site-wide' else None}"><a py:attrs="{'class': 'active' if active_name=='Site-wide' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Site-wide</a></li> <li py:attrs="{'class': 'active' if active_name=='Site-wide' else None}"><a py:attrs="{'class': 'active' if active_name=='Site-wide' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Site-wide</a></li>
<li py:attrs="{'class': 'active' if active_name=='Publishers' else None}"> <li py:attrs="{'class': 'active' if active_name=='Publishers' else None}">
<a py:attrs="{'class': 'active' if active_name=='Publishers' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Publishers</a> <a py:attrs="{'class': 'active' if active_name=='Publishers' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Publishers</a>
</li> </li>
<li py:attrs="{'class': 'active' if active_name=='Datasets' else None}"> <li py:attrs="{'class': 'active' if active_name=='Datasets' else None}">
<a py:attrs="{'class': 'active' if active_name=='Datasets' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Datasets</a> <a py:attrs="{'class': 'active' if active_name=='Datasets' else None}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Datasets</a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
   
   
</html> </html>
   
<html xmlns:py="http://genshi.edgewall.org/" <html xmlns:py="http://genshi.edgewall.org/"
xmlns:i18n="http://genshi.edgewall.org/i18n" xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip=""> py:strip="">
   
<xi:include href="../ga_util.html" /> <xi:include href="../ga_util.html" />
   
<py:def function="page_title">Site usage</py:def> <py:def function="page_title">Site usage</py:def>
   
<py:def function="optional_head"> <py:def function="optional_head">
<link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
<link rel="stylesheet" type="text/css" href="/css/ga_report.css"/> <link rel="stylesheet" type="text/css" href="/css/ga_report.css"/>
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script> <script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script>
<script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
<script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script> <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
<script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script> <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>
<script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script>
</py:def> </py:def>
   
<py:match path="primarysidebar"> <py:match path="primarysidebar">
<li class="widget-container boxed widget_text"> <li class="widget-container boxed widget_text">
<h4>Download</h4> <h4>Download</h4>
<p><center> <p><center>
<a class="btn button btn-primary" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all')}">Download as CSV</a></center> <a class="btn button btn-primary" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all')}">Download as CSV</a></center>
</p> </p>
</li> </li>
  <li class="widget-container boxed widget_text">
  <h4>Graph Legend</h4>
  <div id="graph-legend-container">
  <div id="legend_none">(No graph loaded)</div>
  </div>
  </li>
<xi:include href="../notes.html" /> <xi:include href="../notes.html" />
   
</py:match> </py:match>
   
<div py:match="content"> <div py:match="content">
<h1>Site Usage</h1> <h1>Site Usage</h1>
${usage_nav('Site-wide')} ${usage_nav('Site-wide')}
   
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
<div class="controls"> <div class="controls">
   
${month_selector(c.month, c.months, c.day)} ${month_selector(c.month, c.months, c.day)}
   
<input class="btn button btn-primary" type='submit' value="Update"/> <input class="btn button btn-primary" type='submit' value="Update"/>
</div> </div>
</form> </form>
   
<div class="tabbable"> <div class="tabbable">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="#totals" data-toggle="tab">Totals</a></li> <li><a href="#totals" data-toggle="hashchange">Totals</a></li>
<li class="dropdown"> <li class="dropdown">
<a href="#browsers" class="dropdown-toggle" data-toggle="dropdown">Browsers <a href="#" class="dropdown-toggle" data-toggle="dropdown">Browsers
<b class="caret"></b></a> <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#browsers_names" data-toggle="tab">Browsers</a></li> <li><a href="#browsers_names" data-toggle="hashchange">Browsers</a></li>
<li><a href="#browsers_versions" data-toggle="tab">Versions</a></li> <li><a href="#browsers_versions" data-toggle="hashchange">Versions</a></li>
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#browsers" class="dropdown-toggle" data-toggle="dropdown">Operating<br/>Systems <a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating Systems
<b class="caret"></b></a> <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#os" data-toggle="tab">Operating Systems</a></li> <li><a href="#os" data-toggle="hashchange">Operating Systems</a></li>
<li><a href="#os_versions" data-toggle="tab">Versions</a></li> <li><a href="#os_versions" data-toggle="hashchange">Versions</a></li>
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#browsers" class="dropdown-toggle" data-toggle="dropdown">Social Networks <a href="#" class="dropdown-toggle" data-toggle="dropdown">Social
<b class="caret"></b></a> <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">