[SPIP] +version 3.0.7
[ptitvelo/web/www.git] / www / plugins-dist / statistiques / javascript / jquery.tflot.js
diff --git a/www/plugins-dist/statistiques/javascript/jquery.tflot.js b/www/plugins-dist/statistiques/javascript/jquery.tflot.js
new file mode 100644 (file)
index 0000000..d6307d9
--- /dev/null
@@ -0,0 +1,733 @@
+/**
+ * Librairie tFlot pour jQuery et jQuery.flot
+ * Licence GNU/GPL - Matthieu Marcillaud
+ * Version 1.4.0
+ */
+
+(function($){
+
+       /**
+        * Des variables a garder globalement
+        *
+        * collections : stockage de l'ensemble de toutes les valeurs de tous les graphs et leurs options
+        * collectionsActives : stockage des series actives
+        * plots : stockage des graphiques
+        * vignettes : stockage des vignettes
+        * idGraph : identifiant unique pour tous les graphs
+        */
+       collections = [];
+       collectionsActives = [];
+       plots = [];
+       vignettes = [];
+       vignettesSelection = [];
+       idGraph = 0;
+
+       /*
+        * Fait un graphique d'un tableau donne
+        * $("table.graph").tFlot();
+        * necessite la librairie "jQuery flot".
+        * http://code.google.com/p/flot/
+        */
+       $.fn.tFlot = function(settings) {
+               var options, flot;
+
+
+               options = {
+                       width:'500px',
+                       height:'250px',
+                       parse:{
+                               orientation:'row', // 'column' : tableaux verticaux par defaut...
+                               axeOnTitle:false // les coordonnees x d'axe sont donnes dans l'attribut title du <th> et non dans le <th> ?
+                       },
+                       legendeExterne:false,
+                       legendeActions:false, // ne fonctionne qu'avec l'option legende externe
+                       modeDate:false, // pour calculer les timestamp automatiquement
+                       moyenneGlissante:{
+                               show:false, // pour calculer une moyenne glissante automatiquement
+                               plage:7 // plage de glissement (nombre impair !)
+                       },
+                       grille:{weekend:false},
+                       infobulle:{
+                               show:false,
+                               serie_color:false // utiliser comme couleur de fond la même couleur que les lignes du graph
+                       },
+                       zoom:false,
+                       vignette:{
+                               show:false,
+                               width:'160px',
+                               height:'100px'
+                       },
+                       flot:{
+                               legend:{
+                                       show:true,
+                                       container:null,
+                                       labelFormatter:null
+                               },
+                               bars: {fill:false},
+                               yaxis: { min: 0 },
+                               selection: { mode: "x" }
+                       }
+               }
+               $.extend(true, options, settings);
+
+
+               $(this).each(function(){
+
+                       // identifiant unique pour tous les graphs
+                       // creer les cadres
+                       // .graphique
+                       //     .graphResult
+                       //         .graphInfos
+                       //        .graphLegend
+                       //        .graphOverview
+                       $(this).hide().wrap("<div class='graphique' id='graphique"+idGraph+"'></div>");
+                       graphique = $(this).parent();
+                       values = parseTable(this, options.parse);
+                       $.extend(true, values.options, options.flot);
+
+                       graph = $("<div class='graphResult' style='width:" + options.width + ";height:" + options.height + ";'></div>").appendTo(graphique);
+                       gInfo = $("<div class='graphInfo'></div>").appendTo(graphique);
+
+                       // legende en dehors du dessin ?
+                       if (options.legendeExterne) {
+                               legend = $("<div class='graphLegend' id='grapLegend"+idGraph+"'></div>").appendTo(gInfo);
+                               values.options.legend.container = legend;
+                       }
+                       // legende avec items clicables pour desactiver certaines series
+                       if (options.legendeActions) {
+                                 values.options.legend.labelFormatter = function(label) {
+                                       return '<a href="#label">' + label + '</a>';
+                                 }
+                       }
+                       // si mode time, on calcule des timestamp
+                       // les series sont alors de la forme [[timestamp, valeur],...]
+                       // et pas besoin de ticks declare
+                       if (options.modeDate) {
+                               timestamps = [];
+                               // calcul des timestamps
+                               $.each(values.options.xaxis.ticks, function(i, val){
+                                       timestamps.push([val[0], (new Date(val[1])).getTime()]);
+                               });
+                               // les remettre dans les series
+                               $.each(values.series, function(i, val){
+                                       data = [];
+                                       $.each(val.data, function (j, d){
+                                               data.push([timestamps[j][1], d[1]]);
+                                       });
+                                       values.series[i].data = data;
+                               });
+                               // plus besoin du ticks
+                               // mais toujours besoin des valeurs completes...
+                               values.options.xaxis = $.extend(true, {
+                                                       mode: "time",
+                                                       timeformat: "%d/%m/%y"
+                                               },
+                                               values.options.xaxis,
+                                               {ticks: null}
+                               );
+                               if (options.grille.weekend) {
+                                       values.options.grid = { markings: weekendAreas }
+                               }
+                               if (options.grille.years) {
+                                       values.options.grid = { markings: yearsArea }
+                               }
+                       }
+
+                       // en cas de moyenne glissante, on la calcule
+                       if (options.moyenneGlissante.show) {
+                               values.series = moyenneGlissante(values.series, options.moyenneGlissante);
+                       }
+
+                       // si infobulles, les ajouter
+                       if (options.infobulle.show) {
+                               $.extend(true, options.infobulle, {date:options.modeDate});
+                               infobulle($('#graphique'+idGraph), options.infobulle);
+                               $.extend(true, values.options, {
+                                       grid:{hoverable:true}
+                               });
+                       }
+
+
+                       // dessiner
+                       plots[idGraph] = $.plot(graph, values.series, values.options);
+
+                       // prevoir les actions sur les labels
+                       if (options.legendeActions) {
+                               $.extend(values.options, {legend:{container:null, show:false}});
+                               actionsLegendes($('#graphique'+idGraph));
+                       }
+
+                       // ajouter une mini vue si demandee
+                       if (options.vignette.show) {
+                               $("<div class='graphVignette' id='#graphVignette"+idGraph
+                                       + "' style='width:" + options.vignette.width + ";height:"
+                                       + options.vignette.height + ";'></div>").appendTo(gInfo);
+                               creerVignette($('#graphique'+idGraph), values.series, values.options, options.vignette);
+                       }
+                       // autoriser les zoom
+                       if (options.zoom) {
+                               zoomGraphique($('#graphique'+idGraph));
+                       }
+
+                       // stocker les valeurs
+                       collections.push({id:idGraph, values:values}); // sources
+                       collectionsActives = $.extend(true, {}, collections); // affiches
+
+
+                       ++idGraph;
+               });
+
+               /*
+                * Prendre une table HTML
+                * et calculer les donnees d'un graph jQuery.plot
+                */
+               function parseTable(table, settings){
+                       var options;
+                       flot = [];
+
+                       options = {
+                               ticks:[], // [1:"label 1", 2:"label 2"]
+                               orientation:'row', // 'column'
+                               ticksReels:[], // on sauve les vraies donnees pour les infobulles (1 janvier 2008) et non le code de date (1/1/2008)
+                               axeOnTitle:false,
+                               defaultSerie:{
+                                       bars: {
+                                               barWidth: 0.9,
+                                               align: "center",
+                                               show:true,
+                                               fill:false
+                                       },
+                                       lines: {
+                                               show:false,
+                                               fill:false
+                                       }
+                               }
+                       }
+                       $.extend(options, settings);
+
+                       row = (options.orientation == 'row');
+
+                       //
+                       // recuperer les points d'axes
+                       //
+
+                       //
+                       // Une fonction pour simplifier la recup
+                       //
+                       function getValue(element) {
+                               if (options.axeOnTitle) {
+                                       return element.attr('title');
+                               } else {
+                                       return element.text();
+                               }
+                       }
+
+                       axe=0;
+                       if (row) {
+                               // dans le th de chaque tr
+                               $(table).find('tr:not(:first)').each(function(){
+                                       $(this).find('th:first').each(function(){
+                                               options.ticks.push([++axe, getValue($(this))]);
+                                               options.ticksReels.push([axe, $(this).text()]);
+                                       });
+                               });
+
+                       } else {
+                               // dans les th du premier tr
+                               $(table).find('tr:first th:not(:first)').each(function(){
+                                       options.ticks.push([++axe, getValue($(this))]);
+                                       options.ticksReels.push([axe, $(this).text()]);
+                               });
+                       }
+
+
+                       //
+                       // recuperer les noms de series
+                       //
+                       axe = (axe ? 1 : 0);
+
+                       if (row) {
+                               // si axes definis, on saute une ligne
+                               if (axe) {
+                                       columns = $(table).find('tr:first th:not(:first)');
+                               } else {
+                                       columns = $(table).find('tr:first th');
+                               }
+                               // chaque colonne est une serie
+
+                               for(i=0; i<columns.length; i++){
+                                       cpt=0, data=[];
+                                       th = $(table).find('tr:first th:eq(' + (i + axe) + ')');
+                                       label = th.text();
+                                       serieOptions = optionsCss(th);
+                                       $(table).find('tr td:nth-child(' + (i + 1 + axe) +')').each(function(){
+                                               val = parseFloat($(this).text());
+                                               data.push( [++cpt, val] );
+                                       });
+                                       serie = {label:label, data:data};
+                                       $.extend(serie, serieOptions);
+                                       flot.push(serie);
+                               }
+
+
+                       } else {
+                               // si axes definis, on saute une colonne
+                               if (axe) {
+                                       rows = $(table).find('tr:not(:first)');
+                               } else {
+                                       rows = $(table).find('tr');
+                               }
+                               // chaque ligne est une serie
+                               rows.each(function(){
+                                       cpt=0, data=[];
+                                       th = $(this).find('th');
+                                       label = th.text();
+                                       serieOptions = optionsCss(th);
+                                       // recuperer les valeurs
+                                       $(this).find('td').each(function(){
+                                               val = parseFloat($(this).text());
+                                               data.push( [++cpt, val] );
+                                       });
+                                       serie = {label:label, data:data};
+                                       $.extend(serie, serieOptions);
+                                       flot.push(serie);
+                               });
+                       }
+
+                       //
+                       // mettre les options dans les series
+                       //
+
+                       color=0;
+                       $.each(flot, function(i, serie) {
+                               if (!serie.color) {
+                                       serie.color = color++;
+                               }
+                               serie = $.extend(true, {}, options.defaultSerie, serie);
+                               flot[i] = serie;
+                       });
+
+                       opt = {
+                               xaxis: {}
+                       }
+                       if (options.ticks.length) {
+                               opt.xaxis.ticks = options.ticks;
+                               opt.xaxis.ticksReels = options.ticksReels;
+                       }
+
+                       return {series:flot, options:opt};
+               }
+
+               /*
+                *
+                * Recuperer les options en fonctions de CSS
+                *
+                */
+               function optionsCss(element) {
+                       var options = {};
+                       $element = $(element);
+                       if ($element.data('serie') == 'line') {
+                               $.extend(true, options, {
+                                       lines:{show:true},
+                                       bars:{show:false},
+                                       points:{show:false}
+                               });
+                       }
+                       if ($element.data('serie') == 'bar') {
+                               $.extend(true, options, {
+                                       lines:{show:false},
+                                       bars:{show:true},
+                                       points:{show:false}
+                               });
+                       }
+                       if ($element.data('serie') == 'lineBar') {
+                               $.extend(true, options, {
+                                       lines:{show:true,steps:true},
+                                       bars:{show:false},
+                                       points:{show:false}
+                               });
+                       }
+                       if ($element.data('fill')) {
+                               $.extend(true, options, {
+                                       lines:{
+                                               fill:true,
+                                               fillColor: { colors: [ { opacity: 0.7 }, { opacity: 0 } ] }
+                                       },
+                                       bars:{
+                                               fill:true,
+                                               fillColor: { colors: [ { opacity: 0.7 }, { opacity: 0 } ] }
+                                       }
+                               });
+                       }
+                       if (color = $element.data('color')) {
+                               options.color = color;
+                       }
+                       return options;
+               }
+
+               /*
+                *
+                *  calcul d'une moyenne glissante
+                *
+                */
+               function moyenneGlissante(lesSeries, settings) {
+                       var options;
+                       options = {
+                               plage: 7,
+                               texte:"Moyenne glissante"
+                       }
+                       $.extend(options, settings);
+
+                       g = options.plage;
+                       series = [];
+                       color = 0;
+                       $.each(lesSeries, function(i, val){
+                               // recuperer le numero de couleur max
+                               color = ParseInt(Math.max(color,val.color));
+                               
+                               data = [], moy = [];
+                               $.each(val.data, function (j, d){
+                                       // ajout du nouvel element
+                                       // et retrait du trop vieux
+                                       moy.push(parseInt(d[1]));
+                                       if (moy.length>=g) { moy.shift();}
+
+                                       // calcul de la somme et ajout de la moyenne
+                                       for(var k=0,sum=0;k<moy.length;sum+=moy[k++]);
+                                       data.push([d[0], Math.round(sum/moy.length)]);
+                               });
+
+                               serieG = $.extend(true, {}, val, {
+                                       data:data,
+                                       label:val.label + " ("+options.texte+")",
+                                       lines:{
+                                               show:true,
+                                               fill:false
+                                       },
+                                       bars:{show:false}
+                               });
+                               series.push(val);
+                               series.push(serieG);
+                       });
+                       // remettre les couleurs
+                       $.each(series, function(i, val) {
+                               if (!val.color) {
+                                       val.color = color++;
+                               }
+                       });
+                       return series;
+               }
+
+               //
+               // Permettre de cacher certaines series
+               //
+               function actionsLegendes(graph) {
+                       // actions sur les items de legende
+                       // pour masquer / afficher certaines series
+                       // a ne charger qu'une fois par graph !!!
+                       $(graph).find('.legendLabel a').click(function(){
+                               tr = $(this).parent().parent();
+                               tr.toggleClass('cacher');
+
+                               // bof bof tous ces parent() et ca marche qu'avec legendeExterne:true
+                               master = tr.parent().parent().parent().parent().parent();
+                               pid = master.attr('id').substr(9); // enlever 'graphique'
+
+                               var seriesActives = [];
+                               tr.parent().find('tr:not(.cacher)').each(function(){
+                                       nom = $(this).find('a').text();
+                                       n = collections[pid].values.series.length;
+                                       for(i=0;i<n;i++) {
+                                               if (collections[pid].values.series[i].label == nom) {
+                                                       seriesActives.push(collections[pid].values.series[i]);
+                                                       break;
+                                               }
+                                       }
+                               });
+                               collectionsActives[pid].values.series = seriesActives;
+
+                               $.plot(master.find('.graphResult'), seriesActives, collections[pid].values.options);
+                               // vignettes
+                               if (master.find('.graphVignette').length) {
+                                       creerVignette(master, seriesActives, collections[pid].values.options);
+                               }
+
+                       });
+               }
+
+               //
+               // Afficher une miniature
+               //
+               function creerVignette(graphique, series, optionsParents, settings) {
+                       var options;
+                       options = {
+                               show:true,
+                               zoom:true,
+                               flot:{
+                                       legend: { show: false },
+                                       shadowSize: 0,
+                                       lines: { show: true, lineWidth: 1 },
+                                       grid: { color: "#999", hoverable:null },
+                                       selection: { mode: "x" },
+                                       xaxis:{min:null, max:null},
+                                       yaxis:{min:null, max:null}
+                               }
+                       };
+                       $.extend(true, options, settings);
+                       options.flot = $.extend(true, {}, optionsParents, options.flot);
+
+                       // demarrer la vignette
+                       vignette = $(graphique).find('.graphVignette');
+                       pid = vignette.parent().parent().attr('id').substr(9);
+                       vignettes[pid] = $.plot(vignette, series, options.flot);
+
+                       if (vignettesSelection[pid] !== undefined) {
+                               vignettes[pid].setSelection(vignettesSelection[pid]);
+                       }
+               }
+
+
+
+               //
+               // Permettre le zoom sur le graphique
+               // et sur la miniature
+               //
+               function zoomGraphique(graphique) {
+                       pid = $(graphique).attr('id').substr(9);
+
+                       $(graphique).find('.graphResult').bind("plotselected", function (event, ranges) {
+                               graph = $(event.target);
+                               pid = graph.parent().attr('id').substr(9);
+
+                               // clamp the zooming to prevent eternal zoom
+                               if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
+                                       ranges.xaxis.to = ranges.xaxis.from + 0.00001;
+                               if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
+                                       ranges.yaxis.to = ranges.yaxis.from + 0.00001;
+
+                               // do the zooming
+                               // et sauver les parametres du zoom
+                               plots[pid] = $.plot(graph, collectionsActives[pid].values.series,
+                                       $.extend(true, collections[pid].values.options, {
+                                         xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
+                                         yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
+                                       }));
+
+                               // don't fire event on the overview to prevent eternal loop
+                               if (vignettes[pid] !== undefined) {
+                                       vignettes[pid].setSelection(ranges, true);
+                               }
+                       });
+
+                       // raz sur double clic
+                       $(graphique).find('.graphResult').dblclick(function (event) {
+                               var graphique;
+                               graphique = $(event.target).parent().parent();
+                               pid = graphique.attr('id').substr(9);
+                               vignettesSelection[pid] = undefined;
+                               if (vignettes[pid] != undefined) {
+                                       vignettes[pid].clearSelection();
+                               }
+                               plots[pid] = $.plot(graphique.find('.graphResult'),
+                                       collectionsActives[pid].values.series,
+                                       $.extend(true, collections[pid].values.options, {
+                                               xaxis: { min: null, max: null },
+                                               yaxis: { min: null, max: null }
+                                       }));
+
+                       });
+
+
+                       // si une vignette est presente
+                       vignette = $(graphique).find('.graphVignette');
+
+                       if (vignette.length) {
+
+                               // zoom depuis la miniature
+                               vignette.bind("plotselected", function (event, ranges) {
+                                       graph = $(event.target);
+                                       pid = graph.parent().parent().attr('id').substr(9);
+                                       vignettesSelection[pid] = ranges;
+                                       plots[pid].setSelection(ranges);
+                               });
+
+                               // raz depuis la miniature sur double clic
+                               vignette.dblclick(function (event) {
+                                       var graphique;
+                                       graphique = $(event.target).parent().parent().parent();
+                                       pid = graphique.attr('id').substr(9);
+                                       vignettesSelection[pid] = undefined;
+
+                                       plots[pid] = $.plot(graphique.find('.graphResult'),
+                                               collectionsActives[pid].values.series,
+                                               $.extend(true, collections[pid].values.options, {
+                                                       xaxis: { min: null, max: null },
+                                                       yaxis: { min: null, max: null }
+                                               }));
+                               });
+                       }
+
+               }
+
+               /*
+                *
+                * Infobulles
+                *
+                */
+               var previousPoint = null;
+               function infobulle(graph, settings) {
+                       var options;
+                       options = {
+                               show:true
+                       };
+                       $.extend(true, options, settings);
+
+                       $(graph).bind("plothover", function (event, pos, item) {
+                               $("#x").text(pos.x.toFixed(2));
+                               $("#y").text(pos.y.toFixed(2));
+                               graph = $(event.target);
+                               pid = graph.parent().attr('id').substr(9);
+
+                               if (options.show) {
+                                       if (item) {
+                                               if (previousPoint != item.datapoint) {
+                                                       previousPoint = item.datapoint;
+
+                                                       $("#tooltip").remove();
+                                                       var x = item.datapoint[0],
+                                                               y = item.datapoint[1];
+
+                                                       var color = '';
+                                                       if(options.serie_color){
+                                                               color = item.series.color;
+                                                       }
+                                                       x = collectionsActives[pid].values.options.xaxis.ticksReels[item.dataIndex][1];
+
+                                                       showTooltip(item.pageX, item.pageY,
+                                                                               item.series.label + " [" + x + "] = " + y,
+                                                                               color);
+                                               }
+                                       }
+                                       else {
+                                               $("#tooltip").remove();
+                                               previousPoint = null;
+                                       }
+                               }
+                       });
+               }
+       }
+
+
+       // Adapte du site de Flot (exemple de visites)
+       // helper for returning the weekends in a period
+       function weekendAreas(axes) {
+               var markings = [];
+               var heure = 60 * 60 * 1000;
+               var jour = 24 * heure;
+
+               // les week ends
+               // go to the first Saturday
+               var d = new Date(axes.xaxis.min);
+               d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
+               d.setUTCSeconds(0);
+               d.setUTCMinutes(0);
+               d.setUTCHours(0);
+               var i = d.getTime();
+               do {
+                       markings.push({ xaxis: { from: i, to: i + 2*jour }, color: '#f6f6f6' });
+                       i += 7*jour;
+               } while (i < axes.xaxis.max);
+
+
+               // les mois et les ans...
+               $.each(yearsArea(axes), function(i,j){
+                       markings.push(j);
+               });
+
+               return markings;
+       }
+
+       // une grille pour afficher les mois et les ans...
+       function yearsArea(axes){
+       var markings = [];
+               var heure = 60 * 60 * 1000;
+               var jour = 24 * heure;
+
+               // les mois et les ans...
+               d = new Date(axes.xaxis.min);
+               y = d.getUTCFullYear();
+               m = d.getUTCMonth();
+               if (++m == 12) {m=0; ++y;}
+               d = new Date(Date.UTC(y,m,1,0,0,0));
+               do {
+                       i = d.getTime();
+                       if (m == 0) {couleur = '#CA5F18';}
+                       else {couleur = '#D7C2AF'; }
+                       markings.push({ xaxis: { from: i, to: i + jour}, color: couleur });
+                       if (++m == 12) {m=0; ++y;}
+                       d = new Date(Date.UTC(y,m,1,0,0,0));
+               } while (d.getTime() < axes.xaxis.max);
+
+        return markings;
+       }
+
+       /**
+        * Exemple adapte du site de Flot (exemple d'interactions)
+        * montrer les informations des points
+        *
+        * Arguments :
+        * x (Float) Coordonnee longitudinale de la bulle
+        * y (Float) Coordonnee latitudinale de la bulle
+        * contents (String) Le contenu de la bulle
+        * color La couleur de fond de l'infobulles
+        */
+    function showTooltip(x, y, contents, color) {
+        $('<div id="tooltip">' + contents + '</div>').css( {
+            top: y + 5,
+            left: x + 5,
+            opacity: 0.80,
+            background:color
+        }).addClass('tooltip_statistiques').appendTo("body").fadeIn(200);
+    }
+
+
+       // copie de la fonction de jquery.flot.js
+       // pour utilisation dans infobulle
+       function formatDate(d, fmt, monthNames) {
+               var leftPad = function(n) {
+                       n = "" + n;
+                       return n.length == 1 ? "0" + n : n;
+               };
+
+               var r = [];
+               var escape = false;
+               if (monthNames == null)
+                       monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+               for (var i = 0; i < fmt.length; ++i) {
+                       var c = fmt.charAt(i);
+
+                       if (escape) {
+                               switch (c) {
+                               case 'h': c = "" + d.getUTCHours(); break;
+                               case 'H': c = leftPad(d.getUTCHours()); break;
+                               case 'M': c = leftPad(d.getUTCMinutes()); break;
+                               case 'S': c = leftPad(d.getUTCSeconds()); break;
+                               case 'd': c = "" + d.getUTCDate(); break;
+                               case 'm': c = "" + (d.getUTCMonth() + 1); break;
+                               case 'y': c = "" + d.getUTCFullYear(); break;
+                               case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
+                               }
+                               r.push(c);
+                               escape = false;
+                       }
+                       else {
+                               if (c == "%")
+                                       escape = true;
+                               else
+                                       r.push(c);
+                       }
+               }
+               return r.join("");
+       }
+
+})(jQuery);