Enford %!s(int64=5) %!d(string=hai) anos
pai
achega
161ba67da3
Modificáronse 100 ficheiros con 35092 adicións e 0 borrados
  1. 31 0
      src/main/webapp/static/echarts-2.2.7/src/chart.js
  2. 953 0
      src/main/webapp/static/echarts-2.2.7/src/chart/bar.js
  3. 1707 0
      src/main/webapp/static/echarts-2.2.7/src/chart/base.js
  4. 1098 0
      src/main/webapp/static/echarts-2.2.7/src/chart/chord.js
  5. 303 0
      src/main/webapp/static/echarts-2.2.7/src/chart/eventRiver.js
  6. 977 0
      src/main/webapp/static/echarts-2.2.7/src/chart/force.js
  7. 778 0
      src/main/webapp/static/echarts-2.2.7/src/chart/funnel.js
  8. 634 0
      src/main/webapp/static/echarts-2.2.7/src/chart/gauge.js
  9. 108 0
      src/main/webapp/static/echarts-2.2.7/src/chart/heatmap.js
  10. 251 0
      src/main/webapp/static/echarts-2.2.7/src/chart/island.js
  11. 557 0
      src/main/webapp/static/echarts-2.2.7/src/chart/k.js
  12. 1058 0
      src/main/webapp/static/echarts-2.2.7/src/chart/line.js
  13. 1720 0
      src/main/webapp/static/echarts-2.2.7/src/chart/map.js
  14. 1141 0
      src/main/webapp/static/echarts-2.2.7/src/chart/pie.js
  15. 459 0
      src/main/webapp/static/echarts-2.2.7/src/chart/radar.js
  16. 477 0
      src/main/webapp/static/echarts-2.2.7/src/chart/scatter.js
  17. 630 0
      src/main/webapp/static/echarts-2.2.7/src/chart/tree.js
  18. 586 0
      src/main/webapp/static/echarts-2.2.7/src/chart/treemap.js
  19. 461 0
      src/main/webapp/static/echarts-2.2.7/src/chart/venn.js
  20. 249 0
      src/main/webapp/static/echarts-2.2.7/src/chart/wordCloud.js
  21. 32 0
      src/main/webapp/static/echarts-2.2.7/src/component.js
  22. 345 0
      src/main/webapp/static/echarts-2.2.7/src/component/axis.js
  23. 245 0
      src/main/webapp/static/echarts-2.2.7/src/component/base.js
  24. 804 0
      src/main/webapp/static/echarts-2.2.7/src/component/categoryAxis.js
  25. 1659 0
      src/main/webapp/static/echarts-2.2.7/src/component/dataRange.js
  26. 461 0
      src/main/webapp/static/echarts-2.2.7/src/component/dataView.js
  27. 1244 0
      src/main/webapp/static/echarts-2.2.7/src/component/dataZoom.js
  28. 184 0
      src/main/webapp/static/echarts-2.2.7/src/component/grid.js
  29. 972 0
      src/main/webapp/static/echarts-2.2.7/src/component/legend.js
  30. 979 0
      src/main/webapp/static/echarts-2.2.7/src/component/polar.js
  31. 358 0
      src/main/webapp/static/echarts-2.2.7/src/component/roamController.js
  32. 937 0
      src/main/webapp/static/echarts-2.2.7/src/component/timeline.js
  33. 311 0
      src/main/webapp/static/echarts-2.2.7/src/component/title.js
  34. 1244 0
      src/main/webapp/static/echarts-2.2.7/src/component/toolbox.js
  35. 1740 0
      src/main/webapp/static/echarts-2.2.7/src/component/tooltip.js
  36. 948 0
      src/main/webapp/static/echarts-2.2.7/src/component/valueAxis.js
  37. 233 0
      src/main/webapp/static/echarts-2.2.7/src/config.js
  38. 488 0
      src/main/webapp/static/echarts-2.2.7/src/data/Graph.js
  39. 247 0
      src/main/webapp/static/echarts-2.2.7/src/data/KDTree.js
  40. 241 0
      src/main/webapp/static/echarts-2.2.7/src/data/Tree.js
  41. 79 0
      src/main/webapp/static/echarts-2.2.7/src/data/quickSelect.js
  42. 1759 0
      src/main/webapp/static/echarts-2.2.7/src/echarts.js
  43. 172 0
      src/main/webapp/static/echarts-2.2.7/src/layer/heatmap.js
  44. 145 0
      src/main/webapp/static/echarts-2.2.7/src/layout/Chord.js
  45. 415 0
      src/main/webapp/static/echarts-2.2.7/src/layout/EdgeBundling.js
  46. 248 0
      src/main/webapp/static/echarts-2.2.7/src/layout/Force.js
  47. 90 0
      src/main/webapp/static/echarts-2.2.7/src/layout/Tree.js
  48. 202 0
      src/main/webapp/static/echarts-2.2.7/src/layout/TreeMap.js
  49. 692 0
      src/main/webapp/static/echarts-2.2.7/src/layout/WordCloud.js
  50. 123 0
      src/main/webapp/static/echarts-2.2.7/src/layout/WordCloudRectZero.js
  51. 262 0
      src/main/webapp/static/echarts-2.2.7/src/layout/eventRiver.js
  52. 781 0
      src/main/webapp/static/echarts-2.2.7/src/layout/forceLayoutWorker.js
  53. 13 0
      src/main/webapp/static/echarts-2.2.7/src/theme/default.js
  54. 297 0
      src/main/webapp/static/echarts-2.2.7/src/theme/infographic.js
  55. 257 0
      src/main/webapp/static/echarts-2.2.7/src/theme/macarons.js
  56. 79 0
      src/main/webapp/static/echarts-2.2.7/src/util/accMath.js
  57. 40 0
      src/main/webapp/static/echarts-2.2.7/src/util/coordinates.js
  58. 162 0
      src/main/webapp/static/echarts-2.2.7/src/util/date.js
  59. 624 0
      src/main/webapp/static/echarts-2.2.7/src/util/ecAnimation.js
  60. 115 0
      src/main/webapp/static/echarts-2.2.7/src/util/ecData.js
  61. 444 0
      src/main/webapp/static/echarts-2.2.7/src/util/ecEffect.js
  62. 81 0
      src/main/webapp/static/echarts-2.2.7/src/util/ecQuery.js
  63. 48 0
      src/main/webapp/static/echarts-2.2.7/src/util/kwargs.js
  64. 9 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoCoord.js
  65. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/an_hui_geo.js
  66. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ao_men_geo.js
  67. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/bei_jing_geo.js
  68. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/china_geo.js
  69. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/chong_qing_geo.js
  70. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/fu_jian_geo.js
  71. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/gan_su_geo.js
  72. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/guang_dong_geo.js
  73. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/guang_xi_geo.js
  74. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/gui_zhou_geo.js
  75. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hai_nan_geo.js
  76. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/he_bei_geo.js
  77. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/he_nan_geo.js
  78. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hei_long_jiang_geo.js
  79. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hu_bei_geo.js
  80. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hu_nan_geo.js
  81. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ji_lin_geo.js
  82. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/jiang_su_geo.js
  83. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/jiang_xi_geo.js
  84. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/liao_ning_geo.js
  85. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/nei_meng_gu_geo.js
  86. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ning_xia_geo.js
  87. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/qing_hai_geo.js
  88. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_dong_geo.js
  89. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_xi_1_geo.js
  90. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_xi_2_geo.js
  91. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shang_hai_geo.js
  92. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/si_chuan_geo.js
  93. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/tai_wan_geo.js
  94. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/tian_jin_geo.js
  95. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/world_geo.js
  96. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xi_zang_geo.js
  97. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xiang_gang_geo.js
  98. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xin_jiang_geo.js
  99. 3 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/yun_nan_geo.js
  100. 0 0
      src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/zhe_jiang_geo.js

+ 31 - 0
src/main/webapp/static/echarts-2.2.7/src/chart.js

@@ -0,0 +1,31 @@
+/**
+ * echart图表库
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ */
+define(function (/*require*/) {     //chart
+    var self = {};
+
+    var _chartLibrary = {};         //echart图表库
+
+    /**
+     * 定义图形实现
+     * @param {Object} name
+     * @param {Object} clazz 图形实现
+     */
+    self.define = function (name, clazz) {
+        _chartLibrary[name] = clazz;
+        return self;
+    };
+
+    /**
+     * 获取图形实现
+     * @param {Object} name
+     */
+    self.get = function (name) {
+        return _chartLibrary[name];
+    };
+
+    return self;
+});

+ 953 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/bar.js

@@ -0,0 +1,953 @@
+/**
+ * echarts图表类:柱形图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var RectangleShape = require('zrender/shape/Rectangle');
+    // 组件依赖
+    require('../component/axis');
+    require('../component/grid');
+    require('../component/dataZoom');
+    
+    var ecConfig = require('../config');
+    // 柱形图默认参数
+    ecConfig.bar = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        legendHoverLink: true,
+        // stack: null
+        xAxisIndex: 0,
+        yAxisIndex: 0,
+        barMinHeight: 0,          // 最小高度改为0
+        // barWidth: null,        // 默认自适应
+        barGap: '30%',            // 柱间距离,默认为柱形宽度的30%,可设固定值
+        barCategoryGap: '20%',    // 类目间柱形距离,默认为类目间距的20%,可设固定值
+        itemStyle: {
+            normal: {
+                // color: '各异',
+                barBorderColor: '#fff',       // 柱条边线
+                barBorderRadius: 0,           // 柱条边线圆角,单位px,默认为0
+                barBorderWidth: 0,            // 柱条边线线宽,单位px,默认为1
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            },
+            emphasis: {
+                // color: '各异',
+                barBorderColor: '#fff',            // 柱条边线
+                barBorderRadius: 0,                // 柱条边线圆角,单位px,默认为0
+                barBorderWidth: 0,                 // 柱条边线线宽,单位px,默认为1
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            }
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function Bar(ecTheme, messageCenter, zr, option, myChart){
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+        
+        this.refresh(option);
+    }
+    
+    Bar.prototype = {
+        type: ecConfig.CHART_TYPE_BAR,
+        /**
+         * 绘制图形
+         */
+        _buildShape: function () {
+            this._buildPosition();
+        },
+        
+        _buildNormal: function(seriesArray, maxDataLength, locationMap, xMarkMap, orient) {
+            var series = this.series;
+            // 确定类目轴和数值轴,同一方向随便找一个即可
+            var seriesIndex = locationMap[0][0];
+            var serie = series[seriesIndex];
+            var isHorizontal = orient == 'horizontal';
+            var xAxis = this.component.xAxis;
+            var yAxis = this.component.yAxis;
+            var categoryAxis = isHorizontal 
+                               ? xAxis.getAxis(serie.xAxisIndex)
+                               : yAxis.getAxis(serie.yAxisIndex);
+            var valueAxis;  // 数值轴各异
+
+            var size = this._mapSize(categoryAxis, locationMap);
+            var gap = size.gap;
+            var barGap = size.barGap;
+            var barWidthMap = size.barWidthMap;
+            var barMaxWidthMap = size.barMaxWidthMap;
+            var barWidth = size.barWidth;                   // 自适应宽度
+            var barMinHeightMap = size.barMinHeightMap;
+            var barHeight;
+            var curBarWidth;
+            var interval = size.interval;
+
+            var x;
+            var y;
+            var lastP; // 正向堆积处理
+            var baseP;
+            var lastN; // 负向堆积处理
+            var baseN;
+            var barShape;
+            var data;
+            var value;
+            var islandR = this.deepQuery([this.ecTheme, ecConfig], 'island.r');
+            for (var i = 0, l = maxDataLength; i < l; i++) {
+                if (categoryAxis.getNameByIndex(i) == null) {
+                    // 系列数据超出类目轴长度
+                    break;
+                }
+                isHorizontal
+                    ? (x = categoryAxis.getCoordByIndex(i) - gap / 2)
+                    : (y = categoryAxis.getCoordByIndex(i) + gap / 2);
+
+                for (var j = 0, k = locationMap.length; j < k; j++) {
+                    // 堆积数据用第一条valueAxis
+                    var yAxisIndex = series[locationMap[j][0]].yAxisIndex || 0;
+                    var xAxisIndex = series[locationMap[j][0]].xAxisIndex || 0;
+                    valueAxis = isHorizontal 
+                                ? yAxis.getAxis(yAxisIndex)
+                                : xAxis.getAxis(xAxisIndex);
+                    baseP = lastP = baseN = lastN = valueAxis.getCoord(0);
+                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
+                        seriesIndex = locationMap[j][m];
+                        serie = series[seriesIndex];
+                        data = serie.data[i];
+                        value = this.getDataFromOption(data, '-');
+                        xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
+                                                || {
+                                                    min: Number.POSITIVE_INFINITY,
+                                                    max: Number.NEGATIVE_INFINITY,
+                                                    sum: 0,
+                                                    counter: 0,
+                                                    average: 0
+                                                };
+                        curBarWidth = Math.min(
+                            barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
+                            barWidthMap[seriesIndex] || barWidth
+                        );
+                        if (value === '-') {
+                            // 空数据在做完后补充拖拽提示框
+                            continue;
+                        }
+                        if (value > 0) {
+                            // 正向堆积
+                            barHeight = m > 0 
+                                        ? valueAxis.getCoordSize(value)
+                                        : (
+                                            isHorizontal
+                                            ? (baseP - valueAxis.getCoord(value))
+                                            : (valueAxis.getCoord(value) - baseP)
+                                        );
+                            // 非堆积数据最小高度有效
+                            if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
+                                barHeight = barMinHeightMap[seriesIndex];
+                            }
+                            if (isHorizontal) {
+                                lastP -= barHeight;
+                                y = lastP;
+                            }
+                            else {
+                                x = lastP;
+                                lastP += barHeight;
+                            }
+                        }
+                        else if (value < 0){
+                            // 负向堆积
+                            barHeight = m > 0 
+                                        ? valueAxis.getCoordSize(value)
+                                        : (
+                                            isHorizontal
+                                            ? (valueAxis.getCoord(value) - baseN)
+                                            : (baseN - valueAxis.getCoord(value))
+                                        );
+                            // 非堆积数据最小高度有效
+                            if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
+                                barHeight = barMinHeightMap[seriesIndex];
+                            }
+                            if (isHorizontal) {
+                                y = lastN;
+                                lastN += barHeight;
+                            }
+                            else {
+                                lastN -= barHeight;
+                                x = lastN;
+                            }
+                        }
+                        else {
+                            // 0值
+                            barHeight = 0;
+                            // 最小高度无效
+                            if (isHorizontal) {
+                                lastP -= barHeight;
+                                y = lastP;
+                            }
+                            else {
+                                x = lastP;
+                                lastP += barHeight;
+                            }
+                        }
+                        xMarkMap[seriesIndex][i] = isHorizontal
+                                                   ? (x + curBarWidth / 2) 
+                                                   : (y - curBarWidth / 2);
+                        if (xMarkMap[seriesIndex].min > value) {
+                            xMarkMap[seriesIndex].min = value;
+                            if (isHorizontal) {
+                                xMarkMap[seriesIndex].minY = y;
+                                xMarkMap[seriesIndex].minX = xMarkMap[seriesIndex][i];
+                            }
+                            else {
+                                xMarkMap[seriesIndex].minX = x + barHeight;
+                                xMarkMap[seriesIndex].minY = xMarkMap[seriesIndex][i];
+                            }
+                        }
+                        if (xMarkMap[seriesIndex].max < value) {
+                            xMarkMap[seriesIndex].max = value;
+                            if (isHorizontal) {
+                                xMarkMap[seriesIndex].maxY = y;
+                                xMarkMap[seriesIndex].maxX = xMarkMap[seriesIndex][i];
+                            }
+                            else {
+                                xMarkMap[seriesIndex].maxX = x + barHeight;
+                                xMarkMap[seriesIndex].maxY = xMarkMap[seriesIndex][i];
+                            }
+                            
+                        }
+                        xMarkMap[seriesIndex].sum += value;
+                        xMarkMap[seriesIndex].counter++;
+                        
+                        if (i % interval === 0) {
+                            barShape = this._getBarItem(
+                                seriesIndex, i,
+                                categoryAxis.getNameByIndex(i),
+                                x,
+                                y - (isHorizontal ? 0 : curBarWidth),
+                                isHorizontal ? curBarWidth : barHeight,
+                                isHorizontal ? barHeight : curBarWidth,
+                                isHorizontal ? 'vertical' : 'horizontal'
+                            );
+                            this.shapeList.push(new RectangleShape(barShape));
+                        }
+                    }
+
+                    // 补充空数据的拖拽提示框
+                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
+                        seriesIndex = locationMap[j][m];
+                        serie = series[seriesIndex];
+                        data = serie.data[i];
+                        value = this.getDataFromOption(data, '-');
+                        curBarWidth = Math.min(
+                            barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
+                            barWidthMap[seriesIndex] || barWidth
+                        );
+                        if (value != '-') {
+                            // 只关心空数据
+                            continue;
+                        }
+
+                        if (this.deepQuery([data, serie, this.option], 'calculable')) {
+                            if (isHorizontal) {
+                                lastP -= islandR;
+                                y = lastP;
+                            }
+                            else {
+                                x = lastP;
+                                lastP += islandR;
+                            }
+                            
+                            barShape = this._getBarItem(
+                                seriesIndex, i,
+                                categoryAxis.getNameByIndex(i),
+                                x,
+                                y - (isHorizontal ? 0 : curBarWidth),
+                                isHorizontal ? curBarWidth : islandR,
+                                isHorizontal ? islandR : curBarWidth,
+                                isHorizontal ? 'vertical' : 'horizontal'
+                            );
+                            barShape.hoverable = false;
+                            barShape.draggable = false;
+                            barShape.style.lineWidth = 1;
+                            barShape.style.brushType = 'stroke';
+                            barShape.style.strokeColor = serie.calculableHolderColor
+                                                         || this.ecTheme.calculableHolderColor
+                                                         || ecConfig.calculableHolderColor;
+
+                            this.shapeList.push(new RectangleShape(barShape));
+                        }
+                    }
+                    isHorizontal
+                        ? (x += (curBarWidth + barGap))
+                        : (y -= (curBarWidth + barGap));
+                }
+            }
+            
+            this._calculMarkMapXY(xMarkMap, locationMap, isHorizontal ? 'y' : 'x');
+        },
+        /**
+         * 构建类目轴为水平方向的柱形图系列
+         */
+        _buildHorizontal: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
+            return this._buildNormal(
+                seriesArray, maxDataLength, locationMap, xMarkMap, 'horizontal'
+            );
+        },
+
+        /**
+         * 构建类目轴为垂直方向的柱形图系列
+         */
+        _buildVertical: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
+            return this._buildNormal(
+                seriesArray, maxDataLength, locationMap, xMarkMap, 'vertical'
+            );
+        },
+        
+        /**
+         * 构建双数值轴柱形图
+         */
+        _buildOther: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
+            var series = this.series;
+            
+            for (var j = 0, k = locationMap.length; j < k; j++) {
+                for (var m = 0, n = locationMap[j].length; m < n; m++) {
+                    var seriesIndex = locationMap[j][m];
+                    var serie = series[seriesIndex];
+                    var xAxisIndex = serie.xAxisIndex || 0;
+                    var xAxis = this.component.xAxis.getAxis(xAxisIndex);
+                    var baseX = xAxis.getCoord(0);
+                    var yAxisIndex = serie.yAxisIndex || 0;
+                    var yAxis = this.component.yAxis.getAxis(yAxisIndex);
+                    var baseY = yAxis.getCoord(0);
+                    
+                    xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
+                                            || {
+                                                min0: Number.POSITIVE_INFINITY,
+                                                min1: Number.POSITIVE_INFINITY,
+                                                max0: Number.NEGATIVE_INFINITY,
+                                                max1: Number.NEGATIVE_INFINITY,
+                                                sum0: 0,
+                                                sum1: 0,
+                                                counter0: 0,
+                                                counter1: 0,
+                                                average0: 0,
+                                                average1: 0
+                                            };
+
+                    for (var i = 0, l = serie.data.length; i < l; i++) {
+                        var data = serie.data[i];
+                        var value = this.getDataFromOption(data, '-');
+                        if (!(value instanceof Array)) {
+                            continue;
+                        }
+                        
+                        var x = xAxis.getCoord(value[0]);
+                        var y = yAxis.getCoord(value[1]);
+                        
+                        var queryTarget = [data, serie];
+                        var barWidth = this.deepQuery(queryTarget, 'barWidth') || 10; // 默认柱形
+                        var barHeight = this.deepQuery(queryTarget, 'barHeight');
+                        var orient;
+                        var barShape;
+                        
+                        if (barHeight != null) {
+                            // 条形图
+                            orient = 'horizontal';
+                            
+                            if (value[0] > 0) {
+                                // 正向
+                                barWidth = x - baseX;
+                                x -= barWidth;
+                            }
+                            else if (value[0] < 0){
+                                // 负向
+                                barWidth = baseX - x;
+                            }
+                            else {
+                                // 0值
+                                barWidth = 0;
+                            }
+                            
+                            barShape = this._getBarItem(
+                                seriesIndex, i,
+                                value[0],
+                                x, 
+                                y - barHeight / 2,
+                                barWidth,
+                                barHeight,
+                                orient
+                            );
+                        }
+                        else {
+                            // 柱形
+                            orient = 'vertical';
+                            
+                            if (value[1] > 0) {
+                            // 正向
+                                barHeight = baseY - y;
+                            }
+                            else if (value[1] < 0){
+                                // 负向
+                                barHeight = y - baseY;
+                                y -= barHeight;
+                            }
+                            else {
+                                // 0值
+                                barHeight = 0;
+                            }
+                            barShape = this._getBarItem(
+                                seriesIndex, i,
+                                value[0],
+                                x - barWidth / 2, 
+                                y,
+                                barWidth,
+                                barHeight,
+                                orient
+                            );
+                        }
+                        this.shapeList.push(new RectangleShape(barShape));
+                        
+                        
+                        x = xAxis.getCoord(value[0]);
+                        y = yAxis.getCoord(value[1]);
+                        if (xMarkMap[seriesIndex].min0 > value[0]) {
+                            xMarkMap[seriesIndex].min0 = value[0];
+                            xMarkMap[seriesIndex].minY0 = y;
+                            xMarkMap[seriesIndex].minX0 = x;
+                        }
+                        if (xMarkMap[seriesIndex].max0 < value[0]) {
+                            xMarkMap[seriesIndex].max0 = value[0];
+                            xMarkMap[seriesIndex].maxY0 = y;
+                            xMarkMap[seriesIndex].maxX0 = x;
+                        }
+                        xMarkMap[seriesIndex].sum0 += value[0];
+                        xMarkMap[seriesIndex].counter0++;
+                        
+                        if (xMarkMap[seriesIndex].min1 > value[1]) {
+                            xMarkMap[seriesIndex].min1 = value[1];
+                            xMarkMap[seriesIndex].minY1 = y;
+                            xMarkMap[seriesIndex].minX1 = x;
+                        }
+                        if (xMarkMap[seriesIndex].max1 < value[1]) {
+                            xMarkMap[seriesIndex].max1 = value[1];
+                            xMarkMap[seriesIndex].maxY1 = y;
+                            xMarkMap[seriesIndex].maxX1 = x;
+                        }
+                        xMarkMap[seriesIndex].sum1 += value[1];
+                        xMarkMap[seriesIndex].counter1++;
+                    }
+                }
+            }
+            
+            this._calculMarkMapXY(xMarkMap, locationMap, 'xy');
+        },
+        
+        /**
+         * 我真是自找麻烦啊,为啥要允许系列级个性化最小宽度和高度啊!!!
+         * @param {CategoryAxis} categoryAxis 类目坐标轴,需要知道类目间隔大小
+         * @param {Array} locationMap 整形数据的系列索引
+         */
+        _mapSize: function (categoryAxis, locationMap, ignoreUserDefined) {
+            var res = this._findSpecialBarSzie(locationMap, ignoreUserDefined);
+            var barWidthMap = res.barWidthMap;
+            var barMaxWidthMap = res.barMaxWidthMap;
+            var barMinHeightMap = res.barMinHeightMap;
+            var sBarWidthCounter = res.sBarWidthCounter;    // 用户指定
+            var sBarWidthTotal = res.sBarWidthTotal;        // 用户指定
+            var barGap = res.barGap;
+            var barCategoryGap = res.barCategoryGap;
+            
+            var gap;
+            var barWidth;
+            var interval = 1;
+            if (locationMap.length != sBarWidthCounter) {
+                // 至少存在一个自适应宽度的柱形图
+                if (!ignoreUserDefined) {
+                    gap = typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/)
+                          // 百分比
+                          ? ((categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2) - 0)
+                          // 数值
+                          : (categoryAxis.getGap() - barCategoryGap);
+                    if (typeof barGap === 'string' && barGap.match(/%$/)) {
+                        barGap = parseFloat(barGap) / 100;
+                        barWidth = +(
+                            (gap - sBarWidthTotal) / (
+                                (locationMap.length - 1) * barGap + locationMap.length - sBarWidthCounter
+                            )
+                        ).toFixed(2);
+                        barGap = barWidth * barGap;
+                    }
+                    else {
+                        barGap = parseFloat(barGap);
+                        barWidth = +(
+                            (gap - sBarWidthTotal - barGap * (locationMap.length - 1)) / (
+                                locationMap.length - sBarWidthCounter
+                            )
+                        ).toFixed(2);
+                    }
+                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
+                    if (barWidth <= 0) {
+                        return this._mapSize(categoryAxis, locationMap, true);
+                    }
+                }
+                else {
+                    // 忽略用户定义的宽度设定
+                    gap = categoryAxis.getGap();
+                    barGap = 0;
+                    barWidth = +(gap / locationMap.length).toFixed(2);
+                    // 已经忽略用户定义的宽度设定依然还无法满足显示,只能硬来了;
+                    if (barWidth <= 0) {
+                        interval = Math.floor(locationMap.length / gap);
+                        barWidth = 1;
+                    }
+                }
+            }
+            else {
+                // 全是自定义宽度,barGap无效,系列间隔决定barGap
+                gap = sBarWidthCounter > 1
+                      ? (typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/))
+                          // 百分比
+                          ? +(categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2)
+                          // 数值
+                          : (categoryAxis.getGap() - barCategoryGap)
+                      // 只有一个
+                      : sBarWidthTotal;
+                barWidth = 0;
+                barGap = sBarWidthCounter > 1 
+                         ? +((gap - sBarWidthTotal) / (sBarWidthCounter - 1)).toFixed(2)
+                         : 0;
+                if (barGap < 0) {
+                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
+                    return this._mapSize(categoryAxis, locationMap, true);
+                }
+            }
+            
+            // 检查是否满足barMaxWidthMap
+            
+            return this._recheckBarMaxWidth(
+                locationMap,
+                barWidthMap, barMaxWidthMap, barMinHeightMap,
+                gap,   // 总宽度
+                barWidth, barGap, interval
+            );
+        },
+        
+        /**
+         * 计算堆积下用户特殊指定的各种size 
+         */
+        _findSpecialBarSzie: function(locationMap, ignoreUserDefined) {
+            var series = this.series;
+            var barWidthMap = {};
+            var barMaxWidthMap = {};
+            var barMinHeightMap = {};
+            var sBarWidth;              // 用户指定
+            var sBarMaxWidth;           // 用户指定
+            var sBarWidthCounter = 0;   // 用户指定
+            var sBarWidthTotal = 0;     // 用户指定
+            var barGap;
+            var barCategoryGap;
+            for (var j = 0, k = locationMap.length; j < k; j++) {
+                var hasFound = {
+                    barWidth: false,
+                    barMaxWidth: false
+                };
+                for (var m = 0, n = locationMap[j].length; m < n; m++) {
+                    var seriesIndex = locationMap[j][m];
+                    var queryTarget = series[seriesIndex];
+                    if (!ignoreUserDefined) {
+                        if (!hasFound.barWidth) {
+                            sBarWidth = this.query(queryTarget, 'barWidth');
+                            if (sBarWidth != null) {
+                                // 同一堆积第一个生效barWidth
+                                barWidthMap[seriesIndex] = sBarWidth;
+                                sBarWidthTotal += sBarWidth;
+                                sBarWidthCounter++;
+                                hasFound.barWidth = true;
+                                // 复位前面同一堆积但没被定义的
+                                for (var ii = 0, ll = m; ii < ll; ii++) {
+                                    var pSeriesIndex = locationMap[j][ii];
+                                    barWidthMap[pSeriesIndex] = sBarWidth;
+                                }
+                            }
+                        }
+                        else {
+                            barWidthMap[seriesIndex] = sBarWidth;   // 用找到的一个
+                        }
+                        
+                        if (!hasFound.barMaxWidth) {
+                            sBarMaxWidth = this.query(queryTarget, 'barMaxWidth');
+                            if (sBarMaxWidth != null) {
+                                // 同一堆积第一个生效barMaxWidth
+                                barMaxWidthMap[seriesIndex] = sBarMaxWidth;
+                                hasFound.barMaxWidth = true;
+                                // 复位前面同一堆积但没被定义的
+                                for (var ii = 0, ll = m; ii < ll; ii++) {
+                                    var pSeriesIndex = locationMap[j][ii];
+                                    barMaxWidthMap[pSeriesIndex] = sBarMaxWidth;
+                                }
+                            }
+                        }
+                        else {
+                            barMaxWidthMap[seriesIndex] = sBarMaxWidth;   // 用找到的一个
+                        }
+                    }
+
+                    barMinHeightMap[seriesIndex] = this.query(queryTarget, 'barMinHeight');
+                    barGap = barGap != null ? barGap : this.query(queryTarget, 'barGap');
+                    barCategoryGap = barCategoryGap != null 
+                                     ? barCategoryGap : this.query(queryTarget, 'barCategoryGap');
+                }
+            }
+            
+            return {
+                barWidthMap: barWidthMap,
+                barMaxWidthMap: barMaxWidthMap,
+                barMinHeightMap: barMinHeightMap,
+                sBarWidth: sBarWidth,
+                sBarMaxWidth: sBarMaxWidth,
+                sBarWidthCounter: sBarWidthCounter,
+                sBarWidthTotal: sBarWidthTotal,
+                barGap: barGap,
+                barCategoryGap: barCategoryGap
+            };
+        },
+        
+        /**
+         * 检查是否满足barMaxWidthMap 
+         */
+        _recheckBarMaxWidth: function(
+                locationMap,
+                barWidthMap, barMaxWidthMap, barMinHeightMap,
+                gap,   // 总宽度
+                barWidth, barGap, interval
+        ) {
+            for (var j = 0, k = locationMap.length; j < k; j++) {
+                var seriesIndex = locationMap[j][0];
+                if (barMaxWidthMap[seriesIndex] && barMaxWidthMap[seriesIndex] < barWidth) {
+                    // 不满足最大宽度
+                    gap -= barWidth - barMaxWidthMap[seriesIndex]; // 总宽度减少
+                }
+            }
+            
+            return {
+                barWidthMap: barWidthMap,
+                barMaxWidthMap: barMaxWidthMap,
+                barMinHeightMap: barMinHeightMap ,
+                gap: gap,   // 总宽度
+                barWidth: barWidth,
+                barGap: barGap,
+                interval: interval
+            };
+        },
+        
+        /**
+         * 生成最终图形数据
+         */
+        _getBarItem: function (seriesIndex, dataIndex, name, x, y, width, height, orient) {
+            var series = this.series;
+            var barShape;
+            var serie = series[seriesIndex];
+            var data = serie.data[dataIndex];
+            // 多级控制
+            var defaultColor = this._sIndex2ColorMap[seriesIndex];
+            var queryTarget = [data, serie];
+            
+            var normal = this.deepMerge(queryTarget, 'itemStyle.normal');
+            var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis');
+            var normalBorderWidth = normal.barBorderWidth;
+            
+            barShape = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                clickable: this.deepQuery(queryTarget, 'clickable'),
+                style: {
+                    x: x,
+                    y: y,
+                    width: width,
+                    height: height,
+                    brushType: 'both',
+                    color: this.getItemStyleColor(
+                        this.deepQuery(queryTarget, 'itemStyle.normal.color') || defaultColor,
+                        seriesIndex, dataIndex, data
+                    ),
+                    radius: normal.barBorderRadius,
+                    lineWidth: normalBorderWidth,
+                    strokeColor: normal.barBorderColor
+                },
+                highlightStyle: {
+                    color: this.getItemStyleColor(
+                        this.deepQuery(queryTarget, 'itemStyle.emphasis.color'),
+                        seriesIndex, dataIndex, data
+                    ),
+                    radius: emphasis.barBorderRadius,
+                    lineWidth: emphasis.barBorderWidth,
+                    strokeColor: emphasis.barBorderColor
+                },
+                _orient: orient
+            };
+            var barShapeStyle = barShape.style;
+            barShape.highlightStyle.color = barShape.highlightStyle.color
+                            || (typeof barShapeStyle.color === 'string'
+                                ? zrColor.lift(barShapeStyle.color, -0.3)
+                                : barShapeStyle.color
+                               );
+            //亚像素优化
+            barShapeStyle.x = Math.floor(barShapeStyle.x);
+            barShapeStyle.y = Math.floor(barShapeStyle.y);
+            barShapeStyle.height = Math.ceil(barShapeStyle.height);
+            barShapeStyle.width = Math.ceil(barShapeStyle.width);
+            // 考虑线宽的显示优化
+            if (normalBorderWidth > 0
+                && barShapeStyle.height > normalBorderWidth
+                && barShapeStyle.width > normalBorderWidth
+            ) {
+                barShapeStyle.y += normalBorderWidth / 2;
+                barShapeStyle.height -= normalBorderWidth;
+                barShapeStyle.x += normalBorderWidth / 2;
+                barShapeStyle.width -= normalBorderWidth;
+            }
+            else {
+                // 太小了或者线宽小于0,废了边线
+                barShapeStyle.brushType = 'fill';
+            }
+            
+            barShape.highlightStyle.textColor = barShape.highlightStyle.color;
+            
+            barShape = this.addLabel(barShape, serie, data, name, orient);
+            var barShapeStyleList = [                    // normal emphasis都需要检查
+                barShapeStyle,
+                barShape.highlightStyle
+            ];
+            for (var i = 0, l = barShapeStyleList.length; i < l; i++) {
+                var textPosition = barShapeStyleList[i].textPosition;
+                if (textPosition === 'insideLeft'
+                    || textPosition === 'insideRight'
+                    || textPosition === 'insideTop'
+                    || textPosition === 'insideBottom'
+                ) {
+                    var gap = 5;
+                    switch (textPosition) {
+                        case 'insideLeft':
+                            barShapeStyleList[i].textX = barShapeStyle.x + gap;
+                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
+                            barShapeStyleList[i].textAlign = 'left';
+                            barShapeStyleList[i].textBaseline = 'middle';
+                            break;
+                        case 'insideRight':
+                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width - gap;
+                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
+                            barShapeStyleList[i].textAlign = 'right';
+                            barShapeStyleList[i].textBaseline = 'middle';
+                            break;
+                        case 'insideTop':
+                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
+                            barShapeStyleList[i].textY = barShapeStyle.y + gap / 2;
+                            barShapeStyleList[i].textAlign = 'center';
+                            barShapeStyleList[i].textBaseline = 'top';
+                            break;
+                        case 'insideBottom':
+                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
+                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height - gap / 2;
+                            barShapeStyleList[i].textAlign = 'center';
+                            barShapeStyleList[i].textBaseline = 'bottom';
+                            break;
+                    }
+                    barShapeStyleList[i].textPosition = 'specific';
+                    barShapeStyleList[i].textColor = barShapeStyleList[i].textColor || '#fff';
+                }
+            }
+            
+
+            if (this.deepQuery([data, serie, this.option],'calculable')) {
+                this.setCalculable(barShape);
+                barShape.draggable = true;
+            }
+
+            ecData.pack(
+                barShape,
+                series[seriesIndex], seriesIndex,
+                series[seriesIndex].data[dataIndex], dataIndex,
+                name
+            );
+
+            return barShape;
+        },
+
+        // 位置转换
+        getMarkCoord: function (seriesIndex, mpData) {
+            var serie = this.series[seriesIndex];
+            var xMarkMap = this.xMarkMap[seriesIndex];
+            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
+            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
+            var dataIndex;
+            var pos;
+            if (mpData.type
+                && (mpData.type === 'max' || mpData.type === 'min' || mpData.type === 'average')
+            ) {
+                // 特殊值内置支持
+                var valueIndex = mpData.valueIndex != null 
+                                 ? mpData.valueIndex 
+                                 : xMarkMap.maxX0 != null 
+                                   ? '1' : '';
+                pos = [
+                    xMarkMap[mpData.type + 'X' + valueIndex],
+                    xMarkMap[mpData.type + 'Y' + valueIndex],
+                    xMarkMap[mpData.type + 'Line' + valueIndex],
+                    xMarkMap[mpData.type + valueIndex]
+                ];
+            }
+            else if (xMarkMap.isHorizontal) {
+                // 横向
+                dataIndex = typeof mpData.xAxis === 'string' && xAxis.getIndexByName
+                            ? xAxis.getIndexByName(mpData.xAxis)
+                            : (mpData.xAxis || 0);
+                
+                var x = xMarkMap[dataIndex];
+                x = x != null
+                    ? x 
+                    : typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
+                      ? xAxis.getCoordByIndex(mpData.xAxis || 0)
+                      : xAxis.getCoord(mpData.xAxis || 0);
+                
+                pos = [x, yAxis.getCoord(mpData.yAxis || 0)];
+            }
+            else {
+                // 纵向
+                dataIndex = typeof mpData.yAxis === 'string' && yAxis.getIndexByName
+                            ? yAxis.getIndexByName(mpData.yAxis)
+                            : (mpData.yAxis || 0);
+                
+                var y = xMarkMap[dataIndex];
+                y = y != null
+                    ? y
+                    : typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
+                      ? yAxis.getCoordByIndex(mpData.yAxis || 0)
+                      : yAxis.getCoord(mpData.yAxis || 0);
+                
+                pos = [xAxis.getCoord(mpData.xAxis || 0), y];
+            }
+            
+            return pos;
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        },
+        
+        /**
+         * 动态数据增加动画 
+         */
+        addDataAnimation: function (params, done) {
+            var series = this.series;
+            var aniMap = {}; // seriesIndex索引参数
+            for (var i = 0, l = params.length; i < l; i++) {
+                aniMap[params[i][0]] = params[i];
+            }
+            var x;
+            var dx;
+            var y;
+            var dy;
+            var serie;
+            var seriesIndex;
+            var dataIndex;
+
+            var aniCount = 0;
+            function animationDone() {
+                aniCount--;
+                if (aniCount === 0) {
+                    done && done();
+                }
+            }
+            for (var i = this.shapeList.length - 1; i >= 0; i--) {
+                seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
+                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
+                    // 有数据删除才有移动的动画
+                    if (this.shapeList[i].type === 'rectangle') {
+                        // 主动画
+                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
+                        serie = series[seriesIndex];
+                        if (aniMap[seriesIndex][2] && dataIndex === serie.data.length - 1) {
+                            // 队头加入删除末尾
+                            this.zr.delShape(this.shapeList[i].id);
+                            continue;
+                        }
+                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
+                            // 队尾加入删除头部
+                            this.zr.delShape(this.shapeList[i].id);
+                            continue;
+                        }
+                        if (this.shapeList[i]._orient === 'horizontal') {
+                            // 条形图
+                            dy = this.component.yAxis.getAxis(serie.yAxisIndex || 0).getGap();
+                            y = aniMap[seriesIndex][2] ? -dy : dy;
+                            x = 0;
+                        }
+                        else {
+                            // 柱形图
+                            dx = this.component.xAxis.getAxis(serie.xAxisIndex || 0).getGap();
+                            x = aniMap[seriesIndex][2] ? dx : -dx;
+                            y = 0;
+                        }
+                        this.shapeList[i].position = [0, 0];
+
+                        aniCount++;
+                        this.zr.animate(this.shapeList[i].id, '')
+                            .when(
+                                this.query(this.option, 'animationDurationUpdate'),
+                                { position: [x, y] }
+                            )
+                            .done(animationDone)
+                            .start();
+                    }
+                }
+            }
+            
+            // 没有动画
+            if (!aniCount) {
+                done && done();
+            }
+        }
+    };
+    
+    zrUtil.inherits(Bar, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('bar', Bar);
+    
+    return Bar;
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1707 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/base.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1098 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/chord.js


+ 303 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/eventRiver.js

@@ -0,0 +1,303 @@
+/**
+ * echarts图表类:事件河流图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author clmtulip  (车丽美, clmtulip@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+
+    var eventRiverLayout = require('../layout/eventRiver');
+
+    // 图形依赖
+    var PolygonShape = require('zrender/shape/Polygon');
+
+    // 组件依赖
+    require('../component/axis');
+    require('../component/grid');
+    require('../component/dataZoom');
+
+    var ecConfig = require('../config');
+    // 事件河流图默认参数
+    ecConfig.eventRiver = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        legendHoverLink: true,
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                borderColor: 'rgba(0,0,0,0)',
+                borderWidth: 1,
+                label: {
+                    show: true,
+                    position: 'inside',     // 可选为'left'|'right'|'top'|'bottom'
+                    formatter: '{b}'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            },
+            emphasis: {
+                // color: 各异,
+                borderColor: 'rgba(0,0,0,0)',
+                borderWidth: 1,
+                label: {
+                    show: true
+                }
+            }
+        }
+    };
+    
+    var ecData = require('../util/ecData');
+    var ecDate = require('../util/date');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 数据
+     * @param {Object} component 组件
+     */
+     function EventRiver(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+         
+         var self = this;
+         self._ondragend = function () {
+             self.isDragend = true;
+         };
+         this.refresh(option);
+     }
+
+     EventRiver.prototype = {
+         type: ecConfig.CHART_TYPE_EVENTRIVER,
+         
+         _buildShape: function() {
+             var series = this.series;
+             this.selectedMap = {};
+             
+             // 数据预处理
+             this._dataPreprocessing();
+            
+             var legend = this.component.legend;
+             // 调用布局算法计算事件在Y轴上的位置
+             var eventRiverSeries = [];
+             for (var i = 0; i < series.length; i++) {
+                 if (series[i].type === this.type) {
+                     series[i] = this.reformOption(series[i]);
+                     this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                     var serieName = series[i].name || '';
+                     // 系列图例开关
+                     this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
+                     if (!this.selectedMap[serieName]) {
+                         continue;
+                     }
+                     this.buildMark(i);
+                     eventRiverSeries.push(this.series[i]);
+                 }
+             }
+             
+             eventRiverLayout(
+                 eventRiverSeries,
+                 this._intervalX,
+                 this.component.grid.getArea()
+             );
+             
+             // 绘制事件河
+             this._drawEventRiver();
+             
+             this.addShapeList();
+         },
+
+         /**
+          * 处理数据
+          */
+         _dataPreprocessing: function() {
+             // 将年月日的时间转化为平面坐标
+             var series = this.series;
+             var xAxis;
+             var evolutionList;
+             for (var i = 0, iLen = series.length; i < iLen; i++) {
+                 if (series[i].type === this.type) {
+                     xAxis = this.component.xAxis.getAxis(series[i].xAxisIndex || 0);
+                     for (var j = 0, jLen = series[i].data.length; j < jLen; j++) {
+                         evolutionList = series[i].data[j].evolution;
+                         for (var k = 0, kLen = evolutionList.length; k < kLen; k++) {
+                             evolutionList[k].timeScale = xAxis.getCoord(
+                                 ecDate.getNewDate(evolutionList[k].time) - 0
+                             );
+                             // evolutionList[k].valueScale = evolutionList[k].value;
+                             // modified by limei.che, to normalize the value range
+                             evolutionList[k].valueScale = Math.pow(evolutionList[k].value, 0.8);
+                         }
+                     }
+                 }
+             }
+             // 尾迹长度
+             this._intervalX = Math.round(this.component.grid.getWidth() / 40);
+         },
+
+         /**
+          * 绘制事件河流
+          */
+         _drawEventRiver: function(){
+             var series = this.series;
+             for (var i = 0; i < series.length; i++) {
+                 var serieName = series[i].name || '';
+                 if (series[i].type === this.type && this.selectedMap[serieName]) {
+                     for (var j = 0; j < series[i].data.length; j++) {
+                         this._drawEventBubble(series[i].data[j], i, j);
+                     }
+                 }
+             }
+         },
+
+         /**
+          * 绘制气泡图
+          */
+         _drawEventBubble: function(oneEvent, seriesIndex, dataIndex) {
+             var series = this.series;
+             var serie = series[seriesIndex];
+             var serieName = serie.name || '';
+             var data = serie.data[dataIndex];
+             var queryTarget = [data, serie];
+
+             var legend = this.component.legend;
+             var defaultColor = legend ? legend.getColor(serieName) : this.zr.getColor(seriesIndex);
+             
+             // 多级控制
+             var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
+             var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
+             var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data)
+                               || defaultColor;
+             
+             var emphasisColor = this.getItemStyleColor(
+                                     emphasis.color, seriesIndex, dataIndex, data
+                                 )
+                                 || (typeof normalColor === 'string'
+                                     ? zrColor.lift(normalColor, -0.2)
+                                     : normalColor
+                                 );
+            
+             var pts = this._calculateControlPoints(oneEvent);
+             
+             var eventBubbleShape = {
+                 zlevel: serie.zlevel,
+                 z: serie.z,
+                 clickable: this.deepQuery(queryTarget, 'clickable'),
+                 style: {
+                     pointList: pts,
+                     smooth: 'spline',
+                     brushType: 'both',
+                     lineJoin : 'round',
+                     color: normalColor,
+                     lineWidth: normal.borderWidth,
+                     strokeColor: normal.borderColor
+                 },
+                 highlightStyle:{
+                     color: emphasisColor,
+                     lineWidth: emphasis.borderWidth,
+                     strokeColor: emphasis.borderColor
+                 },
+                 draggable: 'vertical',
+                 ondragend : this._ondragend
+             };
+             
+             eventBubbleShape = new PolygonShape(eventBubbleShape);
+             
+             this.addLabel(eventBubbleShape, serie, data, oneEvent.name);
+             ecData.pack(
+                 eventBubbleShape,
+                 series[seriesIndex], seriesIndex,
+                 series[seriesIndex].data[dataIndex], dataIndex,
+                 series[seriesIndex].data[dataIndex].name
+             );
+             this.shapeList.push(eventBubbleShape);
+         },
+
+         /**
+         * 根据时间-热度,计算气泡形状的控制点
+         */
+         _calculateControlPoints: function(oneEvent) {
+             var intervalX = this._intervalX;
+             var posY = oneEvent.y;
+             
+             var evolution = oneEvent.evolution;
+             var n = evolution.length;
+             if (n < 1) {
+                 return;
+             }
+             
+             var time = [];
+             var value = [];
+             for (var i = 0; i < n; i++) {
+                 time.push(evolution[i].timeScale);
+                 value.push(evolution[i].valueScale);
+             }
+             
+             var pts = [];
+             
+             // 从左向右绘制气泡的上半部分控制点
+             // 第一个矩形的左端点
+             pts.push([time[0], posY]);
+             // 从一个矩形 到 倒数第二个矩形 上半部分的中点
+             var i = 0;
+             for (i = 0; i < n - 1; i++) {
+                 pts.push([(time[i] + time[i + 1]) / 2.0, value[i] / -2.0 + posY]);
+             }
+             // 最后一个矩形上半部分的中点
+             pts.push([(time[i] + (time[i] + intervalX)) / 2.0, value[i] / -2.0 + posY]);
+             // 最后一个矩形的右端点
+             pts.push([time[i] + intervalX, posY]);
+
+             // 从右向左绘制气泡的下半部分控制点
+             // 最后一个矩形下半部分的中点
+             pts.push([(time[i] + (time[i] + intervalX)) / 2.0, value[i] / 2.0 + posY]);
+             // 从倒数第二个矩形 到 一个矩形 下半部分的中点,由于polygon是闭合的,故不需要再加入左端点
+             for (i = n - 1; i > 0; i--) {
+                 pts.push([(time[i] + time[i - 1]) / 2.0, value[i - 1] / 2.0 + posY]);
+             }
+             
+             return pts;
+         },
+         
+        /**
+         * 数据项被拖拽出去
+         */
+        ondragend : function (param, status) {
+            if (!this.isDragend || !param.target) {
+                // 没有在当前实例上发生拖拽行为则直接返回
+                return;
+            }
+
+            // 别status = {}赋值啊!!
+            status.dragOut = true;
+            status.dragIn = true;
+            status.needRefresh = false; // 会有消息触发fresh,不用再刷一遍
+            // 处理完拖拽事件后复位
+            this.isDragend = false;
+        },
+
+         /**
+         * 刷新
+         */
+         refresh: function(newOption) {
+             if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+
+            this.backupShapeList();
+            this._buildShape();
+         }
+     };
+
+     zrUtil.inherits(EventRiver, ChartBase);
+
+     // 图表注册
+     require('../chart').define('eventRiver', EventRiver);
+
+     return EventRiver;
+});

+ 977 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/force.js

@@ -0,0 +1,977 @@
+/**
+ * echarts图表类:力导向图
+ *
+ * @author pissang (https://github.com/pissang/)
+ *
+ */
+
+define(function (require) {
+    'use strict';
+    
+    var ChartBase = require('./base');
+
+    var Graph = require('../data/Graph');
+    var ForceLayout = require('../layout/Force');
+    
+    // 图形依赖
+    var LineShape = require('zrender/shape/Line');
+    var BezierCurveShape = require('zrender/shape/BezierCurve');
+    var ImageShape = require('zrender/shape/Image');
+    var IconShape = require('../util/shape/Icon');
+
+    var ecConfig = require('../config');
+    // 力导向布局图默认参数
+    ecConfig.force = {
+        zlevel: 1,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        // 布局中心
+        center: ['50%', '50%'],
+
+        // 布局大小
+        size: '100%',
+
+        // 防止节点和节点,节点和边之间的重叠
+        preventOverlap: false,
+        
+        // 布局冷却因子,值越小结束时间越短,值越大时间越长但是结果也越收敛
+        coolDown: 0.99,
+        
+        // 数据映射到圆的半径的最小值和最大值
+        minRadius: 10,
+        maxRadius: 20,
+
+        // 是否根据屏幕比例拉伸
+        ratioScaling: false,
+
+        // 在 500+ 顶点的图上建议设置 large 为 true, 会使用 Barnes-Hut simulation
+        // 同时开启 useWorker 并且把 steps 值调大
+        // 关于Barnes-Hut simulation: http://en.wikipedia.org/wiki/Barnes–Hut_simulation
+        large: false,
+
+        // 是否在浏览器支持 worker 的时候使用 web worker
+        useWorker: false,
+        // 每一帧 force 迭代的次数,仅在启用webworker的情况下有用
+        steps: 1,
+
+        // 布局缩放因子,并不完全精确, 效果跟布局大小类似
+        scaling: 1.0,
+
+        // 向心力因子,越大向心力越大( 所有顶点会往 center 的位置收拢 )
+        gravity: 1,
+
+        symbol: 'circle',
+        // symbolSize 为 0 的话使用映射到minRadius-maxRadius后的值
+        symbolSize: 0,
+
+        linkSymbol: null,
+        linkSymbolSize: [10, 15],
+        draggable: true,
+        clickable: true,
+
+        roam: false,
+
+        // 分类里如果有样式会覆盖节点默认样式
+        // categories: [{
+            // itemStyle
+            // symbol
+            // symbolSize
+            // name
+        // }],
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                label: {
+                    show: false,
+                    position: 'inside'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                },
+                nodeStyle: {
+                    brushType : 'both',
+                    borderColor : '#5182ab',
+                    borderWidth: 1
+                },
+                linkStyle: {
+                    color: '#5182ab',
+                    width: 1,
+                    type: 'line'
+                }
+            },
+            emphasis: {
+                // color: 各异,
+                label: {
+                    show: false
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                },
+                nodeStyle: {},
+                linkStyle: {
+                    opacity: 0
+                }
+            }
+        }
+        // nodes: [{
+        //     name: 'xxx',
+        //     value: 1,
+        //     itemStyle: {},
+        //     initial: [0, 0],
+        //     fixX: false,
+        //     fixY: false,
+        //     ignore: false,
+        //     symbol: 'circle',
+        //     symbolSize: 0
+        // }]
+        // links: [{
+        //      source: 1,
+        //      target: 2,
+        //      weight: 1,
+        //      itemStyle: {}
+        // }, {
+        //      source: 'xxx',
+        //      target: 'ooo'
+        // }]
+    };
+    
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrConfig = require('zrender/config');
+    var vec2 = require('zrender/tool/vector');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function Force(ecTheme, messageCenter, zr, option, myChart) {
+        var self = this;
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        // 保存节点的位置,改变数据时能够有更好的动画效果
+        this.__nodePositionMap = {};
+
+        this._graph = new Graph(true);
+        this._layout = new ForceLayout();
+
+        this._layout.onupdate = function() {
+            self._step();
+        };
+
+        this._steps = 1;
+
+        // 关闭可拖拽属性
+        this.ondragstart = function() {
+            ondragstart.apply(self, arguments);
+        };
+        this.ondragend = function() {
+            ondragend.apply(self, arguments);
+        };
+        this.ondrop = function() {};
+        this.shapeHandler.ondragstart = function() {
+            self.isDragstart = true;
+        };
+        this.onmousemove = function() {
+            onmousemove.apply(self, arguments);
+        };
+
+        this.refresh(option);
+    }
+
+    /**
+     * 绘制图形
+     */
+    Force.prototype = {
+
+        constructor: Force,
+
+        type : ecConfig.CHART_TYPE_FORCE,
+
+        _init: function() {
+            this.selectedMap = {};
+            var legend = this.component.legend;
+            var series = this.series;
+            var serieName;
+
+            this.clear();
+
+            for (var i = 0, l = series.length; i < l; i++) {
+                var serie = series[i];
+                if (serie.type === ecConfig.CHART_TYPE_FORCE) {
+                    series[i] = this.reformOption(series[i]);
+                    serieName = series[i].name || '';
+                    
+                    // 系列图例开关
+                    this.selectedMap[serieName] = 
+                        legend ? legend.isSelected(serieName) : true;
+                    if (!this.selectedMap[serieName]) {
+                        continue;
+                    }
+
+                    this.buildMark(i);
+                    
+                    // TODO 多个 force 
+                    this._initSerie(serie, i);
+                    break;
+                }
+            }
+
+            this.animationEffect();
+        },
+
+        _getNodeCategory: function (serie, node) {
+            return serie.categories && serie.categories[node.category || 0];
+        },
+
+        _getNodeQueryTarget: function (serie, node, type) {
+            type = type || 'normal';
+            var category = this._getNodeCategory(serie, node) || {};
+            return [
+                // Node
+                node.itemStyle && node.itemStyle[type],
+                // Category
+                category && category.itemStyle && category.itemStyle[type],
+                // Serie
+                serie.itemStyle[type].nodeStyle
+            ];
+        },
+
+        _getEdgeQueryTarget: function (serie, edge, type) {
+            type = type || 'normal';
+            return [
+                (edge.itemStyle && edge.itemStyle[type]),
+                serie.itemStyle[type].linkStyle
+            ];
+        },
+
+        _initSerie: function(serie, serieIdx) {
+            this._temperature = 1;
+
+            // matrix 表示边
+            if (serie.matrix) {
+                this._graph = this._getSerieGraphFromDataMatrix(serie);
+            }
+            // links 表示边
+            else if (serie.links) {
+                this._graph = this._getSerieGraphFromNodeLinks(serie);
+            }
+
+            this._buildLinkShapes(serie, serieIdx);
+            this._buildNodeShapes(serie, serieIdx);
+
+            var panable = serie.roam === true || serie.roam === 'move';
+            var zoomable = serie.roam === true || serie.roam === 'scale';
+            // Enable pan and zooom
+            this.zr.modLayer(this.getZlevelBase(), {
+                panable: panable,
+                zoomable: zoomable
+            });
+
+            if (
+                this.query('markPoint.effect.show')
+                || this.query('markLine.effect.show')
+            ) {
+                this.zr.modLayer(ecConfig.EFFECT_ZLEVEL, {
+                    panable: panable,
+                    zoomable: zoomable
+                });
+            }
+
+            this._initLayout(serie);
+
+            this._step();
+        },
+
+        _getSerieGraphFromDataMatrix: function (serie) {
+            var nodesData = [];
+            var count = 0;
+            var matrix = [];
+            // 复制一份新的matrix
+            for (var i = 0; i < serie.matrix.length; i++) {
+                matrix[i] = serie.matrix[i].slice();
+            }
+            var data = serie.data || serie.nodes;
+            for (var i = 0; i < data.length; i++) {
+                var node = {};
+                var group = data[i];
+                for (var key in group) {
+                    // name改为id
+                    if (key === 'name') {
+                        node['id'] = group['name'];
+                    }
+                    else {
+                        node[key] = group[key];
+                    }
+                }
+                // legends 选择优先级 category -> group
+                var category = this._getNodeCategory(serie, group);
+                var name = category ? category.name : group.name;
+
+                this.selectedMap[name] = this.isSelected(name);
+                if (this.selectedMap[name]) {
+                    nodesData.push(node);
+                    count++;
+                }
+                else {
+                    // 过滤legend未选中的数据
+                    matrix.splice(count, 1);
+                    for (var j = 0; j < matrix.length; j++) {
+                        matrix[j].splice(count, 1);
+                    }
+                }
+            }
+
+            var graph = Graph.fromMatrix(nodesData, matrix, true);
+
+            // Prepare layout parameters
+            graph.eachNode(function (n, idx) {
+                n.layout = {
+                    size: n.data.value,
+                    mass: 0
+                };
+                n.rawIndex = idx;
+            });
+            graph.eachEdge(function (e) {
+                e.layout = {
+                    weight: e.data.weight
+                };
+            });
+
+            return graph;
+        },
+
+        _getSerieGraphFromNodeLinks: function (serie) {
+            var graph = new Graph(true);
+            var nodes = serie.data || serie.nodes;
+            for (var i = 0, len = nodes.length; i < len; i++) {
+                var n = nodes[i];
+                if (!n || n.ignore) {
+                    continue;
+                }
+                // legends 选择优先级 category -> group
+                var category = this._getNodeCategory(serie, n);
+                var name = category ? category.name : n.name;
+
+                this.selectedMap[name] = this.isSelected(name);
+                if (this.selectedMap[name]) {
+                    var node = graph.addNode(n.name, n);
+                    node.rawIndex = i;
+                }
+            }
+
+            for (var i = 0, len = serie.links.length; i < len; i++) {
+                var e = serie.links[i];
+                var n1 = e.source;
+                var n2 = e.target;
+                if (typeof(n1) === 'number') {
+                    n1 = nodes[n1];
+                    if (n1) {
+                        n1 = n1.name;
+                    }
+                }
+                if (typeof(n2) === 'number') {
+                    n2 = nodes[n2];
+                    if (n2) {
+                        n2 = n2.name;
+                    }
+                }
+                var edge = graph.addEdge(n1, n2, e);
+                if (edge) {
+                    edge.rawIndex = i;
+                }
+            }
+
+            graph.eachNode(function (n) {
+                var value = n.data.value;
+                if (value == null) {    // value 是 null 或者 undefined
+                    value = 0;
+                    // 默认使用所有边值的和作为节点的大小, 不修改 data 里的数值
+                    for (var i = 0; i < n.edges.length; i++) {
+                        value += n.edges[i].data.weight || 0;
+                    }
+                }
+                n.layout = {
+                    size: value,
+                    mass: 0
+                };
+            });
+            graph.eachEdge(function (e) {
+                e.layout = {
+                    // 默认 weight 为1
+                    weight: e.data.weight == null ? 1 : e.data.weight
+                };
+            });
+
+            return graph;
+        },
+
+        _initLayout: function(serie) {
+            var graph = this._graph;
+            var len = graph.nodes.length;
+
+            var minRadius = this.query(serie, 'minRadius');
+            var maxRadius = this.query(serie, 'maxRadius');
+
+            this._steps = serie.steps || 1;
+
+            var layout = this._layout;
+            layout.center = this.parseCenter(this.zr, serie.center);
+            layout.width = this.parsePercent(serie.size, this.zr.getWidth());
+            layout.height = this.parsePercent(serie.size, this.zr.getHeight());
+
+            layout.large = serie.large;
+            layout.scaling = serie.scaling;
+            layout.ratioScaling = serie.ratioScaling;
+            layout.gravity = serie.gravity;
+            layout.temperature = 1;
+            layout.coolDown = serie.coolDown;
+            layout.preventNodeEdgeOverlap = serie.preventOverlap;
+            layout.preventNodeOverlap = serie.preventOverlap;
+
+            // 将值映射到minRadius-maxRadius的范围上
+            var min = Infinity; var max = -Infinity;
+            for (var i = 0; i < len; i++) {
+                var gNode = graph.nodes[i];
+                max = Math.max(gNode.layout.size, max);
+                min = Math.min(gNode.layout.size, min);
+            }
+            var divider = max - min;
+            for (var i = 0; i < len; i++) {
+                var gNode = graph.nodes[i];
+                if (divider > 0) {
+                    gNode.layout.size = 
+                        (gNode.layout.size - min) * (maxRadius - minRadius) / divider
+                        + minRadius;
+                    // 节点质量是归一的
+                    gNode.layout.mass = gNode.layout.size / maxRadius;
+                } else {
+                    gNode.layout.size = (maxRadius - minRadius) / 2;
+                    gNode.layout.mass = 0.5;
+                }
+            }
+
+            for (var i = 0; i < len; i++) {
+                // var initPos;
+                var gNode = graph.nodes[i];
+                if (typeof(this.__nodePositionMap[gNode.id]) !== 'undefined') {
+                    gNode.layout.position = vec2.create();
+                    vec2.copy(gNode.layout.position, this.__nodePositionMap[gNode.id]);
+                }
+                else if (typeof(gNode.data.initial) !== 'undefined') {
+                    gNode.layout.position = vec2.create();
+                    vec2.copy(gNode.layout.position, gNode.data.initial);
+                }
+                else {
+                    var center = this._layout.center;
+                    var size = Math.min(this._layout.width, this._layout.height);
+                    gNode.layout.position = _randomInSquare(
+                        center[0], center[1], size * 0.8
+                    );
+                }
+                var style = gNode.shape.style;
+                var radius = gNode.layout.size;
+                style.width = style.width || (radius * 2);
+                style.height = style.height || (radius * 2);
+                style.x = -style.width / 2;
+                style.y = -style.height / 2;
+                vec2.copy(gNode.shape.position, gNode.layout.position);
+            }
+
+            // 边
+            len = graph.edges.length;
+            max = -Infinity;
+            for (var i = 0; i < len; i++) {
+                var e = graph.edges[i];
+                if (e.layout.weight > max) {
+                    max = e.layout.weight;
+                }
+            }
+            // 权重归一
+            for (var i = 0; i < len; i++) {
+                var e = graph.edges[i];
+                e.layout.weight /= max;
+            }
+
+            this._layout.init(graph, serie.useWorker);
+        },
+
+        _buildNodeShapes: function(serie, serieIdx) {
+            var graph = this._graph;
+
+            var categories = this.query(serie, 'categories');
+
+            graph.eachNode(function (node) {
+                var category = this._getNodeCategory(serie, node.data);
+                var queryTarget = [node.data, category, serie];
+                var styleQueryTarget = this._getNodeQueryTarget(serie, node.data);
+                var emphasisStyleQueryTarget = this._getNodeQueryTarget(
+                    serie, node.data, 'emphasis'
+                );
+
+                var shape = new IconShape({
+                    style: {
+                        x: 0,
+                        y: 0,
+                        color: this.deepQuery(styleQueryTarget, 'color'),
+                        brushType: 'both',
+                        // 兼容原有写法
+                        strokeColor: this.deepQuery(styleQueryTarget, 'strokeColor')
+                            || this.deepQuery(styleQueryTarget, 'borderColor'),
+                        lineWidth: this.deepQuery(styleQueryTarget, 'lineWidth')
+                            || this.deepQuery(styleQueryTarget, 'borderWidth')
+                    },
+                    highlightStyle: {
+                        color: this.deepQuery(emphasisStyleQueryTarget, 'color'),
+                        // 兼容原有写法
+                        strokeColor: this.deepQuery(emphasisStyleQueryTarget, 'strokeColor')
+                            || this.deepQuery(emphasisStyleQueryTarget, 'borderColor'),
+                        lineWidth: this.deepQuery(emphasisStyleQueryTarget, 'lineWidth')
+                            || this.deepQuery(emphasisStyleQueryTarget, 'borderWidth')
+                    },
+                    clickable: serie.clickable,
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase()
+                });
+                if (!shape.style.color) {
+                    shape.style.color = category 
+                        ? this.getColor(category.name) : this.getColor(node.id);
+                }
+
+                shape.style.iconType = this.deepQuery(queryTarget, 'symbol');
+                var symbolSize = this.deepQuery(queryTarget, 'symbolSize') || 0;
+                if (typeof symbolSize === 'number') {
+                    symbolSize = [symbolSize, symbolSize];
+                }
+                // 强制设定节点大小,否则默认映射到 minRadius 到 maxRadius 后的值
+                shape.style.width = symbolSize[0] * 2;
+                shape.style.height = symbolSize[1] * 2;
+
+                if (shape.style.iconType.match('image')) {
+                    shape.style.image = shape.style.iconType.replace(
+                        new RegExp('^image:\\/\\/'), ''
+                    );
+                    shape = new ImageShape({
+                        style: shape.style,
+                        highlightStyle: shape.highlightStyle,
+                        clickable: shape.clickable,
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase()
+                    });
+                }
+                
+                // 节点标签样式
+                if (this.deepQuery(queryTarget, 'itemStyle.normal.label.show')) {
+                    shape.style.text = node.data.label == null ? node.id : node.data.label;
+                    shape.style.textPosition = this.deepQuery(
+                        queryTarget, 'itemStyle.normal.label.position'
+                    ) ;
+                    shape.style.textColor = this.deepQuery(
+                        queryTarget, 'itemStyle.normal.label.textStyle.color'
+                    );
+                    shape.style.textFont = this.getFont(this.deepQuery(
+                        queryTarget, 'itemStyle.normal.label.textStyle'
+                    ) || {});
+                }
+
+                if (this.deepQuery(queryTarget, 'itemStyle.emphasis.label.show')) {
+                    shape.highlightStyle.textPosition = this.deepQuery(
+                        queryTarget, 'itemStyle.emphasis.label.position'
+                    );
+                    shape.highlightStyle.textColor = this.deepQuery(
+                        queryTarget, 'itemStyle.emphasis.label.textStyle.color'
+                    );
+                    shape.highlightStyle.textFont = this.getFont(this.deepQuery(
+                        queryTarget, 'itemStyle.emphasis.label.textStyle'
+                    ) || {});
+                }
+
+                // 拖拽特性
+                if (this.deepQuery(queryTarget, 'draggable')) {
+                    this.setCalculable(shape);
+                    shape.dragEnableTime = 0;
+                    shape.draggable = true;
+                    shape.ondragstart = this.shapeHandler.ondragstart;
+                    shape.ondragover = null;
+                }
+                
+                var categoryName = '';
+                if (typeof(node.category) !== 'undefined') {
+                    var category = categories[node.category];
+                    categoryName = (category && category.name) || '';
+                }
+                // !!Pack data before addShape
+                ecData.pack(
+                    shape,
+                    serie,
+                    serieIdx,
+                    // data
+                    node.data,
+                    // data index
+                    node.rawIndex,
+                    // name
+                    node.data.name || '',
+                    // category
+                    // special
+                    node.category
+                );
+                
+                this.shapeList.push(shape);
+                this.zr.addShape(shape);
+
+                node.shape = shape;
+            }, this);
+        },
+
+        _buildLinkShapes: function(serie, serieIdx) {
+            var graph = this._graph;
+            var len = graph.edges.length;
+
+            for (var i = 0; i < len; i++) {
+                var gEdge = graph.edges[i];
+                var link = gEdge.data;
+                var source = gEdge.node1;
+                var target = gEdge.node2;
+
+                var otherEdge = graph.getEdge(target, source);
+
+                var queryTarget = this._getEdgeQueryTarget(serie, link);
+                var linkType = this.deepQuery(queryTarget, 'type');
+                // TODO 暂时只有线段支持箭头
+                if (serie.linkSymbol && serie.linkSymbol !== 'none') {
+                    linkType = 'line';
+                }
+                var LinkShapeCtor = linkType === 'line' ? LineShape : BezierCurveShape;
+
+                var linkShape = new LinkShapeCtor({
+                    style : {
+                        xStart : 0,
+                        yStart : 0,
+                        xEnd : 0,
+                        yEnd : 0
+                    },
+                    clickable: this.query(serie, 'clickable'),
+                    highlightStyle : {},
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase()
+                });
+
+                if (otherEdge && otherEdge.shape) {
+                    // 偏移一定位置放置双向边重叠
+                    linkShape.style.offset = 4;
+                    otherEdge.shape.style.offset = 4;
+                }
+
+                zrUtil.merge(
+                    linkShape.style,
+                    this.query(serie, 'itemStyle.normal.linkStyle'),
+                    true
+                );
+                zrUtil.merge(
+                    linkShape.highlightStyle,
+                    this.query(serie, 'itemStyle.emphasis.linkStyle'),
+                    true
+                );
+                if (typeof(link.itemStyle) !== 'undefined') {
+                    if(link.itemStyle.normal){
+                        zrUtil.merge(linkShape.style, link.itemStyle.normal, true);
+                    }
+                    if(link.itemStyle.emphasis){
+                        zrUtil.merge(
+                            linkShape.highlightStyle,
+                            link.itemStyle.emphasis,
+                            true
+                        );
+                    }
+                }
+
+                // 兼容原有写法
+                linkShape.style.lineWidth
+                    = linkShape.style.lineWidth || linkShape.style.width;
+                linkShape.style.strokeColor
+                    = linkShape.style.strokeColor || linkShape.style.color;
+                linkShape.highlightStyle.lineWidth
+                    = linkShape.highlightStyle.lineWidth || linkShape.highlightStyle.width;
+                linkShape.highlightStyle.strokeColor
+                    = linkShape.highlightStyle.strokeColor || linkShape.highlightStyle.color;
+
+                ecData.pack(
+                    linkShape,
+                    // serie
+                    serie,
+                    // serie index
+                    serieIdx,
+                    // link data
+                    gEdge.data,
+                    // link data index
+                    gEdge.rawIndex == null ? i : gEdge.rawIndex,
+                    // source name - target name
+                    gEdge.data.name || (source.id + ' - ' + target.id),
+                    // link source id
+                    // special
+                    source.id,
+                    // link target id
+                    // special2
+                    target.id
+                );
+
+                this.shapeList.push(linkShape);
+                this.zr.addShape(linkShape);
+                gEdge.shape = linkShape;
+
+                // Arrow shape
+                if (serie.linkSymbol && serie.linkSymbol !== 'none') {
+                    var symbolShape = new IconShape({
+                        style: {
+                            x: -5,
+                            y: 0,
+                            width: serie.linkSymbolSize[0],
+                            height: serie.linkSymbolSize[1],
+                            iconType: serie.linkSymbol,
+                            brushType: 'fill',
+                            // Use same style with link shape
+                            color: linkShape.style.strokeColor
+                        },
+                        highlightStyle: {
+                            brushType: 'fill'
+                        },
+                        position: [0, 0],
+                        rotation: 0,
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase()
+                    });
+                    linkShape._symbolShape = symbolShape;
+                    this.shapeList.push(symbolShape);
+                    this.zr.addShape(symbolShape);
+                }
+            }
+        },
+
+        _updateLinkShapes: function() {
+            var v = vec2.create();
+            var n = vec2.create();
+            var p1 = vec2.create();
+            var p2 = vec2.create();
+            var edges = this._graph.edges;
+            for (var i = 0, len = edges.length; i < len; i++) {
+                var edge = edges[i];
+                var sourceShape = edge.node1.shape;
+                var targetShape = edge.node2.shape;
+
+                vec2.copy(p1, sourceShape.position);
+                vec2.copy(p2, targetShape.position);
+
+                var edgeShapeStyle = edge.shape.style;
+
+                vec2.sub(v, p1, p2);
+                vec2.normalize(v, v);
+
+                if (edgeShapeStyle.offset) {
+                    n[0] = v[1];
+                    n[1] = - v[0];
+
+                    vec2.scaleAndAdd(p1, p1, n, edgeShapeStyle.offset);
+                    vec2.scaleAndAdd(p2, p2, n, edgeShapeStyle.offset);
+                }
+                else if (edge.shape.type === 'bezier-curve') {
+                    edgeShapeStyle.cpX1 = (p1[0] + p2[0]) / 2 - (p2[1] - p1[1]) / 4;
+                    edgeShapeStyle.cpY1 = (p1[1] + p2[1]) / 2 - (p1[0] - p2[0]) / 4;
+                }
+
+                edgeShapeStyle.xStart = p1[0];
+                edgeShapeStyle.yStart = p1[1];
+                edgeShapeStyle.xEnd = p2[0];
+                edgeShapeStyle.yEnd = p2[1];
+
+                edge.shape.modSelf();
+
+                if (edge.shape._symbolShape) {
+                    var symbolShape = edge.shape._symbolShape;
+                    vec2.copy(symbolShape.position, p2);
+                    vec2.scaleAndAdd(
+                        symbolShape.position, symbolShape.position,
+                        v, targetShape.style.width / 2 + 2
+                    );
+
+                    var angle = Math.atan2(v[1], v[0]);
+                    symbolShape.rotation = Math.PI / 2 - angle;
+
+                    symbolShape.modSelf();
+                }
+            }
+        },
+
+        _syncNodePositions: function() {
+            var graph = this._graph;
+            for (var i = 0; i < graph.nodes.length; i++) {
+                var gNode = graph.nodes[i];
+                var position = gNode.layout.position;
+                var node = gNode.data;
+                var shape = gNode.shape;
+                var fixX = shape.fixed || node.fixX;
+                var fixY = shape.fixed || node.fixY;
+                if (fixX === true) {
+                    fixX = 1;
+                } else if (isNaN(fixX)) {
+                    fixX = 0;
+                }
+                if (fixY === true) {
+                    fixY = 1;
+                } else if (isNaN(fixY)) {
+                    fixY = 0;
+                }
+                shape.position[0] += (position[0] - shape.position[0]) * (1 - fixX);
+                shape.position[1] += (position[1] - shape.position[1]) * (1 - fixY);
+
+                vec2.copy(position, shape.position);
+
+                var nodeName = node.name;
+                if (nodeName) {
+                    var gPos = this.__nodePositionMap[nodeName];
+                    if (!gPos) {
+                        gPos = this.__nodePositionMap[nodeName] = vec2.create();
+                    }
+                    vec2.copy(gPos, position);
+                }
+
+                shape.modSelf();
+            }
+        },
+
+        _step: function(e) {
+            this._syncNodePositions();
+
+            this._updateLinkShapes();
+
+            this.zr.refreshNextFrame();
+
+            if (this._layout.temperature > 0.01) {
+                this._layout.step(this._steps);
+            } else {
+                this.messageCenter.dispatch(
+                    ecConfig.EVENT.FORCE_LAYOUT_END,
+                    {},
+                    {},
+                    this.myChart
+                );
+            }
+        },
+
+        refresh: function(newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = this.option.series;
+            }
+
+            this.legend = this.component.legend;
+            if (this.legend) {
+                this.getColor = function(param) {
+                    return this.legend.getColor(param);
+                };
+                this.isSelected = function(param) {
+                    return this.legend.isSelected(param);
+                };
+            }
+            else {
+                var colorMap = {};
+                var count = 0;
+                this.getColor = function (key) {
+                    if (colorMap[key]) {
+                        return colorMap[key];
+                    }
+                    if (!colorMap[key]) {
+                        colorMap[key] = this.zr.getColor(count++);
+                    }
+
+                    return colorMap[key];
+                };
+                this.isSelected = function () {
+                    return true;
+                };
+            }
+
+            this._init();
+        },
+
+        dispose: function(){
+            this.clear();
+            this.shapeList = null;
+            this.effectList = null;
+
+            this._layout.dispose();
+            this._layout = null;
+
+            this.__nodePositionMap = {};
+        },
+
+        getPosition: function () {
+            var position = [];
+            this._graph.eachNode(function (n) {
+                if (n.layout) {
+                    position.push({
+                        name: n.data.name,
+                        position: Array.prototype.slice.call(n.layout.position)
+                    });
+                }
+            });
+            return position;
+        }
+    };
+
+    /**
+     * 拖拽开始
+     */
+    function ondragstart(param) {
+        if (!this.isDragstart || !param.target) {
+            // 没有在当前实例上发生拖拽行为则直接返回
+            return;
+        }
+
+        var shape = param.target;
+        shape.fixed = true;
+
+        // 处理完拖拽事件后复位
+        this.isDragstart = false;
+
+        this.zr.on(zrConfig.EVENT.MOUSEMOVE, this.onmousemove);
+    }
+
+    function onmousemove() {
+        this._layout.temperature = 0.8;
+        this._step();
+    }
+    
+    /**
+     * 数据项被拖拽出去,重载基类方法
+     */
+    function ondragend(param, status) {
+        if (!this.isDragend || !param.target) {
+            // 没有在当前实例上发生拖拽行为则直接返回
+            return;
+        }
+        var shape = param.target;
+        shape.fixed = false;
+
+        // 别status = {}赋值啊!!
+        status.dragIn = true;
+        //你自己refresh的话把他设为false,设true就会重新调refresh接口
+        status.needRefresh = false;
+
+        // 处理完拖拽事件后复位
+        this.isDragend = false;
+
+        this.zr.un(zrConfig.EVENT.MOUSEMOVE, this.onmousemove);
+    }
+   
+    function _randomInSquare(x, y, size) {
+        var v = vec2.create();
+        v[0] = (Math.random() - 0.5) * size + x;
+        v[1] = (Math.random() - 0.5) * size + y;
+        return v;
+    }
+    
+    zrUtil.inherits(Force, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('force', Force);
+
+    return Force;
+});

+ 778 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/funnel.js

@@ -0,0 +1,778 @@
+/**
+ * echarts图表类:漏斗图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    var PolygonShape = require('zrender/shape/Polygon');
+
+    var ecConfig = require('../config');
+    // 漏斗图默认参数
+    ecConfig.funnel = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        legendHoverLink: true,
+        x: 80,
+        y: 60,
+        x2: 80,
+        y2: 60,
+        // width: {totalWidth} - x - x2,
+        // height: {totalHeight} - y - y2,
+        min: 0,
+        max: 100,
+        minSize: '0%',
+        maxSize: '100%',
+        sort: 'descending', // 'ascending', 'descending'
+        gap: 0,
+        funnelAlign: 'center',
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                borderColor: '#fff',
+                borderWidth: 1,
+                label: {
+                    show: true,
+                    position: 'outer'
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                },
+                labelLine: {
+                    show: true,
+                    length: 10,
+                    lineStyle: {
+                        // color: 各异,
+                        width: 1,
+                        type: 'solid'
+                    }
+                }
+            },
+            emphasis: {
+                // color: 各异,
+                borderColor: 'rgba(0,0,0,0)',
+                borderWidth: 1,
+                label: {
+                    show: true
+                },
+                labelLine: {
+                    show: true
+                }
+            }
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var number = require('../util/number');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    var zrArea = require('zrender/tool/area');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function Funnel(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+        this.refresh(option);
+    }
+    
+    Funnel.prototype = {
+        type: ecConfig.CHART_TYPE_FUNNEL,
+        /**
+         * 绘制图形
+         */
+        _buildShape: function () {
+            var series = this.series;
+            var legend = this.component.legend;
+            // 复用参数索引
+            this._paramsMap = {};
+            this._selected = {};
+            this.selectedMap = {};
+            
+            var serieName;
+            for (var i = 0, l = series.length; i < l; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_FUNNEL) {
+                    series[i] = this.reformOption(series[i]);
+                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                    serieName = series[i].name || '';
+                    // 系列图例开关
+                    this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
+                    if (!this.selectedMap[serieName]) {
+                        continue;
+                    }
+                    this._buildSingleFunnel(i);
+                    this.buildMark(i);
+                }
+            }
+
+            this.addShapeList();
+        },
+        
+        /**
+         * 构建单个仪表盘
+         *
+         * @param {number} seriesIndex 系列索引
+         */
+        _buildSingleFunnel: function (seriesIndex) {
+            var legend = this.component.legend;
+            var serie = this.series[seriesIndex];
+            var data = this._mapData(seriesIndex);
+            var location = this._getLocation(seriesIndex);
+            this._paramsMap[seriesIndex] = {
+                location: location,
+                data: data
+            };
+            
+            var itemName;
+            var total = 0;
+            var selectedData = [];
+            // 计算需要显示的个数和总值
+            for (var i = 0, l = data.length; i < l; i++) {
+                itemName = data[i].name;
+                this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;
+
+                if (this.selectedMap[itemName] && !isNaN(data[i].value)) {
+                    selectedData.push(data[i]);
+                    total++;
+                }
+            }
+            if (total === 0) {
+                return;
+            }
+            // 可计算箱子
+            var funnelCase = this._buildFunnelCase(seriesIndex);
+            var align = serie.funnelAlign;
+            var gap = serie.gap;
+            var height = total > 1 
+                         ? (location.height - (total - 1) * gap) / total : location.height;
+            var width;
+            var lastY = location.y;
+            var lastWidth = serie.sort === 'descending'
+                            ? this._getItemWidth(seriesIndex, selectedData[0].value)
+                            : number.parsePercent(serie.minSize, location.width);
+            var next = serie.sort === 'descending' ? 1 : 0;
+            var centerX = location.centerX;
+            var pointList= [];
+            var x;
+            var polygon;
+            var lastPolygon;
+            for (var i = 0, l = selectedData.length; i < l; i++) {
+                itemName = selectedData[i].name;
+                if (this.selectedMap[itemName] && !isNaN(selectedData[i].value)) {
+                    width = i <= l - 2
+                            ? this._getItemWidth(seriesIndex, selectedData[i + next].value)
+                            : serie.sort === 'descending'
+                              ? number.parsePercent(serie.minSize, location.width)
+                              : number.parsePercent(serie.maxSize, location.width);
+                    switch (align) {
+                        case 'left':
+                            x = location.x;
+                            break;
+                        case 'right':
+                            x = location.x + location.width - lastWidth;
+                            break;
+                        default:
+                            x = centerX - lastWidth / 2;
+                    }
+                    polygon = this._buildItem(
+                        seriesIndex, selectedData[i]._index,
+                        legend // color
+                            ? legend.getColor(itemName) 
+                            : this.zr.getColor(selectedData[i]._index),
+                        x, lastY, lastWidth, width, height, align
+                    );
+                    lastY += height + gap;
+                    lastPolygon = polygon.style.pointList;
+                    
+                    pointList.unshift([lastPolygon[0][0] - 10, lastPolygon[0][1]]); // 左
+                    pointList.push([lastPolygon[1][0] + 10, lastPolygon[1][1]]);    // 右
+                    if (i === 0) {
+                        if (lastWidth === 0) {
+                            lastPolygon = pointList.pop();
+                            align == 'center' && (pointList[0][0] += 10);
+                            align == 'right' && (pointList[0][0] = lastPolygon[0]);
+                            pointList[0][1] -= align == 'center' ? 10 : 15;
+                            if (l == 1) {
+                                lastPolygon = polygon.style.pointList;
+                            }
+                        }
+                        else {
+                            pointList[pointList.length - 1][1] -= 5;
+                            pointList[0][1] -=5;
+                        }
+                    }
+                    lastWidth = width;
+                }
+            }
+            
+            if (funnelCase) {
+                pointList.unshift([lastPolygon[3][0] - 10, lastPolygon[3][1]]); // 左
+                pointList.push([lastPolygon[2][0] + 10, lastPolygon[2][1]]);    // 右
+                if (lastWidth === 0) {
+                    lastPolygon = pointList.pop();
+                    align == 'center' && (pointList[0][0] += 10);
+                    align == 'right' && (pointList[0][0] = lastPolygon[0]);
+                    pointList[0][1] += align == 'center' ? 10 : 15;
+                }
+                else {
+                    pointList[pointList.length - 1][1] += 5;
+                    pointList[0][1] +=5;
+                }
+                funnelCase.style.pointList = pointList;
+            }
+        },
+        
+        _buildFunnelCase: function(seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (this.deepQuery([serie, this.option], 'calculable')) {
+                var location = this._paramsMap[seriesIndex].location;
+                var gap = 10;
+                var funnelCase = {
+                    hoverable: false,
+                    style: {
+                        pointListd: [
+                            [location.x - gap, location.y - gap],
+                            [location.x + location.width + gap, location.y - gap],
+                            [location.x + location.width + gap, location.y + location.height + gap],
+                            [location.x - gap, location.y + location.height + gap]
+                        ],
+                        brushType: 'stroke',
+                        lineWidth: 1,
+                        strokeColor: serie.calculableHolderColor
+                                     || this.ecTheme.calculableHolderColor
+                                     || ecConfig.calculableHolderColor
+                    }
+                };
+                ecData.pack(funnelCase, serie, seriesIndex, undefined, -1);
+                this.setCalculable(funnelCase);
+                funnelCase = new PolygonShape(funnelCase);
+                this.shapeList.push(funnelCase);
+                return funnelCase;
+            }
+        },
+        
+        _getLocation: function (seriesIndex) {
+            var gridOption = this.series[seriesIndex];
+            var zrWidth = this.zr.getWidth();
+            var zrHeight = this.zr.getHeight();
+            var x = this.parsePercent(gridOption.x, zrWidth);
+            var y = this.parsePercent(gridOption.y, zrHeight);
+
+            var width = gridOption.width == null
+                        ? (zrWidth - x - this.parsePercent(gridOption.x2, zrWidth))
+                        : this.parsePercent(gridOption.width, zrWidth);
+
+            return {
+                x: x,
+                y: y,
+                width: width,
+                height: gridOption.height == null
+                        ? (zrHeight - y - this.parsePercent(gridOption.y2, zrHeight))
+                        : this.parsePercent(gridOption.height, zrHeight),
+                centerX: x + width / 2
+            };
+        },
+        
+        _mapData: function(seriesIndex) {
+            var serie = this.series[seriesIndex];
+            var funnelData = zrUtil.clone(serie.data);
+            for (var i = 0, l = funnelData.length; i < l; i++) {
+                funnelData[i]._index = i;
+            }
+            function numDescending (a, b) {
+                if (a.value === '-') {
+                    return 1;
+                }
+                else if (b.value === '-') {
+                    return -1;
+                }
+                return b.value - a.value;
+            }
+            function numAscending (a, b) {
+                return -numDescending(a, b);
+            }
+            if (serie.sort != 'none') {
+                funnelData.sort(serie.sort === 'descending' ? numDescending : numAscending);
+            }
+            
+            return funnelData;
+        },
+        
+        /**
+         * 构建单个扇形及指标
+         */
+        _buildItem: function (
+            seriesIndex, dataIndex, defaultColor,
+            x, y, topWidth, bottomWidth, height, align
+        ) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var data = serie.data[dataIndex];
+            
+            // 漏斗
+            var polygon = this.getPolygon(
+                    seriesIndex, dataIndex, defaultColor,
+                    x, y, topWidth, bottomWidth, height, align
+                );
+            ecData.pack(
+                polygon,
+                series[seriesIndex], seriesIndex,
+                series[seriesIndex].data[dataIndex], dataIndex,
+                series[seriesIndex].data[dataIndex].name
+            );
+            this.shapeList.push(polygon);
+
+            // 文本标签
+            var label = this.getLabel(
+                    seriesIndex, dataIndex, defaultColor,
+                    x, y, topWidth, bottomWidth, height, align
+                );
+            ecData.pack(
+                label,
+                series[seriesIndex], seriesIndex,
+                series[seriesIndex].data[dataIndex], dataIndex,
+                series[seriesIndex].data[dataIndex].name
+            );
+            this.shapeList.push(label);
+            // 特定状态下是否需要显示文本标签
+            if (!this._needLabel(serie, data,false)) {
+                label.invisible = true;
+            }
+
+            // 文本标签视觉引导线
+            var labelLine = this.getLabelLine(
+                    seriesIndex, dataIndex, defaultColor,
+                    x, y, topWidth, bottomWidth, height, align
+                );
+            this.shapeList.push(labelLine);
+            // 特定状态下是否需要显示文本标签引导线
+            if (!this._needLabelLine(serie, data,false)) {
+                labelLine.invisible = true;
+            }
+            
+            var polygonHoverConnect = [];
+            var labelHoverConnect = [];
+            if (this._needLabelLine(serie, data, true)) {
+                polygonHoverConnect.push(labelLine.id);
+                labelHoverConnect.push(labelLine.id);
+            }
+            if (this._needLabel(serie, data, true)) {
+                polygonHoverConnect.push(label.id);
+                labelHoverConnect.push(polygon.id);
+            }
+            polygon.hoverConnect = polygonHoverConnect;
+            label.hoverConnect = labelHoverConnect;
+            
+            return polygon;
+        },
+
+        /**
+         * 根据值计算宽度 
+         */
+        _getItemWidth: function (seriesIndex, value) {
+            var serie = this.series[seriesIndex];
+            var location = this._paramsMap[seriesIndex].location;
+            var min = serie.min;
+            var max = serie.max;
+            var minSize = number.parsePercent(serie.minSize, location.width);
+            var maxSize = number.parsePercent(serie.maxSize, location.width);
+            return (value - min) * (maxSize - minSize) / (max - min) + minSize;
+        },
+        
+        /**
+         * 构建扇形
+         */
+        getPolygon: function (
+            seriesIndex, dataIndex, defaultColor,
+            xLT, y, topWidth, bottomWidth, height, align
+        ) {
+            var serie = this.series[seriesIndex];
+            var data = serie.data[dataIndex];
+            var queryTarget = [data, serie];
+
+            // 多级控制
+            var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
+            var emphasis = this.deepMerge(queryTarget,'itemStyle.emphasis') || {};
+
+            var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data)
+                              || defaultColor;
+            
+            var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data)
+                || (typeof normalColor === 'string'
+                    ? zrColor.lift(normalColor, -0.2)
+                    : normalColor
+                );
+            
+            var  xLB;
+            switch (align) {
+                case 'left':
+                    xLB = xLT;
+                    break;
+                case 'right':
+                    xLB = xLT + (topWidth - bottomWidth);
+                    break;
+                default:
+                    xLB = xLT + (topWidth - bottomWidth) / 2;
+                    break;
+            }
+            var polygon = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                clickable: this.deepQuery(queryTarget, 'clickable'),
+                style: {
+                    pointList: [
+                        [xLT, y],
+                        [xLT + topWidth, y],
+                        [xLB + bottomWidth, y + height],
+                        [xLB, y + height]
+                    ],
+                    brushType: 'both',
+                    color: normalColor,
+                    lineWidth: normal.borderWidth,
+                    strokeColor: normal.borderColor
+                },
+                highlightStyle: {
+                    color: emphasisColor,
+                    lineWidth: emphasis.borderWidth,
+                    strokeColor: emphasis.borderColor
+                }
+            };
+            
+            if (this.deepQuery([data, serie, this.option], 'calculable')) {
+                this.setCalculable(polygon);
+                polygon.draggable = true;
+            }
+
+            return new PolygonShape(polygon);
+        },
+
+        /**
+         * 需要显示则会有返回构建好的shape,否则返回undefined
+         */
+        getLabel: function (
+            seriesIndex, dataIndex, defaultColor,
+            x, y, topWidth, bottomWidth, height, align
+        ) {
+            var serie = this.series[seriesIndex];
+            var data = serie.data[dataIndex];
+            var location = this._paramsMap[seriesIndex].location;
+            // serie里有默认配置,放心大胆的用!
+            var itemStyle = zrUtil.merge(
+                    zrUtil.clone(data.itemStyle) || {},
+                    serie.itemStyle
+                );
+            var status = 'normal';
+            // label配置
+            var labelControl = itemStyle[status].label;
+            var textStyle = labelControl.textStyle || {};
+            var lineLength = itemStyle[status].labelLine.length;
+
+            var text = this.getLabelText(seriesIndex, dataIndex, status);
+            var textFont = this.getFont(textStyle);
+            var textAlign;
+            var textColor = defaultColor;
+            labelControl.position = labelControl.position 
+                                    || itemStyle.normal.label.position;
+            if (labelControl.position === 'inner'
+                || labelControl.position === 'inside'
+                || labelControl.position === 'center'
+            ) {
+                // 内部
+                textAlign = align;
+                textColor = 
+                    Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
+                    ? '#fff' : zrColor.reverse(defaultColor);
+            }
+            else if (labelControl.position === 'left'){
+                // 左侧显示
+                textAlign = 'right';
+            }
+            else {
+                // 右侧显示,默认 labelControl.position === 'outer' || 'right)
+                textAlign = 'left';
+            }
+            
+            var textShape = {
+                zlevel: serie.zlevel,
+                z: serie.z + 1,
+                style: {
+                    x: this._getLabelPoint(
+                           labelControl.position, x, location,
+                           topWidth, bottomWidth,lineLength, align
+                       ),
+                    y: y + height / 2,
+                    color: textStyle.color || textColor,
+                    text: text,
+                    textAlign: textStyle.align || textAlign,
+                    textBaseline: textStyle.baseline || 'middle',
+                    textFont: textFont
+                }
+            };
+            
+            //----------高亮
+            status = 'emphasis';
+            // label配置
+            labelControl = itemStyle[status].label || labelControl;
+            textStyle = labelControl.textStyle || textStyle;
+            lineLength = itemStyle[status].labelLine.length || lineLength;
+            labelControl.position = labelControl.position || itemStyle.normal.label.position;
+            text = this.getLabelText(seriesIndex, dataIndex, status);
+            textFont = this.getFont(textStyle);
+            textColor = defaultColor;
+            if (labelControl.position === 'inner' 
+                || labelControl.position === 'inside'
+                || labelControl.position === 'center'
+            ) {
+                // 内部
+                textAlign = align;
+                textColor = 
+                    Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
+                    ? '#fff' : zrColor.reverse(defaultColor);
+            }
+            else if (labelControl.position === 'left'){
+                // 左侧显示
+                textAlign = 'right';
+            }
+            else {
+                // 右侧显示,默认 labelControl.position === 'outer' || 'right)
+                textAlign = 'left';
+            }
+            
+            textShape.highlightStyle = {
+                x: this._getLabelPoint(
+                       labelControl.position, x, location,
+                       topWidth, bottomWidth,lineLength, align
+                   ),
+                color: textStyle.color || textColor,
+                text: text,
+                textAlign: textStyle.align || textAlign,
+                textFont: textFont,
+                brushType: 'fill'
+            };
+            
+            return new TextShape(textShape);
+        },
+
+        /**
+         * 根据lable.format计算label text
+         */
+        getLabelText: function (seriesIndex, dataIndex, status) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var data = serie.data[dataIndex];
+            var formatter = this.deepQuery(
+                [data, serie],
+                'itemStyle.' + status + '.label.formatter'
+            );
+            
+            if (formatter) {
+                if (typeof formatter === 'function') {
+                    return formatter.call(
+                        this.myChart,
+                        {
+                            seriesIndex: seriesIndex,
+                            seriesName: serie.name || '',
+                            series: serie,
+                            dataIndex: dataIndex,
+                            data: data,
+                            name: data.name,
+                            value: data.value
+                        }
+                    );
+                }
+                else if (typeof formatter === 'string') {
+                    formatter = formatter.replace('{a}','{a0}')
+                                         .replace('{b}','{b0}')
+                                         .replace('{c}','{c0}')
+                                         .replace('{a0}', serie.name)
+                                         .replace('{b0}', data.name)
+                                         .replace('{c0}', data.value);
+    
+                    return formatter;
+                }
+            }
+            else {
+                return data.name;
+            }
+        },
+        
+        /**
+         * 需要显示则会有返回构建好的shape,否则返回undefined
+         */
+        getLabelLine: function (
+            seriesIndex, dataIndex, defaultColor,
+            x, y, topWidth, bottomWidth, height, align
+        ) {
+            var serie = this.series[seriesIndex];
+            var data = serie.data[dataIndex];
+            var location = this._paramsMap[seriesIndex].location;
+
+            // serie里有默认配置,放心大胆的用!
+            var itemStyle = zrUtil.merge(
+                    zrUtil.clone(data.itemStyle) || {},
+                    serie.itemStyle
+                );
+            var status = 'normal';
+            // labelLine配置
+            var labelLineControl = itemStyle[status].labelLine;
+            var lineLength = itemStyle[status].labelLine.length;
+            var lineStyle = labelLineControl.lineStyle || {};
+            
+            var labelControl = itemStyle[status].label;
+            labelControl.position = labelControl.position 
+                                    || itemStyle.normal.label.position;
+
+            var lineShape = {
+                zlevel: serie.zlevel,
+                z: serie.z + 1,
+                hoverable: false,
+                style: {
+                    xStart: this._getLabelLineStartPoint(x, location, topWidth, bottomWidth, align),
+                    yStart: y + height / 2,
+                    xEnd: this._getLabelPoint(
+                              labelControl.position, x, location,
+                              topWidth, bottomWidth,lineLength, align
+                          ),
+                    yEnd: y + height / 2,
+                    strokeColor: lineStyle.color || defaultColor,
+                    lineType: lineStyle.type,
+                    lineWidth: lineStyle.width
+                }
+            };
+            
+            status = 'emphasis';
+            // labelLine配置
+            labelLineControl = itemStyle[status].labelLine || labelLineControl;
+            lineLength = itemStyle[status].labelLine.length || lineLength;
+            lineStyle = labelLineControl.lineStyle || lineStyle;
+
+            labelControl = itemStyle[status].label || labelControl;
+            labelControl.position = labelControl.position;
+            
+            lineShape.highlightStyle = {
+                xEnd: this._getLabelPoint(
+                          labelControl.position, x, location,
+                          topWidth, bottomWidth,lineLength, align
+                      ),
+                strokeColor: lineStyle.color || defaultColor,
+                lineType: lineStyle.type,
+                lineWidth: lineStyle.width
+            };
+            
+            return new LineShape(lineShape);
+        },
+        
+        _getLabelPoint: function(position, x, location, topWidth, bottomWidth, lineLength, align) {
+            position = (position === 'inner' || position === 'inside') ? 'center' : position;
+            switch (position) {
+                case 'center':
+                    return align == 'center'
+                            ? (x + topWidth / 2)
+                            : align == 'left' ? (x + 10) : (x + topWidth - 10);
+                case 'left':
+                    // 左侧文本
+                    if (lineLength === 'auto') {
+                        return location.x - 10;
+                    }
+                    else {
+                        return align == 'center'
+                            // 居中布局
+                            ? (location.centerX - Math.max(topWidth, bottomWidth) / 2 - lineLength)
+                            : align == 'right'
+                                // 右对齐布局
+                                ? (x 
+                                    - (topWidth < bottomWidth ? (bottomWidth - topWidth) : 0)
+                                    - lineLength
+                                )
+                                // 左对齐布局
+                                : (location.x - lineLength);
+                    }
+                    break;
+                default:
+                    // 右侧文本
+                    if (lineLength === 'auto') {
+                        return location.x + location.width + 10;
+                    }
+                    else {
+                        return align == 'center'
+                            // 居中布局
+                            ? (location.centerX + Math.max(topWidth, bottomWidth) / 2 + lineLength)
+                            : align == 'right'
+                                // 右对齐布局
+                                ? (location.x + location.width + lineLength)
+                                // 左对齐布局
+                                : (x + Math.max(topWidth, bottomWidth) + lineLength);
+                    }
+            }
+        },
+        
+        _getLabelLineStartPoint: function(x, location, topWidth, bottomWidth, align) {
+            return align == 'center'
+                   ? location.centerX 
+                   : topWidth < bottomWidth
+                     ? (x + Math.min(topWidth, bottomWidth) / 2)
+                     : (x + Math.max(topWidth, bottomWidth) / 2);
+        },
+
+        /**
+         * 返回特定状态(normal or emphasis)下是否需要显示label标签文本
+         * @param {Object} serie
+         * @param {Object} data
+         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
+         */
+        _needLabel: function (serie, data, isEmphasis) {
+            return this.deepQuery(
+                [data, serie],
+                'itemStyle.'
+                + (isEmphasis ? 'emphasis' : 'normal')
+                + '.label.show'
+            );
+        },
+
+        /**
+         * 返回特定状态(normal or emphasis)下是否需要显示labelLine标签视觉引导线
+         * @param {Object} serie
+         * @param {Object} data
+         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
+         */
+        _needLabelLine: function (serie, data, isEmphasis) {
+            return this.deepQuery(
+                [data, serie],
+                'itemStyle.'
+                + (isEmphasis ? 'emphasis' : 'normal')
+                +'.labelLine.show'
+            );
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        }
+    };
+    
+    zrUtil.inherits(Funnel, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('funnel', Funnel);
+    
+    return Funnel;
+});

+ 634 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/gauge.js

@@ -0,0 +1,634 @@
+/**
+ * echarts图表类:仪表盘
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var GaugePointerShape = require('../util/shape/GaugePointer');
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    var RectangleShape = require('zrender/shape/Rectangle');
+    var CircleShape = require('zrender/shape/Circle');
+    var SectorShape = require('zrender/shape/Sector');
+
+    var ecConfig = require('../config');
+    // 仪表盘默认参数
+    ecConfig.gauge = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        center: ['50%', '50%'],    // 默认全局居中
+        clickable: true,
+        legendHoverLink: true,
+        radius: '75%',
+        startAngle: 225,
+        endAngle: -45,
+        min: 0,                     // 最小值
+        max: 100,                   // 最大值
+        splitNumber: 10,            // 分割段数,默认为10
+        axisLine: {            // 坐标轴线
+            show: true,        // 默认显示,属性show控制显示与否
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: [[0.2, '#228b22'],[0.8, '#48b'],[1, '#ff4500']], 
+                width: 30
+            }
+        },
+        axisTick: {            // 坐标轴小标记
+            show: true,        // 属性show控制显示与否,默认不显示
+            splitNumber: 5,    // 每份split细分多少段
+            length :8,         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#eee',
+                width: 1,
+                type: 'solid'
+            }
+        },
+        axisLabel: {           // 坐标轴文本标签,详见axis.axisLabel
+            show: true,
+            // formatter: null,
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: 'auto'
+            }
+        },
+        splitLine: {           // 分隔线
+            show: true,        // 默认显示,属性show控制显示与否
+            length :30,         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: '#eee',
+                width: 2,
+                type: 'solid'
+            }
+        },
+        pointer: {
+            show: true,
+            length: '80%',
+            width: 8,
+            color: 'auto'
+        },
+        title: {
+            show: true,
+            offsetCenter: [0, '-40%'],      // x, y,单位px
+            textStyle: {                    // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333',
+                fontSize: 15
+            }
+        },
+        detail: {
+            show: true,
+            backgroundColor: 'rgba(0,0,0,0)',
+            borderWidth: 0,
+            borderColor: '#ccc',
+            width: 100,
+            height: 40,
+            offsetCenter: [0, '40%'],   // x, y,单位px
+            // formatter: null,
+            textStyle: {                // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: 'auto',
+                fontSize: 30
+            }
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var accMath = require('../util/accMath');
+    var zrUtil = require('zrender/tool/util');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function Gauge(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+        this.refresh(option);
+    }
+    
+    Gauge.prototype = {
+        type: ecConfig.CHART_TYPE_GAUGE,
+        /**
+         * 绘制图形
+         */
+        _buildShape: function () {
+            var series = this.series;
+            // 复用参数索引
+            this._paramsMap = {};
+            this.selectedMap = {};
+            for (var i = 0, l = series.length; i < l; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_GAUGE) {
+                    //仪表图不用去legend 获取状态,默认这里给的true 
+                    this.selectedMap[series[i].name] = true;
+                    series[i] = this.reformOption(series[i]);
+                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                    this._buildSingleGauge(i);
+                    this.buildMark(i);
+                }
+            }
+
+            this.addShapeList();
+        },
+        
+        /**
+         * 构建单个仪表盘
+         *
+         * @param {number} seriesIndex 系列索引
+         */
+        _buildSingleGauge: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+
+            this._paramsMap[seriesIndex] = {
+                center: this.parseCenter(this.zr, serie.center),
+                radius: this.parseRadius(this.zr, serie.radius),
+                startAngle: serie.startAngle.toFixed(2) - 0,
+                endAngle: serie.endAngle.toFixed(2) - 0
+            };
+            this._paramsMap[seriesIndex].totalAngle = this._paramsMap[seriesIndex].startAngle
+                                                    - this._paramsMap[seriesIndex].endAngle;
+            
+            this._colorMap(seriesIndex);
+            
+            this._buildAxisLine(seriesIndex);
+            
+            this._buildSplitLine(seriesIndex);
+            
+            this._buildAxisTick(seriesIndex);
+            
+            this._buildAxisLabel(seriesIndex);
+            
+            this._buildPointer(seriesIndex);
+            
+            this._buildTitle(seriesIndex);
+            
+            this._buildDetail(seriesIndex);
+        },
+        
+        // 轴线
+        _buildAxisLine: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.axisLine.show) {
+                return;
+            }
+            var min = serie.min;
+            var total = serie.max - min;
+            var params = this._paramsMap[seriesIndex];
+            var center = params.center;
+            var startAngle = params.startAngle;
+            var totalAngle = params.totalAngle;
+            var colorArray = params.colorArray;
+            var lineStyle = serie.axisLine.lineStyle;
+            var lineWidth = this.parsePercent(lineStyle.width, params.radius[1]);
+            var r = params.radius[1];
+            var r0 = r - lineWidth;
+            
+            var sectorShape;
+            var lastAngle = startAngle;
+            var newAngle;
+            for (var i = 0, l = colorArray.length; i < l; i++) {
+                newAngle = startAngle - totalAngle * (colorArray[i][0] - min) / total;
+                sectorShape = this._getSector(
+                    center, r0, r, 
+                    newAngle,           // startAngle
+                    lastAngle,          // endAngle
+                    colorArray[i][1],   // color
+                    lineStyle,
+                    serie.zlevel,
+                    serie.z
+                );
+                lastAngle = newAngle;
+                sectorShape._animationAdd = 'r';
+                ecData.set(sectorShape, 'seriesIndex', seriesIndex);
+                ecData.set(sectorShape, 'dataIndex', i);
+                this.shapeList.push(sectorShape);
+            }
+        },
+        
+        // 坐标轴分割线
+        _buildSplitLine: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.splitLine.show) {
+                return;
+            }
+            
+            var params = this._paramsMap[seriesIndex];
+            var splitNumber = serie.splitNumber;
+            var min = serie.min;
+            var total = serie.max - min;
+            var splitLine = serie.splitLine;
+            var length = this.parsePercent(splitLine.length, params.radius[1]);
+            var lineStyle = splitLine.lineStyle;
+            var color = lineStyle.color;
+            var center = params.center;
+            var startAngle = params.startAngle * Math.PI / 180;
+            var totalAngle = params.totalAngle * Math.PI / 180;
+            var r = params.radius[1];
+            var r0 = r - length;
+            
+            var angle;
+            var sinAngle;
+            var cosAngle;
+            for (var i = 0; i <= splitNumber; i++) {
+                angle = startAngle - totalAngle / splitNumber * i;
+                sinAngle = Math.sin(angle);
+                cosAngle = Math.cos(angle);
+                this.shapeList.push(new LineShape({
+                    zlevel: serie.zlevel,
+                    z: serie.z + 1,
+                    hoverable: false,
+                    style: {
+                        xStart: center[0] + cosAngle * r,
+                        yStart: center[1] - sinAngle * r,
+                        xEnd: center[0] + cosAngle * r0,
+                        yEnd: center[1] - sinAngle * r0,
+                        strokeColor: color === 'auto' 
+                                     ? this._getColor(seriesIndex, min + total / splitNumber * i)
+                                     : color,
+                        lineType: lineStyle.type,
+                        lineWidth: lineStyle.width,
+                        shadowColor: lineStyle.shadowColor,
+                        shadowBlur: lineStyle.shadowBlur,
+                        shadowOffsetX: lineStyle.shadowOffsetX,
+                        shadowOffsetY: lineStyle.shadowOffsetY
+                    }
+                }));
+            }
+        },
+        
+        // 小标记
+        _buildAxisTick: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.axisTick.show) {
+                return;
+            }
+            
+            var params = this._paramsMap[seriesIndex];
+            var splitNumber = serie.splitNumber;
+            var min = serie.min;
+            var total = serie.max - min;
+            var axisTick = serie.axisTick;
+            var tickSplit = axisTick.splitNumber;
+            var length = this.parsePercent(axisTick.length, params.radius[1]);
+            var lineStyle = axisTick.lineStyle;
+            var color = lineStyle.color;
+            
+            var center = params.center;
+            var startAngle = params.startAngle * Math.PI / 180;
+            var totalAngle = params.totalAngle * Math.PI / 180;
+            var r = params.radius[1];
+            var r0 = r - length;
+            
+            var angle;
+            var sinAngle;
+            var cosAngle;
+            for (var i = 0, l = splitNumber * tickSplit; i <= l; i++) {
+                if (i % tickSplit === 0) {   // 同splitLine
+                    continue;
+                }
+                angle = startAngle - totalAngle / l * i;
+                sinAngle = Math.sin(angle);
+                cosAngle = Math.cos(angle);
+                this.shapeList.push(new LineShape({
+                    zlevel: serie.zlevel,
+                    z: serie.z + 1,
+                    hoverable: false,
+                    style: {
+                        xStart: center[0] + cosAngle * r,
+                        yStart: center[1] - sinAngle * r,
+                        xEnd: center[0] + cosAngle * r0,
+                        yEnd: center[1] - sinAngle * r0,
+                        strokeColor: color === 'auto' 
+                                     ? this._getColor(seriesIndex, min + total / l * i)
+                                     : color,
+                        lineType: lineStyle.type,
+                        lineWidth: lineStyle.width,
+                        shadowColor: lineStyle.shadowColor,
+                        shadowBlur: lineStyle.shadowBlur,
+                        shadowOffsetX: lineStyle.shadowOffsetX,
+                        shadowOffsetY: lineStyle.shadowOffsetY
+                    }
+                }));
+            }
+        },
+        
+        // 坐标轴文本
+        _buildAxisLabel: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.axisLabel.show) {
+                return;
+            }
+            
+            var splitNumber = serie.splitNumber;
+            var min = serie.min;
+            var total = serie.max - min;
+            var textStyle = serie.axisLabel.textStyle;
+            var textFont = this.getFont(textStyle);
+            var color = textStyle.color;
+            
+            var params = this._paramsMap[seriesIndex];
+            var center = params.center;
+            var startAngle = params.startAngle;
+            var totalAngle = params.totalAngle;
+            var r0 = params.radius[1] 
+                     - this.parsePercent(serie.splitLine.length, params.radius[1])
+                     - 5;
+            
+            var angle;
+            var sinAngle;
+            var cosAngle;
+            var value;
+            for (var i = 0; i <= splitNumber; i++) {
+                value = accMath.accAdd(
+                            min , accMath.accMul(accMath.accDiv(total , splitNumber), i)
+                        );
+                angle = startAngle - totalAngle / splitNumber * i;
+                sinAngle = Math.sin(angle * Math.PI / 180);
+                cosAngle = Math.cos(angle * Math.PI / 180);
+                angle = (angle + 360) % 360;
+                this.shapeList.push(new TextShape({
+                    zlevel: serie.zlevel,
+                    z: serie.z + 1,
+                    hoverable: false,
+                    style: {
+                        x: center[0] + cosAngle * r0,
+                        y: center[1] - sinAngle * r0,
+                        color: color === 'auto' ? this._getColor(seriesIndex, value) : color,
+                        text: this._getLabelText(serie.axisLabel.formatter, value),
+                        textAlign: (angle >= 110 && angle <= 250)
+                                   ? 'left' 
+                                   : (angle <= 70 || angle >= 290)
+                                       ? 'right'
+                                       : 'center',
+                        textBaseline: (angle >= 10 && angle <= 170)
+                                      ? 'top' 
+                                      : (angle >= 190 && angle <= 350)
+                                          ? 'bottom'
+                                          : 'middle',
+                        textFont: textFont,
+                        shadowColor: textStyle.shadowColor,
+                        shadowBlur: textStyle.shadowBlur,
+                        shadowOffsetX: textStyle.shadowOffsetX,
+                        shadowOffsetY: textStyle.shadowOffsetY
+                    }
+                }));
+            }
+        },
+        
+        _buildPointer: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.pointer.show) {
+                return;
+            }
+            var total = serie.max - serie.min;
+            var pointer = serie.pointer;
+            
+            var params = this._paramsMap[seriesIndex];
+            var length = this.parsePercent(pointer.length, params.radius[1]);
+            var width = this.parsePercent(pointer.width, params.radius[1]);
+            var center = params.center;
+            var value = this._getValue(seriesIndex);
+            value = value < serie.max ? value : serie.max;
+            
+            var angle = (params.startAngle - params.totalAngle / total * (value - serie.min))
+                        * Math.PI / 180;
+            var color = pointer.color === 'auto' 
+                        ? this._getColor(seriesIndex, value)
+                        : pointer.color;
+            
+            var pointShape = new GaugePointerShape({
+                zlevel: serie.zlevel,
+                z: serie.z + 1,
+                clickable: this.query(serie, 'clickable'),
+                style: {
+                    x: center[0],
+                    y: center[1],
+                    r: length,
+                    startAngle: params.startAngle * Math.PI / 180,
+                    angle: angle,
+                    color: color,
+                    width: width,
+                    shadowColor: pointer.shadowColor,
+                    shadowBlur: pointer.shadowBlur,
+                    shadowOffsetX: pointer.shadowOffsetX,
+                    shadowOffsetY: pointer.shadowOffsetY
+                },
+                highlightStyle: {
+                    brushType: 'fill',
+                    width: width > 2 ? 2 : (width / 2),
+                    color: '#fff'
+                }
+            });
+            ecData.pack(
+                pointShape,
+                this.series[seriesIndex], seriesIndex,
+                this.series[seriesIndex].data[0], 0,
+                this.series[seriesIndex].data[0].name,
+                value
+            );
+            this.shapeList.push(pointShape);
+            
+            this.shapeList.push(new CircleShape({
+                zlevel: serie.zlevel,
+                z: serie.z + 2,
+                hoverable: false,
+                style: {
+                    x: center[0],
+                    y: center[1],
+                    r: pointer.width / 2.5,
+                    color: '#fff'
+                }
+            }));
+        },
+        
+        _buildTitle: function(seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.title.show) {
+                return;
+            }
+            
+            var data = serie.data[0];
+            var name = data.name != null ? data.name : '';
+            if (name !== '') { // 不要帮我代码规范
+                var title = serie.title;
+                var offsetCenter = title.offsetCenter;
+                var textStyle = title.textStyle;
+                var textColor = textStyle.color;
+                var params = this._paramsMap[seriesIndex];
+                var x = params.center[0] + this.parsePercent(offsetCenter[0], params.radius[1]);
+                var y = params.center[1] + this.parsePercent(offsetCenter[1], params.radius[1]);
+                this.shapeList.push(new TextShape({
+                    zlevel: serie.zlevel,
+                    z: serie.z + (
+                        (Math.abs(x - params.center[0]) + Math.abs(y - params.center[1])) 
+                          < textStyle.fontSize * 2 ? 2 : 1
+                    ),
+                    hoverable: false,
+                    style: {
+                        x: x,
+                        y: y,
+                        color: textColor === 'auto' ? this._getColor(seriesIndex) : textColor,
+                        text: name,
+                        textAlign: 'center',
+                        textFont: this.getFont(textStyle),
+                        shadowColor: textStyle.shadowColor,
+                        shadowBlur: textStyle.shadowBlur,
+                        shadowOffsetX: textStyle.shadowOffsetX,
+                        shadowOffsetY: textStyle.shadowOffsetY
+                    }
+                }));
+            }
+        },
+        
+        _buildDetail: function(seriesIndex) {
+            var serie = this.series[seriesIndex];
+            if (!serie.detail.show) {
+                return;
+            }
+            
+            var detail = serie.detail;
+            var offsetCenter = detail.offsetCenter;
+            var color = detail.backgroundColor;
+            var textStyle = detail.textStyle;
+            var textColor = textStyle.color;
+                
+            var params = this._paramsMap[seriesIndex];
+            var value = this._getValue(seriesIndex);
+            var x = params.center[0] - detail.width / 2 
+                    + this.parsePercent(offsetCenter[0], params.radius[1]);
+            var y = params.center[1] 
+                    + this.parsePercent(offsetCenter[1], params.radius[1]);
+            this.shapeList.push(new RectangleShape({
+                zlevel: serie.zlevel,
+                z: serie.z + (
+                    (Math.abs(x + detail.width / 2 - params.center[0]) 
+                     + Math.abs(y + detail.height / 2 - params.center[1])) < textStyle.fontSize 
+                    ? 2 : 1
+                ),
+                hoverable: false,
+                style: {
+                    x: x,
+                    y: y,
+                    width: detail.width,
+                    height: detail.height,
+                    brushType: 'both',
+                    color: color === 'auto' ? this._getColor(seriesIndex, value) : color,
+                    lineWidth: detail.borderWidth,
+                    strokeColor: detail.borderColor,
+                    
+                    shadowColor: detail.shadowColor,
+                    shadowBlur: detail.shadowBlur,
+                    shadowOffsetX: detail.shadowOffsetX,
+                    shadowOffsetY: detail.shadowOffsetY,
+                    
+                    text: this._getLabelText(detail.formatter, value),
+                    textFont: this.getFont(textStyle),
+                    textPosition: 'inside',
+                    textColor: textColor === 'auto' ? this._getColor(seriesIndex, value) : textColor
+                }
+            }));
+        },
+        
+        _getValue: function(seriesIndex) {
+            return this.getDataFromOption(this.series[seriesIndex].data[0]);
+        },
+        
+        /**
+         * 颜色索引 
+         */
+        _colorMap: function (seriesIndex) {
+            var serie = this.series[seriesIndex];
+            var min = serie.min;
+            var total = serie.max - min;
+            var color = serie.axisLine.lineStyle.color;
+            if (!(color instanceof Array)) {
+                color = [[1, color]];
+            }
+            var colorArray = [];
+            for (var i = 0, l = color.length; i < l; i++) {
+                colorArray.push([color[i][0] * total + min, color[i][1]]);
+            }
+            this._paramsMap[seriesIndex].colorArray = colorArray;
+        },
+        
+        /**
+         * 自动颜色 
+         */
+        _getColor: function (seriesIndex, value) {
+            if (value == null) {
+                value = this._getValue(seriesIndex);
+            }
+            
+            var colorArray = this._paramsMap[seriesIndex].colorArray;
+            for (var i = 0, l = colorArray.length; i < l; i++) {
+                if (colorArray[i][0] >= value) {
+                    return colorArray[i][1];
+                }
+            }
+            return colorArray[colorArray.length - 1][1];
+        },
+        
+        /**
+         * 构建扇形
+         */
+        _getSector: function (center, r0, r, startAngle, endAngle, color, lineStyle, zlevel, z) {
+            return new SectorShape ({
+                zlevel: zlevel,
+                z: z,
+                hoverable: false,
+                style: {
+                    x: center[0],      // 圆心横坐标
+                    y: center[1],      // 圆心纵坐标
+                    r0: r0,            // 圆环内半径
+                    r: r,              // 圆环外半径
+                    startAngle: startAngle,
+                    endAngle: endAngle,
+                    brushType: 'fill',
+                    color: color,
+                    shadowColor: lineStyle.shadowColor,
+                    shadowBlur: lineStyle.shadowBlur,
+                    shadowOffsetX: lineStyle.shadowOffsetX,
+                    shadowOffsetY: lineStyle.shadowOffsetY
+                }
+            });
+        },
+
+        /**
+         * 根据lable.format计算label text
+         */
+        _getLabelText: function (formatter, value) {
+            if (formatter) {
+                if (typeof formatter === 'function') {
+                    return formatter.call(this.myChart, value);
+                }
+                else if (typeof formatter === 'string') {
+                    return formatter.replace('{value}', value);
+                }
+            }
+            return value;
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        }
+    };
+    
+    zrUtil.inherits(Gauge, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('gauge', Gauge);
+    
+    return Gauge;
+});

+ 108 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/heatmap.js

@@ -0,0 +1,108 @@
+/**
+ * @file defines echarts Heatmap Chart
+ * @author Ovilia (me@zhangwenli.com)
+ * Inspired by https://github.com/mourner/simpleheat
+ *
+ * @module
+ *
+ * @requires /src/chart/base.js
+ * @requires /src/chart/layer/heatmap.js
+ * @requires /src/config.js
+ * @requires /src/util/ecData.js
+ * @requires NPM:zrender/tool/util.js
+ * @requires NPM:zrender/tool/color.js
+ * @requires NPM:zrender/shape/Image.js
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    var HeatmapLayer = require('../layer/heatmap');
+
+    var ecConfig = require('../config');
+    var ecData = require('../util/ecData');
+
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    var zrImage = require('zrender/shape/Image');
+
+    ecConfig.heatmap = {
+        zlevel: 0,
+        z: 2,
+        clickable: true
+    };
+
+    /**
+     * Heatmap Chart
+     *
+     * @class
+     * @extends ChartBase
+     */
+    function Heatmap(ecTheme, messageCenter, zr, option, myChart) {
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+
+    Heatmap.prototype = {
+        type: ecConfig.CHART_TYPE_HEATMAP,
+
+        /**
+         * refreshes the chart
+         * @param {Object} newOption - options to refresh the chart
+         * @public
+         */
+        refresh: function(newOption) {
+            this.clear();
+
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+
+            this._init();
+        },
+
+        /**
+         * init heatmap
+         * @private
+         */
+        _init: function() {
+            var series = this.series;
+            this.backupShapeList();
+
+            var len = series.length;
+            for (var i = 0; i < len; ++i) {
+                if (series[i].type === ecConfig.CHART_TYPE_HEATMAP) {
+                    series[i] = this.reformOption(series[i]);
+
+                    var layer = new HeatmapLayer(series[i]);
+                    var canvas = layer.getCanvas(series[i].data,
+                        this.zr.getWidth(), this.zr.getHeight());
+                    var image = new zrImage({
+                        position: [0, 0],
+                        scale: [1, 1],
+                        hoverable: this.option.hoverable,
+                        style: {
+                            x: 0,
+                            y: 0,
+                            image: canvas,
+                            width: canvas.width,
+                            height: canvas.height
+                        }
+                    });
+                    this.shapeList.push(image);
+                    // this.zr.addShape(image);
+                }
+            }
+
+            this.addShapeList();
+        }
+    };
+
+
+
+    zrUtil.inherits(Heatmap, ChartBase);
+
+    require('../chart').define('heatmap', Heatmap);
+
+    return Heatmap;
+});

+ 251 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/island.js

@@ -0,0 +1,251 @@
+/**
+ * echarts组件:孤岛数据
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var CircleShape = require('zrender/shape/Circle');
+    
+    var ecConfig = require('../config');
+    ecConfig.island = {
+        zlevel: 0,                  // 一级层叠
+        z: 5,                       // 二级层叠
+        r: 15,
+        calculateStep: 0.1  // 滚轮可计算步长 0.1 = 10%
+    };
+
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrEvent = require('zrender/tool/event');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表选项
+     */
+    function Island(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this._nameConnector;
+        this._valueConnector;
+        this._zrHeight = this.zr.getHeight();
+        this._zrWidth = this.zr.getWidth();
+
+        var self = this;
+        /**
+         * 滚轮改变孤岛数据值
+         */
+        self.shapeHandler.onmousewheel = function (param) {
+            var shape = param.target;
+
+            var event = param.event;
+            var delta = zrEvent.getDelta(event);
+            delta = delta > 0 ? (-1) : 1;
+            shape.style.r -= delta;
+            shape.style.r = shape.style.r < 5 ? 5 : shape.style.r;
+
+            var value = ecData.get(shape, 'value');
+            var dvalue = value * self.option.island.calculateStep;
+            value = dvalue > 1
+                    ? (Math.round(value - dvalue * delta))
+                    : +(value - dvalue * delta).toFixed(2);
+
+            var name = ecData.get(shape, 'name');
+            shape.style.text = name + ':' + value;
+
+            ecData.set(shape, 'value', value);
+            ecData.set(shape, 'name', name);
+
+            self.zr.modShape(shape.id);
+            self.zr.refreshNextFrame();
+            zrEvent.stop(event);
+        };
+    }
+    
+    Island.prototype = {
+        type: ecConfig.CHART_TYPE_ISLAND,
+        /**
+         * 孤岛合并
+         *
+         * @param {string} tarShapeIndex 目标索引
+         * @param {Object} srcShape 源目标,合入目标后删除
+         */
+        _combine: function (tarShape, srcShape) {
+            var zrColor = require('zrender/tool/color');
+            var accMath = require('../util/accMath');
+            var value = accMath.accAdd(
+                            ecData.get(tarShape, 'value'),
+                            ecData.get(srcShape, 'value')
+                        );
+            var name = ecData.get(tarShape, 'name')
+                       + this._nameConnector
+                       + ecData.get(srcShape, 'name');
+
+            tarShape.style.text = name + this._valueConnector + value;
+
+            ecData.set(tarShape, 'value', value);
+            ecData.set(tarShape, 'name', name);
+            tarShape.style.r = this.option.island.r;
+            tarShape.style.color = zrColor.mix(
+                tarShape.style.color,
+                srcShape.style.color
+            );
+        },
+
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                newOption.island = this.reformOption(newOption.island);
+                this.option = newOption;
+    
+                this._nameConnector = this.option.nameConnector;
+                this._valueConnector = this.option.valueConnector;
+            }
+        },
+        
+        getOption: function () {
+            return this.option;
+        },
+
+        resize: function () {
+            var newWidth = this.zr.getWidth();
+            var newHieght = this.zr.getHeight();
+            var xScale = newWidth / (this._zrWidth || newWidth);
+            var yScale = newHieght / (this._zrHeight || newHieght);
+            if (xScale === 1 && yScale === 1) {
+                return;
+            }
+            this._zrWidth = newWidth;
+            this._zrHeight = newHieght;
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.modShape(
+                    this.shapeList[i].id,
+                    {
+                        style: {
+                            x: Math.round(this.shapeList[i].style.x * xScale),
+                            y: Math.round(this.shapeList[i].style.y * yScale)
+                        }
+                    }
+                );
+            }
+        },
+
+        add: function (shape) {
+            var name = ecData.get(shape, 'name');
+            var value = ecData.get(shape, 'value');
+            var seriesName = ecData.get(shape, 'series') != null
+                             ? ecData.get(shape, 'series').name
+                             : '';
+            var font = this.getFont(this.option.island.textStyle);
+            var islandOption = this.option.island;
+            var islandShape = {
+                zlevel: islandOption.zlevel,
+                z: islandOption.z,
+                style: {
+                    x: shape.style.x,
+                    y: shape.style.y,
+                    r: this.option.island.r,
+                    color: shape.style.color || shape.style.strokeColor,
+                    text: name + this._valueConnector + value,
+                    textFont: font
+                },
+                draggable: true,
+                hoverable: true,
+                onmousewheel: this.shapeHandler.onmousewheel,
+                _type: 'island'
+            };
+            if (islandShape.style.color === '#fff') {
+                islandShape.style.color = shape.style.strokeColor;
+            }
+            this.setCalculable(islandShape);
+            islandShape.dragEnableTime = 0;
+            ecData.pack(
+                islandShape,
+                {name:seriesName}, -1,
+                value, -1,
+                name
+            );
+            islandShape = new CircleShape(islandShape);
+            this.shapeList.push(islandShape);
+            this.zr.addShape(islandShape);
+        },
+
+        del: function (shape) {
+            this.zr.delShape(shape.id);
+            var newShapeList = [];
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                if (this.shapeList[i].id != shape.id) {
+                    newShapeList.push(this.shapeList[i]);
+                }
+            }
+            this.shapeList = newShapeList;
+        },
+
+        /**
+         * 数据项被拖拽进来, 重载基类方法
+         */
+        ondrop: function (param, status) {
+            if (!this.isDrop || !param.target) {
+                // 没有在当前实例上发生拖拽行为则直接返回
+                return;
+            }
+            // 拖拽产生孤岛数据合并
+            var target = param.target;      // 拖拽安放目标
+            var dragged = param.dragged;    // 当前被拖拽的图形对象
+
+            this._combine(target, dragged);
+            this.zr.modShape(target.id);
+
+            status.dragIn = true;
+
+            // 处理完拖拽事件后复位
+            this.isDrop = false;
+
+            return;
+        },
+
+        /**
+         * 数据项被拖拽出去, 重载基类方法
+         */
+        ondragend: function (param, status) {
+            var target = param.target;      // 拖拽安放目标
+            if (!this.isDragend) {
+                // 拖拽的不是孤岛数据,如果没有图表接受孤岛数据,需要新增孤岛数据
+                if (!status.dragIn) {
+                    target.style.x = zrEvent.getX(param.event);
+                    target.style.y = zrEvent.getY(param.event);
+                    this.add(target);
+                    status.needRefresh = true;
+                }
+            }
+            else {
+                // 拖拽的是孤岛数据,如果有图表接受了孤岛数据,需要删除孤岛数据
+                if (status.dragIn) {
+                    this.del(target);
+                    status.needRefresh = true;
+                }
+            }
+
+            // 处理完拖拽事件后复位
+            this.isDragend = false;
+
+            return;
+        }
+    };
+    
+    zrUtil.inherits(Island, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('island', Island);
+    
+    return Island;
+});

+ 557 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/k.js

@@ -0,0 +1,557 @@
+/**
+ * echarts图表类:K线图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var CandleShape = require('../util/shape/Candle');
+    // 组件依赖
+    require('../component/axis');
+    require('../component/grid');
+    require('../component/dataZoom');
+    
+    var ecConfig = require('../config');
+    // K线图默认参数
+    ecConfig.k = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        hoverable: true,
+        legendHoverLink: false,
+        xAxisIndex: 0,
+        yAxisIndex: 0,
+        // barWidth: null               // 默认自适应
+        // barMaxWidth: null            // 默认自适应 
+        itemStyle: {
+            normal: {
+                color: '#fff',          // 阳线填充颜色
+                color0: '#00aa11',      // 阴线填充颜色
+                lineStyle: {
+                    width: 1,
+                    color: '#ff3200',   // 阳线边框颜色
+                    color0: '#00aa11'   // 阴线边框颜色
+                },
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            },
+            emphasis: {
+                // color: 各异,
+                // color0: 各异,
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            }
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function K(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+    
+    K.prototype = {
+        type: ecConfig.CHART_TYPE_K,
+        /**
+         * 绘制图形
+         */
+        _buildShape: function () {
+            var series = this.series;
+            this.selectedMap = {};
+
+            // 水平垂直双向series索引 ,position索引到seriesIndex
+            var _position2sIndexMap = {
+                top: [],
+                bottom: []
+            };
+            var xAxis;
+            for (var i = 0, l = series.length; i < l; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_K) {
+                    series[i] = this.reformOption(series[i]);
+                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                    xAxis = this.component.xAxis.getAxis(series[i].xAxisIndex);
+                    if (xAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
+                    ) {
+                        _position2sIndexMap[xAxis.getPosition()].push(i);
+                    }
+                }
+            }
+            //console.log(_position2sIndexMap)
+            for (var position in _position2sIndexMap) {
+                if (_position2sIndexMap[position].length > 0) {
+                    this._buildSinglePosition(
+                        position, _position2sIndexMap[position]
+                    );
+                }
+            }
+
+            this.addShapeList();
+        },
+
+        /**
+         * 构建单个方向上的K线图
+         *
+         * @param {number} seriesIndex 系列索引
+         */
+        _buildSinglePosition: function (position, seriesArray) {
+            var mapData = this._mapData(seriesArray);
+            var locationMap = mapData.locationMap;
+            var maxDataLength = mapData.maxDataLength;
+
+            if (maxDataLength === 0 || locationMap.length === 0) {
+                return;
+            }
+            this._buildHorizontal(seriesArray, maxDataLength, locationMap);
+
+            for (var i = 0, l = seriesArray.length; i < l; i++) {
+                this.buildMark(seriesArray[i]);
+            }
+        },
+
+        /**
+         * 数据整形
+         * 数组位置映射到系列索引
+         */
+        _mapData: function (seriesArray) {
+            var series = this.series;
+            var serie;                              // 临时映射变量
+            var serieName;                          // 临时映射变量
+            var legend = this.component.legend;
+            var locationMap = [];                   // 需要返回的东西:数组位置映射到系列索引
+            var maxDataLength = 0;                  // 需要返回的东西:最大数据长度
+            // 计算需要显示的个数和分配位置并记在下面这个结构里
+            for (var i = 0, l = seriesArray.length; i < l; i++) {
+                serie = series[seriesArray[i]];
+                serieName = serie.name;
+                this.selectedMap[serieName] = legend 
+                                              ? legend.isSelected(serieName)
+                                              : true;
+                
+                if (this.selectedMap[serieName]) {
+                    locationMap.push(seriesArray[i]);
+                }
+                // 兼职帮算一下最大长度
+                maxDataLength = Math.max(maxDataLength, serie.data.length);
+            }
+            return {
+                locationMap: locationMap,
+                maxDataLength: maxDataLength
+            };
+        },
+
+        /**
+         * 构建类目轴为水平方向的K线图系列
+         */
+        _buildHorizontal: function (seriesArray, maxDataLength, locationMap) {
+            var series = this.series;
+            // 确定类目轴和数值轴,同一方向随便找一个即可
+            var seriesIndex;
+            var serie;
+            var xAxisIndex;
+            var categoryAxis;
+            var yAxisIndex; // 数值轴各异
+            var valueAxis;  // 数值轴各异
+
+            var pointList = {};
+            var candleWidth;
+            var data;
+            var value;
+            var barMaxWidth;
+            for (var j = 0, k = locationMap.length; j < k; j++) {
+                seriesIndex = locationMap[j];
+                serie = series[seriesIndex];
+                
+                xAxisIndex = serie.xAxisIndex || 0;
+                categoryAxis = this.component.xAxis.getAxis(xAxisIndex);
+                candleWidth = serie.barWidth 
+                              || Math.floor(categoryAxis.getGap() / 2);
+                barMaxWidth = serie.barMaxWidth;
+                if (barMaxWidth && barMaxWidth < candleWidth) {
+                    candleWidth = barMaxWidth;
+                }
+                yAxisIndex = serie.yAxisIndex || 0;
+                valueAxis = this.component.yAxis.getAxis(yAxisIndex);
+                
+                pointList[seriesIndex] = [];
+                for (var i = 0, l = maxDataLength; i < l; i++) {
+                    if (categoryAxis.getNameByIndex(i) == null) {
+                        // 系列数据超出类目轴长度
+                        break;
+                    }
+                    
+                    data = serie.data[i];
+                    value = this.getDataFromOption(data, '-');
+                    if (value === '-' || value.length != 4) {
+                        // 数据格式不符
+                        continue;
+                    }
+                    pointList[seriesIndex].push([
+                        categoryAxis.getCoordByIndex(i),    // 横坐标
+                        candleWidth,
+                        valueAxis.getCoord(value[0]),       // 纵坐标:开盘
+                        valueAxis.getCoord(value[1]),       // 纵坐标:收盘
+                        valueAxis.getCoord(value[2]),       // 纵坐标:最低
+                        valueAxis.getCoord(value[3]),       // 纵坐标:最高
+                        i,                                  // 数据index
+                        categoryAxis.getNameByIndex(i)      // 类目名称
+                    ]);
+                }
+            }
+            // console.log(pointList)
+            this._buildKLine(seriesArray, pointList);
+        },
+
+        /**
+         * 生成K线
+         */
+        _buildKLine: function (seriesArray, pointList) {
+            var series = this.series;
+            // normal:
+            var nLineWidth;
+            var nLineColor;
+            var nLineColor0;    // 阴线
+            var nColor;
+            var nColor0;        // 阴线
+            
+            // emphasis:
+            var eLineWidth;
+            var eLineColor;
+            var eLineColor0;
+            var eColor;
+            var eColor0;
+
+            var serie;
+            var queryTarget;
+            var data;
+            var seriesPL;
+            var singlePoint;
+            var candleType;
+
+            var seriesIndex;
+            for (var sIdx = 0, len = seriesArray.length; sIdx < len; sIdx++) {
+                seriesIndex = seriesArray[sIdx];
+                serie = series[seriesIndex];
+                seriesPL = pointList[seriesIndex];
+                
+                if (this._isLarge(seriesPL)) {
+                    seriesPL = this._getLargePointList(seriesPL);
+                }
+                
+                if (serie.type === ecConfig.CHART_TYPE_K && seriesPL != null) {
+                    // 多级控制
+                    queryTarget = serie;
+                    nLineWidth = this.query(
+                        queryTarget, 'itemStyle.normal.lineStyle.width'
+                    );
+                    nLineColor = this.query(
+                        queryTarget, 'itemStyle.normal.lineStyle.color'
+                    );
+                    nLineColor0 = this.query(
+                        queryTarget, 'itemStyle.normal.lineStyle.color0'
+                    );
+                    nColor = this.query(
+                        queryTarget, 'itemStyle.normal.color'
+                    );
+                    nColor0 = this.query(
+                        queryTarget, 'itemStyle.normal.color0'
+                    );
+                    
+                    eLineWidth = this.query(
+                        queryTarget, 'itemStyle.emphasis.lineStyle.width'
+                    );
+                    eLineColor = this.query(
+                        queryTarget, 'itemStyle.emphasis.lineStyle.color'
+                    );
+                    eLineColor0 = this.query(
+                        queryTarget, 'itemStyle.emphasis.lineStyle.color0'
+                    );
+                    eColor = this.query(
+                        queryTarget, 'itemStyle.emphasis.color'
+                    );
+                    eColor0 = this.query(
+                        queryTarget, 'itemStyle.emphasis.color0'
+                    );
+
+                    /*
+                     * pointlist=[
+                     *      0  x,
+                     *      1  width, 
+                     *      2  y0,
+                     *      3  y1,
+                     *      4  y2,
+                     *      5  y3,
+                     *      6  dataIndex,
+                     *      7  categoryName
+                     * ]
+                     */
+                    for (var i = 0, l = seriesPL.length; i < l; i++) {
+                        singlePoint = seriesPL[i];
+                        data = serie.data[singlePoint[6]];
+                        queryTarget = data;
+                        candleType = singlePoint[3] < singlePoint[2];
+                        this.shapeList.push(this._getCandle(
+                            seriesIndex,    // seriesIndex
+                            singlePoint[6], // dataIndex
+                            singlePoint[7], // name
+                            
+                            singlePoint[0], // x
+                            singlePoint[1], // width
+                            singlePoint[2], // y开盘
+                            singlePoint[3], // y收盘
+                            singlePoint[4], // y最低
+                            singlePoint[5], // y最高
+                            
+                            // 填充颜色
+                            candleType
+                            ? (this.query(          // 阳
+                                   queryTarget, 'itemStyle.normal.color'
+                               ) || nColor)
+                            : (this.query(          // 阴
+                                   queryTarget, 'itemStyle.normal.color0'
+                               ) || nColor0),
+                            
+                            // 线宽
+                            this.query(
+                               queryTarget, 'itemStyle.normal.lineStyle.width'
+                            ) || nLineWidth,
+                            
+                            // 线色
+                            candleType
+                            ? (this.query(          // 阳
+                                   queryTarget,
+                                   'itemStyle.normal.lineStyle.color'
+                               ) || nLineColor)
+                            : (this.query(          // 阴
+                                   queryTarget,
+                                   'itemStyle.normal.lineStyle.color0'
+                               ) || nLineColor0),
+                            
+                            //------------高亮
+                            
+                            // 填充颜色
+                            candleType
+                            ? (this.query(          // 阳
+                                   queryTarget, 'itemStyle.emphasis.color'
+                               ) || eColor || nColor)
+                            : (this.query(          // 阴
+                                   queryTarget, 'itemStyle.emphasis.color0'
+                               ) || eColor0 || nColor0),
+                            
+                            // 线宽
+                            this.query(
+                               queryTarget, 'itemStyle.emphasis.lineStyle.width'
+                            ) || eLineWidth || nLineWidth,
+                            
+                            // 线色
+                            candleType
+                            ? (this.query(          // 阳
+                                   queryTarget,
+                                   'itemStyle.emphasis.lineStyle.color'
+                               ) || eLineColor || nLineColor)
+                            : (this.query(          // 阴
+                                   queryTarget,
+                                   'itemStyle.emphasis.lineStyle.color0'
+                               ) || eLineColor0 || nLineColor0)
+                        ));
+                    }
+                }
+            }
+            // console.log(this.shapeList)
+        },
+
+        _isLarge: function(singlePL) {
+            return singlePL[0][1] < 0.5;
+        },
+        
+        /**
+         * 大规模pointList优化 
+         */
+        _getLargePointList: function(singlePL) {
+            var total = this.component.grid.getWidth();
+            var len = singlePL.length;
+            var newList = [];
+            for (var i = 0; i < total; i++) {
+                newList[i] = singlePL[Math.floor(len / total * i)];
+            }
+            return newList;
+        },
+        
+        /**
+         * 生成K线图上的图形
+         */
+        _getCandle: function (
+            seriesIndex, dataIndex, name, 
+            x, width, y0, y1, y2, y3, 
+            nColor, nLinewidth, nLineColor, 
+            eColor, eLinewidth, eLineColor
+        ) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var data = serie.data[dataIndex];
+            var queryTarget = [data, serie];
+
+            var itemShape = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                clickable: this.deepQuery(queryTarget, 'clickable'),
+                hoverable: this.deepQuery(queryTarget, 'hoverable'),
+                style: {
+                    x: x,
+                    y: [y0, y1, y2, y3],
+                    width: width,
+                    color: nColor,
+                    strokeColor: nLineColor,
+                    lineWidth: nLinewidth,
+                    brushType: 'both'
+                },
+                highlightStyle: {
+                    color: eColor,
+                    strokeColor: eLineColor,
+                    lineWidth: eLinewidth
+                },
+                _seriesIndex: seriesIndex
+            };
+
+            itemShape = this.addLabel(itemShape, serie, data, name);
+            
+            ecData.pack(
+                itemShape,
+                serie, seriesIndex,
+                data, dataIndex,
+                name
+            );
+            
+            itemShape = new CandleShape(itemShape);
+            
+            return itemShape;
+        },
+
+        // 位置转换
+        getMarkCoord: function (seriesIndex, mpData) {
+            var serie = this.series[seriesIndex];
+            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
+            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
+            
+            return [
+                typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
+                    ? xAxis.getCoordByIndex(mpData.xAxis || 0)
+                    : xAxis.getCoord(mpData.xAxis || 0),
+                
+                typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
+                    ? yAxis.getCoordByIndex(mpData.yAxis || 0)
+                    : yAxis.getCoord(mpData.yAxis || 0)
+            ];
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        },
+
+        /**
+         * 动画设定
+         */
+        addDataAnimation: function (params, done) {
+            var series = this.series;
+            var aniMap = {}; // seriesIndex索引参数
+            for (var i = 0, l = params.length; i < l; i++) {
+                aniMap[params[i][0]] = params[i];
+            }
+            var x;
+            var dx;
+            var y;
+            var serie;
+            var seriesIndex;
+            var dataIndex;
+
+            var aniCount = 0;
+            function animationDone() {
+                aniCount--;
+                if (aniCount === 0) {
+                    done && done();
+                }
+            }
+
+             for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                seriesIndex = this.shapeList[i]._seriesIndex;
+                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
+                    // 有数据删除才有移动的动画
+                    if (this.shapeList[i].type === 'candle') {
+                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
+                        serie = series[seriesIndex];
+                        if (aniMap[seriesIndex][2] 
+                            && dataIndex === serie.data.length - 1
+                        ) {
+                            // 队头加入删除末尾
+                            this.zr.delShape(this.shapeList[i].id);
+                            continue;
+                        }
+                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
+                            // 队尾加入删除头部
+                            this.zr.delShape(this.shapeList[i].id);
+                            continue;
+                        }
+                        dx = this.component.xAxis.getAxis(
+                                serie.xAxisIndex || 0
+                             ).getGap();
+                        x = aniMap[seriesIndex][2] ? dx : -dx;
+                        y = 0;
+                        aniCount++;
+                        this.zr.animate(this.shapeList[i].id, '')
+                            .when(
+                                this.query(this.option, 'animationDurationUpdate'),
+                                { position: [ x, y ] }
+                            )
+                            .done(animationDone)
+                            .start();
+                    }
+                }
+            }
+            
+            // 没有动画
+            if (!aniCount) {
+                done && done();
+            }
+        }
+    };
+    
+    zrUtil.inherits(K, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('k', K);
+    
+    return K;
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1058 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/line.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1720 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/map.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1141 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/pie.js


+ 459 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/radar.js

@@ -0,0 +1,459 @@
+/**
+ * echarts图表类:雷达图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Neil (杨骥, 511415343@qq.com)
+ */
+
+ define(function (require) {
+    var ChartBase = require('./base');
+    
+     // 图形依赖
+    var PolygonShape = require('zrender/shape/Polygon');
+     // 组件依赖
+    require('../component/polar');
+    
+    var ecConfig = require('../config');
+    // 雷达图默认参数
+    ecConfig.radar = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        legendHoverLink: true,
+        polarIndex: 0,
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                label: {
+                    show: false
+                },
+                lineStyle: {
+                    width: 2,
+                    type: 'solid'
+                }
+            },
+            emphasis: {
+                // color: 各异,
+                label: {
+                    show: false
+                }
+            }
+        },
+        // symbol: null,            // 拐点图形类型
+        symbolSize: 2               // 可计算特性参数,空数据拖拽提示图形大小
+        // symbolRotate: null,      // 图形旋转控制
+    };
+
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     * @constructor
+     * @exports Radar
+     */
+    function Radar(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+    
+    Radar.prototype = {
+        type : ecConfig.CHART_TYPE_RADAR,
+        /**
+         * 绘制图形
+         */
+        _buildShape : function () {
+            this.selectedMap = {};
+            this._symbol = this.option.symbolList;
+            this._queryTarget;
+            this._dropBoxList = [];
+            this._radarDataCounter = 0;
+            
+            var series = this.series;
+            var legend = this.component.legend;
+            var serieName;
+            for (var i = 0, l = series.length; i < l ; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_RADAR) {
+                    this.serie = this.reformOption(series[i]);
+                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                    serieName = this.serie.name || '';
+                    // 系列图例开关
+                    this.selectedMap[serieName] = 
+                        legend ? legend.isSelected(serieName) : true;
+                    
+                    if (this.selectedMap[serieName]) {
+                        this._queryTarget = [this.serie, this.option];
+    
+                        // 添加可拖拽提示框,多系列共用一个极坐标,第一个优先
+                        if (this.deepQuery(this._queryTarget, 'calculable')) {
+                            this._addDropBox(i);
+                        }
+                        this._buildSingleRadar(i);
+                        this.buildMark(i);
+                    }
+                }
+            }
+
+            this.addShapeList();
+        },
+
+        /**
+         * 构建数据图形
+         * @param {number} 序列的index
+         */
+        _buildSingleRadar : function (index) {
+            var legend = this.component.legend;
+            var iconShape;
+            var data = this.serie.data;
+            var defaultColor;
+            var name;
+            var pointList;
+            var calculable = this.deepQuery(this._queryTarget, 'calculable');
+           
+            for (var i = 0; i < data.length; i++) {
+                name = data[i].name || '';
+                
+                // 图例开关
+                this.selectedMap[name] = legend 
+                    ? legend.isSelected(name) : true;
+                if (!this.selectedMap[name]) {
+                    continue;
+                }
+                
+                 // 默认颜色策略
+                if (legend) {
+                    // 有图例则从图例中获取颜色定义
+                    defaultColor = legend.getColor(name);
+                    iconShape = legend.getItemShape(name);
+                    if (iconShape) {
+                        // 回调legend,换一个更形象的icon
+                        iconShape.style.brushType = this.deepQuery(
+                            [data[i], this.serie], 'itemStyle.normal.areaStyle'
+                        ) ? 'both' : 'stroke';
+                        legend.setItemShape(name, iconShape);
+                    }
+                }
+                else {
+                    // 全局颜色定义
+                    defaultColor = this.zr.getColor(i);
+                }
+
+                pointList = this._getPointList(this.serie.polarIndex, data[i]);
+                // 添加拐点形状
+                this._addSymbol(
+                    pointList, defaultColor, i, index, this.serie.polarIndex);
+                // 添加数据形状
+                this._addDataShape(
+                    pointList, defaultColor, data[i],
+                    index, i, calculable
+                );
+                this._radarDataCounter++;
+            }
+        },
+
+        /**
+         * 获取数据的点集
+         * @param {number} polarIndex
+         * @param {Array<Object>} 处理的数据
+         * @return {Array<Array<number>>} 点集
+         */
+        _getPointList : function (polarIndex, dataArr) {
+            var pointList = [];
+            var vector;
+            var polar = this.component.polar;
+
+            var value;
+            for (var i = 0, l = dataArr.value.length; i < l; i++) {
+                value = this.getDataFromOption(dataArr.value[i]);
+                vector = value != '-' 
+                         ? polar.getVector(polarIndex, i, value)
+                         : false;
+                if (vector) {
+                    pointList.push(vector);
+                } 
+            }
+            return pointList;
+        },
+        
+        /**
+         * 添加拐点
+         * @param {Array<Array<number>>} pointList 点集
+         * @param {string} defaultColor 默认填充颜色
+         * @param {object} data 数据
+         * @param {number} serieIndex
+         */
+        _addSymbol :function (pointList, defaultColor, dataIndex, seriesIndex, polarIndex) {
+            var series = this.series;
+            var itemShape;
+            var polar = this.component.polar;
+
+            for (var i = 0, l = pointList.length; i < l; i++) {
+                itemShape = this.getSymbolShape(
+                    this.deepMerge(
+                        [series[seriesIndex].data[dataIndex], series[seriesIndex]]
+                    ),
+                    seriesIndex, 
+                    series[seriesIndex].data[dataIndex].value[i], i,
+                    polar.getIndicatorText(polarIndex, i),
+                    pointList[i][0],    // x
+                    pointList[i][1],    // y
+                    this._symbol[this._radarDataCounter % this._symbol.length],
+                    defaultColor,
+                    '#fff',
+                    'vertical'
+                );
+                itemShape.zlevel = this.getZlevelBase();
+                itemShape.z = this.getZBase() + 1;
+                
+                ecData.set(itemShape, 'data', series[seriesIndex].data[dataIndex]);
+                ecData.set(itemShape, 'value', series[seriesIndex].data[dataIndex].value);
+                ecData.set(itemShape, 'dataIndex', dataIndex);
+                ecData.set(itemShape, 'special', i);
+                this.shapeList.push(itemShape);
+            }
+        },
+        
+        /**
+         * 添加数据图形
+         * @param {Array<Array<number>>} pointList 点集
+         * @param {string} defaultColor 默认填充颜色
+         * @param {object} data 数据
+         * @param {number} serieIndex
+         * @param {number} dataIndex
+         * @param {boolean} calcalable
+         */ 
+        _addDataShape : function (
+            pointList, defaultColor, data,
+            seriesIndex, dataIndex, calculable
+        ) {
+            var series = this.series;
+            // 多级控制
+            var queryTarget = [data, this.serie];
+            var nColor = this.getItemStyleColor(
+                this.deepQuery(
+                    queryTarget, 'itemStyle.normal.color'
+                ),
+                seriesIndex,
+                dataIndex,
+                data
+            );
+            var nLineWidth = this.deepQuery(
+                queryTarget, 'itemStyle.normal.lineStyle.width'
+            );
+            var nLineType = this.deepQuery(
+                queryTarget, 'itemStyle.normal.lineStyle.type'
+            );
+            var nAreaColor = this.deepQuery(
+                queryTarget, 'itemStyle.normal.areaStyle.color'
+            );
+            var nIsAreaFill = this.deepQuery(
+                queryTarget, 'itemStyle.normal.areaStyle'
+            );
+            var shape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style : {
+                    pointList   : pointList,
+                    brushType   : nIsAreaFill ? 'both' : 'stroke',
+                    color       : nAreaColor 
+                                  || nColor 
+                                  || (typeof defaultColor === 'string' 
+                                      ? zrColor.alpha(defaultColor,0.5) : defaultColor),
+                    strokeColor : nColor || defaultColor,
+                    lineWidth   : nLineWidth,
+                    lineType    : nLineType
+                },
+                highlightStyle : {
+                    brushType   : this.deepQuery(
+                                      queryTarget,
+                                      'itemStyle.emphasis.areaStyle'
+                                  ) || nIsAreaFill 
+                                  ? 'both' : 'stroke',
+                    color       : this.deepQuery(
+                                      queryTarget,
+                                      'itemStyle.emphasis.areaStyle.color'
+                                  ) 
+                                  || nAreaColor 
+                                  || nColor 
+                                  || (typeof defaultColor === 'string' 
+                                      ? zrColor.alpha(defaultColor,0.5) : defaultColor),
+                    strokeColor : this.getItemStyleColor(
+                                       this.deepQuery(
+                                           queryTarget, 'itemStyle.emphasis.color'
+                                       ),
+                                       seriesIndex,
+                                       dataIndex,
+                                       data
+                                   )
+                                   || nColor || defaultColor,
+                    lineWidth   : this.deepQuery(
+                                      queryTarget,
+                                      'itemStyle.emphasis.lineStyle.width'
+                                  ) || nLineWidth,
+                    lineType    : this.deepQuery(
+                                      queryTarget,
+                                      'itemStyle.emphasis.lineStyle.type'
+                                  ) || nLineType
+                }
+            };
+            ecData.pack(
+                shape,
+                series[seriesIndex],    // 系列
+                seriesIndex,            // 系列索引
+                data,                   // 数据
+                dataIndex,              // 数据索引
+                data.name,              // 数据名称
+                // 附加指标信息 
+                this.component.polar.getIndicator(series[seriesIndex].polarIndex)
+            );
+            if (calculable) {
+                shape.draggable = true;
+                this.setCalculable(shape);
+            }
+            
+            shape = new PolygonShape(shape); 
+            this.shapeList.push(shape);
+        },
+
+        /**
+         * 增加外围接受框
+         * @param {number} serie的序列
+         */
+        _addDropBox : function (index) {
+            var series = this.series;
+            var polarIndex = this.deepQuery(
+                this._queryTarget, 'polarIndex'
+            );
+            if (!this._dropBoxList[polarIndex]) {
+                var shape = this.component.polar.getDropBox(polarIndex);
+                shape.zlevel = this.getZlevelBase();
+                shape.z = this.getZBase();
+                
+                this.setCalculable(shape);
+                ecData.pack(shape, series, index, undefined, -1);
+                this.shapeList.push(shape);
+                this._dropBoxList[polarIndex] = true;
+            }
+        },
+
+        /**
+         * 数据项被拖拽出去,重载基类方法
+         */
+        ondragend : function (param, status) {
+            var series = this.series;
+            if (!this.isDragend || !param.target) {
+                // 没有在当前实例上发生拖拽行为则直接返回
+                return;
+            }
+
+            // 被拖拽图形元素
+            var target = param.target;
+
+            var seriesIndex = ecData.get(target, 'seriesIndex');
+            var dataIndex = ecData.get(target, 'dataIndex');
+
+            // 被拖拽的图形是饼图sector,删除被拖拽走的数据
+            this.component.legend && this.component.legend.del(
+                series[seriesIndex].data[dataIndex].name
+            );
+
+            series[seriesIndex].data.splice(dataIndex, 1);
+
+            // 别status = {}赋值啊!!
+            status.dragOut = true;
+            status.needRefresh = true;
+
+            // 处理完拖拽事件后复位
+            this.isDragend = false;
+
+            return;
+        },
+
+         /**
+         * 数据项被拖拽进来, 重载基类方法
+         */
+        ondrop : function (param, status) {
+            var series = this.series;
+            if (!this.isDrop || !param.target) {
+                // 没有在当前实例上发生拖拽行为则直接返回
+                return;
+            }
+
+            var target = param.target;      // 拖拽安放目标
+            var dragged = param.dragged;    // 当前被拖拽的图形对象
+
+            var seriesIndex = ecData.get(target, 'seriesIndex');
+            var dataIndex = ecData.get(target, 'dataIndex');
+
+            var data;
+            var legend = this.component.legend;
+            var value;
+
+            if (dataIndex === -1) {
+                data = {
+                    value : ecData.get(dragged, 'value'),
+                    name : ecData.get(dragged, 'name')
+                };
+
+                series[seriesIndex].data.push(data);
+
+                legend && legend.add(
+                    data.name,
+                    dragged.style.color || dragged.style.strokeColor
+                );
+            }
+            else {
+                // 数据被拖拽到某个数据项上,数据修改
+                var accMath = require('../util/accMath');
+                data = series[seriesIndex].data[dataIndex];
+                legend && legend.del(data.name);
+                data.name += this.option.nameConnector
+                             + ecData.get(dragged, 'name');
+                value = ecData.get(dragged, 'value');
+                for (var i = 0 ; i < value.length; i++) {
+                    data.value[i] = accMath.accAdd(data.value[i], value[i]);
+                }
+                
+                legend && legend.add(
+                    data.name,
+                    dragged.style.color || dragged.style.strokeColor
+                );
+            }
+
+            // 别status = {}赋值啊!!
+            status.dragIn = status.dragIn || true;
+
+            // 处理完拖拽事件后复位
+            this.isDrop = false;
+
+            return;
+        },
+
+        /**
+         * 刷新
+         */
+        refresh : function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        }
+    };
+    
+    zrUtil.inherits(Radar, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('radar', Radar);
+    
+    return Radar;
+});

+ 477 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/scatter.js

@@ -0,0 +1,477 @@
+/**
+ * echarts图表类:散点图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+    
+    // 图形依赖
+    var SymbolShape = require('../util/shape/Symbol');
+    // 组件依赖
+    require('../component/axis');
+    require('../component/grid');
+    require('../component/dataZoom');
+    require('../component/dataRange');
+    
+    var ecConfig = require('../config');
+    // 散点图默认参数
+    ecConfig.scatter = {
+        zlevel: 0,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        clickable: true,
+        legendHoverLink: true,
+        xAxisIndex: 0,
+        yAxisIndex: 0,
+        // symbol: null,        // 图形类型
+        symbolSize: 4,          // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
+        // symbolRotate: null,  // 图形旋转控制
+        large: false,           // 大规模散点图
+        largeThreshold: 2000,   // 大规模阀值,large为true且数据量>largeThreshold才启用大规模模式
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            },
+            emphasis: {
+                // color: '各异'
+                label: {
+                    show: false
+                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
+                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
+                    //           'inside'|'left'|'right'|'top'|'bottom'
+                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                }
+            }
+        }
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     */
+    function Scatter(ecTheme, messageCenter, zr, option, myChart){
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+    
+    Scatter.prototype = {
+        type: ecConfig.CHART_TYPE_SCATTER,
+        /**
+         * 绘制图形
+         */
+        _buildShape: function () {
+            var series = this.series;
+            this._sIndex2ColorMap = {};  // series默认颜色索引,seriesIndex索引到color
+            this._symbol = this.option.symbolList;
+            this._sIndex2ShapeMap = {};  // series图形类型,seriesIndex索引到_symbol
+            
+            this.selectedMap = {};
+            this.xMarkMap = {};
+            
+            var legend = this.component.legend;
+            var seriesArray = [];
+            var serie;                              // 临时映射变量
+            var serieName;                          // 临时映射变量
+            var iconShape;
+            var iconType;
+            for (var i = 0, l = series.length; i < l; i++) {
+                serie = series[i];
+                serieName = serie.name;
+                if (serie.type === ecConfig.CHART_TYPE_SCATTER) {
+                    series[i] = this.reformOption(series[i]);
+                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
+                    this._sIndex2ShapeMap[i] = this.query(serie, 'symbol')
+                                          || this._symbol[i % this._symbol.length];
+                    if (legend){
+                        this.selectedMap[serieName] = legend.isSelected(serieName);
+                        this._sIndex2ColorMap[i] = zrColor.alpha(legend.getColor(serieName), 0.5);
+                            
+                        iconShape = legend.getItemShape(serieName);
+                        if (iconShape) {
+                            // 回调legend,换一个更形象的icon
+                            var iconType = this._sIndex2ShapeMap[i];
+                            iconShape.style.brushType = iconType.match('empty') ? 'stroke' : 'both';
+                            iconType = iconType.replace('empty', '').toLowerCase();
+                            
+                            if (iconType.match('rectangle')) {
+                                iconShape.style.x += Math.round(
+                                    (iconShape.style.width - iconShape.style.height) / 2
+                                );
+                                iconShape.style.width = iconShape.style.height;
+                            }
+                            
+                            if (iconType.match('star')) {
+                                iconShape.style.n = (iconType.replace('star','') - 0) || 5;
+                                iconType = 'star';
+                            }
+                            
+                            if (iconType.match('image')) {
+                                iconShape.style.image = iconType.replace(
+                                    new RegExp('^image:\\/\\/'), ''
+                                );
+                                iconShape.style.x += Math.round(
+                                    (iconShape.style.width - iconShape.style.height) / 2
+                                );
+                                iconShape.style.width = iconShape.style.height;
+                                iconType = 'image';
+                            }
+            
+                            iconShape.style.iconType = iconType;
+                            legend.setItemShape(serieName, iconShape);
+                        }
+                    } 
+                    else {
+                        this.selectedMap[serieName] = true;
+                        this._sIndex2ColorMap[i] = zrColor.alpha(this.zr.getColor(i), 0.5);
+                    }
+                      
+                    if (this.selectedMap[serieName]) {
+                        seriesArray.push(i);
+                    }
+                }
+            }
+            
+            this._buildSeries(seriesArray);
+            
+            this.addShapeList();
+        },
+
+        /**
+         * 构建类目轴为水平方向的散点图系列
+         */
+        _buildSeries: function (seriesArray) {
+            if (seriesArray.length === 0) {
+                return;
+            }
+            var series = this.series;
+            var seriesIndex;
+            var serie;
+            var data;
+            var value;
+            var xAxis;
+            var yAxis; 
+
+            var pointList = {};
+            var x;
+            var y;
+            for (var j = 0, k = seriesArray.length; j < k; j++) {
+                seriesIndex = seriesArray[j];
+                serie = series[seriesIndex];
+                if (serie.data.length === 0) {
+                    continue;
+                }
+                
+                xAxis = this.component.xAxis.getAxis(serie.xAxisIndex || 0);
+                yAxis = this.component.yAxis.getAxis(serie.yAxisIndex || 0);
+                
+                pointList[seriesIndex] = [];
+                for (var i = 0, l = serie.data.length; i < l; i++) {
+                    data = serie.data[i];
+                    value = this.getDataFromOption(data, '-');
+                    if (value === '-' || value.length < 2) {
+                        // 数据格式不符
+                        continue;
+                    }
+                    x = xAxis.getCoord(value[0]);
+                    y = yAxis.getCoord(value[1]);
+                    pointList[seriesIndex].push([
+                        x,                  // 横坐标
+                        y,                  // 纵坐标
+                        i,                  // 数据index
+                        data.name || ''     // 名称
+                    ]);
+                    
+                }
+                this.xMarkMap[seriesIndex] = this._markMap(
+                    xAxis, yAxis, serie.data, pointList[seriesIndex]
+                ); 
+                this.buildMark(seriesIndex);
+            }
+            
+            // console.log(pointList)
+            this._buildPointList(pointList);
+        },
+        
+        _markMap: function (xAxis, yAxis, data, pointList) {
+            var xMarkMap = {
+                min0: Number.POSITIVE_INFINITY,
+                max0: Number.NEGATIVE_INFINITY,
+                sum0: 0,
+                counter0: 0,
+                average0: 0,
+                min1: Number.POSITIVE_INFINITY,
+                max1: Number.NEGATIVE_INFINITY,
+                sum1: 0,
+                counter1: 0,
+                average1: 0
+            };
+            var value;
+            for (var i = 0, l = pointList.length; i < l; i++) {
+                /**
+                x,                  // 横坐标
+                y,                  // 纵坐标
+                i,                  // 数据index
+                data.name || ''     // 名称 
+                 */
+                value = data[pointList[i][2]].value || data[pointList[i][2]];
+                // 横轴
+                if (xMarkMap.min0 > value[0]) {
+                    xMarkMap.min0 = value[0];
+                    xMarkMap.minY0 = pointList[i][1];
+                    xMarkMap.minX0 = pointList[i][0];
+                }
+                if (xMarkMap.max0 < value[0]) {
+                    xMarkMap.max0 = value[0];
+                    xMarkMap.maxY0 = pointList[i][1];
+                    xMarkMap.maxX0 = pointList[i][0];
+                }
+                xMarkMap.sum0 += value[0];
+                xMarkMap.counter0++;
+                
+                // 纵轴
+                if (xMarkMap.min1 > value[1]) {
+                    xMarkMap.min1 = value[1];
+                    xMarkMap.minY1 = pointList[i][1];
+                    xMarkMap.minX1 = pointList[i][0];
+                }
+                if (xMarkMap.max1 < value[1]) {
+                    xMarkMap.max1 = value[1];
+                    xMarkMap.maxY1 = pointList[i][1];
+                    xMarkMap.maxX1 = pointList[i][0];
+                }
+                xMarkMap.sum1 += value[1];
+                xMarkMap.counter1++;
+            }
+            
+            var gridX = this.component.grid.getX();
+            var gridXend = this.component.grid.getXend();
+            var gridY = this.component.grid.getY();
+            var gridYend = this.component.grid.getYend();
+            
+            xMarkMap.average0 = xMarkMap.sum0 / xMarkMap.counter0;
+            var x = xAxis.getCoord(xMarkMap.average0); 
+            // 横轴平均纵向
+            xMarkMap.averageLine0 = [
+                [x, gridYend],
+                [x, gridY]
+            ];
+            xMarkMap.minLine0 = [
+                [xMarkMap.minX0, gridYend],
+                [xMarkMap.minX0, gridY]
+            ];
+            xMarkMap.maxLine0 = [
+                [xMarkMap.maxX0, gridYend],
+                [xMarkMap.maxX0, gridY]
+            ];
+            
+            xMarkMap.average1 = xMarkMap.sum1 / xMarkMap.counter1;
+            var y = yAxis.getCoord(xMarkMap.average1);
+            // 纵轴平均横向
+            xMarkMap.averageLine1 = [
+                [gridX, y],
+                [gridXend, y]
+            ];
+            xMarkMap.minLine1 = [
+                [gridX, xMarkMap.minY1],
+                [gridXend, xMarkMap.minY1]
+            ];
+            xMarkMap.maxLine1 = [
+                [gridX, xMarkMap.maxY1],
+                [gridXend, xMarkMap.maxY1]
+            ];
+            
+            return xMarkMap;
+        },
+        
+        /**
+         * 生成折线和折线上的拐点
+         */
+        _buildPointList: function (pointList) {
+            var series = this.series;
+            var serie;
+            var seriesPL;
+            var singlePoint;
+            var shape;
+            for (var seriesIndex in pointList) {
+                serie = series[seriesIndex];
+                seriesPL = pointList[seriesIndex];                
+                if (serie.large && serie.data.length > serie.largeThreshold) {
+                    this.shapeList.push(this._getLargeSymbol(
+                        serie,
+                        seriesPL, 
+                        this.getItemStyleColor(
+                            this.query(
+                                serie, 'itemStyle.normal.color'
+                            ),
+                            seriesIndex,
+                            -1
+                        ) || this._sIndex2ColorMap[seriesIndex]
+                    ));
+                    continue;
+                }
+
+                /*
+                 * pointlist=[
+                 *      0  x,
+                 *      1  y, 
+                 *      2  数据index
+                 *      3  名称
+                 * ]
+                 */
+                
+                for (var i = 0, l = seriesPL.length; i < l; i++) {
+                    singlePoint = seriesPL[i];
+                    shape = this._getSymbol(
+                        seriesIndex,    // seriesIndex
+                        singlePoint[2], // dataIndex
+                        singlePoint[3], // name
+                        singlePoint[0], // x
+                        singlePoint[1]  // y
+                    );
+                    shape && this.shapeList.push(shape);
+                }
+            }
+            // console.log(this.shapeList)
+        },
+
+        /**
+         * 生成折线图上的拐点图形
+         */
+        _getSymbol: function (seriesIndex, dataIndex, name, x, y) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var data = serie.data[dataIndex];
+            
+            var dataRange = this.component.dataRange;
+            var rangColor;
+            if (dataRange) {
+                rangColor = isNaN(data[2]) 
+                            ? this._sIndex2ColorMap[seriesIndex]
+                            : dataRange.getColor(data[2]);
+                if (!rangColor) {
+                    return null;
+                }
+            }
+            else {
+                rangColor = this._sIndex2ColorMap[seriesIndex];
+            }
+            
+            var itemShape = this.getSymbolShape(
+                serie, seriesIndex, data, dataIndex, name, 
+                x, y,
+                this._sIndex2ShapeMap[seriesIndex], 
+                rangColor,
+                'rgba(0,0,0,0)',
+                'vertical'
+            );
+            itemShape.zlevel = serie.zlevel;
+            itemShape.z = serie.z;
+            
+            itemShape._main = true;
+            return itemShape;
+        },
+        
+        _getLargeSymbol: function (serie, pointList, nColor) {
+            return new SymbolShape({
+                zlevel: serie.zlevel,
+                z: serie.z,
+                _main: true,
+                hoverable: false,
+                style: {
+                    pointList: pointList,
+                    color: nColor,
+                    strokeColor: nColor
+                },
+                highlightStyle: {
+                    pointList: []
+                }
+            });
+        },
+        
+        // 位置转换
+        getMarkCoord: function (seriesIndex, mpData) {
+            var serie = this.series[seriesIndex];
+            var xMarkMap = this.xMarkMap[seriesIndex];
+            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
+            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
+            var pos;
+            
+            if (mpData.type
+                && (mpData.type === 'max' || mpData.type === 'min' || mpData.type === 'average')
+            ) {
+                // 特殊值内置支持
+                // 默认取纵值
+                var valueIndex = mpData.valueIndex != null ? mpData.valueIndex : 1;
+                pos = [
+                    xMarkMap[mpData.type + 'X' + valueIndex],
+                    xMarkMap[mpData.type + 'Y' + valueIndex],
+                    xMarkMap[mpData.type + 'Line' + valueIndex],
+                    xMarkMap[mpData.type + valueIndex]
+                ];
+            }
+            else {
+                pos = [
+                    typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
+                        ? xAxis.getCoordByIndex(mpData.xAxis || 0)
+                        : xAxis.getCoord(mpData.xAxis || 0),
+                    
+                    typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
+                        ? yAxis.getCoordByIndex(mpData.yAxis || 0)
+                        : yAxis.getCoord(mpData.yAxis || 0)
+                ];
+            }
+            
+            return pos;
+        },
+
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+            
+            this.backupShapeList();
+            this._buildShape();
+        },
+        
+        /**
+         * 值域响应
+         * @param {Object} param
+         * @param {Object} status
+         */
+        ondataRange: function (param, status) {
+            if (this.component.dataRange) {
+                this.refresh();
+                status.needRefresh = true;
+            }
+            return;
+        }
+    };
+    
+    zrUtil.inherits(Scatter, ChartBase);
+    
+    // 图表注册
+    require('../chart').define('scatter', Scatter);
+    
+    return Scatter;
+});

+ 630 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/tree.js

@@ -0,0 +1,630 @@
+/**
+ * echarts图表类:树图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Loutongbing (娄同兵, loutongbing@126.com)
+ */
+
+define(function (require) {
+    var ChartBase = require('./base');
+    var GOLDEN_SECTION = 0.618;
+    // 图形依赖
+    var IconShape = require('../util/shape/Icon');
+    var ImageShape = require('zrender/shape/Image');
+    var LineShape = require('zrender/shape/Line');
+    var BezierCurveShape = require('zrender/shape/BezierCurve');
+    // 布局依赖
+    var TreeLayout = require('../layout/Tree');
+    // 数据依赖
+    var TreeData = require('../data/Tree');
+
+    var ecConfig = require('../config');
+    // 默认参数
+    ecConfig.tree = {
+        zlevel: 1,                  // 一级层叠
+        z: 2,                       // 二级层叠
+        calculable: false,
+        clickable: true,
+        rootLocation: {},
+        orient: 'vertical',
+        symbol: 'circle',
+        symbolSize: 20,
+        nodePadding: 30,
+        layerPadding: 100,
+        /*rootLocation: {
+            x: 'center' | 'left' | 'right' | 'x%' | {number},
+            y: 'center' | 'top' | 'bottom' | 'y%' | {number}
+        },*/
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                label: {
+                    show: true
+                },
+                lineStyle: {
+                    width: 1,
+                    color: '#777',
+                    type: 'curve' // curve
+                }
+            },
+            emphasis: {
+
+            }
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var zrConfig = require('zrender/config');
+    var zrEvent = require('zrender/tool/event');
+    var zrUtil = require('zrender/tool/util');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     * @constructor
+     * @exports Tree
+     */
+    function Tree(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+        this.refresh(option);
+    }
+    Tree.prototype = {
+        type : ecConfig.CHART_TYPE_TREE,
+        /**
+         * 构建单个
+         *
+         * @param {Object} data 数据
+         */
+        _buildShape : function (series, seriesIndex) {
+            var data = series.data[0];
+            this.tree = TreeData.fromOptionData(data.name, data.children);
+            // 添加root的data
+            this.tree.root.data = data;
+            // 根据root坐标 方向 对每个节点的坐标进行映射
+            this._setTreeShape(series);
+            // 递归画出树节点与连接线
+            this.tree.traverse(
+                function (treeNode) {
+                    this._buildItem(
+                        treeNode,
+                        series,
+                        seriesIndex
+                    );
+                    // 画连接线
+                    if (treeNode.children.length > 0) {
+                        this._buildLink(
+                            treeNode,
+                            series
+                        );
+                    }
+                },
+                this
+            );
+            var panable = series.roam === true || series.roam === 'move';
+            var zoomable = series.roam === true || series.roam === 'scale';
+            // Enable pan and zooom
+            this.zr.modLayer(this.getZlevelBase(), {
+                panable: panable,
+                zoomable: zoomable
+            });
+            if (
+                this.query('markPoint.effect.show')
+                || this.query('markLine.effect.show')
+            ) {
+                this.zr.modLayer(ecConfig.EFFECT_ZLEVEL, {
+                    panable: panable,
+                    zoomable: zoomable
+                });
+            }
+            this.addShapeList();
+        },
+
+        /**
+         * 构建单个item
+         */
+        _buildItem : function (
+            treeNode,
+            serie,
+            seriesIndex
+        ) {
+            var queryTarget = [treeNode.data, serie];
+            var symbol = this.deepQuery(queryTarget, 'symbol');
+            // 多级控制
+            var normal = this.deepMerge(
+                queryTarget,
+                'itemStyle.normal'
+            ) || {};
+            var emphasis = this.deepMerge(
+                queryTarget,
+                'itemStyle.emphasis'
+            ) || {};
+            var normalColor = normal.color || this.zr.getColor();
+            var emphasisColor = emphasis.color || this.zr.getColor();
+            var angle = -treeNode.layout.angle || 0;
+            // 根节点不旋转
+            if (treeNode.id === this.tree.root.id) {
+                angle = 0;
+            }
+            var textPosition = 'right';
+            if (Math.abs(angle) >= Math.PI / 2 && Math.abs(angle) < Math.PI * 3 / 2) {
+                angle += Math.PI;
+                textPosition = 'left';
+            }
+            var rotation = [
+                angle,
+                treeNode.layout.position[0],
+                treeNode.layout.position[1]
+            ];
+            var shape = new IconShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase() + 1,
+                rotation: rotation,
+                clickable: this.deepQuery(queryTarget, 'clickable'),
+                style: {
+                    x: treeNode.layout.position[0] - treeNode.layout.width * 0.5,
+                    y: treeNode.layout.position[1] - treeNode.layout.height * 0.5,
+                    width: treeNode.layout.width,
+                    height: treeNode.layout.height,
+                    iconType: symbol,
+                    color: normalColor,
+                    brushType: 'both',
+                    lineWidth: normal.borderWidth,
+                    strokeColor: normal.borderColor
+                },
+                highlightStyle: {
+                    color: emphasisColor,
+                    lineWidth: emphasis.borderWidth,
+                    strokeColor: emphasis.borderColor
+                }
+            });
+            if (shape.style.iconType.match('image')) {
+                shape.style.image = shape.style.iconType.replace(
+                    new RegExp('^image:\\/\\/'), ''
+                );
+                shape = new ImageShape({
+                    rotation: rotation,
+                    style: shape.style,
+                    highlightStyle: shape.highlightStyle,
+                    clickable: shape.clickable,
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase()
+                });
+            }
+            // 节点标签样式
+            if (this.deepQuery(queryTarget, 'itemStyle.normal.label.show')) {
+                shape.style.text = treeNode.data.label == null ? treeNode.id : treeNode.data.label;
+                shape.style.textPosition = this.deepQuery(
+                    queryTarget, 'itemStyle.normal.label.position'
+                );
+                // 极坐标另外计算 时钟哪个侧面
+                if (serie.orient === 'radial' && shape.style.textPosition !== 'inside') {
+                    shape.style.textPosition = textPosition;
+                }
+                shape.style.textColor = this.deepQuery(
+                    queryTarget, 'itemStyle.normal.label.textStyle.color'
+                );
+                shape.style.textFont = this.getFont(this.deepQuery(
+                    queryTarget, 'itemStyle.normal.label.textStyle'
+                ) || {});
+            }
+
+            if (this.deepQuery(queryTarget, 'itemStyle.emphasis.label.show')) {
+                shape.highlightStyle.textPosition = this.deepQuery(
+                    queryTarget, 'itemStyle.emphasis.label.position'
+                );
+                shape.highlightStyle.textColor = this.deepQuery(
+                    queryTarget, 'itemStyle.emphasis.label.textStyle.color'
+                );
+                shape.highlightStyle.textFont = this.getFont(this.deepQuery(
+                    queryTarget, 'itemStyle.emphasis.label.textStyle'
+                ) || {});
+            }
+            // todo
+            ecData.pack(
+                shape,
+                serie, seriesIndex,
+                treeNode.data, 0,
+                treeNode.id
+            );
+            this.shapeList.push(shape);
+        },
+
+        _buildLink : function (
+            parentNode,
+            serie
+        ) {
+            var lineStyle = serie.itemStyle.normal.lineStyle;
+            // 折线另外计算
+            if (lineStyle.type === 'broken') {
+                this._buildBrokenLine(
+                    parentNode,
+                    lineStyle,
+                    serie
+                );
+                return;
+            }
+            for (var i = 0; i < parentNode.children.length; i++) {
+                var xStart = parentNode.layout.position[0];
+                var yStart = parentNode.layout.position[1];
+                var xEnd = parentNode.children[i].layout.position[0];
+                var yEnd = parentNode.children[i].layout.position[1];
+                switch (lineStyle.type) {
+                    case 'curve':
+                        this._buildBezierCurve(
+                            parentNode,
+                            parentNode.children[i],
+                            lineStyle,
+                            serie
+                        );
+                        break;
+                    // 折线
+                    case 'broken':
+                        break;
+                    // default画直线
+                    default:
+                        var shape = this._getLine(
+                            xStart,
+                            yStart,
+                            xEnd,
+                            yEnd,
+                            lineStyle
+                        );
+                        this.shapeList.push(shape);
+                }
+            }
+        },
+        _buildBrokenLine: function (
+            parentNode,
+            lineStyle,
+            serie
+        ) {
+            // 引用_getLine需要把type改为solid
+            var solidLineStyle = zrUtil.clone(lineStyle);
+            solidLineStyle.type = 'solid';
+            var shapes = [];
+            var xStart = parentNode.layout.position[0];
+            var yStart = parentNode.layout.position[1];
+            var orient = serie.orient;
+
+            // 子节点的y
+            var yEnd = parentNode.children[0].layout.position[1];
+            // 中点x y
+            var xMiddle = xStart;
+            var yMiddle = yStart + (yEnd - yStart) * (1 - GOLDEN_SECTION);
+            // 中线的起始
+            var xMiddleStart = parentNode.children[0].layout.position[0];
+            var yMiddleStart = yMiddle;
+            var xMiddleEnd = parentNode.children[parentNode.children.length - 1].layout.position[0];
+            var yMiddleEnd = yMiddle;
+            // 水平状态
+            if (orient === 'horizontal') {
+                var xEnd = parentNode.children[0].layout.position[0];
+                xMiddle = xStart + (xEnd - xStart) * (1 - GOLDEN_SECTION);
+                yMiddle = yStart;
+                xMiddleStart = xMiddle;
+                yMiddleStart = parentNode.children[0].layout.position[1];
+                xMiddleEnd = xMiddle;
+                yMiddleEnd = parentNode.children[parentNode.children.length - 1].layout.position[1];
+            }
+            // 第一条 从根节点垂直向下
+            shapes.push(
+                this._getLine(
+                    xStart,
+                    yStart,
+                    xMiddle,
+                    yMiddle,
+                    solidLineStyle
+                )
+            );
+            // 第二条 横向
+            shapes.push(
+                this._getLine(
+                    xMiddleStart,
+                    yMiddleStart,
+                    xMiddleEnd,
+                    yMiddleEnd,
+                    solidLineStyle
+                )
+            );
+            // 第三条 垂直向下到子节点
+            for (var i = 0; i < parentNode.children.length; i++) {
+                xEnd = parentNode.children[i].layout.position[0];
+                yEnd = parentNode.children[i].layout.position[1];
+                // 水平状态
+                if (orient === 'horizontal') {
+                    yMiddleStart = yEnd;
+                }
+                else {
+                    xMiddleStart = xEnd;
+                }
+                shapes.push(
+                    this._getLine(
+                        xMiddleStart,
+                        yMiddleStart,
+                        xEnd,
+                        yEnd,
+                        solidLineStyle
+                    )
+                );
+            }
+            this.shapeList = this.shapeList.concat(shapes);
+        },
+        _getLine: function (
+            xStart,
+            yStart,
+            xEnd,
+            yEnd,
+            lineStyle
+        ) {
+            if (xStart === xEnd) {
+                xStart = xEnd = this.subPixelOptimize(xStart, lineStyle.width);
+            }
+            if (yStart === yEnd) {
+                yStart = yEnd = this.subPixelOptimize(yStart, lineStyle.width);
+            }
+            return new LineShape({
+                zlevel: this.getZlevelBase(),
+                hoverable: false,
+                style: zrUtil.merge(
+                    {
+                        xStart: xStart,
+                        yStart: yStart,
+                        xEnd: xEnd,
+                        yEnd: yEnd,
+                        lineType: lineStyle.type,
+                        strokeColor: lineStyle.color,
+                        lineWidth: lineStyle.width
+                    },
+                    lineStyle,
+                    true
+                )
+            });
+        },
+        _buildBezierCurve: function (
+            parentNode,
+            treeNode,
+            lineStyle,
+            serie
+        ) {
+            var offsetRatio = GOLDEN_SECTION;
+            var orient = serie.orient;
+            var xStart = parentNode.layout.position[0];
+            var yStart = parentNode.layout.position[1];
+            var xEnd = treeNode.layout.position[0];
+            var yEnd = treeNode.layout.position[1];
+            var cpX1 = xStart;
+            var cpY1 = (yEnd - yStart) * offsetRatio + yStart;
+            var cpX2 = xEnd;
+            var cpY2 = (yEnd - yStart) * (1 - offsetRatio) + yStart;
+            if (orient === 'horizontal') {
+                cpX1 = (xEnd - xStart) * offsetRatio + xStart;
+                cpY1 = yStart;
+                cpX2 = (xEnd - xStart) * (1 - offsetRatio) + xStart;
+                cpY2 = yEnd;
+            }
+            else if (orient === 'radial') {
+                // 根节点 画直线
+                if (parentNode.id === this.tree.root.id) {
+                    cpX1 = (xEnd - xStart) * offsetRatio + xStart;
+                    cpY1 = (yEnd - yStart) * offsetRatio + yStart;
+                    cpX2 = (xEnd - xStart) * (1 - offsetRatio) + xStart;
+                    cpY2 = (yEnd - yStart) * (1 - offsetRatio) + yStart;
+                }
+                else {
+                    var xStartOrigin = parentNode.layout.originPosition[0];
+                    var yStartOrigin = parentNode.layout.originPosition[1];
+                    var xEndOrigin = treeNode.layout.originPosition[0];
+                    var yEndOrigin = treeNode.layout.originPosition[1];
+                    var rootX = this.tree.root.layout.position[0];
+                    var rootY = this.tree.root.layout.position[1];
+
+                    cpX1 = xStartOrigin;
+                    cpY1 = (yEndOrigin - yStartOrigin) * offsetRatio + yStartOrigin;
+                    cpX2 = xEndOrigin;
+                    cpY2 = (yEndOrigin - yStartOrigin) * (1 - offsetRatio) + yStartOrigin;
+                    var rad = (cpX1 - this.minX) / this.width * Math.PI * 2;
+                    cpX1 = cpY1 * Math.cos(rad) + rootX;
+                    cpY1 = cpY1 * Math.sin(rad) + rootY;
+
+                    rad = (cpX2 - this.minX) / this.width * Math.PI * 2;
+                    cpX2 = cpY2 * Math.cos(rad) + rootX;
+                    cpY2 = cpY2 * Math.sin(rad) + rootY;
+                }
+            }
+            var shape = new BezierCurveShape({
+                zlevel: this.getZlevelBase(),
+                hoverable: false,
+                style: zrUtil.merge(
+                    {
+                        xStart: xStart,
+                        yStart: yStart,
+                        cpX1: cpX1,
+                        cpY1: cpY1,
+                        cpX2: cpX2,
+                        cpY2: cpY2,
+                        xEnd: xEnd,
+                        yEnd: yEnd,
+                        strokeColor: lineStyle.color,
+                        lineWidth: lineStyle.width
+                    },
+                    lineStyle,
+                    true
+                )
+            });
+            this.shapeList.push(shape);
+        },
+        _setTreeShape : function (serie) {
+            // 跑出来树的layout
+            var treeLayout = new TreeLayout(
+                {
+                    nodePadding: serie.nodePadding,
+                    layerPadding: serie.layerPadding
+                }
+            );
+            this.tree.traverse(
+                function (treeNode) {
+                    var queryTarget = [treeNode.data, serie];
+                    var symbolSize = this.deepQuery(queryTarget, 'symbolSize');
+                    if (typeof symbolSize === 'number') {
+                        symbolSize = [symbolSize, symbolSize];
+                    }
+                    treeNode.layout = {
+                        width: symbolSize[0], // 节点大小
+                        height: symbolSize[1]
+                    };
+                },
+                this
+            );
+            treeLayout.run(this.tree);
+            // 树的方向
+            var orient = serie.orient;
+            var rootX = serie.rootLocation.x;
+            var rootY = serie.rootLocation.y;
+            var zrWidth = this.zr.getWidth();
+            var zrHeight = this.zr.getHeight();
+            if (rootX === 'center') {
+                rootX = zrWidth * 0.5;
+            }
+            else {
+                rootX = this.parsePercent(rootX, zrWidth);
+            }
+            if (rootY === 'center') {
+                rootY = zrHeight * 0.5;
+            }
+            else {
+                rootY = this.parsePercent(rootY, zrHeight);
+            }
+            rootY = this.parsePercent(rootY, zrHeight);
+            // 水平树
+            if (orient === 'horizontal') {
+                rootX = isNaN(rootX) ? 10 : rootX;
+                rootY = isNaN(rootY) ? zrHeight * 0.5 : rootY;
+            }
+            // 极坐标
+            if (orient === 'radial') {
+                rootX = isNaN(rootX) ? zrWidth * 0.5 : rootX;
+                rootY = isNaN(rootY) ? zrHeight * 0.5 : rootY;
+            }
+            // 纵向树
+            else {
+                rootX = isNaN(rootX) ? zrWidth * 0.5 : rootX;
+                rootY = isNaN(rootY) ? 10 : rootY;
+            }
+            // tree layout自动算出来的root的坐标
+            var originRootX = this.tree.root.layout.position[0];
+            // 若是极坐标,则求最大最小值
+            if (orient === 'radial') {
+                var minX = Infinity;
+                var maxX = 0;
+                var maxWidth = 0;
+                this.tree.traverse(
+                    function (treeNode) {
+                        maxX = Math.max(maxX, treeNode.layout.position[0]);
+                        minX = Math.min(minX, treeNode.layout.position[0]);
+                        maxWidth = Math.max(maxWidth, treeNode.layout.width);
+                    }
+                );
+                //  2 * maxWidth 还不大好但至少保证绝不会重叠 todo
+                this.width = maxX - minX + 2 * maxWidth;
+                this.minX = minX;
+            }
+            this.tree.traverse(
+                function (treeNode) {
+                    var x;
+                    var y;
+                    if (orient === 'vertical' && serie.direction === 'inverse') {
+                        x = treeNode.layout.position[0] - originRootX + rootX;
+                        y = rootY - treeNode.layout.position[1];
+                    }
+                    else if (orient === 'vertical') {
+                        x = treeNode.layout.position[0] - originRootX + rootX;
+                        y = treeNode.layout.position[1] + rootY;
+                    }
+                    else if (orient === 'horizontal' && serie.direction === 'inverse') {
+                        y = treeNode.layout.position[0] - originRootX + rootY;
+                        x = rootX - treeNode.layout.position[1];
+                    }
+                    else if (orient === 'horizontal') {
+                        y = treeNode.layout.position[0] - originRootX + rootY;
+                        x = treeNode.layout.position[1] + rootX;
+                    }
+                    // 极坐标
+                    else {
+                        x = treeNode.layout.position[0];
+                        y = treeNode.layout.position[1];
+                        // 记录原始坐标,以后计算贝塞尔曲线的控制点
+                        treeNode.layout.originPosition = [x, y];
+                        var r = y;
+                        var angle = (x - minX) / this.width * Math.PI * 2;
+                        x = r * Math.cos(angle) + rootX;
+                        y = r * Math.sin(angle) + rootY;
+                        treeNode.layout.angle = angle;
+                    }
+                    treeNode.layout.position[0] = x;
+                    treeNode.layout.position[1] = y;
+                },
+                this
+            );
+        },
+        /*
+         * 刷新
+         */
+/*        refresh: function (newOption) {
+            this.clear();
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+
+            this._buildShape();
+        }*/
+        refresh: function (newOption) {
+            this.clear();
+
+            if (newOption) {
+                this.option = newOption;
+                this.series = this.option.series;
+            }
+
+            // Map storing all trees of series
+
+            var series = this.series;
+            var legend = this.component.legend;
+
+            for (var i = 0; i < series.length; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_TREE) {
+                    series[i] = this.reformOption(series[i]);
+
+                    var seriesName = series[i].name || '';
+                    this.selectedMap[seriesName] = 
+                        legend ? legend.isSelected(seriesName) : true;
+                    if (!this.selectedMap[seriesName]) {
+                        continue;
+                    }
+
+                    this._buildSeries(series[i], i);
+                }
+            }
+        },
+
+        _buildSeries: function (series, seriesIndex) {
+            /*var tree = Tree.fromOptionData('root', series.data);
+
+            this._treesMap[seriesIndex] = tree;*/
+
+            // this._buildTreemap(tree.root, seriesIndex);
+            this._buildShape(series, seriesIndex);
+        }
+    };
+
+    zrUtil.inherits(Tree, ChartBase);
+
+    // 图表注册
+    require('../chart').define('tree', Tree);
+
+    return Tree;
+});

+ 586 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/treemap.js

@@ -0,0 +1,586 @@
+/**
+ * echarts图表类:矩形树图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Loutongbing (娄同兵, loutongbing@126.com)
+ */
+
+define(function (require) {
+    var ChartBase = require('./base');
+    // 图形依赖
+    var toolArea = require('zrender/tool/area');
+    // 图形依赖
+    var RectangleShape = require('zrender/shape/Rectangle');
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    // 布局依赖
+    var TreeMapLayout = require('../layout/TreeMap');
+    // 数据依赖
+    var Tree = require('../data/Tree');
+
+    var ecConfig = require('../config');
+    // 维恩图默认参数
+    ecConfig.treemap = {
+        zlevel: 0,                  // 一级层叠
+        z: 1,                       // 二级层叠
+        calculable: false,
+        clickable: true,
+        center: ['50%', '50%'],
+        size: ['80%', '80%'],
+        root: '',
+        itemStyle: {
+            normal: {
+                // color: 各异,
+                label: {
+                    show: true,
+                    x: 5,
+                    y: 12,
+                    textStyle: {
+                        align: 'left',
+                        color: '#000',
+                        fontFamily: 'Arial',
+                        fontSize: 13,
+                        fontStyle: 'normal',
+                        fontWeight: 'normal'
+                    }
+                },
+                breadcrumb: {
+                    show: true,
+                    textStyle: {}
+                },
+                borderWidth: 1,
+                borderColor: '#ccc',
+                childBorderWidth: 1,
+                childBorderColor: '#ccc'
+            },
+            emphasis: {}
+        }
+    };
+
+    var ecData = require('../util/ecData');
+    var zrConfig = require('zrender/config');
+    var zrEvent = require('zrender/tool/event');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     * @constructor
+     * @exports Treemap
+     */
+    function Treemap(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+        this.refresh(option);
+        var self = this;
+        self._onclick = function (params) {
+            return self.__onclick(params);
+        };
+        self.zr.on(zrConfig.EVENT.CLICK, self._onclick);
+    }
+    Treemap.prototype = {
+        type : ecConfig.CHART_TYPE_TREEMAP,
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            this.clear();
+
+            if (newOption) {
+                this.option = newOption;
+                this.series = this.option.series;
+            }
+
+            // Map storing all trees of series
+            this._treesMap = {};
+
+            var series = this.series;
+            var legend = this.component.legend;
+
+            for (var i = 0; i < series.length; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_TREEMAP) {
+                    series[i] = this.reformOption(series[i]);
+
+                    var seriesName = series[i].name || '';
+                    this.selectedMap[seriesName] = 
+                        legend ? legend.isSelected(seriesName) : true;
+                    if (!this.selectedMap[seriesName]) {
+                        continue;
+                    }
+
+                    this._buildSeries(series[i], i);
+                }
+            }
+        },
+
+        _buildSeries: function(series, seriesIndex) {
+            var tree = Tree.fromOptionData(series.name, series.data);
+
+            this._treesMap[seriesIndex] = tree;
+
+            var treeRoot = series.root && tree.getNodeById(series.root) || tree.root;
+            this._buildTreemap(treeRoot, seriesIndex);
+        },
+
+        /**
+         * 构建单个
+         *
+         * @param {Object} data 数据
+         */
+        _buildTreemap : function (treeRoot, seriesIndex) {
+
+            // 清除之前的 shapes
+            var shapeList = this.shapeList;
+            for (var i = 0; i < shapeList.length;) {
+                var shape = shapeList[i];
+                if (ecData.get(shape, 'seriesIndex') === seriesIndex) {
+                    this.zr.delShape(shapeList[i]);
+                    shapeList.splice(i, 1);
+                }
+                else {
+                    i++;
+                }
+            }
+
+            var currentShapeLen = shapeList.length;
+            var series = this.series[seriesIndex];
+
+            var itemStyle = series.itemStyle;
+            var treemapWidth = this.parsePercent(series.size[0], this.zr.getWidth()) || 400;
+            var treemapHeight = this.parsePercent(series.size[1], this.zr.getHeight()) || 500;
+            var center = this.parseCenter(this.zr, series.center);
+            var treemapX = center[0] -  treemapWidth * 0.5;
+            var treemapY = center[1] -  treemapHeight * 0.5;
+
+            var treemapArea = treemapWidth * treemapHeight; // 计算总面积
+            // 遍历数组,通过value与area0计算实际面积area
+            var sum = 0;
+            var areaArr = [];
+
+            var children = treeRoot.children;
+            for (var i = 0; i < children.length; i++) {
+                sum += children[i].data.value;
+            }
+            for (var j = 0; j < children.length; j++) {
+                areaArr.push(children[j].data.value * treemapArea / sum);
+            }
+            var treeMapLayout = new TreeMapLayout({
+                x: treemapX,
+                y: treemapY,
+                width: treemapWidth,
+                height: treemapHeight
+            });
+            var locationArr = treeMapLayout.run(areaArr);
+
+            for (var k = 0; k < locationArr.length; k++) {
+                var dataItem = children[k].data;
+                var rect = locationArr[k];
+                var queryTarget = [dataItem.itemStyle, itemStyle];
+                var itemStyleMerged = this.deepMerge(queryTarget);
+                if (!itemStyleMerged.normal.color) {
+                    itemStyleMerged.normal.color = this.zr.getColor(k);
+                }
+                if (!itemStyleMerged.emphasis.color) {
+                    itemStyleMerged.emphasis.color = itemStyleMerged.normal.color;
+                }
+                this._buildItem(
+                    dataItem,
+                    itemStyleMerged,
+                    rect,
+                    seriesIndex,
+                    k
+                );
+                // 绘制二级节点
+                if (dataItem.children) {
+                    this._buildChildrenTreemap(
+                        dataItem.children,
+                        itemStyleMerged,
+                        rect,
+                        seriesIndex
+                    );
+                }
+            }
+
+            if (this.query(series, 'itemStyle.normal.breadcrumb.show')) {
+                this._buildBreadcrumb(
+                    treeRoot, seriesIndex, treemapX, treemapY + treemapHeight
+                );   
+            }
+
+            for (var i = currentShapeLen; i < shapeList.length; i++) {
+                this.zr.addShape(shapeList[i]);
+            }
+        },
+
+        /**
+         * 构建单个item
+         */
+        _buildItem : function (
+            dataItem,
+            itemStyle,
+            rect,
+            seriesIndex,
+            dataIndex
+        ) {
+            var series = this.series;
+            var rectangle = this.getRectangle(
+                dataItem,
+                itemStyle,
+                rect
+            );
+            // todo
+            ecData.pack(
+                rectangle,
+                series[seriesIndex], seriesIndex,
+                dataItem, dataIndex,
+                dataItem.name
+            );
+            this.shapeList.push(rectangle);
+        },
+
+        /**
+         * 构建矩形
+         * @param {Object} data 数据
+         * @param {Object} itemStyle 合并后的样式
+         * @param {number} x 矩形横坐标
+         * @param {number} y 矩形纵坐标
+         * @param {number} width 矩形宽
+         * @param {number} height 矩形高
+         * @return {Object} 返回一个矩形
+         */
+        getRectangle: function (
+            dataItem,
+            itemStyle,
+            rect
+        ) {
+            var emphasis = itemStyle.emphasis;
+            var normal = itemStyle.normal;
+            var textShape = this.getLabel(
+                itemStyle,
+                rect,
+                dataItem.name,
+                dataItem.value
+            );
+            var hoverable = this.option.hoverable;
+            var rectangleShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                hoverable: hoverable,
+                clickable: true,
+                style: zrUtil.merge(
+                    {
+                        x: rect.x,
+                        y: rect.y,
+                        width: rect.width,
+                        height: rect.height,
+                        brushType: 'both',
+                        color: normal.color,
+                        lineWidth: normal.borderWidth,
+                        strokeColor: normal.borderColor
+                    },
+                    textShape.style,
+                    true
+                ),
+                highlightStyle: zrUtil.merge(
+                    {
+                        color: emphasis.color,
+                        lineWidth: emphasis.borderWidth,
+                        strokeColor: emphasis.borderColor
+                    },
+                    textShape.highlightStyle,
+                    true
+                )
+            };
+            return new RectangleShape(rectangleShape);
+        },
+        /**
+         * 获取label样式
+         * @param {Object} itemStyle 合并后的样式
+         * @param {Object} rect 矩形信息
+         * @param {string} name 数据名称
+         * @param {number} value 数据值
+         * @return {Object} 返回label的样式
+         */
+        getLabel: function (
+            itemStyle,
+            rect,
+            name,
+            value
+        ) {
+            var normalTextStyle = itemStyle.normal.label.textStyle;
+            var queryTarget = [itemStyle.emphasis.label.textStyle, normalTextStyle];
+            var emphasisTextStyle = this.deepMerge(queryTarget);
+
+            var formatter = itemStyle.normal.label.formatter;
+            var text = this.getLabelText(name, value, formatter);
+            var textFont = this.getFont(normalTextStyle);
+            var textWidth = toolArea.getTextWidth(text, textFont);
+            var textHeight = toolArea.getTextHeight(text, textFont);
+
+            var emphasisFormatter = this.deepQuery(
+                [itemStyle.emphasis, itemStyle.normal],
+                'label.formatter'
+            );
+            var emphasisText = this.getLabelText(name, value, emphasisFormatter);
+            var emphasisTextFont = this.getFont(emphasisTextStyle);
+            var emphasisTextWidth = toolArea.getTextWidth(text, emphasisTextFont);
+            var emphasisTextHeight = toolArea.getTextHeight(text, emphasisTextFont);
+            if (!itemStyle.normal.label.show) {
+                text = '';
+            }
+            else if (itemStyle.normal.label.x + textWidth > rect.width
+                || itemStyle.normal.label.y + textHeight > rect.height) {
+                text = '';
+            }
+
+            if (!itemStyle.emphasis.label.show) {
+                emphasisText = '';
+            }
+            else if (emphasisTextStyle.x + emphasisTextWidth > rect.width
+                || emphasisTextStyle.y + emphasisTextHeight > rect.height) {
+                emphasisText = '';
+            }
+
+            // 用label方法写title
+            var textShape = {
+                style: {
+                    textX: rect.x + itemStyle.normal.label.x,
+                    textY: rect.y + itemStyle.normal.label.y,
+                    text: text,
+                    textPosition: 'specific',
+                    textColor: normalTextStyle.color,
+                    textFont: textFont
+                },
+                highlightStyle: {
+                    textX: rect.x + itemStyle.emphasis.label.x,
+                    textY: rect.y + itemStyle.emphasis.label.y,
+                    text: emphasisText,
+                    textColor: emphasisTextStyle.color,
+                    textPosition: 'specific'
+                }
+            };
+            return textShape;
+        },
+        /**
+         * 支持formatter
+         * @param {string} name 数据名称
+         * @param {number} value 数据值
+         * @param {string|Object} formatter 构造器
+         * @return {Object} 返回label的样式
+         */
+        getLabelText: function (name, value, formatter) {
+            if (formatter) {
+                if (typeof formatter === 'function') {
+                    return formatter.call(
+                        this.myChart,
+                        name,
+                        value
+                    );
+                }
+                else if (typeof formatter === 'string') {
+                    formatter = formatter.replace('{b}', '{b0}')
+                                         .replace('{c}', '{c0}');
+                    formatter = formatter.replace('{b0}', name)
+                                         .replace('{c0}', value);
+                    return formatter;
+                }
+            }
+            else {
+                return name;
+            }
+        },
+        /*
+         * 构建子级矩形图,这里用线表示不再画矩形
+         *
+         * @param {Object} data 数据
+         * @param {Object} rect
+         * @return
+        */
+        _buildChildrenTreemap: function (
+            data,
+            itemStyle,
+            rect,
+            seriesIndex
+        ) {
+            var treemapArea = rect.width * rect.height; // 计算总面积
+            // 遍历数组,通过value与area0计算实际面积area
+            var sum = 0;
+            var areaArr = [];
+            for (var i = 0; i < data.length; i++) {
+                sum += data[i].value;
+            }
+            for (var j = 0; j < data.length; j++) {
+                areaArr.push(data[j].value * treemapArea / sum);
+            }
+            var treeMapLayout = new TreeMapLayout({
+                x: rect.x,
+                y: rect.y,
+                width: rect.width,
+                height: rect.height
+            });
+            var locationArr = treeMapLayout.run(areaArr);
+            var lineWidth = itemStyle.normal.childBorderWidth;
+            var lineColor = itemStyle.normal.childBorderColor;
+            for (var k = 0; k < locationArr.length; k++) {
+                var item = locationArr[k];
+                var lines = [];
+                // 容器边框不能重复画
+                // 上边
+                if (rect.y.toFixed(2) !== item.y.toFixed(2)) {
+                    lines.push(this._getLine(
+                        item.x,
+                        item.y,
+                        item.x + item.width,
+                        item.y,
+                        lineWidth,
+                        lineColor
+                    ));
+                }
+                // 左边
+                if (rect.x.toFixed(2) !== item.x.toFixed(2)) {
+                    lines.push(this._getLine(
+                        item.x,
+                        item.y,
+                        item.x,
+                        item.y + item.height,
+                        lineWidth,
+                        lineColor
+                    ));
+                }
+                // 下边
+                if ((rect.y + rect.height).toFixed(2) !== (item.y + item.height).toFixed(2)) {
+                    lines.push(this._getLine(
+                        item.x,
+                        item.y + item.height,
+                        item.x + item.width,
+                        item.y + item.height,
+                        lineWidth,
+                        lineColor
+                    ));
+                }
+                // 右边
+                if ((rect.x + rect.width).toFixed(2) !== (item.x + item.width).toFixed(2)) {
+                    lines.push(this._getLine(
+                        item.x + item.width,
+                        item.y,
+                        item.x + item.width,
+                        item.y + item.height,
+                        lineWidth,
+                        lineColor
+                    ));
+                }
+                for (var l = 0; l < lines.length; l++) {
+                    ecData.set(lines[l], 'seriesIndex', seriesIndex);
+                    this.shapeList.push(lines[l]);
+                }
+            }
+        },
+        /*
+         * 构建线段
+         * @param {number} xStart 开始坐标
+         * @param {number} yStart 开始坐标
+         * @param {number} xEnd 结束坐标
+         * @param {number} yEnd 结束坐标
+         * @param {number} lineWidth 线宽
+         * @param {number} lineColor 颜色
+         * @return {Object} 返回一个线段
+         */
+        _getLine : function (
+            xStart,
+            yStart,
+            xEnd,
+            yEnd,
+            lineWidth,
+            lineColor
+        ) {
+            var lineShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                hoverable: false,
+                style: {
+                    xStart: xStart,
+                    yStart: yStart,
+                    xEnd: xEnd,
+                    yEnd: yEnd,
+                    lineWidth: lineWidth,
+                    strokeColor: lineColor
+                }
+            };
+            return new LineShape(lineShape);
+
+        },
+
+        _buildBreadcrumb: function (treeRoot, seriesIndex, x, y) {
+            var stack = [];
+
+            var current = treeRoot;
+            while (current) {
+                stack.unshift(current.data.name);
+                current = current.parent;
+            }
+
+            var series = this.series[seriesIndex];
+            var textStyle = this.query(series, 'itemStyle.normal.breadcrumb.textStyle') || {};
+            var textEmphasisStyle = 
+                this.query(series, 'itemStyle.emphasis.breadcrumb.textStyle') || {};
+
+            var commonStyle = {
+                y: y + 10,
+                textBaseline: 'top',
+                textAlign: 'left',
+                color: textStyle.color,
+                textFont: this.getFont(textStyle)
+            };
+            var commonHighlightStyle = {
+                brushType: 'fill',
+                color: textEmphasisStyle.color || zrColor.lift(textStyle.color, -0.3),
+                textFont: this.getFont(textEmphasisStyle)
+            };
+
+            for (var i = 0; i < stack.length; i++) {
+                var textShape = new TextShape({
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    style: zrUtil.merge({
+                        x: x,
+                        text: stack[i] + (stack.length - 1 - i ? ' > ' : '')
+                    }, commonStyle),
+                    clickable: true,
+                    highlightStyle: commonHighlightStyle
+                });
+
+                ecData.set(textShape, 'seriesIndex', seriesIndex);
+                ecData.set(textShape, 'name', stack[i]);
+
+                x += textShape.getRect(textShape.style).width;
+
+                this.shapeList.push(textShape);
+            }
+        },
+
+        __onclick : function (params) {
+            var target = params.target;
+            if (target) {
+                var seriesIndex = ecData.get(target, 'seriesIndex');
+                var name = ecData.get(target, 'name');
+                var tree = this._treesMap[seriesIndex];
+                var root = tree.getNodeById(name);
+
+                if (root && root.children.length) {
+                    this._buildTreemap(root, seriesIndex);
+                }
+            }
+        }
+    };
+
+    zrUtil.inherits(Treemap, ChartBase);
+
+    // 图表注册
+    require('../chart').define('treemap', Treemap);
+
+    return Treemap;
+});

+ 461 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/venn.js

@@ -0,0 +1,461 @@
+/**
+ * echarts图表类:维恩图
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Loutongbing (娄同兵, loutongbing@126.com)
+ */
+
+define(function (require) {
+    var ChartBase = require('./base');
+    // 图形依赖
+
+    var TextShape = require('zrender/shape/Text');
+    var CircleShape = require('zrender/shape/Circle');
+    var PathShape = require('zrender/shape/Path');
+
+    var ecConfig = require('../config');
+    // 维恩图默认参数
+    ecConfig.venn = {
+        zlevel: 0,                  // 一级层叠
+        z: 1,                       // 二级层叠
+        calculable: false
+    };
+
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} series 数据
+     * @param {Object} component 组件
+     * @constructor
+     * @exports Venn
+     */
+    function Venn(ecTheme, messageCenter, zr, option, myChart) {
+        // 图表基类
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+    
+    Venn.prototype = {
+        type : ecConfig.CHART_TYPE_VENN,
+        /**
+         * 绘制图形
+         */
+        _buildShape : function () {
+            this.selectedMap = {};
+            this._symbol = this.option.symbolList;
+            this._queryTarget;
+            this._dropBoxList = [];
+            this._vennDataCounter = 0;
+            var series = this.series;
+            var legend = this.component.legend;
+            
+            for (var i = 0; i < series.length; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_VENN) {
+                    series[i] = this.reformOption(series[i]);
+                    var serieName = series[i].name || '';
+
+                    // 系列图例开关
+                    this.selectedMap[serieName] = 
+                        legend ? legend.isSelected(serieName) : true;
+                    if (!this.selectedMap[serieName]) {
+                        continue;
+                    }
+
+                    this._buildVenn(i);
+                }
+            }
+            
+            this.addShapeList();
+        },
+        /**
+         * 构建单个
+         *
+         * @param {Object} data 数据
+         */
+        _buildVenn : function (seriesIndex) {
+            var r0;
+            var r1;
+            var serie = this.series[seriesIndex];
+            var data = serie.data;
+            if (data[0].value > data[1].value) {
+                r0 = this.zr.getHeight() / 3;
+                r1 = r0 * Math.sqrt(data[1].value) / Math.sqrt(data[0].value);
+            }
+            else {
+                r1 = this.zr.getHeight() / 3;
+                r0 = r1 * Math.sqrt(data[0].value) / Math.sqrt(data[1].value);
+            }
+
+            var x0 = this.zr.getWidth() / 2 - r0;
+            // 估值 两个圆心的距离与两圆半径均值之比等于交集与两集合均值的开方之比。
+            // 公共距离(coincideLengthAnchor)/ 公共数值开方 = 半径平均值 / 各自数值均值的开方
+            var coincideLengthAnchor = ((r0 + r1) / 2) * Math.sqrt(data[2].value) / Math.sqrt((data[0].value + data[1].value) / 2);
+            // 如果两者没有公共面积,则圆心距就为两圆半径之和
+            var coincideLength = r0 + r1;
+            if (data[2].value !== 0) {
+                coincideLength = this._getCoincideLength(
+                    data[0].value,
+                    data[1].value,
+                    data[2].value,
+                    r0, r1,
+                    coincideLengthAnchor,
+                    Math.abs(r0 - r1),
+                    r0 + r1
+                );
+            }
+
+            var x1 = x0 +  coincideLength;
+            var y = this.zr.getHeight() / 2;
+            this._buildItem(
+                seriesIndex, 0, data[0],
+                x0,
+                y,
+                r0
+            );
+            this._buildItem(
+                seriesIndex, 1, data[1],
+                x1,
+                y,
+                r1
+            );
+            // 包含关系与无交集关系均不画公共部分
+            if (
+                data[2].value !== 0
+                && data[2].value !== data[0].value
+                && data[2].value !== data[1].value
+            ) {
+                var xLeft = (r0 * r0 - r1 * r1) / (2 * coincideLength) + coincideLength / 2;
+                var xRight = coincideLength / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLength);
+                var h = Math.sqrt(r0 * r0 - xLeft * xLeft);
+                // 判断大小圆弧
+                var rightLargeArcFlag = 0;
+                var leftLargeArcFlag = 0;
+                if (
+                    data[0].value > data[1].value
+                    && x1 < x0 + xLeft
+                ) {
+                    leftLargeArcFlag = 1;
+                }
+                if (
+                    data[0].value < data[1].value
+                    && x1 < x0 + xRight
+                ) {
+                    rightLargeArcFlag = 1;
+                }
+                this._buildCoincideItem(
+                    seriesIndex,
+                    2,
+                    data[2],
+                    x0 + xLeft,
+                    y - h,
+                    y + h,
+                    r0,
+                    r1,
+                    rightLargeArcFlag,
+                    leftLargeArcFlag
+                );
+            }
+        },
+        /**
+         * 逼近算法得到两圆的间距
+         * @param {number} value0 第一个圆的原始数值
+         * @param {number} value1 第二个圆的原始数值
+         * @param {number} value3 公共部分的原始数值
+         * @param {number} r0 第一个圆的半径
+         * @param {number} r1 第二个圆的半径
+         * @param {number} coincideLengthAnchor 锚定
+         * @param {number} coincideLengthAnchorMin 下限
+         * @param {number} coincideLengthAnchorMax 上限
+         * @return {Node}
+        */
+        _getCoincideLength: function (
+            value0,
+            value1,
+            value2,
+            r0,
+            r1,
+            coincideLengthAnchor,
+            coincideLengthAnchorMin,
+            coincideLengthAnchorMax
+        ) {
+            // 计算
+            var x = (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor) + coincideLengthAnchor / 2;
+            var y = coincideLengthAnchor / 2 - (r0 * r0 - r1 * r1) / (2 * coincideLengthAnchor);
+            // 夹角
+            var alfa = Math.acos(x / r0);
+            var beta = Math.acos(y / r1);
+            // 第一个圆的面积
+            var area0 = r0 * r0 * Math.PI;
+            // 计算的公共面积 (思路是扇形减三角形)
+            var area2 = alfa * r0 * r0 - x * r0 * Math.sin(alfa) + beta * r1 * r1 - y * r1 * Math.sin(beta);
+            var scaleAnchor = area2 / area0;
+            var scale = value2 / value0;
+            var approximateValue = Math.abs(scaleAnchor / scale);
+            if (approximateValue > 0.999 && approximateValue < 1.001) {
+                return coincideLengthAnchor;
+            }
+            // 若是公共面积比较小,使距离减小一些,让公共面积增大
+            else if (approximateValue <= 0.999) {
+                coincideLengthAnchorMax = coincideLengthAnchor;
+                // 二分法计算新的步调
+                coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMin) / 2;
+                return this._getCoincideLength(value0, value1, value2, r0, r1,
+                    coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
+            }
+            // 若是公共面积比较大,使距离增大一些,让公共面积减小
+            else {
+                coincideLengthAnchorMin = coincideLengthAnchor;
+                coincideLengthAnchor = (coincideLengthAnchor + coincideLengthAnchorMax) / 2;
+                return this._getCoincideLength(value0, value1, value2, r0, r1,
+                    coincideLengthAnchor, coincideLengthAnchorMin, coincideLengthAnchorMax);
+            }
+        },
+
+        /**
+         * 构建单个圆及指标
+         */
+        _buildItem : function (
+            seriesIndex, dataIndex, dataItem,
+            x, y, r
+        ) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+
+            var circle = this.getCircle(
+                seriesIndex,
+                dataIndex,
+                dataItem,
+                x, y, r
+            );
+            ecData.pack(
+                circle,
+                serie, seriesIndex,
+                dataItem, dataIndex,
+                dataItem.name
+            );
+            this.shapeList.push(circle);
+
+            if (serie.itemStyle.normal.label.show) {
+                // 文本标签
+                var label = this.getLabel(
+                    seriesIndex,
+                    dataIndex,
+                    dataItem,
+                    x, y, r
+                );
+                ecData.pack(
+                    label,
+                    serie, seriesIndex,
+                    serie.data[dataIndex], dataIndex,
+                    serie.data[dataIndex].name
+                );
+                this.shapeList.push(label);
+            }
+        },
+
+        _buildCoincideItem : function (
+            seriesIndex, dataIndex, dataItem,
+            x, y0, y1, r0, r1, rightLargeArcFlag, leftLargeArcFlag
+        ) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var queryTarget = [dataItem, serie];
+
+            // 多级控制
+            var normal = this.deepMerge(
+                queryTarget,
+                'itemStyle.normal'
+            ) || {};
+            var emphasis = this.deepMerge(
+                queryTarget,
+                'itemStyle.emphasis'
+            ) || {};
+            var normalColor = normal.color || this.zr.getColor(dataIndex);
+            var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
+
+            var path = 'M' + x + ',' + y0
+                       + 'A' + r0 + ',' + r0 + ',0,' + rightLargeArcFlag + ',1,' + x + ',' + y1
+                       + 'A' + r1 + ',' + r1 + ',0,' + leftLargeArcFlag + ',1,' + x + ',' + y0;
+            var style = {
+                color: normalColor,
+                // path: rx ry x-axis-rotation large-arc-flag sweep-flag x y
+                path: path
+            };
+
+            var shape = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                style: style,
+                highlightStyle: {
+                    color: emphasisColor,
+                    lineWidth: emphasis.borderWidth,
+                    strokeColor: emphasis.borderColor
+                }
+            };
+            shape = new PathShape(shape);
+            if (shape.buildPathArray) {
+                shape.style.pathArray = shape.buildPathArray(style.path);
+            }
+            ecData.pack(
+                shape,
+                series[seriesIndex], 0,
+                dataItem, dataIndex,
+                dataItem.name
+            );
+            this.shapeList.push(shape);
+        },
+        /**
+         * 构建圆形
+         */
+        getCircle : function (
+            seriesIndex,
+            dataIndex,
+            dataItem,
+            x, y, r
+        ) {
+            var serie = this.series[seriesIndex];
+            var queryTarget = [dataItem, serie];
+
+            // 多级控制
+            var normal = this.deepMerge(
+                queryTarget,
+                'itemStyle.normal'
+            ) || {};
+            var emphasis = this.deepMerge(
+                queryTarget,
+                'itemStyle.emphasis'
+            ) || {};
+            var normalColor = normal.color || this.zr.getColor(dataIndex);
+            var emphasisColor = emphasis.color || this.zr.getColor(dataIndex);
+
+            var circle = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                clickable: true,
+                style: {
+                    x: x,
+                    y: y,
+                    r: r,
+                    brushType: 'fill',
+                    opacity: 1,
+                    color: normalColor
+                },
+                highlightStyle: {
+                    color: emphasisColor,
+                    lineWidth: emphasis.borderWidth,
+                    strokeColor: emphasis.borderColor
+                }
+            };
+
+            if (this.deepQuery([dataItem, serie, this.option], 'calculable')) {
+                this.setCalculable(circle);
+                circle.draggable = true;
+            }
+            return new CircleShape(circle);
+
+        },
+
+        /**
+         * 需要显示则会有返回构建好的shape,否则返回undefined
+         */
+        getLabel: function (
+            seriesIndex,
+            dataIndex,
+            dataItem,
+            x, y, r
+        ) {
+            var serie = this.series[seriesIndex];
+            var itemStyle = serie.itemStyle;
+            var queryTarget = [dataItem, serie];
+
+            // 多级控制
+            var normal = this.deepMerge(
+                queryTarget,
+                'itemStyle.normal'
+            ) || {};
+            var status = 'normal';
+            // label配置
+            var labelControl = itemStyle[status].label;
+            var textStyle = labelControl.textStyle || {};
+            var text = this.getLabelText(dataIndex, dataItem, status);
+            var textFont = this.getFont(textStyle);
+            var textColor = normal.color || this.zr.getColor(dataIndex);
+            // 求出label的纵坐标
+            var textSize = textStyle.fontSize || 12;
+
+            var textShape = {
+                zlevel: serie.zlevel,
+                z: serie.z,
+                style: {
+                    x: x,
+                    y: y - r - textSize,
+                    color: textStyle.color || textColor,
+                    text: text,
+                    textFont: textFont,
+                    textAlign: 'center'
+                }
+            };
+
+            return new TextShape(textShape);
+        },
+
+        /**
+         * 根据lable.format计算label text
+         */
+        getLabelText : function (dataIndex, dataItem, status) {
+            var series = this.series;
+            var serie = series[0];
+            var formatter = this.deepQuery(
+                [dataItem, serie],
+                'itemStyle.' + status + '.label.formatter'
+            );
+
+            if (formatter) {
+                if (typeof formatter == 'function') {
+                    return formatter(
+                        serie.name,
+                        dataItem.name,
+                        dataItem.value
+                    );
+                }
+                else if (typeof formatter == 'string') {
+                    formatter = formatter.replace('{a}','{a0}')
+                                         .replace('{b}','{b0}')
+                                         .replace('{c}','{c0}');
+                    formatter = formatter.replace('{a0}', serie.name)
+                                         .replace('{b0}', dataItem.name)
+                                         .replace('{c0}', dataItem.value);
+
+                    return formatter;
+                }
+            }
+            else {
+                return dataItem.name;
+            }
+        },
+
+        /**
+         * 刷新
+         */
+        refresh : function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+
+            this._buildShape();
+        }
+    };
+
+    zrUtil.inherits(Venn, ChartBase);
+
+    // 图表注册
+    require('../chart').define('venn', Venn);
+
+    return Venn;
+});

+ 249 - 0
src/main/webapp/static/echarts-2.2.7/src/chart/wordCloud.js

@@ -0,0 +1,249 @@
+/**
+ * echarts图表类:字符云
+ * @desc
+ * @author  clmtulip(车丽美, clmtulip@gmail.com) liyong(liyong1239@163.com)
+ */
+define(function (require) {
+    var ChartBase = require('./base');
+
+    var TextShape = require('zrender/shape/Text');
+    var CloudLayout = require('../layout/WordCloud');
+
+    require('../component/grid');
+    require('../component/dataRange');
+    var ecConfig = require('../config');
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+
+    ecConfig.wordCloud = {
+        zlevel: 0,
+        z: 2,
+        clickable: true,
+        
+        center: ['50%', '50%'],
+        
+        size: ['40%', '40%'],
+
+        // 字体旋转角度, 随机从指定数组中取
+        textRotation: [0, 90],
+
+        // 字体之间的间隙  单位px 默认为0
+        textPadding: 0,
+
+        // 字体大小 是否自动缩放,使得尽可能的 字体能够显示
+        autoSize: {
+            enable: true,
+            // 字体缩放后的最小大小
+            minSize: 12
+        },
+
+        itemStyle: {
+            normal: {
+                textStyle: {
+                    // 默认字体大小跟 data.value 一样
+                    fontSize: function (data) {
+                        return data.value;
+                    }
+                }
+            }
+        }
+    };
+
+    function Cloud(ecTheme, messageCenter, zr, option, myChart) {
+        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option); 
+    }
+
+    Cloud.prototype = {
+        type: ecConfig.CHART_TYPE_WORDCLOUD,
+
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.series = newOption.series;
+            }
+
+            //init data
+            this._init();
+        },
+        /**
+         *   初始化数据
+         *   @private
+         */
+        _init: function () {
+            var series = this.series;
+            this.backupShapeList();
+
+            var legend = this.component.legend;
+            for (var i = 0; i < series.length; i++) {
+                if (series[i].type === ecConfig.CHART_TYPE_WORDCLOUD) {
+                    series[i] = this.reformOption(series[i]);
+                    var serieName = series[i].name || '';
+
+                    this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
+
+                    if (! this.selectedMap[serieName]) {
+                        continue;
+                    }
+
+                    this.buildMark(i);
+
+                    this._initSerie(series[i]);
+                }
+            }
+        },
+
+        _initSerie: function (serie) {
+            
+            var textStyle = serie.itemStyle.normal.textStyle;
+
+            var size = [
+                this.parsePercent(serie.size[0], this.zr.getWidth()) || 200,
+                this.parsePercent(serie.size[1], this.zr.getHeight()) || 200
+            ]
+            var center = this.parseCenter(this.zr, serie.center);
+
+            var layoutConfig = {
+                size: size,
+
+                wordletype: {
+                    autoSizeCal: serie.autoSize,
+                },
+
+                center: center,
+
+                rotate: serie.textRotation,
+                padding: serie.textPadding,
+
+                font: textStyle.fontFamily,
+                fontSize: textStyle.fontSize,
+                fontWeight: textStyle.fontWeight,
+                fontStyle: textStyle.fontStyle,
+
+                text: function (d) {
+                    return d.name;
+                },
+
+                data: serie.data
+            };
+
+            // 字符云的布局方法
+            var clouds = new CloudLayout(layoutConfig);
+            var self = this;
+
+            // 字符位置确定后 执行 以下函数
+            clouds.end(function (d) {
+                self._buildShapes(d);
+            });
+
+            // 布局算法 开始执行
+            clouds.start();
+        },
+
+        /**
+         * 通过 data 绘制图形
+         * @param data
+         * @private
+         */
+        _buildShapes: function (data) {
+            //数据的 再度初始化,使得最终 text 是以自己的坐标为中心。。。
+            var len = data.length;
+            for (var i = 0; i < len; i++) {
+                this._buildTextShape(data[i], 0, i);
+            }
+
+            this.addShapeList();
+        },
+
+        /**
+         * 绘制 每个 text
+         * @param oneText {Object}
+         * @param seriesIndex {int}
+         * @param dataIndex {int}
+         * @private
+         */
+        _buildTextShape: function (oneText, seriesIndex, dataIndex) {
+            var series = this.series;
+            var serie = series[seriesIndex];
+            var serieName = serie.name || '';
+            var data = serie.data[dataIndex];
+            var queryTarget = [
+                data,
+                serie
+            ];
+
+            // dataRange 选择结束
+            var legend = this.component.legend;
+            var defaultColor = legend ? legend.getColor(serieName) : this.zr.getColor(seriesIndex);
+            // 多级控制
+            var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
+            var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
+            var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data)
+                || defaultColor;
+
+            var emphasisColor = this.getItemStyleColor(
+                emphasis.color, seriesIndex, dataIndex, data
+            )
+                || (typeof normalColor === 'string'
+                    ? zrColor.lift(normalColor, -0.2)
+                    : normalColor
+                    );
+
+            var textShape = new TextShape({
+                zlevel: serie.zlevel,
+                z: serie.z,
+                hoverable: true,
+                clickable: this.deepQuery(queryTarget, 'clickable'),
+                style: {
+                    x: 0,
+                    y: 0,
+                    text: oneText.text,
+                    color: normalColor,
+                    textFont: [oneText.style,
+                                oneText.weight,
+                                oneText.size + 'px',
+                                oneText.font].join(' '),
+                    textBaseline: 'alphabetic',
+                    textAlign: 'center'
+                },
+                highlightStyle: {
+                    brushType: emphasis.borderWidth ? 'both' : 'fill',
+                    color: emphasisColor,
+                    lineWidth: emphasis.borderWidth || 0,
+                    strokeColor: emphasis.borderColor
+                },
+                position: [
+                    oneText.x,
+                    oneText.y
+                ],
+                rotation: [
+                    -oneText.rotate / 180 * Math.PI,
+                    0,
+                    0
+                ]
+            });
+
+            ecData.pack(
+                textShape,
+                serie, seriesIndex,
+                data, dataIndex,
+                data.name
+            );
+
+            this.shapeList.push(textShape);
+        }
+    };
+
+
+    zrUtil.inherits(Cloud, ChartBase);
+
+    // 图表注册
+    require('../chart').define('wordCloud', Cloud);
+
+    return Cloud;
+});

+ 32 - 0
src/main/webapp/static/echarts-2.2.7/src/component.js

@@ -0,0 +1,32 @@
+/**
+ * echart组件库
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (/*require*/) {     // component
+    var self = {};
+
+    var _componentLibrary = {};     // echart组件库
+
+    /**
+     * 定义图形实现
+     * @param {Object} name
+     * @param {Object} clazz 图形实现
+     */
+    self.define = function (name, clazz) {
+        _componentLibrary[name] = clazz;
+        return self;
+    };
+
+    /**
+     * 获取图形实现
+     * @param {Object} name
+     */
+    self.get = function (name) {
+        return _componentLibrary[name];
+    };
+    
+    return self;
+});

+ 345 - 0
src/main/webapp/static/echarts-2.2.7/src/component/axis.js

@@ -0,0 +1,345 @@
+/**
+ * echarts组件类: 坐标轴
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ * 直角坐标系中坐标轴数组,数组中每一项代表一条横轴(纵轴)坐标轴。
+ * 标准(1.0)中规定最多同时存在2条横轴和2条纵轴
+ *    单条横轴时可指定安放于grid的底部(默认)或顶部,2条同时存在时则默认第一条安放于底部,第二天安放于顶部
+ *    单条纵轴时可指定安放于grid的左侧(默认)或右侧,2条同时存在时则默认第一条安放于左侧,第二天安放于右侧。
+ * 坐标轴有两种类型,类目型和数值型(区别详见axis):
+ *    横轴通常为类目型,但条形图时则横轴为数值型,散点图时则横纵均为数值型
+ *    纵轴通常为数值型,但条形图时则纵轴为类目型。
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+
+    var LineShape = require('zrender/shape/Line');
+
+    var ecConfig = require('../config');
+    var ecData = require('../util/ecData');
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表选项
+     *     @param {string=} option.xAxis.type 坐标轴类型,横轴默认为类目型'category'
+     *     @param {string=} option.yAxis.type 坐标轴类型,纵轴默认为类目型'value'
+     * @param {Object} component 组件
+     * @param {string} axisType 横走or纵轴
+     */
+    function Axis(ecTheme, messageCenter, zr, option, myChart, axisType) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.axisType = axisType;
+        this._axisList = [];
+
+        this.refresh(option);
+    }
+
+    Axis.prototype = {
+        type: ecConfig.COMPONENT_TYPE_AXIS,
+        axisBase: {
+            // 轴线
+            _buildAxisLine: function () {
+                var lineWidth = this.option.axisLine.lineStyle.width;
+                var halfLineWidth = lineWidth / 2;
+                var axShape = {
+                    _axisShape: 'axisLine',
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase() + 3,
+                    hoverable: false
+                };
+                var grid = this.grid;
+                switch (this.option.position) {
+                    case 'left' :
+                        axShape.style = {
+                            xStart: grid.getX() - halfLineWidth,
+                            yStart: grid.getYend(),
+                            xEnd: grid.getX() - halfLineWidth,
+                            yEnd: grid.getY(),
+                            lineCap: 'round'
+                        };
+                        break;
+                    case 'right' :
+                        axShape.style = {
+                            xStart: grid.getXend() + halfLineWidth,
+                            yStart: grid.getYend(),
+                            xEnd: grid.getXend() + halfLineWidth,
+                            yEnd: grid.getY(),
+                            lineCap: 'round'
+                        };
+                        break;
+                    case 'bottom' :
+                        axShape.style = {
+                            xStart: grid.getX(),
+                            yStart: grid.getYend() + halfLineWidth,
+                            xEnd: grid.getXend(),
+                            yEnd: grid.getYend() + halfLineWidth,
+                            lineCap: 'round'
+                        };
+                        break;
+                    case 'top' :
+                        axShape.style = {
+                            xStart: grid.getX(),
+                            yStart: grid.getY() - halfLineWidth,
+                            xEnd: grid.getXend(),
+                            yEnd: grid.getY() - halfLineWidth,
+                            lineCap: 'round'
+                        };
+                        break;
+                }
+                var style = axShape.style;
+                if (this.option.name !== '') { // 别帮我代码规范
+                    style.text = this.option.name;
+                    style.textPosition = this.option.nameLocation;
+                    style.textFont = this.getFont(this.option.nameTextStyle);
+                    if (this.option.nameTextStyle.align) {
+                        style.textAlign = this.option.nameTextStyle.align;
+                    }
+                    if (this.option.nameTextStyle.baseline) {
+                        style.textBaseline = this.option.nameTextStyle.baseline;
+                    }
+                    if (this.option.nameTextStyle.color) {
+                        style.textColor = this.option.nameTextStyle.color;
+                    }
+                }
+                style.strokeColor = this.option.axisLine.lineStyle.color;
+
+                style.lineWidth = lineWidth;
+                // 亚像素优化
+                if (this.isHorizontal()) {
+                    // 横向布局,优化y
+                    style.yStart
+                        = style.yEnd
+                        = this.subPixelOptimize(style.yEnd, lineWidth);
+                }
+                else {
+                    // 纵向布局,优化x
+                    style.xStart
+                        = style.xEnd
+                        = this.subPixelOptimize(style.xEnd, lineWidth);
+                }
+
+                style.lineType = this.option.axisLine.lineStyle.type;
+
+                axShape = new LineShape(axShape);
+                this.shapeList.push(axShape);
+            },
+
+            _axisLabelClickable: function(clickable, axShape) {
+                if (clickable) {
+                    ecData.pack(
+                        axShape, undefined, -1, undefined, -1, axShape.style.text
+                    );
+                    axShape.hoverable = true;
+                    axShape.clickable = true;
+                    axShape.highlightStyle = {
+                        color: zrColor.lift(axShape.style.color, 1),
+                        brushType: 'fill'
+                    };
+                    return axShape;
+                }
+                else {
+                    return axShape;
+                }
+            },
+
+            refixAxisShape: function(zeroX, zeroY) {
+                if (!this.option.axisLine.onZero) {
+                    return;
+                }
+                var tickLength;
+                if (this.isHorizontal() && zeroY != null) {
+                    // 横向布局调整纵向y
+                    for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                        if (this.shapeList[i]._axisShape === 'axisLine') {
+                            this.shapeList[i].style.yStart
+                                = this.shapeList[i].style.yEnd
+                                = this.subPixelOptimize(
+                                    zeroY, this.shapeList[i].stylelineWidth
+                                );
+                            this.zr.modShape(this.shapeList[i].id);
+                        }
+                        else if (this.shapeList[i]._axisShape === 'axisTick') {
+                            tickLength = this.shapeList[i].style.yEnd
+                                         - this.shapeList[i].style.yStart;
+                            this.shapeList[i].style.yStart = zeroY - tickLength;
+                            this.shapeList[i].style.yEnd = zeroY;
+                            this.zr.modShape(this.shapeList[i].id);
+                        }
+                    }
+                }
+                if (!this.isHorizontal() && zeroX != null) {
+                    // 纵向布局调整横向x
+                    for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                        if (this.shapeList[i]._axisShape === 'axisLine') {
+                            this.shapeList[i].style.xStart
+                                = this.shapeList[i].style.xEnd
+                                = this.subPixelOptimize(
+                                    zeroX, this.shapeList[i].stylelineWidth
+                                );
+                            this.zr.modShape(this.shapeList[i].id);
+                        }
+                        else if (this.shapeList[i]._axisShape === 'axisTick') {
+                            tickLength = this.shapeList[i].style.xEnd
+                                         - this.shapeList[i].style.xStart;
+                            this.shapeList[i].style.xStart = zeroX;
+                            this.shapeList[i].style.xEnd = zeroX + tickLength;
+                            this.zr.modShape(this.shapeList[i].id);
+                        }
+                    }
+                }
+            },
+
+            getPosition: function () {
+                return this.option.position;
+            },
+
+            isHorizontal: function() {
+                return this.option.position === 'bottom' || this.option.position === 'top';
+            }
+        },
+        /**
+         * 参数修正&默认值赋值,重载基类方法
+         * @param {Object} opt 参数
+         */
+        reformOption: function (opt) {
+            // 不写或传了个空数值默认为数值轴
+            if (!opt || (opt instanceof Array && opt.length === 0)) {
+                opt = [ { type: ecConfig.COMPONENT_TYPE_AXIS_VALUE } ];
+            }
+            else if (!(opt instanceof Array)){
+                opt = [opt];
+            }
+
+            // 最多两条,其他参数忽略
+            if (opt.length > 2) {
+                opt = [opt[0], opt[1]];
+            }
+
+            if (this.axisType === 'xAxis') {
+                // 横轴位置默认配置
+                if (!opt[0].position            // 没配置或配置错
+                    || (opt[0].position != 'bottom' && opt[0].position != 'top')
+                ) {
+                    opt[0].position = 'bottom';
+                }
+                if (opt.length > 1) {
+                    opt[1].position = opt[0].position === 'bottom' ? 'top' : 'bottom';
+                }
+
+                for (var i = 0, l = opt.length; i < l; i++) {
+                    // 坐标轴类型,横轴默认为类目型'category'
+                    opt[i].type = opt[i].type || 'category';
+                    // 标识轴类型&索引
+                    opt[i].xAxisIndex = i;
+                    opt[i].yAxisIndex = -1;
+                }
+            }
+            else {
+                // 纵轴位置默认配置
+                if (!opt[0].position            // 没配置或配置错
+                    || (opt[0].position != 'left'  && opt[0].position != 'right')
+                ) {
+                    opt[0].position = 'left';
+                }
+
+                if (opt.length > 1) {
+                    opt[1].position = opt[0].position === 'left' ? 'right' : 'left';
+                }
+
+                for (var i = 0, l = opt.length; i < l; i++) {
+                    // 坐标轴类型,纵轴默认为数值型'value'
+                    opt[i].type = opt[i].type || 'value';
+                    // 标识轴类型&索引
+                    opt[i].xAxisIndex = -1;
+                    opt[i].yAxisIndex = i;
+                }
+            }
+
+            return opt;
+        },
+
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            var axisOption;
+            if (newOption) {
+                this.option = newOption;
+                if (this.axisType === 'xAxis') {
+                    this.option.xAxis = this.reformOption(newOption.xAxis);
+                    axisOption = this.option.xAxis;
+                }
+                else {
+                    this.option.yAxis = this.reformOption(newOption.yAxis);
+                    axisOption = this.option.yAxis;
+                }
+                this.series = newOption.series;
+            }
+
+            var CategoryAxis = require('./categoryAxis');
+            var ValueAxis = require('./valueAxis');
+            var len = Math.max((axisOption && axisOption.length || 0), this._axisList.length);
+            for (var i = 0; i < len; i++) {
+                if (this._axisList[i]   // 已有实例
+                    && newOption        // 非空刷新
+                    && (!axisOption[i] || this._axisList[i].type != axisOption[i].type) // 类型不匹配
+                ) {
+                    this._axisList[i].dispose && this._axisList[i].dispose();
+                    this._axisList[i] = false;
+                }
+
+                if (this._axisList[i]) {
+                    this._axisList[i].refresh && this._axisList[i].refresh(
+                        axisOption ? axisOption[i] : false,
+                        this.series
+                    );
+                }
+                else if (axisOption && axisOption[i]) {
+                    this._axisList[i] = axisOption[i].type === 'category'
+                        ? new CategoryAxis(
+                               this.ecTheme, this.messageCenter, this.zr,
+                               axisOption[i], this.myChart, this.axisBase
+                           )
+                        : new ValueAxis(
+                               this.ecTheme, this.messageCenter, this.zr,
+                               axisOption[i], this.myChart, this.axisBase,
+                               this.series
+                        );
+                }
+            }
+        },
+
+        /**
+         * 根据值换算位置
+         * @param {number} idx 坐标轴索引0~1
+         */
+        getAxis: function (idx) {
+            return this._axisList[idx];
+        },
+
+        getAxisCount: function () {
+            return this._axisList.length;
+        },
+
+        clear: function () {
+            for (var i = 0, l = this._axisList.length; i < l; i++) {
+                this._axisList[i].dispose && this._axisList[i].dispose();
+            }
+            this._axisList = [];
+        }
+    };
+
+    zrUtil.inherits(Axis, Base);
+
+    require('../component').define('axis', Axis);
+
+    return Axis;
+});

+ 245 - 0
src/main/webapp/static/echarts-2.2.7/src/component/base.js

@@ -0,0 +1,245 @@
+/**
+ * echarts组件基类
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ecConfig = require('../config');
+    var ecData = require('../util/ecData');
+    var ecQuery = require('../util/ecQuery');
+    var number = require('../util/number');
+    var zrUtil = require('zrender/tool/util');
+    
+    function Base(ecTheme, messageCenter, zr, option, myChart){
+        this.ecTheme = ecTheme;
+        this.messageCenter = messageCenter;
+        this.zr =zr;
+        this.option = option;
+        this.series = option.series;
+        this.myChart = myChart;
+        this.component = myChart.component;
+
+        this.shapeList = [];
+        this.effectList = [];
+        
+        var self = this;
+        
+        self._onlegendhoverlink = function(param) {
+            if (self.legendHoverLink) {
+                var targetName = param.target;
+                var name;
+                for (var i = self.shapeList.length - 1; i >= 0; i--) {
+                    name = self.type == ecConfig.CHART_TYPE_PIE
+                           || self.type == ecConfig.CHART_TYPE_FUNNEL
+                           ? ecData.get(self.shapeList[i], 'name')
+                           : (ecData.get(self.shapeList[i], 'series') || {}).name;
+                    if (name == targetName 
+                        && !self.shapeList[i].invisible 
+                        && !self.shapeList[i].__animating
+                    ) {
+                        self.zr.addHoverShape(self.shapeList[i]);
+                    }
+                }
+            }
+        };
+        messageCenter && messageCenter.bind(
+            ecConfig.EVENT.LEGEND_HOVERLINK, this._onlegendhoverlink
+        );
+    }
+
+    /**
+     * 基类方法
+     */
+    Base.prototype = {
+        canvasSupported: require('zrender/tool/env').canvasSupported,
+        _getZ : function(zWhat) {
+            if (this[zWhat] != null) {
+                return this[zWhat];
+            }
+            var opt = this.ecTheme[this.type];
+            if (opt && opt[zWhat] != null) {
+                return opt[zWhat];
+            }
+            opt = ecConfig[this.type];
+            if (opt && opt[zWhat] != null) {
+                return opt[zWhat];
+            }
+            return 0;
+        },
+
+        /**
+         * 获取zlevel基数配置
+         */
+        getZlevelBase: function () {
+            return this._getZ('zlevel');
+        },
+        
+        /**
+         * 获取z基数配置
+         */
+        getZBase: function() {
+            return this._getZ('z');
+        },
+
+        /**
+         * 参数修正&默认值赋值
+         * @param {Object} opt 参数
+         *
+         * @return {Object} 修正后的参数
+         */
+        reformOption: function (opt) {
+            // 默认配置项动态多级合并,依赖加载的组件选项未被merge到ecTheme里,需要从config里取
+            opt = zrUtil.merge(
+                       zrUtil.merge(
+                           opt || {},
+                           zrUtil.clone(this.ecTheme[this.type] || {})
+                       ),
+                       zrUtil.clone(ecConfig[this.type] || {})
+                   );
+            this.z = opt.z;
+            this.zlevel = opt.zlevel;
+            return opt;
+        },
+        
+        /**
+         * css类属性数组补全,如padding,margin等~
+         */
+        reformCssArray: function (p) {
+            if (p instanceof Array) {
+                switch (p.length + '') {
+                    case '4':
+                        return p;
+                    case '3':
+                        return [p[0], p[1], p[2], p[1]];
+                    case '2':
+                        return [p[0], p[1], p[0], p[1]];
+                    case '1':
+                        return [p[0], p[0], p[0], p[0]];
+                    case '0':
+                        return [0, 0, 0, 0];
+                }
+            }
+            else {
+                return [p, p, p, p];
+            }
+        },
+        
+        getShapeById: function(id) {
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                if (this.shapeList[i].id === id) {
+                    return this.shapeList[i];
+                }
+            }
+            return null;
+        },
+        
+        /**
+         * 获取自定义和默认配置合并后的字体设置
+         */
+        getFont: function (textStyle) {
+            var finalTextStyle = this.getTextStyle(
+                zrUtil.clone(textStyle)
+            );
+            return finalTextStyle.fontStyle + ' '
+                   + finalTextStyle.fontWeight + ' '
+                   + finalTextStyle.fontSize + 'px '
+                   + finalTextStyle.fontFamily;
+        },
+
+        /**
+         * 获取统一主题字体样式
+         */
+        getTextStyle: function(targetStyle) {
+            return zrUtil.merge(
+                       zrUtil.merge(
+                           targetStyle || {},
+                           this.ecTheme.textStyle
+                       ),
+                       ecConfig.textStyle
+                   );
+        },
+        
+        getItemStyleColor: function (itemColor, seriesIndex, dataIndex, data) {
+            return typeof itemColor === 'function'
+                   ? itemColor.call(
+                        this.myChart,
+                        {
+                            seriesIndex: seriesIndex,
+                            series: this.series[seriesIndex],
+                            dataIndex: dataIndex,
+                            data: data
+                        }
+                   )
+                   : itemColor;
+            
+        }, 
+
+        /**
+         * @parmas {object | number} data 目标data
+         * @params {string= | number=} defaultData 无数据时默认返回
+         */
+        getDataFromOption: function (data, defaultData) {
+            return data != null ? (data.value != null ? data.value : data) : defaultData;
+        },
+        
+        // 亚像素优化
+        subPixelOptimize: function (position, lineWidth) {
+            if (lineWidth % 2 === 1) {
+                //position += position === Math.ceil(position) ? 0.5 : 0;
+                position = Math.floor(position) + 0.5;
+            }
+            else {
+                position = Math.round(position);
+            }
+            return position;
+        },
+        
+        // 默认resize
+        resize: function () {
+            this.refresh && this.refresh();
+            this.clearEffectShape && this.clearEffectShape(true);
+            var self = this;
+            setTimeout(function(){
+                self.animationEffect && self.animationEffect();
+            },200);
+        },
+
+        /**
+         * 清除图形数据,实例仍可用
+         */
+        clear :function () {
+            this.clearEffectShape && this.clearEffectShape();
+            this.zr && this.zr.delShape(this.shapeList);
+            this.shapeList = [];
+        },
+
+        /**
+         * 释放后实例不可用
+         */
+        dispose: function () {
+            this.onbeforDispose && this.onbeforDispose();
+            this.clear();
+            this.shapeList = null;
+            this.effectList = null;
+            this.messageCenter && this.messageCenter.unbind(
+                ecConfig.EVENT.LEGEND_HOVERLINK, this._onlegendhoverlink
+            );
+            this.onafterDispose && this.onafterDispose();
+        },
+        
+        query: ecQuery.query,
+        deepQuery: ecQuery.deepQuery,
+        deepMerge: ecQuery.deepMerge,
+        
+        parsePercent: number.parsePercent,
+        parseCenter: number.parseCenter,
+        parseRadius: number.parseRadius,
+        numAddCommas: number.addCommas,
+
+        getPrecision: number.getPrecision
+    };
+    
+    return Base;
+});

+ 804 - 0
src/main/webapp/static/echarts-2.2.7/src/component/categoryAxis.js

@@ -0,0 +1,804 @@
+/**
+ * echarts组件: 类目轴
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    var RectangleShape = require('zrender/shape/Rectangle');
+    
+    var ecConfig = require('../config');
+    // 类目轴
+    ecConfig.categoryAxis =  {
+        zlevel: 0,                  // 一级层叠
+        z: 0,                       // 二级层叠
+        show: true,
+        position: 'bottom',    // 位置
+        name: '',              // 坐标轴名字,默认为空
+        nameLocation: 'end',   // 坐标轴名字位置,支持'start' | 'end'
+        nameTextStyle: {},     // 坐标轴文字样式,默认取全局样式
+        boundaryGap: true,     // 类目起始和结束两端空白策略
+        axisLine: {            // 坐标轴线
+            show: true,        // 默认显示,属性show控制显示与否
+            onZero: true,
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#48b',
+                width: 2,
+                type: 'solid'
+            }
+        },
+        axisTick: {            // 坐标轴小标记
+            show: true,        // 属性show控制显示与否,默认不显示
+            interval: 'auto',
+            inside: false,    // 控制小标记是否在grid里 
+            // onGap: null,
+            length :5,         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#333',
+                width: 1
+            }
+        },
+        axisLabel: {           // 坐标轴文本标签,详见axis.axisLabel
+            show: true,
+            interval: 'auto',
+            rotate: 0,
+            margin: 8,
+            // clickable: false,
+            // formatter: null,
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333'
+            }
+        },
+        splitLine: {           // 分隔线
+            show: true,        // 默认显示,属性show控制显示与否
+            // onGap: null,
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: ['#ccc'],
+                width: 1,
+                type: 'solid'
+            }
+        },
+        splitArea: {           // 分隔区域
+            show: false,       // 默认不显示,属性show控制显示与否
+            // onGap: null,
+            areaStyle: {       // 属性areaStyle(详见areaStyle)控制区域样式
+                color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
+            }
+        }
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var zrArea = require('zrender/tool/area');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 类目轴参数
+     * @param {Grid} component 组件
+     */
+    function CategoryAxis(ecTheme, messageCenter, zr, option, myChart, axisBase) {
+        if (option.data.length < 1) {
+            console.error('option.data.length < 1.');
+            return;
+        }
+        
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+        
+        this.grid = this.component.grid;
+        
+        for (var method in axisBase) {
+            this[method] = axisBase[method];
+        }
+        
+        this.refresh(option);
+    }
+    
+    CategoryAxis.prototype = {
+        type : ecConfig.COMPONENT_TYPE_AXIS_CATEGORY,
+        _getReformedLabel : function (idx) {
+            var data = this.getDataFromOption(this.option.data[idx]);
+            var formatter = this.option.data[idx].formatter 
+                            || this.option.axisLabel.formatter;
+            if (formatter) {
+                if (typeof formatter == 'function') {
+                    data = formatter.call(this.myChart, data);
+                }
+                else if (typeof formatter == 'string') {
+                    data = formatter.replace('{value}', data);
+                }
+            }
+            return data;
+        },
+        
+        /**
+         * 计算标签显示挑选间隔
+         */
+        _getInterval : function () {
+            var interval   = this.option.axisLabel.interval;
+            if (interval == 'auto') {
+                // 麻烦的自适应计算
+                var fontSize = this.option.axisLabel.textStyle.fontSize;
+                var data = this.option.data;
+                var dataLength = this.option.data.length;
+
+                if (this.isHorizontal()) {
+                    // 横向
+                    if (dataLength > 3) {
+                        var gap = this.getGap();
+                        var isEnough = false;
+                        var labelSpace;
+                        var labelSize;
+                        var step = Math.floor(0.5 / gap);
+                        step = step < 1 ? 1 : step;
+                        interval = Math.floor(15 / gap);
+                        while (!isEnough && interval < dataLength) {
+                            interval += step;
+                            isEnough = true;
+                            labelSpace = Math.floor(gap * interval); // 标签左右至少间隔为3px
+                            for (var i = Math.floor((dataLength - 1)/ interval) * interval; 
+                                 i >= 0; i -= interval
+                             ) {
+                                if (this.option.axisLabel.rotate !== 0) {
+                                    // 有旋转
+                                    labelSize = fontSize;
+                                }
+                                else if (data[i].textStyle) {
+                                    labelSize = zrArea.getTextWidth(
+                                        this._getReformedLabel(i),
+                                        this.getFont(
+                                            zrUtil.merge(
+                                                data[i].textStyle,
+                                                this.option.axisLabel.textStyle
+                                           )
+                                        )
+                                    );
+                                }
+                                else {
+                                    /*
+                                    labelSize = zrArea.getTextWidth(
+                                        this._getReformedLabel(i),
+                                        font
+                                    );
+                                    */
+                                    // 不定义data级特殊文本样式,用fontSize优化getTextWidth
+                                    var label = this._getReformedLabel(i) + '';
+                                    var wLen = (label.match(/\w/g) || '').length;
+                                    var oLen = label.length - wLen;
+                                    labelSize = wLen * fontSize * 2 / 3 + oLen * fontSize;
+                                }
+
+                                if (labelSpace < labelSize) {
+                                    // 放不下,中断循环让interval++
+                                    isEnough = false;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    else {
+                        // 少于3个则全部显示
+                        interval = 1;
+                    }
+                }
+                else {
+                    // 纵向
+                    if (dataLength > 3) {
+                        var gap = this.getGap();
+                        interval = Math.floor(11 / gap);
+                        // 标签上下至少间隔为3px
+                        while ((gap * interval - 6) < fontSize
+                                && interval < dataLength
+                        ) {
+                            interval++;
+                        }
+                    }
+                    else {
+                        // 少于3个则全部显示
+                        interval = 1;
+                    }
+                }
+            }
+            else {
+                // 用户自定义间隔,支持funtion
+                interval = typeof interval == 'function' ? 1 : (interval - 0 + 1);
+            }
+
+            return interval;
+        },
+        
+        /**
+         * 绘制图形
+         */
+        _buildShape : function () {
+            // 标签显示的挑选间隔
+            this._interval = this._getInterval();
+            if (!this.option.show) {
+                return;
+            }
+            this.option.splitArea.show && this._buildSplitArea();
+            this.option.splitLine.show && this._buildSplitLine();
+            this.option.axisLine.show && this._buildAxisLine();
+            this.option.axisTick.show && this._buildAxisTick();
+            this.option.axisLabel.show && this._buildAxisLabel();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        // 小标记
+        _buildAxisTick : function () {
+            var axShape;
+            var data       = this.option.data;
+            var dataLength = this.option.data.length;
+            var tickOption = this.option.axisTick;
+            var length     = tickOption.length;
+            var color      = tickOption.lineStyle.color;
+            var lineWidth  = tickOption.lineStyle.width;
+            var intervalFunction = typeof tickOption.interval == 'function'
+                                   ? tickOption.interval 
+                                   : tickOption.interval == 'auto'
+                                     ? typeof this.option.axisLabel.interval == 'function'
+                                       ? this.option.axisLabel.interval : false
+                                     : false;
+            var interval   = intervalFunction
+                             ? 1
+                             : tickOption.interval == 'auto' 
+                               ? this._interval
+                               : (tickOption.interval - 0 + 1);
+            var onGap      = tickOption.onGap;
+            var optGap     = onGap 
+                             ? (this.getGap() / 2) 
+                             : typeof onGap == 'undefined'
+                                   ? (this.option.boundaryGap ? (this.getGap() / 2) : 0)
+                                   : 0;
+            var startIndex = optGap > 0 ? -interval : 0;                       
+            if (this.isHorizontal()) {
+                // 横向
+                var yPosition = this.option.position == 'bottom'
+                        ? (tickOption.inside 
+                           ? (this.grid.getYend() - length - 1) : (this.grid.getYend() + 1))
+                        : (tickOption.inside 
+                           ? (this.grid.getY() + 1) : (this.grid.getY() - length - 1));
+                var x;
+                for (var i = startIndex; i < dataLength; i += interval) {
+                    if (intervalFunction && !intervalFunction(i, data[i])) {
+                        // 回调并且回调返回false则跳过渲染
+                        continue;
+                    }
+                    // 亚像素优化
+                    x = this.subPixelOptimize(
+                        this.getCoordByIndex(i) + (i >= 0 ? optGap : 0), lineWidth
+                    );
+                    axShape = {
+                        _axisShape : 'axisTick',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable : false,
+                        style : {
+                            xStart : x,
+                            yStart : yPosition,
+                            xEnd : x,
+                            yEnd : yPosition + length,
+                            strokeColor : color,
+                            lineWidth : lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+            else {
+                // 纵向
+                var xPosition = this.option.position == 'left'
+                    ? (tickOption.inside 
+                       ? (this.grid.getX() + 1) : (this.grid.getX() - length - 1))
+                    : (tickOption.inside 
+                       ? (this.grid.getXend() - length - 1) : (this.grid.getXend() + 1));
+                        
+                var y;
+                for (var i = startIndex; i < dataLength; i += interval) {
+                    if (intervalFunction && !intervalFunction(i, data[i])) {
+                        // 回调并且回调返回false则中断渲染
+                        continue;
+                    }
+                    // 亚像素优化
+                    y = this.subPixelOptimize(
+                        this.getCoordByIndex(i) - (i >= 0 ? optGap : 0), lineWidth
+                    );
+                    axShape = {
+                        _axisShape : 'axisTick',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable : false,
+                        style : {
+                            xStart : xPosition,
+                            yStart : y,
+                            xEnd : xPosition + length,
+                            yEnd : y,
+                            strokeColor : color,
+                            lineWidth : lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+        },
+
+        // 坐标轴文本
+        _buildAxisLabel : function () {
+            var axShape;
+            var data       = this.option.data;
+            var dataLength = this.option.data.length;
+            var labelOption = this.option.axisLabel;
+            var rotate     = labelOption.rotate;
+            var margin     = labelOption.margin;
+            var clickable  = labelOption.clickable;
+            var textStyle  = labelOption.textStyle;
+            var intervalFunction = typeof labelOption.interval == 'function'
+                                   ? labelOption.interval : false;
+            var dataTextStyle;
+
+            if (this.isHorizontal()) {
+                // 横向
+                var yPosition;
+                var baseLine;
+                if (this.option.position == 'bottom') {
+                    yPosition = this.grid.getYend() + margin;
+                    baseLine = 'top';
+                }
+                else {
+                    yPosition = this.grid.getY() - margin;
+                    baseLine = 'bottom';
+                }
+
+                for (var i = 0; i < dataLength; i += this._interval) {
+                    if ((intervalFunction && !intervalFunction(i, data[i])) 
+                        // 回调并且回调返回false则中断渲染
+                        || this._getReformedLabel(i) === '' // 空文本优化
+                    ) {
+                        continue;
+                    }
+                    dataTextStyle = zrUtil.merge(
+                        data[i].textStyle || {},
+                        textStyle
+                    );
+                    axShape = {
+                        // shape : 'text',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase() + 3,
+                        hoverable : false,
+                        style : {
+                            x : this.getCoordByIndex(i),
+                            y : yPosition,
+                            color : dataTextStyle.color,
+                            text : this._getReformedLabel(i),
+                            textFont : this.getFont(dataTextStyle),
+                            textAlign : dataTextStyle.align || 'center',
+                            textBaseline : dataTextStyle.baseline || baseLine
+                        }
+                    };
+                    if (rotate) {
+                        axShape.style.textAlign = rotate > 0
+                                                  ? (this.option.position == 'bottom'
+                                                    ? 'right' : 'left')
+                                                  : (this.option.position == 'bottom'
+                                                    ? 'left' : 'right');
+                        axShape.rotation = [
+                            rotate * Math.PI / 180,
+                            axShape.style.x,
+                            axShape.style.y
+                        ];
+                    }
+                    this.shapeList.push(new TextShape(
+                        this._axisLabelClickable(clickable, axShape)
+                    ));
+                }
+            }
+            else {
+                // 纵向
+                var xPosition;
+                var align;
+                if (this.option.position == 'left') {
+                    xPosition = this.grid.getX() - margin;
+                    align = 'right';
+                }
+                else {
+                    xPosition = this.grid.getXend() + margin;
+                    align = 'left';
+                }
+
+                for (var i = 0; i < dataLength; i += this._interval) {
+                    if ((intervalFunction && !intervalFunction(i, data[i])) 
+                        // 回调并且回调返回false则中断渲染
+                        || this._getReformedLabel(i) === '' // 空文本优化
+                    ) {
+                        continue;
+                    }
+                    dataTextStyle = zrUtil.merge(
+                        data[i].textStyle || {},
+                        textStyle
+                    );
+                    axShape = {
+                        // shape : 'text',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase() + 3,
+                        hoverable : false,
+                        style : {
+                            x : xPosition,
+                            y : this.getCoordByIndex(i),
+                            color : dataTextStyle.color,
+                            text : this._getReformedLabel(i),
+                            textFont : this.getFont(dataTextStyle),
+                            textAlign : dataTextStyle.align || align,
+                            textBaseline : dataTextStyle.baseline 
+                                           || (i === 0 && this.option.name !== '')
+                                               ? 'bottom'
+                                               : (i == (dataLength - 1) 
+                                                  && this.option.name !== '')
+                                                 ? 'top'
+                                                 : 'middle'
+                        }
+                    };
+                    
+                    if (rotate) {
+                        axShape.rotation = [
+                            rotate * Math.PI / 180,
+                            axShape.style.x,
+                            axShape.style.y
+                        ];
+                    }
+                    this.shapeList.push(new TextShape(
+                        this._axisLabelClickable(clickable, axShape)
+                    ));
+                }
+            }
+        },
+        
+        _buildSplitLine : function () {
+            var axShape;
+            var data        = this.option.data;
+            var dataLength  = this.option.data.length;
+            var sLineOption = this.option.splitLine;
+            var lineType    = sLineOption.lineStyle.type;
+            var lineWidth   = sLineOption.lineStyle.width;
+            var color       = sLineOption.lineStyle.color;
+            color = color instanceof Array ? color : [color];
+            var colorLength = color.length;
+            
+            // splitLine随axisLable
+            var intervalFunction = typeof this.option.axisLabel.interval == 'function'
+                                   ? this.option.axisLabel.interval : false;
+
+            var onGap      = sLineOption.onGap;
+            var optGap     = onGap 
+                             ? (this.getGap() / 2) 
+                             : typeof onGap == 'undefined'
+                                   ? (this.option.boundaryGap ? (this.getGap() / 2) : 0)
+                                   : 0;
+            dataLength -= (onGap || (typeof onGap == 'undefined' && this.option.boundaryGap)) 
+                          ? 1 : 0;
+            if (this.isHorizontal()) {
+                // 横向
+                var sy = this.grid.getY();
+                var ey = this.grid.getYend();
+                var x;
+
+                for (var i = 0; i < dataLength; i += this._interval) {
+                    if (intervalFunction && !intervalFunction(i, data[i])) {
+                        // 回调并且回调返回false则跳过渲染
+                        continue;
+                    }
+                    // 亚像素优化
+                    x = this.subPixelOptimize(
+                        this.getCoordByIndex(i) + optGap, lineWidth
+                    );
+                    axShape = {
+                        // shape : 'line',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable : false,
+                        style : {
+                            xStart : x,
+                            yStart : sy,
+                            xEnd : x,
+                            yEnd : ey,
+                            strokeColor : color[(i / this._interval) % colorLength],
+                            lineType : lineType,
+                            lineWidth : lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+
+            }
+            else {
+                // 纵向
+                var sx = this.grid.getX();
+                var ex = this.grid.getXend();
+                var y;
+
+                for (var i = 0; i < dataLength; i += this._interval) {
+                    if (intervalFunction && !intervalFunction(i, data[i])) {
+                        // 回调并且回调返回false则跳过渲染
+                        continue;
+                    }
+                    // 亚像素优化
+                    y = this.subPixelOptimize(
+                        this.getCoordByIndex(i) - optGap, lineWidth
+                    );
+                    axShape = {
+                        // shape : 'line',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable : false,
+                        style : {
+                            xStart : sx,
+                            yStart : y,
+                            xEnd : ex,
+                            yEnd : y,
+                            strokeColor : color[(i / this._interval) % colorLength],
+                            lineType : lineType,
+                            lineWidth : lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+        },
+
+        _buildSplitArea : function () {
+            var axShape;
+            var data        = this.option.data;
+            var sAreaOption = this.option.splitArea;
+            var color = sAreaOption.areaStyle.color;
+            if (!(color instanceof Array)) {
+                // 非数组一律认为是单一颜色的字符串,单一颜色则用一个背景,颜色错误不负责啊!!!
+                axShape = {
+                    // shape : 'rectangle',
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    hoverable : false,
+                    style : {
+                        x : this.grid.getX(),
+                        y : this.grid.getY(),
+                        width : this.grid.getWidth(),
+                        height : this.grid.getHeight(),
+                        color : color
+                        // type : this.option.splitArea.areaStyle.type,
+                    }
+                };
+                this.shapeList.push(new RectangleShape(axShape));
+            }
+            else {
+                // 多颜色
+                var colorLength = color.length;
+                var dataLength  = this.option.data.length;
+
+                // splitArea随axisLable
+                var intervalFunction = typeof this.option.axisLabel.interval == 'function'
+                                       ? this.option.axisLabel.interval : false;
+        
+                var onGap      = sAreaOption.onGap;
+                var optGap     = onGap 
+                                 ? (this.getGap() / 2) 
+                                 : typeof onGap == 'undefined'
+                                       ? (this.option.boundaryGap ? (this.getGap() / 2) : 0)
+                                       : 0;
+                if (this.isHorizontal()) {
+                    // 横向
+                    var y = this.grid.getY();
+                    var height = this.grid.getHeight();
+                    var lastX = this.grid.getX();
+                    var curX;
+    
+                    for (var i = 0; i <= dataLength; i += this._interval) {
+                        if (intervalFunction && !intervalFunction(i, data[i]) && i < dataLength) {
+                            // 回调并且回调返回false则跳过渲染
+                            continue;
+                        }
+                        curX = i < dataLength
+                               ? (this.getCoordByIndex(i) + optGap)
+                               : this.grid.getXend();
+                        axShape = {
+                            // shape : 'rectangle',
+                            zlevel: this.getZlevelBase(),
+                            z: this.getZBase(),
+                            hoverable : false,
+                            style : {
+                                x : lastX,
+                                y : y,
+                                width : curX - lastX,
+                                height : height,
+                                color : color[(i / this._interval) % colorLength]
+                                // type : this.option.splitArea.areaStyle.type,
+                            }
+                        };
+                        this.shapeList.push(new RectangleShape(axShape));
+                        lastX = curX;
+                    }
+                }
+                else {
+                    // 纵向
+                    var x = this.grid.getX();
+                    var width = this.grid.getWidth();
+                    var lastYend = this.grid.getYend();
+                    var curY;
+    
+                    for (var i = 0; i <= dataLength; i += this._interval) {
+                        if (intervalFunction && !intervalFunction(i, data[i]) && i < dataLength) {
+                            // 回调并且回调返回false则跳过渲染
+                            continue;
+                        }
+                        curY = i < dataLength
+                               ? (this.getCoordByIndex(i) - optGap)
+                               : this.grid.getY();
+                        axShape = {
+                            // shape : 'rectangle',
+                            zlevel: this.getZlevelBase(),
+                            z: this.getZBase(),
+                            hoverable : false,
+                            style : {
+                                x : x,
+                                y : curY,
+                                width : width,
+                                height : lastYend - curY,
+                                color : color[(i / this._interval) % colorLength]
+                                // type : this.option.splitArea.areaStyle.type
+                            }
+                        };
+                        this.shapeList.push(new RectangleShape(axShape));
+                        lastYend = curY;
+                    }
+                }
+            }
+        },
+
+        /**
+         * 刷新
+         */
+        refresh : function (newOption) {
+            if (newOption) {
+                this.option = this.reformOption(newOption);
+                // 通用字体设置
+                this.option.axisLabel.textStyle = this.getTextStyle(
+                    this.option.axisLabel.textStyle
+                );
+            }
+            this.clear();
+            this._buildShape();
+        },
+
+        /**
+         * 返回间隔
+         */
+        getGap : function () {
+            var dataLength = this.option.data.length;
+            var total = this.isHorizontal()
+                        ? this.grid.getWidth()
+                        : this.grid.getHeight();
+            if (this.option.boundaryGap) {              // 留空
+                return total / dataLength;
+            }
+            else {                                      // 顶头
+                return total / (dataLength > 1 ? (dataLength - 1) : 1);
+            }
+        },
+
+        // 根据值换算位置
+        getCoord : function (value) {
+            var data = this.option.data;
+            var dataLength = data.length;
+            var gap = this.getGap();
+            var position = this.option.boundaryGap ? (gap / 2) : 0;
+
+            for (var i = 0; i < dataLength; i++) {
+                if (this.getDataFromOption(data[i]) == value) {
+                    if (this.isHorizontal()) {
+                        // 横向
+                        position = this.grid.getX() + position;
+                    }
+                    else {
+                        // 纵向
+                        position = this.grid.getYend() - position;
+                    }
+                    
+                    return position;
+                    // Math.floor可能引起一些偏差,但性能会更好
+                    /* 准确更重要
+                    return (i === 0 || i == dataLength - 1)
+                           ? position
+                           : Math.floor(position);
+                    */
+                }
+                position += gap;
+            }
+        },
+
+        // 根据类目轴数据索引换算位置
+        getCoordByIndex : function (dataIndex) {
+            if (dataIndex < 0) {
+                if (this.isHorizontal()) {
+                    return this.grid.getX();
+                }
+                else {
+                    return this.grid.getYend();
+                }
+            }
+            else if (dataIndex > this.option.data.length - 1) {
+                if (this.isHorizontal()) {
+                    return this.grid.getXend();
+                }
+                else {
+                    return this.grid.getY();
+                }
+            }
+            else {
+                var gap = this.getGap();
+                var position = this.option.boundaryGap ? (gap / 2) : 0;
+                position += dataIndex * gap;
+                
+                if (this.isHorizontal()) {
+                    // 横向
+                    position = this.grid.getX() + position;
+                }
+                else {
+                    // 纵向
+                    position = this.grid.getYend() - position;
+                }
+                
+                return position;
+                /* 准确更重要
+                return (dataIndex === 0 || dataIndex == this.option.data.length - 1)
+                       ? position
+                       : Math.floor(position);
+                */
+            }
+        },
+
+        // 根据类目轴数据索引换算类目轴名称
+        getNameByIndex : function (dataIndex) {
+            return this.getDataFromOption(this.option.data[dataIndex]);
+        },
+        
+        // 根据类目轴名称换算类目轴数据索引
+        getIndexByName : function (name) {
+            var data = this.option.data;
+            var dataLength = data.length;
+
+            for (var i = 0; i < dataLength; i++) {
+                if (this.getDataFromOption(data[i]) == name) {
+                    return i;
+                }
+            }
+            
+            return -1;
+        },
+        
+        // 根据位置换算值
+        getValueFromCoord : function() {
+            return '';
+        },
+
+        /**
+         * 根据类目轴数据索引返回是否为主轴线
+         * @param {number} dataIndex 类目轴数据索引
+         * @return {boolean} 是否为主轴
+         */
+        isMainAxis : function (dataIndex) {
+            return dataIndex % this._interval === 0;
+        }
+    };
+    
+    zrUtil.inherits(CategoryAxis, Base);
+    
+    require('../component').define('categoryAxis', CategoryAxis);
+    
+    return CategoryAxis;
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1659 - 0
src/main/webapp/static/echarts-2.2.7/src/component/dataRange.js


+ 461 - 0
src/main/webapp/static/echarts-2.2.7/src/component/dataView.js

@@ -0,0 +1,461 @@
+/**
+ * echarts组件:提示框
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+
+    var ecConfig = require('../config');
+    var zrUtil = require('zrender/tool/util');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 提示框参数
+     * @param {HtmlElement} dom 目标对象
+     */
+    function DataView(ecTheme, messageCenter, zr, option, myChart) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.dom = myChart.dom;
+        
+        // dataview dom & css
+        this._tDom = document.createElement('div');
+        this._textArea = document.createElement('textArea');
+        this._buttonRefresh = document.createElement('button');
+        // 高级浏览器默认type为submit
+        // 如果图表出现在form表单时,点击button后会提交表单
+        // 设置为button,防止点击button后提交表单
+        this._buttonRefresh.setAttribute('type', 'button');
+        this._buttonClose = document.createElement('button');
+        this._buttonClose.setAttribute('type', 'button');
+        this._hasShow = false;
+
+        // 缓存一些高宽数据
+        this._zrHeight = zr.getHeight();
+        this._zrWidth = zr.getWidth();
+    
+        this._tDom.className = 'echarts-dataview';
+        this.hide();
+        this.dom.firstChild.appendChild(this._tDom);
+
+        if (window.addEventListener) {
+            this._tDom.addEventListener('click', this._stop);
+            this._tDom.addEventListener('mousewheel', this._stop);
+            this._tDom.addEventListener('mousemove', this._stop);
+            this._tDom.addEventListener('mousedown', this._stop);
+            this._tDom.addEventListener('mouseup', this._stop);
+
+            // mobile支持
+            this._tDom.addEventListener('touchstart', this._stop);
+            this._tDom.addEventListener('touchmove', this._stop);
+            this._tDom.addEventListener('touchend', this._stop);
+        }
+        else {
+            this._tDom.attachEvent('onclick', this._stop);
+            this._tDom.attachEvent('onmousewheel', this._stop);
+            this._tDom.attachEvent('onmousemove', this._stop);
+            this._tDom.attachEvent('onmousedown', this._stop);
+            this._tDom.attachEvent('onmouseup', this._stop);
+        }
+    }
+    
+    DataView.prototype = {
+        type : ecConfig.COMPONENT_TYPE_DATAVIEW,
+        _lang : ['Data View', 'close', 'refresh'],
+        // 通用样式
+        _gCssText : 'position:absolute;'
+                    + 'display:block;'
+                    + 'overflow:hidden;'
+                    + 'transition:height 0.8s,background-color 1s;'
+                    + '-moz-transition:height 0.8s,background-color 1s;'
+                    + '-webkit-transition:height 0.8s,background-color 1s;'
+                    + '-o-transition:height 0.8s,background-color 1s;'
+                    + 'z-index:1;'
+                    + 'left:0;'
+                    + 'top:0;',
+        hide : function () {
+            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
+                           + 'height:' + 0 + 'px;'
+                           + 'background-color:#f0ffff;';
+            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
+            // 这是个很恶心的事情
+            /*
+            this.dom.onselectstart = function () {
+                return false;
+            };
+            */
+        },
+
+        show : function (newOption) {
+            this._hasShow = true;
+            var lang = this.query(this.option, 'toolbox.feature.dataView.lang')
+                       || this._lang;
+
+            this.option = newOption;
+
+            this._tDom.innerHTML = '<p style="padding:8px 0;margin:0 0 10px 0;'
+                              + 'border-bottom:1px solid #eee">'
+                              + (lang[0] || this._lang[0])
+                              + '</p>';
+
+            var customContent = this.query(
+                this.option, 'toolbox.feature.dataView.optionToContent'
+            );
+            if (typeof customContent != 'function') {
+                this._textArea.value = this._optionToContent();
+            }
+            else {
+                // innerHTML the custom optionToContent;
+                this._textArea = document.createElement('div');
+                this._textArea.innerHTML = customContent(this.option);
+            }
+
+            this._textArea.style.cssText =
+                'display:block;margin:0 0 8px 0;padding:4px 6px;overflow:auto;'
+                + 'width:100%;'
+                + 'height:' + (this._zrHeight - 100) + 'px;';
+
+            this._tDom.appendChild(this._textArea);
+
+            this._buttonClose.style.cssText = 'float:right;padding:1px 6px;';
+            this._buttonClose.innerHTML = lang[1] || this._lang[1];
+            var self = this;
+            this._buttonClose.onclick = function (){
+                self.hide();
+            };
+            this._tDom.appendChild(this._buttonClose);
+
+            if (this.query(this.option, 'toolbox.feature.dataView.readOnly')
+                === false
+            ) {
+                this._buttonRefresh.style.cssText =
+                    'float:right;margin-right:10px;padding:1px 6px;';
+                this._buttonRefresh.innerHTML = lang[2] || this._lang[2];
+                this._buttonRefresh.onclick = function (){
+                    self._save();
+                };
+                this._textArea.readOnly = false;
+                this._textArea.style.cursor = 'default';
+            }
+            else {
+                this._buttonRefresh.style.cssText =
+                    'display:none';
+                this._textArea.readOnly = true;
+                this._textArea.style.cursor = 'text';
+            }
+            this._tDom.appendChild(this._buttonRefresh);
+
+            this._sizeCssText = 'width:' + this._zrWidth + 'px;'
+                           + 'height:' + this._zrHeight + 'px;'
+                           + 'background-color:#fff;';
+            this._tDom.style.cssText = this._gCssText + this._sizeCssText;
+            // 这是个很恶心的事情
+            /*
+            this.dom.onselectstart = function () {
+                return true;
+            };
+            */
+        },
+
+        _optionToContent : function () {
+            var i;
+            var j;
+            var k;
+            var len;
+            var data;
+            var valueList;
+            var axisList = [];
+            var content = '';
+            if (this.option.xAxis) {
+                if (this.option.xAxis instanceof Array) {
+                    axisList = this.option.xAxis;
+                } else {
+                    axisList = [this.option.xAxis];
+                }
+                for (i = 0, len = axisList.length; i < len; i++) {
+                    // 横纵默认为类目
+                    if ((axisList[i].type || 'category') == 'category') {
+                        valueList = [];
+                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
+                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
+                        }
+                        content += valueList.join(', ') + '\n\n';
+                    }
+                }
+            }
+
+            if (this.option.yAxis) {
+                if (this.option.yAxis instanceof Array) {
+                    axisList = this.option.yAxis;
+                } else {
+                    axisList = [this.option.yAxis];
+                }
+                for (i = 0, len = axisList.length; i < len; i++) {
+                    if (axisList[i].type  == 'category') {
+                        valueList = [];
+                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
+                            valueList.push(this.getDataFromOption(axisList[i].data[j]));
+                        }
+                        content += valueList.join(', ') + '\n\n';
+                    }
+                }
+            }
+
+            var series = this.option.series;
+            var itemName;
+            for (i = 0, len = series.length; i < len; i++) {
+                valueList = [];
+                for (j = 0, k = series[i].data.length; j < k; j++) {
+                    data = series[i].data[j];
+                    if (series[i].type == ecConfig.CHART_TYPE_PIE
+                        || series[i].type == ecConfig.CHART_TYPE_MAP
+                    ) {
+                        itemName = (data.name || '-') + ':';
+                    }
+                    else {
+                        itemName = '';
+                    }
+                    
+                    if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
+                        data = this.getDataFromOption(data).join(', ');
+                    }
+                    valueList.push(itemName + this.getDataFromOption(data));
+                }
+                content += (series[i].name || '-') + ' : \n';
+                content += valueList.join(
+                    series[i].type == ecConfig.CHART_TYPE_SCATTER ? '\n': ', '
+                );
+                content += '\n\n';
+            }
+
+            return content;
+        },
+
+        _save : function () {
+            var customContent = this.query(
+                this.option, 'toolbox.feature.dataView.contentToOption'
+            );
+            if (typeof customContent != 'function') {
+                var text = this._textArea.value.split('\n');
+                var content = [];
+                for (var i = 0, l = text.length; i < l; i++) {
+                    text[i] = this._trim(text[i]);
+                    if (text[i] !== '') {
+                        content.push(text[i]);
+                    }
+                }
+                this._contentToOption(content);
+            }
+            else {
+                // return the textArea dom for custom contentToOption
+                customContent(this._textArea, this.option);
+            }
+
+            this.hide();
+            
+            var self = this;
+            setTimeout(
+                function (){
+                    self.messageCenter && self.messageCenter.dispatch(
+                        ecConfig.EVENT.DATA_VIEW_CHANGED,
+                        null,
+                        {option : self.option},
+                        self.myChart
+                    );
+                },
+                // 有动画,所以高级浏览器时间更长点
+                self.canvasSupported ? 800 : 100
+            );
+        },
+
+        _contentToOption : function (content) {
+            var i;
+            var j;
+            var k;
+            var len;
+            var data;
+            var axisList = [];
+
+            var contentIdx = 0;
+            var contentValueList;
+            var value;
+
+            if (this.option.xAxis) {
+                if (this.option.xAxis instanceof Array) {
+                    axisList = this.option.xAxis;
+                } else {
+                    axisList = [this.option.xAxis];
+                }
+                for (i = 0, len = axisList.length; i < len; i++) {
+                    // 横纵默认为类目
+                    if ((axisList[i].type || 'category') == 'category'
+                    ) {
+                        contentValueList = content[contentIdx].split(',');
+                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
+                            value = this._trim(contentValueList[j] || '');
+                            data = axisList[i].data[j];
+                            if (typeof axisList[i].data[j].value != 'undefined'
+                            ) {
+                                axisList[i].data[j].value = value;
+                            }
+                            else {
+                                axisList[i].data[j] = value;
+                            }
+                        }
+                        contentIdx++;
+                    }
+                }
+            }
+
+            if (this.option.yAxis) {
+                if (this.option.yAxis instanceof Array) {
+                    axisList = this.option.yAxis;
+                } else {
+                    axisList = [this.option.yAxis];
+                }
+                for (i = 0, len = axisList.length; i < len; i++) {
+                    if (axisList[i].type  == 'category') {
+                        contentValueList = content[contentIdx].split(',');
+                        for (j = 0, k = axisList[i].data.length; j < k; j++) {
+                            value = this._trim(contentValueList[j] || '');
+                            data = axisList[i].data[j];
+                            if (typeof axisList[i].data[j].value != 'undefined'
+                            ) {
+                                axisList[i].data[j].value = value;
+                            }
+                            else {
+                                axisList[i].data[j] = value;
+                            }
+                        }
+                        contentIdx++;
+                    }
+                }
+            }
+
+            var series = this.option.series;
+            for (i = 0, len = series.length; i < len; i++) {
+                contentIdx++;
+                if (series[i].type == ecConfig.CHART_TYPE_SCATTER) {
+                    for (var j = 0, k = series[i].data.length; j < k; j++) {
+                        contentValueList = content[contentIdx];
+                        value = contentValueList.replace(' ','').split(',');
+                        if (typeof series[i].data[j].value != 'undefined'
+                        ) {
+                            series[i].data[j].value = value;
+                        }
+                        else {
+                            series[i].data[j] = value;
+                        }
+                        contentIdx++;
+                    }
+                }
+                else {
+                    contentValueList = content[contentIdx].split(',');
+                    for (var j = 0, k = series[i].data.length; j < k; j++) {
+                        value = (contentValueList[j] || '').replace(/.*:/,'');
+                        value = this._trim(value);
+                        value = (value != '-' && value !== '')
+                                ? (value - 0)
+                                : '-';
+                        if (typeof series[i].data[j].value != 'undefined'
+                        ) {
+                            series[i].data[j].value = value;
+                        }
+                        else {
+                            series[i].data[j] = value;
+                        }
+                    }
+                    contentIdx++;
+                }
+            }
+        },
+
+        _trim : function (str){
+            var trimer = new RegExp(
+                '(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\x24)', 'g'
+            );
+            return str.replace(trimer, '');
+        },
+
+        // 阻塞zrender事件
+        _stop : function (e){
+            e = e || window.event;
+            if (e.stopPropagation) {
+                e.stopPropagation();
+            }
+            else {
+                e.cancelBubble = true;
+            }
+        },
+
+        /**
+         * zrender事件响应:窗口大小改变
+         */
+        resize : function () {
+            this._zrHeight = this.zr.getHeight();
+            this._zrWidth = this.zr.getWidth();
+            if (this._tDom.offsetHeight > 10) {
+                this._sizeCssText = 'width:' + this._zrWidth + 'px;'
+                               + 'height:' + this._zrHeight + 'px;'
+                               + 'background-color:#fff;';
+                this._tDom.style.cssText = this._gCssText + this._sizeCssText;
+                this._textArea.style.cssText = 'display:block;margin:0 0 8px 0;'
+                                        + 'padding:4px 6px;overflow:auto;'
+                                        + 'width:100%;'
+                                        + 'height:' + (this._zrHeight - 100) + 'px;';
+            }
+        },
+
+        /**
+         * 释放后实例不可用,重载基类方法
+         */
+        dispose : function () {
+            if (window.removeEventListener) {
+                this._tDom.removeEventListener('click', this._stop);
+                this._tDom.removeEventListener('mousewheel', this._stop);
+                this._tDom.removeEventListener('mousemove', this._stop);
+                this._tDom.removeEventListener('mousedown', this._stop);
+                this._tDom.removeEventListener('mouseup', this._stop);
+
+                // mobile支持
+                this._tDom.removeEventListener('touchstart', this._stop);
+                this._tDom.removeEventListener('touchmove', this._stop);
+                this._tDom.removeEventListener('touchend', this._stop);
+            }
+            else {
+                this._tDom.detachEvent('onclick', this._stop);
+                this._tDom.detachEvent('onmousewheel', this._stop);
+                this._tDom.detachEvent('onmousemove', this._stop);
+                this._tDom.detachEvent('onmousedown', this._stop);
+                this._tDom.detachEvent('onmouseup', this._stop);
+            }
+
+            this._buttonRefresh.onclick = null;
+            this._buttonClose.onclick = null;
+
+            if (this._hasShow) {
+                this._tDom.removeChild(this._textArea);
+                this._tDom.removeChild(this._buttonRefresh);
+                this._tDom.removeChild(this._buttonClose);
+            }
+
+            this._textArea = null;
+            this._buttonRefresh = null;
+            this._buttonClose = null;
+
+            this.dom.firstChild.removeChild(this._tDom);
+            this._tDom = null;
+        }
+    };
+    
+    zrUtil.inherits(DataView, Base);
+    
+    require('../component').define('dataView', DataView);
+    
+    return DataView;
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1244 - 0
src/main/webapp/static/echarts-2.2.7/src/component/dataZoom.js


+ 184 - 0
src/main/webapp/static/echarts-2.2.7/src/component/grid.js

@@ -0,0 +1,184 @@
+/**
+ * echarts组件: 网格
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var RectangleShape = require('zrender/shape/Rectangle');
+    
+    var ecConfig = require('../config');
+    // 网格
+    ecConfig.grid = {
+        zlevel: 0,                  // 一级层叠
+        z: 0,                       // 二级层叠
+        x: 80,
+        y: 60,
+        x2: 80,
+        y2: 60,
+        // width: {totalWidth} - x - x2,
+        // height: {totalHeight} - y - y2,
+        backgroundColor: 'rgba(0,0,0,0)',
+        borderWidth: 1,
+        borderColor: '#ccc'
+    };
+
+    var zrUtil = require('zrender/tool/util');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表选项
+     *      @param {number=} option.grid.x 直角坐标系内绘图网格起始横坐标,数值单位px
+     *      @param {number=} option.grid.y 直角坐标系内绘图网格起始纵坐标,数值单位px
+     *      @param {number=} option.grid.width 直角坐标系内绘图网格宽度,数值单位px
+     *      @param {number=} option.grid.height 直角坐标系内绘图网格高度,数值单位px
+     */
+    function Grid(ecTheme, messageCenter, zr, option, myChart) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+    
+    Grid.prototype = {
+        type: ecConfig.COMPONENT_TYPE_GRID,
+
+        getX: function () {
+            return this._x;
+        },
+
+        getY: function () {
+            return this._y;
+        },
+
+        getWidth: function () {
+            return this._width;
+        },
+
+        getHeight: function () {
+            return this._height;
+        },
+
+        getXend: function () {
+            return this._x + this._width;
+        },
+
+        getYend: function () {
+            return this._y + this._height;
+        },
+
+        getArea: function () {
+            return {
+                x: this._x,
+                y: this._y,
+                width: this._width,
+                height: this._height
+            };
+        },
+        
+        getBbox: function() {
+            return [
+                [ this._x, this._y ],
+                [ this.getXend(), this.getYend() ]
+            ];
+        },
+        
+        /**
+         * 实在找不到合适的地方做了,各种粗暴的写法~ -_-
+         */
+        refixAxisShape: function(component) {
+            var zeroX;
+            var zeroY;
+            var axisList = component.xAxis._axisList.concat(
+                component.yAxis ? component.yAxis._axisList : []
+            );
+            var len = axisList.length;
+            var axis;
+            while (len--) {
+                axis = axisList[len];
+                if (axis.type == ecConfig.COMPONENT_TYPE_AXIS_VALUE 
+                    && axis._min < 0  
+                    && axis._max >= 0
+                ) {
+                    axis.isHorizontal()
+                    ? (zeroX = axis.getCoord(0))
+                    : (zeroY = axis.getCoord(0));
+                }
+            }
+            if (typeof zeroX != 'undefined' || typeof zeroY != 'undefined') {
+                len = axisList.length;
+                while (len--) {
+                    axisList[len].refixAxisShape(zeroX, zeroY);
+                }
+            }
+        },
+        
+        refresh: function (newOption) {
+            if (newOption
+                || this._zrWidth != this.zr.getWidth() 
+                || this._zrHeight != this.zr.getHeight()
+            ) {
+                this.clear();
+                this.option = newOption || this.option;
+                this.option.grid = this.reformOption(this.option.grid);
+    
+                var gridOption = this.option.grid;
+                this._zrWidth = this.zr.getWidth();
+                this._zrHeight = this.zr.getHeight();
+                this._x = this.parsePercent(gridOption.x, this._zrWidth);
+                this._y = this.parsePercent(gridOption.y, this._zrHeight);
+                var x2 = this.parsePercent(gridOption.x2, this._zrWidth);
+                var y2 = this.parsePercent(gridOption.y2, this._zrHeight);
+                
+    
+                if (typeof gridOption.width == 'undefined') {
+                    this._width = this._zrWidth - this._x - x2;
+                }
+                else {
+                    this._width = this.parsePercent(gridOption.width, this._zrWidth);
+                }
+                this._width = this._width <= 0 ? 10 : this._width;
+    
+                if (typeof gridOption.height == 'undefined') {
+                    this._height = this._zrHeight - this._y - y2;
+                }
+                else {
+                    this._height = this.parsePercent(gridOption.height, this._zrHeight);
+                }
+                this._height = this._height <= 0 ? 10 : this._height;
+                
+                this._x = this.subPixelOptimize(this._x, gridOption.borderWidth);
+                this._y = this.subPixelOptimize(this._y, gridOption.borderWidth);
+    
+                this.shapeList.push(new RectangleShape({
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    hoverable: false,
+                    style: {
+                        x: this._x,
+                        y: this._y,
+                        width: this._width,
+                        height: this._height,
+                        brushType: gridOption.borderWidth > 0 ? 'both' : 'fill',
+                        color: gridOption.backgroundColor,
+                        strokeColor: gridOption.borderColor,
+                        lineWidth: gridOption.borderWidth
+                        // type: this.option.splitArea.areaStyle.type,
+                    }
+                }));
+                this.zr.addShape(this.shapeList[0]);
+            }
+        }
+    };
+    
+    zrUtil.inherits(Grid, Base);
+    
+    require('../component').define('grid', Grid);
+    
+    return Grid;
+});

+ 972 - 0
src/main/webapp/static/echarts-2.2.7/src/component/legend.js

@@ -0,0 +1,972 @@
+/**
+ * echarts组件:图例
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var RectangleShape = require('zrender/shape/Rectangle');
+    var SectorShape = require('zrender/shape/Sector');
+    //var BeziercurveShape = require('zrender/shape/Beziercurve');
+    var IconShape = require('../util/shape/Icon');
+    var CandleShape = require('../util/shape/Candle');
+    
+    var ecConfig = require('../config');
+     // 图例
+    ecConfig.legend = {
+        zlevel: 0,                  // 一级层叠
+        z: 4,                       // 二级层叠
+        show: true,
+        orient: 'horizontal',      // 布局方式,默认为水平布局,可选为:
+                                   // 'horizontal' ¦ 'vertical'
+        x: 'center',               // 水平安放位置,默认为全图居中,可选为:
+                                   // 'center' ¦ 'left' ¦ 'right'
+                                   // ¦ {number}(x坐标,单位px)
+        y: 'top',                  // 垂直安放位置,默认为全图顶端,可选为:
+                                   // 'top' ¦ 'bottom' ¦ 'center'
+                                   // ¦ {number}(y坐标,单位px)
+        backgroundColor: 'rgba(0,0,0,0)',
+        borderColor: '#ccc',       // 图例边框颜色
+        borderWidth: 0,            // 图例边框线宽,单位px,默认为0(无边框)
+        padding: 5,                // 图例内边距,单位px,默认各方向内边距为5,
+                                   // 接受数组分别设定上右下左边距,同css
+        itemGap: 10,               // 各个item之间的间隔,单位px,默认为10,
+                                   // 横向布局时为水平间隔,纵向布局时为纵向间隔
+        itemWidth: 20,             // 图例图形宽度
+        itemHeight: 14,            // 图例图形高度
+        textStyle: {
+            color: '#333'          // 图例文字颜色
+        },
+        selectedMode: true         // 选择模式,默认开启图例开关
+        // selected: null,         // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入
+        // data: [],               // 图例内容(详见legend.data,数组中每一项代表一个item
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var zrArea = require('zrender/tool/area');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表参数
+     */
+    function Legend(ecTheme, messageCenter, zr, option, myChart) {
+        if (!this.query(option, 'legend.data')) {
+            console.error('option.legend.data has not been defined.');
+            return;
+        }
+        
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+        
+        var self = this;
+        self._legendSelected = function (param) {
+            self.__legendSelected(param);
+        };
+        self._dispatchHoverLink = function(param) {
+            return self.__dispatchHoverLink(param);
+        };
+        
+        this._colorIndex = 0;
+        this._colorMap = {};
+        this._selectedMap = {};
+        this._hasDataMap = {};
+        
+        this.refresh(option);
+    }
+    
+    Legend.prototype = {
+        type: ecConfig.COMPONENT_TYPE_LEGEND,
+        _buildShape: function () {
+            if (!this.legendOption.show) {
+                return;
+            }
+            // 图例元素组的位置参数,通过计算所得x, y, width, height
+            this._itemGroupLocation = this._getItemGroupLocation();
+
+            this._buildBackground();
+            this._buildItem();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        /**
+         * 构建所有图例元素
+         */
+        _buildItem: function () {
+            var data = this.legendOption.data;
+            var dataLength = data.length;
+            var itemName;
+            var itemType;
+            var itemShape;
+            var textShape;
+            var textStyle  = this.legendOption.textStyle;
+            var dataTextStyle;
+            var dataFont;
+            var formattedName;
+
+            var zrWidth = this.zr.getWidth();
+            var zrHeight = this.zr.getHeight();
+            var lastX = this._itemGroupLocation.x;
+            var lastY = this._itemGroupLocation.y;
+            var itemWidth = this.legendOption.itemWidth;
+            var itemHeight = this.legendOption.itemHeight;
+            var itemGap = this.legendOption.itemGap;
+            var color;
+
+            if (this.legendOption.orient === 'vertical' && this.legendOption.x === 'right') {
+                lastX = this._itemGroupLocation.x
+                        + this._itemGroupLocation.width
+                        - itemWidth;
+            }
+
+            for (var i = 0; i < dataLength; i++) {
+                dataTextStyle = zrUtil.merge(
+                    data[i].textStyle || {},
+                    textStyle
+                );
+                dataFont = this.getFont(dataTextStyle);
+                
+                itemName = this._getName(data[i]);
+                formattedName = this._getFormatterName(itemName);
+                if (itemName === '') { // 别帮我代码优化
+                    if (this.legendOption.orient === 'horizontal') {
+                        lastX = this._itemGroupLocation.x;
+                        lastY += itemHeight + itemGap;
+                    }
+                    else {
+                        this.legendOption.x === 'right'
+                            ? lastX -= this._itemGroupLocation.maxWidth + itemGap
+                            : lastX += this._itemGroupLocation.maxWidth + itemGap;
+                        lastY = this._itemGroupLocation.y;
+                    }
+                    continue;
+                }
+                itemType = data[i].icon || this._getSomethingByName(itemName).type;
+                
+                color = this.getColor(itemName);
+
+                if (this.legendOption.orient === 'horizontal') {
+                    if (zrWidth - lastX < 200   // 最后200px做分行预判
+                        && (itemWidth + 5 + zrArea.getTextWidth(formattedName, dataFont)
+                            // 分行的最后一个不用算itemGap
+                            + (i === dataLength - 1 || data[i + 1] === '' ? 0 : itemGap)
+                           ) >= zrWidth - lastX
+                    ) {
+                        lastX = this._itemGroupLocation.x;
+                        lastY += itemHeight + itemGap;
+                    }
+                }
+                else {
+                    if (zrHeight - lastY < 200   // 最后200px做分行预判
+                        && (itemHeight
+                            // 分行的最后一个不用算itemGap
+                            + (i === dataLength - 1 || data[i + 1] === '' ? 0 : itemGap)
+                           ) 
+                           >= zrHeight - lastY
+                    ) {
+                        this.legendOption.x === 'right'
+                        ? lastX -= this._itemGroupLocation.maxWidth + itemGap
+                        : lastX += this._itemGroupLocation.maxWidth + itemGap;
+                        lastY = this._itemGroupLocation.y;
+                    }
+                }
+
+                // 图形
+                itemShape = this._getItemShapeByType(
+                    lastX, lastY,
+                    itemWidth, itemHeight,
+                    (this._selectedMap[itemName] && this._hasDataMap[itemName] ? color : '#ccc'),
+                    itemType,
+                    color
+                );
+                itemShape._name = itemName;
+                itemShape = new IconShape(itemShape);
+
+                // 文字
+                textShape = {
+                    // shape: 'text',
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    style: {
+                        x: lastX + itemWidth + 5,
+                        y: lastY + itemHeight / 2,
+                        color: this._selectedMap[itemName]
+                                ? (dataTextStyle.color === 'auto' ? color : dataTextStyle.color)
+                                : '#ccc',
+                        text: formattedName,
+                        textFont: dataFont,
+                        textBaseline: 'middle'
+                    },
+                    highlightStyle: {
+                        color: color,
+                        brushType: 'fill'
+                    },
+                    hoverable: !!this.legendOption.selectedMode,
+                    clickable: !!this.legendOption.selectedMode
+                };
+
+                if (this.legendOption.orient === 'vertical'
+                    && this.legendOption.x === 'right'
+                ) {
+                    textShape.style.x -= (itemWidth + 10);
+                    textShape.style.textAlign = 'right';
+                }
+
+                textShape._name = itemName;
+                textShape = new TextShape(textShape);
+                
+                if (this.legendOption.selectedMode) {
+                    itemShape.onclick = textShape.onclick = this._legendSelected;
+                    itemShape.onmouseover =  textShape.onmouseover = this._dispatchHoverLink;
+                    itemShape.hoverConnect = textShape.id;
+                    textShape.hoverConnect = itemShape.id;
+                }
+                this.shapeList.push(itemShape);
+                this.shapeList.push(textShape);
+
+                if (this.legendOption.orient === 'horizontal') {
+                    lastX += itemWidth + 5
+                             + zrArea.getTextWidth(formattedName, dataFont)
+                             + itemGap;
+                }
+                else {
+                    lastY += itemHeight + itemGap;
+                }
+            }
+        
+            if (this.legendOption.orient === 'horizontal'
+                && this.legendOption.x === 'center'
+                && lastY != this._itemGroupLocation.y
+            ) {
+                // 多行橫排居中优化
+                this._mLineOptimize();
+            }
+        },
+        
+        _getName: function(data) {
+            return typeof data.name != 'undefined' ? data.name : data;
+        },
+
+        _getFormatterName: function(itemName) {
+            var formatter = this.legendOption.formatter;
+            var formattedName;
+            if (typeof formatter === 'function') {
+                formattedName = formatter.call(this.myChart, itemName);
+            }
+            else if (typeof formatter === 'string') {
+                formattedName = formatter.replace('{name}', itemName);
+            }
+            else {
+                formattedName = itemName;
+            }
+            return formattedName;
+        },
+
+        _getFormatterNameFromData: function(data) {
+            var itemName = this._getName(data);
+            return this._getFormatterName(itemName);
+        },
+        
+        // 多行橫排居中优化
+        _mLineOptimize: function () {
+            var lineOffsetArray = []; // 每行宽度
+            var lastX = this._itemGroupLocation.x;
+            for (var i = 2, l = this.shapeList.length; i < l; i++) {
+                if (this.shapeList[i].style.x === lastX) {
+                    lineOffsetArray.push(
+                        (
+                            this._itemGroupLocation.width 
+                            - (
+                                this.shapeList[i - 1].style.x
+                                + zrArea.getTextWidth(
+                                      this.shapeList[i - 1].style.text,
+                                      this.shapeList[i - 1].style.textFont
+                                  )
+                                - lastX
+                            )
+                        ) / 2
+                    );
+                }
+                else if (i === l - 1) {
+                    lineOffsetArray.push(
+                        (
+                            this._itemGroupLocation.width 
+                            - (
+                                this.shapeList[i].style.x
+                                + zrArea.getTextWidth(
+                                      this.shapeList[i].style.text,
+                                      this.shapeList[i].style.textFont
+                                  )
+                                - lastX
+                            )
+                        ) / 2
+                    );
+                }
+            }
+            var curLineIndex = -1;
+            for (var i = 1, l = this.shapeList.length; i < l; i++) {
+                if (this.shapeList[i].style.x === lastX) {
+                    curLineIndex++;
+                }
+                if (lineOffsetArray[curLineIndex] === 0) {
+                    continue;
+                }
+                else {
+                    this.shapeList[i].style.x += lineOffsetArray[curLineIndex];
+                }
+            }
+        },
+
+        _buildBackground: function () {
+            var padding = this.reformCssArray(this.legendOption.padding);
+
+            this.shapeList.push(new RectangleShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                hoverable :false,
+                style: {
+                    x: this._itemGroupLocation.x - padding[3],
+                    y: this._itemGroupLocation.y - padding[0],
+                    width: this._itemGroupLocation.width + padding[3] + padding[1],
+                    height: this._itemGroupLocation.height + padding[0] + padding[2],
+                    brushType: this.legendOption.borderWidth === 0 ? 'fill' : 'both',
+                    color: this.legendOption.backgroundColor,
+                    strokeColor: this.legendOption.borderColor,
+                    lineWidth: this.legendOption.borderWidth
+                }
+            }));
+        },
+
+        /**
+         * 根据选项计算图例实体的位置坐标
+         */
+        _getItemGroupLocation: function () {
+            var data = this.legendOption.data;
+            var dataLength = data.length;
+            var itemGap = this.legendOption.itemGap;
+            var itemWidth = this.legendOption.itemWidth + 5; // 5px是图形和文字的间隔,不可配
+            var itemHeight = this.legendOption.itemHeight;
+            var textStyle  = this.legendOption.textStyle;
+            var font = this.getFont(textStyle);
+            var totalWidth = 0;
+            var totalHeight = 0;
+            var padding = this.reformCssArray(this.legendOption.padding);
+            var zrWidth = this.zr.getWidth() - padding[1] - padding[3];
+            var zrHeight = this.zr.getHeight() - padding[0] - padding[2];
+            
+            var temp = 0; // 宽高计算,用于多行判断
+            var maxWidth = 0; // 垂直布局有用
+            if (this.legendOption.orient === 'horizontal') {
+                // 水平布局,计算总宽度
+                totalHeight = itemHeight;
+                for (var i = 0; i < dataLength; i++) {
+                    if (this._getName(data[i]) === '') {
+                        temp -= itemGap;
+                        totalWidth = Math.max(totalWidth, temp);
+                        totalHeight += itemHeight + itemGap;
+                        temp = 0;
+                        continue;
+                    }
+                    var tempTextWidth = zrArea.getTextWidth(
+                        this._getFormatterNameFromData(data[i]),
+                        data[i].textStyle 
+                        ? this.getFont(zrUtil.merge(
+                            data[i].textStyle || {},
+                            textStyle
+                          ))
+                        : font
+                    );
+                    if (temp + itemWidth + tempTextWidth + itemGap > zrWidth) {
+                        // new line
+                        temp -= itemGap;  // 减去最后一个的itemGap
+                        totalWidth = Math.max(totalWidth, temp);
+                        totalHeight += itemHeight + itemGap;
+                        temp = 0;
+                    }
+                    else {
+                        temp += itemWidth + tempTextWidth + itemGap;
+                        totalWidth = Math.max(totalWidth, temp - itemGap);
+                    }
+                }
+            }
+            else {
+                // 垂直布局,计算总高度
+                for (var i = 0; i < dataLength; i++) {
+                    maxWidth = Math.max(
+                        maxWidth,
+                        zrArea.getTextWidth(
+                            this._getFormatterNameFromData(data[i]),
+                            data[i].textStyle 
+                            ? this.getFont(zrUtil.merge(
+                                  data[i].textStyle || {},
+                                  textStyle
+                              ))
+                            : font
+                        )
+                    );
+                }
+                maxWidth += itemWidth;
+                totalWidth = maxWidth;
+                for (var i = 0; i < dataLength; i++) {
+                    if (this._getName(data[i]) === '') {
+                        totalWidth += maxWidth + itemGap;
+                        temp -= itemGap;  // 减去最后一个的itemGap
+                        totalHeight = Math.max(totalHeight, temp);
+                        temp = 0;
+                        continue;
+                    }
+                    if (temp + itemHeight + itemGap > zrHeight) {
+                        // new line
+                        totalWidth += maxWidth + itemGap;
+                        temp -= itemGap;  // 减去最后一个的itemGap
+                        totalHeight = Math.max(totalHeight, temp);
+                        temp = 0;
+                    }
+                    else {
+                        temp += itemHeight + itemGap;
+                        totalHeight = Math.max(totalHeight, temp - itemGap);
+                    }
+                }
+            }
+
+            zrWidth = this.zr.getWidth();
+            zrHeight = this.zr.getHeight();
+            var x;
+            switch (this.legendOption.x) {
+                case 'center' :
+                    x = Math.floor((zrWidth - totalWidth) / 2);
+                    break;
+                case 'left' :
+                    x = padding[3] + this.legendOption.borderWidth;
+                    break;
+                case 'right' :
+                    x = zrWidth
+                        - totalWidth
+                        - padding[1]
+                        - padding[3]
+                        - this.legendOption.borderWidth * 2;
+                    break;
+                default :
+                    x = this.parsePercent(this.legendOption.x, zrWidth);
+                    break;
+            }
+            
+            var y;
+            switch (this.legendOption.y) {
+                case 'top' :
+                    y = padding[0] + this.legendOption.borderWidth;
+                    break;
+                case 'bottom' :
+                    y = zrHeight
+                        - totalHeight
+                        - padding[0]
+                        - padding[2]
+                        - this.legendOption.borderWidth * 2;
+                    break;
+                case 'center' :
+                    y = Math.floor((zrHeight - totalHeight) / 2);
+                    break;
+                default :
+                    y = this.parsePercent(this.legendOption.y, zrHeight);
+                    break;
+            }
+
+            return {
+                x: x,
+                y: y,
+                width: totalWidth,
+                height: totalHeight,
+                maxWidth: maxWidth
+            };
+        },
+
+        /**
+         * 根据名称返回series数据或data
+         */
+        _getSomethingByName: function (name) {
+            var series = this.option.series;
+            var data;
+            for (var i = 0, l = series.length; i < l; i++) {
+                if (series[i].name === name) {
+                    // 系列名称优先
+                    return {
+                        type: series[i].type,
+                        series: series[i],
+                        seriesIndex: i,
+                        data: null,
+                        dataIndex: -1
+                    };
+                }
+
+                if (
+                    series[i].type === ecConfig.CHART_TYPE_PIE 
+                    || series[i].type === ecConfig.CHART_TYPE_RADAR
+                    || series[i].type === ecConfig.CHART_TYPE_CHORD
+                    || series[i].type === ecConfig.CHART_TYPE_FORCE
+                    || series[i].type === ecConfig.CHART_TYPE_FUNNEL
+                    || series[i].type === ecConfig.CHART_TYPE_TREEMAP
+                ) {
+                    data = series[i].categories || series[i].data || series[i].nodes;
+
+                    for (var j = 0, k = data.length; j < k; j++) {
+                        if (data[j].name === name) {
+                            return {
+                                type: series[i].type,
+                                series: series[i],
+                                seriesIndex: i,
+                                data: data[j],
+                                dataIndex: j
+                            };
+                        }
+                    }
+                }
+            }
+            return {
+                type: 'bar',
+                series: null,
+                seriesIndex: -1,
+                data: null,
+                dataIndex: -1
+            };
+        },
+        
+        _getItemShapeByType: function (x, y, width, height, color, itemType, defaultColor) {
+            var highlightColor = color === '#ccc' ? defaultColor : color;
+            var itemShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    iconType: 'legendicon' + itemType,
+                    x: x,
+                    y: y,
+                    width: width,
+                    height: height,
+                    color: color,
+                    strokeColor: color,
+                    lineWidth: 2
+                },
+                highlightStyle: {
+                    color: highlightColor,
+                    strokeColor: highlightColor,
+                    lineWidth: 1
+                },
+                hoverable: this.legendOption.selectedMode,
+                clickable: this.legendOption.selectedMode
+            };
+            
+            var imageLocation;
+            if (itemType.match('image')) {
+                var imageLocation = itemType.replace(
+                    new RegExp('^image:\\/\\/'), ''
+                );
+                itemType = 'image';
+            }
+            // 特殊设置
+            switch (itemType) {
+                case 'line':
+                    itemShape.style.brushType = 'stroke';
+                    itemShape.highlightStyle.lineWidth = 3;
+                    break;
+                case 'radar':
+                case 'venn':
+                case 'tree':
+                case 'treemap':
+                case 'scatter':
+                    itemShape.highlightStyle.lineWidth = 3;
+                    break;
+                case 'k':
+                    itemShape.style.brushType = 'both';
+                    itemShape.highlightStyle.lineWidth = 3;
+                    itemShape.highlightStyle.color =
+                    itemShape.style.color = this.deepQuery(
+                        [this.ecTheme, ecConfig], 'k.itemStyle.normal.color'
+                    ) || '#fff';
+                    itemShape.style.strokeColor = color != '#ccc' 
+                        ? (
+                            this.deepQuery(
+                                [this.ecTheme, ecConfig], 'k.itemStyle.normal.lineStyle.color'
+                            ) || '#ff3200'
+                        )
+                        : color;
+                    break;
+                case 'image':
+                    itemShape.style.iconType = 'image';
+                    itemShape.style.image = imageLocation;
+                    if (color === '#ccc') {
+                        itemShape.style.opacity = 0.5;
+                    }
+                    break;
+            }
+            return itemShape;
+        },
+
+        __legendSelected: function (param) {
+            var itemName = param.target._name;
+            if (this.legendOption.selectedMode === 'single') {
+                for (var k in this._selectedMap) {
+                    this._selectedMap[k] = false;
+                }
+            }
+            this._selectedMap[itemName] = !this._selectedMap[itemName];
+            this.messageCenter.dispatch(
+                ecConfig.EVENT.LEGEND_SELECTED,
+                param.event,
+                {
+                    selected: this._selectedMap,
+                    target: itemName
+                },
+                this.myChart
+            );
+        },
+        
+        /**
+         * 产生hover link事件 
+         */
+        __dispatchHoverLink : function(param) {
+            this.messageCenter.dispatch(
+                ecConfig.EVENT.LEGEND_HOVERLINK,
+                param.event,
+                {
+                    target: param.target._name
+                },
+                this.myChart
+            );
+            return;
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption || this.option;
+                this.option.legend = this.reformOption(this.option.legend);
+                this.legendOption = this.option.legend;
+                
+                var data = this.legendOption.data || [];
+                var itemName;
+                var something;
+                var color;
+                var queryTarget;
+                if (this.legendOption.selected) {
+                    for (var k in this.legendOption.selected) {
+                        this._selectedMap[k] = typeof this._selectedMap[k] != 'undefined'
+                                               ? this._selectedMap[k]
+                                               : this.legendOption.selected[k];
+                    }
+                }
+                for (var i = 0, dataLength = data.length; i < dataLength; i++) {
+                    itemName = this._getName(data[i]);
+                    if (itemName === '') {
+                        continue;
+                    }
+                    something = this._getSomethingByName(itemName);
+                    if (!something.series) {
+                        this._hasDataMap[itemName] = false;
+                    } 
+                    else {
+                        this._hasDataMap[itemName] = true;
+                        if (something.data
+                            && (something.type === ecConfig.CHART_TYPE_PIE
+                                || something.type === ecConfig.CHART_TYPE_FORCE
+                                || something.type === ecConfig.CHART_TYPE_FUNNEL)
+                        ) {
+                            queryTarget = [something.data, something.series];
+                        }
+                        else {
+                            queryTarget = [something.series];
+                        }
+                        
+                        color = this.getItemStyleColor(
+                            this.deepQuery(queryTarget, 'itemStyle.normal.color'),
+                            something.seriesIndex,
+                            something.dataIndex,
+                            something.data
+                        );
+                        if (color && something.type != ecConfig.CHART_TYPE_K) {
+                            this.setColor(itemName, color);
+                        }
+                        this._selectedMap[itemName] = 
+                            this._selectedMap[itemName] != null
+                            ? this._selectedMap[itemName] : true; 
+                    }
+                }
+            }
+            this.clear();
+            this._buildShape();
+        },
+        
+        getRelatedAmount: function(name) {
+            var amount = 0;
+            var series = this.option.series;
+            var data;
+            for (var i = 0, l = series.length; i < l; i++) {
+                if (series[i].name === name) {
+                    // 系列名称优先
+                    amount++;
+                }
+
+                if (
+                    series[i].type === ecConfig.CHART_TYPE_PIE 
+                    || series[i].type === ecConfig.CHART_TYPE_RADAR
+                    || series[i].type === ecConfig.CHART_TYPE_CHORD
+                    || series[i].type === ecConfig.CHART_TYPE_FORCE
+                    || series[i].type === ecConfig.CHART_TYPE_FUNNEL
+                ) {
+                    data = series[i].type != ecConfig.CHART_TYPE_FORCE
+                           ? series[i].data         // 饼图、雷达图、和弦图得查找里面的数据名字
+                           : series[i].categories;  // 力导布局查找categories配置
+                    for (var j = 0, k = data.length; j < k; j++) {
+                        if (data[j].name === name && data[j].value != '-') {
+                            amount++;
+                        }
+                    }
+                }
+            }
+            return amount;
+        },
+
+        setColor: function (legendName, color) {
+            this._colorMap[legendName] = color;
+        },
+
+        getColor: function (legendName) {
+            if (!this._colorMap[legendName]) {
+                this._colorMap[legendName] = this.zr.getColor(this._colorIndex++);
+            }
+            return this._colorMap[legendName];
+        },
+        
+        hasColor: function (legendName) {
+            return this._colorMap[legendName] ? this._colorMap[legendName] : false;
+        },
+
+        add: function (name, color){
+            var data = this.legendOption.data;
+            for (var i = 0, dataLength = data.length; i < dataLength; i++) {
+                if (this._getName(data[i]) === name) {
+                    // 已有就不重复加了
+                    return;
+                }
+            }
+            this.legendOption.data.push(name);
+            this.setColor(name,color);
+            this._selectedMap[name] = true;
+            this._hasDataMap[name] = true;
+        },
+
+        del: function (name){
+            var data = this.legendOption.data;
+            for (var i = 0, dataLength = data.length; i < dataLength; i++) {
+                if (this._getName(data[i]) === name) {
+                    return this.legendOption.data.splice(i, 1);
+                }
+            }
+        },
+        
+        /**
+         * 特殊图形元素回调设置
+         * @param {Object} name
+         * @param {Object} itemShape
+         */
+        getItemShape: function (name) {
+            if (name == null) {
+                return;
+            }
+            var shape;
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                shape = this.shapeList[i];
+                if (shape._name === name && shape.type != 'text') {
+                    return shape;
+                }
+            }
+        },
+        
+        /**
+         * 特殊图形元素回调设置
+         * @param {Object} name
+         * @param {Object} itemShape
+         */
+        setItemShape: function (name, itemShape) {
+            var shape;
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                shape = this.shapeList[i];
+                if (shape._name === name && shape.type != 'text') {
+                    if (!this._selectedMap[name]) {
+                        itemShape.style.color = '#ccc';
+                        itemShape.style.strokeColor = '#ccc';
+                    }
+                    this.zr.modShape(shape.id, itemShape);
+                }
+            }
+        },
+
+        isSelected: function (itemName) {
+            if (typeof this._selectedMap[itemName] != 'undefined') {
+                return this._selectedMap[itemName];
+            }
+            else {
+                // 没在legend里定义的都为true啊~
+                return true;
+            }
+        },
+        
+        getSelectedMap: function () {
+            return this._selectedMap;
+        },
+        
+        setSelected: function(itemName, selectStatus) {
+            if (this.legendOption.selectedMode === 'single') {
+                for (var k in this._selectedMap) {
+                    this._selectedMap[k] = false;
+                }
+            }
+            this._selectedMap[itemName] = selectStatus;
+            this.messageCenter.dispatch(
+                ecConfig.EVENT.LEGEND_SELECTED,
+                null,
+                {
+                    selected: this._selectedMap,
+                    target: itemName
+                },
+                this.myChart
+            );
+        },
+        
+        /**
+         * 图例选择
+         */
+        onlegendSelected: function (param, status) {
+            var legendSelected = param.selected;
+            for (var itemName in legendSelected) {
+                if (this._selectedMap[itemName] != legendSelected[itemName]) {
+                    // 有一项不一致都需要重绘
+                    status.needRefresh = true;
+                }
+                this._selectedMap[itemName] = legendSelected[itemName];
+            }
+            return;
+        }
+    };
+    
+    var legendIcon = {
+        line: function (ctx, style) {
+            var dy = style.height / 2;
+            ctx.moveTo(style.x,     style.y + dy);
+            ctx.lineTo(style.x + style.width,style.y + dy);
+        },
+        
+        pie: function (ctx, style) {
+            var x = style.x;
+            var y = style.y;
+            var width = style.width;
+            var height = style.height;
+            SectorShape.prototype.buildPath(ctx, {
+                x: x + width / 2,
+                y: y + height + 2,
+                r: height,
+                r0: 6,
+                startAngle: 45,
+                endAngle: 135
+            });
+        },
+        
+        eventRiver: function (ctx, style) {
+            var x = style.x;
+            var y = style.y;
+            var width = style.width;
+            var height = style.height;
+            ctx.moveTo(x, y + height);
+            ctx.bezierCurveTo(
+                x + width, y + height, x, y + 4, x + width, y + 4
+            );
+            ctx.lineTo(x + width, y);
+            ctx.bezierCurveTo(
+                x, y, x + width, y + height - 4, x, y + height - 4
+            );
+            ctx.lineTo(x, y + height);
+        },
+        
+        k: function (ctx, style) {
+            var x = style.x;
+            var y = style.y;
+            var width = style.width;
+            var height = style.height;
+            CandleShape.prototype.buildPath(ctx, {
+                x: x + width / 2,
+                y: [y + 1, y + 1, y + height - 6, y + height],
+                width: width - 6
+            });
+        },
+        
+        bar: function (ctx, style) {
+            var x = style.x;
+            var y = style.y +1;
+            var width = style.width;
+            var height = style.height - 2;
+            var r = 3;
+            
+            ctx.moveTo(x + r, y);
+            ctx.lineTo(x + width - r, y);
+            ctx.quadraticCurveTo(
+                x + width, y, x + width, y + r
+            );
+            ctx.lineTo(x + width, y + height - r);
+            ctx.quadraticCurveTo(
+                x + width, y + height, x + width - r, y + height
+            );
+            ctx.lineTo(x + r, y + height);
+            ctx.quadraticCurveTo(
+                x, y + height, x, y + height - r
+            );
+            ctx.lineTo(x, y + r);
+            ctx.quadraticCurveTo(x, y, x + r, y);
+        },
+        
+        force: function (ctx, style) {
+            IconShape.prototype.iconLibrary.circle(ctx, style);
+        },
+        
+        radar: function (ctx, style) {
+            var n = 6;
+            var x = style.x + style.width / 2;
+            var y = style.y + style.height / 2;
+            var r = style.height / 2;
+
+            var dStep = 2 * Math.PI / n;
+            var deg = -Math.PI / 2;
+            var xStart = x + r * Math.cos(deg);
+            var yStart = y + r * Math.sin(deg);
+            
+            ctx.moveTo(xStart, yStart);
+            deg += dStep;
+            for (var i = 0, end = n - 1; i < end; i ++) {
+                ctx.lineTo(x + r * Math.cos(deg), y + r * Math.sin(deg));
+                deg += dStep;
+            }
+            ctx.lineTo(xStart, yStart);
+        }
+    };
+    legendIcon.chord = legendIcon.pie;
+    legendIcon.map = legendIcon.bar;
+    
+    for (var k in legendIcon) {
+        IconShape.prototype.iconLibrary['legendicon' + k] = legendIcon[k];
+    }
+    
+    zrUtil.inherits(Legend, Base);
+    
+    require('../component').define('legend', Legend);
+    
+    return Legend;
+});
+
+

+ 979 - 0
src/main/webapp/static/echarts-2.2.7/src/component/polar.js

@@ -0,0 +1,979 @@
+/**
+ * echarts组件类:极坐标
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Neil (杨骥, 511415343@qq.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    var PolygonShape = require('zrender/shape/Polygon');
+    var Circle = require('zrender/shape/Circle');
+    var Ring = require('zrender/shape/Ring');
+
+    var ecConfig = require('../config');
+    ecConfig.polar = {
+        zlevel: 0,                  // 一级层叠
+        z: 0,                       // 二级层叠
+        center: ['50%', '50%'],    // 默认全局居中
+        radius: '75%',
+        startAngle: 90,
+        boundaryGap: [0, 0],   // 数值起始和结束两端空白策略
+        splitNumber: 5,
+        name: {
+            show: true,
+            // formatter: null,
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333'
+            }
+        },
+        axisLine: {            // 坐标轴线
+            show: true,        // 默认显示,属性show控制显示与否
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#ccc',
+                width: 1,
+                type: 'solid'
+            }
+        },
+        axisLabel: {           // 坐标轴文本标签,详见axis.axisLabel
+            show: false,
+            // formatter: null,
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333'
+            }
+        },
+        splitArea: {
+            show: true,
+            areaStyle: {
+                color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
+            }
+        },
+        splitLine: {
+            show: true,
+            lineStyle: {
+                width: 1,
+                color: '#ccc'
+            }
+        },
+        type: 'polygon'
+        // indicator: []
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var ecCoordinates = require('../util/coordinates');
+
+    function Polar(ecTheme, messageCenter, zr, option, myChart) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.refresh(option);
+    }
+
+    Polar.prototype = {
+        type : ecConfig.COMPONENT_TYPE_POLAR,
+
+        /**
+         * 绘制图形
+         */
+        _buildShape : function () {
+            for (var i = 0; i < this.polar.length; i ++) {
+                this._index = i;
+                this.reformOption(this.polar[i]);
+
+                this._queryTarget = [this.polar[i], this.option];
+                this._createVector(i);
+                this._buildSpiderWeb(i);
+
+                this._buildText(i);
+
+                this._adjustIndicatorValue(i);
+                this._addAxisLabel(i);
+            }
+
+            for (var i = 0; i < this.shapeList.length; i ++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        /**
+         * 生成蜘蛛网顶点坐标
+         * @param {number} polar的index
+         */
+        _createVector : function (index) {
+            var item = this.polar[index];
+            var indicator = this.deepQuery(this._queryTarget, 'indicator');
+            var length = indicator.length;
+            var startAngle = item.startAngle ;
+            var dStep = 2 * Math.PI / length;
+            var radius = this._getRadius();
+            var __ecIndicator = item.__ecIndicator = [];
+            var vector;
+
+            for (var i = 0 ;i < length ; i ++) {
+                vector = ecCoordinates.polar2cartesian(
+                    radius, startAngle * Math.PI / 180 + dStep * i
+                );
+                __ecIndicator.push({
+                    // 将图形翻转
+                    vector : [vector[1], -vector[0]]
+                });
+            }
+        },
+
+        /**
+         * 获取外圈的半径
+         *
+         * @return {number}
+         */
+        _getRadius : function () {
+            var item = this.polar[this._index];
+            return this.parsePercent(
+                item.radius,
+                Math.min(this.zr.getWidth(), this.zr.getHeight()) / 2
+            );
+        },
+
+        /**
+         * 构建蜘蛛网
+         * @param {number} polar的index
+         */
+        _buildSpiderWeb : function (index) {
+            var item = this.polar[index];
+            var __ecIndicator = item.__ecIndicator;
+            var splitArea = item.splitArea;
+            var splitLine = item.splitLine;
+
+            var center = this.getCenter(index);
+            var splitNumber = item.splitNumber;
+
+            var strokeColor = splitLine.lineStyle.color;
+            var lineWidth = splitLine.lineStyle.width;
+            var show = splitLine.show;
+
+            var axisLine = this.deepQuery(this._queryTarget, 'axisLine');
+
+            this._addArea(
+                __ecIndicator, splitNumber, center,
+                splitArea, strokeColor, lineWidth, show
+            );
+
+            axisLine.show && this._addLine(
+                __ecIndicator, center, axisLine
+            );
+        },
+
+        /**
+         * 绘制axisLabel
+         */
+        _addAxisLabel : function (index) {
+            var accMath = require('../util/accMath');
+            var item = this.polar[index];
+            var indicator = this.deepQuery(this._queryTarget, 'indicator');
+            var __ecIndicator = item.__ecIndicator;
+            var axisLabel;
+            var vector;
+            var style;
+            var newStyle;
+            var splitNumber = this.deepQuery(this._queryTarget, 'splitNumber');
+            var center = this.getCenter(index);
+            var vector;
+            var value;
+            var text;
+            var theta;
+            // var startAngle = this.deepQuery(this._queryTarget, 'startAngle');
+            var offset;
+            var interval;
+
+            for (var i = 0; i < indicator.length; i ++) {
+                axisLabel = this.deepQuery(
+                    [indicator[i], item, this.option], 'axisLabel'
+                );
+
+                if (axisLabel.show) {
+                    var textStyle = this.deepQuery([axisLabel, item, this.option], 'textStyle');
+                    var formatter = this.deepQuery([axisLabel, item], 'formatter');
+                    style = {};
+                    style.textFont = this.getFont(textStyle);
+                    style.color = textStyle.color;
+
+                    style = zrUtil.merge(style, axisLabel);
+                    style.lineWidth = style.width;
+
+                    vector = __ecIndicator[i].vector;
+                    value = __ecIndicator[i].value;
+                    theta = i / indicator.length * 2 * Math.PI;
+                    offset = axisLabel.offset || 10;
+                    interval = axisLabel.interval || 0;
+
+                    if (!value) {
+                        return;
+                    }
+
+                    for (var j = 1 ; j <= splitNumber; j += interval + 1) {
+                        newStyle = zrUtil.merge({}, style);
+                        text = accMath.accAdd(value.min, accMath.accMul(value.step, j));
+
+                        if (typeof formatter === 'function') {
+                            text = formatter(text);
+                        }
+                        else if (typeof formatter === 'string') {
+                            text = formatter.replace('{a}','{a0}')
+                                         .replace('{a0}', text);
+                        }
+                        else {
+                            text = this.numAddCommas(text);
+                        }
+
+                        newStyle.text = text;
+                        newStyle.x = j * vector[0] / splitNumber
+                                     + Math.cos(theta) * offset + center[0];
+                        newStyle.y = j * vector[1] / splitNumber
+                                     + Math.sin(theta) * offset + center[1];
+
+                        this.shapeList.push(new TextShape({
+                            zlevel: this.getZlevelBase(),
+                            z: this.getZBase(),
+                            style : newStyle,
+                            draggable : false,
+                            hoverable : false
+                        }));
+                    }
+                }
+            }
+        },
+
+        /**
+         * 绘制坐标头的文字
+         * @param {number} polar的index
+         */
+        _buildText  : function (index) {
+            var item = this.polar[index];
+            var __ecIndicator = item.__ecIndicator;
+            var vector;
+            var indicator = this.deepQuery(this._queryTarget, 'indicator');
+            var center = this.getCenter(index);
+            var style;
+            var textAlign;
+            var name;
+            var rotation;
+            var x = 0;
+            var y = 0;
+            var margin;
+            var textStyle;
+
+            for (var i = 0; i < indicator.length; i ++) {
+                name = this.deepQuery(
+                    [indicator[i], item, this.option], 'name'
+                );
+
+                if (!name.show) {
+                    continue;
+                }
+                textStyle = this.deepQuery(
+                    [name, item, this.option],
+                    'textStyle'
+                );
+
+                style = {};
+
+                style.textFont = this.getFont(textStyle);
+                style.color = textStyle.color;
+
+                if (typeof name.formatter == 'function') {
+                    style.text = name.formatter.call(this.myChart, indicator[i].text, i);
+                }
+                else if (typeof name.formatter == 'string'){
+                    style.text = name.formatter.replace(
+                        '{value}', indicator[i].text
+                    );
+                }
+                else {
+                    style.text = indicator[i].text;
+                }
+                __ecIndicator[i].text = style.text;
+
+                vector = __ecIndicator[i].vector;
+
+                if (Math.round(vector[0]) > 0) {
+                    textAlign = 'left';
+                }
+                else if (Math.round(vector[0]) < 0) {
+                    textAlign = 'right';
+                }
+                else {
+                    textAlign = 'center';
+                }
+
+                if (name.margin == null) {
+                    vector = this._mapVector(vector, center, 1.1);
+                }
+                else {
+                    margin = name.margin;
+                    x = vector[0] > 0 ? margin : - margin;
+                    y = vector[1] > 0 ? margin : - margin;
+
+                    x = vector[0] === 0 ? 0 : x;
+                    y = vector[1] === 0 ? 0 : y;
+                    vector = this._mapVector(vector, center, 1);
+                }
+
+
+                style.textAlign = textAlign;
+                style.x = vector[0] + x;
+                style.y = vector[1] + y;
+
+                if (name.rotate) {
+                    rotation = [
+                        name.rotate / 180 * Math.PI,
+                        vector[0], vector[1]
+                    ];
+                }
+                else {
+                    rotation = [0, 0, 0];
+                }
+
+                this.shapeList.push(new TextShape({
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    style : style,
+                    draggable : false,
+                    hoverable : false,
+                    rotation : rotation
+                }));
+            }
+        },
+
+        getIndicatorText : function(polarIndex, indicatorIndex) {
+            return this.polar[polarIndex]
+                   && this.polar[polarIndex].__ecIndicator[indicatorIndex]
+                   && this.polar[polarIndex].__ecIndicator[indicatorIndex].text;
+        },
+
+        /**
+         * 添加一个隐形的盒子 当做drop的容器 暴露给外部的图形类使用
+         * @param {number} polar的index
+         * @return {Object} 添加的盒子图形
+         */
+        getDropBox : function (index) {
+            var index = index || 0;
+            var item = this.polar[index];
+            var center = this.getCenter(index);
+            var __ecIndicator = item.__ecIndicator;
+            var len = __ecIndicator.length;
+            var pointList = [];
+            var vector;
+            var shape;
+            var type = item.type;
+
+            if (type == 'polygon') {
+                for (var i = 0; i < len; i ++) {
+                    vector = __ecIndicator[i].vector;
+                    pointList.push(this._mapVector(vector, center, 1.2));
+                }
+                shape = this._getShape(
+                    pointList, 'fill', 'rgba(0,0,0,0)', '', 1
+                );
+            } else if (type == 'circle') {
+                shape = this._getCircle(
+                    '', 1, 1.2, center, 'fill', 'rgba(0,0,0,0)'
+                );
+            }
+
+            return shape;
+        },
+
+        /**
+         * 绘制蜘蛛网的正n变形
+         *
+         * @param {Array<Object>} 指标数组
+         * @param {number} 分割线数量
+         * @param {Array<number>} 中点坐标
+         * @param {Object} 分割区域对象
+         * @param {string} 线条颜色
+         * @param {number} 线条宽度
+         */
+        _addArea : function (
+            __ecIndicator, splitNumber, center,
+            splitArea, strokeColor, lineWidth, show
+        ) {
+            var shape;
+            var scale;
+            var scale1;
+            var pointList;
+            var type = this.deepQuery(this._queryTarget, 'type');
+
+            for (var i = 0; i < splitNumber ; i ++ ) {
+                scale = (splitNumber - i) / splitNumber;
+
+                if (show) {
+                    if (type == 'polygon') {
+                        pointList = this._getPointList(
+                            __ecIndicator, scale, center);
+                        shape = this._getShape(
+                            pointList, 'stroke', '', strokeColor, lineWidth
+                        );
+                    } else if (type == 'circle') {
+                        shape = this._getCircle(
+                            strokeColor, lineWidth, scale, center, 'stroke'
+                        );
+                    }
+
+                    this.shapeList.push(shape);
+                }
+
+                if (splitArea.show) {
+                    scale1 = (splitNumber - i - 1) / splitNumber;
+                    this._addSplitArea(
+                        __ecIndicator, splitArea, scale, scale1, center, i
+                    );
+                }
+            }
+        },
+
+        /**
+         * 绘制圆
+         *
+         * @param {string} strokeColor
+         * @param {number} lineWidth
+         * @param {number} scale
+         * @param {Array.<number>} center
+         * @param {string} brushType
+         * @param {string} color
+         * @return {Circle}
+         */
+        _getCircle : function (
+            strokeColor, lineWidth, scale, center, brushType, color
+        ) {
+            var radius = this._getRadius();
+            return new Circle({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    x: center[0],
+                    y: center[1],
+                    r: radius * scale,
+                    brushType: brushType,
+                    strokeColor: strokeColor,
+                    lineWidth: lineWidth,
+                    color: color
+                },
+                hoverable : false,
+                draggable : false
+            });
+        },
+
+        /**
+         * 绘制圆环
+         *
+         * @param {string} color  间隔颜色
+         * @param {number} scale0  小圆的scale
+         * @param {number} scale1  大圆的scale
+         * @param {Array.<number>} center  圆点
+         * @return {Ring}
+         */
+        _getRing : function (color, scale0, scale1, center) {
+            var radius = this._getRadius();
+            return new Ring({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style : {
+                    x : center[0],
+                    y : center[1],
+                    r : scale0 * radius,
+                    r0 : scale1 * radius,
+                    color : color,
+                    brushType : 'fill'
+                },
+                hoverable : false,
+                draggable : false
+            });
+        },
+
+        /**
+         * 获取需要绘制的多边形的点集
+         * @param {Object} serie的指标参数
+         * @param {number} 缩小的系数
+         * @param {Array<number>} 中点坐标
+         *
+         * @return {Array<Array<number>>} 返回绘制的点集
+         */
+        _getPointList : function (__ecIndicator, scale, center) {
+            var pointList = [];
+            var len = __ecIndicator.length;
+            var vector;
+
+            for (var i = 0 ; i < len ; i ++ ) {
+                vector = __ecIndicator[i].vector;
+
+                pointList.push(this._mapVector(vector, center, scale));
+            }
+            return pointList;
+        },
+
+        /**
+         * 获取绘制的图形
+         * @param {Array<Array<number>>} 绘制的点集
+         * @param {string} 绘制方式 stroke | fill | both 描边 | 填充 | 描边 + 填充
+         * @param {string} 颜色
+         * @param {string} 描边颜色
+         * @param {number} 线条宽度
+         * @return {Object} 绘制的图形对象
+         */
+        _getShape : function (
+            pointList, brushType, color, strokeColor, lineWidth
+        ) {
+            return new PolygonShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style : {
+                    pointList   : pointList,
+                    brushType   : brushType,
+                    color       : color,
+                    strokeColor : strokeColor,
+                    lineWidth   : lineWidth
+                },
+                hoverable : false,
+                draggable : false
+            });
+        },
+
+        /**
+         * 绘制填充区域
+         */
+        _addSplitArea : function (
+            __ecIndicator, splitArea, scale, scale1, center, colorInd
+        ) {
+            var indLen = __ecIndicator.length;
+            var color;
+            var colorArr = splitArea.areaStyle.color;
+            var colorLen;
+
+            var vector;
+            var vector1;
+            var pointList = [];
+            var indLen = __ecIndicator.length;
+            var shape;
+
+            var type = this.deepQuery(this._queryTarget, 'type');
+
+            if (typeof colorArr == 'string') {
+                colorArr = [colorArr];
+            }
+            colorLen = colorArr.length;
+            color = colorArr[ colorInd % colorLen];
+
+            if (type == 'polygon') {
+                for (var i = 0; i < indLen ; i ++) {
+                    pointList = [];
+                    vector = __ecIndicator[i].vector;
+                    vector1 = __ecIndicator[(i + 1) % indLen].vector;
+
+                    pointList.push(this._mapVector(vector, center, scale));
+                    pointList.push(this._mapVector(vector, center, scale1));
+                    pointList.push(this._mapVector(vector1, center, scale1));
+                    pointList.push(this._mapVector(vector1, center, scale));
+
+                    shape = this._getShape(
+                        pointList, 'fill', color, '', 1
+                    );
+                    this.shapeList.push(shape);
+                }
+            } else if (type == 'circle') {
+                shape = this._getRing(color, scale, scale1, center);
+                this.shapeList.push(shape);
+            }
+        },
+
+        /**
+         * 转换坐标
+         *
+         * @param {Array<number>} 原始坐标
+         * @param {Array<number>} 中点坐标
+         * @param {number} 缩小的倍数
+         *
+         * @return {Array<number>} 转换后的坐标
+         */
+        _mapVector : function (vector, center, scale) {
+            return [
+                vector[0] * scale + center[0],
+                vector[1] * scale + center[1]
+            ];
+        },
+
+        /**
+         * 获取中心点位置 暴露给外部图形类使用
+         * @param {number} polar的index
+         */
+        getCenter : function (index) {
+            var index = index || 0;
+            return this.parseCenter(this.zr, this.polar[index].center);
+        },
+
+        /**
+         * 绘制从中点出发的线
+         *
+         * @param {Array<Object>} 指标对象
+         * @param {Array<number>} 中点坐标
+         * @param {string} 线条颜色
+         * @param {number} 线条宽度
+         * @param {string} 线条绘制类型
+         *              solid | dotted | dashed 实线 | 点线 | 虚线
+         */
+        _addLine : function (
+            __ecIndicator, center, axisLine
+        ) {
+            var indLen = __ecIndicator.length;
+            var line;
+            var vector;
+            var lineStyle = axisLine.lineStyle;
+            var strokeColor = lineStyle.color;
+            var lineWidth = lineStyle.width;
+            var lineType = lineStyle.type;
+
+            for (var i = 0; i < indLen ; i ++ ) {
+                vector = __ecIndicator[i].vector;
+                line = this._getLine(
+                    center[0], center[1],
+                    vector[0] + center[0],
+                    vector[1] + center[1],
+                    strokeColor, lineWidth, lineType
+                );
+                this.shapeList.push(line);
+            }
+        },
+
+        /**
+         * 获取线条对象
+         * @param {number} 出发点横坐标
+         * @param {number} 出发点纵坐标
+         * @param {number} 终点横坐标
+         * @param {number} 终点纵坐标
+         * @param {string} 线条颜色
+         * @param {number} 线条宽度
+         * @param {string} 线条类型
+         *
+         * @return {Object} 线条对象
+         */
+        _getLine : function (
+            xStart, yStart, xEnd, yEnd, strokeColor, lineWidth, lineType
+        ) {
+            return new LineShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style : {
+                    xStart : xStart,
+                    yStart : yStart,
+                    xEnd   : xEnd,
+                    yEnd   : yEnd,
+                    strokeColor : strokeColor,
+                    lineWidth   : lineWidth,
+                    lineType    : lineType
+                },
+                hoverable : false
+            });
+        },
+
+        /**
+         * 调整指标的值,当indicator中存在max时设置为固定值
+         * @param {number} polar的index
+         */
+        _adjustIndicatorValue : function (index) {
+            var item = this.polar[index];
+            var indicator = this.deepQuery(this._queryTarget, 'indicator');
+            var len = indicator.length;
+            var __ecIndicator = item.__ecIndicator;
+            var max;
+            var min;
+            var data = this._getSeriesData(index);
+
+            var boundaryGap = item.boundaryGap;
+            var splitNumber = item.splitNumber;
+            var scale = item.scale;
+            var opts;
+
+            var smartSteps = require('../util/smartSteps');
+            for (var i = 0; i < len ; i ++ ) {
+                if (typeof indicator[i].max == 'number') {
+                    max = indicator[i].max;
+                    min = indicator[i].min || 0;
+                    opts = {
+                        max: max,
+                        min: min
+                    };
+                }
+                else {
+                    var value = this._findValue(
+                        data, i, splitNumber, boundaryGap
+                    );
+                    min = value.min;
+                    max = value.max;
+                }
+                 // 非scale下双正,修正最小值为0
+                if (!scale && min >= 0 && max >= 0) {
+                    min = 0;
+                }
+                // 非scale下双负,修正最大值为0
+                if (!scale && min <= 0 && max <= 0) {
+                    max = 0;
+                }
+                var stepOpt = smartSteps(min, max, splitNumber, opts);
+
+                __ecIndicator[i].value = {
+                    min: stepOpt.min,
+                    max: stepOpt.max,
+                    step: stepOpt.step
+                };
+            }
+        },
+
+        /**
+         * 将series中的数据拿出来,如果没有polarIndex属性,默认为零
+         * @param {number} polar 的index
+         * @param {Array<Object>} 需要处理的数据
+         */
+        _getSeriesData : function (index) {
+            var data = [];
+            var serie;
+            var serieData;
+            var legend = this.component.legend;
+            var polarIndex;
+
+            for (var i = 0; i < this.series.length; i ++) {
+                serie = this.series[i];
+                if (serie.type != ecConfig.CHART_TYPE_RADAR) {
+                    continue;
+                }
+                serieData = serie.data || [];
+                for (var j = 0; j < serieData.length; j ++) {
+                    polarIndex = this.deepQuery(
+                        [serieData[j], serie, this.option], 'polarIndex'
+                    ) || 0;
+                    if (polarIndex == index
+                        && (!legend || legend.isSelected(serieData[j].name))
+                    ) {
+                        data.push(serieData[j]);
+                    }
+                }
+            }
+            return data;
+        },
+
+        /**
+         * 查找指标合适的值
+         *
+         * 如果只有一组数据以数据中的最大值作为最大值 0为最小值
+         * 如果是多组,使用同一维度的进行比较 选出最大值最小值
+         * 对它们进行处理
+         * @param {Object} serie 的 data
+         * @param {number} index 指标的序号
+         * @param {number} splitNumber 分段格式
+         * * @param {boolean} boundaryGap 两端留白
+         */
+        _findValue : function (data, index, splitNumber, boundaryGap) {
+            var max;
+            var min;
+            var one;
+
+            if (!data || data.length === 0) {
+                return;
+            }
+
+            function _compare(item) {
+                (item > max || max === undefined) && (max = item);
+                (item < min || min === undefined) && (min = item);
+            }
+
+            if (data.length == 1) {
+                min = 0;
+            }
+            if (data.length != 1) {
+                for (var i = 0; i < data.length; i ++) {
+                    _compare(this.getDataFromOption(data[i].value[index]));
+                }
+            }
+            else {
+                one = data[0];
+                for (var i = 0; i < one.value.length; i ++) {
+                    _compare(this.getDataFromOption(one.value[i]));
+                }
+            }
+
+            var gap = Math.abs(max - min);
+            min = min - Math.abs(gap * boundaryGap[0]);
+            max = max + Math.abs(gap * boundaryGap[1]);
+            if (min === max) {
+                if (max === 0) {
+                    // 修复全0数据
+                    max = 1;
+                }
+                // 修复最大值==最小值时数据整形
+                else if (max > 0) {
+                    min = max / splitNumber;
+                }
+                else { // max < 0
+                    max = max / splitNumber;
+                }
+            }
+
+            return {
+                max : max,
+                min : min
+            };
+        },
+
+        /**
+         * 获取每个指标上某个value对应的坐标
+         * @param {number} polarIndex
+         * @param {number} indicatorIndex
+         * @param {number} value
+         * @return {Array<number>} 对应坐标
+         */
+        getVector : function (polarIndex, indicatorIndex, value) {
+            polarIndex = polarIndex || 0;
+            indicatorIndex = indicatorIndex || 0;
+            var __ecIndicator = this.polar[polarIndex].__ecIndicator;
+
+            if (indicatorIndex >= __ecIndicator.length) {
+                return ;
+            }
+
+            var indicator = this.polar[polarIndex].__ecIndicator[indicatorIndex];
+            var center = this.getCenter(polarIndex);
+            var vector = indicator.vector;
+            var max = indicator.value.max;
+            var min = indicator.value.min;
+            var alpha;
+
+            if (typeof value == 'undefined') {
+                return center;
+            }
+
+            switch (value) {
+                case 'min' :
+                    value = min;
+                    break;
+                case 'max' :
+                    value = max;
+                    break;
+                case 'center' :
+                    value = (max + min) / 2;
+                    break;
+            }
+
+            if (max != min) {
+                alpha = (value - min) / (max - min);
+            }
+            else {
+                alpha = 0.5;
+            }
+
+            return this._mapVector(vector, center, alpha);
+        },
+
+        /**
+         * 判断一个点是否在网内
+         * @param {Array<number>} 坐标
+         * @return {number} 返回polarindex  返回-1表示不在任何polar
+         */
+        isInside : function (vector) {
+            var polar = this.getNearestIndex(vector);
+
+            if (polar) {
+                return polar.polarIndex;
+            }
+            return -1;
+        },
+
+        /**
+         * 如果一个点在网内,返回离它最近的数据轴的index
+         * @param {Array<number>} 坐标
+         * @return {Object} | false
+         *      polarIndex
+         *      valueIndex
+         */
+        getNearestIndex : function (vector) {
+            var item;
+            var center;
+            var radius;
+            var polarVector;
+            var startAngle;
+            var indicator;
+            var len;
+            var angle;
+            var finalAngle;
+            for (var i = 0 ; i < this.polar.length; i ++) {
+                item = this.polar[i];
+                center = this.getCenter(i);
+                if (vector[0] == center[0] && vector[1] == center[1]) {
+                    return {
+                        polarIndex : i,
+                        valueIndex : 0
+                    };
+                }
+                radius = this._getRadius();
+                startAngle = item.startAngle;
+                indicator = item.indicator;
+                len = indicator.length;
+                angle = 2 * Math.PI / len;
+                // 注意y轴的翻转
+                polarVector = ecCoordinates.cartesian2polar(
+                    vector[0] - center[0], center[1] - vector[1]
+                );
+                if (vector[0] - center[0] < 0) {
+                    polarVector[1] += Math.PI;
+                }
+                if (polarVector[1] < 0) {
+                    polarVector[1] += 2 * Math.PI;
+                }
+
+
+                // 减去startAngle的偏移量 再加2PI变成正数
+                finalAngle = polarVector[1] -
+                    startAngle / 180 * Math.PI + Math.PI * 2;
+
+                if (Math.abs(Math.cos(finalAngle % (angle / 2))) * radius
+                    > polarVector[0])
+                {
+                    return {
+                        polarIndex : i,
+                        valueIndex : Math.floor(
+                            (finalAngle + angle / 2 ) / angle
+                            ) % len
+                    };
+                }
+            }
+        },
+
+        /**
+         * 获取指标信息
+         * @param {number} polarIndex
+         * @return {Array<Object>} indicator
+         */
+        getIndicator : function (index) {
+            var index = index || 0;
+            return this.polar[index].indicator;
+        },
+
+         /**
+         * 刷新
+         */
+        refresh : function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+                this.polar = this.option.polar;
+                this.series = this.option.series;
+            }
+            this.clear();
+            this._buildShape();
+        }
+    };
+
+    zrUtil.inherits(Polar, Base);
+
+    require('../component').define('polar', Polar);
+
+    return Polar;
+});

+ 358 - 0
src/main/webapp/static/echarts-2.2.7/src/component/roamController.js

@@ -0,0 +1,358 @@
+/**
+ * echarts组件:漫游控制器
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var RectangleShape = require('zrender/shape/Rectangle');
+    var SectorShape = require('zrender/shape/Sector');
+    var CircleShape = require('zrender/shape/Circle');
+    
+    var ecConfig = require('../config');
+    ecConfig.roamController = {
+        zlevel: 0,                  // 一级层叠
+        z: 4,                       // 二级层叠
+        show: true,
+        x: 'left',                 // 水平安放位置,默认为全图左对齐,可选为:
+                                   // 'center' ¦ 'left' ¦ 'right'
+                                   // ¦ {number}(x坐标,单位px)
+        y: 'top',                  // 垂直安放位置,默认为全图顶端,可选为:
+                                   // 'top' ¦ 'bottom' ¦ 'center'
+                                   // ¦ {number}(y坐标,单位px)
+        width: 80,
+        height: 120,
+        backgroundColor: 'rgba(0,0,0,0)',
+        borderColor: '#ccc',       // 图例边框颜色
+        borderWidth: 0,            // 图例边框线宽,单位px,默认为0(无边框)
+        padding: 5,                // 图例内边距,单位px,默认各方向内边距为5,
+                                   // 接受数组分别设定上右下左边距,同css
+        handleColor: '#6495ed',
+        fillerColor: '#fff',
+        step: 15,                  // 移动幅度
+        mapTypeControl: null
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var zrColor = require('zrender/tool/color');
+    var zrEvent = require('zrender/tool/event');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表参数
+     */
+    function RoamController(ecTheme, messageCenter, zr, option, myChart) {
+        this.rcOption = {};
+
+        if (!option.roamController || !option.roamController.show) {
+            return;
+        }
+        if (!option.roamController.mapTypeControl) {
+            console.error('option.roamController.mapTypeControl has not been defined.');
+            return;
+        }
+        
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+        
+        this.rcOption = option.roamController;
+        
+        var self = this;
+        this._drictionMouseDown = function(params) {
+            return self.__drictionMouseDown(params);
+        };
+        this._drictionMouseUp = function(params) {
+            return self.__drictionMouseUp(params);
+        };
+        this._drictionMouseMove = function(params) {
+            return self.__drictionMouseMove(params);
+        };
+        this._drictionMouseOut = function(params) {
+            return self.__drictionMouseOut(params);
+        };
+        this._scaleHandler = function(params) {
+            return self.__scaleHandler(params);
+        };
+        this.refresh(option);
+    }
+    
+    RoamController.prototype = {
+        type: ecConfig.COMPONENT_TYPE_ROAMCONTROLLER,
+        _buildShape: function () {
+            if (!this.rcOption.show) {
+                return;
+            }
+            // 元素组的位置参数,通过计算所得x, y, width, height
+            this._itemGroupLocation = this._getItemGroupLocation();
+
+            this._buildBackground();
+            this._buildItem();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        /**
+         * 构建所有漫游控制器元素
+         */
+        _buildItem: function () {
+            this.shapeList.push(this._getDirectionShape('up'));
+            this.shapeList.push(this._getDirectionShape('down'));
+            this.shapeList.push(this._getDirectionShape('left'));
+            this.shapeList.push(this._getDirectionShape('right'));
+            this.shapeList.push(this._getScaleShape('scaleUp'));
+            this.shapeList.push(this._getScaleShape('scaleDown'));
+        },
+        
+        _getDirectionShape: function(direction) {
+            var r = this._itemGroupLocation.r;
+            var x = this._itemGroupLocation.x + r;
+            var y = this._itemGroupLocation.y + r;
+            
+            var sectorShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    x: x,          // 圆心横坐标
+                    y: y,          // 圆心纵坐标
+                    r: r,          // 圆环外半径
+                    startAngle: -45,
+                    endAngle: 45,
+                    color: this.rcOption.handleColor,
+                    text: '>',
+                    textX: x + r / 2 + 4,
+                    textY: y - 0.5,
+                    textAlign: 'center',
+                    textBaseline: 'middle',
+                    textPosition: 'specific',
+                    textColor: this.rcOption.fillerColor,
+                    textFont: Math.floor(r / 2) + 'px arial'
+                },
+                highlightStyle: {
+                    color: zrColor.lift(this.rcOption.handleColor, -0.2),
+                    brushType: 'fill'
+                },
+                clickable: true
+            };
+            switch (direction) {
+                case 'up':
+                    sectorShape.rotation = [Math.PI / 2, x, y];
+                    break;
+                case 'left':
+                    sectorShape.rotation = [Math.PI, x, y];
+                    break;
+                case 'down':
+                    sectorShape.rotation = [-Math.PI / 2, x, y];
+                    break;
+            }
+
+            sectorShape = new SectorShape(sectorShape);
+            sectorShape._roamType = direction;
+            sectorShape.onmousedown = this._drictionMouseDown;
+            sectorShape.onmouseup = this._drictionMouseUp;
+            sectorShape.onmousemove = this._drictionMouseMove;
+            sectorShape.onmouseout = this._drictionMouseOut;
+            
+            return sectorShape;
+        },
+        
+        _getScaleShape: function(text) {
+            var width = this._itemGroupLocation.width;
+            var height = this._itemGroupLocation.height - width;
+            height = height < 0 ? 20 : height;  // 确保height不为负
+            
+            var r = Math.min(width / 2 - 5, height) / 2;
+            var x = this._itemGroupLocation.x 
+                    + (text === 'scaleDown' ? (width - r) : r);
+            var y = this._itemGroupLocation.y + this._itemGroupLocation.height - r;
+
+            var scaleShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    x: x,
+                    y: y,
+                    r: r,
+                    color: this.rcOption.handleColor,
+                    text: text === 'scaleDown' ? '-' : '+',
+                    textX: x,
+                    textY: y - 2,
+                    textAlign: 'center',
+                    textBaseline: 'middle',
+                    textPosition: 'specific',
+                    textColor: this.rcOption.fillerColor,
+                    textFont: Math.floor(r) + 'px verdana'
+                },
+                highlightStyle: {
+                    color: zrColor.lift(this.rcOption.handleColor, -0.2),
+                    brushType: 'fill'
+                },
+                clickable: true
+            };
+            
+            scaleShape = new CircleShape(scaleShape);
+            scaleShape._roamType = text;
+            scaleShape.onmousedown = this._scaleHandler;
+            
+            return scaleShape;
+        },
+        
+        _buildBackground: function () {
+            var padding = this.reformCssArray(this.rcOption.padding);
+
+            this.shapeList.push(new RectangleShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                hoverable :false,
+                style: {
+                    x: this._itemGroupLocation.x - padding[3],
+                    y: this._itemGroupLocation.y - padding[0],
+                    width: this._itemGroupLocation.width + padding[3] + padding[1],
+                    height: this._itemGroupLocation.height + padding[0] + padding[2],
+                    brushType: this.rcOption.borderWidth === 0 ? 'fill' : 'both',
+                    color: this.rcOption.backgroundColor,
+                    strokeColor: this.rcOption.borderColor,
+                    lineWidth: this.rcOption.borderWidth
+                }
+            }));
+        },
+
+        /**
+         * 根据选项计算漫游控制器实体的位置坐标
+         */
+        _getItemGroupLocation: function () {
+            var padding = this.reformCssArray(this.rcOption.padding);
+            var width = this.rcOption.width;
+            var height = this.rcOption.height;
+            
+            var zrWidth = this.zr.getWidth();
+            var zrHeight = this.zr.getHeight();
+            var x;
+            switch (this.rcOption.x) {
+                case 'center' :
+                    x = Math.floor((zrWidth - width) / 2);
+                    break;
+                case 'left' :
+                    x = padding[3] + this.rcOption.borderWidth;
+                    break;
+                case 'right' :
+                    x = zrWidth
+                        - width
+                        - padding[1]
+                        - padding[3]
+                        - this.rcOption.borderWidth * 2;
+                    break;
+                default :
+                    x = this.parsePercent(this.rcOption.x, zrWidth);
+                    break;
+            }
+            
+            var y;
+            switch (this.rcOption.y) {
+                case 'top' :
+                    y = padding[0] + this.rcOption.borderWidth;
+                    break;
+                case 'bottom' :
+                    y = zrHeight
+                        - height
+                        - padding[0]
+                        - padding[2]
+                        - this.rcOption.borderWidth * 2;
+                    break;
+                case 'center' :
+                    y = Math.floor((zrHeight - height) / 2);
+                    break;
+                default :
+                    y = this.parsePercent(this.rcOption.y, zrHeight);
+                    break;
+            }
+
+            return {
+                x: x,
+                y: y,
+                r: width / 2,
+                width: width,
+                height: height
+            };
+        },
+
+        __drictionMouseDown: function(params) {
+            this.mousedown = true;
+            this._drictionHandlerOn(params);
+        },
+        
+        __drictionMouseUp: function(params) {
+            this.mousedown = false;
+            this._drictionHandlerOff(params);
+        },
+        
+        __drictionMouseMove: function(params) {
+            if (this.mousedown) {
+                this._drictionHandlerOn(params);
+            }
+        },
+        
+        __drictionMouseOut: function(params) {
+            this._drictionHandlerOff(params);
+        },
+        
+        _drictionHandlerOn: function(params) {
+            this._dispatchEvent(params.event, params.target._roamType);
+            clearInterval(this.dircetionTimer);
+            var self = this;
+            this.dircetionTimer = setInterval(function() {
+                self._dispatchEvent(params.event, params.target._roamType);
+            }, 100);
+            zrEvent.stop(params.event);
+        },
+        
+        _drictionHandlerOff: function(params) {
+            clearInterval(this.dircetionTimer);
+        },
+        
+        __scaleHandler: function(params) {
+            this._dispatchEvent(params.event, params.target._roamType);
+            zrEvent.stop(params.event);
+        },
+        
+        _dispatchEvent: function(event, roamType){
+            this.messageCenter.dispatch(
+                ecConfig.EVENT.ROAMCONTROLLER,
+                event, 
+                {
+                    roamType: roamType,
+                    mapTypeControl: this.rcOption.mapTypeControl,
+                    step: this.rcOption.step
+                },
+                this.myChart
+            );
+        },
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption || this.option;
+                this.option.roamController = this.reformOption(this.option.roamController);
+                this.rcOption = this.option.roamController;
+            }
+            this.clear();
+            this._buildShape();
+        }
+    };
+    
+    
+    zrUtil.inherits(RoamController, Base);
+    
+    require('../component').define('roamController', RoamController);
+    
+    return RoamController;
+});
+
+

+ 937 - 0
src/main/webapp/static/echarts-2.2.7/src/component/timeline.js

@@ -0,0 +1,937 @@
+/**
+ * echarts组件:时间轴组件
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var RectangleShape = require('zrender/shape/Rectangle');
+    var IconShape = require('../util/shape/Icon');
+    var ChainShape = require('../util/shape/Chain');
+    
+    var ecConfig = require('../config');
+    ecConfig.timeline = {
+        zlevel: 0,                  // 一级层叠
+        z: 4,                       // 二级层叠
+        show: true,
+        type: 'time',  // 模式是时间类型,支持 number
+        notMerge: false,
+        realtime: true,
+        x: 80,
+        // y: {number},
+        x2: 80,
+        y2: 0,
+        // width: {totalWidth} - x - x2,
+        height: 50,
+        backgroundColor: 'rgba(0,0,0,0)',   // 时间轴背景颜色
+        borderColor: '#ccc',               // 时间轴边框颜色
+        borderWidth: 0,                    // 时间轴边框线宽,单位px,默认为0(无边框)
+        padding: 5,                        // 时间轴内边距,单位px,默认各方向内边距为5,
+        controlPosition: 'left',           // 'right' | 'none'
+        autoPlay: false,
+        loop: true,
+        playInterval: 2000,                // 播放时间间隔,单位ms
+        lineStyle: {
+            width: 1,
+            color: '#666',
+            type: 'dashed'
+        },
+        label: {                            // 文本标签
+            show: true,
+            interval: 'auto',
+            rotate: 0,
+            // formatter: null,
+            textStyle: {                    // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333'
+            }
+        },
+        checkpointStyle: {
+            symbol: 'auto',
+            symbolSize: 'auto',
+            color: 'auto',
+            borderColor: 'auto',
+            borderWidth: 'auto',
+            label: {                            // 文本标签
+                show: false,
+                textStyle: {                    // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                    color: 'auto'
+                }
+            }
+        },
+        controlStyle: {
+            itemSize: 15,
+            itemGap: 5,
+            normal: { color: '#333'},
+            emphasis: { color: '#1e90ff'}
+        },
+        symbol: 'emptyDiamond',
+        symbolSize: 4,
+        currentIndex: 0
+        // data: []
+    };
+
+    var zrUtil = require('zrender/tool/util');
+    var zrArea = require('zrender/tool/area');
+    var zrEvent = require('zrender/tool/event');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表参数
+     */
+    function Timeline(ecTheme, messageCenter, zr, option, myChart) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        var self = this;
+        self._onclick = function(param) {
+            return self.__onclick(param);
+        };
+        self._ondrift = function (dx, dy) {
+            return self.__ondrift(this, dx, dy);
+        };
+        self._ondragend = function () {
+            return self.__ondragend();
+        };
+        self._setCurrentOption = function() {
+            var timelineOption = self.timelineOption;
+            self.currentIndex %= timelineOption.data.length;
+            // console.log(self.currentIndex);
+            var curOption = self.options[self.currentIndex] || {};
+            self.myChart._setOption(curOption, timelineOption.notMerge, true);
+            
+            self.messageCenter.dispatch(
+                ecConfig.EVENT.TIMELINE_CHANGED,
+                null,
+                {
+                    currentIndex: self.currentIndex,
+                    data: timelineOption.data[self.currentIndex].name != null
+                          ? timelineOption.data[self.currentIndex].name
+                          : timelineOption.data[self.currentIndex]
+                },
+                self.myChart
+            );
+        };
+        self._onFrame = function() {
+            self._setCurrentOption();
+            self._syncHandleShape();
+            
+            if (self.timelineOption.autoPlay) {
+                self.playTicket = setTimeout(
+                    function() {
+                        self.currentIndex += 1;
+                        if (!self.timelineOption.loop
+                            && self.currentIndex >= self.timelineOption.data.length
+                        ) {
+                            self.currentIndex = self.timelineOption.data.length - 1;
+                            self.stop();
+                            return;
+                        }
+                        self._onFrame();
+                    },
+                    self.timelineOption.playInterval
+                );
+            }
+        };
+
+        this.setTheme(false);
+        this.options = this.option.options;
+        this.currentIndex = this.timelineOption.currentIndex % this.timelineOption.data.length;
+        
+        if (!this.timelineOption.notMerge && this.currentIndex !== 0) {
+            /*
+            for (var i = 1, l = this.timelineOption.data.length; i < l; i++) {
+                this.options[i] = zrUtil.merge(
+                    this.options[i], this.options[i - 1]
+                );
+            }
+            */
+           this.options[this.currentIndex] = zrUtil.merge(
+               this.options[this.currentIndex], this.options[0]
+           );
+        }
+        
+        if (this.timelineOption.show) {
+            this._buildShape();
+            this._syncHandleShape();
+        }
+        
+        this._setCurrentOption();
+        
+        if (this.timelineOption.autoPlay) {
+            var self = this;
+            this.playTicket = setTimeout(
+                function() {
+                    self.play();
+                },
+                this.ecTheme.animationDuration != null
+                ? this.ecTheme.animationDuration
+                : ecConfig.animationDuration
+            );
+        }
+    }
+    
+    Timeline.prototype = {
+        type: ecConfig.COMPONENT_TYPE_TIMELINE,
+        _buildShape: function () {
+            // 位置参数,通过计算所得x, y, width, height
+            this._location = this._getLocation();
+            this._buildBackground();
+            this._buildControl();
+            this._chainPoint = this._getChainPoint();
+            if (this.timelineOption.label.show) {
+                // 标签显示的挑选间隔
+                var interval = this._getInterval();
+                for (var i = 0, len = this._chainPoint.length; i < len; i += interval) {
+                    this._chainPoint[i].showLabel = true;
+                }
+            }
+            this._buildChain();
+            this._buildHandle();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        /**
+         * 根据选项计算实体的位置坐标
+         */
+        _getLocation: function () {
+            var timelineOption = this.timelineOption;
+            var padding = this.reformCssArray(this.timelineOption.padding);
+            
+            // 水平布局
+            var zrWidth = this.zr.getWidth();
+            var x = this.parsePercent(timelineOption.x, zrWidth);
+            var x2 = this.parsePercent(timelineOption.x2, zrWidth);
+            var width;
+            if (timelineOption.width == null) {
+                width = zrWidth - x - x2;
+                x2 = zrWidth - x2;
+            }
+            else {
+                width = this.parsePercent(timelineOption.width, zrWidth);
+                x2 = x + width;
+            }
+
+            var zrHeight = this.zr.getHeight();
+            var height = this.parsePercent(timelineOption.height, zrHeight);
+            var y;
+            var y2;
+            if (timelineOption.y != null) {
+                y = this.parsePercent(timelineOption.y, zrHeight);
+                y2 = y + height;
+            }
+            else {
+                y2 = zrHeight - this.parsePercent(timelineOption.y2, zrHeight);
+                y = y2 - height;
+            }
+
+            return {
+                x: x + padding[3],
+                y: y + padding[0],
+                x2: x2 - padding[1],
+                y2: y2 - padding[2],
+                width: width - padding[1] - padding[3],
+                height: height - padding[0] - padding[2]
+            };
+        },
+
+        _getReformedLabel: function (idx) {
+            var timelineOption = this.timelineOption;
+            var data = timelineOption.data[idx].name != null
+                       ? timelineOption.data[idx].name
+                       : timelineOption.data[idx];
+            var formatter = timelineOption.data[idx].formatter 
+                            || timelineOption.label.formatter;
+            if (formatter) {
+                if (typeof formatter === 'function') {
+                    data = formatter.call(this.myChart, data);
+                }
+                else if (typeof formatter === 'string') {
+                    data = formatter.replace('{value}', data);
+                }
+            }
+            return data;
+        },
+        
+        /**
+         * 计算标签显示挑选间隔
+         */
+        _getInterval: function () {
+            var chainPoint = this._chainPoint;
+            var timelineOption = this.timelineOption;
+            var interval   = timelineOption.label.interval;
+            if (interval === 'auto') {
+                // 麻烦的自适应计算
+                var fontSize = timelineOption.label.textStyle.fontSize;
+                var data = timelineOption.data;
+                var dataLength = timelineOption.data.length;
+
+                // 横向
+                if (dataLength > 3) {
+                    var isEnough = false;
+                    var labelSpace;
+                    var labelSize;
+                    interval = 0;
+                    while (!isEnough && interval < dataLength) {
+                        interval++;
+                        isEnough = true;
+                        for (var i = interval; i < dataLength; i += interval) {
+                            labelSpace = chainPoint[i].x - chainPoint[i - interval].x;
+                            if (timelineOption.label.rotate !== 0) {
+                                // 有旋转
+                                labelSize = fontSize;
+                            }
+                            else if (data[i].textStyle) {
+                                labelSize = zrArea.getTextWidth(
+                                    chainPoint[i].name,
+                                    chainPoint[i].textFont
+                                );
+                            }
+                            else {
+                                // 不定义data级特殊文本样式,用fontSize优化getTextWidth
+                                var label = chainPoint[i].name + '';
+                                var wLen = (label.match(/\w/g) || '').length;
+                                var oLen = label.length - wLen;
+                                labelSize = wLen * fontSize * 2 / 3 + oLen * fontSize;
+                            }
+
+                            if (labelSpace < labelSize) {
+                                // 放不下,中断循环让interval++
+                                isEnough = false;
+                                break;
+                            }
+                        }
+                    }
+                }
+                else {
+                    // 少于3个则全部显示
+                    interval = 1;
+                }
+            }
+            else {
+                // 用户自定义间隔
+                interval = interval - 0 + 1;
+            }
+
+            return interval;
+        },
+        
+        /**
+         * 根据选项计算时间链条上的坐标及symbolList
+         */
+        _getChainPoint: function() {
+            var timelineOption = this.timelineOption;
+            var symbol = timelineOption.symbol.toLowerCase();
+            var symbolSize = timelineOption.symbolSize;
+            var rotate = timelineOption.label.rotate;
+            var textStyle = timelineOption.label.textStyle;
+            var textFont = this.getFont(textStyle);
+            var dataTextStyle;
+            var data = timelineOption.data;
+            var x = this._location.x;
+            var y = this._location.y + this._location.height / 4 * 3;
+            var width = this._location.x2 - this._location.x;
+            var len = data.length;
+            
+            function _getName(i) {
+                return (data[i].name != null ? data[i].name : data[i] + '');
+            }
+            var xList = [];
+            if (len > 1) {
+                var boundaryGap = width / len;
+                boundaryGap = boundaryGap > 50 ? 50 : (boundaryGap < 20 ? 5 : boundaryGap);
+                width -= boundaryGap * 2;
+                if (timelineOption.type === 'number') {
+                    // 平均分布
+                    for (var i = 0; i < len; i++) {
+                        xList.push(x + boundaryGap + width / (len - 1) * i);
+                    }
+                }
+                else {
+                    // 时间比例
+                    xList[0] = new Date(_getName(0).replace(/-/g, '/'));
+                    xList[len - 1] = new Date(_getName(len - 1).replace(/-/g, '/')) - xList[0];
+                    for (var i = 1; i < len; i++) {
+                        xList[i] =  x + boundaryGap 
+                                    + width 
+                                      * (new Date(_getName(i).replace(/-/g, '/')) - xList[0]) 
+                                      / xList[len - 1];
+                    }
+                    xList[0] = x + boundaryGap;
+                }
+            }
+            else {
+                xList.push(x + width / 2);
+            }
+            
+            var list = [];
+            var curSymbol;
+            var n;
+            var isEmpty;
+            var textAlign;
+            var rotation;
+            for (var i = 0; i < len; i++) {
+                x = xList[i];
+                curSymbol = (data[i].symbol && data[i].symbol.toLowerCase()) || symbol;
+                if (curSymbol.match('empty')) {
+                    curSymbol = curSymbol.replace('empty', '');
+                    isEmpty = true;
+                }
+                else {
+                    isEmpty = false;
+                }
+                if (curSymbol.match('star')) {
+                    n = (curSymbol.replace('star','') - 0) || 5;
+                    curSymbol = 'star';
+                }
+                
+                dataTextStyle = data[i].textStyle 
+                                ? zrUtil.merge(data[i].textStyle || {}, textStyle)
+                                : textStyle;
+                
+                textAlign = dataTextStyle.align || 'center';
+                
+                if (rotate) {
+                    textAlign = rotate > 0 ? 'right' : 'left';
+                    rotation = [rotate * Math.PI / 180, x, y - 5];
+                }
+                else {
+                    rotation = false;
+                }
+                
+                list.push({
+                    x: x,
+                    n: n,
+                    isEmpty: isEmpty,
+                    symbol: curSymbol,
+                    symbolSize: data[i].symbolSize || symbolSize,
+                    color: data[i].color,
+                    borderColor: data[i].borderColor,
+                    borderWidth: data[i].borderWidth,
+                    name: this._getReformedLabel(i),
+                    textColor: dataTextStyle.color,
+                    textAlign: textAlign,
+                    textBaseline: dataTextStyle.baseline || 'middle',
+                    textX: x,
+                    textY: y - (rotate ? 5 : 0),
+                    textFont: data[i].textStyle ? this.getFont(dataTextStyle) : textFont,
+                    rotation: rotation,
+                    showLabel: false
+                });
+            }
+            
+            return list;
+        },
+        
+        _buildBackground: function () {
+            var timelineOption = this.timelineOption;
+            var padding = this.reformCssArray(this.timelineOption.padding);
+            var width = this._location.width;
+            var height = this._location.height;
+            
+            if (timelineOption.borderWidth !== 0 
+                || timelineOption.backgroundColor.replace(/\s/g,'') != 'rgba(0,0,0,0)'
+            ) {
+                // 背景
+                this.shapeList.push(new RectangleShape({
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    hoverable :false,
+                    style: {
+                        x: this._location.x - padding[3],
+                        y: this._location.y - padding[0],
+                        width: width + padding[1] + padding[3],
+                        height: height + padding[0] + padding[2],
+                        brushType: timelineOption.borderWidth === 0 ? 'fill' : 'both',
+                        color: timelineOption.backgroundColor,
+                        strokeColor: timelineOption.borderColor,
+                        lineWidth: timelineOption.borderWidth
+                    }
+                }));
+            }
+        },
+
+        _buildControl: function() {
+            var self = this;
+            var timelineOption = this.timelineOption;
+            var lineStyle = timelineOption.lineStyle;
+            var controlStyle = timelineOption.controlStyle;
+            if (timelineOption.controlPosition === 'none') {
+                return;
+            }
+            var iconSize = controlStyle.itemSize;
+            var iconGap = controlStyle.itemGap;
+            var x;
+            if (timelineOption.controlPosition === 'left') {
+                x = this._location.x;
+                this._location.x += (iconSize + iconGap) * 3;
+            }
+            else {
+                x = this._location.x2 - ((iconSize + iconGap) * 3 - iconGap);
+                this._location.x2 -= (iconSize + iconGap) * 3;
+            }
+            
+            var y = this._location.y;
+            var iconStyle = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase() + 1,
+                style: {
+                    iconType: 'timelineControl',
+                    symbol: 'last',
+                    x: x,
+                    y: y,
+                    width: iconSize,
+                    height: iconSize,
+                    brushType: 'stroke',
+                    color: controlStyle.normal.color,
+                    strokeColor: controlStyle.normal.color,
+                    lineWidth: lineStyle.width
+                },
+                highlightStyle: {
+                    color: controlStyle.emphasis.color,
+                    strokeColor: controlStyle.emphasis.color,
+                    lineWidth: lineStyle.width + 1
+                },
+                clickable: true
+            };
+            
+            this._ctrLastShape = new IconShape(iconStyle);
+            this._ctrLastShape.onclick = function() {
+                self.last();
+            };
+            this.shapeList.push(this._ctrLastShape);
+            
+            x += iconSize + iconGap;
+            this._ctrPlayShape = new IconShape(zrUtil.clone(iconStyle));
+            this._ctrPlayShape.style.brushType = 'fill';
+            this._ctrPlayShape.style.symbol = 'play';
+            this._ctrPlayShape.style.status = this.timelineOption.autoPlay ? 'playing' : 'stop';
+            this._ctrPlayShape.style.x = x;
+            this._ctrPlayShape.onclick = function() {
+                if (self._ctrPlayShape.style.status === 'stop') {
+                    self.play();
+                }
+                else {
+                    self.stop();
+                }
+            };
+            this.shapeList.push(this._ctrPlayShape);
+            
+            x += iconSize + iconGap;
+            this._ctrNextShape = new IconShape(zrUtil.clone(iconStyle));
+            this._ctrNextShape.style.symbol = 'next';
+            this._ctrNextShape.style.x = x;
+            this._ctrNextShape.onclick = function() {
+                self.next();
+            };
+            this.shapeList.push(this._ctrNextShape);
+        },
+        
+        /**
+         * 构建时间轴
+         */
+        _buildChain: function () {
+            var timelineOption = this.timelineOption;
+            var lineStyle = timelineOption.lineStyle;
+            this._timelineShae = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    x: this._location.x,
+                    y: this.subPixelOptimize(this._location.y, lineStyle.width),
+                    width: this._location.x2 - this._location.x,
+                    height: this._location.height,
+                    chainPoint: this._chainPoint,
+                    brushType:'both',
+                    strokeColor: lineStyle.color,
+                    lineWidth: lineStyle.width,
+                    lineType: lineStyle.type
+                },
+                hoverable: false,
+                clickable: true,
+                onclick: this._onclick
+            };
+
+            this._timelineShae = new ChainShape(this._timelineShae);
+            this.shapeList.push(this._timelineShae);
+        },
+
+        /**
+         * 构建拖拽手柄
+         */
+        _buildHandle: function () {
+            var curPoint = this._chainPoint[this.currentIndex];
+            var symbolSize = curPoint.symbolSize + 1;
+            symbolSize = symbolSize < 5 ? 5 : symbolSize;
+            
+            this._handleShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase() + 1,
+                hoverable: false,
+                draggable: true,
+                style: {
+                    iconType: 'diamond',
+                    n: curPoint.n,
+                    x: curPoint.x - symbolSize,
+                    y: this._location.y + this._location.height / 4 - symbolSize,
+                    width: symbolSize * 2,
+                    height: symbolSize * 2,
+                    brushType:'both',
+                    textPosition: 'specific',
+                    textX: curPoint.x,
+                    textY: this._location.y - this._location.height / 4,
+                    textAlign: 'center',
+                    textBaseline: 'middle'
+                },
+                highlightStyle: {},
+                ondrift: this._ondrift,
+                ondragend: this._ondragend
+            };
+            
+            this._handleShape = new IconShape(this._handleShape);
+            this.shapeList.push(this._handleShape);
+        },
+        
+        /**
+         * 同步拖拽图形样式 
+         */
+        _syncHandleShape: function() {
+            if (!this.timelineOption.show) {
+                return;
+            }
+            
+            var timelineOption = this.timelineOption;
+            var cpStyle = timelineOption.checkpointStyle;
+            var curPoint = this._chainPoint[this.currentIndex];
+
+            this._handleShape.style.text = cpStyle.label.show ? curPoint.name : '';
+            this._handleShape.style.textFont = curPoint.textFont;
+            
+            this._handleShape.style.n = curPoint.n;
+            if (cpStyle.symbol === 'auto') {
+                this._handleShape.style.iconType = curPoint.symbol != 'none' 
+                                                   ? curPoint.symbol : 'diamond';
+            }
+            else {
+                this._handleShape.style.iconType = cpStyle.symbol;
+                if (cpStyle.symbol.match('star')) {
+                    this._handleShape.style.n = (cpStyle.symbol.replace('star','') - 0) || 5;
+                    this._handleShape.style.iconType = 'star';
+                }
+            }
+            
+            var symbolSize;
+            if (cpStyle.symbolSize === 'auto') {
+                symbolSize = curPoint.symbolSize + 2;
+                symbolSize = symbolSize < 5 ? 5 : symbolSize;
+            }
+            else {
+                symbolSize = cpStyle.symbolSize - 0;
+            }
+            
+            this._handleShape.style.color = cpStyle.color === 'auto'
+                                            ? (curPoint.color 
+                                               ? curPoint.color 
+                                               : timelineOption.controlStyle.emphasis.color
+                                              )
+                                            : cpStyle.color;
+            this._handleShape.style.textColor = cpStyle.label.textStyle.color === 'auto'
+                                                ? this._handleShape.style.color
+                                                : cpStyle.label.textStyle.color;
+            this._handleShape.highlightStyle.strokeColor = 
+            this._handleShape.style.strokeColor = cpStyle.borderColor === 'auto'
+                                ? (curPoint.borderColor ? curPoint.borderColor : '#fff')
+                                : cpStyle.borderColor;
+            this._handleShape.style.lineWidth = cpStyle.borderWidth === 'auto'
+                                ? (curPoint.borderWidth ? curPoint.borderWidth : 0)
+                                : (cpStyle.borderWidth - 0);
+            this._handleShape.highlightStyle.lineWidth = this._handleShape.style.lineWidth + 1;
+            
+            this.zr.animate(this._handleShape.id, 'style')
+                .when(
+                    500,
+                    {
+                        x: curPoint.x - symbolSize,
+                        textX: curPoint.x,
+                        y: this._location.y + this._location.height / 4 - symbolSize,
+                        width: symbolSize * 2,
+                        height: symbolSize * 2
+                    }
+                )
+                .start('ExponentialOut');
+        },
+
+        _findChainIndex: function(x) {
+            var chainPoint = this._chainPoint;
+            var len = chainPoint.length;
+            if (x <= chainPoint[0].x) {
+                return 0;
+            }
+            else if (x >= chainPoint[len - 1].x) {
+                return len - 1;
+            }
+            for (var i = 0; i < len - 1; i++) {
+                if (x >= chainPoint[i].x && x <= chainPoint[i + 1].x) {
+                    // catch you!
+                    return (Math.abs(x - chainPoint[i].x) < Math.abs(x - chainPoint[i + 1].x))
+                           ? i : (i + 1);
+                }
+            }
+        },
+        
+        __onclick: function(param) {
+            var x = zrEvent.getX(param.event);
+            var newIndex =  this._findChainIndex(x);
+            if (newIndex === this.currentIndex) {
+                return true; // 啥事都没发生
+            }
+            
+            this.currentIndex = newIndex;
+            this.timelineOption.autoPlay && this.stop(); // 停止自动播放
+            clearTimeout(this.playTicket);
+            this._onFrame();
+        },
+        
+        /**
+         * 拖拽范围控制
+         */
+        __ondrift: function (shape, dx) {
+            this.timelineOption.autoPlay && this.stop(); // 停止自动播放
+            
+            var chainPoint = this._chainPoint;
+            var len = chainPoint.length;
+            var newIndex;
+            if (shape.style.x + dx <= chainPoint[0].x - chainPoint[0].symbolSize) {
+                shape.style.x = chainPoint[0].x - chainPoint[0].symbolSize;
+                newIndex = 0;
+            }
+            else if (shape.style.x + dx >= chainPoint[len - 1].x - chainPoint[len - 1].symbolSize) {
+                shape.style.x = chainPoint[len - 1].x - chainPoint[len - 1].symbolSize;
+                newIndex = len - 1;
+            }
+            else {
+                shape.style.x += dx;
+                newIndex = this._findChainIndex(shape.style.x);
+            }
+            var curPoint = chainPoint[newIndex];
+            var symbolSize = curPoint.symbolSize + 2;
+            shape.style.iconType = curPoint.symbol;
+            shape.style.n = curPoint.n;
+            shape.style.textX = shape.style.x + symbolSize / 2;
+            shape.style.y = this._location.y + this._location.height / 4 - symbolSize;
+            shape.style.width = symbolSize * 2;
+            shape.style.height = symbolSize * 2;
+            shape.style.text = curPoint.name;
+            
+            //console.log(newIndex)
+            if (newIndex === this.currentIndex) {
+                return true; // 啥事都没发生
+            }
+            
+            this.currentIndex = newIndex;
+            if (this.timelineOption.realtime) {
+                clearTimeout(this.playTicket);
+                var self = this;
+                this.playTicket = setTimeout(function() {
+                    self._setCurrentOption();
+                },200);
+            }
+
+            return true;
+        },
+        
+        __ondragend: function () {
+            this.isDragend = true;
+        },
+        
+        /**
+         * 数据项被拖拽出去
+         */
+        ondragend: function (param, status) {
+            if (!this.isDragend || !param.target) {
+                // 没有在当前实例上发生拖拽行为则直接返回
+                return;
+            }
+            !this.timelineOption.realtime && this._setCurrentOption();
+            
+            // 别status = {}赋值啊!!
+            status.dragOut = true;
+            status.dragIn = true;
+            status.needRefresh = false; // 会有消息触发fresh,不用再刷一遍
+            // 处理完拖拽事件后复位
+            this.isDragend = false;
+            this._syncHandleShape();
+            return;
+        },
+        
+        last: function () {
+            this.timelineOption.autoPlay && this.stop(); // 停止自动播放
+            
+            this.currentIndex -= 1;
+            if (this.currentIndex < 0) {
+                this.currentIndex = this.timelineOption.data.length - 1;
+            }
+            this._onFrame();
+            
+            return this.currentIndex;
+        },
+        
+        next: function () {
+            this.timelineOption.autoPlay && this.stop(); // 停止自动播放
+            
+            this.currentIndex += 1;
+            if (this.currentIndex >= this.timelineOption.data.length) {
+                this.currentIndex = 0;
+            }
+            this._onFrame();
+            
+            return this.currentIndex;
+        },
+        
+        play: function (targetIndex, autoPlay) {
+            if (this._ctrPlayShape && this._ctrPlayShape.style.status != 'playing') {
+                this._ctrPlayShape.style.status = 'playing';
+                this.zr.modShape(this._ctrPlayShape.id);
+                this.zr.refreshNextFrame();
+            }
+            
+            
+            this.timelineOption.autoPlay = autoPlay != null ? autoPlay : true;
+            
+            if (!this.timelineOption.autoPlay) {
+                clearTimeout(this.playTicket);
+            }
+            
+            this.currentIndex = targetIndex != null ? targetIndex : (this.currentIndex + 1);
+            if (this.currentIndex >= this.timelineOption.data.length) {
+                this.currentIndex = 0;
+            }
+            this._onFrame();
+            
+            return this.currentIndex;
+        },
+        
+        stop: function () {
+            if (this._ctrPlayShape && this._ctrPlayShape.style.status != 'stop') {
+                this._ctrPlayShape.style.status = 'stop';
+                this.zr.modShape(this._ctrPlayShape.id);
+                this.zr.refreshNextFrame();
+            }
+            
+            this.timelineOption.autoPlay = false;
+            
+            clearTimeout(this.playTicket);
+            
+            return this.currentIndex;
+        },
+        
+        /**
+         * 避免dataZoom带来两次refresh,不设refresh接口,resize重复一下buildshape逻辑 
+         */
+        resize: function () {
+            if (this.timelineOption.show) {
+                this.clear();
+                this._buildShape();
+                this._syncHandleShape();
+            }
+        },
+        
+        setTheme: function(needRefresh) {
+            this.timelineOption = this.reformOption(zrUtil.clone(this.option.timeline));
+            // 通用字体设置
+            this.timelineOption.label.textStyle = this.getTextStyle(
+                this.timelineOption.label.textStyle
+            );
+            this.timelineOption.checkpointStyle.label.textStyle = this.getTextStyle(
+                this.timelineOption.checkpointStyle.label.textStyle
+            );
+            if (!this.myChart.canvasSupported) {
+                // 不支持Canvas的强制关闭实时动画
+                this.timelineOption.realtime = false;
+            }
+            
+            if (this.timelineOption.show && needRefresh) {
+                this.clear();
+                this._buildShape();
+                this._syncHandleShape();
+            }
+        },
+        
+        /**
+         * 释放后实例不可用,重载基类方法
+         */
+        onbeforDispose: function () {
+            clearTimeout(this.playTicket);
+        }
+    };
+    
+    function timelineControl(ctx, style) {
+        var lineWidth = 2;//style.lineWidth;
+        var x = style.x + lineWidth;
+        var y = style.y + lineWidth + 2;
+        var width = style.width - lineWidth;
+        var height = style.height - lineWidth;
+        
+        
+        var symbol = style.symbol;
+        if (symbol === 'last') {
+            ctx.moveTo(x + width - 2, y + height / 3);
+            ctx.lineTo(x + width - 2, y);
+            ctx.lineTo(x + 2, y + height / 2);
+            ctx.lineTo(x + width - 2, y + height);
+            ctx.lineTo(x + width - 2, y + height / 3 * 2);
+            ctx.moveTo(x, y);
+            ctx.lineTo(x, y);
+        } 
+        else if (symbol === 'next') {
+            ctx.moveTo(x + 2, y + height / 3);
+            ctx.lineTo(x + 2, y);
+            ctx.lineTo(x + width - 2, y + height / 2);
+            ctx.lineTo(x + 2, y + height);
+            ctx.lineTo(x + 2, y + height / 3 * 2);
+            ctx.moveTo(x, y);
+            ctx.lineTo(x, y);
+        }
+        else if (symbol === 'play') {
+            if (style.status === 'stop') {
+                ctx.moveTo(x + 2, y);
+                ctx.lineTo(x + width - 2, y + height / 2);
+                ctx.lineTo(x + 2, y + height);
+                ctx.lineTo(x + 2, y);
+            }
+            else {
+                var delta = style.brushType === 'both' ? 2 : 3;
+                ctx.rect(x + 2, y, delta, height);
+                ctx.rect(x + width - delta - 2, y, delta, height);
+            }
+        }
+        else if (symbol.match('image')) {
+            var imageLocation = '';
+            imageLocation = symbol.replace(
+                    new RegExp('^image:\\/\\/'), ''
+                );
+            symbol = IconShape.prototype.iconLibrary.image;
+            symbol(ctx, {
+                x: x,
+                y: y,
+                width: width,
+                height: height,
+                image: imageLocation
+            });
+        }
+    }
+    IconShape.prototype.iconLibrary['timelineControl'] = timelineControl;
+    
+    zrUtil.inherits(Timeline, Base);
+    
+    require('../component').define('timeline', Timeline);
+    
+    return Timeline;
+});

+ 311 - 0
src/main/webapp/static/echarts-2.2.7/src/component/title.js

@@ -0,0 +1,311 @@
+/**
+ * echarts组件:图表标题
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+    
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var RectangleShape = require('zrender/shape/Rectangle');
+    
+    var ecConfig = require('../config');
+    // 图表标题
+    ecConfig.title = {
+        zlevel: 0,                  // 一级层叠
+        z: 6,                       // 二级层叠
+        show: true,
+        text: '',
+        // link: null,             // 超链接跳转
+        // target: null,           // 仅支持self | blank
+        subtext: '',
+        // sublink: null,          // 超链接跳转
+        // subtarget: null,        // 仅支持self | blank
+        x: 'left',                 // 水平安放位置,默认为左对齐,可选为:
+                                   // 'center' ¦ 'left' ¦ 'right'
+                                   // ¦ {number}(x坐标,单位px)
+        y: 'top',                  // 垂直安放位置,默认为全图顶端,可选为:
+                                   // 'top' ¦ 'bottom' ¦ 'center'
+                                   // ¦ {number}(y坐标,单位px)
+        //textAlign: null          // 水平对齐方式,默认根据x设置自动调整
+        backgroundColor: 'rgba(0,0,0,0)',
+        borderColor: '#ccc',       // 标题边框颜色
+        borderWidth: 0,            // 标题边框线宽,单位px,默认为0(无边框)
+        padding: 5,                // 标题内边距,单位px,默认各方向内边距为5,
+                                   // 接受数组分别设定上右下左边距,同css
+        itemGap: 5,                // 主副标题纵向间隔,单位px,默认为10,
+        textStyle: {
+            fontSize: 18,
+            fontWeight: 'bolder',
+            color: '#333'          // 主标题文字颜色
+        },
+        subtextStyle: {
+            color: '#aaa'          // 副标题文字颜色
+        }
+    };
+    
+    var zrUtil = require('zrender/tool/util');
+    var zrArea = require('zrender/tool/area');
+    var zrColor = require('zrender/tool/color');
+    
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 图表参数
+     */
+    function Title(ecTheme, messageCenter, zr, option, myChart) {
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+        
+        this.refresh(option);
+    }
+    
+    Title.prototype = {
+        type: ecConfig.COMPONENT_TYPE_TITLE,
+        _buildShape: function () {
+            if (!this.titleOption.show) {
+                return;
+            }
+            // 标题元素组的位置参数,通过计算所得x, y, width, height
+            this._itemGroupLocation = this._getItemGroupLocation();
+
+            this._buildBackground();
+            this._buildItem();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        /**
+         * 构建所有标题元素
+         */
+        _buildItem: function () {
+            var text = this.titleOption.text;
+            var link = this.titleOption.link;
+            var target = this.titleOption.target;
+            var subtext = this.titleOption.subtext;
+            var sublink = this.titleOption.sublink;
+            var subtarget = this.titleOption.subtarget;
+            var font = this.getFont(this.titleOption.textStyle);
+            var subfont = this.getFont(this.titleOption.subtextStyle);
+            
+            var x = this._itemGroupLocation.x;
+            var y = this._itemGroupLocation.y;
+            var width = this._itemGroupLocation.width;
+            var height = this._itemGroupLocation.height;
+            
+            var textShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    y: y,
+                    color: this.titleOption.textStyle.color,
+                    text: text,
+                    textFont: font,
+                    textBaseline: 'top'
+                },
+                highlightStyle: {
+                    color: zrColor.lift(this.titleOption.textStyle.color, 1),
+                    brushType: 'fill'
+                },
+                hoverable: false
+            };
+            if (link) {
+                textShape.hoverable = true;
+                textShape.clickable = true;
+                textShape.onclick = function (){
+                    if (!target || target != 'self') {
+                        window.open(link);
+                    }
+                    else {
+                        window.location = link;
+                    }
+                };
+            }
+            
+            var subtextShape = {
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                style: {
+                    y: y + height,
+                    color: this.titleOption.subtextStyle.color,
+                    text: subtext,
+                    textFont: subfont,
+                    textBaseline: 'bottom'
+                },
+                highlightStyle: {
+                    color: zrColor.lift(this.titleOption.subtextStyle.color, 1),
+                    brushType: 'fill'
+                },
+                hoverable: false
+            };
+            if (sublink) {
+                subtextShape.hoverable = true;
+                subtextShape.clickable = true;
+                subtextShape.onclick = function (){
+                    if (!subtarget || subtarget != 'self') {
+                        window.open(sublink);
+                    }
+                    else {
+                        window.location = sublink;
+                    }
+                };
+            }
+
+            switch (this.titleOption.x) {
+                case 'center' :
+                    textShape.style.x = subtextShape.style.x = x + width / 2;
+                    textShape.style.textAlign = subtextShape.style.textAlign 
+                                              = 'center';
+                    break;
+                case 'left' :
+                    textShape.style.x = subtextShape.style.x = x;
+                    textShape.style.textAlign = subtextShape.style.textAlign 
+                                              = 'left';
+                    break;
+                case 'right' :
+                    textShape.style.x = subtextShape.style.x = x + width;
+                    textShape.style.textAlign = subtextShape.style.textAlign 
+                                              = 'right';
+                    break;
+                default :
+                    x = this.titleOption.x - 0;
+                    x = isNaN(x) ? 0 : x;
+                    textShape.style.x = subtextShape.style.x = x;
+                    break;
+            }
+            
+            if (this.titleOption.textAlign) {
+                textShape.style.textAlign = subtextShape.style.textAlign 
+                                          = this.titleOption.textAlign;
+            }
+
+            this.shapeList.push(new TextShape(textShape));
+            subtext !== '' && this.shapeList.push(new TextShape(subtextShape));
+        },
+
+        _buildBackground: function () {
+            var padding = this.reformCssArray(this.titleOption.padding);
+
+            this.shapeList.push(new RectangleShape({
+                zlevel: this.getZlevelBase(),
+                z: this.getZBase(),
+                hoverable :false,
+                style: {
+                    x: this._itemGroupLocation.x - padding[3],
+                    y: this._itemGroupLocation.y - padding[0],
+                    width: this._itemGroupLocation.width + padding[3] + padding[1],
+                    height: this._itemGroupLocation.height + padding[0] + padding[2],
+                    brushType: this.titleOption.borderWidth === 0 ? 'fill' : 'both',
+                    color: this.titleOption.backgroundColor,
+                    strokeColor: this.titleOption.borderColor,
+                    lineWidth: this.titleOption.borderWidth
+                }
+            }));
+        },
+
+        /**
+         * 根据选项计算标题实体的位置坐标
+         */
+        _getItemGroupLocation: function () {
+            var padding = this.reformCssArray(this.titleOption.padding);
+            var text = this.titleOption.text;
+            var subtext = this.titleOption.subtext;
+            var font = this.getFont(this.titleOption.textStyle);
+            var subfont = this.getFont(this.titleOption.subtextStyle);
+            
+            var totalWidth = Math.max(
+                    zrArea.getTextWidth(text, font),
+                    zrArea.getTextWidth(subtext, subfont)
+                );
+            var totalHeight = zrArea.getTextHeight(text, font)
+                              + (subtext === ''
+                                 ? 0
+                                 : (this.titleOption.itemGap
+                                    + zrArea.getTextHeight(subtext, subfont))
+                                );
+
+            var x;
+            var zrWidth = this.zr.getWidth();
+            switch (this.titleOption.x) {
+                case 'center' :
+                    x = Math.floor((zrWidth - totalWidth) / 2);
+                    break;
+                case 'left' :
+                    x = padding[3] + this.titleOption.borderWidth;
+                    break;
+                case 'right' :
+                    x = zrWidth
+                        - totalWidth
+                        - padding[1]
+                        - this.titleOption.borderWidth;
+                    break;
+                default :
+                    x = this.titleOption.x - 0;
+                    x = isNaN(x) ? 0 : x;
+                    break;
+            }
+
+            var y;
+            var zrHeight = this.zr.getHeight();
+            switch (this.titleOption.y) {
+                case 'top' :
+                    y = padding[0] + this.titleOption.borderWidth;
+                    break;
+                case 'bottom' :
+                    y = zrHeight
+                        - totalHeight
+                        - padding[2]
+                        - this.titleOption.borderWidth;
+                    break;
+                case 'center' :
+                    y = Math.floor((zrHeight - totalHeight) / 2);
+                    break;
+                default :
+                    y = this.titleOption.y - 0;
+                    y = isNaN(y) ? 0 : y;
+                    break;
+            }
+
+            return {
+                x: x,
+                y: y,
+                width: totalWidth,
+                height: totalHeight
+            };
+        },
+        
+        /**
+         * 刷新
+         */
+        refresh: function (newOption) {
+            if (newOption) {
+                this.option = newOption;
+
+                this.option.title = this.reformOption(this.option.title);
+                this.titleOption = this.option.title;
+                this.titleOption.textStyle = this.getTextStyle(
+                    this.titleOption.textStyle
+                );
+                this.titleOption.subtextStyle = this.getTextStyle(
+                    this.titleOption.subtextStyle
+                );
+            }
+            
+            this.clear();
+            this._buildShape();
+        }
+    };
+    
+    zrUtil.inherits(Title, Base);
+    
+    require('../component').define('title', Title);
+    
+    return Title;
+});
+
+

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1244 - 0
src/main/webapp/static/echarts-2.2.7/src/component/toolbox.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1740 - 0
src/main/webapp/static/echarts-2.2.7/src/component/tooltip.js


+ 948 - 0
src/main/webapp/static/echarts-2.2.7/src/component/valueAxis.js

@@ -0,0 +1,948 @@
+/**
+ * echarts组件: 数值轴
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var Base = require('./base');
+
+    // 图形依赖
+    var TextShape = require('zrender/shape/Text');
+    var LineShape = require('zrender/shape/Line');
+    var RectangleShape = require('zrender/shape/Rectangle');
+
+    var ecConfig = require('../config');
+    // 数值型坐标轴默认参数
+    ecConfig.valueAxis = {
+        zlevel: 0,                  // 一级层叠
+        z: 0,                       // 二级层叠
+        show: true,
+        position: 'left',      // 位置
+        name: '',              // 坐标轴名字,默认为空
+        nameLocation: 'end',   // 坐标轴名字位置,支持'start' | 'end'
+        nameTextStyle: {},     // 坐标轴文字样式,默认取全局样式
+        boundaryGap: [0, 0],   // 数值起始和结束两端空白策略
+        // min: null,          // 最小值
+        // max: null,          // 最大值
+        // scale: false,       // 脱离0值比例,放大聚焦到最终_min,_max区间
+        // splitNumber: 5,        // 分割段数,默认为5
+        axisLine: {            // 坐标轴线
+            show: true,        // 默认显示,属性show控制显示与否
+            onZero: true,
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#48b',
+                width: 2,
+                type: 'solid'
+            }
+        },
+        axisTick: {            // 坐标轴小标记
+            show: false,       // 属性show控制显示与否,默认不显示
+            inside: false,     // 控制小标记是否在grid里
+            length :5,         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#333',
+                width: 1
+            }
+        },
+        axisLabel: {           // 坐标轴文本标签,详见axis.axisLabel
+            show: true,
+            rotate: 0,
+            margin: 8,
+            // clickable: false,
+            // formatter: null,
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#333'
+            }
+        },
+        splitLine: {           // 分隔线
+            show: true,        // 默认显示,属性show控制显示与否
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: ['#ccc'],
+                width: 1,
+                type: 'solid'
+            }
+        },
+        splitArea: {           // 分隔区域
+            show: false,       // 默认不显示,属性show控制显示与否
+            areaStyle: {       // 属性areaStyle(详见areaStyle)控制区域样式
+                color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
+            }
+        }
+    };
+
+    var ecDate = require('../util/date');
+    var zrUtil = require('zrender/tool/util');
+
+    /**
+     * 构造函数
+     * @param {Object} messageCenter echart消息中心
+     * @param {ZRender} zr zrender实例
+     * @param {Object} option 类目轴参数
+     * @param {Object} component 组件
+     * @param {Array} series 数据对象
+     */
+    function ValueAxis(ecTheme, messageCenter, zr, option, myChart, axisBase, series) {
+        if (!series || series.length === 0) {
+            console.err('option.series.length == 0.');
+            return;
+        }
+
+        Base.call(this, ecTheme, messageCenter, zr, option, myChart);
+
+        this.series = series;
+        this.grid = this.component.grid;
+
+        for (var method in axisBase) {
+            this[method] = axisBase[method];
+        }
+
+        this.refresh(option, series);
+    }
+
+    ValueAxis.prototype = {
+        type: ecConfig.COMPONENT_TYPE_AXIS_VALUE,
+
+        _buildShape: function () {
+            this._hasData = false;
+            this._calculateValue();
+            if (!this._hasData || !this.option.show) {
+                return;
+            }
+
+            this.option.splitArea.show && this._buildSplitArea();
+            this.option.splitLine.show && this._buildSplitLine();
+            this.option.axisLine.show && this._buildAxisLine();
+            this.option.axisTick.show && this._buildAxisTick();
+            this.option.axisLabel.show && this._buildAxisLabel();
+
+            for (var i = 0, l = this.shapeList.length; i < l; i++) {
+                this.zr.addShape(this.shapeList[i]);
+            }
+        },
+
+        // 小标记
+        _buildAxisTick: function () {
+            var axShape;
+            var data       = this._valueList;
+            var dataLength = this._valueList.length;
+            var tickOption = this.option.axisTick;
+            var length     = tickOption.length;
+            var color      = tickOption.lineStyle.color;
+            var lineWidth  = tickOption.lineStyle.width;
+
+            if (this.isHorizontal()) {
+                // 横向
+                var yPosition = this.option.position === 'bottom'
+                        ? (tickOption.inside
+                           ? (this.grid.getYend() - length - 1) : (this.grid.getYend()) + 1)
+                        : (tickOption.inside
+                           ? (this.grid.getY() + 1) : (this.grid.getY() - length - 1));
+                var x;
+                for (var i = 0; i < dataLength; i++) {
+                    // 亚像素优化
+                    x = this.subPixelOptimize(this.getCoord(data[i]), lineWidth);
+                    axShape = {
+                        _axisShape: 'axisTick',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable: false,
+                        style: {
+                            xStart: x,
+                            yStart: yPosition,
+                            xEnd: x,
+                            yEnd: yPosition + length,
+                            strokeColor: color,
+                            lineWidth: lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+            else {
+                // 纵向
+                var xPosition = this.option.position === 'left'
+                    ? (tickOption.inside
+                       ? (this.grid.getX() + 1) : (this.grid.getX() - length - 1))
+                    : (tickOption.inside
+                       ? (this.grid.getXend() - length - 1) : (this.grid.getXend() + 1));
+
+                var y;
+                for (var i = 0; i < dataLength; i++) {
+                    // 亚像素优化
+                    y = this.subPixelOptimize(this.getCoord(data[i]), lineWidth);
+                    axShape = {
+                        _axisShape: 'axisTick',
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable: false,
+                        style: {
+                            xStart: xPosition,
+                            yStart: y,
+                            xEnd: xPosition + length,
+                            yEnd: y,
+                            strokeColor: color,
+                            lineWidth: lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+        },
+
+        // 坐标轴文本
+        _buildAxisLabel: function () {
+            var axShape;
+            var data       = this._valueList;
+            var dataLength = this._valueList.length;
+            var rotate     = this.option.axisLabel.rotate;
+            var margin     = this.option.axisLabel.margin;
+            var clickable  = this.option.axisLabel.clickable;
+            var textStyle  = this.option.axisLabel.textStyle;
+
+            if (this.isHorizontal()) {
+                // 横向
+                var yPosition;
+                var baseLine;
+                if (this.option.position === 'bottom') {
+                    yPosition = this.grid.getYend() + margin;
+                    baseLine = 'top';
+                }
+                else {
+                    yPosition = this.grid.getY() - margin;
+                    baseLine = 'bottom';
+                }
+
+                for (var i = 0; i < dataLength; i++) {
+                    axShape = {
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase() +3,
+                        hoverable: false,
+                        style: {
+                            x: this.getCoord(data[i]),
+                            y: yPosition,
+                            color: typeof textStyle.color === 'function'
+                                   ? textStyle.color(data[i]) : textStyle.color,
+                            text: this._valueLabel[i],
+                            textFont: this.getFont(textStyle),
+                            textAlign: textStyle.align || 'center',
+                            textBaseline: textStyle.baseline || baseLine
+                        }
+                    };
+                    if (rotate) {
+                        axShape.style.textAlign = rotate > 0
+                                                  ? (this.option.position === 'bottom'
+                                                    ? 'right' : 'left')
+                                                  : (this.option.position === 'bottom'
+                                                    ? 'left' : 'right');
+                        axShape.rotation = [
+                            rotate * Math.PI / 180,
+                            axShape.style.x,
+                            axShape.style.y
+                        ];
+                    }
+                    this.shapeList.push(new TextShape(
+                        this._axisLabelClickable(clickable, axShape)
+                    ));
+                }
+            }
+            else {
+                // 纵向
+                var xPosition;
+                var align;
+                if (this.option.position === 'left') {
+                    xPosition = this.grid.getX() - margin;
+                    align = 'right';
+                }
+                else {
+                    xPosition = this.grid.getXend() + margin;
+                    align = 'left';
+                }
+
+                for (var i = 0; i < dataLength; i++) {
+                    axShape = {
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase() + 3,
+                        hoverable: false,
+                        style: {
+                            x: xPosition,
+                            y: this.getCoord(data[i]),
+                            color: typeof textStyle.color === 'function'
+                                   ? textStyle.color(data[i]) : textStyle.color,
+                            text: this._valueLabel[i],
+                            textFont: this.getFont(textStyle),
+                            textAlign: textStyle.align || align,
+                            textBaseline: textStyle.baseline
+                                          || (
+                                              (i === 0 && this.option.name !== '')
+                                              ? 'bottom'
+                                                : (i === dataLength - 1 && this.option.name !== '') ? 'top' : 'middle'
+                                          )
+                        }
+                    };
+
+                    if (rotate) {
+                        axShape.rotation = [
+                            rotate * Math.PI / 180,
+                            axShape.style.x,
+                            axShape.style.y
+                        ];
+                    }
+                    this.shapeList.push(new TextShape(
+                        this._axisLabelClickable(clickable, axShape)
+                    ));
+                }
+            }
+        },
+
+        _buildSplitLine: function () {
+            var axShape;
+            var data        = this._valueList;
+            var dataLength  = this._valueList.length;
+            var sLineOption = this.option.splitLine;
+            var lineType    = sLineOption.lineStyle.type;
+            var lineWidth   = sLineOption.lineStyle.width;
+            var color       = sLineOption.lineStyle.color;
+            color = color instanceof Array ? color : [color];
+            var colorLength = color.length;
+
+            if (this.isHorizontal()) {
+                // 横向
+                var sy = this.grid.getY();
+                var ey = this.grid.getYend();
+                var x;
+
+                for (var i = 0; i < dataLength; i++) {
+                    // 亚像素优化
+                    x = this.subPixelOptimize(this.getCoord(data[i]), lineWidth);
+                    axShape = {
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable: false,
+                        style: {
+                            xStart: x,
+                            yStart: sy,
+                            xEnd: x,
+                            yEnd: ey,
+                            strokeColor: color[i % colorLength],
+                            lineType: lineType,
+                            lineWidth: lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+
+            }
+            else {
+                // 纵向
+                var sx = this.grid.getX();
+                var ex = this.grid.getXend();
+                var y;
+
+                for (var i = 0; i < dataLength; i++) {
+                    // 亚像素优化
+                    y = this.subPixelOptimize(this.getCoord(data[i]), lineWidth);
+                    axShape = {
+                        zlevel: this.getZlevelBase(),
+                        z: this.getZBase(),
+                        hoverable: false,
+                        style: {
+                            xStart: sx,
+                            yStart: y,
+                            xEnd: ex,
+                            yEnd: y,
+                            strokeColor: color[i % colorLength],
+                            lineType: lineType,
+                            lineWidth: lineWidth
+                        }
+                    };
+                    this.shapeList.push(new LineShape(axShape));
+                }
+            }
+        },
+
+        _buildSplitArea: function () {
+            var axShape;
+            var color = this.option.splitArea.areaStyle.color;
+
+            if (!(color instanceof Array)) {
+                // 非数组一律认为是单一颜色的字符串,单一颜色则用一个背景,颜色错误不负责啊!!!
+                axShape = {
+                    zlevel: this.getZlevelBase(),
+                    z: this.getZBase(),
+                    hoverable: false,
+                    style: {
+                        x: this.grid.getX(),
+                        y: this.grid.getY(),
+                        width: this.grid.getWidth(),
+                        height: this.grid.getHeight(),
+                        color: color
+                        // type: this.option.splitArea.areaStyle.type,
+                    }
+                };
+                this.shapeList.push(new RectangleShape(axShape));
+            }
+            else {
+                // 多颜色
+                var colorLength = color.length;
+                var data        = this._valueList;
+                var dataLength  = this._valueList.length;
+
+                if (this.isHorizontal()) {
+                    // 横向
+                    var y = this.grid.getY();
+                    var height = this.grid.getHeight();
+                    var lastX = this.grid.getX();
+                    var curX;
+
+                    for (var i = 0; i <= dataLength; i++) {
+                        curX = i < dataLength
+                               ? this.getCoord(data[i])
+                               : this.grid.getXend();
+                        axShape = {
+                            zlevel: this.getZlevelBase(),
+                            z: this.getZBase(),
+                            hoverable: false,
+                            style: {
+                                x: lastX,
+                                y: y,
+                                width: curX - lastX,
+                                height: height,
+                                color: color[i % colorLength]
+                                // type: this.option.splitArea.areaStyle.type,
+                            }
+                        };
+                        this.shapeList.push(new RectangleShape(axShape));
+                        lastX = curX;
+                    }
+                }
+                else {
+                    // 纵向
+                    var x = this.grid.getX();
+                    var width = this.grid.getWidth();
+                    var lastYend = this.grid.getYend();
+                    var curY;
+
+                    for (var i = 0; i <= dataLength; i++) {
+                        curY = i < dataLength
+                               ? this.getCoord(data[i])
+                               : this.grid.getY();
+                        axShape = {
+                            zlevel: this.getZlevelBase(),
+                            z: this.getZBase(),
+                            hoverable: false,
+                            style: {
+                                x: x,
+                                y: curY,
+                                width: width,
+                                height: lastYend - curY,
+                                color: color[i % colorLength]
+                                // type: this.option.splitArea.areaStyle.type
+                            }
+                        };
+                        this.shapeList.push(new RectangleShape(axShape));
+                        lastYend = curY;
+                    }
+                }
+            }
+        },
+
+        /**
+         * 极值计算
+         */
+        _calculateValue: function () {
+            if (isNaN(this.option.min - 0) || isNaN(this.option.max - 0)) {
+                // 有一个没指定都得算
+                // 数据整形
+                var data = {};          // 整形后数据抽取
+                var xIdx;
+                var yIdx;
+                var legend = this.component.legend;
+                for (var i = 0, l = this.series.length; i < l; i++) {
+                    if (this.series[i].type != ecConfig.CHART_TYPE_LINE
+                        && this.series[i].type != ecConfig.CHART_TYPE_BAR
+                        && this.series[i].type != ecConfig.CHART_TYPE_SCATTER
+                        && this.series[i].type != ecConfig.CHART_TYPE_K
+                        && this.series[i].type != ecConfig.CHART_TYPE_EVENTRIVER
+                    ) {
+                        // 非坐标轴支持的不算极值
+                        continue;
+                    }
+                    // 请允许我写开,跟上面一个不是一样东西
+                    if (legend && !legend.isSelected(this.series[i].name)){
+                        continue;
+                    }
+
+                    // 不指定默认为第一轴线
+                    xIdx = this.series[i].xAxisIndex || 0;
+                    yIdx = this.series[i].yAxisIndex || 0;
+                    if ((this.option.xAxisIndex != xIdx)
+                        && (this.option.yAxisIndex != yIdx)
+                    ) {
+                        // 不是自己的数据不计算极值
+                        continue;
+                    }
+
+                    this._calculSum(data, i);
+                }
+
+                // 找极值
+                var oriData;            // 原始数据
+                for (var i in data){
+                    oriData = data[i];
+                    for (var j = 0, k = oriData.length; j < k; j++) {
+                        if (!isNaN(oriData[j])){
+                            this._hasData = true;
+                            this._min = oriData[j];
+                            this._max = oriData[j];
+                            break;
+                        }
+                    }
+                    if (this._hasData) {
+                        break;
+                    }
+                }
+                for (var i in data){
+                    oriData = data[i];
+                    for (var j = 0, k = oriData.length; j < k; j++) {
+                        if (!isNaN(oriData[j])){
+                            this._min = Math.min(this._min, oriData[j]);
+                            this._max = Math.max(this._max, oriData[j]);
+                        }
+                    }
+                }
+
+                // console.log(this._min,this._max,'vvvvv111111',this.option.type)
+                // log情况暂时禁用boundaryGap。
+                var boundaryGap = this.option.type !== 'log' ? this.option.boundaryGap : [0, 0];
+                var gap = Math.abs(this._max - this._min);
+                this._min = isNaN(this.option.min - 0)
+                       ? (this._min - Math.abs(gap * boundaryGap[0]))
+                       : (this.option.min - 0);    // 指定min忽略boundaryGay[0]
+
+                this._max = isNaN(this.option.max - 0)
+                       ? (this._max + Math.abs(gap * boundaryGap[1]))
+                       : (this.option.max - 0);    // 指定max忽略boundaryGay[1]
+                if (this._min === this._max) {
+                    if (this._max === 0) {
+                        // 修复全0数据
+                        this._max = 1;
+                    }
+                    // 修复最大值==最小值时数据整形
+                    else if (this._max > 0) {
+                        this._min = this._max / this.option.splitNumber != null ? this.option.splitNumber : 5;
+                    }
+                    else { // this._max < 0
+                        this._max = this._max / this.option.splitNumber != null ? this.option.splitNumber : 5;
+                    }
+                }
+
+                if (this.option.type === 'time') {
+                    this._reformTimeValue();
+                }
+                else if (this.option.type === 'log') {
+                    this._reformLogValue();
+                }
+                else {
+                    this._reformValue(this.option.scale);
+                }
+            }
+            else {
+                this._hasData = true;
+                // 用户指定min max就不多管闲事了
+                this._min = this.option.min - 0;    // 指定min忽略boundaryGay[0]
+                this._max = this.option.max - 0;    // 指定max忽略boundaryGay[1]
+
+                if (this.option.type === 'time') {
+                    this._reformTimeValue();
+                }
+                else if (this.option.type === 'log') {
+                    this._reformLogValue();
+                }
+                else {
+                    this._customerValue();
+                }
+            }
+        },
+
+        /**
+         * 内部使用,计算某系列下的堆叠和
+         */
+        _calculSum: function (data, i) {
+            var key = this.series[i].name || 'kener';
+            var value;
+            var oriData;
+            if (!this.series[i].stack) {
+                data[key] = data[key] || [];
+                if (this.series[i].type != ecConfig.CHART_TYPE_EVENTRIVER) {
+                    oriData = this.series[i].data;
+                    for (var j = 0, k = oriData.length; j < k; j++) {
+                        value = this.getDataFromOption(oriData[j]);
+                        if (this.series[i].type === ecConfig.CHART_TYPE_K) {
+                            data[key].push(value[0]);
+                            data[key].push(value[1]);
+                            data[key].push(value[2]);
+                            data[key].push(value[3]);
+                        }
+                        else if (value instanceof Array) {
+                            // scatter 、 不等距 line bar
+                            if (this.option.xAxisIndex != -1) {
+                                data[key].push(
+                                    this.option.type != 'time'
+                                    ? value[0] : ecDate.getNewDate(value[0])
+                                );
+                            }
+                            if (this.option.yAxisIndex != -1) {
+                                data[key].push(
+                                    this.option.type != 'time'
+                                    ? value[1] : ecDate.getNewDate(value[1])
+                                );
+                            }
+                        }
+                        else {
+                            data[key].push(value);
+                        }
+                    }
+                }
+                else {
+                    // eventRiver
+                    oriData = this.series[i].data;
+                    for (var j = 0, k = oriData.length; j < k; j++) {
+                        var evolution = oriData[j].evolution;
+                        for (var m = 0, n = evolution.length; m < n; m++) {
+                            data[key].push(ecDate.getNewDate(evolution[m].time));
+                        }
+                    }
+                }
+            }
+            else {
+                // 堆积数据,需要区分正负向堆积
+                var keyP = '__Magic_Key_Positive__' + this.series[i].stack;
+                var keyN = '__Magic_Key_Negative__' + this.series[i].stack;
+                data[keyP] = data[keyP] || [];
+                data[keyN] = data[keyN] || [];
+                data[key] = data[key] || [];  // scale下还需要记录每一个量
+                oriData = this.series[i].data;
+                for (var j = 0, k = oriData.length; j < k; j++) {
+                    value = this.getDataFromOption(oriData[j]);
+                    if (value === '-') {
+                        continue;
+                    }
+                    value = value - 0;
+                    if (value >= 0) {
+                        if (data[keyP][j] != null) {
+                            data[keyP][j] += value;
+                        }
+                        else {
+                            data[keyP][j] = value;
+                        }
+                    }
+                    else {
+                        if (data[keyN][j] != null) {
+                            data[keyN][j] += value;
+                        }
+                        else {
+                            data[keyN][j] = value;
+                        }
+                    }
+                    if (this.option.scale) {
+                        data[key].push(value);
+                    }
+                }
+            }
+        },
+
+        /**
+         * 找到原始数据的极值后根据选项整形最终 this._min / this._max / this._valueList
+         * 如果你不知道这个“整形”的用义,请不要试图去理解和修改这个方法!找我也没用,我相信我已经记不起来!
+         * 如果你有更简洁的数学推导欢迎重写,后果自负~
+         *
+         * by kener.linfeng@gmail.com 2013-1-8
+         * --------
+         * 感谢谢世威(https://github.com/i6ma),终于有人改这个方法了
+         * by Kener 2014-11-6
+         */
+        _reformValue: function (scale) {
+            var smartSteps = require('../util/smartSteps');
+            var splitNumber = this.option.splitNumber;
+
+            // 非scale下双正,修正最小值为0
+            if (!scale && this._min >= 0 && this._max >= 0) {
+                this._min = 0;
+            }
+            // 非scale下双负,修正最大值为0
+            if (!scale && this._min <= 0 && this._max <= 0) {
+                this._max = 0;
+            }
+
+            var stepOpt = smartSteps(this._min, this._max, splitNumber);
+            splitNumber = splitNumber != null ? splitNumber : stepOpt.secs;
+            //this.option.splitNumber = splitNumber;
+            this._min = stepOpt.min;
+            this._max = stepOpt.max;
+            this._valueList = stepOpt.pnts;
+            this._reformLabelData();
+        },
+
+        /**
+         * 格式化时间值
+         */
+        _reformTimeValue : function() {
+            var splitNumber = this.option.splitNumber != null ? this.option.splitNumber : 5;
+
+            // 最优解
+            var curValue = ecDate.getAutoFormatter(this._min, this._max, splitNumber);
+            // 目标
+            var formatter = curValue.formatter;
+            var gapValue = curValue.gapValue;
+
+            this._valueList = [ecDate.getNewDate(this._min)];
+            var startGap;
+            switch (formatter) {
+                case 'week' :
+                    startGap = ecDate.nextMonday(this._min);
+                    break;
+                case 'month' :
+                    startGap = ecDate.nextNthOnMonth(this._min, 1);
+                    break;
+                case 'quarter' :
+                    startGap = ecDate.nextNthOnQuarterYear(this._min, 1);
+                    break;
+                case 'half-year' :
+                    startGap = ecDate.nextNthOnHalfYear(this._min, 1);
+                    break;
+                case 'year' :
+                    startGap = ecDate.nextNthOnYear(this._min, 1);
+                    break;
+                default :
+                    // 大于2小时需要考虑时区不能直接取整
+                    if (gapValue <= 3600000 * 2) {
+                        startGap = (Math.floor(this._min / gapValue) + 1) * gapValue;
+                    }
+                    else {
+                        startGap = ecDate.getNewDate(this._min - (-gapValue));
+                        startGap.setHours(Math.round(startGap.getHours() / 6) * 6);
+                        startGap.setMinutes(0);
+                        startGap.setSeconds(0);
+                    }
+                    break;
+            }
+
+            if (startGap - this._min < gapValue / 2) {
+                startGap -= -gapValue;
+            }
+
+            // console.log(startGap,gapValue,this._min, this._max,formatter)
+            curValue = ecDate.getNewDate(startGap);
+            splitNumber *= 1.5;
+            while (splitNumber-- >= 0) {
+                if (formatter == 'month'
+                    || formatter == 'quarter'
+                    || formatter == 'half-year'
+                    || formatter == 'year'
+                ) {
+                    curValue.setDate(1);
+                }
+                if (this._max - curValue < gapValue / 2) {
+                    break;
+                }
+                this._valueList.push(curValue);
+                curValue = ecDate.getNewDate(curValue - (-gapValue));
+            }
+            this._valueList.push(ecDate.getNewDate(this._max));
+
+            this._reformLabelData((function (formatterStr) {
+                return function (value) {
+                    return ecDate.format(formatterStr, value);
+                };
+            })(formatter));
+        },
+
+        _customerValue: function () {
+            var accMath = require('../util/accMath');
+            var splitNumber = this.option.splitNumber != null ? this.option.splitNumber : 5;
+            var splitGap = (this._max - this._min) / splitNumber;
+
+            this._valueList = [];
+            for (var i = 0; i <= splitNumber; i++) {
+                this._valueList.push(accMath.accAdd(this._min, accMath.accMul(splitGap, i)));
+            }
+            this._reformLabelData();
+        },
+
+        _reformLogValue: function() {
+            // log数轴本质就是缩放,相当于默认this.option.scale === true,所以不修正_min和_max到0。
+            var thisOption = this.option;
+            var result = require('../util/smartLogSteps')({
+                dataMin: this._min,
+                dataMax: this._max,
+                logPositive: thisOption.logPositive,
+                logLabelBase: thisOption.logLabelBase,
+                splitNumber: thisOption.splitNumber
+            });
+
+            this._min = result.dataMin;
+            this._max = result.dataMax;
+            this._valueList = result.tickList;
+            // {value2Coord: {Function}, coord2Value: {Function}}
+            this._dataMappingMethods = result.dataMappingMethods;
+
+            this._reformLabelData(result.labelFormatter);
+        },
+
+        _reformLabelData: function (innerFormatter) {
+            this._valueLabel = [];
+            var formatter = this.option.axisLabel.formatter;
+            if (formatter) {
+                for (var i = 0, l = this._valueList.length; i < l; i++) {
+                    if (typeof formatter === 'function') {
+                        this._valueLabel.push(
+                            innerFormatter
+                                ? formatter.call(this.myChart, this._valueList[i], innerFormatter)
+                                : formatter.call(this.myChart, this._valueList[i])
+                        );
+                    }
+                    else if (typeof formatter === 'string') {
+                        this._valueLabel.push(
+                            innerFormatter
+                                ? ecDate.format(formatter, this._valueList[i])
+                                : formatter.replace('{value}',this._valueList[i])
+                        );
+                    }
+                }
+            }
+            else {
+                for (var i = 0, l = this._valueList.length; i < l; i++) {
+                    this._valueLabel.push(
+                        innerFormatter
+                            ? innerFormatter(this._valueList[i])
+                            : this.numAddCommas(this._valueList[i]) // 每三位默认加,格式化
+                    );
+                }
+            }
+        },
+
+        getExtremum: function () {
+            this._calculateValue();
+            var dataMappingMethods = this._dataMappingMethods;
+            return {
+                min: this._min,
+                max: this._max,
+                dataMappingMethods: dataMappingMethods
+                    ? zrUtil.merge({}, dataMappingMethods) : null
+            };
+        },
+
+        /**
+         * 刷新
+         */
+        refresh: function (newOption, newSeries) {
+            if (newOption) {
+                this.option = this.reformOption(newOption);
+                // 通用字体设置
+                this.option.axisLabel.textStyle = zrUtil.merge(
+                    this.option.axisLabel.textStyle || {},
+                    this.ecTheme.textStyle
+                );
+                this.series = newSeries;
+            }
+            if (this.zr) {   // 数值轴的另外一个功能只是用来计算极值
+                this.clear();
+                this._buildShape();
+            }
+        },
+
+        // 根据值换算位置
+        getCoord: function (value) {
+            if (this._dataMappingMethods) {
+                value = this._dataMappingMethods.value2Coord(value);
+            }
+
+            value = value < this._min ? this._min : value;
+            value = value > this._max ? this._max : value;
+
+            var result;
+            if (!this.isHorizontal()) {
+                // 纵向
+                result = this.grid.getYend()
+                         - (value - this._min)
+                           / (this._max - this._min)
+                           * this.grid.getHeight();
+            }
+            else {
+                // 横向
+                result = this.grid.getX()
+                         + (value - this._min)
+                           / (this._max - this._min)
+                           * this.grid.getWidth();
+            }
+
+            return result;
+            // Math.floor可能引起一些偏差,但性能会更好
+            /* 准确更重要
+            return (value === this._min || value === this._max)
+                   ? result
+                   : Math.floor(result);
+            */
+        },
+
+        // 根据值换算绝对大小
+        getCoordSize: function (value) {
+            if (!this.isHorizontal()) {
+                // 纵向
+                return Math.abs(value / (this._max - this._min) * this.grid.getHeight());
+            }
+            else {
+                // 横向
+                return Math.abs(value / (this._max - this._min) * this.grid.getWidth());
+            }
+        },
+
+        // 根据位置换算值
+        getValueFromCoord: function(coord) {
+            var result;
+
+            if (!this.isHorizontal()) {
+                // 纵向
+                coord = coord < this.grid.getY() ? this.grid.getY() : coord;
+                coord = coord > this.grid.getYend() ? this.grid.getYend() : coord;
+                result = this._max
+                         - (coord - this.grid.getY())
+                           / this.grid.getHeight()
+                           * (this._max - this._min);
+            }
+            else {
+                // 横向
+                coord = coord < this.grid.getX() ? this.grid.getX() : coord;
+                coord = coord > this.grid.getXend() ? this.grid.getXend() : coord;
+                result = this._min
+                         + (coord - this.grid.getX())
+                           / this.grid.getWidth()
+                           * (this._max - this._min);
+            }
+
+            if (this._dataMappingMethods) {
+                result = this._dataMappingMethods.coord2Value(result);
+            }
+
+            return result.toFixed(2) - 0;
+        },
+
+        isMaindAxis : function (value) {
+            for (var i = 0, l = this._valueList.length; i < l; i++) {
+                if (this._valueList[i] === value) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    zrUtil.inherits(ValueAxis, Base);
+
+    require('../component').define('valueAxis', ValueAxis);
+
+    return ValueAxis;
+});
+

+ 233 - 0
src/main/webapp/static/echarts-2.2.7/src/config.js

@@ -0,0 +1,233 @@
+/**
+ * echarts默认配置项
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function() {
+    // 请原谅我这样写,这显然可以直接返回个对象,但那样的话outline就显示不出来了~~
+    var config = {
+        // 图表类型
+        CHART_TYPE_LINE: 'line',
+        CHART_TYPE_BAR: 'bar',
+        CHART_TYPE_SCATTER: 'scatter',
+        CHART_TYPE_PIE: 'pie',
+        CHART_TYPE_RADAR: 'radar',
+        CHART_TYPE_VENN: 'venn',
+        CHART_TYPE_TREEMAP: 'treemap',
+        CHART_TYPE_TREE: 'tree',
+        CHART_TYPE_MAP: 'map',
+        CHART_TYPE_K: 'k',
+        CHART_TYPE_ISLAND: 'island',
+        CHART_TYPE_FORCE: 'force',
+        CHART_TYPE_CHORD: 'chord',
+        CHART_TYPE_GAUGE: 'gauge',
+        CHART_TYPE_FUNNEL: 'funnel',
+        CHART_TYPE_EVENTRIVER: 'eventRiver',
+        CHART_TYPE_WORDCLOUD: 'wordCloud',
+        CHART_TYPE_HEATMAP: 'heatmap',
+
+        // 组件类型
+        COMPONENT_TYPE_TITLE: 'title',
+        COMPONENT_TYPE_LEGEND: 'legend',
+        COMPONENT_TYPE_DATARANGE: 'dataRange',
+        COMPONENT_TYPE_DATAVIEW: 'dataView',
+        COMPONENT_TYPE_DATAZOOM: 'dataZoom',
+        COMPONENT_TYPE_TOOLBOX: 'toolbox',
+        COMPONENT_TYPE_TOOLTIP: 'tooltip',
+        COMPONENT_TYPE_GRID: 'grid',
+        COMPONENT_TYPE_AXIS: 'axis',
+        COMPONENT_TYPE_POLAR: 'polar',
+        COMPONENT_TYPE_X_AXIS: 'xAxis',
+        COMPONENT_TYPE_Y_AXIS: 'yAxis',
+        COMPONENT_TYPE_AXIS_CATEGORY: 'categoryAxis',
+        COMPONENT_TYPE_AXIS_VALUE: 'valueAxis',
+        COMPONENT_TYPE_TIMELINE: 'timeline',
+        COMPONENT_TYPE_ROAMCONTROLLER: 'roamController',
+
+        // 全图默认背景
+        backgroundColor: 'rgba(0,0,0,0)',
+        
+        // 默认色板
+        color: ['#ff7f50','#87cefa','#da70d6','#32cd32','#6495ed',
+                '#ff69b4','#ba55d3','#cd5c5c','#ffa500','#40e0d0',
+                '#1e90ff','#ff6347','#7b68ee','#00fa9a','#ffd700',
+                '#6699FF','#ff6666','#3cb371','#b8860b','#30e0e0'],
+
+        markPoint: {
+            clickable: true,
+            symbol: 'pin',         // 标注类型
+            symbolSize: 10,        // 标注大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
+            // symbolRotate: null, // 标注旋转控制
+            large: false,
+            effect: {
+                show: false,
+                loop: true,
+                period: 15,             // 运动周期,无单位,值越大越慢
+                type: 'scale',          // 可用为 scale | bounce
+                scaleSize: 2,           // 放大倍数,以markPoint点size为基准
+                bounceDistance: 10     // 跳动距离,单位px
+                // color: 'gold',
+                // shadowColor: 'rgba(255,215,0,0.8)',
+                // shadowBlur: 0          // 炫光模糊
+            },
+            itemStyle: {
+                normal: {
+                    // color: 各异,
+                    // borderColor: 各异,        // 标注边线颜色,优先于color 
+                    borderWidth: 2,             // 标注边线线宽,单位px,默认为1
+                    label: {
+                        show: true,
+                        // 标签文本格式器,同Tooltip.formatter,不支持回调
+                        // formatter: null,
+                        position: 'inside'      // 可选为'left'|'right'|'top'|'bottom'
+                        // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                    }
+                },
+                emphasis: {
+                    // color: 各异
+                    label: {
+                        show: true
+                        // 标签文本格式器,同Tooltip.formatter,不支持回调
+                        // formatter: null,
+                        // position: 'inside'  // 'left'|'right'|'top'|'bottom'
+                        // textStyle: null     // 默认使用全局文本样式,详见TEXTSTYLE
+                    }
+                }
+            }
+        },
+        
+        markLine: {
+            clickable: true,
+            // 标线起始和结束的symbol介绍类型,如果都一样,可以直接传string
+            symbol: ['circle', 'arrow'],
+            // 标线起始和结束的symbol大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
+            symbolSize: [2, 4],
+            // 标线起始和结束的symbol旋转控制
+            //symbolRotate: null,
+            //smooth: false,
+            smoothness: 0.2,    // 平滑度
+            precision: 2,
+            effect: {
+                show: false,
+                loop: true,
+                period: 15,                     // 运动周期,无单位,值越大越慢
+                scaleSize: 2                    // 放大倍数,以markLine线lineWidth为基准
+                // color: 'gold',
+                // shadowColor: 'rgba(255,215,0,0.8)',
+                // shadowBlur: lineWidth * 2    // 炫光模糊,默认等于scaleSize计算所得
+            },
+            // 边捆绑
+            bundling: {
+                enable: false,
+                // [0, 90]
+                maxTurningAngle: 45
+            },
+            itemStyle: {
+                normal: {
+                    // color: 各异,               // 标线主色,线色,symbol主色
+                    // borderColor: 随color,     // 标线symbol边框颜色,优先于color 
+                    borderWidth: 1.5,           // 标线symbol边框线宽,单位px,默认为2
+                    label: {
+                        show: true,
+                        // 标签文本格式器,同Tooltip.formatter,不支持回调
+                        // formatter: null,
+                        // 可选为 'start'|'end'|'left'|'right'|'top'|'bottom'
+                        position: 'end'
+                        // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
+                    },
+                    lineStyle: {
+                        // color: 随borderColor, // 主色,线色,优先级高于borderColor和color
+                        // width: 随borderWidth, // 优先于borderWidth
+                        type: 'dashed'
+                        // shadowColor: 'rgba(0,0,0,0)', //默认透明
+                        // shadowBlur: 0,
+                        // shadowOffsetX: 0,
+                        // shadowOffsetY: 0
+                    }
+                },
+                emphasis: {
+                    // color: 各异
+                    label: {
+                        show: false
+                        // 标签文本格式器,同Tooltip.formatter,不支持回调
+                        // formatter: null,
+                        // position: 'inside' // 'left'|'right'|'top'|'bottom'
+                        // textStyle: null    // 默认使用全局文本样式,详见TEXTSTYLE
+                    },
+                    lineStyle: {}
+                }
+            }
+        },
+
+        // 主题,主题
+        textStyle: {
+            decoration: 'none',
+            fontFamily: 'Arial, Verdana, sans-serif',
+            fontFamily2: '微软雅黑',    // IE8- 字体模糊并且,不支持不同字体混排,额外指定一份
+            fontSize: 12,
+            fontStyle: 'normal',
+            fontWeight: 'normal'
+        },
+
+        EVENT: {
+            // -------全局通用
+            REFRESH: 'refresh',
+            RESTORE: 'restore',
+            RESIZE: 'resize',
+            CLICK: 'click',
+            DBLCLICK: 'dblclick',
+            HOVER: 'hover',
+            MOUSEOUT: 'mouseout',
+            //MOUSEWHEEL: 'mousewheel',
+            // -------业务交互逻辑
+            DATA_CHANGED: 'dataChanged',
+            DATA_ZOOM: 'dataZoom',
+            DATA_RANGE: 'dataRange',
+            DATA_RANGE_SELECTED: 'dataRangeSelected',
+            DATA_RANGE_HOVERLINK: 'dataRangeHoverLink',
+            LEGEND_SELECTED: 'legendSelected',
+            LEGEND_HOVERLINK: 'legendHoverLink',
+            MAP_SELECTED: 'mapSelected',
+            PIE_SELECTED: 'pieSelected',
+            MAGIC_TYPE_CHANGED: 'magicTypeChanged',
+            DATA_VIEW_CHANGED: 'dataViewChanged',
+            TIMELINE_CHANGED: 'timelineChanged',
+            MAP_ROAM: 'mapRoam',
+            FORCE_LAYOUT_END: 'forceLayoutEnd',
+            // -------内部通信
+            TOOLTIP_HOVER: 'tooltipHover',
+            TOOLTIP_IN_GRID: 'tooltipInGrid',
+            TOOLTIP_OUT_GRID: 'tooltipOutGrid',
+            ROAMCONTROLLER: 'roamController'
+        },
+        DRAG_ENABLE_TIME: 120,   // 降低图表内元素拖拽敏感度,单位ms,不建议外部干预
+        EFFECT_ZLEVEL : 10,       // 特效动画zlevel
+        effectBlendAlpha: 0.95,
+        // 主题,默认标志图形类型列表
+        symbolList: [
+          'circle', 'rectangle', 'triangle', 'diamond',
+          'emptyCircle', 'emptyRectangle', 'emptyTriangle', 'emptyDiamond'
+        ],
+        loadingEffect: 'spin',
+        loadingText: '数据读取中...',
+        noDataEffect: 'bubble',
+        noDataText: '暂无数据',
+        // noDataLoadingOption: null,
+        // 可计算特性配置,孤岛,提示颜色
+        calculable: false,                      // 默认关闭可计算特性
+        calculableColor: 'rgba(255,165,0,0.6)', // 拖拽提示边框颜色
+        calculableHolderColor: '#ccc',          // 可计算占位提示颜色
+        nameConnector: ' & ',
+        valueConnector: ': ',
+        animation: true,                // 过渡动画是否开启
+        addDataAnimation: true,         // 动态数据接口是否开启动画效果
+        animationThreshold: 2000,       // 动画元素阀值,产生的图形原素超过2000不出动画
+        animationDuration: 2000,        // 过渡动画参数:进入
+        animationDurationUpdate: 500,   // 过渡动画参数:更新
+        animationEasing: 'ExponentialOut'    //BounceOut
+    };
+
+    return config;
+});

+ 488 - 0
src/main/webapp/static/echarts-2.2.7/src/data/Graph.js

@@ -0,0 +1,488 @@
+/**
+ * Graph data structure
+ * 
+ * @module echarts/data/Graph
+ * @author Yi Shen(https://www.github.com/pissang)
+ */
+define(function(require) {
+
+    var util = require('zrender/tool/util');
+
+    'use strict';
+
+    /**
+     * @alias module:echarts/data/Graph
+     * @constructor
+     * @param {boolean} directed
+     */
+    var Graph = function(directed) {
+        /**
+         * 是否是有向图
+         * @type {boolean}
+         * @private
+         */
+        this._directed = directed || false;
+
+        /**
+         * @type {Array}
+         */
+        this.nodes = [];
+        this.edges = [];
+
+        this._nodesMap = {};
+        this._edgesMap = {};
+    };
+
+    /**
+     * 是否是有向图
+     * @return {boolean}
+     */
+    Graph.prototype.isDirected = function () {
+        return this._directed;
+    };
+
+    /**
+     * 添加一个新的节点
+     * @param {string} id 节点名称
+     * @param {*} [data] 存储的数据
+     */
+    Graph.prototype.addNode = function (id, data) {
+        if (this._nodesMap[id]) {
+            return this._nodesMap[id];
+        }
+
+        var node = new Graph.Node(id, data);
+
+        this.nodes.push(node);
+
+        this._nodesMap[id] = node;
+        return node;
+    };
+    
+    /**
+     * 获取节点
+     * @param  {string} id
+     * @return {module:echarts/data/Graph~Node}
+     */
+    Graph.prototype.getNodeById = function (id) {
+        return this._nodesMap[id];
+    };
+
+    /**
+     * 添加边
+     * @param {string|module:echarts/data/Graph~Node} n1
+     * @param {string|module:echarts/data/Graph~Node} n2
+     * @param {*} data
+     * @return {module:echarts/data/Graph~Edge}
+     */
+    Graph.prototype.addEdge = function (n1, n2, data) {
+        if (typeof(n1) == 'string') {
+            n1 = this._nodesMap[n1];
+        }
+        if (typeof(n2) == 'string') {
+            n2 = this._nodesMap[n2];
+        }
+        if (!n1 || !n2) {
+            return;
+        }
+
+        var key = n1.id + '-' + n2.id;
+        if (this._edgesMap[key]) {
+            return this._edgesMap[key];
+        }
+
+        var edge = new Graph.Edge(n1, n2, data);
+
+        if (this._directed) {
+            n1.outEdges.push(edge);
+            n2.inEdges.push(edge);   
+        }
+        n1.edges.push(edge);
+        if (n1 !== n2) {
+            n2.edges.push(edge);
+        }
+
+        this.edges.push(edge);
+        this._edgesMap[key] = edge;
+
+        return edge;
+    };
+
+    /**
+     * 移除边
+     * @param  {module:echarts/data/Graph~Edge} edge
+     */
+    Graph.prototype.removeEdge = function (edge) {
+        var n1 = edge.node1;
+        var n2 = edge.node2;
+        var key = n1.id + '-' + n2.id;
+        if (this._directed) {
+            n1.outEdges.splice(util.indexOf(n1.outEdges, edge), 1);
+            n2.inEdges.splice(util.indexOf(n2.inEdges, edge), 1);   
+        }
+        n1.edges.splice(util.indexOf(n1.edges, edge), 1);
+        if (n1 !== n2) {
+            n2.edges.splice(util.indexOf(n2.edges, edge), 1);
+        }
+
+        delete this._edgesMap[key];
+        this.edges.splice(util.indexOf(this.edges, edge), 1);
+    };
+
+    /**
+     * 获取边
+     * @param  {module:echarts/data/Graph~Node|string} n1
+     * @param  {module:echarts/data/Graph~Node|string} n2
+     * @return {module:echarts/data/Graph~Edge}
+     */
+    Graph.prototype.getEdge = function (n1, n2) {
+        if (typeof(n1) !== 'string') {
+            n1 = n1.id;
+        }
+        if (typeof(n2) !== 'string') {
+            n2 = n2.id;
+        }
+
+        if (this._directed) {
+            return this._edgesMap[n1 + '-' + n2];
+        } else {
+            return this._edgesMap[n1 + '-' + n2]
+                || this._edgesMap[n2 + '-' + n1];
+        }
+    };
+
+    /**
+     * 移除节点(及其邻接边)
+     * @param  {module:echarts/data/Graph~Node|string} node
+     */
+    Graph.prototype.removeNode = function (node) {
+        if (typeof(node) === 'string') {
+            node = this._nodesMap[node];
+            if (!node) {
+                return;
+            }
+        }
+
+        delete this._nodesMap[node.id];
+        this.nodes.splice(util.indexOf(this.nodes, node), 1);
+
+        for (var i = 0; i < this.edges.length;) {
+            var edge = this.edges[i];
+            if (edge.node1 === node || edge.node2 === node) {
+                this.removeEdge(edge);
+            } else {
+                i++;
+            }
+        }
+    };
+
+    /**
+     * 遍历并且过滤指定的节点
+     * @param  {Function} cb
+     * @param  {*}   [context]
+     */
+     Graph.prototype.filterNode = function (cb, context) {
+        var len = this.nodes.length;
+        for (var i = 0; i < len;) {
+            if (cb.call(context, this.nodes[i], i)) {
+                i++;
+            } else {
+                this.removeNode(this.nodes[i]);
+                len--;
+            }
+        }
+     };
+
+    /**
+     * 遍历并且过滤指定的边
+     * @param  {Function} cb
+     * @param  {*}   [context]
+     */
+     Graph.prototype.filterEdge = function (cb, context) {
+        var len = this.edges.length;
+        for (var i = 0; i < len;) {
+            if (cb.call(context, this.edges[i], i)) {
+                i++;
+            } else {
+                this.removeEdge(this.edges[i]);
+                len--;
+            }
+        }
+     };
+
+    /**
+     * 线性遍历所有节点
+     * @param  {Function} cb
+     * @param  {*}   [context]
+     */
+    Graph.prototype.eachNode = function (cb, context) {
+        var len = this.nodes.length;
+        for (var i = 0; i < len; i++) {
+            if (this.nodes[i]) {    // 可能在遍历过程中存在节点删除
+                cb.call(context, this.nodes[i], i);
+            }
+        }
+    };
+    
+    /**
+     * 线性遍历所有边
+     * @param  {Function} cb
+     * @param  {*}   [context]
+     */
+    Graph.prototype.eachEdge = function (cb, context) {
+        var len = this.edges.length;
+        for (var i = 0; i < len; i++) {
+            if (this.edges[i]) {    // 可能在遍历过程中存在边删除
+                cb.call(context, this.edges[i], i);
+            }
+        }
+    };
+    
+    /**
+     * 清空图
+     */
+    Graph.prototype.clear = function() {
+        this.nodes.length = 0;
+        this.edges.length = 0;
+
+        this._nodesMap = {};
+        this._edgesMap = {};
+    };
+    
+    /**
+     * 广度优先遍历
+     * @param {Function} cb
+     * @param {module:echarts/data/Graph~Node} startNode 遍历起始节点
+     * @param {string} [direction=none] none, in, out 指定遍历边
+     * @param {*} [context] 回调函数调用context
+     */
+    Graph.prototype.breadthFirstTraverse = function (
+        cb, startNode, direction, context
+    ) {
+        if (typeof(startNode) === 'string') {
+            startNode = this._nodesMap[startNode];
+        }
+        if (!startNode) {
+            return;
+        }
+
+        var edgeType = 'edges';
+        if (direction === 'out') {
+            edgeType = 'outEdges';
+        } else if (direction === 'in') {
+            edgeType = 'inEdges';
+        }
+        
+        for (var i = 0; i < this.nodes.length; i++) {
+            this.nodes[i].__visited = false;
+        }
+
+        if (cb.call(context, startNode, null)) {
+            return;
+        }
+
+        var queue = [startNode];
+        while (queue.length) {
+            var currentNode = queue.shift();
+            var edges = currentNode[edgeType];
+
+            for (var i = 0; i < edges.length; i++) {
+                var e = edges[i];
+                var otherNode = e.node1 === currentNode 
+                    ? e.node2 : e.node1;
+                if (!otherNode.__visited) {
+                    if (cb.call(otherNode, otherNode, currentNode)) {
+                        // Stop traversing
+                        return;
+                    }
+                    queue.push(otherNode);
+                    otherNode.__visited = true;
+                }
+            }
+        }
+    };
+
+    /**
+     * 复制图
+     */
+    Graph.prototype.clone = function () {
+        var graph = new Graph(this._directed);
+        for (var i = 0; i < this.nodes.length; i++) {
+            graph.addNode(this.nodes[i].id, this.nodes[i].data);
+        }
+        for (var i = 0; i < this.edges.length; i++) {
+            var e = this.edges[i];
+            graph.addEdge(e.node1.id, e.node2.id, e.data);
+        }
+        return graph;
+    };
+
+    /**
+     * 图节点
+     * @alias module:echarts/data/Graph~Node
+     * @param {string} id
+     * @param {*} [data]
+     */
+    var Node = function(id, data) {
+        /**
+         * 节点名称
+         * @type {string}
+         */
+        this.id = id;
+        /**
+         * 节点存储的数据
+         * @type {*}
+         */
+        this.data = data || null;
+        /**
+         * 入边,只在有向图上有效
+         * @type {Array.<module:echarts/data/Graph~Edge>}
+         */
+        this.inEdges = [];
+        /**
+         * 出边,只在有向图上有效
+         * @type {Array.<module:echarts/data/Graph~Edge>}
+         */
+        this.outEdges = [];
+        /**
+         * 邻接边
+         * @type {Array.<module:echarts/data/Graph~Edge>}
+         */
+        this.edges = [];
+    };
+    
+    /**
+     * 度
+     * @return {number}
+     */
+    Node.prototype.degree = function() {
+        return this.edges.length; 
+    };
+    
+    /**
+     * 入度,只在有向图上有效
+     * @return {number}
+     */
+    Node.prototype.inDegree = function() {
+        return this.inEdges.length;
+    };
+    
+    /**
+     * 出度,只在有向图上有效
+     * @return {number}
+     */
+    Node.prototype.outDegree = function() {
+        return this.outEdges.length;
+    };
+
+    /**
+     * 图边
+     * @alias module:echarts/data/Graph~Edge
+     * @param {module:echarts/data/Graph~Node} node1
+     * @param {module:echarts/data/Graph~Node} node2
+     * @param {extra} data
+     */
+    var Edge = function(node1, node2, data) {
+        /**
+         * 节点1,如果是有向图则为源节点
+         * @type {module:echarts/data/Graph~Node}
+         */
+        this.node1 = node1;
+        /**
+         * 节点2,如果是有向图则为目标节点
+         * @type {module:echarts/data/Graph~Node}
+         */
+        this.node2 = node2;
+
+        /**
+         * 边存储的数据
+         * @type {*}
+         */
+        this.data = data || null;
+    };
+
+    Graph.Node = Node;
+    Graph.Edge = Edge;
+
+    /**
+     * 从邻接矩阵生成
+     * ```
+     *        TARGET
+     *    -1--2--3--4--5-
+     *  1| x  x  x  x  x
+     *  2| x  x  x  x  x
+     *  3| x  x  x  x  x  SOURCE
+     *  4| x  x  x  x  x
+     *  5| x  x  x  x  x
+     * ```
+     * 节点的行列总和会被写到`node.data.value`
+     * 对于有向图会计算每一行的和写到`node.data.outValue`,
+     * 计算每一列的和写到`node.data.inValue`。
+     * 边的权重会被然后写到`edge.data.weight`。
+     * 
+     * @method module:echarts/data/Graph.fromMatrix
+     * @param {Array.<Object>} nodesData 节点信息,必须有`id`属性, 会保存到`node.data`中
+     * @param {Array} matrix 邻接矩阵
+     * @param {boolean} directed 是否是有向图
+     * @return {module:echarts/data/Graph}
+     */
+    Graph.fromMatrix = function(nodesData, matrix, directed) {
+        if (
+            !matrix || !matrix.length
+            || (matrix[0].length !== matrix.length)
+            || (nodesData.length !== matrix.length)
+        ) {
+            // Not a valid data
+            return;
+        }
+
+        var size = matrix.length;
+        var graph = new Graph(directed);
+
+        for (var i = 0; i < size; i++) {
+            var node = graph.addNode(nodesData[i].id, nodesData[i]);
+            // TODO
+            // node.data已经有value的情况
+            node.data.value = 0;
+            if (directed) {
+                node.data.outValue = node.data.inValue = 0;
+            }
+        }
+        for (var i = 0; i < size; i++) {
+            for (var j = 0; j < size; j++) {
+                var item = matrix[i][j];
+                if (directed) {
+                    graph.nodes[i].data.outValue += item;
+                    graph.nodes[j].data.inValue += item;
+                }
+                graph.nodes[i].data.value += item;
+                graph.nodes[j].data.value += item;
+            }
+        }
+
+        for (var i = 0; i < size; i++) {
+            for (var j = i; j < size; j++) {
+                var item = matrix[i][j];
+                if (item === 0) {
+                    continue;
+                }
+                var n1 = graph.nodes[i];
+                var n2 = graph.nodes[j];
+                var edge = graph.addEdge(n1, n2, {});
+                edge.data.weight = item;
+                if (i !== j) {
+                    if (directed && matrix[j][i]) {
+                        var inEdge = graph.addEdge(n2, n1, {});
+                        inEdge.data.weight = matrix[j][i];
+                    }
+                }
+            }
+        }
+
+        // console.log(graph.edges.map(function (e) {return e.node1.id + '-' + e.node2.id;}))
+
+        return graph;
+    };
+
+    return Graph;
+});

+ 247 - 0
src/main/webapp/static/echarts-2.2.7/src/data/KDTree.js

@@ -0,0 +1,247 @@
+/**
+ * K-Dimension Tree
+ *
+ * @module echarts/data/KDTree
+ * @author Yi Shen(https://github.com/pissang)
+ */
+define(function (require) {
+
+    var quickSelect = require('./quickSelect');
+
+    function Node(axis, data) {
+        this.left = null;
+        this.right = null;
+        this.axis = axis;
+
+        this.data = data;
+    }
+
+    /**
+     * @constructor
+     * @alias module:echarts/data/KDTree
+     * @param {Array} points List of points.
+     * each point needs an array property to repesent the actual data
+     * @param {Number} [dimension]
+     *        Point dimension.
+     *        Default will use the first point's length as dimensiont
+     */
+    var KDTree = function (points, dimension) {
+        if (!points.length) {
+            return;
+        }
+
+        if (!dimension) {
+            dimension = points[0].array.length;
+        }
+        this.dimension = dimension;
+        this.root = this._buildTree(points, 0, points.length - 1, 0);
+
+        // Use one stack to avoid allocation 
+        // each time searching the nearest point
+        this._stack = [];
+        // Again avoid allocating a new array
+        // each time searching nearest N points
+        this._nearstNList = [];
+    };
+
+    /**
+     * Resursively build the tree
+     */
+    KDTree.prototype._buildTree = function (points, left, right, axis) {
+        if (right < left) {
+            return null;
+        }
+
+        var medianIndex = Math.floor((left + right) / 2);
+        medianIndex = quickSelect(
+            points, left, right, medianIndex,
+            function (a, b) {
+                return a.array[axis] - b.array[axis];
+            }
+        );
+        var median = points[medianIndex];
+
+        var node = new Node(axis, median);
+
+        axis = (axis + 1) % this.dimension;
+        if (right > left) {
+            node.left = this._buildTree(points, left, medianIndex - 1, axis);
+            node.right = this._buildTree(points, medianIndex + 1, right, axis);   
+        }
+
+        return node;
+    };
+
+    /**
+     * Find nearest point
+     * @param  {Array} target Target point
+     * @param  {Function} squaredDistance Squared distance function
+     * @return {Array} Nearest point
+     */
+    KDTree.prototype.nearest = function (target, squaredDistance) {
+        var curr = this.root;
+        var stack = this._stack;
+        var idx = 0;
+        var minDist = Infinity;
+        var nearestNode = null;
+        if (curr.data !== target) {
+            minDist = squaredDistance(curr.data, target);
+            nearestNode = curr;
+        }
+
+        if (target.array[curr.axis] < curr.data.array[curr.axis]) {
+            // Left first
+            curr.right && (stack[idx++] = curr.right);
+            curr.left && (stack[idx++] = curr.left);
+        }
+        else {
+            // Right first
+            curr.left && (stack[idx++] = curr.left);
+            curr.right && (stack[idx++] = curr.right);
+        }
+
+        while (idx--) {
+            curr = stack[idx];
+            var currDist = target.array[curr.axis] - curr.data.array[curr.axis];
+            var isLeft = currDist < 0;
+            var needsCheckOtherSide = false;
+            currDist = currDist * currDist;
+            // Intersecting right hyperplane with minDist hypersphere
+            if (currDist < minDist) {
+                currDist = squaredDistance(curr.data, target);
+                if (currDist < minDist && curr.data !== target) {
+                    minDist = currDist;
+                    nearestNode = curr;
+                }
+                needsCheckOtherSide = true;
+            }
+            if (isLeft) {
+                if (needsCheckOtherSide) {
+                    curr.right && (stack[idx++] = curr.right);
+                }
+                // Search in the left area
+                curr.left && (stack[idx++] = curr.left);
+            }
+            else {
+                if (needsCheckOtherSide) {
+                    curr.left && (stack[idx++] = curr.left);
+                }
+                // Search the right area
+                curr.right && (stack[idx++] = curr.right);
+            }
+        }
+
+        return nearestNode.data;
+    };
+
+    KDTree.prototype._addNearest = function (found, dist, node) {
+        var nearestNList = this._nearstNList;
+
+        // Insert to the right position
+        // Sort from small to large
+        for (var i = found - 1; i > 0; i--) {
+            if (dist >= nearestNList[i - 1].dist) {                
+                break;
+            }
+            else {
+                nearestNList[i].dist = nearestNList[i - 1].dist;
+                nearestNList[i].node = nearestNList[i - 1].node;
+            }
+        }
+
+        nearestNList[i].dist = dist;
+        nearestNList[i].node = node;
+    };
+
+    /**
+     * Find nearest N points
+     * @param  {Array} target Target point
+     * @param  {number} N
+     * @param  {Function} squaredDistance Squared distance function
+     * @param  {Array} [output] Output nearest N points
+     */
+    KDTree.prototype.nearestN = function (target, N, squaredDistance, output) {
+        if (N <= 0) {
+            output.length = 0;
+            return output;
+        }
+
+        var curr = this.root;
+        var stack = this._stack;
+        var idx = 0;
+
+        var nearestNList = this._nearstNList;
+        for (var i = 0; i < N; i++) {
+            // Allocate
+            if (!nearestNList[i]) {
+                nearestNList[i] = {};
+            }
+            nearestNList[i].dist = 0;
+            nearestNList[i].node = null;
+        }
+        var currDist = squaredDistance(curr.data, target);
+
+        var found = 0;
+        if (curr.data !== target) {
+            found++;
+            this._addNearest(found, currDist, curr);
+        }
+
+        if (target.array[curr.axis] < curr.data.array[curr.axis]) {
+            // Left first
+            curr.right && (stack[idx++] = curr.right);
+            curr.left && (stack[idx++] = curr.left);
+        }
+        else {
+            // Right first
+            curr.left && (stack[idx++] = curr.left);
+            curr.right && (stack[idx++] = curr.right);
+        }
+
+        while (idx--) {
+            curr = stack[idx];
+            var currDist = target.array[curr.axis] - curr.data.array[curr.axis];
+            var isLeft = currDist < 0;
+            var needsCheckOtherSide = false;
+            currDist = currDist * currDist;
+            // Intersecting right hyperplane with minDist hypersphere
+            if (found < N || currDist < nearestNList[found - 1].dist) {
+                currDist = squaredDistance(curr.data, target);
+                if (
+                    (found < N || currDist < nearestNList[found - 1].dist)
+                    && curr.data !== target
+                ) {
+                    if (found < N) {
+                        found++;
+                    }
+                    this._addNearest(found, currDist, curr);
+                }
+                needsCheckOtherSide = true;
+            }
+            if (isLeft) {
+                if (needsCheckOtherSide) {
+                    curr.right && (stack[idx++] = curr.right);
+                }
+                // Search in the left area
+                curr.left && (stack[idx++] = curr.left);
+            }
+            else {
+                if (needsCheckOtherSide) {
+                    curr.left && (stack[idx++] = curr.left);
+                }
+                // Search the right area
+                curr.right && (stack[idx++] = curr.right);
+            }
+        }
+
+        // Copy to output
+        for (var i = 0; i < found; i++) {
+            output[i] = nearestNList[i].node.data;
+        }
+        output.length = found;
+
+        return output;
+    };
+
+    return KDTree;
+});

+ 241 - 0
src/main/webapp/static/echarts-2.2.7/src/data/Tree.js

@@ -0,0 +1,241 @@
+/**
+ * Tree data structure
+ * 
+ * @module echarts/data/Tree
+ * @author Yi Shen(https://www.github.com/pissang)
+ */
+define(function(require) {
+
+    var zrUtil = require('zrender/tool/util');
+
+    /**
+     * @constructor module:echarts/data/Tree~TreeNode
+     * @param {string} id Node ID
+     * @param {Object} [data]
+     */
+    function TreeNode(id, data) {
+        /**
+         * @type {string}
+         */
+        this.id = id;
+        /**
+         * 节点的深度
+         * @type {number}
+         */
+        this.depth = 0;
+        /**
+         * 以当前节点为根节点的子树的高度
+         * @type {number}
+         */
+        this.height = 0;
+        /**
+         * 子节点列表
+         * @type {Array.<module:echarts/data/Tree~TreeNode>}
+         */
+        this.children = [];
+
+        /**
+         * @type {module:echarts/data/Tree~TreeNode}
+         */
+        this.parent = null;
+
+        /**
+         * 存储的用户数据
+         * @type {Object}
+         */
+        this.data = data || null;
+    }
+
+    /**
+     * 添加子节点
+     * @param {module:echarts/data/Tree~TreeNode} child
+     */
+    TreeNode.prototype.add = function (child) {
+        var children = this.children;
+        if (child.parent === this) {
+            return;
+        }
+
+        children.push(child);
+        child.parent = this;
+    };
+
+    /**
+     * 移除子节点
+     * @param {module:echarts/data/Tree~TreeNode} child
+     */
+    TreeNode.prototype.remove = function (child) {
+        var children = this.children;
+        var idx = zrUtil.indexOf(children, child);
+        if (idx >= 0) {
+            children.splice(idx, 1);
+            child.parent = null;
+        }
+    };
+
+    /**
+     * 遍历当前节点及其所有子节点
+     * @param  {Function} cb
+     * @param  {Object}   [context]
+     */
+    TreeNode.prototype.traverse = function (cb, context) {
+        cb.call(context, this);
+
+        for (var i = 0; i < this.children.length; i++) {
+            this.children[i].traverse(cb, context);
+        }
+    };
+
+    /**
+     * 更新当前树及所有子树的高度和深度
+     * @param  {number} depth
+     */
+    TreeNode.prototype.updateDepthAndHeight = function (depth) {
+        var height = 0;
+        this.depth = depth;
+        for (var i = 0; i < this.children.length; i++) {
+            var child = this.children[i];
+            child.updateDepthAndHeight(depth + 1);
+            if (child.height > height) {
+                height = child.height;
+            }
+        }
+        this.height = height + 1;
+    };
+
+    /**
+     * @param  {string} id
+     * @return module:echarts/data/Tree~TreeNode
+     */
+    TreeNode.prototype.getNodeById = function (id) {
+        if (this.id === id) {
+            return this;
+        }
+        for (var i = 0; i < this.children.length; i++) {
+            var res = this.children[i].getNodeById(id);
+            if (res) {
+                return res;
+            }
+        }
+    };
+
+    /**
+     * @constructor
+     * @alias module:echarts/data/Tree
+     * @param {string} id
+     */
+    function Tree(id) {
+        /**
+         * @type {module:echarts/data/Tree~TreeNode}
+         */
+        this.root = new TreeNode(id);
+    }
+
+    /**
+     * 遍历树的所有子节点
+     * @param  {Function} cb
+     * @param  {Object}   [context]
+     */
+    Tree.prototype.traverse = function(cb, context) {
+        this.root.traverse(cb, context);
+    };
+
+    /**
+     * 生成子树
+     * @param  {string} id 子树根节点 id
+     * @return {module:echarts/data/Tree}
+     */
+    Tree.prototype.getSubTree = function(id) {
+        var root = this.getNodeById(id);
+        if (root) {
+            var tree = new Tree(root.id);
+            tree.root = root;
+            return tree;
+        }
+    };
+
+    /**
+     * @param  {string} id
+     * @return module:echarts/data/Tree~TreeNode
+     */
+    Tree.prototype.getNodeById = function (id) {
+        return this.root.getNodeById(id);
+    };
+
+
+    /**
+     * 从 option 里的 data 数据构建树
+     * @param {string} id
+     * @param {Array.<Object>} data
+     * @return module:echarts/data/Tree
+     */
+    Tree.fromOptionData = function (id, data) {
+        var tree = new Tree(id);
+        var rootNode = tree.root;
+        // Root node
+        rootNode.data = {
+            name: id,
+            children: data
+        };
+
+        function buildHierarchy(dataNode, parentNode) {
+            var node = new TreeNode(dataNode.name, dataNode);
+            parentNode.add(node);
+            // 遍历添加子节点
+            var children = dataNode.children;
+            if (children) {
+                for (var i = 0; i < children.length; i++) {
+                    buildHierarchy(children[i], node);
+                }
+            }
+        }
+
+        for (var i = 0; i < data.length; i++) {
+            buildHierarchy(data[i], rootNode);
+        }
+
+        tree.root.updateDepthAndHeight(0);
+
+        return tree;
+    };
+
+    // TODO
+    Tree.fromGraph = function (graph) {
+
+        function buildHierarchy(root) {
+            var graphNode = graph.getNodeById(root.id);
+            for (var i = 0; i < graphNode.outEdges.length; i++) {
+                var edge = graphNode.outEdges[i];
+                var childTreeNode = treeNodesMap[edge.node2.id];
+                root.children.push(childTreeNode);
+                buildHierarchy(childTreeNode);
+            }
+        }
+
+        var treeMap = {};
+        var treeNodesMap = {};
+        for (var i = 0; i < graph.nodes.length; i++) {
+            var node = graph.nodes[i];
+            var treeNode;
+            if (node.inDegree() === 0) {
+                treeMap[node.id] = new Tree(node.id);
+                treeNode = treeMap[node.id].root;
+            } else {
+                treeNode = new TreeNode(node.id);
+            }
+
+            treeNode.data = node.data;
+
+            treeNodesMap[node.id] = treeNode;
+        }
+        var treeList = [];
+        for (var id in treeMap) {
+            buildHierarchy(treeMap[id].root);
+            treeMap[id].root.updateDepthAndHeight(0);
+            treeList.push(treeMap[id]);
+        }
+        return treeList;
+    };
+
+    return Tree;
+});

+ 79 - 0
src/main/webapp/static/echarts-2.2.7/src/data/quickSelect.js

@@ -0,0 +1,79 @@
+/**
+ * Quick select n-th element in an array.
+ *
+ * Note: it will change the elements placement in array.
+ *
+ * @module echarts/data/quickSelect
+ * @author Yi Shen(https://github.com/pissang)
+ */
+define(function (require) {
+
+    function defaultCompareFunc(a, b) {
+        return a - b;
+    }
+
+    function swapElement(list, idx0, idx1) {
+        var tmp = list[idx0];
+        list[idx0] = list[idx1];
+        list[idx1] = tmp;
+    }
+
+    function select(list, left, right, nth, compareFunc) {
+        var pivotIdx = left;
+        while (right > left) {
+            var pivotIdx = Math.round((right + left) / 2);
+            var pivotValue = list[pivotIdx];
+            // Swap pivot to the end
+            swapElement(list, pivotIdx, right);
+            pivotIdx = left;
+            for (var i = left; i <= right - 1; i++) {
+                if (compareFunc(pivotValue, list[i]) >= 0) {
+                    swapElement(list, i, pivotIdx);
+                    pivotIdx++;
+                }
+            }
+            swapElement(list, right, pivotIdx);
+
+            if (pivotIdx === nth) {
+                return pivotIdx;
+            } else if (pivotIdx < nth) {
+                left = pivotIdx + 1;
+            } else {
+                right = pivotIdx - 1;
+            }
+        }
+        // Left == right
+        return left;
+    }
+
+    /**
+     * @alias module:echarts/data/quickSelect
+     * @param {Array} list
+     * @param {number} [left]
+     * @param {number} [right]
+     * @param {number} nth
+     * @param {Function} [compareFunc]
+     * @example
+     *     var quickSelect = require('echarts/data/quickSelect');
+     *     var list = [5, 2, 1, 4, 3]
+     *     quickSelect(list, 3);
+     *     quickSelect(list, 0, 3, 1, function (a, b) {return a - b});
+     *
+     * @return {number}
+     */
+    function quickSelect(list, left, right, nth, compareFunc) {
+        if (arguments.length <= 3) {
+            nth = left;
+            if (arguments.length == 2) {
+                compareFunc = defaultCompareFunc;
+            } else {
+                compareFunc = right;
+            }
+            left = 0;
+            right = list.length - 1;
+        }
+        return select(list, left, right, nth, compareFunc);
+    }
+    
+    return quickSelect;
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1759 - 0
src/main/webapp/static/echarts-2.2.7/src/echarts.js


+ 172 - 0
src/main/webapp/static/echarts-2.2.7/src/layer/heatmap.js

@@ -0,0 +1,172 @@
+/**
+ * @file defines echarts Heatmap Chart
+ * @author Ovilia (me@zhangwenli.com)
+ * Inspired by https://github.com/mourner/simpleheat
+ *
+ * @module
+ */
+define(function (require) {
+    var defaultOptions = {
+        blurSize: 30,
+
+        // gradientColors is either shaped of ['blue', 'cyan', 'lime', 'yellow', 'red']
+        // or [{
+        //    offset: 0.2,
+        //    color: 'blue'
+        // }, {
+        //    offset 0.8,
+        //    color: 'cyan'
+        // }]
+        gradientColors: ['blue', 'cyan', 'lime', 'yellow', 'red'],
+        minAlpha: 0.05,
+        valueScale: 1,
+        opacity: 1
+    };
+
+    var BRUSH_SIZE = 20;
+    var GRADIENT_LEVELS = 256;
+
+    /**
+     * Heatmap Chart
+     *
+     * @class
+     * @param {Object} opt options
+     */
+    function Heatmap(opt) {
+        this.option = opt;
+        if (opt) {
+            for (var i in defaultOptions) {
+                if (opt[i] !== undefined) {
+                    this.option[i] = opt[i];
+                } else {
+                    this.option[i] = defaultOptions[i];
+                }
+            }
+        } else {
+            this.option = defaultOptions;
+        }
+    }
+
+    Heatmap.prototype = {
+        /**
+         * Renders Heatmap and returns the rendered canvas
+         * @param {Array} [x, y, value] array of data
+         * @param {number} canvas width
+         * @param {number} canvas height
+         * @return {Object} rendered canvas
+         */
+        getCanvas: function(data, width, height) {
+            var brush = this._getBrush();
+            var gradient = this._getGradient();
+            var r = BRUSH_SIZE + this.option.blurSize;
+
+            var canvas = document.createElement('canvas');
+            canvas.width = width;
+            canvas.height = height;
+            var ctx = canvas.getContext('2d');
+
+            var len = data.length;
+            for (var i = 0; i < len; ++i) {
+                var p = data[i];
+                var x = p[0];
+                var y = p[1];
+                var value = p[2];
+
+                // calculate alpha using value
+                var alpha = Math.min(1, Math.max(value * this.option.valueScale
+                    || this.option.minAlpha, this.option.minAlpha));
+
+                // draw with the circle brush with alpha
+                ctx.globalAlpha = alpha;
+                ctx.drawImage(brush, x - r, y - r);
+            }
+
+            // colorize the canvas using alpha value and set with gradient
+            var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+            var pixels = imageData.data;
+            var len = pixels.length / 4;
+            while(len--) {
+                var id = len * 4 + 3;
+                var alpha = pixels[id] / 256;
+                var colorOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1));
+                pixels[id - 3] = gradient[colorOffset * 4];     // red
+                pixels[id - 2] = gradient[colorOffset * 4 + 1]; // green
+                pixels[id - 1] = gradient[colorOffset * 4 + 2]; // blue
+                pixels[id] *= this.option.opacity;              // alpha
+            }
+            ctx.putImageData(imageData, 0, 0);
+
+            return canvas;
+        },
+
+        /**
+         * get canvas of a black circle brush used for canvas to draw later
+         * @private
+         * @returns {Object} circle brush canvas
+         */
+        _getBrush: function() {
+            if (!this._brushCanvas) {
+                this._brushCanvas = document.createElement('canvas');
+
+                // set brush size
+                var r = BRUSH_SIZE + this.option.blurSize;
+                var d = r * 2;
+                this._brushCanvas.width = d;
+                this._brushCanvas.height = d;
+
+                var ctx = this._brushCanvas.getContext('2d');
+
+                // in order to render shadow without the distinct circle,
+                // draw the distinct circle in an invisible place,
+                // and use shadowOffset to draw shadow in the center of the canvas
+                ctx.shadowOffsetX = d;
+                ctx.shadowBlur = this.option.blurSize;
+                // draw the shadow in black, and use alpha and shadow blur to generate
+                // color in color map
+                ctx.shadowColor = 'black';
+
+                // draw circle in the left to the canvas
+                ctx.beginPath();
+                ctx.arc(-r, r, BRUSH_SIZE, 0, Math.PI * 2, true);
+                ctx.closePath();
+                ctx.fill();
+            }
+            return this._brushCanvas;
+        },
+
+        /**
+         * get gradient color map
+         * @private
+         * @returns {array} gradient color pixels
+         */
+        _getGradient: function() {
+            if (!this._gradientPixels) {
+                var levels = GRADIENT_LEVELS;
+                var canvas = document.createElement('canvas');
+                canvas.width = 1;
+                canvas.height = levels;
+                var ctx = canvas.getContext('2d');
+                
+                // add color to gradient stops
+                var gradient = ctx.createLinearGradient(0, 0, 0, levels);
+                var len = this.option.gradientColors.length;
+                for (var i = 0; i < len; ++i) {
+                    if (typeof this.option.gradientColors[i] === 'string') {
+                        gradient.addColorStop((i + 1) / len,
+                            this.option.gradientColors[i]);
+                    } else {
+                        gradient.addColorStop(this.option.gradientColors[i].offset,
+                            this.option.gradientColors[i].color);
+                    }
+                }
+
+                ctx.fillStyle = gradient;
+                ctx.fillRect(0, 0, 1, levels);
+                this._gradientPixels = ctx.getImageData(0, 0, 1, levels).data;
+            }
+            return this._gradientPixels;
+        }
+    };
+
+    return Heatmap;
+});

+ 145 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/Chord.js

@@ -0,0 +1,145 @@
+/**
+ * Chord layout
+ * @module echarts/layout/Chord
+ * @author pissang(http://github.com/pissang)
+ */
+define(function (require) {
+
+    var ChordLayout = function (opts) {
+
+        opts = opts || {};
+
+        /**
+         * 是否排序组(即图的节点), 可以是`ascending`, `descending`, 或者为空不排序
+         * @type {string}
+         */
+        this.sort = opts.sort || null;
+        /**
+         * 是否排序子组(即图中每个节点的邻接边), 可以是`ascending`, `descending`, 或者为空不排序
+         * @type {string}
+         */
+        this.sortSub = opts.sortSub || null;
+
+        this.padding = 0.05;
+
+        this.startAngle = opts.startAngle || 0;
+        this.clockWise = opts.clockWise == null ? false : opts.clockWise;
+
+        this.center = opts.center || [0, 0];
+
+        this.directed = true;
+    };
+
+    /**
+     * 对指定的一个或多个 Graph 运行 chord 布局
+     * 可以有多个 Graph, 后面几个 Graph 的节点是第一个 Graph 的节点的子集(ID一一对应)
+     *
+     * 布局结果保存在第一个 Graph 的每个节点的 layout.startAngle 和 layout.endAngle.
+     * 以及每个图的边的 layout.startAngle 和 layout.endAngle
+     * 
+     * @param {Array.<module:echarts/data/Graph>|module:echarts/data/Graph} graphs
+     */
+    ChordLayout.prototype.run = function (graphs) {
+        if (!(graphs instanceof Array)) {
+            graphs = [graphs];
+        }
+
+        var gl = graphs.length;
+        if (!gl) {
+            return;
+        }
+        var graph0 = graphs[0];
+        var nl = graph0.nodes.length;
+
+        var groups = [];
+        var sumSize = 0;
+
+        // 使用第一个 graph 的节点
+        for (var i = 0; i < nl; i++) {
+            var g0node = graph0.nodes[i];
+            var group = {
+                size: 0,
+                subGroups: [],
+                node: g0node
+            };
+            groups.push(group);
+
+            var sumWeight = 0;
+
+            // 合并所有 Graph 的 边
+            for (var k = 0; k < graphs.length; k++) {
+                var graph = graphs[k];
+                var node = graph.getNodeById(g0node.id);
+                // 节点可能没有值被过滤掉了
+                if (!node) {
+                    continue;
+                }
+                group.size += node.layout.size;
+                // PENDING
+                var edges = this.directed ? node.outEdges : node.edges;
+                for (var j = 0; j < edges.length; j++) {
+                    var e = edges[j];
+                    var w = e.layout.weight;
+                    group.subGroups.push({
+                        weight: w,
+                        edge: e,
+                        graph: graph
+                    });
+                    sumWeight += w;
+                }
+            }
+            sumSize += group.size;
+
+            // Sum sub group weights to group size
+            var multiplier = group.size / sumWeight;
+            for (var j = 0; j < group.subGroups.length; j++) {
+                group.subGroups[j].weight *= multiplier;
+            }
+
+            if (this.sortSub === 'ascending') {
+                group.subGroups.sort(compareSubGroups);
+            }
+            else if (this.sort === 'descending') {
+                group.subGroups.sort(compareSubGroups);
+                group.subGroups.reverse();
+            }
+        }
+
+        if (this.sort === 'ascending') {
+            groups.sort(compareGroups);
+        }
+        else if (this.sort === 'descending') {
+            groups.sort(compareGroups);
+            groups.reverse();
+        }
+
+        var multiplier = (Math.PI * 2 - this.padding * nl) / sumSize;
+        var angle = this.startAngle;
+        var sign = this.clockWise ? 1 : -1;
+        // Calculate angles
+        for (var i = 0; i < nl; i++) {
+            var group = groups[i];
+            group.node.layout.startAngle = angle;
+            group.node.layout.endAngle = angle + sign * group.size * multiplier;
+
+            group.node.layout.subGroups = [];
+            for (var j = 0; j < group.subGroups.length; j++) {
+                var subGroup = group.subGroups[j];
+                subGroup.edge.layout.startAngle = angle;
+                angle += sign * subGroup.weight * multiplier;
+                subGroup.edge.layout.endAngle = angle;
+            }
+            angle = group.node.layout.endAngle + sign * this.padding;
+        }
+    };
+
+    var compareSubGroups = function (a, b) {
+        return a.weight - b.weight;
+    };
+
+    var compareGroups = function (a, b) {
+        return a.size - b.size;
+    };
+
+    return ChordLayout;
+});

+ 415 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/EdgeBundling.js

@@ -0,0 +1,415 @@
+/**
+ * Edge bundling laytout
+ *
+ * Use MINGLE algorithm
+ * Multilevel agglomerative edge bundling for visualizing large graphs
+ *
+ * @module echarts/layout/EdgeBundling
+ */
+define(function (require) {
+
+    var KDTree = require('../data/KDTree');
+    var vec2 = require('zrender/tool/vector');
+    var v2Create = vec2.create;
+    var v2DistSquare = vec2.distSquare;
+    var v2Dist = vec2.dist;
+    var v2Copy = vec2.copy;
+    var v2Clone = vec2.clone;
+
+    function squaredDistance(a, b) {
+        a = a.array;
+        b = b.array;
+
+        var x = b[0] - a[0];
+        var y = b[1] - a[1];
+        var z = b[2] - a[2];
+        var w = b[3] - a[3];
+
+        return x * x + y * y + z * z + w * w;
+    }
+
+    function CoarsenedEdge(group) {
+        this.points = [
+            group.mp0, group.mp1
+        ];
+
+        this.group = group;
+    }
+
+    function Edge(edge) {
+        var points = edge.points;
+        // Sort on y
+        if (
+            points[0][1] < points[1][1]
+            // If coarsened edge is flipped, the final composition of meet point
+            // will be unordered
+            || edge instanceof CoarsenedEdge
+        ) {
+            this.array = [points[0][0], points[0][1], points[1][0], points[1][1]];
+            this._startPoint = points[0];
+            this._endPoint = points[1];
+        }
+        else {
+            this.array = [points[1][0], points[1][1], points[0][0], points[0][1]];
+            this._startPoint = points[1];
+            this._endPoint = points[0];
+        }
+
+        this.ink = v2Dist(points[0], points[1]);
+
+        this.edge = edge;
+
+        this.group = null;
+    }
+
+    Edge.prototype.getStartPoint = function () {
+        return this._startPoint;
+    };
+
+    Edge.prototype.getEndPoint = function () {
+        return this._endPoint;
+    };
+
+    function BundledEdgeGroup() {
+
+        this.edgeList = [];
+
+        this.mp0 = v2Create();
+        this.mp1 = v2Create();
+
+        this.ink = 0;
+    }
+
+    BundledEdgeGroup.prototype.addEdge = function (edge) {
+        edge.group = this;
+        this.edgeList.push(edge);
+    };
+
+    BundledEdgeGroup.prototype.removeEdge = function (edge) {
+        edge.group = null;
+        this.edgeList.splice(this.edgeList.indexOf(edge), 1);
+    };
+
+    /**
+     * @constructor
+     * @alias module:echarts/layout/EdgeBundling
+     */
+    function EdgeBundling() {
+        this.maxNearestEdge = 6;
+        this.maxTurningAngle = Math.PI / 4;
+        this.maxIteration = 20;
+    }
+
+    EdgeBundling.prototype = {
+        
+        constructor: EdgeBundling,
+
+        run: function (rawEdges) {
+            var res = this._iterate(rawEdges);
+            var nIterate = 0;
+            while (nIterate++ < this.maxIteration) {
+                var coarsenedEdges = [];
+                for (var i = 0; i < res.groups.length; i++) {
+                    coarsenedEdges.push(new CoarsenedEdge(res.groups[i]));
+                }
+                var newRes = this._iterate(coarsenedEdges);
+                if (newRes.savedInk <= 0) {
+                    break;
+                } else {
+                    res = newRes;
+                }
+            }
+
+            // Get new edges
+            var newEdges = [];
+
+            function pointApproxEqual(p0, p1) {
+                // Use Float32Array may affect the precision
+                return v2DistSquare(p0, p1) < 1e-10;
+            }
+            // Clone all points to make sure all points in edge will not reference to the same array
+            // And clean the duplicate points
+            function cleanEdgePoints(edgePoints, rawEdgePoints) {
+                var res = [];
+                var off = 0;
+                for (var i = 0; i < edgePoints.length; i++) {
+                    if (! (off > 0 && pointApproxEqual(edgePoints[i], res[off - 1]))) {
+                        res[off++] = v2Clone(edgePoints[i]);
+                    }
+                }
+                // Edge has been reversed
+                if (rawEdgePoints[0] && !pointApproxEqual(res[0], rawEdgePoints[0])) {
+                    res = res.reverse();
+                }
+                return res;
+            }
+
+            var buildNewEdges = function (groups, fromEdgePoints) {
+                var newEdgePoints;
+                for (var i = 0; i < groups.length; i++) {
+                    var group = groups[i];
+                    if (
+                        group.edgeList[0]
+                        && (group.edgeList[0].edge instanceof CoarsenedEdge)
+                    ) {
+                        var newGroups = [];
+                        for (var j = 0; j < group.edgeList.length; j++) {
+                            newGroups.push(group.edgeList[j].edge.group);
+                        }
+                        if (! fromEdgePoints) {
+                            newEdgePoints = [];
+                        } else {
+                            newEdgePoints = fromEdgePoints.slice();
+                        }
+                        newEdgePoints.unshift(group.mp0);
+                        newEdgePoints.push(group.mp1);
+                        buildNewEdges(newGroups, newEdgePoints);
+                    } else {
+                        // console.log(group.edgeList.length);
+                        for (var j = 0; j < group.edgeList.length; j++) {
+                            var edge = group.edgeList[j];
+                            if (! fromEdgePoints) {
+                                newEdgePoints = [];
+                            } else {
+                                newEdgePoints = fromEdgePoints.slice();
+                            }
+                            newEdgePoints.unshift(group.mp0);
+                            newEdgePoints.push(group.mp1);
+                            newEdgePoints.unshift(edge.getStartPoint());
+                            newEdgePoints.push(edge.getEndPoint());
+                            newEdges.push({
+                                points: cleanEdgePoints(newEdgePoints, edge.edge.points),
+                                rawEdge: edge.edge
+                            });
+                        }
+                    }
+                }
+            };
+
+            buildNewEdges(res.groups);
+
+            return newEdges;
+        },
+
+        _iterate: function (rawEdges) {
+            var edges = [];
+            var groups = [];
+            var totalSavedInk = 0;
+            for (var i = 0; i < rawEdges.length; i++) {
+                var edge = new Edge(rawEdges[i]);
+                edges.push(edge);
+            }
+
+            var tree = new KDTree(edges, 4);
+
+            var nearests = [];
+
+            var _mp0 = v2Create();
+            var _mp1 = v2Create();
+            var _newGroupInk = 0;
+            var mp0 = v2Create();
+            var mp1 = v2Create();
+            var newGroupInk = 0;
+            for (var i = 0; i < edges.length; i++) {
+                var edge = edges[i];
+                if (edge.group) {
+                    // Edge have been groupped
+                    continue;
+                }
+                tree.nearestN(
+                    edge, this.maxNearestEdge,
+                    squaredDistance, nearests
+                );
+                var maxSavedInk = 0;
+                var mostSavingInkEdge = null;
+                var lastCheckedGroup = null;
+                for (var j = 0; j < nearests.length; j++) {
+                    var nearest = nearests[j];
+                    var savedInk = 0;
+                    if (nearest.group) {
+                        if (nearest.group !== lastCheckedGroup) {
+                            lastCheckedGroup = nearest.group;
+                            _newGroupInk = this._calculateGroupEdgeInk(
+                                nearest.group, edge, _mp0, _mp1
+                            );
+                            savedInk = nearest.group.ink + edge.ink - _newGroupInk;
+                        }
+                    }
+                    else {
+                        _newGroupInk = this._calculateEdgeEdgeInk(
+                            edge, nearest, _mp0, _mp1
+                        );
+                        savedInk = nearest.ink + edge.ink - _newGroupInk;
+                    }
+                    if (savedInk > maxSavedInk) {
+                        maxSavedInk = savedInk;
+                        mostSavingInkEdge = nearest;
+                        v2Copy(mp1, _mp1);
+                        v2Copy(mp0, _mp0);
+                        newGroupInk = _newGroupInk;
+                    }
+                }
+                if (mostSavingInkEdge) {
+                    totalSavedInk += maxSavedInk;
+                    var group;
+                    if (! mostSavingInkEdge.group) {
+                        group = new BundledEdgeGroup();
+                        groups.push(group);
+                        group.addEdge(mostSavingInkEdge);
+                    }
+                    group = mostSavingInkEdge.group;
+                    // Use the meet point and group ink calculated before
+                    v2Copy(group.mp0, mp0);
+                    v2Copy(group.mp1, mp1);
+                    group.ink = newGroupInk;
+                    mostSavingInkEdge.group.addEdge(edge);
+                }
+                else {
+                    var group = new BundledEdgeGroup();
+                    groups.push(group);
+                    v2Copy(group.mp0, edge.getStartPoint());
+                    v2Copy(group.mp1, edge.getEndPoint());
+                    group.ink = edge.ink;
+                    group.addEdge(edge);
+                }
+            }
+
+            return {
+                groups: groups,
+                edges: edges,
+                savedInk: totalSavedInk
+            };
+        },
+
+        _calculateEdgeEdgeInk: (function () {
+            var startPointSet = [];
+            var endPointSet = [];
+            return function (e0, e1, mp0, mp1) {
+                startPointSet[0] = e0.getStartPoint();
+                startPointSet[1] = e1.getStartPoint();
+                endPointSet[0] = e0.getEndPoint();
+                endPointSet[1] = e1.getEndPoint();
+
+                this._calculateMeetPoints(
+                    startPointSet, endPointSet, mp0, mp1
+                );
+                var ink = v2Dist(startPointSet[0], mp0)
+                    + v2Dist(mp0, mp1)
+                    + v2Dist(mp1, endPointSet[0])
+                    + v2Dist(startPointSet[1], mp0)
+                    + v2Dist(mp1, endPointSet[1]);
+
+                return ink;
+            };
+        })(),
+
+        _calculateGroupEdgeInk: function (group, edgeTryAdd, mp0, mp1) {
+            var startPointSet = [];
+            var endPointSet = [];
+            for (var i = 0; i < group.edgeList.length; i++) {
+                var edge = group.edgeList[i];
+                startPointSet.push(edge.getStartPoint());
+                endPointSet.push(edge.getEndPoint());
+            }
+            startPointSet.push(edgeTryAdd.getStartPoint());
+            endPointSet.push(edgeTryAdd.getEndPoint());
+
+            this._calculateMeetPoints(
+                startPointSet, endPointSet, mp0, mp1
+            );
+
+            var ink = v2Dist(mp0, mp1);
+            for (var i = 0; i < startPointSet.length; i++) {
+                ink += v2Dist(startPointSet[i], mp0)
+                    + v2Dist(endPointSet[i], mp1);
+            }
+
+            return ink;
+        },
+
+        /**
+         * Calculating the meet points
+         * @method
+         * @param {Array} startPointSet Start points set of bundled edges
+         * @param {Array} endPointSet End points set of bundled edges
+         * @param {Array.<number>} mp0 Output meet point 0
+         * @param {Array.<number>} mp1 Output meet point 1
+         */
+        _calculateMeetPoints: (function () {
+            var cp0 = v2Create();
+            var cp1 = v2Create();
+            return function (startPointSet, endPointSet, mp0, mp1) {
+                vec2.set(cp0, 0, 0);
+                vec2.set(cp1, 0, 0);
+                var len = startPointSet.length;
+                // Calculate the centroid of start points set
+                for (var i = 0; i < len; i++) {
+                    vec2.add(cp0, cp0, startPointSet[i]);
+                }
+                vec2.scale(cp0, cp0, 1 / len);
+
+                // Calculate the centroid of end points set
+                len = endPointSet.length;
+                for (var i = 0; i < len; i++) {
+                    vec2.add(cp1, cp1, endPointSet[i]);
+                }
+                vec2.scale(cp1, cp1, 1 / len);
+
+                this._limitTurningAngle(
+                    startPointSet, cp0, cp1, mp0
+                );
+                this._limitTurningAngle(
+                    endPointSet, cp1, cp0, mp1
+                );
+            };
+        })(),
+
+        _limitTurningAngle: (function () {
+            var v10 = v2Create();
+            var vTmp = v2Create();
+            var project = v2Create();
+            var tmpOut = v2Create();
+            return function (pointSet, p0, p1, out) {
+                // Limit the max turning angle
+                var maxTurningAngleCos = Math.cos(this.maxTurningAngle);
+                var maxTurningAngleTan = Math.tan(this.maxTurningAngle);
+
+                vec2.sub(v10, p0, p1);
+                vec2.normalize(v10, v10);
+
+                // Simply copy the centroid point if no need to turn the angle
+                vec2.copy(out, p0);
+
+                var maxMovement = 0;
+                for (var i = 0; i < pointSet.length; i++) {
+                    var p = pointSet[i];
+                    vec2.sub(vTmp, p, p0);
+                    var len = vec2.len(vTmp);
+                    vec2.scale(vTmp, vTmp, 1 / len);
+                    var turningAngleCos = vec2.dot(vTmp, v10);
+                    // Turning angle is to large
+                    if (turningAngleCos < maxTurningAngleCos) {
+                        // Calculat p's project point on vector p1-p0 
+                        // and distance to the vector
+                        vec2.scaleAndAdd(
+                            project, p0, v10, len * turningAngleCos
+                        );
+                        var distance = v2Dist(project, p);
+
+                        // Use the max turning angle to calculate the new meet point
+                        var d = distance / maxTurningAngleTan;
+                        vec2.scaleAndAdd(tmpOut, project, v10, -d);
+
+                        var movement = v2DistSquare(tmpOut, p0);
+                        if (movement > maxMovement) {
+                            maxMovement = movement;
+                            vec2.copy(out, tmpOut);
+                        }
+                    }
+                }
+            };
+        })()
+    };
+
+    return EdgeBundling;
+});

+ 248 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/Force.js

@@ -0,0 +1,248 @@
+/**
+ * 力导向布局
+ * @module echarts/layout/Force
+ * @author pissang(http://github.com/pissang)
+ */
+define(function(require) {
+
+    var ForceLayoutWorker = require('./forceLayoutWorker');
+    var vec2 = require('zrender/tool/vector');
+
+    var requestAnimationFrame = window.requestAnimationFrame
+                                || window.msRequestAnimationFrame
+                                || window.mozRequestAnimationFrame
+                                || window.webkitRequestAnimationFrame
+                                || function (func) {setTimeout(func, 16);};
+    var ArrayCtor = typeof(Float32Array) == 'undefined' ? Array : Float32Array;
+
+    var workerUrl;
+
+    // function getToken() {
+    //     return Math.round((new Date()).getTime() / 100) % 10000000;
+    // }
+
+    function createWorkerUrl() {
+        if (
+            typeof(Worker) !== 'undefined' &&
+            typeof(Blob) !== 'undefined'
+        ) {
+            try {
+                var blob = new Blob([ForceLayoutWorker.getWorkerCode()]);
+                workerUrl = window.URL.createObjectURL(blob);   
+            }
+            catch (e) {
+                workerUrl = '';
+            }
+        }
+
+        return workerUrl;
+    }
+
+    var ForceLayout = function(opts) {
+
+        if (typeof(workerUrl) === 'undefined') {
+            createWorkerUrl();
+        }
+        opts = opts || {};
+        // 配置项
+        this.width = opts.width || 500;
+        this.height = opts.height || 500;
+        this.center = opts.center || [this.width / 2, this.height / 2];
+        this.ratioScaling = opts.ratioScaling || false;
+        this.scaling = opts.scaling || 1;
+        this.gravity = typeof(opts.gravity) !== 'undefined'
+                        ? opts.gravity : 1;
+        this.large = opts.large || false;
+        this.preventNodeOverlap = opts.preventNodeOverlap || false;
+        this.preventNodeEdgeOverlap = opts.preventNodeEdgeOverlap || false;
+        this.maxSpeedIncrease = opts.maxSpeedIncrease || 1;
+
+        this.onupdate = opts.onupdate || function () {};
+        this.temperature = opts.temperature || 1;
+        this.coolDown = opts.coolDown || 0.99;
+
+        this._layout = null;
+        this._layoutWorker = null;
+
+        var self = this;
+        var _$onupdate = this._$onupdate;
+        this._$onupdate = function(e) {
+            _$onupdate.call(self, e);
+        };
+    };
+
+    ForceLayout.prototype.updateConfig = function () {
+        var width = this.width;
+        var height = this.height;
+        var size = Math.min(width, height);
+
+        var config = {
+            center: this.center,
+            width: this.ratioScaling ? width : size,
+            height: this.ratioScaling ? height : size,
+            scaling: this.scaling || 1.0,
+            gravity: this.gravity || 1.0,
+            barnesHutOptimize: this.large,
+            preventNodeOverlap: this.preventNodeOverlap,
+            preventNodeEdgeOverlap: this.preventNodeEdgeOverlap,
+            
+            maxSpeedIncrease: this.maxSpeedIncrease
+        };
+
+        if (this._layoutWorker) {
+            this._layoutWorker.postMessage({
+                cmd: 'updateConfig',
+                config: config
+            });
+        }
+        else {
+            for (var name in config) {
+                this._layout[name] = config[name];
+            }
+        }
+    };
+
+    ForceLayout.prototype.init = function (graph, useWorker) {
+        if (this._layoutWorker) {
+            this._layoutWorker.terminate();
+            this._layoutWorker = null;
+        }
+        if (workerUrl && useWorker) {
+            try {
+                if (!this._layoutWorker) {
+                    this._layoutWorker = new Worker(workerUrl);
+                    this._layoutWorker.onmessage = this._$onupdate;
+                }
+                this._layout = null;
+            }
+            catch (e) {    // IE10-11 will throw security error when using blog url
+                this._layoutWorker = null;
+                if (!this._layout) {
+                    this._layout = new ForceLayoutWorker();
+                }
+            }
+        }
+        else {
+            if (!this._layout) {
+                this._layout = new ForceLayoutWorker();
+            }
+        }
+
+        this.temperature = 1;
+
+        this.graph = graph;
+
+        // 节点数据
+        var len = graph.nodes.length;
+        var positionArr = new ArrayCtor(len * 2);
+        var massArr = new ArrayCtor(len);
+        var sizeArr = new ArrayCtor(len);
+
+        for (var i = 0; i < len; i++) {
+            var n = graph.nodes[i];
+            positionArr[i * 2] = n.layout.position[0];
+            positionArr[i * 2 + 1] = n.layout.position[1];
+            massArr[i] = typeof(n.layout.mass) === 'undefined'
+                ? 1 : n.layout.mass;
+            sizeArr[i] = typeof(n.layout.size) === 'undefined'
+                ? 1 : n.layout.size;
+
+            n.layout.__index = i;
+        }
+        // 边数据
+        len = graph.edges.length;
+        var edgeArr = new ArrayCtor(len * 2);
+        var edgeWeightArr = new ArrayCtor(len);
+        for (var i = 0; i < len; i++) {
+            var edge = graph.edges[i];
+            edgeArr[i * 2] = edge.node1.layout.__index;
+            edgeArr[i * 2 + 1] = edge.node2.layout.__index;
+            edgeWeightArr[i] = edge.layout.weight || 1;
+        }
+
+        if (this._layoutWorker) {
+
+            this._layoutWorker.postMessage({
+                cmd: 'init',
+                nodesPosition: positionArr,
+                nodesMass: massArr,
+                nodesSize: sizeArr,
+                edges: edgeArr,
+                edgesWeight: edgeWeightArr
+            });
+        }
+        else {
+            this._layout.initNodes(positionArr, massArr, sizeArr);
+            this._layout.initEdges(edgeArr, edgeWeightArr);   
+        }
+
+        this.updateConfig();
+    };
+
+    ForceLayout.prototype.step = function (steps) {
+        var nodes = this.graph.nodes;
+        if (this._layoutWorker) {
+            // Sync back
+            var positionArr = new ArrayCtor(nodes.length * 2);
+            for (var i = 0; i < nodes.length; i++) {
+                var n = nodes[i];
+                positionArr[i * 2] = n.layout.position[0];
+                positionArr[i * 2 + 1] = n.layout.position[1];
+            }
+            this._layoutWorker.postMessage(positionArr.buffer, [positionArr.buffer]);
+
+            this._layoutWorker.postMessage({
+                cmd: 'update',
+                steps: steps,
+                temperature: this.temperature,
+                coolDown: this.coolDown
+            });
+            for (var i = 0; i < steps; i++) {
+                this.temperature *= this.coolDown;
+            }
+        }
+        else {
+            
+            requestAnimationFrame(this._$onupdate);
+
+            for (var i = 0; i < nodes.length; i++) {
+                var n = nodes[i];
+                vec2.copy(this._layout.nodes[i].position, n.layout.position);
+            }
+            for (var i = 0; i < steps; i++) {
+                this._layout.temperature = this.temperature;
+                this._layout.update();
+                this.temperature *= this.coolDown;
+            }
+        }
+    };
+
+    ForceLayout.prototype._$onupdate = function (e) {
+        if (this._layoutWorker) {
+            var positionArr = new Float32Array(e.data);
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                var n = this.graph.nodes[i];
+                n.layout.position[0] = positionArr[i * 2];
+                n.layout.position[1] = positionArr[i * 2 + 1];
+            }
+            this.onupdate && this.onupdate();
+        }
+        else if (this._layout) {
+            for (var i = 0; i < this.graph.nodes.length; i++) {
+                var n = this.graph.nodes[i];
+                vec2.copy(n.layout.position, this._layout.nodes[i].position);
+            }
+            this.onupdate && this.onupdate();
+        }
+    };
+
+    ForceLayout.prototype.dispose = function() {
+        if (this._layoutWorker) {
+            this._layoutWorker.terminate();
+        }
+        this._layoutWorker = null;
+        this._layout = null;
+    };
+
+    return ForceLayout;
+});

+ 90 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/Tree.js

@@ -0,0 +1,90 @@
+/**
+ * Tree layout
+ * @module echarts/layout/Tree
+ * @author pissang(http://github.com/pissang)
+ */
+define(function (require) {
+
+    var vec2 = require('zrender/tool/vector');
+
+    function TreeLayout(opts) {
+
+        opts = opts || {};
+
+        this.nodePadding = opts.nodePadding || 30;
+
+        this.layerPadding = opts.layerPadding || 100;
+
+        this._layerOffsets = [];
+
+        this._layers = [];
+    }
+
+    TreeLayout.prototype.run = function (tree) {
+        this._layerOffsets.length = 0;
+        for (var i = 0; i < tree.root.height + 1; i++) {
+            this._layerOffsets[i] = 0;
+            this._layers[i] = [];
+        }
+        this._updateNodeXPosition(tree.root);
+        var root = tree.root;
+        this._updateNodeYPosition(root, 0, root.layout.height);
+    };
+
+    TreeLayout.prototype._updateNodeXPosition = function (node) {
+        var minX = Infinity;
+        var maxX = -Infinity;
+        node.layout.position = node.layout.position || vec2.create();
+        for (var i = 0; i < node.children.length; i++) {
+            var child = node.children[i];
+            this._updateNodeXPosition(child);
+            var x = child.layout.position[0];
+            if (x < minX) {
+                minX = x;
+            }
+            if (x > maxX) {
+                maxX = x;
+            }
+        }
+        if (node.children.length > 0) {
+            node.layout.position[0] = (minX + maxX) / 2;
+        } else {
+            node.layout.position[0] = 0;
+        }
+        var off = this._layerOffsets[node.depth] || 0;
+        if (off > node.layout.position[0]) {
+            var shift = off - node.layout.position[0];
+            this._shiftSubtree(node, shift);
+            for (var i = node.depth + 1; i < node.height + node.depth; i++) {
+                this._layerOffsets[i] += shift;
+            }
+        }
+        this._layerOffsets[node.depth] = node.layout.position[0] + node.layout.width + this.nodePadding;
+
+        this._layers[node.depth].push(node);
+    };
+
+    TreeLayout.prototype._shiftSubtree = function (root, offset) {
+        root.layout.position[0] += offset;
+        for (var i = 0; i < root.children.length; i++) {
+            this._shiftSubtree(root.children[i], offset);
+        }
+    };
+
+    TreeLayout.prototype._updateNodeYPosition = function (node, y, prevLayerHeight) {
+        node.layout.position[1] = y;
+        var layerHeight = 0;
+        for (var i = 0; i < node.children.length; i++) {
+            layerHeight = Math.max(node.children[i].layout.height, layerHeight);
+        }
+        var layerPadding = this.layerPadding;
+        if (typeof(layerPadding) === 'function') {
+            layerPadding = layerPadding(node.depth);
+        }
+        for (var i = 0; i < node.children.length; i++) {
+            this._updateNodeYPosition(node.children[i], y + layerPadding + prevLayerHeight, layerHeight);
+        }
+    };
+
+    return TreeLayout;
+});

+ 202 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/TreeMap.js

@@ -0,0 +1,202 @@
+/**
+ * TreeMap layout
+ * @module echarts/layout/TreeMap
+ * @author loutongbing(loutongbing@126.com)
+ */
+define(function (require) {
+
+    function TreeMapLayout(opts) {
+        /**
+         * areas 每个子矩形面积
+         * x 父矩形横坐标
+         * y 父矩形横坐标
+         * width 父矩形宽
+         * height 父矩形高
+        */
+        var row = {
+            x: opts.x,
+            y: opts.y,
+            width: opts.width,
+            height: opts.height
+        };
+
+        this.x = opts.x;
+        this.y = opts.y;
+        this.width = opts.width;
+        this.height = opts.height;
+    }
+
+    TreeMapLayout.prototype.run = function (areas) {
+        var out = [];
+
+        this._squarify(areas, {
+            x: this.x,
+            y: this.y,
+            width: this.width,
+            height: this.height
+        }, out);
+
+        return out;
+    };
+
+    TreeMapLayout.prototype._squarify = function (areas, row, out) {
+        var layoutDirection = 'VERTICAL';
+        var width = row.width;
+        var height = row.height;
+        if (row.width < row.height) {
+            layoutDirection = 'HORIZONTAL';
+            width = row.height;
+            height = row.width;
+        }
+        // 把考虑方向与位置的因素剥离出来,只考虑怎么排列,运行完毕之后再修正
+        var shapeArr = this._getShapeListInAbstractRow(
+            areas, width, height
+        );
+        // 首先换算出虚拟的x、y坐标
+        for (var i = 0; i < shapeArr.length; i++) {
+            shapeArr[i].x = 0;
+            shapeArr[i].y = 0;
+            for (var j = 0; j < i; j++) {
+                shapeArr[i].y += shapeArr[j].height;
+            }
+        }
+        var nextRow = {};
+        // 根据虚拟的shapeArr计算真实的小矩形
+        if (layoutDirection == 'VERTICAL') {
+            for (var k = 0; k < shapeArr.length; k++) {
+                out.push(
+                    {
+                        x: shapeArr[k].x + row.x,
+                        y: shapeArr[k].y + row.y,
+                        width: shapeArr[k].width,
+                        height: shapeArr[k].height
+                    }
+                );
+            }
+            nextRow = {
+                x: shapeArr[0].width + row.x,
+                y: row.y,
+                width: row.width - shapeArr[0].width,
+                height: row.height
+            };
+        }
+        else {
+            for (var l = 0; l < shapeArr.length; l++) {
+                out.push(
+                    {
+                        x: shapeArr[l].y + row.x,
+                        y: shapeArr[l].x + row.y,
+                        width: shapeArr[l].height,
+                        height: shapeArr[l].width
+                    }
+                );
+            }
+            nextRow = {
+                x: row.x,
+                y: row.y + shapeArr[0].width,  // 注意是虚拟形状下的width
+                width: row.width,
+                height: row.height - shapeArr[0].width // 注意是虚拟形状下的width
+            };
+        }
+        // 下一步的矩形数组要剔除已经填充过的矩形
+        var nextAreaArr = areas.slice(shapeArr.length);
+        if (nextAreaArr.length === 0) {
+            return;
+        }
+        else {
+            this._squarify(
+                nextAreaArr,
+                nextRow,
+                out
+            );
+        }
+    };
+    TreeMapLayout.prototype._getShapeListInAbstractRow = function (
+        areas,
+        width,
+        height
+    ) {
+        // 如果只剩下一个了,直接返回
+        if (areas.length === 1) {
+            return [
+                {
+                    width: width,
+                    height: height
+                }
+            ];
+        }
+        // 填充进入的个数,从填充一个开始到填充所有小矩形,
+        // 纵横比最优时break并保留结果
+        for (var count = 1; count < areas.length; count++) {
+
+            var shapeArr0 = this._placeFixedNumberRectangles(
+                areas.slice(0, count),
+                width,
+                height
+            );
+            var shapeArr1 = this._placeFixedNumberRectangles(
+                areas.slice(0, count + 1),
+                width,
+                height
+            );
+            if (this._isFirstBetter(shapeArr0, shapeArr1)) {
+                return shapeArr0;
+            }
+        }
+    };
+
+    // 确定数量进行填充
+    TreeMapLayout.prototype._placeFixedNumberRectangles = function (
+        areaSubArr,
+        width,
+        height
+    ) {
+        var count = areaSubArr.length;
+        // 声明返回值-每个矩形的形状(长宽)之数组
+        // 例如:
+        /*[
+            {
+                width: 11
+                height: 12
+            },
+            {
+                width: 11
+                height: 22
+            }
+        ]*/
+        var shapeArr = [];
+
+        // 求出面积总和
+        var sum = 0;
+        for (var i = 0; i < areaSubArr.length; i++) {
+            sum += areaSubArr[i];
+        }
+        var cellWidth = sum / height;
+        for (var j = 0; j < count; j++) {
+            var cellHeight = height * areaSubArr[j] / sum;
+            shapeArr.push(
+                {
+                    width: cellWidth,
+                    height: cellHeight
+                }
+            );
+        }
+        return shapeArr;
+    };
+    // 相邻的两种填充方式放进去,比较是不是前一个的纵横比较小
+    TreeMapLayout.prototype._isFirstBetter = function (
+        shapeArr0,
+        shapeArr1
+    ) {
+        var ratio0 = shapeArr0[0].height / shapeArr0[0].width;
+        ratio0 = (ratio0 > 1) ? 1 / ratio0 : ratio0;
+        var ratio1 =  shapeArr1[0].height / shapeArr1[0].width;
+        ratio1 = (ratio1 > 1) ? 1 / ratio1 : ratio1;
+        if (Math.abs(ratio0 - 1) <= Math.abs(ratio1 - 1)) {
+            return true;
+        }
+        return false;
+    };
+
+    return TreeMapLayout;
+});

+ 692 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/WordCloud.js

@@ -0,0 +1,692 @@
+/**
+ * @author clmtulip(车丽美, clmtulip@gmail.com) liyong(liyong1239@163.com)
+ */
+
+// Modified from d3-cloud
+/*
+Copyright (c) 2013, Jason Davies.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * The name Jason Davies may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL JASON DAVIES BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+define(function (require) {
+    var ZeroArray = require('../layout/WordCloudRectZero');
+    var zrUtil = require('zrender/tool/util');
+
+    function CloudLayout(option) {
+        this._init(option);
+    }
+
+    CloudLayout.prototype = {
+        /**
+         * 启动计算过程
+         */
+        start: function () {
+            var board = null;
+            var maxWit = 0;
+            var maxHit = 0;
+            var maxArea = 0;
+            var i = -1;
+            var tags = [];
+            var maxBounds = null; /*图片的最大可用边框*/
+            // 根据配置 初始化 字符云数组
+            var data = this.wordsdata;
+            var dfop = this.defaultOption;
+            var wordletype = dfop.wordletype;
+            var size = dfop.size;
+            var that = this;
+
+            //得到 ZeroArray 传递过来的 初始值 和面积 最大宽度等值
+            var zeroArrayObj = new ZeroArray({
+                type: wordletype.type,
+                width: size[0],
+                height: size[1]
+            });
+
+            zeroArrayObj.calculate(function (options) {
+                board = options.initarr;
+                maxWit = options.maxWit;
+                maxHit = options.maxHit;
+                maxArea = options.area;
+                maxBounds = options.imgboard;
+                startStep();
+            }, this);
+
+            return this;
+
+            function startStep() {
+                that.totalArea = maxArea;
+                //字体大小的确定,可配置,根据开关 确定是否要根据面积来定。。
+                if (wordletype.autoSizeCal.enable) {
+                    that._autoCalTextSize(data, maxArea, maxWit, maxHit, wordletype.autoSizeCal.minSize);
+                }
+
+                if (dfop.timer) {
+                    clearInterval(dfop.timer);
+                }
+                dfop.timer = setInterval(step, 0);
+                step();
+            }
+
+            function step() {
+                var start = +new Date;
+                var n = data.length;
+                var d;
+                while (+new Date - start < dfop.timeInterval && ++i < n && dfop.timer) {
+                    d = data[i];
+                    // x y 的初始值
+                    d.x = size[0] >> 1;
+                    d.y = size[1] >> 1;
+                    //得到每个 标签所占用的具体的像素点 用 0 1 标记, 每32位作为一个数字
+                    that._cloudSprite(d, data, i);
+                    //place 放置每个字符, 成功后 以 event 事件通知
+                    if (d.hasText && that._place(board, d, maxBounds)) {
+
+                        tags.push(d);
+                        //                        event.word(d);
+                        // Temporary hack
+                        d.x -= size[0] >> 1;
+                        d.y -= size[1] >> 1;
+                    }
+
+                }
+                //全部放置完成 停止cloud,并触发 'end'事件
+                if (i >= n) {
+                    that.stop();
+                    that._fixTagPosition(tags);
+                    dfop.endcallback(tags);
+                }
+            }
+        },
+
+        _fixTagPosition: function (tags) {
+            var center = this.defaultOption.center;
+
+            for (var i = 0, len = tags.length; i < len; i++) {
+                tags[i].x += center[0];
+                tags[i].y += center[1];
+            }
+        },
+
+        /**
+         * 停止计算过程
+         */
+        stop: function () {
+            if (this.defaultOption.timer) {
+                clearInterval(this.defaultOption.timer);
+                this.defaultOption.timer = null;
+            }
+            return this;
+        },
+
+        /**
+         * 计算结束后 执行V
+         * @param v
+         */
+        end: function (v) {
+            if (v) {
+                this.defaultOption.endcallback = v;
+            }
+            return this;
+        },
+
+        /**
+         * 初始化
+         * @param option
+         * @private
+         */
+        _init: function (option) {
+            this.defaultOption = {};
+
+            /*初始化属性*/
+            this._initProperty(option);
+            /*初始化方法*/
+            this._initMethod(option);
+            /*初始化 canvas 画板*/
+            this._initCanvas();
+            /*初始化数据*/
+            this._initData(option.data);
+        },
+
+        _initData: function (datas) {
+            var that = this;
+            var thatop = that.defaultOption;
+            this.wordsdata = datas.map(function (d, i) {
+                d.text = thatop.text.call(that, d, i);
+
+                d.font = thatop.font.call(that, d, i);
+                d.style = thatop.fontStyle.call(that, d, i);
+                d.weight = thatop.fontWeight.call(that, d, i);
+
+                // 字体的旋转角度
+                d.rotate = thatop.rotate.call(that, d, i);
+                // 字体的大小 单位px
+                d.size = ~~thatop.fontSize.call(that, d, i);
+                // 字体间的间距
+                d.padding = thatop.padding.call(that, d, i);
+
+                return d;
+            }).sort(function (a, b) {
+                return b.value - a.value;
+            });
+        },
+
+        /**
+         * 默认函数的定义 及初始化过程
+         * @private
+         */
+        _initMethod: function (option) {
+            /*数据初始化中要用到的函数*/
+            var dfop = this.defaultOption;
+            dfop.text = (option.text) ? functor(option.text) : cloudText;
+            dfop.font = (option.font) ? functor(option.font) : cloudFont;
+            dfop.fontSize = (option.fontSize) ? functor(option.fontSize) : cloudFontSize;
+            dfop.fontStyle = (option.fontStyle) ? functor(option.fontStyle) : cloudFontNormal;
+            dfop.fontWeight = (option.fontWeight) ? functor(option.fontWeight) : cloudFontNormal;
+
+            dfop.rotate = (option.rotate) ? newCloudRotate(option.rotate) : cloudRotate;
+
+            dfop.padding = (option.padding) ? functor(option.padding) : cloudPadding;
+
+            dfop.center = option.center;
+
+            /*其它函数*/
+            dfop.spiral = archimedeanSpiral;
+            dfop.endcallback = function () {
+            };
+            dfop.rectangularSpiral = rectangularSpiral;
+            dfop.archimedeanSpiral = archimedeanSpiral;
+
+            function cloudText(d) {
+                return d.name;
+            }
+
+            function cloudFont() {
+                return "sans-serif";
+            }
+
+            function cloudFontNormal() {
+                return "normal";
+            }
+
+            function cloudFontSize(d) {
+                return d.value;
+            }
+
+            function cloudRotate() {
+                return 0;
+            }
+
+            function newCloudRotate(rotate) {
+                return function () {
+                    return rotate[Math.round(Math.random() * (rotate.length - 1))]
+                }
+            }
+
+            function cloudPadding() {
+                return 0;
+            }
+
+            function archimedeanSpiral(size) {
+                var e = size[0] / size[1];
+                return function (t) {
+                    return [
+                            e * (t *= .1) * Math.cos(t),
+                            t * Math.sin(t)
+                    ];
+                };
+            }
+
+            function rectangularSpiral(size) {
+                var dy = 4;
+                var dx = dy * size[0] / size[1];
+                var x = 0;
+                var y = 0;
+                return function (t) {
+                    var sign = t < 0 ? -1 : 1;
+                    // See triangular numbers: T_n = n * (n + 1) / 2.
+                    switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) {
+                        case 0:
+                            x += dx;
+                            break;
+                        case 1:
+                            y += dy;
+                            break;
+                        case 2:
+                            x -= dx;
+                            break;
+                        default:
+                            y -= dy;
+                            break;
+                    }
+                    return [
+                        x,
+                        y
+                    ];
+                };
+            }
+
+            function functor(v) {
+                return typeof v === "function" ? v : function () {
+                    return v;
+                };
+            }
+
+        },
+
+        _initProperty: function (option) {
+            var dfop = this.defaultOption;
+            //默认值
+            dfop.size = option.size || [
+                256,
+                256
+            ];
+            dfop.wordletype = option.wordletype;
+            dfop.words = option.words || [];
+            dfop.timeInterval = Infinity;
+            dfop.timer = null;
+            dfop.spirals = {
+                archimedean: dfop.archimedeanSpiral,
+                rectangular: dfop.rectangularSpiral
+            };
+
+            zrUtil.merge(dfop, {
+                size: [
+                    256,
+                    256
+                ],
+                wordletype: {
+                    type: 'RECT',
+                    areaPresent: .058,
+                    autoSizeCal: {
+                        enable: true,
+                        minSize: 12
+                    }
+                }
+            });
+        },
+
+        _initCanvas: function () {
+            var cloudRadians = Math.PI / 180;
+            var cw = 1 << 11 >> 5;
+            var ch = 1 << 11;
+            var canvas;
+            var ratio = 1;
+
+            if (typeof document !== "undefined") {
+                canvas = document.createElement("canvas");
+                canvas.width = 1;
+                canvas.height = 1;
+                ratio = Math.sqrt(
+                    canvas.getContext("2d")
+                        .getImageData(0, 0, 1, 1)
+                        .data.length >> 2
+                );
+                canvas.width = (cw << 5) / ratio;
+                canvas.height = ch / ratio;
+            }
+            else {
+                // Attempt to use node-canvas.
+                canvas = new Canvas(cw << 5, ch);
+            }
+
+            var c = canvas.getContext("2d");
+            c.fillStyle = c.strokeStyle = "red";
+            c.textAlign = "center";
+
+            this.defaultOption.c = c;
+            this.defaultOption.cw = cw;
+            this.defaultOption.ch = ch;
+            this.defaultOption.ratio = ratio;
+            this.defaultOption.cloudRadians = cloudRadians;
+        },
+
+
+        _cloudSprite: function (d, data, di) {
+            if (d.sprite) {
+                return;
+            }
+
+            var cw = this.defaultOption.cw;
+            var ch = this.defaultOption.ch;
+            var c = this.defaultOption.c;
+            var ratio = this.defaultOption.ratio;
+            var cloudRadians = this.defaultOption.cloudRadians;
+
+            c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
+            var x = 0;
+            var y = 0;
+            var maxh = 0;
+            var n = data.length;
+            --di;
+            while (++di < n) {
+                d = data[di];
+                c.save();
+                c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
+                //得到 字体 实际的 宽和高
+                var w = c.measureText(d.text + "m").width * ratio;
+                var h = d.size << 1;
+                //得到 d 所占用的具体的宽度 和高度  并取整
+                if (d.rotate) {
+                    var sr = Math.sin(d.rotate * cloudRadians);
+                    var cr = Math.cos(d.rotate * cloudRadians);
+                    var wcr = w * cr;
+                    var wsr = w * sr;
+                    var hcr = h * cr;
+                    var hsr = h * sr;
+                    w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
+                    h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
+                }
+                else {
+                    w = (w + 0x1f) >> 5 << 5;
+                }
+                if (h > maxh) {
+                    maxh = h;
+                }
+                //如果 字体 超出了 宽度 换更远的一行。。。
+                if (x + w >= (cw << 5)) {
+                    x = 0;
+                    y += maxh;
+                    maxh = 0;
+                }
+                if (y + h >= ch) {
+                    break;
+                }
+                c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
+                if (d.rotate) {
+                    c.rotate(d.rotate * cloudRadians);
+                }
+                c.fillText(d.text, 0, 0);
+                if (d.padding) {
+                    c.lineWidth = 2 * d.padding;
+                    c.strokeText(d.text, 0, 0);
+                }
+                c.restore();
+                d.width = w;
+                d.height = h;
+                // xoff  yoff 为 d在画板中的坐标位置
+                d.xoff = x;
+                d.yoff = y;
+                d.x1 = w >> 1;
+                d.y1 = h >> 1;
+                d.x0 = -d.x1;
+                d.y0 = -d.y1;
+                d.hasText = true;
+                x += w;
+            }
+            //得到 所在 区域的 像素值。。进而 确定 其 y的位置。。。
+            var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data;
+            var sprite = [];
+            while (--di >= 0) {
+                d = data[di];
+                if (!d.hasText) {
+                    continue;
+                }
+                var w = d.width;
+                var w32 = w >> 5;
+                var h = d.y1 - d.y0;
+                // Zero the buffer
+                for (var i = 0; i < h * w32; i++) {
+                    sprite[i] = 0;
+                }
+                x = d.xoff;
+                if (x == null) {
+                    return;
+                }
+                y = d.yoff;
+                var seen = 0;
+                var seenRow = -1;
+                for (var j = 0; j < h; j++) {
+                    for (var i = 0; i < w; i++) {
+                        var k = w32 * j + (i >> 5);
+                        var m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0;
+                        sprite[k] |= m;
+                        seen |= m;
+                    }
+                    if (seen) {
+                        seenRow = j;
+                    }
+                    else {
+                        d.y0++;
+                        h--;
+                        j--;
+                        y++;
+                    }
+                }
+                d.y1 = d.y0 + seenRow;
+                d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
+            }
+        },
+
+        _place: function (board, tag, maxBounds) {
+
+            /*判断目前面积总值 是否超过了阈值*/
+            /*判断目前该值是否能够放的下*/
+            var size = this.defaultOption.size;
+            var perimeter = [
+                    {x: 0, y: 0},
+                    {x: size[0], y: size[1]}
+                ];
+            var startX = tag.x;
+            var startY = tag.y;
+            var maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]);
+            var s = this.defaultOption.spiral(size);
+            var dt = Math.random() < .5 ? 1 : -1;
+            var t = -dt;
+            var dxdy;
+            var dx;
+            var dy;
+
+            while (dxdy = s(t += dt)) {
+                dx = ~~dxdy[0];
+                dy = ~~dxdy[1];
+
+                //当dx dy 都大于 maxDelta 即超出了 画板范围, 停止 放置。。 该值舍弃
+                if (Math.min(dx, dy) > maxDelta) {
+                    break;
+                }
+
+                tag.x = startX + dx;
+                tag.y = startY + dy;
+
+                if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 ||
+                    tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) {
+                    continue;
+                }
+                // TODO only check for collisions within current bounds.
+                if (!cloudCollide(tag, board, size[0])) {
+                    if (collideRects(tag, maxBounds)) {
+                        var sprite = tag.sprite;
+                        var w = tag.width >> 5;
+                        var sw = size[0] >> 5;
+                        var lx = tag.x - (w << 4);
+                        var sx = lx & 0x7f;
+                        var msx = 32 - sx;
+                        var h = tag.y1 - tag.y0;
+                        var x = (tag.y + tag.y0) * sw + (lx >> 5);// 计算其在画板的 横坐标的位置
+                        var last;
+
+                        //当 该 字能够被正确放置时, 根据该字体的范围 更改标志位范围
+                        for (var j = 0; j < h; j++) {
+                            last = 0;
+                            for (var i = 0; i <= w; i++) {
+                                board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
+                            }
+                            x += sw;
+                        }
+                        delete tag.sprite;
+                        return true;
+                    }
+                }
+            }
+            return false;
+
+            function cloudCollide(tag, board, sw) {
+                sw >>= 5;
+                var sprite = tag.sprite;
+                var w = tag.width >> 5;
+                var lx = tag.x - (w << 4);
+                var sx = lx & 0x7f;
+                var msx = 32 - sx;
+                var h = tag.y1 - tag.y0;
+                var x = (tag.y + tag.y0) * sw + (lx >> 5);
+                var last;
+                for (var j = 0; j < h; j++) {
+                    last = 0;
+                    for (var i = 0; i <= w; i++) {
+                        if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0))
+                            & board[x + i]) {
+                            return true;
+                        }
+                    }
+                    x += sw;
+                }
+                return false;
+            }
+
+            function collideRects(a, maxBounds) {
+                return maxBounds.row[a.y] && maxBounds.cloumn[a.x]
+                    && a.x >= maxBounds.row[a.y].start
+                    && a.x <= maxBounds.row[a.y].end
+                    && a.y >= maxBounds.cloumn[a.x].start
+                    && a.y <= maxBounds.cloumn[a.x].end;
+            }
+        },
+
+        _autoCalTextSize: function (data, shapeArea, maxwidth, maxheight, minSize) {
+            //循环
+            //面积 归一化
+            //计算 每个字体的面积
+            var sizesum = sum(data, function (k) {
+                    return k.size;
+                });
+            var i = data.length;
+            var maxareapre = .25; /*面积归一化后, 字体占总面积的最大百分比*/
+            var minTextSize = minSize; /*字体所能缩放的最小大小。。如果字体面积 依旧无法满足上述约束, 字体将不会再缩小*/
+            var cw = this.defaultOption.cw;
+            var ch = this.defaultOption.ch;
+            var c = this.defaultOption.c;
+            var ratio = this.defaultOption.ratio;
+            var cloudRadians = this.defaultOption.cloudRadians;
+            var d;
+            var dpre;
+
+            while (i--) {
+                d = data[i];
+                dpre = d.size / sizesum;
+                if (maxareapre) {
+                    d.areapre = (dpre < maxareapre) ? dpre : maxareapre;
+                }
+                else {
+                    d.areapre = dpre;
+                }
+
+                d.area = shapeArea * d.areapre;
+                d.totalarea = shapeArea;
+                measureTextWitHitByarea(d);
+            }
+
+            //根据面积 计算字体的 size
+            //根据 最大宽度 和最大高度 重新检测 字体的size, 不符合条件 重新计算,若字体大小已经是 最小size,则 取消计算。。。
+            function measureTextWitHitByarea(d) {
+                c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
+                c.save();
+                c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
+
+                //得到 字体 实际的 宽和高
+                var w = c.measureText(d.text + "m").width * ratio,
+                    h = d.size << 1;
+
+                //得到 d 所占用的具体的宽度 和高度  并取整
+                w = (w + 0x1f) >> 5 << 5; //给定了一个最小值 1 即不会存在 宽度为0的情况
+
+                c.restore();
+                d.aw = w;
+                d.ah = h;
+
+
+                var k, rw, rh;
+
+                //如果 有旋转  测试 旋转后的 宽和高 是否 超过了 最大的大小
+                if (d.rotate) {
+                    var sr = Math.sin(d.rotate * cloudRadians);
+                    var cr = Math.cos(d.rotate * cloudRadians);
+                    var wcr = w * cr;
+                    var wsr = w * sr;
+                    var hcr = h * cr;
+                    var hsr = h * sr;
+
+                    rw = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
+                    rh = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
+                }
+
+
+                //满足条件 不用继续调整的
+                // size 为 最小, 或者面积在允许范围内 且 宽和高也在允许范围内
+                if ((d.size <= minTextSize)
+                    || (d.rotate && w * h <= d.area && rw <= maxwidth && rh <= maxheight)
+                    || (w * h <= d.area && w <= maxwidth && h <= maxheight)) {
+                    d.area = w * h;
+                    return;
+                }
+
+                //如果 超过了 最大宽度 或最大高度。。 继续计算
+                if (d.rotate && rw > maxwidth && rh > maxheight) {// 有旋转时,超过了 最大宽度和最大高度
+                    k = Math.min(maxwidth / rw, maxheight / rh);
+                }
+                else if (w > maxwidth || h > maxheight) {// 无旋转时,超过了 最大宽度和最大高度
+                    k = Math.min(maxwidth / w, maxheight / h);
+                }
+                else {                                // 当前 面积 大于 规定的面积时
+                    k = Math.sqrt(d.area / (d.aw * d.ah));
+                }
+
+                d.size = ~~(k * d.size);
+
+                if (d.size < minSize) {
+                    d.size = minSize;
+                    return;
+                }
+
+                return measureTextWitHitByarea(d);
+            }
+
+            function sum(dts, callback) {
+                var j = dts.length;
+                var ressum = 0;
+                while (j--) {
+                    ressum += callback(dts[j]);
+                }
+
+                return ressum;
+            }
+        }
+
+    };
+
+
+    return CloudLayout;
+});

+ 123 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/WordCloudRectZero.js

@@ -0,0 +1,123 @@
+/**
+ * @file    主要功能
+ * @author  clmtulip(车丽美, clmtulip@gmail.com) liyong(liyong1239@163.com)
+ */
+define(function (require) {
+    function ZeroArray(option) {
+        this.defaultOption = {type: 'RECT'};
+        this._init(option);
+    }
+
+    ZeroArray.prototype = {
+        RECT: '_calculateRect',
+
+        _init: function (option) {
+            this._initOption(option);
+            this._initCanvas();
+        },
+
+        _initOption: function (option) {
+            for (k in option) {
+                this.defaultOption[k] = option[k];
+            }
+        },
+
+        _initCanvas: function () {
+            var canvas = document.createElement("canvas");
+            canvas.width = 1;
+            canvas.height = 1;
+
+            var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
+
+            canvas.width = this.defaultOption.width;
+            canvas.height = this.defaultOption.height;
+
+            if (canvas.getContext) {
+                var ctx = canvas.getContext('2d');
+            }
+
+            this.canvas = canvas;
+            this.ctx = ctx;
+            this.ratio = ratio;
+        },
+
+        /**执行计算, 并返回
+         *
+         * @param callback
+         * 返回 {initarr, area, maxHit, maxWit} 给callback
+         */
+        calculate: function (callback, callbackObj) {
+            var calType = this.defaultOption.type,
+                calmethod = this[calType];
+
+            this[calmethod].call(this, callback, callbackObj);
+        },
+
+        /**
+         * callback 函数的 正确执行
+         * @param result 计算后的结果,{initarr, area, maxHit, maxWit}
+         * @param callback  计算成功之后的回调函数
+         * @param callbackObj 回调函数的执行作用域
+         * @private
+         */
+        _calculateReturn: function (result, callback, callbackObj) {
+            callback.call(callbackObj, result);
+        },
+
+        _calculateRect: function (callback, callbackObj) {
+            var result = {},
+                width = this.defaultOption.width >> 5 << 5,
+                height = this.defaultOption.height;
+
+            // 初始化数组
+            result.initarr = this._rectZeroArray(width * height);
+
+            // 总面积
+            result.area = width * height;
+
+            // 最大高度
+            result.maxHit = height;
+
+            // 最大宽度
+            result.maxWit = width;
+
+            // 边界
+            result.imgboard = this._rectBoard(width, height);
+
+            this._calculateReturn(result, callback, callbackObj);
+        },
+
+        _rectBoard: function (width, height) {
+
+            var row = [];
+            for (var i = 0; i < height; i++) {
+                row.push({
+                    y: i,
+                    start: 0,
+                    end: width
+                })
+            }
+
+            var cloumn = [];
+            for (var i = 0; i < width; i++) {
+                cloumn.push({
+                    x: i,
+                    start: 0,
+                    end: height
+                })
+            }
+
+            return {row: row, cloumn: cloumn};
+        },
+
+        _rectZeroArray: function (num) {
+            var a = [],
+                n = num,
+                i = -1;
+            while (++i < n) a[i] = 0;
+            return a;
+        }
+    };
+
+    return ZeroArray;
+});

+ 262 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/eventRiver.js

@@ -0,0 +1,262 @@
+/**
+ * eventRiver 布局算法
+ * @module echarts/layout/eventRiver
+ * @author clmtulip  (车丽美, clmtulip@gmail.com)
+ */
+define(function(require) {
+
+    function eventRiverLayout(series, intervalX, area) {
+        var space = 4;
+        var scale = intervalX;
+
+        function importanceSort(a, b) {
+            var x = a.importance;
+            var y = b.importance;
+            return ((x > y) ? -1 : ((x < y) ? 1 : 0));
+        }
+
+        /**
+         * 查询数组中元素的index
+         */
+        function indexOf(array, value) {
+            if (array.indexOf) {
+                return array.indexOf(value);
+            }
+            for (var i = 0, len = array.length; i < len; i++) {
+                if (array[i] === value) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        // step 0. calculate event importance and sort descending
+        for (var i = 0; i < series.length; i++) {
+            for (var j = 0; j < series[i].data.length; j++) {
+                if (series[i].data[j].weight == null) {
+                    series[i].data[j].weight = 1;
+                }
+                var importance = 0;
+                for (var k = 0; k < series[i].data[j].evolution.length; k++) {
+                    importance += series[i].data[j].evolution[k].valueScale;
+                }
+                series[i].data[j].importance = importance * series[i].data[j].weight;
+            }
+            series[i].data.sort(importanceSort);
+        }
+
+        // step 1. 计算每个group的重要值importance,并按递减顺序排序
+        for (var i = 0; i < series.length; i++) {
+            if (series[i].weight == null) {
+                series[i].weight = 1;
+            }
+            var importance = 0;
+            for (var j = 0; j < series[i].data.length; j++) {
+                importance += series[i].data[j].weight;
+            }
+            series[i].importance = importance * series[i].weight;
+        }
+        // 根据importance对groups进行递减排序
+        series.sort(importanceSort);
+
+        // step 3. set bubble positions in group order, then in event order
+        // 找到包含所有事件的时间段,即最小和最大时间点
+        var minTime = Number.MAX_VALUE;
+        var maxTime = 0;
+        for (var i = 0; i < series.length; i++) {
+            for (var j = 0; j < series[i].data.length; j++) {
+                for (var k = 0; k < series[i].data[j].evolution.length; k++) {
+                    var time = series[i].data[j].evolution[k].timeScale;
+                    minTime = Math.min(minTime, time);
+                    maxTime = Math.max(maxTime, time);
+                }
+            }
+        }
+        //console.log('minTime: ' + minTime);
+        //console.log('maxTime: ' + maxTime);
+
+        // 时间范围 即 x轴显示的起始范围
+        minTime = ~~minTime;
+        maxTime = ~~maxTime;
+
+        // 气泡之间的间隙
+        var flagForOffset = (function () {
+
+            var length = maxTime - minTime + 1 + (~~intervalX);
+            if (length <= 0){
+                return [0];
+            }
+            var result = [];
+            while (length--){
+                result.push(0);
+            }
+            return result;
+        })();
+
+        var flagForPos = flagForOffset.slice(0);
+
+        var bubbleData = [];
+        var totalMaxy = 0;
+        var totalOffset = 0;
+
+        for (var i = 0; i < series.length; i++) {
+            for (var j = 0; j < series[i].data.length; j++) {
+                var e = series[i].data[j];
+                e.time = [];
+                e.value = [];
+                var tmp;
+                var maxy = 0;
+                for (var k = 0; k < series[i].data[j].evolution.length; k++) {
+                    tmp = series[i].data[j].evolution[k];
+                    e.time.push(tmp.timeScale);
+                    e.value.push(tmp.valueScale);
+                    maxy = Math.max(maxy, tmp.valueScale);
+                }
+
+                // 边界计算
+                bubbleBound(e, intervalX, minTime);
+
+                // 得到可以放置的位置
+                e.y = findLocation(flagForPos, e, function (e, index){return e.ypx[index];});
+                // 得到偏移量
+                e._offset = findLocation(flagForOffset, e, function (){ return space;});
+
+                totalMaxy = Math.max(totalMaxy, e.y + maxy);
+                totalOffset = Math.max(totalOffset, e._offset);
+
+                bubbleData.push(e);
+            }
+        }
+
+        // 映射到显示区域内
+        scaleY(bubbleData, area, totalMaxy, totalOffset);
+    }
+
+    /**
+     * 映射到显示区域内
+     */
+    function scaleY(bubbleData, area, maxY, offset) {
+        var height = area.height;
+        var offsetScale = offset / height > 0.5 ? 0.5 : 1;
+
+        var yBase = area.y;
+        var yScale = (area.height - offset) / maxY;
+
+        for (var i = 0, length = bubbleData.length; i < length; i++){
+            var e = bubbleData[i];
+            e.y = yBase + yScale * e.y + e._offset * offsetScale;
+
+            delete e.time;
+            delete e.value;
+            delete e.xpx;
+            delete e.ypx;
+            delete e._offset;
+
+            // 修改值域范围
+            var evolutionList = e.evolution;
+            for (var k = 0, klen = evolutionList.length; k < klen; k++) {
+                evolutionList[k].valueScale *= yScale;
+            }
+        }
+    }
+
+
+    /**
+     * 得到两点式的方程函数 y = k*x + b
+     * @param {number} x0 起点横坐标
+     * @param {number} y0 起点纵坐标
+     * @param {number} x1 终点横坐标
+     * @param {number} y1 终点纵坐标
+     * @returns {Function} 输入为横坐标 返回纵坐标s
+     */
+    function line(x0, y0, x1, y1){
+
+        // 横坐标相同,应该抛出错误
+        if (x0 === x1) {
+            throw new Error('x0 is equal with x1!!!');
+        }
+
+        // 纵坐标相同
+        if (y0 === y1) {
+            return function () {
+                return y0;
+            }
+        }
+
+        var k = (y0 - y1) / (x0 - x1);
+        var b = (y1 * x0 - y0 * x1) / (x0 - x1);
+
+        return function (x) {
+            return k * x + b;
+        }
+    }
+
+    /**
+     * 计算当前气泡的值经过的边界
+     * @param {object} e 气泡的值
+     * @param {array} e.time 时间范围
+     * @param {array} e.value 值域范围
+     * @param {number} intervalX 气泡尾巴长度
+     */
+    function bubbleBound(e, intervalX, minX){
+        var space = ~~intervalX;
+        var length = e.time.length;
+
+        e.xpx = [];
+        e.ypx = [];
+
+        var i = 0;
+        var x0 = 0;
+        var x1 = 0;
+        var y0 = 0;
+        var y1 = 0;
+        var newline;
+        for(; i < length; i++){
+
+            x0 = ~~e.time[i];
+            y0 = e.value[i] / 2;
+
+            if (i === length - 1) {
+                // i = length - 1  ~  += intervalX
+                x1 = x0 + space;
+                y1 = 0;
+            } else {
+                x1 = ~~(e.time[i + 1]);
+                y1 = e.value[i + 1] / 2;
+            }
+
+            // to line
+            newline = line(x0, y0, x1, y1);
+            //
+            for (var x = x0; x < x1; x++){
+                e.xpx.push(x - minX);
+                e.ypx.push(newline(x));
+            }
+        }
+
+        e.xpx.push(x1 - minX);
+        e.ypx.push(y1);
+    }
+
+    function findLocation(flags, e, yvalue){
+        var pos = 0;
+
+        var length = e.xpx.length;
+        var i = 0;
+        var y;
+        for(; i < length; i++){
+            y = yvalue(e, i);
+            pos = Math.max(pos, y + flags[e.xpx[i]]);
+        }
+
+        // reset flags
+        for(i = 0; i < length; i++){
+            y = yvalue(e, i);
+            flags[e.xpx[i]] = pos + y;
+        }
+
+        return pos;
+    }
+
+    return eventRiverLayout;
+});

+ 781 - 0
src/main/webapp/static/echarts-2.2.7/src/layout/forceLayoutWorker.js

@@ -0,0 +1,781 @@
+// 1. Graph Drawing by Force-directed Placement
+// 2. http://webatlas.fr/tempshare/ForceAtlas2_Paper.pdf
+define(function __echartsForceLayoutWorker(require) {
+
+    'use strict';
+
+    var vec2;
+    // In web worker
+    var inWorker = typeof(window) === 'undefined' && typeof(require) === 'undefined';
+    if (inWorker) {
+        vec2 = {
+            create: function(x, y) {
+                var out = new Float32Array(2);
+                out[0] = x || 0;
+                out[1] = y || 0;
+                return out;
+            },
+            dist: function(a, b) {
+                var x = b[0] - a[0];
+                var y = b[1] - a[1];
+                return Math.sqrt(x*x + y*y);
+            },
+            len: function(a) {
+                var x = a[0];
+                var y = a[1];
+                return Math.sqrt(x*x + y*y);
+            },
+            scaleAndAdd: function(out, a, b, scale) {
+                out[0] = a[0] + b[0] * scale;
+                out[1] = a[1] + b[1] * scale;
+                return out;
+            },
+            scale: function(out, a, b) {
+                out[0] = a[0] * b;
+                out[1] = a[1] * b;
+                return out;
+            },
+            add: function(out, a, b) {
+                out[0] = a[0] + b[0];
+                out[1] = a[1] + b[1];
+                return out;
+            },
+            sub: function(out, a, b) {
+                out[0] = a[0] - b[0];
+                out[1] = a[1] - b[1];
+                return out;
+            },
+            dot: function (v1, v2) {
+                return v1[0] * v2[0] + v1[1] * v2[1];
+            },
+            normalize: function(out, a) {
+                var x = a[0];
+                var y = a[1];
+                var len = x*x + y*y;
+                if (len > 0) {
+                    //TODO: evaluate use of glm_invsqrt here?
+                    len = 1 / Math.sqrt(len);
+                    out[0] = a[0] * len;
+                    out[1] = a[1] * len;
+                }
+                return out;
+            },
+            negate: function(out, a) {
+                out[0] = -a[0];
+                out[1] = -a[1];
+                return out;
+            },
+            copy: function(out, a) {
+                out[0] = a[0];
+                out[1] = a[1];
+                return out;
+            },
+            set: function(out, x, y) {
+                out[0] = x;
+                out[1] = y;
+                return out;
+            }
+        };
+    }
+    else {
+        vec2 = require('zrender/tool/vector');
+    }
+    var ArrayCtor = typeof(Float32Array) == 'undefined' ? Array : Float32Array;
+
+    /****************************
+     * Class: Region
+     ***************************/
+
+    function Region() {
+
+        this.subRegions = [];
+
+        this.nSubRegions = 0;
+
+        this.node = null;
+
+        this.mass = 0;
+
+        this.centerOfMass = null;
+
+        this.bbox = new ArrayCtor(4);
+
+        this.size = 0;
+    }
+
+    // Reset before update
+    Region.prototype.beforeUpdate = function() {
+        for (var i = 0; i < this.nSubRegions; i++) {
+            this.subRegions[i].beforeUpdate();
+        }
+        this.mass = 0;
+        if (this.centerOfMass) {
+            this.centerOfMass[0] = 0;
+            this.centerOfMass[1] = 0;
+        }
+        this.nSubRegions = 0;
+        this.node = null;
+    };
+    // Clear after update
+    Region.prototype.afterUpdate = function() {
+        this.subRegions.length = this.nSubRegions;
+        for (var i = 0; i < this.nSubRegions; i++) {
+            this.subRegions[i].afterUpdate();
+        }
+    };
+
+    Region.prototype.addNode = function(node) {
+        if (this.nSubRegions === 0) {
+            if (this.node == null) {
+                this.node = node;
+                return;
+            }
+            else {
+                this._addNodeToSubRegion(this.node);
+                this.node = null;
+            }
+        }
+        this._addNodeToSubRegion(node);
+
+        this._updateCenterOfMass(node);
+    };
+
+    Region.prototype.findSubRegion = function(x, y) {
+        for (var i = 0; i < this.nSubRegions; i++) {
+            var region = this.subRegions[i];
+            if (region.contain(x, y)) {
+                return region;
+            }
+        }
+    };
+
+    Region.prototype.contain = function(x, y) {
+        return this.bbox[0] <= x
+            && this.bbox[2] >= x
+            && this.bbox[1] <= y
+            && this.bbox[3] >= y;
+    };
+
+    Region.prototype.setBBox = function(minX, minY, maxX, maxY) {
+        // Min
+        this.bbox[0] = minX;
+        this.bbox[1] = minY;
+        // Max
+        this.bbox[2] = maxX;
+        this.bbox[3] = maxY;
+
+        this.size = (maxX - minX + maxY - minY) / 2;
+    };
+
+    Region.prototype._newSubRegion = function() {
+        var subRegion = this.subRegions[this.nSubRegions];
+        if (!subRegion) {
+            subRegion = new Region();
+            this.subRegions[this.nSubRegions] = subRegion;
+        }
+        this.nSubRegions++;
+        return subRegion;
+    };
+
+    Region.prototype._addNodeToSubRegion = function(node) {
+        var subRegion = this.findSubRegion(node.position[0], node.position[1]);
+        var bbox = this.bbox;
+        if (!subRegion) {
+            var cx = (bbox[0] + bbox[2]) / 2;
+            var cy = (bbox[1] + bbox[3]) / 2;
+            var w = (bbox[2] - bbox[0]) / 2;
+            var h = (bbox[3] - bbox[1]) / 2;
+            
+            var xi = node.position[0] >= cx ? 1 : 0;
+            var yi = node.position[1] >= cy ? 1 : 0;
+
+            var subRegion = this._newSubRegion();
+            // Min
+            subRegion.setBBox(
+                // Min
+                xi * w + bbox[0],
+                yi * h + bbox[1],
+                // Max
+                (xi + 1) * w + bbox[0],
+                (yi + 1) * h + bbox[1]
+            );
+        }
+
+        subRegion.addNode(node);
+    };
+
+    Region.prototype._updateCenterOfMass = function(node) {
+        // Incrementally update
+        if (this.centerOfMass == null) {
+            this.centerOfMass = vec2.create();
+        }
+        var x = this.centerOfMass[0] * this.mass;
+        var y = this.centerOfMass[1] * this.mass;
+        x += node.position[0] * node.mass;
+        y += node.position[1] * node.mass;
+        this.mass += node.mass;
+        this.centerOfMass[0] = x / this.mass;
+        this.centerOfMass[1] = y / this.mass;
+    };
+
+    /****************************
+     * Class: Graph Node
+     ***************************/
+    function GraphNode() {
+        this.position = vec2.create();
+
+        this.force = vec2.create();
+        this.forcePrev = vec2.create();
+
+        this.speed = vec2.create();
+        this.speedPrev = vec2.create();
+
+        // If repulsionByDegree is true
+        //  mass = inDegree + outDegree + 1
+        // Else
+        //  mass is manually set
+        this.mass = 1;
+
+        this.inDegree = 0;
+        this.outDegree = 0;
+    }
+
+    /****************************
+     * Class: Graph Edge
+     ***************************/
+    function GraphEdge(node1, node2) {
+        this.node1 = node1;
+        this.node2 = node2;
+
+        this.weight = 1;
+    }
+
+    /****************************
+     * Class: ForceLayout
+     ***************************/
+    function ForceLayout() {
+
+        this.barnesHutOptimize = false;
+        this.barnesHutTheta = 1.5;
+
+        this.repulsionByDegree = false;
+
+        this.preventNodeOverlap = false;
+        this.preventNodeEdgeOverlap = false;
+
+        this.strongGravity = true;
+
+        this.gravity = 1.0;
+        this.scaling = 1.0;
+
+        this.edgeWeightInfluence = 1.0;
+
+        this.center = [0, 0];
+        this.width = 500;
+        this.height = 500;
+
+        this.maxSpeedIncrease = 1;
+
+        this.nodes = [];
+        this.edges = [];
+
+        this.bbox = new ArrayCtor(4);
+
+        this._rootRegion = new Region();
+        this._rootRegion.centerOfMass = vec2.create();
+
+        this._massArr = null;
+
+        this._k = 0;
+    }
+
+    ForceLayout.prototype.nodeToNodeRepulsionFactor = function (mass, d, k) {
+        return k * k * mass / d;
+    };
+
+    ForceLayout.prototype.edgeToNodeRepulsionFactor = function (mass, d, k) {
+        return k * mass / d;
+    };
+
+    ForceLayout.prototype.attractionFactor = function (w, d, k) {
+        return w * d / k;
+    };
+
+    ForceLayout.prototype.initNodes = function(positionArr, massArr, sizeArr) {
+
+        this.temperature = 1.0;
+
+        var nNodes = positionArr.length / 2;
+        this.nodes.length = 0;
+        var haveSize = typeof(sizeArr) !== 'undefined';
+
+        for (var i = 0; i < nNodes; i++) {
+            var node = new GraphNode();
+            node.position[0] = positionArr[i * 2];
+            node.position[1] = positionArr[i * 2 + 1];
+            node.mass = massArr[i];
+            if (haveSize) {
+                node.size = sizeArr[i];
+            }
+            this.nodes.push(node);
+        }
+
+        this._massArr = massArr;
+        if (haveSize) {
+            this._sizeArr = sizeArr;
+        }
+    };
+
+    ForceLayout.prototype.initEdges = function(edgeArr, edgeWeightArr) {
+        var nEdges = edgeArr.length / 2;
+        this.edges.length = 0;
+        var edgeHaveWeight = typeof(edgeWeightArr) !== 'undefined';
+
+        for (var i = 0; i < nEdges; i++) {
+            var sIdx = edgeArr[i * 2];
+            var tIdx = edgeArr[i * 2 + 1];
+            var sNode = this.nodes[sIdx];
+            var tNode = this.nodes[tIdx];
+
+            if (!sNode || !tNode) {
+                continue;
+            }
+            sNode.outDegree++;
+            tNode.inDegree++;
+            var edge = new GraphEdge(sNode, tNode);
+
+            if (edgeHaveWeight) {
+                edge.weight = edgeWeightArr[i];
+            }
+
+            this.edges.push(edge);
+        }
+    };
+
+    ForceLayout.prototype.update = function() {
+
+        var nNodes = this.nodes.length;
+
+        this.updateBBox();
+
+        this._k = 0.4 * this.scaling * Math.sqrt(this.width * this.height / nNodes);
+
+        if (this.barnesHutOptimize) {
+            this._rootRegion.setBBox(
+                this.bbox[0], this.bbox[1],
+                this.bbox[2], this.bbox[3]
+            );
+            this._rootRegion.beforeUpdate();
+            for (var i = 0; i < nNodes; i++) {
+                this._rootRegion.addNode(this.nodes[i]);
+            }
+            this._rootRegion.afterUpdate();
+        }
+        else {
+            // Update center of mass of whole graph
+            var mass = 0;
+            var centerOfMass = this._rootRegion.centerOfMass;
+            vec2.set(centerOfMass, 0, 0);
+            for (var i = 0; i < nNodes; i++) {
+                var node = this.nodes[i];
+                mass += node.mass;
+                vec2.scaleAndAdd(centerOfMass, centerOfMass, node.position, node.mass);
+            }
+            if (mass > 0) {
+                vec2.scale(centerOfMass, centerOfMass, 1 / mass);
+            }
+        }
+
+        this.updateForce();
+
+        this.updatePosition();
+    };
+
+    ForceLayout.prototype.updateForce = function () {
+        var nNodes = this.nodes.length;
+        // Reset forces
+        for (var i = 0; i < nNodes; i++) {
+            var node = this.nodes[i];
+            vec2.copy(node.forcePrev, node.force);
+            vec2.copy(node.speedPrev, node.speed);
+            vec2.set(node.force, 0, 0);
+        }
+
+        this.updateNodeNodeForce();
+
+        if (this.gravity > 0) {
+            this.updateGravityForce();
+        }
+
+        this.updateEdgeForce();
+
+        if (this.preventNodeEdgeOverlap) {
+            this.updateNodeEdgeForce();
+        }
+    };
+
+    ForceLayout.prototype.updatePosition = function () {
+        var nNodes = this.nodes.length;
+        // Apply forces
+        // var speed = vec2.create();
+        var v = vec2.create();
+        for (var i = 0; i < nNodes; i++) {
+            var node = this.nodes[i];
+            var speed = node.speed;
+
+            // var swing = vec2.dist(node.force, node.forcePrev);
+            // // var swing = 30;
+            // vec2.scale(node.force, node.force, 1 / (1 + Math.sqrt(swing)));
+            vec2.scale(node.force, node.force, 1 / 30);
+
+            // contraint force
+            var df = vec2.len(node.force) + 0.1;
+            var scale = Math.min(df, 500.0) / df;
+            vec2.scale(node.force, node.force, scale);
+
+            vec2.add(speed, speed, node.force);
+            vec2.scale(speed, speed, this.temperature);
+
+            // Prevent swinging
+            // Limited the increase of speed up to 100% each step
+            // TODO adjust by nodes number
+            // TODO First iterate speed control
+            vec2.sub(v, speed, node.speedPrev);
+            var swing = vec2.len(v);
+            if (swing > 0) {
+                vec2.scale(v, v, 1 / swing);
+                var base = vec2.len(node.speedPrev);
+                if (base > 0) {
+                    swing = Math.min(swing / base, this.maxSpeedIncrease) * base;
+                    vec2.scaleAndAdd(speed, node.speedPrev, v, swing);
+                }
+            }
+
+            // constraint speed
+            var ds = vec2.len(speed);
+            var scale = Math.min(ds, 100.0) / (ds + 0.1);
+            vec2.scale(speed, speed, scale);
+
+            vec2.add(node.position, node.position, speed);
+        }
+    };
+
+    ForceLayout.prototype.updateNodeNodeForce = function () {
+        var nNodes = this.nodes.length;
+        // Compute forces
+        // Repulsion
+        for (var i = 0; i < nNodes; i++) {
+            var na = this.nodes[i];
+            if (this.barnesHutOptimize) {
+                this.applyRegionToNodeRepulsion(this._rootRegion, na);
+            }
+            else {
+                for (var j = i + 1; j < nNodes; j++) {
+                    var nb = this.nodes[j];
+                    this.applyNodeToNodeRepulsion(na, nb, false);
+                }
+            }
+        }
+    };
+
+    ForceLayout.prototype.updateGravityForce = function () {
+        for (var i = 0; i < this.nodes.length; i++) {
+            this.applyNodeGravity(this.nodes[i]);
+        }
+    };
+
+    ForceLayout.prototype.updateEdgeForce = function () {
+        // Attraction
+        for (var i = 0; i < this.edges.length; i++) {
+            this.applyEdgeAttraction(this.edges[i]);
+        }
+    };
+
+    ForceLayout.prototype.updateNodeEdgeForce = function () {
+        for (var i = 0; i < this.nodes.length; i++) {
+            for (var j = 0; j < this.edges.length; j++) {
+                this.applyEdgeToNodeRepulsion(this.edges[j], this.nodes[i]);
+            }
+        }
+    };
+
+    ForceLayout.prototype.applyRegionToNodeRepulsion = (function() {
+        var v = vec2.create();
+        return function applyRegionToNodeRepulsion(region, node) {
+            if (region.node) { // Region is a leaf 
+                this.applyNodeToNodeRepulsion(region.node, node, true);
+            }
+            else {
+                // Static region and node
+                if (region.mass === 0 && node.mass === 0) {
+                    return;
+                }
+                vec2.sub(v, node.position, region.centerOfMass);
+                var d2 = v[0] * v[0] + v[1] * v[1];
+                if (d2 > this.barnesHutTheta * region.size * region.size) {
+                    var factor = this._k * this._k * (node.mass + region.mass) / (d2 + 1);
+                    vec2.scaleAndAdd(node.force, node.force, v, factor * 2);
+                }
+                else {
+                    for (var i = 0; i < region.nSubRegions; i++) {
+                        this.applyRegionToNodeRepulsion(region.subRegions[i], node);
+                    }
+                }
+            }
+        };
+    })();
+
+    ForceLayout.prototype.applyNodeToNodeRepulsion = (function() {
+        var v = vec2.create();
+        return function applyNodeToNodeRepulsion(na, nb, oneWay) {
+            if (na === nb) {
+                return;
+            }
+            // Two static node
+            if (na.mass === 0 && nb.mass === 0) {
+                return;
+            }
+            
+            vec2.sub(v, na.position, nb.position);
+            var d2 = v[0] * v[0] + v[1] * v[1];
+
+            // PENDING
+            if (d2 === 0) {
+                return;
+            }
+
+            var factor;
+            var mass = na.mass + nb.mass;
+            var d = Math.sqrt(d2);
+
+            // Normalize v
+            vec2.scale(v, v, 1 / d);
+
+            if (this.preventNodeOverlap) {
+                d = d - na.size - nb.size;
+                if (d > 0) {
+                    factor = this.nodeToNodeRepulsionFactor(
+                        mass, d, this._k
+                    );
+                }
+                else if (d <= 0) {
+                    // A stronger repulsion if overlap
+                    factor = this._k * this._k * 10 * mass;
+                }
+            }
+            else {
+                factor = this.nodeToNodeRepulsionFactor(
+                    mass, d, this._k
+                );
+            }
+
+            if (!oneWay) {
+                vec2.scaleAndAdd(na.force, na.force, v, factor * 2);
+            }
+            vec2.scaleAndAdd(nb.force, nb.force, v, -factor * 2);
+        };
+    })();
+
+    ForceLayout.prototype.applyEdgeAttraction = (function() {
+        var v = vec2.create();
+        return function applyEdgeAttraction(edge) {
+            var na = edge.node1;
+            var nb = edge.node2;
+
+            vec2.sub(v, na.position, nb.position);
+            var d = vec2.len(v);
+
+            var w;
+            if (this.edgeWeightInfluence === 0) {
+                w = 1;
+            }
+            else if (this.edgeWeightInfluence == 1) {
+                w = edge.weight;
+            }
+            else {
+                w = Math.pow(edge.weight, this.edgeWeightInfluence);
+            }
+
+            var factor;
+
+            if (this.preventOverlap) {
+                d = d - na.size - nb.size;
+                if (d <= 0) {
+                    // No attraction
+                    return;
+                }
+            }
+
+            var factor = this.attractionFactor(w, d, this._k);
+
+            vec2.scaleAndAdd(na.force, na.force, v, -factor);
+            vec2.scaleAndAdd(nb.force, nb.force, v, factor);
+        };
+    })();
+
+    ForceLayout.prototype.applyNodeGravity = (function() {
+        var v = vec2.create();
+        return function(node) {
+            // PENDING Move to centerOfMass or [0, 0] ?
+            // vec2.sub(v, this._rootRegion.centerOfMass, node.position);
+            // vec2.negate(v, node.position);
+            vec2.sub(v, this.center, node.position);
+            if (this.width > this.height) {
+                // Stronger gravity on y axis
+                v[1] *= this.width / this.height;
+            }
+            else {
+                // Stronger gravity on x axis
+                v[0] *= this.height / this.width;
+            }
+            var d = vec2.len(v) / 100;
+            
+            if (this.strongGravity) {
+                vec2.scaleAndAdd(node.force, node.force, v, d * this.gravity * node.mass);
+            }
+            else {
+                vec2.scaleAndAdd(node.force, node.force, v, this.gravity * node.mass / (d + 1));
+            }
+        };
+    })();
+
+    ForceLayout.prototype.applyEdgeToNodeRepulsion = (function () {
+        var v12 = vec2.create();
+        var v13 = vec2.create();
+        var p = vec2.create();
+        return function (e, n3) {
+            var n1 = e.node1;
+            var n2 = e.node2;
+
+            if (n1 === n3 || n2 === n3) {
+                return;
+            }
+
+            vec2.sub(v12, n2.position, n1.position);
+            vec2.sub(v13, n3.position, n1.position);
+
+            var len12 = vec2.len(v12);
+            vec2.scale(v12, v12, 1 / len12);
+            var len = vec2.dot(v12, v13);
+
+            // n3 can't project on line n1-n2
+            if (len < 0 || len > len12) {
+                return;
+            }
+
+            // Project point
+            vec2.scaleAndAdd(p, n1.position, v12, len);
+
+            // n3 distance to line n1-n2
+            var dist = vec2.dist(p, n3.position) - n3.size;
+            var factor = this.edgeToNodeRepulsionFactor(
+                n3.mass, Math.max(dist, 0.1), 100
+            );
+            // Use v12 as normal vector
+            vec2.sub(v12, n3.position, p);
+            vec2.normalize(v12, v12);
+            vec2.scaleAndAdd(n3.force, n3.force, v12, factor);
+
+            // PENDING
+            vec2.scaleAndAdd(n1.force, n1.force, v12, -factor);
+            vec2.scaleAndAdd(n2.force, n2.force, v12, -factor);
+        };
+    })();
+
+    ForceLayout.prototype.updateBBox = function() {
+        var minX = Infinity;
+        var minY = Infinity;
+        var maxX = -Infinity;
+        var maxY = -Infinity;
+        for (var i = 0; i < this.nodes.length; i++) {
+            var pos = this.nodes[i].position;
+            minX = Math.min(minX, pos[0]);
+            minY = Math.min(minY, pos[1]);
+            maxX = Math.max(maxX, pos[0]);
+            maxY = Math.max(maxY, pos[1]);
+        }
+        this.bbox[0] = minX;
+        this.bbox[1] = minY;
+        this.bbox[2] = maxX;
+        this.bbox[3] = maxY;
+    };
+
+    ForceLayout.getWorkerCode = function() {
+        var str = __echartsForceLayoutWorker.toString();
+        return str.slice(str.indexOf('{') + 1, str.lastIndexOf('return'));
+    };
+
+    /****************************
+     * Main process
+     ***************************/
+
+    /* jshint ignore:start */
+    if (inWorker) {
+        var forceLayout = null;
+        
+        self.onmessage = function(e) {
+            // Position read back
+            if (e.data instanceof ArrayBuffer) {
+                if (!forceLayout) return;
+
+                var positionArr = new Float32Array(e.data);
+                var nNodes = positionArr.length / 2;
+                for (var i = 0; i < nNodes; i++) {
+                    var node = forceLayout.nodes[i];
+                    node.position[0] = positionArr[i * 2];
+                    node.position[1] = positionArr[i * 2 + 1];
+                }
+                return;
+            }
+
+            switch(e.data.cmd) {
+                case 'init':
+                    if (!forceLayout) {
+                        forceLayout = new ForceLayout();
+                    }
+                    forceLayout.initNodes(e.data.nodesPosition, e.data.nodesMass, e.data.nodesSize);
+                    forceLayout.initEdges(e.data.edges, e.data.edgesWeight);
+                    break;
+                case 'updateConfig':
+                    if (forceLayout) {
+                        for (var name in e.data.config) {
+                            forceLayout[name] = e.data.config[name];
+                        }
+                    }
+                    break;
+                case 'update':
+                    var steps = e.data.steps;
+
+                    if (forceLayout) {
+                        var nNodes = forceLayout.nodes.length;
+                        var positionArr = new Float32Array(nNodes * 2);
+
+                        forceLayout.temperature = e.data.temperature;
+
+                        for (var i = 0; i < steps; i++) {
+                            forceLayout.update();
+                            forceLayout.temperature *= e.data.coolDown;
+                        }
+                        // Callback
+                        for (var i = 0; i < nNodes; i++) {
+                            var node = forceLayout.nodes[i];
+                            positionArr[i * 2] = node.position[0];
+                            positionArr[i * 2 + 1] = node.position[1];
+                        }
+
+                        self.postMessage(positionArr.buffer, [positionArr.buffer]);
+                    }
+                    else {
+                        // Not initialzied yet
+                        var emptyArr = new Float32Array();
+                        // Post transfer object
+                        self.postMessage(emptyArr.buffer, [emptyArr.buffer]);
+                    }
+                    break;
+            }
+        };
+    }
+    /* jshint ignore:end */
+
+    return ForceLayout;
+});

+ 13 - 0
src/main/webapp/static/echarts-2.2.7/src/theme/default.js

@@ -0,0 +1,13 @@
+/**
+ * echarts默认主题,开发中
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function() {
+    var config = {
+    };
+
+    return config;
+});

+ 297 - 0
src/main/webapp/static/echarts-2.2.7/src/theme/infographic.js

@@ -0,0 +1,297 @@
+define(function() {
+
+var theme = {
+    // 默认色板
+    color: [
+        '#C1232B','#B5C334','#FCCE10','#E87C25','#27727B',
+        '#FE8463','#9BCA63','#FAD860','#F3A43B','#60C0DD',
+        '#D7504B','#C6E579','#F4E001','#F0805A','#26C0C0'
+    ],
+
+    // 图表标题
+    title: {
+        textStyle: {
+            fontWeight: 'normal',
+            color: '#27727B'          // 主标题文字颜色
+        }
+    },
+
+    // 值域
+    dataRange: {
+        x:'right',
+        y:'center',
+        itemWidth: 5,
+        itemHeight:25,
+        color:['#C1232B','#FCCE10']
+    },
+
+    toolbox: {
+        color : [
+            '#C1232B','#B5C334','#FCCE10','#E87C25','#27727B',
+            '#FE8463','#9BCA63','#FAD860','#F3A43B','#60C0DD'
+        ],
+        effectiveColor : '#ff4500'
+    },
+
+    // 提示框
+    tooltip: {
+        backgroundColor: 'rgba(50,50,50,0.5)',     // 提示背景颜色,默认为透明度为0.7的黑色
+        axisPointer : {            // 坐标轴指示器,坐标轴触发有效
+            type : 'line',         // 默认为直线,可选为:'line' | 'shadow'
+            lineStyle : {          // 直线指示器样式设置
+                color: '#27727B',
+                type: 'dashed'
+            },
+            crossStyle: {
+                color: '#27727B'
+            },
+            shadowStyle : {                     // 阴影指示器样式设置
+                color: 'rgba(200,200,200,0.3)'
+            }
+        }
+    },
+
+    // 区域缩放控制器
+    dataZoom: {
+        dataBackgroundColor: 'rgba(181,195,52,0.3)',            // 数据背景颜色
+        fillerColor: 'rgba(181,195,52,0.2)',   // 填充颜色
+        handleColor: '#27727B'    // 手柄颜色
+    },
+
+    // 网格
+    grid: {
+        borderWidth:0
+    },
+
+    // 类目轴
+    categoryAxis: {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#27727B'
+            }
+        },
+        splitLine: {           // 分隔线
+            show: false
+        }
+    },
+
+    // 数值型坐标轴默认参数
+    valueAxis: {
+        axisLine: {            // 坐标轴线
+            show: false
+        },
+        splitArea : {
+            show: false
+        },
+        splitLine: {           // 分隔线
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: ['#ccc'],
+                type: 'dashed'
+            }
+        }
+    },
+
+    polar : {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#ddd'
+            }
+        },
+        splitArea : {
+            show : true,
+            areaStyle : {
+                color: ['rgba(250,250,250,0.2)','rgba(200,200,200,0.2)']
+            }
+        },
+        splitLine : {
+            lineStyle : {
+                color : '#ddd'
+            }
+        }
+    },
+
+    timeline : {
+        lineStyle : {
+            color : '#27727B'
+        },
+        controlStyle : {
+            normal : { color : '#27727B'},
+            emphasis : { color : '#27727B'}
+        },
+        symbol : 'emptyCircle',
+        symbolSize : 3
+    },
+
+    // 折线图默认参数
+    line: {
+        itemStyle: {
+            normal: {
+                borderWidth:2,
+                borderColor:'#fff',
+                lineStyle: {
+                    width: 3
+                }
+            },
+            emphasis: {
+                borderWidth:0
+            }
+        },
+        symbol: 'circle',  // 拐点图形类型
+        symbolSize: 3.5           // 拐点图形大小
+    },
+
+    // K线图默认参数
+    k: {
+        itemStyle: {
+            normal: {
+                color: '#C1232B',       // 阳线填充颜色
+                color0: '#B5C334',      // 阴线填充颜色
+                lineStyle: {
+                    width: 1,
+                    color: '#C1232B',   // 阳线边框颜色
+                    color0: '#B5C334'   // 阴线边框颜色
+                }
+            }
+        }
+    },
+
+    // 散点图默认参数
+    scatter: {
+        itemStyle: {
+            normal: {
+                borderWidth:1,
+                borderColor:'rgba(200,200,200,0.5)'
+            },
+            emphasis: {
+                borderWidth:0
+            }
+        },
+        symbol: 'star4',    // 图形类型
+        symbolSize: 4        // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
+    },
+
+    // 雷达图默认参数
+    radar : {
+        symbol: 'emptyCircle',    // 图形类型
+        symbolSize:3
+        //symbol: null,         // 拐点图形类型
+        //symbolRotate : null,  // 图形旋转控制
+    },
+
+    map: {
+        itemStyle: {
+            normal: {
+                areaStyle: {
+                    color: '#ddd'
+                },
+                label: {
+                    textStyle: {
+                        color: '#C1232B'
+                    }
+                }
+            },
+            emphasis: {                 // 也是选中样式
+                areaStyle: {
+                    color: '#fe994e'
+                },
+                label: {
+                    textStyle: {
+                        color: 'rgb(100,0,0)'
+                    }
+                }
+            }
+        }
+    },
+
+    force : {
+        itemStyle: {
+            normal: {
+                linkStyle : {
+                    color : '#27727B'
+                }
+            }
+        }
+    },
+
+    chord : {
+        itemStyle : {
+            normal : {
+                borderWidth: 1,
+                borderColor: 'rgba(128, 128, 128, 0.5)',
+                chordStyle : {
+                    lineStyle : {
+                        color : 'rgba(128, 128, 128, 0.5)'
+                    }
+                }
+            },
+            emphasis : {
+                borderWidth: 1,
+                borderColor: 'rgba(128, 128, 128, 0.5)',
+                chordStyle : {
+                    lineStyle : {
+                        color : 'rgba(128, 128, 128, 0.5)'
+                    }
+                }
+            }
+        }
+    },
+
+    gauge : {
+        center:['50%','80%'],
+        radius:'100%',
+        startAngle: 180,
+        endAngle : 0,
+        axisLine: {            // 坐标轴线
+            show: true,        // 默认显示,属性show控制显示与否
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: [[0.2, '#B5C334'],[0.8, '#27727B'],[1, '#C1232B']],
+                width: '40%'
+            }
+        },
+        axisTick: {            // 坐标轴小标记
+            splitNumber: 2,   // 每份split细分多少段
+            length: 5,        // 属性length控制线长
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#fff'
+            }
+        },
+        axisLabel: {           // 坐标轴文本标签,详见axis.axisLabel
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: '#fff',
+                fontWeight:'bolder'
+            }
+        },
+        splitLine: {           // 分隔线
+            length: '5%',         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: '#fff'
+            }
+        },
+        pointer : {
+            width : '40%',
+            length: '80%',
+            color: '#fff'
+        },
+        title : {
+          offsetCenter: [0, -20],       // x, y,单位px
+          textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+            color: 'auto',
+            fontSize: 20
+          }
+        },
+        detail : {
+            offsetCenter: [0, 0],       // x, y,单位px
+            textStyle: {       // 其余属性默认使用全局文本样式,详见TEXTSTYLE
+                color: 'auto',
+                fontSize: 40
+            }
+        }
+    },
+
+    textStyle: {
+        fontFamily: '微软雅黑, Arial, Verdana, sans-serif'
+    }
+};
+
+    return theme;
+});

+ 257 - 0
src/main/webapp/static/echarts-2.2.7/src/theme/macarons.js

@@ -0,0 +1,257 @@
+define(function() {
+
+var theme = {
+    // 默认色板
+    color: [
+        '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80',
+        '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
+        '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
+        '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
+    ],
+
+    // 图表标题
+    title: {
+        textStyle: {
+            fontWeight: 'normal',
+            color: '#008acd'          // 主标题文字颜色
+        }
+    },
+    
+    // 值域
+    dataRange: {
+        itemWidth: 15,
+        color: ['#5ab1ef','#e0ffff']
+    },
+
+    // 工具箱
+    toolbox: {
+        color : ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'],
+        effectiveColor : '#ff4500'
+    },
+
+    // 提示框
+    tooltip: {
+        backgroundColor: 'rgba(50,50,50,0.5)',     // 提示背景颜色,默认为透明度为0.7的黑色
+        axisPointer : {            // 坐标轴指示器,坐标轴触发有效
+            type : 'line',         // 默认为直线,可选为:'line' | 'shadow'
+            lineStyle : {          // 直线指示器样式设置
+                color: '#008acd'
+            },
+            crossStyle: {
+                color: '#008acd'
+            },
+            shadowStyle : {                     // 阴影指示器样式设置
+                color: 'rgba(200,200,200,0.2)'
+            }
+        }
+    },
+
+    // 区域缩放控制器
+    dataZoom: {
+        dataBackgroundColor: '#efefff',            // 数据背景颜色
+        fillerColor: 'rgba(182,162,222,0.2)',   // 填充颜色
+        handleColor: '#008acd'    // 手柄颜色
+    },
+
+    // 网格
+    grid: {
+        borderColor: '#eee'
+    },
+
+    // 类目轴
+    categoryAxis: {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#008acd'
+            }
+        },
+        splitLine: {           // 分隔线
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: ['#eee']
+            }
+        }
+    },
+
+    // 数值型坐标轴默认参数
+    valueAxis: {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#008acd'
+            }
+        },
+        splitArea : {
+            show : true,
+            areaStyle : {
+                color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
+            }
+        },
+        splitLine: {           // 分隔线
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: ['#eee']
+            }
+        }
+    },
+
+    polar : {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: '#ddd'
+            }
+        },
+        splitArea : {
+            show : true,
+            areaStyle : {
+                color: ['rgba(250,250,250,0.2)','rgba(200,200,200,0.2)']
+            }
+        },
+        splitLine : {
+            lineStyle : {
+                color : '#ddd'
+            }
+        }
+    },
+
+    timeline : {
+        lineStyle : {
+            color : '#008acd'
+        },
+        controlStyle : {
+            normal : { color : '#008acd'},
+            emphasis : { color : '#008acd'}
+        },
+        symbol : 'emptyCircle',
+        symbolSize : 3
+    },
+
+    // 柱形图默认参数
+    bar: {
+        itemStyle: {
+            normal: {
+                barBorderRadius: 5
+            },
+            emphasis: {
+                barBorderRadius: 5
+            }
+        }
+    },
+
+    // 折线图默认参数
+    line: {
+        smooth : true,
+        symbol: 'emptyCircle',  // 拐点图形类型
+        symbolSize: 3           // 拐点图形大小
+    },
+    
+    // K线图默认参数
+    k: {
+        itemStyle: {
+            normal: {
+                color: '#d87a80',       // 阳线填充颜色
+                color0: '#2ec7c9',      // 阴线填充颜色
+                lineStyle: {
+                    color: '#d87a80',   // 阳线边框颜色
+                    color0: '#2ec7c9'   // 阴线边框颜色
+                }
+            }
+        }
+    },
+    
+    // 散点图默认参数
+    scatter: {
+        symbol: 'circle',    // 图形类型
+        symbolSize: 4        // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
+    },
+
+    // 雷达图默认参数
+    radar : {
+        symbol: 'emptyCircle',    // 图形类型
+        symbolSize:3
+        //symbol: null,         // 拐点图形类型
+        //symbolRotate : null,  // 图形旋转控制
+    },
+
+    map: {
+        itemStyle: {
+            normal: {
+                areaStyle: {
+                    color: '#ddd'
+                },
+                label: {
+                    textStyle: {
+                        color: '#d87a80'
+                    }
+                }
+            },
+            emphasis: {                 // 也是选中样式
+                areaStyle: {
+                    color: '#fe994e'
+                }
+            }
+        }
+    },
+    
+    force : {
+        itemStyle: {
+            normal: {
+                linkStyle : {
+                    color : '#1e90ff'
+                }
+            }
+        }
+    },
+
+    chord : {
+        itemStyle : {
+            normal : {
+                borderWidth: 1,
+                borderColor: 'rgba(128, 128, 128, 0.5)',
+                chordStyle : {
+                    lineStyle : {
+                        color : 'rgba(128, 128, 128, 0.5)'
+                    }
+                }
+            },
+            emphasis : {
+                borderWidth: 1,
+                borderColor: 'rgba(128, 128, 128, 0.5)',
+                chordStyle : {
+                    lineStyle : {
+                        color : 'rgba(128, 128, 128, 0.5)'
+                    }
+                }
+            }
+        }
+    },
+
+    gauge : {
+        axisLine: {            // 坐标轴线
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']], 
+                width: 10
+            }
+        },
+        axisTick: {            // 坐标轴小标记
+            splitNumber: 10,   // 每份split细分多少段
+            length :15,        // 属性length控制线长
+            lineStyle: {       // 属性lineStyle控制线条样式
+                color: 'auto'
+            }
+        },
+        splitLine: {           // 分隔线
+            length :22,         // 属性length控制线长
+            lineStyle: {       // 属性lineStyle(详见lineStyle)控制线条样式
+                color: 'auto'
+            }
+        },
+        pointer : {
+            width : 5
+        }
+    },
+    
+    textStyle: {
+        fontFamily: '微软雅黑, Arial, Verdana, sans-serif'
+    }
+};
+
+    return theme;
+});

+ 79 - 0
src/main/webapp/static/echarts-2.2.7/src/util/accMath.js

@@ -0,0 +1,79 @@
+/**
+ * 高精度数学运算
+ */
+define(function() {
+    // 除法函数,用来得到精确的除法结果 
+    // 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 
+    // 调用:accDiv(arg1,arg2) 
+    // 返回值:arg1除以arg2的精确结果
+    function accDiv(arg1,arg2){
+        var s1 = arg1.toString();
+        var s2 = arg2.toString(); 
+        var m = 0;
+        try {
+            m = s2.split('.')[1].length;
+        }
+        catch(e) {}
+        try {
+            m -= s1.split('.')[1].length;
+        }
+        catch(e) {}
+        
+        return (s1.replace('.', '') - 0) / (s2.replace('.', '') - 0) * Math.pow(10, m);
+    }
+
+    // 乘法函数,用来得到精确的乘法结果
+    // 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 
+    // 调用:accMul(arg1,arg2) 
+    // 返回值:arg1乘以arg2的精确结果
+    function accMul(arg1, arg2) {
+        var s1 = arg1.toString();
+        var s2 = arg2.toString();
+        var m = 0;
+        try {
+            m += s1.split('.')[1].length;
+        }
+        catch(e) {}
+        try {
+            m += s2.split('.')[1].length;
+        }
+        catch(e) {}
+        
+        return (s1.replace('.', '') - 0) * (s2.replace('.', '') - 0) / Math.pow(10, m);
+    }
+
+    // 加法函数,用来得到精确的加法结果 
+    // 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。 
+    // 调用:accAdd(arg1,arg2) 
+    // 返回值:arg1加上arg2的精确结果 
+    function accAdd(arg1, arg2) {
+        var r1 = 0;
+        var r2 = 0;
+        try {
+            r1 = arg1.toString().split('.')[1].length;
+        }
+        catch(e) {}
+        try {
+            r2 = arg2.toString().split('.')[1].length;
+        }
+        catch(e) {}
+        
+        var m = Math.pow(10, Math.max(r1, r2));
+        return (Math.round(arg1 * m) + Math.round(arg2 * m)) / m; 
+    }
+
+    //减法函数,用来得到精确的减法结果 
+    //说明:javascript的减法结果会有误差,在两个浮点数减法的时候会比较明显。这个函数返回较为精确的减法结果。 
+    //调用:accSub(arg1,arg2) 
+    //返回值:arg1减法arg2的精确结果 
+    function accSub(arg1,arg2) {
+        return accAdd(arg1, -arg2);
+    }
+
+    return {
+        accDiv : accDiv,
+        accMul : accMul,
+        accAdd : accAdd,
+        accSub : accSub
+    };
+});

+ 40 - 0
src/main/webapp/static/echarts-2.2.7/src/util/coordinates.js

@@ -0,0 +1,40 @@
+/**
+ * echarts坐标处理方法
+ *
+ * @author Neil (杨骥, 511415343@qq.com)
+ */
+
+define(
+    function (require) {
+        var zrMath = require('zrender/tool/math');
+
+        /**
+         * 极坐标转直角坐标
+         *
+         * @param {number} 半径
+         * @param {number} 角度
+         *
+         * @return {Array.<number>} 直角坐标[x,y]
+         */
+        function polar2cartesian(r, theta) {
+            return [r * zrMath.sin(theta), r*zrMath.cos(theta)];
+        }
+
+        /**
+         * 直角坐标转极坐标
+         *
+         * @param {number} 横坐标
+         * @param {number} 纵坐标
+         *
+         * @return {Array.<number>} 极坐标[r,theta]
+         */
+        function cartesian2polar(x, y) {
+            return [Math.sqrt(x * x + y * y), Math.atan(y / x)];
+        }
+
+        return {
+            polar2cartesian : polar2cartesian,
+            cartesian2polar : cartesian2polar
+        };
+    }
+);

+ 162 - 0
src/main/webapp/static/echarts-2.2.7/src/util/date.js

@@ -0,0 +1,162 @@
+/**
+ * echarts日期运算格式化相关
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function() {
+    var _timeGap = [
+        {formatter: 'hh : mm : ss', value: 1000},               // 1s
+        {formatter: 'hh : mm : ss', value: 1000 * 5},           // 5s
+        {formatter: 'hh : mm : ss', value: 1000 * 10},          // 10s
+        {formatter: 'hh : mm : ss', value: 1000 * 15},          // 15s
+        {formatter: 'hh : mm : ss', value: 1000 * 30},          // 30s
+        {formatter: 'hh : mm\nMM - dd', value: 60000},          // 1m
+        {formatter: 'hh : mm\nMM - dd', value: 60000 * 5},      // 5m
+        {formatter: 'hh : mm\nMM - dd', value: 60000 * 10},     // 10m
+        {formatter: 'hh : mm\nMM - dd', value: 60000 * 15},     // 15m
+        {formatter: 'hh : mm\nMM - dd', value: 60000 * 30},     // 30m
+        {formatter: 'hh : mm\nMM - dd', value: 3600000},        // 1h
+        {formatter: 'hh : mm\nMM - dd', value: 3600000 * 2},    // 2h
+        {formatter: 'hh : mm\nMM - dd', value: 3600000 * 6},    // 6h
+        {formatter: 'hh : mm\nMM - dd', value: 3600000 * 12},   // 12h
+        {formatter: 'MM - dd\nyyyy', value: 3600000 * 24},      // 1d
+        {formatter: 'week', value: 3600000 * 24 * 7},           // 7d
+        {formatter: 'month', value: 3600000 * 24 * 31},         // 1M
+        {formatter: 'quarter', value: 3600000 * 24 * 380 / 4},  // 3M
+        {formatter: 'half-year', value: 3600000 * 24 * 380 / 2},// 6M
+        {formatter: 'year', value: 3600000 * 24 * 380}          // 1Y
+    ];
+    
+    /**
+     * 获取最佳formatter
+     * @params {number} min 最小值
+     * @params {number} max 最大值
+     * @params {=number} splitNumber 分隔段数
+     */
+    function getAutoFormatter(min, max, splitNumber) {
+        splitNumber = splitNumber > 1 ? splitNumber : 2;
+        // 最优解
+        var curValue;
+        var totalGap;
+        // 目标
+        var formatter;
+        var gapValue;
+        for (var i = 0, l = _timeGap.length; i < l; i++) {
+            curValue = _timeGap[i].value;
+            totalGap = Math.ceil(max / curValue) * curValue 
+                       - Math.floor(min / curValue) * curValue;
+            if (Math.round(totalGap / curValue) <= splitNumber * 1.2) {
+                formatter =  _timeGap[i].formatter;
+                gapValue = _timeGap[i].value;
+                // console.log(formatter, gapValue,i);
+                break;
+            }
+        }
+        
+        if (formatter == null) {
+            formatter = 'year';
+            curValue = 3600000 * 24 * 367;
+            totalGap = Math.ceil(max / curValue) * curValue 
+                       - Math.floor(min / curValue) * curValue;
+            gapValue = Math.round(totalGap / (splitNumber - 1) / curValue) * curValue;
+        }
+        
+        return {
+            formatter: formatter,
+            gapValue: gapValue
+        };
+    }
+    
+    /**
+     * 一位数字补0 
+     */
+    function s2d (v) {
+        return v < 10 ? ('0' + v) : v;
+    }
+    
+    /**
+     * 百分比计算
+     */
+    function format(formatter, value) {
+        if (formatter == 'week' 
+            || formatter == 'month' 
+            || formatter == 'quarter' 
+            || formatter == 'half-year'
+            || formatter == 'year'
+        ) {
+            formatter = 'MM - dd\nyyyy';
+        }
+            
+        var date = getNewDate(value);
+        var y = date.getFullYear();
+        var M = date.getMonth() + 1;
+        var d = date.getDate();
+        var h = date.getHours();
+        var m = date.getMinutes();
+        var s = date.getSeconds();
+        
+        formatter = formatter.replace('MM', s2d(M));
+        formatter = formatter.toLowerCase();
+        formatter = formatter.replace('yyyy', y);
+        formatter = formatter.replace('yy', y % 100);
+        formatter = formatter.replace('dd', s2d(d));
+        formatter = formatter.replace('d', d);
+        formatter = formatter.replace('hh', s2d(h));
+        formatter = formatter.replace('h', h);
+        formatter = formatter.replace('mm', s2d(m));
+        formatter = formatter.replace('m', m);
+        formatter = formatter.replace('ss', s2d(s));
+        formatter = formatter.replace('s', s);
+
+        return formatter;
+    }
+    
+    function nextMonday(value) {
+        value = getNewDate(value);
+        value.setDate(value.getDate() + 8 - value.getDay());
+        return value;
+    }
+    
+    function nextNthPerNmonth(value, nth, nmon) {
+        value = getNewDate(value);
+        value.setMonth(Math.ceil((value.getMonth() + 1) / nmon) * nmon);
+        value.setDate(nth);
+        return value;
+    }
+    
+    function nextNthOnMonth(value, nth) {
+        return nextNthPerNmonth(value, nth, 1);
+    }
+    
+    function nextNthOnQuarterYear(value, nth) {
+        return nextNthPerNmonth(value, nth, 3);
+    }
+    
+    function nextNthOnHalfYear(value, nth) {
+        return nextNthPerNmonth(value, nth, 6);
+    }
+    
+    function nextNthOnYear(value, nth) {
+        return nextNthPerNmonth(value, nth, 12);
+    }
+    
+    function getNewDate(value) {
+        return value instanceof Date
+               ? value
+               : new Date(typeof value == 'string' ? value.replace(/-/g, '/') : value);
+    }
+    
+    return {
+        getAutoFormatter: getAutoFormatter,
+        getNewDate: getNewDate,
+        format: format,
+        nextMonday: nextMonday,
+        nextNthPerNmonth: nextNthPerNmonth,
+        nextNthOnMonth: nextNthOnMonth,
+        nextNthOnQuarterYear: nextNthOnQuarterYear,
+        nextNthOnHalfYear: nextNthOnHalfYear,
+        nextNthOnYear : nextNthOnYear
+    };
+});

+ 624 - 0
src/main/webapp/static/echarts-2.2.7/src/util/ecAnimation.js

@@ -0,0 +1,624 @@
+/**
+ * echarts图表动画基类
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var zrUtil = require('zrender/tool/util');
+    var curveTool = require('zrender/tool/curve');
+    
+    /**
+     * 折线型动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function pointList(zr, oldShape, newShape, duration, easing) {
+        var newPointList = newShape.style.pointList;
+        var newPointListLen = newPointList.length;
+        var oldPointList;
+
+        if (!oldShape) {        // add
+            oldPointList = [];
+            if (newShape._orient != 'vertical') {
+                var y = newPointList[0][1];
+                for (var i = 0; i < newPointListLen; i++) {
+                    oldPointList[i] = [newPointList[i][0], y];
+                }
+            }
+            else {
+                var x = newPointList[0][0];
+                for (var i = 0; i < newPointListLen; i++) {
+                    oldPointList[i] = [x, newPointList[i][1]];
+                }
+            }
+
+            if (newShape.type == 'half-smooth-polygon') {
+                oldPointList[newPointListLen - 1] = zrUtil.clone(newPointList[newPointListLen - 1]);
+                oldPointList[newPointListLen - 2] = zrUtil.clone(newPointList[newPointListLen - 2]);
+            }
+            oldShape = {style : {pointList : oldPointList}};
+        }
+        
+        oldPointList = oldShape.style.pointList;
+        var oldPointListLen = oldPointList.length;
+        if (oldPointListLen == newPointListLen) {
+            newShape.style.pointList = oldPointList;
+        }
+        else if (oldPointListLen < newPointListLen) {
+            // 原来短,新的长,补全
+            newShape.style.pointList = oldPointList.concat(newPointList.slice(oldPointListLen));
+        }
+        else {
+            // 原来长,新的短,截断
+            newShape.style.pointList = oldPointList.slice(0, newPointListLen);
+        }
+
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                { pointList: newPointList }
+            )
+            .during(function () {
+                // Updating bezier points
+                if (newShape.updateControlPoints) {
+                    newShape.updateControlPoints(newShape.style);
+                }
+            })
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * 复制样式
+     * 
+     * @inner
+     * @param {Object} target 目标对象
+     * @param {Object} source 源对象
+     * @param {...string} props 复制的属性列表
+     */
+    function cloneStyle(target, source) {
+        var len = arguments.length;
+        for (var i = 2; i < len; i++) {
+            var prop = arguments[i];
+            target.style[prop] = source.style[prop];
+        }
+    }
+
+    /**
+     * 方型动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function rectangle(zr, oldShape, newShape, duration, easing) {
+        var newShapeStyle = newShape.style;
+        if (!oldShape) {        // add
+            oldShape = {
+                position : newShape.position,
+                style : {
+                    x : newShapeStyle.x,
+                    y : newShape._orient == 'vertical'
+                        ? newShapeStyle.y + newShapeStyle.height
+                        : newShapeStyle.y,
+                    width: newShape._orient == 'vertical' 
+                           ? newShapeStyle.width : 0,
+                    height: newShape._orient != 'vertical' 
+                           ? newShapeStyle.height : 0
+                }
+            };
+        }
+        
+        var newX = newShapeStyle.x;
+        var newY = newShapeStyle.y;
+        var newWidth = newShapeStyle.width;
+        var newHeight = newShapeStyle.height;
+        var newPosition = [newShape.position[0], newShape.position[1]];
+        cloneStyle(
+            newShape, oldShape,
+            'x', 'y', 'width', 'height'
+        );
+        newShape.position = oldShape.position;
+
+        zr.addShape(newShape);
+        if (newPosition[0] != oldShape.position[0] || newPosition[1] != oldShape.position[1]) {
+            zr.animate(newShape.id, '')
+                .when(
+                    duration,
+                    {
+                        position: newPosition
+                    }
+                )
+                .start(easing);
+        }
+        
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    x: newX,
+                    y: newY,
+                    width: newWidth,
+                    height: newHeight
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * 蜡烛动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function candle(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {        // add
+            var y = newShape.style.y;
+            oldShape = {style : {y : [y[0], y[0], y[0], y[0]]}};
+        }
+        
+        var newY = newShape.style.y;
+        newShape.style.y = oldShape.style.y;
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                { y: newY }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+
+    /**
+     * 环型动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function ring(zr, oldShape, newShape, duration, easing) {
+        var x = newShape.style.x;
+        var y = newShape.style.y;
+        var r0 = newShape.style.r0;
+        var r = newShape.style.r;
+        
+        newShape.__animating = true;
+
+        if (newShape._animationAdd != 'r') {
+            newShape.style.r0 = 0;
+            newShape.style.r = 0;
+            newShape.rotation = [Math.PI*2, x, y];
+            
+            zr.addShape(newShape);
+            zr.animate(newShape.id, 'style')
+                .when(
+                    duration,
+                    {
+                        r0 : r0,
+                        r : r
+                    }
+                )
+                .done(function() {
+                    newShape.__animating = false;
+                })
+                .start(easing);
+            zr.animate(newShape.id, '')
+                .when(
+                    duration,
+                    { rotation : [0, x, y] }
+                )
+                .start(easing);
+        }
+        else {
+            newShape.style.r0 = newShape.style.r;
+            
+            zr.addShape(newShape);
+            zr.animate(newShape.id, 'style')
+                .when(
+                    duration,
+                    {
+                        r0 : r0
+                    }
+                )
+                .done(function() {
+                    newShape.__animating = false;
+                })
+                .start(easing);
+        }
+    }
+    
+    /**
+     * 扇形动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function sector(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {        // add
+            if (newShape._animationAdd != 'r') {
+                oldShape = {
+                    style : {
+                        startAngle : newShape.style.startAngle,
+                        endAngle : newShape.style.startAngle
+                    }
+                };
+            }
+            else {
+                oldShape = {style : {r0 : newShape.style.r}};
+            }
+        }
+        
+        var startAngle = newShape.style.startAngle;
+        var endAngle = newShape.style.endAngle;
+        
+        cloneStyle(
+            newShape, oldShape,
+            'startAngle', 'endAngle'
+        );
+        
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    startAngle : startAngle,
+                    endAngle : endAngle
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * 文本动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function text(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {        // add
+            oldShape = {
+                style : {
+                    x : newShape.style.textAlign == 'left' 
+                        ? newShape.style.x + 100
+                        : newShape.style.x - 100,
+                    y : newShape.style.y
+                }
+            };
+        }
+        
+        var x = newShape.style.x;
+        var y = newShape.style.y;
+        
+        cloneStyle(
+            newShape, oldShape,
+            'x', 'y'
+        );
+        
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    x : x,
+                    y : y
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * 多边形动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function polygon(zr, oldShape, newShape, duration, easing) {
+        var rect = require('zrender/shape/Polygon').prototype.getRect(newShape.style);
+        var x = rect.x + rect.width / 2;
+        var y = rect.y + rect.height / 2;
+        
+        newShape.scale = [0.1, 0.1, x, y];
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, '')
+            .when(
+                duration,
+                {
+                    scale : [1, 1, x, y]
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * 和弦动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function ribbon(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {        // add
+            oldShape = {
+                style : {
+                    source0 : 0,
+                    source1 : newShape.style.source1 > 0 ? 360 : -360,
+                    target0 : 0,
+                    target1 : newShape.style.target1 > 0 ? 360 : -360
+                }
+            };
+        }
+        
+        var source0 = newShape.style.source0;
+        var source1 = newShape.style.source1;
+        var target0 = newShape.style.target0;
+        var target1 = newShape.style.target1;
+        
+        if (oldShape.style) {
+            cloneStyle(
+                newShape, oldShape,
+                'source0', 'source1', 'target0', 'target1'
+            );
+        }
+        
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    source0 : source0,
+                    source1 : source1,
+                    target0 : target0,
+                    target1 : target1
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * gaugePointer动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function gaugePointer(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {        // add
+            oldShape = {
+                style : {
+                    angle : newShape.style.startAngle
+                }
+            };
+        }
+        
+        var angle = newShape.style.angle;
+        newShape.style.angle = oldShape.style.angle;
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    angle : angle
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * icon动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function icon(zr, oldShape, newShape, duration, easing, delay) {
+        // 避免markPoint特效取值在动画帧上
+        newShape.style._x = newShape.style.x;
+        newShape.style._y = newShape.style.y;
+        newShape.style._width = newShape.style.width;
+        newShape.style._height = newShape.style.height;
+
+        if (!oldShape) {    // add
+            var x = newShape._x || 0;
+            var y = newShape._y || 0;
+            newShape.scale = [0.01, 0.01, x, y];
+            zr.addShape(newShape);
+            newShape.__animating = true;
+            zr.animate(newShape.id, '')
+                .delay(delay)
+                .when(
+                    duration,
+                    {scale : [1, 1, x, y]}
+                )
+                .done(function() {
+                    newShape.__animating = false;
+                })
+                .start(easing || 'QuinticOut');
+        }
+        else {              // mod
+            rectangle(zr, oldShape, newShape, duration, easing);
+        }
+    }
+    
+    /**
+     * line动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function line(zr, oldShape, newShape, duration, easing) {
+        if (!oldShape) {
+            oldShape = {
+                style : {
+                    xStart : newShape.style.xStart,
+                    yStart : newShape.style.yStart,
+                    xEnd : newShape.style.xStart,
+                    yEnd : newShape.style.yStart
+                }
+            };
+        }
+        
+        var xStart = newShape.style.xStart;
+        var xEnd = newShape.style.xEnd;
+        var yStart = newShape.style.yStart;
+        var yEnd = newShape.style.yEnd;
+
+        cloneStyle(
+            newShape, oldShape,
+            'xStart', 'xEnd', 'yStart', 'yEnd'
+        );
+
+        zr.addShape(newShape);
+        newShape.__animating = true;
+        zr.animate(newShape.id, 'style')
+            .when(
+                duration,
+                {
+                    xStart: xStart,
+                    xEnd: xEnd,
+                    yStart: yStart,
+                    yEnd: yEnd
+                }
+            )
+            .done(function() {
+                newShape.__animating = false;
+            })
+            .start(easing);
+    }
+    
+    /**
+     * markline动画
+     * 
+     * @param {ZRender} zr
+     * @param {shape} oldShape
+     * @param {shape} newShape
+     * @param {number} duration
+     * @param {tring} easing
+     */
+    function markline(zr, oldShape, newShape, duration, easing) {
+        easing = easing || 'QuinticOut';
+        newShape.__animating = true;
+        zr.addShape(newShape);
+        var newShapeStyle = newShape.style;
+
+        var animationDone = function () {
+            newShape.__animating = false;
+        };
+        var x0 = newShapeStyle.xStart;
+        var y0 = newShapeStyle.yStart;
+        var x2 = newShapeStyle.xEnd;
+        var y2 = newShapeStyle.yEnd;
+        if (newShapeStyle.curveness > 0) {
+            newShape.updatePoints(newShapeStyle);
+            var obj = { p: 0 };
+            var x1 = newShapeStyle.cpX1;
+            var y1 = newShapeStyle.cpY1;
+            var newXArr = [];
+            var newYArr = [];
+            var subdivide = curveTool.quadraticSubdivide;
+            zr.animation.animate(obj)
+                .when(duration, { p: 1 })
+                .during(function () {
+                    // Calculate subdivided curve
+                    subdivide(x0, x1, x2, obj.p, newXArr);
+                    subdivide(y0, y1, y2, obj.p, newYArr);
+                    newShapeStyle.cpX1 = newXArr[1];
+                    newShapeStyle.cpY1 = newYArr[1];
+                    newShapeStyle.xEnd = newXArr[2];
+                    newShapeStyle.yEnd = newYArr[2];
+                    zr.modShape(newShape);
+                })
+                .done(animationDone)
+                .start(easing);
+        }
+        else {
+            zr.animate(newShape.id, 'style')
+                .when(0, {
+                    xEnd: x0,
+                    yEnd: y0
+                })
+                .when(duration, {
+                    xEnd: x2,
+                    yEnd: y2
+                })
+                .done(animationDone)
+                .start(easing);
+        }
+    }
+
+    return {
+        pointList : pointList,
+        rectangle : rectangle,
+        candle : candle,
+        ring : ring,
+        sector : sector,
+        text : text,
+        polygon : polygon,
+        ribbon : ribbon,
+        gaugePointer : gaugePointer,
+        icon : icon,
+        line : line,
+        markline : markline
+    };
+});

+ 115 - 0
src/main/webapp/static/echarts-2.2.7/src/util/ecData.js

@@ -0,0 +1,115 @@
+/**
+ * echarts通用私有数据服务
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function() {
+    /**
+     * 打包私有数据
+     *
+     * @param {shape} shape 修改目标
+     * @param {Object} series
+     * @param {number} seriesIndex
+     * @param {number | Object} data
+     * @param {number} dataIndex
+     * @param {*=} special
+     * @param {*=} special2
+     */
+    function pack(
+        shape, series, seriesIndex, data, dataIndex, name, special, special2
+    ) {
+        var value;
+        if (typeof data != 'undefined') {
+            value = data.value == null
+                ? data
+                : data.value;
+        }
+
+        shape._echartsData = {
+            '_series' : series,
+            '_seriesIndex' : seriesIndex,
+            '_data' : data,
+            '_dataIndex' : dataIndex,
+            '_name' : name,
+            '_value' : value,
+            '_special' : special,
+            '_special2' : special2
+        };
+        return shape._echartsData;
+    }
+
+    /**
+     * 从私有数据中获取特定项
+     * @param {shape} shape
+     * @param {string} key
+     */
+    function get(shape, key) {
+        var data = shape._echartsData;
+        if (!key) {
+            return data;
+        }
+
+        switch (key) {
+            case 'series' :
+            case 'seriesIndex' :
+            case 'data' :
+            case 'dataIndex' :
+            case 'name' :
+            case 'value' :
+            case 'special' :
+            case 'special2' :
+                return data && data['_' + key];
+        }
+
+        return null;
+    }
+
+    /**
+     * 修改私有数据中获取特定项
+     * @param {shape} shape
+     * @param {string} key
+     * @param {*} value
+     */
+    function set(shape, key, value) {
+        shape._echartsData = shape._echartsData || {};
+        switch (key) {
+            case 'series' :             // 当前系列值
+            case 'seriesIndex' :        // 系列数组位置索引
+            case 'data' :               // 当前数据值
+            case 'dataIndex' :          // 数据数组位置索引
+            case 'name' :
+            case 'value' :
+            case 'special' :
+            case 'special2' :
+                shape._echartsData['_' + key] = value;
+                break;
+        }
+    }
+    
+    /**
+     * 私有数据克隆,把source拷贝到target上
+     * @param {shape} source 源
+     * @param {shape} target 目标
+     */
+    function clone(source, target) {
+        target._echartsData =  {
+            '_series' : source._echartsData._series,
+            '_seriesIndex' : source._echartsData._seriesIndex,
+            '_data' : source._echartsData._data,
+            '_dataIndex' : source._echartsData._dataIndex,
+            '_name' : source._echartsData._name,
+            '_value' : source._echartsData._value,
+            '_special' : source._echartsData._special,
+            '_special2' : source._echartsData._special2
+        };
+    }
+
+    return {
+        pack : pack,
+        set : set,
+        get : get,
+        clone : clone
+    };
+});

+ 444 - 0
src/main/webapp/static/echarts-2.2.7/src/util/ecEffect.js

@@ -0,0 +1,444 @@
+/**
+ * echarts图表特效基类
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function (require) {
+    var ecData = require('../util/ecData');
+    
+    var CircleShape = require('zrender/shape/Circle');
+    var ImageShape = require('zrender/shape/Image');
+    var curveTool = require('zrender/tool/curve');
+    var IconShape = require('../util/shape/Icon');
+    var SymbolShape = require('../util/shape/Symbol');
+    var ShapeBundle = require('zrender/shape/ShapeBundle');
+    var Polyline = require('zrender/shape/Polyline');
+    var vec2 = require('zrender/tool/vector');
+
+    var canvasSupported = require('zrender/tool/env').canvasSupported;
+    
+    function point(zr, effectList, shape, zlevel) {
+        var effect = shape.effect;
+        var color = effect.color || shape.style.strokeColor || shape.style.color;
+        var shadowColor = effect.shadowColor || color;
+        var size = effect.scaleSize;
+        var distance = effect.bounceDistance;
+        var shadowBlur = typeof effect.shadowBlur != 'undefined'
+                         ? effect.shadowBlur : size;
+
+        var effectShape;
+        if (shape.type !== 'image') {
+            effectShape = new IconShape({
+                zlevel : zlevel,
+                style : {
+                    brushType : 'stroke',
+                    iconType : shape.style.iconType != 'droplet'
+                               ? shape.style.iconType
+                               : 'circle',
+                    x : shadowBlur + 1, // 线宽
+                    y : shadowBlur + 1,
+                    n : shape.style.n,
+                    width : shape.style._width * size,
+                    height : shape.style._height * size,
+                    lineWidth : 1,
+                    strokeColor : color,
+                    shadowColor : shadowColor,
+                    shadowBlur : shadowBlur
+                },
+                draggable : false,
+                hoverable : false
+            });
+            if (shape.style.iconType == 'pin') {
+                effectShape.style.y += effectShape.style.height / 2 * 1.5;
+            }
+
+            if (canvasSupported) {  // 提高性能,换成image
+                effectShape.style.image = zr.shapeToImage(
+                    effectShape, 
+                    effectShape.style.width + shadowBlur * 2 + 2, 
+                    effectShape.style.height + shadowBlur * 2 + 2
+                ).style.image;
+                
+                effectShape = new ImageShape({
+                    zlevel : effectShape.zlevel,
+                    style : effectShape.style,
+                    draggable : false,
+                    hoverable : false
+                });
+            }
+        }
+        else {
+            effectShape = new ImageShape({
+                zlevel : zlevel,
+                style : shape.style,
+                draggable : false,
+                hoverable : false
+            });
+        }
+        
+        ecData.clone(shape, effectShape);
+        
+        // 改变坐标,不能移到前面
+        effectShape.position = shape.position;
+        effectList.push(effectShape);
+        zr.addShape(effectShape);
+        
+        var devicePixelRatio = shape.type !== 'image' ? (window.devicePixelRatio || 1) : 1;
+        var offset = (effectShape.style.width / devicePixelRatio - shape.style._width) / 2;
+        effectShape.style.x = shape.style._x - offset;
+        effectShape.style.y = shape.style._y - offset;
+
+        if (shape.style.iconType == 'pin') {
+            effectShape.style.y -= shape.style.height / 2 * 1.5;
+        }
+
+        var duration = (effect.period + Math.random() * 10) * 100;
+        
+        zr.modShape(
+            shape.id, 
+            { invisible : true}
+        );
+        
+        var centerX = effectShape.style.x + (effectShape.style.width) / 2 / devicePixelRatio;
+        var centerY = effectShape.style.y + (effectShape.style.height) / 2 / devicePixelRatio;
+
+        if (effect.type === 'scale') {
+            // 放大效果
+            zr.modShape(
+                effectShape.id, 
+                {
+                    scale : [0.1, 0.1, centerX, centerY]
+                }
+            );
+            
+            zr.animate(effectShape.id, '', effect.loop)
+                .when(
+                    duration,
+                    {
+                        scale : [1, 1, centerX, centerY]
+                    }
+                )
+                .done(function() {
+                    shape.effect.show = false;
+                    zr.delShape(effectShape.id);
+                })
+                .start();
+        }
+        else {
+            zr.animate(effectShape.id, 'style', effect.loop)
+                .when(
+                    duration,
+                    {
+                        y : effectShape.style.y - distance
+                    }
+                )
+                .when(
+                    duration * 2,
+                    {
+                        y : effectShape.style.y
+                    }
+                )
+                .done(function() {
+                    shape.effect.show = false;
+                    zr.delShape(effectShape.id);
+                })
+                .start();
+        }
+        
+    }
+    
+    function largePoint(zr, effectList, shape, zlevel) {
+        var effect = shape.effect;
+        var color = effect.color || shape.style.strokeColor || shape.style.color;
+        var size = effect.scaleSize;
+        var shadowColor = effect.shadowColor || color;
+        var shadowBlur = typeof effect.shadowBlur != 'undefined'
+                         ? effect.shadowBlur : (size * 2);
+        var devicePixelRatio = window.devicePixelRatio || 1;
+        var effectShape = new SymbolShape({
+            zlevel : zlevel,
+            position : shape.position,
+            scale : shape.scale,
+            style : {
+                pointList : shape.style.pointList,
+                iconType : shape.style.iconType,
+                color : color,
+                strokeColor : color,
+                shadowColor : shadowColor,
+                shadowBlur : shadowBlur * devicePixelRatio,
+                random : true,
+                brushType: 'fill',
+                lineWidth:1,
+                size : shape.style.size
+            },
+            draggable : false,
+            hoverable : false
+        });
+        
+        effectList.push(effectShape);
+        zr.addShape(effectShape);
+        zr.modShape(
+            shape.id, 
+            { invisible : true}
+        );
+        
+        var duration = Math.round(effect.period * 100);
+        var clip1 = {};
+        var clip2 = {};
+        for (var i = 0; i < 20; i++) {
+            effectShape.style['randomMap' + i] = 0;
+            clip1 = {};
+            clip1['randomMap' + i] = 100;
+            clip2 = {};
+            clip2['randomMap' + i] = 0;
+            effectShape.style['randomMap' + i] = Math.random() * 100;
+            zr.animate(effectShape.id, 'style', true)
+                .when(duration, clip1)
+                .when(duration * 2, clip2)
+                .when(duration * 3, clip1)
+                .when(duration * 4, clip1)
+                .delay(Math.random() * duration * i)
+                //.delay(duration / 15 * (15 - i + 1))
+                .start();
+            
+        }
+    }
+    
+    function line(zr, effectList, shape, zlevel, isLarge) {
+        var effect = shape.effect;
+        var shapeStyle = shape.style;
+        var color = effect.color || shapeStyle.strokeColor || shapeStyle.color;
+        var shadowColor = effect.shadowColor || shapeStyle.strokeColor || color;
+        var size = shapeStyle.lineWidth * effect.scaleSize;
+        var shadowBlur = typeof effect.shadowBlur != 'undefined'
+                         ? effect.shadowBlur : size;
+
+        var effectShape = new CircleShape({
+            zlevel : zlevel,
+            style : {
+                x : shadowBlur,
+                y : shadowBlur,
+                r : size,
+                color : color,
+                shadowColor : shadowColor,
+                shadowBlur : shadowBlur
+            },
+            hoverable : false
+        });
+
+        var offset = 0;
+        if (canvasSupported && ! isLarge) {  // 提高性能,换成image
+            var zlevel = effectShape.zlevel;
+            effectShape = zr.shapeToImage(
+                effectShape,
+                (size + shadowBlur) * 2,
+                (size + shadowBlur) * 2
+            );
+            effectShape.zlevel = zlevel;
+            effectShape.hoverable = false;
+
+            offset = shadowBlur;
+        }
+
+        if (! isLarge) {
+            ecData.clone(shape, effectShape);
+            // 改变坐标, 不能移到前面
+            effectShape.position = shape.position;
+            effectList.push(effectShape);
+            zr.addShape(effectShape);
+        }
+
+        var effectDone = function () {
+            if (! isLarge) {
+                shape.effect.show = false;
+                zr.delShape(effectShape.id);   
+            }
+            effectShape.effectAnimator = null;
+        };
+
+        if (shape instanceof Polyline) {
+            var distanceList = [0];
+            var totalDist = 0;
+            var pointList = shapeStyle.pointList;
+            var controlPointList = shapeStyle.controlPointList;
+            for (var i = 1; i < pointList.length; i++) {
+                if (controlPointList) {
+                    var cp1 = controlPointList[(i - 1) * 2];
+                    var cp2 = controlPointList[(i - 1) * 2 + 1];
+                    totalDist += vec2.dist(pointList[i - 1], cp1)
+                         + vec2.dist(cp1, cp2)
+                         + vec2.dist(cp2, pointList[i]);
+                }
+                else {
+                    totalDist += vec2.dist(pointList[i - 1], pointList[i]);
+                }
+                distanceList.push(totalDist);
+            }
+            var obj = { p: 0 };
+            var animator = zr.animation.animate(obj, { loop: effect.loop });
+
+            for (var i = 0; i < distanceList.length; i++) {
+                animator.when(distanceList[i] * effect.period, { p: i });
+            }
+            animator.during(function () {
+                var i = Math.floor(obj.p);
+                var x, y;
+                if (i == pointList.length - 1) {
+                    x = pointList[i][0];
+                    y = pointList[i][1];
+                }
+                else {
+                    var t = obj.p - i;
+                    var p0 = pointList[i];
+                    var p1 = pointList[i + 1];
+                    if (controlPointList) {
+                        var cp1 = controlPointList[i * 2];
+                        var cp2 = controlPointList[i * 2 + 1];
+                        x = curveTool.cubicAt(
+                            p0[0], cp1[0], cp2[0], p1[0], t
+                        );
+                        y = curveTool.cubicAt(
+                            p0[1], cp1[1], cp2[1], p1[1], t
+                        );
+                    }
+                    else {
+                        x = (p1[0] - p0[0]) * t + p0[0];
+                        y = (p1[1] - p0[1]) * t + p0[1];   
+                    }
+                }
+                effectShape.style.x = x;
+                effectShape.style.y = y;
+                if (! isLarge) {
+                    zr.modShape(effectShape);
+                }
+            })
+            .done(effectDone)
+            .start();
+
+            animator.duration = totalDist * effect.period;
+
+            effectShape.effectAnimator = animator;
+        }
+        else {
+            var x0 = shapeStyle.xStart - offset;
+            var y0 = shapeStyle.yStart - offset;
+            var x2 = shapeStyle.xEnd - offset;
+            var y2 = shapeStyle.yEnd - offset;
+            effectShape.style.x = x0;
+            effectShape.style.y = y0;
+
+            var distance = (x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0);
+            var duration = Math.round(Math.sqrt(Math.round(
+                distance * effect.period * effect.period
+            )));
+
+            if (shape.style.curveness > 0) {
+                var x1 = shapeStyle.cpX1 - offset;
+                var y1 = shapeStyle.cpY1 - offset;
+                effectShape.effectAnimator = zr.animation.animate(effectShape, { loop: effect.loop })
+                    .when(duration, { p: 1 })
+                    .during(function (target, t) {
+                        effectShape.style.x = curveTool.quadraticAt(
+                            x0, x1, x2, t
+                        );
+                        effectShape.style.y = curveTool.quadraticAt(
+                            y0, y1, y2, t
+                        );
+                        if (! isLarge) {
+                            zr.modShape(effectShape);
+                        }
+                    })
+                    .done(effectDone)
+                    .start();
+            }
+            else {
+                // 不用 zr.animate,因为在用 ShapeBundle 的时候单个 effectShape 不会
+                // 被加到 zrender 中
+                effectShape.effectAnimator = zr.animation.animate(effectShape.style, { loop: effect.loop })
+                    .when(duration, {
+                        x: x2,
+                        y: y2
+                    })
+                    .during(function () {
+                        if (! isLarge) {
+                            zr.modShape(effectShape);
+                        }
+                    })
+                    .done(effectDone)
+                    .start();
+            }
+            effectShape.effectAnimator.duration = duration;
+        }
+        return effectShape;
+    }
+
+    function largeLine(zr, effectList, shape, zlevel) {
+        var effectShape = new ShapeBundle({
+            style: {
+                shapeList: []
+            },
+            zlevel: zlevel,
+            hoverable: false
+        });
+        var shapeList = shape.style.shapeList;
+        var effect = shape.effect;
+        effectShape.position = shape.position;
+
+        var maxDuration = 0;
+        var subEffectAnimators = [];
+        for (var i = 0; i < shapeList.length; i++) {
+            shapeList[i].effect = effect;
+            var subEffectShape = line(zr, null, shapeList[i], zlevel, true);
+            var subEffectAnimator = subEffectShape.effectAnimator;
+            effectShape.style.shapeList.push(subEffectShape);
+            if (subEffectAnimator.duration > maxDuration) {
+                maxDuration = subEffectAnimator.duration;
+            }
+            if (i === 0) {
+                effectShape.style.color = subEffectShape.style.color;
+                effectShape.style.shadowBlur = subEffectShape.style.shadowBlur;
+                effectShape.style.shadowColor = subEffectShape.style.shadowColor;
+            }
+            subEffectAnimators.push(subEffectAnimator);
+        }
+        effectList.push(effectShape);
+        zr.addShape(effectShape);
+
+        var clearAllAnimators = function () {
+            for (var i = 0; i < subEffectAnimators.length; i++) {
+                subEffectAnimators[i].stop();
+            }
+        };
+        if (maxDuration) {
+            effectShape.__dummy = 0;
+            // Proxy animator
+            var animator = zr.animate(effectShape.id, '', effect.loop)
+                .when(maxDuration, {
+                    __dummy: 1
+                })
+                .during(function () {
+                    zr.modShape(effectShape);
+                })
+                .done(function () {
+                    shape.effect.show = false;
+                    zr.delShape(effectShape.id);
+                })
+                .start();
+            var oldStop = animator.stop;
+
+            animator.stop = function () {
+                clearAllAnimators();
+                oldStop.call(this);
+            };
+        }
+    }
+
+    return {
+        point : point,
+        largePoint : largePoint,
+        line : line,
+        largeLine: largeLine
+    };
+});

+ 81 - 0
src/main/webapp/static/echarts-2.2.7/src/util/ecQuery.js

@@ -0,0 +1,81 @@
+/**
+ * echarts层级查找方法
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *
+ */
+define(function(require) {
+    var zrUtil = require('zrender/tool/util');
+    
+    /**
+     * 获取嵌套选项的基础方法
+     * 返回optionTarget中位于optionLocation上的值,如果没有定义,则返回undefined
+     */
+    function query(optionTarget, optionLocation) {
+        if (typeof optionTarget == 'undefined') {
+            return;
+        }
+
+        if (!optionLocation) {
+            return optionTarget;
+        }
+
+        optionLocation = optionLocation.split('.');
+        var length = optionLocation.length;
+        var curIdx = 0;
+        while (curIdx < length) {
+            optionTarget = optionTarget[optionLocation[curIdx]];
+            if (typeof optionTarget == 'undefined') {
+                return;
+            }
+            curIdx++;
+        }
+
+        return optionTarget;
+    }
+        
+    /**
+     * 获取多级控制嵌套属性的基础方法
+     * 返回ctrList中优先级最高(最靠前)的非undefined属性,ctrList中均无定义则返回undefined
+     */
+    function deepQuery(ctrList, optionLocation) {
+        var finalOption;
+        for (var i = 0, l = ctrList.length; i < l; i++) {
+            finalOption = query(ctrList[i], optionLocation);
+            if (typeof finalOption != 'undefined') {
+                return finalOption;
+            }
+        }
+    }
+    
+    /**
+     * 获取多级控制嵌套属性的基础方法
+     * 根据ctrList中优先级合并产出目标属性
+     */
+    function deepMerge(ctrList, optionLocation) {
+        var finalOption;
+        var len = ctrList.length;
+        while (len--) {
+            var tempOption = query(ctrList[len], optionLocation);
+            if (typeof tempOption != 'undefined') {
+                if (typeof finalOption == 'undefined') {
+                    finalOption = zrUtil.clone(tempOption);
+                }
+                else {
+                    zrUtil.merge(
+                        finalOption, tempOption, true
+                    );
+                }
+            }
+        }
+        
+        return finalOption;
+    }
+    
+    return {
+        query : query,
+        deepQuery : deepQuery,
+        deepMerge : deepMerge
+    };
+});

+ 48 - 0
src/main/webapp/static/echarts-2.2.7/src/util/kwargs.js

@@ -0,0 +1,48 @@
+define(function (){
+    function kwargs(func, defaults) {
+        /*jshint maxlen : 200*/
+        var removeComments = new RegExp('(\\/\\*[\\w\\\'\\,\\(\\)\\s\\r\\n\\*]*\\*\\/)|(\\/\\/[\\w\\s\\\'][^\\n\\r]*$)|(<![\\-\\-\\s\\w\\>\\/]*>)', 'gim');
+        var removeWhitespc = new RegExp('\\s+', 'gim');
+        var matchSignature = new RegExp('function.*?\\((.*?)\\)', 'i');
+        // get the argument names from function source
+        var names = func.toString()
+                        .replace(removeComments, '')
+                        .replace(removeWhitespc, '')
+                        .match(matchSignature)[1]
+                        .split(',');
+
+        // Check the existance of default, if not create an object
+        if(defaults !== Object(defaults)){
+            defaults = {};
+        }
+
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            var kwargs = args[args.length - 1];
+
+            // Check the existance of the kwargs
+            if (kwargs && kwargs.constructor === Object) {
+                args.pop();
+            }
+            else{
+                kwargs = {};
+            }
+
+            // Fill the arguments and apply them
+            for (var i = 0; i < names.length; i++) {
+                var name = names[i];
+                if (name in kwargs) {
+                    args[i] = kwargs[name];
+                }
+                else if(name in defaults && args[i] == null){
+                    args[i] = defaults[name];
+                }
+            }
+
+            return func.apply(this, args);
+        };
+    }
+    // As function prototype
+    // Function.prototype.kwargs = kwargs;
+    return kwargs;
+});

+ 9 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoCoord.js

@@ -0,0 +1,9 @@
+/**
+ * 经纬度坐标,优先于自定计算
+ */
+define(function() {
+    return {
+        'Russia': [ 100, 60 ],
+        'United States of America': [ -99, 38 ]
+    };
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/an_hui_geo.js


+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ao_men_geo.js

@@ -0,0 +1,3 @@
+define(function() {
+    return {"type":"FeatureCollection","features":[{"type":"Feature","id":"8200","properties":{"name":"澳门","cp":[113.5715,22.1583],"childNum":1},"geometry":{"type":"Polygon","coordinates":["@@HQFMDIDGBI@E@EEKEGCEIGGEKEMGSEU@CBEDAJAP@F@LBT@JCHMPOdADCFADAB@LFLDFFP@DAB@@AF@D@B@@FBD@FADHBBHAD@FAJ@JEDCJI`gFIJW"],"encodeOffsets":[[116325,22699]]}}],"UTF8Encoding":true};
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/bei_jing_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/china_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/chong_qing_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/fu_jian_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/gan_su_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/guang_dong_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/guang_xi_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/gui_zhou_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hai_nan_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/he_bei_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/he_nan_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hei_long_jiang_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hu_bei_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/hu_nan_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ji_lin_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/jiang_su_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/jiang_xi_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/liao_ning_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/nei_meng_gu_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/ning_xia_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/qing_hai_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_dong_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_xi_1_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shan_xi_2_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/shang_hai_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/si_chuan_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/tai_wan_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/tian_jin_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/world_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xi_zang_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xiang_gang_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/xin_jiang_geo.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/yun_nan_geo.js


+ 0 - 0
src/main/webapp/static/echarts-2.2.7/src/util/mapData/geoJson/zhe_jiang_geo.js


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio