xref: /webtrees/resources/js/statistics.js (revision efd891708fa37ba31a16e45c3bc4f4b2ede4a28c)
166ce3d23SRico Sonntag/**
266ce3d23SRico Sonntag * webtrees: online genealogy
366ce3d23SRico Sonntag * Copyright (C) 2019 webtrees development team
466ce3d23SRico Sonntag * This program is free software: you can redistribute it and/or modify
566ce3d23SRico Sonntag * it under the terms of the GNU General Public License as published by
666ce3d23SRico Sonntag * the Free Software Foundation, either version 3 of the License, or
766ce3d23SRico Sonntag * (at your option) any later version.
866ce3d23SRico Sonntag * This program is distributed in the hope that it will be useful,
966ce3d23SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of
1066ce3d23SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1166ce3d23SRico Sonntag * GNU General Public License for more details.
1266ce3d23SRico Sonntag * You should have received a copy of the GNU General Public License
1366ce3d23SRico Sonntag * along with this program. If not, see <http://www.gnu.org/licenses/>.
1466ce3d23SRico Sonntag */
1566ce3d23SRico Sonntag
16*efd89170SGreg Roach'use strict';
1766ce3d23SRico Sonntag
18*efd89170SGreg Roachconst GOOGLE_CHARTS_LIB = 'https://www.gstatic.com/charts/loader.js';
1966ce3d23SRico Sonntag
2066ce3d23SRico Sonntag/**
2166ce3d23SRico Sonntag * Statistics class.
2266ce3d23SRico Sonntag */
23*efd89170SGreg Roachclass Statistics {
2466ce3d23SRico Sonntag  /**
2566ce3d23SRico Sonntag     * Constructor.
2666ce3d23SRico Sonntag     *
2766ce3d23SRico Sonntag     * @returns {Statistics}
2866ce3d23SRico Sonntag     */
29*efd89170SGreg Roach  constructor () {
3066ce3d23SRico Sonntag    // Create singleton instance
3166ce3d23SRico Sonntag    if (!Statistics.instance) {
3266ce3d23SRico Sonntag      Statistics.instance = this;
3366ce3d23SRico Sonntag
3466ce3d23SRico Sonntag      this.callbacks = [];
3566ce3d23SRico Sonntag      this.initialized = false;
3666ce3d23SRico Sonntag      this.loading = false;
3766ce3d23SRico Sonntag    }
3866ce3d23SRico Sonntag
3966ce3d23SRico Sonntag    return Statistics.instance;
4066ce3d23SRico Sonntag  }
4166ce3d23SRico Sonntag
4266ce3d23SRico Sonntag  /**
4366ce3d23SRico Sonntag     * Initializes the google chart engine. Loads the chart lib only once.
4466ce3d23SRico Sonntag     *
4566ce3d23SRico Sonntag     * @param {String} locale Locale, e.g. en, de, ...
4666ce3d23SRico Sonntag     */
47*efd89170SGreg Roach  init (locale) {
4866ce3d23SRico Sonntag    if (this.loading || this.initialized) {
4966ce3d23SRico Sonntag      return;
5066ce3d23SRico Sonntag    }
5166ce3d23SRico Sonntag
5266ce3d23SRico Sonntag    var that = this;
5366ce3d23SRico Sonntag
5466ce3d23SRico Sonntag    Promise.all([
5566ce3d23SRico Sonntag      this.load(GOOGLE_CHARTS_LIB)
5666ce3d23SRico Sonntag    ]).then(function () {
5766ce3d23SRico Sonntag      google.charts.load(
58*efd89170SGreg Roach        'current',
5966ce3d23SRico Sonntag        {
60*efd89170SGreg Roach          packages: [
61*efd89170SGreg Roach            'corechart',
62*efd89170SGreg Roach            'geochart',
63*efd89170SGreg Roach            'bar'
6466ce3d23SRico Sonntag          ],
65*efd89170SGreg Roach          language: locale,
6666ce3d23SRico Sonntag          // Note: you will need to get a mapsApiKey for your project.
6766ce3d23SRico Sonntag          // See: https://developers.google.com/chart/interactive/docs/basic_load_libs#load-settings
68*efd89170SGreg Roach          mapsApiKey: ''
6966ce3d23SRico Sonntag        }
7066ce3d23SRico Sonntag      );
7166ce3d23SRico Sonntag
7266ce3d23SRico Sonntag      google.charts.setOnLoadCallback(function () {
7366ce3d23SRico Sonntag        that.callbacks.forEach(function (element) {
7466ce3d23SRico Sonntag          element();
7566ce3d23SRico Sonntag        });
7666ce3d23SRico Sonntag      });
7766ce3d23SRico Sonntag
7866ce3d23SRico Sonntag      that.initialized = true;
7966ce3d23SRico Sonntag    }).catch(function (error) {
8066ce3d23SRico Sonntag      console.log(error);
8166ce3d23SRico Sonntag    });
8266ce3d23SRico Sonntag  }
8366ce3d23SRico Sonntag
8466ce3d23SRico Sonntag  /**
8566ce3d23SRico Sonntag     * Dynamically loads a script by the given URL.
8666ce3d23SRico Sonntag     *
8766ce3d23SRico Sonntag     * @param {String} url
8866ce3d23SRico Sonntag     *
8966ce3d23SRico Sonntag     * @returns {Promise}
9066ce3d23SRico Sonntag     */
91*efd89170SGreg Roach  load (url) {
9266ce3d23SRico Sonntag    if (this.loading) {
9366ce3d23SRico Sonntag      return;
9466ce3d23SRico Sonntag    }
9566ce3d23SRico Sonntag
9666ce3d23SRico Sonntag    this.loading = true;
9766ce3d23SRico Sonntag
9866ce3d23SRico Sonntag    return new Promise(function (resolve, reject) {
99*efd89170SGreg Roach      const script = document.createElement('script');
10066ce3d23SRico Sonntag
10166ce3d23SRico Sonntag      script.async = true;
10266ce3d23SRico Sonntag      script.onload = function () {
10366ce3d23SRico Sonntag        resolve(url);
104*efd89170SGreg Roach      };
10566ce3d23SRico Sonntag      script.onerror = function () {
10666ce3d23SRico Sonntag        reject(url);
107*efd89170SGreg Roach      };
10866ce3d23SRico Sonntag
10966ce3d23SRico Sonntag      script.src = url;
11066ce3d23SRico Sonntag      document.body.appendChild(script);
11166ce3d23SRico Sonntag    });
11266ce3d23SRico Sonntag  }
11366ce3d23SRico Sonntag
11466ce3d23SRico Sonntag  /**
11566ce3d23SRico Sonntag     * Adds the given callback method to the callback stack or add it directly to
11666ce3d23SRico Sonntag     * the google charts interface once the chart engine is up and running.
11766ce3d23SRico Sonntag     *
11866ce3d23SRico Sonntag     * @param {Function} callback
11966ce3d23SRico Sonntag     */
120*efd89170SGreg Roach  addCallback (callback) {
12166ce3d23SRico Sonntag    if (this.initialized) {
12266ce3d23SRico Sonntag      google.charts.setOnLoadCallback(callback);
12366ce3d23SRico Sonntag    } else {
12466ce3d23SRico Sonntag      this.callbacks.push(callback);
12566ce3d23SRico Sonntag    }
12666ce3d23SRico Sonntag
12766ce3d23SRico Sonntag    $(window).resize(function () {
12866ce3d23SRico Sonntag      callback();
12966ce3d23SRico Sonntag    });
13066ce3d23SRico Sonntag  }
13166ce3d23SRico Sonntag
13266ce3d23SRico Sonntag  /**
13366ce3d23SRico Sonntag     * Draws a google chart.
13466ce3d23SRico Sonntag     *
13566ce3d23SRico Sonntag     * @param {String} containerId
13666ce3d23SRico Sonntag     * @param {String} chartType
13766ce3d23SRico Sonntag     * @param {Array}  dataTable
13866ce3d23SRico Sonntag     * @param {Object} options
13966ce3d23SRico Sonntag     */
140*efd89170SGreg Roach  drawChart (containerId, chartType, data, options) {
141*efd89170SGreg Roach    const dataTable = google.visualization.arrayToDataTable(data);
14266ce3d23SRico Sonntag
143*efd89170SGreg Roach    const wrapper = new google.visualization.ChartWrapper({
14466ce3d23SRico Sonntag      chartType: chartType,
14566ce3d23SRico Sonntag      dataTable: dataTable,
14666ce3d23SRico Sonntag      options: options,
14766ce3d23SRico Sonntag      containerId: containerId
14866ce3d23SRico Sonntag    });
14966ce3d23SRico Sonntag
15066ce3d23SRico Sonntag    wrapper.draw();
15166ce3d23SRico Sonntag  }
15266ce3d23SRico Sonntag
15366ce3d23SRico Sonntag  /**
15466ce3d23SRico Sonntag     * Draws a pie chart.
15566ce3d23SRico Sonntag     *
15666ce3d23SRico Sonntag     * @param {String} elementId The element id of the HTML element the chart is rendered too
15766ce3d23SRico Sonntag     * @param {Array}  data      The chart data array
15866ce3d23SRico Sonntag     * @param {Object} options   The chart specific options to overwrite the default ones
15966ce3d23SRico Sonntag     */
160*efd89170SGreg Roach  drawPieChart (elementId, data, options) {
16166ce3d23SRico Sonntag    // Default chart options
162*efd89170SGreg Roach    const defaults = {
163*efd89170SGreg Roach      title: '',
164*efd89170SGreg Roach      height: '100%',
165*efd89170SGreg Roach      width: '100%',
16666ce3d23SRico Sonntag      pieStartAngle: 0,
167*efd89170SGreg Roach      pieSliceText: 'none',
16866ce3d23SRico Sonntag      pieSliceTextStyle: {
169*efd89170SGreg Roach        color: '#777'
17066ce3d23SRico Sonntag      },
17166ce3d23SRico Sonntag      pieHole: 0.4, // Donut
17266ce3d23SRico Sonntag      // is3D: true,  // 3D (not together with pieHole)
17366ce3d23SRico Sonntag      legend: {
174*efd89170SGreg Roach        alignment: 'center',
17566ce3d23SRico Sonntag        // Flickers on mouseover :(
176*efd89170SGreg Roach        labeledValueText: 'value',
177*efd89170SGreg Roach        position: 'labeled'
17866ce3d23SRico Sonntag      },
17966ce3d23SRico Sonntag      chartArea: {
18066ce3d23SRico Sonntag        left: 0,
181*efd89170SGreg Roach        top: '5%',
182*efd89170SGreg Roach        height: '90%',
183*efd89170SGreg Roach        width: '100%'
18466ce3d23SRico Sonntag      },
18566ce3d23SRico Sonntag      tooltip: {
186*efd89170SGreg Roach        trigger: 'none',
187*efd89170SGreg Roach        text: 'both'
18866ce3d23SRico Sonntag      },
189*efd89170SGreg Roach      backgroundColor: 'transparent',
19066ce3d23SRico Sonntag      colors: []
19166ce3d23SRico Sonntag    };
19266ce3d23SRico Sonntag
19366ce3d23SRico Sonntag    // Merge default with provided options
19466ce3d23SRico Sonntag    options = Object.assign(defaults, options);
19566ce3d23SRico Sonntag
19666ce3d23SRico Sonntag    // Create and draw the chart
197*efd89170SGreg Roach    this.drawChart(elementId, 'PieChart', data, options);
19866ce3d23SRico Sonntag  }
19966ce3d23SRico Sonntag
20066ce3d23SRico Sonntag  /**
20166ce3d23SRico Sonntag     * Draws a column chart.
20266ce3d23SRico Sonntag     *
20366ce3d23SRico Sonntag     * @param {String} elementId The element id of the HTML element the chart is rendered too
20466ce3d23SRico Sonntag     * @param {Array}  data      The chart data array
20566ce3d23SRico Sonntag     * @param {Object} options   The chart specific options to overwrite the default ones
20666ce3d23SRico Sonntag     */
207*efd89170SGreg Roach  drawColumnChart (elementId, data, options) {
20866ce3d23SRico Sonntag    // Default chart options
209*efd89170SGreg Roach    const defaults = {
210*efd89170SGreg Roach      title: '',
211*efd89170SGreg Roach      subtitle: '',
21266ce3d23SRico Sonntag      titleTextStyle: {
213*efd89170SGreg Roach        color: '#757575',
214*efd89170SGreg Roach        fontName: 'Roboto',
215*efd89170SGreg Roach        fontSize: '16px',
21666ce3d23SRico Sonntag        bold: false,
21766ce3d23SRico Sonntag        italic: false
21866ce3d23SRico Sonntag      },
219*efd89170SGreg Roach      height: '100%',
220*efd89170SGreg Roach      width: '100%',
22166ce3d23SRico Sonntag      vAxis: {
222*efd89170SGreg Roach        title: ''
22366ce3d23SRico Sonntag      },
22466ce3d23SRico Sonntag      hAxis: {
225*efd89170SGreg Roach        title: ''
22666ce3d23SRico Sonntag      },
22766ce3d23SRico Sonntag      legend: {
228*efd89170SGreg Roach        position: 'none'
22966ce3d23SRico Sonntag      },
230*efd89170SGreg Roach      backgroundColor: 'transparent'
23166ce3d23SRico Sonntag    };
23266ce3d23SRico Sonntag
23366ce3d23SRico Sonntag    // Merge default with provided options
23466ce3d23SRico Sonntag    options = Object.assign(defaults, options);
23566ce3d23SRico Sonntag
23666ce3d23SRico Sonntag    // Create and draw the chart
237*efd89170SGreg Roach    this.drawChart(elementId, 'ColumnChart', data, options);
23866ce3d23SRico Sonntag  }
23966ce3d23SRico Sonntag
24066ce3d23SRico Sonntag  /**
24166ce3d23SRico Sonntag     * Draws a combo chart.
24266ce3d23SRico Sonntag     *
24366ce3d23SRico Sonntag     * @param {String} elementId The element id of the HTML element the chart is rendered too
24466ce3d23SRico Sonntag     * @param {Array}  data      The chart data array
24566ce3d23SRico Sonntag     * @param {Object} options   The chart specific options to overwrite the default ones
24666ce3d23SRico Sonntag     */
247*efd89170SGreg Roach  drawComboChart (elementId, data, options) {
24866ce3d23SRico Sonntag    // Default chart options
249*efd89170SGreg Roach    const defaults = {
250*efd89170SGreg Roach      title: '',
251*efd89170SGreg Roach      subtitle: '',
25266ce3d23SRico Sonntag      titleTextStyle: {
253*efd89170SGreg Roach        color: '#757575',
254*efd89170SGreg Roach        fontName: 'Roboto',
255*efd89170SGreg Roach        fontSize: '16px',
25666ce3d23SRico Sonntag        bold: false,
25766ce3d23SRico Sonntag        italic: false
25866ce3d23SRico Sonntag      },
259*efd89170SGreg Roach      height: '100%',
260*efd89170SGreg Roach      width: '100%',
26166ce3d23SRico Sonntag      vAxis: {
262*efd89170SGreg Roach        title: ''
26366ce3d23SRico Sonntag      },
26466ce3d23SRico Sonntag      hAxis: {
265*efd89170SGreg Roach        title: ''
26666ce3d23SRico Sonntag      },
26766ce3d23SRico Sonntag      legend: {
268*efd89170SGreg Roach        position: 'none'
26966ce3d23SRico Sonntag      },
270*efd89170SGreg Roach      seriesType: 'bars',
27166ce3d23SRico Sonntag      series: {
27266ce3d23SRico Sonntag        2: {
273*efd89170SGreg Roach          type: 'line'
27466ce3d23SRico Sonntag        }
27566ce3d23SRico Sonntag      },
27666ce3d23SRico Sonntag      colors: [],
277*efd89170SGreg Roach      backgroundColor: 'transparent'
27866ce3d23SRico Sonntag    };
27966ce3d23SRico Sonntag
28066ce3d23SRico Sonntag    // Merge default with provided options
28166ce3d23SRico Sonntag    options = Object.assign(defaults, options);
28266ce3d23SRico Sonntag
28366ce3d23SRico Sonntag    // Create and draw the chart
284*efd89170SGreg Roach    this.drawChart(elementId, 'ComboChart', data, options);
28566ce3d23SRico Sonntag  }
28666ce3d23SRico Sonntag
28766ce3d23SRico Sonntag  /**
28866ce3d23SRico Sonntag     * Draws a geo chart.
28966ce3d23SRico Sonntag     *
29066ce3d23SRico Sonntag     * @param {String} elementId The element id of the HTML element the chart is rendered too
29166ce3d23SRico Sonntag     * @param {Array}  data      The chart data array
29266ce3d23SRico Sonntag     * @param {Object} options   The chart specific options to overwrite the default ones
29366ce3d23SRico Sonntag     */
294*efd89170SGreg Roach  drawGeoChart (elementId, data, options) {
29566ce3d23SRico Sonntag    // Default chart options
296*efd89170SGreg Roach    const defaults = {
297*efd89170SGreg Roach      title: '',
298*efd89170SGreg Roach      subtitle: '',
299*efd89170SGreg Roach      height: '100%',
300*efd89170SGreg Roach      width: '100%'
30166ce3d23SRico Sonntag    };
30266ce3d23SRico Sonntag
30366ce3d23SRico Sonntag    // Merge default with provided options
30466ce3d23SRico Sonntag    options = Object.assign(defaults, options);
30566ce3d23SRico Sonntag
30666ce3d23SRico Sonntag    // Create and draw the chart
307*efd89170SGreg Roach    this.drawChart(elementId, 'GeoChart', data, options);
30866ce3d23SRico Sonntag  }
30966ce3d23SRico Sonntag}
31066ce3d23SRico Sonntag
31166ce3d23SRico Sonntag// Create singleton instance of class
31266ce3d23SRico Sonntagconst statistics = new Statistics();
313