Index: ow/static/js/ow.js
===================================================================
--- ow/static/js/ow.js	(revision 1fe89ea002ea79bfdaa81b47ef82c7f6a265ce0a)
+++ ow/static/js/ow.js	(revision 5cf5787d1c21f5eedd38c23d588cc1906e4bef1d)
@@ -31,5 +31,5 @@
     var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>';
 
-    // Some constants reused through the code
+    // Some vars reused through the code
     var map;
     var gpx;
@@ -147,6 +147,6 @@
            Build a d3 bar chart, populated with data from the given url.
          */
-        var chart = d3.select("svg"),
-            margin = {top: 17, right: 0, bottom: 20, left: 0},
+        var chart = d3.select(chart_selector),
+            margin = {top: 20, right: 20, bottom: 30, left: 50},
             width = +chart.attr("width") - margin.left - margin.right,
             height = +chart.attr("height") - margin.top - margin.bottom,
@@ -211,13 +211,6 @@
 	        })
 	        .attr("y", function (d) {
-                    /*
-                      Get the value for the current bar, then get the maximum
-                      value to be displayed in the bar, which is used to
-                      calculate the proper position of the label for this bar,
-                      relatively to its height (1% above the bar)
-                     */
-                    var max = y.domain()[1];
-                    return y(d.distance + y.domain()[1] * 0.02);
-            })
+                    return y(Number(d.distance) + 5);
+	        })
                 .text(function(d) {
                     if (Number(d.distance) > 0) {
@@ -234,2 +227,205 @@
 
 };
+
+
+owjs.year_chart = function(spec) {
+
+    "use strict";
+
+    // parameters provided when creating an "instance" of the chart
+    var chart_selector = spec.chart_selector,
+        filters_selector = spec.filters_selector,
+        switcher_selector = spec.switcher_selector,
+        urls = spec.urls,
+        current_month = spec.current_month,
+        current_week = spec.current_week,
+        y_axis_labels = spec.y_axis_labels,
+        filter_by = spec.filter_by,
+        url = spec.url;
+
+    // Helpers
+    function select_x_axis_label(d) {
+        /* Given a value, return the label associated with it */
+        return d3.select('.x-axis-b')
+    	    .selectAll('text')
+    	    .filter(function(x) { return x == d.name; });
+    };
+
+    function get_y_value(d, filter_by) {
+        return Number(d[filter_by]);
+    };
+
+    function get_y_axis_label(filter_by) {
+        return y_axis_labels[filter_by];
+    };
+
+    function get_name_for_x(d) {
+        if (d.week == undefined || d.week == 0) {
+            return d.name;
+        }
+        else {
+            return d.id.split('-')[2];
+        }
+    }
+
+    // Methods
+    var filters_setup = function filters_setup() {
+        $(filters_selector).on('click', function(e) {
+            e.preventDefault();
+            filter_by = $(this).attr('class').split('-')[1]
+            var chart = d3.select(chart_selector);
+            chart.selectAll("*").remove();
+            render(filter_by, url);
+        });
+    };
+
+    var switcher_setup = function switcher_setup() {
+        $(switcher_selector).on('click', function(e) {
+            e.preventDefault();
+            url = $(this).attr('class').split('-')[1]
+            var chart = d3.select(chart_selector);
+            chart.selectAll("*").remove();
+            render(filter_by, url);
+        });
+    };
+
+    var render = function render(filter_by, url) {
+        /*
+          Build a d3 bar chart, populated with data from the given url.
+        */
+        var chart = d3.select(chart_selector),
+            margin = {top: 20, right: 20, bottom: 30, left: 50},
+            width = +chart.attr("width") - margin.left - margin.right,
+            height = +chart.attr("height") - margin.top - margin.bottom,
+            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
+            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
+            y = d3.scaleLinear().rangeRound([height, 0]);
+
+        d3.json(urls[url]).then(function (data) {
+	    x.domain(data.map(function (d) {
+                return get_name_for_x(d);
+	        // return d.name;
+	    }));
+
+	    y.domain([0, d3.max(data, function (d) {
+                return get_y_value(d, filter_by);
+	    })]);
+
+	    g.append("g")
+                .attr('class', 'x-axis-b')
+	        .attr("transform", "translate(0," + height + ")")
+                .call(d3.axisBottom(x))
+
+            g.append("g")
+	        .call(d3.axisLeft(y))
+	        .append("text")
+	        .attr("fill", "#000")
+	        .attr("transform", "rotate(-90)")
+	        .attr("y", 6)
+	        .attr("dy", "0.71em")
+	        .attr("text-anchor", "end")
+	        .text(get_y_axis_label(filter_by));
+
+	    g.selectAll(".bar")
+	        .data(data)
+	        .enter().append("rect")
+                .attr("class", function(d) {
+                    var sel_week = current_month + '-' + current_week;
+                    if (d.id == current_month || d.id == sel_week){
+                        /* Bar for the currently selected month or week */
+                        select_x_axis_label(d).attr('style', "font-weight: bold;");
+                        return 'bar current';
+                    }
+                    else {
+                        if (!current_week && d.id.indexOf(current_month) >=0 ) {
+                            /*
+                               User selected a month, then switched to weekly
+                               view, we do highlight all the bars for weeks in
+                               that month
+                            */
+                            select_x_axis_label(d).attr('style', "font-weight: bold;");
+                            return 'bar current';
+                        }
+                        else {
+                            /* Non-selected bar */
+                            return 'bar';
+                        }
+
+                    }
+                })
+	        .attr("x", function (d) {
+		    return x(get_name_for_x(d));
+	        })
+	        .attr("y", function (d) {
+		    return y(get_y_value(d, filter_by));
+	        })
+	        .attr("width", x.bandwidth())
+	        .attr("height", function (d) {
+		    return height - y(get_y_value(d, filter_by));
+	        })
+                .on('mouseover', function(d) {
+                    if (d.id != current_month){
+                        select_x_axis_label(d).attr('style', "font-weight: bold;");
+                    }
+  		})
+  		.on('mouseout', function(d) {
+                    if (d.id != current_month){
+        	        select_x_axis_label(d).attr('style', "font-weight: regular;");
+                    }
+                })
+                .on('click', function(d) {
+                    window.location.href = d.url;
+                });
+
+            if (url == 'monthly') {
+                g.selectAll(".text")
+                    .data(data)
+                    .enter()
+                    .append("text")
+                    .attr("class","label")
+	            .attr("x", function (d) {
+		        return x(get_name_for_x(d)) + x.bandwidth()/2;
+	            })
+	            .attr("y", function (d) {
+                        /*
+                          Get the value for the current bar, then get the maximum
+                          value to be displayed in the bar, which is used to
+                          calculate the proper position of the label for this bar,
+                          relatively to its height (1% above the bar)
+                        */
+                        var value = get_y_value(d, filter_by);
+                        var max = y.domain()[1];
+                        return y(value + y.domain()[1] * 0.01);
+	            })
+                    .text(function(d) {
+                        var value = get_y_value(d, filter_by)
+                        if ( value > 0) {
+                            return value;
+                        }
+                    });
+            }
+
+            if (url == 'weekly') {
+                g.selectAll(".tick")
+                    .each(function (d, i) {
+                        /*
+                          Remove from the x-axis tickets those without letters
+                          on them (useful for the weekly chart)
+                        */
+                        if (d !== parseInt(d, 10)) {
+                            if(!d.match(/[a-z]/i)) {
+                                this.remove();
+                            }
+                        }
+                    });
+            }
+        });
+    };
+
+    var that = {}
+    that.filters_setup = filters_setup;
+    that.switcher_setup = switcher_setup;
+    that.render = render;
+    return that
+
+};
