├── .gitignore ├── pics ├── dashboard.png ├── ganglia.png └── jconsole.png ├── demo ├── lib │ ├── jmxetric-0.0.6.jar │ └── jolokia-jvm-jdk6-0.91-agent.jar ├── etc │ ├── logging.properties │ └── jmxetric.xml ├── ztrace-demo.launch ├── ganglia-demo.launch ├── realtime-monitor-management-demo.launch ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── zhongl │ └── jsmx │ └── demo │ └── Demo.java ├── agent ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── MANIFEST.MF │ │ └── java │ │ └── com │ │ └── github │ │ └── zhongl │ │ └── jsmx │ │ └── agent │ │ ├── Advice.java │ │ ├── InstrumentationAgent.java │ │ ├── Trace.java │ │ ├── PointcutFilter.java │ │ ├── ProbeMethodAdapter.java │ │ ├── Context.java │ │ ├── Probe.java │ │ ├── Performance.java │ │ └── ProbeInstrumentor.java └── pom.xml ├── pom.xml ├── core ├── src │ └── main │ │ ├── resources │ │ ├── statistics.html │ │ └── dashboard.html │ │ └── javascript │ │ ├── highcharts │ │ ├── themes │ │ │ ├── grid.js │ │ │ ├── gray.js │ │ │ ├── dark-blue.js │ │ │ └── dark-green.js │ │ ├── modules │ │ │ ├── exporting.js │ │ │ └── exporting.src.js │ │ └── highcharts.js │ │ ├── json2.js │ │ ├── jsmx.plot.js │ │ ├── stat.plot.js │ │ ├── jolokia-simple.js │ │ └── jolokia.js └── pom.xml └── README.textile /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | target/ 4 | .settings/ 5 | .DS_Store 6 | bin/ 7 | -------------------------------------------------------------------------------- /pics/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongl/atMonitor/HEAD/pics/dashboard.png -------------------------------------------------------------------------------- /pics/ganglia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongl/atMonitor/HEAD/pics/ganglia.png -------------------------------------------------------------------------------- /pics/jconsole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongl/atMonitor/HEAD/pics/jconsole.png -------------------------------------------------------------------------------- /demo/lib/jmxetric-0.0.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongl/atMonitor/HEAD/demo/lib/jmxetric-0.0.6.jar -------------------------------------------------------------------------------- /demo/lib/jolokia-jvm-jdk6-0.91-agent.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongl/atMonitor/HEAD/demo/lib/jolokia-jvm-jdk6-0.91-agent.jar -------------------------------------------------------------------------------- /agent/src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: com.github.zhongl.jsmx.agent.InstrumentationAgent 3 | Agent-Class: com.github.zhongl.jsmx.agent.InstrumentationAgent 4 | Can-Redefine-Classes: true 5 | Can-Retransform-Classes: true 6 | -------------------------------------------------------------------------------- /demo/etc/logging.properties: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Default Logging Configuration File 3 | # 4 | # You can use a different file by specifying a filename 5 | # with the java.util.logging.config.file system property. 6 | # For example java -Djava.util.logging.config.file=myfile 7 | ############################################################ 8 | 9 | handlers= java.util.logging.ConsoleHandler 10 | 11 | .level=INFO 12 | 13 | java.util.logging.ConsoleHandler.level = ALL 14 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 15 | 16 | jmxetric.level=ALL 17 | ganglia.level=ALL 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.zhongl 5 | jsmx 6 | 0.0.1-SNAPSHOT 7 | pom 8 | jsmx 9 | 10 | core 11 | demo 12 | agent 13 | 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 21 | 22 | 1.6 23 | 1.6 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/ztrace-demo.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/ganglia-demo.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/Advice.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import java.util.*; 4 | import java.util.concurrent.*; 5 | 6 | /** 7 | * {@link Advice} 8 | * 9 | * @author zhongl 10 | * @created 2011-8-16 11 | * 12 | */ 13 | public abstract class Advice { 14 | public abstract void enterWith(Context context); 15 | 16 | public abstract void exitWith(Context context); 17 | 18 | protected abstract static class ThreadSafeMap { 19 | public void clear() { 20 | map.clear(); 21 | } 22 | 23 | public V get(K key) { 24 | V value = map.get(key); 25 | if (value != null) return value; 26 | value = initialValue(); 27 | V absent = map.putIfAbsent(key, value); 28 | return absent == null ? value : absent; 29 | } 30 | 31 | public Map snapshot() { 32 | return new HashMap(map); 33 | } 34 | 35 | protected abstract V initialValue(); 36 | 37 | private final ConcurrentHashMap map = new ConcurrentHashMap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/realtime-monitor-management-demo.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /core/src/main/resources/statistics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Invocation Statistics 5 | 6 | 7 | 8 |

Invocation Statistics

9 | 10 | 11 | 12 |
13 |
15 |
17 |
18 | 20 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/InstrumentationAgent.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import java.lang.instrument.*; 4 | 5 | import javax.management.*; 6 | 7 | import org.softee.management.exception.*; 8 | import org.softee.management.helper.MBeanRegistration; 9 | 10 | /** 11 | * {@link InstrumentationAgent} 12 | * 13 | * @author jushi 14 | * @created 2011-8-5 15 | * 16 | */ 17 | public class InstrumentationAgent { 18 | 19 | public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Exception { 20 | main(agentArgs, instrumentation); 21 | } 22 | 23 | public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { 24 | main(agentArgs, instrumentation); 25 | } 26 | 27 | public static void main(String[] args) { 28 | // TODO attach VM with pid 29 | } 30 | 31 | private static void main(String agentArgs, Instrumentation instrumentation) throws ManagementException, 32 | MalformedObjectNameException { 33 | System.out.println("loading..."); 34 | new MBeanRegistration(new ProbeInstrumentor(instrumentation)).register(); 35 | System.out.println("loaded instrumentation mbean."); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/Trace.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import java.text.*; 4 | 5 | /** 6 | * {@link Trace} 7 | * 8 | * @author zhongl 9 | * 10 | */ 11 | public class Trace extends Advice { 12 | 13 | @Override 14 | public void enterWith(Context context) { 15 | println("{0}.{1} is calling with {2}.", context.getClassName(), context.getMethodName(), context.getArguments()); 16 | context.setTraceOn(); 17 | } 18 | 19 | @Override 20 | public void exitWith(Context context) { 21 | Object result = context.getResult(); 22 | boolean breakByThrowing = result instanceof Throwable; 23 | String pattern = "{0}.{1} is called " + (breakByThrowing ? "but throw" : "and return") + " {2}."; 24 | println(pattern, context.getClassName(), context.getMethodName(), result); 25 | StackTraceElement[] stackTrace = (breakByThrowing) ? ((Throwable) result).getStackTrace() : context.getStackTrace(); 26 | println(stackTrace); 27 | } 28 | 29 | private static void println(StackTraceElement[] stackTrace) { 30 | for (StackTraceElement stackTraceElement : stackTrace) { 31 | System.out.println("\t" + stackTraceElement); 32 | } 33 | } 34 | 35 | private static void println(String pattern, Object... arguments) { 36 | System.out.println(MessageFormat.format(pattern, arguments)); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/resources/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JSMX Dashboard 5 | 6 | 7 |

JSMX Dashboard

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
18 |
20 |
21 | 22 | 24 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jsmx 6 | com.github.zhongl 7 | 0.0.1-SNAPSHOT 8 | .. 9 | 10 | jsmx-demo 11 | 12 | 13 | 14 | org.codehaus.mojo 15 | exec-maven-plugin 16 | 1.2 17 | 18 | java 19 | 20 | 21 | -javaagent:../agent/target/jsmx-agent-0.0.1-SNAPSHOT-nodep.jar 22 | 23 | 24 | -javaagent:lib/jolokia-jvm-jdk6-0.91-agent.jar=port=7777,host=localhost 25 | 26 | 27 | -cp 28 | 29 | 30 | 31 | com.github.zhongl.jsmx.demo.Demo 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.softee 42 | pojo-mbean 43 | 1.1 44 | 45 | 46 | org.acplt 47 | oncrpc 48 | 1.0.7 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /demo/etc/jmxetric.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ]> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. What's JSMX 2 | 3 | JSMX is a simple demo of monitor & management solution with JMX, it includes: 4 | # declaring mbean, which used "pojo-mbean":http://code.google.com/p/pojo-mbean/; 5 | # real-time web console, which used "jolokia":http://www.jolokia.org and "highcharts":http://www.highcharts.com; 6 | # integrating with "ganglia":http://ganglia.info , which used "jmxetric":http://code.google.com/p/jmxetric/; 7 | # runtime dynamic trace invocation, which used "asm":http://asm.ow2.org. 8 | 9 | 10 | h1. How to Run Demo 11 | 12 | # git clone git@github.com:zhongl/jsmx.git; 13 | # import it to eclipse; 14 | # make sure ganglia-3.1.x has been set up("how to set up":http://sourceforge.net/apps/trac/ganglia/wiki/ganglia_configuration ); 15 | # make sure "hostname" and "port" in jsmx/demo/etc/jmxetric.xml pointed to gmond; 16 | # run jsmx/demo/realtime-monitor-management-demo.launch; 17 | ** use your web browser open jsmx/core/src/main/resourcse/dashboard.html, then you can see the real-time web console; 18 | # run jsmx/demo/ganglia-demo.launch 19 | ** use your web browser open ganglia web(eg: "http://localhost/ganglia":http://localhost/ganglia ), then you can see mbean state trends in ganglia. 20 | # run jsmx/demo/ztrace.launch 21 | ** run jconsole, and connect to Demo process; 22 | ** go to MBean->jsmx; 23 | ** click Demo->Attributes, you can see nothing show at console but loaded message; 24 | ** invoke ProbeInstrumentor->Operations->addProbeClass: com.github.zhongl.jsmx.demo.Demo$ManagableServer; 25 | ** invoke ProbeInstrumentor->Operations->probe; 26 | ** click Demo->Attributes again, you can see trace log at console now; 27 | ** invoke ProbeInstrumentor->Operations->reset; 28 | ** click Demo->Attributes again, you can see no more trace log at console. 29 | 30 | 31 | Enjoy it! -------------------------------------------------------------------------------- /agent/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jsmx 6 | com.github.zhongl 7 | 0.0.1-SNAPSHOT 8 | .. 9 | 10 | jsmx-agent 11 | jsmx agent 12 | 13 | 14 | 15 | 16 | org.apache.maven.plugins 17 | maven-jar-plugin 18 | 19 | 20 | src/main/resources/META-INF/MANIFEST.MF 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 1.4 28 | 29 | 30 | package 31 | 32 | shade 33 | 34 | 35 | true 36 | nodep 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.softee 47 | pojo-mbean 48 | 1.1 49 | 50 | 51 | asm 52 | asm 53 | 3.3.1 54 | 55 | 56 | asm 57 | asm-commons 58 | 3.3.1 59 | 60 | 61 | -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/themes/grid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grid theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'], 8 | chart: { 9 | backgroundColor: { 10 | linearGradient: [0, 0, 500, 500], 11 | stops: [ 12 | [0, 'rgb(255, 255, 255)'], 13 | [1, 'rgb(240, 240, 255)'] 14 | ] 15 | } 16 | , 17 | borderWidth: 2, 18 | plotBackgroundColor: 'rgba(255, 255, 255, .9)', 19 | plotShadow: true, 20 | plotBorderWidth: 1 21 | }, 22 | title: { 23 | style: { 24 | color: '#000', 25 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 26 | } 27 | }, 28 | subtitle: { 29 | style: { 30 | color: '#666666', 31 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 32 | } 33 | }, 34 | xAxis: { 35 | gridLineWidth: 1, 36 | lineColor: '#000', 37 | tickColor: '#000', 38 | labels: { 39 | style: { 40 | color: '#000', 41 | font: '11px Trebuchet MS, Verdana, sans-serif' 42 | } 43 | }, 44 | title: { 45 | style: { 46 | color: '#333', 47 | fontWeight: 'bold', 48 | fontSize: '12px', 49 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 50 | 51 | } 52 | } 53 | }, 54 | yAxis: { 55 | minorTickInterval: 'auto', 56 | lineColor: '#000', 57 | lineWidth: 1, 58 | tickWidth: 1, 59 | tickColor: '#000', 60 | labels: { 61 | style: { 62 | color: '#000', 63 | font: '11px Trebuchet MS, Verdana, sans-serif' 64 | } 65 | }, 66 | title: { 67 | style: { 68 | color: '#333', 69 | fontWeight: 'bold', 70 | fontSize: '12px', 71 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 72 | } 73 | } 74 | }, 75 | legend: { 76 | itemStyle: { 77 | font: '9pt Trebuchet MS, Verdana, sans-serif', 78 | color: 'black' 79 | 80 | }, 81 | itemHoverStyle: { 82 | color: '#039' 83 | }, 84 | itemHiddenStyle: { 85 | color: 'gray' 86 | } 87 | }, 88 | labels: { 89 | style: { 90 | color: '#99b' 91 | } 92 | } 93 | }; 94 | 95 | // Apply the theme 96 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 97 | 98 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | jsmx 5 | com.github.zhongl 6 | 0.0.1-SNAPSHOT 7 | .. 8 | 9 | jsmx-core 10 | javascript 11 | 12 | 13 | 14 | target/scripts 15 | target/test-scripts 16 | 17 | 18 | org.codehaus.mojo.javascript 19 | javascript-maven-plugin 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.eclipse.m2e 29 | lifecycle-mapping 30 | 1.0.0 31 | 32 | 33 | 34 | 35 | 36 | org.codehaus.mojo.javascript 37 | javascript-maven-plugin 38 | [1.0-alpha-1-SNAPSHOT,) 39 | 40 | compile 41 | prepare-tests 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/PointcutFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import java.lang.reflect.*; 4 | import java.util.*; 5 | import java.util.regex.*; 6 | 7 | /** 8 | * {@link PointcutFilter} 9 | * 10 | * @author zhongl 11 | * @created 2011-8-16 12 | * 13 | */ 14 | public class PointcutFilter { 15 | 16 | private static boolean isEmpty(String value) { 17 | return value == null || value.length() == 0; 18 | } 19 | 20 | private static final Pattern ALL = Pattern.compile(".*"); 21 | 22 | public PointcutFilter(String classPattern, String methodPattern) { 23 | this.classPattern = isEmpty(classPattern) ? ALL : Pattern.compile(classPattern); 24 | this.methodPattern = isEmpty(methodPattern) ? ALL : Pattern.compile(methodPattern); 25 | } 26 | 27 | /** 28 | * @param classes 29 | * @return map key is class name, and value is method names. 30 | */ 31 | public Map> filter(Class[] classes) { 32 | Map> map = new HashMap>(); 33 | 34 | for (Class clazz : classes) { 35 | String className = clazz.getName(); 36 | if (!matchsClass(className)) continue; 37 | if (clazz.getClassLoader() == null) 38 | throw new IllegalArgumentException("Class loaded by Non-Application class loader is not permit."); 39 | Set methodNames = matchMethodsOf(clazz); 40 | if (methodNames.isEmpty()) continue; 41 | map.put(className, methodNames); 42 | } 43 | 44 | return map; 45 | } 46 | 47 | public boolean matchsClass(String name) { 48 | return classPattern.matcher(name).matches(); 49 | } 50 | 51 | public boolean matchsMethod(String className, String methodName) { 52 | return matchsClass(className) && matchsMethod(methodName); 53 | } 54 | 55 | private Set matchMethodsOf(final Class clazz) { 56 | final Set methodNames = new HashSet(); 57 | for (Method method : clazz.getDeclaredMethods()) { 58 | String name = method.getName(); 59 | if (matchsMethod(name)) methodNames.add(name); 60 | } 61 | return methodNames; 62 | } 63 | 64 | private boolean matchsMethod(String name) { 65 | return methodPattern.matcher(name).matches(); 66 | } 67 | 68 | private final Pattern classPattern; 69 | 70 | private final Pattern methodPattern; 71 | 72 | } 73 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/ProbeMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import static org.objectweb.asm.Type.*; 4 | 5 | import org.objectweb.asm.*; 6 | import org.objectweb.asm.commons.*; 7 | 8 | /** 9 | * {@link ProbeMethodAdapter} 10 | * 11 | * @author zhongl 12 | * 13 | */ 14 | class ProbeMethodAdapter extends AdviceAdapter { 15 | 16 | protected ProbeMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className) { 17 | super(mv, access, name, desc); 18 | start = new Label(); 19 | end = new Label(); 20 | methodName = name; 21 | this.className = className; 22 | } 23 | 24 | @Override 25 | public void visitMaxs(int maxStack, int maxLocals) { 26 | mark(end); 27 | catchException(start, end, Type.getType(Throwable.class)); 28 | dup(); 29 | push(className); 30 | push(methodName); 31 | push(methodDesc); 32 | loadThisOrPushNullIfIsStatic(); 33 | invokeStatic(Probe.TYPE, Probe.EXIT); 34 | throwException(); 35 | super.visitMaxs(maxStack, maxLocals); 36 | } 37 | 38 | @Override 39 | protected void onMethodEnter() { 40 | push(className); 41 | push(methodName); 42 | push(methodDesc); 43 | loadThisOrPushNullIfIsStatic(); 44 | loadArgArray(); 45 | invokeStatic(Probe.TYPE, Probe.ENTRY); 46 | mark(start); 47 | } 48 | 49 | @Override 50 | protected void onMethodExit(int opcode) { 51 | if (opcode == ATHROW) return; // do nothing, @see visitMax 52 | prepareResultBy(opcode); 53 | push(className); 54 | push(methodName); 55 | push(methodDesc); 56 | loadThisOrPushNullIfIsStatic(); 57 | invokeStatic(Probe.TYPE, Probe.EXIT); 58 | } 59 | 60 | private boolean isStaticMethod() { 61 | return (methodAccess & ACC_STATIC) != 0; 62 | } 63 | 64 | private void loadThisOrPushNullIfIsStatic() { 65 | if (isStaticMethod()) pushNull(); 66 | else loadThis(); 67 | } 68 | 69 | private void prepareResultBy(int opcode) { 70 | if (opcode == RETURN) { // void 71 | pushNull(); 72 | return; 73 | } 74 | 75 | if (opcode == ARETURN) { // object 76 | dup(); 77 | return; 78 | } 79 | 80 | if (opcode == LRETURN || opcode == DRETURN) { // long or double 81 | dup2(); 82 | } else { // boolean or byte or char or short or int 83 | dup(); 84 | } 85 | 86 | box(getReturnType(methodDesc)); 87 | } 88 | 89 | private void pushNull() { 90 | push((Type) null); 91 | } 92 | 93 | private final String className; 94 | private final String methodName; 95 | private final Label start; 96 | private final Label end; 97 | 98 | } 99 | -------------------------------------------------------------------------------- /demo/src/main/java/com/github/zhongl/jsmx/demo/Demo.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.demo; 2 | 3 | import java.util.*; 4 | 5 | import org.softee.management.annotation.*; 6 | import org.softee.management.helper.MBeanRegistration; 7 | 8 | /** 9 | * {@link Demo} 10 | * 11 | * @author jushi 12 | * @created Jul 20, 2011 13 | * 14 | */ 15 | public class Demo { 16 | 17 | /** 18 | * @param args 19 | * @throws Exception 20 | */ 21 | public static void main(String[] args) throws Exception { 22 | ManagableServer mBean = new ManagableServer(); 23 | new MBeanRegistration(mBean).register(); 24 | mBean.start(); 25 | System.in.read(); 26 | } 27 | 28 | @MBean(objectName = "jsmx:type=Demo") 29 | static class ManagableServer extends Thread { 30 | public ManagableServer() { 31 | super("ManagableBean"); 32 | setDaemon(true); 33 | } 34 | 35 | @ManagedAttribute 36 | public long getCount() { 37 | return count; 38 | } 39 | 40 | @ManagedOperation 41 | public void showException(String message) { 42 | throwRuntimeException(message); 43 | } 44 | 45 | void traceInvokedByMutilContexts() { 46 | /* do nothings */ 47 | } 48 | 49 | void caller0() { 50 | traceInvokedByMutilContexts(); 51 | } 52 | 53 | void caller1() { 54 | traceInvokedByMutilContexts(); 55 | } 56 | 57 | void caller2() { 58 | traceInvokedByMutilContexts(); 59 | } 60 | 61 | private void throwRuntimeException(String message) { 62 | throw new RuntimeException(message); 63 | } 64 | 65 | @ManagedAttribute 66 | public int getGauge() { 67 | return gauge; 68 | } 69 | 70 | @ManagedOperation 71 | public void shutdown() { 72 | running = false; 73 | try { 74 | join(); 75 | } catch (InterruptedException e) { 76 | interrupt(); 77 | } 78 | } 79 | 80 | @Override 81 | public void run() { 82 | while (running) { 83 | try { 84 | sleep(1000L); 85 | } catch (InterruptedException e) { 86 | interrupt(); 87 | e.printStackTrace(); 88 | } 89 | count++; 90 | gauge = Math.round((float) Math.random() * 1000); 91 | 92 | switch (random.nextInt() % 3) { 93 | case 0: 94 | caller0(); 95 | break; 96 | case 1: 97 | caller1(); 98 | break; 99 | case 2: 100 | caller2(); 101 | break; 102 | default: 103 | break; 104 | } 105 | } 106 | } 107 | 108 | private volatile long count = 0L; 109 | 110 | private volatile int gauge = 0; 111 | 112 | private volatile boolean running = true; 113 | 114 | private final Random random = new Random(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/Context.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * {@link Context} 7 | * 8 | * @author zhongl 9 | * @created 2011-8-16 10 | * 11 | */ 12 | public final class Context { 13 | 14 | public Context(String className, String methodName, boolean voidReturn, Object thisObject, Object[] arguments) { 15 | this.className = className; 16 | this.methodName = methodName; 17 | this.voidReturn = voidReturn; 18 | this.thisObject = thisObject.toString(); 19 | this.arguments = Arrays.toString(arguments); 20 | } 21 | 22 | public long elapse() { 23 | return stoped - started; 24 | } 25 | 26 | public String getArguments() { 27 | return arguments; 28 | } 29 | 30 | public String getClassName() { 31 | return className; 32 | } 33 | 34 | public String getMethodName() { 35 | return methodName; 36 | } 37 | 38 | public Object getResult() { 39 | return result; 40 | } 41 | 42 | public StackTraceElement[] getStackTrace() { 43 | return stackTrace; 44 | } 45 | 46 | public long getStarted() { 47 | return started; 48 | } 49 | 50 | public long getStoped() { 51 | return stoped; 52 | } 53 | 54 | public Object getThisObject() { 55 | return thisObject; 56 | } 57 | 58 | public boolean isVoidReturn() { 59 | return voidReturn; 60 | } 61 | 62 | public void setTimeOn() { 63 | timeOn = true; 64 | } 65 | 66 | public void setTraceOn() { 67 | traceOn = true; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return String.format("Context [className=%s, methodName=%s, voidReturn=%s, thisObject=%s, arguments=%s, result=%s, started=%s, stoped=%s, timeOn=%s, traceOn=%s, stackTrace=%s]", 73 | className, 74 | methodName, 75 | voidReturn, 76 | thisObject, 77 | arguments, 78 | result, 79 | started, 80 | stoped, 81 | timeOn, 82 | traceOn, 83 | Arrays.toString(stackTrace)); 84 | } 85 | 86 | void exitWith(Object result) { 87 | this.result = result; 88 | } 89 | 90 | boolean isTimerOn() { 91 | return timeOn; 92 | } 93 | 94 | boolean isTraceOn() { 95 | return traceOn; 96 | } 97 | 98 | void setStackTrace(StackTraceElement[] stackTrace) { 99 | this.stackTrace = stackTrace; 100 | } 101 | 102 | void startAt(long nanoTime) { 103 | started = nanoTime; 104 | } 105 | 106 | void stopAt(long nanoTime) { 107 | stoped = nanoTime; 108 | } 109 | 110 | private final String className; 111 | private final String methodName; 112 | private final boolean voidReturn; 113 | private final String thisObject; 114 | private final String arguments; 115 | 116 | private Object result; 117 | 118 | private long started = -1L; 119 | private long stoped = -1L; 120 | private boolean timeOn = false; 121 | 122 | private boolean traceOn = false; 123 | private StackTraceElement[] stackTrace = null; 124 | 125 | } 126 | -------------------------------------------------------------------------------- /core/src/main/javascript/json2.js: -------------------------------------------------------------------------------- 1 | if(!this.JSON){this.JSON={};} 2 | (function(){"use strict";function f(n){return n<10?'0'+n:n;} 3 | if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ 4 | f(this.getUTCMonth()+1)+'-'+ 5 | f(this.getUTCDate())+'T'+ 6 | f(this.getUTCHours())+':'+ 7 | f(this.getUTCMinutes())+':'+ 8 | f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} 9 | var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} 10 | function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} 11 | if(typeof rep==='function'){value=rep.call(holder,key,value);} 12 | switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} 13 | gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;izhongl 15 | * @created 2011-8-14 16 | * 17 | */ 18 | public class Probe { 19 | 20 | public static String adviceName() { 21 | return advice().getClass().getSimpleName(); 22 | } 23 | 24 | public static void onMethodBegin(String className, 25 | String methodName, 26 | String descriptor, 27 | Object thisObject, 28 | Object[] arguments) { 29 | boolean voidReturn = Type.getReturnType(descriptor).equals(Type.VOID_TYPE); 30 | Context context = new Context(className, methodName, voidReturn, thisObject, arguments); 31 | try { 32 | advice().enterWith(context); 33 | } catch (Throwable t) { 34 | handleEnterError(context, t); 35 | } 36 | if (context.isTimerOn()) context.startAt(System.nanoTime()); 37 | if (context.isTraceOn()) context.setStackTrace(currentStrackTrace()); 38 | CONTEXT_STACK.get().push(context); 39 | } 40 | 41 | public static void onMethodEnd(Object result, 42 | String className, 43 | String methodName, 44 | String descriptor, 45 | Object thisObject) { 46 | Context context = CONTEXT_STACK.get().pop(); 47 | if (context.isTimerOn()) context.stopAt(System.nanoTime()); 48 | context.exitWith(result); 49 | try { 50 | advice().exitWith(context); 51 | } catch (Throwable t) { 52 | handleExitError(context, t); 53 | } 54 | } 55 | 56 | public static void setAdvice(Advice instance) throws Exception { 57 | registerIfIsMBean(instance); 58 | unregisterIfIsMBean(advice.getAndSet(instance)); 59 | } 60 | 61 | private static Advice advice() { 62 | return advice.get(); 63 | } 64 | 65 | private static StackTraceElement[] currentStrackTrace() { 66 | StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 67 | return Arrays.copyOfRange(stackTrace, 4, stackTrace.length); // trim useless stack trace elements. 68 | } 69 | 70 | private static void handleEnterError(Context context, Throwable t) { 71 | t.printStackTrace(); 72 | } 73 | 74 | private static void handleExitError(Context context, Throwable t) { 75 | t.printStackTrace(); 76 | } 77 | 78 | private static Method method(String name, Class... argumentTypes) { 79 | try { 80 | return new Method(name, Type.getMethodDescriptor(Probe.class.getMethod(name, argumentTypes))); 81 | } catch (Exception e) { 82 | throw new Error(e); 83 | } 84 | } 85 | 86 | private static void registerIfIsMBean(Advice instance) throws Exception { 87 | if (instance.getClass().getAnnotation(MBean.class) != null) new MBeanRegistration(instance).register(); 88 | } 89 | 90 | private static void unregisterIfIsMBean(Advice instance) throws Exception { 91 | if (instance.getClass().getAnnotation(MBean.class) != null) new MBeanRegistration(instance).unregister(); 92 | } 93 | 94 | private static final ThreadLocal> CONTEXT_STACK = new ThreadLocal>() { 95 | @Override 96 | protected java.util.Stack initialValue() { 97 | return new Stack(); 98 | } 99 | }; 100 | 101 | public static final AtomicReference advice = new AtomicReference(new Trace()); 102 | 103 | public static final Method ENTRY = method("onMethodBegin", 104 | String.class, 105 | String.class, 106 | String.class, 107 | Object.class, 108 | Object[].class); 109 | 110 | public static final Method EXIT = method("onMethodEnd", 111 | Object.class, 112 | String.class, 113 | String.class, 114 | String.class, 115 | Object.class); 116 | 117 | public static final Type TYPE = Type.getType(Probe.class); 118 | 119 | private Probe() {} 120 | } 121 | -------------------------------------------------------------------------------- /core/src/main/javascript/jsmx.plot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {String} 3 | * mbean 4 | * @param {String} 5 | * attribute 6 | * @param {String} 7 | * path, Optional 8 | * @returns {MBeanAttributeCoordinate} 9 | */ 10 | function MBeanAttributeCoordinate(mbean, attribute, path) { 11 | this.mbean = function() { 12 | return mbean; 13 | }; 14 | this.attribute = function() { 15 | return attribute; 16 | }; 17 | this.path = function() { 18 | return path ? path : ""; 19 | }; 20 | } 21 | 22 | /** 23 | * @param {String} 24 | * yAxisLabel 25 | * @param {Function} 26 | * xAxisValueConvert 27 | * @param {Number} 28 | * xAxisValueKeep 29 | * @returns {PoltOptions} 30 | */ 31 | function PoltOptions(yAxisLabel, xAxisValueConvert, xAxisValueKeep) { 32 | var defaultConvert = function(value) { 33 | return value; 34 | }; 35 | 36 | this.yAxisLabel = function() { 37 | return yAxisLabel ? yAxisLabel : null; 38 | }; 39 | this.xAxisValueConvert = xAxisValueConvert ? xAxisValueConvert 40 | : defaultConvert; 41 | this.xAxisValueKeep = function() { 42 | return xAxisValueKeep ? xAxisValueKeep : 5; 43 | }; 44 | } 45 | 46 | /** 47 | * @param {String} 48 | * placeholder, id of document element. 49 | * @param {MBeanAttributeCoordinate} 50 | * mBeanAttributeCoordinate 51 | * @param {PoltOptions} 52 | * plotOptions 53 | * @returns {MBeanAttributeRealTimePolt} 54 | */ 55 | function MBeanAttributeRealTimePolt(placeholder, mBeanAttributeCoordinate, 56 | plotOptions) { 57 | if (!plotOptions) plotOptions = new PoltOptions(); 58 | var options = { 59 | chart : { 60 | renderTo : placeholder 61 | }, 62 | title : { 63 | text : mBeanAttributeCoordinate.attribute() 64 | }, 65 | xAxis : { 66 | type : "datetime" 67 | }, 68 | yAxis : { 69 | title : { 70 | text : plotOptions.yAxisLabel() 71 | } 72 | }, 73 | series : [ { 74 | name : mBeanAttributeCoordinate.path(), 75 | data : [] 76 | } ] 77 | 78 | }; 79 | 80 | var chart = new Highcharts.Chart(options); 81 | 82 | this.collectAndUpdateBy = function(jolokia) { 83 | jolokia.request({ 84 | type : "read", 85 | mbean : mBeanAttributeCoordinate.mbean(), 86 | attribute : mBeanAttributeCoordinate.attribute(), 87 | path : mBeanAttributeCoordinate.path() 88 | }, { 89 | success : function(response) { 90 | var serie = chart.series[0]; 91 | var shift = serie.data.length > plotOptions.xAxisValueKeep(); 92 | var value = plotOptions.xAxisValueConvert(response.value); 93 | serie.addPoint({ 94 | x : response.timestamp * 1000, 95 | y : value 96 | }, true, shift); 97 | }, 98 | timeout : 2000, // 2 seconds 99 | error : function(response) { 100 | alert("Error: " + response.error); 101 | }, 102 | ajaxError : function(error) { 103 | alert("Ajax error: " + error); 104 | } 105 | }); 106 | }; 107 | } 108 | 109 | function attachGcEvent() { 110 | $("#gc").click(function() { 111 | j4p.execute("java.lang:type=Memory", "gc", { 112 | success : function() { 113 | alert("Garbage collection performed"); 114 | } 115 | }); 116 | }); 117 | } 118 | 119 | function attachUpdateIntervalEvent() { 120 | $("#updateInterval").val(updateInterval).change(function() { 121 | var v = $(this).val(); 122 | updateInterval = (v < 1) ? 1 : v; 123 | $(this).val(updateInterval); 124 | }); 125 | } 126 | 127 | function attachAddNewWatchEvent() { 128 | $("#newWatch") 129 | .click( 130 | function() { 131 | var mbean = $("#mbean").val(); 132 | var attribute = $("#attribute").val(); 133 | 134 | $("#dashboard") 135 | .append( 136 | '
'); 139 | var coordinate = new MBeanAttributeCoordinate(mbean, attribute); 140 | plots.push(new MBeanAttributeRealTimePolt(attribute, coordinate)); 141 | }); 142 | } 143 | 144 | var j4p = new Jolokia({ 145 | url : "http://localhost:7777/jolokia", 146 | jsonp : true 147 | }); 148 | 149 | var updateInterval = 3; 150 | 151 | var plots = []; 152 | $(document).ready( 153 | function() { 154 | attachGcEvent(); 155 | attachUpdateIntervalEvent(); 156 | attachAddNewWatchEvent(); 157 | 158 | var heapMemoryUsage = new MBeanAttributeCoordinate( 159 | "java.lang:type=Memory", "HeapMemoryUsage", "used"); 160 | var heapMemoryUsagePlotOptions = new PoltOptions("Unit: MB", function( 161 | value) { 162 | return value / (1024 * 1024); 163 | }); 164 | 165 | plots.push(new MBeanAttributeRealTimePolt("memory", heapMemoryUsage, 166 | heapMemoryUsagePlotOptions)); 167 | plots.push(new MBeanAttributeRealTimePolt("load", 168 | new MBeanAttributeCoordinate("java.lang:type=OperatingSystem", 169 | "SystemLoadAverage"))); 170 | 171 | function collectAndUpdate() { 172 | $.each(plots, function(index, plot) { 173 | plot.collectAndUpdateBy(j4p); 174 | }); 175 | setTimeout(collectAndUpdate, updateInterval * 1000); 176 | } 177 | 178 | collectAndUpdate(); 179 | }); 180 | -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/themes/gray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gray theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: [0, 0, 0, 400], 12 | stops: [ 13 | [0, 'rgb(96, 96, 96)'], 14 | [1, 'rgb(16, 16, 16)'] 15 | ] 16 | }, 17 | borderWidth: 0, 18 | borderRadius: 15, 19 | plotBackgroundColor: null, 20 | plotShadow: false, 21 | plotBorderWidth: 0 22 | }, 23 | title: { 24 | style: { 25 | color: '#FFF', 26 | font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 27 | } 28 | }, 29 | subtitle: { 30 | style: { 31 | color: '#DDD', 32 | font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 33 | } 34 | }, 35 | xAxis: { 36 | gridLineWidth: 0, 37 | lineColor: '#999', 38 | tickColor: '#999', 39 | labels: { 40 | style: { 41 | color: '#999', 42 | fontWeight: 'bold' 43 | } 44 | }, 45 | title: { 46 | style: { 47 | color: '#AAA', 48 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 49 | } 50 | } 51 | }, 52 | yAxis: { 53 | alternateGridColor: null, 54 | minorTickInterval: null, 55 | gridLineColor: 'rgba(255, 255, 255, .1)', 56 | lineWidth: 0, 57 | tickWidth: 0, 58 | labels: { 59 | style: { 60 | color: '#999', 61 | fontWeight: 'bold' 62 | } 63 | }, 64 | title: { 65 | style: { 66 | color: '#AAA', 67 | font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif' 68 | } 69 | } 70 | }, 71 | legend: { 72 | itemStyle: { 73 | color: '#CCC' 74 | }, 75 | itemHoverStyle: { 76 | color: '#FFF' 77 | }, 78 | itemHiddenStyle: { 79 | color: '#333' 80 | } 81 | }, 82 | labels: { 83 | style: { 84 | color: '#CCC' 85 | } 86 | }, 87 | tooltip: { 88 | backgroundColor: { 89 | linearGradient: [0, 0, 0, 50], 90 | stops: [ 91 | [0, 'rgba(96, 96, 96, .8)'], 92 | [1, 'rgba(16, 16, 16, .8)'] 93 | ] 94 | }, 95 | borderWidth: 0, 96 | style: { 97 | color: '#FFF' 98 | } 99 | }, 100 | 101 | 102 | plotOptions: { 103 | line: { 104 | dataLabels: { 105 | color: '#CCC' 106 | }, 107 | marker: { 108 | lineColor: '#333' 109 | } 110 | }, 111 | spline: { 112 | marker: { 113 | lineColor: '#333' 114 | } 115 | }, 116 | scatter: { 117 | marker: { 118 | lineColor: '#333' 119 | } 120 | }, 121 | candlestick: { 122 | lineColor: 'white' 123 | } 124 | }, 125 | 126 | toolbar: { 127 | itemStyle: { 128 | color: '#CCC' 129 | } 130 | }, 131 | 132 | navigation: { 133 | buttonOptions: { 134 | backgroundColor: { 135 | linearGradient: [0, 0, 0, 20], 136 | stops: [ 137 | [0.4, '#606060'], 138 | [0.6, '#333333'] 139 | ] 140 | }, 141 | borderColor: '#000000', 142 | symbolStroke: '#C0C0C0', 143 | hoverSymbolStroke: '#FFFFFF' 144 | } 145 | }, 146 | 147 | exporting: { 148 | buttons: { 149 | exportButton: { 150 | symbolFill: '#55BE3B' 151 | }, 152 | printButton: { 153 | symbolFill: '#7797BE' 154 | } 155 | } 156 | }, 157 | 158 | // scroll charts 159 | rangeSelector: { 160 | buttonTheme: { 161 | fill: { 162 | linearGradient: [0, 0, 0, 20], 163 | stops: [ 164 | [0.4, '#888'], 165 | [0.6, '#555'] 166 | ] 167 | }, 168 | stroke: '#000000', 169 | style: { 170 | color: '#CCC', 171 | fontWeight: 'bold' 172 | }, 173 | states: { 174 | hover: { 175 | fill: { 176 | linearGradient: [0, 0, 0, 20], 177 | stops: [ 178 | [0.4, '#BBB'], 179 | [0.6, '#888'] 180 | ] 181 | }, 182 | stroke: '#000000', 183 | style: { 184 | color: 'white' 185 | } 186 | }, 187 | select: { 188 | fill: { 189 | linearGradient: [0, 0, 0, 20], 190 | stops: [ 191 | [0.1, '#000'], 192 | [0.3, '#333'] 193 | ] 194 | }, 195 | stroke: '#000000', 196 | style: { 197 | color: 'yellow' 198 | } 199 | } 200 | } 201 | }, 202 | inputStyle: { 203 | backgroundColor: '#333', 204 | color: 'silver' 205 | }, 206 | labelStyle: { 207 | color: 'silver' 208 | } 209 | }, 210 | 211 | navigator: { 212 | handles: { 213 | backgroundColor: '#666', 214 | borderColor: '#AAA' 215 | }, 216 | outlineColor: '#CCC', 217 | maskFill: 'rgba(16, 16, 16, 0.5)', 218 | series: { 219 | color: '#7798BF', 220 | lineColor: '#A6C7ED' 221 | } 222 | }, 223 | 224 | scrollbar: { 225 | barBackgroundColor: { 226 | linearGradient: [0, 0, 0, 20], 227 | stops: [ 228 | [0.4, '#888'], 229 | [0.6, '#555'] 230 | ] 231 | }, 232 | barBorderColor: '#CCC', 233 | buttonArrowColor: '#CCC', 234 | buttonBackgroundColor: { 235 | linearGradient: [0, 0, 0, 20], 236 | stops: [ 237 | [0.4, '#888'], 238 | [0.6, '#555'] 239 | ] 240 | }, 241 | buttonBorderColor: '#CCC', 242 | rifleColor: '#FFF', 243 | trackBackgroundColor: { 244 | linearGradient: [0, 0, 0, 10], 245 | stops: [ 246 | [0, '#000'], 247 | [1, '#333'] 248 | ] 249 | }, 250 | trackBorderColor: '#666' 251 | }, 252 | 253 | // special colors for some of the demo examples 254 | legendBackgroundColor: 'rgba(48, 48, 48, 0.8)', 255 | legendBackgroundColorSolid: 'rgb(70, 70, 70)', 256 | dataLabelsColor: '#444', 257 | textColor: '#E0E0E0', 258 | maskColor: 'rgba(255,255,255,0.3)' 259 | }; 260 | 261 | // Apply the theme 262 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); 263 | -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/themes/dark-blue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: [0, 0, 250, 500], 12 | stops: [ 13 | [0, 'rgb(48, 48, 96)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | legend: { 77 | itemStyle: { 78 | font: '9pt Trebuchet MS, Verdana, sans-serif', 79 | color: '#A0A0A0' 80 | } 81 | }, 82 | tooltip: { 83 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 84 | style: { 85 | color: '#F0F0F0' 86 | } 87 | }, 88 | toolbar: { 89 | itemStyle: { 90 | color: 'silver' 91 | } 92 | }, 93 | plotOptions: { 94 | line: { 95 | dataLabels: { 96 | color: '#CCC' 97 | }, 98 | marker: { 99 | lineColor: '#333' 100 | } 101 | }, 102 | spline: { 103 | marker: { 104 | lineColor: '#333' 105 | } 106 | }, 107 | scatter: { 108 | marker: { 109 | lineColor: '#333' 110 | } 111 | }, 112 | candlestick: { 113 | lineColor: 'white' 114 | } 115 | }, 116 | legend: { 117 | itemStyle: { 118 | color: '#CCC' 119 | }, 120 | itemHoverStyle: { 121 | color: '#FFF' 122 | }, 123 | itemHiddenStyle: { 124 | color: '#444' 125 | } 126 | }, 127 | credits: { 128 | style: { 129 | color: '#666' 130 | } 131 | }, 132 | labels: { 133 | style: { 134 | color: '#CCC' 135 | } 136 | }, 137 | 138 | navigation: { 139 | buttonOptions: { 140 | backgroundColor: { 141 | linearGradient: [0, 0, 0, 20], 142 | stops: [ 143 | [0.4, '#606060'], 144 | [0.6, '#333333'] 145 | ] 146 | }, 147 | borderColor: '#000000', 148 | symbolStroke: '#C0C0C0', 149 | hoverSymbolStroke: '#FFFFFF' 150 | } 151 | }, 152 | 153 | exporting: { 154 | buttons: { 155 | exportButton: { 156 | symbolFill: '#55BE3B' 157 | }, 158 | printButton: { 159 | symbolFill: '#7797BE' 160 | } 161 | } 162 | }, 163 | 164 | // scroll charts 165 | rangeSelector: { 166 | buttonTheme: { 167 | fill: { 168 | linearGradient: [0, 0, 0, 20], 169 | stops: [ 170 | [0.4, '#888'], 171 | [0.6, '#555'] 172 | ] 173 | }, 174 | stroke: '#000000', 175 | style: { 176 | color: '#CCC', 177 | fontWeight: 'bold' 178 | }, 179 | states: { 180 | hover: { 181 | fill: { 182 | linearGradient: [0, 0, 0, 20], 183 | stops: [ 184 | [0.4, '#BBB'], 185 | [0.6, '#888'] 186 | ] 187 | }, 188 | stroke: '#000000', 189 | style: { 190 | color: 'white' 191 | } 192 | }, 193 | select: { 194 | fill: { 195 | linearGradient: [0, 0, 0, 20], 196 | stops: [ 197 | [0.1, '#000'], 198 | [0.3, '#333'] 199 | ] 200 | }, 201 | stroke: '#000000', 202 | style: { 203 | color: 'yellow' 204 | } 205 | } 206 | } 207 | }, 208 | inputStyle: { 209 | backgroundColor: '#333', 210 | color: 'silver' 211 | }, 212 | labelStyle: { 213 | color: 'silver' 214 | } 215 | }, 216 | 217 | navigator: { 218 | handles: { 219 | backgroundColor: '#666', 220 | borderColor: '#AAA' 221 | }, 222 | outlineColor: '#CCC', 223 | maskFill: 'rgba(16, 16, 16, 0.5)', 224 | series: { 225 | color: '#7798BF', 226 | lineColor: '#A6C7ED' 227 | } 228 | }, 229 | 230 | scrollbar: { 231 | barBackgroundColor: { 232 | linearGradient: [0, 0, 0, 20], 233 | stops: [ 234 | [0.4, '#888'], 235 | [0.6, '#555'] 236 | ] 237 | }, 238 | barBorderColor: '#CCC', 239 | buttonArrowColor: '#CCC', 240 | buttonBackgroundColor: { 241 | linearGradient: [0, 0, 0, 20], 242 | stops: [ 243 | [0.4, '#888'], 244 | [0.6, '#555'] 245 | ] 246 | }, 247 | buttonBorderColor: '#CCC', 248 | rifleColor: '#FFF', 249 | trackBackgroundColor: { 250 | linearGradient: [0, 0, 0, 10], 251 | stops: [ 252 | [0, '#000'], 253 | [1, '#333'] 254 | ] 255 | }, 256 | trackBorderColor: '#666' 257 | }, 258 | 259 | // special colors for some of the 260 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 261 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 262 | dataLabelsColor: '#444', 263 | textColor: '#C0C0C0', 264 | maskColor: 'rgba(255,255,255,0.3)' 265 | }; 266 | 267 | // Apply the theme 268 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/themes/dark-green.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dark blue theme for Highcharts JS 3 | * @author Torstein Hønsi 4 | */ 5 | 6 | Highcharts.theme = { 7 | colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 8 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 9 | chart: { 10 | backgroundColor: { 11 | linearGradient: [0, 0, 250, 500], 12 | stops: [ 13 | [0, 'rgb(48, 96, 48)'], 14 | [1, 'rgb(0, 0, 0)'] 15 | ] 16 | }, 17 | borderColor: '#000000', 18 | borderWidth: 2, 19 | className: 'dark-container', 20 | plotBackgroundColor: 'rgba(255, 255, 255, .1)', 21 | plotBorderColor: '#CCCCCC', 22 | plotBorderWidth: 1 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 1, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | 53 | } 54 | } 55 | }, 56 | yAxis: { 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | legend: { 77 | itemStyle: { 78 | font: '9pt Trebuchet MS, Verdana, sans-serif', 79 | color: '#A0A0A0' 80 | } 81 | }, 82 | tooltip: { 83 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 84 | style: { 85 | color: '#F0F0F0' 86 | } 87 | }, 88 | toolbar: { 89 | itemStyle: { 90 | color: 'silver' 91 | } 92 | }, 93 | plotOptions: { 94 | line: { 95 | dataLabels: { 96 | color: '#CCC' 97 | }, 98 | marker: { 99 | lineColor: '#333' 100 | } 101 | }, 102 | spline: { 103 | marker: { 104 | lineColor: '#333' 105 | } 106 | }, 107 | scatter: { 108 | marker: { 109 | lineColor: '#333' 110 | } 111 | }, 112 | candlestick: { 113 | lineColor: 'white' 114 | } 115 | }, 116 | legend: { 117 | itemStyle: { 118 | color: '#CCC' 119 | }, 120 | itemHoverStyle: { 121 | color: '#FFF' 122 | }, 123 | itemHiddenStyle: { 124 | color: '#444' 125 | } 126 | }, 127 | credits: { 128 | style: { 129 | color: '#666' 130 | } 131 | }, 132 | labels: { 133 | style: { 134 | color: '#CCC' 135 | } 136 | }, 137 | 138 | navigation: { 139 | buttonOptions: { 140 | backgroundColor: { 141 | linearGradient: [0, 0, 0, 20], 142 | stops: [ 143 | [0.4, '#606060'], 144 | [0.6, '#333333'] 145 | ] 146 | }, 147 | borderColor: '#000000', 148 | symbolStroke: '#C0C0C0', 149 | hoverSymbolStroke: '#FFFFFF' 150 | } 151 | }, 152 | 153 | exporting: { 154 | buttons: { 155 | exportButton: { 156 | symbolFill: '#55BE3B' 157 | }, 158 | printButton: { 159 | symbolFill: '#7797BE' 160 | } 161 | } 162 | }, 163 | 164 | // scroll charts 165 | rangeSelector: { 166 | buttonTheme: { 167 | fill: { 168 | linearGradient: [0, 0, 0, 20], 169 | stops: [ 170 | [0.4, '#888'], 171 | [0.6, '#555'] 172 | ] 173 | }, 174 | stroke: '#000000', 175 | style: { 176 | color: '#CCC', 177 | fontWeight: 'bold' 178 | }, 179 | states: { 180 | hover: { 181 | fill: { 182 | linearGradient: [0, 0, 0, 20], 183 | stops: [ 184 | [0.4, '#BBB'], 185 | [0.6, '#888'] 186 | ] 187 | }, 188 | stroke: '#000000', 189 | style: { 190 | color: 'white' 191 | } 192 | }, 193 | select: { 194 | fill: { 195 | linearGradient: [0, 0, 0, 20], 196 | stops: [ 197 | [0.1, '#000'], 198 | [0.3, '#333'] 199 | ] 200 | }, 201 | stroke: '#000000', 202 | style: { 203 | color: 'yellow' 204 | } 205 | } 206 | } 207 | }, 208 | inputStyle: { 209 | backgroundColor: '#333', 210 | color: 'silver' 211 | }, 212 | labelStyle: { 213 | color: 'silver' 214 | } 215 | }, 216 | 217 | navigator: { 218 | handles: { 219 | backgroundColor: '#666', 220 | borderColor: '#AAA' 221 | }, 222 | outlineColor: '#CCC', 223 | maskFill: 'rgba(16, 16, 16, 0.5)', 224 | series: { 225 | color: '#7798BF', 226 | lineColor: '#A6C7ED' 227 | } 228 | }, 229 | 230 | scrollbar: { 231 | barBackgroundColor: { 232 | linearGradient: [0, 0, 0, 20], 233 | stops: [ 234 | [0.4, '#888'], 235 | [0.6, '#555'] 236 | ] 237 | }, 238 | barBorderColor: '#CCC', 239 | buttonArrowColor: '#CCC', 240 | buttonBackgroundColor: { 241 | linearGradient: [0, 0, 0, 20], 242 | stops: [ 243 | [0.4, '#888'], 244 | [0.6, '#555'] 245 | ] 246 | }, 247 | buttonBorderColor: '#CCC', 248 | rifleColor: '#FFF', 249 | trackBackgroundColor: { 250 | linearGradient: [0, 0, 0, 10], 251 | stops: [ 252 | [0, '#000'], 253 | [1, '#333'] 254 | ] 255 | }, 256 | trackBorderColor: '#666' 257 | }, 258 | 259 | // special colors for some of the 260 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 261 | legendBackgroundColorSolid: 'rgb(35, 35, 70)', 262 | dataLabelsColor: '#444', 263 | textColor: '#C0C0C0', 264 | maskColor: 'rgba(255,255,255,0.3)' 265 | }; 266 | 267 | // Apply the theme 268 | var highchartsOptions = Highcharts.setOptions(Highcharts.theme); -------------------------------------------------------------------------------- /core/src/main/javascript/stat.plot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {String} 3 | * mbean 4 | * @param {String} 5 | * attribute 6 | * @param {String} 7 | * path, Optional 8 | * @returns {MBeanAttributeCoordinate} 9 | */ 10 | function MBeanAttributeCoordinate(mbean, attribute, path) { 11 | this.mbean = function() { 12 | return mbean; 13 | }; 14 | this.attribute = function() { 15 | return attribute; 16 | }; 17 | this.path = function() { 18 | return path ? path : ""; 19 | }; 20 | } 21 | 22 | /** 23 | * @param {String} 24 | * yAxisLabel 25 | * @param {Function} 26 | * xAxisValueConvert 27 | * @param {Number} 28 | * xAxisValueKeep 29 | * @returns {PoltOptions} 30 | */ 31 | function PoltOptions(yAxisLabel, xAxisValueConvert, xAxisValueKeep) { 32 | var defaultConvert = function(value) { 33 | return value; 34 | }; 35 | 36 | this.yAxisLabel = function() { 37 | return yAxisLabel ? yAxisLabel : null; 38 | }; 39 | this.xAxisValueConvert = xAxisValueConvert ? xAxisValueConvert 40 | : defaultConvert; 41 | this.xAxisValueKeep = function() { 42 | return xAxisValueKeep ? xAxisValueKeep : 5; 43 | }; 44 | } 45 | 46 | /** 47 | * @param {String} 48 | * placeholder, id of document element. 49 | * @param {MBeanAttributeCoordinate} 50 | * mBeanAttributeCoordinate 51 | * @param {PoltOptions} 52 | * plotOptions 53 | * @returns {MBeanAttributeRealTimePolt} 54 | */ 55 | function MBeanAttributeRealTimePolt(placeholder, mBeanAttributeCoordinate, 56 | plotOptions) { 57 | if (!plotOptions) 58 | plotOptions = new PoltOptions(); 59 | var options = { 60 | chart : { 61 | renderTo : placeholder 62 | }, 63 | title : { 64 | text : mBeanAttributeCoordinate.attribute() 65 | }, 66 | xAxis : { 67 | type : "datetime" 68 | }, 69 | yAxis : { 70 | title : { 71 | text : plotOptions.yAxisLabel() 72 | } 73 | }, 74 | series : [ { 75 | name : mBeanAttributeCoordinate.path(), 76 | data : [] 77 | } ] 78 | 79 | }; 80 | 81 | var chart = new Highcharts.Chart(options); 82 | 83 | this.collectAndUpdateBy = function(jolokia) { 84 | jolokia.request({ 85 | type : "read", 86 | mbean : mBeanAttributeCoordinate.mbean(), 87 | attribute : mBeanAttributeCoordinate.attribute(), 88 | path : mBeanAttributeCoordinate.path() 89 | }, { 90 | success : function(response) { 91 | var serie = chart.series[0]; 92 | var shift = serie.data.length > plotOptions.xAxisValueKeep(); 93 | var value = plotOptions.xAxisValueConvert(response.value); 94 | serie.addPoint({ 95 | x : response.timestamp * 1000, 96 | y : value 97 | }, true, shift); 98 | }, 99 | timeout : 2000, // 2 seconds 100 | error : function(response) { 101 | alert("Error: " + response.error); 102 | }, 103 | ajaxError : function(error) { 104 | alert("Ajax error: " + error); 105 | } 106 | }); 107 | }; 108 | } 109 | 110 | function attachGcEvent() { 111 | $("#gc").click(function() { 112 | j4p.execute("java.lang:type=Memory", "gc", { 113 | success : function() { 114 | alert("Garbage collection performed"); 115 | } 116 | }); 117 | }); 118 | } 119 | 120 | function attachUpdateIntervalEvent() { 121 | $("#updateInterval").val(updateInterval).change(function() { 122 | var v = $(this).val(); 123 | updateInterval = (v < 1) ? 1 : v; 124 | $(this).val(updateInterval); 125 | }); 126 | } 127 | 128 | var j4p = new Jolokia({ 129 | url : "http://localhost:7777/jolokia", 130 | jsonp : true 131 | }); 132 | 133 | var updateInterval = 3; 134 | 135 | var plots = []; 136 | $(document).ready(function() { 137 | attachUpdateIntervalEvent(); 138 | 139 | var options = { 140 | chart : { 141 | renderTo : "avg" 142 | 143 | }, 144 | title : { 145 | text : "Invacation Average Elapse" 146 | }, 147 | xAxis : { 148 | type : "datetime" 149 | }, 150 | yAxis : {}, 151 | series : [ { 152 | name : "caller0", 153 | data : [] 154 | }, { 155 | name : "caller1", 156 | data : [] 157 | }, { 158 | name : "callser2", 159 | data : [] 160 | } ] 161 | 162 | }; 163 | 164 | var avg = new Highcharts.Chart(options); 165 | 166 | function collectAndUpdate() { 167 | j4p.request({ 168 | type : "read", 169 | mbean : "jsmx:type=Performance", 170 | attribute : "statistics" 171 | }, { 172 | success : function(response) { 173 | $("#cnt").html(response.value); 174 | var lines = response.value.split('\n'); 175 | var len = lines.length; 176 | var ad = []; 177 | for ( var i = 0; i < len; i++) { 178 | var line = lines[i]; 179 | var length = line.length; 180 | if (line[length - 1] == ":") { 181 | $("#title").html(line.substr(0, length - 2)); 182 | } 183 | var ma = line.match(/avg: ([\d]+),([\d]+)ns/); 184 | if (ma) { 185 | ad.push(parseInt(ma[1]) * 1000 + parseInt(ma[2])); 186 | } 187 | } 188 | console.log(ad); 189 | var shift = avg.series[0].data.length > 5; 190 | for ( var j = 0; j < ad.length; j++) 191 | avg.series[j].addPoint({ 192 | x : response.timestamp * 1000, 193 | y : ad[j] 194 | }, true, shift); 195 | }, 196 | timeout : 2000, // 2 seconds 197 | error : function(response) { 198 | alert("Error: " + response.error); 199 | }, 200 | ajaxError : function(error) { 201 | alert("Ajax error: " + error); 202 | } 203 | }); 204 | 205 | setTimeout(collectAndUpdate, updateInterval * 1000); 206 | } 207 | 208 | collectAndUpdate(); 209 | }); 210 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/Performance.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import static java.text.MessageFormat.*; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.atomic.*; 7 | 8 | import org.softee.management.annotation.*; 9 | 10 | /** 11 | * {@link Performance} 12 | * 13 | * @author zhongl 14 | * @created 2011-8-16 15 | * 16 | */ 17 | @MBean(objectName = "jsmx:type=Performance") 18 | public class Performance extends Advice { 19 | 20 | @Override 21 | public void enterWith(Context context) { 22 | context.setTimeOn(); 23 | context.setTraceOn(); 24 | } 25 | 26 | @Override 27 | public void exitWith(Context context) { 28 | Category key = new Category(context.getClassName(), context.getMethodName()); 29 | aggregation.get(key).increase(context.elapse(), context.getStackTrace()); 30 | } 31 | 32 | @ManagedAttribute 33 | public String getStatistics(){ 34 | return getStatisticsBy(5); 35 | } 36 | 37 | @ManagedOperation 38 | public String getStatisticsBy(int stackDepth) { 39 | Map snapshot = aggregation.snapshot(); 40 | StringBuilder sb = new StringBuilder(); 41 | for (Category category : snapshot.keySet()) { 42 | sb.append(category).append(":\n").append(snapshot.get(category).toString(stackDepth)).append('\n'); 43 | } 44 | return sb.toString(); 45 | } 46 | 47 | private final ThreadSafeMap aggregation = new ThreadSafeMap() { 48 | 49 | @Override 50 | protected Statistics initialValue() { 51 | return new Statistics(); 52 | } 53 | }; 54 | 55 | static class Category { 56 | 57 | public Category(String className, String methodName) { 58 | this.className = className; 59 | this.methodName = methodName; 60 | } 61 | 62 | @Override 63 | public boolean equals(Object obj) { 64 | if (this == obj) return true; 65 | if (obj == null) return false; 66 | if (getClass() != obj.getClass()) return false; 67 | Category other = (Category) obj; 68 | if (className == null) { 69 | if (other.className != null) return false; 70 | } else if (!className.equals(other.className)) return false; 71 | if (methodName == null) { 72 | if (other.methodName != null) return false; 73 | } else if (!methodName.equals(other.methodName)) return false; 74 | return true; 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | final int prime = 31; 80 | int result = 1; 81 | result = prime * result + ((className == null) ? 0 : className.hashCode()); 82 | result = prime * result + ((methodName == null) ? 0 : methodName.hashCode()); 83 | return result; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return className + '.' + methodName; 89 | } 90 | 91 | private final String className; 92 | 93 | private final String methodName; 94 | 95 | } 96 | 97 | static class Statistics { 98 | 99 | public void increase(long elapse, StackTraceElement[] stackTraceElements) { 100 | aggregation.get(key(stackTraceElements)).increase(elapse); 101 | } 102 | 103 | public String toString(int stackDepth) { 104 | Map snapshot = aggregation.snapshot(); 105 | StringBuilder sb = new StringBuilder(); 106 | 107 | for (Key key : snapshot.keySet()) { 108 | Statistic statistic = snapshot.get(key); 109 | sb.append(format("invoked [avg: {0}ns , cnt: {1}] by ", statistic.average(), statistic.count())); 110 | sb.append(key.getCaller()); 111 | if (stackDepth > 1) key.appendTo(sb, 1, stackDepth); 112 | sb.append('\n'); 113 | } 114 | return sb.toString(); 115 | } 116 | 117 | private Key key(StackTraceElement[] stackTraceElements) { 118 | return new Key(stackTraceElements); 119 | } 120 | 121 | private final ThreadSafeMap aggregation = new ThreadSafeMap() { 122 | 123 | @Override 124 | protected Statistic initialValue() { 125 | return new Statistic(); 126 | } 127 | }; 128 | 129 | static class Key { 130 | 131 | public Key(StackTraceElement[] stackTraceElements) { 132 | this.stackTraceElements = stackTraceElements; 133 | } 134 | 135 | public void appendTo(StringBuilder sb, int begin, int end) { 136 | for (int i = 1; i < stackTraceElements.length && i <= end; i++) { 137 | sb.append("\n\t").append(stackTraceElements[i]); 138 | } 139 | } 140 | 141 | @Override 142 | public boolean equals(Object obj) { 143 | if (this == obj) return true; 144 | if (obj == null) return false; 145 | if (getClass() != obj.getClass()) return false; 146 | Key other = (Key) obj; 147 | if (!Arrays.equals(stackTraceElements, other.stackTraceElements)) return false; 148 | return true; 149 | } 150 | 151 | public Object getCaller() { 152 | return stackTraceElements[0]; 153 | } 154 | 155 | @Override 156 | public int hashCode() { 157 | final int prime = 31; 158 | int result = 1; 159 | result = prime * result + Arrays.hashCode(stackTraceElements); 160 | return result; 161 | } 162 | 163 | private final StackTraceElement[] stackTraceElements; 164 | } 165 | 166 | static class Statistic { 167 | 168 | public void increase(long elapse) { 169 | count.incrementAndGet(); 170 | totalElapse.addAndGet(elapse); 171 | } 172 | 173 | long average() { 174 | return totalElapse.get() / count.get(); 175 | } 176 | 177 | long count() { 178 | return count.get(); 179 | } 180 | 181 | private final AtomicLong count = new AtomicLong(); 182 | 183 | private final AtomicLong totalElapse = new AtomicLong(); 184 | 185 | } 186 | 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/modules/exporting.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v2.1.6 (2011-07-08) 3 | Exporting module 4 | 5 | (c) 2010-2011 Torstein H?nsi 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(){var k=Highcharts,y=k.Chart,C=k.addEvent,t=k.createElement,z=k.discardElement,u=k.css,w=k.merge,p=k.each,r=k.extend,D=Math.max,s=document,E=window,A="ontouchstart"in s.documentElement,B=k.setOptions({lang:{downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",exportButtonTitle:"Export to raster or vector image",printButtonTitle:"Print the chart"}});B.navigation={menuStyle:{border:"1px solid #A0A0A0", 10 | background:"#FFFFFF"},menuItemStyle:{padding:"0 5px",background:"none",color:"#303030",fontSize:A?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{align:"right",backgroundColor:{linearGradient:[0,0,0,20],stops:[[0.4,"#F7F7F7"],[0.6,"#E3E3E3"]]},borderColor:"#B0B0B0",borderRadius:3,borderWidth:1,height:20,hoverBorderColor:"#909090",hoverSymbolFill:"#81A7CF",hoverSymbolStroke:"#4572A5",symbolFill:"#E0E0E0",symbolStroke:"#A0A0A0",symbolX:11.5,symbolY:10.5,verticalAlign:"top", 11 | width:24,y:10}};B.exporting={type:"image/png",url:"http://export.highcharts.com/",width:800,enableImages:false,buttons:{exportButton:{symbol:"exportIcon",x:-10,symbolFill:"#A8BF77",hoverSymbolFill:"#768F3E",_titleKey:"exportButtonTitle",menuItems:[{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG", 12 | onclick:function(){this.exportChart({type:"image/svg+xml"})}}]},printButton:{symbol:"printIcon",x:-36,symbolFill:"#B5C9DF",hoverSymbolFill:"#779ABF",_titleKey:"printButtonTitle",onclick:function(){this.print()}}}};r(y.prototype,{getSVG:function(b){var c=this,a,f,d,l,e,j,h=w(c.options,b);if(!s.createElementNS)s.createElementNS=function(i,g){var o=s.createElement(g);o.getBBox=function(){return k.Renderer.prototype.Element.prototype.getBBox.apply({element:o})};return o};b=t("div",null,{position:"absolute", 13 | top:"-9999em",width:c.chartWidth+"px",height:c.chartHeight+"px"},s.body);r(h.chart,{renderTo:b,forExport:true});h.exporting.enabled=false;if(!h.exporting.enableImages)h.chart.plotBackgroundImage=null;h.series=[];p(c.series,function(i){d=i.options;d.animation=false;d.showCheckbox=false;d.visible=i.visible;if(!h.exporting.enableImages)if(d&&d.marker&&/^url\(/.test(d.marker.symbol))d.marker.symbol="circle";d.data=[];p(i.data,function(g){l=g.config;e={x:g.x,y:g.y,name:g.name};typeof l=="object"&&g.config&& 14 | l.constructor!=Array&&r(e,l);e.visible=g.visible;d.data.push(e);if(!h.exporting.enableImages)(j=g.config&&g.config.marker)&&/^url\(/.test(j.symbol)&&delete j.symbol});h.series.push(d)});a=new Highcharts.Chart(h);p(["xAxis","yAxis"],function(i){p(c[i],function(g,o){var n=a[i][o],m=g.getExtremes(),q=m.userMin;m=m.userMax;if(q!==undefined||m!==undefined)n.setExtremes(q,m,true,false)})});f=a.container.innerHTML;h=null;a.destroy();z(b);f=f.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g, 15 | "").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/isTracker="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/]+)/g,'id="$1"').replace(/class=([^" ]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/]*)>/gi,"").replace(/<\/image>/g,"").replace(/]*)([^\/])>/gi,"").replace(/width=(\d+)/g,'width="$1"').replace(/height=(\d+)/g, 16 | 'height="$1"').replace(/hc-svg-href="/g,'xlink:href="').replace(/style="([^"]+)"/g,function(i){return i.toLowerCase()});f=f.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");if(f.match(/ xmlns="/g).length==2)f=f.replace(/xmlns="[^"]+"/,"");return f},exportChart:function(b,c){var a,f=this.getSVG(c);b=w(this.options.exporting,b);a=t("form",{method:"post",action:b.url},{display:"none"},s.body);p(["filename","type","width","svg"],function(d){t("input",{type:"hidden",name:d,value:{filename:b.filename|| 17 | "chart",type:b.type,width:b.width,svg:f}[d]},null,a)});a.submit();z(a)},print:function(){var b=this,c=b.container,a=[],f=c.parentNode,d=s.body,l=d.childNodes;if(!b.isPrinting){b.isPrinting=true;p(l,function(e,j){if(e.nodeType==1){a[j]=e.style.display;e.style.display="none"}});d.appendChild(c);E.print();setTimeout(function(){f.appendChild(c);p(l,function(e,j){if(e.nodeType==1)e.style.display=a[j]});b.isPrinting=false},1E3)}},contextMenu:function(b,c,a,f,d,l){var e=this,j=e.options.navigation,h=j.menuItemStyle, 18 | i=e.chartWidth,g=e.chartHeight,o="cache-"+b,n=e[o],m=D(d,l),q,x;if(!n){e[o]=n=t("div",{className:"highcharts-"+b},{position:"absolute",zIndex:1E3,padding:m+"px"},e.container);q=t("div",null,r({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},j.menuStyle),n);x=function(){u(n,{display:"none"})};C(n,"mouseleave",x);p(c,function(v){if(v)t("div",{onmouseover:function(){u(this,j.menuItemHoverStyle)},onmouseout:function(){u(this,h)},innerHTML:v.text||k.getOptions().lang[v.textKey]}, 19 | r({cursor:"pointer"},h),q)[A?"ontouchstart":"onclick"]=function(){x();v.onclick.apply(e,arguments)}});e.exportMenuWidth=n.offsetWidth;e.exportMenuHeight=n.offsetHeight}b={display:"block"};if(a+e.exportMenuWidth>i)b.right=i-a-d-m+"px";else b.left=a-m+"px";if(f+l+e.exportMenuHeight>g)b.bottom=g-f-m+"px";else b.top=f+l-m+"px";u(n,b)},addButton:function(b){function c(){g.attr(m);i.attr(n)}var a=this,f=a.renderer,d=w(a.options.navigation.buttonOptions,b),l=d.onclick,e=d.menuItems,j=d.width,h=d.height, 20 | i,g,o;b=d.borderWidth;var n={stroke:d.borderColor},m={stroke:d.symbolStroke,fill:d.symbolFill};if(d.enabled!==false){i=f.rect(0,0,j,h,d.borderRadius,b).align(d,true).attr(r({fill:d.backgroundColor,"stroke-width":b,zIndex:19},n)).add();o=f.rect(0,0,j,h,0).align(d).attr({fill:"rgba(255, 255, 255, 0.001)",title:k.getOptions().lang[d._titleKey],zIndex:21}).css({cursor:"pointer"}).on("mouseover",function(){g.attr({stroke:d.hoverSymbolStroke,fill:d.hoverSymbolFill});i.attr({stroke:d.hoverBorderColor})}).on("mouseout", 21 | c).on("click",c).add();if(e)l=function(){c();var q=o.getBBox();a.contextMenu("export-menu",e,q.x,q.y,j,h)};o.on("click",function(){l.apply(a,arguments)});g=f.symbol(d.symbol,d.symbolX,d.symbolY,(d.symbolSize||12)/2).align(d,true).attr(r(m,{"stroke-width":d.symbolStrokeWidth||1,zIndex:20})).add()}}});k.Renderer.prototype.symbols.exportIcon=function(b,c,a){return["M",b-a,c+a,"L",b+a,c+a,b+a,c+a*0.5,b-a,c+a*0.5,"Z","M",b,c+a*0.5,"L",b-a*0.5,c-a/3,b-a/6,c-a/3,b-a/6,c-a,b+a/6,c-a,b+a/6,c-a/3,b+a*0.5,c- 22 | a/3,"Z"]};k.Renderer.prototype.symbols.printIcon=function(b,c,a){return["M",b-a,c+a*0.5,"L",b+a,c+a*0.5,b+a,c-a/3,b-a,c-a/3,"Z","M",b-a*0.5,c-a/3,"L",b-a*0.5,c-a,b+a*0.5,c-a,b+a*0.5,c-a/3,"Z","M",b-a*0.5,c+a*0.5,"L",b-a*0.75,c+a,b+a*0.75,c+a,b+a*0.5,c+a*0.5,"Z"]};y.prototype.callbacks.push(function(b){var c,a=b.options.exporting,f=a.buttons;if(a.enabled!==false)for(c in f)b.addButton(f[c])})})(); 23 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/zhongl/jsmx/agent/ProbeInstrumentor.java: -------------------------------------------------------------------------------- 1 | package com.github.zhongl.jsmx.agent; 2 | 3 | import static java.text.MessageFormat.*; 4 | 5 | import java.io.*; 6 | import java.lang.instrument.*; 7 | import java.security.*; 8 | import java.util.*; 9 | import java.util.logging.*; 10 | 11 | import org.objectweb.asm.*; 12 | import org.softee.management.annotation.*; 13 | 14 | /** 15 | * {@link ProbeInstrumentor} 16 | * 17 | * @author zhongl 18 | * @created 2011-8-14 19 | * 20 | */ 21 | @MBean(objectName = "jsmx:type=ProbeInstrumentor") 22 | public class ProbeInstrumentor { 23 | 24 | private static String slashToDot(String className) { 25 | return className.replace('/', '.'); 26 | } 27 | 28 | private static byte[] toBytes(InputStream stream) throws IOException { 29 | if (stream == null) throw new FileNotFoundException(); 30 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 31 | int read = 0; 32 | while ((read = stream.read()) > -1) 33 | bytes.write(read); 34 | return bytes.toByteArray(); 35 | } 36 | 37 | private static String toString(Class... classes) { 38 | final StringBuilder sb = new StringBuilder("" + classes.length); 39 | for (final Class clazz : classes) { 40 | sb.append("\n").append(clazz); 41 | } 42 | return sb.toString(); 43 | } 44 | 45 | private static void warning(Throwable t) { 46 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 47 | t.printStackTrace(new PrintStream(out)); 48 | LOGGER.warning(new String(out.toByteArray())); 49 | } 50 | 51 | private final static Logger LOGGER = Logger.getLogger(ProbeInstrumentor.class.getName()); 52 | 53 | public ProbeInstrumentor(Instrumentation instrumentation) { 54 | this.instrumentation = instrumentation; 55 | } 56 | 57 | @ManagedOperation 58 | @Description("return a set of methods matched regex.") 59 | public String addProbeByPattern(@Parameter("classPattern") @Description("regex of class, default is '.*'.") String classPattern, 60 | @Parameter("methodPattern") @Description("regex of method, default is '.*'.") String methodPattern) throws Throwable { 61 | PointcutFilter pointcutFilter = new PointcutFilter(classPattern, methodPattern); 62 | try { 63 | Map> result = pointcutFilter.filter(instrumentation.getAllLoadedClasses()); 64 | if (!result.isEmpty()) { 65 | pointcutFilterSet.add(pointcutFilter); 66 | classNames.addAll(result.keySet()); 67 | } 68 | return formatClassAndMethodsOf(result); 69 | } catch (Throwable t) { 70 | warning(t); 71 | throw t; 72 | } 73 | } 74 | 75 | private String formatClassAndMethodsOf(Map> result) { 76 | StringBuilder sb = new StringBuilder(); 77 | Set keySet = result.keySet(); 78 | for (String className : keySet) { 79 | sb.append(className).append('.').append(result.get(className)).append('\n'); 80 | } 81 | return sb.toString(); 82 | } 83 | 84 | @ManagedAttribute 85 | public void setAdvice(String name) throws Throwable { 86 | try { 87 | Advice instance = (Advice) Class.forName("com.github.zhongl.jsmx.agent." + name).newInstance(); 88 | Probe.setAdvice(instance); 89 | } catch (Throwable t) { 90 | warning(t); 91 | throw t; 92 | } 93 | } 94 | 95 | @ManagedAttribute 96 | public String getAdvice() { 97 | return Probe.adviceName(); 98 | } 99 | 100 | @ManagedOperation 101 | public String getAllLoadedClasses() { 102 | return toString(instrumentation.getAllLoadedClasses()); 103 | } 104 | 105 | @ManagedOperation 106 | public String getClassLoaderOf(String className) { 107 | for (Class clazz : instrumentation.getAllLoadedClasses()) { 108 | if (clazz.getName().equals(className)) return clazz.getClassLoader().toString(); 109 | } 110 | return "Unknown"; 111 | } 112 | 113 | @ManagedOperation 114 | public boolean isRetransformClassesSupported() { 115 | return instrumentation.isRetransformClassesSupported(); 116 | } 117 | 118 | @ManagedOperation 119 | @Description("retransform classes matched patterns and set probes.") 120 | public void probe() throws Throwable { 121 | if (classNames.isEmpty()) return; 122 | try { 123 | add(probeTransformer); 124 | instrumentation.retransformClasses(classArray()); 125 | remove(probeTransformer); 126 | } catch (Throwable t) { 127 | warning(t); 128 | throw t; 129 | } 130 | } 131 | 132 | @ManagedOperation 133 | @Description("retransform classes matched patterns and reset them.") 134 | public void reset() throws Throwable { 135 | if (classNames.isEmpty()) return; 136 | try { 137 | add(resetTransformer); 138 | instrumentation.retransformClasses(classArray()); 139 | remove(resetTransformer); 140 | pointcutFilterSet.clear(); 141 | classNames.clear(); 142 | } catch (Throwable t) { 143 | warning(t); 144 | throw t; 145 | } 146 | } 147 | 148 | private void add(ClassFileTransformer transformer) { 149 | instrumentation.addTransformer(transformer, true); 150 | } 151 | 152 | private Class[] classArray() { 153 | ArrayList> classes = new ArrayList>(classNames.size()); 154 | for (String name : classNames) { 155 | try { 156 | classes.add(classOf(name)); 157 | } catch (ClassNotFoundException e) { 158 | warning(e); 159 | } 160 | } 161 | return classes.toArray(new Class[0]); 162 | } 163 | 164 | private Class classOf(String name) throws ClassNotFoundException { 165 | return Class.forName(name.trim()); 166 | } 167 | 168 | private boolean containsClass(String name) { 169 | String value = slashToDot(name); 170 | for (PointcutFilter filter : pointcutFilterSet) { 171 | if (filter.matchsClass(value)) return true; 172 | } 173 | return false; 174 | } 175 | 176 | private boolean containsMethod(String className, String methodName) { 177 | for (PointcutFilter filter : pointcutFilterSet) { 178 | if (filter.matchsMethod(className, methodName)) return true; 179 | } 180 | return false; 181 | } 182 | 183 | private boolean isCurrent(ClassLoader loader) { 184 | // return getClass().getClassLoader().equals(loader); 185 | return true; 186 | } 187 | 188 | private void remove(ClassFileTransformer transformer) { 189 | instrumentation.removeTransformer(transformer); 190 | } 191 | 192 | private final Set classNames = new HashSet(); 193 | 194 | private final Set pointcutFilterSet = new HashSet(); 195 | 196 | private final ClassFileTransformer resetTransformer = new ClassFileTransformer() { 197 | 198 | @Override 199 | public byte[] transform(ClassLoader loader, 200 | String className, 201 | Class classBeingRedefined, 202 | ProtectionDomain protectionDomain, 203 | byte[] classfileBuffer) throws IllegalClassFormatException { 204 | try { 205 | if (isCurrent(loader) && containsClass(className)) { 206 | byte[] bytes = toBytes(loader.getResourceAsStream(className + ".class")); 207 | LOGGER.info(format("reset class {1} from {0}", loader, className)); 208 | return bytes; 209 | } 210 | } catch (Exception e) { 211 | LOGGER.info(format("transfor but not reset class {1} from {0}", loader, className)); 212 | warning(e); 213 | } 214 | return classfileBuffer; 215 | } 216 | 217 | }; 218 | private final ClassFileTransformer probeTransformer = new ClassFileTransformer() { 219 | 220 | @Override 221 | public byte[] transform(ClassLoader loader, 222 | String className, 223 | Class classBeingRedefined, 224 | ProtectionDomain protectionDomain, 225 | byte[] classfileBuffer) throws IllegalClassFormatException { 226 | try { 227 | if (isCurrent(loader) && containsClass(className)) { 228 | LOGGER.info(format("probe class {1} from {0}", loader, className)); 229 | final ClassReader cr = new ClassReader(classfileBuffer); 230 | final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 231 | cr.accept(new ProbeClassAdapter(cw), ClassReader.EXPAND_FRAMES); 232 | return cw.toByteArray(); 233 | } 234 | } catch (Exception e) { 235 | LOGGER.info(format("transfor class {1} from {0}", loader, className)); 236 | warning(e); 237 | } 238 | return classfileBuffer; 239 | } 240 | 241 | }; 242 | 243 | private final Instrumentation instrumentation; 244 | 245 | class ProbeClassAdapter extends ClassAdapter { 246 | 247 | public ProbeClassAdapter(ClassVisitor cv) { 248 | super(cv); 249 | } 250 | 251 | @Override 252 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 253 | className = slashToDot(name); 254 | super.visit(version, access, name, signature, superName, interfaces); 255 | } 256 | 257 | @Override 258 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 259 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 260 | return (mv != null && containsMethod(className, name)) 261 | ? new ProbeMethodAdapter(mv, access, name, desc, className) : mv; 262 | } 263 | 264 | private String className; 265 | 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /core/src/main/javascript/jolokia-simple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2010 Roland Huss 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | /** 19 | * Simplified access to the Jolokia agent. 20 | * 21 | * This script will add convenience methods to Jolokia for 22 | * simplified access to JMX information. Before including this script, "jolokia.js" 23 | * must be included. 24 | * 25 | * It is recommended to compress this script before using it in production. 26 | * 27 | * @author roland 28 | */ 29 | 30 | if (Jolokia) { 31 | (function($) { 32 | /** 33 | * Get one or more attributes 34 | * 35 | * @param mbean objectname of MBean to query. Can be a pattern. 36 | * @param attribute attribute name. If an array, multiple attributes are fetched. 37 | * If null, all attributes are fetched. 38 | * @param path optional path within the return value. For multi-attribute fetch, the path 39 | * is ignored. 40 | * @param opts options passed to Jolokia.request() 41 | * @return the value of the attribute, possibly a complex object 42 | */ 43 | function getAttribute(mbean,attribute,path,opts) { 44 | if (arguments.length === 3 && $.isPlainObject(path)) { 45 | opts = path; 46 | path = null; 47 | } else if (arguments.length == 2 && $.isPlainObject(attribute)) { 48 | opts = attribute; 49 | attribute = null; 50 | path = null; 51 | } 52 | var req = { type: "read", mbean: mbean, attribute: attribute }; 53 | addPath(req,path); 54 | return extractValue(this.request(req,prepareSucessCallback(opts)),opts); 55 | } 56 | 57 | /** 58 | * Set an attribute on a MBean. 59 | * 60 | * @param mbean objectname of MBean to set 61 | * @param attribute the attribute to set 62 | * @param value the value to set 63 | * @param path an optional inner path which, when given, is used to determine 64 | * an inner object to set the value on 65 | * @param opts additional options passed to Jolokia.request() 66 | * @return the previous value 67 | */ 68 | function setAttribute(mbean,attribute,value,path,opts) { 69 | if (arguments.length === 4 && $.isPlainObject(path)) { 70 | opts = path; 71 | path = null; 72 | } 73 | var req = { type: "write", mbean: mbean, attribute: attribute, value: value }; 74 | addPath(req,path); 75 | return extractValue(this.request(req,prepareSucessCallback(opts)),opts); 76 | } 77 | 78 | /** 79 | * Execute a JMX operation and return the result value 80 | * 81 | * @param mbean objectname of the MBean to operate on 82 | * @param operation name of operation to execute. Can contain a signature in case overloaded 83 | * operations are to be called (comma separated fully qualified argument types 84 | * append to the operation name within parentheses) 85 | * @param arg1, arg2, ..... one or more argument required for executing the operation. 86 | * @param opts optional options for Jolokia.request() (must be an object) 87 | * @return the return value of the JMX operation. 88 | */ 89 | function execute(mbean,operation) { 90 | var req = { type: "exec", mbean: mbean, operation: operation }; 91 | var opts, end = arguments.length; 92 | if (arguments.length > 2 && $.isPlainObject(arguments[arguments.length-1])) { 93 | opts = arguments[arguments.length-1]; 94 | end = arguments.length-1; 95 | } 96 | if (end > 2) { 97 | var args = []; 98 | for (var i = 2; i < end; i++) { 99 | args[i-2] = arguments[i]; 100 | } 101 | req.arguments = args; 102 | } 103 | return extractValue(this.request(req,prepareSucessCallback(opts)),opts); 104 | } 105 | 106 | /** 107 | * Search for MBean based on a pattern and return a reference to the list of found 108 | * MBeans names (as string). If no MBean can be found, null is returned. For 109 | * example, 110 | * 111 | * jolokia.search("*:j2eeType=J2EEServer,*") 112 | * 113 | * searches all MBeans whose name are matching this pattern, which are according 114 | * to JSR77 all application servers in all available domains. 115 | * 116 | * @param mbeanPattern pattern to search for 117 | * @param opts optional options for Jolokia.request() 118 | * @return an array with ObjectNames as string 119 | */ 120 | function search(mbeanPattern,opts) { 121 | var req = { type: "search", mbean: mbeanPattern}; 122 | return extractValue(this.request(req,prepareSucessCallback(opts)),opts); 123 | } 124 | 125 | /** 126 | * This method return the version of the agent and the Jolokia protocol 127 | * version as part of an object. If available, server specific information 128 | * like the application server's name are returned as wel. 129 | * A typical response looks like 130 | * 131 | *
132 |          *  {
133 |          *    protocol: "4.0",
134 |          *    agent: "0.82",
135 |          *    info: {
136 |          *       product: "glassfish",
137 |          *       vendor": "Sun",
138 |          *       extraInfo: {
139 |          *          amxBooted: false
140 |          *       }
141 |          *  }
142 |          * 
143 | * 144 | * @param opts optional options for Jolokia.request() 145 | * @param version and other meta information as object 146 | */ 147 | function version(opts) { 148 | return extractValue(this.request({type: "version"},prepareSucessCallback(opts)),opts); 149 | } 150 | 151 | 152 | /** 153 | * Get all MBeans as registered at the specified server. A C<$path> can be 154 | * specified in order to fetchy only a subset of the information. When no path is 155 | * given, the returned value has the following format 156 | * 157 | *
158 |          * {
159 |          *     <domain> :
160 |          *     {
161 |          *       <canonical property list> :
162 |          *       {
163 |          *           "attr" :
164 |          *           {
165 |          *              <atrribute name> :
166 |          *              {
167 |          *                 desc : <description of attribute>
168 |          *                 type : <java type>,
169 |          *                 rw : true/false
170 |          *              },
171 |          *              ....
172 |          *           },
173 |          *           "op" :
174 |          *           {
175 |          *              <operation name> :
176 |          *              {
177 |          *                "desc" : <description of operation>
178 |          *                "ret" : <return java type>
179 |          *                "args" :
180 |          *                [
181 |          *                   {
182 |          *                     "desc" : <description>,
183 |          *                     "name" : <name>,
184 |          *                     "type" : <java type>
185 |          *                   },
186 |          *                   ....
187 |          *                ]
188 |          *              },
189 |          *              ....
190 |          *       },
191 |          *       ....
192 |          *     }
193 |          *     ....
194 |          *  }
195 |          * 
196 | * 197 | * A complete path has the format <domain>/property 198 | * list>/("attribute"|"operation")/<index>"> 199 | * (e.g. java.lang/name=Code Cache,type=MemoryPool/attribute/0). A path can be 200 | * provided partially, in which case the remaining map/array is returned. See also 201 | * the Jolokia Reference Manual for a more detailed discussion of inner pathes. 202 | * 203 | * 204 | * @param path optional path for diving into the list 205 | * @param opts optional opts passed to Jolokia.request() 206 | */ 207 | function list(path,opts) { 208 | if (arguments.length === 1 && typeof path == "object") { 209 | opts = path; 210 | path = null; 211 | } 212 | var req = { type: "list" }; 213 | addPath(req,path); 214 | return extractValue(this.request(req,prepareSucessCallback(opts)),opts); 215 | } 216 | 217 | // ======================================================================= 218 | // Private methods: 219 | 220 | function addPath(req,path) { 221 | if (path != null) { 222 | req.path = path; 223 | } 224 | } 225 | 226 | function extractValue(response,opts) { 227 | if (response == null) { 228 | return null; 229 | } 230 | if (response.status == 200) { 231 | return response.value; 232 | } 233 | if (opts && opts.error) { 234 | opts.error(response); 235 | } else { 236 | throw new Error("Jolokia-Error: " + JSON.stringify(response)); 237 | } 238 | } 239 | 240 | // Prepare callback to receive directly the value (instead of the full blown response) 241 | function prepareSucessCallback(opts) { 242 | if (opts && opts.success) { 243 | var parm = $.extend({},opts); 244 | parm.success = function(resp) { 245 | opts.success(resp.value); 246 | }; 247 | return parm; 248 | } else { 249 | return opts; 250 | } 251 | } 252 | 253 | // Extend the Jolokia prototype with new functionality (mixin) 254 | $.extend(Jolokia.prototype, 255 | { 256 | "getAttribute" : getAttribute, 257 | "setAttribute" : setAttribute, 258 | "execute": execute, 259 | "search": search, 260 | "version": version, 261 | "list": list 262 | }); 263 | })(jQuery); 264 | } else { 265 | console.error("No Jolokia definition found. Please include jolokia.js before jolokia-simple.js"); 266 | } 267 | -------------------------------------------------------------------------------- /core/src/main/javascript/jolokia.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2010 Roland Huss 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* ================================= 18 | * Jolokia Javascript Client Library 19 | * ================================= 20 | * 21 | * Requires jquery.js and json2.js 22 | * (if no native JSON.stringify() support is available) 23 | */ 24 | 25 | var Jolokia = (function($) { 26 | 27 | // Default paramerters for GET and POST requests 28 | var DEFAULT_CLIENT_PARAMS = { 29 | type: "POST", 30 | jsonp: false 31 | }; 32 | 33 | var GET_AJAX_PARAMS = { 34 | type: "GET" 35 | }; 36 | 37 | var POST_AJAX_PARAMS = { 38 | type: "POST", 39 | processData: false, 40 | dataType: "json", 41 | contentType: "text/json" 42 | }; 43 | 44 | // Processing parameters which are added to the 45 | // URL as query parameters if given as options 46 | var PROCESSING_PARAMS = ["maxDepth","maxCollectionSize","maxObjects","ignoreErrors"]; 47 | 48 | /** 49 | * Constructor for creating a client to the Jolokia agent. 50 | * 51 | * An object containing the default parameters can be provided as argument. For the possible parameters 52 | * see {@link #request()}. 53 | * 54 | * @param param either a string in which case it is used as the URL to the agent or 55 | * an object with the default parameters as key-value pairs 56 | */ 57 | function Jolokia(param) { 58 | // If called without 'new', we are constructing an object 59 | // nevertheless 60 | if ( !(this instanceof arguments.callee) ) { 61 | return new Jolokia(param); 62 | } 63 | 64 | // Jolokia Javascript Client version 65 | this.CLIENT_VERSION = "0.91"; 66 | 67 | // Allow a single URL parameter as well 68 | if (typeof param === "string") { 69 | param = {url: param}; 70 | } 71 | $.extend(this,DEFAULT_CLIENT_PARAMS,param); 72 | 73 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 74 | // Public methods 75 | 76 | /** 77 | * The request method using one or more JSON requests and sending it to the agent. Beside the 78 | * request a bunch of options can be given, which are merged with the options provided 79 | * at the constructor (where the options given here take precedence). 80 | * 81 | * Known options are: 82 | * 83 | *
84 | *
url
85 | *
Agent URL, which is mandatory
86 | *
method
87 | *
88 | * Either "post" or "get" depending on the desired HTTP method (case does not matter). 89 | * Please note, that bulk requests are not possible with "get". On the other 90 | * hand, JSONP requests are not possible with "post" (which obviously implies 91 | * that bulk request cannot be used with JSONP requests). Also, when using a 92 | * read type request for multiple attributes, this also can 93 | * only be sent as "post" requests. If not given, a HTTP method is determined 94 | * dyamically. If a method is selected which doesn't fit to the request, an error 95 | * is raised. 96 | *
97 | *
jsonp
98 | *
99 | * Whether the request should be sent via JSONP (a technique for allowing cross 100 | * domain request circumventing the infamous "same-origin-policy"). This can be 101 | * used only with HTTP "get" requests. 102 | *
103 | *
success
104 | *
105 | * Callback function which is called for a successful request. The callback receives 106 | * the response as single argument. If no success callback is given, then 107 | * the request is performed synchronously and gives back the response as return 108 | * value. 109 | *
110 | *
error
111 | *
112 | * Callback in case a Jolokia error occurs. A Jolokia error is one, in which the HTTP request 113 | * suceeded with a status code of 200, but the response object contains a status other 114 | * than OK (200) which happens if the request JMX operation fails. This callback receives 115 | * the full Jolokia response object (with a key error set). If no error callback 116 | * is given, but an asynchronous operation is performed, the error response is printed 117 | * to the Javascript console by default. 118 | *
119 | *
ajaxError
120 | *
121 | * Global error callback called when the Ajax request itself failed. It obtains the same arguments 122 | * as the error callback given for jQuery.ajax(), i.e. the XmlHttpResonse, 123 | * a text status and an error thrown. Refer to the jQuery documentation for more information about 124 | * this error handler. 125 | *
126 | *
username
127 | *
A username used for HTTP authentication
128 | *
password
129 | *
A password used for HTTP authentication
130 | *
timeout
131 | *
Timeout for the HTTP request
132 | *
maxDepth
133 | *
Maximum traversal depth for serialization of complex return values
134 | *
maxCollectionSize
135 | *
136 | * Maximum size of collections returned during serialization. 137 | * If larger, the collection is returned truncated. 138 | *
139 | *
maxObjects
140 | *
141 | * Maximum number of objects contained in the response. 142 | *
143 | *
ignoreErrors
144 | *
145 | * If set to true, errors during JMX operations and JSON serialization 146 | * are ignored. Otherwise if a single deserialization fails, the whole request 147 | * returns with an error. This works only for certain operations like pattern reads.. 148 | *
149 | *
150 | * 151 | * @param request the request to send 152 | * @param params parameters used for sending the request 153 | * @return the response object if called synchronously or nothing if called for asynchronous operation. 154 | */ 155 | this.request = function(request,params) { 156 | var opts = $.extend({},this,params); 157 | assertNotNull(opts.url,"No URL given"); 158 | 159 | var ajaxParams = {}; 160 | 161 | // Copy over direct params for the jQuery ajax call 162 | $.each(["username", "password", "timeout"],function(i,key) { 163 | if (opts[key]) { 164 | ajaxParams[key] = opts[key]; 165 | } 166 | }); 167 | 168 | if (extractMethod(request,opts) === "post") { 169 | $.extend(ajaxParams,POST_AJAX_PARAMS); 170 | ajaxParams.data = JSON.stringify(request); 171 | ajaxParams.url = ensureTrailingSlash(opts.url); 172 | } else { 173 | $.extend(ajaxParams,GET_AJAX_PARAMS); 174 | ajaxParams.dataType = opts.jsonp ? "jsonp" : "json"; 175 | ajaxParams.url = opts.url + "/" + constructGetUrlPath(request); 176 | } 177 | 178 | // Add processing parameters as query parameters 179 | ajaxParams.url = addProcessingParameters(ajaxParams.url,opts); 180 | 181 | // Global error handler 182 | if (opts.ajaxError) { 183 | ajaxParams.error = opts.ajaxError; 184 | } 185 | 186 | // Dispatch Callbacks to error and success handlers 187 | if (opts.success) { 188 | var success_callback = constructCallbackDispatcher(opts.success); 189 | var error_callback = constructCallbackDispatcher(opts.error); 190 | ajaxParams.success = function(data) { 191 | var responses = $.isArray(data) ? data : [ data ]; 192 | for (var idx = 0; idx < responses.length; idx++) { 193 | var resp = responses[idx]; 194 | if (resp.status == null || resp.status != 200) { 195 | error_callback(resp,idx); 196 | } else { 197 | success_callback(resp,idx); 198 | } 199 | } 200 | }; 201 | 202 | // Perform the request 203 | $.ajax(ajaxParams); 204 | } else { 205 | // Synchronous operation requested (i.e. no callbacks provided) 206 | if (opts.jsonp) { 207 | throw Error("JSONP is not supported for synchronous requests"); 208 | } 209 | ajaxParams.async = false; 210 | var xhr = $.ajax(ajaxParams); 211 | if (httpSuccess(xhr)) { 212 | return $.parseJSON(xhr.responseText); 213 | } else { 214 | return null; 215 | } 216 | } 217 | }; 218 | 219 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 220 | } 221 | 222 | // Private Methods: 223 | 224 | // ======================================================================== 225 | 226 | // Construct a callback dispatcher for appropriately dispatching 227 | // to a single callback or within an array of callbacks 228 | function constructCallbackDispatcher(callback) { 229 | if (callback == null) { 230 | return function(response) { 231 | console.log("Ignoring response " + JSON.stringify(response)); 232 | }; 233 | } else if (callback === "ignore") { 234 | // Ignore the return value 235 | return function() {}; 236 | } 237 | var callbackArray = $.isArray(callback) ? callback : [ callback ]; 238 | return function(response,idx) { 239 | callbackArray[idx % callbackArray.length](response,idx); 240 | } 241 | } 242 | 243 | // Extract the HTTP-Method to use and make some sanity checks if 244 | // the method was provided as part of the options, but dont fit 245 | // to the request given 246 | function extractMethod(request,opts) { 247 | var methodGiven = opts && opts.method ? opts.method.toLowerCase() : null, 248 | method; 249 | if (methodGiven) { 250 | if (methodGiven === "get") { 251 | if ($.isArray(request)) { 252 | throw new Error("Cannot use GET with bulk requests"); 253 | } 254 | if (request.type.toLowerCase() === "read" && $.isArray(request.attribute)) { 255 | throw new Error("Cannot use GET for read with multiple attributes"); 256 | } 257 | if (request.target) { 258 | throw new Error("Cannot use GET request with proxy mode"); 259 | } 260 | } 261 | method = methodGiven; 262 | } else { 263 | // Determine method dynamically 264 | method = $.isArray(request) || 265 | (request.type.toLowerCase() === "read" && $.isArray(request.attribute)) || 266 | request.target ? 267 | "post" : "get"; 268 | } 269 | if (opts.jsonp && method === "post") { 270 | throw new Error("Can not use JSONP with POST requests"); 271 | } 272 | return method; 273 | } 274 | 275 | // Add processing parameters given as request options 276 | // to an URL as GET query parameters 277 | function addProcessingParameters(url, opts) { 278 | var sep = url.indexOf("?") > 0 ? "&" : "?"; 279 | $.each(PROCESSING_PARAMS,function(i,key) { 280 | if (opts[key] != null) { 281 | url += sep + key + "=" + opts[key]; 282 | sep = "&"; 283 | } 284 | }); 285 | return url; 286 | } 287 | 288 | // ======================================================================== 289 | // GET-Request handling 290 | 291 | // Create the URL used for a GET request 292 | function constructGetUrlPath(request) { 293 | var type = request.type; 294 | assertNotNull(type,"No request type given for building a GET request"); 295 | type = type.toLowerCase(); 296 | var extractor = GET_URL_EXTRACTORS[type]; 297 | assertNotNull(extractor,"Unknown request type " + type); 298 | var parts = extractor(request); 299 | var url = type; 300 | $.each(parts,function(i,v) { url += "/" + escapePart(v) }); 301 | return url; 302 | } 303 | 304 | // For POST requests it is recommended to have a trailing slash at the URL 305 | // in order to avoid a redirect which the results in a GET request. 306 | // See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=331194#c1 307 | // for an explanation 308 | function ensureTrailingSlash(url) { 309 | // Squeeze any URL to a single one, optionally adding one 310 | return url.replace(/\/*$/,"/"); 311 | } 312 | 313 | // Extractors used for preparing a GET request, i.e. for creating a stack 314 | // of arguments which gets appended to create the proper access URL 315 | // key: lowercase request type 316 | var GET_URL_EXTRACTORS = { 317 | "read" : function(request) { 318 | if (request.attribute == null) { 319 | // Path gets ignored for multiple attribute fetch 320 | return [ request.mbean ]; 321 | } else { 322 | return appendPath([ request.mbean, request.attribute ],request.path); 323 | } 324 | }, 325 | "write" : function(request) { 326 | return appendPath([ request.mbean, request.attribute, valueToString(request.value) ],request.path); 327 | }, 328 | "exec" : function(request) { 329 | var ret = [ request.mbean, request.operation ]; 330 | if (request.arguments && request.arguments.length > 0) { 331 | $.each(request.arguments,function(index,value) { 332 | ret.push(valueToString(value)); 333 | }); 334 | } 335 | return ret; 336 | }, 337 | "version": function() { 338 | return []; 339 | }, 340 | "search": function(request) { 341 | return [ request.mbean ]; 342 | }, 343 | "list": function(request) { 344 | return appendPath([],request.path); 345 | } 346 | }; 347 | 348 | function escapePart(part) { 349 | // TODO: Escaping of slashes 350 | return part; 351 | } 352 | 353 | // Split up a path and append it to a given array. TODO: Check for escaped slashes 354 | function appendPath(array,path) { 355 | return path ? $.merge(array,path.split(/\//)) : array; 356 | } 357 | 358 | // Convert a value to a string for passing it to the Jolokia agent via 359 | // a get request (write, exec). Value can be either a single object or an array 360 | function valueToString(value) { 361 | if (value == null) { 362 | return "[null]"; 363 | } 364 | if ($.isArray(value)) { 365 | var ret = ""; 366 | for (var i = 0; i < value.length; i++) { 367 | ret += value == null ? "[null]" : singleValueToString(value[i]); 368 | if (i < value.length - 1) { 369 | ret += ","; 370 | } 371 | } 372 | return ret; 373 | } else { 374 | return singleValueToString(value); 375 | } 376 | } 377 | 378 | // Single value conversion for write/exec GET requests 379 | function singleValueToString(value) { 380 | if ( typeof value === "string" && value.length == 0) { 381 | return "\"\""; 382 | } else { 383 | return value.toString(); 384 | } 385 | } 386 | 387 | // Check whether a synchronous request was a success or not 388 | // Taken from jQuery 1.4 389 | function httpSuccess(xhr) { 390 | try { 391 | return !xhr.status && location.protocol === "file:" || 392 | xhr.status >= 200 && xhr.status < 300 || 393 | xhr.status === 304 || xhr.status === 1223; 394 | } catch(e) {} 395 | return false; 396 | } 397 | 398 | // =============================================================================================== 399 | // Utility methods: 400 | 401 | function assertNotNull(object,message) { 402 | if (object == null) { 403 | throw new Error(message); 404 | } 405 | } 406 | 407 | // ================================================================================================ 408 | 409 | // Return back exported function/constructor 410 | return Jolokia; 411 | })(jQuery); 412 | -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/modules/exporting.src.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Highcharts JS v2.1.6 (2011-07-08) 3 | * Exporting module 4 | * 5 | * (c) 2010-2011 Torstein Hønsi 6 | * 7 | * License: www.highcharts.com/license 8 | */ 9 | 10 | // JSLint options: 11 | /*global Highcharts, document, window, Math, setTimeout */ 12 | 13 | (function() { // encapsulate 14 | 15 | // create shortcuts 16 | var HC = Highcharts, 17 | Chart = HC.Chart, 18 | addEvent = HC.addEvent, 19 | createElement = HC.createElement, 20 | discardElement = HC.discardElement, 21 | css = HC.css, 22 | merge = HC.merge, 23 | each = HC.each, 24 | extend = HC.extend, 25 | math = Math, 26 | mathMax = math.max, 27 | doc = document, 28 | win = window, 29 | hasTouch = 'ontouchstart' in doc.documentElement, 30 | M = 'M', 31 | L = 'L', 32 | DIV = 'div', 33 | HIDDEN = 'hidden', 34 | NONE = 'none', 35 | PREFIX = 'highcharts-', 36 | ABSOLUTE = 'absolute', 37 | PX = 'px', 38 | UNDEFINED = undefined, 39 | 40 | // Add language and get the defaultOptions 41 | defaultOptions = HC.setOptions({ 42 | lang: { 43 | downloadPNG: 'Download PNG image', 44 | downloadJPEG: 'Download JPEG image', 45 | downloadPDF: 'Download PDF document', 46 | downloadSVG: 'Download SVG vector image', 47 | exportButtonTitle: 'Export to raster or vector image', 48 | printButtonTitle: 'Print the chart' 49 | } 50 | }); 51 | 52 | // Buttons and menus are collected in a separate config option set called 'navigation'. 53 | // This can be extended later to add control buttons like zoom and pan right click menus. 54 | defaultOptions.navigation = { 55 | menuStyle: { 56 | border: '1px solid #A0A0A0', 57 | background: '#FFFFFF' 58 | }, 59 | menuItemStyle: { 60 | padding: '0 5px', 61 | background: NONE, 62 | color: '#303030', 63 | fontSize: hasTouch ? '14px' : '11px' 64 | }, 65 | menuItemHoverStyle: { 66 | background: '#4572A5', 67 | color: '#FFFFFF' 68 | }, 69 | 70 | buttonOptions: { 71 | align: 'right', 72 | backgroundColor: { 73 | linearGradient: [0, 0, 0, 20], 74 | stops: [ 75 | [0.4, '#F7F7F7'], 76 | [0.6, '#E3E3E3'] 77 | ] 78 | }, 79 | borderColor: '#B0B0B0', 80 | borderRadius: 3, 81 | borderWidth: 1, 82 | //enabled: true, 83 | height: 20, 84 | hoverBorderColor: '#909090', 85 | hoverSymbolFill: '#81A7CF', 86 | hoverSymbolStroke: '#4572A5', 87 | symbolFill: '#E0E0E0', 88 | //symbolSize: 12, 89 | symbolStroke: '#A0A0A0', 90 | //symbolStrokeWidth: 1, 91 | symbolX: 11.5, 92 | symbolY: 10.5, 93 | verticalAlign: 'top', 94 | width: 24, 95 | y: 10 96 | } 97 | }; 98 | 99 | 100 | 101 | // Add the export related options 102 | defaultOptions.exporting = { 103 | //enabled: true, 104 | //filename: 'chart', 105 | type: 'image/png', 106 | url: 'http://export.highcharts.com/', 107 | width: 800, 108 | enableImages: false, 109 | buttons: { 110 | exportButton: { 111 | //enabled: true, 112 | symbol: 'exportIcon', 113 | x: -10, 114 | symbolFill: '#A8BF77', 115 | hoverSymbolFill: '#768F3E', 116 | _titleKey: 'exportButtonTitle', 117 | menuItems: [{ 118 | textKey: 'downloadPNG', 119 | onclick: function() { 120 | this.exportChart(); 121 | } 122 | }, { 123 | textKey: 'downloadJPEG', 124 | onclick: function() { 125 | this.exportChart({ 126 | type: 'image/jpeg' 127 | }); 128 | } 129 | }, { 130 | textKey: 'downloadPDF', 131 | onclick: function() { 132 | this.exportChart({ 133 | type: 'application/pdf' 134 | }); 135 | } 136 | }, { 137 | textKey: 'downloadSVG', 138 | onclick: function() { 139 | this.exportChart({ 140 | type: 'image/svg+xml' 141 | }); 142 | } 143 | }/*, { 144 | text: 'View SVG', 145 | onclick: function() { 146 | var svg = this.getSVG() 147 | .replace(//g, '>'); 149 | 150 | doc.body.innerHTML = '
'+ svg +'
'; 151 | } 152 | }*/] 153 | 154 | }, 155 | printButton: { 156 | //enabled: true, 157 | symbol: 'printIcon', 158 | x: -36, 159 | symbolFill: '#B5C9DF', 160 | hoverSymbolFill: '#779ABF', 161 | _titleKey: 'printButtonTitle', 162 | onclick: function() { 163 | this.print(); 164 | } 165 | } 166 | } 167 | }; 168 | 169 | 170 | 171 | extend(Chart.prototype, { 172 | /** 173 | * Return an SVG representation of the chart 174 | * 175 | * @param additionalOptions {Object} Additional chart options for the generated SVG representation 176 | */ 177 | getSVG: function(additionalOptions) { 178 | var chart = this, 179 | chartCopy, 180 | sandbox, 181 | svg, 182 | seriesOptions, 183 | config, 184 | pointOptions, 185 | pointMarker, 186 | options = merge(chart.options, additionalOptions); // copy the options and add extra options 187 | 188 | // IE compatibility hack for generating SVG content that it doesn't really understand 189 | if (!doc.createElementNS) { 190 | doc.createElementNS = function(ns, tagName) { 191 | var elem = doc.createElement(tagName); 192 | elem.getBBox = function() { 193 | return HC.Renderer.prototype.Element.prototype.getBBox.apply({ element: elem }); 194 | }; 195 | return elem; 196 | }; 197 | } 198 | 199 | // create a sandbox where a new chart will be generated 200 | sandbox = createElement(DIV, null, { 201 | position: ABSOLUTE, 202 | top: '-9999em', 203 | width: chart.chartWidth + PX, 204 | height: chart.chartHeight + PX 205 | }, doc.body); 206 | 207 | // override some options 208 | extend(options.chart, { 209 | renderTo: sandbox, 210 | forExport: true 211 | }); 212 | options.exporting.enabled = false; // hide buttons in print 213 | 214 | if (!options.exporting.enableImages) { 215 | options.chart.plotBackgroundImage = null; // the converter doesn't handle images 216 | } 217 | 218 | // prepare for replicating the chart 219 | options.series = []; 220 | each(chart.series, function(serie) { 221 | seriesOptions = serie.options; 222 | 223 | seriesOptions.animation = false; // turn off animation 224 | seriesOptions.showCheckbox = false; 225 | seriesOptions.visible = serie.visible; 226 | 227 | if (!options.exporting.enableImages) { 228 | // remove image markers 229 | if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) { 230 | seriesOptions.marker.symbol = 'circle'; 231 | } 232 | } 233 | 234 | seriesOptions.data = []; 235 | 236 | each(serie.data, function(point) { 237 | 238 | // extend the options by those values that can be expressed in a number or array config 239 | config = point.config; 240 | pointOptions = { 241 | x: point.x, 242 | y: point.y, 243 | name: point.name 244 | }; 245 | 246 | if (typeof config == 'object' && point.config && config.constructor != Array) { 247 | extend(pointOptions, config); 248 | } 249 | 250 | pointOptions.visible = point.visible; 251 | seriesOptions.data.push(pointOptions); // copy fresh updated data 252 | 253 | if (!options.exporting.enableImages) { 254 | // remove image markers 255 | pointMarker = point.config && point.config.marker; 256 | if (pointMarker && /^url\(/.test(pointMarker.symbol)) { 257 | delete pointMarker.symbol; 258 | } 259 | } 260 | }); 261 | 262 | options.series.push(seriesOptions); 263 | }); 264 | 265 | // generate the chart copy 266 | chartCopy = new Highcharts.Chart(options); 267 | 268 | // reflect axis extremes in the export 269 | each(['xAxis', 'yAxis'], function(axisType) { 270 | each (chart[axisType], function(axis, i) { 271 | var axisCopy = chartCopy[axisType][i], 272 | extremes = axis.getExtremes(), 273 | userMin = extremes.userMin, 274 | userMax = extremes.userMax; 275 | 276 | if (userMin !== UNDEFINED || userMax !== UNDEFINED) { 277 | axisCopy.setExtremes(userMin, userMax, true, false); 278 | } 279 | }); 280 | }); 281 | 282 | // get the SVG from the container's innerHTML 283 | svg = chartCopy.container.innerHTML; 284 | 285 | // free up memory 286 | options = null; 287 | chartCopy.destroy(); 288 | discardElement(sandbox); 289 | 290 | // sanitize 291 | svg = svg 292 | .replace(/zIndex="[^"]+"/g, '') 293 | .replace(/isShadow="[^"]+"/g, '') 294 | .replace(/symbolName="[^"]+"/g, '') 295 | .replace(/jQuery[0-9]+="[^"]+"/g, '') 296 | .replace(/isTracker="[^"]+"/g, '') 297 | .replace(/url\([^#]+#/g, 'url(#') 298 | .replace(/')*/ 301 | /* This fails in IE < 8 302 | .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight 303 | return s2 +'.'+ s3[0]; 304 | })*/ 305 | 306 | // IE specific 307 | .replace(/id=([^" >]+)/g, 'id="$1"') 308 | .replace(/class=([^" ]+)/g, 'class="$1"') 309 | .replace(/ transform /g, ' ') 310 | .replace(/:(path|rect)/g, '$1') 311 | .replace(/]*)>/gi, '') 312 | .replace(/<\/image>/g, '') // remove closing tags for images as they'll never have any content 313 | .replace(/]*)([^\/])>/gi, '') // closes image tags for firefox 314 | .replace(/width=(\d+)/g, 'width="$1"') 315 | .replace(/height=(\d+)/g, 'height="$1"') 316 | .replace(/hc-svg-href="/g, 'xlink:href="') 317 | .replace(/style="([^"]+)"/g, function(s) { 318 | return s.toLowerCase(); 319 | }); 320 | 321 | // IE9 beta bugs with innerHTML. Test again with final IE9. 322 | svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1') 323 | .replace(/"/g, "'"); 324 | if (svg.match(/ xmlns="/g).length == 2) { 325 | svg = svg.replace(/xmlns="[^"]+"/, ''); 326 | } 327 | 328 | return svg; 329 | }, 330 | 331 | /** 332 | * Submit the SVG representation of the chart to the server 333 | * @param {Object} options Exporting options. Possible members are url, type and width. 334 | * @param {Object} chartOptions Additional chart options for the SVG representation of the chart 335 | */ 336 | exportChart: function(options, chartOptions) { 337 | var form, 338 | chart = this, 339 | svg = chart.getSVG(chartOptions); 340 | 341 | // merge the options 342 | options = merge(chart.options.exporting, options); 343 | 344 | // create the form 345 | form = createElement('form', { 346 | method: 'post', 347 | action: options.url 348 | }, { 349 | display: NONE 350 | }, doc.body); 351 | 352 | // add the values 353 | each(['filename', 'type', 'width', 'svg'], function(name) { 354 | createElement('input', { 355 | type: HIDDEN, 356 | name: name, 357 | value: { 358 | filename: options.filename || 'chart', 359 | type: options.type, 360 | width: options.width, 361 | svg: svg 362 | }[name] 363 | }, null, form); 364 | }); 365 | 366 | // submit 367 | form.submit(); 368 | 369 | // clean up 370 | discardElement(form); 371 | }, 372 | 373 | /** 374 | * Print the chart 375 | */ 376 | print: function() { 377 | 378 | var chart = this, 379 | container = chart.container, 380 | origDisplay = [], 381 | origParent = container.parentNode, 382 | body = doc.body, 383 | childNodes = body.childNodes; 384 | 385 | if (chart.isPrinting) { // block the button while in printing mode 386 | return; 387 | } 388 | 389 | chart.isPrinting = true; 390 | 391 | // hide all body content 392 | each(childNodes, function(node, i) { 393 | if (node.nodeType == 1) { 394 | origDisplay[i] = node.style.display; 395 | node.style.display = NONE; 396 | } 397 | }); 398 | 399 | // pull out the chart 400 | body.appendChild(container); 401 | 402 | // print 403 | win.print(); 404 | 405 | // allow the browser to prepare before reverting 406 | setTimeout(function() { 407 | 408 | // put the chart back in 409 | origParent.appendChild(container); 410 | 411 | // restore all body content 412 | each(childNodes, function(node, i) { 413 | if (node.nodeType == 1) { 414 | node.style.display = origDisplay[i]; 415 | } 416 | }); 417 | 418 | chart.isPrinting = false; 419 | 420 | }, 1000); 421 | 422 | }, 423 | 424 | /** 425 | * Display a popup menu for choosing the export type 426 | * 427 | * @param {String} name An identifier for the menu 428 | * @param {Array} items A collection with text and onclicks for the items 429 | * @param {Number} x The x position of the opener button 430 | * @param {Number} y The y position of the opener button 431 | * @param {Number} width The width of the opener button 432 | * @param {Number} height The height of the opener button 433 | */ 434 | contextMenu: function(name, items, x, y, width, height) { 435 | var chart = this, 436 | navOptions = chart.options.navigation, 437 | menuItemStyle = navOptions.menuItemStyle, 438 | chartWidth = chart.chartWidth, 439 | chartHeight = chart.chartHeight, 440 | cacheName = 'cache-'+ name, 441 | menu = chart[cacheName], 442 | menuPadding = mathMax(width, height), // for mouse leave detection 443 | boxShadow = '3px 3px 10px #888', 444 | innerMenu, 445 | hide, 446 | menuStyle; 447 | 448 | // create the menu only the first time 449 | if (!menu) { 450 | 451 | // create a HTML element above the SVG 452 | chart[cacheName] = menu = createElement(DIV, { 453 | className: PREFIX + name 454 | }, { 455 | position: ABSOLUTE, 456 | zIndex: 1000, 457 | padding: menuPadding + PX 458 | }, chart.container); 459 | 460 | innerMenu = createElement(DIV, null, 461 | extend({ 462 | MozBoxShadow: boxShadow, 463 | WebkitBoxShadow: boxShadow, 464 | boxShadow: boxShadow 465 | }, navOptions.menuStyle) , menu); 466 | 467 | // hide on mouse out 468 | hide = function() { 469 | css(menu, { display: NONE }); 470 | }; 471 | 472 | addEvent(menu, 'mouseleave', hide); 473 | 474 | 475 | // create the items 476 | each(items, function(item) { 477 | if (item) { 478 | var div = createElement(DIV, { 479 | onmouseover: function() { 480 | css(this, navOptions.menuItemHoverStyle); 481 | }, 482 | onmouseout: function() { 483 | css(this, menuItemStyle); 484 | }, 485 | innerHTML: item.text || HC.getOptions().lang[item.textKey] 486 | }, extend({ 487 | cursor: 'pointer' 488 | }, menuItemStyle), innerMenu); 489 | 490 | div[hasTouch ? 'ontouchstart' : 'onclick'] = function() { 491 | hide(); 492 | item.onclick.apply(chart, arguments); 493 | }; 494 | 495 | } 496 | }); 497 | 498 | chart.exportMenuWidth = menu.offsetWidth; 499 | chart.exportMenuHeight = menu.offsetHeight; 500 | } 501 | 502 | menuStyle = { display: 'block' }; 503 | 504 | // if outside right, right align it 505 | if (x + chart.exportMenuWidth > chartWidth) { 506 | menuStyle.right = (chartWidth - x - width - menuPadding) + PX; 507 | } else { 508 | menuStyle.left = (x - menuPadding) + PX; 509 | } 510 | // if outside bottom, bottom align it 511 | if (y + height + chart.exportMenuHeight > chartHeight) { 512 | menuStyle.bottom = (chartHeight - y - menuPadding) + PX; 513 | } else { 514 | menuStyle.top = (y + height - menuPadding) + PX; 515 | } 516 | 517 | css(menu, menuStyle); 518 | }, 519 | 520 | /** 521 | * Add the export button to the chart 522 | */ 523 | addButton: function(options) { 524 | var chart = this, 525 | renderer = chart.renderer, 526 | btnOptions = merge(chart.options.navigation.buttonOptions, options), 527 | onclick = btnOptions.onclick, 528 | menuItems = btnOptions.menuItems, 529 | /*position = chart.getAlignment(btnOptions), 530 | buttonLeft = position.x, 531 | buttonTop = position.y,*/ 532 | buttonWidth = btnOptions.width, 533 | buttonHeight = btnOptions.height, 534 | box, 535 | symbol, 536 | button, 537 | borderWidth = btnOptions.borderWidth, 538 | boxAttr = { 539 | stroke: btnOptions.borderColor 540 | 541 | }, 542 | symbolAttr = { 543 | stroke: btnOptions.symbolStroke, 544 | fill: btnOptions.symbolFill 545 | }; 546 | 547 | if (btnOptions.enabled === false) { 548 | return; 549 | } 550 | 551 | // element to capture the click 552 | function revert() { 553 | symbol.attr(symbolAttr); 554 | box.attr(boxAttr); 555 | } 556 | 557 | // the box border 558 | box = renderer.rect( 559 | 0, 560 | 0, 561 | buttonWidth, 562 | buttonHeight, 563 | btnOptions.borderRadius, 564 | borderWidth 565 | ) 566 | //.translate(buttonLeft, buttonTop) // to allow gradients 567 | .align(btnOptions, true) 568 | .attr(extend({ 569 | fill: btnOptions.backgroundColor, 570 | 'stroke-width': borderWidth, 571 | zIndex: 19 572 | }, boxAttr)).add(); 573 | 574 | // the invisible element to track the clicks 575 | button = renderer.rect( 576 | 0, 577 | 0, 578 | buttonWidth, 579 | buttonHeight, 580 | 0 581 | ) 582 | .align(btnOptions) 583 | .attr({ 584 | fill: 'rgba(255, 255, 255, 0.001)', 585 | title: HC.getOptions().lang[btnOptions._titleKey], 586 | zIndex: 21 587 | }).css({ 588 | cursor: 'pointer' 589 | }) 590 | .on('mouseover', function() { 591 | symbol.attr({ 592 | stroke: btnOptions.hoverSymbolStroke, 593 | fill: btnOptions.hoverSymbolFill 594 | }); 595 | box.attr({ 596 | stroke: btnOptions.hoverBorderColor 597 | }); 598 | }) 599 | .on('mouseout', revert) 600 | .on('click', revert) 601 | .add(); 602 | 603 | //addEvent(button.element, 'click', revert); 604 | 605 | // add the click event 606 | if (menuItems) { 607 | onclick = function(e) { 608 | revert(); 609 | var bBox = button.getBBox(); 610 | chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight); 611 | }; 612 | } 613 | /*addEvent(button.element, 'click', function() { 614 | onclick.apply(chart, arguments); 615 | });*/ 616 | button.on('click', function() { 617 | onclick.apply(chart, arguments); 618 | }); 619 | 620 | // the icon 621 | symbol = renderer.symbol( 622 | btnOptions.symbol, 623 | btnOptions.symbolX, 624 | btnOptions.symbolY, 625 | (btnOptions.symbolSize || 12) / 2 626 | ) 627 | .align(btnOptions, true) 628 | .attr(extend(symbolAttr, { 629 | 'stroke-width': btnOptions.symbolStrokeWidth || 1, 630 | zIndex: 20 631 | })).add(); 632 | 633 | 634 | 635 | } 636 | }); 637 | 638 | // Create the export icon 639 | HC.Renderer.prototype.symbols.exportIcon = function(x, y, radius) { 640 | return [ 641 | M, // the disk 642 | x - radius, y + radius, 643 | L, 644 | x + radius, y + radius, 645 | x + radius, y + radius * 0.5, 646 | x - radius, y + radius * 0.5, 647 | 'Z', 648 | M, // the arrow 649 | x, y + radius * 0.5, 650 | L, 651 | x - radius * 0.5, y - radius / 3, 652 | x - radius / 6, y - radius / 3, 653 | x - radius / 6, y - radius, 654 | x + radius / 6, y - radius, 655 | x + radius / 6, y - radius / 3, 656 | x + radius * 0.5, y - radius / 3, 657 | 'Z' 658 | ]; 659 | }; 660 | // Create the print icon 661 | HC.Renderer.prototype.symbols.printIcon = function(x, y, radius) { 662 | return [ 663 | M, // the printer 664 | x - radius, y + radius * 0.5, 665 | L, 666 | x + radius, y + radius * 0.5, 667 | x + radius, y - radius / 3, 668 | x - radius, y - radius / 3, 669 | 'Z', 670 | M, // the upper sheet 671 | x - radius * 0.5, y - radius / 3, 672 | L, 673 | x - radius * 0.5, y - radius, 674 | x + radius * 0.5, y - radius, 675 | x + radius * 0.5, y - radius / 3, 676 | 'Z', 677 | M, // the lower sheet 678 | x - radius * 0.5, y + radius * 0.5, 679 | L, 680 | x - radius * 0.75, y + radius, 681 | x + radius * 0.75, y + radius, 682 | x + radius * 0.5, y + radius * 0.5, 683 | 'Z' 684 | ]; 685 | }; 686 | 687 | 688 | // Add the buttons on chart load 689 | Chart.prototype.callbacks.push(function(chart) { 690 | var n, 691 | exportingOptions = chart.options.exporting, 692 | buttons = exportingOptions.buttons; 693 | 694 | if (exportingOptions.enabled !== false) { 695 | 696 | for (n in buttons) { 697 | chart.addButton(buttons[n]); 698 | } 699 | } 700 | }); 701 | 702 | 703 | })(); -------------------------------------------------------------------------------- /core/src/main/javascript/highcharts/highcharts.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v2.1.6 (2011-07-08) 3 | 4 | (c) 2009-2011 Torstein H?nsi 5 | 6 | License: www.highcharts.com/license 7 | */ 8 | (function(){function pa(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function ja(a,b){return parseInt(a,b||10)}function Pb(a){return typeof a==="string"}function Lb(a){return typeof a==="object"}function dc(a){return typeof a==="number"}function qc(a,b){for(var c=a.length;c--;)if(a[c]===b){a.splice(c,1);break}}function M(a){return a!==Va&&a!==null}function Ea(a,b,c){var d,e;if(Pb(b))if(M(c))a.setAttribute(b,c);else{if(a&&a.getAttribute)e=a.getAttribute(b)}else if(M(b)&&Lb(b))for(d in b)a.setAttribute(d, 9 | b[d]);return e}function rc(a){if(!a||a.constructor!==Array)a=[a];return a}function C(){var a=arguments,b,c,d=a.length;for(b=0;b3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+$a(a-c).toFixed(f).slice(2):"")}function Ad(){this.symbol=this.color=0}function ec(a,b){Cc=C(a,b.animation)}function Bd(){var a=Wa.global.useUTC;Dc=a?Date.UTC:function(b,c,d,e,f,g){return(new Date(b,c,C(d,1),C(e, 11 | 0),C(f,0),C(g,0))).getTime()};bd=a?"getUTCMinutes":"getMinutes";cd=a?"getUTCHours":"getHours";dd=a?"getUTCDay":"getDay";sc=a?"getUTCDate":"getDate";Ec=a?"getUTCMonth":"getMonth";Fc=a?"getUTCFullYear":"getFullYear";Cd=a?"setUTCMinutes":"setMinutes";Dd=a?"setUTCHours":"setHours";ed=a?"setUTCDate":"setDate";Ed=a?"setUTCMonth":"setMonth";Fd=a?"setUTCFullYear":"setFullYear"}function Gc(a){Hc||(Hc=ib(Qb));a&&Hc.appendChild(a);Hc.innerHTML=""}function Ic(){}function Gd(a,b){function c(m,i){function y(k, 12 | o){this.pos=k;this.minor=o;this.isNew=true;o||this.addLabel()}function x(k){if(k){this.options=k;this.id=k.id}return this}function Q(k,o,r){this.isNegative=o;this.options=k;this.x=r;this.alignOptions={align:k.align||(qa?o?"left":"right":"center"),verticalAlign:k.verticalAlign||(qa?"middle":o?"bottom":"top"),y:C(k.y,qa?4:o?14:-6),x:C(k.x,qa?o?-6:6:0)};this.textAlign=k.textAlign||(qa?o?"right":"left":"center")}function na(){var k=[],o=[],r;T=ra=null;Fa=[];u(Ia,function(q){r=false;u(["xAxis","yAxis"], 13 | function(ma){if(q.isCartesian&&(ma==="xAxis"&&Ga||ma==="yAxis"&&!Ga)&&(q.options[ma]===i.index||q.options[ma]===Va&&i.index===0)){q[ma]=v;Fa.push(q);r=true}});if(!q.visible&&w.ignoreHiddenSeries)r=false;if(r){var A,U,G,V,Ba;if(!Ga){A=q.options.stacking;Jc=A==="percent";if(A){V=q.type+C(q.options.stack,"");Ba="-"+V;q.stackKey=V;U=k[V]||[];k[V]=U;G=o[Ba]||[];o[Ba]=G}if(Jc){T=0;ra=99}}if(q.isCartesian){u(q.data,function(ma){var s=ma.x,W=ma.y,ba=W<0,ia=ba?G:U,zb=ba?Ba:V;if(T===null)T=ra=ma[$];if(Ga)if(s> 14 | ra)ra=s;else{if(sra)ra=W;else if(ma=0){T=0;Hd=true}else if(ra<0){ra=0;Id=true}}}})}function O(k,o){var r,q;Fb=o?1:sa.pow(10,kb(sa.log(k)/sa.LN10));r=k/Fb;if(!o){o=[1,2,2.5,5,10];if(i.allowDecimals===false||H)if(Fb===1)o=[1,2,5,10];else if(Fb<=0.1)o=[1/Fb]}for(q= 15 | 0;q0||!Id))fa+=k*Kd}Ta=ca===fa?1:Rb&&!A&&U===r.options.tickPixelInterval?r.tickInterval:C(A,Xa?1:(fa-ca)*U/oa);if(!F&&!M(i.tickInterval))Ta=O(Ta);v.tickInterval=Ta;Kc=i.minorTickInterval==="auto"&&Ta?Ta/5:i.minorTickInterval;if(F){ta=[];A=Wa.global.useUTC;var G=1E3/rb,V=6E4/ 17 | rb,Ba=36E5/rb;U=864E5/rb;k=6048E5/rb;q=2592E6/rb;var ma=31556952E3/rb,s=[["second",G,[1,2,5,10,15,30]],["minute",V,[1,2,5,10,15,30]],["hour",Ba,[1,2,3,4,6,8,12]],["day",U,[1,2]],["week",k,[1,2]],["month",q,[1,2,3,4,6]],["year",ma,null]],W=s[6],ba=W[1],ia=W[2];for(r=0;r=G)ia.setSeconds(ba>=V?0:s*kb(ia.getSeconds()/ 18 | s));if(ba>=V)ia[Cd](ba>=Ba?0:s*kb(ia[bd]()/s));if(ba>=Ba)ia[Dd](ba>=U?0:s*kb(ia[cd]()/s));if(ba>=U)ia[ed](ba>=q?1:s*kb(ia[sc]()/s));if(ba>=q){ia[Ed](ba>=ma?0:s*kb(ia[Ec]()/s));o=ia[Fc]()}if(ba>=ma){o-=o%s;ia[Fd](o)}ba===k&&ia[ed](ia[sc]()-ia[dd]()+i.startOfWeek);r=1;o=ia[Fc]();G=ia.getTime()/rb;V=ia[Ec]();for(Ba=ia[sc]();Go&&ta.shift();if(i.endOnTick)fa=r;else faMb[$])Mb[$]=ta.length}}function Aa(){var k,o;Gb=ca;Ld=fa;na();Ma();fb=ua;ua=oa/(fa-ca||1);if(!Ga)for(k in t)for(o in t[k])t[k][o].cum=t[k][o].total;if(!v.isDirty)v.isDirty= 20 | ca!==Gb||fa!==Ld}function Qa(k){k=(new x(k)).render();Sb.push(k);return k}function Ra(){var k=i.title,o=i.stackLabels,r=i.alternateGridColor,q=i.lineWidth,A,U,G=m.hasRendered,V=G&&M(Gb)&&!isNaN(Gb);A=Fa.length&&M(ca)&&M(fa);oa=z?Ca:xa;ua=oa/(fa-ca||1);eb=z?Y:sb;if(A||Rb){if(Kc&&!Xa)for(A=ca+(ta[0]-ca)%Kc;A<=fa;A+=Kc){Zb[A]||(Zb[A]=new y(A,true));V&&Zb[A].isNew&&Zb[A].render(null,true);Zb[A].isActive=true;Zb[A].render()}u(ta,function(s,W){if(!Rb||s>=ca&&s<=fa){V&&tb[s].isNew&&tb[s].render(W,true); 21 | tb[s].isActive=true;tb[s].render(W)}});r&&u(ta,function(s,W){if(W%2===0&&s=1E3?zd(k,0):k},Oc=z&&i.labels.staggerLines,$b=i.reversed,ac=Xa&&i.tickmarkPlacement==="between"?0.5:0;y.prototype={addLabel:function(){var k=this.pos,o=i.labels,r=!(k===ca&&!C(i.showFirstLabel,1)||k===fa&&!C(i.showLastLabel,0)),q=Xa&&z&&Xa.length&&!o.step&&!o.staggerLines&&!o.rotation&&Ca/Xa.length||!z&&Ca/2,A=this.label;k=$d.call({isFirst:k===ta[0],isLast:k===ta[ta.length-1],dateTimeLabelFormat:Lc,value:Xa&&Xa[k]?Xa[k]: 25 | k});q=q&&{width:Ha(1,X(q-2*(o.padding||10)))+bb};q=pa(q,o.style);if(A===Va)this.label=M(k)&&r&&o.enabled?ga.text(k,0,0).attr({align:o.align,rotation:o.rotation}).css(q).add(gc):null;else A&&A.attr({text:k}).css(q)},getLabelSize:function(){var k=this.label;return k?(this.labelBBox=k.getBBox())[z?"height":"width"]:0},render:function(k,o){var r=!this.minor,q=this.label,A=this.pos,U=i.labels,G=this.gridLine,V=r?i.gridLineWidth:i.minorGridLineWidth,Ba=r?i.gridLineColor:i.minorGridLineColor,ma=r?i.gridLineDashStyle: 26 | i.minorGridLineDashStyle,s=this.mark,W=r?i.tickLength:i.minorTickLength,ba=r?i.tickWidth:i.minorTickWidth||0,ia=r?i.tickColor:i.minorTickColor,zb=r?i.tickPosition:i.minorTickPosition;r=U.step;var lb=o&&Pc||Ua,Nb;Nb=z?Ib(A+ac,null,null,o)+eb:Y+P+(Ka?(o&&jd||Ya)-Hb-Y:0);lb=z?lb-sb+P-(Ka?xa:0):lb-Ib(A+ac,null,null,o)-eb;if(V){A=Tb(A+ac,V,o);if(G===Va){G={stroke:Ba,"stroke-width":V};if(ma)G.dashstyle=ma;this.gridLine=G=V?ga.path(A).attr(G).add(L):null}G&&A&&G.animate({d:A})}if(ba){if(zb==="inside")W= 27 | -W;if(Ka)W=-W;V=ga.crispLine([ab,Nb,lb,La,Nb+(z?0:-W),lb+(z?W:0)],ba);if(s)s.animate({d:V});else this.mark=ga.path(V).attr({stroke:ia,"stroke-width":ba}).add(gc)}if(q&&!isNaN(Nb)){Nb=Nb+U.x-(ac&&z?ac*ua*($b?-1:1):0);lb=lb+U.y-(ac&&!z?ac*ua*($b?1:-1):0);M(U.y)||(lb+=ja(q.styles.lineHeight)*0.9-q.getBBox().height/2);if(Oc)lb+=k/(r||1)%Oc*16;if(r)q[k%r?"hide":"show"]();q[this.isNew?"attr":"animate"]({x:Nb,y:lb})}this.isNew=false},destroy:function(){for(var k in this)this[k]&&this[k].destroy&&this[k].destroy()}}; 28 | x.prototype={render:function(){var k=this,o=k.options,r=o.label,q=k.label,A=o.width,U=o.to,G,V=o.from,Ba=o.dashStyle,ma=k.svgElem,s=[],W,ba,ia=o.color;ba=o.zIndex;var zb=o.events;if(A){s=Tb(o.value,A);o={stroke:ia,"stroke-width":A};if(Ba)o.dashstyle=Ba}else if(M(V)&&M(U)){V=Ha(V,ca);U=qb(U,fa);G=Tb(U);if((s=Tb(V))&&G)s.push(G[4],G[5],G[1],G[2]);else s=null;o={fill:ia}}else return;if(M(ba))o.zIndex=ba;if(ma)if(s)ma.animate({d:s},null,ma.onGetPath);else{ma.hide();ma.onGetPath=function(){ma.show()}}else if(s&& 29 | s.length){k.svgElem=ma=ga.path(s).attr(o).add();if(zb){Ba=function(lb){ma.on(lb,function(Nb){zb[lb].apply(k,[Nb])})};for(W in zb)Ba(W)}}if(r&&M(r.text)&&s&&s.length&&Ca>0&&xa>0){r=va({align:z&&G&&"center",x:z?!G&&4:10,verticalAlign:!z&&G&&"middle",y:z?G?16:10:G?6:-4,rotation:z&&!G&&90},r);if(!q)k.label=q=ga.text(r.text,0,0).attr({align:r.textAlign||r.align,rotation:r.rotation,zIndex:ba}).css(r.style).add();G=[s[1],s[4],C(s[6],s[1])];s=[s[2],s[5],C(s[7],s[2])];W=qb.apply(sa,G);ba=qb.apply(sa,s);q.align(r, 30 | false,{x:W,y:ba,width:Ha.apply(sa,G)-W,height:Ha.apply(sa,s)-ba});q.show()}else q&&q.hide();return k},destroy:function(){for(var k in this){this[k]&&this[k].destroy&&this[k].destroy();delete this[k]}qc(Sb,this)}};Q.prototype={setTotal:function(k){this.cum=this.total=k},render:function(k){var o=this.options.formatter.call(this);if(this.label)this.label.attr({text:o,visibility:ub});else this.label=m.renderer.text(o,0,0).css(this.options.style).attr({align:this.textAlign,rotation:this.options.rotation, 31 | visibility:ub}).add(k)},setOffset:function(k,o){var r=this.isNegative,q=v.translate(this.total),A=v.translate(0);A=$a(q-A);var U=m.xAxis[0].translate(this.x)+k,G=m.plotHeight;r={x:qa?r?q:q-A:U,y:qa?G-U-o:r?G-q-A:G-q,width:qa?A:o,height:qa?o:A};this.label&&this.label.align(this.alignOptions,null,r).attr({visibility:Ab})}};Ib=function(k,o,r,q,A){var U=1,G=0,V=q?fb:ua;q=q?Gb:ca;V||(V=ua);if(r){U*=-1;G=oa}if($b){U*=-1;G-=U*oa}if(o){if($b)k=oa-k;k=k/V+q;if(H&&A)k=sa.pow(10,k)}else{if(H&&A)k=sa.log(k)/ 32 | sa.LN10;k=U*(k-q)*V+G}return k};Tb=function(k,o,r){var q,A,U;k=Ib(k,null,null,r);var G=r&&Pc||Ua,V=r&&jd||Ya,Ba;r=A=X(k+eb);q=U=X(G-k-eb);if(isNaN(k))Ba=true;else if(z){q=ha;U=G-sb;if(rY+Ca)Ba=true}else{r=Y;A=V-Hb;if(qha+xa)Ba=true}return Ba?null:ga.crispLine([ab,r,q,La,A,U],o||0)};if(qa&&Ga&&$b===Va)$b=true;pa(v,{addPlotBand:Qa,addPlotLine:Qa,adjustTickAmount:function(){if(Mb&&!F&&!Xa&&!Rb){var k=hc,o=ta.length;hc=Mb[$];if(ok)k=ca;else if(fa'+(H? 37 | Nc("%A, %b %e, %Y",P):P)+""]:[];u(F,function(ua){oa.push(ua.point.tooltipFormatter($))});return oa.join("
")}function y(F,H){z=Za?F:(2*z+F)/3;Z=Za?H:(Z+H)/2;t.translate(z,Z);kd=$a(F-z)>1||$a(H-Z)>1?function(){y(F,H)}:null}function x(){if(!Za){var F=p.hoverPoints;t.hide();u(ea,function(H){H&&H.hide()});F&&u(F,function(H){H.setState()});p.hoverPoints=null;Za=true}}var Q,na=m.borderWidth,O=m.crosshairs,ea=[],Ma=m.style,Aa=m.shared,Qa=ja(Ma.padding),Ra=na+Qa,Za=true,Ga,Ka,z=0,Z=0;Ma.padding= 38 | 0;var t=ga.g("tooltip").attr({zIndex:8}).add(),v=ga.rect(Ra,Ra,0,0,m.borderRadius,na).attr({fill:m.backgroundColor,"stroke-width":na}).add(t).shadow(m.shadow),N=ga.text("",Qa+Ra,ja(Ma.fontSize)+Qa+Ra).attr({zIndex:1}).css(Ma).add(t);t.hide();return{shared:Aa,refresh:function(F){var H,P,$,oa=0,ua={},fb=[];$=F.tooltipPos;H=m.formatter||i;ua=p.hoverPoints;if(Aa){ua&&u(ua,function(eb){eb.setState()});p.hoverPoints=F;u(F,function(eb){eb.setState(Bb);oa+=eb.plotY;fb.push(eb.getLabelConfig())});P=F[0].plotX; 39 | oa=X(oa)/F.length;ua={x:F[0].category};ua.points=fb;F=F[0]}else ua=F.getLabelConfig();ua=H.call(ua);Q=F.series;P=Aa?P:F.plotX;oa=Aa?oa:F.plotY;H=X($?$[0]:qa?Ca-oa:P);P=X($?$[1]:qa?xa-P:oa);$=Aa||!F.series.isCartesian||kc(H,P);if(ua===false||!$)x();else{if(Za){t.show();Za=false}N.attr({text:ua});$=N.getBBox();Ga=$.width+2*Qa;Ka=$.height+2*Qa;v.attr({width:Ga,height:Ka,stroke:m.borderColor||F.color||Q.color||"#606060"});$=H-Ga+Y-25;P=P-Ka+ha+10;if($<7)$=Y+H+15;if(P<5)P=5;else if(P+Ka>Ua)P=Ua-Ka-5;y(X($- 40 | Ra),X(P-Ra))}if(O){O=rc(O);for(H=O.length;H--;){P=F.series[H?"yAxis":"xAxis"];if(O[H]&&P){P=P.getPlotLinePath(F[H?"y":"x"],1);if(ea[H])ea[H].attr({d:P,visibility:Ab});else{$={"stroke-width":O[H].width||1,stroke:O[H].color||"#C0C0C0",zIndex:2};if(O[H].dashStyle)$.dashstyle=O[H].dashStyle;ea[H]=ga.path(P).attr($).add()}}}}},hide:x}}function f(m,i){function y(z){var Z,t=Nd&&wa.width/wa.documentElement.clientWidth-1,v,N,F;z=z||cb.event;if(!z.target)z.target=z.srcElement;Z=z.touches?z.touches.item(0): 41 | z;if(z.type!=="mousemove"||cb.opera||t){v=ya;N={left:v.offsetLeft,top:v.offsetTop};for(v=v.offsetParent;v;){N.left+=v.offsetLeft;N.top+=v.offsetTop;if(v!==wa.body&&v!==wa.documentElement){N.left-=v.scrollLeft;N.top-=v.scrollTop}v=v.offsetParent}tc=N;v=tc.left;N=tc.top}if(Bc){F=z.x;Z=z.y}else if(Z.layerX===Va){F=Z.pageX-v;Z=Z.pageY-N}else{F=z.layerX;Z=z.layerY}if(t){F+=X((t+1)*v-v);Z+=X((t+1)*N-N)}return pa(z,{chartX:F,chartY:Z})}function x(z){var Z={xAxis:[],yAxis:[]};u(db,function(t){var v=t.translate, 42 | N=t.isXAxis;Z[N?"xAxis":"yAxis"].push({axis:t,value:v((qa?!N:N)?z.chartX-Y:xa-z.chartY+ha,true)})});return Z}function Q(){var z=m.hoverSeries,Z=m.hoverPoint;Z&&Z.onMouseOut();z&&z.onMouseOut();uc&&uc.hide();ld=null}function na(){if(Aa){var z={xAxis:[],yAxis:[]},Z=Aa.getBBox(),t=Z.x-Y,v=Z.y-ha;if(Ma){u(db,function(N){var F=N.translate,H=N.isXAxis,P=qa?!H:H,$=F(P?t:xa-v-Z.height,true,0,0,1);F=F(P?t+Z.width:xa-v,true,0,0,1);z[H?"xAxis":"yAxis"].push({axis:N,min:qb($,F),max:Ha($,F)})});Pa(m,"selection", 43 | z,md)}Aa=Aa.destroy()}m.mouseIsDown=nd=Ma=false;Cb(wa,Jb?"touchend":"mouseup",na)}var O,ea,Ma,Aa,Qa=w.zoomType,Ra=/x/.test(Qa),Za=/y/.test(Qa),Ga=Ra&&!qa||Za&&qa,Ka=Za&&!qa||Ra&&qa;Qc=function(){if(Rc){Rc.translate(Y,ha);qa&&Rc.attr({width:m.plotWidth,height:m.plotHeight}).invert()}else m.trackerGroup=Rc=ga.g("tracker").attr({zIndex:9}).add()};Qc();if(i.enabled)m.tooltip=uc=e(i);(function(){var z=true;ya.onmousedown=function(t){t=y(t);!Jb&&t.preventDefault&&t.preventDefault();m.mouseIsDown=nd=true; 44 | O=t.chartX;ea=t.chartY;Sa(wa,Jb?"touchend":"mouseup",na)};var Z=function(t){if(!(t&&t.touches&&t.touches.length>1)){t=y(t);if(!Jb)t.returnValue=false;var v=t.chartX,N=t.chartY,F=!kc(v-Y,N-ha);if(Jb&&t.type==="touchstart")if(Ea(t.target,"isTracker"))m.runTrackerClick||t.preventDefault();else!ae&&!F&&t.preventDefault();if(F){z||Q();if(vY+Ca)v=Y+Ca;if(Nha+xa)N=ha+xa}if(nd&&t.type!=="touchstart"){Ma=Math.sqrt(Math.pow(O-v,2)+Math.pow(ea-N,2));if(Ma>10){if(lc&&(Ra|| 45 | Za)&&kc(O-Y,ea-ha))Aa||(Aa=ga.rect(Y,ha,Ga?1:Ca,Ka?1:xa,0).attr({fill:"rgba(69,114,167,0.25)",zIndex:7}).add());if(Aa&&Ga){v=v-O;Aa.attr({width:$a(v),x:(v>0?0:v)+O})}if(Aa&&Ka){N=N-ea;Aa.attr({height:$a(N),y:(N>0?0:N)+ea})}}}else if(!F){var H;N=m.hoverPoint;v=m.hoverSeries;var P,$,oa=Ya,ua=qa?t.chartY:t.chartX-Y;if(uc&&i.shared){H=[];P=Ia.length;for($=0;$ 46 | oa&&H.splice(P,1);if(H.length&&H[0].plotX!==ld){uc.refresh(H);ld=H[0].plotX}}if(v&&v.tracker)(t=v.tooltipPoints[ua])&&t!==N&&t.onMouseOver()}return(z=F)||!lc}};ya.onmousemove=Z;Sa(ya,"mouseleave",Q);ya.ontouchstart=function(t){if(Ra||Za)ya.onmousedown(t);Z(t)};ya.ontouchmove=Z;ya.ontouchend=function(){Ma&&Q()};ya.onclick=function(t){var v=m.hoverPoint;t=y(t);t.cancelBubble=true;if(!Ma)if(v&&Ea(t.target,"isTracker")){var N=v.plotX,F=v.plotY;pa(v,{pageX:tc.left+Y+(qa?Ca-F:N),pageY:tc.top+ha+(qa?xa- 47 | N:F)});Pa(v.series,"click",pa(t,{point:v}));v.firePointEvent("click",t)}else{pa(t,x(t));kc(t.chartX-Y,t.chartY-ha)&&Pa(m,"click",t)}Ma=false}})();Od=setInterval(function(){kd&&kd()},32);pa(this,{zoomX:Ra,zoomY:Za,resetTracker:Q})}function g(m){var i=m.type||w.type||w.defaultSeriesType,y=vb[i],x=p.hasRendered;if(x)if(qa&&i==="column")y=vb.bar;else if(!qa&&i==="bar")y=vb.column;i=new y;i.init(p,m);if(!x&&i.inverted)qa=true;if(i.isCartesian)lc=i.isCartesian;Ia.push(i);return i}function h(){w.alignTicks!== 48 | false&&u(db,function(m){m.adjustTickAmount()});Mb=null}function j(m){var i=p.isDirtyLegend,y,x=p.isDirtyBox,Q=Ia.length,na=Q,O=p.clipRect;for(ec(m,p);na--;){m=Ia[na];if(m.isDirty&&m.options.stacking){y=true;break}}if(y)for(na=Q;na--;){m=Ia[na];if(m.options.stacking)m.isDirty=true}u(Ia,function(ea){if(ea.isDirty){ea.cleanData();ea.getSegments();if(ea.options.legendType==="point")i=true}});if(i&&od.renderLegend){od.renderLegend();p.isDirtyLegend=false}if(lc){if(!Sc){Mb=null;u(db,function(ea){ea.setScale()})}h(); 49 | vc();u(db,function(ea){if(ea.isDirty||x){ea.redraw();x=true}})}if(x){pd();Qc();if(O){Tc(O);O.animate({width:p.plotSizeX,height:p.plotSizeY})}}u(Ia,function(ea){if(ea.isDirty&&ea.visible&&(!ea.isCartesian||ea.xAxis))ea.redraw()});jc&&jc.resetTracker&&jc.resetTracker();Pa(p,"redraw")}function l(){var m=a.xAxis||{},i=a.yAxis||{},y;m=rc(m);u(m,function(x,Q){x.index=Q;x.isX=true});i=rc(i);u(i,function(x,Q){x.index=Q});db=m.concat(i);p.xAxis=[];p.yAxis=[];db=mc(db,function(x){y=new c(p,x);p[y.isXAxis?"xAxis": 50 | "yAxis"].push(y);return y});h()}function n(m,i){Kb=va(a.title,m);wc=va(a.subtitle,i);u([["title",m,Kb],["subtitle",i,wc]],function(y){var x=y[0],Q=p[x],na=y[1];y=y[2];if(Q&&na){Q.destroy();Q=null}if(y&&y.text&&!Q)p[x]=ga.text(y.text,0,0).attr({align:y.align,"class":"highcharts-"+x,zIndex:1}).css(y.style).add().align(y,false,Ob)})}function J(){mb=w.renderTo;Pd=nc+qd++;if(Pb(mb))mb=wa.getElementById(mb);mb.innerHTML="";if(!mb.offsetWidth){Vb=mb.cloneNode(0);Na(Vb,{position:oc,top:"-9999px",display:""}); 51 | wa.body.appendChild(Vb)}Uc=(Vb||mb).offsetWidth;xc=(Vb||mb).offsetHeight;p.chartWidth=Ya=w.width||Uc||600;p.chartHeight=Ua=w.height||(xc>19?xc:400);p.container=ya=ib(Qb,{className:"highcharts-container"+(w.className?" "+w.className:""),id:Pd},pa({position:Qd,overflow:ub,width:Ya+bb,height:Ua+bb,textAlign:"left"},w.style),Vb||mb);p.renderer=ga=w.forExport?new Vc(ya,Ya,Ua,true):new Wc(ya,Ya,Ua);var m,i;if(Rd&&ya.getBoundingClientRect){m=function(){Na(ya,{left:0,top:0});i=ya.getBoundingClientRect(); 52 | Na(ya,{left:-(i.left-ja(i.left))+bb,top:-(i.top-ja(i.top))+bb})};m();Sa(cb,"resize",m);Sa(p,"destroy",function(){Cb(cb,"resize",m)})}}function D(){function m(){var y=w.width||mb.offsetWidth,x=w.height||mb.offsetHeight;if(y&&x){if(y!==Uc||x!==xc){clearTimeout(i);i=setTimeout(function(){rd(y,x,false)},100)}Uc=y;xc=x}}var i;Sa(cb,"resize",m);Sa(p,"destroy",function(){Cb(cb,"resize",m)})}function aa(){var m=a.labels,i=a.credits,y;n();od=p.legend=new be(p);vc();u(db,function(x){x.setTickPositions(true)}); 53 | h();vc();pd();lc&&u(db,function(x){x.render()});if(!p.seriesGroup)p.seriesGroup=ga.g("series-group").attr({zIndex:3}).add();u(Ia,function(x){x.translate();x.setTooltipPoints();x.render()});m.items&&u(m.items,function(){var x=pa(m.style,this.style),Q=ja(x.left)+Y,na=ja(x.top)+ha+12;delete x.left;delete x.top;ga.text(this.html,Q,na).attr({zIndex:2}).css(x).add()});if(!p.toolbar)p.toolbar=d(p);if(i.enabled&&!p.credits){y=i.href;ga.text(i.text,0,0).on("click",function(){if(y)location.href=y}).attr({align:i.position.align, 54 | zIndex:8}).css(i.style).add().align(i.position)}Qc();p.hasRendered=true;if(Vb){mb.appendChild(ya);Gc(Vb)}}function E(){var m=Ia.length,i=ya&&ya.parentNode;Pa(p,"destroy");Cb(cb,"unload",E);Cb(p);for(u(db,function(y){Cb(y)});m--;)Ia[m].destroy();if(ya){ya.innerHTML="";Cb(ya);i&&i.removeChild(ya);ya=null}if(ga)ga.alignedObjects=null;clearInterval(Od);for(m in p)delete p[m]}function da(){if(!yc&&cb==cb.top&&wa.readyState!=="complete")wa.attachEvent("onreadystatechange",function(){wa.detachEvent("onreadystatechange", 55 | da);wa.readyState==="complete"&&da()});else{J();sd();td();u(a.series||[],function(m){g(m)});p.inverted=qa=C(qa,a.chart.inverted);l();p.render=aa;p.tracker=jc=new f(p,a.tooltip);aa();Pa(p,"load");b&&b.apply(p,[p]);u(p.callbacks,function(m){m.apply(p,[p])})}}Mc=va(Mc,Wa.xAxis);hd=va(hd,Wa.yAxis);Wa.xAxis=Wa.yAxis=null;a=va(Wa,a);var w=a.chart,R=w.margin;R=Lb(R)?R:[R,R,R,R];var B=C(w.marginTop,R[0]),K=C(w.marginRight,R[1]),S=C(w.marginBottom,R[2]),I=C(w.marginLeft,R[3]),za=w.spacingTop,Da=w.spacingRight, 56 | gb=w.spacingBottom,wb=w.spacingLeft,Ob,Kb,wc,ha,Hb,sb,Y,Ub,mb,Vb,ya,Pd,Uc,xc,Ya,Ua,jd,Pc,Xc,ud,vd,Yc,p=this,ae=(R=w.events)&&!!R.click,wd,kc,uc,nd,bc,Sd,xd,xa,Ca,jc,Rc,Qc,od,Wb,Xb,tc,lc=w.showAxes,Sc=0,db=[],Mb,Ia=[],qa,ga,kd,Od,ld,pd,vc,sd,td,rd,md,Td,be=function(m){function i(L,ka){var T=L.legendItem,ra=L.legendLine,Fa=L.legendSymbol,Ja=Ka.color,Oa=ka?O.itemStyle.color:Ja,fa=ka?L.color:Ja;Ja=ka?L.pointAttr[hb]:{stroke:Ja,fill:Ja};T&&T.css({fill:Oa});ra&&ra.attr({stroke:fa});Fa&&Fa.attr(Ja)}function y(L, 57 | ka,T){var ra=L.legendItem,Fa=L.legendLine,Ja=L.legendSymbol;L=L.checkbox;ra&&ra.attr({x:ka,y:T});Fa&&Fa.translate(ka,T-4);Ja&&Ja.attr({x:ka+Ja.xOff,y:T+Ja.yOff});if(L){L.x=ka;L.y=T}}function x(){u(Qa,function(L){var ka=L.checkbox,T=fb.alignAttr;ka&&Na(ka,{left:T.translateX+L.legendItemWidth+ka.x-40+bb,top:T.translateY+ka.y-11+bb})})}function Q(L){var ka,T,ra,Fa,Ja=L.legendItem;Fa=L.series||L;var Oa=Fa.options,fa=Oa&&Oa.borderWidth||0;if(!Ja){Fa=/^(bar|pie|area|column)$/.test(Fa.type);L.legendItem= 58 | Ja=ga.text(O.labelFormatter.call(L),0,0).css(L.visible?Za:Ka).on("mouseover",function(){L.setState(Bb);Ja.css(Ga)}).on("mouseout",function(){Ja.css(L.visible?Za:Ka);L.setState()}).on("click",function(){var Gb=function(){L.setVisible()};L.firePointEvent?L.firePointEvent("legendItemClick",null,Gb):Pa(L,"legendItemClick",null,Gb)}).attr({zIndex:2}).add(fb);if(!Fa&&Oa&&Oa.lineWidth){var ca={"stroke-width":Oa.lineWidth,zIndex:2};if(Oa.dashStyle)ca.dashstyle=Oa.dashStyle;L.legendLine=ga.path([ab,-Ma-Aa, 59 | 0,La,-Aa,0]).attr(ca).add(fb)}if(Fa)ka=ga.rect(T=-Ma-Aa,ra=-11,Ma,12,2).attr({zIndex:3}).add(fb);else if(Oa&&Oa.marker&&Oa.marker.enabled)ka=ga.symbol(L.symbol,T=-Ma/2-Aa,ra=-4,Oa.marker.radius).attr({zIndex:3}).add(fb);if(ka){ka.xOff=T+fa%2/2;ka.yOff=ra+fa%2/2}L.legendSymbol=ka;i(L,L.visible);if(Oa&&Oa.showCheckbox){L.checkbox=ib("input",{type:"checkbox",checked:L.selected,defaultChecked:L.selected},O.itemCheckboxStyle,ya);Sa(L.checkbox,"click",function(Gb){Pa(L,"checkboxClick",{checked:Gb.target.checked}, 60 | function(){L.select()})})}}ka=Ja.getBBox();T=L.legendItemWidth=O.itemWidth||Ma+Aa+ka.width+Z;P=ka.height;if(ea&&N-v+T>(Ib||Ya-2*z-v)){N=v;F+=P}H=F;y(L,N,F);if(ea)N+=T;else F+=P;eb=Ib||Ha(ea?N-v:T,eb)}function na(){N=v;F=t;H=eb=0;fb||(fb=ga.g("legend").attr({zIndex:7}).add());Qa=[];u(Tb,function(ra){var Fa=ra.options;if(Fa.showInLegend)Qa=Qa.concat(Fa.legendType==="point"?ra.data:ra)});Qa.sort(function(ra,Fa){return(ra.options.legendIndex||0)-(Fa.options.legendIndex||0)});gc&&Qa.reverse();u(Qa,Q); 61 | Wb=Ib||eb;Xb=H-t+P;if(oa||ua){Wb+=2*z;Xb+=2*z;if($)Wb>0&&Xb>0&&$.animate($.crisp(null,null,null,Wb,Xb));else $=ga.rect(0,0,Wb,Xb,O.borderRadius,oa||0).attr({stroke:O.borderColor,"stroke-width":oa||0,fill:ua||jb}).add(fb).shadow(O.shadow);$[Qa.length?"show":"hide"]()}for(var L=["left","right","top","bottom"],ka,T=4;T--;){ka=L[T];if(Ra[ka]&&Ra[ka]!=="auto"){O[T<2?"align":"verticalAlign"]=ka;O[T<2?"x":"y"]=ja(Ra[ka])*(T%2?-1:1)}}fb.align(pa(O,{width:Wb,height:Xb}),true,Ob);Sc||x()}var O=m.options.legend; 62 | if(O.enabled){var ea=O.layout==="horizontal",Ma=O.symbolWidth,Aa=O.symbolPadding,Qa,Ra=O.style,Za=O.itemStyle,Ga=O.itemHoverStyle,Ka=O.itemHiddenStyle,z=ja(Ra.padding),Z=20,t=18,v=4+z+Ma+Aa,N,F,H,P=0,$,oa=O.borderWidth,ua=O.backgroundColor,fb,eb,Ib=O.width,Tb=m.series,gc=O.reversed;na();Sa(m,"endResize",x);return{colorizeItem:i,destroyItem:function(L){var ka=L.checkbox;u(["legendItem","legendLine","legendSymbol"],function(T){L[T]&&L[T].destroy()});ka&&Gc(L.checkbox)},renderLegend:na}}};kc=function(m, 63 | i){return m>=0&&m<=Ca&&i>=0&&i<=xa};Td=function(){Pa(p,"selection",{resetSelection:true},md);p.toolbar.remove("zoom")};md=function(m){var i=Wa.lang,y=p.pointCount<100;p.toolbar.add("zoom",i.resetZoom,i.resetZoomTitle,Td);!m||m.resetSelection?u(db,function(x){x.setExtremes(null,null,false,y)}):u(m.xAxis.concat(m.yAxis),function(x){var Q=x.axis;if(p.tracker[Q.isXAxis?"zoomX":"zoomY"])Q.setExtremes(x.min,x.max,false,y)});j()};vc=function(){var m=a.legend,i=C(m.margin,10),y=m.x,x=m.y,Q=m.align,na=m.verticalAlign, 64 | O;sd();if((p.title||p.subtitle)&&!M(B))if(O=Ha(p.title&&!Kb.floating&&!Kb.verticalAlign&&Kb.y||0,p.subtitle&&!wc.floating&&!wc.verticalAlign&&wc.y||0))ha=Ha(ha,O+C(Kb.margin,15)+za);if(m.enabled&&!m.floating)if(Q==="right")M(K)||(Hb=Ha(Hb,Wb-y+i+Da));else if(Q==="left")M(I)||(Y=Ha(Y,Wb+y+i+wb));else if(na==="top")M(B)||(ha=Ha(ha,Xb+x+i+za));else if(na==="bottom")M(S)||(sb=Ha(sb,Xb-x+i+gb));lc&&u(db,function(ea){ea.getOffset()});M(I)||(Y+=Ub[3]);M(B)||(ha+=Ub[0]);M(S)||(sb+=Ub[2]);M(K)||(Hb+=Ub[1]); 65 | td()};rd=function(m,i,y){var x=p.title,Q=p.subtitle;Sc+=1;ec(y,p);Pc=Ua;jd=Ya;p.chartWidth=Ya=X(m);p.chartHeight=Ua=X(i);Na(ya,{width:Ya+bb,height:Ua+bb});ga.setSize(Ya,Ua,y);Ca=Ya-Y-Hb;xa=Ua-ha-sb;Mb=null;u(db,function(na){na.isDirty=true;na.setScale()});u(Ia,function(na){na.isDirty=true});p.isDirtyLegend=true;p.isDirtyBox=true;vc();x&&x.align(null,null,Ob);Q&&Q.align(null,null,Ob);j(y);Pc=null;Pa(p,"resize");setTimeout(function(){Pa(p,"endResize",null,function(){Sc-=1})},Cc&&Cc.duration||500)}; 66 | td=function(){p.plotLeft=Y=X(Y);p.plotTop=ha=X(ha);p.plotWidth=Ca=X(Ya-Y-Hb);p.plotHeight=xa=X(Ua-ha-sb);p.plotSizeX=qa?xa:Ca;p.plotSizeY=qa?Ca:xa;Ob={x:wb,y:za,width:Ya-wb-Da,height:Ua-za-gb}};sd=function(){ha=C(B,za);Hb=C(K,Da);sb=C(S,gb);Y=C(I,wb);Ub=[0,0,0,0]};pd=function(){var m=w.borderWidth||0,i=w.backgroundColor,y=w.plotBackgroundColor,x=w.plotBackgroundImage,Q,na={x:Y,y:ha,width:Ca,height:xa};Q=m+(w.shadow?8:0);if(m||i)if(Xc)Xc.animate(Xc.crisp(null,null,null,Ya-Q,Ua-Q));else Xc=ga.rect(Q/ 67 | 2,Q/2,Ya-Q,Ua-Q,w.borderRadius,m).attr({stroke:w.borderColor,"stroke-width":m,fill:i||jb}).add().shadow(w.shadow);if(y)if(ud)ud.animate(na);else ud=ga.rect(Y,ha,Ca,xa,0).attr({fill:y}).add().shadow(w.plotShadow);if(x)if(vd)vd.animate(na);else vd=ga.image(x,Y,ha,Ca,xa).add();if(w.plotBorderWidth)if(Yc)Yc.animate(Yc.crisp(null,Y,ha,Ca,xa));else Yc=ga.rect(Y,ha,Ca,xa,0,w.plotBorderWidth).attr({stroke:w.plotBorderColor,"stroke-width":w.plotBorderWidth,zIndex:4}).add();p.isDirtyBox=false};Sa(cb,"unload", 68 | E);w.reflow!==false&&Sa(p,"load",D);if(R)for(wd in R)Sa(p,wd,R[wd]);p.options=a;p.series=Ia;p.addSeries=function(m,i,y){var x;if(m){ec(y,p);i=C(i,true);Pa(p,"addSeries",{options:m},function(){x=g(m);x.isDirty=true;p.isDirtyLegend=true;i&&p.redraw()})}return x};p.animation=C(w.animation,true);p.destroy=E;p.get=function(m){var i,y,x;for(i=0;i=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};la&&la.init&&la.init();if(!la&&cb.jQuery){var ob=jQuery;u=function(a,b){for(var c=0,d=a.length;c-1,f=e?7:3,g;b=b.split(" ");c=[].concat(c);var h,j,l=function(n){for(g=n.length;g--;)n[g]===ab&&n.splice(g+1,0,n[g+1],n[g+2],n[g+1],n[g+2])};if(e){l(b);l(c)}if(a.isArea){h=b.splice(b.length-6,6);j=c.splice(c.length-6,6)}if(d){c=[].concat(c).splice(0,f).concat(c);a.shift=false}if(b.length)for(a=c.length;b.length255)b[e]=255}}return this},setOpacity:function(d){b[3]=d;return this}}};Ic.prototype={init:function(a,b){this.element=wa.createElementNS("http://www.w3.org/2000/svg",b);this.renderer=a},animate:function(a,b,c){if(b=C(b,Cc,true)){b=va(b);if(c)b.complete=c;Zc(this,a,b)}else{this.attr(a);c&&c()}},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName,j= 88 | this.renderer,l,n=this.shadows,J,D=this;if(Pb(a)&&M(b)){c=a;a={};a[c]=b}if(Pb(a)){c=a;if(h==="circle")c={x:"cx",y:"cy"}[c]||c;else if(c==="strokeWidth")c="stroke-width";D=Ea(g,c)||this[c]||0;if(c!=="d"&&c!=="visibility")D=parseFloat(D)}else for(c in a){l=false;d=a[c];if(c==="d"){if(d&&d.join)d=d.join(" ");if(/(NaN| {2}|^$)/.test(d))d="M 0 0";this.d=d}else if(c==="x"&&h==="text"){for(e=0;eg||!M(g)&&M(b))){d.insertBefore(f,a);return this}}d.appendChild(f);this.added=true;return this},destroy:function(){var a=this.element||{},b=this.shadows,c=a.parentNode,d;a.onclick=a.onmouseout=a.onmouseover=a.onmousemove=null;Tc(this);c&&c.removeChild(a);b&&u(b,function(e){(c=e.parentNode)&&c.removeChild(e)});qc(this.renderer.alignedObjects,this);for(d in this)delete this[d];return null},empty:function(){for(var a=this.element,b=a.childNodes, 98 | 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);Ea(e,{isShadow:"true",stroke:"rgb(0, 0, 0)","stroke-opacity":0.05*d,"stroke-width":7-2*d,transform:"translate"+g,fill:jb});b?b.element.appendChild(e):f.parentNode.insertBefore(e,f);c.push(e)}this.shadows=c}return this}};var Vc=function(){this.init.apply(this,arguments)};Vc.prototype={Element:Ic,init:function(a,b,c,d){var e=location, 99 | 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=Bc?"":e.href.replace(/#.*?$/,"");this.defs=this.createElement("defs").add();this.forExport=d;this.setSize(b,c,false)},createElement:function(a){var b=new this.Element;b.init(this,a);return b},buildText:function(a){for(var b=a.element,c=C(a.textStr,"").toString().replace(/<(b|strong)>/g,'').replace(/<(i|em)>/g, 100 | '').replace(//g,"").split(//g),d=b.childNodes,e=/style="([^"]+)"/,f=/href="([^"]+)"/,g=Ea(b,"x"),h=a.styles,j=Rd&&h&&h.HcDirection==="rtl"&&!this.forExport&&ja(pc.split("Firefox/")[1])<4,l,n=h&&ja(h.width),J=h&&h.lineHeight,D,aa=d.length;aa--;)b.removeChild(d[aa]);n&&!a.added&&this.box.appendChild(b);u(c,function(E,da){var w,R=0,B;E=E.replace(//g,"|||");w=E.split("|||"); 101 | u(w,function(K){if(K!==""||w.length===1){var S={},I=wa.createElementNS("http://www.w3.org/2000/svg","tspan");e.test(K)&&Ea(I,"style",K.match(e)[1].replace(/(;| |^)color([ :])/,"$1fill$2"));if(f.test(K)){Ea(I,"onclick",'location.href="'+K.match(f)[1]+'"');Na(I,{cursor:"pointer"})}K=(K.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");if(j){l=[];for(aa=K.length;aa--;)l.push(K.charAt(aa));K=l.join("")}I.appendChild(wa.createTextNode(K));if(R)S.dx=3;else S.x=g;if(!R){if(da){!yc&& 102 | a.renderer.forExport&&Na(I,{display:"block"});B=cb.getComputedStyle&&ja(cb.getComputedStyle(D,null).getPropertyValue("line-height"));if(!B||isNaN(B))B=J||D.offsetHeight||18;Ea(I,"dy",B)}D=I}Ea(I,S);b.appendChild(I);R++;if(n){K=K.replace(/-/g,"- ").split(" ");for(var za,Da=[];K.length||Da.length;){za=b.getBBox().width;S=za>n;if(!S||K.length===1){K=Da;Da=[];if(K.length){I=wa.createElementNS("http://www.w3.org/2000/svg","tspan");Ea(I,{dy:J||16,x:g});b.appendChild(I);if(za>n)n=za}}else{I.removeChild(I.firstChild); 103 | Da.unshift(K.pop())}K.length&&I.appendChild(wa.createTextNode(K.join(" ").replace(/- /g,"-")))}}}})})},crispLine:function(a,b){if(a[1]===a[4])a[1]=a[4]=X(a[1])+b%2/2;if(a[2]===a[5])a[2]=a[5]=X(a[2])+b%2/2;return a},path:function(a){return this.createElement("path").attr({d:a,fill:jb})},circle:function(a,b,c){a=Lb(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(Lb(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, 104 | {innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){if(Lb(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:jb});return e.attr(e.crisp(f,a,b,Ha(c,0),Ha(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[C(c,true)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){return this.createElement("g").attr(M(a)&&{"class":nc+a})},image:function(a,b,c,d, 105 | e){var f={preserveAspectRatio:jb};arguments.length>1&&pa(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){var f,g=this.symbols[a];g=g&&g(X(b),X(c),d,e);var h=/^url\((.*?)\)$/,j;if(g){f=this.path(g);pa(f,{symbolName:a,x:b,y:c,r:d});e&&pa(f,e)}else if(h.test(a)){var l=function(n,J){n.attr({width:J[0],height:J[1]}).translate(-X(J[0]/ 106 | 2),-X(J[1]/2))};j=a.match(h)[1];a=Vd[j];f=this.image(j).attr({x:b,y:c});if(a)l(f,a);else{f.attr({width:0,height:0});ib("img",{onload:function(){l(f,Vd[j]=[this.width,this.height])},src:j})}}else f=this.circle(b,c,d);return f},symbols:{square:function(a,b,c){c=0.707*c;return[ab,a-c,b-c,La,a+c,b-c,a+c,b+c,a-c,b+c,"Z"]},triangle:function(a,b,c){return[ab,a,b-1.33*c,La,a+c,b+0.67*c,a-c,b+0.67*c,"Z"]},"triangle-down":function(a,b,c){return[ab,a,b+1.33*c,La,a-c,b-0.67*c,a+c,b-0.67*c,"Z"]},diamond:function(a, 107 | b,c){return[ab,a,b-c,La,a+c,b,a,b+c,a-c,b,"Z"]},arc:function(a,b,c,d){var e=d.start,f=d.end-1.0E-6,g=d.innerR,h=nb(e),j=Db(e),l=nb(f);f=Db(f);d=d.end-e');if(b){c=b===Qb||b==="span"||b==="img"?c.join(""):a.prepVML(c);this.element=ib(c)}this.renderer=a},add:function(a){var b= 110 | this.renderer,c=this.element,d=b.box;d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);zc&&d.gVis===ub&&Na(c,{visibility:ub});d.appendChild(c);this.added=true;this.alignOnAdd&&this.updateTransform();return this},attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,j=this.renderer,l=this.symbolName,n,J,D=this.shadows,aa=this;if(Pb(a)&&M(b)){c=a;a={};a[c]=b}if(Pb(a)){c=a;aa=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c]}else for(c in a){d=a[c];n=false;if(l&&/^(x|y|r|start|end|width|height|innerR)/.test(c)){if(!J){this.symbolAttr(a); 111 | J=true}n=true}else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;for(n=[];e--;)n[e]=dc(d[e])?X(d[e]*10)-5:d[e]==="Z"?"x":d[e];d=n.join(" ")||"x";f.path=d;if(D)for(e=D.length;e--;)D[e].path=d;n=true}else if(c==="zIndex"||c==="visibility"){if(zc&&c==="visibility"&&h==="DIV"){f.gVis=d;n=f.childNodes;for(e=n.length;e--;)Na(n[e],{visibility:d});if(d===Ab)d=null}if(d)g[c]=d;n=true}else if(/^(width|height)$/.test(c)){if(this.updateClipping){this[c]=d;this.updateClipping()}else g[c]=d;n=true}else if(/^(x|y)$/.test(c)){this[c]= 112 | d;if(f.tagName==="SPAN")this.updateTransform();else g[{x:"left",y:"top"}[c]]=d}else if(c==="class")f.className=d;else if(c==="stroke"){d=j.color(d,f,c);c="strokecolor"}else if(c==="stroke-width"||c==="strokeWidth"){f.stroked=d?true:false;c="strokeweight";this[c]=d;if(dc(d))d+=bb}else if(c==="dashstyle"){(f.getElementsByTagName("stroke")[0]||ib(j.prepVML([""]),null,null,f))[c]=d||"solid";this.dashstyle=d;n=true}else if(c==="fill")if(h==="SPAN")g.color=d;else{f.filled=d!==jb?true:false;d=j.color(d, 113 | f,c);c="fillcolor"}else if(c==="translateX"||c==="translateY"||c==="rotation"||c==="align"){if(c==="align")c="textAlign";this[c]=d;this.updateTransform();n=true}else if(c==="text"){this.bBox=null;f.innerHTML=d;n=true}if(D&&c==="visibility")for(e=D.length;e--;)D[e].style[c]=d;if(!n)if(zc)f[c]=d;else Ea(f,c,d)}return aa},clip:function(a){var b=this,c=a.members;c.push(b);b.destroyClip=function(){qc(c,b)};return b.css(a.getCSS(b.inverted))},css:function(a){var b=this.element;if(b=a&&b.tagName==="SPAN"&& 114 | a.width){delete a.width;this.textWidth=b;this.updateTransform()}this.styles=pa(this.styles,a);Na(this.element,a);return this},destroy:function(){this.destroyClip&&this.destroyClip();Ic.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(){var a=this.element,b=this.bBox;if(!b){if(a.nodeName==="text")a.style.position=oc;b=this.bBox={x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}}return b}, 115 | on:function(a,b){this.element["on"+a]=function(){var c=cb.event;c.target=c.srcElement;b(c)};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],j=g&&g!=="left";if(c||d)a.css({marginLeft:c,marginTop:d});a.inverted&&u(b.childNodes,function(R){a.renderer.invertChild(R,b)});if(b.tagName==="SPAN"){var l,n;c=a.rotation;var J;l=0;d=1;var D=0,aa;J=ja(a.textWidth);var E=a.xCorr|| 116 | 0,da=a.yCorr||0,w=[c,g,b.innerHTML,a.textWidth].join(",");if(w!==a.cTT){if(M(c)){l=c*Ud;d=nb(l);D=Db(l);Na(b,{filter:c?["progid:DXImageTransform.Microsoft.Matrix(M11=",d,", M12=",-D,", M21=",D,", M22=",d,", sizingMethod='auto expand')"].join(""):jb})}l=b.offsetWidth;n=b.offsetHeight;if(l>J){Na(b,{width:J+bb,display:"block",whiteSpace:"normal"});l=J}J=X((ja(b.style.fontSize)||12)*1.2);E=d<0&&-l;da=D<0&&-n;aa=d*D<0;E+=D*J*(aa?1-h:h);da-=d*J*(c?aa?h:1-h:1);if(j){E-=l*h*(d<0?-1:1);if(c)da-=n*h*(D<0?-1: 117 | 1);Na(b,{textAlign:g})}a.xCorr=E;a.yCorr=da}Na(b,{left:e+E,top:f+da});a.cTT=w}}else this.alignOnAdd=true},shadow:function(a,b){var c=[],d,e=this.element,f=this.renderer,g,h=e.style,j,l=e.path;if(l&&typeof l.value!=="string")l="x";if(a){for(d=1;d<=3;d++){j=[''];g=ib(f.prepVML(j),null,{left:ja(h.left)+1,top:ja(h.top)+1});j=[''];ib(f.prepVML(j), 118 | null,null,g);b?b.element.appendChild(g):e.parentNode.insertBefore(g,e);c.push(g)}this.shadows=c}return this}});la=function(){this.init.apply(this,arguments)};la.prototype=va(Vc.prototype,{Element:Eb,isIE8:pc.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d;this.alignedObjects=[];d=this.createElement(Qb);a.appendChild(d.element);this.box=d.element;this.boxWrapper=d;this.setSize(b,c,false);if(!wa.namespaces.hcv){wa.namespaces.add("hcv","urn:schemas-microsoft-com:vml");wa.createStyleSheet().cssText= 119 | "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 pa(e,{members:[],left:a,top:b,width:c,height:d,getCSS:function(f){var g=this.top,h=this.left,j=h+this.width,l=g+this.height;g={clip:"rect("+X(f?h:g)+"px,"+X(f?l:j)+"px,"+X(f?j:l)+"px,"+X(f?g:h)+"px)"};!f&&zc&&pa(g,{width:j+bb,height:l+bb});return g},updateClipping:function(){u(e.members,function(f){f.css(e.getCSS(f.inverted))})}})}, 120 | color:function(a,b,c){var d,e=/^rgba/;if(a&&a.linearGradient){var f,g,h=a.linearGradient,j,l,n,J;u(a.stops,function(D,aa){if(e.test(D[1])){d=Yb(D[1]);f=d.get("rgb");g=d.get("a")}else{f=D[1];g=1}if(aa){n=f;J=g}else{j=f;l=g}});a=90-sa.atan((h[3]-h[1])/(h[2]-h[0]))*180/cc;c=["<",c,' colors="0% ',j,",100% ",n,'" angle="',a,'" opacity="',J,'" o:opacity2="',l,'" type="gradient" focus="100%" />'];ib(this.prepVML(c),null,null,b)}else if(e.test(a)&&b.tagName!=="IMG"){d=Yb(a);c=["<",c,' opacity="',d.get("a"), 121 | '"/>'];ib(this.prepVML(c),null,null,b);return d.get("rgb")}else return a},prepVML:function(a){var b=this.isIE8;a=a.join("");if(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);')}else a=a.replace("<","1&&f.css({left:b,top:c,width:d,height:e});return f},rect:function(a,b, 123 | c,d,e,f){if(Lb(a)){b=a.y;c=a.width;d=a.height;e=a.r;f=a.strokeWidth;a=a.x}var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,Ha(c,0),Ha(d,0)))},invertChild:function(a,b){var c=b.style;Na(a,{flip:"x",left:ja(c.width)-10,top:ja(c.height)-10,rotation:-90})},symbols:{arc:function(a,b,c,d){var e=d.start,f=d.end,g=nb(e),h=Db(e),j=nb(f),l=Db(f);d=d.innerR;var n=0.07/c,J=d&&0.1/d||0;if(f-e===0)return["x"];else if(2*cc-f+e',this.name||b.name,": ",!a?"x = "+(this.name||this.x)+", ":"","",!a?"y = ":"",this.y,""].join("")},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g=e.chart;b=C(b,true);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);if(Lb(a)){e.getAttribs();f&&f.attr(d.pointAttr[e.state])}e.isDirty=true;b&&g.redraw(c)})},remove:function(a,b){var c= 129 | this,d=c.series,e=d.chart,f=d.data;ec(b,e);a=C(a,true);c.firePointEvent("remove",null,function(){qc(f,c);c.destroy();d.isDirty=true;a&&e.redraw()})},firePointEvent:function(a,b,c){var d=this,e=this.series.options;if(e.point.events[a]||d.options&&d.options.events&&d.options.events[a])this.importEvents();if(a==="click"&&e.allowPointSelect)c=function(f){d.select(null,f.ctrlKey||f.metaKey||f.shiftKey)};Pa(this,a,b,c)},importEvents:function(){if(!this.hasImportedEvents){var a=va(this.series.options.point, 130 | this.options).events,b;this.events=a;for(b in a)Sa(this,b,a[b]);this.hasImportedEvents=true}},setState:function(a){var b=this.series,c=b.options.states,d=xb[b.type].marker&&b.options.marker,e=d&&!d.enabled,f=(d=d&&d.states[a])&&d.enabled===false,g=b.stateMarkerGraphic,h=b.chart,j=this.pointAttr;a=a||hb;if(!(a===this.state||this.selected&&a!=="select"||c[a]&&c[a].enabled===false||a&&(f||e&&!d.enabled))){if(this.graphic)this.graphic.attr(j[a]);else{if(a){if(!g)b.stateMarkerGraphic=g=h.renderer.circle(0, 131 | 0,j[a].r).attr(j[a]).add(b.group);g.translate(this.plotX,this.plotY)}if(g)g[a?"show":"hide"]()}this.state=a}}};var pb=function(){};pb.prototype={isCartesian:true,type:"line",pointClass:Ac,pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor",r:"radius"},init:function(a,b){var c,d;d=a.series.length;this.chart=a;b=this.setOptions(b);pa(this,{index:d,options:b,name:b.name||"Series "+(d+1),state:hb,pointAttr:{},visible:b.visible!==false,selected:b.selected===true});d=b.events; 132 | for(c in d)Sa(this,c,d[c]);if(d&&d.click||b.point&&b.point.events&&b.point.events.click||b.allowPointSelect)a.runTrackerClick=true;this.getColor();this.getSymbol();this.setData(b.data,false)},autoIncrement:function(){var a=this.options,b=this.xIncrement;b=C(b,a.pointStart,0);this.pointInterval=C(this.pointInterval,a.pointInterval,1);this.xIncrement=b+this.pointInterval;return b},cleanData:function(){var a=this.chart,b=this.data,c,d,e=a.smallestInterval,f,g;b.sort(function(h,j){return h.x-j.x});if(this.options.connectNulls)for(g= 133 | b.length-1;g>=0;g--)b[g].y===null&&b[g-1]&&b[g+1]&&b.splice(g,1);for(g=b.length-1;g>=0;g--)if(b[g-1]){f=b[g].x-b[g-1].x;if(f>0&&(d===Va||fa+1&&b.push(c.slice(a+1,e));a=e}else e===c.length-1&&b.push(c.slice(a+1,e+1))});this.segments=b},setOptions:function(a){var b=this.chart.options.plotOptions;return va(b[this.type],b.series,a)},getColor:function(){var a= 134 | 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.chart.options.symbols,b=this.chart.counters;this.symbol=this.options.marker.symbol||a[b.symbol++];b.wrapSymbol(a.length)},addPoint:function(a,b,c,d){var e=this.data,f=this.graph,g=this.area,h=this.chart;a=(new this.pointClass).init(this,a);ec(d,h);if(f&&c)f.shift=c;if(g){g.shift=c;g.isArea=true}b=C(b,true);e.push(a);c&&e[0].remove(false);this.getAttribs(); 135 | this.isDirty=true;b&&h.redraw()},setData:function(a,b){var c=this,d=c.data,e=c.initialColor,f=c.chart,g=d&&d.length||0;c.xIncrement=null;if(M(e))f.counters.color=e;for(a=mc(rc(a||[]),function(h){return(new c.pointClass).init(c,h)});g--;)d[g].destroy();c.data=a;c.cleanData();c.getSegments();c.getAttribs();c.isDirty=true;f.isDirtyBox=true;C(b,true)&&f.redraw(false)},remove:function(a,b){var c=this,d=c.chart;a=C(a,true);if(!c.isRemoving){c.isRemoving=true;Pa(c,"remove",null,function(){c.destroy();d.isDirtyLegend= 136 | d.isDirtyBox=true;a&&d.redraw(b)})}c.isRemoving=false},translate:function(){for(var a=this.chart,b=this.options.stacking,c=this.xAxis.categories,d=this.yAxis,e=this.data,f=e.length;f--;){var g=e[f],h=g.x,j=g.y,l=g.low,n=d.stacks[(j<0?"-":"")+this.stackKey];g.plotX=this.xAxis.translate(h);if(b&&this.visible&&n&&n[h]){l=n[h];h=l.total;l.cum=l=l.cum-j;j=l+j;if(b==="percent"){l=h?l*100/h:0;j=h?j*100/h:0}g.percentage=h?g.y*100/h:0;g.stackTotal=h}if(M(l))g.yBottom=d.translate(l,0,1,0,1);if(j!==null)g.plotY= 137 | d.translate(j,0,1,0,1);g.clientX=a.inverted?a.plotHeight-g.plotX:g.plotX;g.category=c&&c[g.x]!==Va?c[g.x]:g.x}},setTooltipPoints:function(a){var b=this.chart,c=b.inverted,d=[],e=X((c?b.plotTop:b.plotLeft)+b.plotSizeX),f,g,h=[];if(a)this.tooltipPoints=null;u(this.segments,function(j){d=d.concat(j)});if(this.xAxis&&this.xAxis.reversed)d=d.reverse();u(d,function(j,l){f=d[l-1]?d[l-1]._high+1:0;for(g=j._high=d[l+1]?kb((j.plotX+(d[l+1]?d[l+1].plotX:e))/2):e;f<=g;)h[c?e-f++:f++]=j});this.tooltipPoints=h}, 138 | onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(!(!Jb&&a.mouseIsDown)){b&&b!==this&&b.onMouseOut();this.options.events.mouseOver&&Pa(this,"mouseOver");this.tracker&&this.tracker.toFront();this.setState(Bb);a.hoverSeries=this}},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;d&&d.onMouseOut();this&&a.events.mouseOut&&Pa(this,"mouseOut");c&&!a.stickyTracking&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this.chart,c=this.clipRect, 139 | d=this.options.animation;if(d&&!Lb(d))d={};if(a){if(!c.isAnimating){c.attr("width",0);c.isAnimating=true}}else{c.animate({width:b.plotSizeX},d);this.animate=null}},drawPoints:function(){var a,b=this.data,c=this.chart,d,e,f,g,h,j;if(this.options.marker.enabled)for(f=b.length;f--;){g=b[f];d=g.plotX;e=g.plotY;j=g.graphic;if(e!==Va&&!isNaN(e)){a=g.pointAttr[g.selected?"select":hb];h=a.r;if(j)j.animate({x:d,y:e,r:h});else g.graphic=c.renderer.symbol(C(g.marker&&g.marker.symbol,this.symbol),d,e,h).attr(a).add(this.group)}}}, 140 | 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]=C(a[g],b[f],c[f],d[f])}return h},getAttribs:function(){var a=this,b=xb[a.type].marker?a.options.marker:a.options,c=b.states,d=c[Bb],e,f=a.color,g={stroke:f,fill:f},h=a.data,j=[],l,n=a.pointAttrToOptions,J;if(a.options.marker){d.radius=d.radius||b.radius+2;d.lineWidth=d.lineWidth||b.lineWidth+1}else d.color=d.color||Yb(d.color||f).brighten(d.brightness).get();j[hb]=a.convertAttribs(b, 141 | g);u([Bb,"select"],function(D){j[D]=a.convertAttribs(c[D],j[hb])});a.pointAttr=j;for(f=h.length;f--;){g=h[f];if((b=g.options&&g.options.marker||g.options)&&b.enabled===false)b.radius=0;e=false;if(g.options)for(J in n)if(M(b[n[J]]))e=true;if(e){l=[];c=b.states||{};e=c[Bb]=c[Bb]||{};if(!a.options.marker)e.color=Yb(e.color||g.options.color).brighten(e.brightness||d.brightness).get();l[hb]=a.convertAttribs(b,j[hb]);l[Bb]=a.convertAttribs(c[Bb],j[Bb],l[hb]);l.select=a.convertAttribs(c.select,j.select, 142 | l[hb])}else l=j;g.pointAttr=l}},destroy:function(){var a=this,b=a.chart,c=/\/5[0-9\.]+ (Safari|Mobile)\//.test(pc),d,e;Pa(a,"destroy");Cb(a);a.legendItem&&a.chart.legend.destroyItem(a);u(a.data,function(f){f.destroy()});u(["area","graph","dataLabelsGroup","group","tracker"],function(f){if(a[f]){d=c&&f==="group"?"hide":"destroy";a[f][d]()}});if(b.hoverSeries===a)b.hoverSeries=null;qc(b.series,a);for(e in a)delete a[e]},drawDataLabels:function(){if(this.options.dataLabels.enabled){var a=this,b,c,d= 143 | a.data,e=a.options.dataLabels,f,g=a.dataLabelsGroup,h=a.chart,j=h.inverted,l=a.type,n;n=a.options.stacking;var J=l==="column"||l==="bar",D=e.verticalAlign===null,aa=e.y===null;if(J)if(n){if(D)e=va(e,{verticalAlign:"middle"});if(aa)e=va(e,{y:{top:14,middle:4,bottom:-6}[e.verticalAlign]})}else if(D)e=va(e,{verticalAlign:"top"});if(!g)g=a.dataLabelsGroup=h.renderer.g("data-labels").attr({visibility:a.visible?Ab:ub,zIndex:6}).translate(h.plotLeft,h.plotTop).add();n=e.color;if(n==="auto")n=null;e.style.color= 144 | C(n,a.color);u(d,function(E){var da=E.barX,w=da&&da+E.barW/2||E.plotX||-999,R=C(E.plotY,-999),B=E.dataLabel,K=e.align,S=aa?E.y>0?-6:12:e.y;f=e.formatter.call(E.getLabelConfig());b=(j?h.plotWidth-R:w)+e.x;c=(j?h.plotHeight-w:R)+S;if(l==="column")b+={left:-1,right:1}[K]*E.barW/2||0;if(j&&E.y<0){K="right";b-=10}if(B){if(j&&!e.y)c=c+ja(B.styles.lineHeight)*0.9-B.getBBox().height/2;B.attr({text:f}).animate({x:b,y:c})}else if(M(f)){B=E.dataLabel=h.renderer.text(f,b,c).attr({align:K,rotation:e.rotation, 145 | zIndex:1}).css(e.style).add(g);j&&!e.y&&B.attr({y:c+ja(B.styles.lineHeight)*0.9-B.getBBox().height/2})}if(J&&a.options.stacking){w=E.barY;R=E.barW;E=E.barH;B.align(e,null,{x:j?h.plotWidth-w-E:da,y:j?h.plotHeight-da-R:w,width:j?E:R,height:j?R:E})}})}},drawGraph:function(){var a=this,b=a.options,c=a.graph,d=[],e,f=a.area,g=a.group,h=b.lineColor||a.color,j=b.lineWidth,l=b.dashStyle,n,J=a.chart.renderer,D=a.yAxis.getThreshold(b.threshold||0),aa=/^area/.test(a.type),E=[],da=[];u(a.segments,function(w){n= 146 | [];u(w,function(S,I){if(a.getPointSpline)n.push.apply(n,a.getPointSpline(w,S,I));else{n.push(I?La:ab);I&&b.step&&n.push(S.plotX,w[I-1].plotY);n.push(S.plotX,S.plotY)}});if(w.length>1)d=d.concat(n);else E.push(w[0]);if(aa){var R=[],B,K=n.length;for(B=0;B=0;B--)R.push(w[B].plotX,w[B].yBottom);else R.push(La,w[w.length-1].plotX,D,La,w[0].plotX,D);da=da.concat(R)}});a.graphPath=d;a.singlePoints=E;if(aa){e= 147 | C(b.fillColor,Yb(a.color).setOpacity(b.fillOpacity||0.75).get());if(f)f.animate({d:da});else a.area=a.chart.renderer.path(da).attr({fill:e}).add(g)}if(c)c.animate({d:d});else if(j){c={stroke:h,"stroke-width":j};if(l)c.dashstyle=l;a.graph=J.path(d).attr(c).add(g).shadow(b.shadow)}},render:function(){var a=this,b=a.chart,c,d,e=a.options,f=e.animation,g=f&&a.animate;f=g?f&&f.duration||500:0;var h=a.clipRect,j=b.renderer;if(!h){h=a.clipRect=!b.hasRendered&&b.clipRect?b.clipRect:j.clipRect(0,0,b.plotSizeX, 148 | b.plotSizeY);if(!b.clipRect)b.clipRect=h}if(!a.group){c=a.group=j.g("series");if(b.inverted){d=function(){c.attr({width:b.plotWidth,height:b.plotHeight}).invert()};d();Sa(b,"resize",d);Sa(a,"destroy",function(){Cb(b,"resize",d)})}c.clip(a.clipRect).attr({visibility:a.visible?Ab:ub,zIndex:e.zIndex}).translate(b.plotLeft,b.plotTop).add(b.seriesGroup)}a.drawDataLabels();g&&a.animate(true);a.drawGraph&&a.drawGraph();a.drawPoints();a.options.enableMouseTracking!==false&&a.drawTracker();g&&a.animate(); 149 | setTimeout(function(){h.isAnimating=false;if((c=a.group)&&h!==b.clipRect&&h.renderer){c.clip(a.clipRect=b.clipRect);h.destroy()}},f);a.isDirty=false},redraw:function(){var a=this.chart,b=this.group;if(b){a.inverted&&b.attr({width:a.plotWidth,height:a.plotHeight});b.animate({translateX:a.plotLeft,translateY:a.plotTop})}this.translate();this.setTooltipPoints(true);this.render()},setState:function(a){var b=this.options,c=this.graph,d=b.states;b=b.lineWidth;a=a||hb;if(this.state!==a){this.state=a;if(!(d[a]&& 150 | d[a].enabled===false)){if(a)b=d[a].lineWidth||b+1;if(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,j=this.data,l=c.options.chart.ignoreHiddenSeries;h=this.visible;h=(this.visible=a=a===Va?!h:a)?"show":"hide";e&&e[h]();if(f)f[h]();else for(e=j.length;e--;){f=j[e];f.tracker&&f.tracker[h]()}g&&g[h]();d&&c.legend.colorizeItem(this,a);this.isDirty=true;this.options.stacking&&u(c.series, 151 | function(n){if(n.options.stacking&&n.visible)n.isDirty=true});if(l)c.isDirtyBox=true;b!==false&&c.redraw();Pa(this,h)},show:function(){this.setVisible(true)},hide:function(){this.setVisible(false)},select:function(a){this.selected=a=a===Va?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;Pa(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};var j=a.singlePoints, 152 | l;if(d)for(l=d+1;l--;){c[l]===ab&&c.splice(l+1,0,c[l+1]-f,c[l+2],La);if(l&&c[l]===ab||l===d)c.splice(l,0,La,c[l-2]+f,c[l-1])}for(l=0;la&&j>e){j=Ha(a,e);n=2*e-j}else if(jg&&n>e){n=Ha(g,e);j=2*e-n}else if(nK?za-K:B-(I<=B?K:0)}Kb=gb-3}pa(S,{barX:Da,barY:gb,barW:w,barH:wb});S.shapeType="rect";I=pa(b.renderer.Element.prototype.crisp.apply({},[e,Da,gb,w,wb]),{r:c.borderRadius});if(e% 157 | 2){I.y-=1;I.height+=1}S.shapeArgs=I;S.trackerArgs=M(Kb)&&va(S.shapeArgs,{height:Ha(6,wb+3),y:Kb})})},getSymbol:function(){},drawGraph:function(){},drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d,e;u(a.data,function(f){var g=f.plotY;if(g!==Va&&!isNaN(g)&&f.y!==null){d=f.graphic;e=f.shapeArgs;if(d){Tc(d);d.animate(e)}else f.graphic=c[f.shapeType](e).attr(f.pointAttr[f.selected?"select":hb]).add(a.group).shadow(b.shadow)}})},drawTracker:function(){var a=this,b=a.chart,c=b.renderer, 158 | d,e,f=+new Date,g=a.options.cursor,h=g&&{cursor:g},j;u(a.data,function(l){e=l.tracker;d=l.trackerArgs||l.shapeArgs;delete d.strokeWidth;if(l.y!==null)if(e)e.attr(d);else l.tracker=c[l.shapeType](d).attr({isTracker:f,fill:Wd,visibility:a.visible?Ab:ub,zIndex:1}).on(Jb?"touchstart":"mouseover",function(n){j=n.relatedTarget||n.fromElement;b.hoverSeries!==a&&Ea(j,"isTracker")!==f&&a.onMouseOver();l.onMouseOver()}).on("mouseout",function(n){if(!a.options.stickyTracking){j=n.relatedTarget||n.toElement; 159 | Ea(j,"isTracker")!==f&&a.onMouseOut()}}).css(h).add(l.group||b.trackerGroup)})},animate:function(a){var b=this,c=b.data;if(!a){u(c,function(d){var e=d.graphic;d=d.shapeArgs;if(e){e.attr({height:0,y:b.yAxis.translate(0,0,1)});e.animate({height:d.height,y:d.y},b.options.animation)}});b.animate=null}},remove:function(){var a=this,b=a.chart;b.hasRendered&&u(b.series,function(c){if(c.type===a.type)c.isDirty=true});pb.prototype.remove.apply(a,arguments)}});vb.column=ad;la=yb(ad,{type:"bar",init:function(a){a.inverted= 160 | this.inverted=true;ad.prototype.init.apply(this,arguments)}});vb.bar=la;la=yb(pb,{type:"scatter",translate:function(){var a=this;pb.prototype.translate.apply(a);u(a.data,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;u(a.data,function(e){(d=e.graphic)&&d.attr({isTracker:true}).on("mouseover",function(){a.onMouseOver();e.onMouseOver()}).on("mouseout",function(){a.options.stickyTracking|| 161 | a.onMouseOut()}).css(c)})},cleanData:function(){}});vb.scatter=la;la=yb(Ac,{init:function(){Ac.prototype.init.apply(this,arguments);var a=this,b;pa(a,{visible:a.visible!==false,name:C(a.name,"Slice")});b=function(){a.slice()};Sa(a,"select",b);Sa(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===Va?!this.visible:a)?"show":"hide";this.group[g]();c&&c[g]();d&&d[g]();e&&e[g]();f&&f[g](); 162 | this.legendItem&&b.legend.colorizeItem(this,a)},slice:function(a,b,c){var d=this.series.chart,e=this.slicedTranslation;ec(c,d);C(b,true);a=this.sliced=M(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)}});la=yb(pb,{type:"pie",isCartesian:false,pointClass:la,pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},getColor:function(){this.initialColor=this.chart.counters.color}, 163 | animate:function(){var a=this;u(a.data,function(b){var c=b.graphic;b=b.shapeArgs;var d=-cc/2;if(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},translate:function(){var a=0,b=this,c=-0.25,d=b.options,e=d.slicedOffset,f=e+d.borderWidth,g=d.center.concat([d.size,d.innerSize||0]),h=b.chart,j=h.plotWidth,l=h.plotHeight,n,J,D,aa=b.data,E=2*cc,da,w=qb(j,l),R,B,K,S=d.dataLabels.distance;g=mc(g,function(I,za){return(R=/%$/.test(I))?[j,l,w,w][za]* 164 | ja(I)/100:I});b.getX=function(I,za){D=sa.asin((I-g[1])/(g[2]/2+S));return g[0]+(za?-1:1)*nb(D)*(g[2]/2+S)};b.center=g;u(aa,function(I){a+=I.y});u(aa,function(I){da=a?I.y/a:0;n=X(c*E*1E3)/1E3;c+=da;J=X(c*E*1E3)/1E3;I.shapeType="arc";I.shapeArgs={x:g[0],y:g[1],r:g[2]/2,innerR:g[3]/2,start:n,end:J};D=(J+n)/2;I.slicedTranslation=mc([nb(D)*e+h.plotLeft,Db(D)*e+h.plotTop],X);B=nb(D)*g[2]/2;b.radiusY=K=Db(D)*g[2]/2;I.tooltipPos=[g[0]+B*0.7,g[1]+K*0.7];I.labelPos=[g[0]+B+nb(D)*S,g[1]+K+Db(D)*S,g[0]+B+nb(D)* 165 | f,g[1]+K+Db(D)*f,g[0]+B,g[1]+K,S<0?"center":D0,J=this.center[1],D=[[],[]],aa,E,da,w,R=2,B;if(d.enabled){pb.prototype.drawDataLabels.apply(this);u(a,function(gb){D[gb.labelPos[7]da){h=[].concat(I);h.sort(w);for(B=za;B--;)h[B].rank=B;for(B=za;B--;)I[B].rank>=da&&I.splice(B,1);za=I.length}for(B= 168 | 0;BE&&K[Da+1]!==null||aa