Please right click on the image and save it to your device
");
win.document.close();
}
}
}
var base64Images = {
reset: {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAcCAYAAAAAwr0iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAKRSURBVEiJrdY/iF1FFMfxzwnZrGISUSR/JLGIhoh/QiRNBLWxMLIWEkwbgiAoFgoW2mhlY6dgpY2IlRBRxBSKhSAKIklWJRYuMZKAhiyopAiaTY7FvRtmZ+/ed9/zHRjezLw5v/O9d86cuZGZpmURAfdn5o9DfdZNLXpjz+LziPgyIl6MiG0jPTJzZBuyDrP4BVm0P/AKbljTb4ToY/gGewYA7KyCl+1b3DUYANvwbiHw0gCAGRzBOzjTAXEOu0cC4Ch+r5x/HrpdrcZmvIDFSucMtnYCYC++6HmNDw8FKDT34ETrf639/azOr5vwRk/g5fbeuABtgC04XWk9VQLciMP4EH/3AFzErRNC7MXlQmsesSoHsGPE23hmEoBW+61K66HMXFmIMvN8myilXS36R01ub+KfYvw43ZXwYDX+AHP4BAci4pFJomfmr/ihmNofESsBImJGk7mlncrM45n5JPbhz0kAWpsv+juxaX21YIPmVJS2uNzJMS6ZNexC0d+I7fUWXLFyz2kSZlpWPvASlmqAf/FXNXf3FAF2F/1LuFifAlionB6dRuSI2IwHi6lzmXmp6xR8XY0fiIh7psAwh+3FuDkRHQVjl+a8lkXjo0kLUKH7XaV5oO86PmZ1FTzyP4K/XGl9v/zwfbW7BriiuETGCP5ch9bc9f97HF/vcFzCa5gdEPgWq+t/4v0V63oE1uF4h0DiFJ7HnSWMppDdh1dxtsPvJ2wcBNAKbsJXa0Ck5opdaBPsRNu/usba09i1KsaAVzmLt3sghrRjuK1Tf4xkegInxwy8gKf7dKMVH2QRsV5zXR/Cftyu+aKaKbbkQrsdH+PTzLzcqzkOQAVzM+7FHdiqqe2/YT4zF/t8S/sPmawyvC974vcAAAAASUVORK5CYII="
},
pan: {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAJVSURBVFiFvZe7a1RBGMV/x2hWI4JpfKCIiSBKOoOCkID/wP4BFqIIFkE02ChIiC8QDKlSiI3YqRBsBVGwUNAUdiIEUgjiAzQIIsuKJsfizsXr5t7d+8jmwLDfzHz3nLOzc7+ZxTZlGyDgZiWOCuJ9wH2gCUyuqQFgF/AGcKJNrYkBYBj40CIet+muGQi/96kM4WS7C/Tm5VUg7whJg8BkEGkCR4BDYfodsADUgP6wErO5iCtswsuJb32hdbXy8qzL5TIdmzJinHdZoZIBZcSFkGlAKs1Z3YCketZcBtouuaQNkrblMiBpBrhme7mAgU4wMCvpcFsDkq4C54DFVRTH9h+i6vlE0r5UA5ImgCuh28jB28iIs7BIVCOeStoZD64P4uPAjUTygKSx2FsK2TIwkugfk9Qkfd/E+yMWHQCeSRqx/R3gOp3LazfaS2C4B5gHDgD7U9x3E3uAH7KNpC3AHHAwTL4FHgM9GQ8vAaPA0dB/Abxqk2/gBLA9MXba9r1k/d4LfA3JtwueBeM58ucS+edXnAW23wP10N3advEi9CXizTnyN4bPS7Zn4sH/dq3t18AY4e1YLYSy3g/csj2VnFshZPuOpOeSKHCodUINuGj7YetE6je1PV9QoNPJ9StNHKodx7nRbiWrGHBGXAi5DUiqtQwtpcWK0Jubt8CltA5MEV1IfwO7+VffPwGfia5m34CT4bXujIIX0Qna1/cGMNqV/wUJE2czxD8CQ4X5Sl7Jz7SILwCDpbjKPBRMHAd+EtX4HWV5Spdc2w8kDQGPbH8py/MXMygM69/FKz4AAAAASUVORK5CYII="
},
zoom: {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAMqSURBVFiFvdfbj91TFMDxz57U6GUEMS1aYzyMtCSSDhWjCZMInpAI3khE/QHtgzdRkXgSCS8SES9epKLi0oRKNETjRahREq2KS1stdRujtDPtbA97n5zdn9+5zJxTK9k5v3POXmt991p7r71+IcaoGwkhTOIebMRqzOBTvIG3Y4zTXRmqSoyx5cAKbMJOHMFJnMZ8/jyFaXyMR7G6nb1aH22cP4BvcBxziG3GKfyTIR9D6BYg1KUghPBCDveFlb/24Av8iuUYw41YVsz5G7uxKcZ4aMEpwGt5NY3V/YbHsQ6rcAHOw/kYxigewr5CZw4fYGxBKcCLOFEYehXrMdRhr5yLETxVScsOLOkKAPfn1TYMPIvLFrShUlS2FDZm8XRHACzFAWl3R2xbqPMCYhmeLCAOYEMngAczbcTvuHYxzguIy/FesR9e6gSwU/OoPYHBHgHgviIKX2Flq7k34KhmcVnbi/PC8JX4MgMcxb118wZwdz5aISscqx7VRcox7MrPQ7i+btIAJrAkf9+bI9EPmZY2IAxiTSuAldLq4Y9+AcSUh78KP0tbAcwU35cXMD1JCIFUoGiehlqAz6TNB1f1C0DK+0h+nsNPrQC2a4bqGmlD9kOGcWt+Po6pVgDvSxfJaSkFd4UQBvoAsBYbCoB3a2flM7slA0R8iyt6rAFDeDPbm8eOTpVwGD9qVq7nLbIaZnmksPU1JtsCZMXNmpdRxFasWITzh6Xj3LCzra1OxcD2QjHiGVzdpfORnMqZio2PcF23ABdJF1Np4BPptlyPi6WzPYBzpJZtHe7A6xW9cnyP8TqA//SEIYRL8Bxul7rihvwgtVn78WcGGZXa9HGd5TDujDHuOePXNiHdKjWgZX/YbsxLx/ktqbjVzTlcjUSnvI5JrdlUVp6WesZZ6R1hRrpq9+EVTGS9jTjYAuKIouGpbcurEkIYxC051KNSamazsc+xK8b4S0VnEi/j0hqTP+M27O258egQwZuzs7pI7Mf4WQXIEDc5s9sux+5+1Py2EmP8UOq6GvWhIScxfdYjUERiAt9Jd84J6a16zf8JEKT3yCm8g1UxRv8CC4pyRhzR1uUAAAAASUVORK5CYII="
},
menu: {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDcvMTUvMTTPsvU0AAAAP0lEQVRIie2SMQoAIBDDUvH/X667g8sJJ9KOhYYOkW0qGaU1MPdC0vGSbV19EACo3YMPAFH5BUBUjsqfAPpVXtNgGDfxEDCtAAAAAElFTkSuQmCC"
}
}
function setButtonState(chart, button, state) {
if (button.getAttribute("state") !== state) {
button.setAttribute("state", state);
button.setAttribute("type", 'button');
button.style.position = "relative";
button.style.margin = "0px 0px 0px 0px";
button.style.padding = "3px 4px 0px 4px";
button.style.cssFloat = "left";
button.setAttribute("title", chart._cultureInfo[state + "Text"]);
button.innerHTML = "";
}
}
function show() {
var element = null;
for (var i = 0; i < arguments.length; i++) {
element = arguments[i];
if (element.style)
element.style.display = "inline";
}
}
function hide() {
var element = null;
for (var i = 0; i < arguments.length; i++) {
element = arguments[i];
if (element && element.style)
element.style.display = "none";
}
}
//#endregion Static Methods & variables
//#region Class Definitions
//#region Class CanvasJSObject
function CanvasJSObject(defaultsKey, options, theme, parent) {
this._defaultsKey = defaultsKey;
this.parent = parent;
this._eventListeners = [];//Multidimentional array with an array for each event type
var currentThemeOptions = {};
if (theme && themes[theme] && themes[theme][defaultsKey])
currentThemeOptions = themes[theme][defaultsKey];
this._options = options ? options : {};
this.setOptions(this._options, currentThemeOptions);
}
CanvasJSObject.prototype.setOptions = function (options, currentThemeOptions) {
if (!defaultOptions[this._defaultsKey]) {
if (isDebugMode && window.console)
console.log("defaults not set");
}
else {
var defaults = defaultOptions[this._defaultsKey];
for (var prop in defaults) {
if (options && prop in options)
this[prop] = options[prop];
else if (currentThemeOptions && prop in currentThemeOptions)
this[prop] = currentThemeOptions[prop];
else this[prop] = defaults[prop];
//if (typeof this[prop] === "function") {
// alert("function");
// this[prop] = this[prop]();
//}
}
}
};
// Update options. Returns true if changed or else false
CanvasJSObject.prototype.updateOption = function (prop) {
if (!defaultOptions[this._defaultsKey] && isDebugMode && window.console)
console.log("defaults not set");
var defaults = defaultOptions[this._defaultsKey];
var theme = this._options.theme ? this._options.theme : (this.chart && this.chart._options.theme) ? this.chart._options.theme : "theme1";
var currentThemeOptions = {};
var newValue = this[prop];
if (theme && themes[theme] && themes[theme][this._defaultsKey])
currentThemeOptions = themes[theme][this._defaultsKey];
if (prop in defaults) {
if (prop in this._options)
newValue = this._options[prop];
else if (currentThemeOptions && prop in currentThemeOptions)
newValue = currentThemeOptions[prop];
else newValue = defaults[prop];
}
if (newValue === this[prop])
return false;
this[prop] = newValue;
return true;
}
//Stores values in _oldOptions so that it can be tracked for any changes
CanvasJSObject.prototype.trackChanges = function (option) {
if (!this._options._oldOptions)
this._options._oldOptions = {};
this._options._oldOptions[option] = this._options[option];
};
CanvasJSObject.prototype.isBeingTracked = function (option) {
if (!this._options._oldOptions)
this._options._oldOptions = {};
if (this._options._oldOptions[option])
return true;
else
return false;
};
CanvasJSObject.prototype.hasOptionChanged = function (option) {
if (!this._options._oldOptions)
this._options._oldOptions = {};
//if (!this._options._oldOptions[option])
// this._options._oldOptions[option] = null;
var hasChanged = !(this._options._oldOptions[option] === this._options[option]);
return hasChanged;
};
CanvasJSObject.prototype.addEventListener = function (eventName, eventHandler, context) {
if (!eventName || !eventHandler)
return;
context = context || this;
this._eventListeners[eventName] = this._eventListeners[eventName] || [];
this._eventListeners[eventName].push({ context: context, eventHandler: eventHandler });
}
CanvasJSObject.prototype.removeEventListener = function (eventName, eventHandler) {
if (!eventName || !eventHandler || !this._eventListeners[eventName])
return;
var listeners = this._eventListeners[eventName];
for (var i = 0; i < listeners.length; i++) {
if (listeners[i].eventHandler === eventHandler) {
listeners[i].splice(i, 1);
break;
}
}
}
CanvasJSObject.prototype.removeAllEventListeners = function () {
this._eventListeners = [];
}
CanvasJSObject.prototype.dispatchEvent = function (eventName, eventParameter) {
if (!eventName || !this._eventListeners[eventName])
return;
eventParameter = eventParameter || {};
var listeners = this._eventListeners[eventName];
for (var i = 0; i < listeners.length; i++) {
listeners[i].eventHandler.call(listeners[i].context, eventParameter);
}
}
//#endregion Class CanvasJSObject
//#region Class Chart
function Chart(containerId, options, publicChartReference) {
this._publicChartReference = publicChartReference;
options = options || {};
Chart.base.constructor.call(this, "Chart", options, options.theme ? options.theme : "theme1");
var _this = this;
this._containerId = containerId;
this._objectsInitialized = false;
this.ctx = null;
this.overlaidCanvasCtx = null;
this._indexLabels = [];
this._panTimerId = 0;
this._lastTouchEventType = "";
this._lastTouchData = null;
this.isAnimating = false;
this.renderCount = 0;
this.animatedRender = false;
this.disableToolTip = false;
this.panEnabled = false;
this._defaultCursor = "default";
this.plotArea = { canvas: null, ctx: null, x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 };
this._dataInRenderedOrder = [];
this._container = typeof (this._containerId) === "string" ? document.getElementById(this._containerId) : this._containerId;
if (!this._container) {
if (window.console)
window.console.log("CanvasJS Error: Chart Container with id \"" + this._containerId + "\" was not found");
return;
}
this._container.innerHTML = "";
var width = 0;
var height = 0;
if (this._options.width)
width = this.width;
else
width = this._container.clientWidth > 0 ? this._container.clientWidth : this.width;
if (this._options.height)
height = this.height;
else
height = this._container.clientHeight > 0 ? this._container.clientHeight : this.height;
this.width = width;
this.height = height;
this.x1 = this.y1 = 0;
this.x2 = this.width;
this.y2 = this.height;
this._selectedColorSet = typeof (colorSets[this.colorSet]) !== "undefined" ? colorSets[this.colorSet] : colorSets["colorSet1"];
this._canvasJSContainer = document.createElement("div");
this._canvasJSContainer.setAttribute("class", "canvasjs-chart-container");
this._canvasJSContainer.style.position = "relative";
this._canvasJSContainer.style.textAlign = "left";
this._canvasJSContainer.style.cursor = "auto";
if (!isCanvasSupported) {
this._canvasJSContainer.style.height = "0px";//In IE6 toolTip doesn't show at proper position if not set.
}
this._container.appendChild(this._canvasJSContainer);
this.canvas = createCanvas(width, height);
this.canvas.style.position = "absolute";
if (this.canvas.getContext) {
//try {
// this.canvas.style.background = this.backgroundColor;
//} catch (e) { }
this._canvasJSContainer.appendChild(this.canvas);
this.ctx = this.canvas.getContext("2d");
this.ctx.textBaseline = "top";
extendCtx(this.ctx);
} else
return;
//this.canvas.style.cursor = "pointer";
if (!isCanvasSupported) {
this.plotArea.canvas = createCanvas(width, height);
this.plotArea.canvas.style.position = "absolute";
this.plotArea.canvas.setAttribute("class", "plotAreaCanvas");
this._canvasJSContainer.appendChild(this.plotArea.canvas);
this.plotArea.ctx = this.plotArea.canvas.getContext("2d");
} else {
this.plotArea.ctx = this.ctx;
}
this.overlaidCanvas = createCanvas(width, height);
this.overlaidCanvas.style.position = "absolute";
this._canvasJSContainer.appendChild(this.overlaidCanvas);
this.overlaidCanvasCtx = this.overlaidCanvas.getContext("2d");
this.overlaidCanvasCtx.textBaseline = "top";
this._eventManager = new EventManager(this);
addEvent(window, "resize", function () {
//this._container.addEventListener("DOMSubtreeModified", function () {
if (_this._updateSize())
_this.render();
});
this._toolBar = document.createElement("div");
this._toolBar.setAttribute("class", "canvasjs-chart-toolbar");
this._toolBar.style.cssText = "position: absolute; right: 1px; top: 1px;";
this._canvasJSContainer.appendChild(this._toolBar);
this.bounds = { x1: 0, y1: 0, x2: this.width, y2: this.height };
addEvent(this.overlaidCanvas, 'click', function (e) {
_this._mouseEventHandler(e);
});
addEvent(this.overlaidCanvas, 'mousemove', function (e) {
_this._mouseEventHandler(e);
});
addEvent(this.overlaidCanvas, 'mouseup', function (e) {
_this._mouseEventHandler(e);
});
addEvent(this.overlaidCanvas, 'mousedown', function (e) {
_this._mouseEventHandler(e);
hide(_this._dropdownMenu);
});
addEvent(this.overlaidCanvas, 'mouseout', function (e) {
_this._mouseEventHandler(e);
});
addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerDown" : "touchstart", function (e) {
_this._touchEventHandler(e);
});
addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerMove" : 'touchmove', function (e) {
_this._touchEventHandler(e);
});
addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerUp" : 'touchend', function (e) {
_this._touchEventHandler(e);
});
addEvent(this.overlaidCanvas, window.navigator.msPointerEnabled ? "MSPointerCancel" : 'touchcancel', function (e) {
_this._touchEventHandler(e);
});
if (!this._creditLink) {
this._creditLink = document.createElement("a");
this._creditLink.setAttribute("class", "canvasjs-chart-credit");
this._creditLink.setAttribute("style", "outline:none;margin:0px;position:absolute;right:3px;top:" + (this.height - 14) + "px;color:dimgrey;text-decoration:none;font-size:10px;font-family:Lucida Grande, Lucida Sans Unicode, Arial, sans-serif");
this._creditLink.setAttribute("tabIndex", -1);
this._creditLink.setAttribute("target", "_blank");
}
this._toolTip = new ToolTip(this, this._options.toolTip, this.theme);
this.data = null;
this.axisX = null;
this.axisY = null;
this.axisY2 = null;
this.sessionVariables = {
axisX: {
internalMinimum: null,
internalMaximum: null
},
axisY: {
internalMinimum: null,
internalMaximum: null
},
axisY2: {
internalMinimum: null,
internalMaximum: null
}
};
}
extend(Chart, CanvasJSObject);
//Update Chart Properties
Chart.prototype._updateOptions = function () {
var _this = this;
this.updateOption("width");
this.updateOption("height");
this.updateOption("theme");
if (this.updateOption("colorSet"))
this._selectedColorSet = typeof (colorSets[this.colorSet]) !== "undefined" ? colorSets[this.colorSet] : colorSets["colorSet1"];
this.updateOption("backgroundColor");
if (!this.backgroundColor)
this.backgroundColor = "rgba(0,0,0,0)";
this.updateOption("culture");
this._cultureInfo = new CultureInfo(this._options.culture);
this.updateOption("animationEnabled");
this.animationEnabled = this.animationEnabled && isCanvasSupported;
//Need to check this._options.zoomEnabled because this.zoomEnabled is used internally to keep track of state - and hence changes.
if (this._options.zoomEnabled) {
if (!this._zoomButton) {
hide(this._zoomButton = document.createElement("button"));
setButtonState(this, this._zoomButton, "pan");
this._toolBar.appendChild(this._zoomButton);
addEvent(this._zoomButton, "click", function () {
if (_this.zoomEnabled) {
_this.zoomEnabled = false;
_this.panEnabled = true;
setButtonState(_this, _this._zoomButton, "zoom");
} else {
_this.zoomEnabled = true;
_this.panEnabled = false;
setButtonState(_this, _this._zoomButton, "pan");
}
_this.render();
});
}
if (!this._resetButton) {
hide(this._resetButton = document.createElement("button"));
setButtonState(this, this._resetButton, "reset");
this._toolBar.appendChild(this._resetButton);
addEvent(this._resetButton, "click", function () {
_this._toolTip.hide();
if (_this.zoomEnabled || _this.panEnabled) {
_this.zoomEnabled = true;
_this.panEnabled = false;
setButtonState(_this, _this._zoomButton, "pan");
_this._defaultCursor = "default";
_this.overlaidCanvas.style.cursor = _this._defaultCursor;
} else {
_this.zoomEnabled = false;
_this.panEnabled = false;
}
if (_this._options.axisX && _this._options.axisX.minimum)
_this.sessionVariables.axisX.internalMinimum = _this._options.axisX.minimum;
else
_this.sessionVariables.axisX.internalMinimum = null;
if (_this._options.axisX && _this._options.axisX.maximum)
_this.sessionVariables.axisX.internalMaximum = _this._options.axisX.maximum;
else
_this.sessionVariables.axisX.internalMaximum = null;
_this.resetOverlayedCanvas();
hide(_this._zoomButton, _this._resetButton);
_this.render();
});
this.overlaidCanvas.style.cursor = _this._defaultCursor;
}
if (!this.zoomEnabled && !this.panEnabled) {
if (!this._zoomButton) {
this.zoomEnabled = true;
this.panEnabled = false;
} else {
if (_this._zoomButton.getAttribute("state") === _this._cultureInfo.zoomText) {
this.panEnabled = true;
this.zoomEnabled = false;
}
else {
this.zoomEnabled = true;
this.panEnabled = false;
}
show(_this._zoomButton, _this._resetButton);
}
}
} else {
this.zoomEnabled = false;
this.panEnabled = false;
}
// Update this.exportFileName.
if (typeof (this._options.exportFileName) !== "undefined") {
this.exportFileName = this._options.exportFileName;
}
// Update this.exportEnabled.
if (typeof (this._options.exportEnabled) !== "undefined") {
this.exportEnabled = this._options.exportEnabled;
}
if (this._menuButton) {
if (this.exportEnabled)
show(this._menuButton);
else
hide(this._menuButton);
} else if (this.exportEnabled && isCanvasSupported) {
this._menuButton = document.createElement("button");
setButtonState(this, this._menuButton, "menu");
this._toolBar.appendChild(this._menuButton);
addEvent(this._menuButton, "click", function () {
if (_this._dropdownMenu.style.display === "none") {
if (_this._dropDownCloseTime && ((new Date()).getTime() - _this._dropDownCloseTime.getTime() <= 500))
return;
_this._dropdownMenu.style.display = "block";
_this._menuButton.blur();
_this._dropdownMenu.focus();
}
}, true);
}
if (!this._dropdownMenu && this.exportEnabled && isCanvasSupported) {
this._dropdownMenu = document.createElement("div");
this._dropdownMenu.setAttribute("tabindex", -1);
this._dropdownMenu.style.cssText = "position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 1px;top: 25px;min-width: 120px;outline: 0;border: 1px solid silver;font-size: 14px;font-family: Calibri, Verdana, sans-serif;padding: 5px 0px 5px 0px;text-align: left;background-color: #fff;line-height: 20px;box-shadow: 2px 2px 10px #888888;";
_this._dropdownMenu.style.display = "none";
this._toolBar.appendChild(this._dropdownMenu);
addEvent(this._dropdownMenu, "blur", function () {
hide(_this._dropdownMenu);
_this._dropDownCloseTime = new Date();
}, true);
var exportOption = document.createElement("div");
exportOption.style.cssText = "padding: 2px 15px 2px 10px"
exportOption.innerHTML = this._cultureInfo.saveJPGText;
this._dropdownMenu.appendChild(exportOption);
addEvent(exportOption, "mouseover", function () {
this.style.backgroundColor = "#EEEEEE";
}, true);
addEvent(exportOption, "mouseout", function () {
this.style.backgroundColor = "transparent";
}, true);
addEvent(exportOption, "click", function () {
exportCanvas(_this.canvas, "jpg", _this.exportFileName);
hide(_this._dropdownMenu);
}, true);
var exportOption = document.createElement("div");
exportOption.style.cssText = "padding: 2px 15px 2px 10px"
exportOption.innerHTML = this._cultureInfo.savePNGText;
this._dropdownMenu.appendChild(exportOption);
addEvent(exportOption, "mouseover", function () {
this.style.backgroundColor = "#EEEEEE";
}, true);
addEvent(exportOption, "mouseout", function () {
this.style.backgroundColor = "transparent";
}, true);
addEvent(exportOption, "click", function () {
exportCanvas(_this.canvas, "png", _this.exportFileName);
hide(_this._dropdownMenu);
}, true);
}
if (this._toolBar.style.display !== "none" && this._zoomButton) {
this.panEnabled ? setButtonState(_this, _this._zoomButton, "zoom") : setButtonState(_this, _this._zoomButton, "pan");
if (_this._resetButton.getAttribute("state") !== _this._cultureInfo.resetText)
setButtonState(_this, _this._resetButton, "reset");
}
if (typeof (defaultOptions.Chart.creditHref) === "undefined") {
this.creditHref = "http://canvasjs.com/";
this.creditText = "CanvasJS.com";
} else {
var creditTextChanged = this.updateOption("creditText");
var creditHrefChanged = this.updateOption("creditHref");
}
if (this.renderCount === 0 || (creditTextChanged || creditHrefChanged)) {
this._creditLink.setAttribute("href", this.creditHref);
this._creditLink.innerHTML = this.creditText;
}
if (this.creditHref && this.creditText) {
if (!this._creditLink.parentElement)
this._canvasJSContainer.appendChild(this._creditLink);
}
else if (this._creditLink.parentElement)
this._canvasJSContainer.removeChild(this._creditLink);
if (this._options.toolTip && this._toolTip._options !== this._options.toolTip)
this._toolTip._options = this._options.toolTip
for (var prop in this._toolTip._options) {
if (this._toolTip._options.hasOwnProperty(prop)) {
this._toolTip.updateOption(prop);
}
}
}
Chart.prototype._updateSize = function () {
var width = 0;
var height = 0;
if (this._options.width)
width = this.width;
else
this.width = width = this._container.clientWidth > 0 ? this._container.clientWidth : this.width;
if (this._options.height)
height = this.height;
else
this.height = height = this._container.clientHeight > 0 ? this._container.clientHeight : this.height;
if (this.canvas.width !== width * devicePixelBackingStoreRatio || this.canvas.height !== height * devicePixelBackingStoreRatio) {
setCanvasSize(this.canvas, width, height);
setCanvasSize(this.overlaidCanvas, width, height);
setCanvasSize(this._eventManager.ghostCanvas, width, height);
return true;
}
return false;
}
// initialize chart objects
Chart.prototype._initialize = function () {
///
///Initializes Chart objects/state. Creates DataSeries class instance for each DataSeries provided by ther user. Sets the Axis Type based on the user data
///
//this.width = this.width;
if (!this._animator)
this._animator = new Animator(this);
else {
this._animator.cancelAllAnimations();
}
this.removeAllEventListeners();
this.disableToolTip = false;
this.pieDoughnutClickHandler = null;
//this._touchCurrentCoordinates = null;
if (this.animationRequestId)
this.cancelRequestAnimFrame.call(window, this.animationRequestId);
this._updateOptions();
this.animatedRender = isCanvasSupported && this.animationEnabled && (this.renderCount === 0);
this._updateSize();
//this._selectedColorSet = colorSets["colorSet2"];
//this.ctx.clearRect(0, 0, this.width, this.height);
this.clearCanvas();
this.ctx.beginPath();
this.axisX = null;
this.axisY = null;
this.axisY2 = null;
this._indexLabels = [];
this._dataInRenderedOrder = [];
this._events = [];
if (this._eventManager)
this._eventManager.reset();
this.plotInfo = {
axisPlacement: null,
axisXValueType: null,
plotTypes: []//array of plotType: {type:"", axisYType: "primary", dataSeriesIndexes:[]}
};
this.layoutManager = new LayoutManager(0, 0, this.width, this.height, 2);
if (this.plotArea.layoutManager)
this.plotArea.layoutManager.reset();
this.data = [];
var dataSeriesIndex = 0;
for (var series = 0; series < this._options.data.length; series++) {
//for (series in this._options.data) {
dataSeriesIndex++;
if (!(!this._options.data[series].type || Chart._supportedChartTypes.indexOf(this._options.data[series].type) >= 0))
continue;
var dataSeries = new DataSeries(this, this._options.data[series], this.theme, dataSeriesIndex - 1, ++this._eventManager.lastObjectId);
if (dataSeries.name === null)
dataSeries.name = "DataSeries " + (dataSeriesIndex);
if (dataSeries.color === null) {
if (this._options.data.length > 1) {
dataSeries._colorSet = [this._selectedColorSet[dataSeries.index % this._selectedColorSet.length]];
dataSeries.color = this._selectedColorSet[dataSeries.index % this._selectedColorSet.length];
} else {
if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area"
|| dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea" || dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
dataSeries._colorSet = [this._selectedColorSet[0]];
}
else
dataSeries._colorSet = this._selectedColorSet;
}
} else {
dataSeries._colorSet = [dataSeries.color];
}
if (dataSeries.markerSize === null) {
if (((dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline") && dataSeries.dataPoints && dataSeries.dataPoints.length < this.width / 16) || dataSeries.type === "scatter") {
//if (dataSeries.type === "line") {
dataSeries.markerSize = 8;
}
}
if ((dataSeries.type === "bubble" || dataSeries.type === "scatter") && dataSeries.dataPoints) {
dataSeries.dataPoints.sort(compareDataPointX)
}
//if (dataSeries.markerBorderThickness === null && dataSeries.type === "scatter") {
// dataSeries.markerBorderThickness = 2;
//}
//if (dataSeries.markerType === null) {
// if (dataSeries.type === "line" & dataSeries.dataPoints.length < 500) {
// dataSeries.markerType = "circle";
// }
//}
this.data.push(dataSeries);
var seriesAxisPlacement = dataSeries.axisPlacement;
//if (isDebugMode && window.console)
// window.console.log(dataSeries.type);
var errorMessage;
if (seriesAxisPlacement === "normal") {
if (this.plotInfo.axisPlacement === "xySwapped") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with bar chart";
} else if (this.plotInfo.axisPlacement === "none") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with pie chart";
} else if (this.plotInfo.axisPlacement === null)
this.plotInfo.axisPlacement = "normal";
}
else if (seriesAxisPlacement === "xySwapped") {
if (this.plotInfo.axisPlacement === "normal") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with line, area, column or pie chart";
} else if (this.plotInfo.axisPlacement === "none") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with pie chart";
} else if (this.plotInfo.axisPlacement === null)
this.plotInfo.axisPlacement = "xySwapped";
}
else if (seriesAxisPlacement == "none") {
if (this.plotInfo.axisPlacement === "normal") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with line, area, column or bar chart";
} else if (this.plotInfo.axisPlacement === "xySwapped") {
errorMessage = "You cannot combine \"" + dataSeries.type + "\" with bar chart";
} else if (this.plotInfo.axisPlacement === null)
this.plotInfo.axisPlacement = "none";
}
if (errorMessage && window.console) {
window.console.log(errorMessage);
return;
}
}
//if (isDebugMode && window.console) {
// window.console.log("xMin: " + this.plotInfo.viewPortXMin + "; xMax: " + this.plotInfo.viewPortXMax + "; yMin: " + this.plotInfo.yMin + "; yMax: " + this.plotInfo.yMax);
//}
this._objectsInitialized = true;
}
//indexOf is not supported in IE8-
Chart._supportedChartTypes = addArrayIndexOf(["line", "stepLine", "spline", "column", "area", "stepArea", "splineArea", "bar", "bubble", "scatter",
"stackedColumn", "stackedColumn100", "stackedBar", "stackedBar100",
"stackedArea", "stackedArea100",
"candlestick",
"ohlc",
"rangeColumn",
"rangeBar",
"rangeArea",
"rangeSplineArea",
"pie", "doughnut", "funnel"
]);
Chart.prototype.render = function (options) {
if (options)
this._options = options;
this._initialize();
var plotAreaElements = []; //Elements to be rendered inside the plotArea
//Create Primary and Secondary axis and assign them to the series
for (var i = 0; i < this.data.length; i++) {
if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
if (!this.data[i].axisYType || this.data[i].axisYType === "primary") {
if (!this.axisY) {
if (this.plotInfo.axisPlacement === "normal") {
this.axisY = new Axis(this, this._options.axisY, "axisY", "left");
}
else if (this.plotInfo.axisPlacement === "xySwapped") {
this.axisY = new Axis(this, this._options.axisY, "axisY", "bottom");
}
}
this.data[i].axisY = this.axisY;
}
else if (this.data[i].axisYType === "secondary") {
if (!this.axisY2) {
if (this.plotInfo.axisPlacement === "normal") {
this.axisY2 = new Axis(this, this._options.axisY2, "axisY", "right");
}
else if (this.plotInfo.axisPlacement === "xySwapped") {
this.axisY2 = new Axis(this, this._options.axisY2, "axisY", "top");
}
}
this.data[i].axisY = this.axisY2;
}
if (!this.axisX) {
if (this.plotInfo.axisPlacement === "normal") {
this.axisX = new Axis(this, this._options.axisX, "axisX", "bottom");
} else if (this.plotInfo.axisPlacement === "xySwapped") {
this.axisX = new Axis(this, this._options.axisX, "axisX", "left");
}
}
this.data[i].axisX = this.axisX;
}
}
this._processData();// Categorises the dataSeries and calculates min, max and other values
if (this._options.title) {
this._title = new Title(this, this._options.title);
if (!this._title.dockInsidePlotArea)
this._title.render();
else
plotAreaElements.push(this._title);
}
if (this._options.subtitles) {
for (var i = 0; i < this._options.subtitles.length; i++) {
this.subtitles = [];
var subtitle = new Subtitle(this, this._options.subtitles[i]);
this.subtitles.push(subtitle);
if (!subtitle.dockInsidePlotArea)
subtitle.render();
else
plotAreaElements.push(subtitle);
}
}
this.legend = new Legend(this, this._options.legend, this.theme);
for (var i = 0; i < this.data.length; i++) {
if (this.data[i].showInLegend || this.data[i].type === "pie" || this.data[i].type === "doughnut") {
this.legend.dataSeries.push(this.data[i]);
}
}
if (!this.legend.dockInsidePlotArea)
this.legend.render();
else
plotAreaElements.push(this.legend);
//TBI: Revisit and check if the functionality is enough.
if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
//var freeSpace = this.layoutManager.getFreeSpace();
Axis.setLayoutAndRender(this.axisX, this.axisY, this.axisY2, this.plotInfo.axisPlacement, this.layoutManager.getFreeSpace());
} else if (this.plotInfo.axisPlacement === "none") {
//In case of charts with axis this method is called inside setLayoutAndRender
this.preparePlotArea();
}
else {
return;
}
var index = 0;
for (index in plotAreaElements) {
plotAreaElements[index].render();
}
var animations = [];
if (this.animatedRender) {
var initialState = createCanvas(this.width, this.height);
var initialStateCtx = initialState.getContext("2d");
initialStateCtx.drawImage(this.canvas, 0, 0, this.width, this.height);
}
for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
var plotType = this.plotInfo.plotTypes[i];
for (var j = 0; j < plotType.plotUnits.length; j++) {
var plotUnit = plotType.plotUnits[j];
var animationInfo = null;
plotUnit.targetCanvas = null; //In case chart updates before the animation is complete, previous canvases need to be removed
if (this.animatedRender) {
plotUnit.targetCanvas = createCanvas(this.width, this.height);
plotUnit.targetCanvasCtx = plotUnit.targetCanvas.getContext("2d");
}
if (plotUnit.type === "line")
animationInfo = this.renderLine(plotUnit);
else if (plotUnit.type === "stepLine")
animationInfo = this.renderStepLine(plotUnit);
else if (plotUnit.type === "spline")
animationInfo = this.renderSpline(plotUnit);
else if (plotUnit.type === "column")
animationInfo = this.renderColumn(plotUnit);
else if (plotUnit.type === "bar")
animationInfo = this.renderBar(plotUnit);
else if (plotUnit.type === "area")
animationInfo = this.renderArea(plotUnit);
else if (plotUnit.type === "stepArea")
animationInfo = this.renderStepArea(plotUnit);
else if (plotUnit.type === "splineArea")
animationInfo = this.renderSplineArea(plotUnit);
else if (plotUnit.type === "stackedColumn")
animationInfo = this.renderStackedColumn(plotUnit);
else if (plotUnit.type === "stackedColumn100")
animationInfo = this.renderStackedColumn100(plotUnit);
else if (plotUnit.type === "stackedBar")
animationInfo = this.renderStackedBar(plotUnit);
else if (plotUnit.type === "stackedBar100")
animationInfo = this.renderStackedBar100(plotUnit);
else if (plotUnit.type === "stackedArea")
animationInfo = this.renderStackedArea(plotUnit);
else if (plotUnit.type === "stackedArea100")
animationInfo = this.renderStackedArea100(plotUnit);
else if (plotUnit.type === "bubble")
animationInfo = animationInfo = this.renderBubble(plotUnit);
else if (plotUnit.type === "scatter")
animationInfo = this.renderScatter(plotUnit);
else if (plotUnit.type === "pie")
this.renderPie(plotUnit);
else if (plotUnit.type === "doughnut")
this.renderPie(plotUnit);
else if (plotUnit.type === "candlestick")
animationInfo = this.renderCandlestick(plotUnit);
else if (plotUnit.type === "ohlc")
animationInfo = this.renderCandlestick(plotUnit);
else if (plotUnit.type === "rangeColumn")
animationInfo = this.renderRangeColumn(plotUnit);
else if (plotUnit.type === "rangeBar")
animationInfo = this.renderRangeBar(plotUnit);
else if (plotUnit.type === "rangeArea")
animationInfo = this.renderRangeArea(plotUnit);
else if (plotUnit.type === "rangeSplineArea")
animationInfo = this.renderRangeSplineArea(plotUnit);
for (var k = 0; k < plotUnit.dataSeriesIndexes.length; k++) {
this._dataInRenderedOrder.push(this.data[plotUnit.dataSeriesIndexes[k]]);
}
if (this.animatedRender && animationInfo)
animations.push(animationInfo);
}
}
if (this.animatedRender && this._indexLabels.length > 0) {
var indexLabelCanvas = createCanvas(this.width, this.height);
var indexLabelCanvasCtx = indexLabelCanvas.getContext("2d");
animations.push(this.renderIndexLabels(indexLabelCanvasCtx));
}
var _this = this;
if (animations.length > 0) {
//var animationCount = 0;
_this.disableToolTip = true;
_this._animator.animate(200, _this.animationDuration, function (fractionComplete) {
//console.log(fractionComplete);
//animationCount++;
_this.ctx.clearRect(0, 0, _this.width, _this.height);
// _this.ctx.drawImage(initialState, 0, 0, _this.width * devicePixelBackingStoreRatio, _this.height * devicePixelBackingStoreRatio, 0, 0, _this.width, _this.height);
_this.ctx.drawImage(initialState, 0, 0, Math.floor(_this.width * devicePixelBackingStoreRatio), Math.floor(_this.height * devicePixelBackingStoreRatio), 0, 0, _this.width, _this.height);
for (var l = 0; l < animations.length; l++) {
animationInfo = animations[l];
if (fractionComplete < 1 && typeof (animationInfo.startTimePercent) !== "undefined") {
if (fractionComplete >= animationInfo.startTimePercent) {
//animationInfo.animationCallback(AnimationHelper.easing.linear(fractionComplete - animationInfo.startTimePercent, 0, 1, 1 - animationInfo.startTimePercent), animationInfo);
animationInfo.animationCallback(animationInfo.easingFunction(fractionComplete - animationInfo.startTimePercent, 0, 1, 1 - animationInfo.startTimePercent), animationInfo);
}
} else {
animationInfo.animationCallback(animationInfo.easingFunction(fractionComplete, 0, 1, 1), animationInfo);
}
}
_this.dispatchEvent("dataAnimationIterationEnd",
{
chart: _this
});
}, function () {
animations = [];
var count = 0;
//Delete all render target canvases used for animation.
for (var i = 0; i < _this.plotInfo.plotTypes.length; i++) {
var plotType = _this.plotInfo.plotTypes[i];
for (var j = 0; j < plotType.plotUnits.length; j++) {
var plotUnit = plotType.plotUnits[j];
plotUnit.targetCanvas = null;
}
}
initialState = null;
_this.disableToolTip = false;
//console.log("*********** Animation Complete - " + animationCount + " ***********");
});
} else {
if (_this._indexLabels.length > 0)
_this.renderIndexLabels();
_this.dispatchEvent("dataAnimationIterationEnd",
{
chart: _this
});
}
this.attachPlotAreaEventHandlers();
if (!this.zoomEnabled && !this.panEnabled && this._zoomButton && this._zoomButton.style.display !== "none") {
hide(this._zoomButton, this._resetButton);
}
this._toolTip._updateToolTip();
this.renderCount++;
//if (window.console) {
// window.console.log(new Date().getTime() - dt);
//}
if (isDebugMode) {
var _this = this;
setTimeout(function () {
var ghostCanvasCopy = document.getElementById("ghostCanvasCopy");
if (ghostCanvasCopy) {
//console.log(ghostCanvasCopy.clientWidth);
setCanvasSize(ghostCanvasCopy, _this.width, _this.height);
var ghostCanvasCopyCtx = ghostCanvasCopy.getContext("2d");
//ghostCanvasCopyCtx.scale(1, 1);
//var imageData = this._eventManager.ghostCtx.getImageData(0, 0, this._container.clientWidth, this._container.clientHeight);
//this._eventManager.ghostCtx.drawImage(this._eventManager.ghostCanvas, 0, 0);
//this.ctx.drawImage(this._eventManager.ghostCanvas, 0, 0);
ghostCanvasCopyCtx.drawImage(_this._eventManager.ghostCanvas, 0, 0);
//_this._canvasJSContainer.appendChild(_this._eventManager.ghostCanvas);
//_this.overlaidCanvasCtx.drawImage(_this._eventManager.ghostCanvas, 0, 0);
}
}, 2000);
}
}
Chart.prototype.attachPlotAreaEventHandlers = function () {
//this._toolBar.style.display = "inline";
this.attachEvent({
context: this,
chart: this,
mousedown: this._plotAreaMouseDown,
mouseup: this._plotAreaMouseUp,
mousemove: this._plotAreaMouseMove,
cursor: this.zoomEnabled ? "col-resize" : "move",
cursor: this.panEnabled ? "move" : "default",
capture: true,
bounds: this.plotArea
});
}
Chart.prototype.categoriseDataSeries = function () {
var dataSeries = "";
for (var i = 0; i < this.data.length; i++) {
dataSeries = this.data[i]
if (!dataSeries.dataPoints || dataSeries.dataPoints.length === 0 || !dataSeries.visible)
continue;
if (Chart._supportedChartTypes.indexOf(dataSeries.type) >= 0) {
var plotType = null;
var plotTypeExists = false;
var plotUnit = null;
var plotUnitExists = false;
for (var j = 0; j < this.plotInfo.plotTypes.length; j++) {
if (this.plotInfo.plotTypes[j].type === dataSeries.type) {
plotTypeExists = true;
var plotType = this.plotInfo.plotTypes[j];
break;
}
}
if (!plotTypeExists) {
plotType = {
type: dataSeries.type,
totalDataSeries: 0,
plotUnits: []
};
this.plotInfo.plotTypes.push(plotType)
}
for (var j = 0; j < plotType.plotUnits.length; j++) {
if (plotType.plotUnits[j].axisYType === dataSeries.axisYType) {
plotUnitExists = true;
var plotUnit = plotType.plotUnits[j];
break;
}
}
if (!plotUnitExists) {
plotUnit = {
type: dataSeries.type,
previousDataSeriesCount: 0, //to be set next
index: plotType.plotUnits.length,
plotType: plotType,
axisYType: dataSeries.axisYType,
axisY: dataSeries.axisYType === "primary" ? this.axisY : this.axisY2,
axisX: this.axisX,
dataSeriesIndexes: [], //index of dataSeries
yTotals: []
}
plotType.plotUnits.push(plotUnit);
}
plotType.totalDataSeries++;
plotUnit.dataSeriesIndexes.push(i);
dataSeries.plotUnit = plotUnit;
}
}
for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
var plotType = this.plotInfo.plotTypes[i];
var previousDataSeriesCount = 0;
for (var j = 0; j < plotType.plotUnits.length; j++) {
plotType.plotUnits[j].previousDataSeriesCount = previousDataSeriesCount;
previousDataSeriesCount += plotType.plotUnits[j].dataSeriesIndexes.length;
}
}
}
Chart.prototype.assignIdToDataPoints = function () {
for (var i = 0; i < this.data.length; i++) {
var dataSeries = this.data[i];
if (!dataSeries.dataPoints)
continue;
var length = dataSeries.dataPoints.length;
for (var j = 0; j < length; j++) {
dataSeries.dataPointIds[j] = ++this._eventManager.lastObjectId;
}
}
}
Chart.prototype._processData = function () {
this.assignIdToDataPoints();
this.categoriseDataSeries();
for (var i = 0; i < this.plotInfo.plotTypes.length; i++) {
var plotType = this.plotInfo.plotTypes[i];
for (var j = 0; j < plotType.plotUnits.length; j++) {
var plotUnit = plotType.plotUnits[j];
if (plotUnit.type === "line" || plotUnit.type === "stepLine" || plotUnit.type === "spline" || plotUnit.type === "column" || plotUnit.type === "area" || plotUnit.type === "stepArea" || plotUnit.type === "splineArea" || plotUnit.type === "bar" || plotUnit.type === "bubble" || plotUnit.type === "scatter")
this._processMultiseriesPlotUnit(plotUnit);
else if (plotUnit.type === "stackedColumn" || plotUnit.type === "stackedBar" || plotUnit.type === "stackedArea")
this._processStackedPlotUnit(plotUnit);
else if (plotUnit.type === "stackedColumn100" || plotUnit.type === "stackedBar100" || plotUnit.type === "stackedArea100")
this._processStacked100PlotUnit(plotUnit);
else if (plotUnit.type === "candlestick" || plotUnit.type === "ohlc" || plotUnit.type === "rangeColumn" || plotUnit.type === "rangeBar" || plotUnit.type === "rangeArea" || plotUnit.type === "rangeSplineArea")
this._processMultiYPlotUnit(plotUnit);
}
}
}
Chart.prototype._processMultiseriesPlotUnit = function (plotUnit) {
if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
return;
var axisYDataInfo = plotUnit.axisY.dataInfo;
var axisXDataInfo = plotUnit.axisX.dataInfo;
var dataPointX, dataPointY;
var isDateTime = false;
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
var i = 0;
var isFirstDPInViewPort = false;
var isLastDPInViewPort = false;
if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
var plotAreaXMin = this.sessionVariables.axisX.internalMinimum ? this.sessionVariables.axisX.internalMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
var plotAreaXMax = this.sessionVariables.axisX.internalMaximum ? this.sessionVariables.axisX.internalMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
}
if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
isDateTime = true;
}
for (i = 0; i < dataSeries.dataPoints.length; i++) {
if (typeof dataSeries.dataPoints[i].x === "undefined") {
dataSeries.dataPoints[i].x = i;
}
if (dataSeries.dataPoints[i].x.getTime) {
isDateTime = true;
dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
}
else
dataPointX = dataSeries.dataPoints[i].x;
dataPointY = dataSeries.dataPoints[i].y;
if (dataPointX < axisXDataInfo.min)
axisXDataInfo.min = dataPointX;
if (dataPointX > axisXDataInfo.max)
axisXDataInfo.max = dataPointX;
if (dataPointY < axisYDataInfo.min)
axisYDataInfo.min = dataPointY;
if (dataPointY > axisYDataInfo.max)
axisYDataInfo.max = dataPointY;
if (i > 0) {
var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
axisXDataInfo.minDiff = xDiff;
}
}
// This section makes sure that partially visible dataPoints are included in the begining
if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
continue;
} else if (!isFirstDPInViewPort) {
isFirstDPInViewPort = true;
if (i > 0) {
i -= 2;
continue;
}
}
// This section makes sure that partially visible dataPoints are included at the end
if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
isLastDPInViewPort = true;
} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
continue;
}
if (dataSeries.dataPoints[i].label)
plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
if (dataPointX < axisXDataInfo.viewPortMin)
axisXDataInfo.viewPortMin = dataPointX;
if (dataPointX > axisXDataInfo.viewPortMax)
axisXDataInfo.viewPortMax = dataPointX;
if (dataPointY === null)
continue;
if (dataPointY < axisYDataInfo.viewPortMin)
axisYDataInfo.viewPortMin = dataPointY;
if (dataPointY > axisYDataInfo.viewPortMax)
axisYDataInfo.viewPortMax = dataPointY;
}
this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
}
//this.dataPoints.sort(compareDataPointX);
//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
}
Chart.prototype._processStackedPlotUnit = function (plotUnit) {
if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
return;
var axisYDataInfo = plotUnit.axisY.dataInfo;
var axisXDataInfo = plotUnit.axisX.dataInfo;
var dataPointX, dataPointY;
var isDateTime = false;
var dataPointYPositiveSums = [];
var dataPointYNegativeSums = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
var i = 0;
var isFirstDPInViewPort = false;
var isLastDPInViewPort = false;
if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
var plotAreaXMin = this.sessionVariables.axisX.internalMinimum ? this.sessionVariables.axisX.internalMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
var plotAreaXMax = this.sessionVariables.axisX.internalMaximum ? this.sessionVariables.axisX.internalMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
}
if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
isDateTime = true;
}
for (i = 0; i < dataSeries.dataPoints.length; i++) {
// Requird when no x values are provided
if (typeof dataSeries.dataPoints[i].x === "undefined") {
dataSeries.dataPoints[i].x = i;
}
if (dataSeries.dataPoints[i].x.getTime) {
isDateTime = true;
dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
}
else
dataPointX = dataSeries.dataPoints[i].x;
dataPointY = dataSeries.dataPoints[i].y;
if (dataPointX < axisXDataInfo.min)
axisXDataInfo.min = dataPointX;
if (dataPointX > axisXDataInfo.max)
axisXDataInfo.max = dataPointX;
if (i > 0) {
var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
axisXDataInfo.minDiff = xDiff;
}
}
// This section makes sure that partially visible dataPoints are included in the begining
if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
continue;
} else if (!isFirstDPInViewPort) {
isFirstDPInViewPort = true;
if (i > 0) {
i -= 2;
continue;
}
}
// This section makes sure that partially visible dataPoints are included at the end
if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
isLastDPInViewPort = true;
} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
continue;
}
if (dataSeries.dataPoints[i].label)
plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
if (dataPointX < axisXDataInfo.viewPortMin)
axisXDataInfo.viewPortMin = dataPointX;
if (dataPointX > axisXDataInfo.viewPortMax)
axisXDataInfo.viewPortMax = dataPointX;
if (dataPointY === null)
continue;
plotUnit.yTotals[dataPointX] = (!plotUnit.yTotals[dataPointX] ? 0 : plotUnit.yTotals[dataPointX]) + Math.abs(dataPointY);
if (dataPointY >= 0) {
if (dataPointYPositiveSums[dataPointX])
dataPointYPositiveSums[dataPointX] += dataPointY;
else
dataPointYPositiveSums[dataPointX] = dataPointY;
} else {
if (dataPointYNegativeSums[dataPointX])
dataPointYNegativeSums[dataPointX] += dataPointY;
else
dataPointYNegativeSums[dataPointX] = dataPointY;
}
}
this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
}
for (i in dataPointYPositiveSums) {
if (isNaN(i)) {
continue;
}
var ySum = dataPointYPositiveSums[i];
if (ySum < axisYDataInfo.min)
axisYDataInfo.min = ySum;
if (ySum > axisYDataInfo.max)
axisYDataInfo.max = ySum;
if (i < axisXDataInfo.viewPortMin || i > axisXDataInfo.viewPortMax)
continue;
if (ySum < axisYDataInfo.viewPortMin)
axisYDataInfo.viewPortMin = ySum;
if (ySum > axisYDataInfo.viewPortMax)
axisYDataInfo.viewPortMax = ySum;
}
for (i in dataPointYNegativeSums) {
if (isNaN(i)) {
continue;
}
var ySum = dataPointYNegativeSums[i];
if (ySum < axisYDataInfo.min)
axisYDataInfo.min = ySum;
if (ySum > axisYDataInfo.max)
axisYDataInfo.max = ySum;
if (i < axisXDataInfo.viewPortMin || i > axisXDataInfo.viewPortMax)
continue;
if (ySum < axisYDataInfo.viewPortMin)
axisYDataInfo.viewPortMin = ySum;
if (ySum > axisYDataInfo.viewPortMax)
axisYDataInfo.viewPortMax = ySum;
}
//this.dataPoints.sort(compareDataPointX);
//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
//window.console.log("viewPortYMin: " + plotInfo.viewPortYMin + "; viewPortYMax: " + plotInfo.viewPortYMax);
}
Chart.prototype._processStacked100PlotUnit = function (plotUnit) {
if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
return;
var axisYDataInfo = plotUnit.axisY.dataInfo;
var axisXDataInfo = plotUnit.axisX.dataInfo;
var dataPointX, dataPointY;
var isDateTime = false;
var containsPositiveY = false;
var containsNegativeY = false;
var dataPointYSums = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
var i = 0;
var isFirstDPInViewPort = false;
var isLastDPInViewPort = false;
if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
var plotAreaXMin = this.sessionVariables.axisX.internalMinimum ? this.sessionVariables.axisX.internalMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
var plotAreaXMax = this.sessionVariables.axisX.internalMaximum ? this.sessionVariables.axisX.internalMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
}
if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
isDateTime = true;
}
for (i = 0; i < dataSeries.dataPoints.length; i++) {
// Requird when no x values are provided
if (typeof dataSeries.dataPoints[i].x === "undefined") {
dataSeries.dataPoints[i].x = i;
}
if (dataSeries.dataPoints[i].x.getTime) {
isDateTime = true;
dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
}
else
dataPointX = dataSeries.dataPoints[i].x;
dataPointY = dataSeries.dataPoints[i].y;
if (dataPointX < axisXDataInfo.min)
axisXDataInfo.min = dataPointX;
if (dataPointX > axisXDataInfo.max)
axisXDataInfo.max = dataPointX;
if (i > 0) {
var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
axisXDataInfo.minDiff = xDiff;
}
}
// This section makes sure that partially visible dataPoints are included in the begining
if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
continue;
} else if (!isFirstDPInViewPort) {
isFirstDPInViewPort = true;
if (i > 0) {
i -= 2;
continue;
}
}
// This section makes sure that partially visible dataPoints are included at the end
if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
isLastDPInViewPort = true;
} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
continue;
}
if (dataSeries.dataPoints[i].label)
plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
if (dataPointX < axisXDataInfo.viewPortMin)
axisXDataInfo.viewPortMin = dataPointX;
if (dataPointX > axisXDataInfo.viewPortMax)
axisXDataInfo.viewPortMax = dataPointX;
if (dataPointY === null)
continue;
plotUnit.yTotals[dataPointX] = (!plotUnit.yTotals[dataPointX] ? 0 : plotUnit.yTotals[dataPointX]) + Math.abs(dataPointY);
if (dataPointY >= 0) {
containsPositiveY = true;
} else {
containsNegativeY = true;
}
if (dataPointYSums[dataPointX])
dataPointYSums[dataPointX] += Math.abs(dataPointY);
else
dataPointYSums[dataPointX] = Math.abs(dataPointY);
}
this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
}
if (containsPositiveY && !containsNegativeY) {
axisYDataInfo.max = 99;
axisYDataInfo.min = 1;
} else if (containsPositiveY && containsNegativeY) {
axisYDataInfo.max = 99;
axisYDataInfo.min = -99;
} else if (!containsPositiveY && containsNegativeY) {
axisYDataInfo.max = -1;
axisYDataInfo.min = -99;
}
axisYDataInfo.viewPortMin = axisYDataInfo.min;
axisYDataInfo.viewPortMax = axisYDataInfo.max;
plotUnit.dataPointYSums = dataPointYSums;
//this.dataPoints.sort(compareDataPointX);
//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
//window.console.log("viewPortYMin: " + plotInfo.viewPortYMin + "; viewPortYMax: " + plotInfo.viewPortYMax);
}
Chart.prototype._processMultiYPlotUnit = function (plotUnit) {
if (!plotUnit.dataSeriesIndexes || plotUnit.dataSeriesIndexes.length < 1)
return;
var axisYDataInfo = plotUnit.axisY.dataInfo;
var axisXDataInfo = plotUnit.axisX.dataInfo;
var dataPointX, dataPointY, dataPointYMin, dataPointYMax;
var isDateTime = false;
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeries = this.data[plotUnit.dataSeriesIndexes[j]];
var i = 0;
var isFirstDPInViewPort = false;
var isLastDPInViewPort = false;
if (dataSeries.axisPlacement === "normal" || dataSeries.axisPlacement === "xySwapped") {
var plotAreaXMin = this.sessionVariables.axisX.internalMinimum ? this.sessionVariables.axisX.internalMinimum : (this._options.axisX && this._options.axisX.minimum) ? this._options.axisX.minimum : -Infinity;
var plotAreaXMax = this.sessionVariables.axisX.internalMaximum ? this.sessionVariables.axisX.internalMaximum : (this._options.axisX && this._options.axisX.maximum) ? this._options.axisX.maximum : Infinity;
}
if (dataSeries.dataPoints[i].x && dataSeries.dataPoints[i].x.getTime || dataSeries.xValueType === "dateTime") {
isDateTime = true;
}
for (i = 0; i < dataSeries.dataPoints.length; i++) {
if (typeof dataSeries.dataPoints[i].x === "undefined") {
dataSeries.dataPoints[i].x = i;
}
if (dataSeries.dataPoints[i].x.getTime) {
isDateTime = true;
dataPointX = dataSeries.dataPoints[i].x.getTime();//dataPointX is used so that getTime is called only once in case of dateTime values
}
else
dataPointX = dataSeries.dataPoints[i].x;
dataPointY = dataSeries.dataPoints[i].y;
if (dataPointY && dataPointY.length) {
dataPointYMin = Math.min.apply(null, dataPointY);
dataPointYMax = Math.max.apply(null, dataPointY);
}
if (dataPointX < axisXDataInfo.min)
axisXDataInfo.min = dataPointX;
if (dataPointX > axisXDataInfo.max)
axisXDataInfo.max = dataPointX;
if (dataPointYMin < axisYDataInfo.min)
axisYDataInfo.min = dataPointYMin;
if (dataPointYMax > axisYDataInfo.max)
axisYDataInfo.max = dataPointYMax;
if (i > 0) {
var xDiff = dataPointX - dataSeries.dataPoints[i - 1].x;
xDiff < 0 && (xDiff = xDiff * -1); //If Condition shortcut
if (axisXDataInfo.minDiff > xDiff && xDiff !== 0) {
axisXDataInfo.minDiff = xDiff;
}
}
// This section makes sure that partially visible dataPoints are included in the begining
if (dataPointX < plotAreaXMin && !isFirstDPInViewPort) {
continue;
} else if (!isFirstDPInViewPort) {
isFirstDPInViewPort = true;
if (i > 0) {
i -= 2;
continue;
}
}
// This section makes sure that partially visible dataPoints are included at the end
if (dataPointX > plotAreaXMax && !isLastDPInViewPort) {
isLastDPInViewPort = true;
} else if (dataPointX > plotAreaXMax && isLastDPInViewPort) {
continue;
}
if (dataSeries.dataPoints[i].label)
plotUnit.axisX.labels[dataPointX] = dataSeries.dataPoints[i].label;
if (dataPointX < axisXDataInfo.viewPortMin)
axisXDataInfo.viewPortMin = dataPointX;
if (dataPointX > axisXDataInfo.viewPortMax)
axisXDataInfo.viewPortMax = dataPointX;
if (dataPointY === null)
continue;
if (dataPointYMin < axisYDataInfo.viewPortMin)
axisYDataInfo.viewPortMin = dataPointYMin;
if (dataPointYMax > axisYDataInfo.viewPortMax)
axisYDataInfo.viewPortMax = dataPointYMax;
}
this.plotInfo.axisXValueType = dataSeries.xValueType = isDateTime ? "dateTime" : "number";
}
//this.dataPoints.sort(compareDataPointX);
//this.dataPoints.sort(function (dataPoint1, dataPoint2) { return dataPoint1.x - dataPoint2.x; });
}
//getClosest returns objects nearby and hence shouldn't be used for events like click, mouseover, mousemove, etc which require object that is exactly under the mouse.
Chart.prototype.getDataPointAtXY = function (mouseX, mouseY, getClosest) {
getClosest = getClosest || false;
var results = [];
for (var i = this._dataInRenderedOrder.length - 1; i >= 0; i--) {
var dataSeries = this._dataInRenderedOrder[i];
var result = null;
result = dataSeries.getDataPointAtXY(mouseX, mouseY, getClosest);
if (result)
results.push(result);
}
var closestResult = null;
var onlyLineAreaTypes = false;
for (var m = 0; m < results.length; m++) {
if (results[m].dataSeries.type === "line" || results[m].dataSeries.type === "stepLine" || results[m].dataSeries.type === "area" || results[m].dataSeries.type === "stepArea") {
var markerSize = getProperty("markerSize", results[m].dataPoint, results[m].dataSeries) || 8;
if (results[m].distance <= markerSize / 2) {
onlyLineAreaTypes = true;
break;
}
}
}
for (m = 0; m < results.length; m++) {
if (onlyLineAreaTypes && results[m].dataSeries.type !== "line" && results[m].dataSeries.type !== "stepLine" && results[m].dataSeries.type !== "area" && results[m].dataSeries.type !== "stepArea")
continue;
if (!closestResult) {
closestResult = results[m];
} else if (results[m].distance <= closestResult.distance) {
closestResult = results[m];
}
}
return closestResult;
}
Chart.prototype.getObjectAtXY = function (mouseX, mouseY, getClosest) {
getClosest = getClosest || false;
var id = null;
var dataPointInfo = this.getDataPointAtXY(mouseX, mouseY, getClosest);
if (dataPointInfo) {
id = dataPointInfo.dataSeries.dataPointIds[dataPointInfo.dataPointIndex];
} else if (isCanvasSupported) {//IE9+
id = getObjectId(mouseX, mouseY, this._eventManager.ghostCtx);
}
else {
for (var i = 0; i < this.legend.items.length; i++) {
var item = this.legend.items[i];
if (mouseX >= item.x1 && mouseX <= item.x2 && mouseY >= item.y1 && mouseY <= item.y2) {
id = item.id;
}
}
}
return id;
}
/// Calculates Font Size based on standardSize and Chart Size
/// Standard font size for a Chart with min(width,height) = 400px
/// The area.
Chart.prototype.getAutoFontSize = function (standardSize, width, height) {
width = width || this.width;
height = height || this.height;
var fontSizeScaleFactor = standardSize / 400;
return Math.round(Math.min(this.width, this.height) * fontSizeScaleFactor);
}
//#region Events
Chart.prototype.resetOverlayedCanvas = function () {
//var width = this.overlaidCanvas.width;
//this.overlaidCanvas.width = 0;
//this.overlaidCanvas.width = width;
this.overlaidCanvasCtx.clearRect(0, 0, this.width, this.height);
}
Chart.prototype.clearCanvas = function () {
this.ctx.clearRect(0, 0, this.width, this.height);
if (this.backgroundColor) {
this.ctx.fillStyle = this.backgroundColor;
this.ctx.fillRect(0, 0, this.width, this.height);
}
}
Chart.prototype.attachEvent = function (param) {
this._events.push(param);
}
Chart.prototype._touchEventHandler = function (ev) {
if (!ev.changedTouches || !this.interactivityEnabled)
return;
var mouseEvents = [];
var touches = ev.changedTouches;
var first = touches ? touches[0] : ev;
var touchCurrentCoordinates = null;
//window.console.log(touches.length);
switch (ev.type) {
case "touchstart": case "MSPointerDown":
mouseEvents = ["mousemove", "mousedown"];
this._lastTouchData = getMouseCoordinates(first);
this._lastTouchData.time = new Date();
break;
case "touchmove": case "MSPointerMove": mouseEvents = ["mousemove"]; break;
case "touchend": case "MSPointerUp": mouseEvents = (this._lastTouchEventType === "touchstart" || this._lastTouchEventType === "MSPointerDown") ? ["mouseup", "click"] : ["mouseup"];
break;
default: return;
}
if (touches && touches.length > 1) return;
touchCurrentCoordinates = getMouseCoordinates(first);
touchCurrentCoordinates.time = new Date();
try {
var dy = touchCurrentCoordinates.y - this._lastTouchData.y;
var dx = touchCurrentCoordinates.x - this._lastTouchData.x;
var dt = touchCurrentCoordinates.time - this._lastTouchData.time;
if (Math.abs(dy) > 15 && (!!this._lastTouchData.scroll || dt < 200)) {
//this._lastTouchData.y = touchCurrentCoordinates.y;
this._lastTouchData.scroll = true;
var win = window.parent || window;
if (win && win.scrollBy)
win.scrollBy(0, -dy);
}
} catch (e) { };
this._lastTouchEventType = ev.type;
if (!!this._lastTouchData.scroll && this.zoomEnabled) {
if (this.isDrag)
this.resetOverlayedCanvas();
this.isDrag = false;
return;
}
for (var i = 0; i < mouseEvents.length; i++) {
var type = mouseEvents[i];
var simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY,
first.clientX, first.clientY, false,
false, false, false, 0, null);
first.target.dispatchEvent(simulatedEvent);
if (ev.preventManipulation) {
//alert("preventManipulation");
ev.preventManipulation();
}
if (ev.preventDefault) {
//alert("preventDefault");
ev.preventDefault();
}
}
}
Chart.prototype._mouseEventHandler = function (ev) {
if (!this.interactivityEnabled)
return;
if (this._ignoreNextEvent) {
this._ignoreNextEvent = false;
return;
}
// stop panning and zooming so we can draw
if (ev.preventManipulation) {
//alert("preventManipulation");
ev.preventManipulation();
}
// we are handling this event
if (ev.preventDefault) {
//alert("preventDefault");
ev.preventDefault();
}
//IE8- uses srcElement instead of target. So instead of checking this condition everytime, its better to create a reference called target.
if (typeof (ev.target) === "undefined" && ev.srcElement)
ev.target = ev.srcElement;
//console.log(ev.type);
var xy = getMouseCoordinates(ev);
var type = ev.type;
var eventParam;
var rightclick;
if (!ev) var e = window.event;
if (ev.which) rightclick = (ev.which == 3);
else if (ev.button) rightclick = (ev.button == 2);
//window.console.log(type + " --> x: " + xy.x + "; y:" + xy.y);
//if (type === "mouseout") {
// this._toolTip.hide();
//}
if (isDebugMode && window.console) {
window.console.log(type + " --> x: " + xy.x + "; y:" + xy.y);
if (rightclick)
window.console.log(ev.which);
if (type === "mouseup")
window.console.log("mouseup");
}
if (rightclick)
return;
//if (this.plotInfo.axisPlacement === "xySwapped") {
// //var temp = xy.x;
// //xy.x = xy.y;
// //xy.y = temp;
// xy = {x: xy.y, y: xy.x};
//}
if (Chart.capturedEventParam) {
eventParam = Chart.capturedEventParam;
if (type === "mouseup") {
Chart.capturedEventParam = null;
if (eventParam.chart.overlaidCanvas.releaseCapture)
eventParam.chart.overlaidCanvas.releaseCapture();
else
document.body.removeEventListener("mouseup", eventParam.chart._mouseEventHandler, false);
}
if (eventParam.hasOwnProperty(type))
eventParam[type].call(eventParam.context, xy.x, xy.y);
}
else if (this._events) {
for (var i = 0; i < this._events.length; i++) {
if (!this._events[i].hasOwnProperty(type))
continue;
eventParam = this._events[i];
var bounds = eventParam.bounds;
if (xy.x >= bounds.x1 && xy.x <= bounds.x2 && xy.y >= bounds.y1 && xy.y <= bounds.y2) {
eventParam[type].call(eventParam.context, xy.x, xy.y);
if (type === "mousedown" && eventParam.capture === true) {
Chart.capturedEventParam = eventParam;
if (this.overlaidCanvas.setCapture)
this.overlaidCanvas.setCapture();
else {
document.body.addEventListener("mouseup", this._mouseEventHandler, false);
//addEvent(document.body, "mouseup", this._mouseEventHandler);
}
} else if (type === "mouseup") {
if (eventParam.chart.overlaidCanvas.releaseCapture)
eventParam.chart.overlaidCanvas.releaseCapture();
else
document.body.removeEventListener("mouseup", this._mouseEventHandler, false);
}
break;
}
else
eventParam = null;
}
if (eventParam && eventParam.cursor) {
ev.target.style.cursor = eventParam.cursor;
}
else
ev.target.style.cursor = this._defaultCursor;
//eventParam =
}
if (this._toolTip && this._toolTip.enabled) {
var plotArea = this.plotArea;
if (xy.x < plotArea.x1 || xy.x > plotArea.x2 || xy.y < plotArea.y1 || xy.y > plotArea.y2)
this._toolTip.hide();
}
if ((!this.isDrag || !this.zoomEnabled) && this._eventManager) {
this._eventManager.mouseEventHandler(ev);
//this._updateToolTip(ev.x, ev.y);
}
//if (this._toolTip.enabled)
// this._toolTip.mouseMoveHandler(ev.x, ev.y);
}
Chart.prototype._plotAreaMouseDown = function (x, y) {
this.isDrag = true;
if (this.plotInfo.axisPlacement !== "none") {
this.dragStartPoint = { x: x, y: y, xMinimum: this.axisX.minimum, xMaximum: this.axisX.maximum };
} else {
this.dragStartPoint = { x: x, y: y };
}
}
Chart.prototype._plotAreaMouseUp = function (x, y) {
if (this.plotInfo.axisPlacement === "normal" || this.plotInfo.axisPlacement === "xySwapped") {
if (this.isDrag) {
var dragDelta = 0;
var dragValue = 0;
var axisXProps = this.axisX.lineCoordinates;
if (this.plotInfo.axisPlacement === "xySwapped") {
dragDelta = y - this.dragStartPoint.y;
dragValue = Math.abs(this.axisX.maximum - this.axisX.minimum) / axisXProps.height * dragDelta;
}
else {
dragDelta = this.dragStartPoint.x - x;
dragValue = Math.abs(this.axisX.maximum - this.axisX.minimum) / axisXProps.width * dragDelta;
}
if (Math.abs(dragDelta) > 2) {
if (this.panEnabled) {
var reRender = false;
var overFlow = 0;
//If the user has panned beyond the minimum/maximum value of axisX, then take it back to minimum/maximum.
if (this.axisX.sessionVariables.internalMinimum < this.axisX._absoluteMinimum) {
overFlow = this.axisX._absoluteMinimum - this.axisX.sessionVariables.internalMinimum;
this.axisX.sessionVariables.internalMinimum += overFlow;
this.axisX.sessionVariables.internalMaximum += overFlow;
reRender = true;
} else if (this.axisX.sessionVariables.internalMaximum > this.axisX._absoluteMaximum) {
overFlow = this.axisX.sessionVariables.internalMaximum - this.axisX._absoluteMaximum;
this.axisX.sessionVariables.internalMaximum -= overFlow;
this.axisX.sessionVariables.internalMinimum -= overFlow;
reRender = true;
}
//}
//this.overlaidCanvas.style.cursor = this._defaultCursor;
if (reRender)
this.render();
} else if (this.zoomEnabled) {
this.resetOverlayedCanvas();
//alert("mouse UP");
if (!this.dragStartPoint)
return;
if (this.plotInfo.axisPlacement === "xySwapped") {
//In Pixels
var selectedRegion = { y1: Math.min(this.dragStartPoint.y, y), y2: Math.max(this.dragStartPoint.y, y) };
if (Math.abs(selectedRegion.y1 - selectedRegion.y2) > 1) {
var axisXProps = this.axisX.lineCoordinates;
var minX = this.axisX.maximum - (this.axisX.maximum - this.axisX.minimum) / axisXProps.height * (selectedRegion.y2 - axisXProps.y1);
var maxX = this.axisX.maximum - (this.axisX.maximum - this.axisX.minimum) / axisXProps.height * (selectedRegion.y1 - axisXProps.y1);
minX = Math.max(minX, this.axisX.dataInfo.min);
maxX = Math.min(maxX, this.axisX.dataInfo.max);
if (Math.abs(maxX - minX) > 2 * Math.abs(this.axisX.dataInfo.minDiff)) {
this.axisX.sessionVariables.internalMinimum = minX;
this.axisX.sessionVariables.internalMaximum = maxX;
this.render();
}
}
} else if (this.plotInfo.axisPlacement === "normal") {
var selectedRegion = { x1: Math.min(this.dragStartPoint.x, x), x2: Math.max(this.dragStartPoint.x, x) };
if (Math.abs(selectedRegion.x1 - selectedRegion.x2) > 1) {
var axisXProps = this.axisX.lineCoordinates;
var minX = (this.axisX.maximum - this.axisX.minimum) / axisXProps.width * (selectedRegion.x1 - axisXProps.x1) + this.axisX.minimum;
var maxX = (this.axisX.maximum - this.axisX.minimum) / axisXProps.width * (selectedRegion.x2 - axisXProps.x1) + this.axisX.minimum;
minX = Math.max(minX, this.axisX.dataInfo.min);
maxX = Math.min(maxX, this.axisX.dataInfo.max);
if (Math.abs(maxX - minX) > 2 * Math.abs(this.axisX.dataInfo.minDiff)) {
this.axisX.sessionVariables.internalMinimum = minX;
this.axisX.sessionVariables.internalMaximum = maxX;
this.render();
}
}
}
}
this._ignoreNextEvent = true;//Required so that click event doesn't fire after zooming into a section of the chart.
if (this.zoomEnabled && this._zoomButton.style.display === "none") {
show(this._zoomButton, this._resetButton);
setButtonState(this, this._zoomButton, "pan");
setButtonState(this, this._resetButton, "reset");
}
}
}
}
this.isDrag = false;
//this.dragStartPoint = null;
}
Chart.prototype._plotAreaMouseMove = function (x, y) {
if (this.isDrag && this.plotInfo.axisPlacement !== "none") {
var dragDelta = 0;
var dragValue = 0;
var axisXProps = this.axisX.lineCoordinates;
if (this.plotInfo.axisPlacement === "xySwapped") {
dragDelta = y - this.dragStartPoint.y;
dragValue = Math.abs(this.axisX.maximum - this.axisX.minimum) / axisXProps.height * dragDelta;
}
else {
dragDelta = this.dragStartPoint.x - x;
dragValue = Math.abs(this.axisX.maximum - this.axisX.minimum) / axisXProps.width * dragDelta;
}
if (Math.abs(dragDelta) > 2 && Math.abs(dragDelta) < 8 && (this.panEnabled || this.zoomEnabled)) {
this._toolTip.hide();
} else if (!this.panEnabled && !this.zoomEnabled) {
this._toolTip.mouseMoveHandler(x, y);
}
if (Math.abs(dragDelta) > 2 && (this.panEnabled || this.zoomEnabled)) {
if (this.panEnabled) {
this.axisX.sessionVariables.internalMinimum = this.dragStartPoint.xMinimum + dragValue;
this.axisX.sessionVariables.internalMaximum = this.dragStartPoint.xMaximum + dragValue;
var overFlow = 0;
// This is to stop the user from dragging chart beyond some limit (this.axisX._absoluteMinimum - this.axisX.interval)
if (this.axisX.sessionVariables.internalMinimum < this.axisX._absoluteMinimum - convertToNumber(this.axisX.interval, this.axisX.intervalType)) {
overFlow = (this.axisX._absoluteMinimum - convertToNumber(this.axisX.interval, this.axisX.intervalType)) - this.axisX.sessionVariables.internalMinimum;
this.axisX.sessionVariables.internalMinimum += overFlow;
this.axisX.sessionVariables.internalMaximum += overFlow;
} else if (this.axisX.sessionVariables.internalMaximum > this.axisX._absoluteMaximum + convertToNumber(this.axisX.interval, this.axisX.intervalType)) {
overFlow = this.axisX.sessionVariables.internalMaximum - (this.axisX._absoluteMaximum + convertToNumber(this.axisX.interval, this.axisX.intervalType));
this.axisX.sessionVariables.internalMaximum -= overFlow;
this.axisX.sessionVariables.internalMinimum -= overFlow;
}
//this.dragStartPoint.x = x;
//this.render();
var _this = this;
clearTimeout(this._panTimerId);
this._panTimerId = setTimeout(function () {
_this.render();
}, 0);
} else if (this.zoomEnabled) {
var plotAreaBounds = this.plotArea;
this.resetOverlayedCanvas();
var alpha = this.overlaidCanvasCtx.globalAlpha;
this.overlaidCanvasCtx.globalAlpha = .7;
this.overlaidCanvasCtx.fillStyle = "#A0ABB8";
if (this.plotInfo.axisPlacement === "xySwapped") {
this.overlaidCanvasCtx.fillRect(plotAreaBounds.x1, this.dragStartPoint.y, plotAreaBounds.x2 - plotAreaBounds.x1, y - this.dragStartPoint.y);
}
else if (this.plotInfo.axisPlacement === "normal") {
this.overlaidCanvasCtx.fillRect(this.dragStartPoint.x, plotAreaBounds.y1, x - this.dragStartPoint.x, plotAreaBounds.y2 - plotAreaBounds.y1);
}
this.overlaidCanvasCtx.globalAlpha = alpha;
}
}
//if (dragDelta > 5) {
// this._toolTip.hide();
// return;
//} else if (this._toolTip.enabled)
// this._toolTip.mouseMoveHandler(x, y);
} else
this._toolTip.mouseMoveHandler(x, y);
}
//#endregion Events
Chart.prototype.preparePlotArea = function () {
var plotArea = this.plotArea;
var yAxis = this.axisY ? this.axisY : this.axisY2;
if (!isCanvasSupported && (plotArea.x1 > 0 || plotArea.y1 > 0)) {
plotArea.ctx.translate(plotArea.x1, plotArea.y1);
}
if (this.axisX && yAxis) {
plotArea.x1 = this.axisX.lineCoordinates.x1 < this.axisX.lineCoordinates.x2 ? this.axisX.lineCoordinates.x1 : yAxis.lineCoordinates.x1;
plotArea.y1 = (this.axisX.lineCoordinates.y1 < yAxis.lineCoordinates.y1 ? this.axisX.lineCoordinates.y1 : yAxis.lineCoordinates.y1);
plotArea.x2 = (this.axisX.lineCoordinates.x2 > yAxis.lineCoordinates.x2 ? this.axisX.lineCoordinates.x2 : yAxis.lineCoordinates.x2);
plotArea.y2 = this.axisX.lineCoordinates.y2 > this.axisX.lineCoordinates.y1 ? this.axisX.lineCoordinates.y2 : yAxis.lineCoordinates.y2;
plotArea.width = plotArea.x2 - plotArea.x1;
plotArea.height = plotArea.y2 - plotArea.y1;
//plotArea = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
} else {
//ToDo: @sunil
var freeSpace = this.layoutManager.getFreeSpace();
plotArea.x1 = freeSpace.x1;
plotArea.x2 = freeSpace.x2;
plotArea.y1 = freeSpace.y1;
plotArea.y2 = freeSpace.y2;
plotArea.width = freeSpace.width;
plotArea.height = freeSpace.height;
}
if (!isCanvasSupported) {
plotArea.canvas.width = plotArea.width;
plotArea.canvas.height = plotArea.height;
plotArea.canvas.style.left = plotArea.x1 + "px";
plotArea.canvas.style.top = plotArea.y1 + "px";
if (plotArea.x1 > 0 || plotArea.y1 > 0) {
plotArea.ctx.translate(-plotArea.x1, -plotArea.y1);
}
}
plotArea.layoutManager = new LayoutManager(plotArea.x1, plotArea.y1, plotArea.x2, plotArea.y2, 2);
}
Chart.prototype.getPixelCoordinatesOnPlotArea = function (x, y) {
return { x: this.axisX.getPixelCoordinatesOnAxis(x).x, y: this.axisY.getPixelCoordinatesOnAxis(y).y }
//return { x: 5, y: 10 };
}
//#region Render Methods
Chart.prototype.renderIndexLabels = function (targetCtx) {
var ctx = targetCtx || this.plotArea.ctx;
var plotArea = this.plotArea;
var mid = 0;
var yMinLimit = 0;
var yMaxLimit = 0;
var xMinLimit = 0;
var xMaxLimit = 0;
var marginX = 0, marginY = 0; // Margin between label and dataPoint / PlotArea
var offSetX = 0, offSetY = 0; // Distance to offSet textBlock (top) from dataPoint inorder to position it
var visibleWidth = 0;
var visibleHeight = 0;
for (var i = 0; i < this._indexLabels.length; i++) {
var indexLabel = this._indexLabels[i];
var chartTypeLower = indexLabel.chartType.toLowerCase();
var x, y, angle;
var fontColor = getProperty("indexLabelFontColor", indexLabel.dataPoint, indexLabel.dataSeries);
var fontSize = getProperty("indexLabelFontSize", indexLabel.dataPoint, indexLabel.dataSeries);
var fontFamily = getProperty("indexLabelFontFamily", indexLabel.dataPoint, indexLabel.dataSeries);
var fontStyle = getProperty("indexLabelFontStyle", indexLabel.dataPoint, indexLabel.dataSeries);
var fontWeight = getProperty("indexLabelFontWeight", indexLabel.dataPoint, indexLabel.dataSeries);
var backgroundColor = getProperty("indexLabelBackgroundColor", indexLabel.dataPoint, indexLabel.dataSeries);
var maxWidth = getProperty("indexLabelMaxWidth", indexLabel.dataPoint, indexLabel.dataSeries);
var indexLabelWrap = getProperty("indexLabelWrap", indexLabel.dataPoint, indexLabel.dataSeries);
var percentAndTotal = { percent: null, total: null };
var formatterParameter = null;
if (indexLabel.dataSeries.type.indexOf("stacked") >= 0 || indexLabel.dataSeries.type === "pie" || indexLabel.dataSeries.type === "doughnut")
percentAndTotal = this.getPercentAndTotal(indexLabel.dataSeries, indexLabel.dataPoint);
if (indexLabel.dataSeries.indexLabelFormatter || indexLabel.dataPoint.indexLabelFormatter)
formatterParameter = { chart: this._options, dataSeries: indexLabel.dataSeries, dataPoint: indexLabel.dataPoint, index: indexLabel.indexKeyword, total: percentAndTotal.total, percent: percentAndTotal.percent };
var indexLabelText = indexLabel.dataPoint.indexLabelFormatter ? indexLabel.dataPoint.indexLabelFormatter(formatterParameter)
: indexLabel.dataPoint.indexLabel ? this.replaceKeywordsWithValue(indexLabel.dataPoint.indexLabel, indexLabel.dataPoint, indexLabel.dataSeries, null, indexLabel.indexKeyword)
: indexLabel.dataSeries.indexLabelFormatter ? indexLabel.dataSeries.indexLabelFormatter(formatterParameter)
: indexLabel.dataSeries.indexLabel ? this.replaceKeywordsWithValue(indexLabel.dataSeries.indexLabel, indexLabel.dataPoint, indexLabel.dataSeries, null, indexLabel.indexKeyword) : null;
if (indexLabelText === null || indexLabelText === "")
continue;
var placement = getProperty("indexLabelPlacement", indexLabel.dataPoint, indexLabel.dataSeries);
var orientation = getProperty("indexLabelOrientation", indexLabel.dataPoint, indexLabel.dataSeries);
var angle = 0;
var direction = indexLabel.direction; // +1 for above the point and -1 for below the point
var axisX = indexLabel.dataSeries.axisX;
var axisY = indexLabel.dataSeries.axisY;
var textBlock = new TextBlock(ctx, {
x: 0,
y: 0,
maxWidth: maxWidth ? maxWidth : this.width * .5,
maxHeight: indexLabelWrap ? fontSize * 5 : fontSize * 1.5,
angle: orientation === "horizontal" ? 0 : -90,
text: indexLabelText,
padding: 0,
backgroundColor: backgroundColor,
horizontalAlign: "left",//left, center, right
fontSize: fontSize,//in pixels
fontFamily: fontFamily,
fontWeight: fontWeight, //normal, bold, bolder, lighter,
fontColor: fontColor,
fontStyle: fontStyle, // normal, italic, oblique
textBaseline: "top"
});
var textSize = textBlock.measureText();
//if (indexLabel.dataPoint.x < axisX.minimum || indexLabel.dataPoint.x > axisX.maximum || indexLabel.dataPoint.y < axisY.minimum || indexLabel.dataPoint.y > axisY.maximum)
// continue;
if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0
|| chartTypeLower.indexOf("bubble") >= 0 || chartTypeLower.indexOf("scatter") >= 0) {
if (indexLabel.dataPoint.x < axisX.minimum || indexLabel.dataPoint.x > axisX.maximum || indexLabel.dataPoint.y < axisY.minimum || indexLabel.dataPoint.y > axisY.maximum)
continue;
}
else {
if (indexLabel.dataPoint.x < axisX.minimum || indexLabel.dataPoint.x > axisX.maximum)
continue;
}
marginY = 2;
marginX = 2;
if (orientation === "horizontal") {
visibleWidth = textBlock.width;
visibleHeight = textBlock.height;
} else {
visibleHeight = textBlock.width;
visibleWidth = textBlock.height;
}
if (this.plotInfo.axisPlacement === "normal") {
if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0) {
placement = "auto";
marginY = 4;
} else if (chartTypeLower.indexOf("stacked") >= 0) {
if (placement === "auto")
placement = "inside";
} else if (chartTypeLower === "bubble" || chartTypeLower === "scatter") {
placement = "inside";
}
x = indexLabel.point.x - visibleWidth / 2;
if (placement !== "inside") { //outside or auto
yMinLimit = plotArea.y1;
yMaxLimit = plotArea.y2;
if (direction > 0) {
y = indexLabel.point.y - visibleHeight - marginY;
if (y < yMinLimit) {
if (placement === "auto") {
y = Math.max(indexLabel.point.y, yMinLimit) + marginY;
}
else {
y = yMinLimit + marginY;
}
}
}
else {
y = indexLabel.point.y + marginY;
if (y > yMaxLimit - visibleHeight - marginY) {
if (placement === "auto") {
y = Math.min(indexLabel.point.y, yMaxLimit) - visibleHeight - marginY;
}
else {
y = yMaxLimit - visibleHeight - marginY;
}
}
}
} else {
yMinLimit = Math.max(indexLabel.bounds.y1, plotArea.y1);
yMaxLimit = Math.min(indexLabel.bounds.y2, plotArea.y2);
if (chartTypeLower.indexOf("range") >= 0) {
if (direction > 0)
mid = Math.max(indexLabel.bounds.y1, plotArea.y1) + visibleHeight / 2 + marginY;
else
mid = Math.min(indexLabel.bounds.y2, plotArea.y2) - visibleHeight / 2 - marginY;
}
else
mid = (Math.max(indexLabel.bounds.y1, plotArea.y1) + Math.min(indexLabel.bounds.y2, plotArea.y2)) / 2
if (direction > 0) {
y = Math.max(indexLabel.point.y, mid) - visibleHeight / 2;
if (y < yMinLimit && (chartTypeLower === "bubble" || chartTypeLower === "scatter")) {
y = Math.max(indexLabel.point.y - visibleHeight - marginY, plotArea.y1 + marginY);
}
}
else {
y = Math.min(indexLabel.point.y, mid) - visibleHeight / 2;
if (y > yMaxLimit - visibleHeight - marginY && (chartTypeLower === "bubble" || chartTypeLower === "scatter")) {
y = Math.min(indexLabel.point.y + marginY, plotArea.y2 - visibleHeight - marginY);
}
}
}
}
else {
if (chartTypeLower.indexOf("line") >= 0 || chartTypeLower.indexOf("area") >= 0
|| chartTypeLower.indexOf("scatter") >= 0) {
placement = "auto";
marginX = 4;
} else if (chartTypeLower.indexOf("stacked") >= 0) {
if (placement === "auto")
placement = "inside";
} else if (chartTypeLower === "bubble") {
placement = "inside";
}
y = indexLabel.point.y - visibleHeight / 2;
if (placement !== "inside") { //outside or auto
xMinLimit = plotArea.x1;
xMaxLimit = plotArea.x2;
if (direction < 0) {
x = indexLabel.point.x - visibleWidth - marginX;
if (x < xMinLimit) {
if (placement === "auto") {
x = Math.max(indexLabel.point.x, xMinLimit) + marginX;
}
else {
x = xMinLimit + marginX;
}
}
}
else {
x = indexLabel.point.x + marginX;
if (x > xMaxLimit - visibleWidth - marginX) {
if (placement === "auto") {
x = Math.min(indexLabel.point.x, xMaxLimit) - visibleWidth - marginX;
}
else {
x = xMaxLimit - visibleWidth - marginX;
}
}
}
} else {
xMinLimit = Math.max(indexLabel.bounds.x1, plotArea.x1);
xMaxLimit = Math.min(indexLabel.bounds.x2, plotArea.x2);
if (chartTypeLower.indexOf("range") >= 0) {
if (direction < 0)
mid = Math.max(indexLabel.bounds.x1, plotArea.x1) + visibleWidth / 2 + marginX;
else
mid = Math.min(indexLabel.bounds.x2, plotArea.x2) - visibleWidth / 2 - marginX;
}
else
var mid = (Math.max(indexLabel.bounds.x1, plotArea.x1) + Math.min(indexLabel.bounds.x2, plotArea.x2)) / 2;
if (direction < 0) {
x = Math.max(indexLabel.point.x, mid) - visibleWidth / 2;
//if (y < xMinLimit) {
// y = Math.max(indexLabel.point.y - visibleHeight - marginY, plotArea.y1 + marginY);
//}
}
else {
x = Math.min(indexLabel.point.x, mid) - visibleWidth / 2;
//if (y > xMaxLimit - visibleHeight - marginY) {
// y = Math.min(indexLabel.point.y + marginY, plotArea.y2 - visibleHeight - marginY);
//}
}
}
}
if (orientation === "vertical") {
y += visibleHeight;
}
textBlock.x = x;
textBlock.y = y;
//console.log(textBlock.text + ": " + textBlock.x + "; " + textBlock.y);
textBlock.render(true);
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0, startTimePercent: .7 };
return animationInfo;
}
Chart.prototype.renderLine = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
//var ghostCtx = this.overlaidCanvasCtx;
ctx.save();
var plotArea = this.plotArea;
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
var markers = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
ctx.lineWidth = dataSeries.lineThickness;
var dataPoints = dataSeries.dataPoints;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.strokeStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
var colorSet = dataSeries._colorSet;
var color = colorSet[0];
ctx.strokeStyle = color;
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
// dataSeries.markerSize = 8;
ctx.beginPath();
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
//dataSeries.noDataPointsInPlotArea = 0
var prevDataNull = false;
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
continue;
//if (!isFinite(dataPoints[i].y))
// continue;
if (typeof (dataPoints[i].y) !== "number") {
if (i > 0) {// if first dataPoint is null then no need to call stroke method
ctx.stroke();
if (isCanvasSupported) {
ghostCtx.stroke();
}
}
prevDataNull = true;
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
//dataSeries.noDataPointsInPlotArea++;
if (isFirstDataPointInPlotArea || prevDataNull) {
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
prevDataNull = false;
} else {
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 500 == 0) {
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.stroke();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
}
}
//Render Marker
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "line",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
ctx.stroke();
if (isCanvasSupported)
ghostCtx.stroke();
}
}
RenderHelper.drawMarkers(markers);
ctx.restore();
ctx.beginPath();
if (isCanvasSupported)
ghostCtx.beginPath();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderStepLine = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
//var ghostCtx = this.overlaidCanvasCtx;
ctx.save();
var plotArea = this.plotArea;
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
var markers = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
ctx.lineWidth = dataSeries.lineThickness;
var dataPoints = dataSeries.dataPoints;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.strokeStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
var colorSet = dataSeries._colorSet;
var color = colorSet[0];
ctx.strokeStyle = color;
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
// dataSeries.markerSize = 8;
ctx.beginPath();
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
//dataSeries.noDataPointsInPlotArea = 0
var prevDataNull = false;
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
continue;
//if (!isFinite(dataPoints[i].y))
// continue;
if (typeof (dataPoints[i].y) !== "number") {
if (i > 0) {// if first dataPoint is null then no need to call stroke method
ctx.stroke();
if (isCanvasSupported) {
ghostCtx.stroke();
}
}
prevDataNull = true;
continue;
}
var prevY = y;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
//dataSeries.noDataPointsInPlotArea++;
if (isFirstDataPointInPlotArea || prevDataNull) {
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
prevDataNull = false;
} else {
ctx.lineTo(x, prevY);
if (isCanvasSupported)
ghostCtx.lineTo(x, prevY);
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 500 == 0) {
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.stroke();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
}
}
//Render Marker
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stepLine",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
ctx.stroke();
if (isCanvasSupported)
ghostCtx.stroke();
}
}
RenderHelper.drawMarkers(markers);
ctx.restore();
ctx.beginPath();
if (isCanvasSupported)
ghostCtx.beginPath();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
function getBezierPoints(points, tension) {
var bezierPoints = [];
for (var i = 0; i < points.length; i++) {
if (i == 0) {
bezierPoints.push(points[0]);
continue;
}
var i1, i2, pointIndex;
pointIndex = i - 1;
i1 = pointIndex === 0 ? 0 : pointIndex - 1;
i2 = pointIndex === points.length - 1 ? pointIndex : pointIndex + 1;
var drv1 = { x: (points[i2].x - points[i1].x) / tension, y: (points[i2].y - points[i1].y) / tension }
var cp1 = { x: points[pointIndex].x + drv1.x / 3, y: points[pointIndex].y + drv1.y / 3 }
bezierPoints[bezierPoints.length] = cp1;
pointIndex = i;
i1 = pointIndex === 0 ? 0 : pointIndex - 1;
i2 = pointIndex === points.length - 1 ? pointIndex : pointIndex + 1;
var drv2 = { x: (points[i2].x - points[i1].x) / tension, y: (points[i2].y - points[i1].y) / tension }
var cp2 = { x: points[pointIndex].x - drv2.x / 3, y: points[pointIndex].y - drv2.y / 3 }
bezierPoints[bezierPoints.length] = cp2;
bezierPoints[bezierPoints.length] = points[i];
}
return bezierPoints;
}
Chart.prototype.renderSpline = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
ctx.save();
var plotArea = this.plotArea;
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
var markers = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
ctx.lineWidth = dataSeries.lineThickness;
var dataPoints = dataSeries.dataPoints;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.strokeStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
ghostCtx.lineWidth = dataSeries.lineThickness > 0 ? Math.max(dataSeries.lineThickness, 4) : 0;
var colorSet = dataSeries._colorSet;
var color = colorSet[0];
ctx.strokeStyle = color;
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
//if (!dataSeries._options.markerSize && dataSeries.dataPoints.length < 1000)
// dataSeries.markerSize = 8;
var pixels = [];
ctx.beginPath();
if (dataPoints.length > 0) {
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax)
continue;
//if (!isFinite(dataPoints[i].y))
// continue;
if (typeof (dataPoints[i].y) !== "number") {
if (i > 0) {// if first dataPoint is null then no need to call stroke method
renderBezier(pixels);
pixels = [];
}
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
pixels[pixels.length] = { x: x, y: y };
//Add Markers
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
//window.console.log("index: " + i + "; id: " + id + "; hex: " + markerColor);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
//Add Labels
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "spline",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
}
renderBezier(pixels);
}
RenderHelper.drawMarkers(markers);
ctx.restore();
ctx.beginPath();
if (isCanvasSupported)
ghostCtx.beginPath();
function renderBezier(pixels) {
var bp = getBezierPoints(pixels, 2);
if (bp.length > 0) {
ctx.beginPath();
if (isCanvasSupported)
ghostCtx.beginPath();
ctx.moveTo(bp[0].x, bp[0].y);
if (isCanvasSupported)
ghostCtx.moveTo(bp[0].x, bp[0].y);
for (var i = 0; i < bp.length - 3; i += 3) {
ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
if (isCanvasSupported)
ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
if (i > 0 && i % 3000 === 0) {
ctx.stroke();
ctx.beginPath();
ctx.moveTo(bp[i + 3].x, bp[i + 3].y);
if (isCanvasSupported) {
ghostCtx.stroke();
ghostCtx.beginPath();
ghostCtx.moveTo(bp[i + 3].x, bp[i + 3].y);
}
}
}
ctx.stroke();
if (isCanvasSupported)
ghostCtx.stroke();
}
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
var drawRect = function (ctx, x1, y1, x2, y2, color, borderThickness, borderColor, top, bottom, left, right, fillOpacity) {
if (typeof (fillOpacity) === "undefined")
fillOpacity = 1;
borderThickness = borderThickness || 0;
borderColor = borderColor || "black";
//alert("top"+ top + "bottom" + bottom + " lt" + left+ "rt" + right )
var a1 = x1, a2 = x2, b1 = y1, b2 = y2, edgeY, edgeX;
if (x2 - x1 > 15 && y2 - y1 > 15)
var bevelDepth = 8;
else
var bevelDepth = 0.35 * Math.min((x2 - x1), (y2 - y1));
//alert(a1 + "" + a2);
var color2 = "rgba(255, 255, 255, .4)";
var color3 = "rgba(255, 255, 255, 0.1)";
//color1 = "rgba(" + r + "," + g + ", " + b + ",1)";
var color1 = color;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.save();
ctx.fillStyle = color1;
ctx.globalAlpha = fillOpacity;
ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
ctx.globalAlpha = 1;
if (borderThickness > 0) {
var offset = borderThickness % 2 === 0 ? 0 : .5;
ctx.beginPath();
ctx.lineWidth = borderThickness;
ctx.strokeStyle = borderColor;
ctx.moveTo(x1, y1);
ctx.rect(x1 - offset, y1 - offset, x2 - x1 + 2 * offset, y2 - y1 + 2 * offset);
ctx.stroke();
}
ctx.restore();
// ctx.beginPath();
if (top === true) {
// alert(x1 + "" + x2 + " " + bevelDepth);
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + bevelDepth, y1 + bevelDepth);
ctx.lineTo(x2 - bevelDepth, y1 + bevelDepth);
ctx.lineTo(x2, y1);
ctx.closePath();
var grd = ctx.createLinearGradient((x2 + x1) / 2, b1 + bevelDepth, (x2 + x1) / 2, b1);
grd.addColorStop(0, color1);
grd.addColorStop(1, color2);
ctx.fillStyle = grd;
ctx.fill();
// ctx.stroke();
ctx.restore();
}
if (bottom === true) {
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y2);
ctx.lineTo(x1 + bevelDepth, y2 - bevelDepth);
ctx.lineTo(x2 - bevelDepth, y2 - bevelDepth);
ctx.lineTo(x2, y2);
ctx.closePath();
var grd = ctx.createLinearGradient((x2 + x1) / 2, b2 - bevelDepth, (x2 + x1) / 2, b2);
grd.addColorStop(0, color1);
grd.addColorStop(1, color2);
ctx.fillStyle = grd;
// ctx.stroke();
ctx.fill();
ctx.restore();
}
if (left === true) {
// alert(x1)
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1)
ctx.lineTo(x1 + bevelDepth, y1 + bevelDepth);
ctx.lineTo(x1 + bevelDepth, y2 - bevelDepth);
ctx.lineTo(x1, y2);
ctx.closePath();
var grd = ctx.createLinearGradient(a1 + bevelDepth, (y2 + y1) / 2, a1, (y2 + y1) / 2);
grd.addColorStop(0, color1);
grd.addColorStop(1, color3);
ctx.fillStyle = grd;
ctx.fill();
// ctx.stroke();
ctx.restore();
}
if (right === true) {
ctx.save();
ctx.beginPath();
ctx.moveTo(x2, y1)
ctx.lineTo(x2 - bevelDepth, y1 + bevelDepth);
ctx.lineTo(x2 - bevelDepth, y2 - bevelDepth);
ctx.lineTo(x2, y2);
var grd = ctx.createLinearGradient(a2 - bevelDepth, (y2 + y1) / 2, a2, (y2 + y1) / 2);
grd.addColorStop(0, color1);
grd.addColorStop(1, color3);
ctx.fillStyle = grd;
grd.addColorStop(0, color1);
grd.addColorStop(1, color3);
ctx.fillStyle = grd;
ctx.fill();
ctx.closePath();
// ctx.stroke();
ctx.restore();
}
//
}
Chart.prototype.renderColumn = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.width * .15), this.plotArea.width / plotUnit.plotType.totalDataSeries * .9) << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
//ctx.beginPath();
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//var offsetX = barWidth * plotUnit.index << 0;
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
for (i = 0; i < dataPoints.length; i++) {
dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var x1 = x - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth) << 0;
var x2 = x1 + barWidth << 0;
var y1;
var y2;
if (dataPoints[i].y >= 0) {
y1 = y;
y2 = yZeroToPixel;
if (y1 > y2) {
var temp = y1;
y1 = y2;
y2 = y1;
}
} else {
y2 = y;
y1 = yZeroToPixel;
if (y1 > y2) {
var temp = y1;
y1 = y2;
y2 = y1;
}
}
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
//if (dataSeries.markerType && dataSeries.markerSize > 0) {
// RenderHelper.drawMarker(x1 + (x2 - x1) / 2, y, ctx, dataSeries.markerType, dataSeries.markerSize, color, dataSeries.markerBorderColor, dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : 1);
//}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "column",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x1 + (x2 - x1) / 2, y: dataPoints[i].y >= 0 ? y1 : y2 },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2) },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderStackedColumn = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var offsetPositiveY = [];
var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum));
var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
var x2 = x1 + barWidth << 0;
var y1;
var y2;
if (dataPoints[i].y >= 0) {
var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
y1 = y - offset;
y2 = yZeroToPixel - offset;
offsetPositiveY[dataPointX] = offset + (y2 - y1);
} else {
var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
y2 = y + offset;
y1 = yZeroToPixel + offset;
offsetNegativeY[dataPointX] = offset + (y2 - y1);
}
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
//if (dataSeries.markerType && dataSeries.markerSize > 0) {
// RenderHelper.drawMarker(x1 + (x2 - x1)/2, y1, ctx, dataSeries.markerType, dataSeries.markerSize, color, dataSeries.markerBorderColor, dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : 1);
//}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stackedColumn",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: dataPoints[i].y >= 0 ? y1 : y2 },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2) },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderStackedColumn100 = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var offsetPositiveY = [];
var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
//ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
var yPercent;
if (plotUnit.dataPointYSums[dataPointX] !== 0)
yPercent = dataPoints[i].y / plotUnit.dataPointYSums[dataPointX] * 100;
else
yPercent = 0;
//y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
var x2 = x1 + barWidth << 0;
var y1;
var y2;
if (dataPoints[i].y >= 0) {
var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
y1 = y - offset;
y2 = yZeroToPixel - offset;
offsetPositiveY[dataPointX] = offset + (y2 - y1);
} else {
var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
y2 = y + offset;
y1 = yZeroToPixel + offset;
offsetNegativeY[dataPointX] = offset + (y2 - y1);
}
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled && (dataPoints[i].y >= 0), (dataPoints[i].y < 0) && bevelEnabled, false, false, dataSeries.fillOpacity);
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stackedColumn100",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: dataPoints[i].y >= 0 ? y1 : y2 },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2) },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.min(yZeroToPixel, plotUnit.axisY.boundingRect.y2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.yScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderBar = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
//In case of Bar Chart, yZeroToPixel is x co-ordinate!
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.height * .15), this.plotArea.height / plotUnit.plotType.totalDataSeries * .9) << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
//var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
//x and y are pixel co-ordinates of point and should not be confused with X and Y values
y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var y1 = (y - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth)) << 0;
var y2 = y1 + barWidth << 0;
var x1;
var x2;
if (dataPoints[i].y >= 0) {
x1 = yZeroToPixel;
x2 = x;
} else {
x1 = x;
x2 = yZeroToPixel;
}
//drawRect(ctx, x1, y1, plotArea.x2, y2, "#EEEEEE", 0, null, false, false, false, false);
//drawRect(ctx, x1, y1, plotArea.x2, y2, "#BDCED3", 0, null, false, false, false, false);
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
//color = "#1B4962";
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
this._indexLabels.push({
chartType: "bar",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: dataPoints[i].y >= 0 ? x2 : x1, y: y1 + (y2 - y1) / 2 },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2 },
color: color
});
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderStackedBar = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var offsetPositiveY = [];
var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.height * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
//x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum));
//var x1 = x - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
var y1 = y - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
var y2 = y1 + barWidth << 0;
var x1;
var x2;
if (dataPoints[i].y >= 0) {
var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
x1 = yZeroToPixel + offset;
x2 = x + offset;
offsetPositiveY[dataPointX] = offset + (x2 - x1);
} else {
var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
x1 = x - offset;
x2 = yZeroToPixel - offset;
offsetNegativeY[dataPointX] = offset + (x2 - x1);
}
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
this._indexLabels.push({
chartType: "stackedBar",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: dataPoints[i].y >= 0 ? x2 : x1, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2 },
color: color
});
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderStackedBar100 = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var offsetPositiveY = [];
var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.height * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.plotUnits.length * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
var yPercent;
if (plotUnit.dataPointYSums[dataPointX] !== 0)
yPercent = dataPoints[i].y / plotUnit.dataPointYSums[dataPointX] * 100;
else
yPercent = 0;
//x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
x = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
var y1 = y - (plotUnit.plotType.plotUnits.length * barWidth / 2) + (plotUnit.index * barWidth) << 0;
var y2 = y1 + barWidth << 0;
var x1;
var x2;
if (dataPoints[i].y >= 0) {
var offset = offsetPositiveY[dataPointX] ? offsetPositiveY[dataPointX] : 0;
x1 = yZeroToPixel + offset;
x2 = x + offset;
offsetPositiveY[dataPointX] = offset + (x2 - x1);
} else {
var offset = offsetNegativeY[dataPointX] ? offsetNegativeY[dataPointX] : 0;
x1 = x - offset;
x2 = yZeroToPixel - offset;
offsetNegativeY[dataPointX] = offset + (x2 - x1);
}
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter)
this._indexLabels.push({
chartType: "stackedBar100",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: dataPoints[i].y >= 0 ? x2 : x1, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
bounds: { x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2 },
color: color
});
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationBase = Math.max(yZeroToPixel, plotUnit.axisX.boundingRect.x2);
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xScaleAnimation, easingFunction: AnimationHelper.easing.easeOutQuart, animationBase: animationBase };
return animationInfo;
}
Chart.prototype.renderArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
var axisXProps = plotUnit.axisX.lineCoordinates;
var axisYProps = plotUnit.axisY.lineCoordinates;
var markers = [];
var plotArea = this.plotArea;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
//ghostCtx.lineWidth = 20;
markers = [];
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var baseY;
var startPoint = null;
if (dataPoints.length > 0) {
//ctx.strokeStyle = "#4572A7 ";
var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var prevDataNull = true;
for (; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number") {
closeArea();
prevDataNull = true;
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
if (isFirstDataPointInPlotArea || prevDataNull) {
ctx.beginPath();
ctx.moveTo(x, y);
startPoint = { x: x, y: y };
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
prevDataNull = false;
}
else {
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 250 == 0) {
closeArea();
}
}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
//Render Marker
if (dataPoints[i].markerSize !== 0) {
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "area",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
closeArea();
//startPoint = { x: x, y: y };
RenderHelper.drawMarkers(markers);
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
function closeArea() {
if (!startPoint)
return;
if (dataSeries.lineThickness > 0)
ctx.stroke();
if (plotUnit.axisY.minimum <= 0 && plotUnit.axisY.maximum >= 0) {
baseY = yZeroToPixel;
}
else if (plotUnit.axisY.maximum < 0)
baseY = axisYProps.y1;
else if (plotUnit.axisY.minimum > 0)
baseY = axisXProps.y2;
ctx.lineTo(x, baseY);
ctx.lineTo(startPoint.x, baseY);
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
if (isCanvasSupported) {
ghostCtx.lineTo(x, baseY);
ghostCtx.lineTo(startPoint.x, baseY);
ghostCtx.closePath();
ghostCtx.fill();
}
ctx.beginPath();
ctx.moveTo(x, y);
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
startPoint = { x: x, y: y };
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderSplineArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
var axisXProps = plotUnit.axisX.lineCoordinates;
var axisYProps = plotUnit.axisY.lineCoordinates;
var markers = [];
var plotArea = this.plotArea;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
//ghostCtx.lineWidth = 20;
markers = [];
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var baseY;
var startPoint = null;
var pixels = [];
if (dataPoints.length > 0) {
//ctx.strokeStyle = "#4572A7 ";
color = dataSeries._colorSet[i % dataSeries._colorSet.length];
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
for (; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number") {
if (i > 0) {
renderBezierArea();
pixels = [];
}
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
pixels[pixels.length] = { x: x, y: y };
//Render Marker
if (dataPoints[i].markerSize !== 0) {
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
//Render Index Labels
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "splineArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
renderBezierArea();
RenderHelper.drawMarkers(markers);
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
function renderBezierArea() {
var bp = getBezierPoints(pixels, 2);
if (bp.length > 0) {
ctx.beginPath();
ctx.moveTo(bp[0].x, bp[0].y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(bp[0].x, bp[0].y);
}
for (var i = 0; i < bp.length - 3; i += 3) {
ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
if (isCanvasSupported)
ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
}
if (dataSeries.lineThickness > 0)
ctx.stroke();
if (plotUnit.axisY.minimum <= 0 && plotUnit.axisY.maximum >= 0) {
baseY = yZeroToPixel;
}
else if (plotUnit.axisY.maximum < 0)
baseY = axisYProps.y1;
else if (plotUnit.axisY.minimum > 0)
baseY = axisXProps.y2;
startPoint = { x: bp[0].x, y: bp[0].y };
ctx.lineTo(bp[bp.length - 1].x, baseY);
ctx.lineTo(startPoint.x, baseY);
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
if (isCanvasSupported) {
ghostCtx.lineTo(bp[bp.length - 1].x, baseY);
ghostCtx.lineTo(startPoint.x, baseY);
ghostCtx.closePath();
ghostCtx.fill();
}
}
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderStepArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
var axisXProps = plotUnit.axisX.lineCoordinates;
var axisYProps = plotUnit.axisY.lineCoordinates;
var markers = [];
var plotArea = this.plotArea;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
//ghostCtx.lineWidth = 20;
markers = [];
var isFirstDataPointInPlotArea = true;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var baseY;
var startPoint = null;
var prevDataNull = false;
if (dataPoints.length > 0) {
//ctx.strokeStyle = "#4572A7 ";
var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
for (; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
var prevY = y;
if (typeof (dataPoints[i].y) !== "number") {
closeArea();
prevDataNull = true;
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
if (isFirstDataPointInPlotArea || prevDataNull) {
ctx.beginPath();
ctx.moveTo(x, y);
startPoint = { x: x, y: y };
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
prevDataNull = false;
}
else {
ctx.lineTo(x, prevY);
if (isCanvasSupported)
ghostCtx.lineTo(x, prevY);
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 250 == 0) {
closeArea();
}
}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
//Render Marker
if (dataPoints[i].markerSize !== 0) {
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stepArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
closeArea();
RenderHelper.drawMarkers(markers);
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
function closeArea() {
if (!startPoint)
return;
if (dataSeries.lineThickness > 0)
ctx.stroke();
if (plotUnit.axisY.minimum <= 0 && plotUnit.axisY.maximum >= 0) {
baseY = yZeroToPixel;
}
else if (plotUnit.axisY.maximum < 0)
baseY = axisYProps.y1;
else if (plotUnit.axisY.minimum > 0)
baseY = axisXProps.y2;
ctx.lineTo(x, baseY);
ctx.lineTo(startPoint.x, baseY);
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
if (isCanvasSupported) {
ghostCtx.lineTo(x, baseY);
ghostCtx.lineTo(startPoint.x, baseY);
ghostCtx.closePath();
ghostCtx.fill();
}
ctx.beginPath();
ctx.moveTo(x, y);
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
startPoint = { x: x, y: y };
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderStackedArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var markers = [];
var plotArea = this.plotArea;
var offsetY = [];
var allXValues = [];
//var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var ghostCtx = this._eventManager.ghostCtx;
if (isCanvasSupported)
ghostCtx.beginPath();
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
xValuePresent = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var xValue;
dataSeries.dataPointIndexes = [];
for (i = 0; i < dataPoints.length; i++) {
xValue = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
dataSeries.dataPointIndexes[xValue] = i;
if (!xValuePresent[xValue]) {
allXValues.push(xValue);
xValuePresent[xValue] = true;
}
}
allXValues.sort(compareNumbers);
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
var currentBaseValues = [];
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
if (allXValues.length > 0) {
color = dataSeries._colorSet[0];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
for (i = 0; i < allXValues.length; i++) {
dataPointX = allXValues[i];
var dataPoint = null;
if (dataSeries.dataPointIndexes[dataPointX] >= 0)
dataPoint = dataPoints[dataSeries.dataPointIndexes[dataPointX]];
else
dataPoint = { x: dataPointX, y: 0 };
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoint.y) !== "number")
continue;
var x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
//var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoint.y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoint.y - plotUnit.axisY.conversionParameters.minimum));
var offset = offsetY[dataPointX] ? offsetY[dataPointX] : 0;
y = y - offset;
currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
offsetY[dataPointX] = yZeroToPixel - y;
if (isFirstDataPointInPlotArea) {
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
}
else {
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 250 == 0) {
if (dataSeries.lineThickness > 0)
ctx.stroke();
while (currentBaseValues.length > 0) {
var point = currentBaseValues.pop();
ctx.lineTo(point.x, point.y);
if (isCanvasSupported)
ghostCtx.lineTo(point.x, point.y);
}
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.closePath();
ghostCtx.fill();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
}
}
if (dataSeries.dataPointIndexes[dataPointX] >= 0) {
var id = dataSeries.dataPointIds[dataSeries.dataPointIndexes[dataPointX]];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: dataSeries.dataPointIndexes[dataPointX], x1: x, y1: y };
}
//Render Marker
if (dataSeries.dataPointIndexes[dataPointX] >= 0 && dataPoint.markerSize !== 0) {
if (dataPoint.markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
if (dataPoint.indexLabel || dataSeries.indexLabel || dataPoint.indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stackedArea",
dataPoint: dataPoint,
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
if (dataSeries.lineThickness > 0)
ctx.stroke();
while (currentBaseValues.length > 0) {
var point = currentBaseValues.pop();
ctx.lineTo(point.x, point.y);
if (isCanvasSupported)
ghostCtx.lineTo(point.x, point.y);
}
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.closePath();
ghostCtx.fill();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
}
delete (dataSeries.dataPointIndexes);
}
RenderHelper.drawMarkers(markers);
ctx.restore();
if (isCanvasSupported)
ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderStackedArea100 = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var markers = [];
var offsetY = [];
var allXValues = [];
//var offsetNegativeY = [];
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number everytime it is accessed.
//var yZeroToPixel = (axisYProps.y2 - axisYProps.height / rangeY * Math.abs(0 - plotUnit.axisY.minimum) + .5) << 0;
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) * .9) << 0;
var ghostCtx = this._eventManager.ghostCtx;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
xValuePresent = [];
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var xValue;
dataSeries.dataPointIndexes = [];
for (i = 0; i < dataPoints.length; i++) {
xValue = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
dataSeries.dataPointIndexes[xValue] = i;
if (!xValuePresent[xValue]) {
allXValues.push(xValue);
xValuePresent[xValue] = true;
}
}
allXValues.sort(compareNumbers);
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
if (dataPoints.length == 1)
barWidth = maxBarWidth;
if (barWidth < 1)
barWidth = 1;
else if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
var currentBaseValues = [];
if (allXValues.length > 0) {
color = dataSeries._colorSet[i % dataSeries._colorSet.length];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var bevelEnabled = (barWidth > 5) ? false : false;
//ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < allXValues.length; i++) {
dataPointX = allXValues[i];
var dataPoint = null;
if (dataSeries.dataPointIndexes[dataPointX] >= 0)
dataPoint = dataPoints[dataSeries.dataPointIndexes[dataPointX]];
else
dataPoint = { x: dataPointX, y: 0 };
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoint.y) !== "number")
continue;
var yPercent;
if (plotUnit.dataPointYSums[dataPointX] !== 0)
yPercent = dataPoint.y / plotUnit.dataPointYSums[dataPointX] * 100;
else
yPercent = 0;
var x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
var y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (yPercent - plotUnit.axisY.conversionParameters.minimum));
var offset = offsetY[dataPointX] ? offsetY[dataPointX] : 0;
y = y - offset;
currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
offsetY[dataPointX] = yZeroToPixel - y;
if (isFirstDataPointInPlotArea) {
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
isFirstDataPointInPlotArea = false;
}
else {
ctx.lineTo(x, y);
if (isCanvasSupported)
ghostCtx.lineTo(x, y);
if (i % 250 == 0) {
if (dataSeries.lineThickness > 0)
ctx.stroke();
while (currentBaseValues.length > 0) {
var point = currentBaseValues.pop();
ctx.lineTo(point.x, point.y);
if (isCanvasSupported)
ghostCtx.lineTo(point.x, point.y);
}
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.closePath();
ghostCtx.fill();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
currentBaseValues.push({ x: x, y: yZeroToPixel - offset });
}
}
if (dataSeries.dataPointIndexes[dataPointX] >= 0) {
var id = dataSeries.dataPointIds[dataSeries.dataPointIndexes[dataPointX]];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: dataSeries.dataPointIndexes[dataPointX], x1: x, y1: y };
}
//Render Marker
if (dataSeries.dataPointIndexes[dataPointX] >= 0 && dataPoint.markerSize !== 0) {
if (dataPoint.markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
if (dataPoint.indexLabel || dataSeries.indexLabel || dataPoint.indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "stackedArea100",
dataPoint: dataPoint,
dataSeries: dataSeries,
point: { x: x, y: y },
direction: dataPoints[i].y >= 0 ? 1 : -1,
color: color
});
}
}
if (dataSeries.lineThickness > 0)
ctx.stroke();
while (currentBaseValues.length > 0) {
var point = currentBaseValues.pop();
ctx.lineTo(point.x, point.y);
if (isCanvasSupported)
ghostCtx.lineTo(point.x, point.y);
}
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(x, y);
if (isCanvasSupported) {
ghostCtx.closePath();
ghostCtx.fill();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y);
}
}
delete (dataSeries.dataPointIndexes);
}
RenderHelper.drawMarkers(markers);
ctx.restore();
if (isCanvasSupported)
ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderBubble = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
var maxZ = -Infinity;
var minZ = Infinity;
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var z = 0;
for (var i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].z) !== "undefined") {
z = dataPoints[i].z;
if (z > maxZ)
maxZ = z;
if (z < minZ)
minZ = z;
}
}
}
var minArea = Math.PI * 5 * 5;
var maxArea = Math.max(Math.pow(Math.min(plotArea.height, plotArea.width) * .25 / 2, 2) * Math.PI, minArea);
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
if (dataPoints.length == 1)
barWidth = maxBarWidth;
if (barWidth < 1)
barWidth = 1;
else if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
//var bevelEnabled = (barWidth > 5) ? false : false;
ctx.strokeStyle = "#4572A7 ";
for (var i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var z = dataPoints[i].z;
var area = (maxZ === minZ) ? maxArea / 2 : minArea + (maxArea - minArea) / (maxZ - minZ) * (z - minZ);
var radius = Math.max(Math.sqrt(area / Math.PI) << 0, 1);
var markerSize = radius * 2;
var markerProps = dataSeries.getMarkerProperties(i, ctx);
markerProps.size = markerSize;
ctx.globalAlpha = dataSeries.fillOpacity;
RenderHelper.drawMarker(x, y, ctx, markerProps.type, markerProps.size, markerProps.color, markerProps.borderColor, markerProps.borderThickness);
ctx.globalAlpha = 1;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y, size: markerSize };
var markerColor = intToHexColorString(id);
//RenderHelper.drawMarker(x, y, this._eventManager.ghostCtx, markerType, markerSize, markerColor, markerColor, dataSeries.markerBorderThickness);
if (isCanvasSupported)
RenderHelper.drawMarker(x, y, this._eventManager.ghostCtx, markerProps.type, markerProps.size, markerColor, markerColor, markerProps.borderThickness);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "bubble",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: 1,
bounds: { x1: x - markerProps.size / 2, y1: y - markerProps.size / 2, x2: x + markerProps.size / 2, y2: y + markerProps.size / 2 },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderScatter = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : this.width * .15 << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
if (dataPoints.length == 1)
barWidth = maxBarWidth;
if (barWidth < 1)
barWidth = 1;
else if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
if (dataPoints.length > 0) {
//var bevelEnabled = (barWidth > 5) ? false : false;
ctx.strokeStyle = "#4572A7 ";
var maxArea = Math.pow(Math.min(plotArea.height, plotArea.width) * .3 / 2, 2) * Math.PI;
var prevDataPointX = 0;
var prevDataPointY = 0;
for (var i = 0; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (typeof (dataPoints[i].y) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var markerProps = dataSeries.getMarkerProperties(i, x, y, ctx);
ctx.globalAlpha = dataSeries.fillOpacity;
RenderHelper.drawMarker(markerProps.x, markerProps.y, markerProps.ctx, markerProps.type, markerProps.size, markerProps.color, markerProps.borderColor, markerProps.borderThickness);
ctx.globalAlpha = 1;
//if (Math.abs(prevDataPointX - x) < markerProps.size / 2 && Math.abs(prevDataPointY - y) < markerProps.size / 2) {
// continue;
//}
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
if ((Math.sqrt((prevDataPointX - x) * (prevDataPointX - x) + (prevDataPointY - y) * (prevDataPointY - y)) < Math.min(markerProps.size, 5))
&& dataPoints.length > (Math.min(this.plotArea.width, this.plotArea.height))) {
continue;
}
//Render ID on Ghost Canvas - for event handling
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y };
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
RenderHelper.drawMarker(
markerProps.x, markerProps.y, this._eventManager.ghostCtx,
markerProps.type,
markerProps.size,
markerColor,
markerColor,
markerProps.borderThickness
);
}
//markers.push();
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "scatter",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x, y: y },
direction: 1,
bounds: { x1: x - markerProps.size / 2, y1: y - markerProps.size / 2, x2: x + markerProps.size / 2, y2: y + markerProps.size / 2 },
color: color
});
}
prevDataPointX = x;
prevDataPointY = y;
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderCandlestick = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var ghostCtx = this._eventManager.ghostCtx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y1, y2, y3, y4;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : (this.width * .015);
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) * .7) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
//ctx.beginPath();
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//var offsetX = barWidth * plotUnit.index << 0;
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
for (i = 0; i < dataPoints.length; i++) {
dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (dataPoints[i].y === null || !dataPoints[i].y.length
|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number"
|| typeof (dataPoints[i].y[2]) !== "number" || typeof (dataPoints[i].y[3]) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y3 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[2] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y4 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[3] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var x1 = (x - barWidth / 2) << 0;
var x2 = (x1 + barWidth) << 0;
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[0];
//var borderThickness = Math.max(2, ((barWidth * .1) / 2 << 0) * 2); // Set only even numbers for border
var borderThickness = Math.round(Math.max(1, (barWidth * .15)));
//borderThickness = (borderThickness / 2 << 0) * 2;
//borderThickness = 2;
var offset = borderThickness % 2 === 0 ? 0 : .5;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = {
id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2,
x3: x, y3: y3, x4: x, y4: y4, borderThickness: borderThickness, color: color
};
ctx.strokeStyle = color;
ctx.beginPath();
ctx.lineWidth = borderThickness;
ghostCtx.lineWidth = Math.max(borderThickness, 4);
if (dataSeries.type === "candlestick") {
ctx.moveTo(x - offset, y2);
ctx.lineTo(x - offset, Math.min(y1, y4));
ctx.stroke();
ctx.moveTo(x - offset, Math.max(y1, y4));
ctx.lineTo(x - offset, y3);
ctx.stroke();
drawRect(ctx, x1, Math.min(y1, y4), x2, Math.max(y1, y4), dataPoints[i].y[0] <= dataPoints[i].y[3] ? dataSeries.risingColor : color, borderThickness, color, bevelEnabled, bevelEnabled, false, false, dataSeries.fillOpacity);
if (isCanvasSupported) {
color = intToHexColorString(id);
ghostCtx.strokeStyle = color;
ghostCtx.moveTo(x - offset, y2);
ghostCtx.lineTo(x - offset, Math.min(y1, y4));
ghostCtx.stroke();
ghostCtx.moveTo(x - offset, Math.max(y1, y4));
ghostCtx.lineTo(x - offset, y3);
ghostCtx.stroke();
drawRect(ghostCtx, x1, Math.min(y1, y4), x2, Math.max(y1, y4), color, 0, null, false, false, false, false);
}
}
else if (dataSeries.type === "ohlc") {
ctx.moveTo(x - offset, y2);
ctx.lineTo(x - offset, y3);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y1);
ctx.lineTo(x1, y1);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y4);
ctx.lineTo(x2, y4);
ctx.stroke();
if (isCanvasSupported) {
color = intToHexColorString(id);
ghostCtx.strokeStyle = color;
ghostCtx.moveTo(x - offset, y2);
ghostCtx.lineTo(x - offset, y3);
ghostCtx.stroke();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y1);
ghostCtx.lineTo(x1, y1);
ghostCtx.stroke();
ghostCtx.beginPath();
ghostCtx.moveTo(x, y4);
ghostCtx.lineTo(x2, y4);
ghostCtx.stroke();
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: dataSeries.type,
dataPoint: dataPoints[i],
dataSeries: dataSeries,
point: { x: x1 + (x2 - x1) / 2, y: y2 },
direction: 1,
bounds: { x1: x1, y1: Math.min(y2, y3), x2: x2, y2: Math.max(y2, y3) },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderRangeColumn = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x, y1, y2;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : (this.width * .03);
//var maxBarWidth = (this.width * .015);
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
//var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) * .9) << 0;
var barWidth = (((plotArea.width / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
//ctx.beginPath();
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
// Reducing pixelPerUnit by 1 just to overcome any problems due to rounding off of pixels.
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//var offsetX = barWidth * plotUnit.index << 0;
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
for (i = 0; i < dataPoints.length; i++) {
dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (dataPoints[i].y === null || !dataPoints[i].y.length
|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number")
continue;
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
//var x1 = x - barWidth / 2 << 0;
var x1 = x - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth) << 0;
var x2 = x1 + barWidth << 0;
var y1;
var y2;
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
if (y1 > y2) {
var temp = y1;
y1 = y2;
y2 = temp;
}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
//var borderThickness = Math.max(1, (barWidth * .1 << 0));
var borderThickness = 0;
drawRect(ctx, x1, y1, x2, y2, color, borderThickness, color, bevelEnabled, bevelEnabled, false, false, dataSeries.fillOpacity);
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "rangeColumn",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 0,
point: { x: x1 + (x2 - x1) / 2, y: dataPoints[i].y[1] >= dataPoints[i].y[0] ? y2 : y1 },
direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? -1 : 1,
bounds: { x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2) },
color: color
});
this._indexLabels.push({
chartType: "rangeColumn",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 1,
point: { x: x1 + (x2 - x1) / 2, y: dataPoints[i].y[1] >= dataPoints[i].y[0] ? y1 : y2 },
direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? 1 : -1,
bounds: { x1: x1, y1: Math.min(y1, y2), x2: x2, y2: Math.max(y1, y2) },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderRangeBar = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var color = null;
var plotArea = this.plotArea;
var i = 0, x1, x2, y;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number from dataTime everytime it is used.
//In case of Bar Chart, yZeroToPixel is x co-ordinate!
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum)) << 0;
var maxBarWidth = this.dataPointMaxWidth ? this.dataPointMaxWidth : Math.min((this.height * .15), this.plotArea.height / plotUnit.plotType.totalDataSeries * .9) << 0;
var xMinDiff = plotUnit.axisX.dataInfo.minDiff;
//var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / totalDataSeries * .9) << 0;
var barWidth = (((plotArea.height / Math.abs(plotUnit.axisX.maximum - plotUnit.axisX.minimum)) * Math.abs(xMinDiff)) / plotUnit.plotType.totalDataSeries * .9) << 0;
if (barWidth > maxBarWidth)
barWidth = maxBarWidth;
else if (xMinDiff === Infinity) {
barWidth = maxBarWidth / plotUnit.plotType.totalDataSeries * .9;
} else if (barWidth < 1)
barWidth = 1;
ctx.save();
if (isCanvasSupported)
this._eventManager.ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
this._eventManager.ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
this._eventManager.ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var isFirstDataPointInPlotArea = true;
//dataSeries.maxWidthInX = barWidth / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
if (dataPoints.length > 0) {
//var xy = this.getPixelCoordinatesOnPlotArea(dataPoints[0].x, dataPoints[0].y);
var bevelEnabled = (barWidth > 5) && dataSeries.bevelEnabled ? true : false;
ctx.strokeStyle = "#4572A7 ";
for (i = 0; i < dataPoints.length; i++) {
dataPoints[i].getTime ? dataPointX = dataPoints[i].x.getTime() : dataPointX = dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (dataPoints[i].y === null || !dataPoints[i].y.length
|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number")
continue;
//x and y are pixel co-ordinates of point and should not be confused with X and Y values
x1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
x2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
var y1 = (y - (plotUnit.plotType.totalDataSeries * barWidth / 2) + ((plotUnit.previousDataSeriesCount + j) * barWidth)) << 0;
var y2 = y1 + barWidth << 0;
if (x1 > x2) {
var temp = x1;
x1 = x2;
x2 = temp;
}
//drawRect(ctx, x1, y1, plotArea.x2, y2, "#EEEEEE", 0, null, false, false, false, false);
//drawRect(ctx, x1, y1, plotArea.x2, y2, "#BDCED3", 0, null, false, false, false, false);
color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
//color = "#1B4962";
drawRect(ctx, x1, y1, x2, y2, color, 0, null, bevelEnabled, false, false, false, dataSeries.fillOpacity);
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x1, y1: y1, x2: x2, y2: y2 };
color = intToHexColorString(id);
if (isCanvasSupported)
drawRect(this._eventManager.ghostCtx, x1, y1, x2, y2, color, 0, null, false, false, false, false);
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "rangeBar",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 0,
point: { x: dataPoints[i].y[1] >= dataPoints[i].y[0] ? x1 : x2, y: y1 + (y2 - y1) / 2 },
direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? -1 : 1,
bounds: { x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2 },
color: color
});
this._indexLabels.push({
chartType: "rangeBar",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 1,
point: { x: dataPoints[i].y[1] >= dataPoints[i].y[0] ? x2 : x1, y: y1 + (y2 - y1) / 2 },
direction: dataPoints[i].y[1] >= dataPoints[i].y[0] ? 1 : -1,
bounds: { x1: Math.min(x1, x2), y1: y1, x2: Math.max(x1, x2), y2: y2 },
color: color
});
}
}
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.fadeInAnimation, easingFunction: AnimationHelper.easing.easeInQuad, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderRangeArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
var axisXProps = plotUnit.axisX.lineCoordinates;
var axisYProps = plotUnit.axisY.lineCoordinates;
var markers = [];
var plotArea = this.plotArea;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var closingPath = [];
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
//ghostCtx.lineWidth = 20;
markers = [];
var isFirstDataPointInPlotArea = true;
var i = 0, x, y1, y2;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var baseY;
var startPoint = null;
if (dataPoints.length > 0) {
//ctx.strokeStyle = "#4572A7 ";
var color = dataSeries._colorSet[i % dataSeries._colorSet.length];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
var prevDataNull = true;
for (; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (dataPoints[i].y === null || !dataPoints[i].y.length
|| typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number") {
closeArea();
prevDataNull = true;
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
if (isFirstDataPointInPlotArea || prevDataNull) {
ctx.beginPath();
ctx.moveTo(x, y1);
startPoint = { x: x, y: y1 };
closingPath = [];
closingPath.push({ x: x, y: y2 });
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(x, y1);
}
isFirstDataPointInPlotArea = false;
prevDataNull = false;
}
else {
ctx.lineTo(x, y1);
closingPath.push({ x: x, y: y2 });
if (isCanvasSupported)
ghostCtx.lineTo(x, y1);
if (i % 250 == 0) {
closeArea();
}
}
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y1, y2: y2 };
//Render Marker
if (dataPoints[i].markerSize !== 0) {
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y2, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y2, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
markerProps = dataSeries.getMarkerProperties(i, x, y1, ctx);
markers.push(markerProps);
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y1, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "rangeArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 0,
point: { x: x, y: y1 },
direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? -1 : 1,
color: color
});
this._indexLabels.push({
chartType: "rangeArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 1,
point: { x: x, y: y2 },
direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? 1 : -1,
color: color
});
}
//alert("hi");
}
closeArea();
//startPoint = { x: x, y: y };
RenderHelper.drawMarkers(markers);
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
function closeArea() {
if (!startPoint)
return;
var point = null;
if (dataSeries.lineThickness > 0)
ctx.stroke();
for (var i = closingPath.length - 1; i >= 0; i--) {
point = closingPath[i];
ctx.lineTo(point.x, point.y);
ghostCtx.lineTo(point.x, point.y);
}
ctx.closePath();
//ctx.lineTo(startPoint.x, startPoint.y);
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
ghostCtx.fill();
//if (isCanvasSupported) {
// ghostCtx.lineTo(x, baseY);
// ghostCtx.lineTo(startPoint.x, baseY);
// ghostCtx.closePath();
// ghostCtx.fill();
//}
if (dataSeries.lineThickness > 0) {
ctx.beginPath();
ctx.moveTo(point.x, point.y);
for (var i = 0; i < closingPath.length; i++) {
point = closingPath[i];
ctx.lineTo(point.x, point.y);
}
ctx.stroke();
}
ctx.beginPath();
ctx.moveTo(x, y1);
ghostCtx.beginPath();
ghostCtx.moveTo(x, y1);
startPoint = { x: x, y: y1 };
closingPath = [];
closingPath.push({ x: x, y: y2 });
}
//ctx.beginPath();
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
Chart.prototype.renderRangeSplineArea = function (plotUnit) {
var ctx = plotUnit.targetCanvasCtx || this.plotArea.ctx;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var ghostCtx = this._eventManager.ghostCtx;
var axisXProps = plotUnit.axisX.lineCoordinates;
var axisYProps = plotUnit.axisY.lineCoordinates;
var markers = [];
var plotArea = this.plotArea;
ctx.save();
if (isCanvasSupported)
ghostCtx.save();
ctx.beginPath();
ctx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.clip();
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.rect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ghostCtx.clip();
}
for (var j = 0; j < plotUnit.dataSeriesIndexes.length; j++) {
var dataSeriesIndex = plotUnit.dataSeriesIndexes[j];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var seriesId = dataSeries.id;
this._eventManager.objectMap[seriesId] = { objectType: "dataSeries", dataSeriesIndex: dataSeriesIndex };
var hexColor = intToHexColorString(seriesId);
ghostCtx.fillStyle = hexColor;
//ghostCtx.lineWidth = dataSeries.lineThickness;
//ghostCtx.lineWidth = 20;
markers = [];
var isFirstDataPointInPlotArea = true;
var i = 0, x, y1, y2;
var dataPointX; //Used so that when dataPoint.x is a DateTime value, it doesn't get converted to number back and forth.
var yZeroToPixel = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (0 - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var baseY;
var startPoint = null;
var pixelsY1 = [];
var pixelsY2 = [];
if (dataPoints.length > 0) {
//ctx.strokeStyle = "#4572A7 ";
color = dataSeries._colorSet[i % dataSeries._colorSet.length];
//ctx.strokeStyle = "red";
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.lineWidth = dataSeries.lineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataSeries.lineDashType, dataSeries.lineThickness));
}
for (; i < dataPoints.length; i++) {
dataPointX = dataPoints[i].x.getTime ? dataPoints[i].x.getTime() : dataPoints[i].x;
if (dataPointX < plotUnit.axisX.dataInfo.viewPortMin || dataPointX > plotUnit.axisX.dataInfo.viewPortMax) {
continue;
}
if (dataPoints[i].y === null || !dataPoints[i].y.length || typeof (dataPoints[i].y[0]) !== "number" || typeof (dataPoints[i].y[1]) !== "number") {
if (i > 0) {
renderBezierArea();
pixelsY1 = [];
pixelsY2 = [];
}
continue;
}
x = (plotUnit.axisX.conversionParameters.reference + plotUnit.axisX.conversionParameters.pixelPerUnit * (dataPointX - plotUnit.axisX.conversionParameters.minimum) + .5) << 0;
y1 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[0] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
y2 = (plotUnit.axisY.conversionParameters.reference + plotUnit.axisY.conversionParameters.pixelPerUnit * (dataPoints[i].y[1] - plotUnit.axisY.conversionParameters.minimum) + .5) << 0;
var id = dataSeries.dataPointIds[i];
this._eventManager.objectMap[id] = { id: id, objectType: "dataPoint", dataSeriesIndex: dataSeriesIndex, dataPointIndex: i, x1: x, y1: y1, y2: y2 };
pixelsY1[pixelsY1.length] = { x: x, y: y1 };
pixelsY2[pixelsY2.length] = { x: x, y: y2 };
//Render Marker
if (dataPoints[i].markerSize !== 0) {
if (dataPoints[i].markerSize > 0 || dataSeries.markerSize > 0) {
var markerProps = dataSeries.getMarkerProperties(i, x, y1, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y1, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
var markerProps = dataSeries.getMarkerProperties(i, x, y2, ctx);
markers.push(markerProps);
//if (!dataSeries.maxWidthInX || markerProps.size > dataSeries.maxWidthInX) {
// dataSeries.maxWidthInX = markerProps.size / (plotUnit.axisX.conversionParameters.pixelPerUnit > 1 ? plotUnit.axisX.conversionParameters.pixelPerUnit - 1 : plotUnit.axisX.conversionParameters.pixelPerUnit);
//}
var markerColor = intToHexColorString(id);
if (isCanvasSupported) {
markers.push({
x: x, y: y2, ctx: ghostCtx,
type: markerProps.type,
size: markerProps.size,
color: markerColor,
borderColor: markerColor,
borderThickness: markerProps.borderThickness
});
}
}
}
//Render Index Labels
if (dataPoints[i].indexLabel || dataSeries.indexLabel || dataPoints[i].indexLabelFormatter || dataSeries.indexLabelFormatter) {
this._indexLabels.push({
chartType: "splineArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 0,
point: { x: x, y: y1 },
direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? -1 : 1,
color: color
});
this._indexLabels.push({
chartType: "splineArea",
dataPoint: dataPoints[i],
dataSeries: dataSeries,
indexKeyword: 1,
point: { x: x, y: y2 },
direction: dataPoints[i].y[0] <= dataPoints[i].y[1] ? 1 : -1,
color: color
});
}
}
renderBezierArea();
RenderHelper.drawMarkers(markers);
}
}
ctx.restore();
if (isCanvasSupported)
this._eventManager.ghostCtx.restore();
function renderBezierArea() {
var bp = getBezierPoints(pixelsY1, 2);
if (bp.length > 0) {
ctx.beginPath();
ctx.moveTo(bp[0].x, bp[0].y);
if (isCanvasSupported) {
ghostCtx.beginPath();
ghostCtx.moveTo(bp[0].x, bp[0].y);
}
for (var i = 0; i < bp.length - 3; i += 3) {
ctx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
if (isCanvasSupported)
ghostCtx.bezierCurveTo(bp[i + 1].x, bp[i + 1].y, bp[i + 2].x, bp[i + 2].y, bp[i + 3].x, bp[i + 3].y);
}
if (dataSeries.lineThickness > 0)
ctx.stroke();
bp = getBezierPoints(pixelsY2, 2);
ctx.lineTo(pixelsY2[pixelsY2.length - 1].x, pixelsY2[pixelsY2.length - 1].y);
for (var i = bp.length - 1; i > 2; i -= 3) {
ctx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
if (isCanvasSupported)
ghostCtx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
}
ctx.closePath();
ctx.globalAlpha = dataSeries.fillOpacity;
ctx.fill();
ctx.globalAlpha = 1;
if (dataSeries.lineThickness > 0) {
ctx.beginPath();
ctx.moveTo(pixelsY2[pixelsY2.length - 1].x, pixelsY2[pixelsY2.length - 1].y);
for (var i = bp.length - 1; i > 2; i -= 3) {
ctx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
if (isCanvasSupported)
ghostCtx.bezierCurveTo(bp[i - 1].x, bp[i - 1].y, bp[i - 2].x, bp[i - 2].y, bp[i - 3].x, bp[i - 3].y);
}
ctx.stroke();
}
ctx.beginPath();
if (isCanvasSupported) {
ghostCtx.closePath();
ghostCtx.fill();
}
}
}
//source and dest would be same when animation is not enabled
var animationInfo = { source: ctx, dest: this.plotArea.ctx, animationCallback: AnimationHelper.xClipAnimation, easingFunction: AnimationHelper.easing.linear, animationBase: 0 };
return animationInfo;
}
//#region pieChart
var drawSegment = function (ctx, center, radius, color, type, theta1, theta2, fillOpacity) {
if (typeof (fillOpacity) === "undefined")
fillOpacity = 1;
//IE8- FIX: In IE8- segment doesn't get draw if theta2 is equal to theta1 + 2*PI.
if (!isCanvasSupported) {
var theta2Mod = Number((theta2 % (2 * Math.PI)).toFixed(8));
var theta1Mod = Number((theta1 % (2 * Math.PI)).toFixed(8));
if (theta1Mod === theta2Mod)
theta2 -= .0001;
}
ctx.save();
ctx.globalAlpha = fillOpacity;
if (type === "pie") {
ctx.beginPath();
ctx.moveTo(center.x, center.y);
ctx.arc(center.x, center.y, radius, theta1, theta2, false);
ctx.fillStyle = color;
ctx.strokeStyle = "white";
ctx.lineWidth = 2;
// ctx.shadowOffsetX = 2;
// ctx.shadowOffsetY = 1;
// ctx.shadowBlur = 2;
// ctx.shadowColor = '#BFBFBF';
ctx.closePath();
//ctx.stroke();
ctx.fill();
}
else if (type === "doughnut") {
var widthPercentage = 0.60;
ctx.beginPath();
ctx.arc(center.x, center.y, radius, theta1, theta2, false);
ctx.arc(center.x, center.y, widthPercentage * radius, theta2, theta1, true);
ctx.closePath();
ctx.fillStyle = color;
ctx.strokeStyle = "white";
ctx.lineWidth = 2;
// shadow properties
// ctx.shadowOffsetX = 1;
// ctx.shadowOffsetY = 1;
// ctx.shadowBlur = 1;
// ctx.shadowColor = '#BFBFBF'; //grey shadow
//ctx.stroke();
ctx.fill();
}
ctx.globalAlpha = 1;
ctx.restore();
};
Chart.prototype.renderPie = function (plotUnit) {
var _this = this;
var totalDataSeries = plotUnit.dataSeriesIndexes.length;
if (totalDataSeries <= 0)
return;
var dataSeriesIndex = plotUnit.dataSeriesIndexes[0];
var dataSeries = this.data[dataSeriesIndex];
var dataPoints = dataSeries.dataPoints;
var indexLabelLineEdgeLength = 10;
var explodeDuration = 500;
var plotArea = this.plotArea;
//var maxFrame = isCanvasSupported ? 300 : 4;
var totalRecursions = 0;
var dataPointEOs = []; //dataPoint Extension Objects Behaves like a storage place for all additional data relating to dataPoints. Requred because actual dataPoints should not be modified.
var minDistanceBetweenLabels = 2;
var indexLabelRadiusToRadiusRatio = 1.3;
var poleAnglularDistance = (20 / 180) * Math.PI; //Anglular Distance from 90 & 270 to be considered pole
var precision = 6;
var center = { x: (plotArea.x2 + plotArea.x1) / 2, y: (plotArea.y2 + plotArea.y1) / 2 };
var outerRadius = dataSeries.indexLabelPlacement === "inside" ? (Math.min(plotArea.width, plotArea.height) * 0.92) / 2 : (Math.min(plotArea.width, plotArea.height) * 0.8) / 2;
var innerRadius = outerRadius * .6;
var indexLabelRadius = outerRadius * indexLabelRadiusToRadiusRatio;
var newPieRadius = outerRadius;
var sum = 0;
for (var j = 0; j < dataPoints.length; j++) {
sum += Math.abs(dataPoints[j].y);
}
if (sum === 0)
return;
function initLabels() {
if (!dataSeries || !dataPoints)
return;
var noDPNearSouthPole = 0;
var noDPNearNorthPole = 0;
var firstDPCloseToSouth = 0;
var firstDPCloseToNorth = 0;
for (j = 0; j < dataPoints.length; j++) {
var dataPoint = dataPoints[j];
var id = dataSeries.dataPointIds[j];
var dataPointEO = { id: id, objectType: "dataPoint", dataPointIndex: j, dataSeriesIndex: 0 };
dataPointEOs.push(dataPointEO);
var percentAndTotal = { percent: null, total: null };
var formatterParameter = null;
percentAndTotal = _this.getPercentAndTotal(dataSeries, dataPoint);
if (dataSeries.indexLabelFormatter || dataPoint.indexLabelFormatter)
formatterParameter = { chart: _this._options, dataSeries: dataSeries, dataPoint: dataPoint, total: percentAndTotal.total, percent: percentAndTotal.percent };
var indexLabelText = dataPoint.indexLabelFormatter ? dataPoint.indexLabelFormatter(formatterParameter)
: dataPoint.indexLabel ? _this.replaceKeywordsWithValue(dataPoint.indexLabel, dataPoint, dataSeries, j)
: dataSeries.indexLabelFormatter ? dataSeries.indexLabelFormatter(formatterParameter)
: dataSeries.indexLabel ? _this.replaceKeywordsWithValue(dataSeries.indexLabel, dataPoint, dataSeries, j) : dataPoint.label ? dataPoint.label : '';
_this._eventManager.objectMap[id] = dataPointEO;
//dataPointEO.indexLabelText = j.toString() + " " + "kingfisher: " + dataPoint.y.toString();;
dataPointEO.center = { x: center.x, y: center.y };
dataPointEO.y = dataPoint.y;
dataPointEO.radius = outerRadius;
dataPointEO.indexLabelText = indexLabelText;
dataPointEO.indexLabelPlacement = dataSeries.indexLabelPlacement;
dataPointEO.indexLabelLineColor = dataPoint.indexLabelLineColor ? dataPoint.indexLabelLineColor : dataSeries.indexLabelLineColor ? dataSeries.indexLabelLineColor : dataPoint.color ? dataPoint.color : dataSeries._colorSet[j % dataSeries._colorSet.length];
dataPointEO.indexLabelLineThickness = dataPoint.indexLabelLineThickness ? dataPoint.indexLabelLineThickness : dataSeries.indexLabelLineThickness;
dataPointEO.indexLabelLineDashType = dataPoint.indexLabelLineDashType ? dataPoint.indexLabelLineDashType : dataSeries.indexLabelLineDashType;
dataPointEO.indexLabelFontColor = dataPoint.indexLabelFontColor ? dataPoint.indexLabelFontColor : dataSeries.indexLabelFontColor;
dataPointEO.indexLabelFontStyle = dataPoint.indexLabelFontStyle ? dataPoint.indexLabelFontStyle : dataSeries.indexLabelFontStyle;
dataPointEO.indexLabelFontWeight = dataPoint.indexLabelFontWeight ? dataPoint.indexLabelFontWeight : dataSeries.indexLabelFontWeight;
dataPointEO.indexLabelFontSize = dataPoint.indexLabelFontSize ? dataPoint.indexLabelFontSize : dataSeries.indexLabelFontSize;
dataPointEO.indexLabelFontFamily = dataPoint.indexLabelFontFamily ? dataPoint.indexLabelFontFamily : dataSeries.indexLabelFontFamily;
dataPointEO.indexLabelBackgroundColor = dataPoint.indexLabelBackgroundColor ? dataPoint.indexLabelBackgroundColor : dataSeries.indexLabelBackgroundColor ? dataSeries.indexLabelBackgroundColor : null;
dataPointEO.indexLabelMaxWidth = dataPoint.indexLabelMaxWidth ? dataPoint.indexLabelMaxWidth : dataSeries.indexLabelMaxWidth ? dataSeries.indexLabelMaxWidth : plotArea.width * .33;
dataPointEO.indexLabelWrap = typeof (dataPoint.indexLabelWrap) !== "undefined" ? dataPoint.indexLabelWrap : dataSeries.indexLabelWrap;
dataPointEO.startAngle = j === 0 ? dataSeries.startAngle ? (dataSeries.startAngle / 180) * Math.PI : 0 : dataPointEOs[j - 1].endAngle;
dataPointEO.startAngle = (dataPointEO.startAngle + (2 * Math.PI)) % (2 * Math.PI);
dataPointEO.endAngle = dataPointEO.startAngle + ((2 * Math.PI / sum) * Math.abs(dataPoint.y));
//var midAngle = dataPointEO.startAngle + Math.abs(dataPointEO.endAngle - dataPointEO.startAngle) / 2;
var midAngle = (dataPointEO.endAngle + dataPointEO.startAngle) / 2;
//var midAngle = (180 / Math.PI * midAngle);
midAngle = (midAngle + (2 * Math.PI)) % (2 * Math.PI);
dataPointEO.midAngle = midAngle;
if (dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
if (noDPNearSouthPole === 0 || dataPointEOs[firstDPCloseToSouth].midAngle > dataPointEO.midAngle)
firstDPCloseToSouth = j;
noDPNearSouthPole++;
}
else if (dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
if (noDPNearNorthPole === 0 || dataPointEOs[firstDPCloseToNorth].midAngle > dataPointEO.midAngle)
firstDPCloseToNorth = j;
noDPNearNorthPole++;
}
if (midAngle > (Math.PI / 2) && midAngle <= (3 * Math.PI / 2))
dataPointEO.hemisphere = "left";
else
dataPointEO.hemisphere = "right";
//dataPointEO.indexLabelText = j.toString() + "; " + dataPoint.y.toString() + "; " + midAngle.toString() + "; junk";
dataPointEO.indexLabelTextBlock = new TextBlock(_this.plotArea.ctx, {
fontSize: dataPointEO.indexLabelFontSize, fontFamily: dataPointEO.indexLabelFontFamily, fontColor: dataPointEO.indexLabelFontColor,
fontStyle: dataPointEO.indexLabelFontStyle, fontWeight: dataPointEO.indexLabelFontWeight,
horizontalAlign: "left",
backgroundColor: dataPointEO.indexLabelBackgroundColor,
maxWidth: dataPointEO.indexLabelMaxWidth, maxHeight: dataPointEO.indexLabelWrap ? dataPointEO.indexLabelFontSize * 5 : dataPointEO.indexLabelFontSize * 1.5,
text: dataPointEO.indexLabelText,
padding: 0,
//textBaseline: dataPointEO.indexLabelBackgroundColor ? "middle" : "top"
textBaseline: "top"
});
dataPointEO.indexLabelTextBlock.measureText();
//dataPoint.labelWidth = ctx.measureText(j.toString() + "; " + dataPoint.label).width;
//console.log(dataPoint.label);
}
var noOfDPToRightOfSouthPole = 0;
var noOfDPToLeftOfNorthPole = 0;
var keepSameDirection = false; // once a dataPoint's hemisphere is changed, others should follow the same so that there are no labes near pole pointing in opposite direction.
for (j = 0; j < dataPoints.length; j++) {
var dataPointEO = dataPointEOs[(firstDPCloseToSouth + j) % dataPoints.length];
if (noDPNearSouthPole > 1 && dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
if (noOfDPToRightOfSouthPole <= noDPNearSouthPole / 2 && !keepSameDirection) {
dataPointEO.hemisphere = "right";
noOfDPToRightOfSouthPole++;
}
else {
dataPointEO.hemisphere = "left";
keepSameDirection = true;
}
}
}
keepSameDirection = false;
for (j = 0; j < dataPoints.length; j++) {
var dataPointEO = dataPointEOs[(firstDPCloseToNorth + j) % dataPoints.length];
//if (dataPoint.hemisphere = "right")
// break;
if (noDPNearNorthPole > 1 && dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
if (noOfDPToLeftOfNorthPole <= noDPNearNorthPole / 2 && !keepSameDirection) {
dataPointEO.hemisphere = "left";
noOfDPToLeftOfNorthPole++;
}
else {
dataPointEO.hemisphere = "right";
keepSameDirection = true;
}
}
}
}//End of initLabels()
function renderLabels() {
var ctx = _this.plotArea.ctx;
ctx.fillStyle = "black";
ctx.strokeStyle = "grey";
var fontSize = 16;
//ctx.font = fontSize + "px Arial";
ctx.textBaseline = "middle";
ctx.lineJoin = "round";
var i = 0, j = 0;
for (i = 0; i < dataPoints.length; i++) {
var dataPointEO = dataPointEOs[i];
if (!dataPointEO.indexLabelText)
continue;
dataPointEO.indexLabelTextBlock.y -= dataPointEO.indexLabelTextBlock.height / 2;
var xOffset = 0;
if (dataPointEO.hemisphere === "left") {
var xOffset = dataSeries.indexLabelPlacement !== "inside" ? -(dataPointEO.indexLabelTextBlock.width + indexLabelLineEdgeLength) : -dataPointEO.indexLabelTextBlock.width / 2;
}
else {
var xOffset = dataSeries.indexLabelPlacement !== "inside" ? indexLabelLineEdgeLength : -dataPointEO.indexLabelTextBlock.width / 2;
}
dataPointEO.indexLabelTextBlock.x += xOffset;
dataPointEO.indexLabelTextBlock.render(true);
dataPointEO.indexLabelTextBlock.x -= xOffset;
//if (i < 4)
// customPrompt(i + "; " + center.y + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2));
dataPointEO.indexLabelTextBlock.y += dataPointEO.indexLabelTextBlock.height / 2;
if (dataPointEO.indexLabelPlacement !== "inside") {
var indexLabelLineStartX = dataPointEO.center.x + outerRadius * Math.cos(dataPointEO.midAngle);
var indexLabelLineStartY = dataPointEO.center.y + outerRadius * Math.sin(dataPointEO.midAngle);
//ctx.strokeStyle = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
ctx.strokeStyle = dataPointEO.indexLabelLineColor;
ctx.lineWidth = dataPointEO.indexLabelLineThickness;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(dataPointEO.indexLabelLineDashType, dataPointEO.indexLabelLineThickness));
}
//ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(indexLabelLineStartX, indexLabelLineStartY);
ctx.lineTo(dataPointEO.indexLabelTextBlock.x, dataPointEO.indexLabelTextBlock.y);
ctx.lineTo(dataPointEO.indexLabelTextBlock.x + (dataPointEO.hemisphere === "left" ? -indexLabelLineEdgeLength : indexLabelLineEdgeLength), dataPointEO.indexLabelTextBlock.y);
ctx.stroke();
//ctx.closePath();
//window.alert("contine??");
//animate();
}
ctx.lineJoin = "miter";
}
}
function animate(fractionComplete) {
var ctx = _this.plotArea.ctx;
ctx.clearRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.fillStyle = _this.backgroundColor;
ctx.fillRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
var maxAngle = dataPointEOs[0].startAngle + (2 * Math.PI * fractionComplete);
for (var i = 0; i < dataPoints.length; i++) {
var startAngle = i === 0 ? dataPointEOs[i].startAngle : endAngle;
var endAngle = startAngle + (dataPointEOs[i].endAngle - dataPointEOs[i].startAngle);
var shouldBreak = false;
if (endAngle > maxAngle) {
endAngle = maxAngle;
shouldBreak = true;
}
var color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
if (endAngle > startAngle)
drawSegment(_this.plotArea.ctx, dataPointEOs[i].center, dataPointEOs[i].radius, color, dataSeries.type, startAngle, endAngle, dataSeries.fillOpacity);
if (shouldBreak)
break;
}
}
function explodeToggle(fractionComplete) {
var ctx = _this.plotArea.ctx;
ctx.clearRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
ctx.fillStyle = _this.backgroundColor;
ctx.fillRect(plotArea.x1, plotArea.y1, plotArea.width, plotArea.height);
for (var i = 0; i < dataPoints.length; i++) {
var startAngle = dataPointEOs[i].startAngle;
var endAngle = dataPointEOs[i].endAngle;
if (endAngle > startAngle) {
var offsetX = (outerRadius * .07 * Math.cos(dataPointEOs[i].midAngle));
var offsetY = (outerRadius * .07 * Math.sin(dataPointEOs[i].midAngle));
var isInTransition = false;
if (dataPoints[i].exploded) {
if (Math.abs(dataPointEOs[i].center.x - (center.x + offsetX)) > 0.000000001 || Math.abs(dataPointEOs[i].center.y - (center.y + offsetY)) > 0.000000001) {
dataPointEOs[i].center.x = center.x + offsetX * fractionComplete;
dataPointEOs[i].center.y = center.y + offsetY * fractionComplete;
isInTransition = true;
}
} else if (Math.abs(dataPointEOs[i].center.x - center.x) > 0 || Math.abs(dataPointEOs[i].center.y - center.y) > 0) {
dataPointEOs[i].center.x = center.x + offsetX * (1 - fractionComplete);
dataPointEOs[i].center.y = center.y + offsetY * (1 - fractionComplete);
isInTransition = true;
}
if (isInTransition) {
var entry = {};
entry.dataSeries = dataSeries;
entry.dataPoint = dataSeries.dataPoints[i];
entry.index = i;
_this._toolTip.highlightObjects([entry]);
}
var color = dataPoints[i].color ? dataPoints[i].color : dataSeries._colorSet[i % dataSeries._colorSet.length];
drawSegment(_this.plotArea.ctx, dataPointEOs[i].center, dataPointEOs[i].radius, color, dataSeries.type, startAngle, endAngle, dataSeries.fillOpacity);
}
}
//window.alert("next??");
renderLabels();
}
function areDataPointsTooClose(first, second) {
var label1 = { x1: first.indexLabelTextBlock.x, y1: first.indexLabelTextBlock.y - first.indexLabelTextBlock.height / 2, x2: first.indexLabelTextBlock.x + first.indexLabelTextBlock.width, y2: first.indexLabelTextBlock.y + first.indexLabelTextBlock.height / 2 };
var label2 = { x1: second.indexLabelTextBlock.x, y1: second.indexLabelTextBlock.y - second.indexLabelTextBlock.height / 2, x2: second.indexLabelTextBlock.x + second.indexLabelTextBlock.width, y2: second.indexLabelTextBlock.y + second.indexLabelTextBlock.height / 2 };
if (label1.x2 < label2.x1 - indexLabelLineEdgeLength || label1.x1 > label2.x2 + indexLabelLineEdgeLength || label1.y1 > label2.y2 + indexLabelLineEdgeLength || label1.y2 < label2.y1 - indexLabelLineEdgeLength)
return false;
return true;
}
function getVerticalDistanceBetweenLabels(first, second) {
var distance = 0;
var label1 = { y: first.indexLabelTextBlock.y, y1: first.indexLabelTextBlock.y - first.indexLabelTextBlock.height / 2, y2: first.indexLabelTextBlock.y + first.indexLabelTextBlock.height / 2 };
var label2 = { y: second.indexLabelTextBlock.y, y1: second.indexLabelTextBlock.y - second.indexLabelTextBlock.height / 2, y2: second.indexLabelTextBlock.y + second.indexLabelTextBlock.height / 2 };
if (label2.y > label1.y) {
distance = label2.y1 - label1.y2;
}
else {
distance = label1.y1 - label2.y2;
}
return distance;
}
function getNextLabelIndex(currentLabelIndex) {
var nextLabelIndex = null;
for (var i = 1; i < dataPoints.length; i++) {
nextLabelIndex = (currentLabelIndex + i + dataPointEOs.length) % dataPointEOs.length;
if (dataPointEOs[nextLabelIndex].hemisphere !== dataPointEOs[currentLabelIndex].hemisphere) {
nextLabelIndex = null;
break;
}
else if ((dataPointEOs[nextLabelIndex].indexLabelText) && (nextLabelIndex !== currentLabelIndex)
&& ((getVerticalDistanceBetweenLabels(dataPointEOs[nextLabelIndex], dataPointEOs[currentLabelIndex]) < 0) || (dataPointEOs[currentLabelIndex].hemisphere === "right" ? dataPointEOs[nextLabelIndex].indexLabelTextBlock.y >= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y : dataPointEOs[nextLabelIndex].indexLabelTextBlock.y <= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y)))
break;
else {
nextLabelIndex = null;
}
}
return nextLabelIndex;
}
function getPreviousLabelIndex(currentLabelIndex) {
var prevLabelIndex = null;
for (var i = 1; i < dataPoints.length; i++) {
prevLabelIndex = (currentLabelIndex - i + dataPointEOs.length) % dataPointEOs.length;
if (dataPointEOs[prevLabelIndex].hemisphere !== dataPointEOs[currentLabelIndex].hemisphere) {
prevLabelIndex = null;
break;
}
else if ((dataPointEOs[prevLabelIndex].indexLabelText) && (dataPointEOs[prevLabelIndex].hemisphere === dataPointEOs[currentLabelIndex].hemisphere) && (prevLabelIndex !== currentLabelIndex)
&& ((getVerticalDistanceBetweenLabels(dataPointEOs[prevLabelIndex], dataPointEOs[currentLabelIndex]) < 0) || (dataPointEOs[currentLabelIndex].hemisphere === "right" ? dataPointEOs[prevLabelIndex].indexLabelTextBlock.y <= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y : dataPointEOs[prevLabelIndex].indexLabelTextBlock.y >= dataPointEOs[currentLabelIndex].indexLabelTextBlock.y)))
break;
else {
prevLabelIndex = null;
}
}
return prevLabelIndex;
}
function rePositionLabels(dataPointIndex, offset) {
offset = offset || 0;
var actualOffset = 0;
//var labelYMin = 2;
//var labelYMax = ctx.canvas.height - 2;
//var labelYMin = _this.plotArea.ctx.canvas.height / 2 - indexLabelRadius * 1;
//var labelYMax = _this.plotArea.ctx.canvas.height / 2 + indexLabelRadius * 1;
var labelYMin = center.y - indexLabelRadius * 1;
var labelYMax = center.y + indexLabelRadius * 1;
//console.log(totalRecursions);
if (dataPointIndex >= 0 && dataPointIndex < dataPoints.length) {
var dataPointEO = dataPointEOs[dataPointIndex];
//if (dataPointIndex === 0)
// customPrompt(labelYMin.toFixed(2) + "; " + labelYMax.toFixed(2) + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2));
// If label is already outside the bounds, return
if ((offset < 0 && dataPointEO.indexLabelTextBlock.y < labelYMin) || (offset > 0 && dataPointEO.indexLabelTextBlock.y > labelYMax))
return 0;
var validOffset = offset;
//Check if the offset falls within the bounds (labelYMin, labelYMax, tangential bounds) without considering overlap. Else use the closest offset that is possible - validOffset.
{
var distFromIndexLineStart = 0;
var indexLabelLineStartX = 0;
var indexLabelLineStartY = 0;
var indexLabelAngle = 0;
var indexLabelAngleWhenTangent = 0;
if (validOffset < 0) {
if (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 > labelYMin && dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 + validOffset < labelYMin)
validOffset = -(labelYMin - (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 + validOffset));
} else {
if (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 < labelYMin && dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + validOffset > labelYMax)
validOffset = (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + validOffset) - labelYMax;
}
var newlabelY = dataPointEO.indexLabelTextBlock.y + validOffset;
var newlabelX = 0;
if (dataPointEO.hemisphere === "right") {
newlabelX = center.x + Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newlabelY - center.y, 2));
}
else
newlabelX = center.x - Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newlabelY - center.y, 2));
indexLabelLineStartX = center.x + outerRadius * Math.cos(dataPointEO.midAngle);
indexLabelLineStartY = center.y + outerRadius * Math.sin(dataPointEO.midAngle);
distFromIndexLineStart = Math.sqrt(Math.pow(newlabelX - indexLabelLineStartX, 2) + Math.pow(newlabelY - indexLabelLineStartY, 2));
indexLabelAngleWhenTangent = Math.acos(outerRadius / indexLabelRadius);
//indexLabelAngle = Math.acos((outerRadius * outerRadius + distFromIndexLineStart * distFromIndexLineStart - indexLabelRadius * indexLabelRadius) / (2 * outerRadius * distFromIndexLineStart));
indexLabelAngle = Math.acos((indexLabelRadius * indexLabelRadius + outerRadius * outerRadius - distFromIndexLineStart * distFromIndexLineStart) / (2 * outerRadius * indexLabelRadius));
if (indexLabelAngle < indexLabelAngleWhenTangent) {
validOffset = newlabelY - dataPointEO.indexLabelTextBlock.y;
//dataPointEO.indexLabelTextBlock.x = newlabelX;
}
else {
validOffset = 0;
//dataPointEO.indexLabelTextBlock.x = newlabelX;
//Index Line is overlapping the pie. So lets find out the point where indexline becomes a tangent.
//distFromIndexLineStart = Math.sqrt(indexLabelRadius * indexLabelRadius - outerRadius * outerRadius);
////distFromIndexLineStart *= offset < 0 ? -1 : 1;
////indexLabelAngle = Math.acos((indexLabelRadius * indexLabelRadius + outerRadius * outerRadius - distFromIndexLineStart * distFromIndexLineStart) / (2 * outerRadius * indexLabelRadius));
//indexLabelAngle = Math.atan2(distFromIndexLineStart, outerRadius);
//newlabelX = center.x + indexLabelRadius * Math.cos(indexLabelAngle);
//newlabelY = center.y + indexLabelRadius * Math.sin(indexLabelAngle);
//actualOffset = newlabelY - dataPointEO.indexLabelTextBlock.y;
//dataPointEO.indexLabelTextBlock.y = newlabelY;
//dataPointEO.indexLabelTextBlock.x = newlabelX;
}
}
//var tempIndex = (dataPointIndex + dataPointEOs.length - 1) % dataPointEOs.length;
//var prevDataPointIndex = dataPointEOs[tempIndex].hemisphere === dataPointEO.hemisphere ? tempIndex : null;
var prevDataPointIndex = getPreviousLabelIndex(dataPointIndex);
//tempIndex = (dataPointIndex + dataPointEOs.length + 1) % dataPointEOs.length;
//var nextDataPointIndex = dataPointEOs[tempIndex].hemisphere === dataPointEO.hemisphere ? tempIndex : null;
var nextDataPointIndex = getNextLabelIndex(dataPointIndex);
var otherdataPointEO, otherDataPointIndex, distanceFromOtherLabel;
var otherDataPointOffset = 0;
var otherDataPointActualOffset = 0;
if (validOffset < 0) {
otherDataPointIndex = dataPointEO.hemisphere === "right" ? prevDataPointIndex : nextDataPointIndex;
actualOffset = validOffset;
if (otherDataPointIndex !== null) {
//if (dataPointIndex < 4)
// customPrompt("valid: " + validOffset);
var tempOffset = -validOffset;
var distanceFromOtherLabel = (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2) - (dataPointEOs[otherDataPointIndex].indexLabelTextBlock.y + dataPointEOs[otherDataPointIndex].indexLabelTextBlock.height / 2);
if (distanceFromOtherLabel - tempOffset < minDistanceBetweenLabels) {
otherDataPointOffset = -tempOffset;
totalRecursions++;
otherDataPointActualOffset = rePositionLabels(otherDataPointIndex, otherDataPointOffset);
//if (dataPointIndex < 4)
// customPrompt(dataPointIndex + "; " + "offset: " + otherDataPointOffset);
if (+otherDataPointActualOffset.toFixed(precision) > +otherDataPointOffset.toFixed(precision)) {
if (distanceFromOtherLabel > minDistanceBetweenLabels)
actualOffset = -(distanceFromOtherLabel - minDistanceBetweenLabels);
//else
// actualOffset = 0;
else
actualOffset = -(tempOffset - (otherDataPointActualOffset - otherDataPointOffset));
}
//if (dataPointIndex < 4)
// customPrompt("actual: " + actualOffset);
}
}
} else if (validOffset > 0) {
otherDataPointIndex = dataPointEO.hemisphere === "right" ? nextDataPointIndex : prevDataPointIndex;
actualOffset = validOffset;
if (otherDataPointIndex !== null) {
var tempOffset = validOffset;
var distanceFromOtherLabel = (dataPointEOs[otherDataPointIndex].indexLabelTextBlock.y - dataPointEOs[otherDataPointIndex].indexLabelTextBlock.height / 2) - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2);
if (distanceFromOtherLabel - tempOffset < minDistanceBetweenLabels) {
otherDataPointOffset = tempOffset;
totalRecursions++;
otherDataPointActualOffset = rePositionLabels(otherDataPointIndex, otherDataPointOffset);
if (+otherDataPointActualOffset.toFixed(precision) < +otherDataPointOffset.toFixed(precision)) {
if (distanceFromOtherLabel > minDistanceBetweenLabels)
actualOffset = distanceFromOtherLabel - minDistanceBetweenLabels;
//else
// actualOffset = 0;
else
actualOffset = tempOffset - (otherDataPointOffset - otherDataPointActualOffset);
}
}
}
//if (!(dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + actualOffset < labelYMax)) {
// if (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 < labelYMax) {
// actualOffset = labelYMax - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2);
// }
// else {
// actualOffset = 0;
// }
//}
}
if (actualOffset) {
var newLabelY = dataPointEO.indexLabelTextBlock.y + actualOffset;
var newLabelX = 0;
if (dataPointEO.hemisphere === "right") {
newLabelX = center.x + Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newLabelY - center.y, 2));
}
else
newLabelX = center.x - Math.sqrt(Math.pow(indexLabelRadius, 2) - Math.pow(newLabelY - center.y, 2));
if (dataPointEO.midAngle > (Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (Math.PI / 2) + poleAnglularDistance) {
var prevDPIndex = (dataPointIndex - 1 + dataPointEOs.length) % dataPointEOs.length;
var prevDP = dataPointEOs[prevDPIndex];
var nextDP = dataPointEOs[(dataPointIndex + 1 + dataPointEOs.length) % dataPointEOs.length];
if (dataPointEO.hemisphere === "left" && prevDP.hemisphere === "right" && newLabelX > prevDP.indexLabelTextBlock.x) {
newLabelX = prevDP.indexLabelTextBlock.x - 15;
} else if (dataPointEO.hemisphere === "right" && nextDP.hemisphere === "left" && newLabelX < nextDP.indexLabelTextBlock.x) {
newLabelX = nextDP.indexLabelTextBlock.x + 15;
}
} else if (dataPointEO.midAngle > (3 * Math.PI / 2) - poleAnglularDistance && dataPointEO.midAngle < (3 * Math.PI / 2) + poleAnglularDistance) {
var prevDPIndex = (dataPointIndex - 1 + dataPointEOs.length) % dataPointEOs.length;
var prevDP = dataPointEOs[prevDPIndex];
var nextDP = dataPointEOs[(dataPointIndex + 1 + dataPointEOs.length) % dataPointEOs.length];
if (dataPointEO.hemisphere === "right" && prevDP.hemisphere === "left" && newLabelX < prevDP.indexLabelTextBlock.x) {
newLabelX = prevDP.indexLabelTextBlock.x + 15;
} else if (dataPointEO.hemisphere === "left" && nextDP.hemisphere === "right" && newLabelX > nextDP.indexLabelTextBlock.x) {
newLabelX = nextDP.indexLabelTextBlock.x - 15;
}
}
//if (actualOffset < 0 && dataPointIndex < 4)
// customPrompt(actualOffset.toFixed(2) + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2) + "; " + newLabelY.toFixed(2));
dataPointEO.indexLabelTextBlock.y = newLabelY;
dataPointEO.indexLabelTextBlock.x = newLabelX;
dataPointEO.indexLabelAngle = Math.atan2((dataPointEO.indexLabelTextBlock.y - center.y), (dataPointEO.indexLabelTextBlock.x - center.x));
}
}
return actualOffset;
}
function positionLabels() {
var ctx = _this.plotArea.ctx;
ctx.fillStyle = "grey";
ctx.strokeStyle = "grey";
var fontSize = 16;
ctx.font = fontSize + "px Arial";
ctx.textBaseline = "middle";
var i = 0, j = 0;
var deltaR = 0;
for (j = 0; j < 10 && (j < 1 || deltaR > 0) ; j++) {
//console.log(j);
outerRadius -= deltaR;
//indexLabelRadius -= deltaR + deltaR;
deltaR = 0;
if (dataSeries.indexLabelPlacement !== "inside") {
indexLabelRadius = outerRadius * indexLabelRadiusToRadiusRatio;
for (i = 0; i < dataPoints.length; i++) {
var dataPointEO = dataPointEOs[i];
dataPointEO.indexLabelTextBlock.x = center.x + indexLabelRadius * Math.cos(dataPointEO.midAngle);
dataPointEO.indexLabelTextBlock.y = center.y + indexLabelRadius * Math.sin(dataPointEO.midAngle);
dataPointEO.indexLabelAngle = dataPointEO.midAngle;
dataPointEO.radius = outerRadius;
//dataPointEO.indexLabelFontSize = dataPoint.indexLabelFontSize ? dataPoint.indexLabelFontSize : dataSeries.indexLabelFontSize;
}
var currentDataPoint, nextDataPoint;
for (i = 0; i < dataPoints.length; i++) {
var dataPointEO = dataPointEOs[i];
//dataPointEO.lab
//resetAnimationFrame();
//animate();
//renderLabels();
//var prevDataPointIndex = (i - 1 + dataPointEOs.length) % dataPointEOs.length;
//var nextDataPointIndex = (i + 1 + dataPointEOs.length) % dataPointEOs.length;
//nextDataPointIndex = dataPointEOs[nextDataPointIndex].hemisphere === dataPointEO.hemisphere && nextDataPointIndex !== i ? nextDataPointIndex : null;
var nextDataPointIndex = getNextLabelIndex(i);
if (nextDataPointIndex === null)
continue;
currentDataPoint = dataPointEOs[i];
nextDataPoint = dataPointEOs[nextDataPointIndex];
var distanceFromNextLabel = 0;
//if (dataPointEO.hemisphere === "right")
// distanceFromNextLabel = (nextDataPoint.indexLabelTextBlock.y - nextDataPoint.indexLabelTextBlock.height / 2) - (currentDataPoint.indexLabelTextBlock.y + currentDataPoint.indexLabelTextBlock.height / 2) - minDistanceBetweenLabels;
//else
// distanceFromNextLabel = (currentDataPoint.indexLabelTextBlock.y - currentDataPoint.indexLabelTextBlock.height / 2) - (nextDataPoint.indexLabelTextBlock.y + nextDataPoint.indexLabelTextBlock.height / 2) - minDistanceBetweenLabels;
distanceFromNextLabel = getVerticalDistanceBetweenLabels(currentDataPoint, nextDataPoint) - minDistanceBetweenLabels;
if (distanceFromNextLabel < 0) {
var dataPointsAbove = 0;
var dataPointsBelow = 0;
//var indexLabelAngleWhenTangent = Math.acos(outerRadius / indexLabelRadius) / Math.PI * 180;
for (var k = 0; k < dataPoints.length; k++) {
if (k === i)
continue;
//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].midAngle - dataPointEO.midAngle) > 30)
// continue;
//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].labelAngle - dataPointEO.indexLabelAngle) > 30)
// continue;
//if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere || Math.abs(dataPointEOs[k].midAngle - dataPointEO.midAngle) > indexLabelAngleWhenTangent)
// continue;
if (dataPointEOs[k].hemisphere !== dataPointEO.hemisphere)
continue;
if (dataPointEOs[k].indexLabelTextBlock.y < dataPointEO.indexLabelTextBlock.y)
dataPointsAbove++;
else
dataPointsBelow++;
}
//var upWardsOffset = (distanceFromNextLabel) / dataPoints.length * (dataPointsBelow);
var upWardsOffset = (distanceFromNextLabel) / (dataPointsAbove + dataPointsBelow || 1) * (dataPointsBelow);
var downWardsOffset = -1 * (distanceFromNextLabel - upWardsOffset);
var actualUpwardOffset = 0;
var actualDownwardOffset = 0;
if (dataPointEO.hemisphere === "right") {
actualUpwardOffset = rePositionLabels(i, upWardsOffset);
//if (i < 4 && actualDownwardOffset !== upWardsOffset)
// customPrompt(i + "; " + upWardsOffset.toFixed(2) + "; " + actualUpwardOffset.toFixed(2));
downWardsOffset = -1 * (distanceFromNextLabel - actualUpwardOffset);
actualDownwardOffset = rePositionLabels(nextDataPointIndex, downWardsOffset);
//window.alert(typeof +downWardsOffset.toFixed(precision));
//Setting precision to make sure that they don't become not equal become of minor differences - like a difference of .000001
if (+actualDownwardOffset.toFixed(precision) < +downWardsOffset.toFixed(precision) && +actualUpwardOffset.toFixed(precision) <= +upWardsOffset.toFixed(precision))
rePositionLabels(i, -(downWardsOffset - actualDownwardOffset));
} else {
actualUpwardOffset = rePositionLabels(nextDataPointIndex, upWardsOffset);
downWardsOffset = -1 * (distanceFromNextLabel - actualUpwardOffset);
actualDownwardOffset = rePositionLabels(i, downWardsOffset);
//Setting precision to make sure that they don't become not equal become of minor differences - like a difference of .000001
if (+actualDownwardOffset.toFixed(precision) < +downWardsOffset.toFixed(precision) && +actualUpwardOffset.toFixed(precision) <= +upWardsOffset.toFixed(precision))
rePositionLabels(nextDataPointIndex, -(downWardsOffset - actualDownwardOffset));
}
}
//resetAnimationFrame();
//animate();
//renderLabels();
//window.alert("next??");
}
} else {
for (i = 0; i < dataPoints.length; i++) {
var dataPointEO = dataPointEOs[i];
indexLabelRadius = dataSeries.type === "pie" ? outerRadius * .7 : outerRadius * .8;
var dx = center.x + indexLabelRadius * (Math.cos((dataPointEO.midAngle)));
var dy = center.y + indexLabelRadius * (Math.sin((dataPointEO.midAngle)));
dataPointEO.indexLabelTextBlock.x = dx;
dataPointEO.indexLabelTextBlock.y = dy;
}
}
// Resize Pie based on the label length.
for (i = 0; i < dataPoints.length; i++) {
dataPointEO = dataPointEOs[i];
var size = dataPointEO.indexLabelTextBlock.measureText();
// To make sure that null text or empty strings don't affect the radius. Required when user is not showing any labels
if (size.height === 0 || size.width === 0)
continue;
var xOverFlow = 0;
var xdr = 0;
if (dataPointEO.hemisphere === "right") {
xOverFlow = plotArea.x2 - (dataPointEO.indexLabelTextBlock.x + dataPointEO.indexLabelTextBlock.width + indexLabelLineEdgeLength);
xOverFlow *= -1;
} else {
xOverFlow = plotArea.x1 - (dataPointEO.indexLabelTextBlock.x - dataPointEO.indexLabelTextBlock.width - indexLabelLineEdgeLength);
}
if (xOverFlow > 0) {
if (Math.abs(dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 - center.y) < outerRadius
|| Math.abs(dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 - center.y) < outerRadius) {
xdr = xOverFlow / Math.abs(Math.cos(dataPointEO.indexLabelAngle));
if (xdr > 9)
xdr = xdr * .3;
if (xdr > deltaR)
deltaR = xdr;
} else {
}
}
var yOverFlow = 0;
var ydr = 0;
if (dataPointEO.indexLabelAngle > 0 && dataPointEO.indexLabelAngle < Math.PI) {
yOverFlow = plotArea.y2 - (dataPointEO.indexLabelTextBlock.y + dataPointEO.indexLabelTextBlock.height / 2 + 5);
yOverFlow *= -1;
} else {
yOverFlow = plotArea.y1 - (dataPointEO.indexLabelTextBlock.y - dataPointEO.indexLabelTextBlock.height / 2 - 5);
}
if (yOverFlow > 0) {
if (Math.abs(dataPointEO.indexLabelTextBlock.x - center.x) < outerRadius) {
ydr = yOverFlow / Math.abs(Math.sin(dataPointEO.indexLabelAngle));
if (ydr > 9)
ydr = ydr * .3;
if (ydr > deltaR)
deltaR = ydr;
} else {
}
} else {
//if (i < 4)
// customPrompt(i + "; " + center.y + "; " + dataPointEO.indexLabelTextBlock.y.toFixed(2));
}
}
function removeLabelsForSmallSegments(totalOverlap, startIndex, endIndex) {
//return;
var dpEOs = [];
var totalRemovedLabelHeight = 0;
for (var i = startIndex; true; i = (i + 1 + dataPoints.length) % dataPoints.length) {
dpEOs.push(dataPointEOs[i]);
if (i === endIndex)
break;
}
dpEOs.sort(function (entry1, entry2) {
return entry1.y - entry2.y;
});
for (i = 0; i < dpEOs.length; i++) {
var dpEO = dpEOs[i];
if (totalRemovedLabelHeight < totalOverlap) {
totalRemovedLabelHeight += dpEO.indexLabelTextBlock.height;
dpEO.indexLabelTextBlock.text = "";
dpEO.indexLabelText = "";
dpEO.indexLabelTextBlock.measureText();
} else
break;
}
}
//resetAnimationFrame(1);
//animate();
//window.alert("next??");
var overlapStartIndex = -1;
var overlapEndIndex = -1;
var totalOverlap = 0;
for (var k = 0; k < dataPoints.length; k++) {
currentDataPoint = dataPointEOs[k];
if (!currentDataPoint.indexLabelText)
continue;
var nextLabelIndex = getNextLabelIndex(k);
if (nextLabelIndex === null)
continue;
var nextDataPoint = dataPointEOs[nextLabelIndex];
distanceFromNextLabel = 0;
//if (nextDataPoint.indexLabelTextBlock.y > currentDataPoint.indexLabelTextBlock.y)
// distanceFromNextLabel = (nextDataPoint.indexLabelTextBlock.y - (nextDataPoint.indexLabelTextBlock.height / 2)) - (currentDataPoint.indexLabelTextBlock.y + (currentDataPoint.indexLabelTextBlock.height / 2));
//else
// distanceFromNextLabel = (currentDataPoint.indexLabelTextBlock.y - (currentDataPoint.indexLabelTextBlock.height / 2)) - (nextDataPoint.indexLabelTextBlock.y + (nextDataPoint.indexLabelTextBlock.height / 2));
distanceFromNextLabel = getVerticalDistanceBetweenLabels(currentDataPoint, nextDataPoint);
if (distanceFromNextLabel < 0 && areDataPointsTooClose(currentDataPoint, nextDataPoint)) {
if (overlapStartIndex < 0)
overlapStartIndex = k;
if (nextLabelIndex !== overlapStartIndex)
overlapEndIndex = nextLabelIndex;
totalOverlap += -distanceFromNextLabel;
//nextDataPoint.indexLabelText = "";
//nextDataPoint.indexLabelTextBlock.text = "";
//nextDataPoint.indexLabelTextBlock.measureText();
} else {
if (totalOverlap > 0) {
removeLabelsForSmallSegments(totalOverlap, overlapStartIndex, overlapEndIndex);
overlapStartIndex = -1;
overlapEndIndex = -1;
totalOverlap = 0;
}
}
}
if (totalOverlap > 0)
removeLabelsForSmallSegments(totalOverlap, overlapStartIndex, overlapEndIndex);
}
//window.alert("next??");
//resetAnimationFrame(_this.animationEnabled && _this.renderCount === 0 ? isCanvasSupported ? 60 : 30 : 1);
//animate();
//console.log("totalRecursions: " + totalRecursions);
}
this.pieDoughnutClickHandler = function (e) {
if (_this.isAnimating) {
return;
}
var i = e.dataPointIndex;
var dataPoint = e.dataPoint;
var dataSeries = this;
var id = dataSeries.dataPointIds[i];
//dataPointEO = _this._eventManager.objectMap[id];
if (dataPoint.exploded)
dataPoint.exploded = false;
else
dataPoint.exploded = true;
// So that it doesn't try to explode when there is only one segment
if (dataSeries.dataPoints.length > 1) {
_this._animator.animate(0, explodeDuration, function (fractionComplete) {
explodeToggle(fractionComplete);
renderChartElementsInPlotArea();
//console.log("Explode Start");
});
}
return;
}
initLabels();
positionLabels();
this.disableToolTip = true;
this._animator.animate(0, this.animatedRender ? this.animationDuration : 0, function (fractionComplete) {
animate(fractionComplete);
renderChartElementsInPlotArea();
}, function () {
_this.disableToolTip = false;
_this._animator.animate(0, _this.animatedRender ? explodeDuration : 0, function (fractionComplete) {
explodeToggle(fractionComplete);
renderChartElementsInPlotArea();
});
//console.log("Animation Complete");
});
function renderChartElementsInPlotArea() {
_this.plotArea.layoutManager.reset();
if (_this._title) {
if (_this._title.dockInsidePlotArea || (_this._title.horizontalAlign === "center" && _this._title.verticalAlign === "center"))
_this._title.render();
}
if (_this.subtitles)
for (var i = 0; i < _this.subtitles.length; i++) {
var subtitle = _this.subtitles[i];
if (subtitle.dockInsidePlotArea || (subtitle.horizontalAlign === "center" && subtitle.verticalAlign === "center"))
subtitle.render();
}
if (_this.legend) {
if (_this.legend.dockInsidePlotArea || (_this.legend.horizontalAlign === "center" && _this.legend.verticalAlign === "center"))
_this.legend.render();
}
}
//this.ctx.strokeRect(plotArea.x1 + 1, plotArea.y1, plotArea.width - 2, plotArea.height);
}
//#endregion pieChart
//#endregion Render Methods
Chart.prototype.animationRequestId = null;
Chart.prototype.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
Chart.prototype.cancelRequestAnimFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
clearTimeout
})();
//#endregion Class Chart
//#region Class LayoutManager
function LayoutManager(x1, y1, x2, y2, padding) {
if (typeof (padding) === "undefined")
padding = 0;
this._padding = padding;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
this._topOccupied = this._padding;
this._bottomOccupied = this._padding;
this._leftOccupied = this._padding;
this._rightOccupied = this._padding;
}
LayoutManager.prototype.registerSpace = function (position, size) {
if (position === "top") {
this._topOccupied += size.height;
}
else if (position === "bottom") {
this._bottomOccupied += size.height;
} else if (position === "left") {
this._leftOccupied += size.width; // this is width when seen upright/vertically
} else if (position === "right") {
this._rightOccupied += size.width;// this is width when seen upright/vertically
}
}
LayoutManager.prototype.unRegisterSpace = function (position, size) {
if (position === "top") {
this._topOccupied -= size.height;
}
else if (position === "bottom") {
this._bottomOccupied -= size.height;
} else if (position === "left") {
this._leftOccupied -= size.width;// this is width when seen upright/vertically
} else if (position === "right") {
this._rightOccupied -= size.width;// this is width when seen upright/vertically
}
}
LayoutManager.prototype.getFreeSpace = function () {
///
///Returns available free space {x1:number, y1:number, x2:number, y2:number}
///
return {
x1: this._x1 + this._leftOccupied,
y1: this._y1 + this._topOccupied,
x2: this._x2 - this._rightOccupied,
y2: this._y2 - this._bottomOccupied,
width: (this._x2 - this._x1) - this._rightOccupied - this._leftOccupied,
height: (this._y2 - this._y1) - this._bottomOccupied - this._topOccupied
};
}
LayoutManager.prototype.reset = function () {
//so that there is enough padding.
this._topOccupied = this._padding;
this._bottomOccupied = this._padding;
this._leftOccupied = this._padding;
this._rightOccupied = this._padding;
}
//#endregion Class LayoutManager
//#region Class TextBlock
function TextBlock(ctx, options) {
TextBlock.base.constructor.call(this, "TextBlock", options);
this.ctx = ctx;
this._isDirty = true;
this._wrappedText = null;
this._lineHeight = getFontHeightInPixels(this.fontFamily, this.fontSize, this.fontWeight);
}
extend(TextBlock, CanvasJSObject);
TextBlock.prototype.render = function (preserveContext) {
if (preserveContext)
this.ctx.save();
var font = this.ctx.font;
this.ctx.textBaseline = this.textBaseline;
var offsetY = 0;
if (this._isDirty)
this.measureText(this.ctx);
this.ctx.translate(this.x, this.y + offsetY);
if (this.textBaseline === "middle") {
offsetY = -this._lineHeight / 2;
}
this.ctx.font = this._getFontString();
this.ctx.rotate(Math.PI / 180 * this.angle);
var textLeft = 0;
var textTop = this.padding;
//var textTop = this.padding;
var line = null;
if ((this.borderThickness > 0 && this.borderColor) || this.backgroundColor) {
this.ctx.roundRect(0, offsetY, this.width, this.height, this.cornerRadius, this.borderThickness, this.backgroundColor, this.borderColor);
//if (this.textBaseline === "middle") {
// //textTop += this.fontSize / 2;
// textTop += this._lineHeight / 2;
//}
}
this.ctx.fillStyle = this.fontColor;
for (var i = 0; i < this._wrappedText.lines.length; i++) {
line = this._wrappedText.lines[i];
if (this.horizontalAlign === "right")
textLeft = this.width - line.width - this.padding;
else if (this.horizontalAlign === "left")
textLeft = this.padding;
else if (this.horizontalAlign === "center")
textLeft = (this.width - this.padding * 2) / 2 - line.width / 2 + this.padding;
this.ctx.fillText(line.text, textLeft, textTop);
textTop += line.height;
}
this.ctx.font = font;
if (preserveContext)
this.ctx.restore();
}
TextBlock.prototype.setText = function (text) {
this.text = text;
this._isDirty = true;
this._wrappedText = null;
}
TextBlock.prototype.measureText = function () {
if (this.maxWidth === null) {
throw ("Please set maxWidth and height for TextBlock");
}
this._wrapText(this.ctx);
this._isDirty = false;
return { width: this.width, height: this.height }
}
TextBlock.prototype._getLineWithWidth = function (text, width, clipWord) {
text = String(text);
clipWord = clipWord || false;
if (!text)
return { text: "", width: 0 };
var textWidth = 0,
min = 0,
max = text.length - 1,
mid = Infinity;
this.ctx.font = this._getFontString();
while (min <= max) {
mid = Math.floor((min + max) / 2);
var tempText = text.substr(0, mid + 1);
textWidth = this.ctx.measureText(tempText).width;
if (textWidth < width) {
min = mid + 1;
} else if (textWidth > width) {
max = mid - 1;
} else {
break;
}
}
//edge cases
if (textWidth > width && tempText.length > 1) {
tempText = tempText.substr(0, tempText.length - 1);
textWidth = this.ctx.measureText(tempText).width;
}
var isClipped = true;
if (tempText.length === text.length || text[tempText.length] === " ")
isClipped = false;
if (isClipped) {
var resultWords = tempText.split(" ");
if (resultWords.length > 1)
resultWords.pop();
tempText = resultWords.join(" ");
textWidth = this.ctx.measureText(tempText).width;
}
return { text: tempText, width: textWidth };
}
TextBlock.prototype._wrapText = function wrapText() {
//this.ctx.save();
var text = new String(trimString(String(this.text)));
var lines = [];
var font = this.ctx.font; // Save the current Font
var height = 0;
var width = 0;
this.ctx.font = this._getFontString();
while (text.length > 0) {
var maxWidth = this.maxWidth - this.padding * 2;
var maxHeight = this.maxHeight - this.padding * 2;
var line = this._getLineWithWidth(text, maxWidth, false);
line.height = this._lineHeight;
lines.push(line);
width = Math.max(width, line.width);
height += line.height;
text = trimString(text.slice(line.text.length, text.length));
if (maxHeight && height > maxHeight) {
var line = lines.pop();
height -= line.height;
}
}
this._wrappedText = { lines: lines, width: width, height: height };
this.width = width + this.padding * 2;
this.height = height + this.padding * 2;
this.ctx.font = font; // Restore the font
}
TextBlock.prototype._getFontString = function () {
//return this.fontStyle + " " + this.fontWeight + " " + this.fontSize + "px " + this.fontFamily
return getFontString("", this, null);
}
//#endregion Class TextBlock
//#region Class Title
function Title(chart, options) {
Title.base.constructor.call(this, "Title", options, chart.theme);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = this.chart.ctx;
if (typeof (this._options.fontSize) === "undefined") {
this.fontSize = this.chart.getAutoFontSize(this.fontSize);
//window.console.log("Chart Title fontSize: " + this.fontSize);
}
this.width = null,//read only
this.height = null//read only
this.bounds = { x1: null, y1: null, x2: null, y2: null };
}
extend(Title, CanvasJSObject);
Title.prototype.render = function () {
if (!this.text)
return;
var container = (!this.dockInsidePlotArea ? this.chart : this.chart.plotArea);
var freespace = container.layoutManager.getFreeSpace();
var left = freespace.x1;
var top = freespace.y1;
var angle = 0;
var maxHeight = 0;
var containerMargin = 2; //Margin towards the container
var rightOffset = this.chart._menuButton && this.chart.exportEnabled && this.verticalAlign === "top" ? 22 : 0; //So that Title doesn't overlap menu button.
var textBlockHorizontalAlign;
var position;
if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
if (this.maxWidth === null)
this.maxWidth = freespace.width - containerMargin * 2 - rightOffset * (this.horizontalAlign === "center" ? 2 : 1);
maxHeight = freespace.height * .5 - this.margin - containerMargin;
angle = 0;
}
else if (this.verticalAlign === "center") {
if (this.horizontalAlign === "left" || this.horizontalAlign === "right") {
if (this.maxWidth === null)
this.maxWidth = freespace.height - containerMargin * 2;
maxHeight = freespace.width * .5 - this.margin - containerMargin;
} else if (this.horizontalAlign === "center") {
if (this.maxWidth === null)
this.maxWidth = freespace.width - containerMargin * 2;
maxHeight = freespace.height * .5 - containerMargin * 2;
}
}
if (!this.wrap)
maxHeight = Math.min(maxHeight, Math.max(this.fontSize * 1.5, this.fontSize + this.padding * 2.5));
//console.log(this.maxWidth);
var textBlock = new TextBlock(this.ctx, {
fontSize: this.fontSize, fontFamily: this.fontFamily, fontColor: this.fontColor,
fontStyle: this.fontStyle, fontWeight: this.fontWeight,
horizontalAlign: this.horizontalAlign, verticalAlign: this.verticalAlign,
borderColor: this.borderColor, borderThickness: this.borderThickness,
backgroundColor: this.backgroundColor,
maxWidth: this.maxWidth, maxHeight: maxHeight,
cornerRadius: this.cornerRadius,
text: this.text,
padding: this.padding,
textBaseline: "top"
});
var textBlockSize = textBlock.measureText();
if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
if (this.verticalAlign === "top") {
top = freespace.y1 + containerMargin;
position = "top";
}
else if (this.verticalAlign === "bottom") {
top = freespace.y2 - containerMargin - textBlockSize.height;
position = "bottom";
}
if (this.horizontalAlign === "left") {
left = freespace.x1 + containerMargin;
}
else if (this.horizontalAlign === "center") {
left = freespace.x1 + freespace.width / 2 - textBlockSize.width / 2;
}
else if (this.horizontalAlign === "right") {
left = freespace.x2 - containerMargin - textBlockSize.width - rightOffset;
}
textBlockHorizontalAlign = this.horizontalAlign;
this.width = textBlockSize.width;
this.height = textBlockSize.height;
}
else if (this.verticalAlign === "center") {
if (this.horizontalAlign === "left") {
left = freespace.x1 + containerMargin;
top = freespace.y2 - containerMargin - (this.maxWidth / 2 - textBlockSize.width / 2);
angle = -90;
position = "left";
this.width = textBlockSize.height;
this.height = textBlockSize.width;
}
else if (this.horizontalAlign === "right") {
left = freespace.x2 - containerMargin;
top = freespace.y1 + containerMargin + (this.maxWidth / 2 - textBlockSize.width / 2);
angle = 90;
position = "right";
this.width = textBlockSize.height;
this.height = textBlockSize.width;
}
else if (this.horizontalAlign === "center") {
top = container.y1 + (container.height / 2 - textBlockSize.height / 2);
left = container.x1 + (container.width / 2 - textBlockSize.width / 2);
position = "center";
this.width = textBlockSize.width;
this.height = textBlockSize.height;
}
textBlockHorizontalAlign = "center";
}
textBlock.x = left;
textBlock.y = top;
textBlock.angle = angle;
textBlock.horizontalAlign = textBlockHorizontalAlign;
textBlock.render(true);
container.layoutManager.registerSpace(position, {
width: this.width + (position === "left" || position === "right" ? this.margin + containerMargin : 0),
height: this.height + (position === "top" || position === "bottom" ? this.margin + containerMargin : 0)
});
this.bounds = { x1: left, y1: top, x2: left + this.width, y2: top + this.height };
this.ctx.textBaseline = "top";
}
//#endregion Class Title
//#region Class SubTitle
function Subtitle(chart, options) {
Subtitle.base.constructor.call(this, "Subtitle", options, chart.theme);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = this.chart.ctx;
if (typeof (this._options.fontSize) === "undefined") {
this.fontSize = this.chart.getAutoFontSize(this.fontSize);
//window.console.log("Chart Title fontSize: " + this.fontSize);
}
this.width = null,//read only
this.height = null//read only
this.bounds = { x1: null, y1: null, x2: null, y2: null };
}
extend(Subtitle, CanvasJSObject);
Subtitle.prototype.render = Title.prototype.render;
//#endregion Class SubTitle
//#region Legend
//TBI: Implement Markes for Legend
function Legend(chart, options, theme) {
Legend.base.constructor.call(this, "Legend", options, theme);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = this.chart.ctx;
this.ghostCtx = this.chart._eventManager.ghostCtx;
this.items = [];
this.width = 0,
//this.fontSize = 12,
this.height = 0,
this.orientation = null,
this.dataSeries = [];
this.bounds = { x1: null, y1: null, x2: null, y2: null };
if (typeof (this._options.fontSize) === "undefined") {
this.fontSize = this.chart.getAutoFontSize(this.fontSize);
//window.console.log("fontSize: " + this.fontSize);
}
this.lineHeight = getFontHeightInPixels(this.fontFamily, this.fontSize, this.fontWeight);
this.horizontalSpacing = this.fontSize;
}
extend(Legend, CanvasJSObject);
Legend.prototype.render = function () {
var container = (!this.dockInsidePlotArea ? this.chart : this.chart.plotArea);
var freeSpace = container.layoutManager.getFreeSpace();
var position = null;
var top = 0;
var left = 0;
var maxWidth = 0;
var maxHeight = 0;
var itemMargin = 5;
var items = [];
var rows = [];
//this.ctx.font = getFontString("", this, null);
//this.ctx.fontColor = this.fontColor;
if (this.verticalAlign === "top" || this.verticalAlign === "bottom") {
this.orientation = "horizontal";
position = this.verticalAlign;
maxWidth = this.maxWidth !== null ? this.maxWidth : freeSpace.width * .7;
maxHeight = this.maxHeight !== null ? this.maxHeight : freeSpace.height * .5;
}
else if (this.verticalAlign === "center") {
this.orientation = "vertical";
position = this.horizontalAlign;
maxWidth = this.maxWidth !== null ? this.maxWidth : freeSpace.width * .5;
maxHeight = this.maxHeight !== null ? this.maxHeight : freeSpace.height * .7;
}
for (var i = 0; i < this.dataSeries.length; i++) {
var dataSeries = this.dataSeries[i];
if (dataSeries.type !== "pie" && dataSeries.type !== "doughnut" && dataSeries.type !== "funnel") {
var markerType = dataSeries.legendMarkerType ? dataSeries.legendMarkerType : (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "scatter" || dataSeries.type === "bubble") && dataSeries.markerType ? dataSeries.markerType : DataSeries.getDefaultLegendMarker(dataSeries.type);
var legendText = dataSeries.legendText ? dataSeries.legendText : this.itemTextFormatter ? this.itemTextFormatter({ chart: this.chart, legend: this._options, dataSeries: dataSeries, dataPoint: null })
: dataSeries.name;
var markerColor = dataSeries.legendMarkerColor ? dataSeries.legendMarkerColor : dataSeries.markerColor ? dataSeries.markerColor : dataSeries._colorSet[0];
var markerSize = (!dataSeries.markerSize && (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline")) ? 0 : this.lineHeight * .6;
var markerBorderColor = dataSeries.legendMarkerBorderColor ? dataSeries.legendMarkerBorderColor : dataSeries.markerBorderColor;
var markerBorderThickness = dataSeries.legendMarkerBorderThickness ? dataSeries.legendMarkerBorderThickness : dataSeries.markerBorderThickness ? Math.max(1, Math.round(markerSize * .2)) : 0;
var lineColor = dataSeries._colorSet[0];
legendText = this.chart.replaceKeywordsWithValue(legendText, dataSeries.dataPoints[0], dataSeries, i);
var item = {
markerType: markerType, markerColor: markerColor, text: legendText, textBlock: null, chartType: dataSeries.type, markerSize: markerSize, lineColor: dataSeries._colorSet[0],
dataSeriesIndex: dataSeries.index, dataPointIndex: null, markerBorderColor: markerBorderColor, markerBorderThickness: markerBorderThickness
};
items.push(item);
} else {
for (var dataPointIndex = 0; dataPointIndex < dataSeries.dataPoints.length; dataPointIndex++) {
var dataPoint = dataSeries.dataPoints[dataPointIndex];
var markerType = dataPoint.legendMarkerType ? dataPoint.legendMarkerType : dataSeries.legendMarkerType ? dataSeries.legendMarkerType : DataSeries.getDefaultLegendMarker(dataSeries.type);
var legendText = dataPoint.legendText ? dataPoint.legendText : dataSeries.legendText ? dataSeries.legendText : this.itemTextFormatter ? this.itemTextFormatter({ chart: this.chart, legend: this._options, dataSeries: dataSeries, dataPoint: dataPoint })
: dataPoint.name ? dataPoint.name : "DataPoint: " + (dataPointIndex + 1);
var markerColor = dataPoint.legendMarkerColor ? dataPoint.legendMarkerColor : dataSeries.legendMarkerColor ? dataSeries.legendMarkerColor : dataPoint.color ? dataPoint.color : dataSeries.color ? dataSeries.color : dataSeries._colorSet[dataPointIndex % dataSeries._colorSet.length];
var markerSize = this.lineHeight * .6;
var markerBorderColor = dataPoint.legendMarkerBorderColor ? dataPoint.legendMarkerBorderColor : dataSeries.legendMarkerBorderColor ? dataSeries.legendMarkerBorderColor : dataPoint.markerBorderColor ? dataPoint.markerBorderColor : dataSeries.markerBorderColor;
var markerBorderThickness = dataPoint.legendMarkerBorderThickness ? dataPoint.legendMarkerBorderThickness : dataSeries.legendMarkerBorderThickness ? dataSeries.legendMarkerBorderThickness : dataPoint.markerBorderThickness || dataSeries.markerBorderThickness ? Math.max(1, Math.round(markerSize * .2)) : 0;
legendText = this.chart.replaceKeywordsWithValue(legendText, dataPoint, dataSeries, dataPointIndex);
var item = {
markerType: markerType, markerColor: markerColor, text: legendText, textBlock: null, chartType: dataSeries.type, markerSize: markerSize,
dataSeriesIndex: i, dataPointIndex: dataPointIndex, markerBorderColor: markerBorderColor, markerBorderThickness: markerBorderThickness
};
if (dataPoint.showInLegend || (dataSeries.showInLegend && dataPoint.showInLegend !== false)) {
items.push(item);
}
}
}
item = null;
}
if (this.reversed === true) {
items.reverse();
}
// Find out the required width and height of Legend and position the items relative to the container
if (items.length > 0) {
var row = null;
var rowIndex = 0; // required for vertical orientation
var textMaxWidth = 0;
var columnHeight = 0;
if (this.itemWidth !== null) {
if (this.itemMaxWidth !== null) {
textMaxWidth = Math.min(this.itemWidth, this.itemMaxWidth, maxWidth);
} else {
textMaxWidth = Math.min(this.itemWidth, maxWidth);
}
} else {
if (this.itemMaxWidth !== null) {
textMaxWidth = Math.min(this.itemMaxWidth, maxWidth);
} else {
textMaxWidth = maxWidth;
}
}
markerSize = (markerSize === 0 ? this.lineHeight * .6 : markerSize);
textMaxWidth = textMaxWidth - (markerSize + this.horizontalSpacing * .1);
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") {
textMaxWidth = textMaxWidth - 2 * (this.lineHeight * .1);
}
if (maxHeight <= 0 || typeof (maxHeight) === "undefined" || textMaxWidth <= 0 || typeof (textMaxWidth) === "undefined") {
continue;
}
if (this.orientation === "horizontal") {
item.textBlock = new TextBlock(this.ctx, {
x: 0,
y: 0,//TBI
maxWidth: textMaxWidth,
maxHeight: this.itemWrap ? maxHeight : this.lineHeight, //TBI: FontSize
angle: 0,
text: item.text,
horizontalAlign: "left",//left, center, right
fontSize: this.fontSize,//in pixels
fontFamily: this.fontFamily,
fontWeight: this.fontWeight, //normal, bold, bolder, lighter,
fontColor: this.fontColor,
fontStyle: this.fontStyle, // normal, italic, oblique
textBaseline: "top"
});
item.textBlock.measureText();
if (this.itemWidth !== null) {
item.textBlock.width = this.itemWidth - (markerSize + this.horizontalSpacing * .1 + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
}
if (!row || row.width + Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : (this.horizontalSpacing)) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0)) > maxWidth) {
row = { items: [], width: 0 };
rows.push(row);
this.height += columnHeight;
columnHeight = 0;
}
columnHeight = Math.max(columnHeight, item.textBlock.height);
item.textBlock.x = row.width;
item.textBlock.y = 0;
row.width += Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : this.horizontalSpacing) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
row.items.push(item);
this.width = Math.max(row.width, this.width);
} else {
item.textBlock = new TextBlock(this.ctx, {
x: 0,
y: 0,//TBI
maxWidth: textMaxWidth,
maxHeight: this.itemWrap === true ? maxHeight : this.fontSize * 1.5, //TBI: FontSize
angle: 0,
text: item.text,
horizontalAlign: "left",//left, center, right
fontSize: this.fontSize,//in pixels
fontFamily: this.fontFamily,
fontWeight: this.fontWeight, //normal, bold, bolder, lighter,
fontColor: this.fontColor,
fontStyle: this.fontStyle, // normal, italic, oblique
textBaseline: "top"
});
item.textBlock.measureText();
if (this.itemWidth !== null) {
item.textBlock.width = this.itemWidth - (markerSize + this.horizontalSpacing * .1 + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
}
if (this.height <= maxHeight) {
row = { items: [], width: 0 };
rows.push(row);
} else {
row = rows[rowIndex];
rowIndex = (rowIndex + 1) % rows.length;
}
this.height += item.textBlock.height;
item.textBlock.x = row.width; // relative to the row
item.textBlock.y = 0; // relative to the row
row.width += Math.round(item.textBlock.width + this.horizontalSpacing * .1 + markerSize + (row.width === 0 ? 0 : this.horizontalSpacing) + ((item.chartType === "line" || item.chartType === "spline" || item.chartType === "stepLine") ? 2 * (this.lineHeight * .1) : 0));
row.items.push(item);
this.width = Math.max(row.width, this.width);
}
}
if (this.itemWrap === false) {
this.height = rows.length * (this.lineHeight);
} else {
this.height += columnHeight;
}
this.height = Math.min(maxHeight, this.height);
this.width = Math.min(maxWidth, this.width);
}
if (this.verticalAlign === "top") {
if (this.horizontalAlign === "left")
left = freeSpace.x1;
else if (this.horizontalAlign === "right")
left = freeSpace.x2 - this.width;
else
left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
top = freeSpace.y1;
} else if (this.verticalAlign === "center") {
if (this.horizontalAlign === "left")
left = freeSpace.x1;
else if (this.horizontalAlign === "right")
left = freeSpace.x2 - this.width;
else
left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
top = freeSpace.y1 + freeSpace.height / 2 - this.height / 2;
} else if (this.verticalAlign === "bottom") {
if (this.horizontalAlign === "left")
left = freeSpace.x1;
else if (this.horizontalAlign === "right")
left = freeSpace.x2 - this.width;
else
left = freeSpace.x1 + freeSpace.width / 2 - this.width / 2;
top = freeSpace.y2 - this.height;
}
this.items = items;
//Assign ids to all legendItems
for (var i = 0; i < this.items.length; i++) {
var item = items[i];
item.id = ++this.chart._eventManager.lastObjectId;
this.chart._eventManager.objectMap[item.id] = {
id: item.id, objectType: "legendItem", legendItemIndex: i, dataSeriesIndex: item.dataSeriesIndex, dataPointIndex: item.dataPointIndex
};
//delete item.textBlock;// Not Required anymore
}
var rowHeight = 0;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
var columnHeight = 0;
for (var itemIndex = 0; itemIndex < row.items.length; itemIndex++) {
var item = row.items[itemIndex];
var itemX = item.textBlock.x + left + (itemIndex === 0 ? markerSize * .2 : this.horizontalSpacing);
var itemY = top + rowHeight;
var ghostX = itemX;
if (!this.chart.data[item.dataSeriesIndex].visible)
this.ctx.globalAlpha = .5;
this.ctx.save();
this.ctx.rect(left, top, maxWidth, maxHeight);
this.ctx.clip();
if (item.chartType === "line" || item.chartType === "stepLine" || item.chartType === "spline") {
this.ctx.strokeStyle = item.lineColor;
this.ctx.lineWidth = Math.ceil(this.lineHeight / 8);
this.ctx.beginPath();
this.ctx.moveTo(itemX - this.lineHeight * .1, itemY + this.lineHeight / 2);
this.ctx.lineTo(itemX + this.lineHeight * .7, itemY + this.lineHeight / 2);
this.ctx.stroke();
ghostX -= this.lineHeight * .1;
}
RenderHelper.drawMarker(itemX + markerSize / 2, itemY + (this.lineHeight / 2), this.ctx, item.markerType, item.markerSize, item.markerColor, item.markerBorderColor, item.markerBorderThickness);
item.textBlock.x = itemX + this.horizontalSpacing * .1 + markerSize;
if (item.chartType === "line" || item.chartType === "stepLine" || item.chartType === "spline") {
item.textBlock.x = item.textBlock.x + this.lineHeight * .1;
}
item.textBlock.y = itemY;
item.textBlock.render(true);
this.ctx.restore();
if (itemIndex > 0) {
columnHeight = Math.max(columnHeight, item.textBlock.height);
} else {
columnHeight = item.textBlock.height;
}
if (!this.chart.data[item.dataSeriesIndex].visible)
this.ctx.globalAlpha = 1;
var hexColor = intToHexColorString(item.id);
this.ghostCtx.fillStyle = hexColor;
this.ghostCtx.beginPath();
this.ghostCtx.fillRect(ghostX, item.textBlock.y, item.textBlock.x + item.textBlock.width - ghostX, item.textBlock.height);
item.x1 = this.chart._eventManager.objectMap[item.id].x1 = ghostX;
item.y1 = this.chart._eventManager.objectMap[item.id].y1 = item.textBlock.y;
item.x2 = this.chart._eventManager.objectMap[item.id].x2 = item.textBlock.x + item.textBlock.width;
item.y2 = this.chart._eventManager.objectMap[item.id].y2 = item.textBlock.y + item.textBlock.height;
}
rowHeight = rowHeight + columnHeight;
}
//this.ctx.beginPath();
//this.ctx.lineWidth = 2;
//this.ctx.strokeStyle = "red";
//this.ctx.rect(left, top, this.width, this.height);
//this.ctx.stroke();
container.layoutManager.registerSpace(position, { width: this.width + 2 + 2, height: this.height + 5 + 5 });
this.bounds = { x1: left, y1: top, x2: left + this.width, y2: top + this.height };
}
//#endregion Legend
//#region Class PlotArea
function PlotArea(chart, options) {
PlotArea.base.constructor.call(this, options);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = this.chart.ctx;
}
extend(PlotArea, CanvasJSObject);
PlotArea.prototype.render = function () {
var freeSpace = this.chart.layoutManager.getFreeSpace();
this.ctx.fillStyle = "red";
this.ctx.fillRect(freeSpace.x1, freeSpace.y1, freeSpace.x2, freeSpace.y2);
}
//#endregion Class PlotArea
//#region DataSeries
function DataSeries(chart, options, theme, index, id) {
DataSeries.base.constructor.call(this, "DataSeries", options, theme);
this.chart = chart;
this.canvas = chart.canvas;
this._ctx = chart.canvas.ctx;
this.index = index;
this.noDataPointsInPlotArea = 0;
//this.maxWidthInX = 0;
this.id = id;
this.chart._eventManager.objectMap[id] = { id: id, objectType: "dataSeries", dataSeriesIndex: index }
this.dataPointIds = [];
this.plotUnit = [];
this.axisX = null;
this.axisY = null;
if (this.fillOpacity === null) {
if (this.type.match(/area/i))
this.fillOpacity = .7;
else
this.fillOpacity = 1;
}
this.axisPlacement = this.getDefaultAxisPlacement();
if (typeof (this._options.indexLabelFontSize) === "undefined") {
this.indexLabelFontSize = this.chart.getAutoFontSize(this.indexLabelFontSize);
}
}
extend(DataSeries, CanvasJSObject);
//Static Method that returns the axisPlacement for a given ChartType. Returns one of "normal", "xySwapped", "none"
DataSeries.prototype.getDefaultAxisPlacement = function () {
//if (!this.visible)
// return "none";
//type = this.type.toLowerCase();
var type = this.type;
if (type === "column" || type === "line" || type === "stepLine" || type === "spline" || type === "area" || type === "stepArea" || type === "splineArea" || type === "stackedColumn" || type === "stackedLine" || type === "bubble" || type === "scatter"
|| type === "stackedArea" || type === "stackedColumn100" || type === "stackedLine100" || type === "stackedArea100"
|| type === "candlestick" || type === "ohlc" || type === "rangeColumn" || type === "rangeArea" || type === "rangeSplineArea") {
return "normal";
}
else if (type === "bar" || type === "stackedBar" || type === "stackedBar100" || type === "rangeBar") {
return "xySwapped";
}
else if (type === "pie" || type === "doughnut" || type === "funnel") {
return "none";
} else {
window.console.log("Unknown Chart Type: " + type);
return null;
}
}
DataSeries.getDefaultLegendMarker = function (type) {
//type = type.toLowerCase();
if (type === "column" || type === "stackedColumn" || type === "stackedLine" || type === "bar" || type === "stackedBar" || type === "stackedBar100"
|| type === "bubble" || type === "scatter"
|| type === "stackedColumn100" || type === "stackedLine100" || type === "stepArea"
|| type === "candlestick" || type === "ohlc" || type === "rangeColumn" || type === "rangeBar" || type === "rangeArea" || type === "rangeSplineArea") {
return "square";
}
else if (type === "line" || type === "stepLine" || type === "spline" || type === "pie" || type === "doughnut" || type === "funnel") {
return "circle";
} else if (type === "area" || type === "splineArea" || type === "stackedArea" || type === "stackedArea100") {
return "triangle"
} else {
window.console.log("Unknown Chart Type: " + type);
return null;
}
}
//Finds dataPoint with the given x value. If findClosest is set, finds dataPoint with closest x value.
//Returns searchResult object if found, else returns null
DataSeries.prototype.getDataPointAtX = function (x, findClosest) {
if (!this.dataPoints || this.dataPoints.length === 0) return null;
var searchResult = { dataPoint: null, distance: Infinity, index: NaN };
var dataPoint = null;
var j = 0;
var i = 0;
var direction = 1; // +1 for foward and -1 for backward.
var minimumXDistance = Infinity;
var forwardMissCount = 0, backwardMissCount = 0;
var maxMissCount = 1000;
var searchStartIndex = 0;
if (this.chart.plotInfo.axisPlacement !== "none") {
//var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
//if (xRange > 0)
// searchStartIndex = ((this.dataPoints.length - 1) / xRange * (x - this.dataPoints[0].x)) >> 0;
//else
// searchStartIndex = 0;
var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
if (xRange > 0)
searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (x - this.dataPoints[0].x)) >> 0, 0), this.dataPoints.length);
else
searchStartIndex = 0;
//searchStartIndex = ((this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x) / this.dataPoints.length * (x - this.dataPoints[0].x)) >> 0;
}
while (true) {
i = (direction > 0) ? searchStartIndex + j : searchStartIndex - j;
if (i >= 0 && i < this.dataPoints.length) {
dataPoint = this.dataPoints[i];
var distance = Math.abs(dataPoint.x - x);
if (distance < searchResult.distance) {
searchResult.dataPoint = dataPoint;
searchResult.distance = distance;
searchResult.index = i;
}
var xDistance = Math.abs(dataPoint.x - x);
if (xDistance <= minimumXDistance)
minimumXDistance = xDistance;
else {
if (direction > 0)
forwardMissCount++;
else
backwardMissCount++;
}
if (forwardMissCount > maxMissCount && backwardMissCount > maxMissCount)
break;
} else if (searchStartIndex - j < 0 && searchStartIndex + j >= this.dataPoints.length)
break;
if (direction === -1) {
j++;
direction = 1;
} else
direction = -1;
}
if (!findClosest && searchResult.dataPoint.x === x)
return searchResult;
else if (findClosest && searchResult.dataPoint !== null)
return searchResult;
else
return null;
}
// x & y should be in pixels. Can be used only after rendering the chart.
DataSeries.prototype.getDataPointAtXY = function (x, y, getClosest) {
if (!this.dataPoints || this.dataPoints.length === 0) return null;
getClosest = getClosest || false;
var results = [];
var j = 0, i = 0;
var direction = 1; // +1 for foward and -1 for backward.
var foundDataPoint = false;
var minimumXDistance = Infinity;
var forwardMissCount = 0, backwardMissCount = 0;
var maxMissCount = 1000;
var searchStartIndex = 0;
if (this.chart.plotInfo.axisPlacement !== "none") {
var xval = this.chart.axisX.getXValueAt({ x: x, y: y });
var xRange = (this.dataPoints[this.dataPoints.length - 1].x - this.dataPoints[0].x);
if (xRange > 0)
searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (xval - this.dataPoints[0].x)) >> 0, 0), this.dataPoints.length);
else
searchStartIndex = 0;
//var xRange = (this.axisX._absoluteMaximum - this.axisX._absoluteMinimum);
//if (xRange > 0)
// searchStartIndex = Math.min(Math.max(((this.dataPoints.length - 1) / xRange * (xval - this.axisX._absoluteMinimum)) >> 0, 0), this.dataPoints.length);
//else
// searchStartIndex = 0;
}
while (true) {
//i = searchStartIndex + (j * direction);
i = (direction > 0) ? searchStartIndex + j : searchStartIndex - j;
if (i >= 0 && i < this.dataPoints.length) {
var id = this.dataPointIds[i];
var visualInfo = this.chart._eventManager.objectMap[id];
var dataPoint = this.dataPoints[i];
var distance = null;
if (visualInfo) {
switch (this.type) {
case "column":
case "stackedColumn":
case "stackedColumn100":
case "bar":
case "stackedBar":
case "stackedBar100":
case "rangeColumn":
case "rangeBar":
if (x >= visualInfo.x1 && x <= visualInfo.x2 && y >= visualInfo.y1 && y <= visualInfo.y2) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y1 - y), Math.abs(visualInfo.y2 - y))
//distance:0
});
foundDataPoint = true;
}
break;
case "line":
case "stepLine":
case "spline":
case "area":
case "stepArea":
case "stackedArea":
case "stackedArea100":
case "splineArea":
case "scatter":
var markerSize = getProperty("markerSize", dataPoint, this) || 4;
var snapDistance = getClosest ? 20 : markerSize;
distance = Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2));
if (distance <= snapDistance) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: distance
});
}
var xDistance = Math.abs(visualInfo.x1 - x);
if (xDistance <= minimumXDistance)
minimumXDistance = xDistance;
else {
if (direction > 0)
forwardMissCount++;
else
backwardMissCount++;
}
if (distance <= markerSize / 2) {
foundDataPoint = true;
}
break;
case "rangeArea":
case "rangeSplineArea":
var markerSize = getProperty("markerSize", dataPoint, this) || 4;
var snapDistance = getClosest ? 20 : markerSize;
distance = Math.min(Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2)), Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y2 - y, 2)));
if (distance <= snapDistance) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: distance
});
}
var xDistance = Math.abs(visualInfo.x1 - x);
if (xDistance <= minimumXDistance)
minimumXDistance = xDistance;
else {
if (direction > 0)
forwardMissCount++;
else
backwardMissCount++;
}
if (distance <= markerSize / 2) {
foundDataPoint = true;
}
break;
case "bubble":
var markerSize = visualInfo.size;
distance = Math.sqrt(Math.pow(visualInfo.x1 - x, 2) + Math.pow(visualInfo.y1 - y, 2));
if (distance <= markerSize / 2) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: distance
});
foundDataPoint = true;
}
break;
case "pie":
case "doughnut":
var center = visualInfo.center;
var innerRadius = this.type === "doughnut" ? .6 * visualInfo.radius : 0;
distance = Math.sqrt(Math.pow(center.x - x, 2) + Math.pow(center.y - y, 2));
if (distance < visualInfo.radius && distance > innerRadius) {
var deltaY = y - center.y;
var deltaX = x - center.x;
var angle = Math.atan2(deltaY, deltaX);
if (angle < 0)
angle += Math.PI * 2;
angle = Number((((angle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
//console.log(angle);
var startAngle = Number((((visualInfo.startAngle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
var endAngle = Number((((visualInfo.endAngle / Math.PI * 180 % 360) + 360) % 360).toFixed(12));
//So that data point is detected when there is only one dataPoint
if (endAngle === 0 && visualInfo.endAngle > 1) {
endAngle = 360;
}
if (startAngle >= endAngle && dataPoint.y !== 0) {
endAngle += 360;
if (angle < startAngle)
angle += 360;
}
if (angle > startAngle && angle < endAngle) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: 0
});
foundDataPoint = true;
}
}
break;
case "candlestick":
if (((x >= (visualInfo.x1 - visualInfo.borderThickness / 2)) && (x <= (visualInfo.x2 + visualInfo.borderThickness / 2))
&& (y >= visualInfo.y2 - visualInfo.borderThickness / 2) && (y <= visualInfo.y3 + visualInfo.borderThickness / 2))
|| (Math.abs(visualInfo.x2 - x + visualInfo.x1 - x) < visualInfo.borderThickness && (y >= visualInfo.y1 && y <= visualInfo.y4))) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y2 - y), Math.abs(visualInfo.y3 - y))
//distance:0
});
foundDataPoint = true;
}
break;
case "ohlc":
if ((Math.abs(visualInfo.x2 - x + visualInfo.x1 - x) < visualInfo.borderThickness && (y >= visualInfo.y2 && y <= visualInfo.y3))
|| (x >= visualInfo.x1 && (x <= (visualInfo.x2 + visualInfo.x1) / 2)
&& (y >= visualInfo.y1 - visualInfo.borderThickness / 2) && (y <= visualInfo.y1 + visualInfo.borderThickness / 2))
|| ((x >= (visualInfo.x1 + visualInfo.x2) / 2) && (x <= visualInfo.x2)
&& (y >= visualInfo.y4 - visualInfo.borderThickness / 2) && (y <= visualInfo.y4 + visualInfo.borderThickness / 2))) {
results.push({
dataPoint: dataPoint,
dataPointIndex: i,
dataSeries: this,
distance: Math.min(Math.abs(visualInfo.x1 - x), Math.abs(visualInfo.x2 - x), Math.abs(visualInfo.y2 - y), Math.abs(visualInfo.y3 - y))
//distance:0
});
foundDataPoint = true;
}
break;
}
if (foundDataPoint || (forwardMissCount > maxMissCount && backwardMissCount > maxMissCount))
break;
}
} else if (searchStartIndex - j < 0 && searchStartIndex + j >= this.dataPoints.length)
break;
if (direction === -1) {
j++;
direction = 1;
} else
direction = -1;
}
var closestResult = null;
for (var m = 0; m < results.length; m++) {
if (!closestResult) {
closestResult = results[m];
} else if (results[m].distance <= closestResult.distance) {
closestResult = results[m];
}
}
//if (window.console)
// window.console.log("forwardMissCount: " + forwardMissCount + "; backwardMissCount: " + backwardMissCount + "; getClosest: " + getClosest);
//if (window.console && closestResult)
// window.console.log(j + ": distance = " + closestResult.distance);
return closestResult;
}
DataSeries.prototype.getMarkerProperties = function (index, x, y, ctx) {
var dataPoints = this.dataPoints;
var dataSeries = this;
var markerColor = dataPoints[index].markerColor ? dataPoints[index].markerColor : dataSeries.markerColor ? dataSeries.markerColor : dataPoints[index].color ? dataPoints[index].color : dataSeries.color ? dataSeries.color : dataSeries._colorSet[index % dataSeries._colorSet.length];
var markerBorderColor = dataPoints[index].markerBorderColor ? dataPoints[index].markerBorderColor : dataSeries.markerBorderColor ? dataSeries.markerBorderColor : null;
var markerBorderThickness = dataPoints[index].markerBorderThickness ? dataPoints[index].markerBorderThickness : dataSeries.markerBorderThickness ? dataSeries.markerBorderThickness : null;
var markerType = dataPoints[index].markerType ? dataPoints[index].markerType : dataSeries.markerType;
var markerSize = dataPoints[index].markerSize ? dataPoints[index].markerSize : dataSeries.markerSize;
return {
x: x, y: y, ctx: ctx,
type: markerType,
size: markerSize,
color: markerColor,
borderColor: markerBorderColor,
borderThickness: markerBorderThickness
}
}
//#endregion DataSeries
//#region Axis
function Axis(chart, options, type, position) {
Axis.base.constructor.call(this, "Axis", options, chart.theme);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = chart.ctx;
this.maxWidth = 0;
this.maxHeight = 0;
this.intervalstartTimePercent = 0;
this.labels = [];
this._labels = null;
//Processed information about the data that gets plotted against this axis
this.dataInfo = {
min: Infinity,
max: -Infinity,
viewPortMin: Infinity,
viewPortMax: -Infinity,
minDiff: Infinity // Used only in case of axisX
};
if (type === "axisX") {
this.sessionVariables = this.chart.sessionVariables[type];
if (!this._options.interval)
this.intervalType = null;
} else {
if (position === "left" || position === "top")
this.sessionVariables = this.chart.sessionVariables["axisY"];
else {
this.sessionVariables = this.chart.sessionVariables["axisY2"];
}
}
if (typeof (this._options.titleFontSize) === "undefined") {
this.titleFontSize = this.chart.getAutoFontSize(this.titleFontSize);
//window.console.log("titleFontSize: " + this.titleFontSize);
}
if (typeof (this._options.labelFontSize) === "undefined") {
this.labelFontSize = this.chart.getAutoFontSize(this.labelFontSize);
//window.console.log("labelFontSize: " + this.labelFontSize);
}
//Axis Type : axisX, axisY
this.type = type;
if (type === "axisX" && (!options || typeof (options.gridThickness) === "undefined"))
this.gridThickness = 0;
this._position = position;
this.lineCoordinates = { x1: null, y1: null, x2: null, y2: null, width: null };//{x1:, y1:, x2:, y2:, width:}
//
{
this.labelAngle = ((this.labelAngle % 360) + 360) % 360;
if (this.labelAngle > 90 && this.labelAngle <= 270)
this.labelAngle -= 180;
else if (this.labelAngle > 180 && this.labelAngle <= 270)
this.labelAngle -= 180
else if (this.labelAngle > 270 && this.labelAngle <= 360)
this.labelAngle -= 360
}
if (this._options.stripLines && this._options.stripLines.length > 0) {
this.stripLines = [];
for (var i = 0; i < this._options.stripLines.length; i++) {
this.stripLines.push(new StripLine(this.chart, this._options.stripLines[i], chart.theme, ++this.chart._eventManager.lastObjectId, this));
}
}
this._titleTextBlock = null;
this._absoluteMinimum = null;// Used to determine boundaries while Zooming/Panning
this._absoluteMaximum = null;// Used to determine boundaries while Zooming/Panning
if (this.hasOptionChanged("minimum"))
this.sessionVariables.internalMinimum = this.minimum;
if (this.hasOptionChanged("maximum"))
this.sessionVariables.internalMaximum = this.maximum;
this.trackChanges("minimum");
this.trackChanges("maximum");
}
extend(Axis, CanvasJSObject);
Axis.prototype.createLabels = function () {
var textBlock;
var i = 0;
var endPoint;
var labelMaxWidth = 0;
var labelMaxHeight = 0;
var intervalInPixels = 0;
//var intervalInPixels = this.conversionParameters.pixelPerUnit * this.interval;
if (this._position === "bottom" || this._position === "top") {
intervalInPixels = this.lineCoordinates.width / Math.abs(this.maximum - this.minimum) * this.interval;
if (this.labelAutoFit) {
labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? intervalInPixels * .9 >> 0 : this.labelMaxWidth;
}
else {
labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .7 >> 0 : this.labelMaxWidth;
}
labelMaxHeight = typeof (this._options.labelWrap) === "undefined" || this.labelWrap ? this.chart.height * .5 >> 0 : this.labelFontSize * 1.5;
}
else if (this._position === "left" || this._position === "right") {
intervalInPixels = this.lineCoordinates.height / Math.abs(this.maximum - this.minimum) * this.interval;
if (this.labelAutoFit) {
labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .3 >> 0 : this.labelMaxWidth;
}
else {
labelMaxWidth = typeof (this._options.labelMaxWidth) === "undefined" ? this.chart.width * .5 >> 0 : this.labelMaxWidth;
}
labelMaxHeight = typeof (this._options.labelWrap) === "undefined" || this.labelWrap ? intervalInPixels * 2 >> 0 : this.labelFontSize * 1.5;
}
if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime") {
endPoint = addToDateTime(new Date(this.maximum), this.interval, this.intervalType)
//endPoint = this.maximum;
for (i = this.intervalstartTimePercent; i < endPoint; addToDateTime(i, this.interval, this.intervalType)) {
//var text = dateFormat(i, this.valueFormatString);
var timeInMilliseconds = i.getTime();
var text = this.labelFormatter ? this.labelFormatter({ chart: this.chart, axis: this._options, value: i, label: this.labels[i] ? this.labels[i] : null })
: this.type === "axisX" && this.labels[timeInMilliseconds] ? this.labels[timeInMilliseconds] : dateFormat(i, this.valueFormatString, this.chart._cultureInfo);
textBlock = new TextBlock(this.ctx, {
x: 0,
y: 0,
//maxWidth: this.maxHeight,
//maxHeight: this.labelFontSize,
maxWidth: labelMaxWidth,
maxHeight: labelMaxHeight,
angle: this.labelAngle,
text: this.prefix + text + this.suffix,
horizontalAlign: "left",//left, center, right
fontSize: this.labelFontSize,//in pixels
fontFamily: this.labelFontFamily,
fontWeight: this.labelFontWeight, //normal, bold, bolder, lighter,
fontColor: this.labelFontColor,
fontStyle: this.labelFontStyle, // normal, italic, oblique
textBaseline: "middle"
});
this._labels.push({ position: i.getTime(), textBlock: textBlock, effectiveHeight: null });
}
}
else {
endPoint = this.maximum;
//if ((Math.floor(this.interval) < this.interval && !this._options.interval) || true) {
//Check if it should be rendered as a category axis. If yes, then ceil the interval
if (this.labels && this.labels.length) {
var tempInterval = Math.ceil(this.interval);
var tempStartPoint = Math.ceil(this.intervalstartTimePercent);
var hasAllLabels = false;
for (i = tempStartPoint; i < this.maximum; i += tempInterval) {
if (this.labels[i]) {
hasAllLabels = true;
} else {
hasAllLabels = false;
break;
}
}
if (hasAllLabels) {
this.interval = tempInterval;
this.intervalstartTimePercent = tempStartPoint;
}
}
//parseFloat & toPrecision are being used to avoid issues related to precision.
for (i = this.intervalstartTimePercent; i <= endPoint; i = parseFloat((i + this.interval).toFixed(14))) {
var text = this.labelFormatter ? this.labelFormatter({ chart: this.chart, axis: this._options, value: i, label: this.labels[i] ? this.labels[i] : null })
: this.type === "axisX" && this.labels[i] ? this.labels[i] : numberFormat(i, this.valueFormatString, this.chart._cultureInfo);
textBlock = new TextBlock(this.ctx, {
x: 0,
y: 0,
//maxWidth: this.maxHeight,
//maxHeight: this.labelFontSize,
maxWidth: labelMaxWidth,
maxHeight: labelMaxHeight,
angle: this.labelAngle,
text: this.prefix + text + this.suffix,
horizontalAlign: "left",//left, center, right
fontSize: this.labelFontSize,//in pixels
fontFamily: this.labelFontFamily,
fontWeight: this.labelFontWeight, //normal, bold, bolder, lighter,
fontColor: this.labelFontColor,
fontStyle: this.labelFontStyle, // normal, italic, oblique
textBaseline: "middle",
borderThickness: 0
});
this._labels.push({ position: i, textBlock: textBlock, effectiveHeight: null });
}
}
for (var i = 0; i < this.stripLines.length; i++) {
var stripLine = this.stripLines[i];
textBlock = new TextBlock(this.ctx, {
x: 0,
y: 0,
//maxWidth: this.maxHeight,
//maxHeight: this.labelFontSize,
backgroundColor: stripLine.labelBackgroundColor,
maxWidth: labelMaxWidth,
maxHeight: labelMaxHeight,
angle: this.labelAngle,
text: stripLine.labelFormatter ? stripLine.labelFormatter({ chart: this.chart, axis: this, stripLine: stripLine }) : stripLine.label,
horizontalAlign: "left",//left, center, right
fontSize: stripLine.labelFontSize,//in pixels
fontFamily: stripLine.labelFontFamily,
fontWeight: stripLine.labelFontWeight, //normal, bold, bolder, lighter,
fontColor: stripLine._options.labelFontColor || stripLine.color,
fontStyle: stripLine.labelFontStyle, // normal, italic, oblique
textBaseline: "middle",
borderThickness: 0
});
this._labels.push({ position: stripLine.value, textBlock: textBlock, effectiveHeight: null, stripLine: stripLine });
}
}
Axis.prototype.createLabelsAndCalculateWidth = function () {
var maxLabelEffectiveWidth = 0;
this._labels = [];
if (this._position === "left" || this._position === "right") {
this.createLabels();
for (i = 0; i < this._labels.length; i++) {
var textBlock = this._labels[i].textBlock;
var size = textBlock.measureText();
//var hypotenuse = Math.sqrt(Math.pow(size.height / 2, 2) + Math.pow(size.width, 2));
//labelEffectiveWidth = hypotenuse * Math.cos(Math.abs(Math.PI / 180 * this.labelAngle) - Math.abs(Math.acos(size.width / hypotenuse)));
var labelEffectiveWidth = 0;
if (this.labelAngle === 0)
labelEffectiveWidth = size.width;
else
labelEffectiveWidth = (size.width * Math.cos(Math.PI / 180 * Math.abs(this.labelAngle))) + (size.height / 2 * Math.sin(Math.PI / 180 * Math.abs(this.labelAngle)));
if (maxLabelEffectiveWidth < labelEffectiveWidth)
maxLabelEffectiveWidth = labelEffectiveWidth;
this._labels[i].effectiveWidth = labelEffectiveWidth;
}
}
var titleHeight = this.title ? getFontHeightInPixels(this.titleFontFamily, this.titleFontSize, this.titleFontWeight) + 2 : 0;
var axisWidth = titleHeight + maxLabelEffectiveWidth + this.tickLength + 5;
//if (isDebugMode && window.console) {
// window.console.log(this.type + "--- axisWidth: " + axisWidth);
//}
return axisWidth;
}
Axis.prototype.createLabelsAndCalculateHeight = function () {
var maxLabelEffectiveHeight = 0;
this._labels = [];
var textBlock;
var i = 0;
this.createLabels();
if (this._position === "bottom" || this._position === "top") {
for (i = 0; i < this._labels.length; i++) {
textBlock = this._labels[i].textBlock;
var size = textBlock.measureText();
//var diagonal = Math.sqrt(Math.pow(size.height, 2) + Math.pow(size.width, 2));
//var hypotenuse = Math.sqrt(Math.pow(size.height / 2, 2) + Math.pow(size.width, 2));
//var labelEffectiveHeight = hypotenuse * Math.cos(Math.PI / 2 - (Math.abs(Math.PI / 180 * this.labelAngle) + Math.abs(Math.acos(size.width / hypotenuse))));
var labelEffectiveHeight = 0;
if (this.labelAngle === 0)
labelEffectiveHeight = size.height;
else
labelEffectiveHeight = (size.width * Math.sin(Math.PI / 180 * Math.abs(this.labelAngle))) + (size.height / 2 * Math.cos(Math.PI / 180 * Math.abs(this.labelAngle)));
if (maxLabelEffectiveHeight < labelEffectiveHeight)
maxLabelEffectiveHeight = labelEffectiveHeight;
this._labels[i].effectiveHeight = labelEffectiveHeight;
}
}
//var titleHeight = this.title ? this.titleFontSize + 5 : 0;
var titleHeight = this.title ? getFontHeightInPixels(this.titleFontFamily, this.titleFontSize, this.titleFontWeight) + 2 : 0;
return titleHeight + maxLabelEffectiveHeight + this.tickLength + 5;
}
//Static Method that co-ordinates between axisX, axisY and renders them
Axis.setLayoutAndRender = function (axisX, axisY, axisY2, axisPlacement, freeSpace) {
var x1, y1, x2, y2;
var chart = axisX.chart;
var ctx = chart.ctx;
axisX.calculateAxisParameters();
if (axisY)
axisY.calculateAxisParameters();
if (axisY2)
axisY2.calculateAxisParameters();
if (axisY && axisY2 && typeof (axisY._options.maximum) === "undefined" && typeof (axisY._options.minimum) === "undefined" && typeof (axisY._options.interval) === "undefined"
&& typeof (axisY2._options.maximum) === "undefined" && typeof (axisY2._options.minimum) === "undefined" && typeof (axisY2._options.interval) === "undefined") {
var noTicksY = (axisY.maximum - axisY.minimum) / axisY.interval;
var noTicksY2 = (axisY2.maximum - axisY2.minimum) / axisY2.interval;
if (noTicksY > noTicksY2) {
axisY2.maximum = axisY2.interval * noTicksY + axisY2.minimum;
} else if (noTicksY2 > noTicksY) {
axisY.maximum = axisY.interval * noTicksY2 + axisY.minimum;
}
}
var axisYlineThickness = axisY ? axisY.lineThickness ? axisY.lineThickness : 0 : 0;
var axisY2lineThickness = axisY2 ? axisY2.lineThickness ? axisY2.lineThickness : 0 : 0;
var axisYGridThickness = axisY ? axisY.gridThickness ? axisY.gridThickness : 0 : 0;
var axisY2GridThickness = axisY2 ? axisY2.gridThickness ? axisY2.gridThickness : 0 : 0;
var axisYMargin = axisY ? axisY.margin : 0;
var axisY2Margin = axisY ? axisY.margin : 0;
if (axisPlacement === "normal") {
axisX.lineCoordinates = {};
var axisYWidth = Math.ceil(axisY ? axisY.createLabelsAndCalculateWidth() : 0);
x1 = Math.round(freeSpace.x1 + axisYWidth + axisYMargin);
axisX.lineCoordinates.x1 = x1;
var axisY2Width = Math.ceil(axisY2 ? axisY2.createLabelsAndCalculateWidth() : 0);
x2 = Math.round(freeSpace.x2 - axisY2Width > axisX.chart.width - 10 ? axisX.chart.width - 10 : freeSpace.x2 - axisY2Width);
axisX.lineCoordinates.x2 = x2;
axisX.lineCoordinates.width = Math.abs(x2 - x1); // required early on inside createLabels of axisX
var axisXHeight = Math.ceil(axisX.createLabelsAndCalculateHeight());
// Position axisX based on the available free space, Margin and its height
//x1 = freeSpace.x1 + axisYWidth + axisYMargin + axisYlineThickness / 2;
y1 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
y2 = Math.round(freeSpace.y2 - axisX.margin);
//axisX.lineCoordinates = { x1: x1, y1: y1, x2: x2, y2: y1, width: Math.abs(x2 - x1) }
axisX.lineCoordinates.y1 = y1;
axisX.lineCoordinates.y2 = y1;
axisX.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
//if (isDebugMode) {
// axisX.ctx.rect(axisX.boundingRect.x1, axisX.boundingRect.y1, axisX.boundingRect.width, axisX.boundingRect.height);
// axisX.ctx.stroke();
//}
// Position axisY based on the available free space, Margin and its height
if (axisY) {
x1 = Math.round(freeSpace.x1 + axisY.margin);
y1 = Math.round(freeSpace.y1 < 10 ? 10 : freeSpace.y1);
x2 = Math.round(freeSpace.x1 + axisYWidth + axisY.margin);
//y2 = freeSpace.y2 - axisXHeight - axisX.margin - axisX.lineThickness / 2;
y2 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
axisY.lineCoordinates = { x1: x2, y1: y1, x2: x2, y2: y2, height: Math.abs(y2 - y1) }
axisY.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
}
//if (isDebugMode && axisY) {
// axisY.ctx.rect(axisY.boundingRect.x1, axisY.boundingRect.y1, axisY.boundingRect.width, axisY.boundingRect.height);
// axisY.ctx.stroke();
//}
// Position axisY2 based on the available free space, Margin and its height
if (axisY2) {
x1 = Math.round(axisX.lineCoordinates.x2);
y1 = Math.round(freeSpace.y1 < 10 ? 10 : freeSpace.y1);
x2 = Math.round(x1 + axisY2Width + axisY2.margin);
//y2 = freeSpace.y2 - axisXHeight - axisX.margin - axisX.lineThickness / 2;
y2 = Math.round(freeSpace.y2 - axisXHeight - axisX.margin);
axisY2.lineCoordinates = { x1: x1, y1: y1, x2: x1, y2: y2, height: Math.abs(y2 - y1) }
axisY2.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
}
axisX.calculateValueToPixelconversionParameters();
if (axisY)
axisY.calculateValueToPixelconversionParameters();
if (axisY2)
axisY2.calculateValueToPixelconversionParameters();
ctx.save();
ctx.rect(5, axisX.boundingRect.y1, axisX.chart.width - 10, axisX.boundingRect.height);
ctx.clip();
axisX.renderLabelsTicksAndTitle();
ctx.restore();
if (axisY)
axisY.renderLabelsTicksAndTitle();
if (axisY2)
axisY2.renderLabelsTicksAndTitle();
chart.preparePlotArea();
var plotArea = axisX.chart.plotArea;
ctx.save();
ctx.rect(plotArea.x1, plotArea.y1, Math.abs(plotArea.x2 - plotArea.x1), Math.abs(plotArea.y2 - plotArea.y1));
ctx.clip();
axisX.renderStripLinesOfThicknessType("value");
if (axisY)
axisY.renderStripLinesOfThicknessType("value");
if (axisY2)
axisY2.renderStripLinesOfThicknessType("value");
axisX.renderInterlacedColors();
if (axisY)
axisY.renderInterlacedColors();
if (axisY2)
axisY2.renderInterlacedColors();
ctx.restore();
axisX.renderGrid();
if (axisY)
axisY.renderGrid();
if (axisY2)
axisY2.renderGrid();
axisX.renderAxisLine();
if (axisY)
axisY.renderAxisLine();
if (axisY2)
axisY2.renderAxisLine();
//No need to clip to plotArea because stripLines need to render on top of gridlines
axisX.renderStripLinesOfThicknessType("pixel");
if (axisY)
axisY.renderStripLinesOfThicknessType("pixel");
if (axisY2)
axisY2.renderStripLinesOfThicknessType("pixel");
}
else {
var axisXWidth = Math.ceil(axisX.createLabelsAndCalculateWidth());
if (axisY) {
axisY.lineCoordinates = {};
x1 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
x2 = Math.round(freeSpace.x2 > axisY.chart.width - 10 ? axisY.chart.width - 10 : freeSpace.x2);
axisY.lineCoordinates.x1 = x1;
axisY.lineCoordinates.x2 = x2;
axisY.lineCoordinates.width = Math.abs(x2 - x1);
}
if (axisY2) {
axisY2.lineCoordinates = {};
x1 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
x2 = Math.round(freeSpace.x2 > axisY2.chart.width - 10 ? axisY2.chart.width - 10 : freeSpace.x2);
axisY2.lineCoordinates.x1 = x1;
axisY2.lineCoordinates.x2 = x2;
axisY2.lineCoordinates.width = Math.abs(x2 - x1);
}
var axisYHeight = Math.ceil(axisY ? axisY.createLabelsAndCalculateHeight() : 0);
var axisY2Height = Math.ceil(axisY2 ? axisY2.createLabelsAndCalculateHeight() : 0);
// Position axisY based on the available free space, Margin and its height
if (axisY) {
//x1 = freeSpace.x1 + axisXWidth + axisX.margin + axisX.lineThickness / 2;
//x2 = freeSpace.x2 > axisY.chart.width - 10 ? axisY.chart.width - 10 : freeSpace.x2;
y1 = Math.round(freeSpace.y2 - axisYHeight - axisY.margin);
y2 = Math.round(freeSpace.y2 - axisYMargin > axisY.chart.height - 10 ? axisY.chart.height - 10 : freeSpace.y2 - axisYMargin);
//axisY.lineCoordinates = { x1: x1, y1: y1, x2: x2, y2: y1, width: Math.abs(x2 - x1) }
axisY.lineCoordinates.y1 = y1;
axisY.lineCoordinates.y2 = y1;
axisY.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: axisYHeight };
}
// Position axisY based on the available free space, Margin and its height
if (axisY2) {
//x1 = freeSpace.x1 + axisXWidth + axisX.margin + axisX.lineThickness / 2;
//x2 = freeSpace.x2 > axisY2.chart.width - 10 ? axisY2.chart.width - 10 : freeSpace.x2;
y1 = Math.round(freeSpace.y1 + axisY2.margin);
y2 = (freeSpace.y1 + axisY2.margin + axisY2Height);
//axisY2.lineCoordinates = { x1: x1, y1: y2, x2: x2, y2: y2, width: Math.abs(x2 - x1) }
axisY2.lineCoordinates.y1 = y2;
axisY2.lineCoordinates.y2 = y2;
axisY2.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: axisY2Height };
}
//axisY.ctx.rect(axisY.boundingRect.x1, axisY.boundingRect.y1, axisY.boundingRect.width, axisY.boundingRect.height);
//axisY.ctx.stroke();
// Position axisX based on the available free space, Margin and its height
x1 = Math.round(freeSpace.x1 + axisX.margin);
y1 = Math.round(axisY2 ? axisY2.lineCoordinates.y2 : (freeSpace.y1 < 10 ? 10 : freeSpace.y1));
x2 = Math.round(freeSpace.x1 + axisXWidth + axisX.margin);
y2 = Math.round(axisY ? axisY.lineCoordinates.y1 : (freeSpace.y2 - axisYMargin > axisX.chart.height - 10 ? axisX.chart.height - 10 : freeSpace.y2 - axisYMargin));
axisX.lineCoordinates = { x1: x2, y1: y1, x2: x2, y2: y2, height: Math.abs(y2 - y1) };
axisX.boundingRect = { x1: x1, y1: y1, x2: x2, y2: y2, width: x2 - x1, height: y2 - y1 };
//axisX.ctx.rect(axisX.boundingRect.x1, axisX.boundingRect.y1, axisX.boundingRect.width, axisX.boundingRect.height);
//axisX.ctx.stroke();
axisX.calculateValueToPixelconversionParameters();
if (axisY)
axisY.calculateValueToPixelconversionParameters();
if (axisY2)
axisY2.calculateValueToPixelconversionParameters();
//ctx.save();
//ctx.rect(axisY.boundingRect.x1 - 30, axisY.boundingRect.y1, axisY.boundingRect.width + 60, axisY.boundingRect.height);
//ctx.clip();
if (axisY)
axisY.renderLabelsTicksAndTitle();
if (axisY2)
axisY2.renderLabelsTicksAndTitle();
//ctx.restore();
axisX.renderLabelsTicksAndTitle();
chart.preparePlotArea();
var plotArea = axisX.chart.plotArea;
ctx.save();
ctx.rect(plotArea.x1, plotArea.y1, Math.abs(plotArea.x2 - plotArea.x1), Math.abs(plotArea.y2 - plotArea.y1));
ctx.clip();
//No need to clip to plotArea because stripLines need to render on top of gridlines
axisX.renderStripLinesOfThicknessType("value");
if (axisY)
axisY.renderStripLinesOfThicknessType("value");
if (axisY2)
axisY2.renderStripLinesOfThicknessType("value");
axisX.renderInterlacedColors();
if (axisY)
axisY.renderInterlacedColors();
if (axisY2)
axisY2.renderInterlacedColors();
ctx.restore();
axisX.renderGrid();
if (axisY)
axisY.renderGrid();
if (axisY2)
axisY2.renderGrid();
axisX.renderAxisLine();
if (axisY)
axisY.renderAxisLine();
if (axisY2)
axisY2.renderAxisLine();
axisX.renderStripLinesOfThicknessType("pixel");
if (axisY)
axisY.renderStripLinesOfThicknessType("pixel");
if (axisY2)
axisY2.renderStripLinesOfThicknessType("pixel");
}
}
Axis.prototype.renderLabelsTicksAndTitle = function () {
var skipLabels = false;
var totalLabelWidth = 0;
var thresholdRatio = 1;
var labelCount = 0;
var intervalInPixels = this.conversionParameters.pixelPerUnit * this.interval;
if (this.labelAngle !== 0 && this.labelAngle !== 360)
thresholdRatio = 1.2;
//Don't skip labels when interval is explicitely set
if (typeof (this._options.interval) === "undefined") {
if (this._position === "bottom" || this._position === "top") {
//thresholdRatio = .9;// More space is preferred between labels when axis is horizontally aligned
for (i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.stripLine)// don't consider stripLine's lable
continue;
var width = label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle) + label.textBlock.height * Math.sin(Math.PI / 180 * this.labelAngle);
totalLabelWidth += width;
}
if (totalLabelWidth > this.lineCoordinates.width * thresholdRatio) {
skipLabels = true;
}
} if (this._position === "left" || this._position === "right") {
for (i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.stripLine)// don't consider stripLine's lable
continue;
var width = label.textBlock.height * Math.cos(Math.PI / 180 * this.labelAngle) + label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle);
totalLabelWidth += width;
}
if (totalLabelWidth > this.lineCoordinates.height * thresholdRatio) {
skipLabels = true;
}
}
}
if (this._position === "bottom") {
var i = 0;
var label;
var xy;
for (i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.position > this.maximum)
continue;
xy = this.getPixelCoordinatesOnAxis(label.position);
if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
if (this._labels[i].stripLine) {
stripLine = this._labels[i].stripLine;
this.ctx.lineWidth = stripLine.thickness;
this.ctx.strokeStyle = stripLine.color;
} else {
this.ctx.lineWidth = this.tickThickness;
this.ctx.strokeStyle = this.tickColor;
}
var tickX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
this.ctx.beginPath();
this.ctx.moveTo(tickX, xy.y << 0);
this.ctx.lineTo(tickX, (xy.y + this.tickLength) << 0);
this.ctx.stroke();
}
//Don't skip stripLine's labels
if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
continue;
if (label.textBlock.angle === 0) {
xy.x -= label.textBlock.width / 2;
//xy.y += this.tickLength + label.textBlock.height / 2;
xy.y += this.tickLength + label.textBlock.fontSize / 2;
} else {
xy.x -= (this.labelAngle < 0 ? (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) : 0);
xy.y += this.tickLength + Math.abs((this.labelAngle < 0 ? label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle) - 5 : 5));
}
label.textBlock.x = xy.x;
label.textBlock.y = xy.y;
label.textBlock.render(true);
}
if (this.title) {
this._titleTextBlock = new TextBlock(this.ctx, {
x: this.lineCoordinates.x1,// This is recalculated again
y: this.boundingRect.y2 - this.titleFontSize - 5,// This is recalculated again
maxWidth: this.lineCoordinates.width,
maxHeight: this.titleFontSize * 1.5,
angle: 0,
text: this.title,
horizontalAlign: "center",//left, center, right
fontSize: this.titleFontSize,//in pixels
fontFamily: this.titleFontFamily,
fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
fontColor: this.titleFontColor,
fontStyle: this.titleFontStyle, // normal, italic, oblique
textBaseline: "top"
});
this._titleTextBlock.measureText();
this._titleTextBlock.x = this.lineCoordinates.x1 + this.lineCoordinates.width / 2 - this._titleTextBlock.width / 2;
this._titleTextBlock.y = this.boundingRect.y2 - this._titleTextBlock.height - 3;
this._titleTextBlock.render(true);
}
}
else if (this._position === "top") {
var i = 0;
var label;
var xy;
var stripLine;
for (i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.position > this.maximum)
continue;
xy = this.getPixelCoordinatesOnAxis(label.position);
if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
if (this._labels[i].stripLine) {
stripLine = this._labels[i].stripLine;
this.ctx.lineWidth = stripLine.thickness;
this.ctx.strokeStyle = stripLine.color;
} else {
this.ctx.lineWidth = this.tickThickness;
this.ctx.strokeStyle = this.tickColor;
}
var tickX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
this.ctx.beginPath();
this.ctx.moveTo(tickX, xy.y << 0);
this.ctx.lineTo(tickX, (xy.y - this.tickLength) << 0);
this.ctx.stroke();
}
//Don't skip stripLine's labels
if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
continue;
if (label.textBlock.angle === 0) {
xy.x -= label.textBlock.width / 2;
xy.y -= this.tickLength + label.textBlock.height / 2;
} else {
xy.x -= (this.labelAngle > 0 ? (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) : 0);
xy.y -= this.tickLength + Math.abs((this.labelAngle > 0 ? label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle) + 5 : 5));
}
label.textBlock.x = xy.x;
label.textBlock.y = xy.y;
label.textBlock.render(true);
}
if (this.title) {
this._titleTextBlock = new TextBlock(this.ctx, {
x: this.lineCoordinates.x1,// This is recalculated again
y: this.boundingRect.y1 + 1,
maxWidth: this.lineCoordinates.width,
maxHeight: this.titleFontSize * 1.5,
angle: 0,
text: this.title,
horizontalAlign: "center",//left, center, right
fontSize: this.titleFontSize,//in pixels
fontFamily: this.titleFontFamily,
fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
fontColor: this.titleFontColor,
fontStyle: this.titleFontStyle, // normal, italic, oblique
textBaseline: "top"
});
this._titleTextBlock.measureText();
this._titleTextBlock.x = this.lineCoordinates.x1 + this.lineCoordinates.width / 2 - this._titleTextBlock.width / 2;
this._titleTextBlock.render(true);
}
}
else if (this._position === "left") {
var label;
var xy;
for (var i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.position > this.maximum)
continue;
xy = this.getPixelCoordinatesOnAxis(label.position);
if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
if (this._labels[i].stripLine) {
stripLine = this._labels[i].stripLine;
this.ctx.lineWidth = stripLine.thickness;
this.ctx.strokeStyle = stripLine.color;
} else {
this.ctx.lineWidth = this.tickThickness;
this.ctx.strokeStyle = this.tickColor;
}
var tickY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
this.ctx.beginPath();
this.ctx.moveTo(xy.x << 0, tickY);
this.ctx.lineTo((xy.x - this.tickLength) << 0, tickY);
this.ctx.stroke();
}
//Don't skip stripLine's labels
if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
continue;
label.textBlock.x = xy.x - (label.textBlock.width * Math.cos(Math.PI / 180 * this.labelAngle)) - this.tickLength - 5;
if (this.labelAngle === 0) {
label.textBlock.y = xy.y;
} else
label.textBlock.y = xy.y - (label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle));
label.textBlock.render(true);
}
if (this.title) {
this._titleTextBlock = new TextBlock(this.ctx, {
x: this.boundingRect.x1 + 1,
y: this.lineCoordinates.y2,
maxWidth: this.lineCoordinates.height,
maxHeight: this.titleFontSize * 1.5,
angle: -90,
text: this.title,
horizontalAlign: "center",//left, center, right
fontSize: this.titleFontSize,//in pixels
fontFamily: this.titleFontFamily,
fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
fontColor: this.titleFontColor,
fontStyle: this.titleFontStyle, // normal, italic, oblique
textBaseline: "top"
});
var size = this._titleTextBlock.measureText();
//this._titleTextBlock.x -= 4;
this._titleTextBlock.y = (this.lineCoordinates.height / 2 + this._titleTextBlock.width / 2 + this.lineCoordinates.y1);
this._titleTextBlock.render(true);
//if (isDebugMode) {
// window.console.log("titleFontSize: " + this.titleFontSize + "; width: " + size.width + "; height: " + size.height);
// window.console.log("this.boundingRect.x1: " + this.boundingRect.x1);
// //this.ctx.rect(this._titleTextBlock.x, this._titleTextBlock.y, this._titleTextBlock.height, -this._titleTextBlock.width);
// //this.ctx.stroke();
//}
}
}
else if (this._position === "right") {
var label;
var xy;
for (var i = 0; i < this._labels.length; i++) {
label = this._labels[i];
if (label.position < this.minimum || label.position > this.maximum)
continue;
xy = this.getPixelCoordinatesOnAxis(label.position);
if ((this.tickThickness && !this._labels[i].stripLine) || (this._labels[i].stripLine && this._labels[i].stripLine._thicknessType === "pixel")) {
if (this._labels[i].stripLine) {
stripLine = this._labels[i].stripLine;
this.ctx.lineWidth = stripLine.thickness;
this.ctx.strokeStyle = stripLine.color;
} else {
this.ctx.lineWidth = this.tickThickness;
this.ctx.strokeStyle = this.tickColor;
}
var tickY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
this.ctx.beginPath();
this.ctx.moveTo(xy.x << 0, tickY);
this.ctx.lineTo((xy.x + this.tickLength) << 0, tickY);
this.ctx.stroke();
}
//Don't skip stripLine's labels
if (skipLabels && labelCount++ % 2 !== 0 && !this._labels[i].stripLine)
continue;
label.textBlock.x = xy.x + this.tickLength + 5;
//label.textBlock.y = xy.y - (label.textBlock.width * Math.sin(Math.PI / 180 * this.labelAngle));
if (this.labelAngle === 0) {
label.textBlock.y = xy.y;
}
else
label.textBlock.y = xy.y;
label.textBlock.render(true);
}
if (this.title) {
this._titleTextBlock = new TextBlock(this.ctx, {
x: this.boundingRect.x2 - 1,
y: this.lineCoordinates.y2,
maxWidth: this.lineCoordinates.height,
maxHeight: this.titleFontSize * 1.5,
angle: 90,
text: this.title,
horizontalAlign: "center",//left, center, right
fontSize: this.titleFontSize,//in pixels
fontFamily: this.titleFontFamily,
fontWeight: this.titleFontWeight, //normal, bold, bolder, lighter,
fontColor: this.titleFontColor,
fontStyle: this.titleFontStyle, // normal, italic, oblique
textBaseline: "top"
});
this._titleTextBlock.measureText();
this._titleTextBlock.y = (this.lineCoordinates.height / 2 - this._titleTextBlock.width / 2 + this.lineCoordinates.y1);
this._titleTextBlock.render(true);
}
}
}
Axis.prototype.renderInterlacedColors = function () {
var ctx = this.chart.plotArea.ctx;
//return;
var interlacedGridStartPoint;
var interlacedGridEndPoint;
var plotAreaCoordinates = this.chart.plotArea;
var i = 0, renderInterlacedGrid = true;
if ((this._position === "bottom" || this._position === "top") && this.interlacedColor) {
ctx.fillStyle = this.interlacedColor;
for (i = 0; i < this._labels.length; i++) {
if (this._labels[i].stripLine)
continue;
if (renderInterlacedGrid) {//So that the interlaced color alternates
interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this._labels[i].position);
if (i + 1 >= this._labels.length - 1)
interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this.maximum);
else
interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this._labels[i + 1].position);
ctx.fillRect(interlacedGridStartPoint.x, plotAreaCoordinates.y1, Math.abs(interlacedGridEndPoint.x - interlacedGridStartPoint.x), Math.abs(plotAreaCoordinates.y1 - plotAreaCoordinates.y2));
renderInterlacedGrid = false;
} else
renderInterlacedGrid = true;
}
} else if ((this._position === "left" || this._position === "right") && this.interlacedColor) {
ctx.fillStyle = this.interlacedColor;
for (i = 0; i < this._labels.length; i++) {
if (this._labels[i].stripLine)
continue;
if (renderInterlacedGrid) {//So that the interlaced color alternates
interlacedGridEndPoint = this.getPixelCoordinatesOnAxis(this._labels[i].position);
if (i + 1 >= this._labels.length - 1)
interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this.maximum);
else
interlacedGridStartPoint = this.getPixelCoordinatesOnAxis(this._labels[i + 1].position);
ctx.fillRect(plotAreaCoordinates.x1, interlacedGridStartPoint.y, Math.abs(plotAreaCoordinates.x1 - plotAreaCoordinates.x2), Math.abs(interlacedGridStartPoint.y - interlacedGridEndPoint.y));
renderInterlacedGrid = false;
} else
renderInterlacedGrid = true;
}
//throw "123";
}
ctx.beginPath();
}
//Renders stripLines of given thickness type.
Axis.prototype.renderStripLinesOfThicknessType = function (thicknessType) {
if (!(this.stripLines && this.stripLines.length > 0) || !thicknessType)
return;
var _this = this;
var i = 0;
for (i = 0; i < this.stripLines.length; i++) {
var stripLine = this.stripLines[i];
if (stripLine._thicknessType !== thicknessType)
continue;
//Should be skipped only if thicknessType is "pixel". If it is "value" then clipping is automatically applied before calling.
if (thicknessType === "pixel" && (stripLine.value < this.minimum || stripLine.value > this.maximum))
continue;
if (stripLine.showOnTop) {
this.chart.addEventListener("dataAnimationIterationEnd", stripLine.render, stripLine);
}
else
stripLine.render();
}
};
Axis.prototype.renderGrid = function () {
if (!(this.gridThickness && this.gridThickness > 0))
return;
//var ctx = this.chart.plotArea.ctx;
var ctx = this.chart.ctx;
var xy;
var plotAreaCoordinates = this.chart.plotArea;
var stripLine;
var tempLineWidth, tempStrokeStyle;
//return;
ctx.lineWidth = this.gridThickness;
ctx.strokeStyle = this.gridColor;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(this.gridDashType, this.gridThickness));
}
if (this._position === "bottom" || this._position === "top") {
for (i = 0; i < this._labels.length && !this._labels[i].stripLine; i++) {
if (this._labels[i].position < this.minimum || this._labels[i].position > this.maximum)
continue;
ctx.beginPath();
xy = this.getPixelCoordinatesOnAxis(this._labels[i].position);
var gridX = (ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
ctx.moveTo(gridX, plotAreaCoordinates.y1 << 0);
ctx.lineTo(gridX, plotAreaCoordinates.y2 << 0);
ctx.stroke();
}
}
else if (this._position === "left" || this._position === "right") {
for (var i = 0; i < this._labels.length && !this._labels[i].stripLine; i++) {
if (i === 0 && this.type === "axisY" && this.chart.axisX && this.chart.axisX.lineThickness)
continue;
if (this._labels[i].position < this.minimum || this._labels[i].position > this.maximum)
continue;
ctx.beginPath();
xy = this.getPixelCoordinatesOnAxis(this._labels[i].position);
var gridY = (ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
ctx.moveTo(plotAreaCoordinates.x1 << 0, gridY);
ctx.lineTo(plotAreaCoordinates.x2 << 0, gridY);
ctx.stroke();
}
}
}
Axis.prototype.renderAxisLine = function () {
//var ctx = this.chart.plotArea.ctx;
var ctx = this.chart.ctx;
if (this._position === "bottom" || this._position === "top") {
if (this.lineThickness) {
ctx.lineWidth = this.lineThickness;
ctx.strokeStyle = this.lineColor ? this.lineColor : "black";
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(this.lineDashType, this.lineThickness));
}
var lineY = (this.lineThickness % 2 === 1) ? (this.lineCoordinates.y1 << 0) + .5 : (this.lineCoordinates.y1 << 0);
ctx.beginPath();
ctx.moveTo(this.lineCoordinates.x1, lineY);
ctx.lineTo(this.lineCoordinates.x2, lineY);
ctx.stroke();
}
} else if (this._position === "left" || this._position === "right") {
if (this.lineThickness) {
ctx.lineWidth = this.lineThickness;
ctx.strokeStyle = this.lineColor;
if (ctx.setLineDash) {
ctx.setLineDash(getLineDashArray(this.lineDashType, this.lineThickness));
}
var lineX = (this.lineThickness % 2 === 1) ? (this.lineCoordinates.x1 << 0) + .5 : (this.lineCoordinates.x1 << 0);
ctx.beginPath();
ctx.moveTo(lineX, this.lineCoordinates.y1);
ctx.lineTo(lineX, this.lineCoordinates.y2);
ctx.stroke();
}
}
}
Axis.prototype.getPixelCoordinatesOnAxis = function (value) {
var xy = {};
var width = this.lineCoordinates.width;
var height = this.lineCoordinates.height;
if (this._position === "bottom" || this._position === "top") {
var pixelPerUnit = width / Math.abs(this.maximum - this.minimum);
xy.x = this.lineCoordinates.x1 + (pixelPerUnit * (value - this.minimum));
xy.y = this.lineCoordinates.y1;
}
if (this._position === "left" || this._position === "right") {
var pixelPerUnit = height / Math.abs(this.maximum - this.minimum);
xy.y = this.lineCoordinates.y2 - (pixelPerUnit * (value - this.minimum));
xy.x = this.lineCoordinates.x2;
}
return xy;
}
Axis.prototype.getXValueAt = function (pixel) {
if (!pixel)
return null;
var xval = null;
if (this._position === "left") {
xval = (this.chart.axisX.maximum - this.chart.axisX.minimum) / this.chart.axisX.lineCoordinates.height * ((this.chart.axisX.lineCoordinates.y2 - pixel.y)) + this.chart.axisX.minimum;
}
else if (this._position === "bottom") {
xval = (this.chart.axisX.maximum - this.chart.axisX.minimum) / this.chart.axisX.lineCoordinates.width * (pixel.x - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.minimum;
}
return xval;
}
Axis.prototype.calculateValueToPixelconversionParameters = function (value) {
var xy = {};
var conversionParameters = { pixelPerUnit: null, minimum: null, reference: null };
var width = this.lineCoordinates.width;
var height = this.lineCoordinates.height;
conversionParameters.minimum = this.minimum;
if (this._position === "bottom" || this._position === "top") {
conversionParameters.pixelPerUnit = width / Math.abs(this.maximum - this.minimum);
conversionParameters.reference = this.lineCoordinates.x1;
//xy.x = this.lineCoordinates.x1 + (pixelPerUnit * (value - this.minimum));
//xy.y = this.lineCoordinates.y1;
}
if (this._position === "left" || this._position === "right") {
conversionParameters.pixelPerUnit = -1 * height / Math.abs(this.maximum - this.minimum);
conversionParameters.reference = this.lineCoordinates.y2;
//xy.y = this.lineCoordinates.y2 + (pixelPerUnit * (value - this.minimum));
//xy.x = this.lineCoordinates.x2;
}
this.conversionParameters = conversionParameters;
}
Axis.prototype.calculateAxisParameters = function () {
var freeSpace = this.chart.layoutManager.getFreeSpace();
var availableWidth = 0;
var availableHeight = 0;
var isLessThanTwoDataPoints = false;
if (this._position === "bottom" || this._position === "top") {
this.maxWidth = freeSpace.width;
this.maxHeight = freeSpace.height;
} else {
this.maxWidth = freeSpace.height;
this.maxHeight = freeSpace.width;
}
var noTicks = this.type === "axisX" ? (this.maxWidth < 500 ? 8 : Math.max(6, Math.floor(this.maxWidth / 62))) : Math.max(Math.floor(this.maxWidth / 40), 2);
//var noTicks = 8;
var min, max;
var minDiff;
var range;
var rangePadding = 0;
if (this.type === "axisX") {
min = (this.sessionVariables.internalMinimum !== null) ? this.sessionVariables.internalMinimum : this.dataInfo.viewPortMin;
max = (this.sessionVariables.internalMaximum !== null) ? this.sessionVariables.internalMaximum : this.dataInfo.viewPortMax;
if (max - min === 0) {
rangePadding = typeof (this._options.interval) === "undefined" ? .4 : this._options.interval;
max += rangePadding;
min -= rangePadding;
}
if (this.dataInfo.minDiff !== Infinity)
minDiff = this.dataInfo.minDiff;
else if (max - min > 1) {
minDiff = Math.abs(max - min) * .5;
}
else {
minDiff = 1;
if (this.chart.plotInfo.axisXValueType === "dateTime")
isLessThanTwoDataPoints = true;
}
} else if (this.type === "axisY") {
min = typeof (this._options.minimum) === "undefined" || this._options.minimum === null ? this.dataInfo.viewPortMin : this._options.minimum;
max = typeof (this._options.maximum) === "undefined" || this._options.maximum === null ? this.dataInfo.viewPortMax : this._options.maximum;
if (!isFinite(min) && !isFinite(max)) {
max = typeof (this._options.interval) === "undefined" ? -Infinity : this._options.interval;
min = 0;
}
else
if (min === 0 && max === 0) {// When all dataPoints are zero
max += 9;
min = 0;
}
else if (max - min === 0) {// When there is only a single dataPoint or when all dataPoints have same Y Value
rangePadding = Math.min(Math.abs(Math.abs(max) * .01), 5);
max += rangePadding;
min -= rangePadding;
}
else if (min > max) {
rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), 5);
if (max >= 0)
min = max - rangePadding;
else
max = min + rangePadding;
}
else {
rangePadding = Math.min(Math.abs(Math.abs(max - min) * .01), .05);
if (max !== 0)
max += rangePadding;
if (min !== 0)
min -= rangePadding;
}
//Apply includeZero
if (this.includeZero && (typeof (this._options.minimum) === "undefined" || this._options.minimum === null)) {
if (min > 0)
min = 0;
}
if (this.includeZero && (typeof (this._options.maximum) === "undefined" || this._options.maximum === null)) {
if (max < 0)
max = 0;
}
}
if (this.type === "axisX" && this.chart.plotInfo.axisXValueType === "dateTime") {
range = max - min;
if (!this.intervalType) {
if (range / (1 * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "millisecond";
} else if (range / (1 * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "millisecond";
} else if (range / (1 * 5) <= noTicks) {
this.interval = 5;
this.intervalType = "millisecond";
} else if (range / (1 * 10) <= noTicks) {
this.interval = 10;
this.intervalType = "millisecond";
} else if (range / (1 * 20) <= noTicks) {
this.interval = 20;
this.intervalType = "millisecond";
} else if (range / (1 * 50) <= noTicks) {
this.interval = 50;
this.intervalType = "millisecond";
} else if (range / (1 * 100) <= noTicks) {
this.interval = 100;
this.intervalType = "millisecond";
} else if (range / (1 * 200) <= noTicks) {
this.interval = 200;
this.intervalType = "millisecond";
} else if (range / (1 * 250) <= noTicks) {
this.interval = 250;
this.intervalType = "millisecond";
} else if (range / (1 * 300) <= noTicks) {
this.interval = 300;
this.intervalType = "millisecond";
} else if (range / (1 * 400) <= noTicks) {
this.interval = 400;
this.intervalType = "millisecond";
} else if (range / (1 * 500) <= noTicks) {
this.interval = 500;
this.intervalType = "millisecond";
} else if (range / (constants.secondDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 5) <= noTicks) {
this.interval = 5;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 10) <= noTicks) {
this.interval = 10;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 15) <= noTicks) {
this.interval = 15;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 20) <= noTicks) {
this.interval = 20;
this.intervalType = "second";
} else if (range / (constants.secondDuration * 30) <= noTicks) {
this.interval = 30;
this.intervalType = "second";
} else if (range / (constants.minuteDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 5) <= noTicks) {
this.interval = 5;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 10) <= noTicks) {
this.interval = 10;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 15) <= noTicks) {
this.interval = 15;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 20) <= noTicks) {
this.interval = 20;
this.intervalType = "minute";
} else if (range / (constants.minuteDuration * 30) <= noTicks) {
this.interval = 30;
this.intervalType = "minute";
} else if (range / (constants.hourDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "hour";
} else if (range / (constants.hourDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "hour";
} else if (range / (constants.hourDuration * 3) <= noTicks) {
this.interval = 3;
this.intervalType = "hour";
} else if (range / (constants.hourDuration * 6) <= noTicks) {
this.interval = 6;
this.intervalType = "hour";
} else if (range / (constants.dayDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "day";
} else if (range / (constants.dayDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "day";
} else if (range / (constants.dayDuration * 4) <= noTicks) {
this.interval = 4;
this.intervalType = "day";
} else if (range / (constants.weekDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "week";
} else if (range / (constants.weekDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "week";
} else if (range / (constants.weekDuration * 3) <= noTicks) {
this.interval = 3;
this.intervalType = "week";
} else if (range / (constants.monthDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "month";
} else if (range / (constants.monthDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "month";
} else if (range / (constants.monthDuration * 3) <= noTicks) {
this.interval = 3;
this.intervalType = "month";
} else if (range / (constants.monthDuration * 6) <= noTicks) {
this.interval = 6;
this.intervalType = "month";
} else if (range / (constants.yearDuration * 1) <= noTicks) {
this.interval = 1;
this.intervalType = "year";
} else if (range / (constants.yearDuration * 2) <= noTicks) {
this.interval = 2;
this.intervalType = "year";
} else if (range / (constants.yearDuration * 4) <= noTicks) {
this.interval = 4;
this.intervalType = "year";
} else {
this.interval = Math.floor(Axis.getNiceNumber(range / (noTicks - 1), true) / constants.yearDuration);
this.intervalType = "year";
}
}
if (this.sessionVariables.internalMinimum !== null)
this.minimum = this.sessionVariables.internalMinimum;
else {
this.minimum = min - minDiff / 2;
}
if (this.sessionVariables.internalMaximum !== null)
this.maximum = this.sessionVariables.internalMaximum;
else
this.maximum = max + minDiff / 2;
if (!this.valueFormatString) {
if (isLessThanTwoDataPoints) {
this.valueFormatString = "MMM DD YYYY HH:mm";
} else if (this.intervalType === "year") {
this.valueFormatString = "YYYY";
} else if (this.intervalType === "month") {
this.valueFormatString = "MMM YYYY";
} else if (this.intervalType === "week") {
this.valueFormatString = "MMM DD YYYY";
} else if (this.intervalType === "day") {
this.valueFormatString = "MMM DD YYYY";
} else if (this.intervalType === "hour") {
this.valueFormatString = "hh:mm TT";
} else if (this.intervalType === "minute") {
this.valueFormatString = "hh:mm TT";
} else if (this.intervalType === "second") {
this.valueFormatString = "hh:mm:ss TT";
} else if (this.intervalType === "millisecond") {
this.valueFormatString = "fff'ms'";
}
}
this.intervalstartTimePercent = this.getLabelStartPoint(new Date(this.minimum), this.intervalType, this.interval);
} else {
this.intervalType = "number";
range = Axis.getNiceNumber(max - min, false);
if (this._options && this._options.interval)
this.interval = this._options.interval;
else {
this.interval = Axis.getNiceNumber(range / (noTicks - 1), true);
}
if (this.sessionVariables.internalMinimum !== null)
this.minimum = this.sessionVariables.internalMinimum;
else
this.minimum = Math.floor(min / this.interval) * this.interval;
if (this.sessionVariables.internalMaximum !== null)
this.maximum = this.sessionVariables.internalMaximum;
else
this.maximum = Math.ceil(max / this.interval) * this.interval;
//var nfrac = Math.max(-Math.floor(Math.log(d)/Math.LN10), 0); //number of fractional digits to show
if (this.maximum === 0 && this.minimum === 0) {
if (this._options.minimum === 0) {
this.maximum += 10;
}
else if (this._options.maximum === 0) {
this.minimum -= 10;
}
if (this._options && typeof (this._options.interval) === "undefined") {
this.interval = Axis.getNiceNumber((this.maximum - this.minimum) / (noTicks - 1), true);
}
}
if (this.type === "axisX") {
if (!(this.sessionVariables.internalMinimum !== null)) {
this.minimum = min - minDiff / 2;
}
if (!(this.sessionVariables.internalMaximum !== null)) {
this.maximum = max + minDiff / 2;
}
this.intervalstartTimePercent = Math.floor((this.minimum + (this.interval * .2)) / this.interval) * this.interval;
} else if (this.type === "axisY") {
this.intervalstartTimePercent = this.minimum;
}
}
if (this.type === "axisX") {
this._absoluteMinimum = this._options && typeof (this._options.minimum) !== "undefined" ? this._options.minimum : this.dataInfo.min - minDiff / 2;
this._absoluteMaximum = this._options && typeof (this._options.maximum) !== "undefined" ? this._options.maximum : this.dataInfo.max + minDiff / 2;
}
//Set valueFormatString
if (!this.valueFormatString) {
this.valueFormatString = "#,##0.##";
range = Math.abs(this.maximum - this.minimum);
if (range < 1) {
var numberOfDecimals = Math.floor(Math.abs(Math.log(range) / Math.LN10)) + 2;
if (isNaN(numberOfDecimals) || !isFinite(numberOfDecimals))
numberOfDecimals = 2;
if (numberOfDecimals > 2) {
for (var i = 0; i < numberOfDecimals - 2; i++)
this.valueFormatString += "#";
}
}
}
//if (isDebugMode && window.console) {
// window.console.log(this.type + ": Min = " + this.minimum);
// window.console.log(this.type + ": Max = " + this.maximum);
// window.console.log(this.type + ": Interval = " + this.interval);
//}
}
Axis.getNiceNumber = function (x, round) {
var exp = Math.floor(Math.log(x) / Math.LN10);
var f = x / Math.pow(10, exp);
var nf;
if (round) {
if (f < 1.5)
nf = 1;
else if (f < 3)
nf = 2;
else if (f < 7)
nf = 5;
else
nf = 10;
}
else {
if (f <= 1)
nf = 1;
else if (f <= 2)
nf = 2;
else if (f <= 5)
nf = 5;
else nf = 10;
}
return Number((nf * Math.pow(10, exp)).toFixed(20));
}
Axis.prototype.getLabelStartPoint = function () {
var intervalInMilliseconds = convertToNumber(this.interval, this.intervalType);
var minimum = Math.floor((this.minimum) / intervalInMilliseconds) * intervalInMilliseconds;
var dateTime = new Date(minimum);
if (this.intervalType === "millisecond") {
//millisecond = dateTime.getMilliSecond();
//millisecond = Math.floor((millisecond + this.interval) / this.interval) * this.interval;
}
else if (this.intervalType === "second") {
if (dateTime.getMilliseconds() > 0) {
dateTime.setSeconds(dateTime.getSeconds() + 1);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "minute") {
if (dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setMinutes(dateTime.getMinutes() + 1);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "hour") {
if (dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setHours(dateTime.getHours() + 1);
dateTime.setMinutes(0);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "day") {
if (dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setDate(dateTime.getDate() + 1);
dateTime.setHours(0);
dateTime.setMinutes(0);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "week") {
if (dateTime.getDay() > 0 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setDate(dateTime.getDate() + (7 - dateTime.getDay()));
dateTime.setHours(0);
dateTime.setMinutes(0);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "month") {
if (dateTime.getDate() > 1 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setMonth(dateTime.getMonth() + 1);
dateTime.setDate(1);
dateTime.setHours(0);
dateTime.setMinutes(0);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
else if (this.intervalType === "year") {
if (dateTime.getMonth() > 0 || dateTime.getDate() > 1 || dateTime.getHours() > 0 || dateTime.getMinutes() > 0 || dateTime.getSeconds() > 0 || dateTime.getMilliseconds() > 0) {
dateTime.setFullYear(dateTime.getFullYear() + 1);
dateTime.setMonth(0);
dateTime.setDate(1);
dateTime.setHours(0);
dateTime.setMinutes(0);
dateTime.setSeconds(0);
dateTime.setMilliseconds(0);
}
}
return dateTime;
}
//#endregion Axis
//#region StripLine
function StripLine(chart, options, theme, id, axis) {
StripLine.base.constructor.call(this, "StripLine", options, theme, axis);
this.id = id;
this.chart = chart;
this.ctx = this.chart.ctx;
this.label = this.label;
this._thicknessType = "pixel";
if (this.startValue !== null && this.endValue !== null) {
this.value = ((this.startValue.getTime ? this.startValue.getTime() : this.startValue) + (this.endValue.getTime ? this.endValue.getTime() : this.endValue)) / 2;
this.thickness = Math.max(this.endValue - this.startValue);
this._thicknessType = "value";
}
}
extend(StripLine, CanvasJSObject);
StripLine.prototype.render = function () {
var xy = this.parent.getPixelCoordinatesOnAxis(this.value);
var lineWidth = Math.abs(this._thicknessType === "pixel" ? this.thickness : this.parent.conversionParameters.pixelPerUnit * this.thickness);
if (lineWidth > 0) {
//var opacity = this.opacity === null ? ( this.showOnTop && this._thicknessType === "pixel" ? 1 : 1) : this.opacity;
var opacity = this.opacity === null ? 1 : this.opacity;
this.ctx.strokeStyle = this.color;
this.ctx.beginPath();
var oldGlobalAlpha = this.ctx.globalAlpha;
this.ctx.globalAlpha = opacity;
var hexColor = intToHexColorString(this.id);
var x1, x2, y1, y2;
this.ctx.lineWidth = lineWidth;
if (this.ctx.setLineDash) {
this.ctx.setLineDash(getLineDashArray(this.lineDashType, lineWidth));
}
if (this.parent._position === "bottom" || this.parent._position === "top") {
var stripX = (this.ctx.lineWidth % 2 === 1) ? (xy.x << 0) + .5 : (xy.x << 0);
x1 = x2 = stripX;
y1 = this.chart.plotArea.y1;
y2 = this.chart.plotArea.y2;
}
else if (this.parent._position === "left" || this.parent._position === "right") {
var stripY = (this.ctx.lineWidth % 2 === 1) ? (xy.y << 0) + .5 : (xy.y << 0);
y1 = y2 = stripY;
x1 = this.chart.plotArea.x1;
x2 = this.chart.plotArea.x2;
}
this.ctx.moveTo(x1, y1);
this.ctx.lineTo(x2, y2);
this.ctx.stroke();
this.ctx.globalAlpha = oldGlobalAlpha;
}
};
//#endregion StripLine
//#region ToolTip
function ToolTip(chart, options, theme) {
ToolTip.base.constructor.call(this, "ToolTip", options, theme);
this.chart = chart;
this.canvas = chart.canvas;
this.ctx = this.chart.ctx;
this.currentSeriesIndex = -1;
this.currentDataPointIndex = -1;
this._timerId = 0;
this._prevX = NaN;
this._prevY = NaN;
this._initialize();
}
extend(ToolTip, CanvasJSObject);
ToolTip.prototype._initialize = function () {
if (this.enabled) {
this.container = document.createElement("div");
this.container.setAttribute("class", "canvasjs-chart-tooltip");
this.container.style.position = "absolute";
this.container.style.height = "auto";
this.container.style.boxShadow = "1px 1px 2px 2px rgba(0,0,0,0.1)";
this.container.style.zIndex = "1000";
//this.container.style.pointerEvents = "none";
this.container.style.display = "none";
//this.container.style.whiteSpace = "no-wrap";
var toolTipHtml = " Sample Tooltip
";
this.container.innerHTML = toolTipHtml;
this.contentDiv = this.container.firstChild;
this.container.style.borderRadius = this.contentDiv.style.borderRadius;
this.chart._canvasJSContainer.appendChild(this.container);
}
}
ToolTip.prototype.mouseMoveHandler = function (x, y) {
if (!(this._lastUpdated && (new Date().getTime() - this._lastUpdated) < 40)) {
this._lastUpdated = new Date().getTime();
this._updateToolTip(x, y);
}
}
ToolTip.prototype._updateToolTip = function (mouseX, mouseY) {
//return;
if (this.chart.disableToolTip) // Disabled during animation, etc
return;
if (typeof (mouseX) === "undefined" || typeof (mouseY) === "undefined") {
if (isNaN(this._prevX) || isNaN(this._prevY))
return;
else {
mouseX = this._prevX;
mouseY = this._prevY;
}
} else {
this._prevX = mouseX;
this._prevY = mouseY;
}
var dataPoint = null;
var dataSeries = null;
var toolTipContent = "";
var entries = [];
var toolTipRight;
var toolTipBottom;
var x = 0;
if (this.shared && this.enabled && this.chart.plotInfo.axisPlacement !== "none") {
// && this.chart.plotInfo.axisPlacement !== "none"
if (this.chart.plotInfo.axisPlacement === "xySwapped") {
x = (this.chart.axisX.maximum - this.chart.axisX.minimum) / this.chart.axisX.lineCoordinates.height * ((this.chart.axisX.lineCoordinates.y2 - mouseY)) + this.chart.axisX.minimum;
}
else {
x = (this.chart.axisX.maximum - this.chart.axisX.minimum) / this.chart.axisX.lineCoordinates.width * (mouseX - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.minimum;
}
var nearbyEntries = [];
for (var i = 0; i < this.chart.data.length; i++) {
var entry = this.chart.data[i].getDataPointAtX(x, true);
if (entry && entry.index >= 0) {
entry.dataSeries = this.chart.data[i];
if (entry.dataPoint.y !== null)
nearbyEntries.push(entry);
}
}
if (nearbyEntries.length === 0)
return;
nearbyEntries.sort(function (entry1, entry2) {
return entry1.distance - entry2.distance;
});
var closest = nearbyEntries[0];
for (i = 0; i < nearbyEntries.length; i++) {
if (nearbyEntries[i].dataPoint.x.valueOf() === closest.dataPoint.x.valueOf())
entries.push(nearbyEntries[i]);
}
nearbyEntries = null;
} else {
var dataPointInfo = this.chart.getDataPointAtXY(mouseX, mouseY, true);
//dataPointInfo = null;
if (dataPointInfo) {
this.currentDataPointIndex = dataPointInfo.dataPointIndex;
this.currentSeriesIndex = dataPointInfo.dataSeries.index;
} else if (isCanvasSupported) {
var id = getObjectId(mouseX, mouseY, this.chart._eventManager.ghostCtx);
if (id > 0 && typeof this.chart._eventManager.objectMap[id] !== "undefined") {//DataPoint/DataSeries event
eventObject = this.chart._eventManager.objectMap[id];
if (eventObject.objectType === "legendItem")
return;
//if (this.currentSeriesIndex === eventObject.dataSeriesIndex && this.currentDataPointIndex === eventObject.dataPointIndex)
// return;
//else {
this.currentSeriesIndex = eventObject.dataSeriesIndex;
this.currentDataPointIndex = eventObject.dataPointIndex >= 0 ? eventObject.dataPointIndex : -1;
//}
//window.console.log("id: " + id + "; hex: " + intToHexColorString(id));
} else
this.currentDataPointIndex = -1;
} else
this.currentDataPointIndex = -1;
if (this.currentSeriesIndex >= 0) {
dataSeries = this.chart.data[this.currentSeriesIndex];
var entry = {};
if (this.currentDataPointIndex >= 0) {
dataPoint = dataSeries.dataPoints[this.currentDataPointIndex];
entry.dataSeries = dataSeries;
entry.dataPoint = dataPoint;
entry.index = this.currentDataPointIndex;
entry.distance = Math.abs(dataPoint.x - x);
} else if (this.enabled && (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea"
|| dataSeries.type === "splineArea" || dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea"
|| dataSeries.type === "candlestick" || dataSeries.type === "ohlc")) {
var x = (this.chart.axisX.maximum - this.chart.axisX.minimum) / this.chart.axisX.lineCoordinates.width * (mouseX - this.chart.axisX.lineCoordinates.x1) + this.chart.axisX.minimum.valueOf();
entry = dataSeries.getDataPointAtX(x, true);
entry.dataSeries = dataSeries;
this.currentDataPointIndex = entry.index;
dataPoint = entry.dataPoint;
} else {
//this.hide();
return;
}
if (entry.dataPoint.y !== null)
entries.push(entry);
}
}
if (entries.length > 0) {
this.highlightObjects(entries);
if (this.enabled) {
var toolTipInnerHtml = "";
toolTipInnerHtml = this.getToolTipInnerHTML({ entries: entries });
if (toolTipInnerHtml !== null) {
this.contentDiv.innerHTML = toolTipInnerHtml;
this.contentDiv.innerHTML = toolTipInnerHtml;
var previouslyHidden = false;
if (this.container.style.display === "none") {
previouslyHidden = true;
this.container.style.display = "block";
}
try {
this.contentDiv.style.background = this.backgroundColor ? this.backgroundColor : isCanvasSupported ? "rgba(255,255,255,.9)" : "rgb(255,255,255)";
this.contentDiv.style.borderRightColor = this.contentDiv.style.borderLeftColor = this.contentDiv.style.borderColor = this.borderColor ? this.borderColor : entries[0].dataPoint.color ? entries[0].dataPoint.color : entries[0].dataSeries.color ? entries[0].dataSeries.color : entries[0].dataSeries._colorSet[entries[0].index % entries[0].dataSeries._colorSet.length];
this.contentDiv.style.borderWidth = (this.borderThickness || this.borderThickness === 0) ? this.borderThickness + "px" : 2 + "px";
this.contentDiv.style.borderRadius = (this.cornerRadius || this.cornerRadius === 0) ? this.cornerRadius + "px" : 5 + "px";
this.container.style.borderRadius = this.contentDiv.style.borderRadius;
this.contentDiv.style.fontSize = (this.fontSize || this.fontSize === 0) ? this.fontSize + "px" : 14 + "px";
this.contentDiv.style.color = this.fontColor ? this.fontColor : "#000000";
this.contentDiv.style.fontFamily = this.fontFamily ? this.fontFamily : "Calibri, Arial, Georgia, serif;";
this.contentDiv.style.fontWeight = this.fontWeight ? this.fontWeight : "normal";
this.contentDiv.style.fontStyle = this.fontStyle ? this.fontStyle : isCanvasSupported ? "italic" : "normal";
} catch (e) { }
if (entries[0].dataSeries.type === "pie" || entries[0].dataSeries.type === "doughnut" || entries[0].dataSeries.type === "funnel" || entries[0].dataSeries.type === "bar" || entries[0].dataSeries.type === "rangeBar" || entries[0].dataSeries.type === "stackedBar" || entries[0].dataSeries.type === "stackedBar100") {
toolTipLeft = mouseX - 10 - this.container.clientWidth;
} else {
toolTipLeft = (((this.chart.axisX.lineCoordinates.width / Math.abs(this.chart.axisX.maximum - this.chart.axisX.minimum)) * Math.abs(entries[0].dataPoint.x - this.chart.axisX.minimum)) + this.chart.axisX.lineCoordinates.x1 + .5) - this.container.clientWidth << 0;
toolTipLeft -= 10;
}
if (toolTipLeft < 0) {
toolTipLeft += this.container.clientWidth + 20;
}
if (toolTipLeft + this.container.clientWidth > this.chart._container.clientWidth)
toolTipLeft = Math.max(0, this.chart._container.clientWidth - this.container.clientWidth);
toolTipLeft += "px";
if (entries.length === 1 && !this.shared && (entries[0].dataSeries.type === "line" || entries[0].dataSeries.type === "stepLine" || entries[0].dataSeries.type === "spline" || entries[0].dataSeries.type === "area" || entries[0].dataSeries.type === "stepArea" || entries[0].dataSeries.type === "splineArea" || entries[0].dataSeries.type === "stackedArea" || entries[0].dataSeries.type === "stackedArea100")) {
toolTipBottom = (entries[0].dataSeries.axisY.lineCoordinates.y2 - entries[0].dataSeries.axisY.lineCoordinates.height / Math.abs(entries[0].dataSeries.axisY.maximum - entries[0].dataSeries.axisY.minimum) * Math.abs(entries[0].dataPoint.y - entries[0].dataSeries.axisY.minimum) + .5) << 0;
} else if (entries[0].dataSeries.type === "bar" || entries[0].dataSeries.type === "rangeBar" || entries[0].dataSeries.type === "stackedBar" || entries[0].dataSeries.type === "stackedBar100") {
toolTipBottom = (entries[0].dataSeries.axisX.lineCoordinates.y2 - entries[0].dataSeries.axisX.lineCoordinates.height / Math.abs(entries[0].dataSeries.axisX.maximum - entries[0].dataSeries.axisX.minimum) * Math.abs(entries[0].dataPoint.x - entries[0].dataSeries.axisX.minimum) + .5) << 0;
}
else {
toolTipBottom = mouseY;
}
toolTipBottom = (-toolTipBottom + 10);
if (toolTipBottom + this.container.clientHeight + 5 > 0) {
toolTipBottom -= toolTipBottom + this.container.clientHeight + 5 - 0
}
toolTipBottom += "px";
//this.container.style.right = toolTipRight;
this.container.style.left = toolTipLeft;
this.container.style.bottom = toolTipBottom;
if (!this.animationEnabled || previouslyHidden) {
this.disableAnimation();
}
else
this.enableAnimation();
} else {
this.hide(false);
}
}
//if (isDebugMode)
// console.log("searchX: " + x + " x: " + searchResult.dataPoint.x + "; y: " + searchResult.dataPoint.y + "; distance: " + searchResult.distance + "; steps: " + steps);
}
}
ToolTip.prototype.highlightObjects = function (entries) {
//if (!this.enabled)
// return;
//this.chart.overlaidCanvasCtx.clearRect(0, 0, this.chart.overlaidCanvas.width, this.chart.overlaidCanvas.height);
var overlaidCanvasCtx = this.chart.overlaidCanvasCtx;
this.chart.resetOverlayedCanvas();
overlaidCanvasCtx.save();
var plotArea = this.chart.plotArea;
var offset = 0;
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
var eventObject = this.chart._eventManager.objectMap[entry.dataSeries.dataPointIds[entry.index]];
if (!eventObject || !eventObject.objectType || eventObject.objectType !== "dataPoint")
continue;
var dataSeries = this.chart.data[eventObject.dataSeriesIndex];
var dataPoint = dataSeries.dataPoints[eventObject.dataPointIndex];
var index = eventObject.dataPointIndex;
if (dataPoint.highlightEnabled !== false && (dataSeries.highlightEnabled === true || dataPoint.highlightEnabled === true)) {
if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "scatter"
|| dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea"
|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100"
|| dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y1, this.chart.overlaidCanvasCtx);
markerProps.size = Math.max(markerProps.size * 1.5 << 0, 10);
markerProps.borderColor = markerProps.borderColor || "#FFFFFF";
markerProps.borderThickness = markerProps.borderThickness || Math.ceil(markerProps.size * .1);
//overlaidCanvasCtx.globalAlpha = .8;
RenderHelper.drawMarkers([markerProps]);
//overlaidCanvasCtx.globalAlpha = .8;
if (typeof (eventObject.y2) !== "undefined") {
var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y2, this.chart.overlaidCanvasCtx);
markerProps.size = Math.max(markerProps.size * 1.5 << 0, 10);
markerProps.borderColor = markerProps.borderColor || "#FFFFFF";
markerProps.borderThickness = markerProps.borderThickness || Math.ceil(markerProps.size * .1);
//overlaidCanvasCtx.globalAlpha = .8;
RenderHelper.drawMarkers([markerProps]);
//overlaidCanvasCtx.globalAlpha = .8;
}
} else if (dataSeries.type === "bubble") {
var markerProps = dataSeries.getMarkerProperties(index, eventObject.x1, eventObject.y1, this.chart.overlaidCanvasCtx);
markerProps.size = eventObject.size;
markerProps.color = "white";
markerProps.borderColor = "white";
//markerProps.borderThickness = 2;
overlaidCanvasCtx.globalAlpha = .3;
RenderHelper.drawMarkers([markerProps]);
overlaidCanvasCtx.globalAlpha = 1;
} else if (dataSeries.type === "column" || dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100"
|| dataSeries.type === "bar" || dataSeries.type === "rangeBar" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
|| dataSeries.type === "rangeColumn") {
drawRect(overlaidCanvasCtx, eventObject.x1, eventObject.y1, eventObject.x2, eventObject.y2, "white", 0, null, false, false, false, false, .3);
}
else if (dataSeries.type === "pie" || dataSeries.type === "doughnut") {
drawSegment(overlaidCanvasCtx, eventObject.center, eventObject.radius, "white", dataSeries.type, eventObject.startAngle, eventObject.endAngle, .3);
} else if (dataSeries.type === "candlestick") {
overlaidCanvasCtx.globalAlpha = 1;
overlaidCanvasCtx.strokeStyle = eventObject.color;
overlaidCanvasCtx.lineWidth = eventObject.borderThickness * 2;
offset = (overlaidCanvasCtx.lineWidth) % 2 === 0 ? 0 : .5;
overlaidCanvasCtx.beginPath();
overlaidCanvasCtx.moveTo(eventObject.x3 - offset, eventObject.y2);
overlaidCanvasCtx.lineTo(eventObject.x3 - offset, Math.min(eventObject.y1, eventObject.y4));
overlaidCanvasCtx.stroke();
overlaidCanvasCtx.beginPath();
overlaidCanvasCtx.moveTo(eventObject.x3 - offset, Math.max(eventObject.y1, eventObject.y4));
overlaidCanvasCtx.lineTo(eventObject.x3 - offset, eventObject.y3);
overlaidCanvasCtx.stroke();
drawRect(overlaidCanvasCtx, eventObject.x1, Math.min(eventObject.y1, eventObject.y4), eventObject.x2, Math.max(eventObject.y1, eventObject.y4), "transparent", eventObject.borderThickness * 2, eventObject.color, false, false, false, false);
overlaidCanvasCtx.globalAlpha = 1;
} else if (dataSeries.type === "ohlc") {
overlaidCanvasCtx.globalAlpha = 1;
overlaidCanvasCtx.strokeStyle = eventObject.color;
overlaidCanvasCtx.lineWidth = eventObject.borderThickness * 2;
offset = (overlaidCanvasCtx.lineWidth) % 2 === 0 ? 0 : .5;
overlaidCanvasCtx.beginPath();
overlaidCanvasCtx.moveTo(eventObject.x3 - offset, eventObject.y2);
overlaidCanvasCtx.lineTo(eventObject.x3 - offset, eventObject.y3);
overlaidCanvasCtx.stroke();
overlaidCanvasCtx.beginPath();
overlaidCanvasCtx.moveTo(eventObject.x3, eventObject.y1);
overlaidCanvasCtx.lineTo(eventObject.x1, eventObject.y1);
overlaidCanvasCtx.stroke();
overlaidCanvasCtx.beginPath();
overlaidCanvasCtx.moveTo(eventObject.x3, eventObject.y4);
overlaidCanvasCtx.lineTo(eventObject.x2, eventObject.y4);
overlaidCanvasCtx.stroke();
overlaidCanvasCtx.globalAlpha = 1;
}
}
}
overlaidCanvasCtx.globalAlpha = 1;
overlaidCanvasCtx.beginPath();
return;
}
ToolTip.prototype.getToolTipInnerHTML = function (e) {
var entries = e.entries;
var toolTipInnerHtml = null;
var dataSeries = null;
var dataPoint = null;
var index = 0;
var color = null;
var toolTipContent = "";
var isToolTipDefinedInData = true;
for (var i = 0; i < entries.length; i++) {
if (entries[i].dataSeries.toolTipContent || entries[i].dataPoint.toolTipContent) {
isToolTipDefinedInData = false;
break;
}
}
if (isToolTipDefinedInData && ((this.content && typeof (this.content) === "function") || this.contentFormatter)) {
var param = { chart: this.chart, toolTip: this._options, entries: entries };
toolTipInnerHtml = this.contentFormatter ? this.contentFormatter(param) : this.content(param);
} else {
if (this.shared && this.chart.plotInfo.axisPlacement !== "none") {
var toolTipInnerHtmlPrefix = "";
for (var i = 0; i < entries.length; i++) {
dataSeries = entries[i].dataSeries;
dataPoint = entries[i].dataPoint;
index = entries[i].index;
toolTipContent = "";
if (i === 0 && isToolTipDefinedInData && !this.content) {
toolTipInnerHtmlPrefix += typeof (this.chart.axisX.labels[dataPoint.x]) !== "undefined" ? this.chart.axisX.labels[dataPoint.x] : "{x}";
toolTipInnerHtmlPrefix += "";
toolTipInnerHtmlPrefix = this.chart.replaceKeywordsWithValue(toolTipInnerHtmlPrefix, dataPoint, dataSeries, index);
}
//Allows disabling of toolTip for individual dataPoints/dataSeries
if (dataPoint.toolTipContent === null || (typeof (dataPoint.toolTipContent) === "undefined" && dataSeries._options.toolTipContent === null))
continue;
if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "column" || dataSeries.type === "bar" || dataSeries.type === "scatter"
|| dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100") {
toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "{name}: {y}";
}
else if (dataSeries.type === "bubble") {
toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "{name}: {y}, {z}";
} else if (dataSeries.type === "pie" || dataSeries.type === "doughnut" || dataSeries.type === "funnel") {
toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : " {y}";
} else if (dataSeries.type === "rangeColumn" || dataSeries.type === "rangeBar" || dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "{name}: {y[0]}, {y[1]}";
} else if (dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
toolTipContent += dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "{name}:"
+ "
Open: {y[0]}"
+ "
High: {y[1]}"
+ "
Low: {y[2]}"
+ "
Close: {y[3]}";
}
if (toolTipInnerHtml === null)
toolTipInnerHtml = "";
if (this.reversed === true) {
toolTipInnerHtml = this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index) + toolTipInnerHtml;
if (i < entries.length - 1)
toolTipInnerHtml = "" + toolTipInnerHtml;
} else {
toolTipInnerHtml += this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index);
if (i < entries.length - 1)
toolTipInnerHtml += "";
}
}
if (toolTipInnerHtml !== null)
toolTipInnerHtml = toolTipInnerHtmlPrefix + toolTipInnerHtml;
} else {
dataSeries = entries[0].dataSeries;
dataPoint = entries[0].dataPoint;
index = entries[0].index;
//Allows disabling of toolTip for individual dataPoints/dataSeries
if (dataPoint.toolTipContent === null || (typeof (dataPoint.toolTipContent) === "undefined" && dataSeries._options.toolTipContent === null))
return null;
if (dataSeries.type === "line" || dataSeries.type === "stepLine" || dataSeries.type === "spline" || dataSeries.type === "area" || dataSeries.type === "stepArea" || dataSeries.type === "splineArea" || dataSeries.type === "column" || dataSeries.type === "bar" || dataSeries.type === "scatter"
|| dataSeries.type === "stackedColumn" || dataSeries.type === "stackedColumn100" || dataSeries.type === "stackedBar" || dataSeries.type === "stackedBar100"
|| dataSeries.type === "stackedArea" || dataSeries.type === "stackedArea100") {
toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "" + (dataPoint.label ? "{label}" : "{x}") + " : {y}";
} else if (dataSeries.type === "bubble") {
toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "" + (dataPoint.label ? "{label}" : "{x}") + ": {y}, {z}";
} else if (dataSeries.type === "pie" || dataSeries.type === "doughnut" || dataSeries.type === "funnel") {
toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : (dataPoint.name ? "{name}: " : dataPoint.label ? "{label}: " : "") + "{y}";
} else if (dataSeries.type === "rangeColumn" || dataSeries.type === "rangeBar" || dataSeries.type === "rangeArea" || dataSeries.type === "rangeSplineArea") {
toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "" + (dataPoint.label ? "{label}" : "{x}") + " : {y[0]}, {y[1]}";
} else if (dataSeries.type === "candlestick" || dataSeries.type === "ohlc") {
toolTipContent = dataPoint.toolTipContent ? dataPoint.toolTipContent : dataSeries.toolTipContent ? dataSeries.toolTipContent : this.content && typeof (this.content) !== "function" ? this.content : "" + (dataPoint.label ? "{label}" : "{x}") + ""
+ "
Open: {y[0]}"
+ "
High: {y[1]}"
+ "
Low: {y[2]}"
+ "
Close: {y[3]}";
}
if (toolTipInnerHtml === null)
toolTipInnerHtml = "";
toolTipInnerHtml += this.chart.replaceKeywordsWithValue(toolTipContent, dataPoint, dataSeries, index);
}
}
return toolTipInnerHtml;
}
ToolTip.prototype.enableAnimation = function () {
if (this.container.style.WebkitTransition)
return;
this.container.style.WebkitTransition = "left .2s ease-out, bottom .2s ease-out";
this.container.style.MozTransition = "left .2s ease-out, bottom .2s ease-out";
this.container.style.MsTransition = "left .2s ease-out, bottom .2s ease-out";
this.container.style.transition = "left .2s ease-out, bottom .2s ease-out";
}
ToolTip.prototype.disableAnimation = function () {
if (!this.container.style.WebkitTransition)
return;
this.container.style.WebkitTransition = "";
this.container.style.MozTransition = "";
this.container.style.MsTransition = "";
this.container.style.transition = "";
}
ToolTip.prototype.hide = function (resetOverlayedCanvas) {
if (!this.enabled)
return;
resetOverlayedCanvas = typeof (resetOverlayedCanvas) === "undefined" ? true : resetOverlayedCanvas;
this.container.style.display = "none";
this.currentSeriesIndex = -1;
this._prevX = NaN;
this._prevY = NaN;
//this.chart.overlaidCanvasCtx.clearRect(0, 0, this.chart.overlaidCanvas.width, this.chart.overlaidCanvas.height);
if (resetOverlayedCanvas)
this.chart.resetOverlayedCanvas();
}
Chart.prototype.getPercentAndTotal = function (ds, dp) {
var dpX = null;
var total = null;
var percent = null;
if (ds.type.indexOf("stacked") >= 0) {
total = 0;
dpX = dp.x.getTime ? dp.x.getTime() : dp.x;
if (dpX in ds.plotUnit.yTotals) {
total = ds.plotUnit.yTotals[dpX];
if (!isNaN(dp.y)) {
if (total === 0)
percent = 0;
else
percent = (dp.y / total) * 100;
}
else
percent = 0;
}
} else if (ds.type === "pie" || ds.type === "doughnut") {
total = 0;
for (i = 0; i < ds.dataPoints.length; i++) {
if (!isNaN(ds.dataPoints[i].y))
total += ds.dataPoints[i].y;
}
if (!isNaN(dp.y))
percent = (dp.y / total) * 100;
else
percent = 0;
}
return { percent: percent, total: total };
}
Chart.prototype.replaceKeywordsWithValue = function (str, dp, ds, dpIndex, indexKeywordValue) {
//var regex = /\{\s*[a-zA-Z]+\s*\}|"[^"]*"|'[^']*'/g;
var regex = /\{.*?\}|"[^"]*"|'[^']*'/g;
var chart = this;
indexKeywordValue = typeof (indexKeywordValue) === "undefined" ? 0 : indexKeywordValue;
if ((ds.type.indexOf("stacked") >= 0 || (ds.type === "pie" || ds.type === "doughnut")) && (str.indexOf("#percent") >= 0 || str.indexOf("#total") >= 0)) {
var percent = "#percent";
var total = "#total";
var dpX = null;
var percentAndTotal = this.getPercentAndTotal(ds, dp);
total = percentAndTotal.total ? percentAndTotal.total : total;
percent = isNaN(percentAndTotal.percent) ? percent : percentAndTotal.percent;
do {
var percentFormatString = "";
if (ds.percentFormatString)
percentFormatString = ds.percentFormatString;
else {
percentFormatString = "#,##0.";
var numberOfDecimals = Math.max(Math.ceil(Math.log(1 / Math.abs(percent)) / Math.LN10), 2);
if (isNaN(numberOfDecimals) || !isFinite(numberOfDecimals))
numberOfDecimals = 2;
for (var n = 0; n < numberOfDecimals; n++) {
percentFormatString += "#";
}
}
str = str.replace("#percent", numberFormat(percent, percentFormatString, chart._cultureInfo));
str = str.replace("#total", numberFormat(total, ds.yValueFormatString ? ds.yValueFormatString : "#,##0.########"));
} while (str.indexOf("#percent") >= 0 || str.indexOf("#total") >= 0);
}
var fcn = function ($0) {
if (($0[0] === "\"" && $0[$0.length - 1] === "\"") || ($0[0] === "\'" && $0[$0.length - 1] === "\'"))
return $0.slice(1, $0.length - 1);
var key = trimString($0.slice(1, $0.length - 1));
key = key.replace("#index", indexKeywordValue);
var index = null;
try {
var match = key.match(/(.*?)\s*\[\s*(.*?)\s*\]/);
if (match && match.length > 0) {
index = trimString(match[2]);
key = trimString(match[1]);
}
} catch (e) { };
var obj = null;
if (key === "color") {
return dp.color ? dp.color : ds.color ? ds.color : ds._colorSet[dpIndex % ds._colorSet.length];
}
if (dp.hasOwnProperty(key))
obj = dp;
else if (ds.hasOwnProperty(key))
obj = ds;
else return "";
var value = obj[key];
if (index !== null)
value = value[index];
if (key === "x") {
if (chart.axisX && chart.plotInfo.axisXValueType === "dateTime")
return dateFormat(value, dp.xValueFormatString ? dp.xValueFormatString : ds.xValueFormatString ? ds.xValueFormatString : chart.axisX && chart.axisX.valueFormatString ? chart.axisX.valueFormatString : "DD MMM YY", chart._cultureInfo);
else
return numberFormat(value, dp.xValueFormatString ? dp.xValueFormatString : ds.xValueFormatString ? ds.xValueFormatString : "#,##0.########", chart._cultureInfo);
} else if (key === "y")
return numberFormat(value, dp.yValueFormatString ? dp.yValueFormatString : ds.yValueFormatString ? ds.yValueFormatString : "#,##0.########", chart._cultureInfo);
else if (key === "z")
return numberFormat(value, dp.zValueFormatString ? dp.zValueFormatString : ds.zValueFormatString ? ds.zValueFormatString : "#,##0.########", chart._cultureInfo);
else
return value;
}
return str.replace(regex, fcn);
}
//#endregion ToolTip
//#region Event Manager
function EventManager(chart) {
this.chart = chart;
this.lastObjectId = 0;
var _this = this;
this.objectMap = [];
this.rectangularRegionEventSubscriptions = [];
this.previousDataPointEventObject = null;
//this.previousDataSeriesEventObject = null;
this.ghostCanvas = createCanvas(this.chart.width, this.chart.height);
//this.ghostCanvas.width = this.chart.width;
//this.ghostCanvas.height = this.chart.height;
this.ghostCtx = this.ghostCanvas.getContext("2d");
var eventHandler = function (ev) {
_this.mouseEventHandler.call(_this, ev);
};
this.mouseoveredObjectMaps = [];
//this.chart.canvas.addEventListener("mouseover", eventHandler);
//this.chart.canvas.addEventListener("mousemove", eventHandler);
//this.chart.canvas.addEventListener("mouseout", eventHandler);
//this.chart.canvas.addEventListener("click", eventHandler);
}
EventManager.prototype.reset = function () {
this.lastObjectId = 0;
this.objectMap = [];
this.rectangularRegionEventSubscriptions = [];
this.previousDataPointEventObject = null;
this.eventObjects = [];
//this.ghostCanvas.width = this.chart.width;
//this.ghostCanvas.height = this.chart.height;
if (isCanvasSupported) {
this.ghostCtx.clearRect(0, 0, this.chart.width, this.chart.height);
this.ghostCtx.beginPath();
}
}
EventManager.prototype.getNewObjectTrackingId = function () {
return ++this.lastObjectId;
}
EventManager.prototype.mouseEventHandler = function (ev) {
if (ev.type !== "mousemove" && ev.type !== "click")
return;
var eventObjectMaps = [];
var xy = getMouseCoordinates(ev);
var id = null;
//var dataPointInfo = this.chart.getDataPointAtXY(xy.x, xy.y, false);
//if (dataPointInfo) {
// id = dataPointInfo.dataSeries.dataPointIds[dataPointInfo.dataPointIndex];
//} else if (isCanvasSupported) {//IE9+
// id = getObjectId(xy.x, xy.y, this.ghostCtx);
//}
id = this.chart.getObjectAtXY(xy.x, xy.y, false);
if (id && typeof (this.objectMap[id]) !== "undefined") {
var eventObjectMap = this.objectMap[id];
if (eventObjectMap.objectType === "dataPoint") {
var dataSeries = this.chart.data[eventObjectMap.dataSeriesIndex];
var dataPoint = dataSeries.dataPoints[eventObjectMap.dataPointIndex];
var dataPointIndex = eventObjectMap.dataPointIndex;
//Event Parameter should not contain reference to dataSeries directly. But to its options.
eventObjectMap.eventParameter = {
x: xy.x, y: xy.y, dataPoint: dataPoint,
dataSeries: dataSeries._options, dataPointIndex: dataPointIndex, dataSeriesIndex: dataSeries.index,
chart: this.chart._publicChartReference
};
eventObjectMap.eventContext = { context: dataPoint, userContext: dataPoint, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click" };
eventObjectMaps.push(eventObjectMap);
//Add Dataseries too because mouse event on dataPoint also means there is an event on dataSeries. DataSeries is not present on ghost canvas
eventObjectMap = this.objectMap[dataSeries.id];
//Event Parameter should not contain reference to dataSeries directly. But to its options.
eventObjectMap.eventParameter = {
x: xy.x, y: xy.y,
dataPoint: dataPoint, dataSeries: dataSeries._options, dataPointIndex: dataPointIndex, dataSeriesIndex: dataSeries.index,
chart: this.chart._publicChartReference
};
eventObjectMap.eventContext = { context: dataSeries, userContext: dataSeries._options, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click" };
eventObjectMaps.push(this.objectMap[dataSeries.id]);
}
//else if (eventObjectMap.objectType === "stripLine") {
// //Event Parameter should not contain reference to stripLine directly. But to its options.
// eventObjectMap.eventParameter = { x: xy.x, y: xy.y, stripLine: eventObjectMap.stripLine._options, axis: eventObjectMap.axis._options, stripLineIndex: eventObjectMap.stripLineIndex };
// eventObjectMap.eventContext = { context: eventObjectMap.stripLine, userContext: eventObjectMap.stripLine._options, mouseover: "mouseover", mousemove: "mousemove", mouseout: "mouseout", click: "click" };
// eventObjectMaps.push(eventObjectMap);
//}
else if (eventObjectMap.objectType === "legendItem") {
var dataSeries = this.chart.data[eventObjectMap.dataSeriesIndex];
var dataPoint = eventObjectMap.dataPointIndex !== null ? dataSeries.dataPoints[eventObjectMap.dataPointIndex] : null;
//Event Parameter should not contain reference to DataSeries directly. But to its options.
eventObjectMap.eventParameter = {
x: xy.x, y: xy.y,
dataSeries: dataSeries._options, dataPoint: dataPoint, dataPointIndex: eventObjectMap.dataPointIndex, dataSeriesIndex: eventObjectMap.dataSeriesIndex,
chart: this.chart._publicChartReference
};
eventObjectMap.eventContext = { context: this.chart.legend, userContext: this.chart.legend._options, mouseover: "itemmouseover", mousemove: "itemmousemove", mouseout: "itemmouseout", click: "itemclick" };
eventObjectMaps.push(eventObjectMap);
}
}
//Fire mouseout if existing mouseovered objects are not present in the objectmap.
var mouseOutObjectMapsExcluded = [];
for (var i = 0; i < this.mouseoveredObjectMaps.length; i++) {
var mouseOut = true;
for (var j = 0; j < eventObjectMaps.length; j++) {
if (eventObjectMaps[j].id === this.mouseoveredObjectMaps[i].id) {
mouseOut = false;
break;
}
}
if (mouseOut) {
this.fireEvent(this.mouseoveredObjectMaps[i], "mouseout", ev);
} else {
mouseOutObjectMapsExcluded.push(this.mouseoveredObjectMaps[i]);
}
}
this.mouseoveredObjectMaps = mouseOutObjectMapsExcluded;
//Process new eventObectMaps
//If they already don't exist, add them and fire mouseover
//If ev.type is mousemove, then just fire mousemove
//If ev.type is click, then fire two events - click followed by mousemove
for (var i = 0; i < eventObjectMaps.length; i++) {
var existing = false;
for (var j = 0; j < this.mouseoveredObjectMaps.length; j++) {
if (eventObjectMaps[i].id === this.mouseoveredObjectMaps[j].id) {
existing = true;
break;
}
}
if (!existing) {
this.fireEvent(eventObjectMaps[i], "mouseover", ev);
this.mouseoveredObjectMaps.push(eventObjectMaps[i]);
}
if (ev.type === "click") {
this.fireEvent(eventObjectMaps[i], "click", ev);
} else if (ev.type === "mousemove") {
this.fireEvent(eventObjectMaps[i], "mousemove", ev);
}
}
}
EventManager.prototype.fireEvent = function (eventObjectMap, eventType, ev) {
if (!eventObjectMap || !eventType)
return;
var eventParameter = eventObjectMap.eventParameter;
var eventContext = eventObjectMap.eventContext;
//var context = eventObjectMap.eventContext.context;
var userContext = eventObjectMap.eventContext.userContext
if (userContext && eventContext && userContext[eventContext[eventType]])
userContext[eventContext[eventType]].call(userContext, eventParameter);
if (eventType !== "mouseout") {
if (userContext.cursor && userContext.cursor !== ev.target.style.cursor) {
ev.target.style.cursor = userContext.cursor;
}
} else {
ev.target.style.cursor = this.chart._defaultCursor;
delete eventObjectMap.eventParameter; // reference no longer required.
delete eventObjectMap.eventContext; // reference no longer required.
}
//This is just a quick fix. Need to find a better way of calling internal event handlers.
if (eventType === "click" && eventObjectMap.objectType === "dataPoint" && this.chart.pieDoughnutClickHandler) {
this.chart.pieDoughnutClickHandler.call(this.chart.data[eventObjectMap.dataSeriesIndex], eventParameter);
}
}
//#endregion Event Manager
//#region Class CultureInfo
function CultureInfo(culture) {
var cultureInfo;
if (culture && cultures[culture])
cultureInfo = cultures[culture];
CultureInfo.base.constructor.call(this, "CultureInfo", cultureInfo);
}
extend(CultureInfo, CanvasJSObject);
//#endregion Class CultureInfo
//#region Animator
function Animator(chart) {
this.chart = chart;
this.ctx = this.chart.plotArea.ctx;
this.animations = [];
this.animationRequestId = null;
}
//Animator.prototype.animate = function (duration, base, dest, source, animationCallback, onComplete) {
Animator.prototype.animate = function (startDelay, duration, animationCallback, onComplete, easingFunction) {
var _this = this;
this.chart.isAnimating = true;
easingFunction = easingFunction || AnimationHelper.easing.linear;
if (animationCallback) {
this.animations.push({
startTime: (new Date()).getTime() + (startDelay ? startDelay : 0),
duration: duration,
animationCallback: animationCallback,
onComplete: onComplete
});
}
var remainingAnimations = [];
while (this.animations.length > 0) {
var animation = this.animations.shift();
var now = (new Date()).getTime();
var fractionComplete = 0;
//var fractionComplete = Math.min(((new Date()).getTime() - animation.startTime) / animation.duration, 1);
if (animation.startTime <= now) {
fractionComplete = easingFunction(Math.min((now - animation.startTime), animation.duration), 0, 1, animation.duration);
//var fractionComplete = AnimationHelper.easing.easeOutQuad(Math.min(((new Date()).getTime() - animation.startTime), animation.duration), 0, 1, animation.duration);
fractionComplete = Math.min(fractionComplete, 1);
if (isNaN(fractionComplete) || !isFinite(fractionComplete))
fractionComplete = 1;
}
if (fractionComplete < 1) {
remainingAnimations.push(animation);
}
animation.animationCallback(fractionComplete);
if (fractionComplete >= 1 && animation.onComplete)
animation.onComplete();
}
this.animations = remainingAnimations;
if (this.animations.length > 0) {
this.animationRequestId = this.chart.requestAnimFrame.call(window, function () {
_this.animate.call(_this);
});
} else {
this.chart.isAnimating = false;
}
}
Animator.prototype.cancelAllAnimations = function () {
this.animations = [];
if (this.animationRequestId) {
this.chart.cancelRequestAnimFrame.call(window, this.animationRequestId);
}
this.animationRequestId = null;
this.chart.isAnimating = false;
}
var AnimationHelper = {
yScaleAnimation: function (fractionComplete, animationInfo) {
if (fractionComplete === 0)
return;
var ctx = animationInfo.dest;
var sourceCanvas = animationInfo.source.canvas;
var base = animationInfo.animationBase;
var offsetY = (base - base * fractionComplete);
ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, 0, offsetY, ctx.canvas.width / devicePixelBackingStoreRatio, fractionComplete * ctx.canvas.height / devicePixelBackingStoreRatio);
},
xScaleAnimation: function (fractionComplete, animationInfo) {
if (fractionComplete === 0)
return;
var ctx = animationInfo.dest;
var sourceCanvas = animationInfo.source.canvas;
var base = animationInfo.animationBase;
var offsetX = (base - base * fractionComplete);
ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, offsetX, 0, fractionComplete * ctx.canvas.width / devicePixelBackingStoreRatio, ctx.canvas.height / devicePixelBackingStoreRatio);
},
xClipAnimation: function (fractionComplete, animationInfo) {
if (fractionComplete === 0)
return;
var ctx = animationInfo.dest;
var sourceCanvas = animationInfo.source.canvas;
ctx.save();
if (fractionComplete > 0)
ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width * fractionComplete, sourceCanvas.height, 0, 0, sourceCanvas.width * fractionComplete / devicePixelBackingStoreRatio, sourceCanvas.height / devicePixelBackingStoreRatio);
ctx.restore();
},
fadeInAnimation: function (fractionComplete, animationInfo) {
if (fractionComplete === 0)
return;
var ctx = animationInfo.dest;
var sourceCanvas = animationInfo.source.canvas;
ctx.save();
ctx.globalAlpha = fractionComplete;
ctx.drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height, 0, 0, ctx.canvas.width / devicePixelBackingStoreRatio, ctx.canvas.height / devicePixelBackingStoreRatio);
ctx.restore();
},
easing: {
linear: function (t, b, c, d) {
return c * t / d + b;
},
easeOutQuad: function (t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
},
easeOutQuart: function (t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
},
easeInQuad: function (t, b, c, d) {
return c * (t /= d) * t + b;
},
easeInQuart: function (t, b, c, d) {
return c * (t /= d) * t * t * t + b;
}
}
}
//#endregion Animator
//#region Render Helper
var RenderHelper = {
drawMarker: function (x, y, ctx, markerType, markerSize, markerColor, markerBorderColor, markerBorderThickness) {
if (!ctx)
return;
var alpha = 1;
ctx.fillStyle = markerColor ? markerColor : "#000000";
ctx.strokeStyle = markerBorderColor ? markerBorderColor : "#000000";
ctx.lineWidth = markerBorderThickness ? markerBorderThickness : 0;
if (markerType === "circle") {
ctx.moveTo(x, y);
ctx.beginPath();
//return;
ctx.arc(x, y, markerSize / 2, 0, Math.PI * 2, false);
if (markerColor)
ctx.fill();
if (markerBorderThickness) {
if (!markerBorderColor) {
alpha = ctx.globalAlpha;
ctx.globalAlpha = .15;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.globalAlpha = alpha;
} else
ctx.stroke();
}
}
else if (markerType === "square") {
//ctx.moveTo(x - markerSize / 2, y - markerSize / 2);
ctx.beginPath();
ctx.rect(x - markerSize / 2, y - markerSize / 2, markerSize, markerSize);
if (markerColor)
ctx.fill();
if (markerBorderThickness) {
if (!markerBorderColor) {
alpha = ctx.globalAlpha;
ctx.globalAlpha = .15;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.globalAlpha = alpha;
} else
ctx.stroke();
}
} else if (markerType === "triangle") {
ctx.beginPath();
ctx.moveTo(x - markerSize / 2, y + markerSize / 2);
ctx.lineTo(x + markerSize / 2, y + markerSize / 2);
ctx.lineTo(x, y - markerSize / 2);
ctx.closePath();
if (markerColor)
ctx.fill();
if (markerBorderThickness) {
if (!markerBorderColor) {
alpha = ctx.globalAlpha;
ctx.globalAlpha = .15;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.globalAlpha = alpha;
} else
ctx.stroke();
}
ctx.beginPath();
} else if (markerType === "cross") {
ctx.strokeStyle = markerColor;
markerBorderThickness = markerSize / 4;
ctx.lineWidth = markerBorderThickness;
ctx.beginPath();
ctx.moveTo(x - markerSize / 2, y - markerSize / 2);
ctx.lineTo(x + markerSize / 2, y + markerSize / 2);
ctx.stroke();
ctx.moveTo(x + markerSize / 2, y - markerSize / 2);
ctx.lineTo(x - markerSize / 2, y + markerSize / 2);
ctx.stroke();
}
},
drawMarkers: function (markers) {
for (var i = 0; i < markers.length; i++) {
var marker = markers[i];
RenderHelper.drawMarker(marker.x, marker.y, marker.ctx, marker.type, marker.size, marker.color, marker.borderColor, marker.borderThickness);
}
}
//,
//draw1pxLine: function (x1, y1, x2, y2, color, ctx) {
// ctx.beginPath();
// ctx.drawRect(x1, y1, x2 - x1, y2 - y1);
// ctx.stroke();
//}
}
//#endregion Render Helper
//#endregion Class Definitions
//#region Public API
var CanvasJS = {
Chart: function (containerId, options) {
var _chart = new Chart(containerId, options, this);
this.render = function () { _chart.render(this.options) };
//console.log(_chart);
this.options = _chart._options;
},
addColorSet: function (name, colorSet) {
colorSets[name] = colorSet;
},
addCultureInfo: function (name, cultureInfo) {
cultures[name] = cultureInfo;
},
formatNumber: function (number, formatString, culture) {
culture = culture || "en";
formatString = formatString || "#,##0.##";
if (!cultures[culture])
throw "Unknown Culture Name";
else {
return numberFormat(number, formatString, new CultureInfo(culture));
}
},
formatDate: function (date, formatString, culture) {
culture = culture || "en";
formatString = formatString || "DD MMM YYYY";
if (!cultures[culture])
throw "Unknown Culture Name";
else {
return dateFormat(date, formatString, new CultureInfo(culture));
}
}
}
CanvasJS.Chart.version = "v1.7.0 GA";
window.CanvasJS = CanvasJS;
//#endregion Public API
})();