diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 4269acc..0000000 --- a/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -_site/ -.sass-cache/ -.jekyll-metadata -.DS_Store -.Rproj.user -.Rhistory -.spyproject/ -*__pycache__/ -*.Rproj -Gemfile.lock -deploy_key* -deploy_key*.pub -*.keybase diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f0f2d19..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: ruby -rvm: -- 2.4.0 -dist: trusty -sudo: false -branches: - only: - - master - - develop -env: - global: - - NOKOGIRI_USE_SYSTEM_LIBRARIES=true -# Build. _site directory is the artifact -before_script: -- chmod +x ./scripts/*.sh -- chmod +x ./scripts/deploy/*.sh -script: -- scripts/cibuild.sh -# Deploy. Staging goes back to gh-pages here and prod to external repo -deploy: -- provider: script - script: scripts/deploy/deploy_staging.sh - skip_cleanup: true - on: - branch: develop -- provider: script - script: scripts/deploy/deploy.sh - skip_cleanup: true - on: - branch: master diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index bf80ba7..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -// Place your settings in this file to overwrite default and user settings. -{ - "editor.detectIndentation": false, - "editor.tabSize": 2 -} diff --git a/README.md b/README.md index 1750312..af4ec9a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Sustainable Development Goals (SDGs) Website Theme +# Sustainable Development Goals (SDG) - Theme -This Jekyll theme is designed to work with the SDGs national reporting platform. +This is a platform for collecting and disseminating data for the Sustainable Development Goal global indicators. -## Installation +## Documentation -It is assumed this theme is being used via the `remote_theme` setting from the sdg-indicators repository. +See the [docs folder](docs/index.md) for more information. diff --git a/assets/js/accessibility.js b/_includes/assets/js/accessibility.js similarity index 100% rename from assets/js/accessibility.js rename to _includes/assets/js/accessibility.js diff --git a/assets/js/chartjs/rescaler.js b/_includes/assets/js/chartjs/rescaler.js similarity index 100% rename from assets/js/chartjs/rescaler.js rename to _includes/assets/js/chartjs/rescaler.js diff --git a/assets/js/event.js b/_includes/assets/js/event.js similarity index 100% rename from assets/js/event.js rename to _includes/assets/js/event.js diff --git a/assets/js/indicatorController.js b/_includes/assets/js/indicatorController.js similarity index 100% rename from assets/js/indicatorController.js rename to _includes/assets/js/indicatorController.js diff --git a/assets/js/indicatorDataStore.js b/_includes/assets/js/indicatorDataStore.js similarity index 100% rename from assets/js/indicatorDataStore.js rename to _includes/assets/js/indicatorDataStore.js diff --git a/assets/js/indicatorModel.js b/_includes/assets/js/indicatorModel.js similarity index 100% rename from assets/js/indicatorModel.js rename to _includes/assets/js/indicatorModel.js diff --git a/assets/js/indicatorView.js b/_includes/assets/js/indicatorView.js similarity index 91% rename from assets/js/indicatorView.js rename to _includes/assets/js/indicatorView.js index 49d9e51..5aa9f91 100644 --- a/assets/js/indicatorView.js +++ b/_includes/assets/js/indicatorView.js @@ -1,27 +1,27 @@ var indicatorView = function (model, options) { - + "use strict"; - + var view_obj = this; this._model = model; - + this._chartInstance = undefined; this._rootElement = options.rootElement; this._tableColumnDefs = options.tableColumnDefs; this._mapView = undefined; this._legendElement = options.legendElement; - + var chartHeight = screen.height < options.maxChartHeight ? screen.height : options.maxChartHeight; - + $('.plot-container', this._rootElement).css('height', chartHeight + 'px'); - + $(document).ready(function() { $(view_obj._rootElement).find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { if($(e.target).attr('href') == '#tableview') { setDataTableWidth($(view_obj._rootElement).find('#selectionsTable table')); } else { $($.fn.dataTable.tables(true)).css('width', '100%'); - $($.fn.dataTable.tables(true)).DataTable().columns.adjust().draw(); + $($.fn.dataTable.tables(true)).DataTable().columns.adjust().draw(); } }); @@ -33,30 +33,30 @@ var indicatorView = function (model, options) { meta = ci.getDatasetMeta(index); meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null; - ci.update(); + ci.update(); }); }); - + this._model.onDataComplete.attach(function (sender, args) { - + if(view_obj._model.showData) { - + $('#dataset-size-warning')[args.datasetCountExceedsMax ? 'show' : 'hide'](); - + if(!view_obj._chartInstance) { view_obj.createPlot(args); } else { view_obj.updatePlot(args); } } - + view_obj.createSelectionsTable(args); }); - + this._model.onNoHeadlineData.attach(function() { $('#fields .variable-options :checkbox:eq(0)').trigger('click'); }); - + this._model.onSeriesComplete.attach(function(sender, args) { view_obj.initialiseSeries(args); @@ -78,53 +78,53 @@ var indicatorView = function (model, options) { // selector.parent().removeClass('disabled').removeAttr('title'); // } }); - + this._model.onUnitsComplete.attach(function(sender, args) { view_obj.initialiseUnits(args); }); - + this._model.onUnitsSelectedChanged.attach(function(sender, args) { // update the plot's y axis label // update the data }); - + this._model.onFieldsCleared.attach(function(sender, args) { $(view_obj._rootElement).find(':checkbox').prop('checked', false); $(view_obj._rootElement).find('#clear').addClass('disabled'); - + // reset available/unavailable fields updateWithSelectedFields(); - + // #246 $(view_obj._rootElement).find('.selected').css('width', '0'); // end of #246 }); - + this._model.onSelectionUpdate.attach(function(sender, args) { $(view_obj._rootElement).find('#clear')[args.selectedFields.length ? 'removeClass' : 'addClass']('disabled'); - + // loop through the available fields: $('.variable-selector').each(function(index, element) { var currentField = $(element).data('field'); - + // any info? var match = _.findWhere(args.selectedFields, { field : currentField }); var element = $(view_obj._rootElement).find('.variable-selector[data-field="' + currentField + '"]'); var width = match ? (Number(match.values.length / element.find('.variable-options label').length) * 100) + '%' : '0'; - + $(element).find('.bar .selected').css('width', width); - + // is this an allowed field: $(element)[_.contains(args.allowedFields, currentField) ? 'removeClass' : 'addClass']('disallowed'); }); }); - + this._model.onFieldsStatusUpdated.attach(function (sender, args) { //console.log('updating field states with: ', args); - + // reset: $(view_obj._rootElement).find('label').removeClass('selected possible excluded'); - + _.each(args.data, function(fieldGroup) { _.each(fieldGroup.values, function(fieldItem) { var element = $(view_obj._rootElement).find(':checkbox[value="' + fieldItem.value + '"][data-field="' + fieldGroup.field + '"]'); @@ -133,11 +133,11 @@ var indicatorView = function (model, options) { // Indicate whether the fieldGroup had any data. var fieldGroupElement = $(view_obj._rootElement).find('.variable-selector[data-field="' + fieldGroup.field + '"]'); fieldGroupElement.attr('data-has-data', fieldGroup.hasData); - + // Re-sort the items. view_obj.sortFieldGroup(fieldGroupElement); }); - + _.each(args.selectionStates, function(ss) { // find the appropriate 'bar' var element = $(view_obj._rootElement).find('.variable-selector[data-field="' + ss.field + '"]'); @@ -146,25 +146,25 @@ var indicatorView = function (model, options) { element.find('.bar .excluded').css('width', ss.fieldSelection.excludedState + '%'); }); }); - + $(this._rootElement).on('click', '#clear', function() { view_obj._model.clearSelectedFields(); }); - + $(this._rootElement).on('click', '#fields label', function (e) { - + if(!$(this).closest('.variable-options').hasClass('disallowed')) { $(this).find(':checkbox').trigger('click'); } - + e.preventDefault(); e.stopPropagation(); }); - + $(this._rootElement).on('change', '#units input', function() { view_obj._model.updateSelectedUnit($(this).val()); }); - + // generic helper function, used by clear all/select all and individual checkbox changes: var updateWithSelectedFields = function() { view_obj._model.updateSelectedFields(_.chain(_.map($('#fields input:checked'), function (fieldValue) { @@ -179,11 +179,11 @@ var indicatorView = function (model, options) { }; }).value()); } - + $(this._rootElement).on('click', '.variable-options button', function(e) { var type = $(this).data('type'); var $options = $(this).closest('.variable-options').find(':checkbox'); - + // The clear button can clear all checkboxes. if (type == 'clear') { $options.prop('checked', false); @@ -192,39 +192,39 @@ var indicatorView = function (model, options) { if (type == 'select') { $options.parent().not('[data-has-data=false]').find(':checkbox').prop('checked', true) } - + updateWithSelectedFields(); - + e.stopPropagation(); }); - + $(this._rootElement).on('click', ':checkbox', function(e) { - + // don't permit excluded selections: if($(this).parent().hasClass('excluded') || $(this).closest('.variable-selector').hasClass('disallowed')) { return; } - + updateWithSelectedFields(); - + e.stopPropagation(); }); - + $(this._rootElement).on('click', '.variable-selector', function(e) { - + var options = $(this).find('.variable-options'); var optionsVisible = options.is(':visible'); $(options)[optionsVisible ? 'hide' : 'show'](); - + e.stopPropagation(); }); - + this.initialiseSeries = function(args) { if(args.series.length) { var template = _.template($("#item_template").html()); if(!$('button#clear').length) { - $('').insertBefore('#fields'); + $('').insertBefore('#fields'); } $('#fields').html(template({ @@ -234,42 +234,42 @@ var indicatorView = function (model, options) { })); $(this._rootElement).removeClass('no-series'); - + } else { $(this._rootElement).addClass('no-series'); } }; - + this.initialiseUnits = function(args) { var template = _.template($('#units_template').html()), units = args.units || []; - + $('#units').html(template({ units: units })); if(!units.length) { - $(this._rootElement).addClass('no-units'); + $(this._rootElement).addClass('no-units'); } }; - + this.updatePlot = function(chartInfo) { view_obj._chartInstance.data.datasets = chartInfo.datasets; - + if(chartInfo.selectedUnit) { view_obj._chartInstance.options.scales.yAxes[0].scaleLabel.labelString = chartInfo.selectedUnit; } - + view_obj._chartInstance.update(1000, true); $(this._legendElement).html(view_obj._chartInstance.generateLegend()); }; - + this.createPlot = function (chartInfo) { - + var that = this; - - this._chartInstance = new Chart($(this._rootElement).find('canvas'), { + + var chartConfig = { type: this._model.graphType, data: chartInfo, options: { @@ -299,7 +299,7 @@ var indicatorView = function (model, options) { layout: { padding: { top: 20, - // default of 85, but do a rough line count based on 150 + // default of 85, but do a rough line count based on 150 // characters per line * 20 pixels per row bottom: that._model.footnote ? (20 * (that._model.footnote.length / 150)) + 85 : 85 } @@ -314,7 +314,7 @@ var indicatorView = function (model, options) { text.push(dataset.label); text.push(''); }); - + text.push(''); return text.join(''); }, @@ -332,8 +332,13 @@ var indicatorView = function (model, options) { scaler: {} } } - }); - + }; + if (typeof chartConfigOverrides !== 'undefined') { + $.extend(true, chartConfig, chartConfigOverrides); + } + + this._chartInstance = new Chart($(this._rootElement).find('canvas'), chartConfig); + Chart.pluginService.register({ afterDraw: function(chart) { var $canvas = $(that._rootElement).find('canvas'), @@ -341,22 +346,22 @@ var indicatorView = function (model, options) { canvas = $canvas.get(0), textRowHeight = 20, ctx = canvas.getContext("2d"); - + ctx.font = font; ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#6e6e6e'; - + var getLinesFromText = function(text) { var width = parseInt($canvas.css('width')), //width(), lines = [], line = '', lineTest = '', words = text.split(' '); - + for (var i = 0, len = words.length; i < len; i++) { lineTest = line + words[i] + ' '; - + // Check total width of line or last word if (ctx.measureText(lineTest).width > width) { // Record and reset the current line @@ -366,65 +371,70 @@ var indicatorView = function (model, options) { line = lineTest; } } - + // catch left overs: if (line.length > 0) { lines.push(line.trim()); } - + return lines; }; - + function putTextOutputs(textOutputs, x) { var y = $canvas.height() - 10 - ((textOutputs.length - 1) * textRowHeight); - + _.each(textOutputs, function(textOutput) { ctx.fillText(textOutput, x, y); y += textRowHeight; }); } - + // TODO Merge this with the that.footerFields object used by table - var graphFooterItems = [ - 'Source: ' + (that._model.dataSource ? that._model.dataSource : ''), - 'Geographical Area: ' + (that._model.geographicalArea ? that._model.geographicalArea : ''), - 'Unit of Measurement: ' + (that._model.measurementUnit ? that._model.measurementUnit : '') - ]; - + var graphFooterItems = []; + if (that._model.dataSource) { + graphFooterItems.push(translations.indicator.source + ': ' + that._model.dataSource); + } + if (that._model.geographicalArea) { + graphFooterItems.push(translations.indicator.geographical_area + ': ' + that._model.geographicalArea); + } + if (that._model.measurementUnit) { + graphFooterItems.push(translations.indicator.unit_of_measurement + ': ' + that._model.measurementUnit); + } + if(that._model.footnote) { var footnoteRows = getLinesFromText('Footnote: ' + that._model.footnote); graphFooterItems = graphFooterItems.concat(footnoteRows); - + if(footnoteRows.length > 1) { //that._chartInstance.options.layout.padding.bottom += textRowHeight * footnoteRows.length; that._chartInstance.resize(parseInt($canvas.css('width')), parseInt($canvas.css('height')) + textRowHeight * footnoteRows.length); that._chartInstance.resize(); } } - + putTextOutputs(graphFooterItems, 0); } }); $(this._legendElement).html(view_obj._chartInstance.generateLegend()); }; - + this.toCsv = function (tableData) { var lines = [], headings = _.map(tableData.headings, function(heading) { return '"' + heading + '"'; }); - + lines.push(headings.join(',')); - + _.each(tableData.data, function (dataValues) { var line = []; - + _.each(headings, function (heading, index) { line.push(dataValues[index]); }); - + lines.push(line.join(',')); }); - + return lines.join('\n'); }; @@ -442,11 +452,11 @@ var indicatorView = function (model, options) { } break; } - } + } }); table.removeAttr('style width'); - + var totalWidth = 0; table.find('th').each(function() { if($(this).data('width')) { @@ -465,7 +475,7 @@ var indicatorView = function (model, options) { table.css('width', '100%'); } }; - + var initialiseDataTable = function(el) { var datatables_options = options.datatables_options || { paging: false, @@ -477,12 +487,12 @@ var indicatorView = function (model, options) { }, table = $(el).find('table'); datatables_options.aaSorting = []; - + table.DataTable(datatables_options); setDataTableWidth(table); }; - + this.createSelectionsTable = function(chartInfo) { this.createTable(chartInfo.selectionsTable, chartInfo.indicatorId, '#selectionsTable', true); this.createTableFooter(chartInfo.footerFields, '#selectionsTable'); @@ -493,16 +503,23 @@ var indicatorView = function (model, options) { this.createDownloadButton(chartInfo.selectionsTable, 'Chart', chartInfo.indicatorId, '#chartSelectionDownload'); this.createSourceButton(chartInfo.shortIndicatorId, '#chartSelectionDownload'); }; - + this.createDownloadButton = function(table, name, indicatorId, el) { if(window.Modernizr.blobconstructor) { - $(el).append($('').text('Download ' + name + ' CSV') + var downloadKey = 'download_csv'; + if (name == 'Chart') { + downloadKey = 'download_chart'; + } + if (name == 'Table') { + downloadKey = 'download_table'; + } + $(el).append($('').text(translations.indicator[downloadKey]) .attr({ 'href': URL.createObjectURL(new Blob([this.toCsv(table)], { type: 'text/csv' })), 'download': indicatorId + '.csv', - 'title': 'Download as CSV', + 'title': translations.indicator.download_csv_title, 'class': 'btn btn-primary btn-download', 'tabindex': 0 }) @@ -510,30 +527,30 @@ var indicatorView = function (model, options) { } else { var headlineId = indicatorId.replace('indicator', 'headline'); var id = indicatorId.replace('indicator', ''); - $(el).append($('').text('Download Headline CSV') + $(el).append($('').text(translations.indicator.download_headline) .attr({ - 'href': '{{ site.remotedatabaseurl }}/headline/' + id + '.csv', + 'href': remoteDataBaseUrl + '/headline/' + id + '.csv', 'download': headlineId + '.csv', - 'title': 'Download headline data as CSV', + 'title': translations.indicator.download_headline_title, 'class': 'btn btn-primary btn-download', 'tabindex': 0 })); } } - + this.createSourceButton = function(indicatorId, el) { - $(el).append($('').text('Download Source CSV') + $(el).append($('').text(translations.indicator.download_source) .attr({ - 'href': '{{ site.remotedatabaseurl }}/data/' + indicatorId + '.csv', + 'href': remoteDataBaseUrl + '/data/' + indicatorId + '.csv', 'download': indicatorId + '.csv', - 'title': 'Download source data as CSV', + 'title': translations.indicator.download_source_title, 'class': 'btn btn-primary btn-download', 'tabindex': 0 })); } - + this.createTable = function(table, indicatorId, el) { - + options = options || {}; var that = this, csv_path = options.csv_path, @@ -543,19 +560,19 @@ var indicatorView = function (model, options) { delimiter: '"' }, table_class = options.table_class || 'table table-hover'; - + // clear: $(el).html(''); - + if(table && table.data.length) { var currentTable = $('
| ' + year + ' | '; }).join('') + - '|
|---|---|
| ' + value + ' | '; }).join('') + + _.map(_.pluck(yearValues, 'Year'), function(year) { return '' + year + ' | '; }).join('') + + '
| ' + value + ' | '; }).join('') + '