backbone-fusioncharts
Version:
Simple and Lightweight Backbone wrapper for FusionCharts JavaScript Charting Library
223 lines (193 loc) • 7.15 kB
JavaScript
/* eslint-disable no-prototype-builtins, no-plusplus, no-param-reassign */
import Backbone from 'backbone';
import fusionChartsOptions from './utils/options';
import * as utils from './utils/utils';
export default Backbone.View.extend({
initialize() {
this.model.bind('change', this.onChange.bind(this));
this.props = this.model.toJSON();
this.render();
},
onChange(nextProps) {
const currentOptions = this.resolveChartOptions(nextProps.toJSON());
const { oldOptions } = this;
const optionsUpdatedNatively = [
'width',
'height',
'type',
'dataFormat',
'dataSource',
'events',
];
this.checkAndUpdateChartRenderAt(currentOptions, oldOptions);
this.checkAndUpdateChartDimensions(currentOptions, oldOptions);
this.checkAndUpdateChartType(currentOptions, oldOptions);
this.checkAndUpdateChartData(currentOptions, oldOptions);
this.checkAndUpdateEvents(currentOptions, oldOptions);
this.checkAndUpdateRestOptions(
fusionChartsOptions.filter(option => optionsUpdatedNatively.indexOf(option) === -1),
currentOptions,
oldOptions,
);
this.oldOptions = currentOptions;
},
checkAndUpdateChartRenderAt(currentOptions, oldOptions) {
const currRenderAt = currentOptions.renderAt;
const oldRenderAt = oldOptions.renderAt;
if (String(currRenderAt) !== String(oldRenderAt)) {
this.chart.dispose();
this.props.renderAt = currRenderAt;
this.render();
}
},
checkAndUpdateChartDimensions(currentOptions, oldOptions) {
const currWidth = currentOptions.width;
const currHeight = currentOptions.height;
const oldWidth = oldOptions.width;
const oldHeight = oldOptions.height;
if (String(currWidth) !== String(oldWidth) || String(currHeight) !== String(oldHeight)) {
if (!utils.isUndefined(currWidth) && !utils.isUndefined(currHeight)) {
this.chart.resizeTo(currWidth, currHeight);
} else {
if (!utils.isUndefined(currWidth)) {
this.chart.resizeTo({
w: currWidth,
});
}
if (!utils.isUndefined(currHeight)) {
this.chart.resizeTo({
h: currHeight,
});
}
}
}
},
checkAndUpdateChartType(currentOptions, oldOptions) {
const currType = currentOptions.type;
const oldType = oldOptions.type;
if (String(currType).toLowerCase() !== String(oldType).toLowerCase()) {
if (!utils.isUndefined(currType)) {
this.chart.chartType(String(currType).toLowerCase());
}
}
},
checkAndUpdateChartData(currentOptions, oldOptions) {
const currDataFormat = currentOptions.dataFormat;
const currData = currentOptions.dataSource;
const oldDataFormat = oldOptions.dataFormat;
const oldData = oldOptions.dataSource;
if (String(currDataFormat).toLowerCase() !== String(oldDataFormat).toLowerCase()) {
if (!utils.isUndefined(currDataFormat) && !utils.isUndefined(currData)) {
this.chart.setChartData(currData, String(currDataFormat).toLowerCase());
// If the chart dataFormat is changed then
// animate the chart to show the changes
this.chart.render();
}
} else if (!this.isSameChartData(currData, oldData)) {
if (!utils.isUndefined(currData)) {
this.chart.setChartData(
currData,
// When dataFormat is not given, but data is changed,
// then use 'json' as default dataFormat
currDataFormat ? String(currDataFormat).toLowerCase() : 'json',
);
}
}
},
isSameChartData(currData, oldData) {
if (utils.isObject(currData) && utils.isObject(oldData)) {
return utils.isSameObjectContent(currData, oldData);
}
return currData === oldData;
},
checkAndUpdateEvents(currentOptions, oldOptions) {
const currEvents = currentOptions.events;
const oldEvents = oldOptions.events;
let temp1;
let temp2;
if (this.detectChartEventsChange(currEvents, oldEvents)) {
if (!utils.isUndefined(currEvents)) {
temp1 = Object.assign({}, currEvents);
temp2 = utils.isUndefined(oldEvents) ? {} : Object.assign({}, oldEvents);
Object.keys(temp2).forEach((eventName) => {
if (temp2[eventName] === temp1[eventName]) {
temp1[eventName] = undefined;
} else {
this.chart.removeEventListener(eventName, temp2[eventName]);
}
});
Object.keys(temp1).forEach((eventName) => {
if (temp1[eventName]) {
this.chart.addEventListener(eventName, temp1[eventName]);
}
});
}
}
},
detectChartEventsChange(currEvents, oldEvents) {
if (utils.isObject(currEvents) && utils.isObject(oldEvents)) {
return !(this.isSameChartEvents(currEvents, oldEvents));
}
return !(currEvents === oldEvents);
},
isSameChartEvents(currEvents, oldEvents) {
if (Object.keys(currEvents).length !== Object.keys(oldEvents).length) { return false; }
const currEventNames = Object.keys(currEvents);
for (let i = 0; i < currEventNames.length; ++i) {
const evName = currEventNames[i];
if (currEvents[evName] !== oldEvents[evName]) {
return false;
}
}
return true;
},
checkAndUpdateRestOptions(restOptions, currentOptions, oldOptions) {
let optionsUpdated = false;
restOptions.forEach((optionName) => {
const currValue = currentOptions[optionName];
const oldValue = oldOptions[optionName];
if (!this.isSameOptionValue(currValue, oldValue)) {
if (!utils.isUndefined(currValue)) {
if (this.chart.options && this.chart.options.hasOwnProperty(optionName)) {
this.chart.options[optionName] = currValue;
optionsUpdated = true;
}
}
}
});
if (optionsUpdated) {
this.chart.render(); // re-render the chart to reflect the changes
}
},
isSameOptionValue(currValue, oldValue) {
if (utils.isObject(currValue) && utils.isObject(oldValue)) {
return utils.isSameObjectContent(currValue, oldValue);
}
return String(currValue) === String(oldValue);
},
resolveChartOptions(props) {
const chartConfig = props.chartConfig ? props.chartConfig : {};
const inlineOptions = fusionChartsOptions.reduce((options, optionName) => {
options[optionName] = props[optionName];
return options;
}, {});
Object.assign(inlineOptions, chartConfig);
if (utils.isObject(inlineOptions.dataSource)) {
inlineOptions.dataSource = utils.deepCopyOf(inlineOptions.dataSource);
}
if (utils.isObject(inlineOptions.link)) {
inlineOptions.link = utils.deepCopyOf(inlineOptions.link);
}
if (utils.isObject(inlineOptions.events)) {
inlineOptions.events = Object.assign({}, inlineOptions.events);
}
return inlineOptions;
},
render() {
const currentOptions = this.resolveChartOptions(this.props);
this.chart = new FusionCharts(currentOptions);
this.chart.render();
this.oldOptions = currentOptions;
return this;
},
});