[304] No need to declare a content_class any longer.
[304] No need to declare a content_class any longer.

import re import re
import csv import csv
import sys import sys
import json import json
import logging import logging
import operator import operator
import collections import collections
from ckan.lib.base import (BaseController, c, g, render, request, response, abort) from ckan.lib.base import (BaseController, c, g, render, request, response, abort)
   
import sqlalchemy import sqlalchemy
from sqlalchemy import func, cast, Integer from sqlalchemy import func, cast, Integer
import ckan.model as model import ckan.model as model
from ga_model import GA_Url, GA_Stat, GA_ReferralStat, GA_Publisher from ga_model import GA_Url, GA_Stat, GA_ReferralStat, GA_Publisher
   
log = logging.getLogger('ckanext.ga-report') log = logging.getLogger('ckanext.ga-report')
   
DOWNLOADS_AVAILABLE_FROM = '2012-12' DOWNLOADS_AVAILABLE_FROM = '2012-12'
   
def _get_month_name(strdate): def _get_month_name(strdate):
import calendar import calendar
from time import strptime from time import strptime
d = strptime(strdate, '%Y-%m') d = strptime(strdate, '%Y-%m')
return '%s %s' % (calendar.month_name[d.tm_mon], d.tm_year) return '%s %s' % (calendar.month_name[d.tm_mon], d.tm_year)
   
def _get_unix_epoch(strdate): def _get_unix_epoch(strdate):
from time import strptime,mktime from time import strptime,mktime
d = strptime(strdate, '%Y-%m') d = strptime(strdate, '%Y-%m')
return int(mktime(d)) return int(mktime(d))
   
def _month_details(cls, stat_key=None): def _month_details(cls, stat_key=None):
''' '''
Returns a list of all the periods for which we have data, unfortunately Returns a list of all the periods for which we have data, unfortunately
knows too much about the type of the cls being passed as GA_Url has a knows too much about the type of the cls being passed as GA_Url has a
more complex query more complex query
   
This may need extending if we add a period_name to the stats This may need extending if we add a period_name to the stats
''' '''
months = [] months = []
day = None day = None
   
q = model.Session.query(cls.period_name,cls.period_complete_day)\ q = model.Session.query(cls.period_name,cls.period_complete_day)\
.filter(cls.period_name!='All').distinct(cls.period_name) .filter(cls.period_name!='All').distinct(cls.period_name)
if stat_key: if stat_key:
q= q.filter(cls.stat_name==stat_key) q= q.filter(cls.stat_name==stat_key)
   
vals = q.order_by("period_name desc").all() vals = q.order_by("period_name desc").all()
   
if vals and vals[0][1]: if vals and vals[0][1]:
day = int(vals[0][1]) day = int(vals[0][1])
ordinal = 'th' if 11 <= day <= 13 \ ordinal = 'th' if 11 <= day <= 13 \
else {1:'st',2:'nd',3:'rd'}.get(day % 10, 'th') else {1:'st',2:'nd',3:'rd'}.get(day % 10, 'th')
day = "{day}{ordinal}".format(day=day, ordinal=ordinal) day = "{day}{ordinal}".format(day=day, ordinal=ordinal)
   
for m in vals: for m in vals:
months.append( (m[0], _get_month_name(m[0]))) months.append( (m[0], _get_month_name(m[0])))
   
return months, day return months, day
   
   
class GaReport(BaseController): class GaReport(BaseController):
   
def csv(self, month): def csv(self, month):
import csv import csv
   
q = model.Session.query(GA_Stat).filter(GA_Stat.stat_name!='Downloads') q = model.Session.query(GA_Stat).filter(GA_Stat.stat_name!='Downloads')
if month != 'all': if month != 'all':
q = q.filter(GA_Stat.period_name==month) q = q.filter(GA_Stat.period_name==month)
entries = q.order_by('GA_Stat.period_name, GA_Stat.stat_name, GA_Stat.key').all() entries = q.order_by('GA_Stat.period_name, GA_Stat.stat_name, GA_Stat.key').all()
   
response.headers['Content-Type'] = "text/csv; charset=utf-8" response.headers['Content-Type'] = "text/csv; charset=utf-8"
response.headers['Content-Disposition'] = str('attachment; filename=stats_%s.csv' % (month,)) response.headers['Content-Disposition'] = str('attachment; filename=stats_%s.csv' % (month,))
   
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(["Period", "Statistic", "Key", "Value"]) writer.writerow(["Period", "Statistic", "Key", "Value"])
   
for entry in entries: for entry in entries:
writer.writerow([entry.period_name.encode('utf-8'), writer.writerow([entry.period_name.encode('utf-8'),
entry.stat_name.encode('utf-8'), entry.stat_name.encode('utf-8'),
entry.key.encode('utf-8'), entry.key.encode('utf-8'),
entry.value.encode('utf-8')]) entry.value.encode('utf-8')])
   
   
def index(self): def index(self):
   
# Get the month details by fetching distinct values and determining the # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months, c.day = _month_details(GA_Stat) c.months, c.day = _month_details(GA_Stat)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month_desc = 'all months' c.month_desc = 'all months'
c.month = request.params.get('month', '') c.month = request.params.get('month', '')
if c.month: if c.month:
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
   
q = model.Session.query(GA_Stat).\ q = model.Session.query(GA_Stat).\
filter(GA_Stat.stat_name=='Totals') filter(GA_Stat.stat_name=='Totals')
if c.month: if c.month:
q = q.filter(GA_Stat.period_name==c.month) q = q.filter(GA_Stat.period_name==c.month)
entries = q.order_by('ga_stat.key').all() entries = q.order_by('ga_stat.key').all()
   
def clean_key(key, val): def clean_key(key, val):
if key in ['Average time on site', 'Pages per visit', 'New visits', 'Bounce rate (home page)']: if key in ['Average time on site', 'Pages per visit', 'New visits', 'Bounce rate (home page)']:
val = "%.2f" % round(float(val), 2) val = "%.2f" % round(float(val), 2)
if key == 'Average time on site': if key == 'Average time on site':
mins, secs = divmod(float(val), 60) mins, secs = divmod(float(val), 60)
hours, mins = divmod(mins, 60) hours, mins = divmod(mins, 60)
val = '%02d:%02d:%02d (%s seconds) ' % (hours, mins, secs, val) val = '%02d:%02d:%02d (%s seconds) ' % (hours, mins, secs, val)
if key in ['New visits','Bounce rate (home page)']: if key in ['New visits','Bounce rate (home page)']:
val = "%s%%" % val val = "%s%%" % val
if key in ['Total page views', 'Total visits']: if key in ['Total page views', 'Total visits']:
val = int(val) val = int(val)
   
return key, val return key, val
   
# Query historic values for sparkline rendering # Query historic values for sparkline rendering
sparkline_query = model.Session.query(GA_Stat)\ sparkline_query = model.Session.query(GA_Stat)\
.filter(GA_Stat.stat_name=='Totals')\ .filter(GA_Stat.stat_name=='Totals')\
.order_by(GA_Stat.period_name) .order_by(GA_Stat.period_name)
sparkline_data = {} sparkline_data = {}
for x in sparkline_query: for x in sparkline_query:
sparkline_data[x.key] = sparkline_data.get(x.key,[]) sparkline_data[x.key] = sparkline_data.get(x.key,[])
key, val = clean_key(x.key,float(x.value)) key, val = clean_key(x.key,float(x.value))
tooltip = '%s: %s' % (_get_month_name(x.period_name), val) tooltip = '%s: %s' % (_get_month_name(x.period_name), val)
sparkline_data[x.key].append( (tooltip,x.value) ) sparkline_data[x.key].append( (tooltip,x.value) )
# Trim the latest month, as it looks like a huge dropoff # Trim the latest month, as it looks like a huge dropoff
for key in sparkline_data: for key in sparkline_data:
sparkline_data[key] = sparkline_data[key][:-1] sparkline_data[key] = sparkline_data[key][:-1]
   
c.global_totals = [] c.global_totals = []
if c.month: if c.month:
for e in entries: for e in entries:
key, val = clean_key(e.key, e.value) key, val = clean_key(e.key, e.value)
sparkline = sparkline_data[e.key] sparkline = sparkline_data[e.key]
c.global_totals.append((key, val, sparkline)) c.global_totals.append((key, val, sparkline))
else: else:
d = collections.defaultdict(list) d = collections.defaultdict(list)
for e in entries: for e in entries:
d[e.key].append(float(e.value)) d[e.key].append(float(e.value))
for k, v in d.iteritems(): for k, v in d.iteritems():
if k in ['Total page views', 'Total visits']: if k in ['Total page views', 'Total visits']:
v = sum(v) v = sum(v)
else: else:
v = float(sum(v))/float(len(v)) v = float(sum(v))/float(len(v))
sparkline = sparkline_data[k] sparkline = sparkline_data[k]
key, val = clean_key(k,v) key, val = clean_key(k,v)
   
c.global_totals.append((key, val, sparkline)) c.global_totals.append((key, val, sparkline))
# Sort the global totals into a more pleasant order # Sort the global totals into a more pleasant order
def sort_func(x): def sort_func(x):
key = x[0] key = x[0]
total_order = ['Total page views','Total visits','Pages per visit'] total_order = ['Total page views','Total visits','Pages per visit']
if key in total_order: if key in total_order:
return total_order.index(key) return total_order.index(key)
return 999 return 999
c.global_totals = sorted(c.global_totals, key=sort_func) c.global_totals = sorted(c.global_totals, key=sort_func)
   
keys = { keys = {
'Browser versions': 'browser_versions', 'Browser versions': 'browser_versions',
'Browsers': 'browsers', 'Browsers': 'browsers',
'Operating Systems versions': 'os_versions', 'Operating Systems versions': 'os_versions',
'Operating Systems': 'os', 'Operating Systems': 'os',
'Social sources': 'social_networks', 'Social sources': 'social_networks',
'Languages': 'languages', 'Languages': 'languages',
'Country': 'country' 'Country': 'country'
} }
   
def shorten_name(name, length=60): def shorten_name(name, length=60):
return (name[:length] + '..') if len(name) > 60 else name return (name[:length] + '..') if len(name) > 60 else name
   
def fill_out_url(url): def fill_out_url(url):
import urlparse import urlparse
return urlparse.urljoin(g.site_url, url) return urlparse.urljoin(g.site_url, url)
   
c.social_referrer_totals, c.social_referrers = [], [] c.social_referrer_totals, c.social_referrers = [], []
q = model.Session.query(GA_ReferralStat) q = model.Session.query(GA_ReferralStat)
q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q
q = q.order_by('ga_referrer.count::int desc') q = q.order_by('ga_referrer.count::int desc')
for entry in q.all(): for entry in q.all():
c.social_referrers.append((shorten_name(entry.url), fill_out_url(entry.url), c.social_referrers.append((shorten_name(entry.url), fill_out_url(entry.url),
entry.source,entry.count)) entry.source,entry.count))
   
q = model.Session.query(GA_ReferralStat.url, q = model.Session.query(GA_ReferralStat.url,
func.sum(GA_ReferralStat.count).label('count')) func.sum(GA_ReferralStat.count).label('count'))
q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q
q = q.order_by('count desc').group_by(GA_ReferralStat.url) q = q.order_by('count desc').group_by(GA_ReferralStat.url)
for entry in q.all(): for entry in q.all():
c.social_referrer_totals.append((shorten_name(entry[0]), fill_out_url(entry[0]),'', c.social_referrer_totals.append((shorten_name(entry[0]), fill_out_url(entry[0]),'',
entry[1])) entry[1]))
   
for k, v in keys.iteritems(): for k, v in keys.iteritems():
q = model.Session.query(GA_Stat).\ q = model.Session.query(GA_Stat).\
filter(GA_Stat.stat_name==k).\ filter(GA_Stat.stat_name==k).\
order_by(GA_Stat.period_name) order_by(GA_Stat.period_name)
# Run the query on all months to gather graph data  
graph = {}  
for stat in q:  
graph[ stat.key ] = graph.get(stat.key,{  
'name':stat.key,  
'data': []  
})  
graph[ stat.key ]['data'].append({  
'x':_get_unix_epoch(stat.period_name),  
'y':float(stat.value)  
})  
setattr(c, v+'_graph', json.dumps( _to_rickshaw(graph.values(),percentageMode=True) ))  
   
# Buffer the tabular data # Buffer the tabular data
if c.month: if c.month:
entries = [] entries = []
q = q.filter(GA_Stat.period_name==c.month).\ q = q.filter(GA_Stat.period_name==c.month).\
order_by('ga_stat.value::int desc') order_by('ga_stat.value::int desc')
   
d = collections.defaultdict(int) d = collections.defaultdict(int)
for e in q.all(): for e in q.all():
d[e.key] += int(e.value) d[e.key] += int(e.value)
entries = [] entries = []
for key, val in d.iteritems(): for key, val in d.iteritems():
entries.append((key,val,)) entries.append((key,val,))
entries = sorted(entries, key=operator.itemgetter(1), reverse=True) entries = sorted(entries, key=operator.itemgetter(1), reverse=True)
   
  # Run a query on all months to gather graph data
  graph_query = model.Session.query(GA_Stat).\
  filter(GA_Stat.stat_name==k).\
  order_by(GA_Stat.period_name)
  graph_dict = {}
  for stat in graph_query:
  graph_dict[ stat.key ] = graph_dict.get(stat.key,{
  'name':stat.key,
  'data': []
  })
  graph_dict[ stat.key ]['data'].append({
  'x':_get_unix_epoch(stat.period_name),
  'y':float(stat.value)
  })
  graph = [ graph_dict[x[0]] for x in entries ]
  setattr(c, v+'_graph', json.dumps( _to_rickshaw(graph,percentageMode=True) ))
   
# Get the total for each set of values and then set the value as # Get the total for each set of values and then set the value as
# a percentage of the total # a percentage of the total
if k == 'Social sources': if k == 'Social sources':
total = sum([x for n,x,graph in c.global_totals if n == 'Total visits']) total = sum([x for n,x,graph in c.global_totals if n == 'Total visits'])
else: else:
total = sum([num for _,num in entries]) total = sum([num for _,num in entries])
setattr(c, v, [(k,_percent(v,total)) for k,v in entries ]) setattr(c, v, [(k,_percent(v,total)) for k,v in entries ])
   
return render('ga_report/site/index.html') return render('ga_report/site/index.html')
   
   
class GaDatasetReport(BaseController): class GaDatasetReport(BaseController):
""" """
Displays the pageview and visit count for datasets Displays the pageview and visit count for datasets
with options to filter by publisher and time period. with options to filter by publisher and time period.
""" """
def publisher_csv(self, month): def publisher_csv(self, month):
''' '''
Returns a CSV of each publisher with the total number of dataset Returns a CSV of each publisher with the total number of dataset
views & visits. views & visits.
''' '''
c.month = month if not month == 'all' else '' c.month = month if not month == 'all' else ''
response.headers['Content-Type'] = "text/csv; charset=utf-8" response.headers['Content-Type'] = "text/csv; charset=utf-8"
response.headers['Content-Disposition'] = str('attachment; filename=publishers_%s.csv' % (month,)) response.headers['Content-Disposition'] = str('attachment; filename=publishers_%s.csv' % (month,))
   
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(["Publisher Title", "Publisher Name", "Views", "Visits", "Period Name"]) writer.writerow(["Publisher Title", "Publisher Name", "Views", "Visits", "Period Name"])
   
top_publishers, top_publishers_graph = _get_top_publishers(None) top_publishers, top_publishers_graph = _get_top_publishers(None)
   
for publisher,view,visit in top_publishers: for publisher,view,visit in top_publishers:
writer.writerow([publisher.title.encode('utf-8'), writer.writerow([publisher.title.encode('utf-8'),
publisher.name.encode('utf-8'), publisher.name.encode('utf-8'),
view, view,
visit, visit,
month]) month])
   
def dataset_csv(self, id='all', month='all'): def dataset_csv(self, id='all', month='all'):
''' '''
Returns a CSV with the number of views & visits for each dataset. Returns a CSV with the number of views & visits for each dataset.
   
:param id: A Publisher ID or None if you want for all :param id: A Publisher ID or None if you want for all
:param month: The time period, or 'all' :param month: The time period, or 'all'
''' '''
c.month = month if not month == 'all' else '' c.month = month if not month == 'all' else ''
if id != 'all': if id != 'all':
c.publisher = model.Group.get(id) c.publisher = model.Group.get(id)
if not c.publisher: if not c.publisher:
abort(404, 'A publisher with that name could not be found') abort(404, 'A publisher with that name could not be found')
   
packages = self._get_packages(c.publisher) packages = self._get_packages(c.publisher)
response.headers['Content-Type'] = "text/csv; charset=utf-8" response.headers['Content-Type'] = "text/csv; charset=utf-8"
response.headers['Content-Disposition'] = \ response.headers['Content-Disposition'] = \
str('attachment; filename=datasets_%s_%s.csv' % (c.publisher_name, month,)) str('attachment; filename=datasets_%s_%s.csv' % (c.publisher_name, month,))
   
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(["Dataset Title", "Dataset Name", "Views", "Visits", "Resource downloads", "Period Name"]) writer.writerow(["Dataset Title", "Dataset Name", "Views", "Visits", "Resource downloads", "Period Name"])
   
for package,view,visit,downloads in packages: for package,view,visit,downloads in packages:
writer.writerow([package.title.encode('utf-8'), writer.writerow([package.title.encode('utf-8'),
package.name.encode('utf-8'), package.name.encode('utf-8'),
view, view,
visit, visit,
downloads, downloads,
month]) month])
   
def publishers(self): def publishers(self):
'''A list of publishers and the number of views/visits for each''' '''A list of publishers and the number of views/visits for each'''
   
# Get the month details by fetching distinct values and determining the # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months, c.day = _month_details(GA_Url) c.months, c.day = _month_details(GA_Url)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month = request.params.get('month', '') c.month = request.params.get('month', '')
c.month_desc = 'all months' c.month_desc = 'all months'
if c.month: if c.month:
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
   
c.top_publishers, graph_data = _get_top_publishers() c.top_publishers, graph_data = _get_top_publishers()
c.top_publishers_graph = json.dumps( _to_rickshaw(graph_data.values()) ) c.top_publishers_graph = json.dumps( _to_rickshaw(graph_data) )
   
return render('ga_report/publisher/index.html') return render('ga_report/publisher/index.html')
   
def _get_packages(self, publisher=None, count=-1): def _get_packages(self, publisher=None, count=-1):
'''Returns the datasets in order of views''' '''Returns the datasets in order of views'''
have_download_data = True have_download_data = True
month = c.month or 'All' month = c.month or 'All'
if month != 'All': if month != 'All':
have_download_data = month >= DOWNLOADS_AVAILABLE_FROM have_download_data = month >= DOWNLOADS_AVAILABLE_FROM
   
q = model.Session.query(GA_Url,model.Package)\ q = model.Session.query(GA_Url,model.Package)\
.filter(model.Package.name==GA_Url.package_id)\ .filter(model.Package.name==GA_Url.package_id)\
.filter(GA_Url.url.like('/dataset/%')) .filter(GA_Url.url.like('/dataset/%'))
if publisher: if publisher:
q = q.filter(GA_Url.department_id==publisher.name) q = q.filter(GA_Url.department_id==publisher.name)
q = q.filter(GA_Url.period_name==month) q = q.filter(GA_Url.period_name==month)
q = q.order_by('ga_url.pageviews::int desc') q = q.order_by('ga_url.pageviews::int desc')
top_packages = [] top_packages = []
if count == -1: if count == -1:
entries = q.all() entries = q.all()
else: else:
entries = q.limit(count) entries = q.limit(count)
   
for entry,package in entries: for entry,package in entries:
if package: if package:
# Downloads .... # Downloads ....
if have_download_data: if have_download_data:
dls = model.Session.query(GA_Stat).\ dls = model.Session.query(GA_Stat).\
filter(GA_Stat.stat_name=='Downloads').\ filter(GA_Stat.stat_name=='Downloads').\
filter(GA_Stat.key==package.name) filter(GA_Stat.key==package.name)
if month != 'All': # Fetch everything unless the month is specific if month != 'All': # Fetch everything unless the month is specific
dls = dls.filter(GA_Stat.period_name==month) dls = dls.filter(GA_Stat.period_name==month)
downloads = 0 downloads = 0
for x in dls: for x in dls:
downloads += int(x.value) downloads += int(x.value)
else: else:
downloads = 'No data' downloads = 'No data'
top_packages.append((package, entry.pageviews, entry.visits, downloads)) top_packages.append((package, entry.pageviews, entry.visits, downloads))
else: else:
log.warning('Could not find package associated package') log.warning('Could not find package associated package')
   
return top_packages return top_packages
   
def read(self): def read(self):
''' '''
Lists the most popular datasets across all publishers Lists the most popular datasets across all publishers
''' '''
return self.read_publisher(None) return self.read_publisher(None)
   
def read_publisher(self, id): def read_publisher(self, id):
''' '''
Lists the most popular datasets for a publisher (or across all publishers) Lists the most popular datasets for a publisher (or across all publishers)
''' '''
count = 20 count = 20
   
c.publishers = _get_publishers() c.publishers = _get_publishers()
   
id = request.params.get('publisher', id) id = request.params.get('publisher', id)
if id and id != 'all': if id and id != 'all':
c.publisher = model.Group.get(id) c.publisher = model.Group.get(id)
if not c.publisher: if not c.publisher:
abort(404, 'A publisher with that name could not be found') abort(404, 'A publisher with that name could not be found')
c.publisher_name = c.publisher.name c.publisher_name = c.publisher.name
c.top_packages = [] # package, dataset_views in c.top_packages c.top_packages = [] # package, dataset_views in c.top_packages
   
# Get the month details by fetching distinct values and determining the # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months, c.day = _month_details(GA_Url) c.months, c.day = _month_details(GA_Url)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month = request.params.get('month', '') c.month = request.params.get('month', '')
if not c.month: if not c.month:
c.month_desc = 'all months' c.month_desc = 'all months'
else: else:
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
   
month = c.month or 'All' month = c.month or 'All'
c.publisher_page_views = 0 c.publisher_page_views = 0
q = model.Session.query(GA_Url).\ q = model.Session.query(GA_Url).\
filter(GA_Url.url=='/publisher/%s' % c.publisher_name) filter(GA_Url.url=='/publisher/%s' % c.publisher_name)
entry = q.filter(GA_Url.period_name==c.month).first() entry = q.filter(GA_Url.period_name==c.month).first()
c.publisher_page_views = entry.pageviews if entry else 0 c.publisher_page_views = entry.pageviews if entry else 0
   
c.top_packages = self._get_packages(c.publisher, 20) c.top_packages = self._get_packages(c.publisher, 20)
   
# Graph query # Graph query
top_package_names = [ x[0].name for x in c.top_packages ] top_package_names = [ x[0].name for x in c.top_packages ]
graph_query = model.Session.query(GA_Url,model.Package)\ graph_query = model.Session.query(GA_Url,model.Package)\
.filter(model.Package.name==GA_Url.package_id)\ .filter(model.Package.name==GA_Url.package_id)\
.filter(GA_Url.url.like('/dataset/%'))\ .filter(GA_Url.url.like('/dataset/%'))\
.filter(GA_Url.package_id.in_(top_package_names)) .filter(GA_Url.package_id.in_(top_package_names))
graph_data = {} graph_dict = {}
for entry,package in graph_query: for entry,package in graph_query:
if not package: continue if not package: continue
if entry.period_name=='All': continue if entry.period_name=='All': continue
graph_data[package.id] = graph_data.get(package.id,{ graph_dict[package.name] = graph_dict.get(package.name,{
'name':package.title, 'name':package.title,
'data':[] 'data':[]
}) })
graph_data[package.id]['data'].append({ graph_dict[package.name]['data'].append({
'x':_get_unix_epoch(entry.period_name), 'x':_get_unix_epoch(entry.period_name),
'y':int(entry.pageviews), 'y':int(entry.pageviews),
}) })
  graph = [ graph_dict[x] for x in top_package_names ]
c.graph_data = json.dumps( _to_rickshaw(graph_data.values()) )  
  c.graph_data = json.dumps( _to_rickshaw(graph) )
   
return render('ga_report/publisher/read.html') return render('ga_report/publisher/read.html')
   
def _to_rickshaw(data, percentageMode=False): def _to_rickshaw(data, percentageMode=False):
if data==[]: if data==[]:
return data return data
# Create a consistent x-axis # Create a consistent x-axis between all series
num_points = [ len(package['data']) for package in data ] num_points = [ len(series['data']) for series in data ]
ideal_index = num_points.index( max(num_points) ) ideal_index = num_points.index( max(num_points) )
x_axis = [ point['x'] for point in data[ideal_index]['data'] ] x_axis = []
for package in data: for series in data:
xs = [ point['x'] for point in package['data'] ] for point in series['data']:
assert set(xs).issubset( set(x_axis) ), (xs, x_axis) x_axis.append(point['x'])
# Zero pad any missing values x_axis = sorted( list( set(x_axis) ) )
  # Zero pad any missing values
  for series in data:
  xs = [ point['x'] for point in series['data'] ]
for x in set(x_axis).difference(set(xs)): for x in set(x_axis).difference(set(xs)):
package['data'].append( {'x':x, 'y':0} ) series['data'].append( {'x':x, 'y':0} )
assert len(package['data'])==len(x_axis), (len(package['data']),len(x_axis),package['data'],x_axis,set(x_axis).difference(set(xs)))  
if percentageMode: if percentageMode:
  def get_totals(series_list):
  totals = {}
  for series in series_list:
  for point in series['data']:
  totals[point['x']] = totals.get(point['x'],0) + point['y']
  return totals
# Transform data into percentage stacks # Transform data into percentage stacks
totals = {} totals = get_totals(data)
for x in x_axis:  
for package in data:  
for point in package['data']:  
totals[ point['x'] ] = totals.get(point['x'],0) + point['y']  
# Roll insignificant series into a catch-all # Roll insignificant series into a catch-all
THRESHOLD = 0.01 THRESHOLD = 0.01
significant_series = [] raw_data = data
for package in data: data = []
for point in package['data']: for series in raw_data:
  for point in series['data']:
fraction = float(point['y']) / totals[point['x']] fraction = float(point['y']) / totals[point['x']]
if fraction>THRESHOLD and not (package in significant_series): if not (series in data) and fraction>THRESHOLD:
significant_series.append(package) data.append(series)
temp = {} # Overwrite data with a set of intereting series
for package in data: others = [ x for x in raw_data if not (x in data) ]
if package in significant_series: continue data.append({
for point in package['data']: 'name':'Other',
temp[point['x']] = temp.get(point['x'],0) + point['y'] 'data': [ {'x':x,'y':y} for x,y in get_totals(others).items() ]
catch_all = { 'name':'Other','data': [ {'x':x,'y':y} for x,y in temp.items() ] } })
# Roll insignificant series into one  
data = significant_series  
data.append(catch_all)  
# Turn each point into a percentage # Turn each point into a percentage
for package in data: for series in data:
for point in package['data']: for point in series['data']:
point['y'] = (point['y']*100) / totals[point['x']] point['y'] = (point['y']*100) / totals[point['x']]
# Sort the points # Sort the points
for package in data: for series in data:
package['data'] = sorted( package['data'], key=lambda x:x['x'] ) series['data'] = sorted( series['data'], key=lambda x:x['x'] )
# Strip the latest month's incomplete analytics # Strip the latest month's incomplete analytics
package['data'] = package['data'][:-1] series['data'] = series['data'][:-1]
return data return data
   
   
def _get_top_publishers(limit=20): def _get_top_publishers(limit=20):
''' '''
Returns a list of the top 20 publishers by dataset visits. Returns a list of the top 20 publishers by dataset visits.
(The number to show can be varied with 'limit') (The number to show can be varied with 'limit')
''' '''
month = c.month or 'All' month = c.month or 'All'
connection = model.Session.connection() connection = model.Session.connection()
q = """ q = """
select department_id, sum(pageviews::int) views, sum(visits::int) visits select department_id, sum(pageviews::int) views, sum(visits::int) visits
from ga_url from ga_url
where department_id <> '' where department_id <> ''
and package_id <> '' and package_id <> ''
and url like '/dataset/%%' and url like '/dataset/%%'
and period_name=%s and period_name=%s
group by department_id order by views desc group by department_id order by views desc
""" """
if limit: if limit:
q = q + " limit %s;" % (limit) q = q + " limit %s;" % (limit)
   
top_publishers = [] top_publishers = []
res = connection.execute(q, month) res = connection.execute(q, month)
department_ids = [] department_ids = []
for row in res: for row in res:
g = model.Group.get(row[0]) g = model.Group.get(row[0])
if g: if g:
department_ids.append(row[0]) department_ids.append(row[0])
top_publishers.append((g, row[1], row[2])) top_publishers.append((g, row[1], row[2]))
   
graph = {} graph = []
if limit is not None: if limit is not None:
# Query for a history graph of these publishers # Query for a history graph of these publishers
q = model.Session.query( q = model.Session.query(
GA_Url.department_id, GA_Url.department_id,
GA_Url.period_name, GA_Url.period_name,
func.sum(cast(GA_Url.pageviews,sqlalchemy.types.INT)))\ func.sum(cast(GA_Url.pageviews,sqlalchemy.types.INT)))\
.filter( GA_Url.department_id.in_(department_ids) )\ .filter( GA_Url.department_id.in_(department_ids) )\
.filter( GA_Url.period_name!='All' )\ .filter( GA_Url.period_name!='All' )\
.filter( GA_Url.url.like('/dataset/%') )\ .filter( GA_Url.url.like('/dataset/%') )\
.filter( GA_Url.package_id!='' )\ .filter( GA_Url.package_id!='' )\
.group_by( GA_Url.department_id, GA_Url.period_name ) .group_by( GA_Url.department_id, GA_Url.period_name )
  graph_dict = {}
for dept_id,period_name,views in q: for dept_id,period_name,views in q:
graph[dept_id] = graph.get( dept_id, { graph_dict[dept_id] = graph_dict.get( dept_id, {
'name' : model.Group.get(dept_id).title, 'name' : model.Group.get(dept_id).title,
'data' : [] 'data' : []
}) })
graph[dept_id]['data'].append({ graph_dict[dept_id]['data'].append({
'x': _get_unix_epoch(period_name), 'x': _get_unix_epoch(period_name),
'y': views 'y': views
}) })
  # Sort dict into ordered list
  for id in department_ids:
  graph.append( graph_dict[id] )
return top_publishers, graph return top_publishers, graph
   
   
def _get_publishers(): def _get_publishers():
''' '''
Returns a list of all publishers. Each item is a tuple: Returns a list of all publishers. Each item is a tuple:
(name, title) (name, title)
''' '''
publishers = [] publishers = []
for pub in model.Session.query(model.Group).\ for pub in model.Session.query(model.Group).\
filter(model.Group.type=='publisher').\ filter(model.Group.type=='publisher').\
filter(model.Group.state=='active').\ filter(model.Group.state=='active').\
order_by(model.Group.name): order_by(model.Group.name):
publishers.append((pub.name, pub.title)) publishers.append((pub.name, pub.title))
return publishers return publishers
   
def _percent(num, total): def _percent(num, total):
p = 100 * float(num)/float(total) p = 100 * float(num)/float(total)
return "%.2f%%" % round(p, 2) return "%.2f%%" % round(p, 2)
   
.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;
  /* Hack to hide the momentary flash of text
  * before sparklines are fully rendered */
  font-size: 1px;
  color: transparent;
  overflow: hidden;
} }
.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 {
background: transparent; background: transparent;
width: 100%; width: 100%;
  padding-top: 4px;
} }
.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;
} }
  .rickshaw_legend .instructions {
  color: #000;
  margin-bottom: 6px;
  }
   
  .rickshaw_legend .line .action {
  display: none;
  }
  .rickshaw_legend .line .swatch {
  display: block;
  float: left;
  }
  .rickshaw_legend .line .label {
  display: block;
  white-space: normal;
  float: left;
  width: 200px;
  }
  .rickshaw_legend .line .label:hover {
  text-decoration: underline;
  }
   
  .ga-reports-table .td-numeric {
  text-align: center;
  }
   
   
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 graphLegends = $('#graph-legend-container');
   
  if (!Modernizr.svg) {
  $("#chart_"+css_name)
  .html( '<div class="alert">Your browser does not support vector graphics. No graphs can be rendered.</div>')
  .closest('.rickshaw_chart_container').css('height',50);
  var myLegend = $('<div id="legend_'+css_name+'"/>')
  .html('(Graph cannot be rendered)')
  .appendTo(graphLegends);
  return;
  }
var myLegend = $('<div id="legend_'+css_name+'"/>').appendTo(graphLegends); 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();
}); });
  // Rickshaw renders the legend in reverse order...
  data.reverse();
   
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 shelving = new Rickshaw.Graph.Behavior.Series.Toggle( {
graph: graph, graph: graph,
formatter: function(series, x, y) { legend: legend
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';  
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';  
var content = swatch + series.name + ": " + parseInt(y) + '<br>' + date;  
return content;  
}  
} ); } );
  myLegend.prepend('<div class="instructions">Click on a series below to isolate its graph:</div>');
graph.render(); graph.render();
}; };
   
CKAN.GA_Reports.bind_sparklines = function() { CKAN.GA_Reports.bind_sparklines = function() {
/* /*
* Bind to the 'totals' tab being on screen, when the * Bind to the 'totals' tab being on screen, when the
* Sparkline graphs should be drawn. * Sparkline graphs should be drawn.
* Note that they cannot be drawn sooner. * Note that they cannot be drawn sooner.
*/ */
  var created = false;
$('a[href="#totals"]').on( $('a[href="#totals"]').on(
'shown', 'shown',
function() { function() {
var sparkOptions = { if (!created) {
enableTagOptions: true, var sparkOptions = {
type: 'line', enableTagOptions: true,
width: 100, type: 'line',
height: 26, width: 100,
chartRangeMin: 0, height: 26,
spotColor: '', chartRangeMin: 0,
maxSpotColor: '', spotColor: '',
minSpotColor: '', maxSpotColor: '',
highlightSpotColor: '000000', minSpotColor: '',
lineColor: '3F8E6D', highlightSpotColor: '#000000',
fillColor: 'B7E66B' lineColor: '#3F8E6D',
}; fillColor: '#B7E66B'
$('.sparkline').sparkline('html',sparkOptions); };
} $('.sparkline').sparkline('html',sparkOptions);
  created = true;
  }
  $.sparkline_display_visible();
  }
); );
}; };
   
CKAN.GA_Reports.bind_sidebar = function() { CKAN.GA_Reports.bind_sidebar = function() {
/* /*
* Bind to changes in the tab behaviour: * Bind to changes in the tab behaviour:
* Show the correct rickshaw graph in the sidebar. * Show the correct rickshaw graph in the sidebar.
* Not to be called before all graphs load. * Not to be called before all graphs load.
*/ */
$('a[data-toggle="hashchange"]').on( $('a[data-toggle="hashtab"]').on(
'shown', 'shown',
function(e) { function(e) {
var href = $(e.target).attr('href'); var href = $(e.target).attr('href');
var pane = $(href); var pane = $(href);
if (!pane.length) { console.err('bad href',href); return; } if (!pane.length) { console.err('bad href',href); return; }
var legend_name = "none"; var legend_name = "none";
var graph = pane.find('.rickshaw_chart'); var graph = pane.find('.rickshaw_chart');
if (graph.length) { if (graph.length) {
legend_name = graph.attr('id').replace('chart_',''); legend_name = graph.attr('id').replace('chart_','');
} }
legend_name = '#legend_'+legend_name; legend_name = '#legend_'+legend_name;
$('#graph-legend-container > *').hide(); $('#graph-legend-container > *').hide();
  $('#graph-legend-container .instructions').show();
$(legend_name).show(); $(legend_name).show();
} }
); );
  /* The first tab might already have been shown */
  $('li.active > a[data-toggle="hashtab"]').trigger('shown');
}; };
   
/* CKAN.GA_Reports.bind_month_selector = function() {
* Custom bootstrap plugin for handling data-toggle="hashchange". var handler = function(e) {
* Behaves like data-toggle="tab" but I respond to the hashchange. var target = $(e.delegateTarget);
* Page state is memo-ized in the URL this way. Why doesn't Bootstrap do this? var form = target.closest('form');
*/ var url = form.attr('action')+'?month='+target.val()+window.location.hash;
$(function() { window.location = url;
var mapping = {}; };
$('a[data-toggle="hashchange"]').each( var selectors = $('select[name="month"]');
function(i,link) { assert(selectors.length>0);
link = $(link); selectors.bind('change', handler);
mapping[link.attr('href')] = link; };
}  
);  
$(window).hashchange(function() {  
var link = mapping[window.location.hash];  
if (link) { link.tab('show'); }  
});  
});  
   
  /*
  * Collection of shims to allow d3 and Rickshaw to load, error-free
  * (but ultimately unusable) on Internet Explorer 7. The browser's
  * API lacks several crucial functions which these libraries depend
  * upon to load; we try to hide these errors from the user.
  *
  * With thanks to Array functions from:
  * http://stackoverflow.com/questions/2790001/fixing-javascript-array-functions-in-internet-explorer-indexof-foreach-etc
  *
  * Use (Modernizr.svg==true) to detect whether it's okay to draw a graph.
  */
  'use strict';
 
  window.Element = window.Element || {'prototype': {}};
  window.CSSStyleDeclaration = window.CSSStyleDeclaration || {'prototype':{}};
 
  // Add ECMA262-5 method binding if not supported natively
  //
  if (!('bind' in Function.prototype)) {
  Function.prototype.bind= function(owner) {
  var that= this;
  if (arguments.length<=1) {
  return function() {
  return that.apply(owner, arguments);
  };
  } else {
  var args= Array.prototype.slice.call(arguments, 1);
  return function() {
  return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
  };
  }
  };
  }
 
  // Add ECMA262-5 string trim if not supported natively
  //
  if (!('trim' in String.prototype)) {
  String.prototype.trim= function() {
  return this.replace(/^\s+/, '').replace(/\s+$/, '');
  };
  }
 
  // Add ECMA262-5 Array methods if not supported natively
  //
  if (!('indexOf' in Array.prototype)) {
  Array.prototype.indexOf= function(find, i /*opt*/) {
  if (i===undefined) i= 0;
  if (i<0) i+= this.length;
  if (i<0) i= 0;
  for (var n= this.length; i<n; i++)
  if (i in this && this[i]===find)
  return i;
  return -1;
  };
  }
  if (!('lastIndexOf' in Array.prototype)) {
  Array.prototype.lastIndexOf= function(find, i /*opt*/) {
  if (i===undefined) i= this.length-1;
  if (i<0) i+= this.length;
  if (i>this.length-1) i= this.length-1;
  for (i++; i-->0;) /* i++ because from-argument is sadly inclusive */
  if (i in this && this[i]===find)
  return i;
  return -1;
  };
  }
  if (!('forEach' in Array.prototype)) {
  Array.prototype.forEach= function(action, that /*opt*/) {
  for (var i= 0, n= this.length; i<n; i++)
  if (i in this)
  action.call(that, this[i], i, this);
  };
  }
  if (!('map' in Array.prototype)) {
  Array.prototype.map= function(mapper, that /*opt*/) {
  var other= new Array(this.length);
  for (var i= 0, n= this.length; i<n; i++)
  if (i in this)
  other[i]= mapper.call(that, this[i], i, this);
  return other;
  };
  }
  if (!('filter' in Array.prototype)) {
  Array.prototype.filter= function(filter, that /*opt*/) {
  var other= [], v;
  for (var i=0, n= this.length; i<n; i++)
  if (i in this && filter.call(that, v= this[i], i, this))
  other.push(v);
  return other;
  };
  }
  if (!('every' in Array.prototype)) {
  Array.prototype.every= function(tester, that /*opt*/) {
  for (var i= 0, n= this.length; i<n; i++)
  if (i in this && !tester.call(that, this[i], i, this))
  return false;
  return true;
  };
  }
  if (!('some' in Array.prototype)) {
  Array.prototype.some= function(tester, that /*opt*/) {
  for (var i= 0, n= this.length; i<n; i++)
  if (i in this && tester.call(that, this[i], i, this))
  return true;
  return false;
  };
  }
 
 
var Rickshaw={namespace:function(a,b){var c=a.split("."),d=Rickshaw;for(var e=1,f=c.length;e<f;e++)currentPart=c[e],d[currentPart]=d[currentPart]||{},d=d[currentPart];return d},keys:function(a){var b=[];for(var c in a)b.push(c);return b},extend:function(a,b){for(var c in b)a[c]=b[c];return a}};if(typeof module!="undefined"&&module.exports){var d3=require("d3");module.exports=Rickshaw}(function(a){function j(a){return b.call(a)===i}function k(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function l(a){if(m(a)!==h)throw new TypeError;var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b}function m(a){switch(a){case null:return c;case void 0:return d}var b=typeof a;switch(b){case"boolean":return e;case"number":return f;case"string":return g}return h}function n(a){return typeof a=="undefined"}function p(a){var b=a.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,"").replace(/\s+/g,"").split(",");return b.length==1&&!b[0]?[]:b}function q(a,b){var c=a;return function(){var a=r([t(c,this)],arguments);return b.apply(this,a)}}function r(a,b){var c=a.length,d=b.length;while(d--)a[c+d]=b[d];return a}function s(a,b){return a=o.call(a,0),r(a,b)}function t(a,b){if(arguments.length<2&&n(arguments[0]))return this;var c=a,d=o.call(arguments,2);return function(){var a=s(d,arguments);return c.apply(b,a)}}var b=Object.prototype.toString,c="Null",d="Undefined",e="Boolean",f="Number",g="String",h="Object",i="[object Function]",o=Array.prototype.slice,u=function(){},v=function(){function b(){}function c(){function d(){this.initialize.apply(this,arguments)}var a=null,c=[].slice.apply(arguments);j(c[0])&&(a=c.shift()),k(d,v.Methods),d.superclass=a,d.subclasses=[];if(a){b.prototype=a.prototype,d.prototype=new b;try{a.subclasses.push(d)}catch(e){}}for(var f=0,g=c.length;f<g;f++)d.addMethods(c[f]);return d.prototype.initialize||(d.prototype.initialize=u),d.prototype.constructor=d,d}function d(b){var c=this.superclass&&this.superclass.prototype,d=l(b);a&&(b.toString!=Object.prototype.toString&&d.push("toString"),b.valueOf!=Object.prototype.valueOf&&d.push("valueOf"));for(var e=0,f=d.length;e<f;e++){var g=d[e],h=b[g];if(c&&j(h)&&p(h)[0]=="$super"){var i=h;h=q(function(a){return function(){return c[a].apply(this,arguments)}}(g),i),h.valueOf=t(i.valueOf,i),h.toString=t(i.toString,i)}this.prototype[g]=h}return this}var a=function(){for(var a in{toString:1})if(a==="toString")return!1;return!0}();return{create:c,Methods:{addMethods:d}}}();a.exports?a.exports.Class=v:a.Class=v})(Rickshaw),Rickshaw.namespace("Rickshaw.Compat.ClassList"),Rickshaw.Compat.ClassList=function(){typeof document!="undefined"&&!("classList"in document.createElement("a"))&&function(a){"use strict";var b="classList",c="prototype",d=(a.HTMLElement||a.Element)[c],e=Object,f=String[c].trim||function(){return this.replace(/^\s+|\s+$/g,"")},g=Array[c].indexOf||function(a){var b=0,c=this.length;for(;b<c;b++)if(b in this&&this[b]===a)return b;return-1},h=function(a,b){this.name=a,this.code=DOMException[a],this.message=b},i=function(a,b){if(b==="")throw new h("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(b))throw new h("INVALID_CHARACTER_ERR","String contains an invalid character");return g.call(a,b)},j=function(a){var b=f.call(a.className),c=b?b.split(/\s+/):[],d=0,e=c.length;for(;d<e;d++)this.push(c[d]);this._updateClassName=function(){a.className=this.toString()}},k=j[c]=[],l=function(){return new j(this)};h[c]=Error[c],k.item=function(a){return this[a]||null},k.contains=function(a){return a+="",i(this,a)!==-1},k.add=function(a){a+="",i(this,a)===-1&&(this.push(a),this._updateClassName())},k.remove=function(a){a+="";var b=i(this,a);b!==-1&&(this.splice(b,1),this._updateClassName())},k.toggle=function(a){a+="",i(this,a)===-1?this.add(a):this.remove(a)},k.toString=function(){return this.join(" ")};if(e.defineProperty){var m={get:l,enumerable:!0,configurable:!0};try{e.defineProperty(d,b,m)}catch(n){n.number===-2146823252&&(m.enumerable=!1,e.defineProperty(d,b,m))}}else e[c].__defineGetter__&&d.__defineGetter__(b,l)}(window)},(typeof RICKSHAW_NO_COMPAT!="undefined"&&!RICKSHAW_NO_COMPAT||typeof RICKSHAW_NO_COMPAT=="undefined")&&new Rickshaw.Compat.ClassList,Rickshaw.namespace("Rickshaw.Graph"),Rickshaw.Graph=function(a){this.element=a.element,this.series=a.series,this.defaults={interpolation:"cardinal",offset:"zero",min:undefined,max:undefined},Rickshaw.keys(this.defaults).forEach(function(b){this[b]=a[b]||this.defaults[b]},this),this.window={},this.updateCallbacks=[];var b=this;this.initialize=function(a){this.validateSeries(a.series),this.series.active=function(){return b.series.filter(function(a){return!a.disabled})},this.setSize({width:a.width,height:a.height}),this.element.classList.add("rickshaw_graph"),this.vis=d3.select(this.element).append("svg:svg").attr("width",this.width).attr("height",this.height);var c=[Rickshaw.Graph.Renderer.Stack,Rickshaw.Graph.Renderer.Line,Rickshaw.Graph.Renderer.Bar,Rickshaw.Graph.Renderer.Area,Rickshaw.Graph.Renderer.ScatterPlot];c.forEach(function(a){if(!a)return;b.registerRenderer(new a({graph:b}))}),this.setRenderer(a.renderer||"stack",a),this.discoverRange()},this.validateSeries=function(a){if(!(a instanceof Array||a instanceof Rickshaw.Series)){var b=Object.prototype.toString.apply(a);throw"series is not an array: "+b}var c;a.forEach(function(a){if(!(a instanceof Object))throw"series element is not an object: "+a;if(!a.data)throw"series has no data: "+JSON.stringify(a);if(!(a.data instanceof Array))throw"series data is not an array: "+JSON.stringify(a.data);c=c||a.data.length;if(c&&a.data.length!=c)throw"series cannot have differing numbers of points: "+c+" vs "+a.data.length+"; see Rickshaw.Series.zeroFill()";var b=typeof a.data[0].x,d=typeof a.data[0].y;if(b!="number"||d!="number")throw"x and y properties of points should be numbers instead of "+b+" and "+d})},this.dataDomain=function(){var a=this.series[0].data;return[a[0].x,a.slice(-1).shift().x]},this.discoverRange=function(){var a=this.renderer.domain();this.x=d3.scale.linear().domain(a.x).range([0,this.width]),this.y=d3.scale.linear().domain(a.y).range([this.height,0]),this.y.magnitude=d3.scale.linear().domain([a.y[0]-a.y[0],a.y[1]-a.y[0]]).range([0,this.height])},this.render=function(){var a=this.stackData();this.discoverRange(),this.renderer.render(),this.updateCallbacks.forEach(function(a){a()})},this.update=this.render,this.stackData=function(){var a=this.series.active().map(function(a){return a.data}).map(function(a){return a.filter(function(a){return this._slice(a)},this)},this);this.stackData.hooks.data.forEach(function(c){a=c.f.apply(b,[a])});var c=d3.layout.stack();c.offset(b.offset);var d=c(a);this.stackData.hooks.after.forEach(function(c){d=c.f.apply(b,[a])});var e=0;return this.series.forEach(function(a){if(a.disabled)return;a.stack=d[e++]}),this.stackedData=d,d},this.stackData.hooks={data:[],after:[]},this._slice=function(a){if(this.window.xMin||this.window.xMax){var b=!0;return this.window.xMin&&a.x<this.window.xMin&&(b=!1),this.window.xMax&&a.x>this.window.xMax&&(b=!1),b}return!0},this.onUpdate=function(a){this.updateCallbacks.push(a)},this.registerRenderer=function(a){this._renderers=this._renderers||{},this._renderers[a.name]=a},this.configure=function(a){(a.width||a.height)&&this.setSize(a),Rickshaw.keys(this.defaults).forEach(function(b){this[b]=b in a?a[b]:b in this?this[b]:this.defaults[b]},this),this.setRenderer(a.renderer||this.renderer.name,a)},this.setRenderer=function(a,b){if(!this._renderers[a])throw"couldn't find renderer "+a;this.renderer=this._renderers[a],typeof b=="object"&&this.renderer.configure(b)},this.setSize=function(a){a=a||{};if(typeof window!==undefined)var b=window.getComputedStyle(this.element,null),c=parseInt(b.getPropertyValue("width")),d=parseInt(b.getPropertyValue("height"));this.width=a.width||c||400,this.height=a.height||d||250,this.vis&&this.vis.attr("width",this.width).attr("height",this.height)},this.initialize(a)},Rickshaw.namespace("Rickshaw.Fixtures.Color"),Rickshaw.Fixtures.Color=function(){this.schemes={},this.schemes.spectrum14=["#ecb796","#dc8f70","#b2a470","#92875a","#716c49","#d2ed82","#bbe468","#a1d05d","#e7cbe6","#d8aad6","#a888c2","#9dc2d3","#649eb9","#387aa3"].reverse(),this.schemes.spectrum2000=["#57306f","#514c76","#646583","#738394","#6b9c7d","#84b665","#a7ca50","#bfe746","#e2f528","#fff726","#ecdd00","#d4b11d","#de8800","#de4800","#c91515","#9a0000","#7b0429","#580839","#31082b"],this.schemes.spectrum2001=["#2f243f","#3c2c55","#4a3768","#565270","#6b6b7c","#72957f","#86ad6e","#a1bc5e","#b8d954","#d3e04e","#ccad2a","#cc8412","#c1521d","#ad3821","#8a1010","#681717","#531e1e","#3d1818","#320a1b"],this.schemes.classic9=["#423d4f","#4a6860","#848f39","#a2b73c","#ddcb53","#c5a32f","#7d5836","#963b20","#7c2626","#491d37","#2f254a"].reverse(),this.schemes.httpStatus={503:"#ea5029",502:"#d23f14",500:"#bf3613",410:"#efacea",409:"#e291dc",403:"#f457e8",408:"#e121d2",401:"#b92dae",405:"#f47ceb",404:"#a82a9f",400:"#b263c6",301:"#6fa024",302:"#87c32b",307:"#a0d84c",304:"#28b55c",200:"#1a4f74",206:"#27839f",201:"#52adc9",202:"#7c979f",203:"#a5b8bd",204:"#c1cdd1"},this.schemes.colorwheel=["#b5b6a9","#858772","#785f43","#96557e","#4682b4","#65b9ac","#73c03a","#cb513a"].reverse(),this.schemes.cool=["#5e9d2f","#73c03a","#4682b4","#7bc3b8","#a9884e","#c1b266","#a47493","#c09fb5"],this.schemes.munin=["#00cc00","#0066b3","#ff8000","#ffcc00","#330099","#990099","#ccff00","#ff0000","#808080","#008f00","#00487d","#b35a00","#b38f00","#6b006b","#8fb300","#b30000","#bebebe","#80ff80","#80c9ff","#ffc080","#ffe680","#aa80ff","#ee00cc","#ff8080","#666600","#ffbfff","#00ffcc","#cc6699","#999900"]},Rickshaw.namespace("Rickshaw.Fixtures.RandomData"),Rickshaw.Fixtures.RandomData=function(a){var b;a=a||1;var c=200,d=Math.floor((new Date).getTime()/1e3);this.addData=function(b){var e=Math.random()*100+15+c,f=b[0].length,g=1;b.forEach(function(b){var c=Math.random()*20,h=e/25+g++ +(Math.cos(f*g*11/960)+2)*15+(Math.cos(f/7)+2)*7+(Math.cos(f/17)+2)*1;b.push({x:f*a+d,y:h+c})}),c=e*.85}},Rickshaw.namespace("Rickshaw.Fixtures.Time"),Rickshaw.Fixtures.Time=function(){var a=(new Date).getTimezoneOffset()*60,b=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],this.units=[{name:"decade",seconds:315576e3,formatter:function(a){return parseInt(a.getUTCFullYear()/10)*10}},{name:"year",seconds:31557600,formatter:function(a){return a.getUTCFullYear()}},{name:"month",seconds:2635200,formatter:function(a){return b.months[a.getUTCMonth()]}},{name:"week",seconds:604800,formatter:function(a){return b.formatDate(a)}},{name:"day",seconds:86400,formatter:function(a){return a.getUTCDate()}},{name:"6 hour",seconds:21600,formatter:function(a){return b.formatTime(a)}},{name:"hour",seconds:3600,formatter:function(a){return b.formatTime(a)}},{name:"15 minute",seconds:900,formatter:function(a){return b.formatTime(a)}},{name:"minute",seconds:60,formatter:function(a){return a.getUTCMinutes()}},{name:"15 second",seconds:15,formatter:function(a){return a.getUTCSeconds()+"s"}},{name:"second",seconds:1,formatter:function(a){return a.getUTCSeconds()+"s"}}],this.unit=function(a){return this.units.filter(function(b){return a==b.name}).shift()},this.formatDate=function(a){return a.toUTCString().match(/, (\w+ \w+ \w+)/)[1]},this.formatTime=function(a){return a.toUTCString().match(/(\d+:\d+):/)[1]},this.ceil=function(a,b){if(b.name=="month"){var c=new Date((a+b.seconds-1)*1e3),d=new Date(0);return d.setUTCFullYear(c.getUTCFullYear()),d.setUTCMonth(c.getUTCMonth()),d.setUTCDate(1),d.setUTCHours(0),d.setUTCMinutes(0),d.setUTCSeconds(0),d.setUTCMilliseconds(0),d.getTime()/1e3}if(b.name=="year"){var c=new Date((a+b.seconds-1)*1e3),d=new Date(0);return d.setUTCFullYear(c.getUTCFullYear()),d.setUTCMonth(0),d.setUTCDate(1),d.setUTCHours(0),d.setUTCMinutes(0),d.setUTCSeconds(0),d.setUTCMilliseconds(0),d.getTime()/1e3}return Math.ceil(a/b.seconds)*b.seconds}},Rickshaw.namespace("Rickshaw.Fixtures.Number"),Rickshaw.Fixtures.Number.formatKMBT=function(a){return a>=1e12?a/1e12+"T":a>=1e9?a/1e9+"B":a>=1e6?a/1e6+"M":a>=1e3?a/1e3+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.Fixtures.Number.formatBase1024KMGTP=function(a){return a>=0x4000000000000?a/0x4000000000000+"P":a>=1099511627776?a/1099511627776+"T":a>=1073741824?a/1073741824+"G":a>=1048576?a/1048576+"M":a>=1024?a/1024+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.namespace("Rickshaw.Color.Palette"),Rickshaw.Color.Palette=function(a){var b=new Rickshaw.Fixtures.Color;a=a||{},this.schemes={},this.scheme=b.schemes[a.scheme]||a.scheme||b.schemes.colorwheel,this.runningIndex=0,this.generatorIndex=0;if(a.interpolatedStopCount){var c=this.scheme.length-1,d,e,f=[];for(d=0;d<c;d++){f.push(this.scheme[d]);var g=d3.interpolateHsl(this.scheme[d],this.scheme[d+1]);for(e=1;e<a.interpolatedStopCount;e++)f.push(g(1/a.interpolatedStopCount*e))}f.push(this.scheme[this.scheme.length-1]),this.scheme=f}this.rotateCount=this.scheme.length,this.color=function(a){return this.scheme[a]||this.scheme[this.runningIndex++]||this.interpolateColor()||"#808080"},this.interpolateColor=function(){if(!Array.isArray(this.scheme))return;var a;return this.generatorIndex==this.rotateCount*2-1?(a=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[0])(.5),this.generatorIndex=0,this.rotateCount*=2):(a=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[this.generatorIndex+1])(.5),this.generatorIndex++),this.scheme.push(a),a}},Rickshaw.namespace("Rickshaw.Graph.Ajax"),Rickshaw.Graph.Ajax=Rickshaw.Class.create({initialize:function(a){this.dataURL=a.dataURL,this.onData=a.onData||function(a){return a},this.onComplete=a.onComplete||function(){},this.onError=a.onError||function(){},this.args=a,this.request()},request:function(){$.ajax({url:this.dataURL,dataType:"json",success:this.success.bind(this),error:this.error.bind(this)})},error:function(){console.log("error loading dataURL: "+this.dataURL),this.onError(this)},success:function(a,b){a=this.onData(a),this.args.series=this._splice({data:a,series:this.args.series}),this.graph=new Rickshaw.Graph(this.args),this.graph.render(),this.onComplete(this)},_splice:function(a){var b=a.data,c=a.series;return a.series?(c.forEach(function(a){var c=a.key||a.name;if(!c)throw"series needs a key or a name";b.forEach(function(b){var d=b.key||b.name;if(!d)throw"data needs a key or a name";if(c==d){var e=["color","name","data"];e.forEach(function(c){a[c]=a[c]||b[c]})}})}),c):b}}),Rickshaw.namespace("Rickshaw.Graph.Annotate"),Rickshaw.Graph.Annotate=function(a){var b=this.graph=a.graph;this.elements={timeline:a.element};var c=this;this.data={},this.elements.timeline.classList.add("rickshaw_annotation_timeline"),this.add=function(a,b,d){c.data[a]=c.data[a]||{boxes:[]},c.data[a].boxes.push({content:b,end:d})},this.update=function(){Rickshaw.keys(c.data).forEach(function(a){var b=c.data[a],d=c.graph.x(a);if(d<0||d>c.graph.x.range()[1]){b.element&&(b.line.classList.add("offscreen"),b.element.style.display="none"),b.boxes.forEach(function(a){a.rangeElement&&a.rangeElement.classList.add("offscreen")});return}if(!b.element){var e=b.element=document.createElement("div");e.classList.add("annotation"),this.elements.timeline.appendChild(e),e.addEventListener("click",function(a){e.classList.toggle("active"),b.line.classList.toggle("active"),b.boxes.forEach(function(a){a.rangeElement&&a.rangeElement.classList.toggle("active")})},!1)}b.element.style.left=d+"px",b.element.style.display="block",b.boxes.forEach(function(a){var e=a.element;e||(e=a.element=document.createElement("div"),e.classList.add("content"),e.innerHTML=a.content,b.element.appendChild(e),b.line=document.createElement("div"),b.line.classList.add("annotation_line"),c.graph.element.appendChild(b.line),a.end&&(a.rangeElement=document.createElement("div"),a.rangeElement.classList.add("annotation_range"),c.graph.element.appendChild(a.rangeElement)));if(a.end){var f=d,g=Math.min(c.graph.x(a.end),c.graph.x.range()[1]);f>g&&(g=d,f=Math.max(c.graph.x(a.end),c.graph.x.range()[0]));var h=g-f;a.rangeElement.style.left=f+"px",a.rangeElement.style.width=h+"px",a.rangeElement.classList.remove("offscreen")}b.line.classList.remove("offscreen"),b.line.style.left=d+"px"})},this)},this.graph.onUpdate(function(){c.update()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Time"),Rickshaw.Graph.Axis.Time=function(a){var b=this;this.graph=a.graph,this.elements=[],this.ticksTreatment=a.ticksTreatment||"plain",this.fixedTimeUnit=a.timeUnit;var c=new Rickshaw.Fixtures.Time;this.appropriateTimeUnit=function(){var a,b=c.units,d=this.graph.x.domain(),e=d[1]-d[0];return b.forEach(function(b){Math.floor(e/b.seconds)>=2&&(a=a||b)}),a||c.units[c.units.length-1]},this.tickOffsets=function(){var a=this.graph.x.domain(),b=this.fixedTimeUnit||this.appropriateTimeUnit(),d=Math.ceil((a[1]-a[0])/b.seconds),e=a[0],f=[];for(var g=0;g<d;g++)tickValue=c.ceil(e,b),e=tickValue+b.seconds/2,f.push({value:tickValue,unit:b});return f},this.render=function(){this.elements.forEach(function(a){a.parentNode.removeChild(a)}),this.elements=[];var a=this.tickOffsets();a.forEach(function(a){if(b.graph.x(a.value)>b.graph.x.range()[1])return;var c=document.createElement("div");c.style.left=b.graph.x(a.value)+"px",c.classList.add("x_tick"),c.classList.add(b.ticksTreatment);var d=document.createElement("div");d.classList.add("title"),d.innerHTML=a.unit.formatter(new Date(a.value*1e3)),c.appendChild(d),b.graph.element.appendChild(c),b.elements.push(c)})},this.graph.onUpdate(function(){b.render()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Y"),Rickshaw.Graph.Axis.Y=function(a){var b=this,c=.1;this.initialize=function(a){this.graph=a.graph,this.orientation=a.orientation||"right";var c=a.pixelsPerTick||75;this.ticks=a.ticks||Math.floor(this.graph.height/c),this.tickSize=a.tickSize||4,this.ticksTreatment=a.ticksTreatment||"plain",a.element?(this.element=a.element,this.vis=d3.select(a.element).append("svg:svg").attr("class","rickshaw_graph y_axis"),this.element=this.vis[0][0],this.element.style.position="relative",this.setSize({width:a.width,height:a.height})):this.vis=this.graph.vis,this.graph.onUpdate(function(){b.render()})},this.setSize=function(a){a=a||{};if(!this.element)return;if(typeof window!="undefined"){var b=window.getComputedStyle(this.element.parentNode,null),d=parseInt(b.getPropertyValue("width"));if(!a.auto)var e=parseInt(b.getPropertyValue("height"))}this.width=a.width||d||this.graph.width*c,this.height=a.height||e||this.graph.height,this.vis.attr("width",this.width).attr("height",this.height*(1+c));var f=this.height*c;this.element.style.top=-1*f+"px"},this.render=function(){this.graph.height!==this._renderHeight&&this.setSize({auto:!0});var b=d3.svg.axis().scale(this.graph.y).orient(this.orientation);b.tickFormat(a.tickFormat||function(a){return a});if(this.orientation=="left")var d=this.height*c,e="translate("+this.width+", "+d+")";this.element&&this.vis.selectAll("*").remove(),this.vis.append("svg:g").attr("class",["y_ticks",this.ticksTreatment].join(" ")).attr("transform",e).call(b.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));var f=(this.orientation=="right"?1:-1)*this.graph.width;this.graph.vis.append("svg:g").attr("class","y_grid").call(b.ticks(this.ticks).tickSubdivide(0).tickSize(f)),this._renderHeight=this.graph.height},this.initialize(a)},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Highlight"),Rickshaw.Graph.Behavior.Series.Highlight=function(a){this.graph=a.graph,this.legend=a.legend;var b=this,c={};this.addHighlightEvents=function(a){a.element.addEventListener("mouseover",function(d){b.legend.lines.forEach(function(b){if(a===b)return;c[b.series.name]=c[b.series.name]||b.series.color,b.series.color=d3.interpolateRgb(b.series.color,d3.rgb("#d8d8d8"))(.8).toString()}),b.graph.update()},!1),a.element.addEventListener("mouseout",function(a){b.legend.lines.forEach(function(a){c[a.series.name]&&(a.series.color=c[a.series.name])}),b.graph.update()},!1)},this.legend&&this.legend.lines.forEach(function(a){b.addHighlightEvents(a)})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Order"),Rickshaw.Graph.Behavior.Series.Order=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;$(function(){$(b.legend.list).sortable({containment:"parent",tolerance:"pointer",update:function(a,c){var d=[];$(b.legend.list).find("li").each(function(a,b){if(!b.series)return;d.push(b.series)});for(var e=b.graph.series.length-1;e>=0;e--)b.graph.series[e]=d.shift();b.graph.update()}}),$(b.legend.list).disableSelection()}),this.graph.onUpdate(function(){var a=window.getComputedStyle(b.legend.element).height;b.legend.element.style.height=a})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Toggle"),Rickshaw.Graph.Behavior.Series.Toggle=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;this.addAnchor=function(a){var c=document.createElement("a");c.innerHTML="&#10004;",c.classList.add("action"),a.element.insertBefore(c,a.element.firstChild),c.onclick=function(b){a.series.disabled?(a.series.enable(),a.element.classList.remove("disabled")):(a.series.disable(),a.element.classList.add("disabled"))};var d=a.element.getElementsByTagName("span")[0];d.onclick=function(c){var d=a.series.disabled;if(!d)for(var e=0;e<b.legend.lines.length;e++){var f=b.legend.lines[e];if(a.series!==f.series&&!f.series.disabled){d=!0;break}}d?(a.series.enable(),a.element.classList.remove("disabled"),b.legend.lines.forEach(function(b){a.series!==b.series&&(b.series.disable(),b.element.classList.add("disabled"))})):b.legend.lines.forEach(function(a){a.series.enable(),a.element.classList.remove("disabled")})}},this.legend&&($(this.legend.list).sortable({start:function(a,b){b.item.bind("no.onclick",function(a){a.preventDefault()})},stop:function(a,b){setTimeout(function(){b.item.unbind("no.onclick")},250)}}),this.legend.lines.forEach(function(a){b.addAnchor(a)})),this._addBehavior=function(){this.graph.series.forEach(function(a){a.disable=function(){if(b.graph.series.length<=1)throw"only one series left";a.disabled=!0,b.graph.update()},a.enable=function(){a.disabled=!1,b.graph.update()}})},this._addBehavior(),this.updateBehaviour=function(){this._addBehavior()}},Rickshaw.namespace("Rickshaw.Graph.HoverDetail"),Rickshaw.Graph.HoverDetail=Rickshaw.Class.create({initialize:function(a){var b=this.graph=a.graph;this.xFormatter=a.xFormatter||function(a){return(new Date(a*1e3)).toUTCString()},this.yFormatter=a.yFormatter||function(a){return a.toFixed(2)};var c=this.element=document.createElement("div");c.className="detail",this.visible=!0,b.element.appendChild(c),this.lastEvent=null,this._addListeners(),this.onShow=a.onShow,this.onHide=a.onHide,this.onRender=a.onRender,this.formatter=a.formatter||this.formatter},formatter:function(a,b,c,d,e,f){return a.name+":&nbsp;"+e},update:function(a){a=a||this.lastEvent;if(!a)return;this.lastEvent=a;if(!a.target.nodeName.match(/^(path|svg|rect)$/))return;var b=this.graph,c=a.offsetX||a.layerX,d=a.offsetY||a.layerY,e=b.x.invert(c),f=b.stackedData,g=f.slice(-1).shift(),h=d3.scale.linear().domain([g[0].x,g.slice(-1).shift().x]).range([0,g.length]),i=Math.floor(h(e)),j=Math.min(i||0,f[0].length-1);for(var k=i;k<f[0].length-1;){if(!f[0][k]||!f[0][k+1])break;if(f[0][k].x<=e&&f[0][k+1].x>e){j=k;break}f[0][k+1]<=e?k++:k--}var e=f[0][j].x,l=this.xFormatter(e),m=b.x(e),n=0,o=b.series.active().map(function(a){return{order:n++,series:a,name:a.name,value:a.stack[j]}}),p,q=function(a,b){return a.value.y0+a.value.y-(b.value.y0+b.value.y)},r=b.y.magnitude.invert(b.element.offsetHeight-d);o.sort(q).forEach(function(a){a.formattedYValue=this.yFormatter.constructor==Array?this.yFormatter[o.indexOf(a)](a.value.y):this.yFormatter(a.value.y),a.graphX=m,a.graphY=b.y(a.value.y0+a.value.y),r>a.value.y0&&r<a.value.y0+a.value.y&&!p&&(p=a,a.active=!0)},this),this.element.innerHTML="",this.element.style.left=b.x(e)+"px",this.visible&&this.render({detail:o,domainX:e,formattedXValue:l,mouseX:c,mouseY:d})},hide:function(){this.visible=!1,this.element.classList.add("inactive"),typeof this.onHide=="function"&&this.onHide()},show:function(){this.visible=!0,this.element.classList.remove("inactive"),typeof this.onShow=="function"&&this.onShow()},render:function(a){var b=a.detail,c=a.domainX,d=a.mouseX,e=a.mouseY,f=a.formattedXValue,g=document.createElement("div");g.className="x_label",g.innerHTML=f,this.element.appendChild(g),b.forEach(function(a){var b=document.createElement("div");b.className="item",b.innerHTML=this.formatter(a.series,c,a.value.y,f,a.formattedYValue,a),b.style.top=this.graph.y(a.value.y0+a.value.y)+"px",this.element.appendChild(b);var d=document.createElement("div");d.className="dot",d.style.top=b.style.top,d.style.borderColor=a.series.color,this.element.appendChild(d),a.active&&(b.className="item active",d.className="dot active")},this),this.show(),typeof this.onRender=="function"&&this.onRender(a)},_addListeners:function(){this.graph.element.addEventListener("mousemove",function(a){this.visible=!0,this.update(a)}.bind(this),!1),this.graph.onUpdate(function(){this.update()}.bind(this)),this.graph.element.addEventListener("mouseout",function(a){a.relatedTarget&&!(a.relatedTarget.compareDocumentPosition(this.graph.element)&Node.DOCUMENT_POSITION_CONTAINS)&&this.hide()}.bind(this),!1)}}),Rickshaw.namespace("Rickshaw.Graph.JSONP"),Rickshaw.Graph.JSONP=Rickshaw.Class.create(Rickshaw.Graph.Ajax,{request:function(){$.ajax({url:this.dataURL,dataType:"jsonp",success:this.success.bind(this),error:this.error.bind(this)})}}),Rickshaw.namespace("Rickshaw.Graph.Legend"),Rickshaw.Graph.Legend=function(a){var b=this.element=a.element,c=this.graph=a.graph,d=this;b.classList.add("rickshaw_legend");var e=this.list=document.createElement("ul");b.appendChild(e);var f=c.series.map(function(a){return a}).reverse();this.lines=[],this.addLine=function(a){var b=document.createElement("li");b.className="line";var c=document.createElement("div");c.className="swatch",c.style.backgroundColor=a.color,b.appendChild(c);var f=document.createElement("span");f.className="label",f.innerHTML=a.name,b.appendChild(f),e.appendChild(b),b.series=a,a.noLegend&&(b.style.display="none");var g={element:b,series:a};d.shelving&&(d.shelving.addAnchor(g),d.shelving.updateBehaviour()),d.highlighter&&d.highlighter.addHighlightEvents(g),d.lines.push(g)},f.forEach(function(a){d.addLine(a)}),c.onUpdate(function(){})},Rickshaw.namespace("Rickshaw.Graph.RangeSlider"),Rickshaw.Graph.RangeSlider=function(a){var b=this.element=a.element,c=this.graph=a.graph;$(function(){$(b).slider({range:!0,min:c.dataDomain()[0],max:c.dataDomain()[1],values:[c.dataDomain()[0],c.dataDomain()[1]],slide:function(a,b){c.window.xMin=b.values[0],c.window.xMax=b.values[1],c.update(),c.dataDomain()[0]==b.values[0]&&(c.window.xMin=undefined),c.dataDomain()[1]==b.values[1]&&(c.window.xMax=undefined)}})}),b[0].style.width=c.width+"px",c.onUpdate(function(){var a=$(b).slider("option","values");$(b).slider("option","min",c.dataDomain()[0]),$(b).slider("option","max",c.dataDomain()[1]),c.window.xMin==undefined&&(a[0]=c.dataDomain()[0]),c.window.xMax==undefined&&(a[1]=c.dataDomain()[1]),$(b).slider("option","values",a)})},Rickshaw.namespace("Rickshaw.Graph.Renderer"),Rickshaw.Graph.Renderer=Rickshaw.Class.create({initialize:function(a){this.graph=a.graph,this.tension=a.tension||this.tension,this.graph.unstacker=this.graph.unstacker||new Rickshaw.Graph.Unstacker({graph:this.graph}),this.configure(a)},seriesPathFactory:function(){},seriesStrokeFactory:function(){},defaults:function(){return{tension:.8,strokeWidth:2,unstack:!0,padding:{top:.01,right:0,bottom:.01,left:0},stroke:!1,fill:!1}},domain:function(){var a=[],b=this.graph.stackedData||this.graph.stackData(),c=this.unstack?b:[b.slice(-1).shift()];c.forEach(function(b){b.forEach(function(b){a.push(b.y+b.y0)})});var d=b[0][0].x,e=b[0][b[0].length-1].x;d-=(e-d)*this.padding.left,e+=(e-d)*this.padding.right;var f=this.graph.min==="auto"?d3.min(a):this.graph.min||0,g=this.graph.max||d3.max(a);if(this.graph.min==="auto"||f<0)f-=(g-f)*this.padding.bottom;return this.graph.max===undefined&&(g+=(g-f)*this.padding.top),{x:[d,e],y:[f,g]}},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=a.vis.selectAll("path").data(this.graph.stackedData).enter().append("svg:path").attr("d",this.seriesPathFactory()),c=0;a.series.forEach(function(a){if(a.disabled)return;a.path=b[0][c++],this._styleSeries(a)},this)},_styleSeries:function(a){var b=this.fill?a.color:"none",c=this.stroke?a.color:"none";a.path.setAttribute("fill",b),a.path.setAttribute("stroke",c),a.path.setAttribute("stroke-width",this.strokeWidth),a.path.setAttribute("class",a.className)},configure:function(a){a=a||{},Rickshaw.keys(this.defaults()).forEach(function(b){if(!a.hasOwnProperty(b)){this[b]=this[b]||this.graph[b]||this.defaults()[b];return}typeof this.defaults()[b]=="object"?Rickshaw.keys(this.defaults()[b]).forEach(function(c){this[b][c]=a[b][c]!==undefined?a[b][c]:this[b][c]!==undefined?this[b][c]:this.defaults()[b][c]},this):this[b]=a[b]!==undefined?a[b]:this[b]!==undefined?this[b]:this.graph[b]!==undefined?this.graph[b]:this.defaults()[b]},this)},setStrokeWidth:function(a){a!==undefined&&(this.strokeWidth=a)},setTension:function(a){a!==undefined&&(this.tension=a)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Line"),Rickshaw.Graph.Renderer.Line=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"line",defaults:function($super){return Rickshaw.extend($super(),{unstack:!0,fill:!1,stroke:!0})},seriesPathFactory:function(){var a=this.graph;return d3.svg.line().x(function(b){return a.x(b.x)}).y(function(b){return a.y(b.y)}).interpolate(this.graph.interpolation).tension(this.tension)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Stack"),Rickshaw.Graph.Renderer.Stack=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"stack",defaults:function($super){return Rickshaw.extend($super(),{fill:!0,stroke:!1,unstack:!1})},seriesPathFactory:function(){var a=this.graph;return d3.svg.area().x(function(b){return a.x(b.x)}).y0(function(b){return a.y(b.y0)}).y1(function(b){return a.y(b.y+b.y0)}).interpolate(this.graph.interpolation).tension(this.tension)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Bar"),Rickshaw.Graph.Renderer.Bar=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"bar",defaults:function($super){var a=Rickshaw.extend($super(),{gapSize:.05,unstack:!1});return delete a.tension,a},initialize:function($super,a){a=a||{},this.gapSize=a.gapSize||this.gapSize,$super(a)},domain:function($super){var a=$super(),b=this._frequentInterval();return a.x[1]+=parseInt(b.magnitude),a},barWidth:function(){var a=this.graph.stackedData||this.graph.stackData(),b=a.slice(-1).shift(),c=this._frequentInterval(),d=this.graph.x(b[0].x+c.magnitude*(1-this.gapSize));return d},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=this.barWidth(),c=0,d=a.series.filter(function(a){return!a.disabled}).length,e=this.unstack?b/d:b,f=function(b){var c=[1,0,0,b.y<0?-1:1,0,b.y<0?a.y.magnitude(Math.abs(b.y))*2:0];return"matrix("+c.join(",")+")"};a.series.forEach(function(b){if(b.disabled)return;var d=a.vis.selectAll("path").data(b.stack).enter().append("svg:rect").attr("x",function(b){return a.x(b.x)+c}).attr("y",function(b){return a.y(b.y0+Math.abs(b.y))*(b.y<0?-1:1)}).attr("width",e).attr("height",function(b){return a.y.magnitude(Math.abs(b.y))}).attr("transform",f);Array.prototype.forEach.call(d[0],function(a){a.setAttribute("fill",b.color)}),this.unstack&&(c+=e)},this)},_frequentInterval:function(){var a=this.graph.stackedData||this.graph.stackData(),b=a.slice(-1).shift(),c={};for(var d=0;d<b.length-1;d++){var e=b[d+1].x-b[d].x;c[e]=c[e]||0,c[e]++}var f={count:0};return Rickshaw.keys(c).forEach(function(a){f.count<c[a]&&(f={count:c[a],magnitude:a})}),this._frequentInterval=function(){return f},f}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Area"),Rickshaw.Graph.Renderer.Area=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"area",defaults:function($super){return Rickshaw.extend($super(),{unstack:!1,fill:!1,stroke:!1})},seriesPathFactory:function(){var a=this.graph;return d3.svg.area().x(function(b){return a.x(b.x)}).y0(function(b){return a.y(b.y0)}).y1(function(b){return a.y(b.y+b.y0)}).interpolate(a.interpolation).tension(this.tension)},seriesStrokeFactory:function(){var a=this.graph;return d3.svg.line().x(function(b){return a.x(b.x)}).y(function(b){return a.y(b.y+b.y0)}).interpolate(a.interpolation).tension(this.tension)},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=a.vis.selectAll("path").data(this.graph.stackedData).enter().insert("svg:g","g");b.append("svg:path").attr("d", var Rickshaw={namespace:function(a,b){var c=a.split("."),d=Rickshaw;for(var e=1,f=c.length;e<f;e++)currentPart=c[e],d[currentPart]=d[currentPart]||{},d=d[currentPart];return d},keys:function(a){var b=[];for(var c in a)b.push(c);return b},extend:function(a,b){for(var c in b)a[c]=b[c];return a}};if(typeof module!="undefined"&&module.exports){var d3=require("d3");module.exports=Rickshaw}(function(a){function j(a){return b.call(a)===i}function k(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function l(a){if(m(a)!==h)throw new TypeError;var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b}function m(a){switch(a){case null:return c;case void 0:return d}var b=typeof a;switch(b){case"boolean":return e;case"number":return f;case"string":return g}return h}function n(a){return typeof a=="undefined"}function p(a){var b=a.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,"").replace(/\s+/g,"").split(",");return b.length==1&&!b[0]?[]:b}function q(a,b){var c=a;return function(){var a=r([t(c,this)],arguments);return b.apply(this,a)}}function r(a,b){var c=a.length,d=b.length;while(d--)a[c+d]=b[d];return a}function s(a,b){return a=o.call(a,0),r(a,b)}function t(a,b){if(arguments.length<2&&n(arguments[0]))return this;var c=a,d=o.call(arguments,2);return function(){var a=s(d,arguments);return c.apply(b,a)}}var b=Object.prototype.toString,c="Null",d="Undefined",e="Boolean",f="Number",g="String",h="Object",i="[object Function]",o=Array.prototype.slice,u=function(){},v=function(){function b(){}function c(){function d(){this.initialize.apply(this,arguments)}var a=null,c=[].slice.apply(arguments);j(c[0])&&(a=c.shift()),k(d,v.Methods),d.superclass=a,d.subclasses=[];if(a){b.prototype=a.prototype,d.prototype=new b;try{a.subclasses.push(d)}catch(e){}}for(var f=0,g=c.length;f<g;f++)d.addMethods(c[f]);return d.prototype.initialize||(d.prototype.initialize=u),d.prototype.constructor=d,d}function d(b){var c=this.superclass&&this.superclass.prototype,d=l(b);a&&(b.toString!=Object.prototype.toString&&d.push("toString"),b.valueOf!=Object.prototype.valueOf&&d.push("valueOf"));for(var e=0,f=d.length;e<f;e++){var g=d[e],h=b[g];if(c&&j(h)&&p(h)[0]=="$super"){var i=h;h=q(function(a){return function(){return c[a].apply(this,arguments)}}(g),i),h.valueOf=t(i.valueOf,i),h.toString=t(i.toString,i)}this.prototype[g]=h}return this}var a=function(){for(var a in{toString:1})if(a==="toString")return!1;return!0}();return{create:c,Methods:{addMethods:d}}}();a.exports?a.exports.Class=v:a.Class=v})(Rickshaw),Rickshaw.namespace("Rickshaw.Compat.ClassList"),Rickshaw.Compat.ClassList=function(){typeof document!="undefined"&&!("classList"in document.createElement("a"))&&function(a){"use strict";var b="classList",c="prototype",d=(a.HTMLElement||a.Element)[c],e=Object,f=String[c].trim||function(){return this.replace(/^\s+|\s+$/g,"")},g=Array[c].indexOf||function(a){var b=0,c=this.length;for(;b<c;b++)if(b in this&&this[b]===a)return b;return-1},h=function(a,b){this.name=a,this.code=DOMException[a],this.message=b},i=function(a,b){if(b==="")throw new h("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(b))throw new h("INVALID_CHARACTER_ERR","String contains an invalid character");return g.call(a,b)},j=function(a){var b=f.call(a.className),c=b?b.split(/\s+/):[],d=0,e=c.length;for(;d<e;d++)this.push(c[d]);this._updateClassName=function(){a.className=this.toString()}},k=j[c]=[],l=function(){return new j(this)};h[c]=Error[c],k.item=function(a){return this[a]||null},k.contains=function(a){return a+="",i(this,a)!==-1},k.add=function(a){a+="",i(this,a)===-1&&(this.push(a),this._updateClassName())},k.remove=function(a){a+="";var b=i(this,a);b!==-1&&(this.splice(b,1),this._updateClassName())},k.toggle=function(a){a+="",i(this,a)===-1?this.add(a):this.remove(a)},k.toString=function(){return this.join(" ")};if(e.defineProperty){var m={get:l,enumerable:!0,configurable:!0};try{e.defineProperty(d,b,m)}catch(n){n.number===-2146823252&&(m.enumerable=!1,e.defineProperty(d,b,m))}}else e[c].__defineGetter__&&d.__defineGetter__(b,l)}(window)},(typeof RICKSHAW_NO_COMPAT!="undefined"&&!RICKSHAW_NO_COMPAT||typeof RICKSHAW_NO_COMPAT=="undefined")&&new Rickshaw.Compat.ClassList,Rickshaw.namespace("Rickshaw.Graph"),Rickshaw.Graph=function(a){this.element=a.element,this.series=a.series,this.defaults={interpolation:"cardinal",offset:"zero",min:undefined,max:undefined},Rickshaw.keys(this.defaults).forEach(function(b){this[b]=a[b]||this.defaults[b]},this),this.window={},this.updateCallbacks=[];var b=this;this.initialize=function(a){this.validateSeries(a.series),this.series.active=function(){return b.series.filter(function(a){return!a.disabled})},this.setSize({width:a.width,height:a.height}),this.element.classList.add("rickshaw_graph"),this.vis=d3.select(this.element).append("svg:svg").attr("width",this.width).attr("height",this.height);var c=[Rickshaw.Graph.Renderer.Stack,Rickshaw.Graph.Renderer.Line,Rickshaw.Graph.Renderer.Bar,Rickshaw.Graph.Renderer.Area,Rickshaw.Graph.Renderer.ScatterPlot];c.forEach(function(a){if(!a)return;b.registerRenderer(new a({graph:b}))}),this.setRenderer(a.renderer||"stack",a),this.discoverRange()},this.validateSeries=function(a){if(!(a instanceof Array||a instanceof Rickshaw.Series)){var b=Object.prototype.toString.apply(a);throw"series is not an array: "+b}var c;a.forEach(function(a){if(!(a instanceof Object))throw"series element is not an object: "+a;if(!a.data)throw"series has no data: "+JSON.stringify(a);if(!(a.data instanceof Array))throw"series data is not an array: "+JSON.stringify(a.data);c=c||a.data.length;if(c&&a.data.length!=c)throw"series cannot have differing numbers of points: "+c+" vs "+a.data.length+"; see Rickshaw.Series.zeroFill()";var b=typeof a.data[0].x,d=typeof a.data[0].y;if(b!="number"||d!="number")throw"x and y properties of points should be numbers instead of "+b+" and "+d})},this.dataDomain=function(){var a=this.series[0].data;return[a[0].x,a.slice(-1).shift().x]},this.discoverRange=function(){var a=this.renderer.domain();this.x=d3.scale.linear().domain(a.x).range([0,this.width]),this.y=d3.scale.linear().domain(a.y).range([this.height,0]),this.y.magnitude=d3.scale.linear().domain([a.y[0]-a.y[0],a.y[1]-a.y[0]]).range([0,this.height])},this.render=function(){var a=this.stackData();this.discoverRange(),this.renderer.render(),this.updateCallbacks.forEach(function(a){a()})},this.update=this.render,this.stackData=function(){var a=this.series.active().map(function(a){return a.data}).map(function(a){return a.filter(function(a){return this._slice(a)},this)},this);this.stackData.hooks.data.forEach(function(c){a=c.f.apply(b,[a])});var c=d3.layout.stack();c.offset(b.offset);var d=c(a);this.stackData.hooks.after.forEach(function(c){d=c.f.apply(b,[a])});var e=0;return this.series.forEach(function(a){if(a.disabled)return;a.stack=d[e++]}),this.stackedData=d,d},this.stackData.hooks={data:[],after:[]},this._slice=function(a){if(this.window.xMin||this.window.xMax){var b=!0;return this.window.xMin&&a.x<this.window.xMin&&(b=!1),this.window.xMax&&a.x>this.window.xMax&&(b=!1),b}return!0},this.onUpdate=function(a){this.updateCallbacks.push(a)},this.registerRenderer=function(a){this._renderers=this._renderers||{},this._renderers[a.name]=a},this.configure=function(a){(a.width||a.height)&&this.setSize(a),Rickshaw.keys(this.defaults).forEach(function(b){this[b]=b in a?a[b]:b in this?this[b]:this.defaults[b]},this),this.setRenderer(a.renderer||this.renderer.name,a)},this.setRenderer=function(a,b){if(!this._renderers[a])throw"couldn't find renderer "+a;this.renderer=this._renderers[a],typeof b=="object"&&this.renderer.configure(b)},this.setSize=function(a){a=a||{};if(typeof window!==undefined)var b=window.getComputedStyle(this.element,null),c=parseInt(b.getPropertyValue("width")),d=parseInt(b.getPropertyValue("height"));this.width=a.width||c||400,this.height=a.height||d||250,this.vis&&this.vis.attr("width",this.width).attr("height",this.height)},this.initialize(a)},Rickshaw.namespace("Rickshaw.Fixtures.Color"),Rickshaw.Fixtures.Color=function(){this.schemes={},this.schemes.spectrum14=["#ecb796","#dc8f70","#b2a470","#92875a","#716c49","#d2ed82","#bbe468","#a1d05d","#e7cbe6","#d8aad6","#a888c2","#9dc2d3","#649eb9","#387aa3"].reverse(),this.schemes.spectrum2000=["#57306f","#514c76","#646583","#738394","#6b9c7d","#84b665","#a7ca50","#bfe746","#e2f528","#fff726","#ecdd00","#d4b11d","#de8800","#de4800","#c91515","#9a0000","#7b0429","#580839","#31082b"],this.schemes.spectrum2001=["#2f243f","#3c2c55","#4a3768","#565270","#6b6b7c","#72957f","#86ad6e","#a1bc5e","#b8d954","#d3e04e","#ccad2a","#cc8412","#c1521d","#ad3821","#8a1010","#681717","#531e1e","#3d1818","#320a1b"],this.schemes.classic9=["#423d4f","#4a6860","#848f39","#a2b73c","#ddcb53","#c5a32f","#7d5836","#963b20","#7c2626","#491d37","#2f254a"].reverse(),this.schemes.httpStatus={503:"#ea5029",502:"#d23f14",500:"#bf3613",410:"#efacea",409:"#e291dc",403:"#f457e8",408:"#e121d2",401:"#b92dae",405:"#f47ceb",404:"#a82a9f",400:"#b263c6",301:"#6fa024",302:"#87c32b",307:"#a0d84c",304:"#28b55c",200:"#1a4f74",206:"#27839f",201:"#52adc9",202:"#7c979f",203:"#a5b8bd",204:"#c1cdd1"},this.schemes.colorwheel=["#b5b6a9","#858772","#785f43","#96557e","#4682b4","#65b9ac","#73c03a","#cb513a"].reverse(),this.schemes.cool=["#5e9d2f","#73c03a","#4682b4","#7bc3b8","#a9884e","#c1b266","#a47493","#c09fb5"],this.schemes.munin=["#00cc00","#0066b3","#ff8000","#ffcc00","#330099","#990099","#ccff00","#ff0000","#808080","#008f00","#00487d","#b35a00","#b38f00","#6b006b","#8fb300","#b30000","#bebebe","#80ff80","#80c9ff","#ffc080","#ffe680","#aa80ff","#ee00cc","#ff8080","#666600","#ffbfff","#00ffcc","#cc6699","#999900"]},Rickshaw.namespace("Rickshaw.Fixtures.RandomData"),Rickshaw.Fixtures.RandomData=function(a){var b;a=a||1;var c=200,d=Math.floor((new Date).getTime()/1e3);this.addData=function(b){var e=Math.random()*100+15+c,f=b[0].length,g=1;b.forEach(function(b){var c=Math.random()*20,h=e/25+g++ +(Math.cos(f*g*11/960)+2)*15+(Math.cos(f/7)+2)*7+(Math.cos(f/17)+2)*1;b.push({x:f*a+d,y:h+c})}),c=e*.85}},Rickshaw.namespace("Rickshaw.Fixtures.Time"),Rickshaw.Fixtures.Time=function(){var a=(new Date).getTimezoneOffset()*60,b=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],this.units=[{name:"decade",seconds:315576e3,formatter:function(a){return parseInt(a.getUTCFullYear()/10)*10}},{name:"year",seconds:31557600,formatter:function(a){return a.getUTCFullYear()}},{name:"month",seconds:2635200,formatter:function(a){return b.months[a.getUTCMonth()]}},{name:"week",seconds:604800,formatter:function(a){return b.formatDate(a)}},{name:"day",seconds:86400,formatter:function(a){return a.getUTCDate()}},{name:"6 hour",seconds:21600,formatter:function(a){return b.formatTime(a)}},{name:"hour",seconds:3600,formatter:function(a){return b.formatTime(a)}},{name:"15 minute",seconds:900,formatter:function(a){return b.formatTime(a)}},{name:"minute",seconds:60,formatter:function(a){return a.getUTCMinutes()}},{name:"15 second",seconds:15,formatter:function(a){return a.getUTCSeconds()+"s"}},{name:"second",seconds:1,formatter:function(a){return a.getUTCSeconds()+"s"}}],this.unit=function(a){return this.units.filter(function(b){return a==b.name}).shift()},this.formatDate=function(a){return a.toUTCString().match(/, (\w+ \w+ \w+)/)[1]},this.formatTime=function(a){return a.toUTCString().match(/(\d+:\d+):/)[1]},this.ceil=function(a,b){if(b.name=="month"){var c=new Date((a+b.seconds-1)*1e3),d=new Date(0);return d.setUTCFullYear(c.getUTCFullYear()),d.setUTCMonth(c.getUTCMonth()),d.setUTCDate(1),d.setUTCHours(0),d.setUTCMinutes(0),d.setUTCSeconds(0),d.setUTCMilliseconds(0),d.getTime()/1e3}if(b.name=="year"){var c=new Date((a+b.seconds-1)*1e3),d=new Date(0);return d.setUTCFullYear(c.getUTCFullYear()),d.setUTCMonth(0),d.setUTCDate(1),d.setUTCHours(0),d.setUTCMinutes(0),d.setUTCSeconds(0),d.setUTCMilliseconds(0),d.getTime()/1e3}return Math.ceil(a/b.seconds)*b.seconds}},Rickshaw.namespace("Rickshaw.Fixtures.Number"),Rickshaw.Fixtures.Number.formatKMBT=function(a){return a>=1e12?a/1e12+"T":a>=1e9?a/1e9+"B":a>=1e6?a/1e6+"M":a>=1e3?a/1e3+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.Fixtures.Number.formatBase1024KMGTP=function(a){return a>=0x4000000000000?a/0x4000000000000+"P":a>=1099511627776?a/1099511627776+"T":a>=1073741824?a/1073741824+"G":a>=1048576?a/1048576+"M":a>=1024?a/1024+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.namespace("Rickshaw.Color.Palette"),Rickshaw.Color.Palette=function(a){var b=new Rickshaw.Fixtures.Color;a=a||{},this.schemes={},this.scheme=b.schemes[a.scheme]||a.scheme||b.schemes.colorwheel,this.runningIndex=0,this.generatorIndex=0;if(a.interpolatedStopCount){var c=this.scheme.length-1,d,e,f=[];for(d=0;d<c;d++){f.push(this.scheme[d]);var g=d3.interpolateHsl(this.scheme[d],this.scheme[d+1]);for(e=1;e<a.interpolatedStopCount;e++)f.push(g(1/a.interpolatedStopCount*e))}f.push(this.scheme[this.scheme.length-1]),this.scheme=f}this.rotateCount=this.scheme.length,this.color=function(a){return this.scheme[a]||this.scheme[this.runningIndex++]||this.interpolateColor()||"#808080"},this.interpolateColor=function(){if(!Array.isArray(this.scheme))return;var a;return this.generatorIndex==this.rotateCount*2-1?(a=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[0])(.5),this.generatorIndex=0,this.rotateCount*=2):(a=d3.interpolateHsl(this.scheme[this.generatorIndex],this.scheme[this.generatorIndex+1])(.5),this.generatorIndex++),this.scheme.push(a),a}},Rickshaw.namespace("Rickshaw.Graph.Ajax"),Rickshaw.Graph.Ajax=Rickshaw.Class.create({initialize:function(a){this.dataURL=a.dataURL,this.onData=a.onData||function(a){return a},this.onComplete=a.onComplete||function(){},this.onError=a.onError||function(){},this.args=a,this.request()},request:function(){$.ajax({url:this.dataURL,dataType:"json",success:this.success.bind(this),error:this.error.bind(this)})},error:function(){console.log("error loading dataURL: "+this.dataURL),this.onError(this)},success:function(a,b){a=this.onData(a),this.args.series=this._splice({data:a,series:this.args.series}),this.graph=new Rickshaw.Graph(this.args),this.graph.render(),this.onComplete(this)},_splice:function(a){var b=a.data,c=a.series;return a.series?(c.forEach(function(a){var c=a.key||a.name;if(!c)throw"series needs a key or a name";b.forEach(function(b){var d=b.key||b.name;if(!d)throw"data needs a key or a name";if(c==d){var e=["color","name","data"];e.forEach(function(c){a[c]=a[c]||b[c]})}})}),c):b}}),Rickshaw.namespace("Rickshaw.Graph.Annotate"),Rickshaw.Graph.Annotate=function(a){var b=this.graph=a.graph;this.elements={timeline:a.element};var c=this;this.data={},this.elements.timeline.classList.add("rickshaw_annotation_timeline"),this.add=function(a,b,d){c.data[a]=c.data[a]||{boxes:[]},c.data[a].boxes.push({content:b,end:d})},this.update=function(){Rickshaw.keys(c.data).forEach(function(a){var b=c.data[a],d=c.graph.x(a);if(d<0||d>c.graph.x.range()[1]){b.element&&(b.line.classList.add("offscreen"),b.element.style.display="none"),b.boxes.forEach(function(a){a.rangeElement&&a.rangeElement.classList.add("offscreen")});return}if(!b.element){var e=b.element=document.createElement("div");e.classList.add("annotation"),this.elements.timeline.appendChild(e),e.addEventListener("click",function(a){e.classList.toggle("active"),b.line.classList.toggle("active"),b.boxes.forEach(function(a){a.rangeElement&&a.rangeElement.classList.toggle("active")})},!1)}b.element.style.left=d+"px",b.element.style.display="block",b.boxes.forEach(function(a){var e=a.element;e||(e=a.element=document.createElement("div"),e.classList.add("content"),e.innerHTML=a.content,b.element.appendChild(e),b.line=document.createElement("div"),b.line.classList.add("annotation_line"),c.graph.element.appendChild(b.line),a.end&&(a.rangeElement=document.createElement("div"),a.rangeElement.classList.add("annotation_range"),c.graph.element.appendChild(a.rangeElement)));if(a.end){var f=d,g=Math.min(c.graph.x(a.end),c.graph.x.range()[1]);f>g&&(g=d,f=Math.max(c.graph.x(a.end),c.graph.x.range()[0]));var h=g-f;a.rangeElement.style.left=f+"px",a.rangeElement.style.width=h+"px",a.rangeElement.classList.remove("offscreen")}b.line.classList.remove("offscreen"),b.line.style.left=d+"px"})},this)},this.graph.onUpdate(function(){c.update()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Time"),Rickshaw.Graph.Axis.Time=function(a){var b=this;this.graph=a.graph,this.elements=[],this.ticksTreatment=a.ticksTreatment||"plain",this.fixedTimeUnit=a.timeUnit;var c=new Rickshaw.Fixtures.Time;this.appropriateTimeUnit=function(){var a,b=c.units,d=this.graph.x.domain(),e=d[1]-d[0];return b.forEach(function(b){Math.floor(e/b.seconds)>=2&&(a=a||b)}),a||c.units[c.units.length-1]},this.tickOffsets=function(){var a=this.graph.x.domain(),b=this.fixedTimeUnit||this.appropriateTimeUnit(),d=Math.ceil((a[1]-a[0])/b.seconds),e=a[0],f=[];for(var g=0;g<d;g++)tickValue=c.ceil(e,b),e=tickValue+b.seconds/2,f.push({value:tickValue,unit:b});return f},this.render=function(){this.elements.forEach(function(a){a.parentNode.removeChild(a)}),this.elements=[];var a=this.tickOffsets();a.forEach(function(a){if(b.graph.x(a.value)>b.graph.x.range()[1])return;var c=document.createElement("div");c.style.left=b.graph.x(a.value)+"px",c.classList.add("x_tick"),c.classList.add(b.ticksTreatment);var d=document.createElement("div");d.classList.add("title"),d.innerHTML=a.unit.formatter(new Date(a.value*1e3)),c.appendChild(d),b.graph.element.appendChild(c),b.elements.push(c)})},this.graph.onUpdate(function(){b.render()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Y"),Rickshaw.Graph.Axis.Y=function(a){var b=this,c=.1;this.initialize=function(a){this.graph=a.graph,this.orientation=a.orientation||"right";var c=a.pixelsPerTick||75;this.ticks=a.ticks||Math.floor(this.graph.height/c),this.tickSize=a.tickSize||4,this.ticksTreatment=a.ticksTreatment||"plain",a.element?(this.element=a.element,this.vis=d3.select(a.element).append("svg:svg").attr("class","rickshaw_graph y_axis"),this.element=this.vis[0][0],this.element.style.position="relative",this.setSize({width:a.width,height:a.height})):this.vis=this.graph.vis,this.graph.onUpdate(function(){b.render()})},this.setSize=function(a){a=a||{};if(!this.element)return;if(typeof window!="undefined"){var b=window.getComputedStyle(this.element.parentNode,null),d=parseInt(b.getPropertyValue("width"));if(!a.auto)var e=parseInt(b.getPropertyValue("height"))}this.width=a.width||d||this.graph.width*c,this.height=a.height||e||this.graph.height,this.vis.attr("width",this.width).attr("height",this.height*(1+c));var f=this.height*c;this.element.style.top=-1*f+"px"},this.render=function(){this.graph.height!==this._renderHeight&&this.setSize({auto:!0});var b=d3.svg.axis().scale(this.graph.y).orient(this.orientation);b.tickFormat(a.tickFormat||function(a){return a});if(this.orientation=="left")var d=this.height*c,e="translate("+this.width+", "+d+")";this.element&&this.vis.selectAll("*").remove(),this.vis.append("svg:g").attr("class",["y_ticks",this.ticksTreatment].join(" ")).attr("transform",e).call(b.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));var f=(this.orientation=="right"?1:-1)*this.graph.width;this.graph.vis.append("svg:g").attr("class","y_grid").call(b.ticks(this.ticks).tickSubdivide(0).tickSize(f)),this._renderHeight=this.graph.height},this.initialize(a)},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Highlight"),Rickshaw.Graph.Behavior.Series.Highlight=function(a){this.graph=a.graph,this.legend=a.legend;var b=this,c={};this.addHighlightEvents=function(a){a.element.addEventListener("mouseover",function(d){b.legend.lines.forEach(function(b){if(a===b)return;c[b.series.name]=c[b.series.name]||b.series.color,b.series.color=d3.interpolateRgb(b.series.color,d3.rgb("#d8d8d8"))(.8).toString()}),b.graph.update()},!1),a.element.addEventListener("mouseout",function(a){b.legend.lines.forEach(function(a){c[a.series.name]&&(a.series.color=c[a.series.name])}),b.graph.update()},!1)},this.legend&&this.legend.lines.forEach(function(a){b.addHighlightEvents(a)})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Order"),Rickshaw.Graph.Behavior.Series.Order=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;$(function(){$(b.legend.list).sortable({containment:"parent",tolerance:"pointer",update:function(a,c){var d=[];$(b.legend.list).find("li").each(function(a,b){if(!b.series)return;d.push(b.series)});for(var e=b.graph.series.length-1;e>=0;e--)b.graph.series[e]=d.shift();b.graph.update()}}),$(b.legend.list).disableSelection()}),this.graph.onUpdate(function(){var a=window.getComputedStyle(b.legend.element).height;b.legend.element.style.height=a})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Toggle"),Rickshaw.Graph.Behavior.Series.Toggle=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;this.addAnchor=function(a){var c=document.createElement("a");c.innerHTML="&#10004;",c.classList.add("action"),a.element.insertBefore(c,a.element.firstChild),c.onclick=function(b){a.series.disabled?(a.series.enable(),a.element.classList.remove("disabled")):(a.series.disable(),a.element.classList.add("disabled"))};var d=a.element.getElementsByTagName("span")[0];d.onclick=function(c){var d=a.series.disabled;if(!d)for(var e=0;e<b.legend.lines.length;e++){var f=b.legend.lines[e];if(a.series!==f.series&&!f.series.disabled){d=!0;break}}d?(a.series.enable(),a.element.classList.remove("disabled"),b.legend.lines.forEach(function(b){a.series!==b.series&&(b.series.disable(),b.element.classList.add("disabled"))})):b.legend.lines.forEach(function(a){a.series.enable(),a.element.classList.remove("disabled")})}},this.legend&&(this.legend.lines.forEach(function(a){b.addAnchor(a)})),this._addBehavior=function(){this.graph.series.forEach(function(a){a.disable=function(){if(b.graph.series.length<=1)throw"only one series left";a.disabled=!0,b.graph.update()},a.enable=function(){a.disabled=!1,b.graph.update()}})},this._addBehavior(),this.updateBehaviour=function(){this._addBehavior()}},Rickshaw.namespace("Rickshaw.Graph.HoverDetail"),Rickshaw.Graph.HoverDetail=Rickshaw.Class.create({initialize:function(a){var b=this.graph=a.graph;this.xFormatter=a.xFormatter||function(a){return(new Date(a*1e3)).toUTCString()},this.yFormatter=a.yFormatter||function(a){return a.toFixed(2)};var c=this.element=document.createElement("div");c.className="detail",this.visible=!0,b.element.appendChild(c),this.lastEvent=null,this._addListeners(),this.onShow=a.onShow,this.onHide=a.onHide,this.onRender=a.onRender,this.formatter=a.formatter||this.formatter},formatter:function(a,b,c,d,e,f){return a.name+":&nbsp;"+e},update:function(a){a=a||this.lastEvent;if(!a)return;this.lastEvent=a;if(!a.target.nodeName.match(/^(path|svg|rect)$/))return;var b=this.graph,c=a.offsetX||a.layerX,d=a.offsetY||a.layerY,e=b.x.invert(c),f=b.stackedData,g=f.slice(-1).shift(),h=d3.scale.linear().domain([g[0].x,g.slice(-1).shift().x]).range([0,g.length]),i=Math.floor(h(e)),j=Math.min(i||0,f[0].length-1);for(var k=i;k<f[0].length-1;){if(!f[0][k]||!f[0][k+1])break;if(f[0][k].x<=e&&f[0][k+1].x>e){j=k;break}f[0][k+1]<=e?k++:k--}var e=f[0][j].x,l=this.xFormatter(e),m=b.x(e),n=0,o=b.series.active().map(function(a){return{order:n++,series:a,name:a.name,value:a.stack[j]}}),p,q=function(a,b){return a.value.y0+a.value.y-(b.value.y0+b.value.y)},r=b.y.magnitude.invert(b.element.offsetHeight-d);o.sort(q).forEach(function(a){a.formattedYValue=this.yFormatter.constructor==Array?this.yFormatter[o.indexOf(a)](a.value.y):this.yFormatter(a.value.y),a.graphX=m,a.graphY=b.y(a.value.y0+a.value.y),r>a.value.y0&&r<a.value.y0+a.value.y&&!p&&(p=a,a.active=!0)},this),this.element.innerHTML="",this.element.style.left=b.x(e)+"px",this.visible&&this.render({detail:o,domainX:e,formattedXValue:l,mouseX:c,mouseY:d})},hide:function(){this.visible=!1,this.element.classList.add("inactive"),typeof this.onHide=="function"&&this.onHide()},show:function(){this.visible=!0,this.element.classList.remove("inactive"),typeof this.onShow=="function"&&this.onShow()},render:function(a){var b=a.detail,c=a.domainX,d=a.mouseX,e=a.mouseY,f=a.formattedXValue,g=document.createElement("div");g.className="x_label",g.innerHTML=f,this.element.appendChild(g),b.forEach(function(a){var b=document.createElement("div");b.className="item",b.innerHTML=this.formatter(a.series,c,a.value.y,f,a.formattedYValue,a),b.style.top=this.graph.y(a.value.y0+a.value.y)+"px",this.element.appendChild(b);var d=document.createElement("div");d.className="dot",d.style.top=b.style.top,d.style.borderColor=a.series.color,this.element.appendChild(d),a.active&&(b.className="item active",d.className="dot active")},this),this.show(),typeof this.onRender=="function"&&this.onRender(a)},_addListeners:function(){this.graph.element.addEventListener("mousemove",function(a){this.visible=!0,this.update(a)}.bind(this),!1),this.graph.onUpdate(function(){this.update()}.bind(this)),this.graph.element.addEventListener("mouseout",function(a){a.relatedTarget&&!(a.relatedTarget.compareDocumentPosition(this.graph.element)&Node.DOCUMENT_POSITION_CONTAINS)&&this.hide()}.bind(this),!1)}}),Rickshaw.namespace("Rickshaw.Graph.JSONP"),Rickshaw.Graph.JSONP=Rickshaw.Class.create(Rickshaw.Graph.Ajax,{request:function(){$.ajax({url:this.dataURL,dataType:"jsonp",success:this.success.bind(this),error:this.error.bind(this)})}}),Rickshaw.namespace("Rickshaw.Graph.Legend"),Rickshaw.Graph.Legend=function(a){var b=this.element=a.element,c=this.graph=a.graph,d=this;b.classList.add("rickshaw_legend");var e=this.list=document.createElement("ul");b.appendChild(e);var f=c.series.map(function(a){return a}).reverse();this.lines=[],this.addLine=function(a){var b=document.createElement("li");b.className="line";var c=document.createElement("div");c.className="swatch",c.style.backgroundColor=a.color,b.appendChild(c);var f=document.createElement("span");f.className="label",f.innerHTML=a.name,b.appendChild(f),e.appendChild(b),b.series=a,a.noLegend&&(b.style.display="none");var g={element:b,series:a};d.shelving&&(d.shelving.addAnchor(g),d.shelving.updateBehaviour()),d.highlighter&&d.highlighter.addHighlightEvents(g),d.lines.push(g)},f.forEach(function(a){d.addLine(a)}),c.onUpdate(function(){})},Rickshaw.namespace("Rickshaw.Graph.RangeSlider"),Rickshaw.Graph.RangeSlider=function(a){var b=this.element=a.element,c=this.graph=a.graph;$(function(){$(b).slider({range:!0,min:c.dataDomain()[0],max:c.dataDomain()[1],values:[c.dataDomain()[0],c.dataDomain()[1]],slide:function(a,b){c.window.xMin=b.values[0],c.window.xMax=b.values[1],c.update(),c.dataDomain()[0]==b.values[0]&&(c.window.xMin=undefined),c.dataDomain()[1]==b.values[1]&&(c.window.xMax=undefined)}})}),b[0].style.width=c.width+"px",c.onUpdate(function(){var a=$(b).slider("option","values");$(b).slider("option","min",c.dataDomain()[0]),$(b).slider("option","max",c.dataDomain()[1]),c.window.xMin==undefined&&(a[0]=c.dataDomain()[0]),c.window.xMax==undefined&&(a[1]=c.dataDomain()[1]),$(b).slider("option","values",a)})},Rickshaw.namespace("Rickshaw.Graph.Renderer"),Rickshaw.Graph.Renderer=Rickshaw.Class.create({initialize:function(a){this.graph=a.graph,this.tension=a.tension||this.tension,this.graph.unstacker=this.graph.unstacker||new Rickshaw.Graph.Unstacker({graph:this.graph}),this.configure(a)},seriesPathFactory:function(){},seriesStrokeFactory:function(){},defaults:function(){return{tension:.8,strokeWidth:2,unstack:!0,padding:{top:.01,right:0,bottom:.01,left:0},stroke:!1,fill:!1}},domain:function(){var a=[],b=this.graph.stackedData||this.graph.stackData(),c=this.unstack?b:[b.slice(-1).shift()];c.forEach(function(b){b.forEach(function(b){a.push(b.y+b.y0)})});var d=b[0][0].x,e=b[0][b[0].length-1].x;d-=(e-d)*this.padding.left,e+=(e-d)*this.padding.right;var f=this.graph.min==="auto"?d3.min(a):this.graph.min||0,g=this.graph.max||d3.max(a);if(this.graph.min==="auto"||f<0)f-=(g-f)*this.padding.bottom;return this.graph.max===undefined&&(g+=(g-f)*this.padding.top),{x:[d,e],y:[f,g]}},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=a.vis.selectAll("path").data(this.graph.stackedData).enter().append("svg:path").attr("d",this.seriesPathFactory()),c=0;a.series.forEach(function(a){if(a.disabled)return;a.path=b[0][c++],this._styleSeries(a)},this)},_styleSeries:function(a){var b=this.fill?a.color:"none",c=this.stroke?a.color:"none";a.path.setAttribute("fill",b),a.path.setAttribute("stroke",c),a.path.setAttribute("stroke-width",this.strokeWidth),a.path.setAttribute("class",a.className)},configure:function(a){a=a||{},Rickshaw.keys(this.defaults()).forEach(function(b){if(!a.hasOwnProperty(b)){this[b]=this[b]||this.graph[b]||this.defaults()[b];return}typeof this.defaults()[b]=="object"?Rickshaw.keys(this.defaults()[b]).forEach(function(c){this[b][c]=a[b][c]!==undefined?a[b][c]:this[b][c]!==undefined?this[b][c]:this.defaults()[b][c]},this):this[b]=a[b]!==undefined?a[b]:this[b]!==undefined?this[b]:this.graph[b]!==undefined?this.graph[b]:this.defaults()[b]},this)},setStrokeWidth:function(a){a!==undefined&&(this.strokeWidth=a)},setTension:function(a){a!==undefined&&(this.tension=a)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Line"),Rickshaw.Graph.Renderer.Line=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"line",defaults:function($super){return Rickshaw.extend($super(),{unstack:!0,fill:!1,stroke:!0})},seriesPathFactory:function(){var a=this.graph;return d3.svg.line().x(function(b){return a.x(b.x)}).y(function(b){return a.y(b.y)}).interpolate(this.graph.interpolation).tension(this.tension)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Stack"),Rickshaw.Graph.Renderer.Stack=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"stack",defaults:function($super){return Rickshaw.extend($super(),{fill:!0,stroke:!1,unstack:!1})},seriesPathFactory:function(){var a=this.graph;return d3.svg.area().x(function(b){return a.x(b.x)}).y0(function(b){return a.y(b.y0)}).y1(function(b){return a.y(b.y+b.y0)}).interpolate(this.graph.interpolation).tension(this.tension)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Bar"),Rickshaw.Graph.Renderer.Bar=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"bar",defaults:function($super){var a=Rickshaw.extend($super(),{gapSize:.05,unstack:!1});return delete a.tension,a},initialize:function($super,a){a=a||{},this.gapSize=a.gapSize||this.gapSize,$super(a)},domain:function($super){var a=$super(),b=this._frequentInterval();return a.x[1]+=parseInt(b.magnitude),a},barWidth:function(){var a=this.graph.stackedData||this.graph.stackData(),b=a.slice(-1).shift(),c=this._frequentInterval(),d=this.graph.x(b[0].x+c.magnitude*(1-this.gapSize));return d},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=this.barWidth(),c=0,d=a.series.filter(function(a){return!a.disabled}).length,e=this.unstack?b/d:b,f=function(b){var c=[1,0,0,b.y<0?-1:1,0,b.y<0?a.y.magnitude(Math.abs(b.y))*2:0];return"matrix("+c.join(",")+")"};a.series.forEach(function(b){if(b.disabled)return;var d=a.vis.selectAll("path").data(b.stack).enter().append("svg:rect").attr("x",function(b){return a.x(b.x)+c}).attr("y",function(b){return a.y(b.y0+Math.abs(b.y))*(b.y<0?-1:1)}).attr("width",e).attr("height",function(b){return a.y.magnitude(Math.abs(b.y))}).attr("transform",f);Array.prototype.forEach.call(d[0],function(a){a.setAttribute("fill",b.color)}),this.unstack&&(c+=e)},this)},_frequentInterval:function(){var a=this.graph.stackedData||this.graph.stackData(),b=a.slice(-1).shift(),c={};for(var d=0;d<b.length-1;d++){var e=b[d+1].x-b[d].x;c[e]=c[e]||0,c[e]++}var f={count:0};return Rickshaw.keys(c).forEach(function(a){f.count<c[a]&&(f={count:c[a],magnitude:a})}),this._frequentInterval=function(){return f},f}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.Area"),Rickshaw.Graph.Renderer.Area=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"area",defaults:function($super){return Rickshaw.extend($super(),{unstack:!1,fill:!1,stroke:!1})},seriesPathFactory:function(){var a=this.graph;return d3.svg.area().x(function(b){return a.x(b.x)}).y0(function(b){return a.y(b.y0)}).y1(function(b){return a.y(b.y+b.y0)}).interpolate(a.interpolation).tension(this.tension)},seriesStrokeFactory:function(){var a=this.graph;return d3.svg.line().x(function(b){return a.x(b.x)}).y(function(b){return a.y(b.y+b.y0)}).interpolate(a.interpolation).tension(this.tension)},render:function(){var a=this.graph;a.vis.selectAll("*").remove();var b=a.vis.selectAll("path").data(this.graph.stackedData).enter().insert("svg:g","g");b.append("svg:path").attr("d",
this.seriesPathFactory()).attr("class","area"),this.stroke&&b.append("svg:path").attr("d",this.seriesStrokeFactory()).attr("class","line");var c=0;a.series.forEach(function(a){if(a.disabled)return;a.path=b[0][c++],this._styleSeries(a)},this)},_styleSeries:function(a){if(!a.path)return;d3.select(a.path).select(".area").attr("fill",a.color),this.stroke&&d3.select(a.path).select(".line").attr("fill","none").attr("stroke",a.stroke||d3.interpolateRgb(a.color,"black")(.125)).attr("stroke-width",this.strokeWidth),a.className&&a.path.setAttribute("class",a.className)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.ScatterPlot"),Rickshaw.Graph.Renderer.ScatterPlot=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"scatterplot",defaults:function($super){return Rickshaw.extend($super(),{unstack:!0,fill:!0,stroke:!1,padding:{top:.01,right:.01,bottom:.01,left:.01},dotSize:4})},initialize:function($super,a){$super(a)},render:function(){var a=this.graph;a.vis.selectAll("*").remove(),a.series.forEach(function(b){if(b.disabled)return;var c=a.vis.selectAll("path").data(b.stack).enter().append("svg:circle").attr("cx",function(b){return a.x(b.x)}).attr("cy",function(b){return a.y(b.y)}).attr("r",function(b){return"r"in b?b.r:a.renderer.dotSize});Array.prototype.forEach.call(c[0],function(a){a.setAttribute("fill",b.color)})},this)}}),Rickshaw.namespace("Rickshaw.Graph.Smoother"),Rickshaw.Graph.Smoother=function(a){this.graph=a.graph,this.element=a.element;var b=this;this.aggregationScale=1,this.element&&$(function(){$(b.element).slider({min:1,max:100,slide:function(a,c){b.setScale(c.value),b.graph.update()}})}),b.graph.stackData.hooks.data.push({name:"smoother",orderPosition:50,f:function(a){var c=[];return a.forEach(function(a){var d=[];while(a.length){var e=0,f=0,g=a.splice(0,b.aggregationScale);g.forEach(function(a){e+=a.x/g.length,f+=a.y/g.length}),d.push({x:e,y:f})}c.push(d)}),c}}),this.setScale=function(a){if(a<1)throw"scale out of range: "+a;this.aggregationScale=a,this.graph.update()}},Rickshaw.namespace("Rickshaw.Graph.Unstacker"),Rickshaw.Graph.Unstacker=function(a){this.graph=a.graph;var b=this;this.graph.stackData.hooks.after.push({name:"unstacker",f:function(a){return b.graph.renderer.unstack?(a.forEach(function(a){a.forEach(function(a){a.y0=0})}),a):a}})},Rickshaw.namespace("Rickshaw.Series"),Rickshaw.Series=Rickshaw.Class.create(Array,{initialize:function(a,b,c){c=c||{},this.palette=new Rickshaw.Color.Palette(b),this.timeBase=typeof c.timeBase=="undefined"?Math.floor((new Date).getTime()/1e3):c.timeBase;var d=typeof c.timeInterval=="undefined"?1e3:c.timeInterval;this.setTimeInterval(d),a&&typeof a=="object"&&a instanceof Array&&a.forEach(function(a){this.addItem(a)},this)},addItem:function(a){if(typeof a.name=="undefined")throw"addItem() needs a name";a.color=a.color||this.palette.color(a.name),a.data=a.data||[],a.data.length==0&&this.length&&this.getIndex()>0?this[0].data.forEach(function(b){a.data.push({x:b.x,y:0})}):a.data.length==0&&a.data.push({x:this.timeBase-(this.timeInterval||0),y:0}),this.push(a),this.legend&&this.legend.addLine(this.itemByName(a.name))},addData:function(a){var b=this.getIndex();Rickshaw.keys(a).forEach(function(a){this.itemByName(a)||this.addItem({name:a})},this),this.forEach(function(c){c.data.push({x:(b*this.timeInterval||1)+this.timeBase,y:a[c.name]||0})},this)},getIndex:function(){return this[0]&&this[0].data&&this[0].data.length?this[0].data.length:0},itemByName:function(a){for(var b=0;b<this.length;b++)if(this[b].name==a)return this[b]},setTimeInterval:function(a){this.timeInterval=a/1e3},setTimeBase:function(a){this.timeBase=a},dump:function(){var a={timeBase:this.timeBase,timeInterval:this.timeInterval,items:[]};return this.forEach(function(b){var c={color:b.color,name:b.name,data:[]};b.data.forEach(function(a){c.data.push({x:a.x,y:a.y})}),a.items.push(c)}),a},load:function(a){a.timeInterval&&(this.timeInterval=a.timeInterval),a.timeBase&&(this.timeBase=a.timeBase),a.items&&a.items.forEach(function(a){this.push(a),this.legend&&this.legend.addLine(this.itemByName(a.name))},this)}}),Rickshaw.Series.zeroFill=function(a){var b,c=0,d=a.map(function(a){return a.data});while(c<Math.max.apply(null,d.map(function(a){return a.length})))b=Math.min.apply(null,d.filter(function(a){return a[c]}).map(function(a){return a[c].x})),d.forEach(function(a){(!a[c]||a[c].x!=b)&&a.splice(c,0,{x:b,y:0})}),c++},Rickshaw.namespace("Rickshaw.Series.FixedDuration"),Rickshaw.Series.FixedDuration=Rickshaw.Class.create(Rickshaw.Series,{initialize:function(a,b,c){var c=c||{};if(typeof c.timeInterval=="undefined")throw new Error("FixedDuration series requires timeInterval");if(typeof c.maxDataPoints=="undefined")throw new Error("FixedDuration series requires maxDataPoints");this.palette=new Rickshaw.Color.Palette(b),this.timeBase=typeof c.timeBase=="undefined"?Math.floor((new Date).getTime()/1e3):c.timeBase,this.setTimeInterval(c.timeInterval),this[0]&&this[0].data&&this[0].data.length?(this.currentSize=this[0].data.length,this.currentIndex=this[0].data.length):(this.currentSize=0,this.currentIndex=0),this.maxDataPoints=c.maxDataPoints,a&&typeof a=="object"&&a instanceof Array&&(a.forEach(function(a){this.addItem(a)},this),this.currentSize+=1,this.currentIndex+=1),this.timeBase-=(this.maxDataPoints-this.currentSize)*this.timeInterval;if(typeof this.maxDataPoints!="undefined"&&this.currentSize<this.maxDataPoints)for(var d=this.maxDataPoints-this.currentSize-1;d>0;d--)this.currentSize+=1,this.currentIndex+=1,this.forEach(function(a){a.data.unshift({x:((d-1)*this.timeInterval||1)+this.timeBase,y:0,i:d})},this)},addData:function($super,a){$super(a),this.currentSize+=1,this.currentIndex+=1;if(this.maxDataPoints!==undefined)while(this.currentSize>this.maxDataPoints)this.dropData()},dropData:function(){this.forEach(function(a){a.data.splice(0,1)}),this.currentSize-=1},getIndex:function(){return this.currentIndex}}); this.seriesPathFactory()).attr("class","area"),this.stroke&&b.append("svg:path").attr("d",this.seriesStrokeFactory()).attr("class","line");var c=0;a.series.forEach(function(a){if(a.disabled)return;a.path=b[0][c++],this._styleSeries(a)},this)},_styleSeries:function(a){if(!a.path)return;d3.select(a.path).select(".area").attr("fill",a.color),this.stroke&&d3.select(a.path).select(".line").attr("fill","none").attr("stroke",a.stroke||d3.interpolateRgb(a.color,"black")(.125)).attr("stroke-width",this.strokeWidth),a.className&&a.path.setAttribute("class",a.className)}}),Rickshaw.namespace("Rickshaw.Graph.Renderer.ScatterPlot"),Rickshaw.Graph.Renderer.ScatterPlot=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"scatterplot",defaults:function($super){return Rickshaw.extend($super(),{unstack:!0,fill:!0,stroke:!1,padding:{top:.01,right:.01,bottom:.01,left:.01},dotSize:4})},initialize:function($super,a){$super(a)},render:function(){var a=this.graph;a.vis.selectAll("*").remove(),a.series.forEach(function(b){if(b.disabled)return;var c=a.vis.selectAll("path").data(b.stack).enter().append("svg:circle").attr("cx",function(b){return a.x(b.x)}).attr("cy",function(b){return a.y(b.y)}).attr("r",function(b){return"r"in b?b.r:a.renderer.dotSize});Array.prototype.forEach.call(c[0],function(a){a.setAttribute("fill",b.color)})},this)}}),Rickshaw.namespace("Rickshaw.Graph.Smoother"),Rickshaw.Graph.Smoother=function(a){this.graph=a.graph,this.element=a.element;var b=this;this.aggregationScale=1,this.element&&$(function(){$(b.element).slider({min:1,max:100,slide:function(a,c){b.setScale(c.value),b.graph.update()}})}),b.graph.stackData.hooks.data.push({name:"smoother",orderPosition:50,f:function(a){var c=[];return a.forEach(function(a){var d=[];while(a.length){var e=0,f=0,g=a.splice(0,b.aggregationScale);g.forEach(function(a){e+=a.x/g.length,f+=a.y/g.length}),d.push({x:e,y:f})}c.push(d)}),c}}),this.setScale=function(a){if(a<1)throw"scale out of range: "+a;this.aggregationScale=a,this.graph.update()}},Rickshaw.namespace("Rickshaw.Graph.Unstacker"),Rickshaw.Graph.Unstacker=function(a){this.graph=a.graph;var b=this;this.graph.stackData.hooks.after.push({name:"unstacker",f:function(a){return b.graph.renderer.unstack?(a.forEach(function(a){a.forEach(function(a){a.y0=0})}),a):a}})},Rickshaw.namespace("Rickshaw.Series"),Rickshaw.Series=Rickshaw.Class.create(Array,{initialize:function(a,b,c){c=c||{},this.palette=new Rickshaw.Color.Palette(b),this.timeBase=typeof c.timeBase=="undefined"?Math.floor((new Date).getTime()/1e3):c.timeBase;var d=typeof c.timeInterval=="undefined"?1e3:c.timeInterval;this.setTimeInterval(d),a&&typeof a=="object"&&a instanceof Array&&a.forEach(function(a){this.addItem(a)},this)},addItem:function(a){if(typeof a.name=="undefined")throw"addItem() needs a name";a.color=a.color||this.palette.color(a.name),a.data=a.data||[],a.data.length==0&&this.length&&this.getIndex()>0?this[0].data.forEach(function(b){a.data.push({x:b.x,y:0})}):a.data.length==0&&a.data.push({x:this.timeBase-(this.timeInterval||0),y:0}),this.push(a),this.legend&&this.legend.addLine(this.itemByName(a.name))},addData:function(a){var b=this.getIndex();Rickshaw.keys(a).forEach(function(a){this.itemByName(a)||this.addItem({name:a})},this),this.forEach(function(c){c.data.push({x:(b*this.timeInterval||1)+this.timeBase,y:a[c.name]||0})},this)},getIndex:function(){return this[0]&&this[0].data&&this[0].data.length?this[0].data.length:0},itemByName:function(a){for(var b=0;b<this.length;b++)if(this[b].name==a)return this[b]},setTimeInterval:function(a){this.timeInterval=a/1e3},setTimeBase:function(a){this.timeBase=a},dump:function(){var a={timeBase:this.timeBase,timeInterval:this.timeInterval,items:[]};return this.forEach(function(b){var c={color:b.color,name:b.name,data:[]};b.data.forEach(function(a){c.data.push({x:a.x,y:a.y})}),a.items.push(c)}),a},load:function(a){a.timeInterval&&(this.timeInterval=a.timeInterval),a.timeBase&&(this.timeBase=a.timeBase),a.items&&a.items.forEach(function(a){this.push(a),this.legend&&this.legend.addLine(this.itemByName(a.name))},this)}}),Rickshaw.Series.zeroFill=function(a){var b,c=0,d=a.map(function(a){return a.data});while(c<Math.max.apply(null,d.map(function(a){return a.length})))b=Math.min.apply(null,d.filter(function(a){return a[c]}).map(function(a){return a[c].x})),d.forEach(function(a){(!a[c]||a[c].x!=b)&&a.splice(c,0,{x:b,y:0})}),c++},Rickshaw.namespace("Rickshaw.Series.FixedDuration"),Rickshaw.Series.FixedDuration=Rickshaw.Class.create(Rickshaw.Series,{initialize:function(a,b,c){var c=c||{};if(typeof c.timeInterval=="undefined")throw new Error("FixedDuration series requires timeInterval");if(typeof c.maxDataPoints=="undefined")throw new Error("FixedDuration series requires maxDataPoints");this.palette=new Rickshaw.Color.Palette(b),this.timeBase=typeof c.timeBase=="undefined"?Math.floor((new Date).getTime()/1e3):c.timeBase,this.setTimeInterval(c.timeInterval),this[0]&&this[0].data&&this[0].data.length?(this.currentSize=this[0].data.length,this.currentIndex=this[0].data.length):(this.currentSize=0,this.currentIndex=0),this.maxDataPoints=c.maxDataPoints,a&&typeof a=="object"&&a instanceof Array&&(a.forEach(function(a){this.addItem(a)},this),this.currentSize+=1,this.currentIndex+=1),this.timeBase-=(this.maxDataPoints-this.currentSize)*this.timeInterval;if(typeof this.maxDataPoints!="undefined"&&this.currentSize<this.maxDataPoints)for(var d=this.maxDataPoints-this.currentSize-1;d>0;d--)this.currentSize+=1,this.currentIndex+=1,this.forEach(function(a){a.data.unshift({x:((d-1)*this.timeInterval||1)+this.timeBase,y:0,i:d})},this)},addData:function($super,a){$super(a),this.currentSize+=1,this.currentIndex+=1;if(this.maxDataPoints!==undefined)while(this.currentSize>this.maxDataPoints)this.dropData()},dropData:function(){this.forEach(function(a){a.data.splice(0,1)}),this.currentSize-=1},getIndex:function(){return this.currentIndex}});
   
<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="ga-reports-table 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 class="td-numeric">${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>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
CKAN.GA_Reports.render_rickshaw('$id', $items_json, '$mode', '$colorscheme'); var items = $items_json;
  if (items.length==0) { return; }
  CKAN.GA_Reports.render_rickshaw('$id', items, '$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="ga-reports-table table table-condensed table-bordered table-striped">
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>% ${title}</th> <th class="td-numeric">% ${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 class="td-numeric">${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)">
<div id="minornavigation-bg-left"> <span class="subheading">
<div id="minornavigation-bg-right"> <img src="/images/arrow-down-right.png" />
<ul class="nav nav-pills"> <a class="${if_(active_name=='Site-wide','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}${if_(c.month,'?month='+c.month)}">Site-wide</a>
<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> <span class="divider">|</span>
<li py:attrs="{'class': 'active' if active_name=='Publishers' else None}"> <a class="${if_(active_name=='Publishers','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}${if_(c.month,'?month='+c.month)}">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> <span class="divider">|</span>
</li> <a class="${if_(active_name=='Datasets','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}${if_(c.month,'?month='+c.month)}">Datasets</a>
<li py:attrs="{'class': 'active' if active_name=='Datasets' else None}"> </span>
<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> </div>
</li>  
  <div py:def="ga_sidebar(download_link)">
  <div class="boxed">
  <div class="widget-container widget_text">
  <h4>Download</h4>
  <p><center>
  <a class="btn button btn-primary" href="${download_link}">Download as CSV</a></center>
  </p>
  </div>
  <div class="widget-container widget_text">
  <h4>Graph Legend</h4>
  <div id="graph-legend-container">
  <div id="legend_none">(No graph loaded)</div>
  </div>
  </div>
  <div class="widget-container widget_text">
  <h4>Notes</h4>
  <ul>
  <li>"Views" is the number of times a page was loaded in users' browsers.</li>
  <li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li>
  <li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li>
  <li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
   
   
</html> </html>
   
<html xmlns:py="http://genshi.edgewall.org/"  
xmlns:i18n="http://genshi.edgewall.org/i18n"  
xmlns:xi="http://www.w3.org/2001/XInclude"  
py:strip="">  
 
<li class="widget-container boxed widget_text">  
<h4>Notes</h4>  
<ul>  
<li>"Views" is the number of times a page was loaded in users' browsers.</li>  
<li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li>  
<li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li>  
<li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li>  
</ul>  
</li>  
</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">Usage by Publisher</py:def> <py:def function="page_title">Usage by Publisher</py:def>
   
   
<py:match path="primarysidebar"> <py:match path="primarysidebar">
<li class="widget-container boxed widget_text"> ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all'))}
<h4>Download</h4>  
<p><center>  
<a class="btn button btn-primary" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all')}">Download as CSV</a></center>  
</p>  
</li>  
<li class="widget-container boxed widget_text">  
<h4>Graph Legend</h4>  
<div id="graph-legend-container">  
</div>  
</li>  
<xi:include href="../notes.html" />  
</py:match> </py:match>
   
<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?1"/>
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script> <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></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/rickshaw_ie7_shim.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>
   
<div py:match="content"> <div py:match="content">
   
<h1>Site Usage</h1> <h1>Site Usage ${usage_nav('Publishers')}</h1>
   
${usage_nav('Publishers')} <div class="boxed">
   
  ${rickshaw_graph(c.top_publishers_graph,'publishers')}
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}" method="get"> <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}" method="get">
<div class="controls"> <div class="controls">
  <label>Show stats table for:</label>
${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"/>  
</div> </div>
</form> </form>
   
${rickshaw_graph(c.top_publishers_graph,'publishers')} <table class="ga-reports-table table table-condensed table-bordered table-striped">
<table class="table table-condensed table-bordered table-striped"> <tr>
<tr> <th>Publisher</th>
<th>Publisher</th> <th class="td-numeric">Dataset Views</th>
<th>Dataset Views</th> </tr>
</tr>  
<py:for each="publisher, views, visits in c.top_publishers"> <py:for each="publisher, views, visits in c.top_publishers">
<tr> <tr>
<td> <td>
${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))} ${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))}
</td> </td>
<td>${views}</td> <td class="td-numeric">${views}</td>
</tr> </tr>
</py:for> </py:for>
</table> </table>
  </div><!--/boxed-->
  </div><!--/content-->
</div>  
   
<xi:include href="../../layout.html" />  
   
<py:def function="optional_footer"> <py:def function="optional_footer">
<script type='text/javascript'> <script type="text/javascript">
$('.nav-tabs li a').click(function (e) { $(function() {
e.preventDefault(); CKAN.GA_Reports.bind_month_selector();
$(this).tab('show'); });
})  
</script> </script>
</py:def> </py:def>
  <xi:include href="../../layout.html" />
   
</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">Usage by Dataset</py:def> <py:def function="page_title">Usage by Dataset</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?1"/>
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script> <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></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/rickshaw_ie7_shim.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"> ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all'))}
<h4>Download</h4>  
<p><center>  
<a class="btn button btn-primary" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all')}">Download as CSV</a></center>  
</p>  
</li>  
<li class="widget-container boxed widget_text">  
<h4>Graph Legend</h4>  
<div id="graph-legend-container">  
</div>  
</li>  
<xi:include href="../notes.html" />  
</py:match> </py:match>
   
   
<div py:match="content"> <div py:match="content">
<h1>Site Usage</h1> <h1>Site Usage ${usage_nav('Datasets')}</h1>
   
${usage_nav('Datasets')} <div class="boxed">
  <h3 py:if="c.publisher"><a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></h3>
   
  <p py:if="not c.top_packages">No page views in this period</p>
   
  <py:if test="c.top_packages">
  ${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)}
  </py:if>
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get"> <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get">
<div class="controls"> <div class="controls">
   
${month_selector(c.month, c.months, c.day)} ${month_selector(c.month, c.months, c.day)}
   
<select name="publisher"> <select name="publisher">
<option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option> <option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option>
<py:for each="val,desc in c.publishers"> <py:for each="val,desc in c.publishers">
<option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option> <option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option>
</py:for> </py:for>
</select> </select>
<input class="btn button btn-primary" type='submit' value="Update"/> <input class="btn button btn-primary" type='submit' value="Update"/>
</div> </div>
</form> </form>
   
<h3 py:if="c.publisher"><a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></h3>  
   
<p py:if="not c.top_packages">No page views in this period</p>  
   
<py:if test="c.top_packages"> <py:if test="c.top_packages">
${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)} <table class="ga-reports-table table table-condensed table-bordered table-striped">
<table class="table table-condensed table-bordered table-striped">  
<tr> <tr>
<th>Dataset</th> <th>Dataset</th>
<th>Views</th> <th>Views</th>
<th>Downloads</th> <th>Downloads</th>
</tr> </tr>
<py:for each="package, views, visits,downloads in c.top_packages"> <py:for each="package, views, visits,downloads in c.top_packages">
<tr> <tr>
<td> <td>
${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))} ${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))}
</td> </td>
<td>${views}</td> <td class="td-numeric">${views}</td>
<td>${downloads}</td> <td class="td-numeric">${downloads}</td>
</tr> </tr>
</py:for> </py:for>
</table> </table>
</py:if> </py:if>
   
  </div>
   
</div> </div>
   
<xi:include href="../../layout.html" /> <xi:include href="../../layout.html" />
</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">Downloads</py:def> <py:def function="page_title">Downloads</py:def>
   
<py:match path="primarysidebar"> <py:match path="primarysidebar">
<li py:if="c.downloads" class="widget-container boxed widget_text"> ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv_downloads',month=c.month or 'all'))}
<h4>Download</h4>  
<p><center>  
<a class="btn button btn-primary" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv_downloads',month=c.month or 'all')}">Download as CSV</a></center>  
</p>  
</li>  
<xi:include href="../notes.html" />  
   
</py:match> </py:match>
   
<div py:match="content"> <div py:match="content">
<h1>Downloads</h1> <h1>Downloads ${usage_nav('Downloads')}</h1>
${usage_nav('Downloads')}  
  <div class="boxed">
   
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='downloads')}" method="get"> <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='downloads')}" 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>
   
<py:if test="c.downloads"> <py:if test="c.downloads">
${downloads_table(c.downloads)} ${downloads_table(c.downloads)}
</py:if> </py:if>
<py:if test="not c.downloads"> <py:if test="not c.downloads">
<h4>No data</h4> <h4>No data</h4>
<p>There is no download data available for this month</p> <p>There is no download data available for this month</p>
</py:if> </py:if>
  </div>
</div> </div>
   
<xi:include href="../../layout.html" /> <xi:include href="../../layout.html" />
   
<py:def function="optional_footer"> <py:def function="optional_footer">
<script type='text/javascript'> <script type='text/javascript'>
$('.dropdown-toggle').dropdown(); $('.dropdown-toggle').dropdown();
$('.nav-tabs li a').click(function (e) { $('.nav-tabs li a').click(function (e) {
e.preventDefault(); e.preventDefault();
$(this).tab('show'); $(this).tab('show');
}) })
alert(window.location.hash); alert(window.location.hash);
</script> </script>
</py:def> </py:def>
</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?1"/>
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script> <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></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/rickshaw_ie7_shim.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"> ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all'))}
<h4>Download</h4>  
<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>  
</p>  
</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" />  
   
</py:match> </py:match>
   
<div py:match="content"> <div py:match="content">
<h1>Site Usage</h1> <h1>Site Usage ${usage_nav('Site-wide')}</h1>
${usage_nav('Site-wide')}  
   
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> <div class="boxed">
<div class="controls">  
   
${month_selector(c.month, c.months, c.day)}  
   
<input class="btn button btn-primary" type='submit' value="Update"/>  
</div>  
</form>  
   
<div class="tabbable"> <div class="tabbable">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li><a href="#totals" data-toggle="hashchange">Totals</a></li> <li class="active"><a href="#totals" data-toggle="hashtab">Totals</a></li>
<li class="dropdown"> <li class="dropdown">
<a href="#" 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="hashchange">Browsers</a></li> <li><a href="#browsers_names" data-toggle="hashtab">Browsers</a></li>
<li><a href="#browsers_versions" data-toggle="hashchange">Versions</a></li> <li><a href="#browsers_versions" data-toggle="hashtab">Versions</a></li>
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating 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="hashchange">Operating Systems</a></li> <li><a href="#os" data-toggle="hashtab">Operating Systems</a></li>
<li><a href="#os_versions" data-toggle="hashchange">Versions</a></li> <li><a href="#os_versions" data-toggle="hashtab">Versions</a></li>
</ul> </ul>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Social <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">
<li><a href="#social_networks" data-toggle="hashchange">All networks</a></li> <li><a href="#social_networks" data-toggle="hashtab">All networks</a></li>
<li><a href="#social_referrals_totals" data-toggle="hashchange">Referral links</a></li> <li><a href="#social_referrals_totals" data-toggle="hashtab">Referral links</a></li>
</ul> </ul>
</li> </li>
<li><a href="#languages" data-toggle="hashchange">Languages</a></li> <li><a href="#languages" data-toggle="hashtab">Languages</a></li>
<li><a href="#country" data-toggle="hashchange">Country</a></li> <li><a href="#country" data-toggle="hashtab">Country</a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane" id="totals"> <div class="tab-pane active" id="totals">
<table class="table table-condensed table-bordered table-striped"> <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
  <table class="ga-reports-table table table-condensed table-bordered table-striped">
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Value</th> <th class="td-numeric">Value</th>
<th>History</th> <th>History</th>
</tr> </tr>
<py:for each="name, value, graph in c.global_totals"> <py:for each="name, value, graph in c.global_totals">
<tr> <tr>
<td>${name}</td> <td>${name}</td>
<td>${value}</td> <td class="td-numeric">${value}</td>
<td class="sparkline-cell"> <td class="sparkline-cell">
<span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}"> <span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}">
${','.join([y for x,y in graph])} ${','.join([y for x,y in graph])}
</span> </span>
</td> </td>
</tr> </tr>
</py:for> </py:for>
</table> </table>
</div> </div>
<div class="tab-pane" id="browsers_versions"> <div class="tab-pane" id="browsers_versions">
${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')} ${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')}
<p>Note: Where a browser has a large number of versions, these have been grouped together.</p> <p>Note: Where a browser has a large number of versions, these have been grouped together.</p>
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.browser_versions)} ${stat_table(c.browser_versions)}
</div> </div>
<div class="tab-pane" id="browsers_names"> <div class="tab-pane" id="browsers_names">
${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')} ${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')}
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.browsers)} ${stat_table(c.browsers)}
</div> </div>
<div class="tab-pane" id="os"> <div class="tab-pane" id="os">
${rickshaw_graph(c.os_graph,'os',mode='stack')} ${rickshaw_graph(c.os_graph,'os',mode='stack')}
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.os)} ${stat_table(c.os)}
</div> </div>
<div class="tab-pane" id="os_versions"> <div class="tab-pane" id="os_versions">
${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')} ${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')}
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.os_versions)} ${stat_table(c.os_versions)}
</div> </div>
<div class="tab-pane" id="social_referrals_totals"> <div class="tab-pane" id="social_referrals_totals">
<p>Number of visits that were referred from social networks</p> <p>Number of visits that were referred from social networks</p>
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${social_table(c.social_referrer_totals)} ${social_table(c.social_referrer_totals)}
</div> </div>
<div class="tab-pane" id="social_networks"> <div class="tab-pane" id="social_networks">
${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')} ${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')}
<p>Percentage of visits that were referred from these social networks</p> <p>Percentage of visits that were referred from these social networks</p>
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.social_networks, 'Visits')} ${stat_table(c.social_networks, 'Visits')}
</div> </div>
<div class="tab-pane" id="languages"> <div class="tab-pane" id="languages">
${rickshaw_graph(c.languages_graph,'languages',mode='stack')} ${rickshaw_graph(c.languages_graph,'languages',mode='stack')}
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.languages)} ${stat_table(c.languages)}
</div> </div>
<div class="tab-pane" id="country"> <div class="tab-pane" id="country">
${rickshaw_graph(c.country_graph,'country',mode='stack')} ${rickshaw_graph(c.country_graph,'country',mode='stack')}
  <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get">
  <label>Show stats table for:</label>
  ${month_selector(c.month, c.months, c.day)}
  </form>
${stat_table(c.country)} ${stat_table(c.country)}
</div> </div>
</div> </div>
</div> </div>
  </div>
</div> </div>
   
   
<py:def function="optional_footer"> <py:def function="optional_footer">
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
CKAN.GA_Reports.bind_sparklines(); CKAN.GA_Reports.bind_sparklines();
CKAN.GA_Reports.bind_sidebar(); CKAN.GA_Reports.bind_sidebar();
if (!window.location.hash) { CKAN.GA_Reports.bind_month_selector();
window.location.hash='totals';  
}  
else {  
$(window).trigger('hashchange');  
}  
}); });
</script> </script>
</py:def> </py:def>
   
<xi:include href="../../layout.html" /> <xi:include href="../../layout.html" />
</html> </html>