Skip to content

Instantly share code, notes, and snippets.

@barendb
Created June 21, 2014 01:49
Show Gist options
  • Save barendb/8730a15f558838b1338a to your computer and use it in GitHub Desktop.
Save barendb/8730a15f558838b1338a to your computer and use it in GitHub Desktop.
Modified highcharts-ng.js
'use strict';
angular.module('highcharts-ng', []).factory('highchartsNGUtils', function () {
return {
indexOf: function (arr, find, i) {
if (i === undefined)
i = 0;
if (i < 0)
i += arr.length;
if (i < 0)
i = 0;
for (var n = arr.length; i < n; i++)
if (i in arr && arr[i] === find)
return i;
return -1;
},
prependMethod: function (obj, method, func) {
var original = obj[method];
obj[method] = function () {
var args = Array.prototype.slice.call(arguments);
func.apply(this, args);
if (original) {
return original.apply(this, args);
} else {
return;
}
};
},
deepExtend: function deepExtend(destination, source) {
if (angular.isArray(source)) {
destination = angular.isArray(destination) ? destination : [];
for (var i = 0; i < source.length; i++) {
destination[i] = deepExtend(destination[i] || {}, source[i]);
}
} else if (angular.isObject(source)) {
for (var property in source) {
destination[property] = deepExtend(destination[property] || {}, source[property]);
}
} else {
destination = source;
}
return destination;
}
};
}).directive('highchart', [
'highchartsNGUtils',
function (highchartsNGUtils) {
var seriesId = 0;
var ensureIds = function (series) {
var changed = false;
angular.forEach(series, function (s) {
if (!angular.isDefined(s.id)) {
s.id = 'series-' + seriesId++;
changed = true;
}
});
return changed;
};
var axisNames = [
'xAxis',
'yAxis'
];
var getMergedOptions = function (scope, element, config) {
var mergedOptions = {};
var defaultOptions = {
chart: { events: {} },
title: {},
subtitle: {},
series: [],
credits: {},
plotOptions: {},
navigator: { enabled: false }
};
if (config.options) {
mergedOptions = highchartsNGUtils.deepExtend(defaultOptions, config.options);
} else {
mergedOptions = defaultOptions;
}
mergedOptions.chart.renderTo = element[0];
angular.forEach(axisNames, function (axisName) {
if (angular.isDefined(config[axisName])) {
mergedOptions[axisName] = angular.copy(config[axisName]);
if (angular.isDefined(config[axisName].currentMin) || angular.isDefined(config[axisName].currentMax)) {
highchartsNGUtils.prependMethod(mergedOptions.chart.events, 'selection', function (e) {
var thisChart = this;
if (e[axisName]) {
scope.$apply(function () {
scope.config[axisName].currentMin = e[axisName][0].min;
scope.config[axisName].currentMax = e[axisName][0].max;
});
} else {
scope.$apply(function () {
scope.config[axisName].currentMin = thisChart[axisName][0].dataMin;
scope.config[axisName].currentMax = thisChart[axisName][0].dataMax;
});
}
});
highchartsNGUtils.prependMethod(mergedOptions.chart.events, 'addSeries', function (e) {
scope.config[axisName].currentMin = this[axisName][0].min || scope.config[axisName].currentMin;
scope.config[axisName].currentMax = this[axisName][0].max || scope.config[axisName].currentMax;
});
}
}
});
if(config.drilldown) {
mergedOptions.drilldown = config.drilldown;
};
if (config.title) {
mergedOptions.title = config.title;
}
if (config.subtitle) {
mergedOptions.subtitle = config.subtitle;
}
if (config.credits) {
mergedOptions.credits = config.credits;
}
if (config.size) {
if (config.size.width) {
mergedOptions.chart.width = config.size.width;
}
if (config.size.height) {
mergedOptions.chart.height = config.size.height;
}
}
return mergedOptions;
};
var updateZoom = function (axis, modelAxis) {
var extremes = axis.getExtremes();
if (modelAxis.currentMin !== extremes.dataMin || modelAxis.currentMax !== extremes.dataMax) {
axis.setExtremes(modelAxis.currentMin, modelAxis.currentMax, false);
}
};
var processExtremes = function (chart, axis, axisName) {
if (axis.currentMin || axis.currentMax) {
chart[axisName][0].setExtremes(axis.currentMin, axis.currentMax, true);
}
};
var chartOptionsWithoutEasyOptions = function (options) {
return angular.extend({}, options, {
data: null,
visible: null
});
};
return {
restrict: 'EAC',
replace: true,
template: '<div></div>',
scope: {
config: '=',
disableDataWatch: '='
},
link: function (scope, element, attrs) {
var prevSeriesOptions = {};
var processSeries = function (series) {
var i;
var ids = [];
if (series) {
var setIds = ensureIds(series);
if (setIds && !scope.disableDataWatch) {
return false;
}
angular.forEach(series, function (s) {
ids.push(s.id);
var chartSeries = chart.get(s.id);
if (chartSeries) {
if (!angular.equals(prevSeriesOptions[s.id], chartOptionsWithoutEasyOptions(s))) {
chartSeries.update(angular.copy(s), false);
} else {
if (s.visible !== undefined && chartSeries.visible !== s.visible) {
chartSeries.setVisible(s.visible, false);
}
chartSeries.setData(angular.copy(s.data), false);
}
} else {
chart.addSeries(angular.copy(s), false);
}
prevSeriesOptions[s.id] = chartOptionsWithoutEasyOptions(s);
});
if (scope.config.noData) {
var chartContainsData = false;
for (i = 0; i < series.length; i++) {
if (series[i].data && series[i].data.length > 0) {
chartContainsData = true;
break;
}
}
if (!chartContainsData) {
chart.showLoading(scope.config.noData);
} else {
chart.hideLoading();
}
}
}
for (i = chart.series.length - 1; i >= 0; i--) {
var s = chart.series[i];
if (highchartsNGUtils.indexOf(ids, s.options.id) < 0) {
s.remove(false);
}
}
return true;
};
var chart = false;
var initChart = function () {
if (chart)
chart.destroy();
prevSeriesOptions = {};
var config = scope.config || {};
var mergedOptions = getMergedOptions(scope, element, config);
var func = config.func || undefined;
chart = config.useHighStocks ? new Highcharts.StockChart(mergedOptions, func) : new Highcharts.Chart(mergedOptions, func);
for (var i = 0; i < axisNames.length; i++) {
if (config[axisNames[i]]) {
processExtremes(chart, config[axisNames[i]], axisNames[i]);
}
}
if (config.loading) {
chart.showLoading();
}
};
initChart();
if (scope.disableDataWatch) {
scope.$watchCollection('config.series', function (newSeries, oldSeries) {
processSeries(newSeries);
chart.redraw();
});
} else {
scope.$watch('config.series', function (newSeries, oldSeries) {
var needsRedraw = processSeries(newSeries);
if (needsRedraw) {
chart.redraw();
}
}, true);
}
scope.$watch('config.title', function (newTitle) {
chart.setTitle(newTitle, true);
}, true);
scope.$watch('config.subtitle', function (newSubtitle) {
chart.setTitle(true, newSubtitle);
}, true);
scope.$watch('config.loading', function (loading) {
if (loading) {
chart.showLoading();
} else {
chart.hideLoading();
}
});
scope.$watch('config.credits.enabled', function (enabled) {
if (enabled) {
chart.credits.show();
} else if (chart.credits) {
chart.credits.hide();
}
});
scope.$watch('config.useHighStocks', function (useHighStocks, oldUseHighStocks) {
if (useHighStocks === oldUseHighStocks)
return;
initChart();
});
angular.forEach(axisNames, function (axisName) {
scope.$watch('config.' + axisName, function (newAxes, oldAxes) {
if (newAxes === oldAxes)
return;
if (newAxes) {
chart[axisName][0].update(newAxes, false);
updateZoom(chart[axisName][0], angular.copy(newAxes));
chart.redraw();
}
}, true);
});
scope.$watch('config.options', function (newOptions, oldOptions, scope) {
if (newOptions === oldOptions)
return;
initChart();
processSeries(scope.config.series);
chart.redraw();
}, true);
scope.$watch('config.size', function (newSize, oldSize) {
if (newSize === oldSize)
return;
if (newSize) {
chart.setSize(newSize.width || undefined, newSize.height || undefined);
}
}, true);
scope.$on('highchartsng.reflow', function () {
chart.reflow();
});
scope.$on('$destroy', function () {
if (chart)
chart.destroy();
element.remove();
});
}
};
}
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment