Files
superset2/superset/assets/javascripts/modules/superset.js
Maxime Beauchemin 24a2f5b8f0 [big number] various improvements (#2912)
* [big number] various improvements

* dynamic number of X axis ticks based on width to prevent label overlap
* corrected overflow on the x axis
* improved tooltips (precise arrow and visible data point circle on hover)

* Fixing tooltips in heatmap viz
2017-06-08 08:50:30 -07:00

256 lines
7.8 KiB
JavaScript

/* eslint camel-case: 0 */
import $ from 'jquery';
import Mustache from 'mustache';
import vizMap from '../../visualizations/main';
import { getExploreUrl } from '../explore/exploreUtils';
import { applyDefaultFormData } from '../explore/stores/store';
import { QUERY_TIMEOUT_THRESHOLD } from '../constants';
const utils = require('./utils');
/* eslint wrap-iife: 0*/
const px = function () {
let slice;
function getParam(name) {
/* eslint no-useless-escape: 0 */
const formattedName = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + formattedName + '=([^&#]*)');
const results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
function initFavStars() {
const baseUrl = '/superset/favstar/';
// Init star behavihor for favorite
function show() {
if ($(this).hasClass('selected')) {
$(this).html('<i class="fa fa-star"></i>');
} else {
$(this).html('<i class="fa fa-star-o"></i>');
}
}
$('.favstar')
.attr('title', 'Click to favorite/unfavorite')
.css('cursor', 'pointer')
.each(show)
.each(function () {
let url = baseUrl + $(this).attr('class_name');
const star = this;
url += '/' + $(this).attr('obj_id') + '/';
$.getJSON(url + 'count/', function (data) {
if (data.count > 0) {
$(star).addClass('selected').each(show);
}
});
})
.click(function () {
$(this).toggleClass('selected');
let url = baseUrl + $(this).attr('class_name');
url += '/' + $(this).attr('obj_id') + '/';
if ($(this).hasClass('selected')) {
url += 'select/';
} else {
url += 'unselect/';
}
$.get(url);
$(this).each(show);
})
.tooltip();
}
const Slice = function (data, datasource, controller) {
const token = $('#token_' + data.slice_id);
const containerId = 'con_' + data.slice_id;
const selector = '#' + containerId;
const container = $(selector);
const sliceId = data.slice_id;
const formData = applyDefaultFormData(data.form_data);
slice = {
data,
formData,
container,
containerId,
datasource,
selector,
getWidgetHeader() {
return this.container.parents('div.widget').find('.chart-header');
},
render_template(s) {
const context = {
width: this.width,
height: this.height,
};
return Mustache.render(s, context);
},
jsonEndpoint() {
return this.endpoint('json');
},
endpoint(endpointType = 'json') {
const formDataExtra = Object.assign({}, formData);
const flts = controller.effectiveExtraFilters(sliceId);
if (flts) {
formDataExtra.extra_filters = flts;
}
let endpoint = getExploreUrl(formDataExtra, endpointType, this.force);
if (endpoint.charAt(0) !== '/') {
// Known issue for IE <= 11:
// https://connect.microsoft.com/IE/feedbackdetail/view/1002846/pathname-incorrect-for-out-of-document-elements
endpoint = '/' + endpoint;
}
return endpoint;
},
d3format(col, number) {
// uses the utils memoized d3format function and formats based on
// column level defined preferences
let format = '.3s';
if (this.datasource.column_formats[col]) {
format = this.datasource.column_formats[col];
}
return utils.d3format(format, number);
},
/* eslint no-shadow: 0 */
always(data) {
if (data && data.query) {
slice.viewSqlQuery = data.query;
}
},
done(payload) {
Object.assign(data, payload);
token.find('img.loading').hide();
container.fadeTo(0.5, 1);
container.show();
$('.query-and-save button').removeAttr('disabled');
this.always(data);
controller.done(this);
},
getErrorMsg(xhr) {
let msg = '';
if (!xhr.responseText) {
const status = xhr.status;
if (status === 0) {
// This may happen when the worker in gunicorn times out
msg += (
'The server could not be reached. You may want to ' +
'verify your connection and try again.');
} else {
msg += 'An unknown error occurred. (Status: ' + status + ')';
}
}
return msg;
},
error(msg, xhr) {
let errorMsg = msg;
token.find('img.loading').hide();
container.fadeTo(0.5, 1);
let errHtml = '';
let o;
try {
o = JSON.parse(msg);
if (o.error) {
errorMsg = o.error;
}
} catch (e) {
// pass
}
if (errorMsg) {
errHtml += `<div class="alert alert-danger">${errorMsg}</div>`;
}
if (xhr) {
if (xhr.statusText === 'timeout') {
errHtml += '<div class="alert alert-warning">' +
`Query timeout - visualization query are set to time out at ${QUERY_TIMEOUT_THRESHOLD / 1000} seconds.</div>`;
} else {
const extendedMsg = this.getErrorMsg(xhr);
if (extendedMsg) {
errHtml += `<div class="alert alert-danger">${extendedMsg}</div>`;
}
}
}
container.html(errHtml);
container.show();
$('span.query').removeClass('disabled');
$('.query-and-save button').removeAttr('disabled');
this.always(o);
controller.error(this);
},
clearError() {
$(selector + ' div.alert').remove();
},
width() {
return container.width();
},
height() {
let others = 0;
const widget = container.parents('.widget');
const sliceDescription = widget.find('.slice_description');
if (sliceDescription.is(':visible')) {
others += widget.find('.slice_description').height() + 25;
}
others += widget.find('.chart-header').height();
return widget.height() - others - 10;
},
bindResizeToWindowResize() {
let resizeTimer;
const slice = this;
$(window).on('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
slice.resize();
}, 500);
});
},
render(force) {
if (force === undefined) {
this.force = false;
} else {
this.force = force;
}
token.find('img.loading').show();
container.fadeTo(0.5, 0.25);
container.css('height', this.height());
$.ajax({
url: this.jsonEndpoint(),
timeout: QUERY_TIMEOUT_THRESHOLD,
success: (queryResponse) => {
try {
vizMap[formData.viz_type](this, queryResponse);
this.done(queryResponse);
} catch (e) {
this.error('An error occurred while rendering the visualization: ' + e);
}
},
error: (err) => {
this.error(err.responseText, err);
},
});
},
resize() {
this.render();
},
addFilter(col, vals, merge = true, refresh = true) {
controller.addFilter(sliceId, col, vals, merge, refresh);
},
setFilter(col, vals, refresh = true) {
controller.setFilter(sliceId, col, vals, refresh);
},
getFilters() {
return controller.filters[sliceId];
},
clearFilter() {
controller.clearFilter(sliceId);
},
removeFilter(col, vals) {
controller.removeFilter(sliceId, col, vals);
},
};
return slice;
};
// Export public functions
return {
getParam,
initFavStars,
Slice,
};
}();
module.exports = px;