source: OpenWorkouts-current/ow/static/js/ow.js @ a596867

currentfeature/docs
Last change on this file since a596867 was a596867, checked in by Borja Lopez <borja@…>, 5 years ago

(#68) Weeks for the current month were displayed in the wrong part of the
yearly activity chart in the profile page.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1
2/*
3
4  OpenWorkouts Javascript code
5
6*/
7
8
9// Namespace
10var owjs = {};
11
12
13owjs.map = function(spec) {
14
15    "use strict";
16
17    // parameters provided when creating an "instance" of a map
18    var map_id = spec.map_id;
19    var latitude = spec.latitude;
20    var longitude = spec.longitude;
21    var zoom = spec.zoom;
22    var gpx_url = spec.gpx_url;
23    var start_icon = spec.start_icon;
24    var end_icon = spec.end_icon;
25    var shadow = spec.shadow;
26    var elevation = spec.elevation;
27    var zoom_control = spec.zoom_control;
28
29    // OpenStreetMap urls and references
30    var openstreetmap_url = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
31    var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>';
32
33    // Some vars reused through the code
34    var map;
35    var gpx;
36    var elevation;
37
38    var create_map = function create_map(latitude, longitude, zoom) {
39        /* Create a Leaflet map, set center point and add tiles */
40        map = L.map(map_id, {zoomControl: zoom_control});
41        map.setView([latitude, longitude], zoom);
42        var tile_layer = L.tileLayer(openstreetmap_url, {
43            attribution: openstreetmap_attr
44        });
45        tile_layer.addTo(map);
46    };
47
48    var add_elevation_chart = function add_elevation_chart() {
49        /*
50           Add the elevation chart support to the map.
51           This has to be called *after* create_map and *before* load_gpx.
52        */
53
54        elevation = L.control.elevation({
55            position: "bottomright",
56            theme: "openworkouts-theme",
57            useHeightIndicator: true, //if false a marker is drawn at map position
58            interpolation: d3.curveLinear,
59            elevationDiv: "#elevation",
60            detachedView: true,
61            responsiveView: true,
62            gpxOptions: {
63                async: true,
64                marker_options: {
65                    startIconUrl: null,
66                    endIconUrl: null,
67                    shadowUrl: null,
68                },
69                polyline_options: {
70                    color: '#EE4056',
71                    opacity: 0.75,
72                    weight: 5,
73                    lineCap: 'round'
74                },
75            },
76        });
77        elevation.loadGPX(map, gpx_url);
78        // var ele_container = elevation.addTo(map);
79    };
80
81    var load_gpx = function load_gpx(gpx_url) {
82        /*
83          Load the gpx from the given url, add it to the map and feed it to the
84          elevation chart
85        */
86        var gpx = new L.GPX(gpx_url, {
87            async: true,
88            marker_options: {
89                startIconUrl: start_icon,
90                endIconUrl: end_icon,
91                shadowUrl: shadow,
92            },
93            polyline_options: {
94                color: '#EE4056',
95                opacity: 0.75,
96                weight: 5,
97                lineCap: 'round'
98            },
99        });
100
101        gpx.on('loaded', function(e) {
102            map.fitBounds(e.target.getBounds());
103        });
104
105        if (elevation) {
106            gpx.on("addline",function(e){
107                elevation.addData(e.line);
108            });
109        };
110
111        gpx.addTo(map);
112    };
113
114    var render = function render() {
115        // create the map, add elevation, load gpx (only if needed, as the
116        // elevation plugin already loads the gpx data)
117        create_map(latitude, longitude, zoom);
118        if (elevation) {
119            add_elevation_chart();
120        }
121        else {
122            load_gpx(gpx_url);
123        }
124    };
125
126    var that = {}
127    that.render = render;
128    return that
129
130};
131
132
133owjs.week_chart = function(spec) {
134
135    "use strict";
136
137    // parameters provided when creating an "instance" of the chart
138    var chart_selector = spec.chart_selector,
139        url = spec.url,
140        current_day_name = spec.current_day_name
141
142    // Helpers
143    function select_x_axis_label(d) {
144        /* Given a value, return the label associated with it */
145        return d3.select('.x-axis')
146            .selectAll('text')
147            .filter(function(x) { return x == d.name; });
148    }
149
150    // Methods
151    var render = function render() {
152        /*
153           Build a d3 bar chart, populated with data from the given url.
154         */
155        var chart = d3.select(chart_selector),
156            margin = {top: 17, right: 0, bottom: 20, left: 0},
157
158            width = +chart.attr("width") - margin.left - margin.right,
159            height = +chart.attr("height") - margin.top - margin.bottom,
160            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
161            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
162            y = d3.scaleLinear().rangeRound([height, 0]);
163
164        d3.json(url, {credentials: "same-origin"}).then(function (data) {
165            x.domain(data.map(function (d) {
166                return d.name;
167            }));
168
169            y.domain([0, d3.max(data, function (d) {
170                return Number(d.distance);
171            })]);
172
173            g.append("g")
174                .attr('class', 'x-axis')
175                .attr("transform", "translate(0," + height + ")")
176                .call(d3.axisBottom(x))
177
178            g.selectAll(".bar")
179                .data(data)
180                .enter().append("rect")
181                .attr("class", function(d) {
182                    if (d.name == current_day_name){
183                        select_x_axis_label(d).attr('style', "font-weight: bold;");
184                        return 'bar current'
185                    }
186                    else {
187                        return 'bar'
188                    }
189                })
190                .attr("x", function (d) {
191                    return x(d.name);
192                })
193                .attr("y", function (d) {
194                    return y(Number(d.distance));
195                })
196                .attr("width", x.bandwidth())
197                .attr("height", function (d) {
198                    return height - y(Number(d.distance));
199                })
200                .on('mouseover', function(d) {
201                    if (d.name != current_day_name){
202                        select_x_axis_label(d).attr('style', "font-weight: bold;");
203                    }
204                })
205                .on('mouseout', function(d) {
206                    if (d.name != current_day_name){
207                        select_x_axis_label(d).attr('style', "font-weight: regular;");
208                    }
209                });
210
211            g.selectAll(".text")
212                .data(data)
213                .enter()
214                .append("text")
215                .attr("class","label")
216                .attr("x", function (d) {
217                    return x(d.name) + x.bandwidth()/2;
218                })
219                .attr("y", function (d) {
220                    /*
221                      Get the value for the current bar, then get the maximum
222                      value to be displayed in the bar, which is used to
223                      calculate the proper position of the label for this bar,
224                      relatively to its height (1% above the bar)
225                     */
226                    var max = y.domain()[1];
227                    return y(d.distance + y.domain()[1] * 0.02);
228            })
229                .text(function(d) {
230                    if (Number(d.distance) > 0) {
231                        return d.distance;
232                    }
233                });
234
235        });
236    };
237
238    var that = {}
239    that.render = render;
240    return that
241
242};
243
244
245owjs.year_chart = function(spec) {
246
247    "use strict";
248
249    // parameters provided when creating an "instance" of the chart
250    var chart_selector = spec.chart_selector,
251        filters_selector = spec.filters_selector,
252        switcher_selector = spec.switcher_selector,
253        is_active_class = spec.is_active_class,
254        is_active_selector = '.' + is_active_class,
255        urls = spec.urls,
256        current_month = spec.current_month,
257        current_week = spec.current_week,
258        y_axis_labels = spec.y_axis_labels,
259        filter_by = spec.filter_by,
260        url = spec.url;
261
262    // Helpers
263    function select_x_axis_label(d) {
264        /* Given a value, return the label associated with it */
265        return d3.select('.x-axis-b')
266            .selectAll('text')
267            .filter(function(x) { return x == d.name; });
268    };
269
270    function get_y_value(d, filter_by) {
271        return Number(d[filter_by]);
272    };
273
274    function get_y_axis_label(filter_by) {
275        return y_axis_labels[filter_by];
276    };
277
278    function get_name_for_x(d) {
279        if (d.week == undefined || d.week == 0) {
280            // Monthly chart or first week of the weekly chart, return
281            // the name of the month, which will be shown in the x-axis
282            // ticks
283            return d.name;
284        }
285        else {
286            // Weekly chart, week other than the first, return the id so
287            // we can place it in the chart, we won't show this to the
288            // user
289            return d.id
290        }
291    }
292
293    // Methods
294    var filters_setup = function filters_setup() {
295        $(filters_selector).on('click', function(e) {
296            e.preventDefault();
297            $(filters_selector + is_active_selector).removeClass(is_active_class);
298            /* $(this).removeClass('is-active'); */
299            filter_by = $(this).attr('class').split('-')[1]
300            $(this).addClass(is_active_class);
301            var chart = d3.select(chart_selector);
302            chart.selectAll("*").remove();
303            render(filter_by, url);
304
305        });
306    };
307
308    var switcher_setup = function switcher_setup() {
309        $(switcher_selector).on('click', function(e) {
310            e.preventDefault();
311            $(switcher_selector + is_active_selector).removeClass(is_active_class);
312            url = $(this).attr('class').split('-')[1]
313            $(this).addClass(is_active_class);
314            var chart = d3.select(chart_selector);
315            chart.selectAll("*").remove();
316            render(filter_by, url);
317        });
318    };
319
320    var render = function render(filter_by, url) {
321        /*
322          Build a d3 bar chart, populated with data from the given url.
323        */
324        var chart = d3.select(chart_selector),
325            margin = {top: 20, right: 20, bottom: 30, left: 50},
326            width = +chart.attr("width") - margin.left - margin.right,
327            height = +chart.attr("height") - margin.top - margin.bottom,
328            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
329            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
330            y = d3.scaleLinear().rangeRound([height, 0]);
331
332        d3.json(urls[url], {credentials: "same-origin"}).then(function (data) {
333            x.domain(data.map(function (d) {
334                return get_name_for_x(d);
335                // return d.name;
336            }));
337
338            y.domain([0, d3.max(data, function (d) {
339                return get_y_value(d, filter_by);
340            })]);
341
342            g.append("g")
343                .attr('class', 'x-axis-b')
344                .attr("transform", "translate(0," + height + ")")
345                .call(d3.axisBottom(x))
346
347            g.append("g")
348                .call(d3.axisLeft(y))
349                .append("text")
350                .attr("fill", "#000")
351                .attr("transform", "rotate(-90)")
352                .attr("y", 6)
353                .attr("dy", "0.71em")
354                .attr("text-anchor", "end")
355                .text(get_y_axis_label(filter_by));
356
357            g.selectAll(".bar")
358                .data(data)
359                .enter().append("rect")
360                .attr("class", function(d) {
361                    var sel_week = current_month + '-' + current_week;
362                    if (d.id == current_month || d.id == sel_week){
363                        /* Bar for the currently selected month or week */
364                        select_x_axis_label(d).attr('style', "font-weight: bold;");
365                        return 'bar current';
366                    }
367                    else {
368                        if (!current_week && d.id.indexOf(current_month) >=0 ) {
369                            /*
370                               User selected a month, then switched to weekly
371                               view, we do highlight all the bars for weeks in
372                               that month
373                            */
374                            select_x_axis_label(d).attr('style', "font-weight: bold;");
375                            return 'bar current';
376                        }
377                        else {
378                            /* Non-selected bar */
379                            return 'bar';
380                        }
381
382                    }
383                })
384                .attr("x", function (d) {
385                    return x(get_name_for_x(d));
386                })
387                .attr("y", function (d) {
388                    return y(get_y_value(d, filter_by));
389                })
390                .attr("width", x.bandwidth())
391                .attr("height", function (d) {
392                    return height - y(get_y_value(d, filter_by));
393                })
394                .on('mouseover', function(d) {
395                    if (d.id != current_month){
396                        select_x_axis_label(d).attr('style', "font-weight: bold;");
397                    }
398                })
399                .on('mouseout', function(d) {
400                    if (d.id != current_month){
401                        select_x_axis_label(d).attr('style', "font-weight: regular;");
402                    }
403                })
404                .on('click', function(d) {
405                    window.location.href = d.url;
406                });
407
408            if (url == 'monthly') {
409                g.selectAll(".text")
410                    .data(data)
411                    .enter()
412                    .append("text")
413                    .attr("class","label")
414                    .attr("x", function (d) {
415                        return x(get_name_for_x(d)) + x.bandwidth()/2;
416                    })
417                    .attr("y", function (d) {
418                        /*
419                          Get the value for the current bar, then get the maximum
420                          value to be displayed in the bar, which is used to
421                          calculate the proper position of the label for this bar,
422                          relatively to its height (1% above the bar)
423                        */
424                        var value = get_y_value(d, filter_by);
425                        var max = y.domain()[1];
426                        return y(value + y.domain()[1] * 0.01);
427                    })
428                    .text(function(d) {
429                        var value = get_y_value(d, filter_by)
430                        if ( value > 0) {
431                            return value;
432                        }
433                    });
434            }
435
436            if (url == 'weekly') {
437                g.selectAll(".tick")
438                    .each(function (d, i) {
439                        /*
440                          Remove from the x-axis tickets those without letters
441                          on them (useful for the weekly chart)
442                        */
443                        if (d !== parseInt(d, 10)) {
444                            if(!d.match(/[a-z]/i)) {
445                                this.remove();
446                            }
447                        }
448                    });
449            }
450        });
451    };
452
453    var that = {}
454    that.filters_setup = filters_setup;
455    that.switcher_setup = switcher_setup;
456    that.render = render;
457    return that
458
459};
460
461
462owjs.map_shots = function(spec) {
463
464    "use strict";
465
466    var img_selector = spec.img_selector;
467
468    var run = function run(){
469        $(img_selector).each(function(){
470            var img = $(this);
471            var a = $(this).parent();
472            var url = a.attr('href') + 'map-shot';
473            var jqxhr = $.getJSON(url, function(info) {
474                img.fadeOut('fast', function () {
475                    img.attr('src', info['url']);
476                    img.fadeIn('fast');
477                });
478                img.removeClass('js-needs-map');
479            });
480        });
481    };
482
483    var that = {}
484    that.run = run;
485    return that
486
487};
Note: See TracBrowser for help on using the repository browser.