Skip to content

Instantly share code, notes, and snippets.

@dsyer
Created February 8, 2012 15:58
Show Gist options
  • Save dsyer/1770630 to your computer and use it in GitHub Desktop.
Save dsyer/1770630 to your computer and use it in GitHub Desktop.
Some Gatling Experiments
.cache
target/
*~
#*
*#
*/src/main/*/META-INF/
*/src/main/*/WEB-INF/lib/
.access_token
.result
.classpath
.project
.DS_Store
.settings/
*.iml
*.iws
*.ipr
.idea/
cargo-installs/

Some Gatling Experiments

package org.cloudfoundry.runtime.gatling
import com.excilys.ebi.gatling.core.Predef.chain
import com.excilys.ebi.gatling.core.Predef.scenario
import com.excilys.ebi.gatling.core.feeder.Feeder
import com.excilys.ebi.gatling.http.Predef.http
import com.excilys.ebi.gatling.http.Predef.httpConfig
import com.excilys.ebi.gatling.http.Predef.intToString
import com.excilys.ebi.gatling.http.Predef.regex
import com.excilys.ebi.gatling.http.Predef.status
import com.excilys.ebi.gatling.http.Predef.toHttpProtocolConfiguration
import com.excilys.ebi.gatling.script.GatlingSimulation
class CreateUsersSimulation extends GatlingSimulation {
val urlBase = sys.env.getOrElse("GATLING_UAA_BASE", "http://localhost:8080/uaa")
val httpConf = httpConfig.baseURL(urlBase).proxy("localhost", 8080)
val plainHeaders = Map(
"Accept" -> """application/json""",
"Content-Type" -> """application/x-www-form-urlencoded""")
var counter = 0
val feeder = new Feeder(null) {
def next = {
println("Counter " + counter)
counter += 1
Map("username" -> ("joel" + counter))
}
}
val create = chain.exec(
http("getToken")
.post("/oauth/token")
.basicAuth("scim", "scimsecret")
.param("client_id", "scim")
.param("scope", "write password")
.param("grant_type", "client_credentials")
.headers(plainHeaders)
.check(status.eq(200), regex(""""access_token":"(.*?)"""").saveAs("access_token"))).exec(
http("createUser")
.post("/User")
.header("Authorization", "Bearer ${access_token}")
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.body("""
{"name":{"givenName":"Joe","familyName":"User","formatted":"Joe User"},"userName":"${username}","emails":[{"value":"${username}@blah.com"}]}
""")
.check(status.eq(201)))
val user = scenario("Create user").feed(feeder).insertChain(create)
runSimulation(
user.configure users 10 protocolConfig httpConf)
}
import com.excilys.ebi.gatling.app.Gatling
import com.excilys.ebi.gatling.core.util.PathHelper.path2string
import IDEPathHelper.{ resultsFolder, requestBodiesFolder, packageName, eclipseSimulationFolder, eclipseAssetsFolder, dataFolder }
object Engine extends App {
Gatling(dataFolder, resultsFolder, requestBodiesFolder, eclipseAssetsFolder, eclipseSimulationFolder, packageName)
}
#########################
# Gatling Configuration #
#########################
# This file contains all the settings configurable for Gatling with their default values
gatling {
encoding = "utf-8" # encoding for every file manipulation made in gatling
simulation {
timeout = 86400 # max duration of a simulation in seconds
scalaPackage = ""
}
charting {
indicators {
lowerBound = 100 # in ms
higherBound = 500 # in ms
}
}
http {
provider = "Netty" # Choose between 'Netty', 'JDK', 'Apache' or 'Grizzly'
compressionEnabled = true # Set if compression should be supported or not
connectionTimeout = 60000 # Timeout of the connection to the server (ms)
requestTimeout = 60000 # Timeout of the requests (ms)
maxRetry = 5 # number of times that a request should be tried again
}
}
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
* @license Highcharts JS v2.1.9 (2011-11-11)
*
* (c) 2009-2011 Torstein Hønsi
*
* License: www.highcharts.com/license
*/
// JSLint options:
/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $ */
(function () {
// encapsulated variables
var 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,
isIE = /msie/i.test(userAgent) && !win.opera,
docMode8 = doc.documentMode === 8,
isWebKit = /AppleWebKit/.test(userAgent),
isFirefox = /Firefox/.test(userAgent),
SVG_NS = 'http://www.w3.org/2000/svg',
hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
hasRtlBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
Renderer,
hasTouch = doc.documentElement.ontouchstart !== undefined,
symbolSizes = {},
idCounter = 0,
garbageBin,
defaultOptions,
dateFormat, // function
globalAnimation,
pathAnim,
timeUnits,
// some constants for frequently used strings
UNDEFINED,
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)
* FF: 0.00000000001 (unlimited)
* Chrome: 0.000001
* Safari: 0.000001
* Opera: 0.00000000001 (unlimited)
*/
TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.000001 : 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
FILL = 'fill',
LINEAR_GRADIENT = 'linearGradient',
STOPS = 'stops',
STROKE = 'stroke',
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,
// check for a custom HighchartsAdapter defined prior to this file
globalAdapter = win.HighchartsAdapter,
adapter = globalAdapter || {},
// 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.
each = adapter.each,
grep = adapter.grep,
offset = adapter.offset,
map = adapter.map,
merge = adapter.merge,
addEvent = adapter.addEvent,
removeEvent = adapter.removeEvent,
fireEvent = adapter.fireEvent,
animate = adapter.animate,
stop = adapter.stop,
// lookup over the types and the associated classes
seriesTypes = {};
// The Highcharts namespace
win.Highcharts = {};
function arrayMin(data) {
var i = 1,
min = data[0],
length = data.length;
for (; i < length; i++) {
if (data[i] < min) min = data[i];
}
return min;
}
function arrayMax(data) {
var i = 1,
max = data[0],
length = data.length;
for (; i < length; i++) {
if (data[i] > max) max = data[i];
}
return max;
}
/**
* 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;
}
/**
* 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,
c = 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 || 0).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) : "");
}
/**
* 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) {
function pad(number, length) {
// two digits
number = number.toString().replace(/^([0-9])$/, '0$1');
// three digits
if (length === 3) {
number = number.toString().replace(/^([0-9]{2})$/, '0$1');
}
return number;
}
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,
/* // uncomment this and the 'W' format key below to enable week numbers
weekNumber = function () {
var clone = new Date(date.valueOf()),
day = clone[getDay]() == 0 ? 7 : clone[getDay](),
dayNumber;
clone.setDate(clone[getDate]() + 4 - day);
dayNumber = mathFloor((clone.getTime() - new Date(clone[getFullYear](), 0, 1, -6)) / 86400000);
return 1 + mathFloor(dayNumber / 7);
},
*/
// list all format keys
replacements = {
// 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(timestamp % 1000, 3) // Milliseconds (naming from Ruby)
};
// do the replaces
for (key in replacements) {
format = format.replace('%' + key, replacements[key]);
}
// Optionally capitalize the string and return
return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format;
};
/**
* 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 = multiples ? 1 : math.pow(10, mathFloor(math.log(interval) / math.LN10));
magnitude = pick(magnitude, 1);
normalized = interval / magnitude;
// multiples for a linear scale
if (!multiples) {
multiples = [1, 2, 2.5, 5, 10];
//multiples = [1, 2, 2.5, 4, 5, 7.5, 10];
// the allowDecimals option
if (options && (options.allowDecimals === false || options.type === 'logarithmic')) {
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;
}
/**
* 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 {Number} tickInterval The approximate interval in axis values (ms)
* @param {Number} min The minimum in axis values
* @param {Number} max The maximum in axis values
* @param {Number} startOfWeek
* @param {Array} unitsOption
*/
function getTimeTicks(tickInterval, min, max, startOfWeek, unitsOption) {
var tickPositions = [],
i,
useUTC = defaultOptions.global.useUTC,
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];
// 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 minimum value by flooring the date
var multitude = normalizeTickInterval(tickInterval / interval, multiples),
minYear, // used in months and years as a basis for Date.UTC()
minDate = new Date(min);
minDate.setMilliseconds(0);
if (interval >= timeUnits[SECOND]) { // second
minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
multitude * mathFloor(minDate.getSeconds() / multitude));
}
if (interval >= timeUnits[MINUTE]) { // minute
minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
multitude * mathFloor(minDate[getMinutes]() / multitude));
}
if (interval >= timeUnits[HOUR]) { // hour
minDate[setHours](interval >= timeUnits[DAY] ? 0 :
multitude * mathFloor(minDate[getHours]() / multitude));
}
if (interval >= timeUnits[DAY]) { // day
minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
multitude * mathFloor(minDate[getDate]() / multitude));
}
if (interval >= timeUnits[MONTH]) { // month
minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
multitude * mathFloor(minDate[getMonth]() / multitude));
minYear = minDate[getFullYear]();
}
if (interval >= timeUnits[YEAR]) { // year
minYear -= minYear % multitude;
minDate[setFullYear](minYear);
}
// week is a special case that runs outside the hierarchy
if (interval === timeUnits[WEEK]) {
// get start of current week, independent of multitude
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]();
// 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 * multitude, 0);
// if the interval is months, use Date.UTC to increase months
} else if (interval === timeUnits[MONTH]) {
time = makeTime(minYear, minMonth + i * multitude);
// 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 * multitude * (interval === timeUnits[DAY] ? 1 : 7));
// else, the interval is fixed and we use simple addition
} else {
time += interval * multitude;
}
i++;
}
// push the last time
tickPositions.push(time);
// record information on the chosen unit - for dynamic label formatter
tickPositions.info = {
unitName: unit[0],
unitRange: interval,
count: multitude,
totalRange: interval * multitude
};
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 extracted from Tooltip code that places a tooltip in a chart without spilling over
* and not covering the point it self.
*/
function placeBox(boxWidth, boxHeight, outerLeft, outerTop, outerWidth, outerHeight, point, distance) {
// keep the box within the chart area
var pointX = point.x,
pointY = point.y,
x = pointX - boxWidth + outerLeft - distance,
y = pointY - boxHeight + outerTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip
alignedRight;
// it is too far to the left, adjust it
if (x < 7) {
x = outerLeft + pointX + distance;
}
// Test to see if the tooltip is too far to the right,
// if it is, move it back to be inside and then up to not cover the point.
if ((x + boxWidth) > (outerLeft + outerWidth)) {
x -= (x + boxWidth) - (outerLeft + outerWidth);
y = pointY - boxHeight + outerTop - distance;
alignedRight = true;
}
// if it is now above the plot area, align it to the top of the plot area
if (y < outerTop + 5) {
y = outerTop + 5;
// If the tooltip is still covering the point, move it below instead
if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
y = pointY + outerTop + distance; // below
}
} else if (y + boxHeight > outerTop + outerHeight) {
y = outerTop + outerHeight - boxHeight - distance; // below
}
return {x: x, y: y};
}
/**
* 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,
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) {
var 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
}
}
/**
* 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.
*/
function destroyObjectProperties(obj) {
var n;
for (n in obj) {
// If the object is non-null and destroy is defined
if (obj[n] && obj[n].destroy) {
// Invoke the destroy
obj[n].destroy();
}
// Delete the property from the object.
delete obj[n];
}
}
/**
* 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, 30 * 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 === 1) {
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;
}
};
/**
* 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);
}
/*
* Define the adapter for frameworks. If an external adapter is not defined,
* Highcharts reverts to the built-in jQuery adapter.
*/
if (globalAdapter && globalAdapter.init) {
// Initialize the adapter with the pathAnim object that takes care
// of path animations.
globalAdapter.init(pathAnim);
}
if (!globalAdapter && win.jQuery) {
var jQ = jQuery;
/**
* Utility for iterating over an array. Parameters are reversed compared to jQuery.
* @param {Array} arr
* @param {Function} fn
*/
each = function (arr, fn) {
var i = 0,
len = arr.length;
for (; i < len; i++) {
if (fn.call(arr[i], arr[i], i, arr) === false) {
return i;
}
}
};
/**
* Filter an array
*/
grep = jQ.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;
};
/**
* Deep merge two objects and return a third object
*/
merge = function () {
var args = arguments;
return jQ.extend(true, null, args[0], args[1], args[2], args[3]);
};
/**
* Get the position of an element relative to the top left of the page
*/
offset = function (el) {
return jQ(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) {
jQ(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[func]) {
el[func] = function () {};
}
jQ(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 = jQ.Event(type),
detachedType = 'detached' + type;
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;
}
// trigger it
jQ(el).trigger(event);
// attach the method
if (el[detachedType]) {
el[type] = el[detachedType];
el[detachedType] = null;
}
if (defaultFunction && !event.isDefaultPrevented()) {
defaultFunction(event);
}
};
/**
* 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 = jQ(el);
if (params.d) {
el.toD = params.d; // keep the array form for paths, used in jQ.fx.step.d
params.d = 1; // because in jQuery, animating to an array has a different meaning
}
$el.stop();
$el.animate(params, options);
};
/**
* Stop running animation
*/
stop = function (el) {
jQ(el).stop();
};
//=== Extend jQuery on init
/*jslint unparam: true*//* allow unused param x in this function */
jQ.extend(jQ.easing, {
easeOutQuad: function (x, t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
}
});
/*jslint unparam: false*/
// extend the animate function to allow SVG animations
var jFx = jQuery.fx,
jStep = jFx.step;
// extend some methods to check for elem.attr, which means it is a Highcharts SVG object
each(['cur', '_default', 'width', 'height'], function (fn, i) {
var obj = i ? jStep : jFx.prototype, // 'cur', the getter' relates to jFx.prototype
base = obj[fn],
elem;
if (base) { // step.width and step.height don't exist in jQuery < 1.7
// create the extended function replacement
obj[fn] = function (fx) {
// jFx.prototype.cur does not use fx argument
fx = i ? fx : this;
// shortcut
elem = fx.elem;
// jFX.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, fx.now) : // apply the SVG wrapper's method
base.apply(this, arguments); // use jQuery's built-in method
};
}
});
// animate paths
jStep.d = function (fx) {
var elem = fx.elem;
// 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) {
var 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));
};
}
/**
* 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;
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 = useUTC ? 'getUTCMinutes' : 'getMinutes';
getHours = useUTC ? 'getUTCHours' : 'getHours';
getDay = useUTC ? 'getUTCDay' : 'getDay';
getDate = useUTC ? 'getUTCDate' : 'getDate';
getMonth = useUTC ? 'getUTCMonth' : 'getMonth';
getFullYear = useUTC ? 'getUTCFullYear' : 'getFullYear';
setMinutes = useUTC ? 'setUTCMinutes' : 'setMinutes';
setHours = useUTC ? 'setUTCHours' : 'setHours';
setDate = useUTC ? 'setUTCDate' : 'setDate';
setMonth = useUTC ? 'setUTCMonth' : 'setMonth';
setFullYear = useUTC ? 'setUTCFullYear' : 'setFullYear';
}
/**
* Merge the default options with custom options and return the new options structure
* @param {Object} options The new custom options
*/
function setOptions(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;
}
/**
* 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 = '';
}
/* ****************************************************************************
* Handle the options *
*****************************************************************************/
var
defaultLabelOptions = {
enabled: true,
// rotation: 0,
align: 'center',
x: 0,
y: 15,
/*formatter: function () {
return this.value;
},*/
style: {
color: '#666',
fontSize: '11px',
lineHeight: '14px'
}
};
defaultOptions = {
colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
'#DB843D', '#92A8CD', '#A47D7C', '#B5CA92'],
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', 'June', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
decimalPoint: '.',
resetZoom: 'Reset zoom',
resetZoomTitle: 'Reset zoom level 1:1',
thousandsSep: ','
},
global: {
useUTC: true
},
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,
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: ''
},
title: {
text: 'Chart title',
align: 'center',
// floating: false,
// margin: 15,
// x: 0,
// verticalAlign: 'top',
y: 15,
style: {
color: '#3E576F',
fontSize: '16px'
}
},
subtitle: {
text: '',
align: 'center',
// floating: false
// x: 0,
// verticalAlign: 'top',
y: 30,
style: {
color: '#6D869F'
}
},
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: true,
// stacking: null,
marker: {
enabled: true,
//symbol: null,
lineWidth: 0,
radius: 4,
lineColor: '#FFFFFF',
//fillColor: null,
states: { // states for a single point
hover: {
//radius: base + 2
},
select: {
fillColor: '#FFFFFF',
lineColor: '#000000',
lineWidth: 2
}
}
},
point: {
events: {}
},
dataLabels: merge(defaultLabelOptions, {
enabled: false,
y: -6,
formatter: function () {
return this.y;
}
}),
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>'
//yDecimals: null,
//xDateFormat: '%A, %b %e, %Y',
//yPrefix: '',
//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,
// margin: 10,
// reversed: false,
shadow: false,
// backgroundColor: null,
style: {
padding: '5px'
},
itemStyle: {
cursor: 'pointer',
color: '#3E576F'
},
itemHoverStyle: {
//cursor: 'pointer', removed as of #601
color: '#000000'
},
itemHiddenStyle: {
color: '#C0C0C0'
},
itemCheckboxStyle: {
position: ABSOLUTE,
width: '13px', // for IE precision
height: '13px'
},
// itemWidth: undefined,
symbolWidth: 16,
symbolPadding: 5,
verticalAlign: 'bottom',
// width: undefined,
x: 0,
y: 0
},
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,
//crosshairs: null,
backgroundColor: 'rgba(255, 255, 255, .85)',
borderWidth: 2,
borderRadius: 5,
//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: hasTouch ? 25 : 10,
style: {
color: '#333333',
fontSize: '12px',
padding: '5px',
whiteSpace: 'nowrap'
}
//xDateFormat: '%A, %b %e, %Y',
//yDecimals: null,
//yPrefix: '',
//ySuffix: ''
},
toolbar: {
itemStyle: {
color: '#4572A7',
cursor: 'pointer'
}
},
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: '10px'
}
}
};
// Axis defaults
/*jslint white: true*/
var defaultXAxisOptions = {
// allowDecimals: null,
// alternateGridColor: null,
// categories: [],
dateTimeLabelFormats: hash(
MILLISECOND, '%H:%M:%S.%L',
SECOND, '%H:%M:%S',
MINUTE, '%H:%M',
HOUR, '%H:%M',
DAY, '%e. %b',
WEEK, '%e. %b',
MONTH, '%b \'%y',
YEAR, '%Y'
),
endOnTick: false,
gridLineColor: '#C0C0C0',
// gridLineDashStyle: 'solid',
// gridLineWidth: 0,
// reversed: false,
labels: defaultLabelOptions,
// { step: null },
lineColor: '#C0D0E0',
lineWidth: 1,
//linkedTo: null,
max: null,
min: null,
minPadding: 0.01,
maxPadding: 0.01,
//minRange: null, // docs
minorGridLineColor: '#E0E0E0',
// minorGridLineDashStyle: null,
minorGridLineWidth: 1,
minorTickColor: '#A0A0A0',
//minorTickInterval: null,
minorTickLength: 2,
minorTickPosition: 'outside', // inside or outside
//minorTickWidth: 0,
//opposite: false,
//offset: 0,
//plotBands: [{
// events: {},
// zIndex: 1,
// labels: { align, x, verticalAlign, y, style, rotation, textAlign }
//}],
//plotLines: [{
// events: {}
// dashStyle: {}
// zIndex:
// labels: { align, x, verticalAlign, y, style, rotation, textAlign }
//}],
//reversed: false,
// showFirstLabel: true,
// showLastLabel: true,
startOfWeek: 1,
startOnTick: false,
tickColor: '#C0D0E0',
//tickInterval: null,
tickLength: 5,
tickmarkPlacement: 'between', // on or between
tickPixelInterval: 100,
tickPosition: 'outside',
tickWidth: 1,
title: {
//text: null,
align: 'middle', // low, middle or high
//margin: 0 for horizontal, 10 for vertical axes,
//rotation: 0,
//side: 'outside',
style: {
color: '#6D869F',
//font: defaultFont.replace('normal', 'bold')
fontWeight: 'bold'
}
//x: 0,
//y: 0
},
type: 'linear' // linear, logarithmic or datetime
},
defaultYAxisOptions = merge(defaultXAxisOptions, {
endOnTick: true,
gridLineWidth: 1,
tickPixelInterval: 72,
showLastLabel: true,
labels: {
align: 'right',
x: -8,
y: 3
},
lineWidth: 0,
maxPadding: 0.05,
minPadding: 0.05,
startOnTick: true,
tickWidth: 0,
title: {
rotation: 270,
text: 'Y-values'
},
stackLabels: {
enabled: false,
//align: dynamic,
//y: dynamic,
//x: dynamic,
//verticalAlign: dynamic,
//textAlign: dynamic,
//rotation: 0,
formatter: function () {
return this.total;
},
style: defaultLabelOptions.style
}
}),
defaultLeftAxisOptions = {
labels: {
align: 'right',
x: -8,
y: null
},
title: {
rotation: 270
}
},
defaultRightAxisOptions = {
labels: {
align: 'left',
x: 8,
y: null
},
title: {
rotation: 90
}
},
defaultBottomAxisOptions = { // horizontal axis
labels: {
align: 'center',
x: 0,
y: 14
// staggerLines: null
},
title: {
rotation: 0
}
},
defaultTopAxisOptions = merge(defaultBottomAxisOptions, {
labels: {
y: -5
// staggerLines: null
}
});
/*jslint white: false*/
// Series defaults
var defaultPlotOptions = defaultOptions.plotOptions,
defaultSeriesOptions = defaultPlotOptions.line;
//defaultPlotOptions.line = merge(defaultSeriesOptions);
defaultPlotOptions.spline = merge(defaultSeriesOptions);
defaultPlotOptions.scatter = merge(defaultSeriesOptions, {
lineWidth: 0,
states: {
hover: {
lineWidth: 0
}
},
tooltip: {
headerFormat: '<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',
pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
}
});
defaultPlotOptions.area = merge(defaultSeriesOptions, {
threshold: 0
// lineColor: null, // overrides color, but lets fillColor be unaltered
// fillOpacity: 0.75,
// fillColor: null
});
defaultPlotOptions.areaspline = merge(defaultPlotOptions.area);
defaultPlotOptions.column = merge(defaultSeriesOptions, {
borderColor: '#FFFFFF',
borderWidth: 1,
borderRadius: 0,
//colorByPoint: undefined,
groupPadding: 0.2,
marker: null, // point options are specified in the base options
pointPadding: 0.1,
//pointWidth: null,
minPointLength: 0,
cropThreshold: 50, // when there are more points, they will not animate out of the chart on xAxis.setExtremes
pointRange: null, // null means auto, meaning 1 in a categorized axis and least distance between points if not categories
states: {
hover: {
brightness: 0.1,
shadow: false
},
select: {
color: '#C0C0C0',
borderColor: '#000000',
shadow: false
}
},
dataLabels: {
y: null,
verticalAlign: null
},
threshold: 0
});
defaultPlotOptions.bar = merge(defaultPlotOptions.column, {
dataLabels: {
align: 'left',
x: 5,
y: 0
}
});
defaultPlotOptions.pie = merge(defaultSeriesOptions, {
//dragType: '', // n/a
borderColor: '#FFFFFF',
borderWidth: 1,
center: ['50%', '50%'],
colorByPoint: true, // always true for pies
dataLabels: {
// align: null,
// connectorWidth: 1,
// connectorColor: point.color,
// connectorPadding: 5,
distance: 30,
enabled: true,
formatter: function () {
return this.point.name;
},
// softConnector: true,
y: 5
},
//innerSize: 0,
legendType: 'point',
marker: null, // point options are specified in the base options
size: '75%',
showInLegend: false,
slicedOffset: 10,
states: {
hover: {
brightness: 0.1,
shadow: false
}
}
});
// set the default time methods
setTimeMethods();
/**
* 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;
/**
* Parse the input color to rgba array
* @param {String} input
*/
function init(input) {
// 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];
}
}
}
/**
* Return the color a specified format
* @param {String} format
*/
function get(format) {
var ret;
// it's NaN if gradient colors on a column chart
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 (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,
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 = 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
* setter to run. Returning a value causes the default setter to set that value. Used in
* Renderer.label.
*/
wrapper.attrSetters = {};
},
/**
* Animate a given attribute
* @param {Object} params
* @param {Number} options The same options as in jQuery animation
* @param {Function} complete Function to perform at the end of animation
*/
animate: function (params, options, complete) {
var animOptions = pick(options, globalAnimation, true);
if (animOptions) {
animOptions = merge(animOptions);
if (complete) { // allows using a callback with the global animation without overwriting it
animOptions.complete = complete;
}
animate(this, params, animOptions);
} else {
this.attr(params);
if (complete) {
complete();
}
}
},
/**
* Set or get a given attribute
* @param {Object|String} hash
* @param {Mixed|Undefined} val
*/
attr: function (hash, val) {
var wrapper = this,
key,
value,
result,
i,
child,
element = wrapper.element,
nodeName = element.nodeName,
renderer = wrapper.renderer,
skipAttr,
attrSetters = wrapper.attrSetters,
shadows = wrapper.shadows,
htmlNode = wrapper.htmlNode,
hasSetSymbolSize,
ret = wrapper;
// single key-value pair
if (isString(hash) && defined(val)) {
key = hash;
hash = {};
hash[key] = val;
}
// used as a getter: first argument is a string, second is undefined
if (isString(hash)) {
key = hash;
if (nodeName === 'circle') {
key = { x: 'cx', y: 'cy' }[key] || key;
} else if (key === 'strokeWidth') {
key = 'stroke-width';
}
ret = attr(element, key) || wrapper[key] || 0;
if (key !== 'd' && key !== 'visibility') { // 'd' is string in animation step
ret = parseFloat(ret);
}
// setter
} else {
for (key in hash) {
skipAttr = false; // reset
value = hash[key];
// check for a specific attribute setter
result = attrSetters[key] && attrSetters[key](value, key);
if (result !== false) {
if (result !== UNDEFINED) {
value = result; // the attribute setter has returned a new value to set
}
// paths
if (key === 'd') {
if (value && value.join) { // join path
value = value.join(' ');
}
if (/(NaN| {2}|^$)/.test(value)) {
value = 'M 0 0';
}
wrapper.d = value; // shortcut for animations
// update child tspans x values
} else if (key === 'x' && nodeName === 'text') {
for (i = 0; i < element.childNodes.length; i++) {
child = element.childNodes[i];
// if the x values are equal, the tspan represents a linebreak
if (attr(child, 'x') === attr(element, 'x')) {
//child.setAttribute('x', value);
attr(child, 'x', value);
}
}
if (wrapper.rotation) {
attr(element, 'transform', 'rotate(' + wrapper.rotation + ' ' + value + ' ' +
pInt(hash.y || attr(element, 'y')) + ')');
}
// apply gradients
} else if (key === 'fill') {
value = renderer.color(value, element, key);
// circle x and y
} else if (nodeName === 'circle' && (key === 'x' || key === 'y')) {
key = { x: 'cx', y: 'cy' }[key] || key;
// rectangle border radius
} else if (nodeName === 'rect' && key === 'r') {
attr(element, {
rx: value,
ry: value
});
skipAttr = true;
// translation and text rotation
} else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || key === 'verticalAlign') {
wrapper[key] = value;
wrapper.updateTransform();
skipAttr = true;
// apply opacity as subnode (required by legacy WebKit and Batik)
} else if (key === 'stroke') {
value = renderer.color(value, element, key);
// emulate VML's dashstyle implementation
} else if (key === 'dashstyle') {
key = 'stroke-dasharray';
value = value && value.toLowerCase();
if (value === 'solid') {
value = NONE;
} else if (value) {
value = value
.replace('shortdashdotdot', '3,1,1,1,1,1,')
.replace('shortdashdot', '3,1,1,1')
.replace('shortdot', '1,1,')
.replace('shortdash', '3,1,')
.replace('longdash', '8,3,')
.replace(/dot/g, '1,3,')
.replace('dash', '4,3,')
.replace(/,$/, '')
.split(','); // ending comma
i = value.length;
while (i--) {
value[i] = pInt(value[i]) * hash['stroke-width'];
}
value = value.join(',');
}
// special
} else if (key === 'isTracker') {
wrapper[key] = value;
// IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2
// is unable to cast them. Test again with final IE9.
} else if (key === 'width') {
value = pInt(value);
// Text alignment
} else if (key === 'align') {
key = 'text-anchor';
value = { left: 'start', center: 'middle', right: 'end' }[value];
// Title requires a subnode, #431
} else if (key === 'title') {
var title = doc.createElementNS(SVG_NS, 'title');
title.appendChild(doc.createTextNode(value));
element.appendChild(title);
}
// jQuery animate changes case
if (key === 'strokeWidth') {
key = 'stroke-width';
}
// Chrome/Win < 6 bug (http://code.google.com/p/chromium/issues/detail?id=15461)
if (isWebKit && key === 'stroke-width' && value === 0) {
value = 0.000001;
}
// symbols
if (wrapper.symbolName && /^(x|y|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
if (!hasSetSymbolSize) {
wrapper.symbolAttr(hash);
hasSetSymbolSize = true;
}
skipAttr = true;
}
// let the shadow follow the main element
if (shadows && /^(width|height|visibility|x|y|d|transform)$/.test(key)) {
i = shadows.length;
while (i--) {
attr(shadows[i], key, value);
}
}
// validate heights
if ((key === 'width' || key === 'height') && nodeName === 'rect' && value < 0) {
value = 0;
}
if (key === 'text') {
// only one node allowed
wrapper.textStr = value;
if (wrapper.added) {
renderer.buildText(wrapper);
}
} else if (!skipAttr) {
attr(element, key, value);
}
}
// Issue #38
if (htmlNode && (key === 'x' || key === 'y' ||
key === 'translateX' || key === 'translateY' || key === 'visibility')) {
var bBox,
arr = htmlNode.length ? htmlNode : [this],
length = arr.length,
itemWrapper,
j;
for (j = 0; j < length; j++) {
itemWrapper = arr[j];
bBox = itemWrapper.getBBox();
htmlNode = itemWrapper.htmlNode; // reassign to child item
css(htmlNode, extend(wrapper.styles, {
left: (bBox.x + (wrapper.translateX || 0)) + PX,
top: (bBox.y + (wrapper.translateY || 0)) + PX
}));
if (key === 'visibility') {
css(htmlNode, {
visibility: value
});
}
}
}
}
}
return ret;
},
/**
* If one of the symbol size affecting parameters are changed,
* check all the others only once for each call to an element's
* .attr() method
* @param {Object} hash
*/
symbolAttr: function (hash) {
var wrapper = this;
each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) {
wrapper[key] = pick(hash[key], wrapper[key]);
});
wrapper.attr({
d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
});
},
/**
* Apply a clipping path to this object
* @param {String} id
*/
clip: function (clipRect) {
return this.attr('clip-path', 'url(' + this.renderer.url + '#' + clipRect.id + ')');
},
/**
* Calculate the coordinates needed for drawing a rectangle crisply and return the
* calculated attributes
* @param {Number} strokeWidth
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
*/
crisp: function (strokeWidth, x, y, width, height) {
var wrapper = this,
key,
attribs = {},
values = {},
normalizer;
strokeWidth = strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
// normalize for crisp edges
values.x = mathFloor(x || wrapper.x || 0) + normalizer;
values.y = mathFloor(y || wrapper.y || 0) + normalizer;
values.width = mathFloor((width || wrapper.width || 0) - 2 * normalizer);
values.height = mathFloor((height || wrapper.height || 0) - 2 * normalizer);
values.strokeWidth = strokeWidth;
for (key in values) {
if (wrapper[key] !== values[key]) { // only set attribute if changed
wrapper[key] = attribs[key] = values[key];
}
}
return attribs;
},
/**
* Set styles for the element
* @param {Object} styles
*/
css: function (styles) {
/*jslint unparam: true*//* allow unused param a in the regexp function below */
var elemWrapper = this,
elem = elemWrapper.element,
textWidth = styles && styles.width && elem.nodeName === 'text',
n,
serializedCss = '',
hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
/*jslint unparam: false*/
// convert legacy
if (styles && styles.color) {
styles.fill = styles.color;
}
// Merge the new styles with the old ones
styles = extend(
elemWrapper.styles,
styles
);
// store object
elemWrapper.styles = styles;
// serialize and set style attribute
if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute
if (textWidth) {
delete styles.width;
}
css(elemWrapper.element, styles);
} else {
for (n in styles) {
serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';';
}
elemWrapper.attr({
style: serializedCss
});
}
// re-build text
if (textWidth && elemWrapper.added) {
elemWrapper.renderer.buildText(elemWrapper);
}
return elemWrapper;
},
/**
* Add an event listener
* @param {String} eventType
* @param {Function} handler
*/
on: function (eventType, handler) {
var fn = handler;
// touch
if (hasTouch && eventType === 'click') {
eventType = 'touchstart';
fn = function (e) {
e.preventDefault();
handler();
};
}
// simplest possible event model for internal use
this.element['on' + eventType] = fn;
return this;
},
/**
* Move an object and its children by x and y values
* @param {Number} x
* @param {Number} y
*/
translate: function (x, y) {
return this.attr({
translateX: x,
translateY: y
});
},
/**
* Invert a group, rotate and flip
*/
invert: function () {
var wrapper = this;
wrapper.inverted = true;
wrapper.updateTransform();
return wrapper;
},
/**
* Private method to update the transform attribute based on internal
* properties
*/
updateTransform: function () {
var wrapper = this,
translateX = wrapper.translateX || 0,
translateY = wrapper.translateY || 0,
inverted = wrapper.inverted,
rotation = wrapper.rotation,
transform = [];
// flipping affects translate as adjustment for flipping around the group's axis
if (inverted) {
translateX += wrapper.attr('width');
translateY += wrapper.attr('height');
}
// apply translate
if (translateX || translateY) {
transform.push('translate(' + translateX + ',' + translateY + ')');
}
// apply rotation
if (inverted) {
transform.push('rotate(90) scale(-1,1)');
} else if (rotation) { // text rotation
transform.push('rotate(' + rotation + ' ' + wrapper.x + ' ' + wrapper.y + ')');
}
if (transform.length) {
attr(wrapper.element, 'transform', transform.join(' '));
}
},
/**
* Bring the element to the front
*/
toFront: function () {
var element = this.element;
element.parentNode.appendChild(element);
return this;
},
/**
* Break down alignment options like align, verticalAlign, x and y
* to x and y relative to the chart.
*
* @param {Object} alignOptions
* @param {Boolean} alignByTranslate
* @param {Object} box The box to align to, needs a width and height
*
*/
align: function (alignOptions, alignByTranslate, box) {
var elemWrapper = this;
if (!alignOptions) { // called on resize
alignOptions = elemWrapper.alignOptions;
alignByTranslate = elemWrapper.alignByTranslate;
} else { // first call on instanciate
elemWrapper.alignOptions = alignOptions;
elemWrapper.alignByTranslate = alignByTranslate;
if (!box) { // boxes other than renderer handle this internally
elemWrapper.renderer.alignedObjects.push(elemWrapper);
}
}
box = pick(box, elemWrapper.renderer);
var align = alignOptions.align,
vAlign = alignOptions.verticalAlign,
x = (box.x || 0) + (alignOptions.x || 0), // default: left align
y = (box.y || 0) + (alignOptions.y || 0), // default: top align
attribs = {};
// align
if (/^(right|center)$/.test(align)) {
x += (box.width - (alignOptions.width || 0)) /
{ right: 1, center: 2 }[align];
}
attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x);
// vertical align
if (/^(bottom|middle)$/.test(vAlign)) {
y += (box.height - (alignOptions.height || 0)) /
({ bottom: 1, middle: 2 }[vAlign] || 1);
}
attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y);
// animate only if already placed
elemWrapper[elemWrapper.placed ? 'animate' : 'attr'](attribs);
elemWrapper.placed = true;
elemWrapper.alignAttr = attribs;
return elemWrapper;
},
/**
* Get the bounding box (width, height, x and y) for the element
*/
getBBox: function () {
var bBox,
width,
height,
rotation = this.rotation,
rad = rotation * deg2rad;
try { // fails in Firefox if the container has display: none
// use extend because IE9 is not allowed to change width and height in case
// of rotation (below)
bBox = extend({}, this.element.getBBox());
} catch (e) {
bBox = { width: 0, height: 0 };
}
width = bBox.width;
height = bBox.height;
// adjust for rotated text
if (rotation) {
bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad));
bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad));
}
return bBox;
},
/**
* Show the element
*/
show: function () {
return this.attr({ visibility: VISIBLE });
},
/**
* Hide the element
*/
hide: function () {
return this.attr({ visibility: HIDDEN });
},
/**
* Add the element
* @param {Object|Undefined} parent Can be an element, an element wrapper or undefined
* to append the element to the renderer.box.
*/
add: function (parent) {
var renderer = this.renderer,
parentWrapper = parent || renderer,
parentNode = parentWrapper.element || renderer.box,
childNodes = parentNode.childNodes,
element = this.element,
zIndex = attr(element, 'zIndex'),
otherElement,
otherZIndex,
i,
inserted;
// mark as inverted
this.parentInverted = parent && parent.inverted;
// build formatted text
if (this.textStr !== undefined) {
renderer.buildText(this);
}
// register html spans in groups
if (parent && this.htmlNode) {
if (!parent.htmlNode) {
parent.htmlNode = [];
}
parent.htmlNode.push(this);
}
// mark the container as having z indexed children
if (zIndex) {
parentWrapper.handleZ = true;
zIndex = pInt(zIndex);
}
// insert according to this and other elements' zIndex
if (parentWrapper.handleZ) { // this element or any of its siblings has a z index
for (i = 0; i < childNodes.length; i++) {
otherElement = childNodes[i];
otherZIndex = attr(otherElement, 'zIndex');
if (otherElement !== element && (
// insert before the first element with a higher zIndex
pInt(otherZIndex) > zIndex ||
// if no zIndex given, insert before the first element with a zIndex
(!defined(zIndex) && defined(otherZIndex))
)) {
parentNode.insertBefore(element, otherElement);
inserted = true;
break;
}
}
}
// default: append at the end
if (!inserted) {
parentNode.appendChild(element);
}
// mark as added
this.added = true;
// fire an event for internal hooks
fireEvent(this, 'add');
return this;
},
/**
* Removes a child either by removeChild or move to garbageBin.
* Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
*/
safeRemoveChild: function (element) {
var parentNode = element.parentNode;
if (parentNode) {
parentNode.removeChild(element);
}
},
/**
* Destroy the element and element wrapper
*/
destroy: function () {
var wrapper = this,
element = wrapper.element || {},
shadows = wrapper.shadows,
box = wrapper.box,
key,
i;
// remove events
element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = null;
stop(wrapper); // stop running animations
if (wrapper.clipPath) {
wrapper.clipPath = wrapper.clipPath.destroy();
}
// Destroy stops in case this is a gradient object
if (wrapper.stops) {
for (i = 0; i < wrapper.stops.length; i++) {
wrapper.stops[i] = wrapper.stops[i].destroy();
}
wrapper.stops = null;
}
// remove element
wrapper.safeRemoveChild(element);
// destroy shadows
if (shadows) {
each(shadows, function (shadow) {
wrapper.safeRemoveChild(shadow);
});
}
// destroy label box
if (box) {
box.destroy();
}
// remove from alignObjects
erase(wrapper.renderer.alignedObjects, wrapper);
for (key in wrapper) {
delete wrapper[key];
}
return null;
},
/**
* Empty a group element
*/
empty: function () {
var element = this.element,
childNodes = element.childNodes,
i = childNodes.length;
while (i--) {
element.removeChild(childNodes[i]);
}
},
/**
* Add a shadow to the element. Must be done after the element is added to the DOM
* @param {Boolean} apply
*/
shadow: function (apply, group) {
var shadows = [],
i,
shadow,
element = this.element,
// compensate for inverted plot area
transform = this.parentInverted ? '(-1,-1)' : '(1,1)';
if (apply) {
for (i = 1; i <= 3; i++) {
shadow = element.cloneNode(0);
attr(shadow, {
'isShadow': 'true',
'stroke': 'rgb(0, 0, 0)',
'stroke-opacity': 0.05 * i,
'stroke-width': 7 - 2 * i,
'transform': 'translate' + transform,
'fill': NONE
});
if (group) {
group.element.appendChild(shadow);
} else {
element.parentNode.insertBefore(shadow, element);
}
shadows.push(shadow);
}
this.shadows = shadows;
}
return this;
}
};
/**
* The default SVG renderer
*/
var SVGRenderer = function () {
this.init.apply(this, arguments);
};
SVGRenderer.prototype = {
Element: SVGElement,
/**
* Initialize the SVGRenderer
* @param {Object} container
* @param {Number} width
* @param {Number} height
* @param {Boolean} forExport
*/
init: function (container, width, height, forExport) {
var renderer = this,
loc = location,
boxWrapper;
boxWrapper = renderer.createElement('svg')
.attr({
xmlns: SVG_NS,
version: '1.1'
});
container.appendChild(boxWrapper.element);
// object properties
renderer.box = boxWrapper.element;
renderer.boxWrapper = boxWrapper;
renderer.alignedObjects = [];
renderer.url = isIE ? '' : loc.href.replace(/#.*?$/, ''); // page url used for internal references
renderer.defs = this.createElement('defs').add();
renderer.forExport = forExport;
renderer.gradients = []; // Array where gradient SvgElements are stored
renderer.setSize(width, height, false);
},
/**
* Destroys the renderer and its allocated members.
*/
destroy: function () {
var renderer = this,
i,
rendererGradients = renderer.gradients,
rendererDefs = renderer.defs;
renderer.box = null;
renderer.boxWrapper = renderer.boxWrapper.destroy();
// Call destroy on all gradient elements
if (rendererGradients) { // gradients are null in VMLRenderer
for (i = 0; i < rendererGradients.length; i++) {
renderer.gradients[i] = rendererGradients[i].destroy();
}
renderer.gradients = null;
}
// Defs are null in VMLRenderer
// Otherwise, destroy them here.
if (rendererDefs) {
renderer.defs = rendererDefs.destroy();
}
renderer.alignedObjects = null;
return null;
},
/**
* Create a wrapper for an SVG element
* @param {Object} nodeName
*/
createElement: function (nodeName) {
var wrapper = new this.Element();
wrapper.init(this, nodeName);
return wrapper;
},
/**
* Parse a simple HTML string into SVG tspans
*
* @param {Object} textNode The parent text SVG node
*/
buildText: function (wrapper) {
var textNode = wrapper.element,
lines = pick(wrapper.textStr, '').toString()
.replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
.replace(/<(i|em)>/g, '<span style="font-style:italic">')
.replace(/<a/g, '<span')
.replace(/<\/(b|strong|i|em|a)>/g, '</span>')
.split(/<br.*?>/g),
childNodes = textNode.childNodes,
styleRegex = /style="([^"]+)"/,
hrefRegex = /href="([^"]+)"/,
parentX = attr(textNode, 'x'),
textStyles = wrapper.styles,
renderAsHtml = textStyles && wrapper.useHTML && !this.forExport,
htmlNode = wrapper.htmlNode,
//arr, issue #38 workaround
width = textStyles && pInt(textStyles.width),
textLineHeight = textStyles && textStyles.lineHeight,
lastLine,
GET_COMPUTED_STYLE = 'getComputedStyle',
i = childNodes.length;
// remove old text
while (i--) {
textNode.removeChild(childNodes[i]);
}
if (width && !wrapper.added) {
this.box.appendChild(textNode); // attach it to the DOM to read offset width
}
// remove empty line at end
if (lines[lines.length - 1] === '') {
lines.pop();
}
// build the lines
each(lines, function (line, lineNo) {
var spans, spanNo = 0, lineHeight;
line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
spans = line.split('|||');
each(spans, function (span) {
if (span !== '' || spans.length === 1) {
var attributes = {},
tspan = doc.createElementNS(SVG_NS, 'tspan');
if (styleRegex.test(span)) {
attr(
tspan,
'style',
span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2')
);
}
if (hrefRegex.test(span)) {
attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
css(tspan, { cursor: 'pointer' });
}
span = (span.replace(/<(.|\n)*?>/g, '') || ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>');
// issue #38 workaround.
/*if (reverse) {
arr = [];
i = span.length;
while (i--) {
arr.push(span.charAt(i));
}
span = arr.join('');
}*/
// add the text node
tspan.appendChild(doc.createTextNode(span));
if (!spanNo) { // first span in a line, align it to the left
attributes.x = parentX;
} else {
// Firefox ignores spaces at the front or end of the tspan
attributes.dx = 3; // space
}
// first span on subsequent line, add the line height
if (!spanNo) {
if (lineNo) {
// allow getting the right offset height in exporting in IE
if (!hasSVG && wrapper.renderer.forExport) {
css(tspan, { display: 'block' });
}
// Webkit and opera sometimes return 'normal' as the line height. In that
// case, webkit uses offsetHeight, while Opera falls back to 18
lineHeight = win[GET_COMPUTED_STYLE] &&
pInt(win[GET_COMPUTED_STYLE](lastLine, null).getPropertyValue('line-height'));
if (!lineHeight || isNaN(lineHeight)) {
lineHeight = textLineHeight || lastLine.offsetHeight || 18;
}
attr(tspan, 'dy', lineHeight);
}
lastLine = tspan; // record for use in next line
}
// add attributes
attr(tspan, attributes);
// append it
textNode.appendChild(tspan);
spanNo++;
// check width and apply soft breaks
if (width) {
var words = span.replace(/-/g, '- ').split(' '),
tooLong,
actualWidth,
rest = [];
while (words.length || rest.length) {
actualWidth = wrapper.getBBox().width;
tooLong = actualWidth > width;
if (!tooLong || words.length === 1) { // new line needed
words = rest;
rest = [];
if (words.length) {
tspan = doc.createElementNS(SVG_NS, 'tspan');
attr(tspan, {
dy: textLineHeight || 16,
x: parentX
});
textNode.appendChild(tspan);
if (actualWidth > width) { // a single word is pressing it out
width = actualWidth;
}
}
} else { // append to existing line tspan
tspan.removeChild(tspan.firstChild);
rest.unshift(words.pop());
}
if (words.length) {
tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-')));
}
}
}
}
});
});
// Fix issue #38 and allow HTML in tooltips and other labels
if (renderAsHtml) {
if (!htmlNode) {
htmlNode = wrapper.htmlNode = createElement('span', null, extend(textStyles, {
position: ABSOLUTE,
top: 0,
left: 0
}), this.box.parentNode);
}
htmlNode.innerHTML = wrapper.textStr;
i = childNodes.length;
while (i--) {
childNodes[i].style.visibility = HIDDEN;
}
}
},
/**
* Create a button with preset states
* @param {String} text
* @param {Number} x
* @param {Number} y
* @param {Function} callback
* @param {Object} normalState
* @param {Object} hoverState
* @param {Object} pressedState
*/
button: function (text, x, y, callback, normalState, hoverState, pressedState) {
var label = this.label(text, x, y),
curState = 0,
stateOptions,
stateStyle,
normalStyle,
hoverStyle,
pressedStyle,
STYLE = 'style',
verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 };
// prepare the attributes
/*jslint white: true*/
normalState = merge(hash(
STROKE_WIDTH, 1,
STROKE, '#999',
FILL, hash(
LINEAR_GRADIENT, verticalGradient,
STOPS, [
[0, '#FFF'],
[1, '#DDD']
]
),
'r', 3,
'padding', 3,
STYLE, hash(
'color', 'black'
)
), normalState);
/*jslint white: false*/
normalStyle = normalState[STYLE];
delete normalState[STYLE];
/*jslint white: true*/
hoverState = merge(normalState, hash(
STROKE, '#68A',
FILL, hash(
LINEAR_GRADIENT, verticalGradient,
STOPS, [
[0, '#FFF'],
[1, '#ACF']
]
)
), hoverState);
/*jslint white: false*/
hoverStyle = hoverState[STYLE];
delete hoverState[STYLE];
/*jslint white: true*/
pressedState = merge(normalState, hash(
STROKE, '#68A',
FILL, hash(
LINEAR_GRADIENT, verticalGradient,
STOPS, [
[0, '#9BD'],
[1, '#CDF']
]
)
), pressedState);
/*jslint white: false*/
pressedStyle = pressedState[STYLE];
delete pressedState[STYLE];
// add the events
addEvent(label.element, 'mouseenter', function () {
label.attr(hoverState)
.css(hoverStyle);
});
addEvent(label.element, 'mouseleave', function () {
stateOptions = [normalState, hoverState, pressedState][curState];
stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
label.attr(stateOptions)
.css(stateStyle);
});
label.setState = function (state) {
curState = state;
if (!state) {
label.attr(normalState)
.css(normalStyle);
} else if (state === 2) {
label.attr(pressedState)
.css(pressedStyle);
}
};
return label
.on('click', function () {
callback.call(label);
})
.attr(normalState)
.css(extend({ cursor: 'default' }, normalStyle));
},
/**
* Make a straight line crisper by not spilling out to neighbour pixels
* @param {Array} points
* @param {Number} width
*/
crispLine: function (points, width) {
// points format: [M, 0, 0, L, 100, 0]
// normalize to a crisp line
if (points[1] === points[4]) {
points[1] = points[4] = mathRound(points[1]) + (width % 2 / 2);
}
if (points[2] === points[5]) {
points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2);
}
return points;
},
/**
* Draw a path
* @param {Array} path An SVG path in array form
*/
path: function (path) {
return this.createElement('path').attr({
d: path,
fill: NONE
});
},
/**
* Draw and return an SVG circle
* @param {Number} x The x position
* @param {Number} y The y position
* @param {Number} r The radius
*/
circle: function (x, y, r) {
var attr = isObject(x) ?
x :
{
x: x,
y: y,
r: r
};
return this.createElement('circle').attr(attr);
},
/**
* Draw and return an arc
* @param {Number} x X position
* @param {Number} y Y position
* @param {Number} r Radius
* @param {Number} innerR Inner radius like used in donut charts
* @param {Number} start Starting angle
* @param {Number} end Ending angle
*/
arc: function (x, y, r, innerR, start, end) {
// arcs are defined as symbols for the ability to set
// attributes in attr and animate
if (isObject(x)) {
y = x.y;
r = x.r;
innerR = x.innerR;
start = x.start;
end = x.end;
x = x.x;
}
return this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
innerR: innerR || 0,
start: start || 0,
end: end || 0
});
},
/**
* Draw and return a rectangle
* @param {Number} x Left position
* @param {Number} y Top position
* @param {Number} width
* @param {Number} height
* @param {Number} r Border corner radius
* @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing
*/
rect: function (x, y, width, height, r, strokeWidth) {
if (isObject(x)) {
y = x.y;
width = x.width;
height = x.height;
r = x.r;
strokeWidth = x.strokeWidth;
x = x.x;
}
var wrapper = this.createElement('rect').attr({
rx: r,
ry: r,
fill: NONE
});
return wrapper.attr(wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0)));
},
/**
* Resize the box and re-align all aligned elements
* @param {Object} width
* @param {Object} height
* @param {Boolean} animate
*
*/
setSize: function (width, height, animate) {
var renderer = this,
alignedObjects = renderer.alignedObjects,
i = alignedObjects.length;
renderer.width = width;
renderer.height = height;
renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({
width: width,
height: height
});
while (i--) {
alignedObjects[i].align();
}
},
/**
* Create a group
* @param {String} name The group will be given a class name of 'highcharts-{name}'.
* This can be used for styling and scripting.
*/
g: function (name) {
var elem = this.createElement('g');
return defined(name) ? elem.attr({ 'class': PREFIX + name }) : elem;
},
/**
* Display an image
* @param {String} src
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
*/
image: function (src, x, y, width, height) {
var attribs = {
preserveAspectRatio: NONE
},
elemWrapper;
// optional properties
if (arguments.length > 1) {
extend(attribs, {
x: x,
y: y,
width: width,
height: height
});
}
elemWrapper = this.createElement('image').attr(attribs);
// set the href in the xlink namespace
if (elemWrapper.element.setAttributeNS) {
elemWrapper.element.setAttributeNS('http://www.w3.org/1999/xlink',
'href', src);
} else {
// could be exporting in IE
// using href throws "not supported" in ie7 and under, requries regex shim to fix later
elemWrapper.element.setAttribute('hc-svg-href', src);
}
return elemWrapper;
},
/**
* Draw a symbol out of pre-defined shape paths from the namespace 'symbol' object.
*
* @param {Object} symbol
* @param {Object} x
* @param {Object} y
* @param {Object} radius
* @param {Object} options
*/
symbol: function (symbol, x, y, width, height, options) {
var obj,
// get the symbol definition function
symbolFn = this.symbols[symbol],
// check if there's a path defined for this symbol
path = symbolFn && symbolFn(
mathRound(x),
mathRound(y),
width,
height,
options
),
imageRegex = /^url\((.*?)\)$/,
imageSrc,
imageSize;
if (path) {
obj = this.path(path);
// expando properties for use in animate and attr
extend(obj, {
symbolName: symbol,
x: x,
y: y,
width: width,
height: height
});
if (options) {
extend(obj, options);
}
// image symbols
} else if (imageRegex.test(symbol)) {
var centerImage = function (img, size) {
img.attr({
width: size[0],
height: size[1]
}).translate(
-mathRound(size[0] / 2),
-mathRound(size[1] / 2)
);
};
imageSrc = symbol.match(imageRegex)[1];
imageSize = symbolSizes[imageSrc];
// create the image synchronously, add attribs async
obj = this.image(imageSrc)
.attr({
x: x,
y: y
});
if (imageSize) {
centerImage(obj, imageSize);
} else {
// initialize image to be 0 size so export will still function if there's no cached sizes
obj.attr({ width: 0, height: 0 });
// create a dummy JavaScript image to get the width and height
createElement('img', {
onload: function () {
var img = this;
centerImage(obj, symbolSizes[imageSrc] = [img.width, img.height]);
},
src: imageSrc
});
}
}
return obj;
},
/**
* An extendable collection of functions for defining symbol paths.
*/
symbols: {
'circle': function (x, y, w, h) {
var cpw = 0.166 * w;
return [
M, x + w / 2, y,
'C', x + w + cpw, y, x + w + cpw, y + h, x + w / 2, y + h,
'C', x - cpw, y + h, x - cpw, y, x + w / 2, y,
'Z'
];
},
'square': function (x, y, w, h) {
return [
M, x, y,
L, x + w, y,
x + w, y + h,
x, y + h,
'Z'
];
},
'triangle': function (x, y, w, h) {
return [
M, x + w / 2, y,
L, x + w, y + h,
x, y + h,
'Z'
];
},
'triangle-down': function (x, y, w, h) {
return [
M, x, y,
L, x + w, y,
x + w / 2, y + h,
'Z'
];
},
'diamond': function (x, y, w, h) {
return [
M, x + w / 2, y,
L, x + w, y + h / 2,
x + w / 2, y + h,
x, y + h / 2,
'Z'
];
},
'arc': function (x, y, w, h, options) {
var start = options.start,
radius = options.r || w || h,
end = options.end - 0.000001, // to prevent cos and sin of start and end from becoming equal on 360 arcs
innerRadius = options.innerR,
cosStart = mathCos(start),
sinStart = mathSin(start),
cosEnd = mathCos(end),
sinEnd = mathSin(end),
longArc = options.end - start < mathPI ? 0 : 1;
return [
M,
x + radius * cosStart,
y + radius * sinStart,
'A', // arcTo
radius, // x radius
radius, // y radius
0, // slanting
longArc, // long or short arc
1, // clockwise
x + radius * cosEnd,
y + radius * sinEnd,
L,
x + innerRadius * cosEnd,
y + innerRadius * sinEnd,
'A', // arcTo
innerRadius, // x radius
innerRadius, // y radius
0, // slanting
longArc, // long or short arc
0, // clockwise
x + innerRadius * cosStart,
y + innerRadius * sinStart,
'Z' // close
];
}
},
/**
* Define a clipping rectangle
* @param {String} id
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
*/
clipRect: function (x, y, width, height) {
var wrapper,
id = PREFIX + idCounter++,
clipPath = this.createElement('clipPath').attr({
id: id
}).add(this.defs);
wrapper = this.rect(x, y, width, height, 0).add(clipPath);
wrapper.id = id;
wrapper.clipPath = clipPath;
return wrapper;
},
/**
* Take a color and return it if it's a string, make it a gradient if it's a
* gradient configuration object. Prior to Highstock, an array was used to define
* a linear gradient with pixel positions relative to the SVG. In newer versions
* we change the coordinates to apply relative to the shape, using coordinates
* 0-1 within the shape. To preserve backwards compatibility, linearGradient
* in this definition is an object of x1, y1, x2 and y2.
*
* @param {Object} color The color or config object
*/
color: function (color, elem, prop) {
var colorObject,
regexRgba = /^rgba/;
if (color && color.linearGradient) {
var renderer = this,
linearGradient = color[LINEAR_GRADIENT],
relativeToShape = !linearGradient.length, // keep backwards compatibility
id = PREFIX + idCounter++,
gradientObject,
stopColor,
stopOpacity;
gradientObject = renderer.createElement(LINEAR_GRADIENT)
.attr(extend({
id: id,
x1: linearGradient.x1 || linearGradient[0] || 0,
y1: linearGradient.y1 || linearGradient[1] || 0,
x2: linearGradient.x2 || linearGradient[2] || 0,
y2: linearGradient.y2 || linearGradient[3] || 0
}, relativeToShape ? null : { gradientUnits: 'userSpaceOnUse' }))
.add(renderer.defs);
// Keep a reference to the gradient object so it is possible to destroy it later
renderer.gradients.push(gradientObject);
// The gradient needs to keep a list of stops to be able to destroy them
gradientObject.stops = [];
each(color.stops, function (stop) {
var stopObject;
if (regexRgba.test(stop[1])) {
colorObject = Color(stop[1]);
stopColor = colorObject.get('rgb');
stopOpacity = colorObject.get('a');
} else {
stopColor = stop[1];
stopOpacity = 1;
}
stopObject = renderer.createElement('stop').attr({
offset: stop[0],
'stop-color': stopColor,
'stop-opacity': stopOpacity
}).add(gradientObject);
// Add the stop element to the gradient
gradientObject.stops.push(stopObject);
});
return 'url(' + this.url + '#' + id + ')';
// Webkit and Batik can't show rgba.
} else if (regexRgba.test(color)) {
colorObject = Color(color);
attr(elem, prop + '-opacity', colorObject.get('a'));
return colorObject.get('rgb');
} else {
// Remove the opacity attribute added above. Does not throw if the attribute is not there.
elem.removeAttribute(prop + '-opacity');
return color;
}
},
/**
* Add text to the SVG object
* @param {String} str
* @param {Number} x Left position
* @param {Number} y Top position
* @param {Boolean} useHTML Use HTML to render the text
*/
text: function (str, x, y, useHTML) {
// declare variables
var renderer = this,
defaultChartStyle = defaultOptions.chart.style,
wrapper;
x = mathRound(pick(x, 0));
y = mathRound(pick(y, 0));
wrapper = renderer.createElement('text')
.attr({
x: x,
y: y,
text: str
})
.css({
fontFamily: defaultChartStyle.fontFamily,
fontSize: defaultChartStyle.fontSize
});
wrapper.x = x;
wrapper.y = y;
wrapper.useHTML = useHTML;
return wrapper;
},
/**
* Add a label, a text item that can hold a colored or gradient background
* as well as a border and shadow.
* @param {string} str
* @param {Number} x
* @param {Number} y
* @param {String} shape
* @param {Number} anchorX In case the shape has a pointer, like a flag, this is the
* coordinates it should be pinned to
* @param {Number} anchorY
*/
label: function (str, x, y, shape, anchorX, anchorY) {
var renderer = this,
wrapper = renderer.g(),
text = renderer.text()
.attr({
zIndex: 1
})
.add(wrapper),
box,
bBox,
align = 'left',
padding = 3,
width,
height,
wrapperX,
wrapperY,
crispAdjust = 0,
deferredAttr = {},
attrSetters = wrapper.attrSetters;
/**
* This function runs after the label is added to the DOM (when the bounding box is
* available), and after the text of the label is updated to detect the new bounding
* box and reflect it in the border box.
*/
function updateBoxSize() {
bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) &&
text.getBBox(true);
wrapper.width = (width || bBox.width) + 2 * padding;
wrapper.height = (height || bBox.height) + 2 * padding;
// create the border box if it is not already present
if (!box) {
wrapper.box = box = shape ?
renderer.symbol(shape, 0, 0, wrapper.width, wrapper.height) :
renderer.rect(0, 0, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
box.add(wrapper);
}
// apply the box attributes
box.attr(merge({
width: wrapper.width,
height: wrapper.height
}, deferredAttr));
deferredAttr = null;
}
/**
* This function runs after setting text or padding, but only if padding is changed
*/
function updateTextPadding() {
var styles = wrapper.styles,
textAlign = styles && styles.textAlign,
x = padding,
y = padding + mathRound(pInt(wrapper.element.style.fontSize || 11) * 1.2);
// compensate for alignment
if (defined(width) && (textAlign === 'center' || textAlign === 'right')) {
x += { center: 0.5, right: 1 }[textAlign] * (width - bBox.width);
}
// update if anything changed
if (x !== text.x || y !== text.y) {
text.attr({
x: x,
y: y
});
}
// record current values
text.x = x;
text.y = y;
}
/**
* Set a box attribute, or defer it if the box is not yet created
* @param {Object} key
* @param {Object} value
*/
function boxAttr(key, value) {
if (box) {
box.attr(key, value);
} else {
deferredAttr[key] = value;
}
}
function getSizeAfterAdd() {
wrapper.attr({
text: str, // alignment is available now
x: x,
y: y,
anchorX: anchorX,
anchorY: anchorY
});
}
/**
* After the text element is added, get the desired size of the border box
* and add it before the text in the DOM.
*/
addEvent(wrapper, 'add', getSizeAfterAdd);
/*
* Add specific attribute setters.
*/
// only change local variables
attrSetters.width = function (value) {
width = value;
return false;
};
attrSetters.height = function (value) {
height = value;
return false;
};
attrSetters.padding = function (value) {
padding = value;
updateTextPadding();
return false;
};
// change local variable and set attribue as well
attrSetters.align = function (value) {
align = value;
return false; // prevent setting text-anchor on the group
};
// apply these to the box and the text alike
attrSetters.text = function (value, key) {
text.attr(key, value);
updateBoxSize();
updateTextPadding();
return false;
};
// apply these to the box but not to the text
attrSetters[STROKE_WIDTH] = function (value, key) {
crispAdjust = value % 2 / 2;
boxAttr(key, value);
return false;
};
attrSetters.stroke = attrSetters.fill = attrSetters.r = function (value, key) {
boxAttr(key, value);
return false;
};
attrSetters.anchorX = function (value, key) {
anchorX = value;
boxAttr(key, value + crispAdjust - wrapperX);
return false;
};
attrSetters.anchorY = function (value, key) {
anchorY = value;
boxAttr(key, value - wrapperY);
return false;
};
// rename attributes
attrSetters.x = function (value) {
wrapperX = value;
wrapperX -= { left: 0, center: 0.5, right: 1 }[align] * ((width || bBox.width) + padding);
wrapper.attr('translateX', mathRound(wrapperX));
return false;
};
attrSetters.y = function (value) {
wrapperY = value;
wrapper.attr('translateY', mathRound(value));
return false;
};
// Redirect certain methods to either the box or the text
var baseCss = wrapper.css;
return extend(wrapper, {
/**
* Pick up some properties and apply them to the text instead of the wrapper
*/
css: function (styles) {
if (styles) {
var textStyles = {};
styles = merge({}, styles); // create a copy to avoid altering the original object (#537)
each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight'], function (prop) {
if (styles[prop] !== UNDEFINED) {
textStyles[prop] = styles[prop];
delete styles[prop];
}
});
text.css(textStyles);
}
return baseCss.call(wrapper, styles);
},
/**
* Return the bounding box of the box, not the group
*/
getBBox: function () {
return box.getBBox();
},
/**
* Apply the shadow to the box
*/
shadow: function (b) {
box.shadow(b);
return wrapper;
},
/**
* Destroy and release memory.
*/
destroy: function () {
removeEvent(wrapper, 'add', getSizeAfterAdd);
// Added by button implementation
removeEvent(wrapper.element, 'mouseenter');
removeEvent(wrapper.element, 'mouseleave');
if (text) {
// Destroy the text element
text = text.destroy();
}
// Call base implementation to destroy the rest
SVGElement.prototype.destroy.call(wrapper);
}
});
}
}; // end SVGRenderer
// general renderer
Renderer = SVGRenderer;
/* ****************************************************************************
* *
* START OF INTERNET EXPLORER <= 8 SPECIFIC CODE *
* *
* For applications and websites that don't need IE support, like platform *
* targeted mobile apps and web apps, this code can be removed. *
* *
*****************************************************************************/
/**
* @constructor
*/
var VMLRenderer;
if (!hasSVG) {
/**
* The VML element wrapper.
*/
var VMLElement = extendClass(SVGElement, {
/**
* Initialize a new VML element wrapper. It builds the markup as a string
* to minimize DOM traffic.
* @param {Object} renderer
* @param {Object} nodeName
*/
init: function (renderer, nodeName) {
var wrapper = this,
markup = ['<', nodeName, ' filled="f" stroked="f"'],
style = ['position: ', ABSOLUTE, ';'];
// divs and shapes need size
if (nodeName === 'shape' || nodeName === DIV) {
style.push('left:0;top:0;width:10px;height:10px;');
}
if (docMode8) {
style.push('visibility: ', nodeName === DIV ? HIDDEN : VISIBLE);
}
markup.push(' style="', style.join(''), '"/>');
// create element with default attributes and style
if (nodeName) {
markup = nodeName === DIV || nodeName === 'span' || nodeName === 'img' ?
markup.join('')
: renderer.prepVML(markup);
wrapper.element = createElement(markup);
}
wrapper.renderer = renderer;
wrapper.attrSetters = {};
},
/**
* Add the node to the given parent
* @param {Object} parent
*/
add: function (parent) {
var wrapper = this,
renderer = wrapper.renderer,
element = wrapper.element,
box = renderer.box,
inverted = parent && parent.inverted,
// get the parent node
parentNode = parent ?
parent.element || parent :
box;
// if the parent group is inverted, apply inversion on all children
if (inverted) { // only on groups
renderer.invertChild(element, parentNode);
}
// issue #140 workaround - related to #61 and #74
if (docMode8 && parentNode.gVis === HIDDEN) {
css(element, { visibility: HIDDEN });
}
// append it
parentNode.appendChild(element);
// align text after adding to be able to read offset
wrapper.added = true;
if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) {
wrapper.updateTransform();
}
// fire an event for internal hooks
fireEvent(wrapper, 'add');
return wrapper;
},
/**
* In IE8 documentMode 8, we need to recursively set the visibility down in the DOM
* tree for nested groups. Related to #61, #586.
*/
toggleChildren: function (element, visibility) {
var childNodes = element.childNodes,
i = childNodes.length;
while (i--) {
// apply the visibility
css(childNodes[i], { visibility: visibility });
// we have a nested group, apply it to its children again
if (childNodes[i].nodeName === 'DIV') {
this.toggleChildren(childNodes[i], visibility);
}
}
},
/**
* Get or set attributes
*/
attr: function (hash, val) {
var wrapper = this,
key,
value,
i,
result,
element = wrapper.element || {},
elemStyle = element.style,
nodeName = element.nodeName,
renderer = wrapper.renderer,
symbolName = wrapper.symbolName,
hasSetSymbolSize,
shadows = wrapper.shadows,
skipAttr,
attrSetters = wrapper.attrSetters,
ret = wrapper;
// single key-value pair
if (isString(hash) && defined(val)) {
key = hash;
hash = {};
hash[key] = val;
}
// used as a getter, val is undefined
if (isString(hash)) {
key = hash;
if (key === 'strokeWidth' || key === 'stroke-width') {
ret = wrapper.strokeweight;
} else {
ret = wrapper[key];
}
// setter
} else {
for (key in hash) {
value = hash[key];
skipAttr = false;
// check for a specific attribute setter
result = attrSetters[key] && attrSetters[key](value, key);
if (result !== false) {
if (result !== UNDEFINED) {
value = result; // the attribute setter has returned a new value to set
}
// prepare paths
// symbols
if (symbolName && /^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(key)) {
// if one of the symbol size affecting parameters are changed,
// check all the others only once for each call to an element's
// .attr() method
if (!hasSetSymbolSize) {
wrapper.symbolAttr(hash);
hasSetSymbolSize = true;
}
skipAttr = true;
} else if (key === 'd') {
value = value || [];
wrapper.d = value.join(' '); // used in getter for animation
// convert paths
i = value.length;
var convertedPath = [];
while (i--) {
// Multiply by 10 to allow subpixel precision.
// Substracting half a pixel seems to make the coordinates
// align with SVG, but this hasn't been tested thoroughly
if (isNumber(value[i])) {
convertedPath[i] = mathRound(value[i] * 10) - 5;
} else if (value[i] === 'Z') { // close the path
convertedPath[i] = 'x';
} else {
convertedPath[i] = value[i];
}
}
value = convertedPath.join(' ') || 'x';
element.path = value;
// update shadows
if (shadows) {
i = shadows.length;
while (i--) {
shadows[i].path = value;
}
}
skipAttr = true;
// directly mapped to css
} else if (key === 'zIndex' || key === 'visibility') {
// workaround for #61 and #586
if (docMode8 && key === 'visibility' && nodeName === 'DIV') {
element.gVis = value;
wrapper.toggleChildren(element, value);
if (value === VISIBLE) { // #74
value = null;
}
}
if (value) {
elemStyle[key] = value;
}
skipAttr = true;
// width and height
} else if (key === 'width' || key === 'height') {
value = mathMax(0, value); // don't set width or height below zero (#311)
this[key] = value; // used in getter
// clipping rectangle special
if (wrapper.updateClipping) {
wrapper[key] = value;
wrapper.updateClipping();
} else {
// normal
elemStyle[key] = value;
}
skipAttr = true;
// x and y
} else if (/^(x|y)$/.test(key)) {
wrapper[key] = value; // used in getter
if (element.tagName === 'SPAN') {
wrapper.updateTransform();
} else {
elemStyle[{ x: 'left', y: 'top' }[key]] = value;
}
// class name
} else if (key === 'class') {
// IE8 Standards mode has problems retrieving the className
element.className = value;
// stroke
} else if (key === 'stroke') {
value = renderer.color(value, element, key);
key = 'strokecolor';
// stroke width
} else if (key === 'stroke-width' || key === 'strokeWidth') {
element.stroked = value ? true : false;
key = 'strokeweight';
wrapper[key] = value; // used in getter, issue #113
if (isNumber(value)) {
value += PX;
}
// dashStyle
} else if (key === 'dashstyle') {
var strokeElem = element.getElementsByTagName('stroke')[0] ||
createElement(renderer.prepVML(['<stroke/>']), null, null, element);
strokeElem[key] = value || 'solid';
wrapper.dashstyle = value; /* because changing stroke-width will change the dash length
and cause an epileptic effect */
skipAttr = true;
// fill
} else if (key === 'fill') {
if (nodeName === 'SPAN') { // text color
elemStyle.color = value;
} else {
element.filled = value !== NONE ? true : false;
value = renderer.color(value, element, key);
key = 'fillcolor';
}
// translation for animation
} else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || key === 'align') {
if (key === 'align') {
key = 'textAlign';
}
wrapper[key] = value;
wrapper.updateTransform();
skipAttr = true;
// text for rotated and non-rotated elements
} else if (key === 'text') {
this.bBox = null;
element.innerHTML = value;
skipAttr = true;
}
// let the shadow follow the main element
if (shadows && key === 'visibility') {
i = shadows.length;
while (i--) {
shadows[i].style[key] = value;
}
}
if (!skipAttr) {
if (docMode8) { // IE8 setAttribute bug
element[key] = value;
} else {
attr(element, key, value);
}
}
}
}
}
return ret;
},
/**
* Set the element's clipping to a predefined rectangle
*
* @param {String} id The id of the clip rectangle
*/
clip: function (clipRect) {
var wrapper = this,
clipMembers = clipRect.members;
clipMembers.push(wrapper);
wrapper.destroyClip = function () {
erase(clipMembers, wrapper);
};
return wrapper.css(clipRect.getCSS(wrapper.inverted));
},
/**
* Set styles for the element
* @param {Object} styles
*/
css: function (styles) {
var wrapper = this,
element = wrapper.element,
textWidth = styles && element.tagName === 'SPAN' && styles.width;
if (textWidth) {
delete styles.width;
wrapper.textWidth = textWidth;
wrapper.updateTransform();
}
wrapper.styles = extend(wrapper.styles, styles);
css(wrapper.element, styles);
return wrapper;
},
/**
* Removes a child either by removeChild or move to garbageBin.
* Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
*/
safeRemoveChild: function (element) {
// discardElement will detach the node from its parent before attaching it
// to the garbage bin. Therefore it is important that the node is attached and have parent.
var parentNode = element.parentNode;
if (parentNode) {
discardElement(element);
}
},
/**
* Extend element.destroy by removing it from the clip members array
*/
destroy: function () {
var wrapper = this;
if (wrapper.destroyClip) {
wrapper.destroyClip();
}
return SVGElement.prototype.destroy.apply(wrapper);
},
/**
* Remove all child nodes of a group, except the v:group element
*/
empty: function () {
var element = this.element,
childNodes = element.childNodes,
i = childNodes.length,
node;
while (i--) {
node = childNodes[i];
node.parentNode.removeChild(node);
}
},
/**
* VML override for calculating the bounding box based on offsets
* @param {Boolean} refresh Whether to force a fresh value from the DOM or to
* use the cached value
*
* @return {Object} A hash containing values for x, y, width and height
*/
getBBox: function (refresh) {
var wrapper = this,
element = wrapper.element,
bBox = wrapper.bBox;
// faking getBBox in exported SVG in legacy IE
if (!bBox || refresh) {
// faking getBBox in exported SVG in legacy IE
if (element.nodeName === 'text') {
element.style.position = ABSOLUTE;
}
bBox = wrapper.bBox = {
x: element.offsetLeft,
y: element.offsetTop,
width: element.offsetWidth,
height: element.offsetHeight
};
}
return bBox;
},
/**
* Add an event listener. VML override for normalizing event parameters.
* @param {String} eventType
* @param {Function} handler
*/
on: function (eventType, handler) {
// simplest possible event model for internal use
this.element['on' + eventType] = function () {
var evt = win.event;
evt.target = evt.srcElement;
handler(evt);
};
return this;
},
/**
* VML override private method to update elements based on internal
* properties based on SVG transform
*/
updateTransform: function () {
// aligning non added elements is expensive
if (!this.added) {
this.alignOnAdd = true;
return;
}
var wrapper = this,
elem = wrapper.element,
translateX = wrapper.translateX || 0,
translateY = wrapper.translateY || 0,
x = wrapper.x || 0,
y = wrapper.y || 0,
align = wrapper.textAlign || 'left',
alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
nonLeft = align && align !== 'left',
shadows = wrapper.shadows;
// apply translate
if (translateX || translateY) {
css(elem, {
marginLeft: translateX,
marginTop: translateY
});
if (shadows) { // used in labels/tooltip
each(shadows, function (shadow) {
css(shadow, {
marginLeft: translateX + 1,
marginTop: translateY + 1
});
});
}
}
// apply inversion
if (wrapper.inverted) { // wrapper is a group
each(elem.childNodes, function (child) {
wrapper.renderer.invertChild(child, elem);
});
}
if (elem.tagName === 'SPAN') {
var width, height,
rotation = wrapper.rotation,
lineHeight,
radians = 0,
costheta = 1,
sintheta = 0,
quad,
textWidth = pInt(wrapper.textWidth),
xCorr = wrapper.xCorr || 0,
yCorr = wrapper.yCorr || 0,
currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(',');
if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
if (defined(rotation)) {
radians = rotation * deg2rad; // deg to rad
costheta = mathCos(radians);
sintheta = mathSin(radians);
// Adjust for alignment and rotation.
// Test case: http://highcharts.com/tests/?file=text-rotation
css(elem, {
filter: rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
', sizingMethod=\'auto expand\')'].join('') : NONE
});
}
width = pick(wrapper.elemWidth, elem.offsetWidth);
height = pick(wrapper.elemHeight, elem.offsetHeight);
// update textWidth
if (width > textWidth) {
css(elem, {
width: textWidth + PX,
display: 'block',
whiteSpace: 'normal'
});
width = textWidth;
}
// correct x and y
lineHeight = mathRound((pInt(elem.style.fontSize) || 12) * 1.2);
xCorr = costheta < 0 && -width;
yCorr = sintheta < 0 && -height;
// correct for lineHeight and corners spilling out after rotation
quad = costheta * sintheta < 0;
xCorr += sintheta * lineHeight * (quad ? 1 - alignCorrection : alignCorrection);
yCorr -= costheta * lineHeight * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
// correct for the length/height of the text
if (nonLeft) {
xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
if (rotation) {
yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
}
css(elem, {
textAlign: align
});
}
// record correction
wrapper.xCorr = xCorr;
wrapper.yCorr = yCorr;
}
// apply position with correction
css(elem, {
left: x + xCorr,
top: y + yCorr
});
// record current text transform
wrapper.cTT = currentTextTransform;
}
},
/**
* Apply a drop shadow by copying elements and giving them different strokes
* @param {Boolean} apply
*/
shadow: function (apply, group) {
var shadows = [],
i,
element = this.element,
renderer = this.renderer,
shadow,
elemStyle = element.style,
markup,
path = element.path;
// some times empty paths are not strings
if (path && typeof path.value !== 'string') {
path = 'x';
}
if (apply) {
for (i = 1; i <= 3; i++) {
markup = ['<shape isShadow="true" strokeweight="', (7 - 2 * i),
'" filled="false" path="', path,
'" coordsize="100,100" style="', element.style.cssText, '" />'];
shadow = createElement(renderer.prepVML(markup),
null, {
left: pInt(elemStyle.left) + 1,
top: pInt(elemStyle.top) + 1
}
);
// apply the opacity
markup = ['<stroke color="black" opacity="', (0.05 * i), '"/>'];
createElement(renderer.prepVML(markup), null, null, shadow);
// insert it
if (group) {
group.element.appendChild(shadow);
} else {
element.parentNode.insertBefore(shadow, element);
}
// record it
shadows.push(shadow);
}
this.shadows = shadows;
}
return this;
}
});
/**
* The VML renderer
*/
VMLRenderer = function () {
this.init.apply(this, arguments);
};
VMLRenderer.prototype = merge(SVGRenderer.prototype, { // inherit SVGRenderer
Element: VMLElement,
isIE8: userAgent.indexOf('MSIE 8.0') > -1,
/**
* Initialize the VMLRenderer
* @param {Object} container
* @param {Number} width
* @param {Number} height
*/
init: function (container, width, height) {
var renderer = this,
boxWrapper;
renderer.alignedObjects = [];
boxWrapper = renderer.createElement(DIV);
container.appendChild(boxWrapper.element);
// generate the containing box
renderer.box = boxWrapper.element;
renderer.boxWrapper = boxWrapper;
renderer.setSize(width, height, false);
// The only way to make IE6 and IE7 print is to use a global namespace. However,
// with IE8 the only way to make the dynamic shapes visible in screen and print mode
// seems to be to add the xmlns attribute and the behaviour style inline.
if (!doc.namespaces.hcv) {
doc.namespaces.add('hcv', 'urn:schemas-microsoft-com:vml');
// setup default css
doc.createStyleSheet().cssText =
'hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke' +
'{ behavior:url(#default#VML); display: inline-block; } ';
}
},
/**
* Define a clipping rectangle. In VML it is accomplished by storing the values
* for setting the CSS style to all associated members.
*
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
*/
clipRect: function (x, y, width, height) {
// create a dummy element
var clipRect = this.createElement();
// mimic a rectangle with its style object for automatic updating in attr
return extend(clipRect, {
members: [],
left: x,
top: y,
width: width,
height: height,
getCSS: function (inverted) {
var rect = this,//clipRect.element.style,
top = rect.top,
left = rect.left,
right = left + rect.width,
bottom = top + rect.height,
ret = {
clip: 'rect(' +
mathRound(inverted ? left : top) + 'px,' +
mathRound(inverted ? bottom : right) + 'px,' +
mathRound(inverted ? right : bottom) + 'px,' +
mathRound(inverted ? top : left) + 'px)'
};
// issue 74 workaround
if (!inverted && docMode8) {
extend(ret, {
width: right + PX,
height: bottom + PX
});
}
return ret;
},
// used in attr and animation to update the clipping of all members
updateClipping: function () {
each(clipRect.members, function (member) {
member.css(clipRect.getCSS(member.inverted));
});
}
});
},
/**
* Take a color and return it if it's a string, make it a gradient if it's a
* gradient configuration object, and apply opacity.
*
* @param {Object} color The color or config object
*/
color: function (color, elem, prop) {
var colorObject,
regexRgba = /^rgba/,
markup;
if (color && color[LINEAR_GRADIENT]) {
var stopColor,
stopOpacity,
linearGradient = color[LINEAR_GRADIENT],
x1 = linearGradient.x1 || linearGradient[0] || 0,
y1 = linearGradient.y1 || linearGradient[1] || 0,
x2 = linearGradient.x2 || linearGradient[2] || 0,
y2 = linearGradient.y2 || linearGradient[3] || 0,
angle,
color1,
opacity1,
color2,
opacity2;
each(color.stops, function (stop, i) {
if (regexRgba.test(stop[1])) {
colorObject = Color(stop[1]);
stopColor = colorObject.get('rgb');
stopOpacity = colorObject.get('a');
} else {
stopColor = stop[1];
stopOpacity = 1;
}
if (!i) { // first
color1 = stopColor;
opacity1 = stopOpacity;
} else {
color2 = stopColor;
opacity2 = stopOpacity;
}
});
// calculate the angle based on the linear vector
angle = 90 - math.atan(
(y2 - y1) / // y vector
(x2 - x1) // x vector
) * 180 / mathPI;
// when colors attribute is used, the meanings of opacity and o:opacity2
// are reversed.
markup = ['<', prop, ' colors="0% ', color1, ',100% ', color2, '" angle="', angle,
'" opacity="', opacity2, '" o:opacity2="', opacity1,
'" type="gradient" focus="100%" method="any" />'];
createElement(this.prepVML(markup), null, null, elem);
// if the color is an rgba color, split it and add a fill node
// to hold the opacity component
} else if (regexRgba.test(color) && elem.tagName !== 'IMG') {
colorObject = Color(color);
markup = ['<', prop, ' opacity="', colorObject.get('a'), '"/>'];
createElement(this.prepVML(markup), null, null, elem);
return colorObject.get('rgb');
} else {
var strokeNodes = elem.getElementsByTagName(prop);
if (strokeNodes.length) {
strokeNodes[0].opacity = 1;
}
return color;
}
},
/**
* Take a VML string and prepare it for either IE8 or IE6/IE7.
* @param {Array} markup A string array of the VML markup to prepare
*/
prepVML: function (markup) {
var vmlStyle = 'display:inline-block;behavior:url(#default#VML);',
isIE8 = this.isIE8;
markup = markup.join('');
if (isIE8) { // add xmlns and style inline
markup = markup.replace('/>', ' xmlns="urn:schemas-microsoft-com:vml" />');
if (markup.indexOf('style="') === -1) {
markup = markup.replace('/>', ' style="' + vmlStyle + '" />');
} else {
markup = markup.replace('style="', 'style="' + vmlStyle);
}
} else { // add namespace
markup = markup.replace('<', '<hcv:');
}
return markup;
},
/**
* Create rotated and aligned text
* @param {String} str
* @param {Number} x
* @param {Number} y
*/
text: function (str, x, y) {
var defaultChartStyle = defaultOptions.chart.style;
return this.createElement('span')
.attr({
text: str,
x: mathRound(x),
y: mathRound(y)
})
.css({
whiteSpace: 'nowrap',
fontFamily: defaultChartStyle.fontFamily,
fontSize: defaultChartStyle.fontSize
});
},
/**
* Create and return a path element
* @param {Array} path
*/
path: function (path) {
// create the shape
return this.createElement('shape').attr({
// subpixel precision down to 0.1 (width and height = 10px)
coordsize: '100 100',
d: path
});
},
/**
* Create and return a circle element. In VML circles are implemented as
* shapes, which is faster than v:oval
* @param {Number} x
* @param {Number} y
* @param {Number} r
*/
circle: function (x, y, r) {
return this.symbol('circle').attr({ x: x, y: y, r: r});
},
/**
* Create a group using an outer div and an inner v:group to allow rotating
* and flipping. A simple v:group would have problems with positioning
* child HTML elements and CSS clip.
*
* @param {String} name The name of the group
*/
g: function (name) {
var wrapper,
attribs;
// set the class name
if (name) {
attribs = { 'className': PREFIX + name, 'class': PREFIX + name };
}
// the div to hold HTML and clipping
wrapper = this.createElement(DIV).attr(attribs);
return wrapper;
},
/**
* VML override to create a regular HTML image
* @param {String} src
* @param {Number} x
* @param {Number} y
* @param {Number} width
* @param {Number} height
*/
image: function (src, x, y, width, height) {
var obj = this.createElement('img')
.attr({ src: src });
if (arguments.length > 1) {
obj.css({
left: x,
top: y,
width: width,
height: height
});
}
return obj;
},
/**
* VML uses a shape for rect to overcome bugs and rotation problems
*/
rect: function (x, y, width, height, r, strokeWidth) {
if (isObject(x)) {
y = x.y;
width = x.width;
height = x.height;
strokeWidth = x.strokeWidth;
x = x.x;
}
var wrapper = this.symbol('rect');
wrapper.r = r;
return wrapper.attr(wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0)));
},
/**
* In the VML renderer, each child of an inverted div (group) is inverted
* @param {Object} element
* @param {Object} parentNode
*/
invertChild: function (element, parentNode) {
var parentStyle = parentNode.style;
css(element, {
flip: 'x',
left: pInt(parentStyle.width) - 10,
top: pInt(parentStyle.height) - 10,
rotation: -90
});
},
/**
* Symbol definitions that override the parent SVG renderer's symbols
*
*/
symbols: {
// VML specific arc function
arc: function (x, y, w, h, options) {
var start = options.start,
end = options.end,
radius = options.r || w || h,
cosStart = mathCos(start),
sinStart = mathSin(start),
cosEnd = mathCos(end),
sinEnd = mathSin(end),
innerRadius = options.innerR,
circleCorrection = 0.07 / radius,
innerCorrection = (innerRadius && 0.1 / innerRadius) || 0;
if (end - start === 0) { // no angle, don't show it.
return ['x'];
//} else if (end - start == 2 * mathPI) { // full circle
} else if (2 * mathPI - end + start < circleCorrection) { // full circle
// empirical correction found by trying out the limits for different radii
cosEnd = -circleCorrection;
} else if (end - start < innerCorrection) { // issue #186, another mysterious VML arc problem
cosEnd = mathCos(start + innerCorrection);
}
return [
'wa', // clockwise arc to
x - radius, // left
y - radius, // top
x + radius, // right
y + radius, // bottom
x + radius * cosStart, // start x
y + radius * sinStart, // start y
x + radius * cosEnd, // end x
y + radius * sinEnd, // end y
'at', // anti clockwise arc to
x - innerRadius, // left
y - innerRadius, // top
x + innerRadius, // right
y + innerRadius, // bottom
x + innerRadius * cosEnd, // start x
y + innerRadius * sinEnd, // start y
x + innerRadius * cosStart, // end x
y + innerRadius * sinStart, // end y
'x', // finish path
'e' // close
];
},
// Add circle symbol path. This performs significantly faster than v:oval.
circle: function (x, y, w, h) {
return [
'wa', // clockwisearcto
x, // left
y, // top
x + w, // right
y + h, // bottom
x + w, // start x
y + h / 2, // start y
x + w, // end x
y + h / 2, // end y
//'x', // finish path
'e' // close
];
},
/**
* Add rectangle symbol path which eases rotation and omits arcsize problems
* compared to the built-in VML roundrect shape
*
* @param {Number} left Left position
* @param {Number} top Top position
* @param {Number} r Border radius
* @param {Object} options Width and height
*/
rect: function (left, top, width, height, options) {
/*for (var n in r) {
logTime && console .log(n)
}*/
if (!defined(options)) {
return [];
}
var right = left + width,
bottom = top + height,
r = mathMin(options.r || 0, width, height);
return [
M,
left + r, top,
L,
right - r, top,
'wa',
right - 2 * r, top,
right, top + 2 * r,
right - r, top,
right, top + r,
L,
right, bottom - r,
'wa',
right - 2 * r, bottom - 2 * r,
right, bottom,
right, bottom - r,
right - r, bottom,
L,
left + r, bottom,
'wa',
left, bottom - 2 * r,
left + 2 * r, bottom,
left + r, bottom,
left, bottom - r,
L,
left, top + r,
'wa',
left, top,
left + 2 * r, top + 2 * r,
left, top + r,
left + r, top,
'x',
'e'
];
}
}
});
// general renderer
Renderer = VMLRenderer;
}
/* ****************************************************************************
* *
* END OF INTERNET EXPLORER <= 8 SPECIFIC CODE *
* *
*****************************************************************************/
/**
* The chart class
* @param {Object} options
* @param {Function} callback Function to run when the chart has loaded
*/
function Chart(options, callback) {
defaultXAxisOptions = merge(defaultXAxisOptions, defaultOptions.xAxis);
defaultYAxisOptions = merge(defaultYAxisOptions, defaultOptions.yAxis);
defaultOptions.xAxis = defaultOptions.yAxis = null;
// Handle regular options
var seriesOptions = options.series; // skip merging data points to increase performance
options.series = null;
options = merge(defaultOptions, options); // do the merge
options.series = seriesOptions; // set back the series data
// Define chart variables
var optionsChart = options.chart,
optionsMargin = optionsChart.margin,
margin = isObject(optionsMargin) ?
optionsMargin :
[optionsMargin, optionsMargin, optionsMargin, optionsMargin],
optionsMarginTop = pick(optionsChart.marginTop, margin[0]),
optionsMarginRight = pick(optionsChart.marginRight, margin[1]),
optionsMarginBottom = pick(optionsChart.marginBottom, margin[2]),
optionsMarginLeft = pick(optionsChart.marginLeft, margin[3]),
spacingTop = optionsChart.spacingTop,
spacingRight = optionsChart.spacingRight,
spacingBottom = optionsChart.spacingBottom,
spacingLeft = optionsChart.spacingLeft,
spacingBox,
chartTitleOptions,
chartSubtitleOptions,
plotTop,
marginRight,
marginBottom,
plotLeft,
axisOffset,
renderTo,
renderToClone,
container,
containerId,
containerWidth,
containerHeight,
chartWidth,
chartHeight,
oldChartWidth,
oldChartHeight,
chartBackground,
plotBackground,
plotBGImage,
plotBorder,
chart = this,
chartEvents = optionsChart.events,
runChartClick = chartEvents && !!chartEvents.click,
eventType,
isInsidePlot, // function
tooltip,
mouseIsDown,
loadingDiv,
loadingSpan,
loadingShown,
plotHeight,
plotWidth,
tracker,
trackerGroup,
placeTrackerGroup,
legend,
legendWidth,
legendHeight,
chartPosition,
hasCartesianSeries = optionsChart.showAxes,
isResizing = 0,
axes = [],
maxTicks, // handle the greatest amount of ticks on grouped axes
series = [],
inverted,
renderer,
tooltipTick,
tooltipInterval,
hoverX,
drawChartBox, // function
getMargins, // function
resetMargins, // function
setChartSize, // function
resize,
zoom, // function
zoomOut; // function
/**
* Create a new axis object
* @param {Object} options
*/
function Axis(userOptions) {
// Define variables
var isXAxis = userOptions.isX,
opposite = userOptions.opposite, // needed in setOptions
horiz = inverted ? !isXAxis : isXAxis,
side = horiz ?
(opposite ? 0 : 2) : // top : bottom
(opposite ? 1 : 3), // right : left
stacks = {},
options = merge(
isXAxis ? defaultXAxisOptions : defaultYAxisOptions,
[defaultTopAxisOptions, defaultRightAxisOptions,
defaultBottomAxisOptions, defaultLeftAxisOptions][side],
userOptions
),
axis = this,
axisTitle,
type = options.type,
isDatetimeAxis = type === 'datetime',
isLog = type === 'logarithmic',
offset = options.offset || 0,
xOrY = isXAxis ? 'x' : 'y',
axisLength = 0,
oldAxisLength,
transA, // translation factor
transB, // translation addend
oldTransA, // used for prerendering
axisLeft,
axisTop,
axisWidth,
axisHeight,
axisBottom,
axisRight,
translate, // fn
getPlotLinePath, // fn
axisGroup,
gridGroup,
axisLine,
dataMin,
dataMax,
minRange = options.minRange || options.maxZoom,
range = options.range,
userMin,
userMax,
oldUserMin,
oldUserMax,
max = null,
min = null,
oldMin,
oldMax,
minPadding = options.minPadding,
maxPadding = options.maxPadding,
minPixelPadding = 0,
isLinked = defined(options.linkedTo),
ignoreMinPadding, // can be set to true by a column or bar series
ignoreMaxPadding,
usePercentage,
events = options.events,
eventType,
plotLinesAndBands = [],
tickInterval,
minorTickInterval,
magnitude,
tickPositions, // array containing predefined positions
tickPositioner = options.tickPositioner,
ticks = {},
minorTicks = {},
alternateBands = {},
tickAmount,
labelOffset,
axisTitleMargin,// = options.title.margin,
dateTimeLabelFormat,
categories = options.categories,
labelFormatter = options.labels.formatter || // can be overwritten by dynamic format
function () {
var value = this.value,
ret;
if (dateTimeLabelFormat) { // datetime axis
ret = dateFormat(dateTimeLabelFormat, value);
} else if (tickInterval % 1000000 === 0) { // use M abbreviation
ret = (value / 1000000) + 'M';
} else if (tickInterval % 1000 === 0) { // use k abbreviation
ret = (value / 1000) + 'k';
} else if (!categories && value >= 1000) { // add thousands separators
ret = numberFormat(value, 0);
} else { // strings (categories) and small numbers
ret = value;
}
return ret;
},
staggerLines = horiz && options.labels.staggerLines,
reversed = options.reversed,
tickmarkOffset = (categories && options.tickmarkPlacement === 'between') ? 0.5 : 0;
/**
* The Tick class
*/
function Tick(pos, minor) {
var tick = this;
tick.pos = pos;
tick.minor = minor;
tick.isNew = true;
if (!minor) {
tick.addLabel();
}
}
Tick.prototype = {
/**
* Write the tick label
*/
addLabel: function () {
var tick = this,
pos = tick.pos,
labelOptions = options.labels,
str,
width = (categories && horiz && categories.length &&
!labelOptions.step && !labelOptions.staggerLines &&
!labelOptions.rotation &&
plotWidth / categories.length) ||
(!horiz && plotWidth / 2),
isFirst = pos === tickPositions[0],
isLast = pos === tickPositions[tickPositions.length - 1],
css,
value = categories && defined(categories[pos]) ? categories[pos] : pos,
label = tick.label;
// set properties for access in render method
tick.isFirst = isFirst;
tick.isLast = isLast;
// get the string
str = labelFormatter.call({
isFirst: isFirst,
isLast: isLast,
dateTimeLabelFormat: dateTimeLabelFormat,
value: isLog ? lin2log(value) : value
});
// prepare CSS
css = width && { width: mathMax(1, mathRound(width - 2 * (labelOptions.padding || 10))) + PX };
css = extend(css, labelOptions.style);
// first call
if (!defined(label)) {
tick.label =
defined(str) && labelOptions.enabled ?
renderer.text(
str,
0,
0,
labelOptions.useHTML
)
.attr({
align: labelOptions.align,
rotation: labelOptions.rotation
})
// without position absolute, IE export sometimes is wrong
.css(css)
.add(axisGroup) :
null;
// update
} else if (label) {
label.attr({
text: str
})
.css(css);
}
},
/**
* Get the offset height or width of the label
*/
getLabelSize: function () {
var label = this.label;
return label ?
((this.labelBBox = label.getBBox()))[horiz ? 'height' : 'width'] :
0;
},
/**
* Put everything in place
*
* @param index {Number}
* @param old {Boolean} Use old coordinates to prepare an animation into new position
*/
render: function (index, old) {
var tick = this,
major = !tick.minor,
label = tick.label,
pos = tick.pos,
labelOptions = options.labels,
gridLine = tick.gridLine,
gridLineWidth = major ? options.gridLineWidth : options.minorGridLineWidth,
gridLineColor = major ? options.gridLineColor : options.minorGridLineColor,
dashStyle = major ?
options.gridLineDashStyle :
options.minorGridLineDashStyle,
gridLinePath,
mark = tick.mark,
markPath,
tickLength = major ? options.tickLength : options.minorTickLength,
tickWidth = major ? options.tickWidth : (options.minorTickWidth || 0),
tickColor = major ? options.tickColor : options.minorTickColor,
tickPosition = major ? options.tickPosition : options.minorTickPosition,
step = labelOptions.step,
cHeight = (old && oldChartHeight) || chartHeight,
attribs,
x,
y;
// get x and y position for ticks and labels
x = horiz ?
translate(pos + tickmarkOffset, null, null, old) + transB :
axisLeft + offset + (opposite ? ((old && oldChartWidth) || chartWidth) - axisRight - axisLeft : 0);
y = horiz ?
cHeight - axisBottom + offset - (opposite ? axisHeight : 0) :
cHeight - translate(pos + tickmarkOffset, null, null, old) - transB;
// create the grid line
if (gridLineWidth) {
gridLinePath = getPlotLinePath(pos + tickmarkOffset, gridLineWidth, old);
if (gridLine === UNDEFINED) {
attribs = {
stroke: gridLineColor,
'stroke-width': gridLineWidth
};
if (dashStyle) {
attribs.dashstyle = dashStyle;
}
if (major) {
attribs.zIndex = 1;
}
tick.gridLine = gridLine =
gridLineWidth ?
renderer.path(gridLinePath)
.attr(attribs).add(gridGroup) :
null;
}
// If the parameter 'old' is set, the current call will be followed
// by another call, therefore do not do any animations this time
if (!old && gridLine && gridLinePath) {
gridLine.animate({
d: gridLinePath
});
}
}
// create the tick mark
if (tickWidth) {
// negate the length
if (tickPosition === 'inside') {
tickLength = -tickLength;
}
if (opposite) {
tickLength = -tickLength;
}
markPath = renderer.crispLine([
M,
x,
y,
L,
x + (horiz ? 0 : -tickLength),
y + (horiz ? tickLength : 0)
], tickWidth);
if (mark) { // updating
mark.animate({
d: markPath
});
} else { // first time
tick.mark = renderer.path(
markPath
).attr({
stroke: tickColor,
'stroke-width': tickWidth
}).add(axisGroup);
}
}
// the label is created on init - now move it into place
if (label && !isNaN(x)) {
x = x + labelOptions.x - (tickmarkOffset && horiz ?
tickmarkOffset * transA * (reversed ? -1 : 1) : 0);
y = y + labelOptions.y - (tickmarkOffset && !horiz ?
tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
// vertically centered
if (!defined(labelOptions.y)) {
y += pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
}
// correct for staggered labels
if (staggerLines) {
y += (index / (step || 1) % staggerLines) * 16;
}
// apply show first and show last
if ((tick.isFirst && !pick(options.showFirstLabel, 1)) ||
(tick.isLast && !pick(options.showLastLabel, 1))) {
label.hide();
} else {
// show those that may have been previously hidden, either by show first/last, or by step
label.show();
}
// apply step
if (step && index % step) {
// show those indices dividable by step
label.hide();
}
label[tick.isNew ? 'attr' : 'animate']({
x: x,
y: y
});
}
tick.isNew = false;
},
/**
* Destructor for the tick prototype
*/
destroy: function () {
destroyObjectProperties(this);
}
};
/**
* The object wrapper for plot lines and plot bands
* @param {Object} options
*/
function PlotLineOrBand(options) {
var plotLine = this;
if (options) {
plotLine.options = options;
plotLine.id = options.id;
}
//plotLine.render()
return plotLine;
}
PlotLineOrBand.prototype = {
/**
* Render the plot line or plot band. If it is already existing,
* move it.
*/
render: function () {
var plotLine = this,
options = plotLine.options,
optionsLabel = options.label,
label = plotLine.label,
width = options.width,
to = options.to,
from = options.from,
value = options.value,
toPath, // bands only
dashStyle = options.dashStyle,
svgElem = plotLine.svgElem,
path = [],
addEvent,
eventType,
xs,
ys,
x,
y,
color = options.color,
zIndex = options.zIndex,
events = options.events,
attribs;
// logarithmic conversion
if (isLog) {
from = log2lin(from);
to = log2lin(to);
value = log2lin(value);
}
// plot line
if (width) {
path = getPlotLinePath(value, width);
attribs = {
stroke: color,
'stroke-width': width
};
if (dashStyle) {
attribs.dashstyle = dashStyle;
}
} else if (defined(from) && defined(to)) { // plot band
// keep within plot area
from = mathMax(from, min);
to = mathMin(to, max);
toPath = getPlotLinePath(to);
path = getPlotLinePath(from);
if (path && toPath) {
path.push(
toPath[4],
toPath[5],
toPath[1],
toPath[2]
);
} else { // outside the axis area
path = null;
}
attribs = {
fill: color
};
} else {
return;
}
// zIndex
if (defined(zIndex)) {
attribs.zIndex = zIndex;
}
// common for lines and bands
if (svgElem) {
if (path) {
svgElem.animate({
d: path
}, null, svgElem.onGetPath);
} else {
svgElem.hide();
svgElem.onGetPath = function () {
svgElem.show();
};
}
} else if (path && path.length) {
plotLine.svgElem = svgElem = renderer.path(path)
.attr(attribs).add();
// events
if (events) {
addEvent = function (eventType) {
svgElem.on(eventType, function (e) {
events[eventType].apply(plotLine, [e]);
});
};
for (eventType in events) {
addEvent(eventType);
}
}
}
// the plot band/line label
if (optionsLabel && defined(optionsLabel.text) && path && path.length && axisWidth > 0 && axisHeight > 0) {
// apply defaults
optionsLabel = merge({
align: horiz && toPath && 'center',
x: horiz ? !toPath && 4 : 10,
verticalAlign : !horiz && toPath && 'middle',
y: horiz ? toPath ? 16 : 10 : toPath ? 6 : -4,
rotation: horiz && !toPath && 90
}, optionsLabel);
// add the SVG element
if (!label) {
plotLine.label = label = renderer.text(
optionsLabel.text,
0,
0
)
.attr({
align: optionsLabel.textAlign || optionsLabel.align,
rotation: optionsLabel.rotation,
zIndex: zIndex
})
.css(optionsLabel.style)
.add();
}
// get the bounding box and align the label
xs = [path[1], path[4], pick(path[6], path[1])];
ys = [path[2], path[5], pick(path[7], path[2])];
x = arrayMin(xs);
y = arrayMin(ys);
label.align(optionsLabel, false, {
x: x,
y: y,
width: arrayMax(xs) - x,
height: arrayMax(ys) - y
});
label.show();
} else if (label) { // move out of sight
label.hide();
}
// chainable
return plotLine;
},
/**
* Remove the plot line or band
*/
destroy: function () {
var obj = this;
destroyObjectProperties(obj);
// remove it from the lookup
erase(plotLinesAndBands, obj);
}
};
/**
* The class for stack items
*/
function StackItem(options, isNegative, x, stackOption) {
var stackItem = this;
// Tells if the stack is negative
stackItem.isNegative = isNegative;
// Save the options to be able to style the label
stackItem.options = options;
// Save the x value to be able to position the label later
stackItem.x = x;
// Save the stack option on the series configuration object
stackItem.stack = stackOption;
// The align options and text align varies on whether the stack is negative and
// if the chart is inverted or not.
// First test the user supplied value, then use the dynamic.
stackItem.alignOptions = {
align: options.align || (inverted ? (isNegative ? 'left' : 'right') : 'center'),
verticalAlign: options.verticalAlign || (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)),
x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0)
};
stackItem.textAlign = options.textAlign || (inverted ? (isNegative ? 'right' : 'left') : 'center');
}
StackItem.prototype = {
destroy: function () {
destroyObjectProperties(this);
},
/**
* Sets the total of this stack. Should be called when a serie is hidden or shown
* since that will affect the total of other stacks.
*/
setTotal: function (total) {
this.total = total;
this.cum = total;
},
/**
* Renders the stack total label and adds it to the stack label group.
*/
render: function (group) {
var stackItem = this, // aliased this
str = stackItem.options.formatter.call(stackItem); // format the text in the label
// Change the text to reflect the new total and set visibility to hidden in case the serie is hidden
if (stackItem.label) {
stackItem.label.attr({text: str, visibility: HIDDEN});
// Create new label
} else {
stackItem.label =
chart.renderer.text(str, 0, 0) // dummy positions, actual position updated with setOffset method in columnseries
.css(stackItem.options.style) // apply style
.attr({align: stackItem.textAlign, // fix the text-anchor
rotation: stackItem.options.rotation, // rotation
visibility: HIDDEN }) // hidden until setOffset is called
.add(group); // add to the labels-group
}
},
/**
* Sets the offset that the stack has from the x value and repositions the label.
*/
setOffset: function (xOffset, xWidth) {
var stackItem = this, // aliased this
neg = stackItem.isNegative, // special treatment is needed for negative stacks
y = axis.translate(stackItem.total), // stack value translated mapped to chart coordinates
yZero = axis.translate(0), // stack origin
h = mathAbs(y - yZero), // stack height
x = chart.xAxis[0].translate(stackItem.x) + xOffset, // stack x position
plotHeight = chart.plotHeight,
stackBox = { // this is the box for the complete stack
x: inverted ? (neg ? y : y - h) : x,
y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y),
width: inverted ? h : xWidth,
height: inverted ? xWidth : h
};
if (stackItem.label) {
stackItem.label
.align(stackItem.alignOptions, null, stackBox) // align the label to the box
.attr({visibility: VISIBLE}); // set visibility
}
}
};
/**
* Get the minimum and maximum for the series of each axis
*/
function getSeriesExtremes() {
var posStack = [],
negStack = [],
i;
// reset dataMin and dataMax in case we're redrawing
dataMin = dataMax = null;
// loop through this axis' series
each(axis.series, function (series) {
if (series.visible || !optionsChart.ignoreHiddenSeries) {
var seriesOptions = series.options,
stacking,
posPointStack,
negPointStack,
stackKey,
stackOption,
negKey,
xData,
yData,
x,
y,
threshold = seriesOptions.threshold,
yDataLength,
distance,
activeYData = [],
activeCounter = 0;
// Get dataMin and dataMax for X axes
if (isXAxis) {
xData = series.xData;
dataMin = mathMin(pick(dataMin, xData[0]), arrayMin(xData));
dataMax = mathMax(pick(dataMax, xData[0]), arrayMax(xData));
// Get dataMin and dataMax for Y axes, as well as handle stacking and processed data
} else {
var isNegative,
pointStack,
key,
cropped = series.cropped,
xExtremes = series.xAxis.getExtremes(),
findPointRange,
pointRange,
j,
hasModifyValue = !!series.modifyValue;
// Handle stacking
stacking = seriesOptions.stacking;
usePercentage = stacking === 'percent';
// create a stack for this particular series type
if (stacking) {
stackOption = series.options.stack;
stackKey = series.type + pick(stackOption, '');
negKey = '-' + stackKey;
series.stackKey = stackKey; // used in translate
posPointStack = posStack[stackKey] || []; // contains the total values for each x
posStack[stackKey] = posPointStack;
negPointStack = negStack[negKey] || [];
negStack[negKey] = negPointStack;
}
if (usePercentage) {
dataMin = 0;
dataMax = 99;
}
// get clipped and grouped data
series.processData();
// processData can alter series.pointRange, so this goes after
findPointRange = series.pointRange === null;
xData = series.processedXData;
yData = series.processedYData;
yDataLength = yData.length;
// loop over the non-null y values and read them into a local array
for (i = 0; i < yDataLength; i++) {
x = xData[i];
y = yData[i];
if (y !== null && y !== UNDEFINED) {
// read stacked values into a stack based on the x value,
// the sign of y and the stack key
if (stacking) {
isNegative = y < 0;
pointStack = isNegative ? negPointStack : posPointStack;
key = isNegative ? negKey : stackKey;
y = pointStack[x] =
defined(pointStack[x]) ?
pointStack[x] + y : y;
// add the series
if (!stacks[key]) {
stacks[key] = {};
}
// If the StackItem is there, just update the values,
// if not, create one first
if (!stacks[key][x]) {
stacks[key][x] = new StackItem(options.stackLabels, isNegative, x, stackOption);
}
stacks[key][x].setTotal(y);
// general hook, used for Highstock compare values feature
} else if (hasModifyValue) {
y = series.modifyValue(y);
}
// get the smallest distance between points
if (i) {
distance = mathAbs(xData[i] - xData[i - 1]);
pointRange = pointRange === UNDEFINED ? distance : mathMin(distance, pointRange);
}
// for points within the visible range, including the first point outside the
// visible range, consider y extremes
if (cropped || ((xData[i + 1] || x) >= xExtremes.min && (xData[i - 1] || x) <= xExtremes.max)) {
j = y.length;
if (j) { // array, like ohlc data
while (j--) {
if (y[j] !== null) {
activeYData[activeCounter++] = y[j];
}
}
} else {
activeYData[activeCounter++] = y;
}
}
}
}
// record the least unit distance
if (findPointRange) {
series.pointRange = pointRange || 1;
}
series.closestPointRange = pointRange;
// Get the dataMin and dataMax so far. If percentage is used, the min and max are
// always 0 and 100. If the length of activeYData is 0, continue with null values.
if (!usePercentage && activeYData.length) {
dataMin = mathMin(pick(dataMin, activeYData[0]), arrayMin(activeYData));
dataMax = mathMax(pick(dataMax, activeYData[0]), arrayMax(activeYData));
}
// todo: instead of checking useThreshold, just set the threshold to 0
// in area and column-like chart types
if (series.useThreshold && threshold !== null) {
if (dataMin >= threshold) {
dataMin = threshold;
ignoreMinPadding = true;
} else if (dataMax < threshold) {
dataMax = threshold;
ignoreMaxPadding = true;
}
}
}
}
});
}
/**
* Translate from axis value to pixel position on the chart, or back
*
*/
translate = function (val, backwards, cvsCoord, old, handleLog) {
var sign = 1,
cvsOffset = 0,
localA = old ? oldTransA : transA,
localMin = old ? oldMin : min,
returnValue;
if (!localA) {
localA = transA;
}
if (cvsCoord) {
sign *= -1; // canvas coordinates inverts the value
cvsOffset = axisLength;
}
if (reversed) { // reversed axis
sign *= -1;
cvsOffset -= sign * axisLength;
}
if (backwards) { // reverse translation
if (reversed) {
val = axisLength - val;
}
returnValue = val / localA + localMin; // from chart pixel to value
if (isLog && handleLog) {
returnValue = lin2log(returnValue);
}
} else { // normal translation, from axis value to pixel, relative to plot
if (isLog && handleLog) {
val = log2lin(val);
}
returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * minPixelPadding);
}
return returnValue;
};
/**
* Create the path for a plot line that goes from the given value on
* this axis, across the plot to the opposite side
* @param {Number} value
* @param {Number} lineWidth Used for calculation crisp line
* @param {Number] old Use old coordinates (for resizing and rescaling)
*/
getPlotLinePath = function (value, lineWidth, old) {
var x1,
y1,
x2,
y2,
translatedValue = translate(value, null, null, old),
cHeight = (old && oldChartHeight) || chartHeight,
cWidth = (old && oldChartWidth) || chartWidth,
skip;
x1 = x2 = mathRound(translatedValue + transB);
y1 = y2 = mathRound(cHeight - translatedValue - transB);
if (isNaN(translatedValue)) { // no min or max
skip = true;
} else if (horiz) {
y1 = axisTop;
y2 = cHeight - axisBottom;
if (x1 < axisLeft || x1 > axisLeft + axisWidth) {
skip = true;
}
} else {
x1 = axisLeft;
x2 = cWidth - axisRight;
if (y1 < axisTop || y1 > axisTop + axisHeight) {
skip = true;
}
}
return skip ?
null :
renderer.crispLine([M, x1, y1, L, x2, y2], lineWidth || 0);
};
/**
* Fix JS round off float errors
* @param {Number} num
*/
function correctFloat(num) {
var invMag, ret = num;
magnitude = pick(magnitude, math.pow(10, mathFloor(math.log(tickInterval) / math.LN10)));
if (magnitude < 1) {
invMag = mathRound(1 / magnitude) * 10;
ret = mathRound(num * invMag) / invMag;
}
return ret;
}
/**
* Set the tick positions of a linear axis to round values like whole tens or every five.
*/
function setLinearTickPositions() {
var i,
roundedMin = correctFloat(mathFloor(min / tickInterval) * tickInterval),
roundedMax = correctFloat(mathCeil(max / tickInterval) * tickInterval);
tickPositions = [];
// populate the intermediate values
i = correctFloat(roundedMin);
while (i <= roundedMax) {
tickPositions.push(i);
i = correctFloat(i + tickInterval);
}
}
/**
* Adjust the min and max for the minimum range
*/
function adjustForMinRange(secondPass) {
var zoomOffset,
halfPointRange = (axis.pointRange || 0) / 2,
spaceAvailable = dataMax - dataMin > minRange,
minArgs,
maxArgs;
// set the automatic minimum range based on the closest point distance
if (secondPass && minRange === UNDEFINED) {
minRange = isXAxis && !defined(options.min) && !defined(options.max) ?
mathMin(axis.closestPointRange * 5, dataMax - dataMin) :
null;
}
// if minRange is exceeded, adjust
if (max - min < minRange) {
zoomOffset = (minRange - max + min) / 2;
// if min and max options have been set, don't go beyond it
minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
if (spaceAvailable) { // if space is available, stay within the data range
minArgs[2] = dataMin - halfPointRange;
}
min = mathMax(0, arrayMax(minArgs));
maxArgs = [min + minRange, pick(options.max, min + minRange)];
if (spaceAvailable) { // if space is availabe, stay within the data range
maxArgs[2] = dataMax + halfPointRange;
}
max = mathMin(0, arrayMin(maxArgs));
// now if the max is adjusted, adjust the min back
if (max - min < minRange) {
minArgs[0] = max - minRange;
minArgs[1] = pick(options.min, max - minRange);
min = mathMax(0, arrayMax(minArgs));
}
}
}
/**
* Set the tick positions to round values and optionally extend the extremes
* to the nearest tick
*/
function setTickPositions(secondPass) {
var length,
linkedParent,
linkedParentExtremes,
tickIntervalOption = options.tickInterval,
tickPixelIntervalOption = options.tickPixelInterval;
// linked axis gets the extremes from the parent axis
if (isLinked) {
linkedParent = chart[isXAxis ? 'xAxis' : 'yAxis'][options.linkedTo];
linkedParentExtremes = linkedParent.getExtremes();
min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
} else { // initial min and max from the extreme data values
min = pick(userMin, options.min, dataMin);
max = pick(userMax, options.max, dataMax);
}
if (isLog) {
min = log2lin(min);
max = log2lin(max);
}
// handle zoomed range
if (range) {
userMin = min = max - range;
userMax = max;
if (secondPass) {
range = null; // don't use it when running setExtremes
}
}
// adjust min and max for the minimum range
adjustForMinRange(secondPass);
// pad the values to get clear of the chart's edges
if (!categories && !usePercentage && !isLinked && defined(min) && defined(max)) {
length = (max - min) || 1;
if (!defined(options.min) && !defined(userMin) && minPadding && (dataMin < 0 || !ignoreMinPadding)) {
min -= length * minPadding;
}
if (!defined(options.max) && !defined(userMax) && maxPadding && (dataMax > 0 || !ignoreMaxPadding)) {
max += length * maxPadding;
}
}
// get tickInterval
if (min === max || min === undefined || max === undefined) {
tickInterval = 1;
} else if (isLinked && !tickIntervalOption &&
tickPixelIntervalOption === linkedParent.options.tickPixelInterval) {
tickInterval = linkedParent.tickInterval;
} else {
tickInterval = pick(
tickIntervalOption,
categories ? // for categoried axis, 1 is default, for linear axis use tickPix
1 :
(max - min) * tickPixelIntervalOption / (axisLength || 1)
);
}
if (!isDatetimeAxis) { // linear
magnitude = math.pow(10, mathFloor(math.log(tickInterval) / math.LN10));
if (!defined(options.tickInterval)) {
tickInterval = normalizeTickInterval(tickInterval, null, magnitude, options);
}
}
axis.tickInterval = tickInterval; // record for linked axis
// get minorTickInterval
minorTickInterval = options.minorTickInterval === 'auto' && tickInterval ?
tickInterval / 5 : options.minorTickInterval;
// find the tick positions
tickPositions = options.tickPositions || (tickPositioner && tickPositioner.apply(axis, [min, max])); // docs
if (!tickPositions) {
if (isDatetimeAxis) {
tickPositions = getTimeTicks(tickInterval, min, max, options.startOfWeek, options.units); // docs
dateTimeLabelFormat = options.dateTimeLabelFormats[tickPositions.info.unitName];
} else {
setLinearTickPositions();
}
}
if (!isLinked) {
// reset min/max or remove extremes based on start/end on tick
var roundedMin = tickPositions[0],
roundedMax = tickPositions[tickPositions.length - 1];
if (options.startOnTick) {
min = roundedMin;
} else if (min > roundedMin) {
tickPositions.shift();
}
if (options.endOnTick) {
max = roundedMax;
} else if (max < roundedMax) {
tickPositions.pop();
}
// record the greatest number of ticks for multi axis
if (!maxTicks) { // first call, or maxTicks have been reset after a zoom operation
maxTicks = {
x: 0,
y: 0
};
}
if (!isDatetimeAxis && tickPositions.length > maxTicks[xOrY] && options.alignTicks !== false) {
maxTicks[xOrY] = tickPositions.length;
}
}
}
/**
* When using multiple axes, adjust the number of ticks to match the highest
* number of ticks in that group
*/
function adjustTickAmount() {
if (maxTicks && maxTicks[xOrY] && !isDatetimeAxis && !categories && !isLinked && options.alignTicks !== false) { // only apply to linear scale
var oldTickAmount = tickAmount,
calculatedTickAmount = tickPositions.length;
// set the axis-level tickAmount to use below
tickAmount = maxTicks[xOrY];
if (calculatedTickAmount < tickAmount) {
while (tickPositions.length < tickAmount) {
tickPositions.push(correctFloat(
tickPositions[tickPositions.length - 1] + tickInterval
));
}
transA *= (calculatedTickAmount - 1) / (tickAmount - 1);
max = tickPositions[tickPositions.length - 1];
}
if (defined(oldTickAmount) && tickAmount !== oldTickAmount) {
axis.isDirty = true;
}
}
}
/**
* Set the scale based on data min and max, user set min and max or options
*
*/
function setScale() {
var type,
i,
isDirtyData;
oldMin = min;
oldMax = max;
oldAxisLength = axisLength;
// set the new axisLength
axisLength = horiz ? axisWidth : axisHeight;
// is there new data?
each(axis.series, function (series) {
if (series.isDirtyData || series.isDirty ||
series.xAxis.isDirty) { // when x axis is dirty, we need new data extremes for y as well
isDirtyData = true;
}
});
// do we really need to go through all this?
if (axisLength !== oldAxisLength || isDirtyData || isLinked ||
userMin !== oldUserMin || userMax !== oldUserMax) {
// get data extremes if needed
getSeriesExtremes();
// get fixed positions based on tickInterval
setTickPositions();
// record old values to decide whether a rescale is necessary later on (#540)
oldUserMin = userMin;
oldUserMax = userMax;
// the translation factor used in translate function
oldTransA = transA;
transA = axisLength / ((max - min + (axis.pointRange || 0)) || 1);
// reset stacks
if (!isXAxis) {
for (type in stacks) {
for (i in stacks[type]) {
stacks[type][i].cum = stacks[type][i].total;
}
}
}
// Mark as dirty if it is not already set to dirty and extremes have changed. #595.
if (!axis.isDirty) {
axis.isDirty = chart.isDirtyBox || min !== oldMin || max !== oldMax;
}
}
}
/**
* Set the extremes and optionally redraw
* @param {Number} newMin
* @param {Number} newMax
* @param {Boolean} redraw
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*
*/
function setExtremes(newMin, newMax, redraw, animation) {
redraw = pick(redraw, true); // defaults to true
fireEvent(axis, 'setExtremes', { // fire an event to enable syncing of multiple charts
min: newMin,
max: newMax
}, function () { // the default event handler
userMin = newMin;
userMax = newMax;
// redraw
if (redraw) {
chart.redraw(animation);
}
});
// this event contains the min and max values that may be modified by padding etc.
fireEvent(axis, 'afterSetExtremes', {
min: min,
max: max
});
}
/**
* Update the axis metrics
*/
function setAxisSize() {
var offsetLeft = options.offsetLeft || 0,
offsetRight = options.offsetRight || 0,
range = max - min,
pointRange = 0,
closestPointRange,
seriesClosestPointRange;
// basic values
axisLeft = pick(options.left, plotLeft + offsetLeft);
axisTop = pick(options.top, plotTop);
axisWidth = pick(options.width, plotWidth - offsetLeft + offsetRight);
axisHeight = pick(options.height, plotHeight);
axisBottom = chartHeight - axisHeight - axisTop;
axisRight = chartWidth - axisWidth - axisLeft;
axisLength = horiz ? axisWidth : axisHeight;
// adjust translation for padding
if (isXAxis) {
each(axis.series, function (series) {
pointRange = mathMax(pointRange, series.pointRange);
seriesClosestPointRange = series.closestPointRange;
if (!series.noSharedTooltip && defined(seriesClosestPointRange)) {
closestPointRange = defined(closestPointRange) ?
mathMin(closestPointRange, seriesClosestPointRange) :
seriesClosestPointRange;
}
});
// pointRange means the width reserved for each point, like in a column chart
if ((defined(userMin) || defined(userMax)) && pointRange > tickInterval / 2) {
// prevent great padding when zooming tightly in to view columns
pointRange = 0;
}
axis.pointRange = pointRange;
// closestPointRange means the closest distance between points. In columns
// it is mostly equal to pointRange, but in lines pointRange is 0 while closestPointRange
// is some other value
axis.closestPointRange = closestPointRange;
}
// secondary values
transA = axisLength / ((range + pointRange) || 1);
transB = horiz ? axisLeft : axisBottom; // translation addend
minPixelPadding = transA * (pointRange / 2);
// expose to use in Series object and navigator
axis.left = axisLeft;
axis.top = axisTop;
axis.len = axisLength;
}
/**
* Get the actual axis extremes
*/
function getExtremes() {
return {
min: min,
max: max,
dataMin: dataMin,
dataMax: dataMax,
userMin: userMin,
userMax: userMax
};
}
/**
* Get the zero plane either based on zero or on the min or max value.
* Used in bar and area plots
*/
function getThreshold(threshold) {
if (min > threshold || threshold === null) {
threshold = min;
} else if (max < threshold) {
threshold = max;
}
return translate(threshold, 0, 1);
}
/**
* Add a plot band or plot line after render time
*
* @param options {Object} The plotBand or plotLine configuration object
*/
function addPlotBandOrLine(options) {
var obj = new PlotLineOrBand(options).render();
plotLinesAndBands.push(obj);
return obj;
}
/**
* Render the tick labels to a preliminary position to get their sizes
*/
function getOffset() {
var hasData = axis.series.length && defined(min) && defined(max),
showAxis = hasData || pick(options.showEmpty, true), // docs
titleOffset = 0,
titleMargin = 0,
axisTitleOptions = options.title,
labelOptions = options.labels,
directionFactor = [-1, 1, 1, -1][side],
n;
if (!axisGroup) {
axisGroup = renderer.g('axis')
.attr({ zIndex: 7 })
.add();
gridGroup = renderer.g('grid')
.attr({ zIndex: options.gridZIndex || 1 }) // docs
.add();
}
labelOffset = 0; // reset
if (hasData || isLinked) {
each(tickPositions, function (pos) {
if (!ticks[pos]) {
ticks[pos] = new Tick(pos);
} else {
ticks[pos].addLabel(); // update labels depending on tick interval
}
});
each(tickPositions, function (pos) {
// left side must be align: right and right side must have align: left for labels
if (side === 0 || side === 2 || { 1: 'left', 3: 'right' }[side] === labelOptions.align) {
// get the highest offset
labelOffset = mathMax(
ticks[pos].getLabelSize(),
labelOffset
);
}
});
if (staggerLines) {
labelOffset += (staggerLines - 1) * 16;
}
} else { // doesn't have data
for (n in ticks) {
ticks[n].destroy();
delete ticks[n];
}
}
if (axisTitleOptions && axisTitleOptions.text) {
if (!axisTitle) {
axisTitle = axis.axisTitle = renderer.text(
axisTitleOptions.text,
0,
0,
axisTitleOptions.useHTML
)
.attr({
zIndex: 7,
rotation: axisTitleOptions.rotation || 0,
align:
axisTitleOptions.textAlign ||
{ low: 'left', middle: 'center', high: 'right' }[axisTitleOptions.align]
})
.css(axisTitleOptions.style)
.add();
axisTitle.isNew = true;
}
if (showAxis) {
titleOffset = axisTitle.getBBox()[horiz ? 'height' : 'width'];
titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10);
}
// hide or show the title depending on whether showEmpty is set
axisTitle[showAxis ? 'show' : 'hide']();
}
// handle automatic or user set offset
offset = directionFactor * pick(options.offset, axisOffset[side]);
axisTitleMargin =
pick(axisTitleOptions.offset, // docs
labelOffset + titleMargin +
(side !== 2 && labelOffset && directionFactor * options.labels[horiz ? 'y' : 'x'])
);
axisOffset[side] = mathMax(
axisOffset[side],
axisTitleMargin + titleOffset + directionFactor * offset
);
}
/**
* Render the axis
*/
function render() {
var axisTitleOptions = options.title,
stackLabelOptions = options.stackLabels,
alternateGridColor = options.alternateGridColor,
lineWidth = options.lineWidth,
lineLeft,
lineTop,
linePath,
hasRendered = chart.hasRendered,
slideInTicks = hasRendered && defined(oldMin) && !isNaN(oldMin),
hasData = axis.series.length && defined(min) && defined(max),
showAxis = hasData || pick(options.showEmpty, true);
// If the series has data draw the ticks. Else only the line and title
if (hasData || isLinked) {
// minor ticks
if (minorTickInterval && !categories) {
var pos = min + (tickPositions[0] - min) % minorTickInterval;
for (; pos <= max; pos += minorTickInterval) {
if (!minorTicks[pos]) {
minorTicks[pos] = new Tick(pos, true);
}
// render new ticks in old position
if (slideInTicks && minorTicks[pos].isNew) {
minorTicks[pos].render(null, true);
}
minorTicks[pos].isActive = true;
minorTicks[pos].render();
}
}
// major ticks
each(tickPositions, function (pos, i) {
// linked axes need an extra check to find out if
if (!isLinked || (pos >= min && pos <= max)) {
// render new ticks in old position
if (slideInTicks && ticks[pos].isNew) {
ticks[pos].render(i, true);
}
ticks[pos].isActive = true;
ticks[pos].render(i);
}
});
// alternate grid color
if (alternateGridColor) {
each(tickPositions, function (pos, i) {
if (i % 2 === 0 && pos < max) {
if (!alternateBands[pos]) {
alternateBands[pos] = new PlotLineOrBand();
}
alternateBands[pos].options = {
from: pos,
to: tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] : max,
color: alternateGridColor
};
alternateBands[pos].render();
alternateBands[pos].isActive = true;
}
});
}
// custom plot lines and bands
if (!axis._addedPlotLB) { // only first time
each((options.plotLines || []).concat(options.plotBands || []), function (plotLineOptions) {
plotLinesAndBands.push(new PlotLineOrBand(plotLineOptions).render());
});
axis._addedPlotLB = true;
}
} // end if hasData
// remove inactive ticks
each([ticks, minorTicks, alternateBands], function (coll) {
var pos;
for (pos in coll) {
if (!coll[pos].isActive) {
coll[pos].destroy();
delete coll[pos];
} else {
coll[pos].isActive = false; // reset
}
}
});
// Static items. As the axis group is cleared on subsequent calls
// to render, these items are added outside the group.
// axis line
if (lineWidth) {
lineLeft = axisLeft + (opposite ? axisWidth : 0) + offset;
lineTop = chartHeight - axisBottom - (opposite ? axisHeight : 0) + offset;
linePath = renderer.crispLine([
M,
horiz ?
axisLeft :
lineLeft,
horiz ?
lineTop :
axisTop,
L,
horiz ?
chartWidth - axisRight :
lineLeft,
horiz ?
lineTop :
chartHeight - axisBottom
], lineWidth);
if (!axisLine) {
axisLine = renderer.path(linePath)
.attr({
stroke: options.lineColor,
'stroke-width': lineWidth,
zIndex: 7
})
.add();
} else {
axisLine.animate({ d: linePath });
}
// show or hide the line depending on options.showEmpty
axisLine[showAxis ? 'show' : 'hide']();
}
if (axisTitle && showAxis) {
// compute anchor points for each of the title align options
var margin = horiz ? axisLeft : axisTop,
fontSize = pInt(axisTitleOptions.style.fontSize || 12),
// the position in the length direction of the axis
alongAxis = {
low: margin + (horiz ? 0 : axisLength),
middle: margin + axisLength / 2,
high: margin + (horiz ? axisLength : 0)
}[axisTitleOptions.align],
// the position in the perpendicular direction of the axis
offAxis = (horiz ? axisTop + axisHeight : axisLeft) +
(horiz ? 1 : -1) * // horizontal axis reverses the margin
(opposite ? -1 : 1) * // so does opposite axes
axisTitleMargin +
(side === 2 ? fontSize : 0);
axisTitle[axisTitle.isNew ? 'attr' : 'animate']({
x: horiz ?
alongAxis :
offAxis + (opposite ? axisWidth : 0) + offset +
(axisTitleOptions.x || 0), // x
y: horiz ?
offAxis - (opposite ? axisHeight : 0) + offset :
alongAxis + (axisTitleOptions.y || 0) // y
});
axisTitle.isNew = false;
}
// Stacked totals:
if (stackLabelOptions && stackLabelOptions.enabled) {
var stackKey, oneStack, stackCategory,
stackTotalGroup = axis.stackTotalGroup;
// Create a separate group for the stack total labels
if (!stackTotalGroup) {
axis.stackTotalGroup = stackTotalGroup =
renderer.g('stack-labels')
.attr({
visibility: VISIBLE,
zIndex: 6
})
.translate(plotLeft, plotTop)
.add();
}
// Render each stack total
for (stackKey in stacks) {
oneStack = stacks[stackKey];
for (stackCategory in oneStack) {
oneStack[stackCategory].render(stackTotalGroup);
}
}
}
// End stacked totals
axis.isDirty = false;
}
/**
* Remove a plot band or plot line from the chart by id
* @param {Object} id
*/
function removePlotBandOrLine(id) {
var i = plotLinesAndBands.length;
while (i--) {
if (plotLinesAndBands[i].id === id) {
plotLinesAndBands[i].destroy();
}
}
}
/**
* Redraw the axis to reflect changes in the data or axis extremes
*/
function redraw() {
// hide tooltip and hover states
if (tracker.resetTracker) {
tracker.resetTracker();
}
// render the axis
render();
// move plot lines and bands
each(plotLinesAndBands, function (plotLine) {
plotLine.render();
});
// mark associated series as dirty and ready for redraw
each(axis.series, function (series) {
series.isDirty = true;
});
}
/**
* Set new axis categories and optionally redraw
* @param {Array} newCategories
* @param {Boolean} doRedraw
*/
function setCategories(newCategories, doRedraw) {
// set the categories
axis.categories = userOptions.categories = categories = newCategories;
// force reindexing tooltips
each(axis.series, function (series) {
series.translate();
series.setTooltipPoints(true);
});
// optionally redraw
axis.isDirty = true;
if (pick(doRedraw, true)) {
chart.redraw();
}
}
/**
* Destroys an Axis instance.
*/
function destroy() {
var stackKey;
// Remove the events
removeEvent(axis);
// Destroy each stack total
for (stackKey in stacks) {
destroyObjectProperties(stacks[stackKey]);
stacks[stackKey] = null;
}
// Destroy stack total group
if (axis.stackTotalGroup) {
axis.stackTotalGroup = axis.stackTotalGroup.destroy();
}
// Destroy collections
each([ticks, minorTicks, alternateBands, plotLinesAndBands], function (coll) {
destroyObjectProperties(coll);
});
// Destroy local variables
each([axisLine, axisGroup, gridGroup, axisTitle], function (obj) {
if (obj) {
obj.destroy();
}
});
axisLine = axisGroup = gridGroup = axisTitle = null;
}
// Run Axis
// Register
axes.push(axis);
chart[isXAxis ? 'xAxis' : 'yAxis'].push(axis);
// inverted charts have reversed xAxes as default
if (inverted && isXAxis && reversed === UNDEFINED) {
reversed = true;
}
// expose some variables
extend(axis, {
addPlotBand: addPlotBandOrLine,
addPlotLine: addPlotBandOrLine,
adjustTickAmount: adjustTickAmount,
categories: categories,
getExtremes: getExtremes,
getPlotLinePath: getPlotLinePath,
getThreshold: getThreshold,
isXAxis: isXAxis,
options: options,
plotLinesAndBands: plotLinesAndBands,
getOffset: getOffset,
render: render,
setAxisSize: setAxisSize,
setCategories: setCategories,
setExtremes: setExtremes,
setScale: setScale,
setTickPositions: setTickPositions,
translate: translate,
redraw: redraw,
removePlotBand: removePlotBandOrLine,
removePlotLine: removePlotBandOrLine,
reversed: reversed,
series: [], // populated by Series
stacks: stacks,
destroy: destroy
});
// register event listeners
for (eventType in events) {
addEvent(axis, eventType, events[eventType]);
}
// set min and max
//setScale();
} // end Axis
/**
* The toolbar object
*/
function Toolbar() {
var buttons = {};
/*jslint unparam: true*//* allow the unused param title until Toolbar rewrite*/
function add(id, text, title, fn) {
if (!buttons[id]) {
var button = renderer.text(
text,
0,
0
)
.css(options.toolbar.itemStyle)
.align({
align: 'right',
x: -marginRight - 20,
y: plotTop + 30
})
.on('click', fn)
.attr({
align: 'right',
zIndex: 20
})
.add();
buttons[id] = button;
}
}
/*jslint unparam: false*/
function remove(id) {
discardElement(buttons[id].element);
buttons[id] = null;
}
// public
return {
add: add,
remove: remove
};
}
/**
* The tooltip object
* @param {Object} options Tooltip options
*/
function Tooltip(options) {
var currentSeries,
borderWidth = options.borderWidth,
crosshairsOptions = options.crosshairs,
crosshairs = [],
style = options.style,
shared = options.shared,
padding = pInt(style.padding),
tooltipIsHidden = true,
currentX = 0,
currentY = 0;
// remove padding CSS and apply padding on box instead
style.padding = 0;
// create the label
var label = renderer.label('', 0, 0)
.attr({
padding: padding,
fill: options.backgroundColor,
'stroke-width': borderWidth,
r: options.borderRadius,
zIndex: 8
})
.css(style)
.hide()
.add()
.shadow(options.shadow);
/**
* Destroy the tooltip and its elements.
*/
function destroy() {
each(crosshairs, function (crosshair) {
if (crosshair) {
crosshair.destroy();
}
});
// Destroy and clear local variables
if (label) {
label = label.destroy();
}
}
/**
* In case no user defined formatter is given, this will be used
*/
function defaultFormatter() {
var pThis = this,
items = pThis.points || splat(pThis),
series = items[0].series,
s;
// build the header
s = [series.tooltipHeaderFormatter(items[0].key)];
// build the values
each(items, function (item) {
series = item.series;
s.push((series.tooltipFormatter && series.tooltipFormatter(item)) ||
item.point.tooltipFormatter(series.tooltipOptions.pointFormat));
});
return s.join('');
}
/**
* Provide a soft movement for the tooltip
*
* @param {Number} finalX
* @param {Number} finalY
*/
function move(finalX, finalY) {
// get intermediate values for animation
currentX = tooltipIsHidden ? finalX : (2 * currentX + finalX) / 3;
currentY = tooltipIsHidden ? finalY : (currentY + finalY) / 2;
// move to the intermediate value
label.attr({ x: currentX, y: currentY });
// run on next tick of the mouse tracker
if (mathAbs(finalX - currentX) > 1 || mathAbs(finalY - currentY) > 1) {
tooltipTick = function () {
move(finalX, finalY);
};
} else {
tooltipTick = null;
}
}
/**
* Hide the tooltip
*/
function hide() {
if (!tooltipIsHidden) {
var hoverPoints = chart.hoverPoints;
label.hide();
// hide previous hoverPoints and set new
if (hoverPoints) {
each(hoverPoints, function (point) {
point.setState();
});
}
chart.hoverPoints = null;
tooltipIsHidden = true;
}
}
/**
* Hide the crosshairs
*/
function hideCrosshairs() {
each(crosshairs, function (crosshair) {
if (crosshair) {
crosshair.hide();
}
});
}
/**
* Refresh the tooltip's text and position.
* @param {Object} point
*
*/
function refresh(point) {
var x,
y,
show,
plotX,
plotY,
textConfig = {},
text,
pointConfig = [],
tooltipPos = point.tooltipPos,
formatter = options.formatter || defaultFormatter,
hoverPoints = chart.hoverPoints,
placedTooltipPoint;
// shared tooltip, array is sent over
if (shared && !(point.series && point.series.noSharedTooltip)) {
plotY = 0;
// hide previous hoverPoints and set new
if (hoverPoints) {
each(hoverPoints, function (point) {
point.setState();
});
}
chart.hoverPoints = point;
each(point, function (item) {
item.setState(HOVER_STATE);
plotY += item.plotY; // for average
pointConfig.push(item.getLabelConfig());
});
plotX = point[0].plotX;
plotY = mathRound(plotY) / point.length; // mathRound because Opera 10 has problems here
textConfig = {
x: point[0].category
};
textConfig.points = pointConfig;
point = point[0];
// single point tooltip
} else {
textConfig = point.getLabelConfig();
}
text = formatter.call(textConfig);
// register the current series
currentSeries = point.series;
// get the reference point coordinates (pie charts use tooltipPos)
plotX = pick(plotX, point.plotX);
plotY = pick(plotY, point.plotY);
x = mathRound(tooltipPos ? tooltipPos[0] : (inverted ? plotWidth - plotY : plotX));
y = mathRound(tooltipPos ? tooltipPos[1] : (inverted ? plotHeight - plotX : plotY));
// hide tooltip if the point falls outside the plot
show = shared || !point.series.isCartesian || isInsidePlot(x, y);
// update the inner HTML
if (text === false || !show) {
hide();
} else {
// show it
if (tooltipIsHidden) {
label.show();
tooltipIsHidden = false;
}
// update text
label.attr({
text: text
});
// set the stroke color of the box
label.attr({
stroke: options.borderColor || point.color || currentSeries.color || '#606060'
});
placedTooltipPoint = placeBox(label.width, label.height, plotLeft, plotTop,
plotWidth, plotHeight, {x: x, y: y}, pick(options.distance, 12));
// do the move
move(mathRound(placedTooltipPoint.x), mathRound(placedTooltipPoint.y));
}
// crosshairs
if (crosshairsOptions) {
crosshairsOptions = splat(crosshairsOptions); // [x, y]
var path,
i = crosshairsOptions.length,
attribs,
axis;
while (i--) {
axis = point.series[i ? 'yAxis' : 'xAxis'];
if (crosshairsOptions[i] && axis) {
path = axis
.getPlotLinePath(point[i ? 'y' : 'x'], 1);
if (crosshairs[i]) {
crosshairs[i].attr({ d: path, visibility: VISIBLE });
} else {
attribs = {
'stroke-width': crosshairsOptions[i].width || 1,
stroke: crosshairsOptions[i].color || '#C0C0C0',
zIndex: crosshairsOptions[i].zIndex || 2
};
if (crosshairsOptions[i].dashStyle) {
attribs.dashstyle = crosshairsOptions[i].dashStyle;
}
crosshairs[i] = renderer.path(path)
.attr(attribs)
.add();
}
}
}
}
}
// public members
return {
shared: shared,
refresh: refresh,
hide: hide,
hideCrosshairs: hideCrosshairs,
destroy: destroy
};
}
/**
* The mouse tracker object
* @param {Object} options
*/
function MouseTracker(options) {
var mouseDownX,
mouseDownY,
hasDragged,
selectionMarker,
zoomType = optionsChart.zoomType,
zoomX = /x/.test(zoomType),
zoomY = /y/.test(zoomType),
zoomHor = (zoomX && !inverted) || (zoomY && inverted),
zoomVert = (zoomY && !inverted) || (zoomX && inverted);
/**
* Add crossbrowser support for chartX and chartY
* @param {Object} e The event object in standard browsers
*/
function normalizeMouseEvent(e) {
var ePos,
pageZoomFix = isWebKit &&
doc.width / doc.body.scrollWidth -
1, // #224, #348
chartPosLeft,
chartPosTop,
chartX,
chartY;
// common IE normalizing
e = e || win.event;
if (!e.target) {
e.target = e.srcElement;
}
// jQuery only copies over some properties. IE needs e.x and iOS needs touches.
if (e.originalEvent) {
e = e.originalEvent;
}
// The same for MooTools. It renames e.pageX to e.page.x. #445.
if (e.event) {
e = e.event;
}
// iOS
ePos = e.touches ? e.touches.item(0) : e;
// get mouse position
chartPosition = offset(container);
chartPosLeft = chartPosition.left;
chartPosTop = chartPosition.top;
// chartX and chartY
if (isIE) { // IE including IE9 that has pageX but in a different meaning
chartX = e.x;
chartY = e.y;
} else {
chartX = ePos.pageX - chartPosLeft;
chartY = ePos.pageY - chartPosTop;
}
// correct for page zoom bug in WebKit
if (pageZoomFix) {
chartX += mathRound((pageZoomFix + 1) * chartPosLeft - chartPosLeft);
chartY += mathRound((pageZoomFix + 1) * chartPosTop - chartPosTop);
}
return extend(e, {
chartX: chartX,
chartY: chartY
});
}
/**
* Get the click position in terms of axis values.
*
* @param {Object} e A mouse event
*/
function getMouseCoordinates(e) {
var coordinates = {
xAxis: [],
yAxis: []
};
each(axes, function (axis) {
var translate = axis.translate,
isXAxis = axis.isXAxis,
isHorizontal = inverted ? !isXAxis : isXAxis;
coordinates[isXAxis ? 'xAxis' : 'yAxis'].push({
axis: axis,
value: translate(
isHorizontal ?
e.chartX - plotLeft :
plotHeight - e.chartY + plotTop,
true
)
});
});
return coordinates;
}
/**
* With line type charts with a single tracker, get the point closest to the mouse
*/
function onmousemove(e) {
var point,
points,
hoverPoint = chart.hoverPoint,
hoverSeries = chart.hoverSeries,
i,
j,
distance = chartWidth,
index = inverted ? e.chartY : e.chartX - plotLeft; // wtf?
// shared tooltip
if (tooltip && options.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) {
points = [];
// loop over all series and find the ones with points closest to the mouse
i = series.length;
for (j = 0; j < i; j++) {
if (series[j].visible &&
series[j].options.enableMouseTracking !== false &&
!series[j].noSharedTooltip && series[j].tooltipPoints.length) {
point = series[j].tooltipPoints[index];
point._dist = mathAbs(index - point.plotX);
distance = mathMin(distance, point._dist);
points.push(point);
}
}
// remove furthest points
i = points.length;
while (i--) {
if (points[i]._dist > distance) {
points.splice(i, 1);
}
}
// refresh the tooltip if necessary
if (points.length && (points[0].plotX !== hoverX)) {
tooltip.refresh(points);
hoverX = points[0].plotX;
}
}
// separate tooltip and general mouse events
if (hoverSeries && hoverSeries.tracker) { // only use for line-type series with common tracker
// get the point
point = hoverSeries.tooltipPoints[index];
// a new point is hovered, refresh the tooltip
if (point && point !== hoverPoint) {
// trigger the events
point.onMouseOver();
}
}
}
/**
* Reset the tracking by hiding the tooltip, the hover series state and the hover point
*/
function resetTracker() {
var hoverSeries = chart.hoverSeries,
hoverPoint = chart.hoverPoint;
if (hoverPoint) {
hoverPoint.onMouseOut();
}
if (hoverSeries) {
hoverSeries.onMouseOut();
}
if (tooltip) {
tooltip.hide();
tooltip.hideCrosshairs();
}
hoverX = null;
}
/**
* Mouse up or outside the plot area
*/
function drop() {
if (selectionMarker) {
var selectionData = {
xAxis: [],
yAxis: []
},
selectionBox = selectionMarker.getBBox(),
selectionLeft = selectionBox.x - plotLeft,
selectionTop = selectionBox.y - plotTop;
// a selection has been made
if (hasDragged) {
// record each axis' min and max
each(axes, function (axis) {
if (axis.options.zoomEnabled !== false) {
var translate = axis.translate,
isXAxis = axis.isXAxis,
isHorizontal = inverted ? !isXAxis : isXAxis,
selectionMin = translate(
isHorizontal ?
selectionLeft :
plotHeight - selectionTop - selectionBox.height,
true,
0,
0,
1
),
selectionMax = translate(
isHorizontal ?
selectionLeft + selectionBox.width :
plotHeight - selectionTop,
true,
0,
0,
1
);
selectionData[isXAxis ? 'xAxis' : 'yAxis'].push({
axis: axis,
min: mathMin(selectionMin, selectionMax), // for reversed axes,
max: mathMax(selectionMin, selectionMax)
});
}
});
fireEvent(chart, 'selection', selectionData, zoom);
}
selectionMarker = selectionMarker.destroy();
}
css(container, { cursor: 'auto' });
chart.mouseIsDown = mouseIsDown = hasDragged = false;
removeEvent(doc, hasTouch ? 'touchend' : 'mouseup', drop);
}
/**
* Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea.
*/
function hideTooltipOnMouseMove(e) {
var pageX = defined(e.pageX) ? e.pageX : e.page.x, // In mootools the event is wrapped and the page x/y position is named e.page.x
pageY = defined(e.pageX) ? e.pageY : e.page.y; // Ref: http://mootools.net/docs/core/Types/DOMEvent
if (chartPosition &&
!isInsidePlot(pageX - chartPosition.left - plotLeft,
pageY - chartPosition.top - plotTop)) {
resetTracker();
}
}
/**
* When mouse leaves the container, hide the tooltip.
*/
function hideTooltipOnMouseLeave() {
resetTracker();
chartPosition = null; // also reset the chart position, used in #149 fix
}
/**
* Set the JS events on the container element
*/
function setDOMEvents() {
var lastWasOutsidePlot = true;
/*
* Record the starting position of a dragoperation
*/
container.onmousedown = function (e) {
e = normalizeMouseEvent(e);
// issue #295, dragging not always working in Firefox
if (!hasTouch && e.preventDefault) {
e.preventDefault();
}
// record the start position
chart.mouseIsDown = mouseIsDown = true;
mouseDownX = e.chartX;
mouseDownY = e.chartY;
addEvent(doc, hasTouch ? 'touchend' : 'mouseup', drop);
};
// The mousemove, touchmove and touchstart event handler
var mouseMove = function (e) {
// let the system handle multitouch operations like two finger scroll
// and pinching
if (e && e.touches && e.touches.length > 1) {
return;
}
// normalize
e = normalizeMouseEvent(e);
if (!hasTouch) { // not for touch devices
e.returnValue = false;
}
var chartX = e.chartX,
chartY = e.chartY,
isOutsidePlot = !isInsidePlot(chartX - plotLeft, chartY - plotTop);
// on touch devices, only trigger click if a handler is defined
if (hasTouch && e.type === 'touchstart') {
if (attr(e.target, 'isTracker')) {
if (!chart.runTrackerClick) {
e.preventDefault();
}
} else if (!runChartClick && !isOutsidePlot) {
e.preventDefault();
}
}
// cancel on mouse outside
if (isOutsidePlot) {
/*if (!lastWasOutsidePlot) {
// reset the tracker
resetTracker();
}*/
// drop the selection if any and reset mouseIsDown and hasDragged
//drop();
if (chartX < plotLeft) {
chartX = plotLeft;
} else if (chartX > plotLeft + plotWidth) {
chartX = plotLeft + plotWidth;
}
if (chartY < plotTop) {
chartY = plotTop;
} else if (chartY > plotTop + plotHeight) {
chartY = plotTop + plotHeight;
}
}
if (mouseIsDown && e.type !== 'touchstart') { // make selection
// determine if the mouse has moved more than 10px
hasDragged = Math.sqrt(
Math.pow(mouseDownX - chartX, 2) +
Math.pow(mouseDownY - chartY, 2)
);
if (hasDragged > 10) {
var clickedInside = isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop),
hoverPoints = chart.hoverPoints;
// make a selection
if (hasCartesianSeries && (zoomX || zoomY) && clickedInside) {
if (!selectionMarker) {
selectionMarker = renderer.rect(
plotLeft,
plotTop,
zoomHor ? 1 : plotWidth,
zoomVert ? 1 : plotHeight,
0
)
.attr({
fill: optionsChart.selectionMarkerFill || 'rgba(69,114,167,0.25)',
zIndex: 7
})
.add();
}
}
// adjust the width of the selection marker
if (selectionMarker && zoomHor) {
var xSize = chartX - mouseDownX;
selectionMarker.attr({
width: mathAbs(xSize),
x: (xSize > 0 ? 0 : xSize) + mouseDownX
});
}
// adjust the height of the selection marker
if (selectionMarker && zoomVert) {
var ySize = chartY - mouseDownY;
selectionMarker.attr({
height: mathAbs(ySize),
y: (ySize > 0 ? 0 : ySize) + mouseDownY
});
}
// panning
if (clickedInside && !selectionMarker && optionsChart.panning) {
var xAxis = chart.xAxis[0],
halfPointRange = xAxis.pointRange / 2,
extremes = xAxis.getExtremes(),
newMin = xAxis.translate(mouseDownX - chartX, true) + halfPointRange,
newMax = xAxis.translate(mouseDownX + plotWidth - chartX, true) - halfPointRange;
// remove active points for shared tooltip
if (hoverPoints) {
each(hoverPoints, function (point) {
point.setState();
});
}
if (newMin > mathMin(extremes.dataMin, extremes.min) && newMax < mathMax(extremes.dataMax, extremes.max)) {
xAxis.setExtremes(newMin, newMax, true, false);
}
mouseDownX = chartX;
css(container, { cursor: 'move' });
}
}
} else if (!isOutsidePlot) {
// show the tooltip
onmousemove(e);
}
lastWasOutsidePlot = isOutsidePlot;
// when outside plot, allow touch-drag by returning true
return isOutsidePlot || !hasCartesianSeries;
};
/*
* When the mouse enters the container, run mouseMove
*/
container.onmousemove = mouseMove;
/*
* When the mouse leaves the container, hide the tracking (tooltip).
*/
addEvent(container, 'mouseleave', hideTooltipOnMouseLeave);
// issue #149 workaround
// The mouseleave event above does not always fire. Whenever the mouse is moving
// outside the plotarea, hide the tooltip
addEvent(doc, 'mousemove', hideTooltipOnMouseMove);
container.ontouchstart = function (e) {
// For touch devices, use touchmove to zoom
if (zoomX || zoomY) {
container.onmousedown(e);
}
// Show tooltip and prevent the lower mouse pseudo event
mouseMove(e);
};
/*
* Allow dragging the finger over the chart to read the values on touch
* devices
*/
container.ontouchmove = mouseMove;
/*
* Allow dragging the finger over the chart to read the values on touch
* devices
*/
container.ontouchend = function () {
if (hasDragged) {
resetTracker();
}
};
// MooTools 1.2.3 doesn't fire this in IE when using addEvent
container.onclick = function (e) {
var hoverPoint = chart.hoverPoint;
e = normalizeMouseEvent(e);
e.cancelBubble = true; // IE specific
if (!hasDragged) {
if (hoverPoint && attr(e.target, 'isTracker')) {
var plotX = hoverPoint.plotX,
plotY = hoverPoint.plotY;
// add page position info
extend(hoverPoint, {
pageX: chartPosition.left + plotLeft +
(inverted ? plotWidth - plotY : plotX),
pageY: chartPosition.top + plotTop +
(inverted ? plotHeight - plotX : plotY)
});
// the series click event
fireEvent(hoverPoint.series, 'click', extend(e, {
point: hoverPoint
}));
// the point click event
hoverPoint.firePointEvent('click', e);
} else {
extend(e, getMouseCoordinates(e));
// fire a click event in the chart
if (isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) {
fireEvent(chart, 'click', e);
}
}
}
// reset mouseIsDown and hasDragged
hasDragged = false;
};
}
/**
* Destroys the MouseTracker object and disconnects DOM events.
*/
function destroy() {
// Destroy the tracker group element
if (chart.trackerGroup) {
chart.trackerGroup = trackerGroup = chart.trackerGroup.destroy();
}
removeEvent(container, 'mouseleave', hideTooltipOnMouseLeave);
removeEvent(doc, 'mousemove', hideTooltipOnMouseMove);
container.onclick = container.onmousedown = container.onmousemove = container.ontouchstart = container.ontouchend = container.ontouchmove = null;
}
/**
* Create the image map that listens for mouseovers
*/
placeTrackerGroup = function () {
// first create - plot positions is not final at this stage
if (!trackerGroup) {
chart.trackerGroup = trackerGroup = renderer.g('tracker')
.attr({ zIndex: 9 })
.add();
// then position - this happens on load and after resizing and changing
// axis or box positions
} else {
trackerGroup.translate(plotLeft, plotTop);
if (inverted) {
trackerGroup.attr({
width: chart.plotWidth,
height: chart.plotHeight
}).invert();
}
}
};
// Run MouseTracker
placeTrackerGroup();
if (options.enabled) {
chart.tooltip = tooltip = Tooltip(options);
}
setDOMEvents();
// set the fixed interval ticking for the smooth tooltip
tooltipInterval = setInterval(function () {
if (tooltipTick) {
tooltipTick();
}
}, 32);
// expose properties
extend(this, {
zoomX: zoomX,
zoomY: zoomY,
resetTracker: resetTracker,
normalizeMouseEvent: normalizeMouseEvent,
destroy: destroy
});
}
/**
* The overview of the chart's series
*/
var Legend = function () {
var options = chart.options.legend;
if (!options.enabled) {
return;
}
var horizontal = options.layout === 'horizontal',
symbolWidth = options.symbolWidth,
symbolPadding = options.symbolPadding,
allItems,
style = options.style,
itemStyle = options.itemStyle,
itemHoverStyle = options.itemHoverStyle,
itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle),
padding = pInt(style.padding),
y = 18,
initialItemX = 4 + padding + symbolWidth + symbolPadding,
itemX,
itemY,
lastItemY,
itemHeight = 0,
box,
legendBorderWidth = options.borderWidth,
legendBackgroundColor = options.backgroundColor,
legendGroup,
offsetWidth,
widthOption = options.width,
series = chart.series,
reversedLegend = options.reversed;
/**
* Set the colors for the legend item
* @param {Object} item A Series or Point instance
* @param {Object} visible Dimmed or colored
*/
function colorizeItem(item, visible) {
var legendItem = item.legendItem,
legendLine = item.legendLine,
legendSymbol = item.legendSymbol,
hiddenColor = itemHiddenStyle.color,
textColor = visible ? options.itemStyle.color : hiddenColor,
symbolColor = visible ? item.color : hiddenColor;
if (legendItem) {
legendItem.css({ fill: textColor });
}
if (legendLine) {
legendLine.attr({ stroke: symbolColor });
}
if (legendSymbol) {
legendSymbol.attr({
stroke: symbolColor,
fill: symbolColor
});
}
}
/**
* Position the legend item
* @param {Object} item A Series or Point instance
* @param {Object} visible Dimmed or colored
*/
function positionItem(item, itemX, itemY) {
var legendItem = item.legendItem,
legendLine = item.legendLine,
legendSymbol = item.legendSymbol,
checkbox = item.checkbox;
if (legendItem) {
legendItem.attr({
x: itemX,
y: itemY
});
}
if (legendLine) {
legendLine.translate(itemX, itemY - 4);
}
if (legendSymbol) {
legendSymbol.attr({
x: itemX + legendSymbol.xOff,
y: itemY + legendSymbol.yOff
});
}
if (checkbox) {
checkbox.x = itemX;
checkbox.y = itemY;
}
}
/**
* Destroy a single legend item
* @param {Object} item The series or point
*/
function destroyItem(item) {
var checkbox = item.checkbox;
// destroy SVG elements
each(['legendItem', 'legendLine', 'legendSymbol'], function (key) {
if (item[key]) {
item[key].destroy();
}
});
if (checkbox) {
discardElement(item.checkbox);
}
}
/**
* Destroys the legend.
*/
function destroy() {
if (box) {
box = box.destroy();
}
if (legendGroup) {
legendGroup = legendGroup.destroy();
}
}
/**
* Position the checkboxes after the width is determined
*/
function positionCheckboxes() {
each(allItems, function (item) {
var checkbox = item.checkbox,
alignAttr = legendGroup.alignAttr;
if (checkbox) {
css(checkbox, {
left: (alignAttr.translateX + item.legendItemWidth + checkbox.x - 40) + PX,
top: (alignAttr.translateY + checkbox.y - 11) + PX
});
}
});
}
/**
* Render a single specific legend item
* @param {Object} item A series or point
*/
function renderItem(item) {
var bBox,
itemWidth,
legendSymbol,
symbolX,
symbolY,
simpleSymbol,
radius,
li = item.legendItem,
series = item.series || item,
itemOptions = series.options,
strokeWidth = (itemOptions && itemOptions.borderWidth) || 0;
if (!li) { // generate it once, later move it
// let these series types use a simple symbol
simpleSymbol = /^(bar|pie|area|column)$/.test(series.type);
// generate the list item text
item.legendItem = li = renderer.text(
options.labelFormatter.call(item),
0,
0
)
.css(item.visible ? itemStyle : itemHiddenStyle)
.on('mouseover', function () {
item.setState(HOVER_STATE);
li.css(itemHoverStyle);
})
.on('mouseout', function () {
li.css(item.visible ? itemStyle : itemHiddenStyle);
item.setState();
})
.on('click', function () {
var strLegendItemClick = 'legendItemClick',
fnLegendItemClick = function () {
item.setVisible();
};
// click the name or symbol
if (item.firePointEvent) { // point
item.firePointEvent(strLegendItemClick, null, fnLegendItemClick);
} else {
fireEvent(item, strLegendItemClick, null, fnLegendItemClick);
}
})
.attr({ zIndex: 2 })
.add(legendGroup);
// draw the line
if (!simpleSymbol && itemOptions && itemOptions.lineWidth) {
var attrs = {
'stroke-width': itemOptions.lineWidth,
zIndex: 2
};
if (itemOptions.dashStyle) {
attrs.dashstyle = itemOptions.dashStyle;
}
item.legendLine = renderer.path([
M,
-symbolWidth - symbolPadding,
0,
L,
-symbolPadding,
0
])
.attr(attrs)
.add(legendGroup);
}
// draw a simple symbol
if (simpleSymbol) { // bar|pie|area|column
legendSymbol = renderer.rect(
(symbolX = -symbolWidth - symbolPadding),
(symbolY = -11),
symbolWidth,
12,
2
).attr({
//'stroke-width': 0,
zIndex: 3
}).add(legendGroup);
} else if (itemOptions && itemOptions.marker && itemOptions.marker.enabled) { // draw the marker
radius = itemOptions.marker.radius;
legendSymbol = renderer.symbol(
item.symbol,
(symbolX = -symbolWidth / 2 - symbolPadding - radius),
(symbolY = -4 - radius),
2 * radius,
2 * radius
)
.attr(item.pointAttr[NORMAL_STATE])
.attr({ zIndex: 3 })
.add(legendGroup);
}
if (legendSymbol) {
legendSymbol.xOff = symbolX + (strokeWidth % 2 / 2);
legendSymbol.yOff = symbolY + (strokeWidth % 2 / 2);
}
item.legendSymbol = legendSymbol;
// colorize the items
colorizeItem(item, item.visible);
// add the HTML checkbox on top
if (itemOptions && itemOptions.showCheckbox) {
item.checkbox = createElement('input', {
type: 'checkbox',
checked: item.selected,
defaultChecked: item.selected // required by IE7
}, options.itemCheckboxStyle, container);
addEvent(item.checkbox, 'click', function (event) {
var target = event.target;
fireEvent(item, 'checkboxClick', {
checked: target.checked
},
function () {
item.select();
}
);
});
}
}
// calculate the positions for the next line
bBox = li.getBBox();
itemWidth = item.legendItemWidth =
options.itemWidth || symbolWidth + symbolPadding + bBox.width + padding;
itemHeight = bBox.height;
// if the item exceeds the width, start a new line
if (horizontal && itemX - initialItemX + itemWidth >
(widthOption || (chartWidth - 2 * padding - initialItemX))) {
itemX = initialItemX;
itemY += itemHeight;
}
lastItemY = itemY;
// position the newly generated or reordered items
positionItem(item, itemX, itemY);
// advance
if (horizontal) {
itemX += itemWidth;
} else {
itemY += itemHeight;
}
// the width of the widest item
offsetWidth = widthOption || mathMax(
horizontal ? itemX - initialItemX : itemWidth,
offsetWidth
);
// add it all to an array to use below
//allItems.push(item);
}
/**
* Render the legend. This method can be called both before and after
* chart.render. If called after, it will only rearrange items instead
* of creating new ones.
*/
function renderLegend() {
itemX = initialItemX;
itemY = y;
offsetWidth = 0;
lastItemY = 0;
if (!legendGroup) {
legendGroup = renderer.g('legend')
.attr({ zIndex: 10 }) // in front of trackers, #414
.add();
}
// add each series or point
allItems = [];
each(series, function (serie) {
var seriesOptions = serie.options;
if (!seriesOptions.showInLegend) {
return;
}
// use points or series for the legend item depending on legendType
allItems = allItems.concat(seriesOptions.legendType === 'point' ?
serie.data :
serie
);
});
// sort by legendIndex
stableSort(allItems, function (a, b) {
return (a.options.legendIndex || 0) - (b.options.legendIndex || 0);
});
// reversed legend
if (reversedLegend) {
allItems.reverse();
}
// render the items
each(allItems, renderItem);
// Draw the border
legendWidth = widthOption || offsetWidth;
legendHeight = lastItemY - y + itemHeight;
if (legendBorderWidth || legendBackgroundColor) {
legendWidth += 2 * padding;
legendHeight += 2 * padding;
if (!box) {
box = renderer.rect(
0,
0,
legendWidth,
legendHeight,
options.borderRadius,
legendBorderWidth || 0
).attr({
stroke: options.borderColor,
'stroke-width': legendBorderWidth || 0,
fill: legendBackgroundColor || NONE
})
.add(legendGroup)
.shadow(options.shadow);
box.isNew = true;
} else if (legendWidth > 0 && legendHeight > 0) {
box[box.isNew ? 'attr' : 'animate'](
box.crisp(null, null, null, legendWidth, legendHeight)
);
box.isNew = false;
}
// hide the border if no items
box[allItems.length ? 'show' : 'hide']();
}
// 1.x compatibility: positioning based on style
var props = ['left', 'right', 'top', 'bottom'],
prop,
i = 4;
while (i--) {
prop = props[i];
if (style[prop] && style[prop] !== 'auto') {
options[i < 2 ? 'align' : 'verticalAlign'] = prop;
options[i < 2 ? 'x' : 'y'] = pInt(style[prop]) * (i % 2 ? -1 : 1);
}
}
if (allItems.length) {
legendGroup.align(extend(options, {
width: legendWidth,
height: legendHeight
}), true, spacingBox);
}
if (!isResizing) {
positionCheckboxes();
}
}
// run legend
renderLegend();
// move checkboxes
addEvent(chart, 'endResize', positionCheckboxes);
// expose
return {
colorizeItem: colorizeItem,
destroyItem: destroyItem,
renderLegend: renderLegend,
destroy: destroy
};
};
/**
* Initialize an individual series, called internally before render time
*/
function initSeries(options) {
var type = options.type || optionsChart.type || optionsChart.defaultSeriesType,
typeClass = seriesTypes[type],
serie,
hasRendered = chart.hasRendered;
// an inverted chart can't take a column series and vice versa
if (hasRendered) {
if (inverted && type === 'column') {
typeClass = seriesTypes.bar;
} else if (!inverted && type === 'bar') {
typeClass = seriesTypes.column;
}
}
serie = new typeClass();
serie.init(chart, options);
// set internal chart properties
if (!hasRendered && serie.inverted) {
inverted = true;
}
if (serie.isCartesian) {
hasCartesianSeries = serie.isCartesian;
}
series.push(serie);
return serie;
}
/**
* Add a series dynamically after time
*
* @param {Object} options The config options
* @param {Boolean} redraw Whether to redraw the chart after adding. Defaults to true.
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*
* @return {Object} series The newly created series object
*/
function addSeries(options, redraw, animation) {
var series;
if (options) {
setAnimation(animation, chart);
redraw = pick(redraw, true); // defaults to true
fireEvent(chart, 'addSeries', { options: options }, function () {
series = initSeries(options);
series.isDirty = true;
chart.isDirtyLegend = true; // the series array is out of sync with the display
if (redraw) {
chart.redraw();
}
});
}
return series;
}
/**
* Check whether a given point is within the plot area
*
* @param {Number} x Pixel x relative to the plot area
* @param {Number} y Pixel y relative to the plot area
*/
isInsidePlot = function (x, y) {
return x >= 0 &&
x <= plotWidth &&
y >= 0 &&
y <= plotHeight;
};
/**
* Adjust all axes tick amounts
*/
function adjustTickAmounts() {
if (optionsChart.alignTicks !== false) {
each(axes, function (axis) {
axis.adjustTickAmount();
});
}
maxTicks = null;
}
/**
* Redraw legend, axes or series based on updated data
*
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*/
function redraw(animation) {
var redrawLegend = chart.isDirtyLegend,
hasStackedSeries,
isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed?
seriesLength = series.length,
i = seriesLength,
clipRect = chart.clipRect,
serie;
setAnimation(animation, chart);
// link stacked series
while (i--) {
serie = series[i];
if (serie.isDirty && serie.options.stacking) {
hasStackedSeries = true;
break;
}
}
if (hasStackedSeries) { // mark others as dirty
i = seriesLength;
while (i--) {
serie = series[i];
if (serie.options.stacking) {
serie.isDirty = true;
}
}
}
// handle updated data in the series
each(series, function (serie) {
if (serie.isDirty) { // prepare the data so axis can read it
if (serie.options.legendType === 'point') {
redrawLegend = true;
}
}
});
// handle added or removed series
if (redrawLegend && legend.renderLegend) { // series or pie points are added or removed
// draw legend graphics
legend.renderLegend();
chart.isDirtyLegend = false;
}
if (hasCartesianSeries) {
if (!isResizing) {
// reset maxTicks
maxTicks = null;
// set axes scales
each(axes, function (axis) {
axis.setScale();
});
}
adjustTickAmounts();
getMargins();
// redraw axes
each(axes, function (axis) {
if (axis.isDirty) {
axis.redraw();
//isDirtyBox = true; // force redrawing subsequent axes
}
});
}
// the plot areas size has changed
if (isDirtyBox) {
drawChartBox();
placeTrackerGroup();
// move clip rect
if (clipRect) {
stop(clipRect);
clipRect.animate({ // for chart resize
width: chart.plotSizeX,
height: chart.plotSizeY + 1
});
}
}
// redraw affected series
each(series, function (serie) {
if (serie.isDirty && serie.visible &&
(!serie.isCartesian || serie.xAxis)) { // issue #153
serie.redraw();
}
});
// hide tooltip and hover states
if (tracker && tracker.resetTracker) {
tracker.resetTracker();
}
// fire the event
fireEvent(chart, 'redraw'); // jQuery breaks this when calling it from addEvent. Overwrites chart.redraw
}
/**
* Dim the chart and show a loading text or symbol
* @param {String} str An optional text to show in the loading label instead of the default one
*/
function showLoading(str) {
var loadingOptions = options.loading;
// create the layer at the first call
if (!loadingDiv) {
loadingDiv = createElement(DIV, {
className: PREFIX + 'loading'
}, extend(loadingOptions.style, {
left: plotLeft + PX,
top: plotTop + PX,
width: plotWidth + PX,
height: plotHeight + PX,
zIndex: 10,
display: NONE
}), container);
loadingSpan = createElement(
'span',
null,
loadingOptions.labelStyle,
loadingDiv
);
}
// update text
loadingSpan.innerHTML = str || options.lang.loading;
// show it
if (!loadingShown) {
css(loadingDiv, { opacity: 0, display: '' });
animate(loadingDiv, {
opacity: loadingOptions.style.opacity
}, {
duration: loadingOptions.showDuration || 0
});
loadingShown = true;
}
}
/**
* Hide the loading layer
*/
function hideLoading() {
if (loadingDiv) {
animate(loadingDiv, {
opacity: 0
}, {
duration: options.loading.hideDuration || 100,
complete: function () {
css(loadingDiv, { display: NONE });
}
});
}
loadingShown = false;
}
/**
* Get an axis, series or point object by id.
* @param id {String} The id as given in the configuration options
*/
function get(id) {
var i,
j,
points;
// search axes
for (i = 0; i < axes.length; i++) {
if (axes[i].options.id === id) {
return axes[i];
}
}
// search series
for (i = 0; i < series.length; i++) {
if (series[i].options.id === id) {
return series[i];
}
}
// search points
for (i = 0; i < series.length; i++) {
points = series[i].points;
for (j = 0; j < points.length; j++) {
if (points[j].id === id) {
return points[j];
}
}
}
return null;
}
/**
* Create the Axis instances based on the config options
*/
function getAxes() {
var xAxisOptions = options.xAxis || {},
yAxisOptions = options.yAxis || {},
optionsArray,
axis;
// make sure the options are arrays and add some members
xAxisOptions = splat(xAxisOptions);
each(xAxisOptions, function (axis, i) {
axis.index = i;
axis.isX = true;
});
yAxisOptions = splat(yAxisOptions);
each(yAxisOptions, function (axis, i) {
axis.index = i;
});
// concatenate all axis options into one array
optionsArray = xAxisOptions.concat(yAxisOptions);
each(optionsArray, function (axisOptions) {
axis = new Axis(axisOptions);
});
adjustTickAmounts();
}
/**
* Get the currently selected points from all series
*/
function getSelectedPoints() {
var points = [];
each(series, function (serie) {
points = points.concat(grep(serie.points, function (point) {
return point.selected;
}));
});
return points;
}
/**
* Get the currently selected series
*/
function getSelectedSeries() {
return grep(series, function (serie) {
return serie.selected;
});
}
/**
* Zoom out to 1:1
*/
zoomOut = function () {
fireEvent(chart, 'selection', { resetSelection: true }, zoom);
chart.toolbar.remove('zoom');
};
/**
* Zoom into a given portion of the chart given by axis coordinates
* @param {Object} event
*/
zoom = function (event) {
// add button to reset selection
var lang = defaultOptions.lang,
animate = chart.pointCount < 100;
if (chart.resetZoomEnabled !== false) { // hook for Stock charts etc.
chart.toolbar.add('zoom', lang.resetZoom, lang.resetZoomTitle, zoomOut);
}
// if zoom is called with no arguments, reset the axes
if (!event || event.resetSelection) {
each(axes, function (axis) {
if (axis.options.zoomEnabled !== false) {
axis.setExtremes(null, null, true, animate);
}
});
} else { // else, zoom in on all axes
each(event.xAxis.concat(event.yAxis), function (axisData) {
var axis = axisData.axis;
// don't zoom more than minRange
if (chart.tracker[axis.isXAxis ? 'zoomX' : 'zoomY']) {
axis.setExtremes(axisData.min, axisData.max, true, animate);
}
});
}
};
/**
* Show the title and subtitle of the chart
*
* @param titleOptions {Object} New title options
* @param subtitleOptions {Object} New subtitle options
*
*/
function setTitle(titleOptions, subtitleOptions) {
chartTitleOptions = merge(options.title, titleOptions);
chartSubtitleOptions = merge(options.subtitle, subtitleOptions);
// add title and subtitle
each([
['title', titleOptions, chartTitleOptions],
['subtitle', subtitleOptions, chartSubtitleOptions]
], function (arr) {
var name = arr[0],
title = chart[name],
titleOptions = arr[1],
chartTitleOptions = arr[2];
if (title && titleOptions) {
title = title.destroy(); // remove old
}
if (chartTitleOptions && chartTitleOptions.text && !title) {
chart[name] = renderer.text(
chartTitleOptions.text,
0,
0,
chartTitleOptions.useHTML
)
.attr({
align: chartTitleOptions.align,
'class': PREFIX + name,
zIndex: 1
})
.css(chartTitleOptions.style)
.add()
.align(chartTitleOptions, false, spacingBox);
}
});
}
/**
* Get chart width and height according to options and container size
*/
function getChartSize() {
containerWidth = (renderToClone || renderTo).offsetWidth;
containerHeight = (renderToClone || renderTo).offsetHeight;
chart.chartWidth = chartWidth = optionsChart.width || containerWidth || 600;
chart.chartHeight = chartHeight = optionsChart.height ||
// the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7:
(containerHeight > 19 ? containerHeight : 400);
}
/**
* Get the containing element, determine the size and create the inner container
* div to hold the chart
*/
function getContainer() {
renderTo = optionsChart.renderTo;
containerId = PREFIX + idCounter++;
if (isString(renderTo)) {
renderTo = doc.getElementById(renderTo);
}
// remove previous chart
renderTo.innerHTML = '';
// If the container doesn't have an offsetWidth, it has or is a child of a node
// that has display:none. We need to temporarily move it out to a visible
// state to determine the size, else the legend and tooltips won't render
// properly
if (!renderTo.offsetWidth) {
renderToClone = renderTo.cloneNode(0);
css(renderToClone, {
position: ABSOLUTE,
top: '-9999px',
display: ''
});
doc.body.appendChild(renderToClone);
}
// get the width and height
getChartSize();
// create the inner container
chart.container = container = createElement(DIV, {
className: PREFIX + 'container' +
(optionsChart.className ? ' ' + optionsChart.className : ''),
id: containerId
}, extend({
position: RELATIVE,
overflow: HIDDEN, // needed for context menu (avoid scrollbars) and
// content overflow in IE
width: chartWidth + PX,
height: chartHeight + PX,
textAlign: 'left',
lineHeight: 'normal' // #427
}, optionsChart.style),
renderToClone || renderTo
);
chart.renderer = renderer =
optionsChart.forExport ? // force SVG, used for SVG export
new SVGRenderer(container, chartWidth, chartHeight, true) :
new Renderer(container, chartWidth, chartHeight);
// Issue 110 workaround:
// In Firefox, if a div is positioned by percentage, its pixel position may land
// between pixels. The container itself doesn't display this, but an SVG element
// inside this container will be drawn at subpixel precision. In order to draw
// sharp lines, this must be compensated for. This doesn't seem to work inside
// iframes though (like in jsFiddle).
var subPixelFix, rect;
if (isFirefox && container.getBoundingClientRect) {
subPixelFix = function () {
css(container, { left: 0, top: 0 });
rect = container.getBoundingClientRect();
css(container, {
left: (-(rect.left - pInt(rect.left))) + PX,
top: (-(rect.top - pInt(rect.top))) + PX
});
};
// run the fix now
subPixelFix();
// run it on resize
addEvent(win, 'resize', subPixelFix);
// remove it on chart destroy
addEvent(chart, 'destroy', function () {
removeEvent(win, 'resize', subPixelFix);
});
}
}
/**
* Calculate margins by rendering axis labels in a preliminary position. Title,
* subtitle and legend have already been rendered at this stage, but will be
* moved into their final positions
*/
getMargins = function () {
var legendOptions = options.legend,
legendMargin = pick(legendOptions.margin, 10),
legendX = legendOptions.x,
legendY = legendOptions.y,
align = legendOptions.align,
verticalAlign = legendOptions.verticalAlign,
titleOffset;
resetMargins();
// adjust for title and subtitle
if ((chart.title || chart.subtitle) && !defined(optionsMarginTop)) {
titleOffset = mathMax(
(chart.title && !chartTitleOptions.floating && !chartTitleOptions.verticalAlign && chartTitleOptions.y) || 0,
(chart.subtitle && !chartSubtitleOptions.floating && !chartSubtitleOptions.verticalAlign && chartSubtitleOptions.y) || 0
);
if (titleOffset) {
plotTop = mathMax(plotTop, titleOffset + pick(chartTitleOptions.margin, 15) + spacingTop);
}
}
// adjust for legend
if (legendOptions.enabled && !legendOptions.floating) {
if (align === 'right') { // horizontal alignment handled first
if (!defined(optionsMarginRight)) {
marginRight = mathMax(
marginRight,
legendWidth - legendX + legendMargin + spacingRight
);
}
} else if (align === 'left') {
if (!defined(optionsMarginLeft)) {
plotLeft = mathMax(
plotLeft,
legendWidth + legendX + legendMargin + spacingLeft
);
}
} else if (verticalAlign === 'top') {
if (!defined(optionsMarginTop)) {
plotTop = mathMax(
plotTop,
legendHeight + legendY + legendMargin + spacingTop
);
}
} else if (verticalAlign === 'bottom') {
if (!defined(optionsMarginBottom)) {
marginBottom = mathMax(
marginBottom,
legendHeight - legendY + legendMargin + spacingBottom
);
}
}
}
// adjust for scroller
if (chart.extraBottomMargin) {
marginBottom += chart.extraBottomMargin;
}
if (chart.extraTopMargin) {
plotTop += chart.extraTopMargin;
}
// pre-render axes to get labels offset width
if (hasCartesianSeries) {
each(axes, function (axis) {
axis.getOffset();
});
}
if (!defined(optionsMarginLeft)) {
plotLeft += axisOffset[3];
}
if (!defined(optionsMarginTop)) {
plotTop += axisOffset[0];
}
if (!defined(optionsMarginBottom)) {
marginBottom += axisOffset[2];
}
if (!defined(optionsMarginRight)) {
marginRight += axisOffset[1];
}
setChartSize();
};
/**
* Add the event handlers necessary for auto resizing
*
*/
function initReflow() {
var reflowTimeout;
function reflow() {
var width = optionsChart.width || renderTo.offsetWidth,
height = optionsChart.height || renderTo.offsetHeight;
if (width && height) { // means container is display:none
if (width !== containerWidth || height !== containerHeight) {
clearTimeout(reflowTimeout);
reflowTimeout = setTimeout(function () {
resize(width, height, false);
}, 100);
}
containerWidth = width;
containerHeight = height;
}
}
addEvent(win, 'resize', reflow);
addEvent(chart, 'destroy', function () {
removeEvent(win, 'resize', reflow);
});
}
/**
* Fires endResize event on chart instance.
*/
function fireEndResize() {
fireEvent(chart, 'endResize', null, function () {
isResizing -= 1;
});
}
/**
* Resize the chart to a given width and height
* @param {Number} width
* @param {Number} height
* @param {Object|Boolean} animation
*/
resize = function (width, height, animation) {
var chartTitle = chart.title,
chartSubtitle = chart.subtitle;
isResizing += 1;
// set the animation for the current process
setAnimation(animation, chart);
oldChartHeight = chartHeight;
oldChartWidth = chartWidth;
if (defined(width)) {
chart.chartWidth = chartWidth = mathRound(width);
}
if (defined(height)) {
chart.chartHeight = chartHeight = mathRound(height);
}
css(container, {
width: chartWidth + PX,
height: chartHeight + PX
});
renderer.setSize(chartWidth, chartHeight, animation);
// update axis lengths for more correct tick intervals:
plotWidth = chartWidth - plotLeft - marginRight;
plotHeight = chartHeight - plotTop - marginBottom;
// handle axes
maxTicks = null;
each(axes, function (axis) {
axis.isDirty = true;
axis.setScale();
});
// make sure non-cartesian series are also handled
each(series, function (serie) {
serie.isDirty = true;
});
chart.isDirtyLegend = true; // force legend redraw
chart.isDirtyBox = true; // force redraw of plot and chart border
getMargins();
// move titles
if (chartTitle) {
chartTitle.align(null, null, spacingBox);
}
if (chartSubtitle) {
chartSubtitle.align(null, null, spacingBox);
}
redraw(animation);
oldChartHeight = null;
fireEvent(chart, 'resize');
// fire endResize and set isResizing back
// If animation is disabled, fire without delay
if (globalAnimation === false) {
fireEndResize();
} else { // else set a timeout with the animation duration
setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500);
}
};
/**
* Set the public chart properties. This is done before and after the pre-render
* to determine margin sizes
*/
setChartSize = function () {
chart.plotLeft = plotLeft = mathRound(plotLeft);
chart.plotTop = plotTop = mathRound(plotTop);
chart.plotWidth = plotWidth = mathRound(chartWidth - plotLeft - marginRight);
chart.plotHeight = plotHeight = mathRound(chartHeight - plotTop - marginBottom);
chart.plotSizeX = inverted ? plotHeight : plotWidth;
chart.plotSizeY = inverted ? plotWidth : plotHeight;
spacingBox = {
x: spacingLeft,
y: spacingTop,
width: chartWidth - spacingLeft - spacingRight,
height: chartHeight - spacingTop - spacingBottom
};
each(axes, function (axis) {
if (axis.isDirty) {
axis.setAxisSize();
}
});
};
/**
* Initial margins before auto size margins are applied
*/
resetMargins = function () {
plotTop = pick(optionsMarginTop, spacingTop);
marginRight = pick(optionsMarginRight, spacingRight);
marginBottom = pick(optionsMarginBottom, spacingBottom);
plotLeft = pick(optionsMarginLeft, spacingLeft);
axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
};
/**
* Draw the borders and backgrounds for chart and plot area
*/
drawChartBox = function () {
var chartBorderWidth = optionsChart.borderWidth || 0,
chartBackgroundColor = optionsChart.backgroundColor,
plotBackgroundColor = optionsChart.plotBackgroundColor,
plotBackgroundImage = optionsChart.plotBackgroundImage,
mgn,
plotSize = {
x: plotLeft,
y: plotTop,
width: plotWidth,
height: plotHeight
};
// Chart area
mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
if (chartBorderWidth || chartBackgroundColor) {
if (!chartBackground) {
chartBackground = renderer.rect(mgn / 2, mgn / 2, chartWidth - mgn, chartHeight - mgn,
optionsChart.borderRadius, chartBorderWidth)
.attr({
stroke: optionsChart.borderColor,
'stroke-width': chartBorderWidth,
fill: chartBackgroundColor || NONE
})
.add()
.shadow(optionsChart.shadow);
} else { // resize
chartBackground.animate(
chartBackground.crisp(null, null, null, chartWidth - mgn, chartHeight - mgn)
);
}
}
// Plot background
if (plotBackgroundColor) {
if (!plotBackground) {
plotBackground = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0)
.attr({
fill: plotBackgroundColor
})
.add()
.shadow(optionsChart.plotShadow);
} else {
plotBackground.animate(plotSize);
}
}
if (plotBackgroundImage) {
if (!plotBGImage) {
plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight)
.add();
} else {
plotBGImage.animate(plotSize);
}
}
// Plot area border
if (optionsChart.plotBorderWidth) {
if (!plotBorder) {
plotBorder = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0, optionsChart.plotBorderWidth)
.attr({
stroke: optionsChart.plotBorderColor,
'stroke-width': optionsChart.plotBorderWidth,
zIndex: 4
})
.add();
} else {
plotBorder.animate(
plotBorder.crisp(null, plotLeft, plotTop, plotWidth, plotHeight)
);
}
}
// reset
chart.isDirtyBox = false;
};
/**
* Detect whether the chart is inverted, either by setting the chart.inverted option
* or adding a bar series to the configuration options
*/
function setInverted() {
var BAR = 'bar',
isInverted = (
inverted || // it is set before
optionsChart.inverted ||
optionsChart.type === BAR || // default series type
optionsChart.defaultSeriesType === BAR // backwards compatible
),
seriesOptions = options.series,
i = seriesOptions && seriesOptions.length;
// check if a bar series is present in the config options
while (!isInverted && i--) {
if (seriesOptions[i].type === BAR) {
isInverted = true;
}
}
// set the chart property and the chart scope variable
chart.inverted = inverted = isInverted;
}
/**
* Render all graphics for the chart
*/
function render() {
var labels = options.labels,
credits = options.credits,
creditsHref;
// Title
setTitle();
// Legend
legend = chart.legend = new Legend();
// Get margins by pre-rendering axes
// set axes scales
each(axes, function (axis) {
axis.setScale();
});
getMargins();
each(axes, function (axis) {
axis.setTickPositions(true); // update to reflect the new margins
});
adjustTickAmounts();
getMargins(); // second pass to check for new labels
// Draw the borders and backgrounds
drawChartBox();
// Axes
if (hasCartesianSeries) {
each(axes, function (axis) {
axis.render();
});
}
// The series
if (!chart.seriesGroup) {
chart.seriesGroup = renderer.g('series-group')
.attr({ zIndex: 3 })
.add();
}
each(series, function (serie) {
serie.translate();
serie.setTooltipPoints();
serie.render();
});
// Labels
if (labels.items) {
each(labels.items, function () {
var style = extend(labels.style, this.style),
x = pInt(style.left) + plotLeft,
y = pInt(style.top) + plotTop + 12;
// delete to prevent rewriting in IE
delete style.left;
delete style.top;
renderer.text(
this.html,
x,
y
)
.attr({ zIndex: 2 })
.css(style)
.add();
});
}
// Toolbar (don't redraw)
if (!chart.toolbar) {
chart.toolbar = Toolbar();
}
// Credits
if (credits.enabled && !chart.credits) {
creditsHref = credits.href;
chart.credits = renderer.text(
credits.text,
0,
0
)
.on('click', function () {
if (creditsHref) {
location.href = creditsHref;
}
})
.attr({
align: credits.position.align,
zIndex: 8
})
.css(credits.style)
.add()
.align(credits.position);
}
placeTrackerGroup();
// Set flag
chart.hasRendered = true;
// If the chart was rendered outside the top container, put it back in
if (renderToClone) {
renderTo.appendChild(container);
discardElement(renderToClone);
//updatePosition(container);
}
}
/**
* Clean up memory usage
*/
function destroy() {
var i,
parentNode = container && container.parentNode;
// If the chart is destroyed already, do nothing.
// This will happen if if a script invokes chart.destroy and
// then it will be called again on win.unload
if (chart === null) {
return;
}
// fire the chart.destoy event
fireEvent(chart, 'destroy');
// remove events
removeEvent(chart);
// ==== Destroy collections:
// Destroy axes
i = axes.length;
while (i--) {
axes[i] = axes[i].destroy();
}
// Destroy each series
i = series.length;
while (i--) {
series[i] = series[i].destroy();
}
// ==== Destroy chart properties:
each(['title', 'subtitle', 'seriesGroup', 'clipRect', 'credits', 'tracker', 'scroller', 'rangeSelector'], function (name) {
var prop = chart[name];
if (prop) {
chart[name] = prop.destroy();
}
});
// ==== Destroy local variables:
each([chartBackground, plotBorder, plotBackground, legend, tooltip, renderer, tracker], function (obj) {
if (obj && obj.destroy) {
obj.destroy();
}
});
chartBackground = plotBorder = plotBackground = legend = tooltip = renderer = tracker = null;
// remove container and all SVG
if (container) { // can break in IE when destroyed before finished loading
container.innerHTML = '';
removeEvent(container);
if (parentNode) {
discardElement(container);
}
// IE6 leak
container = null;
}
// memory and CPU leak
clearInterval(tooltipInterval);
// clean it all up
for (i in chart) {
delete chart[i];
}
chart = null;
options = null;
}
/**
* Prepare for first rendering after all data are loaded
*/
function firstRender() {
// VML namespaces can't be added until after complete. Listening
// for Perini's doScroll hack is not enough.
var ONREADYSTATECHANGE = 'onreadystatechange',
COMPLETE = 'complete';
// Note: in spite of JSLint's complaints, win == win.top is required
/*jslint eqeq: true*/
if (!hasSVG && win == win.top && doc.readyState !== COMPLETE) {
/*jslint eqeq: false*/
doc.attachEvent(ONREADYSTATECHANGE, function () {
doc.detachEvent(ONREADYSTATECHANGE, firstRender);
if (doc.readyState === COMPLETE) {
firstRender();
}
});
return;
}
// create the container
getContainer();
// Run an early event after the container and renderer are established
fireEvent(chart, 'init');
// Initialize range selector for stock charts
if (Highcharts.RangeSelector && options.rangeSelector.enabled) {
chart.rangeSelector = new Highcharts.RangeSelector(chart);
}
resetMargins();
setChartSize();
// Set the common inversion and transformation for inverted series after initSeries
setInverted();
// get axes
getAxes();
// Initialize the series
each(options.series || [], function (serieOptions) {
initSeries(serieOptions);
});
// Run an event where series and axes can be added
//fireEvent(chart, 'beforeRender');
// Initialize scroller for stock charts
if (Highcharts.Scroller && (options.navigator.enabled || options.scrollbar.enabled)) {
chart.scroller = new Highcharts.Scroller(chart);
}
chart.render = render;
// depends on inverted and on margins being set
chart.tracker = tracker = new MouseTracker(options.tooltip);
render();
// run callbacks
if (callback) {
callback.apply(chart, [chart]);
}
each(chart.callbacks, function (fn) {
fn.apply(chart, [chart]);
});
fireEvent(chart, 'load');
}
// Run chart
// Set up auto resize
if (optionsChart.reflow !== false) {
addEvent(chart, 'load', initReflow);
}
// Chart event handlers
if (chartEvents) {
for (eventType in chartEvents) {
addEvent(chart, eventType, chartEvents[eventType]);
}
}
chart.options = options;
chart.series = series;
chart.xAxis = [];
chart.yAxis = [];
// Expose methods and variables
chart.addSeries = addSeries;
chart.animation = pick(optionsChart.animation, true);
chart.Axis = Axis;
chart.destroy = destroy;
chart.get = get;
chart.getSelectedPoints = getSelectedPoints;
chart.getSelectedSeries = getSelectedSeries;
chart.hideLoading = hideLoading;
chart.initSeries = initSeries;
chart.isInsidePlot = isInsidePlot;
chart.redraw = redraw;
chart.setSize = resize;
chart.setTitle = setTitle;
chart.showLoading = showLoading;
chart.pointCount = 0;
chart.counters = new ChartCounters();
/*
if ($) $(function() {
$container = $('#container');
var origChartWidth,
origChartHeight;
if ($container) {
$('<button>+</button>')
.insertBefore($container)
.click(function() {
if (origChartWidth === UNDEFINED) {
origChartWidth = chartWidth;
origChartHeight = chartHeight;
}
chart.resize(chartWidth *= 1.1, chartHeight *= 1.1);
});
$('<button>-</button>')
.insertBefore($container)
.click(function() {
if (origChartWidth === UNDEFINED) {
origChartWidth = chartWidth;
origChartHeight = chartHeight;
}
chart.resize(chartWidth *= 0.9, chartHeight *= 0.9);
});
$('<button>1:1</button>')
.insertBefore($container)
.click(function() {
if (origChartWidth === UNDEFINED) {
origChartWidth = chartWidth;
origChartHeight = chartHeight;
}
chart.resize(origChartWidth, origChartHeight);
});
}
})
*/
firstRender();
} // end Chart
// Hook for exporting module
Chart.prototype.callbacks = [];
/**
* The Point object and prototype. Inheritable and used as base for PiePoint
*/
var Point = function () {};
Point.prototype = {
/**
* Initialize the point
* @param {Object} series The series object containing this point
* @param {Object} options The data in either number, array or object format
*/
init: function (series, options, x) {
var point = this,
counters = series.chart.counters,
defaultColors;
point.series = series;
point.applyOptions(options, x);
point.pointAttr = {};
if (series.options.colorByPoint) {
defaultColors = series.chart.options.colors;
if (!point.options) {
point.options = {};
}
point.color = point.options.color = point.color || defaultColors[counters.color++];
// loop back to zero
counters.wrapColor(defaultColors.length);
}
series.chart.pointCount++;
return point;
},
/**
* Apply the options containing the x and y data and possible some extra properties.
* This is called on point init or from point.update.
*
* @param {Object} options
*/
applyOptions: function (options, x) {
var point = this,
series = point.series,
optionsType = typeof options;
point.config = options;
// onedimensional array input
if (optionsType === 'number' || options === null) {
point.y = options;
} else if (typeof options[0] === 'number') { // two-dimentional array
point.x = options[0];
point.y = options[1];
} else if (optionsType === 'object' && typeof options.length !== 'number') { // object input
// copy options directly to point
extend(point, options);
point.options = options;
} else if (typeof options[0] === 'string') { // categorized data with name in first position
point.name = options[0];
point.y = options[1];
}
/*
* If no x is set by now, get auto incremented value. All points must have an
* x value, however the y value can be null to create a gap in the series
*/
// todo: skip this? It is only used in applyOptions, in translate it should not be used
if (point.x === UNDEFINED) {
point.x = x === UNDEFINED ? series.autoIncrement() : x;
}
},
/**
* Destroy a point to clear memory. Its reference still stays in series.data.
*/
destroy: function () {
var point = this,
series = point.series,
hoverPoints = series.chart.hoverPoints,
prop;
series.chart.pointCount--;
if (hoverPoints) {
point.setState();
erase(hoverPoints, point);
}
if (point === series.chart.hoverPoint) {
point.onMouseOut();
}
series.chart.hoverPoints = null;
// remove all events
if (point.graphic || point.dataLabel) { // removeEvent and destroyElements are performance expensive
removeEvent(point);
point.destroyElements();
}
if (point.legendItem) { // pies have legend items
point.series.chart.legend.destroyItem(point);
}
for (prop in point) {
point[prop] = null;
}
},
/**
* Destroy SVG elements associated with the point
*/
destroyElements: function () {
var point = this,
props = ['graphic', 'tracker', 'dataLabel', 'group', 'connector', 'shadowGroup'],
prop,
i = 6;
while (i--) {
prop = props[i];
if (point[prop]) {
point[prop] = point[prop].destroy();
}
}
},
/**
* Return the configuration hash needed for the data label and tooltip formatters
*/
getLabelConfig: function () {
var point = this;
return {
x: point.category,
y: point.y,
key: point.name || point.category,
series: point.series,
point: point,
percentage: point.percentage,
total: point.total || point.stackTotal
};
},
/**
* Toggle the selection status of a point
* @param {Boolean} selected Whether to select or unselect the point.
* @param {Boolean} accumulate Whether to add to the previous selection. By default,
* this happens if the control key (Cmd on Mac) was pressed during clicking.
*/
select: function (selected, accumulate) {
var point = this,
series = point.series,
chart = series.chart;
selected = pick(selected, !point.selected);
// fire the event with the defalut handler
point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
point.selected = selected;
point.setState(selected && SELECT_STATE);
// unselect all other points unless Ctrl or Cmd + click
if (!accumulate) {
each(chart.getSelectedPoints(), function (loopPoint) {
if (loopPoint.selected && loopPoint !== point) {
loopPoint.selected = false;
loopPoint.setState(NORMAL_STATE);
loopPoint.firePointEvent('unselect');
}
});
}
});
},
onMouseOver: function () {
var point = this,
series = point.series,
chart = series.chart,
tooltip = chart.tooltip,
hoverPoint = chart.hoverPoint;
// set normal state to previous series
if (hoverPoint && hoverPoint !== point) {
hoverPoint.onMouseOut();
}
// trigger the event
point.firePointEvent('mouseOver');
// update the tooltip
if (tooltip && (!tooltip.shared || series.noSharedTooltip)) {
tooltip.refresh(point);
}
// hover this
point.setState(HOVER_STATE);
chart.hoverPoint = point;
},
onMouseOut: function () {
var point = this;
point.firePointEvent('mouseOut');
point.setState();
point.series.chart.hoverPoint = null;
},
/**
* Extendable method for formatting each point's tooltip line
*
* @return {String} A string to be concatenated in to the common tooltip text
*/
tooltipFormatter: function (pointFormat) {
var point = this,
series = point.series,
seriesTooltipOptions = series.tooltipOptions,
split = String(point.y).split('.'),
originalDecimals = split[1] ? split[1].length : 0,
match = pointFormat.match(/\{(series|point)\.[a-zA-Z]+\}/g),
splitter = /[\.}]/,
obj,
key,
replacement,
i;
// loop over the variables defined on the form {series.name}, {point.y} etc
for (i in match) {
key = match[i];
if (isString(key) && key !== pointFormat) { // IE matches more than just the variables
obj = key.indexOf('point') === 1 ? point : series;
if (key === '{point.y}') { // add some preformatting
replacement = (seriesTooltipOptions.yPrefix || '') +
numberFormat(point.y, pick(seriesTooltipOptions.yDecimals, originalDecimals)) +
(seriesTooltipOptions.ySuffix || '');
} else { // automatic replacement
replacement = obj[match[i].split(splitter)[1]];
}
pointFormat = pointFormat.replace(match[i], replacement);
}
}
return pointFormat;
},
/**
* Update the point with new options (typically x/y data) and optionally redraw the series.
*
* @param {Object} options Point options as defined in the series.data array
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*
*/
update: function (options, redraw, animation) {
var point = this,
series = point.series,
graphic = point.graphic,
i,
data = series.data,
dataLength = data.length,
chart = series.chart;
redraw = pick(redraw, true);
// fire the event with a default handler of doing the update
point.firePointEvent('update', { options: options }, function () {
point.applyOptions(options);
// update visuals
if (isObject(options)) {
series.getAttribs();
if (graphic) {
graphic.attr(point.pointAttr[series.state]);
}
}
// record changes in the parallel arrays
for (i = 0; i < dataLength; i++) {
if (data[i] === point) {
series.xData[i] = point.x;
series.yData[i] = point.y;
series.options.data[i] = options;
break;
}
}
// redraw
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
chart.redraw(animation);
}
});
},
/**
* Remove a point and optionally redraw the series and if necessary the axes
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*/
remove: function (redraw, animation) {
var point = this,
series = point.series,
chart = series.chart,
i,
data = series.data,
dataLength = data.length;
setAnimation(animation, chart);
redraw = pick(redraw, true);
// fire the event with a default handler of removing the point
point.firePointEvent('remove', null, function () {
//erase(series.data, point);
for (i = 0; i < dataLength; i++) {
if (data[i] === point) {
// splice all the parallel arrays
data.splice(i, 1);
series.options.data.splice(i, 1);
series.xData.splice(i, 1);
series.yData.splice(i, 1);
break;
}
}
point.destroy();
// redraw
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
chart.redraw();
}
});
},
/**
* Fire an event on the Point object. Must not be renamed to fireEvent, as this
* causes a name clash in MooTools
* @param {String} eventType
* @param {Object} eventArgs Additional event arguments
* @param {Function} defaultFunction Default event handler
*/
firePointEvent: function (eventType, eventArgs, defaultFunction) {
var point = this,
series = this.series,
seriesOptions = series.options;
// load event handlers on demand to save time on mouseover/out
if (seriesOptions.point.events[eventType] || (point.options && point.options.events && point.options.events[eventType])) {
this.importEvents();
}
// add default handler if in selection mode
if (eventType === 'click' && seriesOptions.allowPointSelect) {
defaultFunction = function (event) {
// Control key is for Windows, meta (= Cmd key) for Mac, Shift for Opera
point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
};
}
fireEvent(this, eventType, eventArgs, defaultFunction);
},
/**
* Import events from the series' and point's options. Only do it on
* demand, to save processing time on hovering.
*/
importEvents: function () {
if (!this.hasImportedEvents) {
var point = this,
options = merge(point.series.options.point, point.options),
events = options.events,
eventType;
point.events = events;
for (eventType in events) {
addEvent(point, eventType, events[eventType]);
}
this.hasImportedEvents = true;
}
},
/**
* Set the point's state
* @param {String} state
*/
setState: function (state) {
var point = this,
plotX = point.plotX,
plotY = point.plotY,
series = point.series,
stateOptions = series.options.states,
markerOptions = defaultPlotOptions[series.type].marker && series.options.marker,
normalDisabled = markerOptions && !markerOptions.enabled,
markerStateOptions = markerOptions && markerOptions.states[state],
stateDisabled = markerStateOptions && markerStateOptions.enabled === false,
stateMarkerGraphic = series.stateMarkerGraphic,
chart = series.chart,
radius,
pointAttr = point.pointAttr;
state = state || NORMAL_STATE; // empty string
if (
// already has this state
state === point.state ||
// selected points don't respond to hover
(point.selected && state !== SELECT_STATE) ||
// series' state options is disabled
(stateOptions[state] && stateOptions[state].enabled === false) ||
// point marker's state options is disabled
(state && (stateDisabled || (normalDisabled && !markerStateOptions.enabled)))
) {
return;
}
// apply hover styles to the existing point
if (point.graphic) {
radius = pointAttr[state].r;
point.graphic.attr(merge(
pointAttr[state],
radius ? extend({ // new symbol attributes
x: plotX - radius,
y: plotY - radius
}, point.graphic.symbolName ? { // don't apply to image symbols #507
width: 2 * radius,
height: 2 * radius
} : {}) : {}
));
} else {
// if a graphic is not applied to each point in the normal state, create a shared
// graphic for the hover state
if (state) {
if (!stateMarkerGraphic) {
radius = markerOptions.radius;
series.stateMarkerGraphic = stateMarkerGraphic = chart.renderer.symbol(
series.symbol,
-radius,
-radius,
2 * radius,
2 * radius
)
.attr(pointAttr[state])
.add(series.group);
}
stateMarkerGraphic.translate(
plotX,
plotY
);
}
if (stateMarkerGraphic) {
stateMarkerGraphic[state ? 'show' : 'hide']();
}
}
point.state = state;
}
};
/**
* @classDescription The base function which all other series types inherit from. The data in the series is stored
* in various arrays.
*
* - First, series.options.data contains all the original config options for
* each point whether added by options or methods like series.addPoint.
* - Next, series.data contains those values converted to points, but in case the series data length
* exceeds the cropThreshold, or if the data is grouped, series.data doesn't contain all the points. It
* only contains the points that have been created on demand.
* - Then there's series.points that contains all currently visible point objects. In case of cropping,
* the cropped-away points are not part of this array. The series.points array starts at series.cropStart
* compared to series.data and series.options.data. If however the series data is grouped, these can't
* be correlated one to one.
* - series.xData and series.processedXData contain clean x values, equivalent to series.data and series.points.
* - series.yData and series.processedYData contain clean x values, equivalent to series.data and series.points.
*
* @param {Object} chart
* @param {Object} options
*/
var Series = function () {};
Series.prototype = {
isCartesian: true,
type: 'line',
pointClass: Point,
pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
stroke: 'lineColor',
'stroke-width': 'lineWidth',
fill: 'fillColor',
r: 'radius'
},
init: function (chart, options) {
var series = this,
eventType,
events,
//pointEvent,
index = chart.series.length;
series.chart = chart;
series.options = options = series.setOptions(options); // merge with plotOptions
// bind the axes
series.bindAxes();
// set some variables
extend(series, {
index: index,
name: options.name || 'Series ' + (index + 1),
state: NORMAL_STATE,
pointAttr: {},
visible: options.visible !== false, // true by default
selected: options.selected === true // false by default
});
// register event listeners
events = options.events;
for (eventType in events) {
addEvent(series, eventType, events[eventType]);
}
if (
(events && events.click) ||
(options.point && options.point.events && options.point.events.click) ||
options.allowPointSelect
) {
chart.runTrackerClick = true;
}
series.getColor();
series.getSymbol();
// set the data
series.setData(options.data, false);
},
/**
* Set the xAxis and yAxis properties of cartesian series, and register the series
* in the axis.series array
*/
bindAxes: function () {
var series = this,
seriesOptions = series.options,
chart = series.chart,
axisOptions;
if (series.isCartesian) {
each(['xAxis', 'yAxis'], function (AXIS) { // repeat for xAxis and yAxis
each(chart[AXIS], function (axis) { // loop through the chart's axis objects
axisOptions = axis.options;
// apply if the series xAxis or yAxis option mathches the number of the
// axis, or if undefined, use the first axis
if ((seriesOptions[AXIS] === axisOptions.index) ||
(seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) {
// register this series in the axis.series lookup
axis.series.push(series);
// set this series.xAxis or series.yAxis reference
series[AXIS] = axis;
// mark dirty for redraw
axis.isDirty = true;
}
});
});
}
},
/**
* Return an auto incremented x value based on the pointStart and pointInterval options.
* This is only used if an x value is not given for the point that calls autoIncrement.
*/
autoIncrement: function () {
var series = this,
options = series.options,
xIncrement = series.xIncrement;
xIncrement = pick(xIncrement, options.pointStart, 0);
series.pointInterval = pick(series.pointInterval, options.pointInterval, 1);
series.xIncrement = xIncrement + series.pointInterval;
return xIncrement;
},
/**
* Divide the series data into segments divided by null values.
*/
getSegments: function () {
var series = this,
lastNull = -1,
segments = [],
i,
points = series.points;
// if connect nulls, just remove null points
if (series.options.connectNulls) {
i = points.length - 1;
while (i--) {
if (points[i].y === null) {
points.splice(i, 1);
}
}
segments = [points];
// else, split on null points
} else {
each(points, function (point, i) {
if (point.y === null) {
if (i > lastNull + 1) {
segments.push(points.slice(lastNull + 1, i));
}
lastNull = i;
} else if (i === points.length - 1) { // last value
segments.push(points.slice(lastNull + 1, i + 1));
}
});
}
// register it
series.segments = segments;
},
/**
* Set the series options by merging from the options tree
* @param {Object} itemOptions
*/
setOptions: function (itemOptions) {
var series = this,
chart = series.chart,
chartOptions = chart.options,
plotOptions = chartOptions.plotOptions,
data = itemOptions.data,
options;
itemOptions.data = null; // remove from merge to prevent looping over the data set
options = merge(
plotOptions[this.type],
plotOptions.series,
itemOptions
);
options.data = data;
// the tooltip options are merged between global and series specific options
series.tooltipOptions = merge(chartOptions.tooltip, options.tooltip);
return options;
},
/**
* Get the series' color
*/
getColor: function () {
var defaultColors = this.chart.options.colors,
counters = this.chart.counters;
this.color = this.options.color || defaultColors[counters.color++] || '#0000ff';
counters.wrapColor(defaultColors.length);
},
/**
* Get the series' symbol
*/
getSymbol: function () {
var defaultSymbols = this.chart.options.symbols,
counters = this.chart.counters;
this.symbol = this.options.marker.symbol || defaultSymbols[counters.symbol++];
counters.wrapSymbol(defaultSymbols.length);
},
/**
* Add a point dynamically after chart load time
* @param {Object} options Point options as given in series.data
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean} shift If shift is true, a point is shifted off the start
* of the series as one is appended to the end.
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*/
addPoint: function (options, redraw, shift, animation) {
var series = this,
data = series.data,
graph = series.graph,
area = series.area,
chart = series.chart,
xData = series.xData,
yData = series.yData,
currentShift = (graph && graph.shift) || 0,
dataOptions = series.options.data,
point;
//point = (new series.pointClass()).init(series, options);
setAnimation(animation, chart);
if (graph && shift) { // make graph animate sideways
graph.shift = currentShift + 1;
}
if (area) {
area.shift = currentShift + 1;
area.isArea = true;
}
redraw = pick(redraw, true);
// Get options and push the point to xData, yData and series.options. In series.generatePoints
// the Point instance will be created on demand and pushed to the series.data array.
point = { series: series };
series.pointClass.prototype.applyOptions.apply(point, [options]);
xData.push(point.x);
yData.push(point.y);
dataOptions.push(options);
// Shift the first point off the parallel arrays
// todo: consider series.removePoint(i) method
if (shift) {
if (data[0]) {
data[0].remove(false);
} else {
data.shift();
xData.shift();
yData.shift();
dataOptions.shift();
}
}
series.getAttribs();
// redraw
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
chart.redraw();
}
},
/**
* Replace the series data with a new set of data
* @param {Object} data
* @param {Object} redraw
*/
setData: function (data, redraw) {
var series = this,
oldData = series.points,
options = series.options,
initialColor = series.initialColor,
chart = series.chart,
firstPoint = null,
i;
// reset properties
series.xIncrement = null;
series.pointRange = (series.xAxis && series.xAxis.categories && 1) || options.pointRange;
if (defined(initialColor)) { // reset colors for pie
chart.counters.color = initialColor;
}
// parallel arrays
var xData = [],
yData = [],
dataLength = data.length,
turboThreshold = options.turboThreshold || 1000,
pt,
ohlc = series.valueCount === 4;
// In turbo mode, only one- or twodimensional arrays of numbers are allowed. The
// first value is tested, and we assume that all the rest are defined the same
// way. Although the 'for' loops are similar, they are repeated inside each
// if-else conditional for max performance.
if (dataLength > turboThreshold) {
// find the first non-null point
i = 0;
while (firstPoint === null && i < dataLength) {
firstPoint = data[i];
i++;
}
if (isNumber(firstPoint)) { // assume all points are numbers
var x = pick(options.pointStart, 0),
pointInterval = pick(options.pointInterval, 1);
for (i = 0; i < dataLength; i++) {
xData[i] = x;
yData[i] = data[i];
x += pointInterval;
}
series.xIncrement = x;
} else if (isArray(firstPoint)) { // assume all points are arrays
if (ohlc) { // [x, o, h, l, c]
for (i = 0; i < dataLength; i++) {
pt = data[i];
xData[i] = pt[0];
yData[i] = pt.slice(1, 5);
}
} else { // [x, y]
for (i = 0; i < dataLength; i++) {
pt = data[i];
xData[i] = pt[0];
yData[i] = pt[1];
}
}
}
} else {
for (i = 0; i < dataLength; i++) {
pt = { series: series };
series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
xData[i] = pt.x;
yData[i] = ohlc ? [pt.open, pt.high, pt.low, pt.close] : pt.y;
}
}
series.data = [];
series.options.data = data;
series.xData = xData;
series.yData = yData;
// destroy old points
i = (oldData && oldData.length) || 0;
while (i--) {
if (oldData[i] && oldData[i].destroy) {
oldData[i].destroy();
}
}
// redraw
series.isDirty = series.isDirtyData = chart.isDirtyBox = true;
if (pick(redraw, true)) {
chart.redraw(false);
}
},
/**
* Remove a series and optionally redraw the chart
*
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*/
remove: function (redraw, animation) {
var series = this,
chart = series.chart;
redraw = pick(redraw, true);
if (!series.isRemoving) { /* prevent triggering native event in jQuery
(calling the remove function from the remove event) */
series.isRemoving = true;
// fire the event with a default handler of removing the point
fireEvent(series, 'remove', null, function () {
// destroy elements
series.destroy();
// redraw
chart.isDirtyLegend = chart.isDirtyBox = true;
if (redraw) {
chart.redraw(animation);
}
});
}
series.isRemoving = false;
},
/**
* Process the data by cropping away unused data points if the series is longer
* than the crop threshold. This saves computing time for lage series.
*/
processData: function () {
var series = this,
processedXData = series.xData, // copied during slice operation below
processedYData = series.yData,
dataLength = processedXData.length,
cropStart = 0,
cropEnd = dataLength,
cropped,
i, // loop variable
cropThreshold = series.options.cropThreshold; // todo: consider combining it with turboThreshold
// If the series data or axes haven't changed, don't go through this. Return false to pass
// the message on to override methods like in data grouping.
if (series.isCartesian && !series.isDirty && !series.xAxis.isDirty && !series.yAxis.isDirty) {
return false;
}
// optionally filter out points outside the plot area
if (!cropThreshold || dataLength > cropThreshold || series.forceCrop) {
var extremes = series.xAxis.getExtremes(),
min = extremes.min,
max = extremes.max;
// it's outside current extremes
if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
processedXData = [];
processedYData = [];
// only crop if it's actually spilling out
} else if (processedXData[0] < min || processedXData[dataLength - 1] > max) {
// iterate up to find slice start
for (i = 0; i < dataLength; i++) {
if (processedXData[i] >= min) {
cropStart = mathMax(0, i - 1);
break;
}
}
// proceed to find slice end
for (; i < dataLength; i++) {
if (processedXData[i] > max) {
cropEnd = i + 1;
break;
}
}
processedXData = processedXData.slice(cropStart, cropEnd);
processedYData = processedYData.slice(cropStart, cropEnd);
cropped = true;
}
}
series.cropped = cropped; // undefined or true
series.cropStart = cropStart;
series.processedXData = processedXData;
series.processedYData = processedYData;
},
/**
* Generate the data point after the data has been processed by cropping away
* unused points and optionally grouped in Highcharts Stock.
*/
generatePoints: function () {
var series = this,
options = series.options,
dataOptions = options.data,
data = series.data,
dataLength,
processedXData = series.processedXData,
processedYData = series.processedYData,
pointClass = series.pointClass,
processedDataLength = processedXData.length,
cropStart = series.cropStart || 0,
cursor,
hasGroupedData = series.hasGroupedData,
point,
points = [],
i;
if (!data && !hasGroupedData) {
var arr = [];
arr.length = dataOptions.length;
data = series.data = arr;
}
for (i = 0; i < processedDataLength; i++) {
cursor = cropStart + i;
if (!hasGroupedData) {
if (data[cursor]) {
point = data[cursor];
} else {
data[cursor] = point = (new pointClass()).init(series, dataOptions[cursor], processedXData[i]);
}
points[i] = point;
} else {
// splat the y data in case of ohlc data array
points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
}
}
// hide cropped-away points - this only runs when the number of points is above cropThreshold
if (data && processedDataLength !== (dataLength = data.length)) {
for (i = 0; i < dataLength; i++) {
if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points
i += processedDataLength;
}
if (data[i]) {
data[i].destroyElements();
}
}
}
series.data = data;
series.points = points;
},
/**
* Translate data points from raw data values to chart specific positioning data
* needed later in drawPoints, drawGraph and drawTracker.
*/
translate: function () {
if (!this.processedXData) { // hidden series
this.processData();
}
this.generatePoints();
var series = this,
chart = series.chart,
options = series.options,
stacking = options.stacking,
xAxis = series.xAxis,
categories = xAxis.categories,
yAxis = series.yAxis,
points = series.points,
dataLength = points.length,
hasModifyValue = !!series.modifyValue,
i;
for (i = 0; i < dataLength; i++) {
var point = points[i],
xValue = point.x,
yValue = point.y,
yBottom = point.low,
stack = yAxis.stacks[(yValue < 0 ? '-' : '') + series.stackKey],
pointStack,
pointStackTotal;
// get the plotX translation
point.plotX = mathRound(series.xAxis.translate(xValue) * 10) / 10; // Math.round fixes #591
// calculate the bottom y value for stacked series
if (stacking && series.visible && stack && stack[xValue]) {
pointStack = stack[xValue];
pointStackTotal = pointStack.total;
pointStack.cum = yBottom = pointStack.cum - yValue; // start from top
yValue = yBottom + yValue;
if (stacking === 'percent') {
yBottom = pointStackTotal ? yBottom * 100 / pointStackTotal : 0;
yValue = pointStackTotal ? yValue * 100 / pointStackTotal : 0;
}
point.percentage = pointStackTotal ? point.y * 100 / pointStackTotal : 0;
point.stackTotal = pointStackTotal;
}
if (defined(yBottom)) {
point.yBottom = yAxis.translate(yBottom, 0, 1, 0, 1);
}
// general hook, used for Highstock compare mode
if (hasModifyValue) {
yValue = series.modifyValue(yValue, point);
}
// set the y value
if (yValue !== null) {
point.plotY = mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10; // Math.round fixes #591
}
// set client related positions for mouse tracking
point.clientX = chart.inverted ?
chart.plotHeight - point.plotX :
point.plotX; // for mouse tracking
// some API data
point.category = categories && categories[point.x] !== UNDEFINED ?
categories[point.x] : point.x;
}
// now that we have the cropped data, build the segments
series.getSegments();
},
/**
* Memoize tooltip texts and positions
*/
setTooltipPoints: function (renew) {
var series = this,
chart = series.chart,
inverted = chart.inverted,
points = [],
pointsLength,
plotSize = mathRound((inverted ? chart.plotTop : chart.plotLeft) + chart.plotSizeX),
low,
high,
xAxis = series.xAxis,
point,
i,
tooltipPoints = []; // a lookup array for each pixel in the x dimension
// don't waste resources if tracker is disabled
if (series.options.enableMouseTracking === false) {
return;
}
// renew
if (renew) {
series.tooltipPoints = null;
}
// concat segments to overcome null values
each(series.segments || series.points, function (segment) {
points = points.concat(segment);
});
// loop the concatenated points and apply each point to all the closest
// pixel positions
if (xAxis && xAxis.reversed) {
points = points.reverse();//reverseArray(points);
}
//each(points, function (point, i) {
pointsLength = points.length;
for (i = 0; i < pointsLength; i++) {
point = points[i];
low = points[i - 1] ? points[i - 1]._high + 1 : 0;
high = point._high = points[i + 1] ?
(mathFloor((point.plotX + (points[i + 1] ? points[i + 1].plotX : plotSize)) / 2)) :
plotSize;
while (low <= high) {
tooltipPoints[inverted ? plotSize - low++ : low++] = point;
}
}
series.tooltipPoints = tooltipPoints;
},
/**
* Format the header of the tooltip
*/
tooltipHeaderFormatter: function (key) {
var series = this,
tooltipOptions = series.tooltipOptions,
xDateFormat = tooltipOptions.xDateFormat || '%A, %b %e, %Y',
xAxis = series.xAxis,
isDateTime = xAxis && xAxis.options.type === 'datetime';
return tooltipOptions.headerFormat
.replace('{point.key}', isDateTime ? dateFormat(xDateFormat, key) : key)
.replace('{series.name}', series.name)
.replace('{series.color}', series.color);
},
/**
* Series mouse over handler
*/
onMouseOver: function () {
var series = this,
chart = series.chart,
hoverSeries = chart.hoverSeries;
if (!hasTouch && chart.mouseIsDown) {
return;
}
// set normal state to previous series
if (hoverSeries && hoverSeries !== series) {
hoverSeries.onMouseOut();
}
// trigger the event, but to save processing time,
// only if defined
if (series.options.events.mouseOver) {
fireEvent(series, 'mouseOver');
}
// hover this
series.setState(HOVER_STATE);
chart.hoverSeries = series;
},
/**
* Series mouse out handler
*/
onMouseOut: function () {
// trigger the event only if listeners exist
var series = this,
options = series.options,
chart = series.chart,
tooltip = chart.tooltip,
hoverPoint = chart.hoverPoint;
// trigger mouse out on the point, which must be in this series
if (hoverPoint) {
hoverPoint.onMouseOut();
}
// fire the mouse out event
if (series && options.events.mouseOut) {
fireEvent(series, 'mouseOut');
}
// hide the tooltip
if (tooltip && !options.stickyTracking && !tooltip.shared) {
tooltip.hide();
}
// set normal state
series.setState();
chart.hoverSeries = null;
},
/**
* Animate in the series
*/
animate: function (init) {
var series = this,
chart = series.chart,
clipRect = series.clipRect,
animation = series.options.animation;
if (animation && !isObject(animation)) {
animation = {};
}
if (init) { // initialize the animation
if (!clipRect.isAnimating) { // apply it only for one of the series
clipRect.attr('width', 0);
clipRect.isAnimating = true;
}
} else { // run the animation
clipRect.animate({
width: chart.plotSizeX
}, animation);
// delete this function to allow it only once
this.animate = null;
}
},
/**
* Draw the markers
*/
drawPoints: function () {
var series = this,
pointAttr,
points = series.points,
chart = series.chart,
plotX,
plotY,
i,
point,
radius,
graphic;
if (series.options.marker.enabled) {
i = points.length;
while (i--) {
point = points[i];
plotX = point.plotX;
plotY = point.plotY;
graphic = point.graphic;
// only draw the point if y is defined
if (plotY !== UNDEFINED && !isNaN(plotY)) {
// shortcuts
pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE];
radius = pointAttr.r;
if (graphic) { // update
graphic.animate(extend({
x: plotX - radius,
y: plotY - radius
}, graphic.symbolName ? { // don't apply to image symbols #507
width: 2 * radius,
height: 2 * radius
} : {}));
} else {
point.graphic = chart.renderer.symbol(
pick(point.marker && point.marker.symbol, series.symbol),
plotX - radius,
plotY - radius,
2 * radius,
2 * radius
)
.attr(pointAttr)
.add(series.group);
}
}
}
}
},
/**
* Convert state properties from API naming conventions to SVG attributes
*
* @param {Object} options API options object
* @param {Object} base1 SVG attribute object to inherit from
* @param {Object} base2 Second level SVG attribute object to inherit from
*/
convertAttribs: function (options, base1, base2, base3) {
var conversion = this.pointAttrToOptions,
attr,
option,
obj = {};
options = options || {};
base1 = base1 || {};
base2 = base2 || {};
base3 = base3 || {};
for (attr in conversion) {
option = conversion[attr];
obj[attr] = pick(options[option], base1[attr], base2[attr], base3[attr]);
}
return obj;
},
/**
* Get the state attributes. Each series type has its own set of attributes
* that are allowed to change on a point's state change. Series wide attributes are stored for
* all series, and additionally point specific attributes are stored for all
* points with individual marker options. If such options are not defined for the point,
* a reference to the series wide attributes is stored in point.pointAttr.
*/
getAttribs: function () {
var series = this,
normalOptions = defaultPlotOptions[series.type].marker ? series.options.marker : series.options,
stateOptions = normalOptions.states,
stateOptionsHover = stateOptions[HOVER_STATE],
pointStateOptionsHover,
seriesColor = series.color,
normalDefaults = {
stroke: seriesColor,
fill: seriesColor
},
points = series.points,
i,
point,
seriesPointAttr = [],
pointAttr,
pointAttrToOptions = series.pointAttrToOptions,
hasPointSpecificOptions,
key;
// series type specific modifications
if (series.options.marker) { // line, spline, area, areaspline, scatter
// if no hover radius is given, default to normal radius + 2
stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2;
stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1;
} else { // column, bar, pie
// if no hover color is given, brighten the normal color
stateOptionsHover.color = stateOptionsHover.color ||
Color(stateOptionsHover.color || seriesColor)
.brighten(stateOptionsHover.brightness).get();
}
// general point attributes for the series normal state
seriesPointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, normalDefaults);
// HOVER_STATE and SELECT_STATE states inherit from normal state except the default radius
each([HOVER_STATE, SELECT_STATE], function (state) {
seriesPointAttr[state] =
series.convertAttribs(stateOptions[state], seriesPointAttr[NORMAL_STATE]);
});
// set it
series.pointAttr = seriesPointAttr;
// Generate the point-specific attribute collections if specific point
// options are given. If not, create a referance to the series wide point
// attributes
i = points.length;
while (i--) {
point = points[i];
normalOptions = (point.options && point.options.marker) || point.options;
if (normalOptions && normalOptions.enabled === false) {
normalOptions.radius = 0;
}
hasPointSpecificOptions = false;
// check if the point has specific visual options
if (point.options) {
for (key in pointAttrToOptions) {
if (defined(normalOptions[pointAttrToOptions[key]])) {
hasPointSpecificOptions = true;
}
}
}
// a specific marker config object is defined for the individual point:
// create it's own attribute collection
if (hasPointSpecificOptions) {
pointAttr = [];
stateOptions = normalOptions.states || {}; // reassign for individual point
pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {};
// if no hover color is given, brighten the normal color
if (!series.options.marker) { // column, bar, point
pointStateOptionsHover.color =
Color(pointStateOptionsHover.color || point.options.color)
.brighten(pointStateOptionsHover.brightness ||
stateOptionsHover.brightness).get();
}
// normal point state inherits series wide normal state
pointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, seriesPointAttr[NORMAL_STATE]);
// inherit from point normal and series hover
pointAttr[HOVER_STATE] = series.convertAttribs(
stateOptions[HOVER_STATE],
seriesPointAttr[HOVER_STATE],
pointAttr[NORMAL_STATE]
);
// inherit from point normal and series hover
pointAttr[SELECT_STATE] = series.convertAttribs(
stateOptions[SELECT_STATE],
seriesPointAttr[SELECT_STATE],
pointAttr[NORMAL_STATE]
);
// no marker config object is created: copy a reference to the series-wide
// attribute collection
} else {
pointAttr = seriesPointAttr;
}
point.pointAttr = pointAttr;
}
},
/**
* Clear DOM objects and free up memory
*/
destroy: function () {
var series = this,
chart = series.chart,
seriesClipRect = series.clipRect,
issue134 = /AppleWebKit\/533/.test(userAgent),
destroy,
i,
data = series.data || [],
point,
prop,
axis;
// add event hook
fireEvent(series, 'destroy');
// remove all events
removeEvent(series);
// erase from axes
each(['xAxis', 'yAxis'], function (AXIS) {
axis = series[AXIS];
if (axis) {
erase(axis.series, series);
axis.isDirty = true;
}
});
// remove legend items
if (series.legendItem) {
series.chart.legend.destroyItem(series);
}
// destroy all points with their elements
i = data.length;
while (i--) {
point = data[i];
if (point && point.destroy) {
point.destroy();
}
}
series.points = null;
// If this series clipRect is not the global one (which is removed on chart.destroy) we
// destroy it here.
if (seriesClipRect && seriesClipRect !== chart.clipRect) {
series.clipRect = seriesClipRect.destroy();
}
// destroy all SVGElements associated to the series
each(['area', 'graph', 'dataLabelsGroup', 'group', 'tracker'], function (prop) {
if (series[prop]) {
// issue 134 workaround
destroy = issue134 && prop === 'group' ?
'hide' :
'destroy';
series[prop][destroy]();
}
});
// remove from hoverSeries
if (chart.hoverSeries === series) {
chart.hoverSeries = null;
}
erase(chart.series, series);
// clear all members
for (prop in series) {
delete series[prop];
}
},
/**
* Draw the data labels
*/
drawDataLabels: function () {
if (this.options.dataLabels.enabled) {
var series = this,
x,
y,
points = series.points,
seriesOptions = series.options,
options = seriesOptions.dataLabels,
str,
dataLabelsGroup = series.dataLabelsGroup,
chart = series.chart,
xAxis = series.xAxis,
groupLeft = xAxis ? xAxis.left : chart.plotLeft,
yAxis = series.yAxis,
groupTop = yAxis ? yAxis.top : chart.plotTop,
renderer = chart.renderer,
inverted = chart.inverted,
seriesType = series.type,
color,
stacking = seriesOptions.stacking,
isBarLike = seriesType === 'column' || seriesType === 'bar',
vAlignIsNull = options.verticalAlign === null,
yIsNull = options.y === null;
if (isBarLike) {
if (stacking) {
// In stacked series the default label placement is inside the bars
if (vAlignIsNull) {
options = merge(options, {verticalAlign: 'middle'});
}
// If no y delta is specified, try to create a good default
if (yIsNull) {
options = merge(options, {y: {top: 14, middle: 4, bottom: -6}[options.verticalAlign]});
}
} else {
// In non stacked series the default label placement is on top of the bars
if (vAlignIsNull) {
options = merge(options, {verticalAlign: 'top'});
}
}
}
// create a separate group for the data labels to avoid rotation
if (!dataLabelsGroup) {
dataLabelsGroup = series.dataLabelsGroup =
renderer.g('data-labels')
.attr({
visibility: series.visible ? VISIBLE : HIDDEN,
zIndex: 6
})
.translate(groupLeft, groupTop)
.add();
} else {
dataLabelsGroup.translate(groupLeft, groupTop);
}
// determine the color
color = options.color;
if (color === 'auto') { // 1.0 backwards compatibility
color = null;
}
options.style.color = pick(color, series.color, 'black');
// make the labels for each point
each(points, function (point) {
var barX = point.barX,
plotX = (barX && barX + point.barW / 2) || point.plotX || -999,
plotY = pick(point.plotY, -999),
dataLabel = point.dataLabel,
align = options.align,
individualYDelta = yIsNull ? (point.y >= 0 ? -6 : 12) : options.y;
// get the string
str = options.formatter.call(point.getLabelConfig());
x = (inverted ? chart.plotWidth - plotY : plotX) + options.x;
y = (inverted ? chart.plotHeight - plotX : plotY) + individualYDelta;
// in columns, align the string to the column
if (seriesType === 'column') {
x += { left: -1, right: 1 }[align] * point.barW / 2 || 0;
}
if (inverted && point.y < 0) {
align = 'right';
x -= 10;
}
// update existing label
if (dataLabel) {
// vertically centered
if (inverted && !options.y) {
y = y + pInt(dataLabel.styles.lineHeight) * 0.9 - dataLabel.getBBox().height / 2;
}
dataLabel
.attr({
text: str
}).animate({
x: x,
y: y
});
// create new label
} else if (defined(str)) {
dataLabel = point.dataLabel = renderer.text(
str,
x,
y
)
.attr({
align: align,
rotation: options.rotation,
zIndex: 1
})
.css(options.style)
.add(dataLabelsGroup);
// vertically centered
if (inverted && !options.y) {
dataLabel.attr({
y: y + pInt(dataLabel.styles.lineHeight) * 0.9 - dataLabel.getBBox().height / 2
});
}
}
if (isBarLike && seriesOptions.stacking && dataLabel) {
var barY = point.barY,
barW = point.barW,
barH = point.barH;
dataLabel.align(options, null,
{
x: inverted ? chart.plotWidth - barY - barH : barX,
y: inverted ? chart.plotHeight - barX - barW : barY,
width: inverted ? barH : barW,
height: inverted ? barW : barH
});
}
});
}
},
/**
* Draw the actual graph
*/
drawGraph: function () {
var series = this,
options = series.options,
chart = series.chart,
graph = series.graph,
graphPath = [],
fillColor,
area = series.area,
group = series.group,
color = options.lineColor || series.color,
lineWidth = options.lineWidth,
dashStyle = options.dashStyle,
segmentPath,
renderer = chart.renderer,
translatedThreshold = series.yAxis.getThreshold(options.threshold),
useArea = /^area/.test(series.type),
singlePoints = [], // used in drawTracker
areaPath = [],
attribs;
// divide into segments and build graph and area paths
each(series.segments, function (segment) {
segmentPath = [];
// build the segment line
each(segment, function (point, i) {
if (series.getPointSpline) { // generate the spline as defined in the SplineSeries object
segmentPath.push.apply(segmentPath, series.getPointSpline(segment, point, i));
} else {
// moveTo or lineTo
segmentPath.push(i ? L : M);
// step line?
if (i && options.step) {
var lastPoint = segment[i - 1];
segmentPath.push(
point.plotX,
lastPoint.plotY
);
}
// normal line to next point
segmentPath.push(
point.plotX,
point.plotY
);
}
});
// add the segment to the graph, or a single point for tracking
if (segment.length > 1) {
graphPath = graphPath.concat(segmentPath);
} else {
singlePoints.push(segment[0]);
}
// build the area
if (useArea) {
var areaSegmentPath = [],
i,
segLength = segmentPath.length;
for (i = 0; i < segLength; i++) {
areaSegmentPath.push(segmentPath[i]);
}
if (segLength === 3) { // for animation from 1 to two points
areaSegmentPath.push(L, segmentPath[1], segmentPath[2]);
}
if (options.stacking && series.type !== 'areaspline') {
// Follow stack back. Todo: implement areaspline. A general solution could be to
// reverse the entire graphPath of the previous series, though may be hard with
// splines and with series with different extremes
for (i = segment.length - 1; i >= 0; i--) {
// step line?
if (i < segment.length - 1 && options.step) {
areaSegmentPath.push(segment[i + 1].plotX, segment[i].yBottom);
}
areaSegmentPath.push(segment[i].plotX, segment[i].yBottom);
}
} else { // follow zero line back
areaSegmentPath.push(
L,
segment[segment.length - 1].plotX,
translatedThreshold,
L,
segment[0].plotX,
translatedThreshold
);
}
areaPath = areaPath.concat(areaSegmentPath);
}
});
// used in drawTracker:
series.graphPath = graphPath;
series.singlePoints = singlePoints;
// draw the area if area series or areaspline
if (useArea) {
fillColor = pick(
options.fillColor,
Color(series.color).setOpacity(options.fillOpacity || 0.75).get()
);
if (area) {
area.animate({ d: areaPath });
} else {
// draw the area
series.area = series.chart.renderer.path(areaPath)
.attr({
fill: fillColor
}).add(group);
}
}
// draw the graph
if (graph) {
stop(graph); // cancel running animations, #459
graph.animate({ d: graphPath });
} else {
if (lineWidth) {
attribs = {
'stroke': color,
'stroke-width': lineWidth
};
if (dashStyle) {
attribs.dashstyle = dashStyle;
}
series.graph = renderer.path(graphPath)
.attr(attribs).add(group).shadow(options.shadow);
}
}
},
/**
* Render the graph and markers
*/
render: function () {
var series = this,
chart = series.chart,
group,
setInvert,
options = series.options,
doClip = options.clip !== false,
animation = options.animation,
doAnimation = animation && series.animate,
duration = doAnimation ? (animation && animation.duration) || 500 : 0,
clipRect = series.clipRect,
renderer = chart.renderer;
// Add plot area clipping rectangle. If this is before chart.hasRendered,
// create one shared clipRect.
// Todo: since creating the clip property, the clipRect is created but
// never used when clip is false. A better way would be that the animation
// would run, then the clipRect destroyed.
if (!clipRect) {
clipRect = series.clipRect = !chart.hasRendered && chart.clipRect ?
chart.clipRect :
renderer.clipRect(0, 0, chart.plotSizeX, chart.plotSizeY + 1);
if (!chart.clipRect) {
chart.clipRect = clipRect;
}
}
// the group
if (!series.group) {
group = series.group = renderer.g('series');
if (chart.inverted) {
setInvert = function () {
group.attr({
width: chart.plotWidth,
height: chart.plotHeight
}).invert();
};
setInvert(); // do it now
addEvent(chart, 'resize', setInvert); // do it on resize
addEvent(series, 'destroy', function () {
removeEvent(chart, 'resize', setInvert);
});
}
if (doClip) {
group.clip(series.clipRect);
}
group.attr({
visibility: series.visible ? VISIBLE : HIDDEN,
zIndex: options.zIndex
})
.translate(series.xAxis.left, series.yAxis.top)
.add(chart.seriesGroup);
}
series.drawDataLabels();
// initiate the animation
if (doAnimation) {
series.animate(true);
}
// cache attributes for shapes
series.getAttribs();
// draw the graph if any
if (series.drawGraph) {
series.drawGraph();
}
// draw the points
series.drawPoints();
// draw the mouse tracking area
if (series.options.enableMouseTracking !== false) {
series.drawTracker();
}
// run the animation
if (doAnimation) {
series.animate();
}
// finish the individual clipRect
setTimeout(function () {
clipRect.isAnimating = false;
group = series.group; // can be destroyed during the timeout
if (group && clipRect !== chart.clipRect && clipRect.renderer) {
if (doClip) {
group.clip((series.clipRect = chart.clipRect));
}
clipRect.destroy();
}
}, duration);
series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
// (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
},
/**
* Redraw the series after an update in the axes.
*/
redraw: function () {
var series = this,
chart = series.chart,
wasDirtyData = series.isDirtyData, // cache it here as it is set to false in render, but used after
group = series.group;
// reposition on resize
if (group) {
if (chart.inverted) {
group.attr({
width: chart.plotWidth,
height: chart.plotHeight
});
}
group.animate({
translateX: series.xAxis.left,
translateY: series.yAxis.top
});
}
series.translate();
series.setTooltipPoints(true);
series.render();
if (wasDirtyData) {
fireEvent(series, 'updatedData');
}
},
/**
* Set the state of the graph
*/
setState: function (state) {
var series = this,
options = series.options,
graph = series.graph,
stateOptions = options.states,
lineWidth = options.lineWidth;
state = state || NORMAL_STATE;
if (series.state !== state) {
series.state = state;
if (stateOptions[state] && stateOptions[state].enabled === false) {
return;
}
if (state) {
lineWidth = stateOptions[state].lineWidth || lineWidth + 1;
}
if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML
graph.attr({ // use attr because animate will cause any other animation on the graph to stop
'stroke-width': lineWidth
}, state ? 0 : 500);
}
}
},
/**
* Set the visibility of the graph
*
* @param vis {Boolean} True to show the series, false to hide. If UNDEFINED,
* the visibility is toggled.
*/
setVisible: function (vis, redraw) {
var series = this,
chart = series.chart,
legendItem = series.legendItem,
seriesGroup = series.group,
seriesTracker = series.tracker,
dataLabelsGroup = series.dataLabelsGroup,
showOrHide,
i,
points = series.points,
point,
ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
oldVisibility = series.visible;
// if called without an argument, toggle visibility
series.visible = vis = vis === UNDEFINED ? !oldVisibility : vis;
showOrHide = vis ? 'show' : 'hide';
// show or hide series
if (seriesGroup) { // pies don't have one
seriesGroup[showOrHide]();
}
// show or hide trackers
if (seriesTracker) {
seriesTracker[showOrHide]();
} else if (points) {
i = points.length;
while (i--) {
point = points[i];
if (point.tracker) {
point.tracker[showOrHide]();
}
}
}
if (dataLabelsGroup) {
dataLabelsGroup[showOrHide]();
}
if (legendItem) {
chart.legend.colorizeItem(series, vis);
}
// rescale or adapt to resized chart
series.isDirty = true;
// in a stack, all other series are affected
if (series.options.stacking) {
each(chart.series, function (otherSeries) {
if (otherSeries.options.stacking && otherSeries.visible) {
otherSeries.isDirty = true;
}
});
}
if (ignoreHiddenSeries) {
chart.isDirtyBox = true;
}
if (redraw !== false) {
chart.redraw();
}
fireEvent(series, showOrHide);
},
/**
* Show the graph
*/
show: function () {
this.setVisible(true);
},
/**
* Hide the graph
*/
hide: function () {
this.setVisible(false);
},
/**
* Set the selected state of the graph
*
* @param selected {Boolean} True to select the series, false to unselect. If
* UNDEFINED, the selection state is toggled.
*/
select: function (selected) {
var series = this;
// if called without an argument, toggle
series.selected = selected = (selected === UNDEFINED) ? !series.selected : selected;
if (series.checkbox) {
series.checkbox.checked = selected;
}
fireEvent(series, selected ? 'select' : 'unselect');
},
/**
* Draw the tracker object that sits above all data labels and markers to
* track mouse events on the graph or points. For the line type charts
* the tracker uses the same graphPath, but with a greater stroke width
* for better control.
*/
drawTracker: function () {
var series = this,
options = series.options,
trackerPath = [].concat(series.graphPath),
trackerPathLength = trackerPath.length,
chart = series.chart,
snap = chart.options.tooltip.snap,
tracker = series.tracker,
cursor = options.cursor,
css = cursor && { cursor: cursor },
singlePoints = series.singlePoints,
singlePoint,
i;
// Extend end points. A better way would be to use round linecaps,
// but those are not clickable in VML.
if (trackerPathLength) {
i = trackerPathLength + 1;
while (i--) {
if (trackerPath[i] === M) { // extend left side
trackerPath.splice(i + 1, 0, trackerPath[i + 1] - snap, trackerPath[i + 2], L);
}
if ((i && trackerPath[i] === M) || i === trackerPathLength) { // extend right side
trackerPath.splice(i, 0, L, trackerPath[i - 2] + snap, trackerPath[i - 1]);
}
}
}
// handle single points
for (i = 0; i < singlePoints.length; i++) {
singlePoint = singlePoints[i];
trackerPath.push(M, singlePoint.plotX - snap, singlePoint.plotY,
L, singlePoint.plotX + snap, singlePoint.plotY);
}
// draw the tracker
if (tracker) {
tracker.attr({ d: trackerPath });
} else { // create
series.tracker = chart.renderer.path(trackerPath)
.attr({
isTracker: true,
stroke: TRACKER_FILL,
fill: NONE,
'stroke-width' : options.lineWidth + 2 * snap,
visibility: series.visible ? VISIBLE : HIDDEN,
zIndex: options.zIndex || 1
})
.on(hasTouch ? 'touchstart' : 'mouseover', function () {
if (chart.hoverSeries !== series) {
series.onMouseOver();
}
})
.on('mouseout', function () {
if (!options.stickyTracking) {
series.onMouseOut();
}
})
.css(css)
.add(chart.trackerGroup);
}
}
}; // end Series prototype
/**
* LineSeries object
*/
var LineSeries = extendClass(Series);
seriesTypes.line = LineSeries;
/**
* AreaSeries object
*/
var AreaSeries = extendClass(Series, {
type: 'area',
useThreshold: true
});
seriesTypes.area = AreaSeries;
/**
* SplineSeries object
*/
var SplineSeries = extendClass(Series, {
type: 'spline',
/**
* Draw the actual graph
*/
getPointSpline: function (segment, point, i) {
var smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc
denom = smoothing + 1,
plotX = point.plotX,
plotY = point.plotY,
lastPoint = segment[i - 1],
nextPoint = segment[i + 1],
leftContX,
leftContY,
rightContX,
rightContY,
ret;
// find control points
if (i && i < segment.length - 1) {
var lastX = lastPoint.plotX,
lastY = lastPoint.plotY,
nextX = nextPoint.plotX,
nextY = nextPoint.plotY,
correction;
leftContX = (smoothing * plotX + lastX) / denom;
leftContY = (smoothing * plotY + lastY) / denom;
rightContX = (smoothing * plotX + nextX) / denom;
rightContY = (smoothing * plotY + nextY) / denom;
// have the two control points make a straight line through main point
correction = ((rightContY - leftContY) * (rightContX - plotX)) /
(rightContX - leftContX) + plotY - rightContY;
leftContY += correction;
rightContY += correction;
// to prevent false extremes, check that control points are between
// neighbouring points' y values
if (leftContY > lastY && leftContY > plotY) {
leftContY = mathMax(lastY, plotY);
rightContY = 2 * plotY - leftContY; // mirror of left control point
} else if (leftContY < lastY && leftContY < plotY) {
leftContY = mathMin(lastY, plotY);
rightContY = 2 * plotY - leftContY;
}
if (rightContY > nextY && rightContY > plotY) {
rightContY = mathMax(nextY, plotY);
leftContY = 2 * plotY - rightContY;
} else if (rightContY < nextY && rightContY < plotY) {
rightContY = mathMin(nextY, plotY);
leftContY = 2 * plotY - rightContY;
}
// record for drawing in next point
point.rightContX = rightContX;
point.rightContY = rightContY;
}
// moveTo or lineTo
if (!i) {
ret = [M, plotX, plotY];
} else { // curve from last point to this
ret = [
'C',
lastPoint.rightContX || lastPoint.plotX,
lastPoint.rightContY || lastPoint.plotY,
leftContX || plotX,
leftContY || plotY,
plotX,
plotY
];
lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
}
return ret;
}
});
seriesTypes.spline = SplineSeries;
/**
* AreaSplineSeries object
*/
var AreaSplineSeries = extendClass(SplineSeries, {
type: 'areaspline',
useThreshold: true
});
seriesTypes.areaspline = AreaSplineSeries;
/**
* ColumnSeries object
*/
var ColumnSeries = extendClass(Series, {
type: 'column',
useThreshold: true,
pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
stroke: 'borderColor',
'stroke-width': 'borderWidth',
fill: 'color',
r: 'borderRadius'
},
init: function () {
Series.prototype.init.apply(this, arguments);
var series = this,
chart = series.chart;
// if the series is added dynamically, force redraw of other
// series affected by a new column
if (chart.hasRendered) {
each(chart.series, function (otherSeries) {
if (otherSeries.type === series.type) {
otherSeries.isDirty = true;
}
});
}
},
/**
* Translate each point to the plot area coordinate system and find shape positions
*/
translate: function () {
var series = this,
chart = series.chart,
options = series.options,
stacking = options.stacking,
borderWidth = options.borderWidth,
columnCount = 0,
xAxis = series.xAxis,
reversedXAxis = xAxis.reversed,
stackGroups = {},
stackKey,
columnIndex;
Series.prototype.translate.apply(series);
// Get the total number of column type series.
// This is called on every series. Consider moving this logic to a
// chart.orderStacks() function and call it on init, addSeries and removeSeries
each(chart.series, function (otherSeries) {
if (otherSeries.type === series.type && otherSeries.visible &&
series.options.group === otherSeries.options.group) { // used in Stock charts navigator series
if (otherSeries.options.stacking) {
stackKey = otherSeries.stackKey;
if (stackGroups[stackKey] === UNDEFINED) {
stackGroups[stackKey] = columnCount++;
}
columnIndex = stackGroups[stackKey];
} else {
columnIndex = columnCount++;
}
otherSeries.columnIndex = columnIndex;
}
});
// calculate the width and position of each column based on
// the number of column series in the plot, the groupPadding
// and the pointPadding options
var points = series.points,
pointRange = pick(series.pointRange, xAxis.pointRange),
categoryWidth = mathAbs(xAxis.translate(0) - xAxis.translate(pointRange)),
groupPadding = categoryWidth * options.groupPadding,
groupWidth = categoryWidth - 2 * groupPadding,
pointOffsetWidth = groupWidth / columnCount,
optionPointWidth = options.pointWidth,
pointPadding = defined(optionPointWidth) ? (pointOffsetWidth - optionPointWidth) / 2 :
pointOffsetWidth * options.pointPadding,
pointWidth = mathCeil(mathMax(pick(optionPointWidth, pointOffsetWidth - 2 * pointPadding), 1)),
colIndex = (reversedXAxis ? columnCount -
series.columnIndex : series.columnIndex) || 0,
pointXOffset = pointPadding + (groupPadding + colIndex *
pointOffsetWidth - (categoryWidth / 2)) *
(reversedXAxis ? -1 : 1),
threshold = options.threshold,
translatedThreshold = series.yAxis.getThreshold(threshold),
minPointLength = pick(options.minPointLength, 5);
// record the new values
each(points, function (point) {
var plotY = point.plotY,
yBottom = point.yBottom || translatedThreshold,
barX = point.plotX + pointXOffset,
barY = mathCeil(mathMin(plotY, yBottom)),
barH = mathCeil(mathMax(plotY, yBottom) - barY),
stack = series.yAxis.stacks[(point.y < 0 ? '-' : '') + series.stackKey],
trackerY,
shapeArgs;
// Record the offset'ed position and width of the bar to be able to align the stacking total correctly
if (stacking && series.visible && stack && stack[point.x]) {
stack[point.x].setOffset(pointXOffset, pointWidth);
}
// handle options.minPointLength and tracker for small points
if (mathAbs(barH) < minPointLength) {
if (minPointLength) {
barH = minPointLength;
barY =
mathAbs(barY - translatedThreshold) > minPointLength ? // stacked
yBottom - minPointLength : // keep position
translatedThreshold - (plotY <= translatedThreshold ? minPointLength : 0);
}
trackerY = barY - 3;
}
extend(point, {
barX: barX,
barY: barY,
barW: pointWidth,
barH: barH
});
// create shape type and shape args that are reused in drawPoints and drawTracker
point.shapeType = 'rect';
shapeArgs = extend(chart.renderer.Element.prototype.crisp.apply({}, [
borderWidth,
barX,
barY,
pointWidth,
barH
]), {
r: options.borderRadius
});
if (borderWidth % 2) { // correct for shorting in crisp method, visible in stacked columns with 1px border
shapeArgs.y -= 1;
shapeArgs.height += 1;
}
point.shapeArgs = shapeArgs;
// make small columns responsive to mouse
point.trackerArgs = defined(trackerY) && merge(point.shapeArgs, {
height: mathMax(6, barH + 3),
y: trackerY
});
});
},
getSymbol: function () {
},
/**
* Columns have no graph
*/
drawGraph: function () {},
/**
* Draw the columns. For bars, the series.group is rotated, so the same coordinates
* apply for columns and bars. This method is inherited by scatter series.
*
*/
drawPoints: function () {
var series = this,
options = series.options,
renderer = series.chart.renderer,
graphic,
shapeArgs;
// draw the columns
each(series.points, function (point) {
var plotY = point.plotY;
if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
graphic = point.graphic;
shapeArgs = point.shapeArgs;
if (graphic) { // update
stop(graphic);
graphic.animate(shapeArgs);
} else {
point.graphic = graphic = renderer[point.shapeType](shapeArgs)
.attr(point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE])
.add(series.group)
.shadow(options.shadow);
}
}
});
},
/**
* Draw the individual tracker elements.
* This method is inherited by scatter and pie charts too.
*/
drawTracker: function () {
var series = this,
chart = series.chart,
renderer = chart.renderer,
shapeArgs,
tracker,
trackerLabel = +new Date(),
options = series.options,
cursor = options.cursor,
css = cursor && { cursor: cursor },
rel;
each(series.points, function (point) {
tracker = point.tracker;
shapeArgs = point.trackerArgs || point.shapeArgs;
delete shapeArgs.strokeWidth;
if (point.y !== null) {
if (tracker) {// update
tracker.attr(shapeArgs);
} else {
point.tracker =
renderer[point.shapeType](shapeArgs)
.attr({
isTracker: trackerLabel,
fill: TRACKER_FILL,
visibility: series.visible ? VISIBLE : HIDDEN,
zIndex: options.zIndex || 1
})
.on(hasTouch ? 'touchstart' : 'mouseover', function (event) {
rel = event.relatedTarget || event.fromElement;
if (chart.hoverSeries !== series && attr(rel, 'isTracker') !== trackerLabel) {
series.onMouseOver();
}
point.onMouseOver();
})
.on('mouseout', function (event) {
if (!options.stickyTracking) {
rel = event.relatedTarget || event.toElement;
if (attr(rel, 'isTracker') !== trackerLabel) {
series.onMouseOut();
}
}
})
.css(css)
.add(point.group || chart.trackerGroup); // pies have point group - see issue #118
}
}
});
},
/**
* Animate the column heights one by one from zero
* @param {Boolean} init Whether to initialize the animation or run it
*/
animate: function (init) {
var series = this,
points = series.points;
if (!init) { // run the animation
/*
* Note: Ideally the animation should be initialized by calling
* series.group.hide(), and then calling series.group.show()
* after the animation was started. But this rendered the shadows
* invisible in IE8 standards mode. If the columns flicker on large
* datasets, this is the cause.
*/
each(points, function (point) {
var graphic = point.graphic,
shapeArgs = point.shapeArgs;
if (graphic) {
// start values
graphic.attr({
height: 0,
y: series.yAxis.translate(0, 0, 1)
});
// animate
graphic.animate({
height: shapeArgs.height,
y: shapeArgs.y
}, series.options.animation);
}
});
// delete this function to allow it only once
series.animate = null;
}
},
/**
* Remove this series from the chart
*/
remove: function () {
var series = this,
chart = series.chart;
// column and bar series affects other series of the same type
// as they are either stacked or grouped
if (chart.hasRendered) {
each(chart.series, function (otherSeries) {
if (otherSeries.type === series.type) {
otherSeries.isDirty = true;
}
});
}
Series.prototype.remove.apply(series, arguments);
}
});
seriesTypes.column = ColumnSeries;
var BarSeries = extendClass(ColumnSeries, {
type: 'bar',
init: function () {
this.inverted = true;
ColumnSeries.prototype.init.apply(this, arguments);
}
});
seriesTypes.bar = BarSeries;
/**
* The scatter series class
*/
var ScatterSeries = extendClass(Series, {
type: 'scatter',
/**
* Extend the base Series' translate method by adding shape type and
* arguments for the point trackers
*/
translate: function () {
var series = this;
Series.prototype.translate.apply(series);
each(series.points, function (point) {
point.shapeType = 'circle';
point.shapeArgs = {
x: point.plotX,
y: point.plotY,
r: series.chart.options.tooltip.snap
};
});
},
/**
* Create individual tracker elements for each point
*/
//drawTracker: ColumnSeries.prototype.drawTracker,
drawTracker: function () {
var series = this,
cursor = series.options.cursor,
css = cursor && { cursor: cursor },
graphic;
each(series.points, function (point) {
graphic = point.graphic;
if (graphic) { // doesn't exist for null points
graphic
.attr({ isTracker: true })
.on('mouseover', function () {
series.onMouseOver();
point.onMouseOver();
})
.on('mouseout', function () {
if (!series.options.stickyTracking) {
series.onMouseOut();
}
})
.css(css);
}
});
}//,
/**
* Cleaning the data is not necessary in a scatter plot
*/
//cleanData: function () {}
});
seriesTypes.scatter = ScatterSeries;
/**
* Extended point object for pies
*/
var PiePoint = extendClass(Point, {
/**
* Initiate the pie slice
*/
init: function () {
Point.prototype.init.apply(this, arguments);
var point = this,
toggleSlice;
//visible: options.visible !== false,
extend(point, {
visible: point.visible !== false,
name: pick(point.name, 'Slice')
});
// add event listener for select
toggleSlice = function () {
point.slice();
};
addEvent(point, 'select', toggleSlice);
addEvent(point, 'unselect', toggleSlice);
return point;
},
/**
* Toggle the visibility of the pie slice
* @param {Boolean} vis Whether to show the slice or not. If undefined, the
* visibility is toggled
*/
setVisible: function (vis) {
var point = this,
chart = point.series.chart,
tracker = point.tracker,
dataLabel = point.dataLabel,
connector = point.connector,
shadowGroup = point.shadowGroup,
method;
// if called without an argument, toggle visibility
point.visible = vis = vis === UNDEFINED ? !point.visible : vis;
method = vis ? 'show' : 'hide';
point.group[method]();
if (tracker) {
tracker[method]();
}
if (dataLabel) {
dataLabel[method]();
}
if (connector) {
connector[method]();
}
if (shadowGroup) {
shadowGroup[method]();
}
if (point.legendItem) {
chart.legend.colorizeItem(point, vis);
}
},
/**
* Set or toggle whether the slice is cut out from the pie
* @param {Boolean} sliced When undefined, the slice state is toggled
* @param {Boolean} redraw Whether to redraw the chart. True by default.
*/
slice: function (sliced, redraw, animation) {
var point = this,
series = point.series,
chart = series.chart,
slicedTranslation = point.slicedTranslation,
translation;
setAnimation(animation, chart);
// redraw is true by default
redraw = pick(redraw, true);
// if called without an argument, toggle
sliced = point.sliced = defined(sliced) ? sliced : !point.sliced;
translation = {
translateX: (sliced ? slicedTranslation[0] : chart.plotLeft),
translateY: (sliced ? slicedTranslation[1] : chart.plotTop)
};
point.group.animate(translation);
if (point.shadowGroup) {
point.shadowGroup.animate(translation);
}
}
});
/**
* The Pie series class
*/
var PieSeries = extendClass(Series, {
type: 'pie',
isCartesian: false,
pointClass: PiePoint,
pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
stroke: 'borderColor',
'stroke-width': 'borderWidth',
fill: 'color'
},
/**
* Pies have one color each point
*/
getColor: function () {
// record first color for use in setData
this.initialColor = this.chart.counters.color;
},
/**
* Animate the column heights one by one from zero
*/
animate: function () {
var series = this,
points = series.points;
each(points, function (point) {
var graphic = point.graphic,
args = point.shapeArgs,
up = -mathPI / 2;
if (graphic) {
// start values
graphic.attr({
r: 0,
start: up,
end: up
});
// animate
graphic.animate({
r: args.r,
start: args.start,
end: args.end
}, series.options.animation);
}
});
// delete this function to allow it only once
series.animate = null;
},
/**
* Extend the basic setData method by running processData and generatePoints immediately,
* in order to access the points from the legend.
*/
setData: function () {
Series.prototype.setData.apply(this, arguments);
this.processData();
this.generatePoints();
},
/**
* Do translation for pie slices
*/
translate: function () {
this.generatePoints();
var total = 0,
series = this,
cumulative = -0.25, // start at top
precision = 1000, // issue #172
options = series.options,
slicedOffset = options.slicedOffset,
connectorOffset = slicedOffset + options.borderWidth,
positions = options.center.concat([options.size, options.innerSize || 0]),
chart = series.chart,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
start,
end,
angle,
points = series.points,
circ = 2 * mathPI,
fraction,
smallestSize = mathMin(plotWidth, plotHeight),
isPercent,
radiusX, // the x component of the radius vector for a given point
radiusY,
labelDistance = options.dataLabels.distance;
// get positions - either an integer or a percentage string must be given
positions = map(positions, function (length, i) {
isPercent = /%$/.test(length);
return isPercent ?
// i == 0: centerX, relative to width
// i == 1: centerY, relative to height
// i == 2: size, relative to smallestSize
// i == 4: innerSize, relative to smallestSize
[plotWidth, plotHeight, smallestSize, smallestSize][i] *
pInt(length) / 100 :
length;
});
// utility for getting the x value from a given y, used for anticollision logic in data labels
series.getX = function (y, left) {
angle = math.asin((y - positions[1]) / (positions[2] / 2 + labelDistance));
return positions[0] +
(left ? -1 : 1) *
(mathCos(angle) * (positions[2] / 2 + labelDistance));
};
// set center for later use
series.center = positions;
// get the total sum
each(points, function (point) {
total += point.y;
});
each(points, function (point) {
// set start and end angle
fraction = total ? point.y / total : 0;
start = mathRound(cumulative * circ * precision) / precision;
cumulative += fraction;
end = mathRound(cumulative * circ * precision) / precision;
// set the shape
point.shapeType = 'arc';
point.shapeArgs = {
x: positions[0],
y: positions[1],
r: positions[2] / 2,
innerR: positions[3] / 2,
start: start,
end: end
};
// center for the sliced out slice
angle = (end + start) / 2;
point.slicedTranslation = map([
mathCos(angle) * slicedOffset + chart.plotLeft,
mathSin(angle) * slicedOffset + chart.plotTop
], mathRound);
// set the anchor point for tooltips
radiusX = mathCos(angle) * positions[2] / 2;
radiusY = mathSin(angle) * positions[2] / 2;
point.tooltipPos = [
positions[0] + radiusX * 0.7,
positions[1] + radiusY * 0.7
];
// set the anchor point for data labels
point.labelPos = [
positions[0] + radiusX + mathCos(angle) * labelDistance, // first break of connector
positions[1] + radiusY + mathSin(angle) * labelDistance, // a/a
positions[0] + radiusX + mathCos(angle) * connectorOffset, // second break, right outside pie
positions[1] + radiusY + mathSin(angle) * connectorOffset, // a/a
positions[0] + radiusX, // landing point for connector
positions[1] + radiusY, // a/a
labelDistance < 0 ? // alignment
'center' :
angle < circ / 4 ? 'left' : 'right', // alignment
angle // center angle
];
// API properties
point.percentage = fraction * 100;
point.total = total;
});
this.setTooltipPoints();
},
/**
* Render the slices
*/
render: function () {
var series = this;
// cache attributes for shapes
series.getAttribs();
this.drawPoints();
// draw the mouse tracking area
if (series.options.enableMouseTracking !== false) {
series.drawTracker();
}
this.drawDataLabels();
if (series.options.animation && series.animate) {
series.animate();
}
// (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see
series.isDirty = false; // means data is in accordance with what you see
},
/**
* Draw the data points
*/
drawPoints: function () {
var series = this,
chart = series.chart,
renderer = chart.renderer,
groupTranslation,
//center,
graphic,
group,
shadow = series.options.shadow,
shadowGroup,
shapeArgs;
// draw the slices
each(series.points, function (point) {
graphic = point.graphic;
shapeArgs = point.shapeArgs;
group = point.group;
shadowGroup = point.shadowGroup;
// put the shadow behind all points
if (shadow && !shadowGroup) {
shadowGroup = point.shadowGroup = renderer.g('shadow')
.attr({ zIndex: 4 })
.add();
}
// create the group the first time
if (!group) {
group = point.group = renderer.g('point')
.attr({ zIndex: 5 })
.add();
}
// if the point is sliced, use special translation, else use plot area traslation
groupTranslation = point.sliced ? point.slicedTranslation : [chart.plotLeft, chart.plotTop];
group.translate(groupTranslation[0], groupTranslation[1]);
if (shadowGroup) {
shadowGroup.translate(groupTranslation[0], groupTranslation[1]);
}
// draw the slice
if (graphic) {
graphic.animate(shapeArgs);
} else {
point.graphic =
renderer.arc(shapeArgs)
.attr(extend(
point.pointAttr[NORMAL_STATE],
{ 'stroke-linejoin': 'round' }
))
.add(point.group)
.shadow(shadow, shadowGroup);
}
// detect point specific visibility
if (point.visible === false) {
point.setVisible(false);
}
});
},
/**
* Override the base drawDataLabels method by pie specific functionality
*/
drawDataLabels: function () {
var series = this,
data = series.data,
point,
chart = series.chart,
options = series.options.dataLabels,
connectorPadding = pick(options.connectorPadding, 10),
connectorWidth = pick(options.connectorWidth, 1),
connector,
connectorPath,
softConnector = pick(options.softConnector, true),
distanceOption = options.distance,
seriesCenter = series.center,
radius = seriesCenter[2] / 2,
centerY = seriesCenter[1],
outside = distanceOption > 0,
dataLabel,
labelPos,
labelHeight,
halves = [// divide the points into right and left halves for anti collision
[], // right
[] // left
],
x,
y,
visibility,
rankArr,
sort,
i = 2,
j;
// get out if not enabled
if (!options.enabled) {
return;
}
// run parent method
Series.prototype.drawDataLabels.apply(series);
// arrange points for detection collision
each(data, function (point) {
if (point.dataLabel) { // it may have been cancelled in the base method (#407)
halves[
point.labelPos[7] < mathPI / 2 ? 0 : 1
].push(point);
}
});
halves[1].reverse();
// define the sorting algorithm
sort = function (a, b) {
return b.y - a.y;
};
// assume equal label heights
labelHeight = halves[0][0] && halves[0][0].dataLabel && pInt(halves[0][0].dataLabel.styles.lineHeight);
/* Loop over the points in each quartile, starting from the top and bottom
* of the pie to detect overlapping labels.
*/
while (i--) {
var slots = [],
slotsLength,
usedSlots = [],
points = halves[i],
pos,
length = points.length,
slotIndex;
// build the slots
for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
slots.push(pos);
// visualize the slot
/*
var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
slotY = pos + chart.plotTop;
if (!isNaN(slotX)) {
chart.renderer.rect(slotX, slotY - 7, 100, labelHeight)
.attr({
'stroke-width': 1,
stroke: 'silver'
})
.add();
chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
.attr({
fill: 'silver'
}).add();
}
// */
}
slotsLength = slots.length;
// if there are more values than available slots, remove lowest values
if (length > slotsLength) {
// create an array for sorting and ranking the points within each quarter
rankArr = [].concat(points);
rankArr.sort(sort);
j = length;
while (j--) {
rankArr[j].rank = j;
}
j = length;
while (j--) {
if (points[j].rank >= slotsLength) {
points.splice(j, 1);
}
}
length = points.length;
}
// The label goes to the nearest open slot, but not closer to the edge than
// the label's index.
for (j = 0; j < length; j++) {
point = points[j];
labelPos = point.labelPos;
var closest = 9999,
distance,
slotI;
// find the closest slot index
for (slotI = 0; slotI < slotsLength; slotI++) {
distance = mathAbs(slots[slotI] - labelPos[1]);
if (distance < closest) {
closest = distance;
slotIndex = slotI;
}
}
// if that slot index is closer to the edges of the slots, move it
// to the closest appropriate slot
if (slotIndex < j && slots[j] !== null) { // cluster at the top
slotIndex = j;
} else if (slotsLength < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
slotIndex = slotsLength - length + j;
while (slots[slotIndex] === null) { // make sure it is not taken
slotIndex++;
}
} else {
// Slot is taken, find next free slot below. In the next run, the next slice will find the
// slot above these, because it is the closest one
while (slots[slotIndex] === null) { // make sure it is not taken
slotIndex++;
}
}
usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
slots[slotIndex] = null; // mark as taken
}
// sort them in order to fill in from the top
usedSlots.sort(sort);
// now the used slots are sorted, fill them up sequentially
for (j = 0; j < length; j++) {
point = points[j];
labelPos = point.labelPos;
dataLabel = point.dataLabel;
var slot = usedSlots.pop(),
naturalY = labelPos[1];
visibility = point.visible === false ? HIDDEN : VISIBLE;
slotIndex = slot.i;
// if the slot next to currrent slot is free, the y value is allowed
// to fall back to the natural position
y = slot.y;
if ((naturalY > y && slots[slotIndex + 1] !== null) ||
(naturalY < y && slots[slotIndex - 1] !== null)) {
y = naturalY;
}
// get the x - use the natural x position for first and last slot, to prevent the top
// and botton slice connectors from touching each other on either side
x = series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
// move or place the data label
dataLabel
.attr({
visibility: visibility,
align: labelPos[6]
})[dataLabel.moved ? 'animate' : 'attr']({
x: x + options.x +
({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0),
y: y + options.y
});
dataLabel.moved = true;
// draw the connector
if (outside && connectorWidth) {
connector = point.connector;
connectorPath = softConnector ? [
M,
x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
'C',
x, y, // first break, next to the label
2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
labelPos[2], labelPos[3], // second break
L,
labelPos[4], labelPos[5] // base
] : [
M,
x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label
L,
labelPos[2], labelPos[3], // second break
L,
labelPos[4], labelPos[5] // base
];
if (connector) {
connector.animate({ d: connectorPath });
connector.attr('visibility', visibility);
} else {
point.connector = connector = series.chart.renderer.path(connectorPath).attr({
'stroke-width': connectorWidth,
stroke: options.connectorColor || point.color || '#606060',
visibility: visibility,
zIndex: 3
})
.translate(chart.plotLeft, chart.plotTop)
.add();
}
}
}
}
},
/**
* Draw point specific tracker objects. Inherit directly from column series.
*/
drawTracker: ColumnSeries.prototype.drawTracker,
/**
* Pies don't have point marker symbols
*/
getSymbol: function () {}
});
seriesTypes.pie = PieSeries;
// global variables
extend(Highcharts, {
Chart: Chart,
dateFormat: dateFormat,
pathAnim: pathAnim,
getOptions: getOptions,
hasRtlBug: hasRtlBug,
numberFormat: numberFormat,
Point: Point,
Color: Color,
Renderer: Renderer,
seriesTypes: seriesTypes,
setOptions: setOptions,
Series: Series,
// Expose utility funcitons for modules
addEvent: addEvent,
removeEvent: removeEvent,
createElement: createElement,
discardElement: discardElement,
css: css,
each: each,
extend: extend,
map: map,
merge: merge,
pick: pick,
splat: splat,
extendClass: extendClass,
product: 'Highcharts',
version: '2.1.9'
});
}());
/*
Highstock JS v1.1.2 (2011-12-23)
(c) 2009-2011 Torstein H?nsi
License: www.highcharts.com/license
*/
(function(){function I(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function Ka(){for(var a=0,b=arguments,c=b.length,d={};a<c;a++)d[b[a++]]=b[a];return d}function O(a,b){return parseInt(a,b||10)}function Ab(a){return typeof a==="string"}function sb(a){return typeof a==="object"}function Ub(a){return typeof a==="number"}function Hb(a){return sa.log(a)/sa.LN10}function pc(a){return sa.pow(10,a)}function Ib(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function z(a){return a!==
B&&a!==null}function P(a,b,c){var d,e;if(Ab(b))z(c)?a.setAttribute(b,c):a&&a.getAttribute&&(e=a.getAttribute(b));else if(z(b)&&sb(b))for(d in b)a.setAttribute(d,b[d]);return e}function tb(a){return Object.prototype.toString.call(a)==="[object Array]"?a:[a]}function q(){var a=arguments,b,c,d=a.length;for(b=0;b<d;b++)if(c=a[b],typeof c!=="undefined"&&c!==null)return c}function C(a,b){if(Vb&&b&&b.opacity!==B)b.filter="alpha(opacity="+b.opacity*100+")";I(a.style,b)}function Y(a,b,c,d,e){a=T.createElement(a);
b&&I(a,b);e&&C(a,{padding:0,border:La,margin:0});c&&C(a,c);d&&d.appendChild(a);return a}function ka(a,b){var c=function(){};c.prototype=new a;I(c.prototype,b);return c}function Wb(a,b,c,d){var e=la.lang,f=isNaN(b=Ha(b))?2:b,b=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=a<0?"-":"",c=String(O(a=Ha(+a||0).toFixed(f))),g=c.length>3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+Ha(a-c).toFixed(f).slice(2):"")}function qc(a,b,c,d){var e,c=
q(c,1);e=a/c;if(!b&&(b=[1,2,2.5,5,10],d&&(d.allowDecimals===!1||d.type==="logarithmic")))c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c]);for(d=0;d<b.length;d++)if(a=b[d],e<=(b[d]+(b[d+1]||b[d]))/2)break;a*=c;return a}function rc(a,b,c,d,e){for(var f=[],g={},h=la.global.useUTC,k=e||[[pb,[1,2,5,10,20,25,50,100,200,500]],[qa,[1,2,5,10,15,30]],[ab,[1,2,5,10,15,30]],[Va,[1,2,3,4,6,8,12]],[ma,[1,2]],[Ea,[1,2]],[Pa,[1,2,3,4,6]],[bb,null]],i=k[k.length-1],j=L[i[0]],l=i[1],e=0;e<k.length;e++)if(i=k[e],j=L[i[0]],l=i[1],
k[e+1]&&a<=(j*l[l.length-1]+L[k[e+1][0]])/2)break;j===L[bb]&&a<5*j&&(l=[1,2,5]);var a=qc(a/j,l),m,b=new Date(b);b.setMilliseconds(0);j>=L[qa]&&b.setSeconds(j>=L[ab]?0:a*Ra(b.getSeconds()/a));if(j>=L[ab])b[sc](j>=L[Va]?0:a*Ra(b[hc]()/a));if(j>=L[Va])b[tc](j>=L[ma]?0:a*Ra(b[ic]()/a));if(j>=L[ma])b[jc](j>=L[Pa]?1:a*Ra(b[ub]()/a));j>=L[Pa]&&(b[uc](j>=L[bb]?0:a*Ra(b[Xb]()/a)),m=b[Yb]());j>=L[bb]&&(m-=m%a,b[vc](m));if(j===L[Ea])b[jc](b[ub]()-b[kc]()+q(d,1));e=1;m=b[Yb]();d=b.getTime();k=b[Xb]();for(b=b[ub]();d<
c;)f.push(d),j===L[bb]?d=Zb(m+e*a,0):j===L[Pa]?d=Zb(m,k+e*a):!h&&(j===L[ma]||j===L[Ea])?d=Zb(m,k,b+e*a*(j===L[ma]?1:7)):(d+=j*a,j<=L[Va]&&d%L[ma]===0&&(g[d]=ma)),e++;f.push(d);f.info={unitName:i[0],unitRange:j,count:a,higherRanks:g,totalRange:j*a};return f}function wc(){this.symbol=this.color=0}function Mc(a,b,c,d,e,f,g,h){var k=g.x,g=g.y,i=k-a+c-h,j=g-b+d+15,l;i<7&&(i=c+k+h);i+a>c+e&&(i-=i+a-(c+e),j=g-b+d-h,l=!0);j<d+5?(j=d+5,l&&g>=j&&g<=j+b&&(j=g+d+h)):j+b>d+f&&(j=d+f-b-h);return{x:i,y:j}}function Nc(a,
b){var c=a.length,d,e;for(e=0;e<c;e++)a[e].ss_i=e;a.sort(function(a,c){d=b(a,c);return d===0?a.ss_i-c.ss_i:d});for(e=0;e<c;e++)delete a[e].ss_i}function Jb(a){for(var b=a.length,c=a[0];b--;)a[b]<c&&(c=a[b]);return c}function Bb(a){for(var b=a.length,c=a[0];b--;)a[b]>c&&(c=a[b]);return c}function Cb(a){for(var b in a)a[b]&&a[b].destroy&&a[b].destroy(),delete a[b]}function Kb(a,b){Nb=q(a,b.animation)}function xc(){var a=la.global.useUTC;Zb=a?Date.UTC:function(a,c,d,e,f,g){return(new Date(a,c,q(d,1),
q(e,0),q(f,0),q(g,0))).getTime()};hc=a?"getUTCMinutes":"getMinutes";ic=a?"getUTCHours":"getHours";kc=a?"getUTCDay":"getDay";ub=a?"getUTCDate":"getDate";Xb=a?"getUTCMonth":"getMonth";Yb=a?"getUTCFullYear":"getFullYear";sc=a?"setUTCMinutes":"setMinutes";tc=a?"setUTCHours":"setHours";jc=a?"setUTCDate":"setDate";uc=a?"setUTCMonth":"setMonth";vc=a?"setUTCFullYear":"setFullYear"}function Db(a){$b||($b=Y(vb));a&&$b.appendChild(a);$b.innerHTML=""}function Eb(){}function ac(a,b){function c(a){function b(a,
c){this.pos=a;this.type=c||"";this.isNew=!0;c||this.addLabel()}function c(a){if(a)this.options=a,this.id=a.id;return this}function d(a,b,c,e){this.isNegative=b;this.options=a;this.x=c;this.stack=e;this.alignOptions={align:a.align||(ea?b?"left":"right":"center"),verticalAlign:a.verticalAlign||(ea?"middle":b?"bottom":"top"),y:q(a.y,ea?4:b?14:-6),x:q(a.x,ea?b?-6:6:0)};this.textAlign=a.textAlign||(ea?b?"right":"left":"center")}function e(){var a=[],b=[],c;Ua=C=null;n(D.series,function(e){if(e.visible||
!v.ignoreHiddenSeries){var f=e.options,g,h,j,k,m,l,o,Q,p,D=f.threshold,fa,n=[],yc=0;if(i)f=e.xData,f.length&&(Ua=wa(q(Ua,f[0]),Jb(f)),C=R(q(C,f[0]),Bb(f)));else{var u,t,r,H=e.cropped,s=e.xAxis.getExtremes(),va=!!e.modifyValue;g=f.stacking;lb=g==="percent";if(g)m=f.stack,k=e.type+q(m,""),l="-"+k,e.stackKey=k,h=a[k]||[],a[k]=h,j=b[l]||[],b[l]=j;lb&&(Ua=0,C=99);e.processData();f=e.processedXData;o=e.processedYData;fa=o.length;for(c=0;c<fa;c++)if(Q=f[c],p=o[c],p!==null&&p!==B&&(g?(t=(u=p<D)?j:h,r=u?l:
k,p=t[Q]=z(t[Q])?t[Q]+p:p,w[r]||(w[r]={}),w[r][Q]||(w[r][Q]=new d(A.stackLabels,u,Q,m)),w[r][Q].setTotal(p)):va&&(p=e.modifyValue(p)),H||(f[c+1]||Q)>=s.min&&(f[c-1]||Q)<=s.max))if(Q=p.length)for(;Q--;)p[Q]!==null&&(n[yc++]=p[Q]);else n[yc++]=p;!lb&&n.length&&(Ua=wa(q(Ua,n[0]),Jb(n)),C=R(q(C,n[0]),Bb(n)));e.useThreshold&&D!==null&&(Ua>=D?(Ua=D,ka=!0):C<D&&(C=D,pa=!0))}}})}function f(a){var b;b=a;ya=q(ya,sa.pow(10,Ra(sa.log(Ca)/sa.LN10)));ya<1&&(b=y(1/ya)*10,b=y(a*b)/b);return b}function g(a){var b,
c,d=A.tickInterval,e=A.tickPixelInterval;a&&D.beforeSetTickPositions&&D.beforeSetTickPositions();Y?(c=o[i?"xAxis":"yAxis"][A.linkedTo],b=c.getExtremes(),N=q(b.min,b.dataMin),S=q(b.max,b.dataMax)):(N=q(Oa,A.min,Ua),S=q(gb,A.max,C));r&&(N=Hb(N),S=Hb(S));ga&&(Oa=N=R(N,S-ga),gb=S,a&&(ga=null));var h;b=(D.pointRange||0)/2;var j=C-Ua>P,k;a&&P===B&&(P=i&&!z(A.min)&&!z(A.max)?wa(D.closestPointRange*5,C-Ua):null);S-N<P&&(h=(P-S+N)/2,h=[N-h,q(A.min,N-h)],j&&(h[2]=Ua-b),N=Bb(h),k=[N+P,q(A.max,N+P)],j&&(k[2]=
C+b),S=Jb(k),S-N<P&&(h[0]=S-P,h[1]=q(A.min,S-P),N=Bb(h)));if(!Sa&&!lb&&!Y&&z(N)&&z(S)){b=S-N||1;if(!z(A.min)&&!z(Oa)&&la&&(Ua<0||!ka))N-=b*la;if(!z(A.max)&&!z(gb)&&ma&&(C>0||!pa))S+=b*ma}Ca=N===S||N===void 0||S===void 0?1:Y&&!d&&e===c.options.tickPixelInterval?c.tickInterval:q(d,Sa?1:(S-N)*e/(s||1));a&&D.postProcessTickInterval&&(Ca=D.postProcessTickInterval(Ca));t||(ya=sa.pow(10,Ra(sa.log(Ca)/sa.LN10)),z(A.tickInterval)||(Ca=qc(Ca,null,ya,A)));D.tickInterval=Ca;rb=A.minorTickInterval==="auto"&&Ca?
Ca/5:A.minorTickInterval;ca=A.tickPositions||Ja&&Ja.apply(D,[N,S]);if(!ca)if(t)ca=rc(Ca,N,S,A.startOfWeek,A.units);else{var m,d=f(Ra(N/Ca)*Ca);c=f(bc(S/Ca)*Ca);for(ca=[];d<=c;){ca.push(d);d=f(d+Ca);if(d===m)break;m=d}}a&&U(D,"afterSetTickPositions",{tickPositions:ca});if(!Y&&(a=ca[0],m=ca[ca.length-1],A.startOnTick?N=a:N>a&&ca.shift(),A.endOnTick?S=m:S<m&&ca.pop(),jb||(jb={x:0,y:0}),!t&&ca.length>jb[va]&&A.alignTicks!==!1))jb[va]=ca.length}function h(a){a=(new c(a)).render();ua.push(a);return a}function j(){var a=
A.title,d=A.stackLabels,e=A.alternateGridColor,f=A.lineWidth,g,k,i=o.hasRendered&&z(db)&&!isNaN(db),Q=(g=D.series.length&&z(N)&&z(S))||q(A.showEmpty,!0);if(g||Y){if(rb&&!Sa)for(g=N+(ca[0]-N)%rb;g<=S;g+=rb)ja[g]||(ja[g]=new b(g,"minor")),i&&ja[g].isNew&&ja[g].render(null,!0),ja[g].isActive=!0,ja[g].render();n(ca,function(a,c){if(!Y||a>=N&&a<=S)Qa[a]||(Qa[a]=new b(a)),i&&Qa[a].isNew&&Qa[a].render(c,!0),Qa[a].isActive=!0,Qa[a].render(c)});e&&n(ca,function(a,b){if(b%2===0&&a<S)xa[a]||(xa[a]=new c),xa[a].options=
{from:a,to:ca[b+1]!==B?ca[b+1]:S,color:e},xa[a].render(),xa[a].isActive=!0});if(!D._addedPlotLB)n((A.plotLines||[]).concat(A.plotBands||[]),function(a){h(a)}),D._addedPlotLB=!0}n([Qa,ja,xa],function(a){for(var b in a)a[b].isActive?a[b].isActive=!1:(a[b].destroy(),delete a[b])});f&&(g=Da+(l?x:0)+H,k=Ia-Za-(l?kb:0)+H,g=Z.crispLine([ta,m?Da:g,m?k:za,ha,m?ia-F:g,m?k:Ia-Za],f),T?T.animate({d:g}):T=Z.path(g).attr({stroke:A.lineColor,"stroke-width":f,zIndex:7}).add(),T[Q?"show":"hide"]());if(fa&&Q)Q=m?Da:
za,f=O(a.style.fontSize||12),Q={low:Q+(m?0:s),middle:Q+s/2,high:Q+(m?s:0)}[a.align],f=(m?za+kb:Da)+(m?1:-1)*(l?-1:1)*La+(p===2?f:0),fa[fa.isNew?"attr":"animate"]({x:m?Q:f+(l?x:0)+H+(a.x||0),y:m?f-(l?kb:0)+H:Q+(a.y||0)}),fa.isNew=!1;if(d&&d.enabled){var r,u,d=D.stackTotalGroup;if(!d)D.stackTotalGroup=d=Z.g("stack-labels").attr({visibility:Wa,zIndex:6}).translate($,V).add();for(r in w)for(u in a=w[r],a)a[u].render(d)}D.isDirty=!1}function k(a){for(var b=ua.length;b--;)ua[b].id===a&&ua[b].destroy()}
var i=a.isX,l=a.opposite,m=ea?!i:i,p=m?l?0:2:l?1:3,w={},A=G(i?cc:lc,[Oc,Pc,zc,Qc][p],a),D=this,fa,u=A.type,t=u==="datetime",r=u==="logarithmic",H=A.offset||0,va=i?"x":"y",s=0,Na,ba,E,da,Da,za,x,kb,Za,F,L,M,J,eb,T,Ua,C,P=A.minRange||A.maxZoom,ga=A.range,Oa,gb,X,Mb,S=null,N=null,db,aa,la=A.minPadding,ma=A.maxPadding,na=0,Y=z(A.linkedTo),ka,pa,lb,u=A.events,Ea,ua=[],Ca,rb,ya,ca,Ja=A.tickPositioner,Qa={},ja={},xa={},Ya,oa,La,Sa=A.categories,Pa=A.labels.formatter||function(){var a=this.value,b=this.dateTimeLabelFormat;
return b?wb(b,a):Ca%1E6===0?a/1E6+"M":Ca%1E3===0?a/1E3+"k":!Sa&&a>=1E3?Wb(a,0):a},Xa=m&&A.labels.staggerLines,Fb=A.reversed,qa=Sa&&A.tickmarkPlacement==="between"?0.5:0;b.prototype={addLabel:function(){var a=this.pos,b=A.labels,c=Sa&&m&&Sa.length&&!b.step&&!b.staggerLines&&!b.rotation&&Aa/Sa.length||!m&&Aa/2,d=a===ca[0],e=a===ca[ca.length-1],f=Sa&&z(Sa[a])?Sa[a]:a,g=this.label,h;if(t)h=ca.info,h=A.dateTimeLabelFormats[h.higherRanks[a]||h.unitName];this.isFirst=d;this.isLast=e;a=Pa.call({isFirst:d,
isLast:e,dateTimeLabelFormat:h,value:r?pc(f):f});c=c&&{width:R(1,y(c-2*(b.padding||10)))+Fa};c=I(c,b.style);z(g)?g&&g.attr({text:a}).css(c):this.label=z(a)&&b.enabled?Z.text(a,0,0,b.useHTML).attr({align:b.align,rotation:b.rotation}).css(c).add(J):null},getLabelSize:function(){var a=this.label;return a?(this.labelBBox=a.getBBox())[m?"height":"width"]:0},render:function(a,b){var c=this.type,d=this.label,e=this.pos,f=A.labels,g=this.gridLine,h=c?c+"Grid":"grid",j=c?c+"Tick":"tick",k=A[h+"LineWidth"],
i=A[h+"LineColor"],o=A[h+"LineDashStyle"],Q=A[j+"Length"],h=A[j+"Width"]||0,p=A[j+"Color"],D=A[j+"Position"],j=this.mark,w=f.step,fa=b&&Ka||Ia,n;n=m?L(e+qa,null,null,b)+E:Da+H+(l?(b&&Ma||ia)-F-Da:0);fa=m?fa-Za+H-(l?kb:0):fa-L(e+qa,null,null,b)-E;if(k){e=M(e+qa,k,b);if(g===B){g={stroke:i,"stroke-width":k};if(o)g.dashstyle=o;if(!c)g.zIndex=1;this.gridLine=g=k?Z.path(e).attr(g).add(eb):null}!b&&g&&e&&g.animate({d:e})}if(h)D==="inside"&&(Q=-Q),l&&(Q=-Q),c=Z.crispLine([ta,n,fa,ha,n+(m?0:-Q),fa+(m?Q:0)],
h),j?j.animate({d:c}):this.mark=Z.path(c).attr({stroke:p,"stroke-width":h}).add(J);d&&!isNaN(n)&&(n=n+f.x-(qa&&m?qa*ba*(Fb?-1:1):0),fa=fa+f.y-(qa&&!m?qa*ba*(Fb?1:-1):0),z(f.y)||(fa+=O(d.styles.lineHeight)*0.9-d.getBBox().height/2),Xa&&(fa+=a/(w||1)%Xa*16),this.isFirst&&!q(A.showFirstLabel,1)||this.isLast&&!q(A.showLastLabel,1)?d.hide():d.show(),w&&a%w&&d.hide(),d[this.isNew?"attr":"animate"]({x:n,y:fa}));this.isNew=!1},destroy:function(){Cb(this)}};c.prototype={render:function(){var a=this,b=a.options,
c=b.label,d=a.label,e=b.width,f=b.to,g=b.from,h=b.value,j,k=b.dashStyle,i=a.svgElem,l=[],o,Q,p=b.color;Q=b.zIndex;var fa=b.events;r&&(g=Hb(g),f=Hb(f),h=Hb(h));if(e){if(l=M(h,e),b={stroke:p,"stroke-width":e},k)b.dashstyle=k}else if(z(g)&&z(f))g=R(g,N),f=wa(f,S),j=M(f),(l=M(g))&&j?l.push(j[4],j[5],j[1],j[2]):l=null,b={fill:p};else return;if(z(Q))b.zIndex=Q;if(i)l?i.animate({d:l},null,i.onGetPath):(i.hide(),i.onGetPath=function(){i.show()});else if(l&&l.length&&(a.svgElem=i=Z.path(l).attr(b).add(),fa))for(o in k=
function(b){i.on(b,function(c){fa[b].apply(a,[c])})},fa)k(o);if(c&&z(c.text)&&l&&l.length&&x>0&&kb>0){c=G({align:m&&j&&"center",x:m?!j&&4:10,verticalAlign:!m&&j&&"middle",y:m?j?16:10:j?6:-4,rotation:m&&!j&&90},c);if(!d)a.label=d=Z.text(c.text,0,0).attr({align:c.textAlign||c.align,rotation:c.rotation,zIndex:Q}).css(c.style).add();j=[l[1],l[4],q(l[6],l[1])];l=[l[2],l[5],q(l[7],l[2])];o=Jb(j);Q=Jb(l);d.align(c,!1,{x:o,y:Q,width:Bb(j)-o,height:Bb(l)-Q});d.show()}else d&&d.hide();return a},destroy:function(){Cb(this);
Ib(ua,this)}};d.prototype={destroy:function(){Cb(this)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options.formatter.call(this);this.label?this.label.attr({text:b,visibility:Ta}):this.label=o.renderer.text(b,0,0).css(this.options.style).attr({align:this.textAlign,rotation:this.options.rotation,visibility:Ta}).add(a)},setOffset:function(a,b){var c=this.isNegative,d=D.translate(this.total),e=D.translate(0),e=Ha(d-e),f=o.xAxis[0].translate(this.x)+a,g=o.plotHeight,c={x:ea?
c?d:d-e:f,y:ea?g-f-b:c?g-d-e:g-d,width:ea?e:b,height:ea?b:e};this.label&&this.label.align(this.alignOptions,null,c).attr({visibility:Wa})}};L=function(a,b,c,d,e){var f=1,g=0,h=d?da:ba,d=d?db:N,e=A.ordinal||r&&e;h||(h=ba);c&&(f*=-1,g=s);Fb&&(f*=-1,g-=f*s);b?(Fb&&(a=s-a),a=a/h+d,e&&(a=D.lin2val(a))):(e&&(a=D.val2lin(a)),a=f*(a-d)*h+g+f*na);return a};M=function(a,b,c){var d,e,f,a=L(a,null,null,c),g=c&&Ka||Ia,h=c&&Ma||ia,j,c=e=y(a+E);d=f=y(g-a-E);if(isNaN(a))j=!0;else if(m){if(d=za,f=g-Za,c<Da||c>Da+
x)j=!0}else if(c=Da,e=h-F,d<za||d>za+kb)j=!0;return j?null:Z.crispLine([ta,c,d,ha,e,f],b||0)};Ga.push(D);o[i?"xAxis":"yAxis"].push(D);ea&&i&&Fb===B&&(Fb=!0);I(D,{addPlotBand:h,addPlotLine:h,adjustTickAmount:function(){if(jb&&jb[va]&&!t&&!Sa&&!Y&&A.alignTicks!==!1){var a=Ya,b=ca.length;Ya=jb[va];if(b<Ya){for(;ca.length<Ya;)ca.push(f(ca[ca.length-1]+Ca));ba*=(b-1)/(Ya-1);S=ca[ca.length-1]}if(z(a)&&Ya!==a)D.isDirty=!0}},categories:Sa,getExtremes:function(){return{min:N,max:S,dataMin:Ua,dataMax:C,userMin:Oa,
userMax:gb}},getPlotLinePath:M,getThreshold:function(a){N>a||a===null?a=N:S<a&&(a=S);return L(a,0,1)},isXAxis:i,options:A,plotLinesAndBands:ua,getOffset:function(){var a=D.series.length&&z(N)&&z(S),c=a||q(A.showEmpty,!0),d=0,e=0,f=A.title,g=A.labels,h=[-1,1,1,-1][p],j;J||(J=Z.g("axis").attr({zIndex:7}).add(),eb=Z.g("grid").attr({zIndex:A.gridZIndex||1}).add());oa=0;if(a||Y)n(ca,function(a){Qa[a]?Qa[a].addLabel():Qa[a]=new b(a)}),n(ca,function(a){if(p===0||p===2||{1:"left",3:"right"}[p]===g.align)oa=
R(Qa[a].getLabelSize(),oa)}),Xa&&(oa+=(Xa-1)*16);else for(j in Qa)Qa[j].destroy(),delete Qa[j];if(f&&f.text){if(!fa)fa=D.axisTitle=Z.text(f.text,0,0,f.useHTML).attr({zIndex:7,rotation:f.rotation||0,align:f.textAlign||{low:"left",middle:"center",high:"right"}[f.align]}).css(f.style).add(),fa.isNew=!0;c&&(d=fa.getBBox()[m?"height":"width"],e=q(f.margin,m?5:10));fa[c?"show":"hide"]()}H=h*q(A.offset,K[p]);La=q(f.offset,oa+e+(p!==2&&oa&&h*A.labels[m?"y":"x"]));K[p]=R(K[p],La+d+h*H)},render:j,setAxisSize:function(){var a=
A.offsetLeft||0,b=A.offsetRight||0,c=S-N,d=0,e,f;Da=q(A.left,$+a);za=q(A.top,V);x=q(A.width,Aa-a+b);kb=q(A.height,Ba);Za=Ia-kb-za;F=ia-x-Da;s=m?x:kb;if(i){n(D.series,function(a){d=R(d,a.pointRange);f=a.closestPointRange;!a.noSharedTooltip&&z(f)&&(e=z(e)?wa(e,f):f)});if((z(Oa)||z(gb))&&d>Ca/2)d=0;D.pointRange=d;D.closestPointRange=e}D.translationSlope=ba=s/(c+d||1);E=m?Da:Za;na=ba*(d/2);D.left=Da;D.top=za;D.len=s},setCategories:function(b,c){D.categories=a.categories=Sa=b;n(D.series,function(a){a.translate();
a.setTooltipPoints(!0)});D.isDirty=!0;q(c,!0)&&o.redraw()},setExtremes:function(a,b,c,d){c=q(c,!0);U(D,"setExtremes",{min:a,max:b},function(){Oa=a;gb=b;c&&o.redraw(d)});U(D,"afterSetExtremes",{min:N,max:S})},setScale:function(){var a,b,c;db=N;aa=S;Na=s;s=m?x:kb;n(D.series,function(a){if(a.isDirtyData||a.isDirty||a.xAxis.isDirty)c=!0});if(s!==Na||c||Y||Oa!==X||gb!==Mb){e();g();X=Oa;Mb=gb;da=ba;D.translationSlope=ba=s/(S-N+(D.pointRange||0)||1);if(!i)for(a in w)for(b in w[a])w[a][b].cum=w[a][b].total;
if(!D.isDirty)D.isDirty=o.isDirtyBox||N!==db||S!==aa}},setTickPositions:g,translate:L,redraw:function(){xb.resetTracker&&xb.resetTracker();A.ordinal&&g(!0);j();n(ua,function(a){a.render()});n(D.series,function(a){a.isDirty=!0})},removePlotBand:k,removePlotLine:k,reversed:Fb,series:[],stacks:w,destroy:function(){var a;ra(D);for(a in w)Cb(w[a]),w[a]=null;if(D.stackTotalGroup)D.stackTotalGroup=D.stackTotalGroup.destroy();n([Qa,ja,xa,ua],function(a){Cb(a)});n([T,J,eb,fa],function(a){a&&a.destroy()});
T=J=eb=fa=null}});for(Ea in u)W(D,Ea,u[Ea]);if(r)D.val2lin=Hb,D.lin2val=pc}function d(){var b={};return{add:function(c,d,e,f){b[c]||(d=Z.text(d,0,0).css(a.toolbar.itemStyle).align({align:"right",x:-J-20,y:V+30}).on("click",f).attr({align:"right",zIndex:20}).add(),b[c]=d)},remove:function(a){Db(b[a].element);b[a]=null}}}function e(a){function b(){var a=this.points||tb(this),c=a[0].series,d;d=[c.tooltipHeaderFormatter(a[0].key)];n(a,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||
a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});return d.join("")}function c(a,b){l=m?a:(2*l+a)/3;p=m?b:(p+b)/2;w.attr({x:l,y:p});$a=Ha(a-l)>1||Ha(b-p)>1?function(){c(a,b)}:null}function d(){if(!m){var a=o.hoverPoints;w.hide();a&&n(a,function(a){a.setState()});o.hoverPoints=null;m=!0}}var e,f=a.borderWidth,g=a.crosshairs,h=[],j=a.style,k=a.shared,i=O(j.padding),m=!0,l=0,p=0;j.padding=0;var w=Z.label("",0,0).attr({padding:i,fill:a.backgroundColor,"stroke-width":f,r:a.borderRadius,zIndex:8}).css(j).hide().add().shadow(a.shadow);
return{shared:k,refresh:function(f){var j,i,l,p,r={},u=[];l=f.tooltipPos;j=a.formatter||b;r=o.hoverPoints;k&&(!f.series||!f.series.noSharedTooltip)?(p=0,r&&n(r,function(a){a.setState()}),o.hoverPoints=f,n(f,function(a){a.setState(cb);p+=a.plotY;u.push(a.getLabelConfig())}),i=f[0].plotX,p=y(p)/f.length,r={x:f[0].category},r.points=u,f=f[0]):r=f.getLabelConfig();r=j.call(r);e=f.series;i=q(i,f.plotX);p=q(p,f.plotY);j=y(l?l[0]:ea?Aa-p:i);i=y(l?l[1]:ea?Ba-i:p);l=k||!f.series.isCartesian||Xa(j,i);r===!1||
!l?d():(m&&(w.show(),m=!1),w.attr({text:r}),w.attr({stroke:a.borderColor||f.color||e.color||"#606060"}),i=Mc(w.width,w.height,$,V,Aa,Ba,{x:j,y:i},q(a.distance,12)),c(y(i.x),y(i.y)));if(g){g=tb(g);for(i=g.length;i--;)if(l=f.series[i?"yAxis":"xAxis"],g[i]&&l)if(l=l.getPlotLinePath(f[i?"y":"x"],1),h[i])h[i].attr({d:l,visibility:Wa});else{j={"stroke-width":g[i].width||1,stroke:g[i].color||"#C0C0C0",zIndex:g[i].zIndex||2};if(g[i].dashStyle)j.dashstyle=g[i].dashStyle;h[i]=Z.path(l).attr(j).add()}}},hide:d,
hideCrosshairs:function(){n(h,function(a){a&&a.hide()})},destroy:function(){n(h,function(a){a&&a.destroy()});w&&(w=w.destroy())}}}function f(a){function b(a){var c,d=Ac&&T.width/T.body.scrollWidth-1,e,f,g,a=a||ja.event;if(!a.target)a.target=a.srcElement;if(a.originalEvent)a=a.originalEvent;if(a.event)a=a.event;c=a.touches?a.touches.item(0):a;qb=Bc(F);e=qb.left;f=qb.top;Vb?(g=a.x,c=a.y):(g=c.pageX-e,c=c.pageY-f);d&&(g+=y((d+1)*e-e),c+=y((d+1)*f-f));return I(a,{chartX:g,chartY:c})}function c(a){var b=
{xAxis:[],yAxis:[]};n(Ga,function(c){var d=c.translate,e=c.isXAxis;b[e?"xAxis":"yAxis"].push({axis:c,value:d((ea?!e:e)?a.chartX-$:Ba-a.chartY+V,!0)})});return b}function d(){var a=o.hoverSeries,b=o.hoverPoint;if(b)b.onMouseOut();if(a)a.onMouseOut();xa&&(xa.hide(),xa.hideCrosshairs());ab=null}function f(){if(l){var a={xAxis:[],yAxis:[]},b=l.getBBox(),c=b.x-$,d=b.y-V;k&&(n(Ga,function(e){if(e.options.zoomEnabled!==!1){var f=e.translate,g=e.isXAxis,h=ea?!g:g,j=f(h?c:Ba-d-b.height,!0,0,0,1),f=f(h?c+b.width:
Ba-d,!0,0,0,1);a[g?"xAxis":"yAxis"].push({axis:e,min:wa(j,f),max:R(j,f)})}}),U(o,"selection",a,pb));l=l.destroy()}C(F,{cursor:"auto"});o.mouseIsDown=Da=k=!1;ra(T,ua?"touchend":"mouseup",f)}function g(a){var b=z(a.pageX)?a.pageX:a.page.x,a=z(a.pageX)?a.pageY:a.page.y;qb&&!Xa(b-qb.left-$,a-qb.top-V)&&d()}function h(){d();qb=null}var j,i,k,l,m=v.zoomType,p=/x/.test(m),w=/y/.test(m),r=p&&!ea||w&&ea,u=w&&!ea||p&&ea;rb=function(){db?(db.translate($,V),ea&&db.attr({width:o.plotWidth,height:o.plotHeight}).invert()):
o.trackerGroup=db=Z.g("tracker").attr({zIndex:9}).add()};rb();if(a.enabled)o.tooltip=xa=e(a);(function(){F.onmousedown=function(a){a=b(a);!ua&&a.preventDefault&&a.preventDefault();o.mouseIsDown=Da=!0;o.mouseDownX=j=a.chartX;i=a.chartY;W(T,ua?"touchend":"mouseup",f)};var e=function(c){if(!c||!(c.touches&&c.touches.length>1)){c=b(c);if(!ua)c.returnValue=!1;var d=c.chartX,e=c.chartY,f=!Xa(d-$,e-V);ua&&c.type==="touchstart"&&(P(c.target,"isTracker")?o.runTrackerClick||c.preventDefault():!bb&&!f&&c.preventDefault());
f&&(d<$?d=$:d>$+Aa&&(d=$+Aa),e<V?e=V:e>V+Ba&&(e=V+Ba));if(Da&&c.type!=="touchstart"){if(k=Math.sqrt(Math.pow(j-d,2)+Math.pow(i-e,2)),k>10){var g=Xa(j-$,i-V);if(Ea&&(p||w)&&g)l||(l=Z.rect($,V,r?1:Aa,u?1:Ba,0).attr({fill:v.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add());l&&r&&(c=d-j,l.attr({width:Ha(c),x:(c>0?0:c)+j}));l&&u&&(e-=i,l.attr({height:Ha(e),y:(e>0?0:e)+i}));g&&!l&&v.panning&&o.pan(d)}}else if(!f){var h,d=o.hoverPoint,e=o.hoverSeries,m,n,g=ia,t=ea?c.chartY:c.chartX-$;if(xa&&
a.shared&&(!e||!e.noSharedTooltip)){h=[];m=ga.length;for(n=0;n<m;n++)if(ga[n].visible&&ga[n].options.enableMouseTracking!==!1&&!ga[n].noSharedTooltip&&ga[n].tooltipPoints.length)c=ga[n].tooltipPoints[t],c._dist=Ha(t-c.plotX),g=wa(g,c._dist),h.push(c);for(m=h.length;m--;)h[m]._dist>g&&h.splice(m,1);if(h.length&&h[0].plotX!==ab)xa.refresh(h),ab=h[0].plotX}if(e&&e.tracker&&(c=e.tooltipPoints[t])&&c!==d)c.onMouseOver()}return f||!Ea}};F.onmousemove=e;W(F,"mouseleave",h);W(T,"mousemove",g);F.ontouchstart=
function(a){if(p||w)F.onmousedown(a);e(a)};F.ontouchmove=e;F.ontouchend=function(){k&&d()};F.onclick=function(a){var d=o.hoverPoint,a=b(a);a.cancelBubble=!0;if(!k)if(d&&P(a.target,"isTracker")){var e=d.plotX,f=d.plotY;I(d,{pageX:qb.left+$+(ea?Aa-f:e),pageY:qb.top+V+(ea?Ba-e:f)});U(d.series,"click",I(a,{point:d}));d.firePointEvent("click",a)}else I(a,c(a)),Xa(a.chartX-$,a.chartY-V)&&U(o,"click",a);k=!1}})();ib=setInterval(function(){$a&&$a()},32);I(this,{zoomX:p,zoomY:w,resetTracker:d,normalizeMouseEvent:b,
destroy:function(){if(o.trackerGroup)o.trackerGroup=db=o.trackerGroup.destroy();ra(F,"mouseleave",h);ra(T,"mousemove",g);F.onclick=F.onmousedown=F.onmousemove=F.ontouchstart=F.ontouchend=F.ontouchmove=null}})}function g(a){var b=a.type||v.type||v.defaultSeriesType,c=aa[b],d=o.hasRendered;if(d)if(ea&&b==="column")c=aa.bar;else if(!ea&&b==="bar")c=aa.column;b=new c;b.init(o,a);!d&&b.inverted&&(ea=!0);if(b.isCartesian)Ea=b.isCartesian;ga.push(b);return b}function h(){v.alignTicks!==!1&&n(Ga,function(a){a.adjustTickAmount()});
jb=null}function k(a){var b=o.isDirtyLegend,c,d=o.isDirtyBox,e=ga.length,f=e,g=o.clipRect;for(Kb(a,o);f--;)if(a=ga[f],a.isDirty&&a.options.stacking){c=!0;break}if(c)for(f=e;f--;)if(a=ga[f],a.options.stacking)a.isDirty=!0;n(ga,function(a){a.isDirty&&a.options.legendType==="point"&&(b=!0)});if(b&&Ya.renderLegend)Ya.renderLegend(),o.isDirtyLegend=!1;Ea&&(Pa||(jb=null,n(Ga,function(a){a.setScale()})),h(),qa(),n(Ga,function(a){a.isDirty&&a.redraw()}));d&&(hb(),rb(),g&&(Lb(g),g.animate({width:o.plotSizeX,
height:o.plotSizeY+1})));n(ga,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});xb&&xb.resetTracker&&xb.resetTracker();U(o,"redraw")}function i(){var b=a.xAxis||{},d=a.yAxis||{},b=tb(b);n(b,function(a,b){a.index=b;a.isX=!0});d=tb(d);n(d,function(a,b){a.index=b});b=b.concat(d);n(b,function(a){new c(a)});h()}function j(b,c){eb=G(a.title,b);L=G(a.subtitle,c);n([["title",b,eb],["subtitle",c,L]],function(a){var b=a[0],c=o[b],d=a[1],a=a[2];c&&d&&(c=c.destroy());a&&a.text&&!c&&(o[b]=
Z.text(a.text,0,0,a.useHTML).attr({align:a.align,"class":fb+b,zIndex:1}).css(a.style).add().align(a,!1,x))})}function l(){M=v.renderTo;ka=fb+mc++;Ab(M)&&(M=T.getElementById(M));M.innerHTML="";M.offsetWidth||(X=M.cloneNode(0),C(X,{position:Gb,top:"-9999px",display:""}),T.body.appendChild(X));na=(X||M).offsetWidth;lb=(X||M).offsetHeight;o.chartWidth=ia=v.width||na||600;o.chartHeight=Ia=v.height||(lb>19?lb:400);o.container=F=Y(vb,{className:fb+"container"+(v.className?" "+v.className:""),id:ka},I({position:Cc,
overflow:Ta,width:ia+Fa,height:Ia+Fa,textAlign:"left",lineHeight:"normal"},v.style),X||M);o.renderer=Z=v.forExport?new Ob(F,ia,Ia,!0):new Pb(F,ia,Ia);var a,b;Dc&&F.getBoundingClientRect&&(a=function(){C(F,{left:0,top:0});b=F.getBoundingClientRect();C(F,{left:-(b.left-O(b.left))+Fa,top:-(b.top-O(b.top))+Fa})},a(),W(ja,"resize",a),W(o,"destroy",function(){ra(ja,"resize",a)}))}function m(){function a(){var c=v.width||M.offsetWidth,d=v.height||M.offsetHeight;if(c&&d){if(c!==na||d!==lb)clearTimeout(b),
b=setTimeout(function(){ob(c,d,!1)},100);na=c;lb=d}}var b;W(ja,"resize",a);W(o,"destroy",function(){ra(ja,"resize",a)})}function p(){o&&U(o,"endResize",null,function(){Pa-=1})}function H(){for(var b=ea||v.inverted||v.type==="bar"||v.defaultSeriesType==="bar",c=a.series,d=c&&c.length;!b&&d--;)c[d].type==="bar"&&(b=!0);o.inverted=ea=b}function s(){var b=a.labels,c=a.credits,e;j();Ya=o.legend=new Eb;n(Ga,function(a){a.setScale()});qa();n(Ga,function(a){a.setTickPositions(!0)});h();qa();hb();Ea&&n(Ga,
function(a){a.render()});if(!o.seriesGroup)o.seriesGroup=Z.g("series-group").attr({zIndex:3}).add();n(ga,function(a){a.translate();a.setTooltipPoints();a.render()});b.items&&n(b.items,function(){var a=I(b.style,this.style),c=O(a.left)+$,d=O(a.top)+V+12;delete a.left;delete a.top;Z.text(this.html,c,d).attr({zIndex:2}).css(a).add()});if(!o.toolbar)o.toolbar=d();if(c.enabled&&!o.credits)e=c.href,o.credits=Z.text(c.text,0,0).on("click",function(){if(e)location.href=e}).attr({align:c.position.align,zIndex:8}).css(c.style).add().align(c.position);
rb();o.hasRendered=!0;X&&(M.appendChild(F),Db(X))}function t(){if(!Qb&&ja==ja.top&&T.readyState!=="complete")T.attachEvent("onreadystatechange",function(){T.detachEvent("onreadystatechange",t);T.readyState==="complete"&&t()});else{l();U(o,"init");if(Highcharts.RangeSelector&&a.rangeSelector.enabled)o.rangeSelector=new Highcharts.RangeSelector(o);mb();nb();H();i();n(a.series||[],function(a){g(a)});if(Highcharts.Scroller&&(a.navigator.enabled||a.scrollbar.enabled))o.scroller=new Highcharts.Scroller(o);
o.render=s;o.tracker=xb=new f(a.tooltip);s();b&&b.apply(o,[o]);n(o.callbacks,function(a){a.apply(o,[o])});U(o,"load")}}var r=a.series;a.series=null;a=G(la,a);a.series=r;var v=a.chart,r=v.margin,r=sb(r)?r:[r,r,r,r],u=q(v.marginTop,r[0]),Na=q(v.marginRight,r[1]),va=q(v.marginBottom,r[2]),E=q(v.marginLeft,r[3]),Za=v.spacingTop,w=v.spacingRight,ba=v.spacingBottom,da=v.spacingLeft,x,eb,L,V,J,Oa,$,K,M,X,F,ka,na,lb,ia,Ia,Ma,Ka,ma,pa,Ja,ya,o=this,bb=(r=v.events)&&!!r.click,Va,Xa,xa,Da,za,gb,Mb,Ba,Aa,xb,db,
rb,Ya,yb,zb,qb,Ea=v.showAxes,Pa=0,Ga=[],jb,ga=[],ea,Z,$a,ib,ab,hb,qa,mb,nb,ob,pb,ub,Eb=function(){function a(b,c){var d=b.legendItem,e=b.legendLine,g=b.legendSymbol,h=p.color,j=c?f.itemStyle.color:h,h=c?b.color:h;d&&d.css({fill:j});e&&e.attr({stroke:h});g&&g.attr({stroke:h,fill:h})}function b(a,c,d){var e=a.legendItem,f=a.legendLine,g=a.legendSymbol,a=a.checkbox;e&&e.attr({x:c,y:d});f&&f.translate(c,d-4);g&&g.attr({x:c+g.xOff,y:d+g.yOff});if(a)a.x=c,a.y=d}function c(){n(i,function(a){var b=a.checkbox,
c=E.alignAttr;b&&C(b,{left:c.translateX+a.legendItemWidth+b.x-40+Fa,top:c.translateY+b.y-11+Fa})})}function d(c){var e,i,k,o,n=c.legendItem;o=c.series||c;var r=o.options,ba=r&&r.borderWidth||0;if(!n){o=/^(bar|pie|area|column)$/.test(o.type);c.legendItem=n=Z.text(f.labelFormatter.call(c),0,0).css(c.visible?l:p).on("mouseover",function(){c.setState(cb);n.css(m)}).on("mouseout",function(){n.css(c.visible?l:p);c.setState()}).on("click",function(){var a=function(){c.setVisible()};c.firePointEvent?c.firePointEvent("legendItemClick",
null,a):U(c,"legendItemClick",null,a)}).attr({zIndex:2}).add(E);if(!o&&r&&r.lineWidth){var Na={"stroke-width":r.lineWidth,zIndex:2};if(r.dashStyle)Na.dashstyle=r.dashStyle;c.legendLine=Z.path([ta,-h-j,0,ha,-j,0]).attr(Na).add(E)}if(o)k=Z.rect(e=-h-j,i=-11,h,12,2).attr({zIndex:3}).add(E);else if(r&&r.marker&&r.marker.enabled)k=r.marker.radius,k=Z.symbol(c.symbol,e=-h/2-j-k,i=-4-k,2*k,2*k).attr(c.pointAttr[oa]).attr({zIndex:3}).add(E);if(k)k.xOff=e+ba%2/2,k.yOff=i+ba%2/2;c.legendSymbol=k;a(c,c.visible);
if(r&&r.showCheckbox)c.checkbox=Y("input",{type:"checkbox",checked:c.selected,defaultChecked:c.selected},f.itemCheckboxStyle,F),W(c.checkbox,"click",function(a){U(c,"checkboxClick",{checked:a.target.checked},function(){c.select()})})}e=n.getBBox();i=c.legendItemWidth=f.itemWidth||h+j+e.width+w;s=e.height;if(g&&t-u+i>(za||ia-2*w-u))t=u,v+=va+s+q;H=v+q;b(c,t,v);g?t+=i:v+=va+s+q;da=za||R(g?t-u:i,da)}function e(){t=u;v=w+va+r-5;H=da=0;E||(E=Z.g("legend").attr({zIndex:10}).add());i=[];n(z,function(a){var b=
a.options;b.showInLegend&&(i=i.concat(a.legendItems||(b.legendType==="point"?a.data:a)))});Nc(i,function(a,b){return(a.options.legendIndex||0)-(b.options.legendIndex||0)});y&&i.reverse();n(i,d);yb=za||da;zb=H-r+s;if(Na||Da){yb+=2*w;zb+=2*w;if(ba){if(yb>0&&zb>0)ba[ba.isNew?"attr":"animate"](ba.crisp(null,null,null,yb,zb)),ba.isNew=!1}else ba=Z.rect(0,0,yb,zb,f.borderRadius,Na||0).attr({stroke:f.borderColor,"stroke-width":Na||0,fill:Da||La}).add(E).shadow(f.shadow),ba.isNew=!0;ba[i.length?"show":"hide"]()}for(var a=
["left","right","top","bottom"],b,g=4;g--;)b=a[g],k[b]&&k[b]!=="auto"&&(f[g<2?"align":"verticalAlign"]=b,f[g<2?"x":"y"]=O(k[b])*(g%2?-1:1));i.length&&E.align(I(f,{width:yb,height:zb}),!0,x);Pa||c()}var f=o.options.legend;if(f.enabled){var g=f.layout==="horizontal",h=f.symbolWidth,j=f.symbolPadding,i,k=f.style,l=f.itemStyle,m=f.itemHoverStyle,p=G(l,f.itemHiddenStyle),w=f.padding||O(k.padding),r=18,u=4+w+h+j,t,v,H,s=0,va=f.itemMarginTop||0,q=f.itemMarginBottom||0,ba,Na=f.borderWidth,Da=f.backgroundColor,
E,da,za=f.width,z=o.series,y=f.reversed;e();W(o,"endResize",c);return{colorizeItem:a,destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol"],function(b){a[b]&&a[b].destroy()});b&&Db(a.checkbox)},renderLegend:e,destroy:function(){ba&&(ba=ba.destroy());E&&(E=E.destroy())}}}};Xa=function(a,b){return a>=0&&a<=Aa&&b>=0&&b<=Ba};ub=function(){U(o,"selection",{resetSelection:!0},pb);o.toolbar.remove("zoom")};pb=function(a){var b=la.lang,c=o.pointCount<100;o.resetZoomEnabled!==
!1&&o.toolbar.add("zoom",b.resetZoom,b.resetZoomTitle,ub);!a||a.resetSelection?n(Ga,function(a){a.options.zoomEnabled!==!1&&a.setExtremes(null,null,!0,c)}):n(a.xAxis.concat(a.yAxis),function(a){var b=a.axis;o.tracker[b.isXAxis?"zoomX":"zoomY"]&&b.setExtremes(a.min,a.max,!0,c)})};o.pan=function(a){var b=o.xAxis[0],c=o.mouseDownX,d=b.pointRange/2,e=b.getExtremes(),f=b.translate(c-a,!0)+d,c=b.translate(c+Aa-a,!0)-d;(d=o.hoverPoints)&&n(d,function(a){a.setState()});f>wa(e.dataMin,e.min)&&c<R(e.dataMax,
e.max)&&b.setExtremes(f,c,!0,!1);o.mouseDownX=a;C(F,{cursor:"move"})};qa=function(){var b=a.legend,c=q(b.margin,10),d=b.x,e=b.y,f=b.align,g=b.verticalAlign,h;mb();if((o.title||o.subtitle)&&!z(u))(h=R(o.title&&!eb.floating&&!eb.verticalAlign&&eb.y||0,o.subtitle&&!L.floating&&!L.verticalAlign&&L.y||0))&&(V=R(V,h+q(eb.margin,15)+Za));b.enabled&&!b.floating&&(f==="right"?z(Na)||(J=R(J,yb-d+c+w)):f==="left"?z(E)||($=R($,yb+d+c+da)):g==="top"?z(u)||(V=R(V,zb+e+c+Za)):g==="bottom"&&(z(va)||(Oa=R(Oa,zb-e+
c+ba))));o.extraBottomMargin&&(Oa+=o.extraBottomMargin);o.extraTopMargin&&(V+=o.extraTopMargin);Ea&&n(Ga,function(a){a.getOffset()});z(E)||($+=K[3]);z(u)||(V+=K[0]);z(va)||(Oa+=K[2]);z(Na)||(J+=K[1]);nb()};ob=function(a,b,c){var d=o.title,e=o.subtitle;Pa+=1;Kb(c,o);Ka=Ia;Ma=ia;if(z(a))o.chartWidth=ia=y(a);if(z(b))o.chartHeight=Ia=y(b);C(F,{width:ia+Fa,height:Ia+Fa});Z.setSize(ia,Ia,c);Aa=ia-$-J;Ba=Ia-V-Oa;jb=null;n(Ga,function(a){a.isDirty=!0;a.setScale()});n(ga,function(a){a.isDirty=!0});o.isDirtyLegend=
!0;o.isDirtyBox=!0;qa();d&&d.align(null,null,x);e&&e.align(null,null,x);k(c);Ka=null;U(o,"resize");Nb===!1?p():setTimeout(p,Nb&&Nb.duration||500)};nb=function(){o.plotLeft=$=y($);o.plotTop=V=y(V);o.plotWidth=Aa=y(ia-$-J);o.plotHeight=Ba=y(Ia-V-Oa);o.plotSizeX=ea?Ba:Aa;o.plotSizeY=ea?Aa:Ba;x={x:da,y:Za,width:ia-da-w,height:Ia-Za-ba};n(Ga,function(a){a.isDirty&&a.setAxisSize()})};mb=function(){V=q(u,Za);J=q(Na,w);Oa=q(va,ba);$=q(E,da);K=[0,0,0,0]};hb=function(){var a=v.borderWidth||0,b=v.backgroundColor,
c=v.plotBackgroundColor,d=v.plotBackgroundImage,e,f={x:$,y:V,width:Aa,height:Ba};e=a+(v.shadow?8:0);if(a||b)ma?ma.animate(ma.crisp(null,null,null,ia-e,Ia-e)):ma=Z.rect(e/2,e/2,ia-e,Ia-e,v.borderRadius,a).attr({stroke:v.borderColor,"stroke-width":a,fill:b||La}).add().shadow(v.shadow);c&&(pa?pa.animate(f):pa=Z.rect($,V,Aa,Ba,0).attr({fill:c}).add().shadow(v.plotShadow));d&&(Ja?Ja.animate(f):Ja=Z.image(d,$,V,Aa,Ba).add());v.plotBorderWidth&&(ya?ya.animate(ya.crisp(null,$,V,Aa,Ba)):ya=Z.rect($,V,Aa,Ba,
0,v.plotBorderWidth).attr({stroke:v.plotBorderColor,"stroke-width":v.plotBorderWidth,zIndex:4}).add());o.isDirtyBox=!1};v.reflow!==!1&&W(o,"load",m);if(r)for(Va in r)W(o,Va,r[Va]);o.options=a;o.series=ga;o.xAxis=[];o.yAxis=[];o.addSeries=function(a,b,c){var d;a&&(Kb(c,o),b=q(b,!0),U(o,"addSeries",{options:a},function(){d=g(a);d.isDirty=!0;o.isDirtyLegend=!0;b&&o.redraw()}));return d};o.animation=q(v.animation,!0);o.Axis=c;o.destroy=function(){var b,c=F&&F.parentNode;if(o!==null){U(o,"destroy");ra(o);
for(b=Ga.length;b--;)Ga[b]=Ga[b].destroy();for(b=ga.length;b--;)ga[b]=ga[b].destroy();n("title,subtitle,seriesGroup,clipRect,credits,tracker,scroller,rangeSelector".split(","),function(a){var b=o[a];b&&(o[a]=b.destroy())});n([ma,ya,pa,Ya,xa,Z,xb],function(a){a&&a.destroy&&a.destroy()});ma=ya=pa=Ya=xa=Z=xb=null;if(F)F.innerHTML="",ra(F),c&&Db(F),F=null;clearInterval(ib);for(b in o)delete o[b];a=o=null}};o.get=function(a){var b,c,d;for(b=0;b<Ga.length;b++)if(Ga[b].options.id===a)return Ga[b];for(b=
0;b<ga.length;b++)if(ga[b].options.id===a)return ga[b];for(b=0;b<ga.length;b++){d=ga[b].points;for(c=0;c<d.length;c++)if(d[c].id===a)return d[c]}return null};o.getSelectedPoints=function(){var a=[];n(ga,function(b){a=a.concat(nc(b.points,function(a){return a.selected}))});return a};o.getSelectedSeries=function(){return nc(ga,function(a){return a.selected})};o.hideLoading=function(){za&&dc(za,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){C(za,{display:La})}});Mb=!1};o.initSeries=
g;o.isInsidePlot=Xa;o.redraw=k;o.setSize=ob;o.setTitle=j;o.showLoading=function(b){var c=a.loading;za||(za=Y(vb,{className:fb+"loading"},I(c.style,{left:$+Fa,top:V+Fa,width:Aa+Fa,height:Ba+Fa,zIndex:10,display:La}),F),gb=Y("span",null,c.labelStyle,za));gb.innerHTML=b||a.lang.loading;Mb||(C(za,{opacity:0,display:""}),dc(za,{opacity:c.style.opacity},{duration:c.showDuration||0}),Mb=!0)};o.pointCount=0;o.counters=new wc;t()}var B,T=document,ja=window,sa=Math,y=sa.round,Ra=sa.floor,bc=sa.ceil,R=sa.max,
wa=sa.min,Ha=sa.abs,Ma=sa.cos,pa=sa.sin,hb=sa.PI,Ec=hb*2/360,mb=navigator.userAgent,Vb=/msie/i.test(mb)&&!ja.opera,Rb=T.documentMode===8,Ac=/AppleWebKit/.test(mb),Dc=/Firefox/.test(mb),Qb=!!T.createElementNS&&!!T.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,Rc=Dc&&parseInt(mb.split("Firefox/")[1],10)<4,Pb,ua=T.documentElement.ontouchstart!==B,Fc={},mc=0,$b,la,wb,Nb,Sb,L,vb="div",Gb="absolute",Cc="relative",Ta="hidden",fb="highcharts-",Wa="visible",Fa="px",La="none",ta="M",ha=
"L",Gc="rgba(192,192,192,"+(Qb?1.0E-6:0.0020)+")",oa="",cb="hover",pb="millisecond",qa="second",ab="minute",Va="hour",ma="day",Ea="week",Pa="month",bb="year",Zb,hc,ic,kc,ub,Xb,Yb,sc,tc,jc,uc,vc,x=ja.HighchartsAdapter,na=x||{},n=na.each,nc=na.grep,Bc=na.offset,nb=na.map,G=na.merge,W=na.addEvent,ra=na.removeEvent,U=na.fireEvent,dc=na.animate,Lb=na.stop,aa={};ja.Highcharts={};wb=function(a,b,c){function d(a,b){a=a.toString().replace(/^([0-9])$/,"0$1");b===3&&(a=a.toString().replace(/^([0-9]{2})$/,"0$1"));
return a}if(!z(b)||isNaN(b))return"Invalid date";var a=q(a,"%Y-%m-%d %H:%M:%S"),e=new Date(b),f,g=e[ic](),h=e[kc](),k=e[ub](),i=e[Xb](),j=e[Yb](),l=la.lang,m=l.weekdays,b={a:m[h].substr(0,3),A:m[h],d:d(k),e:k,b:l.shortMonths[i],B:l.months[i],m:d(i+1),y:j.toString().substr(2,2),Y:j,H:d(g),I:d(g%12||12),l:g%12||12,M:d(e[hc]()),p:g<12?"AM":"PM",P:g<12?"am":"pm",S:d(e.getSeconds()),L:d(b%1E3,3)};for(f in b)a=a.replace("%"+f,b[f]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};wc.prototype={wrapColor:function(a){if(this.color>=
a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};L=Ka(pb,1,qa,1E3,ab,6E4,Va,36E5,ma,864E5,Ea,6048E5,Pa,2592E6,bb,31556952E3);Sb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,k,i=function(a){for(g=a.length;g--;)a[g]===ta&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(i(b),i(c));a.isArea&&(h=b.splice(b.length-6,6),k=c.splice(c.length-6,6));d===1&&(c=[].concat(c).splice(0,f).concat(c));a.shift=0;if(b.length)for(a=
c.length;b.length<a;)d=[].concat(b).splice(b.length-f,f),e&&(d[f-6]=d[f-2],d[f-5]=d[f-1]),b=b.concat(d);h&&(b=b.concat(h),c=c.concat(k));return[b,c]},step:function(a,b,c,d){var e=[],f=a.length;if(c===1)e=d;else if(f===b.length&&c<1)for(;f--;)d=parseFloat(a[f]),e[f]=isNaN(d)?a[f]:c*parseFloat(b[f]-d)+d;else e=b;return e}};x&&x.init&&x.init(Sb);if(!x&&ja.jQuery){var Ja=jQuery,n=function(a,b){for(var c=0,d=a.length;c<d;c++)if(b.call(a[c],a[c],c,a)===!1)return c},nc=Ja.grep,nb=function(a,b){for(var c=
[],d=0,e=a.length;d<e;d++)c[d]=b.call(a[d],a[d],d,a);return c},G=function(){var a=arguments;return Ja.extend(!0,null,a[0],a[1],a[2],a[3])},Bc=function(a){return Ja(a).offset()},W=function(a,b,c){Ja(a).bind(b,c)},ra=function(a,b,c){var d=T.removeEventListener?"removeEventListener":"detachEvent";T[d]&&!a[d]&&(a[d]=function(){});Ja(a).unbind(b,c)},U=function(a,b,c,d){var e=Ja.Event(b),f="detached"+b;I(e,c);a[b]&&(a[f]=a[b],a[b]=null);Ja(a).trigger(e);a[f]&&(a[b]=a[f],a[f]=null);d&&!e.isDefaultPrevented()&&
d(e)},dc=function(a,b,c){var d=Ja(a);if(b.d)a.toD=b.d,b.d=1;d.stop();d.animate(b,c)},Lb=function(a){Ja(a).stop()};Ja.extend(Ja.easing,{easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c}});var Hc=jQuery.fx,Ic=Hc.step;n(["cur","_default","width","height"],function(a,b){var c=b?Ic:Hc.prototype,d=c[a],e;d&&(c[a]=function(a){a=b?a:this;e=a.elem;return e.attr?e.attr(a.prop,a.now):d.apply(this,arguments)})});Ic.d=function(a){var b=a.elem;if(!a.started){var c=Sb.init(b,b.d,b.toD);a.start=c[0];a.end=
c[1];a.started=!0}b.attr("d",Sb.step(a.start,a.end,a.pos,b.toD))}}x={enabled:!0,align:"center",x:0,y:15,style:{color:"#666",fontSize:"11px",lineHeight:"14px"}};la={colors:"#4572A7,#AA4643,#89A54E,#80699B,#3D96AE,#DB843D,#92A8CD,#A47D7C,#B5CA92".split(","),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),shortMonths:"Jan,Feb,Mar,Apr,May,June,Jul,Aug,Sep,Oct,Nov,Dec".split(","),
weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),decimalPoint:".",resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:","},global:{useUTC:!0},chart:{borderColor:"#4572A7",borderRadius:5,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacingTop:10,spacingRight:10,spacingBottom:15,spacingLeft:10,style:{fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif',fontSize:"12px"},backgroundColor:"#FFFFFF",plotBorderColor:"#C0C0C0"},
title:{text:"Chart title",align:"center",y:15,style:{color:"#3E576F",fontSize:"16px"}},subtitle:{text:"",align:"center",y:30,style:{color:"#6D869F"}},plotOptions:{line:{allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},lineWidth:2,shadow:!0,marker:{enabled:!0,lineWidth:0,radius:4,lineColor:"#FFFFFF",states:{hover:{},select:{fillColor:"#FFFFFF",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:G(x,{enabled:!1,y:-6,formatter:function(){return this.y}}),cropThreshold:300,
pointRange:0,showInLegend:!0,states:{hover:{marker:{}},select:{marker:{}}},stickyTracking:!0}},labels:{style:{position:Gb,color:"#3E576F"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderWidth:1,borderColor:"#909090",borderRadius:5,shadow:!1,style:{padding:"5px"},itemStyle:{cursor:"pointer",color:"#3E576F"},itemHoverStyle:{color:"#000000"},itemHiddenStyle:{color:"#C0C0C0"},itemCheckboxStyle:{position:Gb,width:"13px",height:"13px"},symbolWidth:16,
symbolPadding:5,verticalAlign:"bottom",x:0,y:0},loading:{labelStyle:{fontWeight:"bold",position:Cc,top:"1em"},style:{position:Gb,backgroundColor:"white",opacity:0.5,textAlign:"center"}},tooltip:{enabled:!0,backgroundColor:"rgba(255, 255, 255, .85)",borderWidth:2,borderRadius:5,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:!0,snap:ua?25:10,style:{color:"#333333",fontSize:"12px",
padding:"5px",whiteSpace:"nowrap"}},toolbar:{itemStyle:{color:"#4572A7",cursor:"pointer"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"10px"}}};var cc={dateTimeLabelFormats:Ka(pb,"%H:%M:%S.%L",qa,"%H:%M:%S",ab,"%H:%M",Va,"%H:%M",ma,"%e. %b",Ea,"%e. %b",Pa,"%b '%y",bb,"%Y"),endOnTick:!1,gridLineColor:"#C0C0C0",labels:x,lineColor:"#C0D0E0",lineWidth:1,max:null,
min:null,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#6D869F",fontWeight:"bold"}},type:"linear"},lc=G(cc,{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,
maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Y-values"},stackLabels:{enabled:!1,formatter:function(){return this.total},style:x.style}}),Qc={labels:{align:"right",x:-8,y:null},title:{rotation:270}},Pc={labels:{align:"left",x:8,y:null},title:{rotation:90}},zc={labels:{align:"center",x:0,y:14},title:{rotation:0}},Oc=G(zc,{labels:{y:-5}}),J=la.plotOptions,x=J.line;J.spline=G(x);J.scatter=G(x,{lineWidth:0,states:{hover:{lineWidth:0}},tooltip:{headerFormat:'<span style="font-size: 10px; color:{series.color}">{series.name}</span><br/>',
pointFormat:"x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>"}});J.area=G(x,{threshold:0});J.areaspline=G(J.area);J.column=G(x,{borderColor:"#FFFFFF",borderWidth:1,borderRadius:0,groupPadding:0.2,marker:null,pointPadding:0.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{brightness:0.1,shadow:!1},select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}},dataLabels:{y:null,verticalAlign:null},threshold:0});J.bar=G(J.column,{dataLabels:{align:"left",x:5,y:0}});J.pie=G(x,{borderColor:"#FFFFFF",
borderWidth:1,center:["50%","50%"],colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name},y:5},legendType:"point",marker:null,size:"75%",showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}}});xc();var $a=function(a){var b=[],c;(function(a){(c=/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(a))?b=[O(c[1]),O(c[2]),O(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))&&
(b=[O(c[1],16),O(c[2],16),O(c[3],16),1])})(a);return{get:function(c){return b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a},brighten:function(a){if(Ub(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=O(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},setOpacity:function(a){b[3]=a;return this}}};Eb.prototype={init:function(a,b){this.element=T.createElementNS("http://www.w3.org/2000/svg",b);this.renderer=a;this.attrSetters={}},animate:function(a,b,
c){b=q(b,Nb,!0);Lb(this);if(b){b=G(b);if(c)b.complete=c;dc(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName,k=this.renderer,i,j=this.attrSetters,l=this.shadows,m=this.htmlNode,p,n=this;Ab(a)&&z(b)&&(c=a,a={},a[c]=b);if(Ab(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),n=P(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(n=parseFloat(n));else for(c in a){i=!1;d=a[c];e=j[c]&&j[c](d,c);if(e!==!1){e!==B&&(d=e);if(c==="d")d&&
d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0"),this.d=d;else if(c==="x"&&h==="text"){for(e=0;e<g.childNodes.length;e++)f=g.childNodes[e],P(f,"x")===P(g,"x")&&P(f,"x",d);this.rotation&&P(g,"transform","rotate("+this.rotation+" "+d+" "+O(a.y||P(g,"y"))+")")}else if(c==="fill")d=k.color(d,g,c);else if(h==="circle"&&(c==="x"||c==="y"))c={x:"cx",y:"cy"}[c]||c;else if(h==="rect"&&c==="r")P(g,{rx:d,ry:d}),i=!0;else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="verticalAlign")this[c]=
d,this.updateTransform(),i=!0;else if(c==="stroke")d=k.color(d,g,c);else if(c==="dashstyle")if(c="stroke-dasharray",d=d&&d.toLowerCase(),d==="solid")d=La;else{if(d){d=d.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(e=d.length;e--;)d[e]=O(d[e])*a["stroke-width"];d=d.join(",")}}else c==="isTracker"?this[c]=d:c===
"width"?d=O(d):c==="align"?(c="text-anchor",d={left:"start",center:"middle",right:"end"}[d]):c==="title"&&(e=T.createElementNS("http://www.w3.org/2000/svg","title"),e.appendChild(T.createTextNode(d)),g.appendChild(e));c==="strokeWidth"&&(c="stroke-width");Ac&&c==="stroke-width"&&d===0&&(d=1.0E-6);this.symbolName&&/^(x|y|r|start|end|innerR|anchorX|anchorY)/.test(c)&&(p||(this.symbolAttr(a),p=!0),i=!0);if(l&&/^(width|height|visibility|x|y|d|transform)$/.test(c))for(e=l.length;e--;)P(l[e],c,d);if((c===
"width"||c==="height")&&h==="rect"&&d<0)d=0;c==="text"?(this.textStr=d,this.added&&k.buildText(this)):i||P(g,c,d)}if(m&&(c==="x"||c==="y"||c==="translateX"||c==="translateY"||c==="visibility")){e=m.length?m:[this];f=e.length;var s;for(s=0;s<f;s++)m=e[s],i=m.getBBox(),m=m.htmlNode,C(m,I(this.styles,{left:i.x+(this.translateX||0)+Fa,top:i.y+(this.translateY||0)+Fa})),c==="visibility"&&C(m,{visibility:d})}}return n},symbolAttr:function(a){var b=this;n("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","),
function(c){b[c]=q(a[c],b[c])});b.attr({d:b.renderer.symbols[b.symbolName](b.x,b.y,b.width,b.height,b)})},clip:function(a){return this.attr("clip-path","url("+this.renderer.url+"#"+a.id+")")},crisp:function(a,b,c,d,e){var f,g={},h={},k,a=a||this.strokeWidth||this.attr&&this.attr("stroke-width")||0;k=y(a)%2/2;h.x=Ra(b||this.x||0)+k;h.y=Ra(c||this.y||0)+k;h.width=Ra((d||this.width||0)-2*k);h.height=Ra((e||this.height||0)-2*k);h.strokeWidth=a;for(f in h)this[f]!==h[f]&&(this[f]=g[f]=h[f]);return g},
css:function(a){var b=this.element,b=a&&a.width&&b.nodeName==="text",c,d="",e=function(a,b){return"-"+b.toLowerCase()};if(a&&a.color)a.fill=a.color;this.styles=a=I(this.styles,a);if(Vb&&!Qb)b&&delete a.width,C(this.element,a);else{for(c in a)d+=c.replace(/([A-Z])/g,e)+":"+a[c]+";";this.attr({style:d})}b&&this.added&&this.renderer.buildText(this);return this},on:function(a,b){var c=b;ua&&a==="click"&&(a="touchstart",c=function(a){a.preventDefault();b()});this.element["on"+a]=c;return this},translate:function(a,
b){return this.attr({translateX:a,translateY:b})},invert:function(){this.inverted=!0;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.inverted,d=this.rotation,e=[];c&&(a+=this.attr("width"),b+=this.attr("height"));(a||b)&&e.push("translate("+a+","+b+")");c?e.push("rotate(90) scale(-1,1)"):d&&e.push("rotate("+d+" "+this.x+" "+this.y+")");e.length&&P(this.element,"transform",e.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);
return this},align:function(a,b,c){a?(this.alignOptions=a,this.alignByTranslate=b,c||this.renderer.alignedObjects.push(this)):(a=this.alignOptions,b=this.alignByTranslate);var c=q(c,this.renderer),d=a.align,e=a.verticalAlign,f=(c.x||0)+(a.x||0),g=(c.y||0)+(a.y||0),h={};/^(right|center)$/.test(d)&&(f+=(c.width-(a.width||0))/{right:1,center:2}[d]);h[b?"translateX":"x"]=y(f);/^(bottom|middle)$/.test(e)&&(g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1));h[b?"translateY":"y"]=y(g);this[this.placed?
"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a,b,c,d=this.rotation,e=d*Ec;try{a=I({},this.element.getBBox())}catch(f){a={width:0,height:0}}b=a.width;c=a.height;if(d)a.width=Ha(c*pa(e))+Ha(b*Ma(e)),a.height=Ha(c*Ma(e))+Ha(b*pa(e));return a},show:function(){return this.attr({visibility:Wa})},hide:function(){return this.attr({visibility:Ta})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=P(f,"zIndex"),h;this.parentInverted=
a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(a&&this.htmlNode){if(!a.htmlNode)a.htmlNode=[];a.htmlNode.push(this)}if(g)c.handleZ=!0,g=O(g);if(c.handleZ)for(c=0;c<e.length;c++)if(a=e[c],b=P(a,"zIndex"),a!==f&&(O(b)>g||!z(g)&&z(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;U(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.box,e,f;b.onclick=b.onmouseout=b.onmouseover=
b.onmousemove=null;Lb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f<a.stops.length;f++)a.stops[f]=a.stops[f].destroy();a.stops=null}a.safeRemoveChild(b);c&&n(c,function(b){a.safeRemoveChild(b)});d&&d.destroy();Ib(a.renderer.alignedObjects,a);for(e in a)delete a[e];return null},empty:function(){for(var a=this.element,b=a.childNodes,c=b.length;c--;)a.removeChild(b[c])},shadow:function(a,b){var c=[],d,e,f=this.element,g=this.parentInverted?"(-1,-1)":"(1,1)";if(a){for(d=1;d<=
3;d++)e=f.cloneNode(0),P(e,{isShadow:"true",stroke:"rgb(0, 0, 0)","stroke-opacity":0.05*d,"stroke-width":7-2*d,transform:"translate"+g,fill:La}),b?b.element.appendChild(e):f.parentNode.insertBefore(e,f),c.push(e);this.shadows=c}return this}};var Ob=function(){this.init.apply(this,arguments)};Ob.prototype={Element:Eb,init:function(a,b,c,d){var e=location,f;f=this.createElement("svg").attr({xmlns:"http://www.w3.org/2000/svg",version:"1.1"});a.appendChild(f.element);this.box=f.element;this.boxWrapper=
f;this.alignedObjects=[];this.url=Vb?"":e.href.replace(/#.*?$/,"");this.defs=this.createElement("defs").add();this.forExport=d;this.gradients=[];this.setSize(b,c,!1)},destroy:function(){var a,b=this.gradients,c=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();if(b){for(a=0;a<b.length;a++)this.gradients[a]=b[a].destroy();this.gradients=null}if(c)this.defs=c.destroy();return this.alignedObjects=null},createElement:function(a){var b=new this.Element;b.init(this,a);return b},buildText:function(a){for(var b=
a.element,c=q(a.textStr,"").toString().replace(/<(b|strong)>/g,'<span style="font-weight:bold">').replace(/<(i|em)>/g,'<span style="font-style:italic">').replace(/<a/g,"<span").replace(/<\/(b|strong|i|em|a)>/g,"</span>").split(/<br.*?>/g),d=b.childNodes,e=/style="([^"]+)"/,f=/href="([^"]+)"/,g=P(b,"x"),h=a.styles,k=h&&a.useHTML&&!this.forExport,i=a.htmlNode,j=h&&O(h.width),l=h&&h.lineHeight,m,p=d.length;p--;)b.removeChild(d[p]);j&&!a.added&&this.box.appendChild(b);c[c.length-1]===""&&c.pop();n(c,
function(c,d){var h,i=0,k,c=c.replace(/<span/g,"|||<span").replace(/<\/span>/g,"</span>|||");h=c.split("|||");n(h,function(c){if(c!==""||h.length===1){var p={},n=T.createElementNS("http://www.w3.org/2000/svg","tspan");e.test(c)&&P(n,"style",c.match(e)[1].replace(/(;| |^)color([ :])/,"$1fill$2"));f.test(c)&&(P(n,"onclick",'location.href="'+c.match(f)[1]+'"'),C(n,{cursor:"pointer"}));c=(c.replace(/<(.|\n)*?>/g,"")||" ").replace(/&lt;/g,"<").replace(/&gt;/g,">");n.appendChild(T.createTextNode(c));i?
p.dx=3:p.x=g;if(!i){if(d){!Qb&&a.renderer.forExport&&C(n,{display:"block"});k=ja.getComputedStyle&&O(ja.getComputedStyle(m,null).getPropertyValue("line-height"));if(!k||isNaN(k))k=l||m.offsetHeight||18;P(n,"dy",k)}m=n}P(n,p);b.appendChild(n);i++;if(j)for(var c=c.replace(/-/g,"- ").split(" "),q,H=[];c.length||H.length;)q=a.getBBox().width,p=q>j,!p||c.length===1?(c=H,H=[],c.length&&(n=T.createElementNS("http://www.w3.org/2000/svg","tspan"),P(n,{dy:l||16,x:g}),b.appendChild(n),q>j&&(j=q))):(n.removeChild(n.firstChild),
H.unshift(c.pop())),c.length&&n.appendChild(T.createTextNode(c.join(" ").replace(/- /g,"-")))}})});if(k){if(!i)i=a.htmlNode=Y("span",null,I(h,{position:Gb,top:0,left:0}),this.box.parentNode);i.innerHTML=a.textStr;for(p=d.length;p--;)d[p].style.visibility=Ta}},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c),k=0,i,j,l,m,p,a={x1:0,y1:0,x2:0,y2:1},e=G(Ka("stroke-width",1,"stroke","#999","fill",Ka("linearGradient",a,"stops",[[0,"#FFF"],[1,"#DDD"]]),"r",3,"padding",3,"style",Ka("color","black")),
e);l=e.style;delete e.style;f=G(e,Ka("stroke","#68A","fill",Ka("linearGradient",a,"stops",[[0,"#FFF"],[1,"#ACF"]])),f);m=f.style;delete f.style;g=G(e,Ka("stroke","#68A","fill",Ka("linearGradient",a,"stops",[[0,"#9BD"],[1,"#CDF"]])),g);p=g.style;delete g.style;W(h.element,"mouseenter",function(){h.attr(f).css(m)});W(h.element,"mouseleave",function(){i=[e,f,g][k];j=[l,m,p][k];h.attr(i).css(j)});h.setState=function(a){(k=a)?a===2&&h.attr(g).css(p):h.attr(e).css(l)};return h.on("click",function(){d.call(h)}).attr(e).css(I({cursor:"default"},
l))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=y(a[1])+b%2/2);a[2]===a[5]&&(a[2]=a[5]=y(a[2])+b%2/2);return a},path:function(a){return this.createElement("path").attr({d:a,fill:La})},circle:function(a,b,c){a=sb(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(sb(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){if(sb(a))b=a.y,c=a.width,d=a.height,
e=a.r,f=a.strokeWidth,a=a.x;e=this.createElement("rect").attr({rx:e,ry:e,fill:La});return e.attr(e.crisp(f,a,b,R(c,0),R(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[q(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return z(a)?b.attr({"class":fb+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:La};arguments.length>1&&I(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);
f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(y(b),y(c),d,e,f),k=/^url\((.*?)\)$/,i;if(h)g=this.path(h),I(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&I(g,f);else if(k.test(a)){var j=function(a,b){a.attr({width:b[0],height:b[1]}).translate(-y(b[0]/2),-y(b[1]/2))};i=a.match(k)[1];a=Fc[i];g=this.image(i).attr({x:b,y:c});a?j(g,a):(g.attr({width:0,
height:0}),Y("img",{onload:function(){j(g,Fc[i]=[this.width,this.height])},src:i}))}return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return[ta,a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return[ta,a,b,ha,a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return[ta,a+c/2,b,ha,a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return[ta,a,b,ha,a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return[ta,a+c/2,b,ha,a+c,b+d/2,a+c/2,b+d,a,
b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-1.0E-6,d=e.innerR,h=Ma(f),k=pa(f),i=Ma(g),g=pa(g),e=e.end-f<hb?0:1;return[ta,a+c*h,b+c*k,"A",c,c,0,e,1,a+c*i,b+c*g,ha,a+d*i,b+d*g,"A",d,d,0,e,0,a+d*h,b+d*k,"Z"]}},clipRect:function(a,b,c,d){var e=fb+mc++,f=this.createElement("clipPath").attr({id:e}).add(this.defs),a=this.rect(a,b,c,d,0).add(f);a.id=e;a.clipPath=f;return a},color:function(a,b,c){var d,e=/^rgba/;if(a&&a.linearGradient){var f=this,b=a.linearGradient,c=!b.length,g=
fb+mc++,h,k,i;h=f.createElement("linearGradient").attr(I({id:g,x1:b.x1||b[0]||0,y1:b.y1||b[1]||0,x2:b.x2||b[2]||0,y2:b.y2||b[3]||0},c?null:{gradientUnits:"userSpaceOnUse"})).add(f.defs);f.gradients.push(h);h.stops=[];n(a.stops,function(a){e.test(a[1])?(d=$a(a[1]),k=d.get("rgb"),i=d.get("a")):(k=a[1],i=1);a=f.createElement("stop").attr({offset:a[0],"stop-color":k,"stop-opacity":i}).add(h);h.stops.push(a)});return"url("+this.url+"#"+g+")"}else return e.test(a)?(d=$a(a),P(b,c+"-opacity",d.get("a")),
d.get("rgb")):(b.removeAttribute(c+"-opacity"),a)},text:function(a,b,c,d){var e=la.chart.style,b=y(q(b,0)),c=y(q(c,0)),a=this.createElement("text").attr({x:b,y:c,text:a}).css({fontFamily:e.fontFamily,fontSize:e.fontSize});a.x=b;a.y=c;a.useHTML=d;return a},label:function(a,b,c,d,e,f){function g(){var a=j.styles,a=a&&a.textAlign,b=s,c=s+y(O(j.element.style.fontSize||11)*1.2);if(z(t)&&(a==="center"||a==="right"))b+={center:0.5,right:1}[a]*(t-p.width);(b!==l.x||c!==l.y)&&l.attr({x:b,y:c});l.x=b;l.y=c}
function h(a,b){m?m.attr(a,b):va[a]=b}function k(){j.attr({text:a,x:b,y:c,anchorX:e,anchorY:f})}var i=this,j=i.g(),l=i.text().attr({zIndex:1}).add(j),m,p,q="left",s=3,t,r,v,u,Na=0,va={},E=j.attrSetters;W(j,"add",k);E.width=function(a){t=a;return!1};E.height=function(a){r=a;return!1};E.padding=function(a){s=a;g();return!1};E.align=function(a){q=a;return!1};E.text=function(a,b){l.attr(b,a);p=(t===void 0||r===void 0||j.styles.textAlign)&&l.getBBox(!0);j.width=(t||p.width)+2*s;j.height=(r||p.height)+
2*s;if(!m)j.box=m=d?i.symbol(d,0,0,j.width,j.height):i.rect(0,0,j.width,j.height,0,va["stroke-width"]),m.add(j);m.attr(G({width:j.width,height:j.height},va));va=null;g();return!1};E["stroke-width"]=function(a,b){Na=a%2/2;h(b,a);return!1};E.stroke=E.fill=E.r=function(a,b){h(b,a);return!1};E.anchorX=function(a,b){e=a;h(b,a+Na-v);return!1};E.anchorY=function(a,b){f=a;h(b,a-u);return!1};E.x=function(a){v=a;v-={left:0,center:0.5,right:1}[q]*((t||p.width)+s);j.attr("translateX",y(v));return!1};E.y=function(a){u=
a;j.attr("translateY",y(a));return!1};var x=j.css;return I(j,{css:function(a){if(a){var b={},a=G({},a);n("fontSize,fontWeight,fontFamily,color,lineHeight,width".split(","),function(c){a[c]!==B&&(b[c]=a[c],delete a[c])});l.css(b)}return x.call(j,a)},getBBox:function(){return m.getBBox()},shadow:function(a){m.shadow(a);return j},destroy:function(){ra(j,"add",k);ra(j.element,"mouseenter");ra(j.element,"mouseleave");l&&(l=l.destroy());Eb.prototype.destroy.call(j)}})}};Pb=Ob;var Tb;if(!Qb)x=ka(Eb,{init:function(a,
b){var c=["<",b,' filled="f" stroked="f"'],d=["position: ",Gb,";"];(b==="shape"||b===vb)&&d.push("left:0;top:0;width:10px;height:10px;");Rb&&d.push("visibility: ",b===vb?Ta:Wa);c.push(' style="',d.join(""),'"/>');if(b)c=b===vb||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=Y(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);Rb&&d.gVis===Ta&&C(c,{visibility:Ta});d.appendChild(c);this.added=
!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();U(this,"add");return this},toggleChildren:function(a,b){for(var c=a.childNodes,d=c.length;d--;)C(c[d],{visibility:b}),c[d].nodeName==="DIV"&&this.toggleChildren(c[d],b)},attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,k=this.renderer,i=this.symbolName,j,l=this.shadows,m,p=this.attrSetters,n=this;Ab(a)&&z(b)&&(c=a,a={},a[c]=b);if(Ab(a))c=a,n=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c];
else for(c in a)if(d=a[c],m=!1,e=p[c]&&p[c](d,c),e!==!1){e!==B&&(d=e);if(i&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))j||(this.symbolAttr(a),j=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;for(m=[];e--;)m[e]=Ub(d[e])?y(d[e]*10)-5:d[e]==="Z"?"x":d[e];d=m.join(" ")||"x";f.path=d;if(l)for(e=l.length;e--;)l[e].path=d;m=!0}else if(c==="zIndex"||c==="visibility"){if(Rb&&c==="visibility"&&h==="DIV")f.gVis=d,this.toggleChildren(f,d),d===Wa&&(d=null);d&&(g[c]=d);m=
!0}else if(c==="width"||c==="height")d=R(0,d),this[c]=d,this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,m=!0;else if(/^(x|y)$/.test(c))this[c]=d,f.tagName==="SPAN"?this.updateTransform():g[{x:"left",y:"top"}[c]]=d;else if(c==="class")f.className=d;else if(c==="stroke")d=k.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ub(d)&&(d+=Fa);else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||Y(k.prepVML(["<stroke/>"]),
null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")h==="SPAN"?g.color=d:(f.filled=d!==La?!0:!1,d=k.color(d,f,c),c="fillcolor");else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="align")c==="align"&&(c="textAlign"),this[c]=d,this.updateTransform(),m=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,m=!0;if(l&&c==="visibility")for(e=l.length;e--;)l[e].style[c]=d;m||(Rb?f[c]=d:P(f,c,d))}return n},clip:function(a){var b=this,c=a.members;c.push(b);b.destroyClip=function(){Ib(c,
b)};return b.css(a.getCSS(b.inverted))},css:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&&a.width)delete a.width,this.textWidth=b,this.updateTransform();this.styles=I(this.styles,a);C(this.element,a);return this},safeRemoveChild:function(a){a.parentNode&&Db(a)},destroy:function(){this.destroyClip&&this.destroyClip();return Eb.prototype.destroy.apply(this)},empty:function(){for(var a=this.element.childNodes,b=a.length,c;b--;)c=a[b],c.parentNode.removeChild(c)},getBBox:function(a){var b=
this.element,c=this.bBox;if(!c||a){if(b.nodeName==="text")b.style.position=Gb;c=this.bBox={x:b.offsetLeft,y:b.offsetTop,width:b.offsetWidth,height:b.offsetHeight}}return c},on:function(a,b){this.element["on"+a]=function(){var a=ja.event;a.target=a.srcElement;b(a)};return this},updateTransform:function(){if(this.added){var a=this,b=a.element,c=a.translateX||0,d=a.translateY||0,e=a.x||0,f=a.y||0,g=a.textAlign||"left",h={left:0,center:0.5,right:1}[g],k=g&&g!=="left",i=a.shadows;if(c||d)C(b,{marginLeft:c,
marginTop:d}),i&&n(i,function(a){C(a,{marginLeft:c+1,marginTop:d+1})});a.inverted&&n(b.childNodes,function(c){a.renderer.invertChild(c,b)});if(b.tagName==="SPAN"){var j,l,i=a.rotation,m;j=0;var p=1,H=0,s;m=O(a.textWidth);var t=a.xCorr||0,r=a.yCorr||0,v=[i,g,b.innerHTML,a.textWidth].join(",");if(v!==a.cTT)z(i)&&(j=i*Ec,p=Ma(j),H=pa(j),C(b,{filter:i?["progid:DXImageTransform.Microsoft.Matrix(M11=",p,", M12=",-H,", M21=",H,", M22=",p,", sizingMethod='auto expand')"].join(""):La})),j=q(a.elemWidth,b.offsetWidth),
l=q(a.elemHeight,b.offsetHeight),j>m&&(C(b,{width:m+Fa,display:"block",whiteSpace:"normal"}),j=m),m=y((O(b.style.fontSize)||12)*1.2),t=p<0&&-j,r=H<0&&-l,s=p*H<0,t+=H*m*(s?1-h:h),r-=p*m*(i?s?h:1-h:1),k&&(t-=j*h*(p<0?-1:1),i&&(r-=l*h*(H<0?-1:1)),C(b,{textAlign:g})),a.xCorr=t,a.yCorr=r;C(b,{left:e+t,top:f+r});a.cTT=v}}else this.alignOnAdd=!0},shadow:function(a,b){var c=[],d,e=this.element,f=this.renderer,g,h=e.style,k,i=e.path;i&&typeof i.value!=="string"&&(i="x");if(a){for(d=1;d<=3;d++)k=['<shape isShadow="true" strokeweight="',
7-2*d,'" filled="false" path="',i,'" coordsize="100,100" style="',e.style.cssText,'" />'],g=Y(f.prepVML(k),null,{left:O(h.left)+1,top:O(h.top)+1}),k=['<stroke color="black" opacity="',0.05*d,'"/>'],Y(f.prepVML(k),null,null,g),b?b.element.appendChild(g):e.parentNode.insertBefore(g,e),c.push(g);this.shadows=c}return this}}),Tb=function(){this.init.apply(this,arguments)},Tb.prototype=G(Ob.prototype,{Element:x,isIE8:mb.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d;this.alignedObjects=[];d=this.createElement(vb);
a.appendChild(d.element);this.box=d.element;this.boxWrapper=d;this.setSize(b,c,!1);if(!T.namespaces.hcv)T.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),T.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},clipRect:function(a,b,c,d){var e=this.createElement();return I(e,{members:[],left:a,top:b,width:c,height:d,getCSS:function(a){var b=this.top,c=this.left,d=c+this.width,e=b+this.height,b={clip:"rect("+y(a?
c:b)+"px,"+y(a?e:d)+"px,"+y(a?d:e)+"px,"+y(a?b:c)+"px)"};!a&&Rb&&I(b,{width:d+Fa,height:e+Fa});return b},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a.inverted))})}})},color:function(a,b,c){var d,e=/^rgba/;if(a&&a.linearGradient){var f,g,h=a.linearGradient,k=h.x1||h[0]||0,i=h.y1||h[1]||0,j=h.x2||h[2]||0,h=h.y2||h[3]||0,l,m,p,q;n(a.stops,function(a,b){e.test(a[1])?(d=$a(a[1]),f=d.get("rgb"),g=d.get("a")):(f=a[1],g=1);b?(p=f,q=g):(l=f,m=g)});a=90-sa.atan((h-i)/(j-k))*180/hb;a=["<",
c,' colors="0% ',l,",100% ",p,'" angle="',a,'" opacity="',q,'" o:opacity2="',m,'" type="gradient" focus="100%" method="any" />'];Y(this.prepVML(a),null,null,b)}else if(e.test(a)&&b.tagName!=="IMG")return d=$a(a),a=["<",c,' opacity="',d.get("a"),'"/>'],Y(this.prepVML(a),null,null,b),d.get("rgb");else{b=b.getElementsByTagName(c);if(b.length)b[0].opacity=1;return a}},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===
-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","<hcv:");return a},text:function(a,b,c){var d=la.chart.style;return this.createElement("span").attr({text:a,x:y(b),y:y(c)}).css({whiteSpace:"nowrap",fontFamily:d.fontFamily,fontSize:d.fontSize})},path:function(a){return this.createElement("shape").attr({coordsize:"100 100",d:a})},circle:function(a,b,c){return this.symbol("circle").attr({x:a,
y:b,r:c})},g:function(a){var b;a&&(b={className:fb+a,"class":fb+a});return this.createElement(vb).attr(b)},image:function(a,b,c,d,e){var f=this.createElement("img").attr({src:a});arguments.length>1&&f.css({left:b,top:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(sb(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,R(c,0),R(d,0)))},invertChild:function(a,b){var c=b.style;C(a,{flip:"x",left:O(c.width)-10,top:O(c.height)-10,
rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,c=e.r||c||d,d=Ma(f),h=pa(f),k=Ma(g),i=pa(g),e=e.innerR,j=0.07/c,l=e&&0.1/e||0;if(g-f===0)return["x"];else 2*hb-g+f<j?k=-j:g-f<l&&(k=Ma(f+l));return["wa",a-c,b-c,a+c,b+c,a+c*d,b+c*h,a+c*k,b+c*i,"at",a-e,b-e,a+e,b+e,a+e*k,b+e*i,a+e*d,b+e*h,"x","e"]},circle:function(a,b,c,d){return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){if(!z(e))return[];var f=a+c,g=b+d,c=wa(e.r||0,c,d);return[ta,a+c,b,ha,f-c,b,"wa",f-
2*c,b,f,b+2*c,f-c,b,f,b+c,ha,f,g-c,"wa",f-2*c,g-2*c,f,g,f,g-c,f-c,g,ha,a+c,g,"wa",a,g-2*c,a+2*c,g,a+c,g,a,g-c,ha,a,b+c,"wa",a,b,a+2*c,b+2*c,a,b+c,a+c,b,"x","e"]}}}),Pb=Tb;ac.prototype.callbacks=[];var ib=function(){};ib.prototype={init:function(a,b,c){var d=a.chart.counters;this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint){b=a.chart.options.colors;if(!this.options)this.options={};this.color=this.options.color=this.color||b[d.color++];d.wrapColor(b.length)}a.chart.pointCount++;
return this},applyOptions:function(a,b){var c=this.series,d=typeof a;this.config=a;if(d==="number"||a===null)this.y=a;else if(typeof a[0]==="number")this.x=a[0],this.y=a[1];else if(d==="object"&&typeof a.length!=="number")I(this,a),this.options=a;else if(typeof a[0]==="string")this.name=a[0],this.y=a[1];if(this.x===B)this.x=b===B?c.autoIncrement():b},destroy:function(){var a=this.series,b=a.chart.hoverPoints,c;a.chart.pointCount--;b&&(this.setState(),Ib(b,this));if(this===a.chart.hoverPoint)this.onMouseOut();
a.chart.hoverPoints=null;if(this.graphic||this.dataLabel)ra(this),this.destroyElements();this.legendItem&&this.series.chart.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,tracker,dataLabel,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},
select:function(a,b){var c=this,d=c.series.chart,a=q(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=a;c.setState(a&&"select");b||n(d.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=!1,a.setState(oa),a.firePointEvent("unselect")})})},onMouseOver:function(){var a=this.series,b=a.chart,c=b.tooltip,d=b.hoverPoint;if(d&&d!==this)d.onMouseOut();this.firePointEvent("mouseOver");c&&(!c.shared||a.noSharedTooltip)&&c.refresh(this);this.setState(cb);
b.hoverPoint=this},onMouseOut:function(){this.firePointEvent("mouseOut");this.setState();this.series.chart.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=String(this.y).split("."),d=d[1]?d[1].length:0,e=a.match(/\{(series|point)\.[a-zA-Z]+\}/g),f=/[\.}]/,g,h,k;for(k in e)h=e[k],Ab(h)&&h!==a&&(g=h.indexOf("point")===1?this:b,g=h==="{point.y}"?(c.yPrefix||"")+Wb(this.y,q(c.yDecimals,d))+(c.ySuffix||""):g[e[k].split(f)[1]],a=a.replace(e[k],g));return a},update:function(a,
b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,k=h.length,i=e.chart,b=q(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);sb(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state]));for(g=0;g<k;g++)if(h[g]===d){e.xData[g]=d.x;e.yData[g]=d.y;e.options.data[g]=a;break}e.isDirty=!0;e.isDirtyData=!0;b&&i.redraw(c)})},remove:function(a,b){var c=this,d=c.series,e=d.chart,f,g=d.data,h=g.length;Kb(b,e);a=q(a,!0);c.firePointEvent("remove",null,function(){for(f=0;f<h;f++)if(g[f]===c){g.splice(f,
1);d.options.data.splice(f,1);d.xData.splice(f,1);d.yData.splice(f,1);break}c.destroy();d.isDirty=!0;d.isDirtyData=!0;a&&e.redraw()})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])&&this.importEvents();a==="click"&&e.allowPointSelect&&(c=function(a){d.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});U(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a=G(this.series.options.point,this.options).events,
b;this.events=a;for(b in a)W(this,b,a[b]);this.hasImportedEvents=!0}},setState:function(a){var b=this.plotX,c=this.plotY,d=this.series,e=d.options.states,f=J[d.type].marker&&d.options.marker,g=f&&!f.enabled,h=f&&f.states[a],k=h&&h.enabled===!1,i=d.stateMarkerGraphic,j=d.chart,l=this.pointAttr,a=a||oa;if(!(a===this.state||this.selected&&a!=="select"||e[a]&&e[a].enabled===!1||a&&(k||g&&!h.enabled))){if(this.graphic)e=this.graphic.symbolName&&l[a].r,this.graphic.attr(G(l[a],e?{x:b-e,y:c-e,width:2*e,
height:2*e}:{}));else{if(a){if(!i)e=f.radius,d.stateMarkerGraphic=i=j.renderer.symbol(d.symbol,-e,-e,2*e,2*e).attr(l[a]).add(d.group);i.translate(b,c)}if(i)i[a?"show":"hide"]()}this.state=a}}};var X=function(){};X.prototype={isCartesian:!0,type:"line",pointClass:ib,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},init:function(a,b){var c,d;d=a.series.length;this.chart=a;this.options=b=this.setOptions(b);this.bindAxes();I(this,{index:d,name:b.name||"Series "+
(d+1),state:oa,pointAttr:{},visible:b.visible!==!1,selected:b.selected===!0});d=b.events;for(c in d)W(this,c,d[c]);if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;this.getColor();this.getSymbol();this.setData(b.data,!1)},bindAxes:function(){var a=this,b=a.options,c=a.chart,d;a.isCartesian&&n(["xAxis","yAxis"],function(e){n(c[e],function(c){d=c.options;if(b[e]===d.index||b[e]===B&&d.index===0)c.series.push(a),a[e]=c,c.isDirty=!0})})},autoIncrement:function(){var a=
this.options,b=this.xIncrement,b=q(b,a.pointStart,0);this.pointInterval=q(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},getSegments:function(){var a=-1,b=[],c,d=this.points,e=d.length;if(e)if(this.options.connectNulls){for(c=e;c--;)d[c].y===null&&d.splice(c,1);b=[d]}else n(d,function(c,g){c.y===null?(g>a+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions,d=a.data;
a.data=null;a=G(c[this.type],c.series,a);a.data=d;this.tooltipOptions=G(b.tooltip,a.tooltip);return a},getColor:function(){var a=this.chart.options.colors,b=this.chart.counters;this.color=this.options.color||a[b.color++]||"#0000ff";b.wrapColor(a.length)},getSymbol:function(){var a=this.options.marker,b=this.chart,c=b.options.symbols,b=b.counters;this.symbol=a.symbol||c[b.symbol++];if(/^url/.test(this.symbol))a.radius=0;b.wrapSymbol(c.length)},addPoint:function(a,b,c,d){var e=this.data,f=this.graph,
g=this.area,h=this.chart,k=this.xData,i=this.yData,j=f&&f.shift||0,l=this.options.data;Kb(d,h);if(f&&c)f.shift=j+1;if(g)g.shift=j+1,g.isArea=!0;b=q(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);k.push(d.x);i.push(this.valueCount===4?[d.open,d.high,d.low,d.close]:d.y);l.push(a);c&&(e[0]?e[0].remove(!1):(e.shift(),k.shift(),i.shift(),l.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&h.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.initialColor,
f=this.chart,g=null;this.xIncrement=null;this.pointRange=this.xAxis&&this.xAxis.categories&&1||d.pointRange;if(z(e))f.counters.color=e;var h=[],k=[],i=a?a.length:[],j=this.valueCount===4;if(i>(d.turboThreshold||1E3)){for(e=0;g===null&&e<i;)g=a[e],e++;if(Ub(g)){g=q(d.pointStart,0);d=q(d.pointInterval,1);for(e=0;e<i;e++)h[e]=g,k[e]=a[e],g+=d;this.xIncrement=g}else if(Object.prototype.toString.call(g)==="[object Array]")if(j)for(e=0;e<i;e++)d=a[e],h[e]=d[0],k[e]=d.slice(1,5);else for(e=0;e<i;e++)d=a[e],
h[e]=d[0],k[e]=d[1]}else for(e=0;e<i;e++)d={series:this},this.pointClass.prototype.applyOptions.apply(d,[a[e]]),h[e]=d.x,k[e]=j?[d.open,d.high,d.low,d.close]:d.y;this.data=[];this.options.data=a;this.xData=h;this.yData=k;for(e=c&&c.length||0;e--;)c[e]&&c[e].destroy&&c[e].destroy();this.isDirty=this.isDirtyData=f.isDirtyBox=!0;q(b,!0)&&f.redraw(!1)},remove:function(a,b){var c=this,d=c.chart,a=q(a,!0);if(!c.isRemoving)c.isRemoving=!0,U(c,"remove",null,function(){c.destroy();d.isDirtyLegend=d.isDirtyBox=
!0;a&&d.redraw(b)});c.isRemoving=!1},processData:function(){var a=this.xData,b=this.yData,c=a.length,d=0,e=c,f,g,h,k=this.options;h=k.cropThreshold;if(this.isCartesian&&!this.isDirty&&!this.xAxis.isDirty&&!this.yAxis.isDirty)return!1;if(!h||c>h||this.forceCrop){h=this.xAxis.getExtremes();var i=h.min,j=h.max;if(a[c-1]<i||a[0]>j)a=[],b=[];else if(a[0]<i||a[c-1]>j){for(h=0;h<c;h++)if(a[h]>=i){d=R(0,h-1);break}for(;h<c;h++)if(a[h]>j){e=h+1;break}a=a.slice(d,e);b=b.slice(d,e);f=!0}}for(h=a.length-1;h>
0;h--)if(c=a[h]-a[h-1],g===B||c<g)g=c;this.cropped=f;this.cropStart=d;this.processedXData=a;this.processedYData=b;if(k.pointRange===null)this.pointRange=g||1;this.closestPointRange=g},generatePoints:function(){var a=this.options.data,b=this.data,c,d=this.processedXData,e=this.processedYData,f=this.pointClass,g=d.length,h=this.cropStart||0,k,i=this.hasGroupedData,j,l=[],m;if(!b&&!i)b=[],b.length=a.length,b=this.data=b;for(m=0;m<g;m++)k=h+m,i?l[m]=(new f).init(this,[d[m]].concat(tb(e[m]))):(b[k]?j=
b[k]:b[k]=j=(new f).init(this,a[k],d[m]),l[m]=j);if(b&&(g!==(c=b.length)||i))for(m=0;m<c;m++)m===h&&!i&&(m+=g),b[m]&&b[m].destroyElements();this.data=b;this.points=l},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.chart,b=this.options,c=b.stacking,d=this.xAxis,e=d.categories,f=this.yAxis,g=this.points,h=g.length,k=!!this.modifyValue,i=this.index===f.series.length-1,j;for(j=0;j<h;j++){var l=g[j],m=l.x,p=l.y,n=l.low,q=f.stacks[(p<b.threshold?"-":"")+this.stackKey];
l.plotX=y(d.translate(m)*10)/10;if(c&&this.visible&&q&&q[m]){n=q[m];m=n.total;n.cum=n=n.cum-p;p=n+p;if(i)n=b.threshold;c==="percent"&&(n=m?n*100/m:0,p=m?p*100/m:0);l.percentage=m?l.y*100/m:0;l.stackTotal=m}if(z(n))l.yBottom=f.translate(n,0,1,0,1);k&&(p=this.modifyValue(p,l));if(p!==null)l.plotY=y(f.translate(p,0,1,0,1)*10)/10;l.clientX=a.inverted?a.plotHeight-l.plotX:l.plotX;l.category=e&&e[l.x]!==B?e[l.x]:l.x}this.getSegments()},setTooltipPoints:function(a){var b=this.chart,c=b.inverted,d=[],b=y((c?
b.plotTop:b.plotLeft)+b.plotSizeX),e,f;e=this.xAxis;var g,h,k=[];if(this.options.enableMouseTracking!==!1){if(a)this.tooltipPoints=null;n(this.segments||this.points,function(a){d=d.concat(a)});e&&e.reversed&&(d=d.reverse());a=d.length;for(h=0;h<a;h++){g=d[h];e=d[h-1]?d[h-1]._high+1:0;for(f=g._high=d[h+1]?Ra((g.plotX+(d[h+1]?d[h+1].plotX:b))/2):b;e<=f;)k[c?b-e++:e++]=g}this.tooltipPoints=k}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat||"%A, %b %e, %Y",d=this.xAxis;
return b.headerFormat.replace("{point.key}",d&&d.options.type==="datetime"?wb(c,a):a).replace("{series.name}",this.name).replace("{series.color}",this.color)},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(ua||!a.mouseIsDown){if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&U(this,"mouseOver");this.setState(cb);a.hoverSeries=this}},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&U(this,"mouseOut");
c&&!a.stickyTracking&&!c.shared&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this.chart,c=this.clipRect,d=this.options.animation;d&&!sb(d)&&(d={});if(a){if(!c.isAnimating)c.attr("width",0),c.isAnimating=!0}else c.animate({width:b.plotSizeX},d),this.animate=null},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,k;if(this.options.marker.enabled)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,e!==B&&!isNaN(e))if(a=g.pointAttr[g.selected?"select":
oa],h=a.r,k)k.animate(I({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else if(h>0)g.graphic=c.renderer.symbol(q(g.marker&&g.marker.symbol,this.symbol),d-h,e-h,2*h,2*h).attr(a).add(this.group)},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=q(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=J[a.type].marker?a.options.marker:a.options,c=b.states,d=c[cb],e,f=a.color,g={stroke:f,fill:f},h=a.points,
k=[],i,j=a.pointAttrToOptions,l;a.options.marker?(d.radius=d.radius||b.radius+2,d.lineWidth=d.lineWidth||b.lineWidth+1):d.color=d.color||$a(d.color||f).brighten(d.brightness).get();k[oa]=a.convertAttribs(b,g);n([cb,"select"],function(b){k[b]=a.convertAttribs(c[b],k[oa])});a.pointAttr=k;for(f=h.length;f--;){g=h[f];if((b=g.options&&g.options.marker||g.options)&&b.enabled===!1)b.radius=0;e=!1;if(g.options)for(l in j)z(b[j[l]])&&(e=!0);if(e){i=[];c=b.states||{};e=c[cb]=c[cb]||{};if(!a.options.marker)e.color=
$a(e.color||g.options.color).brighten(e.brightness||d.brightness).get();i[oa]=a.convertAttribs(b,k[oa]);i[cb]=a.convertAttribs(c[cb],k[cb],i[oa]);i.select=a.convertAttribs(c.select,k.select,i[oa])}else i=k;g.pointAttr=i}},destroy:function(){var a=this,b=a.chart,c=a.clipRect,d=/AppleWebKit\/533/.test(mb),e,f,g=a.data||[],h,k,i;U(a,"destroy");ra(a);n(["xAxis","yAxis"],function(b){if(i=a[b])Ib(i.series,a),i.isDirty=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(f=g.length;f--;)(h=g[f])&&h.destroy&&
h.destroy();a.points=null;if(c&&c!==b.clipRect)a.clipRect=c.destroy();n(["area","graph","dataLabelsGroup","group","tracker"],function(b){a[b]&&(e=d&&b==="group"?"hide":"destroy",a[b][e]())});if(b.hoverSeries===a)b.hoverSeries=null;Ib(b.series,a);for(k in a)delete a[k]},drawDataLabels:function(){if(this.options.dataLabels.enabled){var a,b,c=this.points,d=this.options,e=d.dataLabels,f,g=this.dataLabelsGroup,h=this.chart,k=this.xAxis,k=k?k.left:h.plotLeft,i=this.yAxis,i=i?i.top:h.plotTop,j=h.renderer,
l=h.inverted,m=this.type,p=d.stacking,H=m==="column"||m==="bar",s=e.verticalAlign===null,t=e.y===null;H&&(p?(s&&(e=G(e,{verticalAlign:"middle"})),t&&(e=G(e,{y:{top:14,middle:4,bottom:-6}[e.verticalAlign]}))):s&&(e=G(e,{verticalAlign:"top"})));g?g.translate(k,i):g=this.dataLabelsGroup=j.g("data-labels").attr({visibility:this.visible?Wa:Ta,zIndex:6}).translate(k,i).add();k=e.color;k==="auto"&&(k=null);e.style.color=q(k,this.color,"black");n(c,function(c){var i=c.barX,k=i&&i+c.barW/2||c.plotX||-999,
n=q(c.plotY,-999),p=c.dataLabel,s=e.align,y=t?c.y>=0?-6:12:e.y;f=e.formatter.call(c.getLabelConfig());a=(l?h.plotWidth-n:k)+e.x;b=(l?h.plotHeight-k:n)+y;m==="column"&&(a+={left:-1,right:1}[s]*c.barW/2||0);l&&c.y<0&&(s="right",a-=10);if(p)l&&!e.y&&(b=b+O(p.styles.lineHeight)*0.9-p.getBBox().height/2),p.attr({text:f}).animate({x:a,y:b});else if(z(f))p=c.dataLabel=j.text(f,a,b).attr({align:s,rotation:e.rotation,zIndex:1}).css(e.style).add(g),l&&!e.y&&p.attr({y:b+O(p.styles.lineHeight)*0.9-p.getBBox().height/
2});if(H&&d.stacking&&p)k=c.barY,n=c.barW,c=c.barH,p.align(e,null,{x:l?h.plotWidth-k-c:i,y:l?h.plotHeight-i-n:k,width:l?c:n,height:l?n:c})})}},drawGraph:function(){var a=this,b=a.options,c=a.graph,d=[],e,f=a.area,g=a.group,h=b.lineColor||a.color,k=b.lineWidth,i=b.dashStyle,j,l=a.chart.renderer,m=a.yAxis.getThreshold(b.threshold),p=/^area/.test(a.type),H=[],s=[];n(a.segments,function(c){j=[];n(c,function(d,e){a.getPointSpline?j.push.apply(j,a.getPointSpline(c,d,e)):(j.push(e?ha:ta),e&&b.step&&j.push(d.plotX,
c[e-1].plotY),j.push(d.plotX,d.plotY))});c.length>1?d=d.concat(j):H.push(c[0]);if(p){var e=[],f,g=j.length;for(f=0;f<g;f++)e.push(j[f]);g===3&&e.push(ha,j[1],j[2]);if(b.stacking&&a.type!=="areaspline")for(f=c.length-1;f>=0;f--)f<c.length-1&&b.step&&e.push(c[f+1].plotX,c[f].yBottom),e.push(c[f].plotX,c[f].yBottom);else e.push(ha,c[c.length-1].plotX,m,ha,c[0].plotX,m);s=s.concat(e)}});a.graphPath=d;a.singlePoints=H;if(p)e=q(b.fillColor,$a(a.color).setOpacity(b.fillOpacity||0.75).get()),f?f.animate({d:s}):
a.area=a.chart.renderer.path(s).attr({fill:e}).add(g);if(c)Lb(c),c.animate({d:d});else if(k){c={stroke:h,"stroke-width":k};if(i)c.dashstyle=i;a.graph=l.path(d).attr(c).add(g).shadow(b.shadow)}},render:function(){var a=this,b=a.chart,c,d,e=a.options,f=e.clip!==!1,g=e.animation,h=g&&a.animate,g=h?g&&g.duration||500:0,k=a.clipRect,i=b.renderer;if(!k&&(k=a.clipRect=!b.hasRendered&&b.clipRect?b.clipRect:i.clipRect(0,0,b.plotSizeX,b.plotSizeY+1),!b.clipRect))b.clipRect=k;if(!a.group)c=a.group=i.g("series"),
b.inverted&&(d=function(){c.attr({width:b.plotWidth,height:b.plotHeight}).invert()},d(),W(b,"resize",d),W(a,"destroy",function(){ra(b,"resize",d)})),f&&c.clip(a.clipRect),c.attr({visibility:a.visible?Wa:Ta,zIndex:e.zIndex}).translate(a.xAxis.left,a.yAxis.top).add(b.seriesGroup);a.drawDataLabels();h&&a.animate(!0);a.getAttribs();a.drawGraph&&a.drawGraph();a.drawPoints();a.options.enableMouseTracking!==!1&&a.drawTracker();h&&a.animate();setTimeout(function(){k.isAnimating=!1;if((c=a.group)&&k!==b.clipRect&&
k.renderer){if(f)c.clip(a.clipRect=b.clipRect);k.destroy()}},g);a.isDirty=a.isDirtyData=!1},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:this.xAxis.left,translateY:this.yAxis.top}));this.translate();this.setTooltipPoints(!0);this.render();b&&U(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=b.states,b=b.lineWidth,a=a||oa;if(this.state!==a)this.state=a,d[a]&&d[a].enabled===
!1||(a&&(b=d[a].lineWidth||b+1),c&&!c.dashstyle&&c.attr({"stroke-width":b},a?0:500))},setVisible:function(a,b){var c=this.chart,d=this.legendItem,e=this.group,f=this.tracker,g=this.dataLabelsGroup,h,k=this.points,i=c.options.chart.ignoreHiddenSeries;h=this.visible;h=(this.visible=a=a===B?!h:a)?"show":"hide";if(e)e[h]();if(f)f[h]();else if(k)for(e=k.length;e--;)if(f=k[e],f.tracker)f.tracker[h]();if(g)g[h]();d&&c.legend.colorizeItem(this,a);this.isDirty=!0;this.options.stacking&&n(c.series,function(a){if(a.options.stacking&&
a.visible)a.isDirty=!0});if(i)c.isDirtyBox=!0;b!==!1&&c.redraw();U(this,h)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===B?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;U(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=[].concat(a.graphPath),d=c.length,e=a.chart,f=e.options.tooltip.snap,g=a.tracker,h=b.cursor,h=h&&{cursor:h},k=a.singlePoints,i;if(d)for(i=d+1;i--;)c[i]===ta&&c.splice(i+1,0,
c[i+1]-f,c[i+2],ha),(i&&c[i]===ta||i===d)&&c.splice(i,0,ha,c[i-2]+f,c[i-1]);for(i=0;i<k.length;i++)d=k[i],c.push(ta,d.plotX-f,d.plotY,ha,d.plotX+f,d.plotY);g?g.attr({d:c}):a.tracker=e.renderer.path(c).attr({isTracker:!0,stroke:Gc,fill:La,"stroke-width":b.lineWidth+2*f,visibility:a.visible?Wa:Ta,zIndex:b.zIndex||1}).on(ua?"touchstart":"mouseover",function(){if(e.hoverSeries!==a)a.onMouseOver()}).on("mouseout",function(){if(!b.stickyTracking)a.onMouseOut()}).css(h).add(e.trackerGroup)}};x=ka(X);aa.line=
x;x=ka(X,{type:"area",useThreshold:!0});aa.area=x;x=ka(X,{type:"spline",getPointSpline:function(a,b,c){var d=b.plotX,e=b.plotY,f=a[c-1],g=a[c+1],h,k,i,j;if(c&&c<a.length-1){a=f.plotY;i=g.plotX;var g=g.plotY,l;h=(1.5*d+f.plotX)/2.5;k=(1.5*e+a)/2.5;i=(1.5*d+i)/2.5;j=(1.5*e+g)/2.5;l=(j-k)*(i-d)/(i-h)+e-j;k+=l;j+=l;k>a&&k>e?(k=R(a,e),j=2*e-k):k<a&&k<e&&(k=wa(a,e),j=2*e-k);j>g&&j>e?(j=R(g,e),k=2*e-j):j<g&&j<e&&(j=wa(g,e),k=2*e-j);b.rightContX=i;b.rightContY=j}c?(b=["C",f.rightContX||f.plotX,f.rightContY||
f.plotY,h||d,k||e,d,e],f.rightContX=f.rightContY=null):b=[ta,d,e];return b}});aa.spline=x;x=ka(x,{type:"areaspline",useThreshold:!0});aa.areaspline=x;var ec=ka(X,{type:"column",useThreshold:!0,pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",r:"borderRadius"},init:function(){X.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0})},translate:function(){var a=this,b=a.chart,c=a.options,d=c.stacking,
e=c.borderWidth,f=0,g=a.xAxis,h=g.reversed,k={},i,j;X.prototype.translate.apply(a);n(b.series,function(b){if(b.type===a.type&&b.visible&&a.options.group===b.options.group)b.options.stacking?(i=b.stackKey,k[i]===B&&(k[i]=f++),j=k[i]):j=f++,b.columnIndex=j});var l=a.points,g=Ha(g.translationSlope)*(g.ordinalSlope||g.closestPointRange),m=g*c.groupPadding,p=(g-2*m)/f,H=c.pointWidth,s=z(H)?(p-H)/2:p*c.pointPadding,t=bc(R(q(H,p-2*s),1)),r=s+(m+((h?f-a.columnIndex:a.columnIndex)||0)*p-g/2)*(h?-1:1),v=a.yAxis.getThreshold(c.threshold),
u=q(c.minPointLength,5);n(l,function(f){var g=f.plotY,h=f.yBottom||v,j=f.plotX+r,i=bc(wa(g,h)),k=bc(R(g,h)-i),l=a.yAxis.stacks[(f.y<0?"-":"")+a.stackKey],m;d&&a.visible&&l&&l[f.x]&&l[f.x].setOffset(r,t);Ha(k)<u&&(u&&(k=u,i=Ha(i-v)>u?h-u:v-(g<=v?u:0)),m=i-3);I(f,{barX:j,barY:i,barW:t,barH:k});f.shapeType="rect";g=I(b.renderer.Element.prototype.crisp.apply({},[e,j,i,t,k]),{r:c.borderRadius});e%2&&(g.y-=1,g.height+=1);f.shapeArgs=g;f.trackerArgs=z(m)&&G(f.shapeArgs,{height:R(6,k+3),y:m})})},getSymbol:function(){},
drawGraph:function(){},drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d,e;n(a.points,function(f){var g=f.plotY;if(g!==B&&!isNaN(g)&&f.y!==null)d=f.graphic,e=f.shapeArgs,d?(Lb(d),d.animate(e)):f.graphic=d=c[f.shapeType](e).attr(f.pointAttr[f.selected?"select":oa]).add(a.group).shadow(b.shadow)})},drawTracker:function(){var a=this,b=a.chart,c=b.renderer,d,e,f=+new Date,g=a.options,h=g.cursor,k=h&&{cursor:h},i;n(a.points,function(h){e=h.tracker;d=h.trackerArgs||h.shapeArgs;delete d.strokeWidth;
if(h.y!==null)e?e.attr(d):h.tracker=c[h.shapeType](d).attr({isTracker:f,fill:Gc,visibility:a.visible?Wa:Ta,zIndex:g.zIndex||1}).on(ua?"touchstart":"mouseover",function(c){i=c.relatedTarget||c.fromElement;if(b.hoverSeries!==a&&P(i,"isTracker")!==f)a.onMouseOver();h.onMouseOver()}).on("mouseout",function(b){if(!g.stickyTracking&&(i=b.relatedTarget||b.toElement,P(i,"isTracker")!==f))a.onMouseOut()}).css(k).add(h.group||b.trackerGroup)})},animate:function(a){var b=this,c=b.points;if(!a)n(c,function(a){var c=
a.graphic,a=a.shapeArgs;c&&(c.attr({height:0,y:b.yAxis.translate(0,0,1)}),c.animate({height:a.height,y:a.y},b.options.animation))}),b.animate=null},remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});X.prototype.remove.apply(a,arguments)}});aa.column=ec;x=ka(ec,{type:"bar",init:function(){this.inverted=!0;ec.prototype.init.apply(this,arguments)}});aa.bar=x;x=ka(X,{type:"scatter",translate:function(){var a=this;X.prototype.translate.apply(a);
n(a.points,function(b){b.shapeType="circle";b.shapeArgs={x:b.plotX,y:b.plotY,r:a.chart.options.tooltip.snap}})},drawTracker:function(){var a=this,b=a.options.cursor,c=b&&{cursor:b},d;n(a.points,function(b){(d=b.graphic)&&d.attr({isTracker:!0}).on("mouseover",function(){a.onMouseOver();b.onMouseOver()}).on("mouseout",function(){if(!a.options.stickyTracking)a.onMouseOut()}).css(c)})}});aa.scatter=x;x=ka(ib,{init:function(){ib.prototype.init.apply(this,arguments);var a=this,b;I(a,{visible:a.visible!==
!1,name:q(a.name,"Slice")});b=function(){a.slice()};W(a,"select",b);W(a,"unselect",b);return a},setVisible:function(a){var b=this.series.chart,c=this.tracker,d=this.dataLabel,e=this.connector,f=this.shadowGroup,g;g=(this.visible=a=a===B?!this.visible:a)?"show":"hide";this.group[g]();if(c)c[g]();if(d)d[g]();if(e)e[g]();if(f)f[g]();this.legendItem&&b.legend.colorizeItem(this,a)},slice:function(a,b,c){var d=this.series.chart,e=this.slicedTranslation;Kb(c,d);q(b,!0);a=this.sliced=z(a)?a:!this.sliced;
a={translateX:a?e[0]:d.plotLeft,translateY:a?e[1]:d.plotTop};this.group.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}});x=ka(X,{type:"pie",isCartesian:!1,pointClass:x,pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},getColor:function(){this.initialColor=this.chart.counters.color},animate:function(){var a=this;n(a.points,function(b){var c=b.graphic,b=b.shapeArgs,d=-hb/2;c&&(c.attr({r:0,start:d,end:d}),c.animate({r:b.r,start:b.start,end:b.end},a.options.animation))});
a.animate=null},setData:function(){X.prototype.setData.apply(this,arguments);this.processData();this.generatePoints()},translate:function(){this.generatePoints();var a=0,b=-0.25,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f=c.center.concat([c.size,c.innerSize||0]),g=this.chart,h=g.plotWidth,k=g.plotHeight,i,j,l,m=this.points,p=2*hb,q,s=wa(h,k),t,r,v,u=c.dataLabels.distance,f=nb(f,function(a,b){return(t=/%$/.test(a))?[h,k,s,s][b]*O(a)/100:a});this.getX=function(a,b){l=sa.asin((a-f[1])/(f[2]/
2+u));return f[0]+(b?-1:1)*Ma(l)*(f[2]/2+u)};this.center=f;n(m,function(b){a+=b.y});n(m,function(c){q=a?c.y/a:0;i=y(b*p*1E3)/1E3;b+=q;j=y(b*p*1E3)/1E3;c.shapeType="arc";c.shapeArgs={x:f[0],y:f[1],r:f[2]/2,innerR:f[3]/2,start:i,end:j};l=(j+i)/2;c.slicedTranslation=nb([Ma(l)*d+g.plotLeft,pa(l)*d+g.plotTop],y);r=Ma(l)*f[2]/2;v=pa(l)*f[2]/2;c.tooltipPos=[f[0]+r*0.7,f[1]+v*0.7];c.labelPos=[f[0]+r+Ma(l)*u,f[1]+v+pa(l)*u,f[0]+r+Ma(l)*e,f[1]+v+pa(l)*e,f[0]+r,f[1]+v,u<0?"center":l<p/4?"left":"right",l];c.percentage=
q*100;c.total=a});this.setTooltipPoints()},render:function(){this.getAttribs();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();this.drawDataLabels();this.options.animation&&this.animate&&this.animate();this.isDirty=!1},drawPoints:function(){var a=this.chart,b=a.renderer,c,d,e,f=this.options.shadow,g,h;n(this.points,function(k){d=k.graphic;h=k.shapeArgs;e=k.group;g=k.shadowGroup;if(f&&!g)g=k.shadowGroup=b.g("shadow").attr({zIndex:4}).add();if(!e)e=k.group=b.g("point").attr({zIndex:5}).add();
c=k.sliced?k.slicedTranslation:[a.plotLeft,a.plotTop];e.translate(c[0],c[1]);g&&g.translate(c[0],c[1]);d?d.animate(h):k.graphic=b.arc(h).attr(I(k.pointAttr[oa],{"stroke-linejoin":"round"})).add(k.group).shadow(f,g);k.visible===!1&&k.setVisible(!1)})},drawDataLabels:function(){var a=this.data,b,c=this.chart,d=this.options.dataLabels,e=q(d.connectorPadding,10),f=q(d.connectorWidth,1),g,h,k=q(d.softConnector,!0),i=d.distance,j=this.center,l=j[2]/2,j=j[1],m=i>0,p=[[],[]],H,s,t,r,v=2,u;if(d.enabled){X.prototype.drawDataLabels.apply(this);
n(a,function(a){a.dataLabel&&p[a.labelPos[7]<hb/2?0:1].push(a)});p[1].reverse();r=function(a,b){return b.y-a.y};for(a=p[0][0]&&p[0][0].dataLabel&&O(p[0][0].dataLabel.styles.lineHeight);v--;){var y=[],z=[],E=p[v],x=E.length,w;for(u=j-l-i;u<=j+l+i;u+=a)y.push(u);t=y.length;if(x>t){h=[].concat(E);h.sort(r);for(u=x;u--;)h[u].rank=u;for(u=x;u--;)E[u].rank>=t&&E.splice(u,1);x=E.length}for(u=0;u<x;u++){b=E[u];h=b.labelPos;b=9999;for(s=0;s<t;s++)g=Ha(y[s]-h[1]),g<b&&(b=g,w=s);if(w<u&&y[u]!==null)w=u;else for(t<
x-u+w&&y[u]!==null&&(w=t-x+u);y[w]===null;)w++;z.push({i:w,y:y[w]});y[w]=null}z.sort(r);for(u=0;u<x;u++){b=E[u];h=b.labelPos;g=b.dataLabel;s=z.pop();H=h[1];t=b.visible===!1?Ta:Wa;w=s.i;s=s.y;if(H>s&&y[w+1]!==null||H<s&&y[w-1]!==null)s=H;H=this.getX(w===0||w===y.length-1?H:s,v);g.attr({visibility:t,align:h[6]})[g.moved?"animate":"attr"]({x:H+d.x+({left:e,right:-e}[h[6]]||0),y:s+d.y});g.moved=!0;if(m&&f)g=b.connector,h=k?[ta,H+(h[6]==="left"?5:-5),s,"C",H,s,2*h[2]-h[4],2*h[3]-h[5],h[2],h[3],ha,h[4],
h[5]]:[ta,H+(h[6]==="left"?5:-5),s,ha,h[2],h[3],ha,h[4],h[5]],g?(g.animate({d:h}),g.attr("visibility",t)):b.connector=g=this.chart.renderer.path(h).attr({"stroke-width":f,stroke:d.connectorColor||b.color||"#606060",visibility:t,zIndex:3}).translate(c.plotLeft,c.plotTop).add()}}}},drawTracker:ec.prototype.drawTracker,getSymbol:function(){}});aa.pie=x;var M=X.prototype,Sc=M.processData,Tc=M.generatePoints,Uc=M.destroy,Vc=M.tooltipHeaderFormatter,x={approximation:"average",groupPixelWidth:2,dateTimeLabelFormats:Ka(pb,
["%A, %b %e, %H:%M:%S.%L","%A, %b %e, %H:%M:%S.%L","-%H:%M:%S.%L"],qa,["%A, %b %e, %H:%M:%S","%A, %b %e, %H:%M:%S","-%H:%M:%S"],ab,["%A, %b %e, %H:%M","%A, %b %e, %H:%M","-%H:%M"],Va,["%A, %b %e, %H:%M","%A, %b %e, %H:%M","-%H:%M"],ma,["%A, %b %e, %Y","%A, %b %e","-%A, %b %e, %Y"],Ea,["Week from %A, %b %e, %Y","%A, %b %e","-%A, %b %e, %Y"],Pa,["%B %Y","%B","-%B %Y"],bb,["%Y","%Y","-%Y"])},Jc=[[pb,[1,2,5,10,20,25,50,100,200,500]],[qa,[1,2,5,10,15,30]],[ab,[1,2,5,10,15,30]],[Va,[1,2,3,4,6,8,12]],[ma,
[1]],[Ea,[1]],[Pa,[1,3,6]],[bb,null]],ob={sum:function(a){var b=a.length,c;if(!b&&a.hasNulls)c=null;else if(b)for(c=0;b--;)c+=a[b];return c},average:function(a){var b=a.length,a=ob.sum(a);typeof a==="number"&&b&&(a/=b);return a},open:function(a){return a.length?a[0]:a.hasNulls?null:B},high:function(a){return a.length?Bb(a):a.hasNulls?null:B},low:function(a){return a.length?Jb(a):a.hasNulls?null:B},close:function(a){return a.length?a[a.length-1]:a.hasNulls?null:B},ohlc:function(a,b,c,d){a=ob.open(a);
b=ob.high(b);c=ob.low(c);d=ob.close(d);if(typeof a==="number"||typeof b==="number"||typeof c==="number"||typeof d==="number")return[a,b,c,d]}};M.groupData=function(a,b,c,d){var e=this.data,f=this.options.data,g=[],h=[],k=a.length,i,j,l=!!b;j=[];var m=[],n=[],q=[],s=typeof d==="function"?d:ob[d],t;for(t=0;t<=k;t++){for(;c[1]!==B&&a[t]>=c[1]||t===k;)if(i=c.shift(),j=s(j,m,n,q),j!==B&&(g.push(i),h.push(j)),j=[],m=[],n=[],q=[],t===k)break;if(t===k)break;i=l?b[t]:null;if(d==="ohlc"){i=this.cropStart+t;
var r=e&&e[i]||this.pointClass.prototype.applyOptions.apply({},[f[i]]);i=r.open;var v=r.high,u=r.low,r=r.close;if(typeof i==="number")j.push(i);else if(i===null)j.hasNulls=!0;if(typeof v==="number")m.push(v);else if(v===null)m.hasNulls=!0;if(typeof u==="number")n.push(u);else if(u===null)n.hasNulls=!0;if(typeof r==="number")q.push(r);else if(r===null)q.hasNulls=!0}else if(typeof i==="number")j.push(i);else if(i===null)j.hasNulls=!0}return[g,h]};M.processData=function(){var a=this.options,b=a.dataGrouping,
c=b&&b.enabled,d;this.forceCrop=c;if(Sc.apply(this)!==!1&&c){var c=this.chart,e=this.processedXData,f=this.processedYData,g=c.plotSizeX,h=this.xAxis,k=q(h.groupPixelWidth,b.groupPixelWidth),i=g/k,j=e.length,l=this.groupedData,m=c.series;if(!h.groupPixelWidth){for(c=m.length;c--;)m[c].xAxis===h&&m[c].options.dataGrouping&&(k=R(k,m[c].options.dataGrouping.groupPixelWidth));h.groupPixelWidth=k}n(l||[],function(a,b){a&&(l[b]=a.destroy?a.destroy():null)});if(j>i||b.forced){d=!0;this.points=null;c=h.getExtremes();
i=c.min;m=c.max;g=k*(m-i)/(h.options.ordinal?g*((m-i)/(j*this.closestPointRange)):g);h=rc(g,i,m,null,b.units||Jc);c=M.groupData.apply(this,[e,f,h,b.approximation]);e=c[0];f=c[1];if(b.smoothed){c=e.length-1;for(e[c]=m;c--&&c>0;)e[c]+=g/2;e[0]=i}this.currentDataGrouping=h.info;if(a.pointRange===null)this.pointRange=h.info.totalRange;this.closestPointRange=h.info.totalRange;this.processedXData=e;this.processedYData=f}else this.currentDataGrouping=null,this.pointRange=a.pointRange;this.hasGroupedData=
d}};M.generatePoints=function(){Tc.apply(this);this.groupedData=this.hasGroupedData?this.points:null};M.tooltipHeaderFormatter=function(a){var b=this.tooltipOptions,c=this.options.dataGrouping,d=b.xDateFormat,e,f=this.xAxis,g,h;if(f&&f.options.type==="datetime"&&c){g=this.currentDataGrouping;c=c.dateTimeLabelFormats;if(g)f=c[g.unitName],g.count===1?d=f[0]:(d=f[1],e=f[2]);else if(!d)for(h in L)if(L[h]>=f.closestPointRange){d=c[h][0];break}d=wb(d,a);e&&(d+=wb(e,a+g.totalRange-1));a=b.headerFormat.replace("{point.key}",
d)}else a=Vc.apply(this,[a]);return a};M.destroy=function(){for(var a=this.groupedData||[],b=a.length;b--;)a[b]&&a[b].destroy();Uc.apply(this)};J.line.dataGrouping=J.spline.dataGrouping=J.area.dataGrouping=J.areaspline.dataGrouping=x;J.column.dataGrouping=G(x,{approximation:"sum",groupPixelWidth:10});J.ohlc=G(J.column,{lineWidth:1,dataGrouping:{approximation:"ohlc",enabled:!0,groupPixelWidth:5},states:{hover:{lineWidth:3}}});var x=ka(ib,{applyOptions:function(a){var b=this.series,c=0;if(typeof a===
"object"&&typeof a.length!=="number")I(this,a),this.options=a;else if(a.length){if(a.length===5){if(typeof a[0]==="string")this.name=a[0];else if(typeof a[0]==="number")this.x=a[0];c++}this.open=a[c++];this.high=a[c++];this.low=a[c++];this.close=a[c++]}this.y=this.high;if(this.x===B&&b)this.x=b.autoIncrement();return this},tooltipFormatter:function(){var a=this.series;return['<span style="color:'+a.color+';font-weight:bold">',this.name||a.name,"</span><br/>Open: ",this.open,"<br/>High: ",this.high,
"<br/>Low: ",this.low,"<br/>Close: ",this.close,"<br/>"].join("")}}),oc=ka(aa.column,{type:"ohlc",valueCount:4,pointClass:x,useThreshold:!1,pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},translate:function(){var a=this.yAxis;aa.column.prototype.translate.apply(this);n(this.points,function(b){if(b.open!==null)b.plotOpen=a.translate(b.open,0,1,0,1);if(b.close!==null)b.plotClose=a.translate(b.close,0,1,0,1)})},drawPoints:function(){var a=this,b=a.chart,c,d,e,f,g,h,k,i;n(a.points,function(j){if(j.plotY!==
B)k=j.graphic,c=j.pointAttr[j.selected?"selected":""],f=c["stroke-width"]%2/2,i=y(j.plotX)+f,g=y(j.barW/2),h=["M",i,y(j.yBottom),"L",i,y(j.plotY)],j.open!==null&&(d=y(j.plotOpen)+f,h.push("M",i,d,"L",i-g,d)),j.close!==null&&(e=y(j.plotClose)+f,h.push("M",i,e,"L",i+g,e)),k?k.animate({d:h}):j.graphic=b.renderer.path(h).attr(c).add(a.group)})},animate:null});aa.ohlc=oc;J.candlestick=G(J.column,{dataGrouping:{approximation:"ohlc",enabled:!0},lineColor:"black",lineWidth:1,upColor:"white",states:{hover:{lineWidth:2}}});
x=ka(oc,{type:"candlestick",pointAttrToOptions:{fill:"color",stroke:"lineColor","stroke-width":"lineWidth"},getAttribs:function(){oc.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,a=a.upColor,c=G(this.pointAttr);c[""].fill=a;c.hover.fill=b.hover.upColor||a;c.select.fill=b.select.upColor||a;n(this.points,function(a){if(a.open<a.close)a.pointAttr=c})},drawPoints:function(){var a=this,b=a.chart,c,d,e,f,g,h,k,i,j,l;n(a.points,function(m){i=m.graphic;if(m.plotY!==B)c=m.pointAttr[m.selected?
"selected":""],h=c["stroke-width"]%2/2,k=y(m.plotX)+h,d=y(m.plotOpen)+h,e=y(m.plotClose)+h,f=sa.min(d,e),g=sa.max(d,e),l=y(m.barW/2),j=["M",k-l,g,"L",k-l,f,"L",k+l,f,"L",k+l,g,"L",k-l,g,"M",k,g,"L",k,y(m.yBottom),"M",k,f,"L",k,y(m.plotY),"Z"],i?i.animate({d:j}):m.graphic=b.renderer.path(j).attr(c).add(a.group)})}});aa.candlestick=x;var fc=Ob.prototype.symbols;J.flags=G(J.column,{fillColor:"white",lineWidth:1,shape:"flag",stackDistance:7,states:{hover:{lineColor:"black",fillColor:"#FCFFC5"}},style:{fontSize:"11px",
fontWeight:"bold",textAlign:"center"},y:-30});aa.flags=ka(aa.column,{type:"flags",noSharedTooltip:!0,useThreshold:!1,init:X.prototype.init,pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth",r:"radius"},translate:function(){aa.column.prototype.translate.apply(this);var a=this.chart,b=this.points,c=b.length-1,d,e,f,g=(d=this.options.onSeries)&&a.get(d),h,k;if(g){h=g.points;d=h.length;for(b.sort(function(a,b){return a.x-b.x});d--&&b[c];)if(e=b[c],k=h[d],k.x<=e.x&&(e.plotY=
k.plotY,c--,d++,c<0))break}n(b,function(c,d){if(!g)c.plotY=c.y===B?a.plotHeight:c.plotY;if((f=b[d-1])&&f.plotX===c.plotX){if(f.stackIndex===B)f.stackIndex=0;c.stackIndex=f.stackIndex+1}})},drawPoints:function(){var a,b=this.points,c=this.chart.renderer,d,e,f=this.options,g=f.y,h=f.shape,k,i,j,l,m=f.lineWidth%2/2,n;for(i=b.length;i--;)if(j=b[i],d=j.plotX+m,a=j.stackIndex,e=j.plotY+g+m-(a!==B&&a*f.stackDistance),isNaN(e)&&(e=0),k=a?B:j.plotX+m,n=a?B:j.plotY,l=j.graphic,e!==B)a=j.pointAttr[j.selected?
"select":""],l?l.attr({x:d,y:e,r:a.r,anchorX:k,anchorY:n}):l=j.graphic=c.label(j.options.title||f.title||"A",d,e,h,k,n).css(G(f.style,j.style)).attr(a).attr({align:h==="flag"?"left":"center",width:f.width,height:f.height}).add(this.group).shadow(f.shadow),k=l.box,a=k.getBBox(),j.shapeArgs=I(a,{x:d-(h==="flag"?0:k.attr("width")/2),y:e})},drawTracker:function(){aa.column.prototype.drawTracker.apply(this);n(this.points,function(a){W(a.tracker.element,"mouseover",function(){a.graphic.toFront()})})},tooltipFormatter:function(a){return a.point.text},
animate:function(){}});fc.flag=function(a,b,c,d,e){var f=e&&e.anchorX||a,e=e&&e.anchorY||b;return["M",f,e,"L",a,b+d,a,b,a+c,b,a+c,b+d,a,b+d,"M",f,e,"Z"]};n(["circle","square"],function(a){fc[a+"pin"]=function(b,c,d,e,f){var g=f&&f.anchorX,f=f&&f.anchorY,b=fc[a](b,c,d,e);g&&f&&b.push("M",g,c+e,"L",g,f);return b}});Pb===Tb&&n(["flag","circlepin","squarepin"],function(a){Tb.prototype.symbols[a]=fc[a]});var gc=ua?"touchstart":"mousedown",Kc=ua?"touchmove":"mousemove",Lc=ua?"touchend":"mouseup",x=Ka("linearGradient",
{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#FFF"],[1,"#CCC"]]),na=[].concat(Jc);na[4]=[ma,[1,2,3,4]];na[5]=[Ea,[1,2,3]];I(la,{navigator:{handles:{backgroundColor:"#FFF",borderColor:"#666"},height:40,margin:10,maskFill:"rgba(255, 255, 255, 0.75)",outlineColor:"#444",outlineWidth:1,series:{type:"areaspline",color:"#4572A7",compare:null,fillOpacity:0.4,dataGrouping:{approximation:"average",groupPixelWidth:2,smoothed:!0,units:na},dataLabels:{enabled:!1},id:fb+"navigator-series",lineColor:"#4572A7",lineWidth:1,
marker:{enabled:!1},pointRange:0,shadow:!1},xAxis:{tickWidth:0,lineWidth:0,gridLineWidth:1,tickPixelInterval:200,labels:{align:"left",x:3,y:-4}},yAxis:{gridLineWidth:0,startOnTick:!1,endOnTick:!1,minPadding:0.1,maxPadding:0.1,labels:{enabled:!1},title:{text:null},tickWidth:0}},scrollbar:{height:ua?20:14,barBackgroundColor:x,barBorderRadius:2,barBorderWidth:1,barBorderColor:"#666",buttonArrowColor:"#666",buttonBackgroundColor:x,buttonBorderColor:"#666",buttonBorderRadius:2,buttonBorderWidth:1,rifleColor:"#666",
trackBackgroundColor:Ka("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#EEE"],[1,"#FFF"]]),trackBorderColor:"#CCC",trackBorderWidth:1}});Highcharts.Scroller=function(a){function b(a,b){var c={fill:M.backgroundColor,stroke:M.borderColor,"stroke-width":1},d;ja||(ka[b]=k.g().css({cursor:"e-resize"}).attr({zIndex:3}).add(),d=k.rect(-4.5,0,9,16,3,1).attr(c).add(ka[b]),xa.push(d),d=k.path(["M",-1.5,4,"L",-1.5,12,"M",0.5,4,"L",0.5,12]).attr(c).add(ka[b]),xa.push(d));ka[b].translate(U+K+parseInt(a,10),
F+J/2-8)}function c(a){var b;ja||(pa[a]=k.g().add(ya),b=k.rect(-0.5,-0.5,K+1,K+1,t.buttonBorderRadius,t.buttonBorderWidth).attr({stroke:t.buttonBorderColor,"stroke-width":t.buttonBorderWidth,fill:t.buttonBackgroundColor}).add(pa[a]),xa.push(b),b=k.path(["M",K/2+(a?-1:1),K/2-3,"L",K/2+(a?-1:1),K/2+3,K/2+(a?2:-2),K/2]).attr({fill:t.buttonArrowColor}).add(pa[a]),xa.push(b));a&&pa[a].attr({translateX:ia-K})}function d(d,e,f,g){if(!isNaN(d)){var h=t.barBorderWidth;X=F+Y;m=q(w.left,a.plotLeft+K);p=q(w.len,
a.plotWidth-2*K);U=m-K;ia=p+2*K;if(w.getExtremes){var i=a.xAxis[0].getExtremes(),n=i.dataMin===null,s=w.getExtremes(),u=wa(i.dataMin,s.dataMin),i=R(i.dataMax,s.dataMax);!n&&(u!==s.min||i!==s.max)&&w.setExtremes(u,i,!0,!1)}f=q(f,w.translate(d));g=q(g,w.translate(e));da=O(wa(f,g));C=O(R(f,g));B=C-da;if(!ja&&(l&&(la=k.rect().attr({fill:j.maskFill,zIndex:3}).add(),ma=k.rect().attr({fill:j.maskFill,zIndex:3}).add(),na=k.path().attr({"stroke-width":$,stroke:j.outlineColor,zIndex:3}).add()),r))ya=k.g().add(),
d=t.trackBorderWidth,o=k.rect().attr({y:-d%2/2,fill:t.trackBackgroundColor,stroke:t.trackBorderColor,"stroke-width":d,r:t.trackBorderRadius||0,height:K}).add(ya),oa=k.rect().attr({y:-h%2/2,height:K,fill:t.barBackgroundColor,stroke:t.barBorderColor,"stroke-width":h,r:T}).add(ya),qa=k.path().attr({stroke:t.rifleColor,"stroke-width":1}).add(ya);l&&(la.attr({x:m,y:F,width:da,height:J}),ma.attr({x:m+C,y:F,width:p-C,height:J}),na.attr({d:[ta,U,X,ha,m+da+Y,X,m+da+Y,X+P-K,ta,m+C-Y,X+P-K,ha,m+C-Y,X,U+ia,X]}),
b(da+Y,0),b(C+Y,1));r&&(c(0),c(1),ya.translate(U,y(X+J)),o.attr({width:ia}),oa.attr({x:y(K+da)+h%2/2,width:B-h}),h=K+da+B/2-0.5,qa.attr({d:[ta,h-3,K/4,ha,h-3,2*K/3,ta,h,K/4,ha,h,2*K/3,ta,h+3,K/4,ha,h+3,2*K/3],visibility:B>12?Wa:Ta}));ja=!0}}function e(b){var b=a.tracker.normalizeMouseEvent(b),c=b.chartX,d=b.chartY,e=ua?10:7;if(d>F&&d<F+J+K)(d=!r||d<F+J)&&sa.abs(c-da-m)<e?(v=!0,va=C):d&&sa.abs(c-C-m)<e?(u=!0,va=da):c>m+da&&c<m+C?(z=c,V=L.cursor,L.cursor="ew-resize",E=c-da):c>U&&c<U+ia&&(c=d?c-m-B/
2:c<m?da-wa(10,B):c>U+ia-K?da+wa(10,B):c<m+da?da-B:C,c<0?c=0:c+B>p&&(c=p-B),c!==da&&a.xAxis[0].setExtremes(w.translate(c,!0),w.translate(c+B,!0),!0,!1));b.preventDefault&&b.preventDefault()}function f(b){b=a.tracker.normalizeMouseEvent(b);b=b.chartX;b<m?b=m:b>U+ia-K&&(b=U+ia-K);v?(I=!0,d(0,0,b-m,va)):u?(I=!0,d(0,0,va,b-m)):z&&(I=!0,b<E?b=E:b>p+E-B&&(b=p+E-B),d(0,0,b-E,b-E+B))}function g(){I&&a.xAxis[0].setExtremes(w.translate(da,!0),w.translate(C,!0),!0,!1);v=u=z=I=E=null;L.cursor=V}function h(){var b=
aa.xAxis,c=b.getExtremes(),e=c.min,f=c.max,g=c.dataMin,c=c.dataMax,h=f-e,i,j,k,l,m;i=x.xData;var n=!!b.setExtremes;j=f>=i[i.length-1];i=e<=g;if(!s)x.options.pointStart=aa.xData[0],x.setData(aa.options.data,!1),m=!0;i&&(l=g,k=l+h);j&&(k=c,i||(l=R(k-h,x.xData[0])));n&&(i||j)?b.setExtremes(l,k,!0,!1):(m&&a.redraw(!1),d(R(e,g),wa(f,c)))}var k=a.renderer,i=a.options,j=i.navigator,l=j.enabled,m,p,x,s,t=i.scrollbar,r=t.enabled,v,u,z,va,E,I,w,ba,da,C,B,L=document.body.style,V,M=j.handles,J=l?j.height:0,$=
j.outlineWidth,K=r?t.height:0,P=J+K,T=t.barBorderRadius,F=j.top||a.chartHeight-J-K-i.chart.spacingBottom,Y=$/2,X,U,ia,ja,i=j.baseSeries,aa=a.series[i]||typeof i==="string"&&a.get(i)||a.series[0],la,ma,na,ka=[],ya,o,oa,qa,pa=[],xa=[];a.resetZoomEnabled=!1;(function(){var b=a.xAxis.length,c=a.yAxis.length;a.extraBottomMargin=P+j.margin;if(l){var d=aa.options,i=d.data,k=j.series;s=k.data;d.data=k.data=null;w=new a.Axis(G({ordinal:aa.xAxis.options.ordinal},j.xAxis,{isX:!0,type:"datetime",index:b,height:J,
top:F,offset:0,offsetLeft:K,offsetRight:-K,startOnTick:!1,endOnTick:!1,minPadding:0,maxPadding:0,zoomEnabled:!1}));ba=new a.Axis(G(j.yAxis,{alignTicks:!1,height:J,top:F,offset:0,index:c,zoomEnabled:!1}));b=G(aa.options,k,{threshold:null,clip:!1,enableMouseTracking:!1,group:"nav",padXAxis:!1,xAxis:b,yAxis:c,name:"Navigator",showInLegend:!1,isInternal:!0,visible:!0});d.data=i;k.data=s;b.data=s||i;x=a.initSeries(b);W(aa,"updatedData",h)}else w={translate:function(b,c){var d=aa.xAxis.getExtremes(),e=
a.plotWidth-2*K,f=d.dataMin,d=d.dataMax-f;return c?b*d/e+f:e*(b-f)/d}};W(a.container,gc,e);W(a.container,Kc,f);W(document,Lc,g)})();return{render:d,destroy:function(){ra(a.container,gc,e);ra(a.container,Kc,f);ra(document,Lc,g);l&&ra(aa,"updatedData",h);n([w,ba,la,ma,na,o,oa,qa,ya],function(a){a&&a.destroy&&a.destroy()});w=ba=la=ma=na=o=oa=qa=ya=null;n([pa,ka,xa],function(a){Cb(a)})}}};I(la,{rangeSelector:{buttonTheme:{width:28,height:16,padding:1,r:0,zIndex:10}}});la.lang=G(la.lang,{rangeSelectorZoom:"Zoom",
rangeSelectorFrom:"From:",rangeSelectorTo:"To:"});Highcharts.RangeSelector=function(a){function b(b,c,d){var e=a.xAxis[0],f=e&&e.getExtremes(),g,h=f&&f.dataMin,i=f&&f.dataMax,k,j=e&&wa(f.max,i),f=new Date(j);g=c.type;var c=c.count,l,m,n={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5};if(!(h===null||i===null||b===s))n[g]?(l=n[g]*c,k=R(j-l,h)):g==="month"?(f.setMonth(f.getMonth()-c),k=R(f.getTime(),h),l=2592E6*c):g==="ytd"?(f=new Date(0),g=new Date,m=g.getFullYear(),f.setFullYear(m),
String(m)!==wb("%Y",f)&&f.setFullYear(m-1),k=m=R(h||0,f.getTime()),g=g.getTime(),j=wa(i||g,g)):g==="year"?(f.setFullYear(f.getFullYear()-c),k=R(h,f.getTime()),l=31536E6*c):g==="all"&&e&&(k=h,j=i),r[b]&&r[b].setState(2),e?setTimeout(function(){e.setExtremes(k,j,q(d,1),0);s=b},1):(h=a.options.xAxis,h[0]=G(h[0],{range:l,min:m}),s=b)}function c(){j&&j.blur();l&&l.blur()}function d(a,b){var c=a.hasFocus?u.inputEditDateFormat||"%Y-%m-%d":u.inputDateFormat||"%b %e, %Y";if(b)a.HCTime=b;a.value=wb(c,a.HCTime)}
function e(b){var c=b==="min",e;m[b]=Y("span",{innerHTML:k[c?"rangeSelectorFrom":"rangeSelectorTo"]},u.labelStyle,i);e=Y("input",{name:b,className:fb+"range-selector",type:"text"},I({width:"80px",height:"16px",border:"1px solid silver",marginLeft:"5px",marginRight:c?"5px":"0",textAlign:"center"},u.inputStyle),i);e.onfocus=e.onblur=function(a){a=a||window.event;e.hasFocus=a.type==="focus";d(e)};e.onchange=function(){var b=e.value,d=Date.parse(b),f=a.xAxis[0].getExtremes();isNaN(d)&&(d=b.split("-"),
d=Date.UTC(O(d[0]),O(d[1])-1,O(d[2])));if(!isNaN(d)&&(c&&d>f.dataMin&&d<l.HCTime||!c&&d<f.dataMax&&d>j.HCTime))a.xAxis[0].setExtremes(c?d:f.min,c?f.max:d)};return e}var f=a.renderer,g,h=a.container,k=la.lang,i,j,l,m={},p,y,s,t,r=[],v,u,x=[{type:"month",count:1,text:"1m"},{type:"month",count:3,text:"3m"},{type:"month",count:6,text:"6m"},{type:"ytd",text:"YTD"},{type:"year",count:1,text:"1y"},{type:"all",text:"All"}];a.resetZoomEnabled=!1;(function(){a.extraTopMargin=25;u=a.options.rangeSelector;v=
u.buttons||x;var d=u.selected;W(h,gc,c);d!==B&&v[d]&&b(d,v[d],!1);W(a,"load",function(){W(a.xAxis[0],"afterSetExtremes",function(){r[s]&&r[s].setState(0);s=null})})})();return{render:function(c,m){var q=a.options.chart.style,w=u.buttonTheme,x=u.inputEnabled!==!1,z=w&&w.states,B=a.plotLeft,C;g||(t=f.text(k.rangeSelectorZoom,B,a.plotTop-10).css(u.labelStyle).add(),C=B+t.getBBox().width+5,n(v,function(c,d){r[d]=f.button(c.text,C,a.plotTop-25,function(){b(d,c);this.isActive=!0},w,z&&z.hover,z&&z.select).css({textAlign:"center"}).add();
C+=r[d].width+(u.buttonSpacing||0);s===d&&r[d].setState(2)}),x&&(y=i=Y("div",null,{position:"relative",height:0,fontFamily:q.fontFamily,fontSize:q.fontSize,zIndex:1}),h.parentNode.insertBefore(i,h),p=i=Y("div",null,I({position:"absolute",top:a.plotTop-25+"px",right:a.chartWidth-a.plotLeft-a.plotWidth+"px"},u.inputBoxStyle),i),j=e("min"),l=e("max")));x&&(d(j,c),d(l,m));g=!0},destroy:function(){ra(h,gc,c);n([r],function(a){Cb(a)});t&&(t=t.destroy());if(j)j.onfocus=j.onblur=j.onchange=null;if(l)l.onfocus=
l.onblur=l.onchange=null;n([j,l,m.min,m.max,p,y],function(a){Db(a)});j=l=m=i=p=y=null}}};ac.prototype.callbacks.push(function(a){function b(){f=a.xAxis[0].getExtremes();g.render(R(f.min,f.dataMin),wa(f.max,f.dataMax))}function c(){f=a.xAxis[0].getExtremes();h.render(f.min,f.max)}function d(a){g.render(a.min,a.max)}function e(a){h.render(a.min,a.max)}var f,g=a.scroller,h=a.rangeSelector;g&&(W(a.xAxis[0],"afterSetExtremes",d),W(a,"resize",b),b());h&&(W(a.xAxis[0],"afterSetExtremes",e),W(a,"resize",
c),c());W(a,"destroy",function(){g&&(ra(a,"resize",b),ra(a.xAxis[0],"afterSetExtremes",d));h&&(ra(a,"resize",c),ra(a.xAxis[0],"afterSetExtremes",e))})});Highcharts.StockChart=function(a,b){var c=a.series,d,e={marker:{enabled:!1,states:{hover:{enabled:!0,radius:5}}},gapSize:5,shadow:!1,states:{hover:{lineWidth:2}},dataGrouping:{enabled:!0}};a.xAxis=nb(tb(a.xAxis||{}),function(a){return G({minPadding:0,maxPadding:0,ordinal:!0,title:{text:null},showLastLabel:!0},a,{type:"datetime",categories:null})});
a.yAxis=nb(tb(a.yAxis||{}),function(a){d=a.opposite;return G({labels:{align:d?"right":"left",x:d?-2:2,y:-2},showLastLabel:!1,title:{text:null}},a)});a.series=null;a=G({chart:{panning:!0},navigator:{enabled:!0},scrollbar:{enabled:!0},rangeSelector:{enabled:!0},title:{text:null},tooltip:{shared:!0,crosshairs:!0},legend:{enabled:!1},plotOptions:{line:e,spline:e,area:e,areaspline:e,column:{shadow:!1,borderWidth:0,dataGrouping:{enabled:!0}}}},a,{chart:{inverted:!1}});a.series=c;return new ac(a,b)};var Wc=
M.init,Xc=M.processData,Yc=ib.prototype.tooltipFormatter;M.init=function(){Wc.apply(this,arguments);var a=this.options.compare;if(a)this.modifyValue=function(b,c){var d=this.compareValue,b=a==="value"?b-d:b=100*(b/d)-100;if(c)c.change=b;return b}};M.processData=function(){Xc.apply(this);if(this.options.compare)for(var a=0,b=this.processedXData,c=this.processedYData,d=c.length,e=this.xAxis.getExtremes().min;a<d;a++)if(typeof c[a]==="number"&&b[a]>=e){this.compareValue=c[a];break}};ib.prototype.tooltipFormatter=
function(a){a=a.replace("{point.change}",(this.change>0?"+":"")+Wb(this.change,this.series.tooltipOptions.changeDecimals||2));return Yc.apply(this,[a])};(function(){var a=M.init,b=M.getSegments;M.init=function(){var b,d;a.apply(this,arguments);b=this.chart;(d=this.xAxis)&&d.options.ordinal&&W(this,"updatedData",function(){delete d.ordinalIndex});if(d&&d.options.ordinal&&!d.hasOrdinalExtension){d.hasOrdinalExtension=!0;d.beforeSetTickPositions=function(){var a,b=[],c=!1,d,e;if(this.options.ordinal){n(this.series,
function(a,c){if(a.visible!==!1&&(b=b.concat(a.processedXData),c)){b.sort(function(a,b){return a-b});for(c=b.length-1;c--;)b[c]===b[c+1]&&b.splice(c,1)}});a=b.length;if(a>2){d=b[1]-b[0];for(e=a-1;e--&&!c;)b[e+1]-b[e]!==d&&(c=!0)}c?(this.ordinalSlope=(b[a-1]-b[0])/(a-1),this.ordinalOffset=b[0],this.ordinalPositions=b):this.ordinalPositions=this.ordinalSlope=this.ordinalOffset=B}};d.val2lin=function(a,b){var c=this.ordinalPositions;if(c){var d=c.length,e,j;for(e=d;e--;)if(c[e]===a){j=e;break}for(e=
d-1;e--;)if(a>c[e]){c=(a-c[e])/(c[e+1]-c[e]);j=e+c;break}return b?j:this.ordinalSlope*(j||0)+this.ordinalOffset}else return a};d.lin2val=function(a,b){var c=this.ordinalPositions;if(c){var d=this.ordinalSlope,e=this.ordinalOffset,j=c.length-1,l,m;if(b)a<0?a=c[0]:a>j?a=c[j]:(j=Ra(a),m=a-j);else for(;j--;)if(l=d*j+e,a>=l){d=d*(j+1)+e;m=(a-l)/(d-l);break}return m!==B&&c[j]!==B?c[j]+m*(c[j+1]-c[j]):a}else return a};d.getExtendedPositions=function(){var a=d.series[0].currentDataGrouping,e=d.ordinalIndex,
h=a?a.count+a.unitName:"raw",k=d.getExtremes(),i,j;if(!e)e=d.ordinalIndex={};if(!e[h])i={series:[],getExtremes:function(){return{min:k.dataMin,max:k.dataMax}},options:{ordinal:!0}},n(d.series,function(d){j={xAxis:i,xData:d.xData,chart:b};j.options={dataGrouping:a?{enabled:!0,forced:!0,approximation:"open",units:[[a.unitName,[a.count]]]}:{enabled:!1}};d.processData.apply(j);i.series.push(j)}),d.beforeSetTickPositions.apply(i),e[h]=i.ordinalPositions;return e[h]};d.postProcessTickInterval=function(a){var b=
this.ordinalSlope;return b?a/(b/d.closestPointRange):a};W(d,"afterSetTickPositions",function(a){var b=d.options.tickPixelInterval,a=a.tickPositions;if(d.ordinalPositions&&z(b))for(var c=a.length,e,i,j=(e=a.info)?e.higherRanks:[];c--;)e=d.translate(a[c]),i&&i-e<b*0.6?a.splice(j[a[c]]&&!j[a[c+1]]?c+1:c,1):i=e});var e=b.pan;b.pan=function(a){var d=b.xAxis[0],h=!1;if(d.options.ordinal){var k=b.mouseDownX,i=d.getExtremes(),j=i.dataMax,l=i.min,m=i.max,p;p=b.hoverPoints;var q=d.closestPointRange,k=(k-a)/
(d.translationSlope*(d.ordinalSlope||q)),s={ordinalPositions:d.getExtendedPositions()},t,q=d.lin2val,r=d.val2lin;if(s.ordinalPositions){if(Ha(k)>1)p&&n(p,function(a){a.setState()}),k<0?(p=s,s=d.ordinalPositions?d:s):p=d.ordinalPositions?d:s,t=s.ordinalPositions,j>t[t.length-1]&&t.push(j),p=q.apply(p,[r.apply(p,[l,!0])+k,!0]),k=q.apply(s,[r.apply(s,[m,!0])+k,!0]),p>wa(i.dataMin,l)&&k<R(j,m)&&d.setExtremes(p,k,!0,!1),b.mouseDownX=a,C(b.container,{cursor:"move"})}else h=!0}else h=!0;h&&e.apply(b,arguments)}}};
M.getSegments=function(){var a=this,d,e=a.options.gapSize;b.apply(a);if(a.xAxis.options.ordinal&&e)d=a.segments,n(d,function(b,g){for(var h=b.length-1;h--;)b[h+1].x-b[h].x>a.xAxis.closestPointRange*e&&d.splice(g+1,0,b.splice(h+1,b.length-h))})}})();I(Highcharts,{Chart:ac,dateFormat:wb,pathAnim:Sb,getOptions:function(){return la},hasRtlBug:Rc,numberFormat:Wb,Point:ib,Color:$a,Renderer:Pb,seriesTypes:aa,setOptions:function(a){cc=G(cc,a.xAxis);lc=G(lc,a.yAxis);a.xAxis=a.yAxis=B;la=G(la,a);xc();return la},
Series:X,addEvent:W,removeEvent:ra,createElement:Y,discardElement:Db,css:C,each:n,extend:I,map:nb,merge:G,pick:q,splat:tb,extendClass:ka,product:"Highstock",version:"1.1.2"})})();
import scala.tools.nsc.io.File
object IDEPathHelper {
val packageName = "org.cloudfoundry.runtime.gatling"
val url = getClass.getClassLoader.getResource("gatling.conf").getPath
val projectDir = File(url).parents(2)
val dataFolder = projectDir / "src/main/resources/data"
val resultsFolder = projectDir / "target/gatling-results"
val requestBodiesFolder = projectDir / "src/main/resources/request-bodies"
val eclipseAssetsFolder = projectDir / "src/main/resources/assets"
val eclipseSimulationFolder = projectDir / "src/main/scala" / packageName.replace(".", "/")
val outputFolder = projectDir / "src/main/scala" / packageName.replace(".", "/")
println(eclipseSimulationFolder)
}
/*! jQuery v1.7.1 jquery.com | jquery.org/license */
(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
package org.cloudfoundry.runtime.gatling
import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.core.feeder.Feeder
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.script.GatlingSimulation
class LoadLoginSimulation extends GatlingSimulation {
val urlBase = sys.env.getOrElse("GATLING_UAA_BASE", "http://localhost:8080/uaa")
val httpConf = httpConfig.baseURL(urlBase).proxy("localhost", 8080)
val plainHeaders = Map(
"Accept" -> """application/json""",
"Content-Type" -> """application/x-www-form-urlencoded""")
var counter = 0
val feeder = new Feeder(null) {
def next = {
println("Counter " + counter)
counter += 1
Map("username" -> ("joel" + counter))
}
}
val scn = scenario("Scenario name")
.loop(
chain
.exec(
http("login")
.post("/oauth/authorize")
.param("client_id", "vmc")
.param("scope", "read")
.param("credentials", """{"username":"${username}","password":"koala"}""")
.param("redirect_uri", "uri:oauth:token")
.param("response_type", "token")
.headers(plainHeaders)
.check(status.eq(302)))).times(200)
runSimulation(
scn.configure users 10 protocolConfig httpConf)
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread{10}][%-5level] %logger{15} - %msg%n%rEx</pattern>
</encoder>
</appender>
<root>
<level value="WARN" />
<appender-ref ref="CONSOLE" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.cloudfoundry.runtime</groupId>
<artifactId>cloudfoundry-identity-gatling</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<scala.version>2.9.1</scala.version>
<encoding>UTF-8</encoding>
<gatling.version>1.0.3</gatling.version>
<gatling-highcharts.version>1.0.3</gatling-highcharts.version>
<maven-scala-plugin.version>2.15.2</maven-scala-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.excilys.ebi.gatling</groupId>
<artifactId>gatling-app</artifactId>
<version>${gatling.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.excilys.ebi.gatling</groupId>
<artifactId>gatling-recorder</artifactId>
<version>${gatling.version}</version>
</dependency>
<dependency>
<groupId>com.excilys.ebi.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>${gatling-highcharts.version}</version>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.excilys.ebi.gatling</groupId>
<artifactId>gatling-app</artifactId>
</dependency>
<dependency>
<groupId>com.excilys.ebi.gatling</groupId>
<artifactId>gatling-recorder</artifactId>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
</dependency>
<dependency>
<groupId>com.excilys.ebi.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repository.excilys.com</id>
<url>http://repository.excilys.com/content/repositories/releases</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>${maven-scala-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-make:transitive</arg>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
import com.excilys.ebi.gatling.core.util.PathHelper.path2string
import com.excilys.ebi.gatling.recorder.ui.GatlingHttpProxyUI
import IDEPathHelper.{ requestBodiesFolder, outputFolder }
object Recorder extends App {
GatlingHttpProxyUI.main(Array("-scala", "-of", outputFolder, "-run", "-eclipse", "com.excilys.ebi.gatling.toto", "-rbf", requestBodiesFolder))
}
body{font-family:arial;margin:0;padding:0;background:#DCDBD4;color:#000;font-size:12px;}
.container{width:1200px;margin:0 auto;position:relative;overflow:visible;}
.frise{background:url('fond-degrade.gif') repeat-x;height:530px;width: 100%;position:absolute;top:60px;z-index:-1;}
.details{height:1250px;}
.global{height:650px}
a{text-decoration:none;color:#E37400;}
a:hover{color:#D1D1CE;}
p{margin:10px 0;padding:0;}
img{border:0;}
h1{font-size:18px;color:#E37400;margin: 10px 0;}
h1 span{color:#D1D1CE;}
.main{width:100%}
.head{height:100px;width:600px;padding:10px 0 0 160px;background:url('fond-lueur.gif') no-repeat;background-position:70px 0;position:absolute;top:0;left:0;}
.foot{background:#92918C;width:100%;color:#FFF;}
.foot a{width:85px;height:27px;margin:3px auto;display:block}
.cadre{width:1050px;padding:38px 0 0 100px;
position:absolute;
top:70px;
}
.onglet{
background:#FFF;
padding:7px 80px;
margin:5px 5px 0 5px;
color:#BCBCB5;
font-size:18px;
font-weight:bold;
position:absolute;
right:0;
top:-9px;
z-index:8;
height:28px;
box-shadow:0 0 0 black,4px -4px 3px #D1D1CE,-4px -4px 3px #D1D1CE;
-moz-box-shadow:0 0 0 black,4px -4px 3px #D1D1CE,-4px -4px 3px #D1D1CE;
-webkit-box-shadow:0 0 0 black,4px -4px 3px #D1D1CE,-4px -4px 3px #D1D1CE;
-webkit-border-top-right-radius: 8px;
-webkit-border-top-left-radius: 8px;
border-color: #D1D1CE}
.onglet img{position:absolute;top:7px;left:7px;}
.onglet span{color:#E37400;margin-right:10px;}
.onglet p{margin:7px 0 10px 0; font-size:22px}
.content{
margin-right: 5px;
padding:0 10px 40px 1px;
background:#FFF;
box-shadow:-4px -4px 3px #D1D1CE, 4px 4px 3px #D1D1CE, -4px 4px 3px #D1D1CE, 4px -4px 3px #D1D1CE;
-moz-box-shadow:-4px -4px 3px #D1D1CE, 4px 4px 3px #D1D1CE, -4px 4px 3px #D1D1CE, 4px -4px 3px #D1D1CE;
-webkit-box-shadow:-4px -4px 3px #D1D1CE, 4px 4px 3px #D1D1CE, -4px 4px 3px #D1D1CE, 4px -4px 3px #D1D1CE;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
-moz-border-top-left-radius: 8px;
-moz-border-bottom-left-radius: 8px;
-moz-border-bottom-right-radius: 8px;
-webkit-border-top-left-radius: 8px;
-webkit-border-bottom-left-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
border-color: #D1D1CE
}
.content-in{margin:30px 30px 30px 120px;}
.sous-menu{border-bottom:2px #E37400 solid;padding:10px 0 5px 0;margin:0 5px;z-index:-1;}
.sous-menu a{color:#FFF;font-size:14px;font-weight:bold;}
.sous-menu .item{background:#D1D1CE url('sous-menu-fleche.png') 12px 6px no-repeat;display:inline;margin:0 10px 0 0;padding:5px 15px 5px 25px;
border-top-right-radius:8px;
border-top-left-radius:8px}
.sous-menu .ouvert{background:#E37400 url('sou-menu-fleche-ouvert.png') 10px 7px no-repeat;}
.nav{top:180px;width:200px;background:#C5C3BC;position:absolute;z-index: 9;border-radius:8px;border:1px solid #C5C3BC}
.nav ul{padding:0;margin:3px}
.nav li{margin:0 auto;padding:3px 0;list-style:none;width:190px;border-bottom:1px #acaba6 solid;border-top:1px #d4d2cc solid;}
.nav li:first-child{border-top:none;}
.nav li:last-child{border-bottom:none;}
.nav a{color:#000;padding:5px 10px;display:block;width:170px;font-size:14px;font-weight:bold;margin: 0 auto}
.nav a:hover{background-color:#92918C;border-radius:8px}
.nav .on a{color:#FFF;background-color:#E37400;border-radius:8px}
.article{position:relative;}
.infos{position:absolute;top:0;right:-63px;width:250px;color:#FFF;}
.infos-in{padding:0 0 25px 0;position:relative;}
.info{margin:0;height:100%;background:#CF6900 url('stat-fond.png') repeat-x;}
.repli{position:absolute;bottom:0;right:0;background:url('stat-fleche-bas.png') no-repeat top left;height:25px;width:22px;}
.decor{background:url('stat-forme.jpg') no-repeat 170px 0;width:100%;padding:15px 0px 15px 0;}
.infos p{margin:3px 0px 3px 40px}
.infos p strong{font-weight:bold;margin-left:10px;}
.titre{background:#ff9916;text-align:center;width:120px;height:15px;font-weight:bold;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
-moz-border-top-left-radius: 8px;
-moz-border-top-right-radius: 8px;
-webkit-border-top-left-radius: 8px;
-webkit-border-top-right-radius: 8px;
padding-top:5px;
}
.infos h2{font-size:13px;font-weight:bold;margin:0;padding:3px 0 0 25px;color:#FFF;height:19px;width:165px;
border-top-right-radius:8px;
border-bottom-right-radius:8px;
-moz-border-top-right-radius:8px;
-moz-border-bottom-right-radius:8px;
-webkit-border-top-right-radius:8px;
-webkit-border-bottom-right-radius:8px;
}
.infos .first {
background: url('stat-l-roue.png') no-repeat 3px 3px #d16b00;
}
.infos .second {
background: url('stat-l-temps.png') no-repeat 3px 3px #d16b00;
}
.schema{background:#EAEAEA;border-radius:8px;border:1px solid #EAEAEA;margin-bottom:20px}
.demi{width:670px;height:205px;}
.geant{width:900px;height:350px;}
.chart_title{
background:#b0b0a8;
padding:2px 10px;
color:#FFF;
font-weight:bold;
border-radius:8px;
}
/*
* Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
*
* Licensed under the Gatling Highcharts License
*/
Highcharts.theme = {
chart: {
backgroundColor: '#e6e5e0',
borderWidth: 0,
borderRadius: 8,
plotBackgroundColor: null,
plotShadow: false,
plotBorderWidth: 0
},
xAxis: {
gridLineWidth: 0,
lineColor: '#666',
tickColor: '#666',
labels: {
style: {
color: '#666',
}
},
title: {
style: {
color: '#666'
}
}
},
yAxis: {
alternateGridColor: null,
minorTickInterval: null,
gridLineColor: '#999',
lineWidth: 0,
tickWidth: 0,
labels: {
style: {
color: '#666',
fontWeight: 'bold'
}
},
title: {
style: {
color: '#666',
font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
}
}
},
labels: {
style: {
color: '#CCC'
}
},
rangeSelector: {
buttonTheme: {
fill: '#cfc9c6',
stroke: '#000000',
style: {
color: '#34332e',
fontWeight: 'bold',
borderColor: '#b2b2a9'
},
states: {
hover: {
fill: '#92918C',
stroke: '#000000',
style: {
color: '#34332e',
fontWeight: 'bold',
borderColor: '#8b897d'
},
},
select: {
fill: '#E37400',
stroke: '#000000',
style: {
color: '#FFF'
}
}
}
},
inputStyle: {
backgroundColor: '#333',
color: 'silver'
},
labelStyle: {
color: '#8b897d'
}
},
navigator: {
handles: {
backgroundColor: '#e6e5e0',
borderColor: '#92918C'
},
outlineColor: '#92918C',
outlineWidth: 1,
maskFill: 'rgba(146, 145, 140, 0.5)',
series: {
color: '#4572A7',
lineColor: '#4572A7'
}
},
scrollbar: {
buttonBackgroundColor: '#e6e5e0',
buttonBorderWidth: 1,
buttonBorderColor: '#92918C',
buttonArrowColor: '#92918C',
buttonBorderRadius: 2,
barBorderWidth: 1,
barBorderRadius: 0,
barBackgroundColor: '#92918C',
barBorderColor: '#92918C',
rifleColor: '#92918C',
trackBackgroundColor: '#b0b0a8',
trackBorderWidth: 1,
trackBorderColor: '#b0b0a8'
}
};
Highcharts.setOptions(Highcharts.theme);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment