(function (H) {
var UNDEFINED,
addEvent = H.addEvent,
removeEvent = H.removeEvent,
fireEvent = H.fireEvent,
numberFormat = H.numberFormat,
dateFormat = H.dateFormat,
format = H.format,
pick = H.pick,
merge = H.merge,
splat = H.splat,
each = H.each,
extend = H.extend,
wrap = H.wrap,
mathMin = Math.min,
mathMax = Math.max,
mathRound = Math.round,
defaultOptions = {
plotOptions: {
series: {
connectNulls: true
}
},
navigator: {
enabled: false
},
rangeSelector: {
allButtonsEnabled: true
},
scrollbar: {
enabled: false
},
xAxis: {
startOnTick: false,
endOnTick: false,
allowZoomOutside: true
}, // xAxis
yAxis: {
labels: {
align: 'left',
x: 2
},
opposite: false
}
},
userId,
cypher,
userAgent = navigator.userAgent,
isOpera = window.opera,
isIE = /msie/i.test(userAgent) && !isOpera,
isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
},
isNumber = function(n) {
return typeof n === 'number';
};
var USFpush = window.USFpush || window.VWD ? window.VWD.USFpush : UNDEFINED;
wrap(H, 'setOptions', function (proceed, options) {
merge(true, defaultOptions, options);
return(proceed.apply(this, Array.prototype.slice.call(arguments, 1)));
});
// language DE
H.setOptions({
chart: {
zoomType: 'x'
},
credits: {
href: "http://www.vwd.com",
text: "vwd Vereinigte Wirtschaftsdienste GmbH"
},
global: {
useUTC: false
},
lang: {
contextButtonTitle: 'Chart Kontextmenü',
decimalPoint: ',',
downloadJPEG: 'Als JPEG Bild herunterladen',
downloadPDF: 'Als PDF herunterladen',
downloadPNG: 'Als PNG Bild herunterladen',
downloadSVG: 'Als SVG Vektor herunterladen',
exportButtonTitle: 'Nach Raster- oder Vektor-Bild exportieren',
loading: 'Lade...',
months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
noData: 'Keine Daten vorhanden',
printButtonTitle: 'Drucken',
printChart: 'Chart drucken',
quoteTypes: {
ask: 'Briefkurs',
bid: 'Geldkurs',
last: 'Schlusskurs'
},
rangeSelectorFrom: '',
rangeSelectorTo: '-',
rangeSelectorZoom: '',
resetZoom: 'Zoom zurücksetzen',
resetZoomTitle: 'Zoom zurücksetzen level 1:1',
shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
thousandsSep: '.',
weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
},
navigator: {
xAxis: {
dateTimeLabelFormats: {
month: '%b %Y'
}
}
},
plotOptions: {
area: {
threshold: null
},
series: {
dataGrouping: {
dateTimeLabelFormats: {
millisecond: ['%d.%m.%Y, %H:%M:%S.%L', '%d.%m.%Y, %H:%M:%S.%L', '-%H:%M:%S.%L'],
second: ['%d.%m.%Y, %H:%M:%S', '%d.%m.%Y, %H:%M:%S', '-%H:%M:%S'],
minute: ['%d.%m.%Y, %H:%M', '%d.%m.%Y, %H:%M', '-%H:%M'],
hour: ['%d.%m.%Y, %H:%M', '%d.%m.%Y, %H:%M', '-%H:%M'],
day: ['%d.%m.%Y', '%d.%m.%Y', '-%d.%m.%Y'],
week: ['Woche vom %d.%m.%Y', '%d.%m.%Y', '-%d.%m.%Y'],
month: ['%B %Y', '%B', '-%B %Y'],
year: ['%Y', '%Y', '-%Y']
}
}
}
},
rangeSelector: {
inputDateFormat: '%d.%m.%Y',
inputEnabled: false
},
xAxis: {
dateTimeLabelFormats: {
month: '%b %Y'
}
}
});
// /language DE
// event handler
var updatedData = function (e) { // undocumented event!
var s = this;
// highlight last point
// check if last point is visible
if (s.xAxis.getExtremes().max >= s.xData[s.xData.length - 1]) {
if (s.points.length > 0) {
//series._hasPointMarkers = true;
var lastPoint = s.points[s.points.length - 1];
var color = pick(USFpush.colors.bgFromEqual, '#999999');
if (s.USFpushUpdate === 'down') {
color = pick(USFpush.colors.bgFromDown, '#CC0033');
} else if (s.USFpushUpdate === 'up') {
color = pick(USFpush.colors.bgFromUp, '#009933');
}
if (s.type == 'candlestick') {
// highlight candle
if (lastPoint.graphic) {
lastPoint.graphic.attr($.extend({}, lastPoint.pointAttr.hover, {'stroke': color}));
if (s.USFpushLastPointHighlighted && lastPoint !== s.USFpushLastPointHighlighted) {
// reset highlighting from previous point
if (s.USFpushLastPointHighlighted.graphic) {
s.USFpushLastPointHighlighted.graphic.attr($.extend({'stroke': 'black'}, s.USFpushLastPointHighlighted.pointAttr['']));
}
}
s.USFpushLastPointHighlighted = lastPoint;
clearTimeout(s.pushMarkerTimer);
s.pushMarkerTimer = setTimeout(function () {
if (s.USFpushLastPointHighlighted.graphic) {
s.USFpushLastPointHighlighted.graphic.attr($.extend({'stroke': 'black'}, s.USFpushLastPointHighlighted.pointAttr['']));
}
}, USFpush.highlightTime);
}
} else {
// blink marker
if (s.pushMarkerGraphic) {
s.pushMarkerGraphic.attr({
fill: color,
r: 6,
x: lastPoint.plotX,
y: lastPoint.plotY
}).show().animate({r: 2}, {duration: USFpush.highlightTime});
}
}
}
}
}; // /updatedData
// init push for chart
H.Chart.prototype.initPush = function () {
if (typeof(USFpush) !== 'object') {
return;
}
var chart = this;
// init
chart.USFreadyToDraw = true;
chart.USFhasNewPushData = false;
// init axes push
each(chart.yAxis, function (axis) {
axis.initPush();
});
// init series push
each(chart.series, function (series) {
series.initPush();
});
// check for push updates after redraw and re-init push after axis-update
addEvent(chart, 'redraw', function () {
var chart = this;
setTimeout(function () {
if (chart) {
if (chart.USFhasNewPushData === true) {
chart.redraw();
} else {
chart.USFreadyToDraw = true;
}
}
}, 100); // give last redraw time to complete and USFhasNewPushData to be updated
});
};
// init push for plotlines
H.Axis.prototype.initPush = function () {
if (typeof(USFpush) !== 'object') {
return;
}
var axis = this;
axis.isPushDirty = false;
if (axis.options.plotLines) {
each(axis.options.plotLines, function (p, i) {
if ((typeof(p.USFpush) === 'object') && (p.USFpush.pushIdValue !== '')) {
var pushIdValue = p.USFpush.pushIdValue.replace(/^id="([^"]*)"$/, RegExp.$1);
p.USFpush.fieldIdValue = pushIdValue.split(USFpush.delimiter)[2];
// add html elements if necessary to enable push
if ($('span[id^="' + pushIdValue.split(USFpush.delimiter).slice(0, 4).join(USFpush.delimiter) + '"]').length === 0) {
$('' + p.value + '').appendTo(chart.renderTo);
}
var aPushId = pushIdValue.split(USFpush.delimiter);
var symbol = aPushId[0];
var exchange = aPushId[1];
USFpush.registerCallback(symbol, exchange, p.USFpush.callback || function (oData) {
if (!p || !p.USFpush) {
// chart or plotline has been destroyed
USFpush.unRegisterCallback(symbol, exchange, arguments.callee);
return;
}
var value = oData.fields[p.USFpush.fieldIdValue];
if (value !== '' && value != p.value) {
var newPlotLine = $.extend(true, {}, p, {value: value, label: {text: p.label.text.replace(/[0-9,\.]+/, H.numberFormat(value, p.USFpush.postcomma || 2))}});
axis.removePlotLine(p.id);
axis.addPlotLine(newPlotLine);
}
});
}
});
}
};
// init push for series
H.Series.prototype.initPush = function () {
if (typeof(USFpush) !== 'object') {
return;
}
var s = this;
var chart = s.chart;
s.isPushDirty = false;
// check push enabled for series
if ((typeof(s.options.USFpush) === 'object') && (s.options.USFpush.enabled !== false) && (s.options.USFpush.pushIdValue !== '') && (s.options.USFpush.pushIdDate !== '') && (s.name !== 'Navigator')) {
var lastX = s.xData[s.xData.length - 1];
var lastY = s.yData[s.yData.length - 1];
var pushIdValue = s.options.USFpush.pushIdValue.replace(/^id="([^"]*)"$/, RegExp.$1);
var pushIdDate = s.options.USFpush.pushIdDate.replace(/^id="([^"]*)"$/, RegExp.$1);
s.options.USFpush.fieldIdValue = pushIdValue.split(USFpush.delimiter)[2];
s.options.USFpush.fieldIdDate = pushIdDate.split(USFpush.delimiter)[2];
// add marker for highlighting
if (s.symbol) {
s.pushMarkerGraphic = chart.renderer.circle(0, 0, 4).attr({'class': 'pushMarker', zIndex: 3}).hide().add(s.markerGroup);
// listen for updatedData to highlight marker
addEvent(s, 'updatedData', updatedData);
}
// add html elements if necessary to enable push
if ($('span[id^="' + pushIdValue.split(USFpush.delimiter).slice(0, 4).join(USFpush.delimiter) + '"]').length === 0) {
$('' + lastY + '').appendTo(chart.renderTo);
}
if ($('span[id^="' + pushIdDate.split(USFpush.delimiter).slice(0, 4).join(USFpush.delimiter) + '"]').length === 0) {
$('' + lastX + '').appendTo(chart.renderTo);
}
var aPushId = pushIdValue.split(USFpush.delimiter);
var symbol = aPushId[0];
var exchange = aPushId[1];
// check special pushIds for different datatypes (ask/bid/last)
each(['intraday', 'interday', 'history'], function (hist) {
if (s.options.USFdata && s.options.USFdata[hist]) {
var USFhistData = s.options.USFdata[hist],
pushOptions = $.extend(true, {}, s.options.USFpush, USFhistData);
if ((pushOptions.pushIdValue) && (pushOptions.pushIdValue !== '') && (pushOptions.pushIdDate !== '') && (s.name !== 'Navigator')) {
var pushIdValue = pushOptions.pushIdValue.replace(/^id="([^"]*)"$/, RegExp.$1);
var pushIdDate = pushOptions.pushIdDate.replace(/^id="([^"]*)"$/, RegExp.$1);
pushOptions.fieldIdValue = pushIdValue.split(USFpush.delimiter)[2];
pushOptions.fieldIdDate = pushIdDate.split(USFpush.delimiter)[2];
// add html elements if necessary to enable push
if ($('span[id^="' + pushIdValue.split(USFpush.delimiter).slice(0, 4).join(USFpush.delimiter) + '"]').length === 0) {
$('' + lastY + '').appendTo(chart.renderTo);
}
if ($('span[id^="' + pushIdDate.split(USFpush.delimiter).slice(0, 4).join(USFpush.delimiter) + '"]').length === 0) {
$('' + lastX + '').appendTo(chart.renderTo);
}
}
}
});
USFpush.registerCallback(symbol, exchange, s.options.USFpush.callback || function (oData) {
if (!s || !s.options || !s.options.USFpush) {
// chart or series has been destroyed
USFpush.unRegisterCallback(symbol, exchange, arguments.callee);
return;
}
var pushIdValue = s.options.USFpush.pushIdValue.replace(/^id="([^"]*)"$/, RegExp.$1);
var aPushId = pushIdValue.split(USFpush.delimiter);
if (symbol !== aPushId[0] || exchange !== aPushId[1]) {
// series has been replaced by another instrument
USFpush.unRegisterCallback(symbol, exchange, arguments.callee);
return;
}
chart.USFhasNewPushData = true;
// hide no data error
if (s.visible) {
chart.hideError();
}
var extremes = s.xAxis.getExtremes();
var needRedraw = false;
each(['intraday', 'interday', 'history'], function (hist) {
if (s.options.USFdata && s.options.USFdata[hist] && s.options.USFdata[hist].data) {
var USFdata = s.options.USFdata[hist],
pushOptions = $.extend(true, {}, s.options.USFpush, USFdata),
shift,
newMin;
if (!pushOptions.fieldIdValue || !pushOptions.fieldIdDate) {
return;
}
var date = oData.fields[pushOptions.fieldIdDate];
var value = oData.fields[pushOptions.fieldIdValue];
var oldValue = oData.oldFields[pushOptions.fieldIdValue];
// R-65781 ignore empty fields
if ((value === '') || (value === UNDEFINED) || (!date) || (!date.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/))) {
return;
}
if (value > oldValue) {
s.USFpushUpdate = 'up';
} else if (value < oldValue) {
s.USFpushUpdate = 'down';
} else {
s.USFpushUpdate = 'equal';
}
date = Date.parse(date.replace(/-/g, '/'));
value = parseFloat(value);
var lastX = USFdata.data.length > 0 ? (USFdata.data[USFdata.data.length - 1][0] || USFdata.data[USFdata.data.length - 1].x) : s.xData.length > 0 ? s.xData[s.xData.length - 1] : 0;
var deltaTime = date - lastX;
var timeAggregation = pushOptions.timeAggregation || s.closestPointRange;
if (deltaTime >= timeAggregation) {
// add new point
var newX = date - (date % timeAggregation);
if (hist === s.options.USFdata.current) {
// visible dataset
var newPoint;
if (s.type === 'candlestick' || s.type === 'ohlc') {
newPoint = [newX, value, value, value, value];
} else {
newPoint = [newX, value];
}
s.addPoint(newPoint, false);
needRedraw = true;
// add point updates s.options.data
// check if USFdata is a clone or reference
if (s.options.data !== USFdata.data) {
if (s.type === 'candlestick' || s.type === 'ohlc') {
USFdata.data.push([newX, value, value, value, value]);
} else {
USFdata.data.push([newX, value]);
}
}
// update extremes if visible
if (s.visible) {
var selectedOption = chart.rangeSelector && chart.rangeSelector.selected;
if (typeof(selectedOption) === 'number') {
var rangeSelector = chart.rangeSelector,
buttons = rangeSelector.buttons,
selectedButtonOptions = rangeSelector.buttonOptions[selectedOption];
if (selectedButtonOptions.timeRange === 'intraday') {
if (s.index === 0) { // show intraday range for baseSeries
// set to current exchangeOpen / -Close
var exchange = getExchangeOpenClose(s.options.USFdata.exchangeOpen, s.options.USFdata.exchangeClose, newX);
if ((extremes.min !== exchange.open) || (extremes.max !== exchange.close)) {
s.xAxis.setExtremes(exchange.open, exchange.close, false, null, {trigger: 'pushUpdate'});
chart.fixedRange = exchange.close - exchange.open; // prevent rangeSelctor button deactivation
}
}
} else {
if (!chart.scroller || chart.scroller.zoomedMax === chart.scroller.navigatorWidth) {
// change visible range to show added point
if (selectedButtonOptions.type === 'all') {
newMin = s.xAxis.dataMin;
} else if (selectedButtonOptions._range) {
newMin = mathMax(newX - selectedButtonOptions._range, s.xAxis.dataMin);
} else {
shift = newX - lastX;
newMin = extremes.min + shift; // leave visible range as it was before
}
s.xAxis.setExtremes(newMin, newX, false, null, {trigger: 'pushUpdate'});
chart.fixedRange = newX - newMin; // prevent rangeSelctor button deactivation
}
}
} else if (lastX === extremes.max) {
// if visible range is at right edge, change visible range to show added point
shift = newX - lastX;
newMin = extremes.min + shift; // leave visible range as it was before
s.xAxis.setExtremes(newMin, newX, false, null, {trigger: 'pushUpdate'});
}
}
} else {
// update invisible dataset (not currently active)
if (s.type === 'candlestick' || s.type === 'ohlc') {
USFdata.data.push([newX, value, value, value, value]);
} else {
USFdata.data.push([newX, value]);
}
}
} else { // /add new point
// update last point
if (s.type == 'candlestick' || s.type === 'ohlc') {
var lastDataPoint = USFdata.data[USFdata.data.length - 1];
USFdata.data[USFdata.data.length - 1] = [
lastDataPoint[0], // date
lastDataPoint[1], // open
Math.max(lastDataPoint[2], value), // high
Math.min(lastDataPoint[3], value), // low
value // close
];
} else if (s.type == 'column') {
// add to last point
if (USFdata.data.length > 0) {
USFdata.data[USFdata.data.length - 1][1] += value;
}
} else {
if (USFdata.data.length > 0) {
USFdata.data[USFdata.data.length - 1][1] = value;
}
}
if (hist === s.options.USFdata.current) {
// visible dataset
var lastPoint = s.data.length > 0 ? s.data[s.data.length - 1] : null;
// update extremes if visible
if (s.visible) {
s.xAxis.isDirty = true; // mark axes for redraw, so that serie is drawn too
}
needRedraw = true;
if (s.type == 'candlestick' || s.type === 'ohlc') {
s.yData[s.yData.length - 1] = USFdata.data[USFdata.data.length - 1].slice(1); // yData without x
if (lastPoint) {
lastPoint.update([
lastPoint.x, // date
lastPoint.open, // open
Math.max(lastPoint.high, value), // high
Math.min(lastPoint.low, value), // low
value // close
], false);
}
} else if (s.type == 'column') {
// add to last point
s.yData[s.yData.length - 1] += value;
if (lastPoint) {
lastPoint.update([lastPoint.x, lastPoint.y + value], false);
}
} else {
s.yData[s.yData.length - 1] = value;
if (lastPoint) {
lastPoint.update([lastPoint.x, value], false);
}
}
}
} // /update last point
} // /if chartOptions.series[s.index].USFdata[hist]
}); // /each(intraday, interday, historic)
if(needRedraw) {
s.isDirtyData = true;
if (chart.USFreadyToDraw) {
chart.USFreadyToDraw = false;
setTimeout(function () {
if (chart) {
chart.redraw();
chart.USFreadyToDraw = true;
}
}, 1); // give other series time to update before redrawing
}
}
}, {chart: chart, series: s});
}
}; // / H.Series.prototype.initPush
/**
* Returns true if the object is not null or undefined. Like MooTools' $.defined.
* @param {Object} obj
*/
function defined(obj) {
return obj !== UNDEFINED && obj !== null;
}
// monitor pending ajax requests
wrap(H.Chart.prototype, 'init', function (proceed) {
this.pendingDataRequests = 0;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
H.Chart.prototype.getDataType = function (min, max) {
var range = max - min;
if (range <= 86400000) {
return 'intraday';
} else if (range <= 86400000 * 7) {
return 'interday';
} else {
return 'history';
}
};
// reset USFhasNewPushData on redraw
wrap(H.Chart.prototype, 'redraw', function (proceed, animation) {
this.USFhasNewPushData = false;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
// draw rangeSelector even if currently no data available since data could be available in other dataTypes (intraday/interday/history)
H.Chart.prototype.callbacks.push(function (chart) {
var extremes = chart.xAxis[0].getExtremes(),
rangeSelector = chart.rangeSelector;
if (rangeSelector && isNaN(extremes.min)) {
rangeSelector.render(extremes.min, extremes.max);
}
});
// init push for series
wrap(H.Series.prototype, 'init', function (proceed, chart, options) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
// call initPush in redraw after all points are generated
this.isPushDirty = true;
});
// reinit push for series after update
wrap(H.Series.prototype, 'redraw', function (proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (this.isPushDirty === true) {
this.initPush();
}
});
// animate pushmarker when series graph is animated
wrap(H.Series.prototype, 'drawGraph', function (proceed) {
var series = this;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
var lastX = series.graphPath[series.graphPath.length - 2];
var lastY = series.graphPath[series.graphPath.length - 1];
if (series.pushMarkerGraphic) {
// check if last point is visible
if (series.xAxis.getExtremes().max >= series.xData[series.xData.length - 1]) {
series.pushMarkerGraphic.animate({x: lastX, y: lastY});
} else {
series.pushMarkerGraphic.hide();
}
}
});
// show/hide pushmarker when series is shown/hidden
wrap(H.Series.prototype, 'setVisible', function (proceed, vis) {
var series = this,
oldVisibility = series.visible,
showOrHide;
// if called without an argument, toggle visibility
vis = vis === UNDEFINED ? !oldVisibility : vis;
if (this.pushMarkerGraphic && !vis) {
this.pushMarkerGraphic.hide();
}
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
series.chart.checkDataAvailable(); // show/hide "no data" error
});
H.Series.prototype.getExchangeOpenClose = function (t) {
return(getExchangeOpenClose(this.options.USFdata.exchangeOpen, this.options.USFdata.exchangeClose, t));
};
wrap(H.Series.prototype, 'setData', function (proceed, data, redraw, animation, updatePoints) {
var series = this,
chart = series.chart,
chartOptions,
point,
exchange;
// ref #9241 show line in intraday, if only one point available
if (data && data.length === 1 && (series.type === 'line' || series.type === 'area') && series.options.USFdata.current === 'intraday') {
point = { series: series };
series.pointClass.prototype.applyOptions.apply(point, [data[0]]);
exchange = series.getExchangeOpenClose(point.x);
var x = (new Date()).getTime();
x = x - (x % ((series.options.USFdata.intraday && series.options.USFdata.intraday.timeAggregation) || series.closestPointRange)); // current time based on timeAggregation
data.push([mathMin(x, exchange.close), point.y]);
}
// show marker if only one point available
if (data && data.length === 1 && (series.type === 'line' || series.type === 'area')) {
point = { series: series };
series.pointClass.prototype.applyOptions.apply(point, [data[0]]);
data[0] = {
marker: {
enabled: true
},
x: point.x,
y: point.y
};
}
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
// update USFdata as well
if (series.options.USFdata) {
series.options.USFdata[series.options.USFdata.current] = series.options.USFdata[series.options.USFdata.current] || {};
series.userOptions.USFdata[series.options.USFdata.current] = series.userOptions.USFdata[series.options.USFdata.current] || {};
series.options.USFdata[series.options.USFdata.current].data = series.userOptions.USFdata[series.options.USFdata.current].data = data;
}
if (series.name !== 'Navigator') {
// store options in chartOptions for re-initializing chart properly in handleButtonClick
chartOptions = $(chart.renderTo).data('chartOptions');
if (chartOptions && chartOptions.series && isNumber(series.index)) {
chartOptions.series[series.index] = series.userOptions;
$(chart.renderTo).data('chartOptions', chartOptions);
}
}
});
// ajax data plugin
H.Chart.prototype.loadData = function (min, max, callback) {
if (this.options.chart.ajaxLoadData !== true) {
callback(min, max);
} else {
each(this.series, function (s) {
s.loadData(min, max, callback);
});
}
};
// show closeprice plotline
H.Series.prototype.showHighLowClosePricePlotLines = function () {
var series = this,
chart = series.chart,
yAxis = series.yAxis,
options = series.options;
each([{optionsField: 'highPricePlotLine', priceField: 'high', defaultColor: 'green'},
{optionsField: 'lowPricePlotLine', priceField: 'low', defaultColor: 'red'},
{optionsField: 'closePricePlotLine', priceField: 'yesterdayPrice', defaultColor: '#A7C227'}], function(type) {
var optionsField = type.optionsField,
priceField = type.priceField,
defaultColor = type.defaultColor,
plotLineOptions = options[optionsField];
if (plotLineOptions && plotLineOptions.enabled === true && options.USFdata && options.USFdata[priceField] && options.USFdata.current === 'intraday') {
var plotLineId = plotLineOptions.id || optionsField,
opts = {
allwaysVisible: plotLineOptions.allwaysVisible !== false,
color: plotLineOptions.color || defaultColor,
id: plotLineId,
label: {
align: plotLineOptions.textAlign || 'center',
style: {
fontSize: plotLineOptions.fontSize || '12px',
color: plotLineOptions.textColor || plotLineOptions.color || defaultColor,
fontWeight: plotLineOptions.fontWeight || 'bold',
textShadow: plotLineOptions.textShadow || '',
filter: plotLineOptions.filter || ''
},
text: plotLineOptions.text !== UNDEFINED ? format(plotLineOptions.text, options.USFdata) : numberFormat(options.USFdata[priceField], options.tooltip.valueDecimals),
useHTML: plotLineOptions.useHTML || false,
x: plotLineOptions.x || undefined
},
value: options.USFdata[priceField],
width: 1,
zIndex: plotLineOptions.zIndex || 4
};
// remove old closeprice plotline
yAxis.removePlotLine(plotLineId);
yAxis.addPlotLine(opts);
}
});
};
// remove closeprice plotline
H.Series.prototype.removeHighLowClosePricePlotLines = function () {
var series = this,
yAxis = series.yAxis,
options = series.options;
each(['highPricePlotLine', 'lowPricePlotLine', 'closePricePlotLine'], function(optionsField) {
var plotLineOptions = options[optionsField] || {},
plotLineId = plotLineOptions.id || optionsField;
yAxis.removePlotLine(plotLineId);
});
};
// has closeprice plotline
H.Series.prototype.hasClosePricePlotLine = function () {
var series = this,
options = series.options;
return(options.closePricePlotLine && options.closePricePlotLine.enabled === true && options.USFdata && isNumber(options.USFdata.yesterdayPrice) && options.USFdata.current === 'intraday');
};
// has highprice plotline
H.Series.prototype.hasHighPricePlotLine = function () {
var series = this,
options = series.options;
return(options.highPricePlotLine && options.highPricePlotLine.enabled === true && options.USFdata && options.USFdata.high && options.USFdata.current === 'intraday');
};
// has lowprice plotline
H.Series.prototype.hasLowPricePlotLine = function () {
var series = this,
options = series.options;
return(options.lowPricePlotLine && options.lowPricePlotLine.enabled === true && options.USFdata && options.USFdata.low && options.USFdata.current === 'intraday');
};
// update close-/high-/lowprice plotline
wrap(H.Series.prototype, 'update', function (proceed, newOptions, redraw) {
var series = this,
chart = series.chart,
chartOptions;
series.removeHighLowClosePricePlotLines();
addEvent(chart, 'redraw', function () {
setTimeout(function () {
removeEvent(chart, 'redraw', arguments.callee); // execute only once
}, 0); // let trigger('redraw') loop finish first
series.showHighLowClosePricePlotLines();
});
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (series.name !== 'Navigator') {
// store options in chartOptions for re-initializing chart properly in handleButtonClick
chartOptions = $(chart.renderTo).data('chartOptions');
if (chartOptions && chartOptions.series && isNumber(series.index)) {
chartOptions.series[series.index] = series.userOptions;
$(chart.renderTo).data('chartOptions', chartOptions);
}
}
});
H.Series.prototype.getTimeAggregation = function (min, max) {
var series = this,
currentExtremes = series.xAxis.getExtremes(),
range;
if (series.options.USFdata && series.options.USFdata.current) {
if (series.options.USFdata.current === 'intraday') {
return 'MINUTE';
} else if (series.options.USFdata.current === 'interday') {
return 'HOUR';
} else {
return 'DAY';
}
}
min = min || currentExtremes.min;
max = max || currentExtremes.max;
range = max - min;
if (range <= 86400000) {
return 'MINUTE';
} else if (range <= 86400000 * 7) {
return 'HOUR';
} else {
return 'DAY';
}
};
H.Series.prototype.getDefaultExtremes = function () {
var series = this,
min,
max,
xAxis = series.xAxis,
now = new Date().getTime(),
currentExtremes = series.xAxis.getExtremes();
max = currentExtremes.max || now;
min = currentExtremes.min || max - xAxis.options.range;
return({min: min, max: max});
};
H.Series.prototype.getLoadDataURL = function (min, max) {
var series = this,
minParam = '',
maxParam = '',
xAxis = series.xAxis,
now = new Date().getTime(),
USFbaseUrl = window.USFbaseUrl || window.VWD ? window.VWD.USFbaseUrl : '',
USFinterfaceUrl = window.USFinterfaceUrl || window.VWD ? window.VWD.USFinterfaceUrl : UNDEFINED;
if (min) {
if(series.timeAggregation === 'DAY' || series.timeAggregation === 'WEEK' || series.timeAggregation === 'MONTH') {
minParam = dateFormat('%Y%m%d000000', min);
} else {
minParam = dateFormat('%Y%m%d%H%M%S', min);
}
}
if (max && max !== now) {
if(series.timeAggregation === 'DAY' || series.timeAggregation === 'WEEK' || series.timeAggregation === 'MONTH') {
maxParam = dateFormat('%Y%m%d000000', max+86399999);
} else {
maxParam = dateFormat('%Y%m%d%H%M%S', max);
}
}
return((USFinterfaceUrl || USFbaseUrl + 'ifaceredir/') + 'JSON/QUOTE/HISTORYQUOTE/' + series.options.instrumentId + '/' + series.timeAggregation + '/' + ((minParam || maxParam) ? (minParam + '-' + maxParam) : '') );
};
H.Series.prototype.loadData = function (args, max, callback) {
var series = this,
chart = series.chart,
url,
min,
timeAggregation,
dataType,
defaultExtremes = true,
headers = {
'X-USF-USER_ID': userId,
'X_USF_ABORT_AUTH_HEADER': 1
},
cache = true;
if (args && (typeof(args) === 'object')) {
// new param style: object with args
min = args.min;
max = args.max;
callback = args.callback;
timeAggregation = args.timeAggregation;
dataType = args.dataType;
defaultExtremes = args.defaultExtremes;
} else {
// old parameter style: min, max, callback
min = args;
}
//queue loadData request until last one finishes (because of token)
if (series.userOptions.USFdata && series.userOptions.USFdata.jqXHRdataState === 'pending') {
series.userOptions.loadDataQueue = [arguments];
return;
}
if ((!isNumber(min) || !isNumber(max)) && defaultExtremes) {
extremes = series.getDefaultExtremes();
min = extremes.min;
max = extremes.max;
}
series.options.USFdata.current = dataType || chart.getDataType(min, max);
series.timeAggregation = timeAggregation || series.getTimeAggregation(min, max);
chart.showLoading();
chart.pendingDataRequests++;
url = series.getLoadDataURL(min, max);
series.userOptions.USFdata.jqXHRdataState = 'pending';
if(cypher) {
headers['X-USF-USER_CYPHER'] = cypher;
} else {
headers['X-USF-Token'] = series.userOptions.USFdata.token;
cache = false; // pevent caching so that no old tokens are used
}
$.ajax(url, {
cache: cache,
complete: function (jqXHR) {
var token = jqXHR.getResponseHeader('X-USF-Token');
if (token) {
series.userOptions.USFdata.token = token;
}
series.userOptions.USFdata.jqXHRdataState = 'complete';
chart.pendingDataRequests--;
if (series.userOptions.loadDataQueue && series.userOptions.loadDataQueue.length > 0) {
series.loadData.apply(series, series.userOptions.loadDataQueue.pop());
return;
}
if (chart.pendingDataRequests === 0) {
fireEvent(chart, 'loadData', null, function () {
chart.hideLoading();
chart.checkDataAvailable();
if (callback) {
callback.call(series, min, max); // setExtremes and/or redraw chart
}
});
}
},
dataType: 'JSON',
headers: headers,
success: function (data, textStatus, jqXHR) {
var chartOptions;
if (data && data.historyQuoteList && data.historyQuoteList.quote &&
(!series.userOptions.loadDataQueue || series.userOptions.loadDataQueue.length === 0)) {
var seriesData = $.map(data.historyQuoteList.quote, function (point, i) {
if (/^(\d+)\.(\d+)\.(\d+) (\d+):(\d+):(\d+)$/.test(point.quoteDate)) {
var date = new Date(RegExp.$3, RegExp.$2 - 1, RegExp.$1, RegExp.$4, RegExp.$5, RegExp.$6);
if (series.type === 'candlestick' || series.type === 'ohlc') {
return([
[date.getTime(), point.openPrice, point.highPrice, point.lowPrice, point.closePrice]
]);
} else {
return([
[date.getTime(), point.closePrice]
]);
}
}
});
fireEvent(series, 'loadDataSuccess', {data: seriesData}, function (e) {
series.setData(e.data, false); // redraw in loadDataComplete
});
}
}
});
};
wrap(H.Axis.prototype, 'init', function (proceed, chart, userOptions) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
this.allowZoomOutside = !!userOptions.allowZoomOutside;
// call initPush in chart.redraw event when all plotlines/-band are generated
this.isPushDirty = true;
});
// reinit push for plotlines
wrap(H.Axis.prototype, 'redraw', function (proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (this.isPushDirty === true) {
this.initPush();
}
});
// set axis.min/max to reflect maxMin/minMax
wrap(H.Axis.prototype, 'beforePadding', function (proceed) {
var axis = this,
chart = axis.chart,
options = axis.options,
isXAxis = axis.isXAxis,
maxMin = axis.options.maxMin,
minMax = axis.options.minMax;
if(!axis.isLog) {
if (!isXAxis) {
if (isNumber(axis.min) && isNumber(axis.max)) {
if (chart.series.length) {
each(chart.series, function (s) {
if (s.hasClosePricePlotLine() && s.options.closePricePlotLine.allwaysVisible !== false) {
if (axis.max < s.options.USFdata.yesterdayPrice) {
axis.max = s.options.USFdata.yesterdayPrice;
} else if (axis.min > s.options.USFdata.yesterdayPrice) {
axis.min = s.options.USFdata.yesterdayPrice;
}
}
if (s.hasHighPricePlotLine() && s.options.highPricePlotLine.allwaysVisible !== false) {
if (axis.max < s.options.USFdata.high) {
axis.max = s.options.USFdata.high;
} else if (axis.min > s.options.USFdata.high) {
axis.min = s.options.USFdata.high;
}
}
if (s.hasLowPricePlotLine() && s.options.lowPricePlotLine.allwaysVisible !== false) {
if (axis.max < s.options.USFdata.low) {
axis.max = s.options.USFdata.low;
} else if (axis.min > s.options.USFdata.low) {
axis.min = s.options.USFdata.low;
}
}
});
}
}
}
// check for maxMin/minMax
if (isNumber(maxMin) && (axis.min > maxMin)) {
axis.min = maxMin;
}
if (isNumber(minMax) && (axis.max < minMax)) {
axis.max = minMax;
}
}
if(typeof(proceed) === 'function') {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
}
});
// reinitialize events, that highstock does not after axis.update
wrap(H.Axis.prototype, 'update', function (proceed, newOptions, redraw) {
var axis = this,
chart = axis.chart,
rangeSelector = chart.rangeSelector;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
// taken from RangeSelector.prototype.init
// normalize the pressed button whenever a new range is selected
if (axis === chart.xAxis[0] && rangeSelector) {
addEvent(axis, 'afterSetExtremes', function () {
rangeSelector.updateButtonStates(true);
});
}
});
// reset chart.extraTopMargin if hidden rangeSelector
wrap(H.RangeSelector.prototype, 'init', function (proceed, chart) {
var rangeSelector = this,
options;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
options = rangeSelector.options;
if (options.buttonTheme && options.buttonTheme.style && options.buttonTheme.style.display === 'none' && options.inputEnabled === false) {
chart.extraTopMargin = 0;
}
});
// set button width to buttonOption.width
wrap(H.RangeSelector.prototype, 'render', function (proceed) {
var rangeSelector = this,
firstRender = !rangeSelector.rendered;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (firstRender) {
var x = rangeSelector.buttons[0].attr('x');
each(rangeSelector.buttons, function (b, i) {
b.attr({
x: x
});
b.attr("width", rangeSelector.buttonOptions[i].width || b.attr("width"));
var text = $(b.element).find('text, span').text();
b.attr("text", text); // force updateBoxSize() to be run
x += b.width - 1;
});
}
});
H.RangeSelector.prototype.handleButtonClick = function (i, rangeOptions, newDataType, redraw) {
var rangeSelector = this,
chart = rangeSelector.chart,
currentDataType;
if (chart.options.chart.ajaxLoadData === true) {
return true;
}
if (redraw !== false) { // redraw is false during RangeSelector.init
// switch data type if necessary
var s = chart.options.series[0];
if (s.USFdata && s.USFdata.current) {
currentDataType = s.USFdata.current;
if (chart.initNewChart || currentDataType !== newDataType) {
// init new chart
// TODO: use axis.update / series.update instead of new chart
var newOpts = $.extend(true, {}, $(chart.renderTo).data('chartOptions'));
each(newOpts.series, function (o) {
if (o.USFdata) {
o.USFdata.current = newDataType;
o.data = o.USFdata[newDataType].data;
}
});
newOpts.rangeSelector.selected = i;
chart.destroy();
chart = null;
new FinanceChart(newOpts);
return false;
}
}
}
return true;
};
// add logic to switch data if necessary
// and jump to latest data
wrap(H.RangeSelector.prototype, 'clickButton', function (proceed, i, redraw) {
var rangeSelector = this,
selected = rangeSelector.selected,
chart = rangeSelector.chart,
buttons = rangeSelector.buttons,
rangeOptions = rangeSelector.buttonOptions[i],
baseAxis = chart.xAxis[0],
unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
dataMin = unionExtremes.dataMin,
dataMax = unionExtremes.dataMax,
newMin,
//newMax = baseAxis && mathRound(mathMax(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
newMax = baseAxis && mathRound(pick(dataMax, baseAxis.max)), // #1568
now,
date = new Date(newMax),
type = rangeOptions.type,
count = rangeOptions.count,
baseXAxisOptions,
range = rangeOptions._range,
rangeMin,
year,
timeName,
baseSeriesOptions = chart.options.series[0],
baseSeriesIntradayData = baseSeriesOptions.USFdata && baseSeriesOptions.USFdata.intraday && baseSeriesOptions.USFdata.intraday.data,
baseSeriesInterdayData = baseSeriesOptions.USFdata && baseSeriesOptions.USFdata.interday && baseSeriesOptions.USFdata.interday.data,
baseSeriesHistoryData = baseSeriesOptions.USFdata && baseSeriesOptions.USFdata.history && baseSeriesOptions.USFdata.history.data,
newDataType,
rangeMax;
if (i === rangeSelector.selected) { // same button is clicked twice
return;
}
if (rangeOptions.timeRange === 'intraday') {
var lastX;
if (baseSeriesIntradayData && baseSeriesIntradayData.length) {
lastX = baseSeriesIntradayData[baseSeriesIntradayData.length - 1][0];
var exchange = getExchangeOpenClose(baseSeriesOptions.USFdata.exchangeOpen, baseSeriesOptions.USFdata.exchangeClose, lastX);
newMin = rangeMin = exchange.open;
newMax = rangeMax = exchange.close;
range = newMax - newMin;
}
newDataType = 'intraday';
} else if (type === 'month' || type === 'year') {
if (baseSeriesHistoryData && baseSeriesHistoryData.length) {
newMax = baseSeriesHistoryData[baseSeriesHistoryData.length - 1].x || baseSeriesHistoryData[baseSeriesHistoryData.length - 1][0];
date = new Date(newMax);
dataMin = baseSeriesHistoryData[0][0];
}
if (chart.options.chart.ajaxLoadData === true) {
date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
newMax = date.getTime();
dataMin = Number.MIN_VALUE;
}
newDataType = 'history';
timeName = { month: 'Month', year: 'FullYear'}[type];
date['set' + timeName](date['get' + timeName]() - count);
newMin = date.getTime();
dataMin = pick(dataMin, Number.MIN_VALUE);
if (!isNumber(newMin) || newMin < dataMin) {
if(rangeSelector.options.showFullRange !== true) {
newMin = dataMin;
}
} else {
range = newMax - newMin;
}
// Fixed times like minutes, hours, days
} else if (range) {
if (chart.options.chart.ajaxLoadData === true) {
newMax = new Date().getTime();
dataMin = Number.MIN_VALUE;
} else if (range <= 86400 * 1000) {
newDataType = 'intraday';
if (baseSeriesIntradayData && baseSeriesIntradayData.length) {
dataMin = baseSeriesIntradayData[0][0];
}
} else if (range <= 7 * 86400 * 1000) {
newDataType = 'interday';
if (baseSeriesInterdayData && baseSeriesInterdayData.length) {
dataMin = baseSeriesInterdayData[0][0];
}
} else {
newDataType = 'history';
if (baseSeriesHistoryData && baseSeriesHistoryData.length) {
dataMin = baseSeriesHistoryData[0][0];
}
}
newMin = mathMax(newMax - range, dataMin);
newMax = mathMin(newMin + range, dataMax);
} else if (type === 'ytd') {
// On user clicks on the buttons, or a delayed action running from the beforeRender
// event (below), the baseAxis is defined.
if (baseAxis) {
// When "ytd" is the pre-selected button for the initial view, its calculation
// is delayed and rerun in the beforeRender event (below). When the series
// are initialized, but before the chart is rendered, we have access to the xData
// array (#942).
if (dataMax === UNDEFINED) {
dataMin = Number.MAX_VALUE;
dataMax = Number.MIN_VALUE;
each(chart.series, function (series) {
var xData = series.xData; // reassign it to the last item
dataMin = mathMin(xData[0], dataMin);
dataMax = mathMax(xData[xData.length - 1], dataMax);
});
redraw = false;
}
now = new Date(dataMax);
year = now.getFullYear();
newMin = rangeMin = mathMax(dataMin || 0, Date.UTC(year, 0, 1));
now = now.getTime();
newMax = mathMin(dataMax || now, now);
newDataType = 'history';
// "ytd" is pre-selected. We don't yet have access to processed point and extremes data
// (things like pointStart and pointInterval are missing), so we delay the process (#942)
} else {
addEvent(chart, 'beforeRender', function () {
rangeSelector.clickButton(i, rangeOptions);
});
return;
}
} else if (type === 'all' && baseAxis) {
if (baseSeriesHistoryData && baseSeriesHistoryData.length) {
dataMin = baseSeriesHistoryData[0][0];
}
newMin = dataMin;
newMax = dataMax;
newDataType = 'history';
}
if (!newDataType) {
newDataType = chart.getDataType(newMin, newMax);
}
if (rangeSelector.handleButtonClick && rangeSelector.handleButtonClick(i, rangeOptions, newDataType, redraw) === false) {
return;
}
// Deselect previous button
if (buttons[selected]) {
buttons[selected].setState(0);
}
// Select this button
if (buttons[i]) {
buttons[i].setState(2);
}
chart.fixedRange = range;
// update the chart
if (!baseAxis) {
// Axis not yet instanciated. Temporarily set min and range
// options and remove them on chart load (#4317).
baseXAxisOptions = chart.options.xAxis[0];
rangeSetting = baseXAxisOptions.range;
if(rangeSelector.options.showFullRange === true) {
baseXAxisOptions.min = newMin;
baseXAxisOptions.max = newMax;
} else {
baseXAxisOptions.min = rangeMin;
baseXAxisOptions.range = range;
}
minSetting = baseXAxisOptions.min;
rangeSelector.setSelected(i);
addEvent(chart, 'load', function resetMinAndRange() {
baseXAxisOptions.range = rangeSetting;
baseXAxisOptions.min = newMin;
});
} else { // existing axis object; after render time
chart.loadData(newMin, newMax, function (newMin, newMax) {
baseAxis.setExtremes(
newMin,
newMax,
pick(redraw, 1),
chart.animation,
{
trigger: 'rangeSelectorButton',
rangeSelectorButton: rangeOptions
}
);
rangeSelector.selected = i;
});
}
});
// helper functions
var timeUnits = {
'millisecond': 1,
'second': 1000,
'minute': 60000,
'hour': 3600000,
'day': 24 * 3600000,
'week': 7 * 24 * 3600000,
'month': 30 * 24 * 3600000,
'year': 31556952000
};
var getExchangeOpenClose = function (open, close, t) {
t = t || new Date().getTime();
var date = new Date(t);
if (open === UNDEFINED || close === UNDEFINED) {
return;
}
var aOpen = open.split(':');
var aClose = close.split(':');
var timeOfDay = (date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds()) * 1000;
var openTime = (parseInt(aOpen[0].replace(/^0/, '')) * 3600 + parseInt(aOpen[1].replace(/^0/, '')) * 60) * 1000;
var closeTime = (parseInt(aClose[0].replace(/^0/, '')) * 3600 + parseInt(aClose[1].replace(/^0/, '')) * 60) * 1000;
var openDate = date.getTime() - (timeOfDay - openTime);
if (timeOfDay < openTime) {
openDate -= 86400000;
}
var closeDate = openDate + closeTime - openTime;
if (timeOfDay > (closeTime + 3600000)) { // add one hour for after hours
closeDate += 86400000;
}
return({
'open': openDate,
'close': closeDate
});
};
H.Chart.prototype.checkDataAvailable = function () {
var dataAvailable = false;
each(this.series, function (s) {
if (s.visible && s.options.data && s.options.data.length > 0) {
dataAvailable = true;
}
});
if (!dataAvailable) {
this.showError('Keine Daten verfügbar');
} else {
this.hideError();
}
};
H.Chart.prototype.showError = function (msg) {
var chart = this;
if (chart.USFerrorText && chart.USFerrorText.show) {
chart.USFerrorText.show().toFront();
} else {
var text = chart.renderer.text(msg, 0, 0).css({fontWeight: 'bold'}).add();
var box = text.getBBox();
text.attr({x: chart.plotLeft + (chart.plotWidth / 2 - box.width / 2), y: chart.plotTop + (chart.plotHeight / 2), zIndex: 5}); //center text in plot area
text.toFront();
chart.USFerrorText = text;
}
};
H.Chart.prototype.hideError = function () {
var chart = this;
if (chart.USFerrorText && chart.USFerrorText.hide) {
chart.USFerrorText.hide();
}
};
wrap(H.Chart.prototype, 'destroy', function (proceed) {
var chart = this;
// remove chart from internal chartlist
FinanceChart.charts = $.grep(FinanceChart.charts, function (c) {
return c !== chart;
});
return(proceed.apply(this, Array.prototype.slice.call(arguments, 1)));
});
wrap(H.SVGRenderer.prototype, 'init', function(proceed) {
var renderer = this,
returnValue = proceed.apply(this, Array.prototype.slice.call(arguments, 1)),
textShadowFilter = renderer.createElement('filter').add(renderer.defs).attr({
id: 'white-drop-shadow'
}),
merge;
renderer.createElement('feGaussianBlur').add(textShadowFilter).attr({
'in': "SourceAlpha",
stdDeviation: "0"
});
renderer.createElement('feOffset').add(textShadowFilter).attr({
result: "offsetblur",
dx: "1",
dy: "1"
});
renderer.createElement('feFlood').add(textShadowFilter).attr({
'flood-color': 'rgba(255,255,255,1)'
});
renderer.createElement('feComposite').add(textShadowFilter).attr({
operator: "in",
in2: "offsetblur"
});
merge = renderer.createElement('feMerge').add(textShadowFilter);
renderer.createElement('feMergeNode').add(merge);
renderer.createElement('feMergeNode').add(merge).attr({
'in': "SourceGraphic"
});
return(returnValue);
});
wrap(H.Pointer.prototype, 'setDOMEvents', function (proceed) {
var pointer = this,
container = pointer.chart.container;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
// check if pointer has been destroyed after click on rangeselector button
container.onclick = function (e) {
if (typeof(pointer.onContainerClick) === 'function') {
pointer.onContainerClick(e);
}
};
});
/**
* Augments Series.drawPoints to use different colors for volume charts (type=volume)
* Works for Highcharts <= 4.x; The logic for Highcharts 5.x is in function external:Highcharts.Series#pointAttribs
* @function external:Highcharts.Series#drawPoints
*/
wrap(H.seriesTypes.column.prototype, 'drawPoints', function (proceed) {
var series = this,
chart = this.chart,
points = series.points,
i = points.length,
colorEqual = series.options.USFcolorEqual || '#666666',
colorUp = series.options.USFcolorUp || '#029800',
colorDown = series.options.USFcolorDown || '#C60400',
color = colorEqual,
baseSeriesIndex = series.options.USFbaseSeriesIndex,
point,
previousPoint,
seriesPointAttr,
baseSeriesPoints,
j;
if (series.pointAttr) {
if (isNumber(baseSeriesIndex) && (chart.series.length > baseSeriesIndex)) {
baseSeriesPoints = chart.series[baseSeriesIndex].points;
j = baseSeriesPoints.length;
while (i-- && j--) {
point = baseSeriesPoints[j];
previousPoint = baseSeriesPoints[j - 1];
color = colorEqual;
if (point && previousPoint && isNumber(point.y) && isNumber(previousPoint.y)) {
if (point.y > previousPoint.y) {
color = colorUp;
} else if (point.y < previousPoint.y) {
color = colorDown;
}
}
seriesPointAttr = merge(series.pointAttr);
seriesPointAttr[''].fill = color;
seriesPointAttr.hover.fill = H.Color(color).brighten(0.3).get();
seriesPointAttr.select.fill = color;
points[i].pointAttr = seriesPointAttr;
}
}
}
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
/**
* Augments Series.pointAttribs to use different colors for volume charts (type=volume)
* @function external:Highcharts.Series#pointAttribs
*/
wrap(H.seriesTypes.column.prototype, 'pointAttribs', function (proceed, point) {
var series = this,
chart = this.chart,
points = series.points,
i = points.indexOf(point),
colorEqual = series.options.colorEqual || '#666666',
colorUp = series.options.colorUp || '#029800',
colorDown = series.options.colorDown || '#C60400',
color = colorEqual,
baseSeriesIndex = series.options.USFbaseSeriesIndex,
quoteSeries,
currentPoint,
previousPoint,
baseSeriesPoints,
ret;
ret = proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if(isNumber(baseSeriesIndex) && (chart.series.length > baseSeriesIndex)) {
quoteSeries = chart.series[baseSeriesIndex];
}
if (quoteSeries) {
baseSeriesPoints = quoteSeries.points;
currentPoint = baseSeriesPoints[i];
previousPoint = baseSeriesPoints[i - 1];
color = colorEqual;
if (currentPoint && previousPoint && isNumber(currentPoint.y) && isNumber(previousPoint.y)) {
if (currentPoint.y > previousPoint.y) {
color = colorUp;
} else if (currentPoint.y < previousPoint.y) {
color = colorDown;
}
}
ret.fill = color;
}
return ret;
});
var FinanceChart = function (userOptions, callback) {
var rs = userOptions.rangeSelector,
rsbutton = {},
options;
if (rs && rs.buttons) {
// add index to each button for quick access in setExtremes
each(rs.buttons, function (b, i) {
b.index = i;
});
if ((rs.selected !== UNDEFINED) && rs.buttons[rs.selected]) {
rsbutton = rs.buttons[rs.selected];
}
}
// store user options for proper reinitializing chart
$('#' + userOptions.chart.renderTo).data('chartOptions', userOptions);
// apply userOptions and optional specific options for selected rangeselector
options = $.extend(true, {}, defaultOptions, userOptions, rsbutton.chartOptions);
// merge axis opts if multiple axis in userOptions defined
each(['xAxis', 'yAxis'], function(a) {
if(isArray(userOptions[a])) {
options[a] = [];
each(userOptions[a], function(axis) {
options[a].push( $.extend(true, {}, defaultOptions[a], axis, rsbutton.chartOptions[a]) );
});
}
});
// prevent series hiding if only one series is visible
if (options.series.length === 1) {
if (!options.series[0].events) {
options.series[0].events = {};
}
options.series[0].events.legendItemClick = function (e) {
return(false);
};
}
each(options.series, function (s) {
// set quoteType label for legend
if (s.USFdata) {
// get quoteType for preloaded data
if (s.USFdata.current && s.USFdata[s.USFdata.current]) {
var quoteType = s.USFdata[s.USFdata.current].intradayType || 'last';
s.quoteTypeLabel = defaultOptions.lang.quoteTypes[quoteType];
}
}
});
fireEvent($('#' + userOptions.chart.renderTo).get(0), 'afterSetOptions', {chartOptions: options});
this.chart = new H.StockChart(options, function () {
var chart = this;
if (chart.options.chart.preLoadData === true) {
// check for data available
chart.checkDataAvailable();
} else if (chart.options.chart.ajaxLoadData === true) {
chart.loadData(UNDEFINED, UNDEFINED, function (min, max) {
chart.series[0].xAxis.setExtremes(min, max);
});
}
// register for push updates and handle them
chart.initPush();
// show high-/low-/closeprice plotlines
each(chart.series, function (s) {
s.showHighLowClosePricePlotLines();
});
// call callback from user params
if (callback) {
callback.apply(chart);
}
});
FinanceChart.charts.push(this.chart);
};
// add Highcharts as jQuery plugin if used with standalone framework (for compatibility with jquery-ba-resize.js)
// see: http://forum.highcharts.com/highstock-usage/conflict-with-jquery-ba-resize-js-and-jquery-flot-resize-js-t27620/
if (!$.fn.highcharts) {
$.fn.highcharts = function () {
var constr = 'Chart', // default constructor
args = arguments,
options,
ret,
chart;
if (typeof(args[0]) === 'string') {
constr = args[0];
args = Array.prototype.slice.call(args, 1);
}
options = args[0];
// Create the chart
if (options !== UNDEFINED) {
/*jslint unused:false*/
options.chart = options.chart || {};
options.chart.renderTo = this[0];
chart = new H[constr](options, args[1]);
ret = this;
/*jslint unused:true*/
}
// When called without parameters or with the return argument, get a predefined chart
if (options === UNDEFINED) {
ret = H.charts[$(this[0]).attr('data-highcharts-chart')];
}
return ret;
};
}
window.FinanceChart = FinanceChart;
// public interface
extend(FinanceChart, {
charts: [],
destroyCharts: function () {
for (var i = FinanceChart.charts.length - 1; i >= 0; i--) {
FinanceChart.charts[i].destroy();
}
},
getExchangeOpenClose: getExchangeOpenClose, // get open and close timestamp based on a quote-timestamp and exchange open/close in format HH:mm
timeUnits: timeUnits,
setUserId: function (u) {
userId = u;
},
setUserCypher: function (c) {
cypher = c;
}
});
}(Highcharts));