PHP Classes

File: vendors/chart.js/src/core/core.helpers.js

Recommend this page to a friend!
  Classes of Jorge Castro   Gentelella BladeOne   vendors/chart.js/src/core/core.helpers.js   Download  
File: vendors/chart.js/src/core/core.helpers.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Gentelella BladeOne
Render templates using Bootstrap for presentation
Author: By
Last change:
Date: 3 years ago
Size: 29,506 bytes
 

Contents

Class file image Download
/*global window: false */ /*global document: false */ "use strict"; var color = require('chartjs-color'); module.exports = function(Chart) { //Global Chart helpers object for utility methods and classes var helpers = Chart.helpers = {}; //-- Basic js utility methods helpers.each = function(loopable, callback, self, reverse) { // Check to see if null or undefined firstly. var i, len; if (helpers.isArray(loopable)) { len = loopable.length; if (reverse) { for (i = len - 1; i >= 0; i--) { callback.call(self, loopable[i], i); } } else { for (i = 0; i < len; i++) { callback.call(self, loopable[i], i); } } } else if (typeof loopable === 'object') { var keys = Object.keys(loopable); len = keys.length; for (i = 0; i < len; i++) { callback.call(self, loopable[keys[i]], keys[i]); } } }; helpers.clone = function(obj) { var objClone = {}; helpers.each(obj, function(value, key) { if (helpers.isArray(value)) { objClone[key] = value.slice(0); } else if (typeof value === 'object' && value !== null) { objClone[key] = helpers.clone(value); } else { objClone[key] = value; } }); return objClone; }; helpers.extend = function(base) { var len = arguments.length; var additionalArgs = []; for (var i = 1; i < len; i++) { additionalArgs.push(arguments[i]); } helpers.each(additionalArgs, function(extensionObject) { helpers.each(extensionObject, function(value, key) { base[key] = value; }); }); return base; }; // Need a special merge function to chart configs since they are now grouped helpers.configMerge = function(_base) { var base = helpers.clone(_base); helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { helpers.each(extension, function(value, key) { if (key === 'scales') { // Scale config merging is complex. Add out own function here for that base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value); } else if (key === 'scale') { // Used in polar area & radar charts since there is only one scale base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value); } else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) { // In this case we have an array of objects replacing another array. Rather than doing a strict replace, // merge. This allows easy scale option merging var baseArray = base[key]; helpers.each(value, function(valueObj, index) { if (index < baseArray.length) { if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) { // Two objects are coming together. Do a merge of them. baseArray[index] = helpers.configMerge(baseArray[index], valueObj); } else { // Just overwrite in this case since there is nothing to merge baseArray[index] = valueObj; } } else { baseArray.push(valueObj); // nothing to merge } }); } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { // If we are overwriting an object with an object, do a merge of the properties. base[key] = helpers.configMerge(base[key], value); } else { // can just overwrite the value in this case base[key] = value; } }); }); return base; }; helpers.extendDeep = function(_base) { return _extendDeep.apply(this, arguments); function _extendDeep(dst) { helpers.each(arguments, function(obj) { if (obj !== dst) { helpers.each(obj, function(value, key) { if (dst[key] && dst[key].constructor && dst[key].constructor === Object) { _extendDeep(dst[key], value); } else { dst[key] = value; } }); } }); return dst; } }; helpers.scaleMerge = function(_base, extension) { var base = helpers.clone(_base); helpers.each(extension, function(value, key) { if (key === 'xAxes' || key === 'yAxes') { // These properties are arrays of items if (base.hasOwnProperty(key)) { helpers.each(value, function(valueObj, index) { var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); var axisDefaults = Chart.scaleService.getScaleDefaults(axisType); if (index >= base[key].length || !base[key][index].type) { base[key].push(helpers.configMerge(axisDefaults, valueObj)); } else if (valueObj.type && valueObj.type !== base[key][index].type) { // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj); } else { // Type is the same base[key][index] = helpers.configMerge(base[key][index], valueObj); } }); } else { base[key] = []; helpers.each(value, function(valueObj) { var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj)); }); } } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { // If we are overwriting an object with an object, do a merge of the properties. base[key] = helpers.configMerge(base[key], value); } else { // can just overwrite the value in this case base[key] = value; } }); return base; }; helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) { if (value === undefined || value === null) { return defaultValue; } if (helpers.isArray(value)) { return index < value.length ? value[index] : defaultValue; } return value; }; helpers.getValueOrDefault = function(value, defaultValue) { return value === undefined ? defaultValue : value; }; helpers.indexOf = function(arrayToSearch, item) { if (Array.prototype.indexOf) { return arrayToSearch.indexOf(item); } else { for (var i = 0; i < arrayToSearch.length; i++) { if (arrayToSearch[i] === item) return i; } return -1; } }; helpers.where = function(collection, filterCallback) { if (helpers.isArray(collection) && Array.prototype.filter) { return collection.filter(filterCallback); } else { var filtered = []; helpers.each(collection, function(item) { if (filterCallback(item)) { filtered.push(item); } }); return filtered; } }; helpers.findIndex = function(arrayToSearch, callback, thisArg) { var index = -1; if (Array.prototype.findIndex) { index = arrayToSearch.findIndex(callback, thisArg); } else { for (var i = 0; i < arrayToSearch.length; ++i) { thisArg = thisArg !== undefined ? thisArg : arrayToSearch; if (callback.call(thisArg, arrayToSearch[i], i, arrayToSearch)) { index = i; break; } } } return index; }; helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to start of the array if (startIndex === undefined || startIndex === null) { startIndex = -1; } for (var i = startIndex + 1; i < arrayToSearch.length; i++) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { // Default to end of the array if (startIndex === undefined || startIndex === null) { startIndex = arrayToSearch.length; } for (var i = startIndex - 1; i >= 0; i--) { var currentItem = arrayToSearch[i]; if (filterCallback(currentItem)) { return currentItem; } } }; helpers.inherits = function(extensions) { //Basic javascript inheritance based on the model created in Backbone.js var parent = this; var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() { return parent.apply(this, arguments); }; var Surrogate = function() { this.constructor = ChartElement; }; Surrogate.prototype = parent.prototype; ChartElement.prototype = new Surrogate(); ChartElement.extend = helpers.inherits; if (extensions) { helpers.extend(ChartElement.prototype, extensions); } ChartElement.__super__ = parent.prototype; return ChartElement; }; helpers.noop = function() {}; helpers.uid = (function() { var id = 0; return function() { return id++; }; })(); helpers.warn = function(str) { //Method for warning of errors if (console && typeof console.warn === "function") { console.warn(str); } }; //-- Math methods helpers.isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); }; helpers.almostEquals = function(x, y, epsilon) { return Math.abs(x - y) < epsilon; }; helpers.max = function(array) { return array.reduce(function(max, value) { if (!isNaN(value)) { return Math.max(max, value); } else { return max; } }, Number.NEGATIVE_INFINITY); }; helpers.min = function(array) { return array.reduce(function(min, value) { if (!isNaN(value)) { return Math.min(min, value); } else { return min; } }, Number.POSITIVE_INFINITY); }; helpers.sign = function(x) { if (Math.sign) { return Math.sign(x); } else { x = +x; // convert to a number if (x === 0 || isNaN(x)) { return x; } return x > 0 ? 1 : -1; } }; helpers.log10 = function(x) { if (Math.log10) { return Math.log10(x); } else { return Math.log(x) / Math.LN10; } }; helpers.toRadians = function(degrees) { return degrees * (Math.PI / 180); }; helpers.toDegrees = function(radians) { return radians * (180 / Math.PI); }; // Gets the angle from vertical upright to the point about a centre. helpers.getAngleFromPoint = function(centrePoint, anglePoint) { var distanceFromXCenter = anglePoint.x - centrePoint.x, distanceFromYCenter = anglePoint.y - centrePoint.y, radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); if (angle < (-0.5 * Math.PI)) { angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] } return { angle: angle, distance: radialDistanceFromCenter }; }; helpers.aliasPixel = function(pixelWidth) { return (pixelWidth % 2 === 0) ? 0 : 0.5; }; helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { //Props to Rob Spencer at scaled innovation for his post on splining between points //http://scaledinnovation.com/analytics/splines/aboutSplines.html // This function must also respect "skipped" points var previous = firstPoint.skip ? middlePoint : firstPoint, current = middlePoint, next = afterPoint.skip ? middlePoint : afterPoint; var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); var s01 = d01 / (d01 + d12); var s12 = d12 / (d01 + d12); // If all points are the same, s01 & s02 will be inf s01 = isNaN(s01) ? 0 : s01; s12 = isNaN(s12) ? 0 : s12; var fa = t * s01; // scaling factor for triangle Ta var fb = t * s12; return { previous: { x: current.x - fa * (next.x - previous.x), y: current.y - fa * (next.y - previous.y) }, next: { x: current.x + fb * (next.x - previous.x), y: current.y + fb * (next.y - previous.y) } }; }; helpers.nextItem = function(collection, index, loop) { if (loop) { return index >= collection.length - 1 ? collection[0] : collection[index + 1]; } return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; }; helpers.previousItem = function(collection, index, loop) { if (loop) { return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; } return index <= 0 ? collection[0] : collection[index - 1]; }; // Implementation of the nice number algorithm used in determining where axis labels will go helpers.niceNum = function(range, round) { var exponent = Math.floor(helpers.log10(range)); var fraction = range / Math.pow(10, exponent); var niceFraction; if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else { if (fraction <= 1.0) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 5; } else { niceFraction = 10; } } return niceFraction * Math.pow(10, exponent); }; //Easing functions adapted from Robert Penner's easing equations //http://www.robertpenner.com/easing/ var easingEffects = helpers.easingEffects = { linear: function(t) { return t; }, easeInQuad: function(t) { return t * t; }, easeOutQuad: function(t) { return -1 * t * (t - 2); }, easeInOutQuad: function(t) { if ((t /= 1 / 2) < 1) { return 1 / 2 * t * t; } return -1 / 2 * ((--t) * (t - 2) - 1); }, easeInCubic: function(t) { return t * t * t; }, easeOutCubic: function(t) { return 1 * ((t = t / 1 - 1) * t * t + 1); }, easeInOutCubic: function(t) { if ((t /= 1 / 2) < 1) { return 1 / 2 * t * t * t; } return 1 / 2 * ((t -= 2) * t * t + 2); }, easeInQuart: function(t) { return t * t * t * t; }, easeOutQuart: function(t) { return -1 * ((t = t / 1 - 1) * t * t * t - 1); }, easeInOutQuart: function(t) { if ((t /= 1 / 2) < 1) { return 1 / 2 * t * t * t * t; } return -1 / 2 * ((t -= 2) * t * t * t - 2); }, easeInQuint: function(t) { return 1 * (t /= 1) * t * t * t * t; }, easeOutQuint: function(t) { return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); }, easeInOutQuint: function(t) { if ((t /= 1 / 2) < 1) { return 1 / 2 * t * t * t * t * t; } return 1 / 2 * ((t -= 2) * t * t * t * t + 2); }, easeInSine: function(t) { return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; }, easeOutSine: function(t) { return 1 * Math.sin(t / 1 * (Math.PI / 2)); }, easeInOutSine: function(t) { return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); }, easeInExpo: function(t) { return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); }, easeOutExpo: function(t) { return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); }, easeInOutExpo: function(t) { if (t === 0) { return 0; } if (t === 1) { return 1; } if ((t /= 1 / 2) < 1) { return 1 / 2 * Math.pow(2, 10 * (t - 1)); } return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); }, easeInCirc: function(t) { if (t >= 1) { return t; } return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); }, easeOutCirc: function(t) { return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); }, easeInOutCirc: function(t) { if ((t /= 1 / 2) < 1) { return -1 / 2 * (Math.sqrt(1 - t * t) - 1); } return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); }, easeInElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if ((t /= 1) === 1) { return 1; } if (!p) { p = 1 * 0.3; } if (a < Math.abs(1)) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); }, easeOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if ((t /= 1) === 1) { return 1; } if (!p) { p = 1 * 0.3; } if (a < Math.abs(1)) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; }, easeInOutElastic: function(t) { var s = 1.70158; var p = 0; var a = 1; if (t === 0) { return 0; } if ((t /= 1 / 2) === 2) { return 1; } if (!p) { p = 1 * (0.3 * 1.5); } if (a < Math.abs(1)) { a = 1; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(1 / a); } if (t < 1) { return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); } return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; }, easeInBack: function(t) { var s = 1.70158; return 1 * (t /= 1) * t * ((s + 1) * t - s); }, easeOutBack: function(t) { var s = 1.70158; return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); }, easeInOutBack: function(t) { var s = 1.70158; if ((t /= 1 / 2) < 1) { return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); } return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); }, easeInBounce: function(t) { return 1 - easingEffects.easeOutBounce(1 - t); }, easeOutBounce: function(t) { if ((t /= 1) < (1 / 2.75)) { return 1 * (7.5625 * t * t); } else if (t < (2 / 2.75)) { return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); } else if (t < (2.5 / 2.75)) { return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); } else { return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); } }, easeInOutBounce: function(t) { if (t < 1 / 2) { return easingEffects.easeInBounce(t * 2) * 0.5; } return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; } }; //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ helpers.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { return window.setTimeout(callback, 1000 / 60); }; })(); helpers.cancelAnimFrame = (function() { return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function(callback) { return window.clearTimeout(callback, 1000 / 60); }; })(); //-- DOM methods helpers.getRelativePosition = function(evt, chart) { var mouseX, mouseY; var e = evt.originalEvent || evt, canvas = evt.currentTarget || evt.srcElement, boundingRect = canvas.getBoundingClientRect(); var touches = e.touches; if (touches && touches.length > 0) { mouseX = touches[0].clientX; mouseY = touches[0].clientY; } else { mouseX = e.clientX; mouseY = e.clientY; } // Scale mouse coordinates into canvas coordinates // by following the pattern laid out by 'jerryj' in the comments of // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); return { x: mouseX, y: mouseY }; }; helpers.addEvent = function(node, eventType, method) { if (node.addEventListener) { node.addEventListener(eventType, method); } else if (node.attachEvent) { node.attachEvent("on" + eventType, method); } else { node["on" + eventType] = method; } }; helpers.removeEvent = function(node, eventType, handler) { if (node.removeEventListener) { node.removeEventListener(eventType, handler, false); } else if (node.detachEvent) { node.detachEvent("on" + eventType, handler); } else { node["on" + eventType] = helpers.noop; } }; helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) { // Create the events object if it's not already present var events = chartInstance.events = chartInstance.events || {}; helpers.each(arrayOfEvents, function(eventName) { events[eventName] = function() { handler.apply(chartInstance, arguments); }; helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]); }); }; helpers.unbindEvents = function(chartInstance, arrayOfEvents) { var canvas = chartInstance.chart.canvas; helpers.each(arrayOfEvents, function(handler, eventName) { helpers.removeEvent(canvas, eventName, handler); }); }; // Private helper function to convert max-width/max-height values that may be percentages into a number function parseMaxStyle(styleValue, node, parentProperty) { var valueInPixels; if (typeof(styleValue) === 'string') { valueInPixels = parseInt(styleValue, 10); if (styleValue.indexOf('%') != -1) { // percentage * size in dimension valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; } } else { valueInPixels = styleValue; } return valueInPixels; } /** * Returns if the given value contains an effective constraint. * @private */ function isConstrainedValue(value) { return value !== undefined && value !== null && value !== 'none'; } // Private helper to get a constraint dimension // @param domNode : the node to check the constraint on // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) // @param percentageProperty : property of parent to use when calculating width as a percentage // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser function getConstraintDimension(domNode, maxStyle, percentageProperty) { var view = document.defaultView; var parentNode = domNode.parentNode; var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; var hasCNode = isConstrainedValue(constrainedNode); var hasCContainer = isConstrainedValue(constrainedContainer); var infinity = Number.POSITIVE_INFINITY; if (hasCNode || hasCContainer) { return Math.min( hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); } return 'none'; } // returns Number or undefined if no constraint helpers.getConstraintWidth = function(domNode) { return getConstraintDimension(domNode, 'max-width', 'clientWidth'); }; // returns Number or undefined if no constraint helpers.getConstraintHeight = function(domNode) { return getConstraintDimension(domNode, 'max-height', 'clientHeight'); }; helpers.getMaximumWidth = function(domNode) { var container = domNode.parentNode; var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right')); var w = container.clientWidth - padding; var cw = helpers.getConstraintWidth(domNode); return isNaN(cw)? w : Math.min(w, cw); }; helpers.getMaximumHeight = function(domNode) { var container = domNode.parentNode; var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom')); var h = container.clientHeight - padding; var ch = helpers.getConstraintHeight(domNode); return isNaN(ch)? h : Math.min(h, ch); }; helpers.getStyle = function(el, property) { return el.currentStyle ? el.currentStyle[property] : document.defaultView.getComputedStyle(el, null).getPropertyValue(property); }; helpers.retinaScale = function(chart) { var ctx = chart.ctx; var canvas = chart.canvas; var width = canvas.width; var height = canvas.height; var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1; if (pixelRatio !== 1) { canvas.height = height * pixelRatio; canvas.width = width * pixelRatio; ctx.scale(pixelRatio, pixelRatio); // Store the device pixel ratio so that we can go backwards in `destroy`. // The devicePixelRatio changes with zoom, so there are no guarantees that it is the same // when destroy is called chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio; } canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; }; //-- Canvas methods helpers.clear = function(chart) { chart.ctx.clearRect(0, 0, chart.width, chart.height); }; helpers.fontString = function(pixelSize, fontStyle, fontFamily) { return fontStyle + " " + pixelSize + "px " + fontFamily; }; helpers.longestText = function(ctx, font, arrayOfStrings, cache) { cache = cache || {}; var data = cache.data = cache.data || {}; var gc = cache.garbageCollect = cache.garbageCollect || []; if (cache.font !== font) { data = cache.data = {}; gc = cache.garbageCollect = []; cache.font = font; } ctx.font = font; var longest = 0; helpers.each(arrayOfStrings, function(string) { // Undefined strings should not be measured if (string !== undefined && string !== null) { var textWidth = data[string]; if (!textWidth) { textWidth = data[string] = ctx.measureText(string).width; gc.push(string); } if (textWidth > longest) { longest = textWidth; } } }); var gcLen = gc.length / 2; if (gcLen > arrayOfStrings.length) { for (var i = 0; i < gcLen; i++) { delete data[gc[i]]; } gc.splice(0, gcLen); } return longest; }; helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.lineTo(x + width - radius, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius); ctx.lineTo(x + width, y + height - radius); ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); ctx.lineTo(x + radius, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius); ctx.lineTo(x, y + radius); ctx.quadraticCurveTo(x, y, x + radius, y); ctx.closePath(); }; helpers.color = function(c) { if (!color) { console.log('Color.js not found!'); return c; } /* global CanvasGradient */ if (c instanceof CanvasGradient) { return color(Chart.defaults.global.defaultColor); } return color(c); }; helpers.addResizeListener = function(node, callback) { // Hide an iframe before the node var hiddenIframe = document.createElement('iframe'); var hiddenIframeClass = 'chartjs-hidden-iframe'; if (hiddenIframe.classlist) { // can use classlist hiddenIframe.classlist.add(hiddenIframeClass); } else { hiddenIframe.setAttribute('class', hiddenIframeClass); } // Set the style var style = hiddenIframe.style; style.width = '100%'; style.display = 'block'; style.border = 0; style.height = 0; style.margin = 0; style.position = 'absolute'; style.left = 0; style.right = 0; style.top = 0; style.bottom = 0; // Insert the iframe so that contentWindow is available node.insertBefore(hiddenIframe, node.firstChild); (hiddenIframe.contentWindow || hiddenIframe).onresize = function() { if (callback) { callback(); } }; }; helpers.removeResizeListener = function(node) { var hiddenIframe = node.querySelector('.chartjs-hidden-iframe'); // Remove the resize detect iframe if (hiddenIframe) { hiddenIframe.parentNode.removeChild(hiddenIframe); } }; helpers.isArray = function(obj) { if (!Array.isArray) { return Object.prototype.toString.call(obj) === '[object Array]'; } return Array.isArray(obj); }; //! @see http://stackoverflow.com/a/14853974 helpers.arrayEquals = function(a0, a1) { var i, ilen, v0, v1; if (!a0 || !a1 || a0.length != a1.length) { return false; } for (i = 0, ilen=a0.length; i < ilen; ++i) { v0 = a0[i]; v1 = a1[i]; if (v0 instanceof Array && v1 instanceof Array) { if (!helpers.arrayEquals(v0, v1)) { return false; } } else if (v0 != v1) { // NOTE: two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; }; helpers.pushAllIfDefined = function(element, array) { if (typeof element === "undefined") { return; } if (helpers.isArray(element)) { array.push.apply(array, element); } else { array.push(element); } }; helpers.callCallback = function(fn, args, _tArg) { if (fn && typeof fn.call === 'function') { fn.apply(_tArg, args); } }; helpers.getHoverColor = function(color) { /* global CanvasPattern */ return (color instanceof CanvasPattern) ? color : helpers.color(color).saturate(0.5).darken(0.1).rgbString(); }; };