UNPKG

hdjs

Version:
2,017 lines (1,784 loc) 444 kB
// ==ClosureCompiler== // @compilation_level SIMPLE_OPTIMIZATIONS /** * @license Highcharts JS v3.0.6 (2013-10-04) * * (c) 2009-2013 Torstein Hønsi * * License: www.highcharts.com/license */ // JSLint options: /*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */ (function () { // encapsulated variables var UNDEFINED, doc = document, win = window, math = Math, mathRound = math.round, mathFloor = math.floor, mathCeil = math.ceil, mathMax = math.max, mathMin = math.min, mathAbs = math.abs, mathCos = math.cos, mathSin = math.sin, mathPI = math.PI, deg2rad = mathPI * 2 / 360, // some variables userAgent = navigator.userAgent, isOpera = win.opera, isIE = /msie/i.test(userAgent) && !isOpera, docMode8 = doc.documentMode === 8, isWebKit = /AppleWebKit/.test(userAgent), isFirefox = /Firefox/.test(userAgent), isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent), SVG_NS = 'http://www.w3.org/2000/svg', hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect, hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38 useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext, Renderer, hasTouch = doc.documentElement.ontouchstart !== UNDEFINED, symbolSizes = {}, idCounter = 0, garbageBin, defaultOptions, dateFormat, // function globalAnimation, pathAnim, timeUnits, noop = function () {}, charts = [], PRODUCT = 'Highcharts', VERSION = '3.0.6', // some constants for frequently used strings DIV = 'div', ABSOLUTE = 'absolute', RELATIVE = 'relative', HIDDEN = 'hidden', PREFIX = 'highcharts-', VISIBLE = 'visible', PX = 'px', NONE = 'none', M = 'M', L = 'L', /* * Empirical lowest possible opacities for TRACKER_FILL * IE6: 0.002 * IE7: 0.002 * IE8: 0.002 * IE9: 0.00000000001 (unlimited) * IE10: 0.0001 (exporting only) * FF: 0.00000000001 (unlimited) * Chrome: 0.000001 * Safari: 0.000001 * Opera: 0.00000000001 (unlimited) */ TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable //TRACKER_FILL = 'rgba(192,192,192,0.5)', NORMAL_STATE = '', HOVER_STATE = 'hover', SELECT_STATE = 'select', MILLISECOND = 'millisecond', SECOND = 'second', MINUTE = 'minute', HOUR = 'hour', DAY = 'day', WEEK = 'week', MONTH = 'month', YEAR = 'year', // constants for attributes LINEAR_GRADIENT = 'linearGradient', STOPS = 'stops', STROKE_WIDTH = 'stroke-width', // time methods, changed based on whether or not UTC is used makeTime, getMinutes, getHours, getDay, getDate, getMonth, getFullYear, setMinutes, setHours, setDate, setMonth, setFullYear, // lookup over the types and the associated classes seriesTypes = {}; // The Highcharts namespace win.Highcharts = win.Highcharts ? error(16, true) : {}; /** * Extend an object with the members of another * @param {Object} a The object to be extended * @param {Object} b The object to add to the first one */ function extend(a, b) { var n; if (!a) { a = {}; } for (n in b) { a[n] = b[n]; } return a; } /** * Deep merge two or more objects and return a third object. * Previously this function redirected to jQuery.extend(true), but this had two limitations. * First, it deep merged arrays, which lead to workarounds in Highcharts. Second, * it copied properties from extended prototypes. */ function merge() { var i, len = arguments.length, ret = {}, doCopy = function (copy, original) { var value, key; // An object is replacing a primitive if (typeof copy !== 'object') { copy = {}; } for (key in original) { if (original.hasOwnProperty(key)) { value = original[key]; // Copy the contents of objects, but not arrays or DOM nodes if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]' && typeof value.nodeType !== 'number') { copy[key] = doCopy(copy[key] || {}, value); // Primitives and arrays are copied over directly } else { copy[key] = original[key]; } } } return copy; }; // For each argument, extend the return for (i = 0; i < len; i++) { ret = doCopy(ret, arguments[i]); } return ret; } /** * Take an array and turn into a hash with even number arguments as keys and odd numbers as * values. Allows creating constants for commonly used style properties, attributes etc. * Avoid it in performance critical situations like looping */ function hash() { var i = 0, args = arguments, length = args.length, obj = {}; for (; i < length; i++) { obj[args[i++]] = args[i]; } return obj; } /** * Shortcut for parseInt * @param {Object} s * @param {Number} mag Magnitude */ function pInt(s, mag) { return parseInt(s, mag || 10); } /** * Check for string * @param {Object} s */ function isString(s) { return typeof s === 'string'; } /** * Check for object * @param {Object} obj */ function isObject(obj) { return typeof obj === 'object'; } /** * Check for array * @param {Object} obj */ function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } /** * Check for number * @param {Object} n */ function isNumber(n) { return typeof n === 'number'; } function log2lin(num) { return math.log(num) / math.LN10; } function lin2log(num) { return math.pow(10, num); } /** * Remove last occurence of an item from an array * @param {Array} arr * @param {Mixed} item */ function erase(arr, item) { var i = arr.length; while (i--) { if (arr[i] === item) { arr.splice(i, 1); break; } } //return arr; } /** * Returns true if the object is not null or undefined. Like MooTools' $.defined. * @param {Object} obj */ function defined(obj) { return obj !== UNDEFINED && obj !== null; } /** * Set or get an attribute or an object of attributes. Can't use jQuery attr because * it attempts to set expando properties on the SVG element, which is not allowed. * * @param {Object} elem The DOM element to receive the attribute(s) * @param {String|Object} prop The property or an abject of key-value pairs * @param {String} value The value if a single property is set */ function attr(elem, prop, value) { var key, setAttribute = 'setAttribute', ret; // if the prop is a string if (isString(prop)) { // set the value if (defined(value)) { elem[setAttribute](prop, value); // get the value } else if (elem && elem.getAttribute) { // elem not defined when printing pie demo... ret = elem.getAttribute(prop); } // else if prop is defined, it is a hash of key/value pairs } else if (defined(prop) && isObject(prop)) { for (key in prop) { elem[setAttribute](key, prop[key]); } } return ret; } /** * Check if an element is an array, and if not, make it into an array. Like * MooTools' $.splat. */ function splat(obj) { return isArray(obj) ? obj : [obj]; } /** * Return the first value that is defined. Like MooTools' $.pick. */ function pick() { var args = arguments, i, arg, length = args.length; for (i = 0; i < length; i++) { arg = args[i]; if (typeof arg !== 'undefined' && arg !== null) { return arg; } } } /** * Set CSS on a given element * @param {Object} el * @param {Object} styles Style object with camel case property names */ function css(el, styles) { if (isIE) { if (styles && styles.opacity !== UNDEFINED) { styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')'; } } extend(el.style, styles); } /** * Utility function to create element with attributes and styles * @param {Object} tag * @param {Object} attribs * @param {Object} styles * @param {Object} parent * @param {Object} nopad */ function createElement(tag, attribs, styles, parent, nopad) { var el = doc.createElement(tag); if (attribs) { extend(el, attribs); } if (nopad) { css(el, {padding: 0, border: NONE, margin: 0}); } if (styles) { css(el, styles); } if (parent) { parent.appendChild(el); } return el; } /** * Extend a prototyped class by new members * @param {Object} parent * @param {Object} members */ function extendClass(parent, members) { var object = function () {}; object.prototype = new parent(); extend(object.prototype, members); return object; } /** * Format a number and return a string based on input settings * @param {Number} number The input number to format * @param {Number} decimals The amount of decimals * @param {String} decPoint The decimal point, defaults to the one given in the lang options * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options */ function numberFormat(number, decimals, decPoint, thousandsSep) { var lang = defaultOptions.lang, // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/ n = +number || 0, c = decimals === -1 ? (n.toString().split('.')[1] || '').length : // preserve decimals (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals), d = decPoint === undefined ? lang.decimalPoint : decPoint, t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep, s = n < 0 ? "-" : "", i = String(pInt(n = mathAbs(n).toFixed(c))), j = i.length > 3 ? i.length % 3 : 0; return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + mathAbs(n - i).toFixed(c).slice(2) : ""); } /** * Pad a string to a given length by adding 0 to the beginning * @param {Number} number * @param {Number} length */ function pad(number, length) { // Create an array of the remaining length +1 and join it with 0's return new Array((length || 2) + 1 - String(number).length).join(0) + number; } /** * Wrap a method with extended functionality, preserving the original function * @param {Object} obj The context object that the method belongs to * @param {String} method The name of the method to extend * @param {Function} func A wrapper function callback. This function is called with the same arguments * as the original function, except that the original function is unshifted and passed as the first * argument. */ function wrap(obj, method, func) { var proceed = obj[method]; obj[method] = function () { var args = Array.prototype.slice.call(arguments); args.unshift(proceed); return func.apply(this, args); }; } /** * Based on http://www.php.net/manual/en/function.strftime.php * @param {String} format * @param {Number} timestamp * @param {Boolean} capitalize */ dateFormat = function (format, timestamp, capitalize) { if (!defined(timestamp) || isNaN(timestamp)) { return 'Invalid date'; } format = pick(format, '%Y-%m-%d %H:%M:%S'); var date = new Date(timestamp), key, // used in for constuct below // get the basic time values hours = date[getHours](), day = date[getDay](), dayOfMonth = date[getDate](), month = date[getMonth](), fullYear = date[getFullYear](), lang = defaultOptions.lang, langWeekdays = lang.weekdays, // List all format keys. Custom formats can be added from the outside. replacements = extend({ // Day 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon' 'A': langWeekdays[day], // Long weekday, like 'Monday' 'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31 'e': dayOfMonth, // Day of the month, 1 through 31 // Week (none implemented) //'W': weekNumber(), // Month 'b': lang.shortMonths[month], // Short month, like 'Jan' 'B': lang.months[month], // Long month, like 'January' 'm': pad(month + 1), // Two digit month number, 01 through 12 // Year 'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009 'Y': fullYear, // Four digits year, like 2009 // Time 'H': pad(hours), // Two digits hours in 24h format, 00 through 23 'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11 'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12 'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59 'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM 'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM 'S': pad(date.getSeconds()), // Two digits seconds, 00 through 59 'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby) }, Highcharts.dateFormats); // do the replaces for (key in replacements) { while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]); } } // Optionally capitalize the string and return return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format; }; /** * Format a single variable. Similar to sprintf, without the % prefix. */ function formatSingle(format, val) { var floatRegex = /f$/, decRegex = /\.([0-9])/, lang = defaultOptions.lang, decimals; if (floatRegex.test(format)) { // float decimals = format.match(decRegex); decimals = decimals ? decimals[1] : -1; val = numberFormat( val, decimals, lang.decimalPoint, format.indexOf(',') > -1 ? lang.thousandsSep : '' ); } else { val = dateFormat(format, val); } return val; } /** * Format a string according to a subset of the rules of Python's String.format method. */ function format(str, ctx) { var splitter = '{', isInside = false, segment, valueAndFormat, path, i, len, ret = [], val, index; while ((index = str.indexOf(splitter)) !== -1) { segment = str.slice(0, index); if (isInside) { // we're on the closing bracket looking back valueAndFormat = segment.split(':'); path = valueAndFormat.shift().split('.'); // get first and leave format len = path.length; val = ctx; // Assign deeper paths for (i = 0; i < len; i++) { val = val[path[i]]; } // Format the replacement if (valueAndFormat.length) { val = formatSingle(valueAndFormat.join(':'), val); } // Push the result and advance the cursor ret.push(val); } else { ret.push(segment); } str = str.slice(index + 1); // the rest isInside = !isInside; // toggle splitter = isInside ? '}' : '{'; // now look for next matching bracket } ret.push(str); return ret.join(''); } /** * Get the magnitude of a number */ function getMagnitude(num) { return math.pow(10, mathFloor(math.log(num) / math.LN10)); } /** * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5 * @param {Number} interval * @param {Array} multiples * @param {Number} magnitude * @param {Object} options */ function normalizeTickInterval(interval, multiples, magnitude, options) { var normalized, i; // round to a tenfold of 1, 2, 2.5 or 5 magnitude = pick(magnitude, 1); normalized = interval / magnitude; // multiples for a linear scale if (!multiples) { multiples = [1, 2, 2.5, 5, 10]; // the allowDecimals option if (options && options.allowDecimals === false) { if (magnitude === 1) { multiples = [1, 2, 5, 10]; } else if (magnitude <= 0.1) { multiples = [1 / magnitude]; } } } // normalize the interval to the nearest multiple for (i = 0; i < multiples.length; i++) { interval = multiples[i]; if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) { break; } } // multiply back to the correct magnitude interval *= magnitude; return interval; } /** * Get a normalized tick interval for dates. Returns a configuration object with * unit range (interval), count and name. Used to prepare data for getTimeTicks. * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs * of segments in stock charts, the normalizing logic was extracted in order to * prevent it for running over again for each segment having the same interval. * #662, #697. */ function normalizeTimeTickInterval(tickInterval, unitsOption) { var units = unitsOption || [[ MILLISECOND, // unit name [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples ], [ SECOND, [1, 2, 5, 10, 15, 30] ], [ MINUTE, [1, 2, 5, 10, 15, 30] ], [ HOUR, [1, 2, 3, 4, 6, 8, 12] ], [ DAY, [1, 2] ], [ WEEK, [1, 2] ], [ MONTH, [1, 2, 3, 4, 6] ], [ YEAR, null ]], unit = units[units.length - 1], // default unit is years interval = timeUnits[unit[0]], multiples = unit[1], count, i; // loop through the units to find the one that best fits the tickInterval for (i = 0; i < units.length; i++) { unit = units[i]; interval = timeUnits[unit[0]]; multiples = unit[1]; if (units[i + 1]) { // lessThan is in the middle between the highest multiple and the next unit. var lessThan = (interval * multiples[multiples.length - 1] + timeUnits[units[i + 1][0]]) / 2; // break and keep the current unit if (tickInterval <= lessThan) { break; } } } // prevent 2.5 years intervals, though 25, 250 etc. are allowed if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) { multiples = [1, 2, 5]; } // get the count count = normalizeTickInterval( tickInterval / interval, multiples, unit[0] === YEAR ? getMagnitude(tickInterval / interval) : 1 // #1913 ); return { unitRange: interval, count: count, unitName: unit[0] }; } /** * Set the tick positions to a time unit that makes sense, for example * on the first of each month or on every Monday. Return an array * with the time positions. Used in datetime axes as well as for grouping * data on a datetime axis. * * @param {Object} normalizedInterval The interval in axis values (ms) and the count * @param {Number} min The minimum in axis values * @param {Number} max The maximum in axis values * @param {Number} startOfWeek */ function getTimeTicks(normalizedInterval, min, max, startOfWeek) { var tickPositions = [], i, higherRanks = {}, useUTC = defaultOptions.global.useUTC, minYear, // used in months and years as a basis for Date.UTC() minDate = new Date(min), interval = normalizedInterval.unitRange, count = normalizedInterval.count; if (defined(min)) { // #1300 if (interval >= timeUnits[SECOND]) { // second minDate.setMilliseconds(0); minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 : count * mathFloor(minDate.getSeconds() / count)); } if (interval >= timeUnits[MINUTE]) { // minute minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 : count * mathFloor(minDate[getMinutes]() / count)); } if (interval >= timeUnits[HOUR]) { // hour minDate[setHours](interval >= timeUnits[DAY] ? 0 : count * mathFloor(minDate[getHours]() / count)); } if (interval >= timeUnits[DAY]) { // day minDate[setDate](interval >= timeUnits[MONTH] ? 1 : count * mathFloor(minDate[getDate]() / count)); } if (interval >= timeUnits[MONTH]) { // month minDate[setMonth](interval >= timeUnits[YEAR] ? 0 : count * mathFloor(minDate[getMonth]() / count)); minYear = minDate[getFullYear](); } if (interval >= timeUnits[YEAR]) { // year minYear -= minYear % count; minDate[setFullYear](minYear); } // week is a special case that runs outside the hierarchy if (interval === timeUnits[WEEK]) { // get start of current week, independent of count minDate[setDate](minDate[getDate]() - minDate[getDay]() + pick(startOfWeek, 1)); } // get tick positions i = 1; minYear = minDate[getFullYear](); var time = minDate.getTime(), minMonth = minDate[getMonth](), minDateDate = minDate[getDate](), timezoneOffset = useUTC ? 0 : (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950 // iterate and add tick positions at appropriate values while (time < max) { tickPositions.push(time); // if the interval is years, use Date.UTC to increase years if (interval === timeUnits[YEAR]) { time = makeTime(minYear + i * count, 0); // if the interval is months, use Date.UTC to increase months } else if (interval === timeUnits[MONTH]) { time = makeTime(minYear, minMonth + i * count); // if we're using global time, the interval is not fixed as it jumps // one hour at the DST crossover } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) { time = makeTime(minYear, minMonth, minDateDate + i * count * (interval === timeUnits[DAY] ? 1 : 7)); // else, the interval is fixed and we use simple addition } else { time += interval * count; } i++; } // push the last time tickPositions.push(time); // mark new days if the time is dividible by day (#1649, #1760) each(grep(tickPositions, function (time) { return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset; }), function (time) { higherRanks[time] = DAY; }); } // record information on the chosen unit - for dynamic label formatter tickPositions.info = extend(normalizedInterval, { higherRanks: higherRanks, totalRange: interval * count }); return tickPositions; } /** * Helper class that contains variuos counters that are local to the chart. */ function ChartCounters() { this.color = 0; this.symbol = 0; } ChartCounters.prototype = { /** * Wraps the color counter if it reaches the specified length. */ wrapColor: function (length) { if (this.color >= length) { this.color = 0; } }, /** * Wraps the symbol counter if it reaches the specified length. */ wrapSymbol: function (length) { if (this.symbol >= length) { this.symbol = 0; } } }; /** * Utility method that sorts an object array and keeping the order of equal items. * ECMA script standard does not specify the behaviour when items are equal. */ function stableSort(arr, sortFunction) { var length = arr.length, sortValue, i; // Add index to each item for (i = 0; i < length; i++) { arr[i].ss_i = i; // stable sort index } arr.sort(function (a, b) { sortValue = sortFunction(a, b); return sortValue === 0 ? a.ss_i - b.ss_i : sortValue; }); // Remove index from items for (i = 0; i < length; i++) { delete arr[i].ss_i; // stable sort index } } /** * Non-recursive method to find the lowest member of an array. Math.min raises a maximum * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This * method is slightly slower, but safe. */ function arrayMin(data) { var i = data.length, min = data[0]; while (i--) { if (data[i] < min) { min = data[i]; } } return min; } /** * Non-recursive method to find the lowest member of an array. Math.min raises a maximum * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This * method is slightly slower, but safe. */ function arrayMax(data) { var i = data.length, max = data[0]; while (i--) { if (data[i] > max) { max = data[i]; } } return max; } /** * Utility method that destroys any SVGElement or VMLElement that are properties on the given object. * It loops all properties and invokes destroy if there is a destroy method. The property is * then delete'ed. * @param {Object} The object to destroy properties on * @param {Object} Exception, do not destroy this property, only delete it. */ function destroyObjectProperties(obj, except) { var n; for (n in obj) { // If the object is non-null and destroy is defined if (obj[n] && obj[n] !== except && obj[n].destroy) { // Invoke the destroy obj[n].destroy(); } // Delete the property from the object. delete obj[n]; } } /** * Discard an element by moving it to the bin and delete * @param {Object} The HTML node to discard */ function discardElement(element) { // create a garbage bin element, not part of the DOM if (!garbageBin) { garbageBin = createElement(DIV); } // move the node and empty bin if (element) { garbageBin.appendChild(element); } garbageBin.innerHTML = ''; } /** * Provide error messages for debugging, with links to online explanation */ function error(code, stop) { var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code; if (stop) { throw msg; } else if (win.console) { console.log(msg); } } /** * Fix JS round off float errors * @param {Number} num */ function correctFloat(num) { return parseFloat( num.toPrecision(14) ); } /** * Set the global animation to either a given value, or fall back to the * given chart's animation option * @param {Object} animation * @param {Object} chart */ function setAnimation(animation, chart) { globalAnimation = pick(animation, chart.animation); } /** * The time unit lookup */ /*jslint white: true*/ timeUnits = hash( MILLISECOND, 1, SECOND, 1000, MINUTE, 60000, HOUR, 3600000, DAY, 24 * 3600000, WEEK, 7 * 24 * 3600000, MONTH, 31 * 24 * 3600000, YEAR, 31556952000 ); /*jslint white: false*/ /** * Path interpolation algorithm used across adapters */ pathAnim = { /** * Prepare start and end values so that the path can be animated one to one */ init: function (elem, fromD, toD) { fromD = fromD || ''; var shift = elem.shift, bezier = fromD.indexOf('C') > -1, numParams = bezier ? 7 : 3, endLength, slice, i, start = fromD.split(' '), end = [].concat(toD), // copy startBaseLine, endBaseLine, sixify = function (arr) { // in splines make move points have six parameters like bezier curves i = arr.length; while (i--) { if (arr[i] === M) { arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]); } } }; if (bezier) { sixify(start); sixify(end); } // pull out the base lines before padding if (elem.isArea) { startBaseLine = start.splice(start.length - 6, 6); endBaseLine = end.splice(end.length - 6, 6); } // if shifting points, prepend a dummy point to the end path if (shift <= end.length / numParams && start.length === end.length) { while (shift--) { end = [].concat(end).splice(0, numParams).concat(end); } } elem.shift = 0; // reset for following animations // copy and append last point until the length matches the end length if (start.length) { endLength = end.length; while (start.length < endLength) { //bezier && sixify(start); slice = [].concat(start).splice(start.length - numParams, numParams); if (bezier) { // disable first control point slice[numParams - 6] = slice[numParams - 2]; slice[numParams - 5] = slice[numParams - 1]; } start = start.concat(slice); } } if (startBaseLine) { // append the base lines for areas start = start.concat(startBaseLine); end = end.concat(endBaseLine); } return [start, end]; }, /** * Interpolate each value of the path and return the array */ step: function (start, end, pos, complete) { var ret = [], i = start.length, startVal; if (pos === 1) { // land on the final path without adjustment points appended in the ends ret = complete; } else if (i === end.length && pos < 1) { while (i--) { startVal = parseFloat(start[i]); ret[i] = isNaN(startVal) ? // a letter instruction like M or L start[i] : pos * (parseFloat(end[i] - startVal)) + startVal; } } else { // if animation is finished or length not matching, land on right value ret = end; } return ret; } }; (function ($) { /** * The default HighchartsAdapter for jQuery */ win.HighchartsAdapter = win.HighchartsAdapter || ($ && { /** * Initialize the adapter by applying some extensions to jQuery */ init: function (pathAnim) { // extend the animate function to allow SVG animations var Fx = $.fx, Step = Fx.step, dSetter, Tween = $.Tween, propHooks = Tween && Tween.propHooks, opacityHook = $.cssHooks.opacity; /*jslint unparam: true*//* allow unused param x in this function */ $.extend($.easing, { easeOutQuad: function (x, t, b, c, d) { return -c * (t /= d) * (t - 2) + b; } }); /*jslint unparam: false*/ // extend some methods to check for elem.attr, which means it is a Highcharts SVG object $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) { var obj = Step, base, elem; // Handle different parent objects if (fn === 'cur') { obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype } else if (fn === '_default' && Tween) { // jQuery 1.8 model obj = propHooks[fn]; fn = 'set'; } // Overwrite the method base = obj[fn]; if (base) { // step.width and step.height don't exist in jQuery < 1.7 // create the extended function replacement obj[fn] = function (fx) { // Fx.prototype.cur does not use fx argument fx = i ? fx : this; // Don't run animations on textual properties like align (#1821) if (fx.prop === 'align') { return; } // shortcut elem = fx.elem; // Fx.prototype.cur returns the current value. The other ones are setters // and returning a value has no effect. return elem.attr ? // is SVG element wrapper elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method base.apply(this, arguments); // use jQuery's built-in method }; } }); // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+ wrap(opacityHook, 'get', function (proceed, elem, computed) { return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed); }); // Define the setter function for d (path definitions) dSetter = function (fx) { var elem = fx.elem, ends; // Normally start and end should be set in state == 0, but sometimes, // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped // in these cases if (!fx.started) { ends = pathAnim.init(elem, elem.d, elem.toD); fx.start = ends[0]; fx.end = ends[1]; fx.started = true; } // interpolate each value of the path elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD)); }; // jQuery 1.8 style if (Tween) { propHooks.d = { set: dSetter }; // pre 1.8 } else { // animate paths Step.d = dSetter; } /** * Utility for iterating over an array. Parameters are reversed compared to jQuery. * @param {Array} arr * @param {Function} fn */ this.each = Array.prototype.forEach ? function (arr, fn) { // modern browsers return Array.prototype.forEach.call(arr, fn); } : function (arr, fn) { // legacy var i = 0, len = arr.length; for (; i < len; i++) { if (fn.call(arr[i], arr[i], i, arr) === false) { return i; } } }; /** * Register Highcharts as a plugin in the respective framework */ $.fn.highcharts = function () { var constr = 'Chart', // default constructor args = arguments, options, ret, chart; if (isString(args[0])) { constr = args[0]; args = Array.prototype.slice.call(args, 1); } options = args[0]; // Create the chart if (options !== UNDEFINED) { /*jslint unused:false*/ options.chart = options.chart || {}; options.chart.renderTo = this[0]; chart = new Highcharts[constr](options, args[1]); ret = this; /*jslint unused:true*/ } // When called without parameters or with the return argument, get a predefined chart if (options === UNDEFINED) { ret = charts[attr(this[0], 'data-highcharts-chart')]; } return ret; }; }, /** * Downloads a script and executes a callback when done. * @param {String} scriptLocation * @param {Function} callback */ getScript: $.getScript, /** * Return the index of an item in an array, or -1 if not found */ inArray: $.inArray, /** * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method. * @param {Object} elem The HTML element * @param {String} method Which method to run on the wrapped element */ adapterRun: function (elem, method) { return $(elem)[method](); }, /** * Filter an array */ grep: $.grep, /** * Map an array * @param {Array} arr * @param {Function} fn */ map: function (arr, fn) { //return jQuery.map(arr, fn); var results = [], i = 0, len = arr.length; for (; i < len; i++) { results[i] = fn.call(arr[i], arr[i], i, arr); } return results; }, /** * Get the position of an element relative to the top left of the page */ offset: function (el) { return $(el).offset(); }, /** * Add an event listener * @param {Object} el A HTML element or custom object * @param {String} event The event type * @param {Function} fn The event handler */ addEvent: function (el, event, fn) { $(el).bind(event, fn); }, /** * Remove event added with addEvent * @param {Object} el The object * @param {String} eventType The event type. Leave blank to remove all events. * @param {Function} handler The function to remove */ removeEvent: function (el, eventType, handler) { // workaround for jQuery issue with unbinding custom events: // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2 var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent'; if (doc[func] && el && !el[func]) { el[func] = function () {}; } $(el).unbind(eventType, handler); }, /** * Fire an event on a custom object * @param {Object} el * @param {String} type * @param {Object} eventArguments * @param {Function} defaultFunction */ fireEvent: function (el, type, eventArguments, defaultFunction) { var event = $.Event(type), detachedType = 'detached' + type, defaultPrevented; // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts // never uses these properties, Chrome includes them in the default click event and // raises the warning when they are copied over in the extend statement below. // // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid // testing if they are there (warning in chrome) the only option is to test if running IE. if (!isIE && eventArguments) { delete eventArguments.layerX; delete eventArguments.layerY; } extend(event, eventArguments); // Prevent jQuery from triggering the object method that is named the // same as the event. For example, if the event is 'select', jQuery // attempts calling el.select and it goes into a loop. if (el[type]) { el[detachedType] = el[type]; el[type] = null; } // Wrap preventDefault and stopPropagation in try/catch blocks in // order to prevent JS errors when cancelling events on non-DOM // objects. #615. /*jslint unparam: true*/ $.each(['preventDefault', 'stopPropagation'], function (i, fn) { var base = event[fn]; event[fn] = function () { try { base.call(event); } catch (e) { if (fn === 'preventDefault') { defaultPrevented = true; } } }; }); /*jslint unparam: false*/ // trigger it $(el).trigger(event); // attach the method if (el[detachedType]) { el[type] = el[detachedType]; el[detachedType] = null; } if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) { defaultFunction(event); } }, /** * Extension method needed for MooTools */ washMouseEvent: function (e) { var ret = e.originalEvent || e; // computed by jQuery, needed by IE8 if (ret.pageX === UNDEFINED) { // #1236 ret.pageX = e.pageX; ret.pageY = e.pageY; } return ret; }, /** * Animate a HTML element or SVG element wrapper * @param {Object} el * @param {Object} params * @param {Object} options jQuery-like animation options: duration, easing, callback */ animate: function (el, params, options) { var $el = $(el); if (!el.style) { el.style = {}; // #1881 } if (params.d) { el.toD = params.d; // keep the array form for paths, used in $.fx.step.d params.d = 1; // because in jQuery, animating to an array has a different meaning } $el.stop(); if (params.opacity !== UNDEFINED && el.attr) { params.opacity += 'px'; // force jQuery to use same logic as width and height (#2161) } $el.animate(params, options); }, /** * Stop running animation */ stop: function (el) { $(el).stop(); } }); }(win.jQuery)); // check for a custom HighchartsAdapter defined prior to this file var globalAdapter = win.HighchartsAdapter, adapter = globalAdapter || {}; // Initialize the adapter if (globalAdapter) { globalAdapter.init.call(globalAdapter, pathAnim); } // Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object // and all the utility functions will be null. In that case they are populated by the // default adapters below. var adapterRun = adapter.adapterRun, getScript = adapter.getScript, inArray = adapter.inArray, each = adapter.each, grep = adapter.grep, offset = adapter.offset, map = adapter.map, addEvent = adapter.addEvent, removeEvent = adapter.removeEvent, fireEvent = adapter.fireEvent, washMouseEvent = adapter.washMouseEvent, animate = adapter.animate, stop = adapter.stop; /* **************************************************************************** * Handle the options * *****************************************************************************/ var defaultLabelOptions = { enabled: true, // rotation: 0, // align: 'center', x: 0, y: 15, /*formatter: function () { return this.value; },*/ style: { color: '#666', cursor: 'default', fontSize: '11px', lineHeight: '14px' } }; defaultOptions = { colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a'], symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'], lang: { loading: 'Loading...', months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], decimalPoint: '.', numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels resetZoom: 'Reset zoom', resetZoomTitle: 'Reset zoom level 1:1', thousandsSep: ',' }, global: { useUTC: true, canvasToolsURL: 'http://code.highcharts.com/3.0.6/modules/canvas-tools.js', VMLRadialGradientURL: 'http://code.highcharts.com/3.0.6/gfx/vml-radial-gradient.png' }, chart: { //animation: true, //alignTicks: false, //reflow: true, //className: null, //events: { load, selection }, //margin: [null], //marginTop: null, //marginRight: null, //marginBottom: null, //marginLeft: null, borderColor: '#4572A7', //borderWidth: 0, borderRadius: 5, defaultSeriesType: 'line', ignoreHiddenSeries: true, //inverted: false, //shadow: false, spacing: [10, 10, 15, 10], //spacingTop: 10, //spacingRight: 10, //spacingBottom: 15, //spacingLeft: 10, style: { fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font fontSize: '12px' }, backgroundColor: '#FFFFFF', //plotBackgroundColor: null, plotBorderColor: '#C0C0C0', //plotBorderWidth: 0, //plotShadow: false, //zoomType: '' resetZoomButton: { theme: { zIndex: 20 }, position: { align: 'right', x: -10, //verticalAlign: 'top', y: 10 } // relativeTo: 'plot' } }, title: { text: 'Chart title', align: 'center', // floating: false, margin: 15, // x: 0, // verticalAlign: 'top', // y: null, style: { color: '#274b6d',//#3E576F', fontSize: '16px' } }, subtitle: { text: '', align: 'center', // floating: false // x: 0, // verticalAlign: 'top', // y: null, style: { color: '#4d759e' } }, plotOptions: { line: { // base series options allowPointSelect: false, showCheckbox: false, animation: { duration: 1000 }, //connectNulls: false, //cursor: 'default', //clip: true, //dashStyle: null, //enableMouseTracking: true, events: {}, //legendIndex: 0, lineWidth: 2, //shadow: false, // stacking: null, marker: { enabled: true, //symbol: null, lineWidth: 0, radius: 4, lineColor: '#FFFFFF', //fillColor: null, states: { // states for a single point hover: { enabled: true //radius: base + 2 }, select: { fillColor: '#FFFFFF', lineColor: '#000000', lineWidth: 2 } } }, point: { events: {} }, dataLabels: merge(defaultLabelOptions, { align: 'center', enabled: false, formatter: function () { return this.y === null ? '' : numberFormat(this.y, -1); }, verticalAlign: 'bottom', // above singular point y: 0 // backgroundColor: undefined, // borderColor: undefined, // borderRadius: undefined, // borderWidth: undefined, // padding: 3, // shadow: false }), cropThreshold: 300, // draw points outside the plot area when the number of points is less than this pointRange: 0, //pointStart: 0, //pointInterval: 1, showInLegend: true, states: { // states for the entire series hover: { //enabled: false, //lineWidth: base + 1, marker: { // lineWidth: base + 1, // radius: base + 1 } }, select: { marker: {} } }, stickyTracking: true //tooltip: { //pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b>' //valueDecimals: null, //xDateFormat: '%A, %b %e, %Y', //valuePrefix: '', //ySuffix: '' //} // turboThreshold: 1000 // zIndex: null } }, labels: { //items: [], style: { //font: defaultFont, position: ABSOLUTE, color: '#3E576F' } }, legend: { enabled: true, align: 'center', //floating: false, layout: 'horizontal', labelFormatter: function () { return this.name; }, borderWidth: 1, borderColor: '#909090', borderRadius: 5, navigation: { // animation: true, activeColor: '#274b6d', // arrowSize: 12 inactiveColor: '#CCC' // style: {} // text styles }, // margin: 10, // reversed: false, shadow: false, // backgroundColor: null, /*style: { padding: '5px' },*/ itemStyle: { cursor: 'pointer', color: '#274b6d', fontSize: '12px' }, itemHoverStyle: { //cursor: 'pointer', removed as of #601 color: '#000' }, itemHiddenStyle: { color: '#CCC' }, itemCheckboxStyle: { position: ABSOLUTE, width: '13px', // for IE precision height: '13px' }, // itemWidth: undefined, symbolWidth: 16, symbolPadding: 5, verticalAlign: 'bottom', // width: undefined, x: 0, y: 0, title: { //text: null, style: { fontWeight: 'bold' } } }, loading: { // hideDuration: 100, labelStyle: { fontWeight: 'bold', position: RELATIVE, top: '1em' }, // showDuration: 0, style: { position: ABSOLUTE, backgroundColor: 'white', opacity: 0.5, textAlign: 'center' } }, tooltip: { enabled: true, animation: hasSVG, //crosshairs: null, backgroundColor: 'rgba(255, 255, 255, .85)', borderWidth: 1, borderRadius: 3, dateTimeLabelFormats: { millisecond: '%A, %b %e, %H:%M:%S.%L', second: '%A, %b %e, %H:%M:%S', minute: '%A, %b %e, %H:%M', hour: '%A, %b %e, %H:%M', day: '%A, %b %e, %Y', week: 'Week from %A, %b %e, %Y', month: '%B %Y', year: '%Y' }, //formatter: defaultFormatter, headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>', pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>', shadow: true, //shared: false, snap: isTouchDevice ? 25 : 10, style: { color: '#333333', cursor: 'default', fontSize: '12px', padding: '8px', whiteSpace: 'nowrap' } //xDateFormat: '%A, %b %e, %Y', //valueDecimals: null, //valuePrefix: '', //valueSuffix: '' }, credits: { enabled: true, text: 'Highcharts.com', href: 'http://www.highcharts.com', position: { align: 'right', x: -10, verticalAlign: 'bottom', y: -5 }, style: { cursor: 'pointer', color: '#909090', fontSize: '9px' } } }; // Series defaults var defaultPlotOptions = defaultOptions.plotOptions, defaultSeriesOptions = defaultPlotOptions.line; // set the default time methods setTimeMethods(); /** * Set the time methods globally based on the useUTC option. Time method can be either * local time or UTC (default). */ function setTimeMethods() { var useUTC = defaultOptions.global.useUTC, GET = useUTC ? 'getUTC' : 'get', SET = useUTC ? 'setUTC' : 'set'; makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) { return new Date( year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0) ).getTime(); }; getMinutes = GET + 'Minutes'; getHours = GET + 'Hours'; getDay = GET + 'Day'; getDate = GET + 'Date'; getMonth = GET + 'Month'; getFullYear = GET + 'FullYear'; setMinutes = SET + 'Minutes'; setHours = SET + 'Hours'; setDate = SET + 'Date'; setMonth = SET + 'Month'; setFullYear = SET + 'FullYear'; } /** * Merge the default options with custom options and return the new options structure * @param {Object} options The new custom options */ function setOptions(options) { // Pull out axis options and apply them to the respective default axis options /*defaultXAxisOptions = merge(defaultXAxisOptions, options.xAxis); defaultYAxisOptions = merge(defaultYAxisOptions, options.yAxis); options.xAxis = options.yAxis = UNDEFINED;*/ // Merge in the default options defaultOptions = merge(defaultOptions, options); // Apply UTC setTimeMethods(); return defaultOptions; } /** * Get the updated default options. Merely exposing defaultOptions for outside modules * isn't enough because the setOptions method creates a new object. */ function getOptions() { return defaultOptions; } /** * Handle color operations. The object methods are chainable. * @param {String} input The input color in either rbga or hex format */ var Color = function (input) { // declare variables var rgba = [], result, stops; /** * Parse the input color to rgba array * @param {String} input */ function init(input) { // Gradients if (input && input.stops) { stops = map(input.stops, function (stop) { return Color(stop[1]); }); // Solid colors } else { // rgba result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(input); if (result) { rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)]; } else { // hex result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input); if (result) { rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1]; } else { // rgb result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(input); if (result) { rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1]; } } } } } /** * Return the color a specified format * @param {String} format */ function get(format) { var ret; if (stops) { ret = merge(input); ret.stops = [].concat(ret.stops); each(stops, function (stop, i) { ret.stops[i] = [ret.stops[i][0], stop.get(format)]; }); // it's NaN if gradient colors on a column chart } else if (rgba && !isNaN(rgba[0])) { if (format === 'rgb') { ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')'; } else if (format === 'a') { ret = rgba[3]; } else { ret = 'rgba(' + rgba.join(',') + ')'; } } else { ret = input; } return ret; } /** * Brighten the color * @param {Number} alpha */ function brighten(alpha) { if (stops) { each(stops, function (stop) { stop.brighten(alpha); }); } else if (isNumber(alpha) && alpha !== 0) { var i; for (i = 0; i < 3; i++) { rgba[i] += pInt(alpha * 255); if (rgba[i] < 0) { rgba[i] = 0; } if (rgba[i] > 255) { rgba[i] = 255; } } } return this; } /** * Set the color's opacity to a given alpha value * @param {Number} alpha */ function setOpacity(alpha) { rgba[3] = alpha; return this; } // initialize: parse the input init(input); // public methods return { get: get, brighten: brighten, rgba: rgba, setOpacity: setOpacity }; }; /** * A wrapper object for SVG elements */ function SVGElement() {} SVGElement.prototype = { /** * Initialize the SVG renderer * @param {Object} renderer * @param {String} nodeName */ init: function (renderer, nodeName) { var wrapper = this; wrapper.element = nodeName === 'span' ? createElement(nodeName) : doc.createElementNS(SVG_NS, nodeName); wrapper.renderer = renderer; /** * A collection of attribute setters. These methods, if defined, are called right before a certain * attribute is set on an element wrapper. Returning false prevents the default attribute * se