Files
redash/rd_ui/app/scripts/visualizations/base.js

227 lines
7.4 KiB
JavaScript

(function () {
var VisualizationProvider = function () {
this.visualizations = {};
this.visualizationTypes = {};
var defaultConfig = {
defaultOptions: {},
skipTypes: false,
editorTemplate: null
};
this.registerVisualization = function (config) {
var visualization = _.extend({}, defaultConfig, config);
// TODO: this is prone to errors; better refactor.
if (_.isEmpty(this.visualizations)) {
this.defaultVisualization = visualization;
}
this.visualizations[config.type] = visualization;
if (!config.skipTypes) {
this.visualizationTypes[config.name] = config.type;
}
};
this.getSwitchTemplate = function (property) {
var pattern = /(<[a-zA-Z0-9-]*?)( |>)/;
var mergedTemplates = _.reduce(this.visualizations, function (templates, visualization) {
if (visualization[property]) {
var ngSwitch = '$1 ng-switch-when="' + visualization.type + '" $2';
var template = visualization[property].replace(pattern, ngSwitch);
return templates + "\n" + template;
}
return templates;
}, "");
mergedTemplates = '<div ng-switch on="visualization.type">' + mergedTemplates + "</div>";
return mergedTemplates;
};
this.$get = ['$resource', function ($resource) {
var Visualization = $resource('api/visualizations/:id', {id: '@id'});
Visualization.visualizations = this.visualizations;
Visualization.visualizationTypes = this.visualizationTypes;
Visualization.renderVisualizationsTemplate = this.getSwitchTemplate('renderTemplate');
Visualization.editorTemplate = this.getSwitchTemplate('editorTemplate');
Visualization.defaultVisualization = this.defaultVisualization;
return Visualization;
}];
};
var VisualizationName = function(Visualization) {
return {
restrict: 'E',
scope: {
visualization: '='
},
template: '{{name}}',
replace: false,
link: function (scope) {
if (Visualization.visualizations[scope.visualization.type].name !== scope.visualization.name) {
scope.name = scope.visualization.name;
}
}
};
};
var VisualizationRenderer = function ($location, Visualization) {
return {
restrict: 'E',
scope: {
visualization: '=',
queryResult: '='
},
// TODO: using switch here (and in the options editor) might introduce errors and bad
// performance wise. It's better to eventually show the correct template based on the
// visualization type and not make the browser render all of them.
template: '<filters></filters>\n' + Visualization.renderVisualizationsTemplate,
replace: false,
link: function (scope) {
scope.$watch('queryResult && queryResult.getFilters()', function (filters) {
if (filters) {
scope.filters = filters;
}
});
}
};
};
var VisualizationOptionsEditor = function (Visualization) {
return {
restrict: 'E',
template: Visualization.editorTemplate,
replace: false
};
};
var Filters = function () {
return {
restrict: 'E',
templateUrl: '/views/visualizations/filters.html'
};
};
var FilterValueFilter = function() {
return function(value, filter) {
if (_.isArray(value)) {
value = value[0];
}
// TODO: deduplicate code with table.js:
if (filter.column.type === 'date') {
if (value && moment.isMoment(value)) {
return value.format(clientConfig.dateFormat);
}
} else if (filter.column.type === 'datetime') {
if (value && moment.isMoment(value)) {
return value.format(clientConfig.dateTimeFormat);
}
}
return value;
};
};
var EditVisualizationForm = function (Events, Visualization, growl) {
return {
restrict: 'E',
templateUrl: '/views/visualizations/edit_visualization.html',
replace: true,
scope: {
query: '=',
queryResult: '=',
originalVisualization: '=?',
onNewSuccess: '=?',
modalInstance: '=?'
},
link: function (scope) {
scope.visualization = angular.copy(scope.originalVisualization);
scope.editRawOptions = currentUser.hasPermission('edit_raw_chart');
scope.visTypes = Visualization.visualizationTypes;
scope.newVisualization = function () {
return {
'type': Visualization.defaultVisualization.type,
'name': Visualization.defaultVisualization.name,
'description': '',
'options': Visualization.defaultVisualization.defaultOptions
};
};
if (!scope.visualization) {
var unwatch = scope.$watch('query.id', function (queryId) {
if (queryId) {
unwatch();
scope.visualization = scope.newVisualization();
}
});
}
scope.$watch('visualization.type', function (type, oldType) {
// if not edited by user, set name to match type
if (type && oldType !== type && scope.visualization && !scope.visForm.name.$dirty) {
scope.visualization.name = _.string.titleize(scope.visualization.type);
}
if (type && oldType !== type && scope.visualization) {
scope.visualization.options = Visualization.visualizations[scope.visualization.type].defaultOptions;
}
});
scope.submit = function () {
if (scope.visualization.id) {
Events.record(currentUser, "update", "visualization", scope.visualization.id, {'type': scope.visualization.type});
} else {
Events.record(currentUser, "create", "visualization", null, {'type': scope.visualization.type});
}
scope.visualization.query_id = scope.query.id;
Visualization.save(scope.visualization, function success(result) {
growl.addSuccessMessage("Visualization saved");
var visIds = _.pluck(scope.query.visualizations, 'id');
var index = visIds.indexOf(result.id);
if (index > -1) {
scope.query.visualizations[index] = result;
} else {
// new visualization
scope.query.visualizations.push(result);
scope.onNewSuccess && scope.onNewSuccess(result);
}
scope.modalInstance.close();
}, function error() {
growl.addErrorMessage("Visualization could not be saved");
});
};
scope.close = function() {
if (scope.visForm.$dirty) {
if (confirm("Are you sure you want to close the editor without saving?")) {
scope.modalInstance.close();
}
} else {
scope.modalInstance.close();
}
}
}
};
};
angular.module('redash.visualization', [])
.provider('Visualization', VisualizationProvider)
.directive('visualizationRenderer', ['$location', 'Visualization', VisualizationRenderer])
.directive('visualizationOptionsEditor', ['Visualization', VisualizationOptionsEditor])
.directive('visualizationName', ['Visualization', VisualizationName])
.directive('filters', Filters)
.filter('filterValue', FilterValueFilter)
.directive('editVisulatizationForm', ['Events', 'Visualization', 'growl', EditVisualizationForm]);
})();