diff --git a/doc/CodingStyle.md b/doc/CodingStyle.md
index 4424f88..20473d3 100644
--- a/doc/CodingStyle.md
+++ b/doc/CodingStyle.md
@@ -1,308 +1,314 @@
-# DataV项目编码规范
-一套良好的编码规范如果在团队开发中被遵守,可以降低团队成员之间交流的成本,同时也降低犯错的几率。可以说编码规范只需花费20%的精力来遵循,却可以降低80%的犯低级错误几率。
-这一版的编码规范,主要查考了Douglas Crockford的JSLint的检查条件和过去的一些编码经验,整理而出。
-
-## 命名空间
-项目的总命名空间为:**`DataV`**。
-除了Rapheal和D3自身的代码外,我们提交的任何方法都应该挂载在`DataV`命名空间中。
-例外:某些方法和功能可以独立抽取出来,适用于别的项目。但是也应该由一个命名空间来管理它们。
-
-## 语法
-### 缩进规范
-由于该项目是普通的前端JavaScript项目,所以缩进采用4个空格的方式。禁止使用tab符号。
-推荐更改编辑器中的设置,使得按tab键可以自动插入4个空格。
-每一层级之间,要保证缩进是正确的。
-### 空格
-
-* `=`前后应该存在一个空格
-* `(`前面应该存在一个空格
-* `)`后面应该存在一个空格
-* `{`前面应该存在一个空格
-
-### 分号
-每一个申明或者定义,都应该以分号结尾。
-
- var username = "Jackson";
-
- var foo = function () {};
-每一行调用都应该明确以分号结尾。
-
- foo();
-
-### 大括号
-请明确使用`{}`来约束代码段。
-推荐:
-
- if (condition) {
- foo = "bar";
- }
-不推荐:
-
- if (condition) boo = "bar";
-
-## 命名规范
-### 变量
-采用驼峰首字母小写,后续单词均需大写首字母,不得在中间包含下划线。
-每一个变量都应该通过`var`申明,以防止变量污染。
-正确:
-
- var pipeChart;
-
-错误:
-
- var pipechart;
- var pipe_chart;
-
-### 方法
-方法名同变量名,采用驼峰式命名。
-定义方法均通过`var foo = function () {};`而不是`function foo () {}`。
-
- var foo = function () {
- // TODO
- };
-
-方法名应该可以表示方法的行为和暗示方法的返回值。善用`set`、`get`、`is`、`has`等方法前缀。
-
-### 类
-由于JavaScript中类和方法的关键字都是`function`,为了区别两者的混淆,类名的定义通常是大写首字母,以表示它是一个作为类来调用的。
-
- var Person = function () {
- // TODO
- };
-
-类的方法请尽量赋值在`prototype`属性上,使得通过原型链查找的方式降低内存占用。
-类的数据请尽量在构造函数中赋值,以加快执行时的查找速度。
-
- var Person = function (age) {
- this.age = age; // 在构造函数中赋值数据
- };
-
- // 在原型链中设置方法
- Person.prototype.getAge = function () {
- return this.age;
- };
-
-对于私有方法,在前面添加下划线表示不推荐被外部调用。
-
- Persion.prototype._sleep = function () {};
-
-### 模块
-为了统一规划,我们的类均需通过模块的定义方式,与命名空间结合,以保证环境的干净。
-
- (function () {
- var private = "I am private";
- var Person = function () {
- this.username = private;
- };
-
- DataV.Person = Person;
- }());
-
-## 注释
-注释对于团队而言是十分重要的,每一个方法都应该包含说明。
-必选的注释是方法的说明,方法的参数,和一个例子。
-任何诡异的地方,都应该附有简单的注释来描述它,以提醒后来人注意,以区别对待。
-
- /**
- * Create chart panel by passin width and height
- * @param {string} type Type name
- * @param {number} width Chart panel's width value
- * @param {number} height Chart panel's height value
- * @example
- * createChart("pipe", 500, 500);
- */
- var createChart = function (type, width, height) {
- // Trick comments
- trickCall();
- };
-
-通过[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释,将利于我们后期导出API文档。
-
-## 文件名
-文件名的方式也是通过驼峰式,但是以下划线分割单词,一律小写单词。如:
-
- d3.js
- child_process.js
-
-## JSLint扫描代码
-JSLint的调用方式如下:
-
- node precommit.js file
-
-配置方式存放在`config.json`中。
-
-## 目录结构说明
-目前datav组件库的目录结构如下:
-
-```
-JacksonTianmatoMacBook-Pro:datav.js jacksontian$ tree -d
-.
-├── bin
-├── deps
-├── doc
-├── example
-├── lib
-└── test
-```
-其中详细说明一下:
-
-- `bin`目录用于存放一些可运行的脚本或者工具,例如`jslint`。
-- `deps`目录用于存放项目的依赖文件,其中有`raphael`和`d3`的相关文件。
-- `doc`目录用于用于存放项目的文档,如API生成文档或一些说明文档。
-- `example`目录存放案例代码。
-- `lib`目录为组件库具体代码,根据每个图的类型不同,存为对应文件。
-- `test`目录存放单元测试代码或自动化测试代码。
-
-## 单元测试
-`DataV`项目采用[`QUnit`](http://docs.jquery.com/QUnit)作为测试框架,详细文档请见。
-简单测试例子:
-
-```
-// 分模块
-module("DataV");
-// 测试用例
-test("Themes.get", function () {
- // 单个断言
- equal(DataV.Themes.get("inexsit"), undefined, "Should get undefined when key is inexsit");
- equal(DataV.Themes.get("COLOR_MODE"), "gradient", "Should get gradient when key is COLOR_MODE");
- equal(typeof DataV.Themes.get("COLOR_ARGS"), "object", "COLOR_ARGS should be an object");
- equal(DataV.Themes.get("COLOR_ARGS").length, 2, "COLOR_ARGS should have two items");
-});
-```
-
-## 数据格式
-数据格式统一采用二维表的方式,结构比较类似数据库中的表。列名通过文档详细来描述,写在代码的[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释中。
-标准数据格式例子如下:
-
-```
-/**
- * 设置数据源
- * Example 举个例字,假设下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
- * [
- * [1,2,3,9],
- * [3,4,6,3]
- * ]
- * @param {Array} source 数据源数组.
- */
-Stream.prototype.setSource = function (source) {
- // TODO
-};
-```
-所有的组件库的调用都是相同的接口`setSource`:
-
-```
-var stream = new DataV.Stream("chart");
-stream.setSource(source);
-stream.render();
-```
-## 事件注入
-每一个组件都可能向外提供一些事件钩子,包括DOM事件,或者业务逻辑事件。绑定的方式十分简单:
-
-```
-stream.on("click", function (event) {
- console.log(event);
-});
-
-stream.on("dblclick", function (event) {
- alert("double click");
-});
-
-stream.on("contextmenu", function (event) {
- alert("mouse right");
-});
-```
-
-组件的内部实现是通过继承`EventProxy`提供自定义方式,在创建画布后,就绑定一些必要的事件到画布节点上,然后将事件触发出去。如果用户如上文,侦听了这些业务事件,将会调用执行。
-
-```
-Stream.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = Raphael(this.node, conf.width, conf.height);
- this.DOMNode = $(this.canvas.canvas);
- var that = this;
- this.DOMNode.click(function (event) {
- that.trigger("click", event);
- });
- this.DOMNode.dblclick(function (event) {
- that.trigger("dblclick", event);
- });
- this.DOMNode.bind("contextmenu", function (event) {
- that.trigger("contextmenu", event);
- });
- this.DOMNode.delegate("path", "click", function (event) {
- that.trigger("path_click", event);
- });
-};
-```
-
-## 常见错误
-### 判断一个对象是否是数组
-下面是宁朗写的:
-
-```
-if (color.constructor !== Array) {
- throw new Error("The color should be Array");
-}
-```
-下面是Underscore的方法:
-
-```
-_.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) == '[object Array]';
-};
-```
-### 数组去重
-
-```
-// make an array's every element unique by delete other same element
-Array.prototype.uniq = function () {
- var temp = {},
- len = this.length;
-
- for (var i = 0; i < len; i++) {
- if (typeof temp[this[i]] == "undefined") {
- temp[this[i]] = 1;
- }
- }
- this.length = 0;
- len = 0;
- for (var i in temp) {
- this[len++] = i;
- }
- return this;
-};
-```
-这里可以调用underscore的uniq方法即可。节省N行代码量
-
-### 元素是否存在数组中
-
-```
-//check if a string is in an array
-var _strInArray = function (str, array) {
- var i = 0,
- l = 0;
- for (i = 0, l = array.length; i < l; i++) {
- if (array[i] === str) {
- return true;
- }
- }
- return false;
-};
-```
-换用`_.indexOf(array, value)`可以节省代码量,且返回值比boolean值更有价值
-
-### meta
-
-
-
-希望大家潮一点,别在写上面那行代码了。下面的表达方式更简洁,且无任何副作用。
-
-
-
-### script标签的type属性
-
-
-
-如果是默认的text/javascript的话,就可以直接省略掉。变成下面这样子:
-
+# DataV项目编码规范
+一套良好的编码规范如果在团队开发中被遵守,可以降低团队成员之间交流的成本,同时也降低犯错的几率。可以说编码规范只需花费20%的精力来遵循,却可以降低80%的犯低级错误几率。
+这一版的编码规范,主要查考了Douglas Crockford的JSLint的检查条件和过去的一些编码经验,整理而出。
+
+## 命名空间
+项目的总命名空间为:**`DataV`**。
+除了Rapheal和D3自身的代码外,我们提交的任何方法都应该挂载在`DataV`命名空间中。
+例外:某些方法和功能可以独立抽取出来,适用于别的项目。但是也应该由一个命名空间来管理它们。
+
+## 语法
+### 缩进规范
+由于该项目是普通的前端JavaScript项目,所以缩进采用2个空格的方式。禁止使用tab符号。
+推荐更改编辑器中的设置,使得按tab键可以自动插入2个空格。
+每一层级之间,要保证缩进是正确的。
+### 空格
+
+* `=`前后应该存在一个空格
+* `(`前面应该存在一个空格
+* `)`后面应该存在一个空格
+* `{`前面应该存在一个空格
+
+### 分号
+每一个申明或者定义,都应该以分号结尾。
+
+ var username = "Jackson";
+
+ var foo = function () {};
+每一行调用都应该明确以分号结尾。
+
+ foo();
+
+### 大括号
+请明确使用`{}`来约束代码段。
+推荐:
+
+ if (condition) {
+ foo = "bar";
+ }
+不推荐:
+
+ if (condition) boo = "bar";
+
+## 命名规范
+### 变量
+采用驼峰首字母小写,后续单词均需大写首字母,不得在中间包含下划线。
+每一个变量都应该通过`var`申明,以防止变量污染。
+正确:
+
+ var pipeChart;
+
+错误:
+
+ var pipechart;
+ var pipe_chart;
+
+### 方法
+方法名同变量名,采用驼峰式命名。
+定义方法均通过`var foo = function () {};`而不是`function foo () {}`。
+
+ var foo = function () {
+ // TODO
+ };
+
+方法名应该可以表示方法的行为和暗示方法的返回值。善用`set`、`get`、`is`、`has`等方法前缀。
+
+### 类
+由于JavaScript中类和方法的关键字都是`function`,为了区别两者的混淆,类名的定义通常是大写首字母,以表示它是一个作为类来调用的。
+
+ var Person = function () {
+ // TODO
+ };
+
+类的方法请尽量赋值在`prototype`属性上,使得通过原型链查找的方式降低内存占用。
+类的数据请尽量在构造函数中赋值,以加快执行时的查找速度。
+
+ var Person = function (age) {
+ this.age = age; // 在构造函数中赋值数据
+ };
+
+ // 在原型链中设置方法
+ Person.prototype.getAge = function () {
+ return this.age;
+ };
+
+对于私有方法,在前面添加下划线表示不推荐被外部调用。
+
+ Persion.prototype._sleep = function () {};
+
+### 模块
+为了统一规划,我们的类均需通过模块的定义方式,与命名空间结合,以保证环境的干净。
+
+ (function () {
+ var private = "I am private";
+ var Person = function () {
+ this.username = private;
+ };
+
+ DataV.Person = Person;
+ }());
+
+## 注释
+注释对于团队而言是十分重要的,每一个方法都应该包含说明。
+必选的注释是方法的说明,方法的参数,和一个例子。
+任何诡异的地方,都应该附有简单的注释来描述它,以提醒后来人注意,以区别对待。
+
+ /**
+ * Create chart panel by passin width and height
+ * @param {string} type Type name
+ * @param {number} width Chart panel's width value
+ * @param {number} height Chart panel's height value
+ * @example
+ * createChart("pipe", 500, 500);
+ */
+ var createChart = function (type, width, height) {
+ // Trick comments
+ trickCall();
+ };
+
+通过[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释,将利于我们后期导出API文档。
+
+## 文件名
+文件名的方式也是通过驼峰式,但是以下划线分割单词,一律小写单词。如:
+
+ d3.js
+ child_process.js
+
+## 文件内容
+### 换行符
+请用Unix的CRLF换行符
+### 文件编码
+请用UTF-8 without BOM作为文件内容的编码
+
+## JSLint扫描代码
+JSLint的调用方式如下:
+
+ node precommit.js file
+
+配置方式存放在`config.json`中。
+
+## 目录结构说明
+目前datav组件库的目录结构如下:
+
+```
+JacksonTianmatoMacBook-Pro:datav.js jacksontian$ tree -d
+.
+├── bin
+├── deps
+├── doc
+├── example
+├── lib
+└── test
+```
+其中详细说明一下:
+
+- `bin`目录用于存放一些可运行的脚本或者工具,例如`jslint`。
+- `deps`目录用于存放项目的依赖文件,其中有`raphael`和`d3`的相关文件。
+- `doc`目录用于用于存放项目的文档,如API生成文档或一些说明文档。
+- `example`目录存放案例代码。
+- `lib`目录为组件库具体代码,根据每个图的类型不同,存为对应文件。
+- `test`目录存放单元测试代码或自动化测试代码。
+
+## 单元测试
+`DataV`项目采用[`QUnit`](http://docs.jquery.com/QUnit)作为测试框架,详细文档请见。
+简单测试例子:
+
+```
+// 分模块
+module("DataV");
+// 测试用例
+test("Themes.get", function () {
+ // 单个断言
+ equal(DataV.Themes.get("inexsit"), undefined, "Should get undefined when key is inexsit");
+ equal(DataV.Themes.get("COLOR_MODE"), "gradient", "Should get gradient when key is COLOR_MODE");
+ equal(typeof DataV.Themes.get("COLOR_ARGS"), "object", "COLOR_ARGS should be an object");
+ equal(DataV.Themes.get("COLOR_ARGS").length, 2, "COLOR_ARGS should have two items");
+});
+```
+
+## 数据格式
+数据格式统一采用二维表的方式,结构比较类似数据库中的表。列名通过文档详细来描述,写在代码的[jsdoc](http://code.google.com/p/jsdoc-toolkit/w/list)的注释中。
+标准数据格式例子如下:
+
+```
+/**
+ * 设置数据源
+ * Example 举个例字,假设下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
+ * [
+ * [1,2,3,9],
+ * [3,4,6,3]
+ * ]
+ * @param {Array} source 数据源数组.
+ */
+Stream.prototype.setSource = function (source) {
+ // TODO
+};
+```
+所有的组件库的调用都是相同的接口`setSource`:
+
+```
+var stream = new DataV.Stream("chart");
+stream.setSource(source);
+stream.render();
+```
+## 事件注入
+每一个组件都可能向外提供一些事件钩子,包括DOM事件,或者业务逻辑事件。绑定的方式十分简单:
+
+```
+stream.on("click", function (event) {
+ console.log(event);
+});
+
+stream.on("dblclick", function (event) {
+ alert("double click");
+});
+
+stream.on("contextmenu", function (event) {
+ alert("mouse right");
+});
+```
+
+组件的内部实现是通过继承`EventProxy`提供自定义方式,在创建画布后,就绑定一些必要的事件到画布节点上,然后将事件触发出去。如果用户如上文,侦听了这些业务事件,将会调用执行。
+
+```
+Stream.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = Raphael(this.node, conf.width, conf.height);
+ this.DOMNode = $(this.canvas.canvas);
+ var that = this;
+ this.DOMNode.click(function (event) {
+ that.trigger("click", event);
+ });
+ this.DOMNode.dblclick(function (event) {
+ that.trigger("dblclick", event);
+ });
+ this.DOMNode.bind("contextmenu", function (event) {
+ that.trigger("contextmenu", event);
+ });
+ this.DOMNode.delegate("path", "click", function (event) {
+ that.trigger("path_click", event);
+ });
+};
+```
+
+## 常见错误
+### 判断一个对象是否是数组
+下面是宁朗写的:
+
+```
+if (color.constructor !== Array) {
+ throw new Error("The color should be Array");
+}
+```
+下面是Underscore的方法:
+
+```
+_.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) == '[object Array]';
+};
+```
+### 数组去重
+
+```
+// make an array's every element unique by delete other same element
+Array.prototype.uniq = function () {
+ var temp = {},
+ len = this.length;
+
+ for (var i = 0; i < len; i++) {
+ if (typeof temp[this[i]] == "undefined") {
+ temp[this[i]] = 1;
+ }
+ }
+ this.length = 0;
+ len = 0;
+ for (var i in temp) {
+ this[len++] = i;
+ }
+ return this;
+};
+```
+这里可以调用underscore的uniq方法即可。节省N行代码量
+
+### 元素是否存在数组中
+
+```
+//check if a string is in an array
+var _strInArray = function (str, array) {
+ var i = 0,
+ l = 0;
+ for (i = 0, l = array.length; i < l; i++) {
+ if (array[i] === str) {
+ return true;
+ }
+ }
+ return false;
+};
+```
+换用`_.indexOf(array, value)`可以节省代码量,且返回值比boolean值更有价值
+
+### meta
+
+
+
+希望大家潮一点,别在写上面那行代码了。下面的表达方式更简洁,且无任何副作用。
+
+
+
+### script标签的type属性
+
+
+
+如果是默认的text/javascript的话,就可以直接省略掉。变成下面这样子:
+
\ No newline at end of file
diff --git a/lib/charts/axis.js b/lib/charts/axis.js
index 6ac321d..0321825 100644
--- a/lib/charts/axis.js
+++ b/lib/charts/axis.js
@@ -1,326 +1,326 @@
-//copy codes from d3.js, add 4 functions: tickAttr, tickTextAttr, minorTickAttr and domainAttr;
-//axis() changes, need a raphael paper object param, return raphael set object.
-//examples in ../examples/axis/ to know the usage.
-//a basic part for other data visualization format
-/*global d3*/
-/*!
- * Axis兼容定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Axis', function (require) {
- /**
- * function from d3, get scaleRange of an ordinal scale
- * @param {Array} domain ordinal scale's range
- */
- function d3_scaleExtent(domain) {
- var start = domain[0], stop = domain[domain.length - 1];
- return start < stop ? [start, stop] : [stop, start];
- }
-
- /**
- * function from d3, get scaleRange of a scale
- */
- function d3_scaleRange(scale) {
- return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
- }
-
- /**
- * function from d3, get subticks
- * @param scale, scale
- * @param ticks, major ticks of scale
- * @param m, number of subdivide
- */
- function d3_svg_axisSubdivide(scale, ticks, m) {
- var subticks = [];
- if (m && ticks.length > 1) {
- var extent = d3_scaleExtent(scale.domain()),
- i = -1,
- n = ticks.length,
- d = (ticks[1] - ticks[0]) / ++m,
- j,
- v;
- while (++i < n) {
- for (j = m; --j > 0;) {
- if ((v = +ticks[i] - j * d) >= extent[0]) {
- subticks.push(v);
- }
- }
- }
- for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
- subticks.push(v);
- }
- }
- return subticks;
- }
-
- var Axis = function () {
- var scale = d3.scale.linear(),
- orient = "bottom",
- tickMajorSize = 6,
- tickMinorSize = 6,
- tickEndSize = 6,
- tickPadding = 3,
- tickArguments_ = [10],
- tickFormat_,
- tickSubdivide = 0,
-
- tickAttr_ = {},
- tickTextAttr_ = {},
- minorTickAttr_ = {},
- domainAttr_ = {};
-
- /**
- * @param paper: raphael's paper object.
- * @return axisSet: raphael's set object.
- */
- function axis(paper) {
- // Ticks for quantitative scale, or domain values for ordinal scale.
- var ticks = scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain(),
- tickFormat = tickFormat_ === undefined ?
- (scale.tickFormat ?
- scale.tickFormat.apply(scale, tickArguments_)
- : String)
- : tickFormat_;
-
- var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide);
- var range = d3_scaleRange(scale);
-
- var axisSet = paper.set();
-
- switch (orient) {
- case "bottom":
- subticks.forEach(function (d, i, arr) {
- var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickX + "," + tickMinorSize + "V0")
- .attr(minorTickAttr_));
- });
- ticks.forEach(function (d, i, arr) {
- var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickX + "," + tickMajorSize + "V0")
- .attr(tickAttr_));
- axisSet.push(paper
- .text(tickX, Math.max(tickMajorSize, 0) + tickPadding + 2,
- typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
- .attr({"text-anchor": "middle"})
- .attr(tickTextAttr_));
- });
- axisSet.push(paper
- .path("M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize)
- .attr(domainAttr_));
- break;
-
- case "top":
- subticks.forEach(function (d, i, arr) {
- var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickX + "," + -tickMinorSize + "V0")
- .attr(minorTickAttr_));
- });
- ticks.forEach(function (d, i, arr) {
- var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickX + "," + -tickMajorSize + "V0")
- .attr(tickAttr_));
- axisSet.push(paper
- .text(tickX, -(Math.max(tickMajorSize, 0) + tickPadding + 2),
- typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
- .attr({"text-anchor": "middle"})
- .attr(tickTextAttr_));
- });
- axisSet.push(paper
- .path("M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize)
- .attr(domainAttr_));
- break;
-
- case "left":
- subticks.forEach(function (d, i, arr) {
- var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + -tickMinorSize + "," + tickY + "H0")
- .attr(minorTickAttr_));
- });
- ticks.forEach(function (d, i, arr) {
- var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + -tickMajorSize + "," + tickY + "H0")
- .attr(tickAttr_));
- axisSet.push(paper
- .text(-(Math.max(tickMajorSize, 0) + tickPadding), tickY,
- typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
- .attr({"text-anchor": "end"})
- .attr(tickTextAttr_));
- });
- axisSet.push(paper
- .path("M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize)
- .attr(domainAttr_));
- break;
-
- case "right":
- subticks.forEach(function (d, i, arr) {
- var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickMinorSize + "," + tickY + "H0")
- .attr(minorTickAttr_));
- });
- ticks.forEach(function (d, i, arr) {
- var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
- axisSet.push(paper
- .path("M" + tickMajorSize + "," + tickY + "H0")
- .attr(tickAttr_));
- axisSet.push(paper
- .text(Math.max(tickMajorSize, 0) + tickPadding, tickY,
- typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
- .attr({"text-anchor": "start"})
- .attr(tickTextAttr_));
- });
- axisSet.push(paper
- .path("M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize)
- .attr(domainAttr_));
- break;
- }
-
- return axisSet;
- }
-
- /**
- * get or set axis' scale.
- */
- axis.scale = function (x) {
- if (!arguments.length) {
- return scale;
- }
- scale = x;
- return axis;
- };
-
- /**
- * get or set axis' orinet: "bottom", "top", "left", "right", default orient is bottom.
- */
- axis.orient = function (x) {
- if (!arguments.length) {
- return orient;
- }
- orient = x;
- return axis;
- };
-
- /**
- * get or set axis' ticks number.
- */
- axis.ticks = function () {
- if (!arguments.length) {
- return tickArguments_;
- }
- tickArguments_ = arguments;
- return axis;
- };
-
- /**
- * get or set axis' ticks format function, it's a function change format style.
- * from one string format to another string format.
- */
- axis.tickFormat = function (x) {
- if (!arguments.length) {
- return tickFormat_;
- }
- tickFormat_ = x;
- return axis;
- };
-
- /**
- * get or set axis' tick size(length of tick line, unit: px).
- * @param arguments.length === 0, get axis' major tick size.
- * @param arguments.length === 1, set axis' all tick sizes as x.
- * @param arguments.length === 2, get axis' major tick size as x, minor and end size as y.
- * @param arguments.length === 3, get axis' major tick size as x, minor size as y, end size as z.
- */
- axis.tickSize = function (x, y, z) {
- if (!arguments.length) {
- return tickMajorSize;
- }
- var n = arguments.length - 1;
- tickMajorSize = +x;
- tickMinorSize = n > 1 ? +y : tickMajorSize;
- tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
- return axis;
- };
-
- /**
- * get or set axis' tick padding(the distance between tick text and axis).
- * @param x is a number, unit is px;
- */
- axis.tickPadding = function (x) {
- if (!arguments.length) {
- return tickPadding;
- }
- tickPadding = +x;
- return axis;
- };
-
- /**
- * get or set axis' sub tick divide number(divide number between two major ticks).
- */
- axis.tickSubdivide = function (x) {
- if (!arguments.length) {
- return tickSubdivide;
- }
- tickSubdivide = +x;
- return axis;
- };
-
- /**
- * get or set axis' tick attribute(Raphael format).
- */
- axis.tickAttr = function (x) {
- if (!arguments.length) {
- return tickAttr_;
- }
- tickAttr_ = x;
- return axis;
- };
-
- /**
- * get or set axis' tick text attribute(Raphael format).
- */
- axis.tickTextAttr = function (x) {
- if (!arguments.length) {
- return tickTextAttr_;
- }
- tickTextAttr_ = x;
- return axis;
- };
-
- /**
- * get or set axis' minor tick attribute(Raphael format).
- */
- axis.minorTickAttr = function (x) {
- if (!arguments.length) {
- return minorTickAttr_;
- }
- minorTickAttr_ = x;
- return axis;
- };
-
- /**
- * get or set axis' domain(axis line) attribute(Raphael format).
- */
- axis.domainAttr = function (x) {
- if (!arguments.length) {
- return domainAttr_;
- }
- domainAttr_ = x;
- return axis;
- };
-
- return axis;
- };
-
- return Axis;
-});
+//copy codes from d3.js, add 4 functions: tickAttr, tickTextAttr, minorTickAttr and domainAttr;
+//axis() changes, need a raphael paper object param, return raphael set object.
+//examples in ../examples/axis/ to know the usage.
+//a basic part for other data visualization format
+/*global d3*/
+/*!
+ * Axis兼容定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Axis', function (require) {
+ /**
+ * function from d3, get scaleRange of an ordinal scale
+ * @param {Array} domain ordinal scale's range
+ */
+ function d3_scaleExtent(domain) {
+ var start = domain[0], stop = domain[domain.length - 1];
+ return start < stop ? [start, stop] : [stop, start];
+ }
+
+ /**
+ * function from d3, get scaleRange of a scale
+ */
+ function d3_scaleRange(scale) {
+ return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
+ }
+
+ /**
+ * function from d3, get subticks
+ * @param scale, scale
+ * @param ticks, major ticks of scale
+ * @param m, number of subdivide
+ */
+ function d3_svg_axisSubdivide(scale, ticks, m) {
+ var subticks = [];
+ if (m && ticks.length > 1) {
+ var extent = d3_scaleExtent(scale.domain()),
+ i = -1,
+ n = ticks.length,
+ d = (ticks[1] - ticks[0]) / ++m,
+ j,
+ v;
+ while (++i < n) {
+ for (j = m; --j > 0;) {
+ if ((v = +ticks[i] - j * d) >= extent[0]) {
+ subticks.push(v);
+ }
+ }
+ }
+ for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
+ subticks.push(v);
+ }
+ }
+ return subticks;
+ }
+
+ var Axis = function () {
+ var scale = d3.scale.linear(),
+ orient = "bottom",
+ tickMajorSize = 6,
+ tickMinorSize = 6,
+ tickEndSize = 6,
+ tickPadding = 3,
+ tickArguments_ = [10],
+ tickFormat_,
+ tickSubdivide = 0,
+
+ tickAttr_ = {},
+ tickTextAttr_ = {},
+ minorTickAttr_ = {},
+ domainAttr_ = {};
+
+ /**
+ * @param paper: raphael's paper object.
+ * @return axisSet: raphael's set object.
+ */
+ function axis(paper) {
+ // Ticks for quantitative scale, or domain values for ordinal scale.
+ var ticks = scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain(),
+ tickFormat = tickFormat_ === undefined ?
+ (scale.tickFormat ?
+ scale.tickFormat.apply(scale, tickArguments_)
+ : String)
+ : tickFormat_;
+
+ var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide);
+ var range = d3_scaleRange(scale);
+
+ var axisSet = paper.set();
+
+ switch (orient) {
+ case "bottom":
+ subticks.forEach(function (d, i, arr) {
+ var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickX + "," + tickMinorSize + "V0")
+ .attr(minorTickAttr_));
+ });
+ ticks.forEach(function (d, i, arr) {
+ var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickX + "," + tickMajorSize + "V0")
+ .attr(tickAttr_));
+ axisSet.push(paper
+ .text(tickX, Math.max(tickMajorSize, 0) + tickPadding + 2,
+ typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
+ .attr({"text-anchor": "middle"})
+ .attr(tickTextAttr_));
+ });
+ axisSet.push(paper
+ .path("M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize)
+ .attr(domainAttr_));
+ break;
+
+ case "top":
+ subticks.forEach(function (d, i, arr) {
+ var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickX + "," + -tickMinorSize + "V0")
+ .attr(minorTickAttr_));
+ });
+ ticks.forEach(function (d, i, arr) {
+ var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickX + "," + -tickMajorSize + "V0")
+ .attr(tickAttr_));
+ axisSet.push(paper
+ .text(tickX, -(Math.max(tickMajorSize, 0) + tickPadding + 2),
+ typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
+ .attr({"text-anchor": "middle"})
+ .attr(tickTextAttr_));
+ });
+ axisSet.push(paper
+ .path("M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize)
+ .attr(domainAttr_));
+ break;
+
+ case "left":
+ subticks.forEach(function (d, i, arr) {
+ var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + -tickMinorSize + "," + tickY + "H0")
+ .attr(minorTickAttr_));
+ });
+ ticks.forEach(function (d, i, arr) {
+ var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + -tickMajorSize + "," + tickY + "H0")
+ .attr(tickAttr_));
+ axisSet.push(paper
+ .text(-(Math.max(tickMajorSize, 0) + tickPadding), tickY,
+ typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
+ .attr({"text-anchor": "end"})
+ .attr(tickTextAttr_));
+ });
+ axisSet.push(paper
+ .path("M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize)
+ .attr(domainAttr_));
+ break;
+
+ case "right":
+ subticks.forEach(function (d, i, arr) {
+ var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickMinorSize + "," + tickY + "H0")
+ .attr(minorTickAttr_));
+ });
+ ticks.forEach(function (d, i, arr) {
+ var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
+ axisSet.push(paper
+ .path("M" + tickMajorSize + "," + tickY + "H0")
+ .attr(tickAttr_));
+ axisSet.push(paper
+ .text(Math.max(tickMajorSize, 0) + tickPadding, tickY,
+ typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
+ .attr({"text-anchor": "start"})
+ .attr(tickTextAttr_));
+ });
+ axisSet.push(paper
+ .path("M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize)
+ .attr(domainAttr_));
+ break;
+ }
+
+ return axisSet;
+ }
+
+ /**
+ * get or set axis' scale.
+ */
+ axis.scale = function (x) {
+ if (!arguments.length) {
+ return scale;
+ }
+ scale = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' orinet: "bottom", "top", "left", "right", default orient is bottom.
+ */
+ axis.orient = function (x) {
+ if (!arguments.length) {
+ return orient;
+ }
+ orient = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' ticks number.
+ */
+ axis.ticks = function () {
+ if (!arguments.length) {
+ return tickArguments_;
+ }
+ tickArguments_ = arguments;
+ return axis;
+ };
+
+ /**
+ * get or set axis' ticks format function, it's a function change format style.
+ * from one string format to another string format.
+ */
+ axis.tickFormat = function (x) {
+ if (!arguments.length) {
+ return tickFormat_;
+ }
+ tickFormat_ = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' tick size(length of tick line, unit: px).
+ * @param arguments.length === 0, get axis' major tick size.
+ * @param arguments.length === 1, set axis' all tick sizes as x.
+ * @param arguments.length === 2, get axis' major tick size as x, minor and end size as y.
+ * @param arguments.length === 3, get axis' major tick size as x, minor size as y, end size as z.
+ */
+ axis.tickSize = function (x, y, z) {
+ if (!arguments.length) {
+ return tickMajorSize;
+ }
+ var n = arguments.length - 1;
+ tickMajorSize = +x;
+ tickMinorSize = n > 1 ? +y : tickMajorSize;
+ tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
+ return axis;
+ };
+
+ /**
+ * get or set axis' tick padding(the distance between tick text and axis).
+ * @param x is a number, unit is px;
+ */
+ axis.tickPadding = function (x) {
+ if (!arguments.length) {
+ return tickPadding;
+ }
+ tickPadding = +x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' sub tick divide number(divide number between two major ticks).
+ */
+ axis.tickSubdivide = function (x) {
+ if (!arguments.length) {
+ return tickSubdivide;
+ }
+ tickSubdivide = +x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' tick attribute(Raphael format).
+ */
+ axis.tickAttr = function (x) {
+ if (!arguments.length) {
+ return tickAttr_;
+ }
+ tickAttr_ = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' tick text attribute(Raphael format).
+ */
+ axis.tickTextAttr = function (x) {
+ if (!arguments.length) {
+ return tickTextAttr_;
+ }
+ tickTextAttr_ = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' minor tick attribute(Raphael format).
+ */
+ axis.minorTickAttr = function (x) {
+ if (!arguments.length) {
+ return minorTickAttr_;
+ }
+ minorTickAttr_ = x;
+ return axis;
+ };
+
+ /**
+ * get or set axis' domain(axis line) attribute(Raphael format).
+ */
+ axis.domainAttr = function (x) {
+ if (!arguments.length) {
+ return domainAttr_;
+ }
+ domainAttr_ = x;
+ return axis;
+ };
+
+ return axis;
+ };
+
+ return Axis;
+});
diff --git a/lib/charts/brush.js b/lib/charts/brush.js
index 10083cc..7ba8ff8 100644
--- a/lib/charts/brush.js
+++ b/lib/charts/brush.js
@@ -1,615 +1,615 @@
-/*global d3,Raphael,$*/
-/*!
- * Brush的兼容定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Brush', function (require) {
-
- var d3_svg_brush,
- d3_svg_brushDispatch,
- d3_svg_brushTarget,
- d3_svg_brushX,
- d3_svg_brushY,
- d3_svg_brushExtent,
- d3_svg_brushDrag,
- d3_svg_brushResize,
- d3_svg_brushCenter,
- d3_svg_brushOffset,
- d3_svg_brushEls;
-
- /**
- * set foreground and resizers' x and width;
- */
- function d3_svg_brushRedrawX(brushEls, extent) {
- brushEls.fg.attr({"x": extent[0][0],
- "width": extent[1][0] - extent[0][0] });
- brushEls.resizerSet.forEach(function (el) {
- var orient = el.data("resizeOrient");
-
- if (orient === "n" ||
- orient === "s" ||
- orient === "w" ||
- orient === "nw" ||
- orient === "sw") {
- el.attr({"x": extent[0][0] - 2});
- } else { // "e" "ne" "se"
- el.attr({"x": extent[1][0] - 2});
- }
- if (orient === "n" || orient === "s") {
- el.attr({"width": extent[1][0] - extent[0][0]});
- }
- });
- }
-
- /**
- * set foreground and resizers' y and height;
- */
- function d3_svg_brushRedrawY(brushEls, extent) {
- brushEls.fg.attr({"y": extent[0][1],
- "height": extent[1][1] - extent[0][1] });
- brushEls.resizerSet.forEach(function (el) {
- var orient = el.data("resizeOrient");
- if (orient === "n" ||
- orient === "e" ||
- orient === "w" ||
- orient === "nw" ||
- orient === "ne") {
- el.attr({"y": extent[0][1] - 3});
- } else { // "s" "se" "sw"
- el.attr({"y": extent[1][1] - 4});
- }
- if (orient === "e" || orient === "w") {
- el.attr({"height": extent[1][1] - extent[0][1]});
- }
- });
- }
-
- /**
- * function from d3, get scaleRange of an ordinal scale
- * @param domain, ordinal scale's range
- */
- function d3_scaleExtent(domain) {
- var start = domain[0], stop = domain[domain.length - 1];
- return start < stop ? [start, stop] : [stop, start];
- }
-
- /**
- * function from d3, get scaleRange of a scale
- */
- function d3_scaleRange(scale) {
- return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
- }
-
- /**
- * function from d3, called by d3_svg_brushMove, compute new brush extent after brush moved
- */
- function d3_svg_brushMove1(mouse, scale, i) {
- var range = d3_scaleRange(scale),
- r0 = range[0],
- r1 = range[1],
- offset = d3_svg_brushOffset[i],
- size = d3_svg_brushExtent[1][i] - d3_svg_brushExtent[0][i],
- min,
- max;
-
- // When dragging, reduce the range by the extent size and offset.
- if (d3_svg_brushDrag) {
- r0 -= offset;
- r1 -= size + offset;
- }
-
- // Clamp the mouse so that the extent fits within the range extent.
- min = Math.max(r0, Math.min(r1, mouse[i]));
-
- // Compute the new extent bounds.
- if (d3_svg_brushDrag) {
- max = (min += offset) + size;
- } else {
- // If the ALT key is pressed, then preserve the center of the extent.
- if (d3_svg_brushCenter) {
- offset = Math.max(r0, Math.min(r1, 2 * d3_svg_brushCenter[i] - min));
- }
-
- // Compute the min and max of the offset and mouse.
- if (offset < min) {
- max = min;
- min = offset;
- } else {
- max = offset;
- }
- }
-
- // Update the stored bounds.
- d3_svg_brushExtent[0][i] = min;
- d3_svg_brushExtent[1][i] = max;
- }
-
- /**
- * function from d3, after brush moved, compute new brush extent
- * and redraw foreground and resizer.
- */
- function d3_svg_brushMove(e) {
- if (d3_svg_brushOffset) {
- var bgOffset = $(d3_svg_brushTarget).offset();
- var mouse = [e.pageX - bgOffset.left, e.pageY - bgOffset.top];
-
- if (!d3_svg_brushDrag) {
- // If needed, determine the center from the current extent.
- if (e.altKey) {
- if (!d3_svg_brushCenter) {
- d3_svg_brushCenter = [
- (d3_svg_brushExtent[0][0] + d3_svg_brushExtent[1][0]) / 2,
- (d3_svg_brushExtent[0][1] + d3_svg_brushExtent[1][1]) / 2
- ];
- }
-
- // Update the offset, for when the ALT key is released.
- d3_svg_brushOffset[0] = d3_svg_brushExtent[+(mouse[0] < d3_svg_brushCenter[0])][0];
- d3_svg_brushOffset[1] = d3_svg_brushExtent[+(mouse[1] < d3_svg_brushCenter[1])][1];
- } else {
- // When the ALT key is released, we clear the center.
- d3_svg_brushCenter = null;
- }
- }
-
- // Update the brush extent for each dimension.
- if (d3_svg_brushX) {
- d3_svg_brushMove1(mouse, d3_svg_brushX, 0);
- d3_svg_brushRedrawX(d3_svg_brushEls, d3_svg_brushExtent);
- }
- if (d3_svg_brushY) {
- d3_svg_brushMove1(mouse, d3_svg_brushY, 1);
- d3_svg_brushRedrawY(d3_svg_brushEls, d3_svg_brushExtent);
- }
-
- // Notify listeners.
- d3_svg_brushDispatch("brush");
- }
- }
-
- /**
- * function from d3,
- * reset brush offset if user presses "space" key while brushing a new area,
- * to ensure foreground's size unchanged while position changing.
- */
- function d3_svg_brushKeydown(e) {
- if (e.keyCode === 32 && d3_svg_brushTarget && !d3_svg_brushDrag) {
- d3_svg_brushCenter = null;
- d3_svg_brushOffset[0] -= d3_svg_brushExtent[1][0];
- d3_svg_brushOffset[1] -= d3_svg_brushExtent[1][1];
- d3_svg_brushDrag = 2;
- e.stopPropagation();
- }
- }
-
- /**
- * function from d3,
- * reset brush offset if "space" key up to restore normal drush state.
- */
- function d3_svg_brushKeyup(e) {
- if (e.keyCode === 32 && d3_svg_brushDrag === 2) {
- d3_svg_brushOffset[0] += d3_svg_brushExtent[1][0];
- d3_svg_brushOffset[1] += d3_svg_brushExtent[1][1];
- d3_svg_brushDrag = 0;
- e.stopPropagation();
- }
- }
-
- /**
- * function from d3,
- * mouse up and stop brushing.
- */
- function d3_svg_brushUp(e) {
- if (d3_svg_brushOffset) {
- d3_svg_brushMove(e);
- d3_svg_brushEls.resizerSet.forEach(function (resizer) {
- //adjust all resizers
- var orient = resizer.data("resizeOrient");
- var size = d3_svg_brush.empty() ? 0 : 6;
- if (orient === "n" || orient === "s") {
- resizer.attr({"height": size});
- } else {
- resizer.attr({"width": size});
- }
- });
- d3_svg_brushDispatch("brushend");
- d3_svg_brush =
- d3_svg_brushDispatch =
- d3_svg_brushTarget =
- d3_svg_brushX =
- d3_svg_brushY =
- d3_svg_brushExtent =
- d3_svg_brushDrag =
- d3_svg_brushResize =
- d3_svg_brushCenter =
- d3_svg_brushOffset =
- d3_svg_brushEls = null;
- e.stopPropagation();
- }
- }
-
- var d3_svg_brushCursor = {
- n: "ns-resize",
- e: "ew-resize",
- s: "ns-resize",
- w: "ew-resize",
- nw: "nwse-resize",
- ne: "nesw-resize",
- se: "nwse-resize",
- sw: "nesw-resize"
- };
- var vml_brushCursor = {
- n: "row-resize",
- e: "col-resize",
- s: "row-resize",
- w: "col-resize",
- nw: "all-scroll",
- ne: "all-scroll",
- se: "all-scroll",
- sw: "all-scroll"
- };
-
- var Brush = function () {
- var event = d3.dispatch("brushstart", "brush", "brushend"),
- x, // x-scale, optional
- y, // y-scale, optional
- extent = [[0, 0], [0, 0]], // [x0, y0], [x1, y1]
- e,
- left,
- top,
- width,
- height,
- backgroundAttr = {
- "fill": "#dddddd",
- "stroke": "none",
- "cursor": "crosshair"
- },
- foregroundAttr = {
- "fill": "steelblue",
- "stroke": "none",
- "cursor": "move"
- },
- brushStart = function () {},
- brushing = function () {},
- brushEnd = function () {},
-
- brushEls = {},
- brushClass;
-
- /*!
- * mouse down and start brushing or dragging.
- */
- function down(e) {
- var target = e.target,
- bgOffset;
-
- // Store some global state for the duration of the brush gesture.
- d3_svg_brush = brush;
- d3_svg_brushTarget = $(brushEls.paper.canvas).parent();
- d3_svg_brushExtent = extent;
- bgOffset = $(d3_svg_brushTarget).offset();
-
- d3_svg_brushOffset = [e.pageX - bgOffset.left, e.pageY - bgOffset.top];
- d3_svg_brushEls = brushEls;
-
- // If the extent was clicked on, drag rather than brush;
- // store the offset between the mouse and extent origin instead.
- d3_svg_brushDrag = target.__brushNodeType__ === "fg" ? true : false;
- if (d3_svg_brushDrag) {
- d3_svg_brushOffset[0] = extent[0][0] - d3_svg_brushOffset[0];
- d3_svg_brushOffset[1] = extent[0][1] - d3_svg_brushOffset[1];
- } else if (/^resize/.test(target.__brushNodeType__)) {
- // If a resizer was clicked on, record which side is to be resized.
- // Also, set the offset to the opposite side.
- d3_svg_brushResize = target.__brushNodeType__.split("_")[1];
- d3_svg_brushOffset[0] = extent[+(/w$/.test(d3_svg_brushResize))][0];
- d3_svg_brushOffset[1] = extent[+(/^n/.test(d3_svg_brushResize))][1];
- } else if (e.altKey) {
- // If the ALT key is down when starting a brush, the center is at the mouse.
- d3_svg_brushCenter = d3_svg_brushOffset.slice();
- }
-
- // Restrict which dimensions are resized.
- d3_svg_brushX = !/^(n|s)$/.test(d3_svg_brushResize) && x;
- d3_svg_brushY = !/^(e|w)$/.test(d3_svg_brushResize) && y;
-
- // Notify listeners.
- d3_svg_brushDispatch = dispatcher(this, arguments);
- d3_svg_brushDispatch("brushstart");
- d3_svg_brushMove(e);
- e.stopPropagation();
- }
-
- /*!
- * create brush
- * input a Raphael paper, return a brush object.
- */
- function brush(paper) {
- var resizes = x && y ? ["n", "e", "s", "w", "nw", "ne", "se", "sw"]
- : x ? ["e", "w"]
- : y ? ["n", "s"]
- : [];
-
- if (x) {
- e = d3_scaleRange(x);
- left = e[0];
- width = e[1] - e[0];
- }
-
- if (y) {
- e = d3_scaleRange(y);
- top = e[0];
- height = e[1] - e[0];
- }
-
- brushEls.paper = paper;
- brushEls.brushSet = paper.set();
- brushEls.resizerSet = paper.set();
- brushEls.bg = paper.rect(left, top, width, height)
- .attr({"fill": "#dddddd",
- "stroke": "none",
- "cursor": "crosshair"
- })
- .attr(backgroundAttr);
- brushEls.bg.node.__brushNodeType__ = "bg";
- brushEls.bg.node.ondragstart = function () { return false; };//firefox drag bug fix;
-
- brushClass = "brush" + brushEls.bg.id;
-
- //$(brushEls.bg.node).addClass("brush bg rvml"); // fail to svg
- brushEls.bg.node.setAttribute("class", "brush bg rvml " + brushClass);
- brushEls.bg.node.setAttribute("className", "brush bg rvml " + brushClass);// IE 6,7
-
- brushEls.fg = paper.rect(left, top, (x ? 0 : width), (y ? 0 : height))
- .attr({"fill": "steelblue",
- "stroke": "none",
- "cursor": "move"
- })
- .attr(foregroundAttr);
- brushEls.fg.node.__brushNodeType__ = "fg";
- brushEls.fg.node.ondragstart = function () { return false; };//firefox drag bug fix;
- //$(brushEls.fg.node).addClass("brush fg rvml"); //fail to svg
- brushEls.fg.node.setAttribute("class", "brush fg rvml " + brushClass);
- brushEls.fg.node.setAttribute("className", "brush fg rvml " + brushClass);// IE 6,7
-
- resizes.forEach(function (d) {
- var resizer = paper.rect(left, top, (x ? 6 : width), (y ? 6 : height))
- .data("resizeOrient", d)
- .attr({"cursor": d3_svg_brushCursor[d],
- "fill": "white",
- "stroke": "black",
- "opacity": 0});
- if (Raphael.vml) {
- resizer.attr({"cursor": vml_brushCursor[d]});
- }
- if (brush.empty()) {
- //hide all resizers
- if (d === "n" || d === "s") {
- resizer.attr({"height": 0});
- } else {
- resizer.attr({"width": 0});
- }
- }
- resizer.node.__brushNodeType__ = "resizer_" + d;
- resizer.node.ondragstart = function () { return false; };//firefox drag bug fix;
- //$(resizer.node).addClass("brush rvml " + d3_svg_brushCursor[d]); //fail to svg
- resizer.node.setAttribute("class", "brush rvml " + brushClass + " " + d3_svg_brushCursor[d]);
- //IE 6,7
- resizer.node.setAttribute("className", "brush rvml " + brushClass + " " + d3_svg_brushCursor[d]);
- brushEls.resizerSet.push(resizer);
- });
-
- if (x) {
- d3_svg_brushRedrawX(brushEls, extent);
- }
-
- if (y) {
- d3_svg_brushRedrawY(brushEls, extent);
- }
-
- //$(paper.canvas).delegate(".brush","mousedown", down);
- //$(paper.canvas).undelegate(".brush","mousedown", down);
- //$(paper.canvas).delegate(".brush","mousedown", down);
- //$(paper.canvas).off("mousedown", ".brush", down);
- $(paper.canvas).on("mousedown", "." + brushClass, down);
-
- brush.brushElements = brushEls;
- return brush;
- }
-
- // dispatch event, bind data to golbal variant d3.event.
- var dispatcher = function (that, argumentz) {
- return function (type) {
- var e = d3.event;
- try {
- d3.event = {type: type, target: brush};
- event[type].apply(that, argumentz);
- } finally {
- d3.event = e;
- }
- };
- };
-
- /*!
- * get or set brush's left
- * @param z, a value in brush scale's domain
- */
- brush.left = function (z) {
- if (!arguments.length) { return left; }
- left = z;
- return brush;
- };
-
- /*!
- * get or set brush's top
- * @param z, a value in brush scale's domain
- */
- brush.top = function (z) {
- if (!arguments.length) { return top; }
- top = z;
- return brush;
- };
-
- /*!
- * get or set brush's width
- * @param z, a value in brush scale's domain
- */
- brush.width = function (z) {
- if (!arguments.length) { return width; }
- width = z;
- return brush;
- };
-
- /*!
- * get or set brush's height
- * @param z, a value in brush scale's domain
- */
- brush.height = function (z) {
- if (!arguments.length) { return height; }
- height = z;
- return brush;
- };
-
- /*!
- * get or set brush's x scale
- * @param z, d3's sacle object
- */
- brush.x = function (z) {
- if (!arguments.length) { return x; }
- x = z;
- return brush;
- };
-
- /*!
- * get or set brush's y scale
- * @param z, d3's sacle object
- */
- brush.y = function (z) {
- if (!arguments.length) { return y; }
- y = z;
- return brush;
- };
-
- /*!
- * get or set brush's extent in scale's domain format.
- * if both x and y exist, @param z's format is [[x0, y0], [x1, y1]]
- * if only one of x and y exists, @param z's format is [x0, x1] or [y0, y1].
- */
- brush.extent = function (z) {
- var x0, x1, y0, y1, t;
-
- // Invert the pixel extent to data-space.
- if (!arguments.length) {
- if (x) {
- x0 = extent[0][0]; x1 = extent[1][0];
- if (x.invert) {
- x0 = x.invert(x0); x1 = x.invert(x1);
- }
- if (x1 < x0) {
- t = x0; x0 = x1; x1 = t;
- }
- }
- if (y) {
- y0 = extent[0][1]; y1 = extent[1][1];
- if (y.invert) {
- y0 = y.invert(y0); y1 = y.invert(y1);
- }
- if (y1 < y0) {
- t = y0; y0 = y1; y1 = t;
- }
- }
- return x && y ? [[x0, y0], [x1, y1]] : x ? [x0, x1] : y && [y0, y1];
- }
-
- // Scale the data-space extent to pixels.
- if (x) {
- x0 = z[0]; x1 = z[1];
- if (y) {
- x0 = x0[0]; x1 = x1[0];
- }
- if (x.invert) {
- x0 = x(x0); x1 = x(x1);
- }
- if (x1 < x0) {
- t = x0; x0 = x1; x1 = t;
- }
- extent[0][0] = x0; extent[1][0] = x1;
- }
- if (y) {
- y0 = z[0]; y1 = z[1];
- if (x) {
- y0 = y0[1]; y1 = y1[1];
- }
- if (y.invert) {
- y0 = y(y0); y1 = y(y1);
- }
- if (y1 < y0) {
- t = y0; y0 = y1; y1 = t;
- }
- extent[0][1] = y0; extent[1][1] = y1;
- }
-
- return brush;
- };
-
- //empty extent and refresh foreground
- brush.clear = function () {
- extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0;
- brush.refresh();
- return brush;
- };
-
- //refresh foreground
- brush.refresh = function () {
- if (x) {
- d3_svg_brushRedrawX(brushEls, extent);
- }
- if (y) {
- d3_svg_brushRedrawY(brushEls, extent);
- }
- return brush;
- };
-
- //remove all brush elements, so users can reset brush attributes and redraw it.
- brush.remove = function () {
- $(paper.canvas).off("mousedown", "." + brushClass, down);
- brushEls.fg.remove();
- brushEls.bg.remove();
- brushEls.resizerSet.remove();
- return brush;
- };
-
- // if brush is empty, return true, else false;
- brush.empty = function () {
- return (x && extent[0][0] === extent[1][0]) || (y && extent[0][1] === extent[1][1]);
- };
-
- // set background attribute.
- brush.backgroundAttr = function (x) {
- if (!arguments.length) { return backgroundAttr; }
- backgroundAttr = x;
- return brush;
- };
-
- // set foreground attribute.
- brush.foregroundAttr = function (x) {
- if (!arguments.length) { return foregroundAttr; }
- foregroundAttr = x;
- return brush;
- };
-
- $(document).bind("mousemove", d3_svg_brushMove)
- .bind("mouseup", d3_svg_brushUp)
- .bind("keydown", d3_svg_brushKeydown)
- .bind("keyup", d3_svg_brushKeyup);
-
- return d3.rebind(brush, event, "on");
- };
-
- return Brush;
-});
+/*global d3,Raphael,$*/
+/*!
+ * Brush的兼容定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Brush', function (require) {
+
+ var d3_svg_brush,
+ d3_svg_brushDispatch,
+ d3_svg_brushTarget,
+ d3_svg_brushX,
+ d3_svg_brushY,
+ d3_svg_brushExtent,
+ d3_svg_brushDrag,
+ d3_svg_brushResize,
+ d3_svg_brushCenter,
+ d3_svg_brushOffset,
+ d3_svg_brushEls;
+
+ /**
+ * set foreground and resizers' x and width;
+ */
+ function d3_svg_brushRedrawX(brushEls, extent) {
+ brushEls.fg.attr({"x": extent[0][0],
+ "width": extent[1][0] - extent[0][0] });
+ brushEls.resizerSet.forEach(function (el) {
+ var orient = el.data("resizeOrient");
+
+ if (orient === "n" ||
+ orient === "s" ||
+ orient === "w" ||
+ orient === "nw" ||
+ orient === "sw") {
+ el.attr({"x": extent[0][0] - 2});
+ } else { // "e" "ne" "se"
+ el.attr({"x": extent[1][0] - 2});
+ }
+ if (orient === "n" || orient === "s") {
+ el.attr({"width": extent[1][0] - extent[0][0]});
+ }
+ });
+ }
+
+ /**
+ * set foreground and resizers' y and height;
+ */
+ function d3_svg_brushRedrawY(brushEls, extent) {
+ brushEls.fg.attr({"y": extent[0][1],
+ "height": extent[1][1] - extent[0][1] });
+ brushEls.resizerSet.forEach(function (el) {
+ var orient = el.data("resizeOrient");
+ if (orient === "n" ||
+ orient === "e" ||
+ orient === "w" ||
+ orient === "nw" ||
+ orient === "ne") {
+ el.attr({"y": extent[0][1] - 3});
+ } else { // "s" "se" "sw"
+ el.attr({"y": extent[1][1] - 4});
+ }
+ if (orient === "e" || orient === "w") {
+ el.attr({"height": extent[1][1] - extent[0][1]});
+ }
+ });
+ }
+
+ /**
+ * function from d3, get scaleRange of an ordinal scale
+ * @param domain, ordinal scale's range
+ */
+ function d3_scaleExtent(domain) {
+ var start = domain[0], stop = domain[domain.length - 1];
+ return start < stop ? [start, stop] : [stop, start];
+ }
+
+ /**
+ * function from d3, get scaleRange of a scale
+ */
+ function d3_scaleRange(scale) {
+ return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
+ }
+
+ /**
+ * function from d3, called by d3_svg_brushMove, compute new brush extent after brush moved
+ */
+ function d3_svg_brushMove1(mouse, scale, i) {
+ var range = d3_scaleRange(scale),
+ r0 = range[0],
+ r1 = range[1],
+ offset = d3_svg_brushOffset[i],
+ size = d3_svg_brushExtent[1][i] - d3_svg_brushExtent[0][i],
+ min,
+ max;
+
+ // When dragging, reduce the range by the extent size and offset.
+ if (d3_svg_brushDrag) {
+ r0 -= offset;
+ r1 -= size + offset;
+ }
+
+ // Clamp the mouse so that the extent fits within the range extent.
+ min = Math.max(r0, Math.min(r1, mouse[i]));
+
+ // Compute the new extent bounds.
+ if (d3_svg_brushDrag) {
+ max = (min += offset) + size;
+ } else {
+ // If the ALT key is pressed, then preserve the center of the extent.
+ if (d3_svg_brushCenter) {
+ offset = Math.max(r0, Math.min(r1, 2 * d3_svg_brushCenter[i] - min));
+ }
+
+ // Compute the min and max of the offset and mouse.
+ if (offset < min) {
+ max = min;
+ min = offset;
+ } else {
+ max = offset;
+ }
+ }
+
+ // Update the stored bounds.
+ d3_svg_brushExtent[0][i] = min;
+ d3_svg_brushExtent[1][i] = max;
+ }
+
+ /**
+ * function from d3, after brush moved, compute new brush extent
+ * and redraw foreground and resizer.
+ */
+ function d3_svg_brushMove(e) {
+ if (d3_svg_brushOffset) {
+ var bgOffset = $(d3_svg_brushTarget).offset();
+ var mouse = [e.pageX - bgOffset.left, e.pageY - bgOffset.top];
+
+ if (!d3_svg_brushDrag) {
+ // If needed, determine the center from the current extent.
+ if (e.altKey) {
+ if (!d3_svg_brushCenter) {
+ d3_svg_brushCenter = [
+ (d3_svg_brushExtent[0][0] + d3_svg_brushExtent[1][0]) / 2,
+ (d3_svg_brushExtent[0][1] + d3_svg_brushExtent[1][1]) / 2
+ ];
+ }
+
+ // Update the offset, for when the ALT key is released.
+ d3_svg_brushOffset[0] = d3_svg_brushExtent[+(mouse[0] < d3_svg_brushCenter[0])][0];
+ d3_svg_brushOffset[1] = d3_svg_brushExtent[+(mouse[1] < d3_svg_brushCenter[1])][1];
+ } else {
+ // When the ALT key is released, we clear the center.
+ d3_svg_brushCenter = null;
+ }
+ }
+
+ // Update the brush extent for each dimension.
+ if (d3_svg_brushX) {
+ d3_svg_brushMove1(mouse, d3_svg_brushX, 0);
+ d3_svg_brushRedrawX(d3_svg_brushEls, d3_svg_brushExtent);
+ }
+ if (d3_svg_brushY) {
+ d3_svg_brushMove1(mouse, d3_svg_brushY, 1);
+ d3_svg_brushRedrawY(d3_svg_brushEls, d3_svg_brushExtent);
+ }
+
+ // Notify listeners.
+ d3_svg_brushDispatch("brush");
+ }
+ }
+
+ /**
+ * function from d3,
+ * reset brush offset if user presses "space" key while brushing a new area,
+ * to ensure foreground's size unchanged while position changing.
+ */
+ function d3_svg_brushKeydown(e) {
+ if (e.keyCode === 32 && d3_svg_brushTarget && !d3_svg_brushDrag) {
+ d3_svg_brushCenter = null;
+ d3_svg_brushOffset[0] -= d3_svg_brushExtent[1][0];
+ d3_svg_brushOffset[1] -= d3_svg_brushExtent[1][1];
+ d3_svg_brushDrag = 2;
+ e.stopPropagation();
+ }
+ }
+
+ /**
+ * function from d3,
+ * reset brush offset if "space" key up to restore normal drush state.
+ */
+ function d3_svg_brushKeyup(e) {
+ if (e.keyCode === 32 && d3_svg_brushDrag === 2) {
+ d3_svg_brushOffset[0] += d3_svg_brushExtent[1][0];
+ d3_svg_brushOffset[1] += d3_svg_brushExtent[1][1];
+ d3_svg_brushDrag = 0;
+ e.stopPropagation();
+ }
+ }
+
+ /**
+ * function from d3,
+ * mouse up and stop brushing.
+ */
+ function d3_svg_brushUp(e) {
+ if (d3_svg_brushOffset) {
+ d3_svg_brushMove(e);
+ d3_svg_brushEls.resizerSet.forEach(function (resizer) {
+ //adjust all resizers
+ var orient = resizer.data("resizeOrient");
+ var size = d3_svg_brush.empty() ? 0 : 6;
+ if (orient === "n" || orient === "s") {
+ resizer.attr({"height": size});
+ } else {
+ resizer.attr({"width": size});
+ }
+ });
+ d3_svg_brushDispatch("brushend");
+ d3_svg_brush =
+ d3_svg_brushDispatch =
+ d3_svg_brushTarget =
+ d3_svg_brushX =
+ d3_svg_brushY =
+ d3_svg_brushExtent =
+ d3_svg_brushDrag =
+ d3_svg_brushResize =
+ d3_svg_brushCenter =
+ d3_svg_brushOffset =
+ d3_svg_brushEls = null;
+ e.stopPropagation();
+ }
+ }
+
+ var d3_svg_brushCursor = {
+ n: "ns-resize",
+ e: "ew-resize",
+ s: "ns-resize",
+ w: "ew-resize",
+ nw: "nwse-resize",
+ ne: "nesw-resize",
+ se: "nwse-resize",
+ sw: "nesw-resize"
+ };
+ var vml_brushCursor = {
+ n: "row-resize",
+ e: "col-resize",
+ s: "row-resize",
+ w: "col-resize",
+ nw: "all-scroll",
+ ne: "all-scroll",
+ se: "all-scroll",
+ sw: "all-scroll"
+ };
+
+ var Brush = function () {
+ var event = d3.dispatch("brushstart", "brush", "brushend"),
+ x, // x-scale, optional
+ y, // y-scale, optional
+ extent = [[0, 0], [0, 0]], // [x0, y0], [x1, y1]
+ e,
+ left,
+ top,
+ width,
+ height,
+ backgroundAttr = {
+ "fill": "#dddddd",
+ "stroke": "none",
+ "cursor": "crosshair"
+ },
+ foregroundAttr = {
+ "fill": "steelblue",
+ "stroke": "none",
+ "cursor": "move"
+ },
+ brushStart = function () {},
+ brushing = function () {},
+ brushEnd = function () {},
+
+ brushEls = {},
+ brushClass;
+
+ /*!
+ * mouse down and start brushing or dragging.
+ */
+ function down(e) {
+ var target = e.target,
+ bgOffset;
+
+ // Store some global state for the duration of the brush gesture.
+ d3_svg_brush = brush;
+ d3_svg_brushTarget = $(brushEls.paper.canvas).parent();
+ d3_svg_brushExtent = extent;
+ bgOffset = $(d3_svg_brushTarget).offset();
+
+ d3_svg_brushOffset = [e.pageX - bgOffset.left, e.pageY - bgOffset.top];
+ d3_svg_brushEls = brushEls;
+
+ // If the extent was clicked on, drag rather than brush;
+ // store the offset between the mouse and extent origin instead.
+ d3_svg_brushDrag = target.__brushNodeType__ === "fg" ? true : false;
+ if (d3_svg_brushDrag) {
+ d3_svg_brushOffset[0] = extent[0][0] - d3_svg_brushOffset[0];
+ d3_svg_brushOffset[1] = extent[0][1] - d3_svg_brushOffset[1];
+ } else if (/^resize/.test(target.__brushNodeType__)) {
+ // If a resizer was clicked on, record which side is to be resized.
+ // Also, set the offset to the opposite side.
+ d3_svg_brushResize = target.__brushNodeType__.split("_")[1];
+ d3_svg_brushOffset[0] = extent[+(/w$/.test(d3_svg_brushResize))][0];
+ d3_svg_brushOffset[1] = extent[+(/^n/.test(d3_svg_brushResize))][1];
+ } else if (e.altKey) {
+ // If the ALT key is down when starting a brush, the center is at the mouse.
+ d3_svg_brushCenter = d3_svg_brushOffset.slice();
+ }
+
+ // Restrict which dimensions are resized.
+ d3_svg_brushX = !/^(n|s)$/.test(d3_svg_brushResize) && x;
+ d3_svg_brushY = !/^(e|w)$/.test(d3_svg_brushResize) && y;
+
+ // Notify listeners.
+ d3_svg_brushDispatch = dispatcher(this, arguments);
+ d3_svg_brushDispatch("brushstart");
+ d3_svg_brushMove(e);
+ e.stopPropagation();
+ }
+
+ /*!
+ * create brush
+ * input a Raphael paper, return a brush object.
+ */
+ function brush(paper) {
+ var resizes = x && y ? ["n", "e", "s", "w", "nw", "ne", "se", "sw"]
+ : x ? ["e", "w"]
+ : y ? ["n", "s"]
+ : [];
+
+ if (x) {
+ e = d3_scaleRange(x);
+ left = e[0];
+ width = e[1] - e[0];
+ }
+
+ if (y) {
+ e = d3_scaleRange(y);
+ top = e[0];
+ height = e[1] - e[0];
+ }
+
+ brushEls.paper = paper;
+ brushEls.brushSet = paper.set();
+ brushEls.resizerSet = paper.set();
+ brushEls.bg = paper.rect(left, top, width, height)
+ .attr({"fill": "#dddddd",
+ "stroke": "none",
+ "cursor": "crosshair"
+ })
+ .attr(backgroundAttr);
+ brushEls.bg.node.__brushNodeType__ = "bg";
+ brushEls.bg.node.ondragstart = function () { return false; };//firefox drag bug fix;
+
+ brushClass = "brush" + brushEls.bg.id;
+
+ //$(brushEls.bg.node).addClass("brush bg rvml"); // fail to svg
+ brushEls.bg.node.setAttribute("class", "brush bg rvml " + brushClass);
+ brushEls.bg.node.setAttribute("className", "brush bg rvml " + brushClass);// IE 6,7
+
+ brushEls.fg = paper.rect(left, top, (x ? 0 : width), (y ? 0 : height))
+ .attr({"fill": "steelblue",
+ "stroke": "none",
+ "cursor": "move"
+ })
+ .attr(foregroundAttr);
+ brushEls.fg.node.__brushNodeType__ = "fg";
+ brushEls.fg.node.ondragstart = function () { return false; };//firefox drag bug fix;
+ //$(brushEls.fg.node).addClass("brush fg rvml"); //fail to svg
+ brushEls.fg.node.setAttribute("class", "brush fg rvml " + brushClass);
+ brushEls.fg.node.setAttribute("className", "brush fg rvml " + brushClass);// IE 6,7
+
+ resizes.forEach(function (d) {
+ var resizer = paper.rect(left, top, (x ? 6 : width), (y ? 6 : height))
+ .data("resizeOrient", d)
+ .attr({"cursor": d3_svg_brushCursor[d],
+ "fill": "white",
+ "stroke": "black",
+ "opacity": 0});
+ if (Raphael.vml) {
+ resizer.attr({"cursor": vml_brushCursor[d]});
+ }
+ if (brush.empty()) {
+ //hide all resizers
+ if (d === "n" || d === "s") {
+ resizer.attr({"height": 0});
+ } else {
+ resizer.attr({"width": 0});
+ }
+ }
+ resizer.node.__brushNodeType__ = "resizer_" + d;
+ resizer.node.ondragstart = function () { return false; };//firefox drag bug fix;
+ //$(resizer.node).addClass("brush rvml " + d3_svg_brushCursor[d]); //fail to svg
+ resizer.node.setAttribute("class", "brush rvml " + brushClass + " " + d3_svg_brushCursor[d]);
+ //IE 6,7
+ resizer.node.setAttribute("className", "brush rvml " + brushClass + " " + d3_svg_brushCursor[d]);
+ brushEls.resizerSet.push(resizer);
+ });
+
+ if (x) {
+ d3_svg_brushRedrawX(brushEls, extent);
+ }
+
+ if (y) {
+ d3_svg_brushRedrawY(brushEls, extent);
+ }
+
+ //$(paper.canvas).delegate(".brush","mousedown", down);
+ //$(paper.canvas).undelegate(".brush","mousedown", down);
+ //$(paper.canvas).delegate(".brush","mousedown", down);
+ //$(paper.canvas).off("mousedown", ".brush", down);
+ $(paper.canvas).on("mousedown", "." + brushClass, down);
+
+ brush.brushElements = brushEls;
+ return brush;
+ }
+
+ // dispatch event, bind data to golbal variant d3.event.
+ var dispatcher = function (that, argumentz) {
+ return function (type) {
+ var e = d3.event;
+ try {
+ d3.event = {type: type, target: brush};
+ event[type].apply(that, argumentz);
+ } finally {
+ d3.event = e;
+ }
+ };
+ };
+
+ /*!
+ * get or set brush's left
+ * @param z, a value in brush scale's domain
+ */
+ brush.left = function (z) {
+ if (!arguments.length) { return left; }
+ left = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's top
+ * @param z, a value in brush scale's domain
+ */
+ brush.top = function (z) {
+ if (!arguments.length) { return top; }
+ top = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's width
+ * @param z, a value in brush scale's domain
+ */
+ brush.width = function (z) {
+ if (!arguments.length) { return width; }
+ width = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's height
+ * @param z, a value in brush scale's domain
+ */
+ brush.height = function (z) {
+ if (!arguments.length) { return height; }
+ height = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's x scale
+ * @param z, d3's sacle object
+ */
+ brush.x = function (z) {
+ if (!arguments.length) { return x; }
+ x = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's y scale
+ * @param z, d3's sacle object
+ */
+ brush.y = function (z) {
+ if (!arguments.length) { return y; }
+ y = z;
+ return brush;
+ };
+
+ /*!
+ * get or set brush's extent in scale's domain format.
+ * if both x and y exist, @param z's format is [[x0, y0], [x1, y1]]
+ * if only one of x and y exists, @param z's format is [x0, x1] or [y0, y1].
+ */
+ brush.extent = function (z) {
+ var x0, x1, y0, y1, t;
+
+ // Invert the pixel extent to data-space.
+ if (!arguments.length) {
+ if (x) {
+ x0 = extent[0][0]; x1 = extent[1][0];
+ if (x.invert) {
+ x0 = x.invert(x0); x1 = x.invert(x1);
+ }
+ if (x1 < x0) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ }
+ if (y) {
+ y0 = extent[0][1]; y1 = extent[1][1];
+ if (y.invert) {
+ y0 = y.invert(y0); y1 = y.invert(y1);
+ }
+ if (y1 < y0) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ }
+ return x && y ? [[x0, y0], [x1, y1]] : x ? [x0, x1] : y && [y0, y1];
+ }
+
+ // Scale the data-space extent to pixels.
+ if (x) {
+ x0 = z[0]; x1 = z[1];
+ if (y) {
+ x0 = x0[0]; x1 = x1[0];
+ }
+ if (x.invert) {
+ x0 = x(x0); x1 = x(x1);
+ }
+ if (x1 < x0) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ extent[0][0] = x0; extent[1][0] = x1;
+ }
+ if (y) {
+ y0 = z[0]; y1 = z[1];
+ if (x) {
+ y0 = y0[1]; y1 = y1[1];
+ }
+ if (y.invert) {
+ y0 = y(y0); y1 = y(y1);
+ }
+ if (y1 < y0) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ extent[0][1] = y0; extent[1][1] = y1;
+ }
+
+ return brush;
+ };
+
+ //empty extent and refresh foreground
+ brush.clear = function () {
+ extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0;
+ brush.refresh();
+ return brush;
+ };
+
+ //refresh foreground
+ brush.refresh = function () {
+ if (x) {
+ d3_svg_brushRedrawX(brushEls, extent);
+ }
+ if (y) {
+ d3_svg_brushRedrawY(brushEls, extent);
+ }
+ return brush;
+ };
+
+ //remove all brush elements, so users can reset brush attributes and redraw it.
+ brush.remove = function () {
+ $(paper.canvas).off("mousedown", "." + brushClass, down);
+ brushEls.fg.remove();
+ brushEls.bg.remove();
+ brushEls.resizerSet.remove();
+ return brush;
+ };
+
+ // if brush is empty, return true, else false;
+ brush.empty = function () {
+ return (x && extent[0][0] === extent[1][0]) || (y && extent[0][1] === extent[1][1]);
+ };
+
+ // set background attribute.
+ brush.backgroundAttr = function (x) {
+ if (!arguments.length) { return backgroundAttr; }
+ backgroundAttr = x;
+ return brush;
+ };
+
+ // set foreground attribute.
+ brush.foregroundAttr = function (x) {
+ if (!arguments.length) { return foregroundAttr; }
+ foregroundAttr = x;
+ return brush;
+ };
+
+ $(document).bind("mousemove", d3_svg_brushMove)
+ .bind("mouseup", d3_svg_brushUp)
+ .bind("keydown", d3_svg_brushKeydown)
+ .bind("keyup", d3_svg_brushKeyup);
+
+ return d3.rebind(brush, event, "on");
+ };
+
+ return Brush;
+});
diff --git a/lib/charts/bullet.js b/lib/charts/bullet.js
index b363c75..3473ea3 100644
--- a/lib/charts/bullet.js
+++ b/lib/charts/bullet.js
@@ -1,349 +1,349 @@
-/*global Raphael, d3 */
-/*!
- * Bullet的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Bullet', function (require) {
- var DataV = require('DataV');
- var Axis = require('Axis');
-
- /**
- * Bullet构造函数
- * Options:
- *
- * - `width` 数字,图片宽度,默认为200,表示图片高200px
- * - `height` 数字,图片高度,默认为80
- * - `orient` string,图片方向,默认为"horizonal",表示水平方向。若为"vertical",则表示垂直方向
- * - `axisStyle` string, 坐标系类型,默认为"linear",表示坐标系为线性坐标系。若为"log",则表示对数坐标系
- * - `logBase` 数字, 采用对数坐标系时的对数基,默认为Math.E
- * - `tickDivide` 数字,表示坐标的标尺的分段数,默认为5
- * - `margin` 数字数组,表示图片上、右、下、左的边距,默认为 [20, 20, 20, 20]
- * - `centerBarRatio` 数字,表示中间的测度条的高度与背景条高度的比值, 默认为0.3
- * - `markerWidth` 数字,表示标记条的宽度, 默认为4,单位为像素
- * - `markerRatio` 数字,表示标记条的高度与背景条高度的比值,默认为0.7
- * - `titleRatio` 数字,表示子弹图title的高度与背景条高度的比值,默认为0.6,此时title与subtitle的比值
- * - `backgroundColor` string数组,表示背景条颜色的渐变数组,默认为["#666", "#ddd"],背景条颜色就是这两种颜色的渐变
- * - `measureColor` string数组,表示测度条颜色的渐变数组,默认为["steelblue", "#B0C4DE"],测度条颜色就是这两种颜色的渐变
- * - `markerColor` string,表示标记条的颜色,默认为"#000"
- *
- * Examples:
- * ```
- * //Create bullet in a dom node with id "chart", width is 500; height is 600px;
- * var bullet = new Bullet("chart", {
- * "width": 500,
- * "height": 600,
- * "margin": [10, 10, 20, 70]
- * });
- * //Create bullet with log base;
- * var log = new Bullet("chart2", {
- * width: 300,
- * height: 60,
- * margin: [10, 10, 20, 70],
- * backgroundColor: ["#66f", "#ddf"],
- * measureColor: ["#000", "#000"],
- * markerColor: "#44f",
- * axisStyle: "log",
- * logBase: 10
- * });
- * ```
- * @param {Mix} node The dom node or dom node Id
- * @param {Object} options options json object for determin stream style.
- */
- var Bullet = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Bullet";
- this.node = this.checkContainer(node);
- // Properties
- this.defaults.orient = "horizonal"; // "horizonal", "vertical"
- this.defaults.axisStyle = "linear"; // "linear", "log"
- this.defaults.logBase = Math.E;
- this.defaults.tickDivide = 5;
- this.defaults.margin = [10, 10, 20, 80];//top, right, bottom, left
- this.defaults.centerBarRatio = 0.3;
- this.defaults.markerWidth = 4;
- this.defaults.markerRatio = 0.7;
- this.defaults.titleRatio = 0.6; //title's text height : subtitle's text height = 6:4
- this.defaults.backgroundColor = ["#666", "#ddd"]; //dark, light
- this.defaults.measureColor = ["steelblue", "#B0C4DE"]; //dark, light
- this.defaults.markerColor = "#000";
-
- // canvas
- this.defaults.width = 200;
- this.defaults.height = 80;
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /*!
- * 创建画布
- */
- Bullet.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.width, conf.height);
- };
-
- /**
- * 设置数据源
- * Examples:
- * ```
- * bullet.setSource({
- * title: "Sample",
- * subtitle: "ratio",
- * ranges: [0, 0.5, 0.8, 1],
- * measures: [0.7, 0.9],
- * markers: [0.6],
- * rangeTitles: ["below 50%", "top 20% - 50%", "top 20%"],
- * measureTitles: ["value is 0.7", "value is 0.9"],
- * markerTitles: ["mean is 0.6"]
- * })
- * ```
- * @param {Object} source 数据源
- */
- Bullet.prototype.setSource = function (source) {
- var conf = this.defaults,
- range,
- axisOrient;
- this.data = source;
- if (conf.orient === "horizonal") {
- axisOrient = "bottom";
- range = [conf.margin[3], conf.width - conf.margin[1]];
- } else if (conf.orient === "vertical") {
- axisOrient = "left";
- range = [conf.height - conf.margin[2], conf.margin[0]];
- }
-
- if (conf.axisStyle === "linear") {
- this.scale = d3.scale.linear();
- } else if (conf.axisStyle === "log") {
- this.scale = d3.scale.log();
- }
-
- this.data.min = this.data.ranges[0];
- this.data.max = this.data.ranges[this.data.ranges.length - 1];
- this.scale.domain([this.data.min, this.data.max])
- .range(range);
-
- if (conf.axisStyle === "linear") {
- this.axis = Axis().scale(this.scale).orient(axisOrient).ticks(conf.tickDivide).domainAttr({"stroke": "none"});
- } else if (conf.axisStyle === "log") {
- this.logScale = d3.scale.linear()
- .domain([Math.log(this.data.min)/Math.log(conf.logBase), Math.log(this.data.max)/Math.log(conf.logBase)])
- .range(range);
- this.axis = Axis()
- .orient(axisOrient)
- .scale(this.logScale)
- .ticks(conf.tickDivide)
- .tickFormat(function (d) {return Math.round(Math.pow(conf.logBase, d));})
- .domainAttr({"stroke": "none"});
- }
- };
-
- /*!
- * generate bullet dom path
- */
- Bullet.prototype.generatePaths = function () {
- var conf = this.defaults;
- //get color function
- if (conf.backgroundColor) {
- this.color = d3.interpolateRgb.apply(null, [conf.backgroundColor[0], conf.backgroundColor[1]]);
- }
- if (conf.measureColor) {
- this.measureColor = d3.interpolateRgb.apply(null, [conf.measureColor[0], conf.measureColor[1]]);
- }
-
- if (conf.orient === "horizonal") {
- this.paintHorizonal();
- } else if (conf.orient === "vertical") {
- this.paintVertical();
- }
- };
-
- /*!
- * paint orient horizonal bullet
- */
- Bullet.prototype.paintHorizonal = function () {
- var conf = this.defaults;
- var paper = this.canvas,
- data = this.data,
- m = conf.margin,
- ranges = [],
- measures = [],
- markers = [],
- rangeTitles = [],
- i,
- l,
- rect,
- titleRatio,
- w,
- h = conf.height - m[0] - m[2],
- left;
-
- //axis
- this.axis(paper).attr({transform: "t" + 0 + ',' + (conf.height - m[2])});
- //color rect
- ranges = data.ranges;
- if (data.rangeTitles) {
- rangeTitles = data.rangeTitles;
- }
- left = m[3];
- for (i = 0, l = ranges.length - 1; i < l; i++) {
- w = this.scale(ranges[i + 1]) - this.scale(ranges[i]);
- rect = paper.rect(left, m[0], w, h)
- .attr({"stroke": "none",
- "fill": this.color(l === 1 ? 1 : i / (l - 1)),
- "title": rangeTitles[i] ? rangeTitles[i] : ""});
- left += w;
- }
-
- //measure bar
- data.measures.forEach(function (d, i) {
- var mTitles = data.measureTitles;
- var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
- measures.push({measure: d, measureTitle: mTitle});
- });
- measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
- left = this.scale(data.min);
- for (i = 0, l = measures.length; i < l; i++) {
- value = Math.max(data.min, Math.min(data.max, measures[i].measure));
- w = this.scale(value) - left;
- paper.rect(left,
- m[0] + h * (1 - conf.centerBarRatio) / 2,
- w,
- h * conf.centerBarRatio)
- .attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
- left += w;
- }
-
- //marker bar
- markers = data.markers;
- for (i = 0, l = markers.length; i < l; i++) {
- paper.rect(this.scale(markers[i]) - conf.markerWidth / 2,
- m[0] + h * (1 - conf.markerRatio) / 2,
- conf.markerWidth,
- h * conf.markerRatio)
- .attr({"stroke": "none", "fill": conf.markerColor,
- "title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
- }
-
- //title
- if (data.title) {
- titleRatio = data.subtitle ? conf.titleRatio : 1;
- this.title = paper.text(m[3] - 5, m[0] + h / 2, data.title)
- .attr({"text-anchor": "end", "font-weight": "bold", "font-size": h * titleRatio * 0.9});
- }
-
- //subtitle
- if (data.subtitle) {
- this.subtitle = paper.text(m[3] - 5, conf.height - m[2], data.subtitle)
- .attr({"text-anchor": "end", "font-size": h * (1 - conf.titleRatio) * 0.9});
- }
- };
-
- /*!
- * paint orient vertical bullet
- */
- Bullet.prototype.paintVertical = function () {
- var conf = this.defaults;
- var paper = this.canvas,
- data = this.data,
- m = conf.margin,
- ranges = [],
- measures = [],
- markers = [],
- rangeTitles = [],
- i,
- l,
- rect,
- titleRatio,
- w = conf.width - m[1] - m[3],
- h,
- bottom;
-
- //axis
- this.axis(paper).attr({transform: "t" + m[3] + ',' + 0});
-
- //color rect
- ranges = data.ranges;
- if (data.rangeTitles) {
- rangeTitles = data.rangeTitles;
- }
- bottom = conf.height - m[2];
- for (i = 0, l = ranges.length - 1; i < l; i++) {
- h = -this.scale(ranges[i + 1]) + this.scale(ranges[i]);
- rect = paper.rect(m[3], bottom - h, w, h)
- .attr({"stroke": "none",
- "fill": this.color(l === 1 ? 1 : i / (l - 1)),
- "title": rangeTitles[i] ? rangeTitles[i] : ""});
- bottom -= h;
- }
-
- //measure bar
- data.measures.forEach(function (d, i) {
- var mTitles = data.measureTitles;
- var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
- measures.push({measure: d, measureTitle: mTitle});
- });
- measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
- bottom = this.scale(data.min);
- for (i = 0, l = measures.length; i < l; i++) {
- value = Math.max(data.min, Math.min(data.max, measures[i].measure));
- h = -this.scale(value) + bottom;
- paper.rect(m[3] + w * (1 - conf.centerBarRatio) / 2,
- bottom - h,
- w * conf.centerBarRatio,
- h)
- .attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
- bottom -= h;
- }
-
- //marker bar
- markers = data.markers;
- for (i = 0, l = markers.length; i < l; i++) {
- paper.rect(m[3] + w * (1 - conf.markerRatio) / 2,
- this.scale(markers[i]) - conf.markerWidth / 2,
- w * conf.markerRatio,
- conf.markerWidth)
- .attr({"stroke": "none", "fill": conf.markerColor,
- "title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
- }
-
- //title
- if (data.title) {
- titleRatio = data.subtitle ? conf.titleRatio : 1;
- m[0] *= 0.9; //some ratio adjust;
- this.title = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * titleRatio / 2, data.title)
- .attr({"text-anchor": "middle", "font-weight": "bold", "font-size": m[0] * titleRatio * 0.8});
- }
-
- //subtitle
- if (data.subtitle) {
- this.subtitle = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * (1 - (1 - titleRatio) / 2), data.subtitle)
- .attr({"text-anchor": "middle", "font-size": m[0] * (1 - titleRatio) * 0.8});
- }
- };
-
- /*!
- * clean canvas
- */
- Bullet.prototype.clearCanvas = function () {
- this.canvas.clear();
- };
-
- /**
- * render bullet
- */
- Bullet.prototype.render = function (options) {
- this.setOptions(options);
- this.generatePaths();
- };
-
- return Bullet;
-});
-
+/*global Raphael, d3 */
+/*!
+ * Bullet的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Bullet', function (require) {
+ var DataV = require('DataV');
+ var Axis = require('Axis');
+
+ /**
+ * Bullet构造函数
+ * Options:
+ *
+ * - `width` 数字,图片宽度,默认为200,表示图片高200px
+ * - `height` 数字,图片高度,默认为80
+ * - `orient` string,图片方向,默认为"horizonal",表示水平方向。若为"vertical",则表示垂直方向
+ * - `axisStyle` string, 坐标系类型,默认为"linear",表示坐标系为线性坐标系。若为"log",则表示对数坐标系
+ * - `logBase` 数字, 采用对数坐标系时的对数基,默认为Math.E
+ * - `tickDivide` 数字,表示坐标的标尺的分段数,默认为5
+ * - `margin` 数字数组,表示图片上、右、下、左的边距,默认为 [20, 20, 20, 20]
+ * - `centerBarRatio` 数字,表示中间的测度条的高度与背景条高度的比值, 默认为0.3
+ * - `markerWidth` 数字,表示标记条的宽度, 默认为4,单位为像素
+ * - `markerRatio` 数字,表示标记条的高度与背景条高度的比值,默认为0.7
+ * - `titleRatio` 数字,表示子弹图title的高度与背景条高度的比值,默认为0.6,此时title与subtitle的比值
+ * - `backgroundColor` string数组,表示背景条颜色的渐变数组,默认为["#666", "#ddd"],背景条颜色就是这两种颜色的渐变
+ * - `measureColor` string数组,表示测度条颜色的渐变数组,默认为["steelblue", "#B0C4DE"],测度条颜色就是这两种颜色的渐变
+ * - `markerColor` string,表示标记条的颜色,默认为"#000"
+ *
+ * Examples:
+ * ```
+ * //Create bullet in a dom node with id "chart", width is 500; height is 600px;
+ * var bullet = new Bullet("chart", {
+ * "width": 500,
+ * "height": 600,
+ * "margin": [10, 10, 20, 70]
+ * });
+ * //Create bullet with log base;
+ * var log = new Bullet("chart2", {
+ * width: 300,
+ * height: 60,
+ * margin: [10, 10, 20, 70],
+ * backgroundColor: ["#66f", "#ddf"],
+ * measureColor: ["#000", "#000"],
+ * markerColor: "#44f",
+ * axisStyle: "log",
+ * logBase: 10
+ * });
+ * ```
+ * @param {Mix} node The dom node or dom node Id
+ * @param {Object} options options json object for determin stream style.
+ */
+ var Bullet = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Bullet";
+ this.node = this.checkContainer(node);
+ // Properties
+ this.defaults.orient = "horizonal"; // "horizonal", "vertical"
+ this.defaults.axisStyle = "linear"; // "linear", "log"
+ this.defaults.logBase = Math.E;
+ this.defaults.tickDivide = 5;
+ this.defaults.margin = [10, 10, 20, 80];//top, right, bottom, left
+ this.defaults.centerBarRatio = 0.3;
+ this.defaults.markerWidth = 4;
+ this.defaults.markerRatio = 0.7;
+ this.defaults.titleRatio = 0.6; //title's text height : subtitle's text height = 6:4
+ this.defaults.backgroundColor = ["#666", "#ddd"]; //dark, light
+ this.defaults.measureColor = ["steelblue", "#B0C4DE"]; //dark, light
+ this.defaults.markerColor = "#000";
+
+ // canvas
+ this.defaults.width = 200;
+ this.defaults.height = 80;
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /*!
+ * 创建画布
+ */
+ Bullet.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+ };
+
+ /**
+ * 设置数据源
+ * Examples:
+ * ```
+ * bullet.setSource({
+ * title: "Sample",
+ * subtitle: "ratio",
+ * ranges: [0, 0.5, 0.8, 1],
+ * measures: [0.7, 0.9],
+ * markers: [0.6],
+ * rangeTitles: ["below 50%", "top 20% - 50%", "top 20%"],
+ * measureTitles: ["value is 0.7", "value is 0.9"],
+ * markerTitles: ["mean is 0.6"]
+ * })
+ * ```
+ * @param {Object} source 数据源
+ */
+ Bullet.prototype.setSource = function (source) {
+ var conf = this.defaults,
+ range,
+ axisOrient;
+ this.data = source;
+ if (conf.orient === "horizonal") {
+ axisOrient = "bottom";
+ range = [conf.margin[3], conf.width - conf.margin[1]];
+ } else if (conf.orient === "vertical") {
+ axisOrient = "left";
+ range = [conf.height - conf.margin[2], conf.margin[0]];
+ }
+
+ if (conf.axisStyle === "linear") {
+ this.scale = d3.scale.linear();
+ } else if (conf.axisStyle === "log") {
+ this.scale = d3.scale.log();
+ }
+
+ this.data.min = this.data.ranges[0];
+ this.data.max = this.data.ranges[this.data.ranges.length - 1];
+ this.scale.domain([this.data.min, this.data.max])
+ .range(range);
+
+ if (conf.axisStyle === "linear") {
+ this.axis = Axis().scale(this.scale).orient(axisOrient).ticks(conf.tickDivide).domainAttr({"stroke": "none"});
+ } else if (conf.axisStyle === "log") {
+ this.logScale = d3.scale.linear()
+ .domain([Math.log(this.data.min)/Math.log(conf.logBase), Math.log(this.data.max)/Math.log(conf.logBase)])
+ .range(range);
+ this.axis = Axis()
+ .orient(axisOrient)
+ .scale(this.logScale)
+ .ticks(conf.tickDivide)
+ .tickFormat(function (d) {return Math.round(Math.pow(conf.logBase, d));})
+ .domainAttr({"stroke": "none"});
+ }
+ };
+
+ /*!
+ * generate bullet dom path
+ */
+ Bullet.prototype.generatePaths = function () {
+ var conf = this.defaults;
+ //get color function
+ if (conf.backgroundColor) {
+ this.color = d3.interpolateRgb.apply(null, [conf.backgroundColor[0], conf.backgroundColor[1]]);
+ }
+ if (conf.measureColor) {
+ this.measureColor = d3.interpolateRgb.apply(null, [conf.measureColor[0], conf.measureColor[1]]);
+ }
+
+ if (conf.orient === "horizonal") {
+ this.paintHorizonal();
+ } else if (conf.orient === "vertical") {
+ this.paintVertical();
+ }
+ };
+
+ /*!
+ * paint orient horizonal bullet
+ */
+ Bullet.prototype.paintHorizonal = function () {
+ var conf = this.defaults;
+ var paper = this.canvas,
+ data = this.data,
+ m = conf.margin,
+ ranges = [],
+ measures = [],
+ markers = [],
+ rangeTitles = [],
+ i,
+ l,
+ rect,
+ titleRatio,
+ w,
+ h = conf.height - m[0] - m[2],
+ left;
+
+ //axis
+ this.axis(paper).attr({transform: "t" + 0 + ',' + (conf.height - m[2])});
+ //color rect
+ ranges = data.ranges;
+ if (data.rangeTitles) {
+ rangeTitles = data.rangeTitles;
+ }
+ left = m[3];
+ for (i = 0, l = ranges.length - 1; i < l; i++) {
+ w = this.scale(ranges[i + 1]) - this.scale(ranges[i]);
+ rect = paper.rect(left, m[0], w, h)
+ .attr({"stroke": "none",
+ "fill": this.color(l === 1 ? 1 : i / (l - 1)),
+ "title": rangeTitles[i] ? rangeTitles[i] : ""});
+ left += w;
+ }
+
+ //measure bar
+ data.measures.forEach(function (d, i) {
+ var mTitles = data.measureTitles;
+ var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
+ measures.push({measure: d, measureTitle: mTitle});
+ });
+ measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
+ left = this.scale(data.min);
+ for (i = 0, l = measures.length; i < l; i++) {
+ value = Math.max(data.min, Math.min(data.max, measures[i].measure));
+ w = this.scale(value) - left;
+ paper.rect(left,
+ m[0] + h * (1 - conf.centerBarRatio) / 2,
+ w,
+ h * conf.centerBarRatio)
+ .attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
+ left += w;
+ }
+
+ //marker bar
+ markers = data.markers;
+ for (i = 0, l = markers.length; i < l; i++) {
+ paper.rect(this.scale(markers[i]) - conf.markerWidth / 2,
+ m[0] + h * (1 - conf.markerRatio) / 2,
+ conf.markerWidth,
+ h * conf.markerRatio)
+ .attr({"stroke": "none", "fill": conf.markerColor,
+ "title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
+ }
+
+ //title
+ if (data.title) {
+ titleRatio = data.subtitle ? conf.titleRatio : 1;
+ this.title = paper.text(m[3] - 5, m[0] + h / 2, data.title)
+ .attr({"text-anchor": "end", "font-weight": "bold", "font-size": h * titleRatio * 0.9});
+ }
+
+ //subtitle
+ if (data.subtitle) {
+ this.subtitle = paper.text(m[3] - 5, conf.height - m[2], data.subtitle)
+ .attr({"text-anchor": "end", "font-size": h * (1 - conf.titleRatio) * 0.9});
+ }
+ };
+
+ /*!
+ * paint orient vertical bullet
+ */
+ Bullet.prototype.paintVertical = function () {
+ var conf = this.defaults;
+ var paper = this.canvas,
+ data = this.data,
+ m = conf.margin,
+ ranges = [],
+ measures = [],
+ markers = [],
+ rangeTitles = [],
+ i,
+ l,
+ rect,
+ titleRatio,
+ w = conf.width - m[1] - m[3],
+ h,
+ bottom;
+
+ //axis
+ this.axis(paper).attr({transform: "t" + m[3] + ',' + 0});
+
+ //color rect
+ ranges = data.ranges;
+ if (data.rangeTitles) {
+ rangeTitles = data.rangeTitles;
+ }
+ bottom = conf.height - m[2];
+ for (i = 0, l = ranges.length - 1; i < l; i++) {
+ h = -this.scale(ranges[i + 1]) + this.scale(ranges[i]);
+ rect = paper.rect(m[3], bottom - h, w, h)
+ .attr({"stroke": "none",
+ "fill": this.color(l === 1 ? 1 : i / (l - 1)),
+ "title": rangeTitles[i] ? rangeTitles[i] : ""});
+ bottom -= h;
+ }
+
+ //measure bar
+ data.measures.forEach(function (d, i) {
+ var mTitles = data.measureTitles;
+ var mTitle = mTitles && mTitles[i] ? mTitles[i] : "";
+ measures.push({measure: d, measureTitle: mTitle});
+ });
+ measures.sort(function (a, b) { return d3.ascending(a.measure, b.measure); });
+ bottom = this.scale(data.min);
+ for (i = 0, l = measures.length; i < l; i++) {
+ value = Math.max(data.min, Math.min(data.max, measures[i].measure));
+ h = -this.scale(value) + bottom;
+ paper.rect(m[3] + w * (1 - conf.centerBarRatio) / 2,
+ bottom - h,
+ w * conf.centerBarRatio,
+ h)
+ .attr({"stroke": "none", "fill": this.measureColor(l === 1 ? 1 : i / (l - 1)), "title": measures[i].measureTitle});
+ bottom -= h;
+ }
+
+ //marker bar
+ markers = data.markers;
+ for (i = 0, l = markers.length; i < l; i++) {
+ paper.rect(m[3] + w * (1 - conf.markerRatio) / 2,
+ this.scale(markers[i]) - conf.markerWidth / 2,
+ w * conf.markerRatio,
+ conf.markerWidth)
+ .attr({"stroke": "none", "fill": conf.markerColor,
+ "title": data.markerTitles && data.markerTitles[i] ? data.markerTitles[i] : ""});
+ }
+
+ //title
+ if (data.title) {
+ titleRatio = data.subtitle ? conf.titleRatio : 1;
+ m[0] *= 0.9; //some ratio adjust;
+ this.title = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * titleRatio / 2, data.title)
+ .attr({"text-anchor": "middle", "font-weight": "bold", "font-size": m[0] * titleRatio * 0.8});
+ }
+
+ //subtitle
+ if (data.subtitle) {
+ this.subtitle = paper.text((conf.width + m[3] - m[1])/ 2, m[0] * (1 - (1 - titleRatio) / 2), data.subtitle)
+ .attr({"text-anchor": "middle", "font-size": m[0] * (1 - titleRatio) * 0.8});
+ }
+ };
+
+ /*!
+ * clean canvas
+ */
+ Bullet.prototype.clearCanvas = function () {
+ this.canvas.clear();
+ };
+
+ /**
+ * render bullet
+ */
+ Bullet.prototype.render = function (options) {
+ this.setOptions(options);
+ this.generatePaths();
+ };
+
+ return Bullet;
+});
+
diff --git a/lib/charts/bundle.js b/lib/charts/bundle.js
index 24e7b07..1e909c0 100644
--- a/lib/charts/bundle.js
+++ b/lib/charts/bundle.js
@@ -1,424 +1,424 @@
-/*global EventProxy, d3, Raphael, self, packages, $ */
-
-/*!
- * Bundle的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Bundle', function (require) {
- var DataV = require('DataV');
-
- /**
- * 构造函数,node参数表示在html的哪个容器中绘制该组件
- * options对象为用户自定义的组件的属性,比如画布大小
- */
- var Bundle = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Bundle";
- this.node = this.checkContainer(node);
- this.json = {};
-
- // 图的半径
- this.defaults.diameter = 960;
- this.defaults.radius = this.defaults.diameter / 2;
- this.defaults.innerRadius = this.defaults.radius - 120;
- this.defaults.tension = 0.85;
-
- this.defaults.color = {
- defaultLineColor: "#4065AF",
- defaultWordColor: "#000000",
- lineHoverColor: "#02B0ED",
- nodeHoverColor: "#02B0ED",
- importNodesColor: "#5DA714", //被引用的节点
- exportNodesColor: "#FE3919" //引用当前节点的节点
- };
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * 设置用户自定义属性
- */
- Bundle.prototype.setOptions = function (options) {
- if (options) {
- var prop;
- for (prop in options) {
- if (options.hasOwnProperty(prop)) {
- this.defaults[prop] = options[prop];
- if (prop === "diameter") {
- this.defaults.radius = this.defaults.diameter / 2;
- this.defaults.innerRadius = this.defaults.radius - 120;
- } else if (prop === "radius") {
- this.defaults.diameter = this.defaults.radius * 2;
- this.defaults.innerRadius = this.defaults.radius - 120;
- } else if (prop === "innerRadius") {
- this.defaults.radius = this.defaults.innerRadius + 120;
- this.defaults.diameter = this.defaults.radius * 2;
- } else if (prop === "width") {
- this.defaults.diameter = this.defaults.width;
- this.defaults.radius = this.defaults.diameter / 2;
- this.defaults.innerRadius = this.defaults.radius - 120;
- }
- }
- }
- }
- };
-
- /**
- * 对原始数据进行处理
- * TODO: 改进为获取值时运算
- */
- Bundle.prototype.setSource = function (source) {
- if (source[0] && source[0] instanceof Array) {
- // csv or 2d array source
- if (source[0][0] === "name") {
- source = source.slice(1); // 从第一行开始,第0行舍去
- }
- var nData = [];
- var imports = [];
- //var isNode = true;
- var nodeNum;
- var that = this;
- source.forEach(function (d, i) {
- if (d[0] === "") {
- throw new Error("name can not be empty(line:" + (i + 1) + ").");
- }
- if (d[1] !== "") {
- imports = d[1].split(" ");
- }
- nData[i] = {
- name: d[0],
- imports: imports
- };
- });
- this.json = nData;
- } else {
- // json source
- this.json = source;
- }
- };
-
- /**
- * 创建画布
- */
- Bundle.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.diameter, conf.diameter);
-
- //var c = this.canvas.circle(50, 50, 40);
- };
-
- /**
- * 布局
- */
- Bundle.prototype.layout = function () {
- var packages = {
- // Lazily construct the package hierarchy from class names.
- root: function (classes) {
- var map = {};
- function construct(name, data) {
- var node = map[name], i;
- if (!node) {
- node = map[name] = data || {name: name, children: []};
- if (name.length) {
- node.parent = construct(name.substring(0, i = name.lastIndexOf(".")));
- node.parent.children.push(node);
- node.key = name.substring(i + 1);
- }
- }
- return node;
- }
-
- classes.forEach(function (d) {
- construct(d.name, d);
- });
-
- return map[""];
- },
-
- // Return a list of imports for the given array of nodes.
- imports: function (nodes) {
- var map = {},
- imports = [];
-
- // Compute a map from name to node.
- nodes.forEach(function (d) {
- map[d.name] = d;
- });
-
- // For each import, construct a link from the source to target node.
- nodes.forEach(function (d) {
- if (d.imports) {
- d.imports.forEach(function (i) {imports.push({source: map[d.name], target: map[i]});
- });
- }
- });
-
- return imports;
- }
- };
-
- var cluster = d3.layout.cluster()
- .size([360, this.defaults.innerRadius]) //.size(角度,半径)
- .sort(null)
- .value(function (d) {
- return d.size;
- });
- this.nodes = cluster.nodes(packages.root(this.json));
- this.links = packages.imports(this.nodes);
- };
-
- /**
- * 渲染弦图
- */
- Bundle.prototype.render = function () {
- this.layout();
- this.generatePaths();
- };
-
- /**
- * 生成路径
- */
- Bundle.prototype.generatePaths = function (options) {
- var that = this;
-
- var canvas = this.canvas;
- var rNodes = canvas.set();
- var rLinks = canvas.set();
-
- var bundle = d3.layout.bundle();
-
- var line = d3.svg.line.radial()
- .interpolate("bundle")
- .tension(this.defaults.tension)
- .radius(function (d) {
- return d.y;
- })
- .angle(function (d) {
- return d.x / 180 * Math.PI;
- });
-
- //定义图中的弦和节点
- var nodes = this.nodes;
- var links = this.links;
- var linksCount = links.length;
- var paths = bundle(links);
-
- var locateStr = ""; //对文字进行平移和旋转
- var locateBBox = ""; //对文字的bounding box进行平移和旋转
- var r = 0;
- var angle = 0;
- var xTrans = 0;
- var yTrans = 0;
- var anchor; //text-anchor: start or end
- var rotateStr = "";
-
- //element data cache
- var nodeRelatedElements = {};// {key: {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []}}
- var nodeElements = {}; //{key: Els}
- var bBoxElements = {}; //{key: Els}
-
- var i,
- j,
- key,
- textEl,
- bBox,
- bBoxNew,
- tCenterX,
- tCenterY,
- bBoxEl,
- linkEl;
-
- var mouseoverLink = function () {
- var current = this;
- //var color = that.data("color");
- if (rLinks.preLink) {
- rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
- .attr("stroke-width", 1)
- .attr("stroke-opacity", 0.6);
-
- }
- rLinks.preLink = this;
-
- current.attr("stroke", that.defaults.color.lineHoverColor)
- .attr("stroke-width", 2)
- .attr("stroke-opacity", 1.0)
- .toFront(); //把当前弦移到画布最上层
- };
-
- var mouseoverNode = function () {
- var relatedEl = this.data("relatedElements");
- //高亮所选节点的文字颜色
- this.data("relatedNode").attr({"fill": that.defaults.color.nodeHoverColor,
- "fill-opacity": 1.0, "font-weight": "600"});
- //将包围盒颜色设为透明
- this.attr({"fill": that.defaults.color.nodeHoverColor, "fill-opacity": 0.0/*, "font-weight": "600"*/});
-
- relatedEl.sourceLink.forEach(function (d) { //set green
- d.attr({"stroke": that.defaults.color.importNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
- .toFront();
- });
- relatedEl.sourceNode.forEach(function (d) {
- d.attr({"fill": that.defaults.color.importNodesColor, "font-weight": "600"});
- });
- relatedEl.targetLink.forEach(function (d) { //set red
- d.attr({"stroke": that.defaults.color.exportNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
- .toFront();
- });
- relatedEl.targetNode.forEach(function (d) {
- d.attr({"fill": that.defaults.color.exportNodesColor, "font-weight": "600"});
- });
- };
-
- var mouseoutNode = function () {
- var relatedEl = this.data("relatedElements");
- this.data("relatedNode").attr({"fill": that.defaults.color.defaultWordColor,
- "font-weight": "400", "fill-opacity": 1.0});
- relatedEl.targetLink.forEach(function (d) {
- d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
- });
- relatedEl.targetNode.forEach(function (d) {
- d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
- });
- relatedEl.sourceLink.forEach(function (d) {
- d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
- });
- relatedEl.sourceNode.forEach(function (d) {
- d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
- });
- };
-
- for (j = 0; j < nodes.length; j++) {
- //若为叶子节点
- if (!nodes[j].children) {
- locateStr = "T" + that.defaults.radius + "," + that.defaults.radius + "R"; //使用大写T、R、S--绝对,not相对
-
- //半径: add a padding between lines and words
- r = nodes[j].y + 20;
-
- //计算旋转角度和水平、竖直方向所需平移的距离
- angle = (nodes[j].x - 90) * Math.PI / 180;
- xTrans = r * Math.cos(angle);
- yTrans = r * Math.sin(angle);
-
- //计算text-anchor
- if (nodes[j].x < 180) {
- anchor = "start";
- } else {
- anchor = "end";
- }
-
- //计算文字方向是否需要旋转180度
- if (nodes[j].x < 180) {
- rotateStr = "";
- } else {
- rotateStr = "R180";
- }
-
- //计算文字需要如何经过平移和旋转被排列在圆周上
- locateStr += (nodes[j].x - 90) + rotateStr + "T" + xTrans + "," + yTrans;
-
- //绘制文字
- textEl = canvas.text()
- .attr("font", "11px arial")
- .data("color", that.defaults.color)
- .attr("text", nodes[j].key)
- //.attr("title", nodes[j].size)
- .transform(locateStr)
- .attr("text-anchor", anchor)
- .attr("fill", that.defaults.color.defaultWordColor);
-
- //获取旋转平移之前文字的bounding box
- bBox = textEl.getBBox(true);
-
- //canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height);
- //获取旋转平移之后文字的bounding box
- bBoxNew = textEl.getBBox();
- //adjust vml box center
- if (Raphael.vml) {
- //vml's word bbox is not related to text-anchor, always middle;
- //svg's word bbox is related to text-anchor;
- bBoxNew.x = bBoxNew.x + bBox.width / 2 * Math.cos(angle);
- bBoxNew.y = bBoxNew.y + bBox.width / 2 * Math.sin(angle);
- }
- //canvas.rect(bBoxNew.x, bBoxNew.y, bBoxNew.width, bBoxNew.height);
-
- //新旧bounding box的中心坐标变化
- tCenterX = bBoxNew.x + bBoxNew.width / 2 - bBox.x - bBox.width / 2;
- tCenterY = bBoxNew.y + bBoxNew.height / 2 - bBox.y - bBox.height / 2;
- //对bounding box进行平移和旋转
- locateBBox = "T" + tCenterX + "," + tCenterY + "R" + (nodes[j].x - 90) + rotateStr;
-
- // 包围盒
- bBoxEl = canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height)
- .transform(locateBBox)
- .data("relatedNode", textEl)
- .attr({"fill": "#fff", "opacity": 0.01});
-
- key = nodes[j].key;
- nodeElements[key] = textEl;
- bBoxElements[key] = bBoxEl;
- nodeRelatedElements[key] = {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []};
-
- rNodes.push(textEl);
- }
- }
-
- //绘制曲线
- for (i = 0; i < linksCount; i++) {
- var l = paths[i];
-
- //对paths数组中的每一项进行计算,由路径节点信息得到坐标值
- var spline = line(l);
- var sourceKey = links[i].source.key;
- var targetKey = links[i].target.key;
- var tips = "link source: " + sourceKey + "\n"
- + "link target: " + targetKey;
-
- linkEl = canvas.path(spline)
- //.attr("stroke", that.defaults.defaultLineColor)
- .attr("stroke-opacity", 0.6)
- .attr("title", tips)
- .attr("d", spline)
- .attr("stroke", that.defaults.color.defaultLineColor)
- .translate(that.defaults.radius, that.defaults.radius)
- .mouseover(mouseoverLink);
- //.mouseout(mouseoutLink);
- linkEl[0].el = linkEl;
-
- nodeRelatedElements[sourceKey].targetLink.push(linkEl);
- nodeRelatedElements[sourceKey].targetNode.push(nodeElements[targetKey]);
- nodeRelatedElements[targetKey].sourceLink.push(linkEl);
- nodeRelatedElements[targetKey].sourceNode.push(nodeElements[sourceKey]);
- rLinks.push(linkEl);
- }
-
- $(this.canvas.canvas).mousemove(function (e) {
- if(!e.target.el && rLinks.preLink){
- rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
- .attr("stroke-width", 1)
- .attr("stroke-opacity", 0.6);
- rLinks.preLink = undefined;
- //console.log("a");
- }
- });
-
- //bind text words hover event
- for (key in bBoxElements) {
- if (bBoxElements.hasOwnProperty(key)) {
- bBoxElements[key].data("relatedElements", nodeRelatedElements[key])
- .mouseover(mouseoverNode)
- .mouseout(mouseoutNode);
- }
- }
-
- };
-
- return Bundle;
-});
+/*global EventProxy, d3, Raphael, self, packages, $ */
+
+/*!
+ * Bundle的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Bundle', function (require) {
+ var DataV = require('DataV');
+
+ /**
+ * 构造函数,node参数表示在html的哪个容器中绘制该组件
+ * options对象为用户自定义的组件的属性,比如画布大小
+ */
+ var Bundle = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Bundle";
+ this.node = this.checkContainer(node);
+ this.json = {};
+
+ // 图的半径
+ this.defaults.diameter = 960;
+ this.defaults.radius = this.defaults.diameter / 2;
+ this.defaults.innerRadius = this.defaults.radius - 120;
+ this.defaults.tension = 0.85;
+
+ this.defaults.color = {
+ defaultLineColor: "#4065AF",
+ defaultWordColor: "#000000",
+ lineHoverColor: "#02B0ED",
+ nodeHoverColor: "#02B0ED",
+ importNodesColor: "#5DA714", //被引用的节点
+ exportNodesColor: "#FE3919" //引用当前节点的节点
+ };
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * 设置用户自定义属性
+ */
+ Bundle.prototype.setOptions = function (options) {
+ if (options) {
+ var prop;
+ for (prop in options) {
+ if (options.hasOwnProperty(prop)) {
+ this.defaults[prop] = options[prop];
+ if (prop === "diameter") {
+ this.defaults.radius = this.defaults.diameter / 2;
+ this.defaults.innerRadius = this.defaults.radius - 120;
+ } else if (prop === "radius") {
+ this.defaults.diameter = this.defaults.radius * 2;
+ this.defaults.innerRadius = this.defaults.radius - 120;
+ } else if (prop === "innerRadius") {
+ this.defaults.radius = this.defaults.innerRadius + 120;
+ this.defaults.diameter = this.defaults.radius * 2;
+ } else if (prop === "width") {
+ this.defaults.diameter = this.defaults.width;
+ this.defaults.radius = this.defaults.diameter / 2;
+ this.defaults.innerRadius = this.defaults.radius - 120;
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * 对原始数据进行处理
+ * TODO: 改进为获取值时运算
+ */
+ Bundle.prototype.setSource = function (source) {
+ if (source[0] && source[0] instanceof Array) {
+ // csv or 2d array source
+ if (source[0][0] === "name") {
+ source = source.slice(1); // 从第一行开始,第0行舍去
+ }
+ var nData = [];
+ var imports = [];
+ //var isNode = true;
+ var nodeNum;
+ var that = this;
+ source.forEach(function (d, i) {
+ if (d[0] === "") {
+ throw new Error("name can not be empty(line:" + (i + 1) + ").");
+ }
+ if (d[1] !== "") {
+ imports = d[1].split(" ");
+ }
+ nData[i] = {
+ name: d[0],
+ imports: imports
+ };
+ });
+ this.json = nData;
+ } else {
+ // json source
+ this.json = source;
+ }
+ };
+
+ /**
+ * 创建画布
+ */
+ Bundle.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.diameter, conf.diameter);
+
+ //var c = this.canvas.circle(50, 50, 40);
+ };
+
+ /**
+ * 布局
+ */
+ Bundle.prototype.layout = function () {
+ var packages = {
+ // Lazily construct the package hierarchy from class names.
+ root: function (classes) {
+ var map = {};
+ function construct(name, data) {
+ var node = map[name], i;
+ if (!node) {
+ node = map[name] = data || {name: name, children: []};
+ if (name.length) {
+ node.parent = construct(name.substring(0, i = name.lastIndexOf(".")));
+ node.parent.children.push(node);
+ node.key = name.substring(i + 1);
+ }
+ }
+ return node;
+ }
+
+ classes.forEach(function (d) {
+ construct(d.name, d);
+ });
+
+ return map[""];
+ },
+
+ // Return a list of imports for the given array of nodes.
+ imports: function (nodes) {
+ var map = {},
+ imports = [];
+
+ // Compute a map from name to node.
+ nodes.forEach(function (d) {
+ map[d.name] = d;
+ });
+
+ // For each import, construct a link from the source to target node.
+ nodes.forEach(function (d) {
+ if (d.imports) {
+ d.imports.forEach(function (i) {imports.push({source: map[d.name], target: map[i]});
+ });
+ }
+ });
+
+ return imports;
+ }
+ };
+
+ var cluster = d3.layout.cluster()
+ .size([360, this.defaults.innerRadius]) //.size(角度,半径)
+ .sort(null)
+ .value(function (d) {
+ return d.size;
+ });
+ this.nodes = cluster.nodes(packages.root(this.json));
+ this.links = packages.imports(this.nodes);
+ };
+
+ /**
+ * 渲染弦图
+ */
+ Bundle.prototype.render = function () {
+ this.layout();
+ this.generatePaths();
+ };
+
+ /**
+ * 生成路径
+ */
+ Bundle.prototype.generatePaths = function (options) {
+ var that = this;
+
+ var canvas = this.canvas;
+ var rNodes = canvas.set();
+ var rLinks = canvas.set();
+
+ var bundle = d3.layout.bundle();
+
+ var line = d3.svg.line.radial()
+ .interpolate("bundle")
+ .tension(this.defaults.tension)
+ .radius(function (d) {
+ return d.y;
+ })
+ .angle(function (d) {
+ return d.x / 180 * Math.PI;
+ });
+
+ //定义图中的弦和节点
+ var nodes = this.nodes;
+ var links = this.links;
+ var linksCount = links.length;
+ var paths = bundle(links);
+
+ var locateStr = ""; //对文字进行平移和旋转
+ var locateBBox = ""; //对文字的bounding box进行平移和旋转
+ var r = 0;
+ var angle = 0;
+ var xTrans = 0;
+ var yTrans = 0;
+ var anchor; //text-anchor: start or end
+ var rotateStr = "";
+
+ //element data cache
+ var nodeRelatedElements = {};// {key: {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []}}
+ var nodeElements = {}; //{key: Els}
+ var bBoxElements = {}; //{key: Els}
+
+ var i,
+ j,
+ key,
+ textEl,
+ bBox,
+ bBoxNew,
+ tCenterX,
+ tCenterY,
+ bBoxEl,
+ linkEl;
+
+ var mouseoverLink = function () {
+ var current = this;
+ //var color = that.data("color");
+ if (rLinks.preLink) {
+ rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
+ .attr("stroke-width", 1)
+ .attr("stroke-opacity", 0.6);
+
+ }
+ rLinks.preLink = this;
+
+ current.attr("stroke", that.defaults.color.lineHoverColor)
+ .attr("stroke-width", 2)
+ .attr("stroke-opacity", 1.0)
+ .toFront(); //把当前弦移到画布最上层
+ };
+
+ var mouseoverNode = function () {
+ var relatedEl = this.data("relatedElements");
+ //高亮所选节点的文字颜色
+ this.data("relatedNode").attr({"fill": that.defaults.color.nodeHoverColor,
+ "fill-opacity": 1.0, "font-weight": "600"});
+ //将包围盒颜色设为透明
+ this.attr({"fill": that.defaults.color.nodeHoverColor, "fill-opacity": 0.0/*, "font-weight": "600"*/});
+
+ relatedEl.sourceLink.forEach(function (d) { //set green
+ d.attr({"stroke": that.defaults.color.importNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
+ .toFront();
+ });
+ relatedEl.sourceNode.forEach(function (d) {
+ d.attr({"fill": that.defaults.color.importNodesColor, "font-weight": "600"});
+ });
+ relatedEl.targetLink.forEach(function (d) { //set red
+ d.attr({"stroke": that.defaults.color.exportNodesColor, "stroke-width": 1, "stroke-opacity": 0.9})
+ .toFront();
+ });
+ relatedEl.targetNode.forEach(function (d) {
+ d.attr({"fill": that.defaults.color.exportNodesColor, "font-weight": "600"});
+ });
+ };
+
+ var mouseoutNode = function () {
+ var relatedEl = this.data("relatedElements");
+ this.data("relatedNode").attr({"fill": that.defaults.color.defaultWordColor,
+ "font-weight": "400", "fill-opacity": 1.0});
+ relatedEl.targetLink.forEach(function (d) {
+ d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
+ });
+ relatedEl.targetNode.forEach(function (d) {
+ d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
+ });
+ relatedEl.sourceLink.forEach(function (d) {
+ d.attr({"stroke": that.defaults.color.defaultLineColor, "stroke-width": 1, "stroke-opacity": 0.6});
+ });
+ relatedEl.sourceNode.forEach(function (d) {
+ d.attr({"fill": that.defaults.color.defaultWordColor, "font-weight": "400"});
+ });
+ };
+
+ for (j = 0; j < nodes.length; j++) {
+ //若为叶子节点
+ if (!nodes[j].children) {
+ locateStr = "T" + that.defaults.radius + "," + that.defaults.radius + "R"; //使用大写T、R、S--绝对,not相对
+
+ //半径: add a padding between lines and words
+ r = nodes[j].y + 20;
+
+ //计算旋转角度和水平、竖直方向所需平移的距离
+ angle = (nodes[j].x - 90) * Math.PI / 180;
+ xTrans = r * Math.cos(angle);
+ yTrans = r * Math.sin(angle);
+
+ //计算text-anchor
+ if (nodes[j].x < 180) {
+ anchor = "start";
+ } else {
+ anchor = "end";
+ }
+
+ //计算文字方向是否需要旋转180度
+ if (nodes[j].x < 180) {
+ rotateStr = "";
+ } else {
+ rotateStr = "R180";
+ }
+
+ //计算文字需要如何经过平移和旋转被排列在圆周上
+ locateStr += (nodes[j].x - 90) + rotateStr + "T" + xTrans + "," + yTrans;
+
+ //绘制文字
+ textEl = canvas.text()
+ .attr("font", "11px arial")
+ .data("color", that.defaults.color)
+ .attr("text", nodes[j].key)
+ //.attr("title", nodes[j].size)
+ .transform(locateStr)
+ .attr("text-anchor", anchor)
+ .attr("fill", that.defaults.color.defaultWordColor);
+
+ //获取旋转平移之前文字的bounding box
+ bBox = textEl.getBBox(true);
+
+ //canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height);
+ //获取旋转平移之后文字的bounding box
+ bBoxNew = textEl.getBBox();
+ //adjust vml box center
+ if (Raphael.vml) {
+ //vml's word bbox is not related to text-anchor, always middle;
+ //svg's word bbox is related to text-anchor;
+ bBoxNew.x = bBoxNew.x + bBox.width / 2 * Math.cos(angle);
+ bBoxNew.y = bBoxNew.y + bBox.width / 2 * Math.sin(angle);
+ }
+ //canvas.rect(bBoxNew.x, bBoxNew.y, bBoxNew.width, bBoxNew.height);
+
+ //新旧bounding box的中心坐标变化
+ tCenterX = bBoxNew.x + bBoxNew.width / 2 - bBox.x - bBox.width / 2;
+ tCenterY = bBoxNew.y + bBoxNew.height / 2 - bBox.y - bBox.height / 2;
+ //对bounding box进行平移和旋转
+ locateBBox = "T" + tCenterX + "," + tCenterY + "R" + (nodes[j].x - 90) + rotateStr;
+
+ // 包围盒
+ bBoxEl = canvas.rect(bBox.x, bBox.y, bBox.width, bBox.height)
+ .transform(locateBBox)
+ .data("relatedNode", textEl)
+ .attr({"fill": "#fff", "opacity": 0.01});
+
+ key = nodes[j].key;
+ nodeElements[key] = textEl;
+ bBoxElements[key] = bBoxEl;
+ nodeRelatedElements[key] = {targetLink: [], sourceLink: [], targetNode: [], sourceNode: []};
+
+ rNodes.push(textEl);
+ }
+ }
+
+ //绘制曲线
+ for (i = 0; i < linksCount; i++) {
+ var l = paths[i];
+
+ //对paths数组中的每一项进行计算,由路径节点信息得到坐标值
+ var spline = line(l);
+ var sourceKey = links[i].source.key;
+ var targetKey = links[i].target.key;
+ var tips = "link source: " + sourceKey + "\n"
+ + "link target: " + targetKey;
+
+ linkEl = canvas.path(spline)
+ //.attr("stroke", that.defaults.defaultLineColor)
+ .attr("stroke-opacity", 0.6)
+ .attr("title", tips)
+ .attr("d", spline)
+ .attr("stroke", that.defaults.color.defaultLineColor)
+ .translate(that.defaults.radius, that.defaults.radius)
+ .mouseover(mouseoverLink);
+ //.mouseout(mouseoutLink);
+ linkEl[0].el = linkEl;
+
+ nodeRelatedElements[sourceKey].targetLink.push(linkEl);
+ nodeRelatedElements[sourceKey].targetNode.push(nodeElements[targetKey]);
+ nodeRelatedElements[targetKey].sourceLink.push(linkEl);
+ nodeRelatedElements[targetKey].sourceNode.push(nodeElements[sourceKey]);
+ rLinks.push(linkEl);
+ }
+
+ $(this.canvas.canvas).mousemove(function (e) {
+ if(!e.target.el && rLinks.preLink){
+ rLinks.preLink.attr("stroke", that.defaults.color.defaultLineColor)
+ .attr("stroke-width", 1)
+ .attr("stroke-opacity", 0.6);
+ rLinks.preLink = undefined;
+ //console.log("a");
+ }
+ });
+
+ //bind text words hover event
+ for (key in bBoxElements) {
+ if (bBoxElements.hasOwnProperty(key)) {
+ bBoxElements[key].data("relatedElements", nodeRelatedElements[key])
+ .mouseover(mouseoverNode)
+ .mouseout(mouseoutNode);
+ }
+ }
+
+ };
+
+ return Bundle;
+});
diff --git a/lib/charts/chinamap.js b/lib/charts/chinamap.js
index ee9452a..fc03eb6 100644
--- a/lib/charts/chinamap.js
+++ b/lib/charts/chinamap.js
@@ -1,1164 +1,1164 @@
-/*global d3, $, define */
-/*!
- * Chinamap兼容定义部分
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Chinamap', function (require) {
- var DataV = require('DataV');
-
- /*
- * Chinamap构造函数,继承自Chart
- * Options:
- *
- * - `width` 数字,图片宽度,默认为750,表示图片高750px
- * - `height` 数字,图片高度,默认为500
- * - `showBackTag` 布尔值,回退操作导航条是否显示,默认为 true, 显示;设为false则不显示
- * - `backHeight` 数字,回退操作导航条宽度,默认为20
- * - `level1BorderWidth` 数字,一级方框的边框宽度,默认为1(1px),不建议修改
- * - `level2BorderWidth` 数字,二级方框的边框宽度,默认为1(1px),不建议修改
- * - `fontSizeRatio` 数字,表示图中文字大小。默认为1.0(1倍), 若设为2.0,字体大小会加倍;
- * - `customEvent` 函数对象,其中有4个自定义函数。`leafNodeClick` 函数,表示点击叶子节点的事件响应,默认为空函数; `hoverIn` 函数,表示鼠标移进方框的事件响应,默认为空函数; `hoverOut` 函数,表示鼠标移出方框的事件响应,默认为空函数; `mouseover` 函数,表示在方框内移动鼠标的事件响应,默认为设置浮框的内容,可以替换它修改浮框内容; 这些函数可以在创建对象或setOption()时一起设置,也可以通过on函数单独设置。
- *
- * Examples:
- * create chinamap in a dom node with id "chart", width is 500; height is 600px;
- * ```
- * var chinamap = new Chinamap("chart", {"width": 500, "height": 600});
- * ```
- * @param {Object} node The dom node or dom node Id
- * @param {Object} options JSON object for determin chinamap style
- */
- var Chinamap = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- var noop = function () {};
- this.type = "Chinamap";
- this.node = this.checkContainer(node);
-
- // Properties
- /*
- this.floatTag;//浮框对象,这是个可操作的对象。
- */
- this.viewBox = [];
- this.mapCache = {};
- this.states = [];
- this.words = [];
- this.projection = function () {}; // d3 map prejection function
- this.getAreaPath = function () {}; // input geojson feature, return area path string
- this.colorGenerator = function () {}; // produce gradient or discrete color;
- this.sourceData = {};
- this.geoData = {};
- this.geoData.areaIdIndex = {
- '全国': 0,
- '新疆': 65,
- '西藏': 54,
- '内蒙古': 15,
- '青海': 63,
- '四川': 51,
- '黑龙江': 23,
- '甘肃': 62,
- '云南': 53,
- '广西': 45,
- '湖南': 43,
- '陕西': 61,
- '广东': 44,
- '吉林': 22,
- '河北': 13,
- '湖北': 42,
- '贵州': 52,
- '山东': 37,
- '江西': 36,
- '河南': 41,
- '辽宁': 21,
- '山西': 14,
- '安徽': 34,
- '福建': 35,
- '浙江': 33,
- '江苏': 32,
- '重庆': 50,
- '宁夏': 64,
- '海南': 46,
- '台湾': 71,
- '北京': 11,
- '天津': 12,
- '上海': 31,
- '香港': 81,
- '澳门': 82
- };
- this.geoData.areaBoxes = {
- //x, y, width, height when projection scale is 4000
- 0: [-1174.6445229087194, -1437.3577680805693, 3039.3970214233723, 2531.19589698184],
- 65: [-1174.9404317915883, -1136.0130934711678, 1216.4169237052663, 939.4360818385251],
- 54: [-1061.2905098655508, -273.40253896102865, 1182.4138890465167, 728.4762434212385],
- 15: [81.92106433333947, -1404.5655158641246, 1337.913665139638, 1168.7030286278964],
- 63: [-398.0407413665446, -404.86540158240564, 770.5429460357634, 553.4881569694239],
- 51: [34.77351011413543, -24.727858097581816, 654.265749584143, 581.5837904142871],
- 23: [1185.0861642873883, -1435.9087566254907, 680.9449423479143, 618.3772597960831],
- 62: [-197.5222870378875, -631.2015222269291, 884.6861134736321, 734.2542202456989],
- 53: [-4.030270169151834, 326.89754492870105, 561.4971786143803, 565.9079094851168],
- 45: [444.4355364538484, 524.7911424174906, 490.6548359068431, 384.1667316158848],
- 43: [716.7125751678784, 265.3988842488122, 346.1702652872375, 377.50144051998274],
- 61: [508.5948583446903, -399.56997062473215, 321.038690321553, 559.1002147021181],
- 44: [790.2032875493967, 572.9640361040085, 494.8279567104971, 388.7112686526252],
- 22: [1287.5729431804648, -950.943295028444, 504.33243011403374, 354.162667814153],
- 13: [940.0156020671719, -646.4007207319194, 325.33903805510784, 477.4542727272415],
- 42: [683.8325394595918, 45.82949601748078, 468.66717545627034, 295.2142095820616],
- 52: [392.5021834497175, 337.4483828727408, 375.50579966539516, 320.9420464446699],
- 37: [1035.7855473594757, -382.19242168799906, 412.5747391303373, 313.152767793266],
- 36: [1012.6841751377355, 236.50140310944056, 295.599802392515, 400.86430917822287],
- 41: [785.5419798731749, -185.2911232263814, 362.6977821251186, 340.3902676066224],
- 21: [1203.0641741691293, -757.0946871553339, 352.71788824534656, 357.71276541155214],
- 14: [776.5185040689469, -493.6204506126494, 212.68572802329425, 448.08485211774945],
- 34: [1054.014965660052, -80.43770626104327, 295.73127466484925, 352.03731065611606],
- 35: [1172.0955040211252, 341.81292779438445, 288.99462739279807, 339.42845011348845],
- 33: [1272.1789620983063, 123.46272678646208, 286.17816622252326, 286.73860446060394],
- 32: [1125.161343490302, -134.97368204682834, 356.1806346879009, 291.4961628010442],
- 50: [497.78832088614774, 127.0051229616378, 291.91221530072164, 280.8880182020781],
- 64: [441.193675072408, -376.31946967355213, 183.76989823787306, 293.0024551112753],
- 46: [723.8031601361929, 946.050886515855, 183.33374783084207, 147.66048518654895],
- 71: [1459.925544038912, 519.7445429876257, 103.06085087505835, 237.80851484008463],
- 11: [1031.6052083127613, -530.1928574952913, 103.23943439987329, 114.66079087790081],
- 12: [1106.9649995752443, -479.16508616378724, 71.21176554916747, 120.01987096046025],
- 31: [1420.334836525578, 71.79837578328207, 70.41721601016525, 81.99461244072737],
- 81: [1061.983645387268, 769.0837862603122, 50.65584483626753, 32.17422147262721],
- 82: [1043.1350056914507, 798.0786255550063, 5.387452843479423, 7.564113979470676]
- };
- this.geoData.provinceIndex = {
- '新疆': {'loc': [84.9023,41.748], 'fullName': '新疆'},
- '西藏': {'loc': [88.7695,31.6846], 'fullName': '西藏'},
- '内蒙': {'loc': [117.5977,44.3408], 'fullName': '内蒙古'},
- '青海': {'loc': [96.2402,35.4199], 'fullName': '青海'},
- '四川': {'loc': [102.9199,30.1904], 'fullName': '四川'},
- '黑龙': {'loc': [128.1445,48.5156], 'fullName': '黑龙江'},
- '甘肃': {'loc': [95.7129,40.166], 'fullName': '甘肃'},
- '云南': {'loc': [101.8652,25.1807], 'fullName': '云南'},
- '广西': {'loc': [108.2813,23.6426], 'fullName': '广西'},
- '湖南': {'loc': [111.5332,27.3779], 'fullName': '湖南'},
- '陕西': {'loc': [109.5996,35.6396], 'fullName': '陕西'},
- '广东': {'loc': [113.4668,22.8076], 'fullName': '广东'},
- '吉林': {'loc': [126.4746,43.5938], 'fullName': '吉林'},
- '河北': {'loc': [115.4004,37.9688], 'fullName': '河北'},
- '湖北': {'loc': [112.2363,31.1572], 'fullName': '湖北'},
- '贵州': {'loc': [106.6113,26.9385], 'fullName': '贵州'},
- '山东': {'loc': [118.7402,36.4307], 'fullName': '山东'},
- '江西': {'loc': [116.0156,27.29], 'fullName': '江西'},
- '河南': {'loc': [113.4668,33.8818], 'fullName': '河南'},
- '辽宁': {'loc': [122.3438,41.0889], 'fullName': '辽宁'},
- '山西': {'loc': [112.4121,37.6611], 'fullName': '山西'},
- '安徽': {'loc': [117.2461,32.0361], 'fullName': '安徽'},
- '福建': {'loc': [118.3008,25.9277], 'fullName': '福建'},
- '浙江': {'loc': [120.498,29.0918], 'fullName': '浙江'},
- '江苏': {'loc': [120.0586,32.915], 'fullName': '江苏'},
- '重庆': {'loc': [107.7539,30.1904], 'fullName': '重庆'},
- '宁夏': {'loc': [105.9961,37.3096], 'fullName': '宁夏'},
- '海南': {'loc': [109.9512,19.2041], 'fullName': '海南'},
- '台湾': {'loc': [121.0254,23.5986], 'fullName': '台湾'},
- '北京': {'loc': [116.4551,40.2539], 'fullName': '北京'},
- '天津': {'loc': [117.4219,39.4189], 'fullName': '天津'},
- '上海': {'loc': [121.4648,31.2891], 'fullName': '上海'},
- '香港': {'loc': [114.2578,22.3242], 'fullName': '香港'},
- '澳门': {'loc': [113.5547,22.1484], 'fullName': '澳门'}
- };
- this.geoData.cityIndex = {
- '重庆': {'loc': [107.7539,30.1904], 'fullName': '重庆'},
- '北京': {'loc': [116.4551,40.2539], 'fullName': '北京'},
- '天津': {'loc': [117.4219,39.4189], 'fullName': '天津'},
- '上海': {'loc': [121.4648,31.2891], 'fullName': '上海'},
- '香港': {'loc': [114.2578,22.3242], 'fullName': '香港'},
- '澳门': {'loc': [113.5547,22.1484], 'fullName': '澳门'},
- '巴音': {'loc': [88.1653,39.6002], 'fullName': '巴音郭楞蒙古自治州'},
- '和田': {'loc': [81.167,36.9855], 'fullName': '和田地区'},
- '哈密': {'loc': [93.7793,42.9236], 'fullName': '哈密地区'},
- '阿克': {'loc': [82.9797,41.0229], 'fullName': '阿克苏地区'},
- '阿勒': {'loc': [88.2971,47.0929], 'fullName': '阿勒泰地区'},
- '喀什': {'loc': [77.168,37.8534], 'fullName': '喀什地区'},
- '塔城': {'loc': [86.6272,45.8514], 'fullName': '塔城地区'},
- '昌吉': {'loc': [89.6814,44.4507], 'fullName': '昌吉回族自治州'},
- '克孜': {'loc': [74.6301,39.5233], 'fullName': '克孜勒苏柯尔克孜自治州'},
- '吐鲁': {'loc': [89.6375,42.4127], 'fullName': '吐鲁番地区'},
- '伊犁': {'loc': [82.5513,43.5498], 'fullName': '伊犁哈萨克自治州'},
- '博尔': {'loc': [81.8481,44.6979], 'fullName': '博尔塔拉蒙古自治州'},
- '乌鲁': {'loc': [87.9236,43.5883], 'fullName': '乌鲁木齐市'},
- '克拉': {'loc': [85.2869,45.5054], 'fullName': '克拉玛依市'},
- '阿拉尔': {'loc': [81.2769,40.6549], 'fullName': '阿拉尔市'},
- '图木': {'loc': [79.1345,39.8749], 'fullName': '图木舒克市'},
- '五家': {'loc': [87.5391,44.3024], 'fullName': '五家渠市'},
- '石河': {'loc': [86.0229,44.2914], 'fullName': '石河子市'},
- '那曲': {'loc': [88.1982,33.3215], 'fullName': '那曲地区'},
- '阿里': {'loc': [82.3645,32.7667], 'fullName': '阿里地区'},
- '日喀': {'loc': [86.2427,29.5093], 'fullName': '日喀则地区'},
- '林芝': {'loc': [95.4602,29.1138], 'fullName': '林芝地区'},
- '昌都': {'loc': [97.0203,30.7068], 'fullName': '昌都地区'},
- '山南': {'loc': [92.2083,28.3392], 'fullName': '山南地区'},
- '拉萨': {'loc': [91.1865,30.1465], 'fullName': '拉萨市'},
- '呼伦': {'loc': [120.8057,50.2185], 'fullName': '呼伦贝尔市'},
- '阿拉善': {'loc': [102.019,40.1001], 'fullName': '阿拉善盟'},
- '锡林': {'loc': [115.6421,44.176], 'fullName': '锡林郭勒盟'},
- '鄂尔': {'loc': [108.9734,39.2487], 'fullName': '鄂尔多斯市'},
- '赤峰': {'loc': [118.6743,43.2642], 'fullName': '赤峰市'},
- '巴彦': {'loc': [107.5562,41.3196], 'fullName': '巴彦淖尔市'},
- '通辽': {'loc': [121.4758,43.9673], 'fullName': '通辽市'},
- '乌兰': {'loc': [112.5769,41.77], 'fullName': '乌兰察布市'},
- '兴安': {'loc': [121.3879,46.1426], 'fullName': '兴安盟'},
- '包头': {'loc': [110.3467,41.4899], 'fullName': '包头市'},
- '呼和': {'loc': [111.4124,40.4901], 'fullName': '呼和浩特市'},
- '乌海': {'loc': [106.886,39.4739], 'fullName': '乌海市'},
- '海西': {'loc': [94.9768,37.1118], 'fullName': '海西蒙古族藏族自治州'},
- '玉树': {'loc': [93.5925,33.9368], 'fullName': '玉树藏族自治州'},
- '果洛': {'loc': [99.3823,34.0466], 'fullName': '果洛藏族自治州'},
- '海南': {'loc': [100.3711,35.9418], 'fullName': '海南藏族自治州'},
- '海北': {'loc': [100.3711,37.9138], 'fullName': '海北藏族自治州'},
- '黄南': {'loc': [101.5686,35.1178], 'fullName': '黄南藏族自治州'},
- '海东': {'loc': [102.3706,36.2988], 'fullName': '海东地区'},
- '西宁': {'loc': [101.4038,36.8207], 'fullName': '西宁市'},
- '甘孜': {'loc': [99.9207,31.0803], 'fullName': '甘孜藏族自治州'},
- '阿坝': {'loc': [102.4805,32.4536], 'fullName': '阿坝藏族羌族自治州'},
- '凉山': {'loc': [101.9641,27.6746], 'fullName': '凉山彝族自治州'},
- '绵阳': {'loc': [104.7327,31.8713], 'fullName': '绵阳市'},
- '达州': {'loc': [107.6111,31.333], 'fullName': '达州市'},
- '广元': {'loc': [105.6885,32.2284], 'fullName': '广元市'},
- '雅安': {'loc': [102.6672,29.8938], 'fullName': '雅安市'},
- '宜宾': {'loc': [104.6558,28.548], 'fullName': '宜宾市'},
- '乐山': {'loc': [103.5791,29.1742], 'fullName': '乐山市'},
- '南充': {'loc': [106.2048,31.1517], 'fullName': '南充市'},
- '巴中': {'loc': [107.0618,31.9977], 'fullName': '巴中市'},
- '泸州': {'loc': [105.4578,28.493], 'fullName': '泸州市'},
- '成都': {'loc': [103.9526,30.7617], 'fullName': '成都市'},
- '资阳': {'loc': [104.9744,30.1575], 'fullName': '资阳市'},
- '攀枝': {'loc': [101.6895,26.7133], 'fullName': '攀枝花市'},
- '眉山': {'loc': [103.8098,30.0146], 'fullName': '眉山市'},
- '广安': {'loc': [106.6333,30.4376], 'fullName': '广安市'},
- '德阳': {'loc': [104.48,31.1133], 'fullName': '德阳市'},
- '内江': {'loc': [104.8535,29.6136], 'fullName': '内江市'},
- '遂宁': {'loc': [105.5347,30.6683], 'fullName': '遂宁市'},
- '自贡': {'loc': [104.6667,29.2786], 'fullName': '自贡市'},
- '黑河': {'loc': [127.1448,49.2957], 'fullName': '黑河市'},
- '大兴': {'loc': [124.1016,52.2345], 'fullName': '大兴安岭地区'},
- '哈尔': {'loc': [127.9688,45.368], 'fullName': '哈尔滨市'},
- '齐齐': {'loc': [124.541,47.5818], 'fullName': '齐齐哈尔市'},
- '牡丹': {'loc': [129.7815,44.7089], 'fullName': '牡丹江市'},
- '绥化': {'loc': [126.7163,46.8018], 'fullName': '绥化市'},
- '伊春': {'loc': [129.1992,47.9608], 'fullName': '伊春市'},
- '佳木': {'loc': [133.0005,47.5763], 'fullName': '佳木斯市'},
- '鸡西': {'loc': [132.7917,45.7361], 'fullName': '鸡西市'},
- '双鸭': {'loc': [133.5938,46.7523], 'fullName': '双鸭山市'},
- '大庆': {'loc': [124.7717,46.4282], 'fullName': '大庆市'},
- '鹤岗': {'loc': [130.4407,47.7081], 'fullName': '鹤岗市'},
- '七台': {'loc': [131.2756,45.9558], 'fullName': '七台河市'},
- '酒泉': {'loc': [96.2622,40.4517], 'fullName': '酒泉市'},
- '张掖': {'loc': [99.7998,38.7433], 'fullName': '张掖市'},
- '甘南': {'loc': [102.9199,34.6893], 'fullName': '甘南藏族自治州'},
- '武威': {'loc': [103.0188,38.1061], 'fullName': '武威市'},
- '陇南': {'loc': [105.304,33.5632], 'fullName': '陇南市'},
- '庆阳': {'loc': [107.5342,36.2], 'fullName': '庆阳市'},
- '白银': {'loc': [104.8645,36.5076], 'fullName': '白银市'},
- '定西': {'loc': [104.5569,35.0848], 'fullName': '定西市'},
- '天水': {'loc': [105.6445,34.6289], 'fullName': '天水市'},
- '兰州': {'loc': [103.5901,36.3043], 'fullName': '兰州市'},
- '平凉': {'loc': [107.0728,35.321], 'fullName': '平凉市'},
- '临夏': {'loc': [103.2715,35.5737], 'fullName': '临夏回族自治州'},
- '金昌': {'loc': [102.074,38.5126], 'fullName': '金昌市'},
- '嘉峪': {'loc': [98.1738,39.8035], 'fullName': '嘉峪关市'},
- '普洱': {'loc': [100.7446,23.4229], 'fullName': '普洱市'},
- '红河': {'loc': [103.0408,23.6041], 'fullName': '红河哈尼族彝族自治州'},
- '文山': {'loc': [104.8865,23.5712], 'fullName': '文山壮族苗族自治州'},
- '曲靖': {'loc': [103.9417,25.7025], 'fullName': '曲靖市'},
- '楚雄': {'loc': [101.6016,25.3619], 'fullName': '楚雄彝族自治州'},
- '大理': {'loc': [99.9536,25.6805], 'fullName': '大理白族自治州'},
- '临沧': {'loc': [99.613,24.0546], 'fullName': '临沧市'},
- '迪庆': {'loc': [99.4592,27.9327], 'fullName': '迪庆藏族自治州'},
- '昭通': {'loc': [104.0955,27.6031], 'fullName': '昭通市'},
- '昆明': {'loc': [102.9199,25.4663], 'fullName': '昆明市'},
- '丽江': {'loc': [100.448,26.955], 'fullName': '丽江市'},
- '西双': {'loc': [100.8984,21.8628], 'fullName': '西双版纳傣族自治州'},
- '保山': {'loc': [99.0637,24.9884], 'fullName': '保山市'},
- '玉溪': {'loc': [101.9312,23.8898], 'fullName': '玉溪市'},
- '怒江': {'loc': [99.1516,26.5594], 'fullName': '怒江傈僳族自治州'},
- '德宏': {'loc': [98.1299,24.5874], 'fullName': '德宏傣族景颇族自治州'},
- '百色': {'loc': [106.6003,23.9227], 'fullName': '百色市'},
- '河池': {'loc': [107.8638,24.5819], 'fullName': '河池市'},
- '桂林': {'loc': [110.5554,25.318], 'fullName': '桂林市'},
- '南宁': {'loc': [108.479,23.1152], 'fullName': '南宁市'},
- '柳州': {'loc': [109.3799,24.9774], 'fullName': '柳州市'},
- '崇左': {'loc': [107.3364,22.4725], 'fullName': '崇左市'},
- '来宾': {'loc': [109.7095,23.8403], 'fullName': '来宾市'},
- '玉林': {'loc': [110.2148,22.3792], 'fullName': '玉林市'},
- '梧州': {'loc': [110.9949,23.5052], 'fullName': '梧州市'},
- '贺州': {'loc': [111.3135,24.4006], 'fullName': '贺州市'},
- '钦州': {'loc': [109.0283,22.0935], 'fullName': '钦州市'},
- '贵港': {'loc': [109.9402,23.3459], 'fullName': '贵港市'},
- '防城': {'loc': [108.0505,21.9287], 'fullName': '防城港市'},
- '北海': {'loc': [109.314,21.6211], 'fullName': '北海市'},
- '怀化': {'loc': [109.9512,27.4438], 'fullName': '怀化市'},
- '永州': {'loc': [111.709,25.752], 'fullName': '永州市'},
- '邵阳': {'loc': [110.9619,26.8121], 'fullName': '邵阳市'},
- '郴州': {'loc': [113.2361,25.8673], 'fullName': '郴州市'},
- '常德': {'loc': [111.4014,29.2676], 'fullName': '常德市'},
- '湘西': {'loc': [109.7864,28.6743], 'fullName': '湘西土家族苗族自治州'},
- '衡阳': {'loc': [112.4121,26.7902], 'fullName': '衡阳市'},
- '岳阳': {'loc': [113.2361,29.1357], 'fullName': '岳阳市'},
- '益阳': {'loc': [111.731,28.3832], 'fullName': '益阳市'},
- '长沙': {'loc': [113.0823,28.2568], 'fullName': '长沙市'},
- '株洲': {'loc': [113.5327,27.0319], 'fullName': '株洲市'},
- '张家界': {'loc': [110.5115,29.328], 'fullName': '张家界市'},
- '娄底': {'loc': [111.6431,27.7185], 'fullName': '娄底市'},
- '湘潭': {'loc': [112.5439,27.7075], 'fullName': '湘潭市'},
- '榆林': {'loc': [109.8743,38.205], 'fullName': '榆林市'},
- '延安': {'loc': [109.1052,36.4252], 'fullName': '延安市'},
- '汉中': {'loc': [106.886,33.0139], 'fullName': '汉中市'},
- '安康': {'loc': [109.1162,32.7722], 'fullName': '安康市'},
- '商洛': {'loc': [109.8083,33.761], 'fullName': '商洛市'},
- '宝鸡': {'loc': [107.1826,34.3433], 'fullName': '宝鸡市'},
- '渭南': {'loc': [109.7864,35.0299], 'fullName': '渭南市'},
- '咸阳': {'loc': [108.4131,34.8706], 'fullName': '咸阳市'},
- '西安': {'loc': [109.1162,34.2004], 'fullName': '西安市'},
- '铜川': {'loc': [109.0393,35.1947], 'fullName': '铜川市'},
- '清远': {'loc': [112.9175,24.3292], 'fullName': '清远市'},
- '韶关': {'loc': [113.7964,24.7028], 'fullName': '韶关市'},
- '湛江': {'loc': [110.3577,20.9894], 'fullName': '湛江市'},
- '梅州': {'loc': [116.1255,24.1534], 'fullName': '梅州市'},
- '河源': {'loc': [114.917,23.9722], 'fullName': '河源市'},
- '肇庆': {'loc': [112.1265,23.5822], 'fullName': '肇庆市'},
- '惠州': {'loc': [114.6204,23.1647], 'fullName': '惠州市'},
- '茂名': {'loc': [111.0059,22.0221], 'fullName': '茂名市'},
- '江门': {'loc': [112.6318,22.1484], 'fullName': '江门市'},
- '阳江': {'loc': [111.8298,22.0715], 'fullName': '阳江市'},
- '云浮': {'loc': [111.7859,22.8516], 'fullName': '云浮市'},
- '广州': {'loc': [113.5107,23.2196], 'fullName': '广州市'},
- '汕尾': {'loc': [115.5762,23.0438], 'fullName': '汕尾市'},
- '揭阳': {'loc': [116.1255,23.313], 'fullName': '揭阳市'},
- '珠海': {'loc': [113.7305,22.1155], 'fullName': '珠海市'},
- '佛山': {'loc': [112.8955,23.1097], 'fullName': '佛山市'},
- '潮州': {'loc': [116.7847,23.8293], 'fullName': '潮州市'},
- '汕头': {'loc': [117.1692,23.3405], 'fullName': '汕头市'},
- '深圳': {'loc': [114.5435,22.5439], 'fullName': '深圳市'},
- '东莞': {'loc': [113.8953,22.901], 'fullName': '东莞市'},
- '中山': {'loc': [113.4229,22.478], 'fullName': '中山市'},
- '延边': {'loc': [129.397,43.2587], 'fullName': '延边朝鲜族自治州'},
- '吉林': {'loc': [126.8372,43.6047], 'fullName': '吉林市'},
- '白城': {'loc': [123.0029,45.2637], 'fullName': '白城市'},
- '松原': {'loc': [124.0906,44.7198], 'fullName': '松原市'},
- '长春': {'loc': [125.8154,44.2584], 'fullName': '长春市'},
- '白山': {'loc': [127.2217,42.0941], 'fullName': '白山市'},
- '通化': {'loc': [125.9583,41.8579], 'fullName': '通化市'},
- '四平': {'loc': [124.541,43.4894], 'fullName': '四平市'},
- '辽源': {'loc': [125.343,42.7643], 'fullName': '辽源市'},
- '承德': {'loc': [117.5757,41.4075], 'fullName': '承德市'},
- '张家口': {'loc': [115.1477,40.8527], 'fullName': '张家口市'},
- '保定': {'loc': [115.0488,39.0948], 'fullName': '保定市'},
- '唐山': {'loc': [118.4766,39.6826], 'fullName': '唐山市'},
- '沧州': {'loc': [116.8286,38.2104], 'fullName': '沧州市'},
- '石家': {'loc': [114.4995,38.1006], 'fullName': '石家庄市'},
- '邢台': {'loc': [114.8071,37.2821], 'fullName': '邢台市'},
- '邯郸': {'loc': [114.4775,36.535], 'fullName': '邯郸市'},
- '秦皇': {'loc': [119.2126,40.0232], 'fullName': '秦皇岛市'},
- '衡水': {'loc': [115.8838,37.7161], 'fullName': '衡水市'},
- '廊坊': {'loc': [116.521,39.0509], 'fullName': '廊坊市'},
- '恩施': {'loc': [109.5007,30.2563], 'fullName': '恩施土家族苗族自治州'},
- '十堰': {'loc': [110.5115,32.3877], 'fullName': '十堰市'},
- '宜昌': {'loc': [111.1707,30.7617], 'fullName': '宜昌市'},
- '襄樊': {'loc': [111.9397,31.9263], 'fullName': '襄樊市'},
- '黄冈': {'loc': [115.2686,30.6628], 'fullName': '黄冈市'},
- '荆州': {'loc': [113.291,30.0092], 'fullName': '荆州市'},
- '荆门': {'loc': [112.6758,30.9979], 'fullName': '荆门市'},
- '咸宁': {'loc': [114.2578,29.6631], 'fullName': '咸宁市'},
- '随州': {'loc': [113.4338,31.8768], 'fullName': '随州市'},
- '孝感': {'loc': [113.9502,31.1188], 'fullName': '孝感市'},
- '武汉': {'loc': [114.3896,30.6628], 'fullName': '武汉市'},
- '黄石': {'loc': [115.0159,29.9213], 'fullName': '黄石市'},
- '神农': {'loc': [110.4565,31.5802], 'fullName': '神农架林区'},
- '天门': {'loc': [113.0273,30.6409], 'fullName': '天门市'},
- '仙桃': {'loc': [113.3789,30.3003], 'fullName': '仙桃市'},
- '潜江': {'loc': [112.7637,30.3607], 'fullName': '潜江市'},
- '鄂州': {'loc': [114.7302,30.4102], 'fullName': '鄂州市'},
- '遵义': {'loc': [106.908,28.1744], 'fullName': '遵义市'},
- '黔东': {'loc': [108.4241,26.4166], 'fullName': '黔东南苗族侗族自治州'},
- '毕节': {'loc': [105.1611,27.0648], 'fullName': '毕节地区'},
- '黔南': {'loc': [107.2485,25.8398], 'fullName': '黔南布依族苗族自治州'},
- '铜仁': {'loc': [108.6218,28.0096], 'fullName': '铜仁地区'},
- '黔西': {'loc': [105.5347,25.3949], 'fullName': '黔西南布依族苗族自治州'},
- '六盘': {'loc': [104.7546,26.0925], 'fullName': '六盘水市'},
- '安顺': {'loc': [105.9082,25.9882], 'fullName': '安顺市'},
- '贵阳': {'loc': [106.6992,26.7682], 'fullName': '贵阳市'},
- '烟台': {'loc': [120.7397,37.5128], 'fullName': '烟台市'},
- '临沂': {'loc': [118.3118,35.2936], 'fullName': '临沂市'},
- '潍坊': {'loc': [119.0918,36.524], 'fullName': '潍坊市'},
- '青岛': {'loc': [120.4651,36.3373], 'fullName': '青岛市'},
- '菏泽': {'loc': [115.6201,35.2057], 'fullName': '菏泽市'},
- '济宁': {'loc': [116.8286,35.3375], 'fullName': '济宁市'},
- '德州': {'loc': [116.6858,37.2107], 'fullName': '德州市'},
- '滨州': {'loc': [117.8174,37.4963], 'fullName': '滨州市'},
- '聊城': {'loc': [115.9167,36.4032], 'fullName': '聊城市'},
- '东营': {'loc': [118.7073,37.5513], 'fullName': '东营市'},
- '济南': {'loc': [117.1582,36.8701], 'fullName': '济南市'},
- '泰安': {'loc': [117.0264,36.0516], 'fullName': '泰安市'},
- '威海': {'loc': [121.9482,37.1393], 'fullName': '威海市'},
- '日照': {'loc': [119.2786,35.5023], 'fullName': '日照市'},
- '淄博': {'loc': [118.0371,36.6064], 'fullName': '淄博市'},
- '枣庄': {'loc': [117.323,34.8926], 'fullName': '枣庄市'},
- '莱芜': {'loc': [117.6526,36.2714], 'fullName': '莱芜市'},
- '赣州': {'loc': [115.2795,25.8124], 'fullName': '赣州市'},
- '吉安': {'loc': [114.884,26.9659], 'fullName': '吉安市'},
- '上饶': {'loc': [117.8613,28.7292], 'fullName': '上饶市'},
- '九江': {'loc': [115.4224,29.3774], 'fullName': '九江市'},
- '抚州': {'loc': [116.4441,27.4933], 'fullName': '抚州市'},
- '宜春': {'loc': [115.0159,28.3228], 'fullName': '宜春市'},
- '南昌': {'loc': [116.0046,28.6633], 'fullName': '南昌市'},
- '景德': {'loc': [117.334,29.3225], 'fullName': '景德镇市'},
- '萍乡': {'loc': [113.9282,27.4823], 'fullName': '萍乡市'},
- '鹰潭': {'loc': [117.0813,28.2349], 'fullName': '鹰潭市'},
- '新余': {'loc': [114.95,27.8174], 'fullName': '新余市'},
- '南阳': {'loc': [112.4011,33.0359], 'fullName': '南阳市'},
- '信阳': {'loc': [114.8291,32.0197], 'fullName': '信阳市'},
- '洛阳': {'loc': [112.0605,34.3158], 'fullName': '洛阳市'},
- '驻马': {'loc': [114.1589,32.9041], 'fullName': '驻马店市'},
- '周口': {'loc': [114.873,33.6951], 'fullName': '周口市'},
- '商丘': {'loc': [115.741,34.2828], 'fullName': '商丘市'},
- '三门': {'loc': [110.8301,34.3158], 'fullName': '三门峡市'},
- '新乡': {'loc': [114.2029,35.3595], 'fullName': '新乡市'},
- '平顶': {'loc': [112.9724,33.739], 'fullName': '平顶山市'},
- '郑州': {'loc': [113.4668,34.6234], 'fullName': '郑州市'},
- '安阳': {'loc': [114.5325,36.0022], 'fullName': '安阳市'},
- '开封': {'loc': [114.5764,34.6124], 'fullName': '开封市'},
- '焦作': {'loc': [112.8406,35.1508], 'fullName': '焦作市'},
- '许昌': {'loc': [113.6975,34.0466], 'fullName': '许昌市'},
- '濮阳': {'loc': [115.1917,35.799], 'fullName': '濮阳市'},
- '漯河': {'loc': [113.8733,33.6951], 'fullName': '漯河市'},
- '鹤壁': {'loc': [114.3787,35.744], 'fullName': '鹤壁市'},
- '大连': {'loc': [122.2229,39.4409], 'fullName': '大连市'},
- '朝阳': {'loc': [120.0696,41.4899], 'fullName': '朝阳市'},
- '丹东': {'loc': [124.541,40.4242], 'fullName': '丹东市'},
- '铁岭': {'loc': [124.2773,42.7423], 'fullName': '铁岭市'},
- '沈阳': {'loc': [123.1238,42.1216], 'fullName': '沈阳市'},
- '抚顺': {'loc': [124.585,41.8579], 'fullName': '抚顺市'},
- '葫芦': {'loc': [120.1575,40.578], 'fullName': '葫芦岛市'},
- '阜新': {'loc': [122.0032,42.2699], 'fullName': '阜新市'},
- '锦州': {'loc': [121.6626,41.4294], 'fullName': '锦州市'},
- '鞍山': {'loc': [123.0798,40.6055], 'fullName': '鞍山市'},
- '本溪': {'loc': [124.1455,41.1987], 'fullName': '本溪市'},
- '营口': {'loc': [122.4316,40.4297], 'fullName': '营口市'},
- '辽阳': {'loc': [123.4094,41.1383], 'fullName': '辽阳市'},
- '盘锦': {'loc': [121.9482,41.0449], 'fullName': '盘锦市'},
- '忻州': {'loc': [112.4561,38.8971], 'fullName': '忻州市'},
- '吕梁': {'loc': [111.3574,37.7325], 'fullName': '吕梁市'},
- '临汾': {'loc': [111.4783,36.1615], 'fullName': '临汾市'},
- '晋中': {'loc': [112.7747,37.37], 'fullName': '晋中市'},
- '运城': {'loc': [111.1487,35.2002], 'fullName': '运城市'},
- '大同': {'loc': [113.7854,39.8035], 'fullName': '大同市'},
- '长治': {'loc': [112.8625,36.4746], 'fullName': '长治市'},
- '朔州': {'loc': [113.0713,39.6991], 'fullName': '朔州市'},
- '晋城': {'loc': [112.7856,35.6342], 'fullName': '晋城市'},
- '太原': {'loc': [112.3352,37.9413], 'fullName': '太原市'},
- '阳泉': {'loc': [113.4778,38.0951], 'fullName': '阳泉市'},
- '六安': {'loc': [116.3123,31.8329], 'fullName': '六安市'},
- '安庆': {'loc': [116.7517,30.5255], 'fullName': '安庆市'},
- '滁州': {'loc': [118.1909,32.536], 'fullName': '滁州市'},
- '宣城': {'loc': [118.8062,30.6244], 'fullName': '宣城市'},
- '阜阳': {'loc': [115.7629,32.9919], 'fullName': '阜阳市'},
- '宿州': {'loc': [117.5208,33.6841], 'fullName': '宿州市'},
- '黄山': {'loc': [118.0481,29.9542], 'fullName': '黄山市'},
- '巢湖': {'loc': [117.7734,31.4978], 'fullName': '巢湖市'},
- '亳州': {'loc': [116.1914,33.4698], 'fullName': '亳州市'},
- '池州': {'loc': [117.3889,30.2014], 'fullName': '池州市'},
- '合肥': {'loc': [117.29,32.0581], 'fullName': '合肥市'},
- '蚌埠': {'loc': [117.4109,33.1073], 'fullName': '蚌埠市'},
- '芜湖': {'loc': [118.3557,31.0858], 'fullName': '芜湖市'},
- '淮北': {'loc': [116.6968,33.6896], 'fullName': '淮北市'},
- '淮南': {'loc': [116.7847,32.7722], 'fullName': '淮南市'},
- '马鞍': {'loc': [118.6304,31.5363], 'fullName': '马鞍山市'},
- '铜陵': {'loc': [117.9382,30.9375], 'fullName': '铜陵市'},
- '南平': {'loc': [118.136,27.2845], 'fullName': '南平市'},
- '三明': {'loc': [117.5317,26.3013], 'fullName': '三明市'},
- '龙岩': {'loc': [116.8066,25.2026], 'fullName': '龙岩市'},
- '宁德': {'loc': [119.6521,26.9824], 'fullName': '宁德市'},
- '福州': {'loc': [119.4543,25.9222], 'fullName': '福州市'},
- '漳州': {'loc': [117.5757,24.3732], 'fullName': '漳州市'},
- '泉州': {'loc': [118.3228,25.1147], 'fullName': '泉州市'},
- '莆田': {'loc': [119.0918,25.3455], 'fullName': '莆田市'},
- '厦门': {'loc': [118.1689,24.6478], 'fullName': '厦门市'},
- '丽水': {'loc': [119.5642,28.1854], 'fullName': '丽水市'},
- '杭州': {'loc': [119.5313,29.8773], 'fullName': '杭州市'},
- '温州': {'loc': [120.498,27.8119], 'fullName': '温州市'},
- '宁波': {'loc': [121.5967,29.6466], 'fullName': '宁波市'},
- '舟山': {'loc': [122.2559,30.2234], 'fullName': '舟山市'},
- '台州': {'loc': [121.1353,28.6688], 'fullName': '台州市'},
- '金华': {'loc': [120.0037,29.1028], 'fullName': '金华市'},
- '衢州': {'loc': [118.6853,28.8666], 'fullName': '衢州市'},
- '绍兴': {'loc': [120.564,29.7565], 'fullName': '绍兴市'},
- '嘉兴': {'loc': [120.9155,30.6354], 'fullName': '嘉兴市'},
- '湖州': {'loc': [119.8608,30.7782], 'fullName': '湖州市'},
- '盐城': {'loc': [120.2234,33.5577], 'fullName': '盐城市'},
- '徐州': {'loc': [117.5208,34.3268], 'fullName': '徐州市'},
- '南通': {'loc': [121.1023,32.1625], 'fullName': '南通市'},
- '淮安': {'loc': [118.927,33.4039], 'fullName': '淮安市'},
- '苏州': {'loc': [120.6519,31.3989], 'fullName': '苏州市'},
- '宿迁': {'loc': [118.5535,33.7775], 'fullName': '宿迁市'},
- '连云': {'loc': [119.1248,34.552], 'fullName': '连云港市'},
- '扬州': {'loc': [119.4653,32.8162], 'fullName': '扬州市'},
- '南京': {'loc': [118.8062,31.9208], 'fullName': '南京市'},
- '泰州': {'loc': [120.0586,32.5525], 'fullName': '泰州市'},
- '无锡': {'loc': [120.3442,31.5527], 'fullName': '无锡市'},
- '常州': {'loc': [119.4543,31.5582], 'fullName': '常州市'},
- '镇江': {'loc': [119.4763,31.9702], 'fullName': '镇江市'},
- '吴忠': {'loc': [106.853,37.3755], 'fullName': '吴忠市'},
- '中卫': {'loc': [105.4028,36.9525], 'fullName': '中卫市'},
- '固原': {'loc': [106.1389,35.9363], 'fullName': '固原市'},
- '银川': {'loc': [106.3586,38.1775], 'fullName': '银川市'},
- '石嘴': {'loc': [106.4795,39.0015], 'fullName': '石嘴山市'},
- '儋州': {'loc': [109.3291,19.5653], 'fullName': '儋州市'},
- '文昌': {'loc': [110.8905,19.7823], 'fullName': '文昌市'},
- '乐东': {'loc': [109.0283,18.6301], 'fullName': '乐东黎族自治县'},
- '三亚': {'loc': [109.3716,18.3698], 'fullName': '三亚市'},
- '琼中': {'loc': [109.8413,19.0736], 'fullName': '琼中黎族苗族自治县'},
- '东方': {'loc': [108.8498,19.0414], 'fullName': '东方市'},
- '海口': {'loc': [110.3893,19.8516], 'fullName': '海口市'},
- '万宁': {'loc': [110.3137,18.8388], 'fullName': '万宁市'},
- '澄迈': {'loc': [109.9937,19.7314], 'fullName': '澄迈县'},
- '白沙': {'loc': [109.3703,19.211], 'fullName': '白沙黎族自治县'},
- '琼海': {'loc': [110.4208,19.224], 'fullName': '琼海市'},
- '昌江': {'loc': [109.0407,19.2137], 'fullName': '昌江黎族自治县'},
- '临高': {'loc': [109.6957,19.8063], 'fullName': '临高县'},
- '陵水': {'loc': [109.9924,18.5415], 'fullName': '陵水黎族自治县'},
- '屯昌': {'loc': [110.0377,19.362], 'fullName': '屯昌县'},
- '定安': {'loc': [110.3384,19.4698], 'fullName': '定安县'},
- '保亭': {'loc': [109.6284,18.6108], 'fullName': '保亭黎族苗族自治县'},
- '五指': {'loc': [109.5282,18.8299], 'fullName': '五指山市'}
- };
-
- // Canvas
- this.defaults = {};
- this.defaults.geoDataPath = "";
- this.defaults.width = 600;
- this.defaults.height = 500;
- this.defaults.mapId = "0";
- this.defaults.showWords = true; // show words or not
- this.defaults.levelChangeable = true; // show words or not
- this.defaults.colorModel = "discrete"; // discrete or gradient color
- //this.defaults.colors = ["#1f77b4", "#ff7f0e"];
- this.defaults.colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
- this.defaults.defaultAreaColor = "#dddddd"; // if area with no data show this color
- this.defaults.wordStyle = {};
- this.defaults.borderStyle = {};
-
- /*
- this.defaults.fontSizeRatio = 1.0;
- */
-
- //event
- this.defaults.customEvent = {
- areaHoverIn : noop,
- areaHoverOut : noop,
- areaClick: noop,
- wordHoverIn: noop,
- wordHoverOut: noop,
- wordClick: noop
- //mousemove : function () {}
- };
- this.renderCallback = noop;
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * get value from indexes
- */
- Chinamap.prototype._searchIndex = function (key, name, regionType) {
- var conf = this.defaults;
- var map = this;
- var result;
- var search = function (regionType, name) {
- var shortName = name.substr(0, 2);
- if (regionType === 'city') {
- //prevent duplicate,张家口市,张家界市,阿拉善盟, 阿拉尔市
- if (shortName === '阿拉' || shortName === '张家') {
- shortName = name.substr(0, 3);
- }
- }
-
- var hash = regionType === 'city' ? map.geoData.cityIndex : map.geoData.provinceIndex;
- var result = hash[shortName];
- if (typeof result === 'undefined') {
- return undefined;
- }
- return result[key];
- };
-
- if (typeof regionType === 'undefined') {
- //province, then city
- if (name === '吉林市' || name === '海南藏族自治州') {
- //吉林省, 吉林市; 海南省,海南藏族自治州
- result = search("city", name);
- } else {
- result = search("province", name) || search("city", name);
- }
- } else {
- if (regionType === 'province') {
- //province
- result = search("province", name);
- } else if (regionType === 'city') {
- //city
- result = search("city", name);
- }
- }
-
- return result;
- };
-
- /**
- * get longitude and latitude center by city or porvince name
- * regionType is optional, if it's undefined, then first search province, then city
- */
- Chinamap.prototype.getLoc = function (name, regionType) {
- return this._searchIndex('loc', name, regionType);
- };
-
- /**
- * get longitude and latitude center by porvince name
- */
- Chinamap.prototype.getProvinceCenter = function (name) {
- return this.getLoc(name, 'province');
- };
-
- /**
- * get longitude and latitude center by city name
- */
- Chinamap.prototype.getCityCenter = function (name) {
- return this.getLoc(name, 'city');
- };
-
- /**
- * get format name by city or porvince name
- * regionType is optional, if it's undefined, then first search province, then city
- */
- Chinamap.prototype.getFormatName = function (name, regionType) {
- return this._searchIndex('fullName', name, regionType);
- };
-
- /**
- * get fullName by porvince name
- */
- Chinamap.prototype.getProvinceFormatName = function (name) {
- return this.getFormatName(name, 'province');
- };
-
- /**
- * get fullName by city name
- */
- Chinamap.prototype.getCityFormatName = function (name) {
- return this.getFormatName(name, 'city');
- };
-
- /**
- * Create dom node relate to chinamap
- */
- Chinamap.prototype.createCanvas = function () {
- var conf = this.defaults,
- canvasStyle,
- container = this.node;
-
- this.canvas = document.createElement("div");
- canvasStyle = this.canvas.style;
- canvasStyle.position = "relative";
- canvasStyle.width = conf.width + "px";
- canvasStyle.height = conf.height + "px";
- container.appendChild(this.canvas);
-
- this.paper = new Raphael(this.canvas, conf.width, conf.height);
- //$(this.node).css("opacity", 0.01);
-
- //this.node.style.position = "relative";
- this.floatTag = DataV.FloatTag()(this.canvas);
- this.floatTag.css({"visibility": "hidden"});
- /*
- */
-
- };
-
-
- /**
- * 获取颜色
- * Examples:
- * ```
- * // 获取第二种颜色的渐变色。
- * {mode: "gradient", index: 1}
- * // 获取最深的离散色。
- * {mode: "random", ratio: 0}
- * // 获取最浅的离散色。
- * {mode: "random", ratio: 1}
- * // 获取适中的离散色。
- * {mode: "random", ratio: 0.5}
- * ```
- * @param {Object} colorJson Way to get color from color theme matrix
- * @return {Array} 返回颜色数组
- */
- Chinamap.prototype.getColor = function (d) {
- var colors = this.defaults.colors;
- var value;
- if (typeof this.colorGenerator.range === 'undefined') {
- if (this.defaults.colorModel === 'discrete') {
- this.colorGenerator = d3.scale.ordinal().range(colors);
- } else {
- this.colorGenerator = d3.scale.linear()
- .range(colors.length === 1 ? colors.concat(colors) : colors)
- .domain(d3.range(0, 1, 1 / (colors.length - 1)).concat([1]));
- }
- this.colorGenerator.min = d3.min(d3.values(this.sourceData));
- this.colorGenerator.domainBand = d3.max(d3.values(this.sourceData)) - this.colorGenerator.min;
- }
- //return this.colorGenerator(d.id);
- //return this.colorGenerator(this.sourceData(d.properties.name));
- /*
- console.log((parseInt(d.id, 10) % 100) / 50);
- console.log(this.colorGenerator((parseInt(d.id, 10) % 100) / 50));
- return this.colorGenerator((parseInt(d.id, 10) % 100) / 50);
- */
- value = this.sourceData[d.properties.name];
- if (typeof value === 'undefined') {
- //no data area color
- return this.defaults.defaultAreaColor;
- } else {
- if (this.defaults.colorModel === 'discrete') {
- return this.colorGenerator(value);
- } else {
- return this.colorGenerator(this.colorGenerator.domainBand === 0
- ? 1
- : (value - this.colorGenerator.min) / this.colorGenerator.domainBand
- );
- }
- }
- };
-
-
- /*
- * 设置数据源
- * Examples:
- * treemap数据输入的格式可以是二维数组。例如下面的数组表示2000年4个季度的天数。
- * 第1季度下面还列出了1-3月的天数。数组的第一行为四个固定的字符串"ID","name","size"和"parentID"。
- * 四列数据分别表示层次数据集中各结点的ID,名称,大小和父节点ID。叶子节点必须有大小,根结点不能有父节点ID。各结点的ID、名称必须要有。
- * ```
- * [
- * ["ID", "name", "size", "parentID"],
- * [0, "2000", , ],
- * [1, "season1", , 0],
- * [2, "January", 31, 1],
- * [3, "February", 29, 1],
- * [4, "Match", 31, 1],
- * [5, "season2", 91, 0],
- * [6, "season3", 92, 0],
- * [7, "season4", 92, 0]
- * ]
- * ```
- * 数据还可以是json格式。每个结点都有`name`,如果是父节点则还有`children`,如果为叶节点则还有`size`。以上数组数据对应的json数据如下:
- * @param {Array|Object} source json or 2-d array
- */
- Chinamap.prototype.setSource = function (source) {
- var key, value, formatName;
- for (key in source) {
- var formatName = this.getFormatName(key);
- if (typeof formatName !== 'undefined') {
- this.sourceData[formatName] = source[key];
- }
- }
- /*
- if (source instanceof Array) {
- this.rawData = this._arrayToJson(source);
- } else {
- this.rawData = source;
- }
- this.source = this._remapSource(this.rawData);
- this.selectedTreeNodes = [this.source[0]];
- */
- };
-
- /*!
- * d3 chinamap layout
- */
- Chinamap.prototype.layout = function () {
- /*
- var chinamap = this._createTreemap()
- .sort(function (a, b) { return a.value - b.value; });
- this.nodes = chinamap.nodes(this.treeNodeJson);
- */
- this.projection = d3.geo.albers()
- .origin([105, 30.5])
- .scale(4000);
-
- this.getAreaPath = d3.geo.path()
- .projection(this.projection);
- };
-
- /*!
- * 生成绘制路径
- */
- Chinamap.prototype.generatePaths = function () {
- var conf = this.defaults;
- var customEvent = conf.customEvent;
- var map = this;
- var states = map.states;
- var words = map.words;
- var projection = map.projection;
- var getAreaPath = map.getAreaPath;
- var mapCache = map.mapCache;
- var paper = this.paper;
- var areaBoxes = this.geoData.areaBoxes;
-
- var render = function (areaId, json) {
- var getTitle = function (d) {
- return d.properties.name;
- };
-
- var getCallback = function (d) {
- return function () {
- parseInt(areaId, 10) === 0
- ? (function () {
- if (parseInt(d.properties.childNum, 10) === 1) {
- return;
- }
- if (typeof mapCache[d.id] === 'undefined') {
- d3.json(conf.geoDataPath + d.id + ".json", function (j) {
- render(d.id, j);
- });
- } else {
- render(d.id);
- }
- }())
- : (function () {
- if (typeof map.mapCache[0] === 'undefined') {
- d3.json(conf.geoDataPath + "0.json", function (j) {
- render(0, j);
- });
- } else {
- render(0);
- }
- }());
- };
- };
- var getCenterX = function (d) {
- return projection(d.properties.cp)[0];
- };
- var getCenterY = function (d) {
- return projection(d.properties.cp)[1];
- };
- var getText = function (d) {
- return d.properties.name;
- };
-
- //paper.clear();
- states.forEach(function (d, i) {
- d.hide();
- });
- words.forEach(function (d, i) {
- d.hide();
- });
-
- states = map.states = [];
- words = map.words = [];
-
- if (typeof mapCache[areaId] === 'undefined') {//no cache
- cache = mapCache[areaId] = {
- states: [],
- words: []
- };
-
- //state
- json.features.forEach(function (d, i) {
- var state = paper.path(getAreaPath(d));
- d.fillColor = map.getColor(d);
- state.attr({
- "fill": d.fillColor,
- "stroke": "#fff"
- })
- .data("info", d)
- .click(customEvent.areaClick)
- .mouseover(customEvent.areaHoverIn)
- .mouseout(customEvent.areaHoverOut);
- if (conf.levelChangeable) {
- state.click(getCallback(d));
- }
- states.push(state);
- state.node.debugName = d.id;
- cache.states.push(state);
- });
-
- //word
- json.features.forEach(function (d, i) {
- var word = paper.text(getCenterX(d), getCenterY(d), getText(d));
- word.attr({
- "font-family": '"微软雅黑", "宋体"'
- })
- .data("info", d)
- .click(customEvent.wordClick)
- .mouseover(customEvent.wordHoverIn)
- .mouseout(customEvent.wordHoverOut);
- if (!conf.showWords) {
- word.hide();
- }
- if (conf.levelChangeable) {
- word.click(getCallback(d));
- }
- words.push(word);
- cache.words.push(word);
- });
-
- states.forEach(function (d, i) {
- d.data("word", words[i])
- .data("map", map);
- words[i].data("state", d)
- .data("map", map);
- });
-
- } else {//cached
- //state
- states = mapCache[areaId].states;
- states.forEach(function (d) {
- d.show();
- });
-
- //word
- words = mapCache[areaId].words;
- if (conf.showWords) {
- words.forEach(function (d) {
- d.show();
- });
- }
- }
-
- var getStatesBox = function () {
- var box = {};
- var areabox = areaBoxes[areaId];
- box.x = areabox[0];
- box.y = areabox[1];
- box.width = areabox[2];
- box.height = areabox[3];
- box.x2 = areabox[2] + areabox[0];
- box.y2 = areabox[3] + areabox[1];
- return box;
- };
-
- (function trans () {
- var recentViewBox = map.viewBox.slice();
- var statesBox = getStatesBox();
- var newBox = [statesBox.x, statesBox.y, statesBox.width, statesBox.height];
- var scale = Math.max(statesBox.width / conf.width, statesBox.height / conf.height); // the ratio that keep the font be the same size no matter what view box size is.
-
- var viewBoxAnim = function (oldBox, newBox, time) {
- var ti = 30;
- var flag = true;
- var start = +new Date();
- var getBox = function (ratio) {
- var box = [];
- var i, l;
- if (ratio >= 1) {
- return newBox;
- }
- for (i = 0, l = oldBox.length; i < l; i++) {
- box[i] = (newBox[i] - oldBox[i]) * ratio + oldBox[i];
- };
- return box;
- };
- var getRatio = function () {
- var t = +new Date();
- var ratio = (t - start) / time;
- if (ratio > 1) {
- ratio = 1;
- }
- var easing = function (n) {
- return Math.pow(n, 1.7);
- };
- return easing(ratio);
- };
- var anim = function () {
- if (flag === false) {
- return;
- }
- //not permit new flame
- flag = false;
- //set new flame;
- var ratio = getRatio();
- var box = getBox(ratio);
- //draw
- paper.setViewBox(box[0], box[1], box[2], box[3], true);
- //console.log(box[0] + " " + box[1] + " " + box[2] + " " + box[3]);
-
- //clearInterval; permit new flame;
- if (ratio >= 1) {
- clearInterval(interval);
- }
- flag = true;
- };
- var interval = setInterval(anim, ti);
- };
-
- states.forEach(function (d, i) {
- states[i].attr({
- "stroke-width": 1 //2 * scale
- })
- .attr(conf.wordStyle);
- words[i].attr({
- "font-size": Math.round(Math.max(14 * scale, 1))//14 * scale + "px"
- })
- .attr(conf.borderStyle);
- });
-
- if (recentViewBox.length === 0) { // first render
- paper.setViewBox(newBox[0], newBox[1], newBox[2], newBox[3], true);
- $("#chart").css("opacity", 1);
- } else {
- if (Raphael.vml) {
- paper.setViewBox(newBox[0], newBox[1], newBox[2], newBox[3], true);
- } else {
- viewBoxAnim(recentViewBox, newBox, 750);
- }
- }
-
- map.viewBox = newBox;
- map.viewBoxShift = (function (x, y, w, h, fit) {
- var width = conf.width,
- height = conf.height,
- size = 1 / Math.max(w / width, h / height),
- H, W;
- if (fit) {
- H = height / h;
- W = width / w;
- if (w * H < width) {
- x -= (width - w * H) / 2 / H;
- }
- if (h * W < height) {
- y -= (height - h * W) / 2 / W;
- }
- }
- return {
- dx: -x,
- dy: -y,
- scale: size
- };
- }(newBox[0], newBox[1], newBox[2], newBox[3], true));
- }());
-
- map.renderCallback();
- };
-
- d3.json(conf.geoDataPath + conf.mapId + ".json", function(json) {
- render(conf.mapId, json);
- });
-
- };
-
- /**
- * 清除画布
- */
- Chinamap.prototype.clearCanvas = function () {
- //this.canvas.innerHTML = "";
- this.paper.clear();
- };
-
- /**
- * 计算布局位置,并渲染图表
- */
- Chinamap.prototype.render = function (options) {
- this.setOptions(options);
- this.clearCanvas();
- this.layout();
- this.generatePaths();
- };
-
- /**
- * 将点在矢量图中的位置 转化为 实际显示点相对于图片左上角的位置(像素距离)
- */
- Chinamap.prototype._scaleLocToPixelLoc = function (scaleLoc) {
- var map = this;
- var scale = map.viewBoxShift.scale;
- var viewCenter = {
- 'x': map.viewBox[0] + map.viewBox[2] / 2,
- 'y': map.viewBox[1] + map.viewBox[3] / 2
- };
- return {
- 'x': (scaleLoc.x - viewCenter.x) * scale + map.defaults.width / 2,
- 'y': (scaleLoc.y - viewCenter.y) * scale + map.defaults.height / 2
- };
- };
-
- /**
- * 将实际显示点相对于图片左上角的位置(像素距离) 转化为 点在矢量图中的位置
- */
- Chinamap.prototype._pixelLocToScaleLoc = function (pixelLoc) {
- var map = this;
- var scale = map.viewBoxShift.scale;
- var viewCenter = {
- 'x': map.viewBox[0] + map.viewBox[2] / 2,
- 'y': map.viewBox[1] + map.viewBox[3] / 2
- };
- return {
- 'x': (pixelLoc.x - map.defaults.width / 2) / scale + viewCenter.x,
- 'y': (pixelLoc.y - map.defaults.height / 2) / scale + viewCenter.y
- };
- };
-
- /**
- * 渲染城市点
- */
- Chinamap.prototype.createCityPoints = function (cities, callback) {
- var conf = this.defaults;
- var map = this;
- var point;
- var points = [];
- var cb = callback || function (city) {
- return this.paper.circle(city.coord[0], city.coord[1], 20)
- .attr({
- "fill": "steelblue",
- "fill-opacity": 0.5
- });
- };
-
- cities.forEach(function (d) {
- var viewCenter = {
- 'x': map.viewBox[0] + map.viewBox[2] / 2,
- 'y': map.viewBox[1] + map.viewBox[3] / 2
- };
- //get format name
- var formatName = map.getCityFormatName(d.name);
- if (typeof formatName === 'undefined') {
- if (typeof d.lanlon === 'undefined') {
- return;
- } else {
- d.formatName = d.name;
- }
- } else {
- d.formatName = formatName;
- //get loc (lan, lon), use user provided lanlon by default;
- d.lanlon = d.lanlon || map.getCityCenter(d.formatName);
- }
- //process loc (geo projection)
- d.coord = map.projection(d.lanlon);
- //x and y of circle center in the container;
- d.pointLoc = map._scaleLocToPixelLoc({
- 'x': d.coord[0],
- 'y': d.coord[1]
- });
- //callback
- point = cb.call(map, d);
- points.push(point);
- });
- return points;
- };
-
- /**
- * 设置自定义事件
- */
- Chinamap.prototype.setCustomEvent = function (eventName, callback) {
- if ($.inArray(eventName, ["areaHoverIn", "areaHoverOut", "areaClick",
- "wordHoverIn", "wordHoverOut", "wordClick"]) !== -1) {
- this.defaults.customEvent[eventName] = callback;
- }
- };
-
- /*!
- * 导出Chinamap
- */
- return Chinamap;
-});
+/*global d3, $, define */
+/*!
+ * Chinamap兼容定义部分
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Chinamap', function (require) {
+ var DataV = require('DataV');
+
+ /*
+ * Chinamap构造函数,继承自Chart
+ * Options:
+ *
+ * - `width` 数字,图片宽度,默认为750,表示图片高750px
+ * - `height` 数字,图片高度,默认为500
+ * - `showBackTag` 布尔值,回退操作导航条是否显示,默认为 true, 显示;设为false则不显示
+ * - `backHeight` 数字,回退操作导航条宽度,默认为20
+ * - `level1BorderWidth` 数字,一级方框的边框宽度,默认为1(1px),不建议修改
+ * - `level2BorderWidth` 数字,二级方框的边框宽度,默认为1(1px),不建议修改
+ * - `fontSizeRatio` 数字,表示图中文字大小。默认为1.0(1倍), 若设为2.0,字体大小会加倍;
+ * - `customEvent` 函数对象,其中有4个自定义函数。`leafNodeClick` 函数,表示点击叶子节点的事件响应,默认为空函数; `hoverIn` 函数,表示鼠标移进方框的事件响应,默认为空函数; `hoverOut` 函数,表示鼠标移出方框的事件响应,默认为空函数; `mouseover` 函数,表示在方框内移动鼠标的事件响应,默认为设置浮框的内容,可以替换它修改浮框内容; 这些函数可以在创建对象或setOption()时一起设置,也可以通过on函数单独设置。
+ *
+ * Examples:
+ * create chinamap in a dom node with id "chart", width is 500; height is 600px;
+ * ```
+ * var chinamap = new Chinamap("chart", {"width": 500, "height": 600});
+ * ```
+ * @param {Object} node The dom node or dom node Id
+ * @param {Object} options JSON object for determin chinamap style
+ */
+ var Chinamap = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ var noop = function () {};
+ this.type = "Chinamap";
+ this.node = this.checkContainer(node);
+
+ // Properties
+ /*
+ this.floatTag;//浮框对象,这是个可操作的对象。
+ */
+ this.viewBox = [];
+ this.mapCache = {};
+ this.states = [];
+ this.words = [];
+ this.projection = function () {}; // d3 map prejection function
+ this.getAreaPath = function () {}; // input geojson feature, return area path string
+ this.colorGenerator = function () {}; // produce gradient or discrete color;
+ this.sourceData = {};
+ this.geoData = {};
+ this.geoData.areaIdIndex = {
+ '全国': 0,
+ '新疆': 65,
+ '西藏': 54,
+ '内蒙古': 15,
+ '青海': 63,
+ '四川': 51,
+ '黑龙江': 23,
+ '甘肃': 62,
+ '云南': 53,
+ '广西': 45,
+ '湖南': 43,
+ '陕西': 61,
+ '广东': 44,
+ '吉林': 22,
+ '河北': 13,
+ '湖北': 42,
+ '贵州': 52,
+ '山东': 37,
+ '江西': 36,
+ '河南': 41,
+ '辽宁': 21,
+ '山西': 14,
+ '安徽': 34,
+ '福建': 35,
+ '浙江': 33,
+ '江苏': 32,
+ '重庆': 50,
+ '宁夏': 64,
+ '海南': 46,
+ '台湾': 71,
+ '北京': 11,
+ '天津': 12,
+ '上海': 31,
+ '香港': 81,
+ '澳门': 82
+ };
+ this.geoData.areaBoxes = {
+ //x, y, width, height when projection scale is 4000
+ 0: [-1174.6445229087194, -1437.3577680805693, 3039.3970214233723, 2531.19589698184],
+ 65: [-1174.9404317915883, -1136.0130934711678, 1216.4169237052663, 939.4360818385251],
+ 54: [-1061.2905098655508, -273.40253896102865, 1182.4138890465167, 728.4762434212385],
+ 15: [81.92106433333947, -1404.5655158641246, 1337.913665139638, 1168.7030286278964],
+ 63: [-398.0407413665446, -404.86540158240564, 770.5429460357634, 553.4881569694239],
+ 51: [34.77351011413543, -24.727858097581816, 654.265749584143, 581.5837904142871],
+ 23: [1185.0861642873883, -1435.9087566254907, 680.9449423479143, 618.3772597960831],
+ 62: [-197.5222870378875, -631.2015222269291, 884.6861134736321, 734.2542202456989],
+ 53: [-4.030270169151834, 326.89754492870105, 561.4971786143803, 565.9079094851168],
+ 45: [444.4355364538484, 524.7911424174906, 490.6548359068431, 384.1667316158848],
+ 43: [716.7125751678784, 265.3988842488122, 346.1702652872375, 377.50144051998274],
+ 61: [508.5948583446903, -399.56997062473215, 321.038690321553, 559.1002147021181],
+ 44: [790.2032875493967, 572.9640361040085, 494.8279567104971, 388.7112686526252],
+ 22: [1287.5729431804648, -950.943295028444, 504.33243011403374, 354.162667814153],
+ 13: [940.0156020671719, -646.4007207319194, 325.33903805510784, 477.4542727272415],
+ 42: [683.8325394595918, 45.82949601748078, 468.66717545627034, 295.2142095820616],
+ 52: [392.5021834497175, 337.4483828727408, 375.50579966539516, 320.9420464446699],
+ 37: [1035.7855473594757, -382.19242168799906, 412.5747391303373, 313.152767793266],
+ 36: [1012.6841751377355, 236.50140310944056, 295.599802392515, 400.86430917822287],
+ 41: [785.5419798731749, -185.2911232263814, 362.6977821251186, 340.3902676066224],
+ 21: [1203.0641741691293, -757.0946871553339, 352.71788824534656, 357.71276541155214],
+ 14: [776.5185040689469, -493.6204506126494, 212.68572802329425, 448.08485211774945],
+ 34: [1054.014965660052, -80.43770626104327, 295.73127466484925, 352.03731065611606],
+ 35: [1172.0955040211252, 341.81292779438445, 288.99462739279807, 339.42845011348845],
+ 33: [1272.1789620983063, 123.46272678646208, 286.17816622252326, 286.73860446060394],
+ 32: [1125.161343490302, -134.97368204682834, 356.1806346879009, 291.4961628010442],
+ 50: [497.78832088614774, 127.0051229616378, 291.91221530072164, 280.8880182020781],
+ 64: [441.193675072408, -376.31946967355213, 183.76989823787306, 293.0024551112753],
+ 46: [723.8031601361929, 946.050886515855, 183.33374783084207, 147.66048518654895],
+ 71: [1459.925544038912, 519.7445429876257, 103.06085087505835, 237.80851484008463],
+ 11: [1031.6052083127613, -530.1928574952913, 103.23943439987329, 114.66079087790081],
+ 12: [1106.9649995752443, -479.16508616378724, 71.21176554916747, 120.01987096046025],
+ 31: [1420.334836525578, 71.79837578328207, 70.41721601016525, 81.99461244072737],
+ 81: [1061.983645387268, 769.0837862603122, 50.65584483626753, 32.17422147262721],
+ 82: [1043.1350056914507, 798.0786255550063, 5.387452843479423, 7.564113979470676]
+ };
+ this.geoData.provinceIndex = {
+ '新疆': {'loc': [84.9023,41.748], 'fullName': '新疆'},
+ '西藏': {'loc': [88.7695,31.6846], 'fullName': '西藏'},
+ '内蒙': {'loc': [117.5977,44.3408], 'fullName': '内蒙古'},
+ '青海': {'loc': [96.2402,35.4199], 'fullName': '青海'},
+ '四川': {'loc': [102.9199,30.1904], 'fullName': '四川'},
+ '黑龙': {'loc': [128.1445,48.5156], 'fullName': '黑龙江'},
+ '甘肃': {'loc': [95.7129,40.166], 'fullName': '甘肃'},
+ '云南': {'loc': [101.8652,25.1807], 'fullName': '云南'},
+ '广西': {'loc': [108.2813,23.6426], 'fullName': '广西'},
+ '湖南': {'loc': [111.5332,27.3779], 'fullName': '湖南'},
+ '陕西': {'loc': [109.5996,35.6396], 'fullName': '陕西'},
+ '广东': {'loc': [113.4668,22.8076], 'fullName': '广东'},
+ '吉林': {'loc': [126.4746,43.5938], 'fullName': '吉林'},
+ '河北': {'loc': [115.4004,37.9688], 'fullName': '河北'},
+ '湖北': {'loc': [112.2363,31.1572], 'fullName': '湖北'},
+ '贵州': {'loc': [106.6113,26.9385], 'fullName': '贵州'},
+ '山东': {'loc': [118.7402,36.4307], 'fullName': '山东'},
+ '江西': {'loc': [116.0156,27.29], 'fullName': '江西'},
+ '河南': {'loc': [113.4668,33.8818], 'fullName': '河南'},
+ '辽宁': {'loc': [122.3438,41.0889], 'fullName': '辽宁'},
+ '山西': {'loc': [112.4121,37.6611], 'fullName': '山西'},
+ '安徽': {'loc': [117.2461,32.0361], 'fullName': '安徽'},
+ '福建': {'loc': [118.3008,25.9277], 'fullName': '福建'},
+ '浙江': {'loc': [120.498,29.0918], 'fullName': '浙江'},
+ '江苏': {'loc': [120.0586,32.915], 'fullName': '江苏'},
+ '重庆': {'loc': [107.7539,30.1904], 'fullName': '重庆'},
+ '宁夏': {'loc': [105.9961,37.3096], 'fullName': '宁夏'},
+ '海南': {'loc': [109.9512,19.2041], 'fullName': '海南'},
+ '台湾': {'loc': [121.0254,23.5986], 'fullName': '台湾'},
+ '北京': {'loc': [116.4551,40.2539], 'fullName': '北京'},
+ '天津': {'loc': [117.4219,39.4189], 'fullName': '天津'},
+ '上海': {'loc': [121.4648,31.2891], 'fullName': '上海'},
+ '香港': {'loc': [114.2578,22.3242], 'fullName': '香港'},
+ '澳门': {'loc': [113.5547,22.1484], 'fullName': '澳门'}
+ };
+ this.geoData.cityIndex = {
+ '重庆': {'loc': [107.7539,30.1904], 'fullName': '重庆'},
+ '北京': {'loc': [116.4551,40.2539], 'fullName': '北京'},
+ '天津': {'loc': [117.4219,39.4189], 'fullName': '天津'},
+ '上海': {'loc': [121.4648,31.2891], 'fullName': '上海'},
+ '香港': {'loc': [114.2578,22.3242], 'fullName': '香港'},
+ '澳门': {'loc': [113.5547,22.1484], 'fullName': '澳门'},
+ '巴音': {'loc': [88.1653,39.6002], 'fullName': '巴音郭楞蒙古自治州'},
+ '和田': {'loc': [81.167,36.9855], 'fullName': '和田地区'},
+ '哈密': {'loc': [93.7793,42.9236], 'fullName': '哈密地区'},
+ '阿克': {'loc': [82.9797,41.0229], 'fullName': '阿克苏地区'},
+ '阿勒': {'loc': [88.2971,47.0929], 'fullName': '阿勒泰地区'},
+ '喀什': {'loc': [77.168,37.8534], 'fullName': '喀什地区'},
+ '塔城': {'loc': [86.6272,45.8514], 'fullName': '塔城地区'},
+ '昌吉': {'loc': [89.6814,44.4507], 'fullName': '昌吉回族自治州'},
+ '克孜': {'loc': [74.6301,39.5233], 'fullName': '克孜勒苏柯尔克孜自治州'},
+ '吐鲁': {'loc': [89.6375,42.4127], 'fullName': '吐鲁番地区'},
+ '伊犁': {'loc': [82.5513,43.5498], 'fullName': '伊犁哈萨克自治州'},
+ '博尔': {'loc': [81.8481,44.6979], 'fullName': '博尔塔拉蒙古自治州'},
+ '乌鲁': {'loc': [87.9236,43.5883], 'fullName': '乌鲁木齐市'},
+ '克拉': {'loc': [85.2869,45.5054], 'fullName': '克拉玛依市'},
+ '阿拉尔': {'loc': [81.2769,40.6549], 'fullName': '阿拉尔市'},
+ '图木': {'loc': [79.1345,39.8749], 'fullName': '图木舒克市'},
+ '五家': {'loc': [87.5391,44.3024], 'fullName': '五家渠市'},
+ '石河': {'loc': [86.0229,44.2914], 'fullName': '石河子市'},
+ '那曲': {'loc': [88.1982,33.3215], 'fullName': '那曲地区'},
+ '阿里': {'loc': [82.3645,32.7667], 'fullName': '阿里地区'},
+ '日喀': {'loc': [86.2427,29.5093], 'fullName': '日喀则地区'},
+ '林芝': {'loc': [95.4602,29.1138], 'fullName': '林芝地区'},
+ '昌都': {'loc': [97.0203,30.7068], 'fullName': '昌都地区'},
+ '山南': {'loc': [92.2083,28.3392], 'fullName': '山南地区'},
+ '拉萨': {'loc': [91.1865,30.1465], 'fullName': '拉萨市'},
+ '呼伦': {'loc': [120.8057,50.2185], 'fullName': '呼伦贝尔市'},
+ '阿拉善': {'loc': [102.019,40.1001], 'fullName': '阿拉善盟'},
+ '锡林': {'loc': [115.6421,44.176], 'fullName': '锡林郭勒盟'},
+ '鄂尔': {'loc': [108.9734,39.2487], 'fullName': '鄂尔多斯市'},
+ '赤峰': {'loc': [118.6743,43.2642], 'fullName': '赤峰市'},
+ '巴彦': {'loc': [107.5562,41.3196], 'fullName': '巴彦淖尔市'},
+ '通辽': {'loc': [121.4758,43.9673], 'fullName': '通辽市'},
+ '乌兰': {'loc': [112.5769,41.77], 'fullName': '乌兰察布市'},
+ '兴安': {'loc': [121.3879,46.1426], 'fullName': '兴安盟'},
+ '包头': {'loc': [110.3467,41.4899], 'fullName': '包头市'},
+ '呼和': {'loc': [111.4124,40.4901], 'fullName': '呼和浩特市'},
+ '乌海': {'loc': [106.886,39.4739], 'fullName': '乌海市'},
+ '海西': {'loc': [94.9768,37.1118], 'fullName': '海西蒙古族藏族自治州'},
+ '玉树': {'loc': [93.5925,33.9368], 'fullName': '玉树藏族自治州'},
+ '果洛': {'loc': [99.3823,34.0466], 'fullName': '果洛藏族自治州'},
+ '海南': {'loc': [100.3711,35.9418], 'fullName': '海南藏族自治州'},
+ '海北': {'loc': [100.3711,37.9138], 'fullName': '海北藏族自治州'},
+ '黄南': {'loc': [101.5686,35.1178], 'fullName': '黄南藏族自治州'},
+ '海东': {'loc': [102.3706,36.2988], 'fullName': '海东地区'},
+ '西宁': {'loc': [101.4038,36.8207], 'fullName': '西宁市'},
+ '甘孜': {'loc': [99.9207,31.0803], 'fullName': '甘孜藏族自治州'},
+ '阿坝': {'loc': [102.4805,32.4536], 'fullName': '阿坝藏族羌族自治州'},
+ '凉山': {'loc': [101.9641,27.6746], 'fullName': '凉山彝族自治州'},
+ '绵阳': {'loc': [104.7327,31.8713], 'fullName': '绵阳市'},
+ '达州': {'loc': [107.6111,31.333], 'fullName': '达州市'},
+ '广元': {'loc': [105.6885,32.2284], 'fullName': '广元市'},
+ '雅安': {'loc': [102.6672,29.8938], 'fullName': '雅安市'},
+ '宜宾': {'loc': [104.6558,28.548], 'fullName': '宜宾市'},
+ '乐山': {'loc': [103.5791,29.1742], 'fullName': '乐山市'},
+ '南充': {'loc': [106.2048,31.1517], 'fullName': '南充市'},
+ '巴中': {'loc': [107.0618,31.9977], 'fullName': '巴中市'},
+ '泸州': {'loc': [105.4578,28.493], 'fullName': '泸州市'},
+ '成都': {'loc': [103.9526,30.7617], 'fullName': '成都市'},
+ '资阳': {'loc': [104.9744,30.1575], 'fullName': '资阳市'},
+ '攀枝': {'loc': [101.6895,26.7133], 'fullName': '攀枝花市'},
+ '眉山': {'loc': [103.8098,30.0146], 'fullName': '眉山市'},
+ '广安': {'loc': [106.6333,30.4376], 'fullName': '广安市'},
+ '德阳': {'loc': [104.48,31.1133], 'fullName': '德阳市'},
+ '内江': {'loc': [104.8535,29.6136], 'fullName': '内江市'},
+ '遂宁': {'loc': [105.5347,30.6683], 'fullName': '遂宁市'},
+ '自贡': {'loc': [104.6667,29.2786], 'fullName': '自贡市'},
+ '黑河': {'loc': [127.1448,49.2957], 'fullName': '黑河市'},
+ '大兴': {'loc': [124.1016,52.2345], 'fullName': '大兴安岭地区'},
+ '哈尔': {'loc': [127.9688,45.368], 'fullName': '哈尔滨市'},
+ '齐齐': {'loc': [124.541,47.5818], 'fullName': '齐齐哈尔市'},
+ '牡丹': {'loc': [129.7815,44.7089], 'fullName': '牡丹江市'},
+ '绥化': {'loc': [126.7163,46.8018], 'fullName': '绥化市'},
+ '伊春': {'loc': [129.1992,47.9608], 'fullName': '伊春市'},
+ '佳木': {'loc': [133.0005,47.5763], 'fullName': '佳木斯市'},
+ '鸡西': {'loc': [132.7917,45.7361], 'fullName': '鸡西市'},
+ '双鸭': {'loc': [133.5938,46.7523], 'fullName': '双鸭山市'},
+ '大庆': {'loc': [124.7717,46.4282], 'fullName': '大庆市'},
+ '鹤岗': {'loc': [130.4407,47.7081], 'fullName': '鹤岗市'},
+ '七台': {'loc': [131.2756,45.9558], 'fullName': '七台河市'},
+ '酒泉': {'loc': [96.2622,40.4517], 'fullName': '酒泉市'},
+ '张掖': {'loc': [99.7998,38.7433], 'fullName': '张掖市'},
+ '甘南': {'loc': [102.9199,34.6893], 'fullName': '甘南藏族自治州'},
+ '武威': {'loc': [103.0188,38.1061], 'fullName': '武威市'},
+ '陇南': {'loc': [105.304,33.5632], 'fullName': '陇南市'},
+ '庆阳': {'loc': [107.5342,36.2], 'fullName': '庆阳市'},
+ '白银': {'loc': [104.8645,36.5076], 'fullName': '白银市'},
+ '定西': {'loc': [104.5569,35.0848], 'fullName': '定西市'},
+ '天水': {'loc': [105.6445,34.6289], 'fullName': '天水市'},
+ '兰州': {'loc': [103.5901,36.3043], 'fullName': '兰州市'},
+ '平凉': {'loc': [107.0728,35.321], 'fullName': '平凉市'},
+ '临夏': {'loc': [103.2715,35.5737], 'fullName': '临夏回族自治州'},
+ '金昌': {'loc': [102.074,38.5126], 'fullName': '金昌市'},
+ '嘉峪': {'loc': [98.1738,39.8035], 'fullName': '嘉峪关市'},
+ '普洱': {'loc': [100.7446,23.4229], 'fullName': '普洱市'},
+ '红河': {'loc': [103.0408,23.6041], 'fullName': '红河哈尼族彝族自治州'},
+ '文山': {'loc': [104.8865,23.5712], 'fullName': '文山壮族苗族自治州'},
+ '曲靖': {'loc': [103.9417,25.7025], 'fullName': '曲靖市'},
+ '楚雄': {'loc': [101.6016,25.3619], 'fullName': '楚雄彝族自治州'},
+ '大理': {'loc': [99.9536,25.6805], 'fullName': '大理白族自治州'},
+ '临沧': {'loc': [99.613,24.0546], 'fullName': '临沧市'},
+ '迪庆': {'loc': [99.4592,27.9327], 'fullName': '迪庆藏族自治州'},
+ '昭通': {'loc': [104.0955,27.6031], 'fullName': '昭通市'},
+ '昆明': {'loc': [102.9199,25.4663], 'fullName': '昆明市'},
+ '丽江': {'loc': [100.448,26.955], 'fullName': '丽江市'},
+ '西双': {'loc': [100.8984,21.8628], 'fullName': '西双版纳傣族自治州'},
+ '保山': {'loc': [99.0637,24.9884], 'fullName': '保山市'},
+ '玉溪': {'loc': [101.9312,23.8898], 'fullName': '玉溪市'},
+ '怒江': {'loc': [99.1516,26.5594], 'fullName': '怒江傈僳族自治州'},
+ '德宏': {'loc': [98.1299,24.5874], 'fullName': '德宏傣族景颇族自治州'},
+ '百色': {'loc': [106.6003,23.9227], 'fullName': '百色市'},
+ '河池': {'loc': [107.8638,24.5819], 'fullName': '河池市'},
+ '桂林': {'loc': [110.5554,25.318], 'fullName': '桂林市'},
+ '南宁': {'loc': [108.479,23.1152], 'fullName': '南宁市'},
+ '柳州': {'loc': [109.3799,24.9774], 'fullName': '柳州市'},
+ '崇左': {'loc': [107.3364,22.4725], 'fullName': '崇左市'},
+ '来宾': {'loc': [109.7095,23.8403], 'fullName': '来宾市'},
+ '玉林': {'loc': [110.2148,22.3792], 'fullName': '玉林市'},
+ '梧州': {'loc': [110.9949,23.5052], 'fullName': '梧州市'},
+ '贺州': {'loc': [111.3135,24.4006], 'fullName': '贺州市'},
+ '钦州': {'loc': [109.0283,22.0935], 'fullName': '钦州市'},
+ '贵港': {'loc': [109.9402,23.3459], 'fullName': '贵港市'},
+ '防城': {'loc': [108.0505,21.9287], 'fullName': '防城港市'},
+ '北海': {'loc': [109.314,21.6211], 'fullName': '北海市'},
+ '怀化': {'loc': [109.9512,27.4438], 'fullName': '怀化市'},
+ '永州': {'loc': [111.709,25.752], 'fullName': '永州市'},
+ '邵阳': {'loc': [110.9619,26.8121], 'fullName': '邵阳市'},
+ '郴州': {'loc': [113.2361,25.8673], 'fullName': '郴州市'},
+ '常德': {'loc': [111.4014,29.2676], 'fullName': '常德市'},
+ '湘西': {'loc': [109.7864,28.6743], 'fullName': '湘西土家族苗族自治州'},
+ '衡阳': {'loc': [112.4121,26.7902], 'fullName': '衡阳市'},
+ '岳阳': {'loc': [113.2361,29.1357], 'fullName': '岳阳市'},
+ '益阳': {'loc': [111.731,28.3832], 'fullName': '益阳市'},
+ '长沙': {'loc': [113.0823,28.2568], 'fullName': '长沙市'},
+ '株洲': {'loc': [113.5327,27.0319], 'fullName': '株洲市'},
+ '张家界': {'loc': [110.5115,29.328], 'fullName': '张家界市'},
+ '娄底': {'loc': [111.6431,27.7185], 'fullName': '娄底市'},
+ '湘潭': {'loc': [112.5439,27.7075], 'fullName': '湘潭市'},
+ '榆林': {'loc': [109.8743,38.205], 'fullName': '榆林市'},
+ '延安': {'loc': [109.1052,36.4252], 'fullName': '延安市'},
+ '汉中': {'loc': [106.886,33.0139], 'fullName': '汉中市'},
+ '安康': {'loc': [109.1162,32.7722], 'fullName': '安康市'},
+ '商洛': {'loc': [109.8083,33.761], 'fullName': '商洛市'},
+ '宝鸡': {'loc': [107.1826,34.3433], 'fullName': '宝鸡市'},
+ '渭南': {'loc': [109.7864,35.0299], 'fullName': '渭南市'},
+ '咸阳': {'loc': [108.4131,34.8706], 'fullName': '咸阳市'},
+ '西安': {'loc': [109.1162,34.2004], 'fullName': '西安市'},
+ '铜川': {'loc': [109.0393,35.1947], 'fullName': '铜川市'},
+ '清远': {'loc': [112.9175,24.3292], 'fullName': '清远市'},
+ '韶关': {'loc': [113.7964,24.7028], 'fullName': '韶关市'},
+ '湛江': {'loc': [110.3577,20.9894], 'fullName': '湛江市'},
+ '梅州': {'loc': [116.1255,24.1534], 'fullName': '梅州市'},
+ '河源': {'loc': [114.917,23.9722], 'fullName': '河源市'},
+ '肇庆': {'loc': [112.1265,23.5822], 'fullName': '肇庆市'},
+ '惠州': {'loc': [114.6204,23.1647], 'fullName': '惠州市'},
+ '茂名': {'loc': [111.0059,22.0221], 'fullName': '茂名市'},
+ '江门': {'loc': [112.6318,22.1484], 'fullName': '江门市'},
+ '阳江': {'loc': [111.8298,22.0715], 'fullName': '阳江市'},
+ '云浮': {'loc': [111.7859,22.8516], 'fullName': '云浮市'},
+ '广州': {'loc': [113.5107,23.2196], 'fullName': '广州市'},
+ '汕尾': {'loc': [115.5762,23.0438], 'fullName': '汕尾市'},
+ '揭阳': {'loc': [116.1255,23.313], 'fullName': '揭阳市'},
+ '珠海': {'loc': [113.7305,22.1155], 'fullName': '珠海市'},
+ '佛山': {'loc': [112.8955,23.1097], 'fullName': '佛山市'},
+ '潮州': {'loc': [116.7847,23.8293], 'fullName': '潮州市'},
+ '汕头': {'loc': [117.1692,23.3405], 'fullName': '汕头市'},
+ '深圳': {'loc': [114.5435,22.5439], 'fullName': '深圳市'},
+ '东莞': {'loc': [113.8953,22.901], 'fullName': '东莞市'},
+ '中山': {'loc': [113.4229,22.478], 'fullName': '中山市'},
+ '延边': {'loc': [129.397,43.2587], 'fullName': '延边朝鲜族自治州'},
+ '吉林': {'loc': [126.8372,43.6047], 'fullName': '吉林市'},
+ '白城': {'loc': [123.0029,45.2637], 'fullName': '白城市'},
+ '松原': {'loc': [124.0906,44.7198], 'fullName': '松原市'},
+ '长春': {'loc': [125.8154,44.2584], 'fullName': '长春市'},
+ '白山': {'loc': [127.2217,42.0941], 'fullName': '白山市'},
+ '通化': {'loc': [125.9583,41.8579], 'fullName': '通化市'},
+ '四平': {'loc': [124.541,43.4894], 'fullName': '四平市'},
+ '辽源': {'loc': [125.343,42.7643], 'fullName': '辽源市'},
+ '承德': {'loc': [117.5757,41.4075], 'fullName': '承德市'},
+ '张家口': {'loc': [115.1477,40.8527], 'fullName': '张家口市'},
+ '保定': {'loc': [115.0488,39.0948], 'fullName': '保定市'},
+ '唐山': {'loc': [118.4766,39.6826], 'fullName': '唐山市'},
+ '沧州': {'loc': [116.8286,38.2104], 'fullName': '沧州市'},
+ '石家': {'loc': [114.4995,38.1006], 'fullName': '石家庄市'},
+ '邢台': {'loc': [114.8071,37.2821], 'fullName': '邢台市'},
+ '邯郸': {'loc': [114.4775,36.535], 'fullName': '邯郸市'},
+ '秦皇': {'loc': [119.2126,40.0232], 'fullName': '秦皇岛市'},
+ '衡水': {'loc': [115.8838,37.7161], 'fullName': '衡水市'},
+ '廊坊': {'loc': [116.521,39.0509], 'fullName': '廊坊市'},
+ '恩施': {'loc': [109.5007,30.2563], 'fullName': '恩施土家族苗族自治州'},
+ '十堰': {'loc': [110.5115,32.3877], 'fullName': '十堰市'},
+ '宜昌': {'loc': [111.1707,30.7617], 'fullName': '宜昌市'},
+ '襄樊': {'loc': [111.9397,31.9263], 'fullName': '襄樊市'},
+ '黄冈': {'loc': [115.2686,30.6628], 'fullName': '黄冈市'},
+ '荆州': {'loc': [113.291,30.0092], 'fullName': '荆州市'},
+ '荆门': {'loc': [112.6758,30.9979], 'fullName': '荆门市'},
+ '咸宁': {'loc': [114.2578,29.6631], 'fullName': '咸宁市'},
+ '随州': {'loc': [113.4338,31.8768], 'fullName': '随州市'},
+ '孝感': {'loc': [113.9502,31.1188], 'fullName': '孝感市'},
+ '武汉': {'loc': [114.3896,30.6628], 'fullName': '武汉市'},
+ '黄石': {'loc': [115.0159,29.9213], 'fullName': '黄石市'},
+ '神农': {'loc': [110.4565,31.5802], 'fullName': '神农架林区'},
+ '天门': {'loc': [113.0273,30.6409], 'fullName': '天门市'},
+ '仙桃': {'loc': [113.3789,30.3003], 'fullName': '仙桃市'},
+ '潜江': {'loc': [112.7637,30.3607], 'fullName': '潜江市'},
+ '鄂州': {'loc': [114.7302,30.4102], 'fullName': '鄂州市'},
+ '遵义': {'loc': [106.908,28.1744], 'fullName': '遵义市'},
+ '黔东': {'loc': [108.4241,26.4166], 'fullName': '黔东南苗族侗族自治州'},
+ '毕节': {'loc': [105.1611,27.0648], 'fullName': '毕节地区'},
+ '黔南': {'loc': [107.2485,25.8398], 'fullName': '黔南布依族苗族自治州'},
+ '铜仁': {'loc': [108.6218,28.0096], 'fullName': '铜仁地区'},
+ '黔西': {'loc': [105.5347,25.3949], 'fullName': '黔西南布依族苗族自治州'},
+ '六盘': {'loc': [104.7546,26.0925], 'fullName': '六盘水市'},
+ '安顺': {'loc': [105.9082,25.9882], 'fullName': '安顺市'},
+ '贵阳': {'loc': [106.6992,26.7682], 'fullName': '贵阳市'},
+ '烟台': {'loc': [120.7397,37.5128], 'fullName': '烟台市'},
+ '临沂': {'loc': [118.3118,35.2936], 'fullName': '临沂市'},
+ '潍坊': {'loc': [119.0918,36.524], 'fullName': '潍坊市'},
+ '青岛': {'loc': [120.4651,36.3373], 'fullName': '青岛市'},
+ '菏泽': {'loc': [115.6201,35.2057], 'fullName': '菏泽市'},
+ '济宁': {'loc': [116.8286,35.3375], 'fullName': '济宁市'},
+ '德州': {'loc': [116.6858,37.2107], 'fullName': '德州市'},
+ '滨州': {'loc': [117.8174,37.4963], 'fullName': '滨州市'},
+ '聊城': {'loc': [115.9167,36.4032], 'fullName': '聊城市'},
+ '东营': {'loc': [118.7073,37.5513], 'fullName': '东营市'},
+ '济南': {'loc': [117.1582,36.8701], 'fullName': '济南市'},
+ '泰安': {'loc': [117.0264,36.0516], 'fullName': '泰安市'},
+ '威海': {'loc': [121.9482,37.1393], 'fullName': '威海市'},
+ '日照': {'loc': [119.2786,35.5023], 'fullName': '日照市'},
+ '淄博': {'loc': [118.0371,36.6064], 'fullName': '淄博市'},
+ '枣庄': {'loc': [117.323,34.8926], 'fullName': '枣庄市'},
+ '莱芜': {'loc': [117.6526,36.2714], 'fullName': '莱芜市'},
+ '赣州': {'loc': [115.2795,25.8124], 'fullName': '赣州市'},
+ '吉安': {'loc': [114.884,26.9659], 'fullName': '吉安市'},
+ '上饶': {'loc': [117.8613,28.7292], 'fullName': '上饶市'},
+ '九江': {'loc': [115.4224,29.3774], 'fullName': '九江市'},
+ '抚州': {'loc': [116.4441,27.4933], 'fullName': '抚州市'},
+ '宜春': {'loc': [115.0159,28.3228], 'fullName': '宜春市'},
+ '南昌': {'loc': [116.0046,28.6633], 'fullName': '南昌市'},
+ '景德': {'loc': [117.334,29.3225], 'fullName': '景德镇市'},
+ '萍乡': {'loc': [113.9282,27.4823], 'fullName': '萍乡市'},
+ '鹰潭': {'loc': [117.0813,28.2349], 'fullName': '鹰潭市'},
+ '新余': {'loc': [114.95,27.8174], 'fullName': '新余市'},
+ '南阳': {'loc': [112.4011,33.0359], 'fullName': '南阳市'},
+ '信阳': {'loc': [114.8291,32.0197], 'fullName': '信阳市'},
+ '洛阳': {'loc': [112.0605,34.3158], 'fullName': '洛阳市'},
+ '驻马': {'loc': [114.1589,32.9041], 'fullName': '驻马店市'},
+ '周口': {'loc': [114.873,33.6951], 'fullName': '周口市'},
+ '商丘': {'loc': [115.741,34.2828], 'fullName': '商丘市'},
+ '三门': {'loc': [110.8301,34.3158], 'fullName': '三门峡市'},
+ '新乡': {'loc': [114.2029,35.3595], 'fullName': '新乡市'},
+ '平顶': {'loc': [112.9724,33.739], 'fullName': '平顶山市'},
+ '郑州': {'loc': [113.4668,34.6234], 'fullName': '郑州市'},
+ '安阳': {'loc': [114.5325,36.0022], 'fullName': '安阳市'},
+ '开封': {'loc': [114.5764,34.6124], 'fullName': '开封市'},
+ '焦作': {'loc': [112.8406,35.1508], 'fullName': '焦作市'},
+ '许昌': {'loc': [113.6975,34.0466], 'fullName': '许昌市'},
+ '濮阳': {'loc': [115.1917,35.799], 'fullName': '濮阳市'},
+ '漯河': {'loc': [113.8733,33.6951], 'fullName': '漯河市'},
+ '鹤壁': {'loc': [114.3787,35.744], 'fullName': '鹤壁市'},
+ '大连': {'loc': [122.2229,39.4409], 'fullName': '大连市'},
+ '朝阳': {'loc': [120.0696,41.4899], 'fullName': '朝阳市'},
+ '丹东': {'loc': [124.541,40.4242], 'fullName': '丹东市'},
+ '铁岭': {'loc': [124.2773,42.7423], 'fullName': '铁岭市'},
+ '沈阳': {'loc': [123.1238,42.1216], 'fullName': '沈阳市'},
+ '抚顺': {'loc': [124.585,41.8579], 'fullName': '抚顺市'},
+ '葫芦': {'loc': [120.1575,40.578], 'fullName': '葫芦岛市'},
+ '阜新': {'loc': [122.0032,42.2699], 'fullName': '阜新市'},
+ '锦州': {'loc': [121.6626,41.4294], 'fullName': '锦州市'},
+ '鞍山': {'loc': [123.0798,40.6055], 'fullName': '鞍山市'},
+ '本溪': {'loc': [124.1455,41.1987], 'fullName': '本溪市'},
+ '营口': {'loc': [122.4316,40.4297], 'fullName': '营口市'},
+ '辽阳': {'loc': [123.4094,41.1383], 'fullName': '辽阳市'},
+ '盘锦': {'loc': [121.9482,41.0449], 'fullName': '盘锦市'},
+ '忻州': {'loc': [112.4561,38.8971], 'fullName': '忻州市'},
+ '吕梁': {'loc': [111.3574,37.7325], 'fullName': '吕梁市'},
+ '临汾': {'loc': [111.4783,36.1615], 'fullName': '临汾市'},
+ '晋中': {'loc': [112.7747,37.37], 'fullName': '晋中市'},
+ '运城': {'loc': [111.1487,35.2002], 'fullName': '运城市'},
+ '大同': {'loc': [113.7854,39.8035], 'fullName': '大同市'},
+ '长治': {'loc': [112.8625,36.4746], 'fullName': '长治市'},
+ '朔州': {'loc': [113.0713,39.6991], 'fullName': '朔州市'},
+ '晋城': {'loc': [112.7856,35.6342], 'fullName': '晋城市'},
+ '太原': {'loc': [112.3352,37.9413], 'fullName': '太原市'},
+ '阳泉': {'loc': [113.4778,38.0951], 'fullName': '阳泉市'},
+ '六安': {'loc': [116.3123,31.8329], 'fullName': '六安市'},
+ '安庆': {'loc': [116.7517,30.5255], 'fullName': '安庆市'},
+ '滁州': {'loc': [118.1909,32.536], 'fullName': '滁州市'},
+ '宣城': {'loc': [118.8062,30.6244], 'fullName': '宣城市'},
+ '阜阳': {'loc': [115.7629,32.9919], 'fullName': '阜阳市'},
+ '宿州': {'loc': [117.5208,33.6841], 'fullName': '宿州市'},
+ '黄山': {'loc': [118.0481,29.9542], 'fullName': '黄山市'},
+ '巢湖': {'loc': [117.7734,31.4978], 'fullName': '巢湖市'},
+ '亳州': {'loc': [116.1914,33.4698], 'fullName': '亳州市'},
+ '池州': {'loc': [117.3889,30.2014], 'fullName': '池州市'},
+ '合肥': {'loc': [117.29,32.0581], 'fullName': '合肥市'},
+ '蚌埠': {'loc': [117.4109,33.1073], 'fullName': '蚌埠市'},
+ '芜湖': {'loc': [118.3557,31.0858], 'fullName': '芜湖市'},
+ '淮北': {'loc': [116.6968,33.6896], 'fullName': '淮北市'},
+ '淮南': {'loc': [116.7847,32.7722], 'fullName': '淮南市'},
+ '马鞍': {'loc': [118.6304,31.5363], 'fullName': '马鞍山市'},
+ '铜陵': {'loc': [117.9382,30.9375], 'fullName': '铜陵市'},
+ '南平': {'loc': [118.136,27.2845], 'fullName': '南平市'},
+ '三明': {'loc': [117.5317,26.3013], 'fullName': '三明市'},
+ '龙岩': {'loc': [116.8066,25.2026], 'fullName': '龙岩市'},
+ '宁德': {'loc': [119.6521,26.9824], 'fullName': '宁德市'},
+ '福州': {'loc': [119.4543,25.9222], 'fullName': '福州市'},
+ '漳州': {'loc': [117.5757,24.3732], 'fullName': '漳州市'},
+ '泉州': {'loc': [118.3228,25.1147], 'fullName': '泉州市'},
+ '莆田': {'loc': [119.0918,25.3455], 'fullName': '莆田市'},
+ '厦门': {'loc': [118.1689,24.6478], 'fullName': '厦门市'},
+ '丽水': {'loc': [119.5642,28.1854], 'fullName': '丽水市'},
+ '杭州': {'loc': [119.5313,29.8773], 'fullName': '杭州市'},
+ '温州': {'loc': [120.498,27.8119], 'fullName': '温州市'},
+ '宁波': {'loc': [121.5967,29.6466], 'fullName': '宁波市'},
+ '舟山': {'loc': [122.2559,30.2234], 'fullName': '舟山市'},
+ '台州': {'loc': [121.1353,28.6688], 'fullName': '台州市'},
+ '金华': {'loc': [120.0037,29.1028], 'fullName': '金华市'},
+ '衢州': {'loc': [118.6853,28.8666], 'fullName': '衢州市'},
+ '绍兴': {'loc': [120.564,29.7565], 'fullName': '绍兴市'},
+ '嘉兴': {'loc': [120.9155,30.6354], 'fullName': '嘉兴市'},
+ '湖州': {'loc': [119.8608,30.7782], 'fullName': '湖州市'},
+ '盐城': {'loc': [120.2234,33.5577], 'fullName': '盐城市'},
+ '徐州': {'loc': [117.5208,34.3268], 'fullName': '徐州市'},
+ '南通': {'loc': [121.1023,32.1625], 'fullName': '南通市'},
+ '淮安': {'loc': [118.927,33.4039], 'fullName': '淮安市'},
+ '苏州': {'loc': [120.6519,31.3989], 'fullName': '苏州市'},
+ '宿迁': {'loc': [118.5535,33.7775], 'fullName': '宿迁市'},
+ '连云': {'loc': [119.1248,34.552], 'fullName': '连云港市'},
+ '扬州': {'loc': [119.4653,32.8162], 'fullName': '扬州市'},
+ '南京': {'loc': [118.8062,31.9208], 'fullName': '南京市'},
+ '泰州': {'loc': [120.0586,32.5525], 'fullName': '泰州市'},
+ '无锡': {'loc': [120.3442,31.5527], 'fullName': '无锡市'},
+ '常州': {'loc': [119.4543,31.5582], 'fullName': '常州市'},
+ '镇江': {'loc': [119.4763,31.9702], 'fullName': '镇江市'},
+ '吴忠': {'loc': [106.853,37.3755], 'fullName': '吴忠市'},
+ '中卫': {'loc': [105.4028,36.9525], 'fullName': '中卫市'},
+ '固原': {'loc': [106.1389,35.9363], 'fullName': '固原市'},
+ '银川': {'loc': [106.3586,38.1775], 'fullName': '银川市'},
+ '石嘴': {'loc': [106.4795,39.0015], 'fullName': '石嘴山市'},
+ '儋州': {'loc': [109.3291,19.5653], 'fullName': '儋州市'},
+ '文昌': {'loc': [110.8905,19.7823], 'fullName': '文昌市'},
+ '乐东': {'loc': [109.0283,18.6301], 'fullName': '乐东黎族自治县'},
+ '三亚': {'loc': [109.3716,18.3698], 'fullName': '三亚市'},
+ '琼中': {'loc': [109.8413,19.0736], 'fullName': '琼中黎族苗族自治县'},
+ '东方': {'loc': [108.8498,19.0414], 'fullName': '东方市'},
+ '海口': {'loc': [110.3893,19.8516], 'fullName': '海口市'},
+ '万宁': {'loc': [110.3137,18.8388], 'fullName': '万宁市'},
+ '澄迈': {'loc': [109.9937,19.7314], 'fullName': '澄迈县'},
+ '白沙': {'loc': [109.3703,19.211], 'fullName': '白沙黎族自治县'},
+ '琼海': {'loc': [110.4208,19.224], 'fullName': '琼海市'},
+ '昌江': {'loc': [109.0407,19.2137], 'fullName': '昌江黎族自治县'},
+ '临高': {'loc': [109.6957,19.8063], 'fullName': '临高县'},
+ '陵水': {'loc': [109.9924,18.5415], 'fullName': '陵水黎族自治县'},
+ '屯昌': {'loc': [110.0377,19.362], 'fullName': '屯昌县'},
+ '定安': {'loc': [110.3384,19.4698], 'fullName': '定安县'},
+ '保亭': {'loc': [109.6284,18.6108], 'fullName': '保亭黎族苗族自治县'},
+ '五指': {'loc': [109.5282,18.8299], 'fullName': '五指山市'}
+ };
+
+ // Canvas
+ this.defaults = {};
+ this.defaults.geoDataPath = "";
+ this.defaults.width = 600;
+ this.defaults.height = 500;
+ this.defaults.mapId = "0";
+ this.defaults.showWords = true; // show words or not
+ this.defaults.levelChangeable = true; // show words or not
+ this.defaults.colorModel = "discrete"; // discrete or gradient color
+ //this.defaults.colors = ["#1f77b4", "#ff7f0e"];
+ this.defaults.colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
+ this.defaults.defaultAreaColor = "#dddddd"; // if area with no data show this color
+ this.defaults.wordStyle = {};
+ this.defaults.borderStyle = {};
+
+ /*
+ this.defaults.fontSizeRatio = 1.0;
+ */
+
+ //event
+ this.defaults.customEvent = {
+ areaHoverIn : noop,
+ areaHoverOut : noop,
+ areaClick: noop,
+ wordHoverIn: noop,
+ wordHoverOut: noop,
+ wordClick: noop
+ //mousemove : function () {}
+ };
+ this.renderCallback = noop;
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * get value from indexes
+ */
+ Chinamap.prototype._searchIndex = function (key, name, regionType) {
+ var conf = this.defaults;
+ var map = this;
+ var result;
+ var search = function (regionType, name) {
+ var shortName = name.substr(0, 2);
+ if (regionType === 'city') {
+ //prevent duplicate,张家口市,张家界市,阿拉善盟, 阿拉尔市
+ if (shortName === '阿拉' || shortName === '张家') {
+ shortName = name.substr(0, 3);
+ }
+ }
+
+ var hash = regionType === 'city' ? map.geoData.cityIndex : map.geoData.provinceIndex;
+ var result = hash[shortName];
+ if (typeof result === 'undefined') {
+ return undefined;
+ }
+ return result[key];
+ };
+
+ if (typeof regionType === 'undefined') {
+ //province, then city
+ if (name === '吉林市' || name === '海南藏族自治州') {
+ //吉林省, 吉林市; 海南省,海南藏族自治州
+ result = search("city", name);
+ } else {
+ result = search("province", name) || search("city", name);
+ }
+ } else {
+ if (regionType === 'province') {
+ //province
+ result = search("province", name);
+ } else if (regionType === 'city') {
+ //city
+ result = search("city", name);
+ }
+ }
+
+ return result;
+ };
+
+ /**
+ * get longitude and latitude center by city or porvince name
+ * regionType is optional, if it's undefined, then first search province, then city
+ */
+ Chinamap.prototype.getLoc = function (name, regionType) {
+ return this._searchIndex('loc', name, regionType);
+ };
+
+ /**
+ * get longitude and latitude center by porvince name
+ */
+ Chinamap.prototype.getProvinceCenter = function (name) {
+ return this.getLoc(name, 'province');
+ };
+
+ /**
+ * get longitude and latitude center by city name
+ */
+ Chinamap.prototype.getCityCenter = function (name) {
+ return this.getLoc(name, 'city');
+ };
+
+ /**
+ * get format name by city or porvince name
+ * regionType is optional, if it's undefined, then first search province, then city
+ */
+ Chinamap.prototype.getFormatName = function (name, regionType) {
+ return this._searchIndex('fullName', name, regionType);
+ };
+
+ /**
+ * get fullName by porvince name
+ */
+ Chinamap.prototype.getProvinceFormatName = function (name) {
+ return this.getFormatName(name, 'province');
+ };
+
+ /**
+ * get fullName by city name
+ */
+ Chinamap.prototype.getCityFormatName = function (name) {
+ return this.getFormatName(name, 'city');
+ };
+
+ /**
+ * Create dom node relate to chinamap
+ */
+ Chinamap.prototype.createCanvas = function () {
+ var conf = this.defaults,
+ canvasStyle,
+ container = this.node;
+
+ this.canvas = document.createElement("div");
+ canvasStyle = this.canvas.style;
+ canvasStyle.position = "relative";
+ canvasStyle.width = conf.width + "px";
+ canvasStyle.height = conf.height + "px";
+ container.appendChild(this.canvas);
+
+ this.paper = new Raphael(this.canvas, conf.width, conf.height);
+ //$(this.node).css("opacity", 0.01);
+
+ //this.node.style.position = "relative";
+ this.floatTag = DataV.FloatTag()(this.canvas);
+ this.floatTag.css({"visibility": "hidden"});
+ /*
+ */
+
+ };
+
+
+ /**
+ * 获取颜色
+ * Examples:
+ * ```
+ * // 获取第二种颜色的渐变色。
+ * {mode: "gradient", index: 1}
+ * // 获取最深的离散色。
+ * {mode: "random", ratio: 0}
+ * // 获取最浅的离散色。
+ * {mode: "random", ratio: 1}
+ * // 获取适中的离散色。
+ * {mode: "random", ratio: 0.5}
+ * ```
+ * @param {Object} colorJson Way to get color from color theme matrix
+ * @return {Array} 返回颜色数组
+ */
+ Chinamap.prototype.getColor = function (d) {
+ var colors = this.defaults.colors;
+ var value;
+ if (typeof this.colorGenerator.range === 'undefined') {
+ if (this.defaults.colorModel === 'discrete') {
+ this.colorGenerator = d3.scale.ordinal().range(colors);
+ } else {
+ this.colorGenerator = d3.scale.linear()
+ .range(colors.length === 1 ? colors.concat(colors) : colors)
+ .domain(d3.range(0, 1, 1 / (colors.length - 1)).concat([1]));
+ }
+ this.colorGenerator.min = d3.min(d3.values(this.sourceData));
+ this.colorGenerator.domainBand = d3.max(d3.values(this.sourceData)) - this.colorGenerator.min;
+ }
+ //return this.colorGenerator(d.id);
+ //return this.colorGenerator(this.sourceData(d.properties.name));
+ /*
+ console.log((parseInt(d.id, 10) % 100) / 50);
+ console.log(this.colorGenerator((parseInt(d.id, 10) % 100) / 50));
+ return this.colorGenerator((parseInt(d.id, 10) % 100) / 50);
+ */
+ value = this.sourceData[d.properties.name];
+ if (typeof value === 'undefined') {
+ //no data area color
+ return this.defaults.defaultAreaColor;
+ } else {
+ if (this.defaults.colorModel === 'discrete') {
+ return this.colorGenerator(value);
+ } else {
+ return this.colorGenerator(this.colorGenerator.domainBand === 0
+ ? 1
+ : (value - this.colorGenerator.min) / this.colorGenerator.domainBand
+ );
+ }
+ }
+ };
+
+
+ /*
+ * 设置数据源
+ * Examples:
+ * treemap数据输入的格式可以是二维数组。例如下面的数组表示2000年4个季度的天数。
+ * 第1季度下面还列出了1-3月的天数。数组的第一行为四个固定的字符串"ID","name","size"和"parentID"。
+ * 四列数据分别表示层次数据集中各结点的ID,名称,大小和父节点ID。叶子节点必须有大小,根结点不能有父节点ID。各结点的ID、名称必须要有。
+ * ```
+ * [
+ * ["ID", "name", "size", "parentID"],
+ * [0, "2000", , ],
+ * [1, "season1", , 0],
+ * [2, "January", 31, 1],
+ * [3, "February", 29, 1],
+ * [4, "Match", 31, 1],
+ * [5, "season2", 91, 0],
+ * [6, "season3", 92, 0],
+ * [7, "season4", 92, 0]
+ * ]
+ * ```
+ * 数据还可以是json格式。每个结点都有`name`,如果是父节点则还有`children`,如果为叶节点则还有`size`。以上数组数据对应的json数据如下:
+ * @param {Array|Object} source json or 2-d array
+ */
+ Chinamap.prototype.setSource = function (source) {
+ var key, value, formatName;
+ for (key in source) {
+ var formatName = this.getFormatName(key);
+ if (typeof formatName !== 'undefined') {
+ this.sourceData[formatName] = source[key];
+ }
+ }
+ /*
+ if (source instanceof Array) {
+ this.rawData = this._arrayToJson(source);
+ } else {
+ this.rawData = source;
+ }
+ this.source = this._remapSource(this.rawData);
+ this.selectedTreeNodes = [this.source[0]];
+ */
+ };
+
+ /*!
+ * d3 chinamap layout
+ */
+ Chinamap.prototype.layout = function () {
+ /*
+ var chinamap = this._createTreemap()
+ .sort(function (a, b) { return a.value - b.value; });
+ this.nodes = chinamap.nodes(this.treeNodeJson);
+ */
+ this.projection = d3.geo.albers()
+ .origin([105, 30.5])
+ .scale(4000);
+
+ this.getAreaPath = d3.geo.path()
+ .projection(this.projection);
+ };
+
+ /*!
+ * 生成绘制路径
+ */
+ Chinamap.prototype.generatePaths = function () {
+ var conf = this.defaults;
+ var customEvent = conf.customEvent;
+ var map = this;
+ var states = map.states;
+ var words = map.words;
+ var projection = map.projection;
+ var getAreaPath = map.getAreaPath;
+ var mapCache = map.mapCache;
+ var paper = this.paper;
+ var areaBoxes = this.geoData.areaBoxes;
+
+ var render = function (areaId, json) {
+ var getTitle = function (d) {
+ return d.properties.name;
+ };
+
+ var getCallback = function (d) {
+ return function () {
+ parseInt(areaId, 10) === 0
+ ? (function () {
+ if (parseInt(d.properties.childNum, 10) === 1) {
+ return;
+ }
+ if (typeof mapCache[d.id] === 'undefined') {
+ d3.json(conf.geoDataPath + d.id + ".json", function (j) {
+ render(d.id, j);
+ });
+ } else {
+ render(d.id);
+ }
+ }())
+ : (function () {
+ if (typeof map.mapCache[0] === 'undefined') {
+ d3.json(conf.geoDataPath + "0.json", function (j) {
+ render(0, j);
+ });
+ } else {
+ render(0);
+ }
+ }());
+ };
+ };
+ var getCenterX = function (d) {
+ return projection(d.properties.cp)[0];
+ };
+ var getCenterY = function (d) {
+ return projection(d.properties.cp)[1];
+ };
+ var getText = function (d) {
+ return d.properties.name;
+ };
+
+ //paper.clear();
+ states.forEach(function (d, i) {
+ d.hide();
+ });
+ words.forEach(function (d, i) {
+ d.hide();
+ });
+
+ states = map.states = [];
+ words = map.words = [];
+
+ if (typeof mapCache[areaId] === 'undefined') {//no cache
+ cache = mapCache[areaId] = {
+ states: [],
+ words: []
+ };
+
+ //state
+ json.features.forEach(function (d, i) {
+ var state = paper.path(getAreaPath(d));
+ d.fillColor = map.getColor(d);
+ state.attr({
+ "fill": d.fillColor,
+ "stroke": "#fff"
+ })
+ .data("info", d)
+ .click(customEvent.areaClick)
+ .mouseover(customEvent.areaHoverIn)
+ .mouseout(customEvent.areaHoverOut);
+ if (conf.levelChangeable) {
+ state.click(getCallback(d));
+ }
+ states.push(state);
+ state.node.debugName = d.id;
+ cache.states.push(state);
+ });
+
+ //word
+ json.features.forEach(function (d, i) {
+ var word = paper.text(getCenterX(d), getCenterY(d), getText(d));
+ word.attr({
+ "font-family": '"微软雅黑", "宋体"'
+ })
+ .data("info", d)
+ .click(customEvent.wordClick)
+ .mouseover(customEvent.wordHoverIn)
+ .mouseout(customEvent.wordHoverOut);
+ if (!conf.showWords) {
+ word.hide();
+ }
+ if (conf.levelChangeable) {
+ word.click(getCallback(d));
+ }
+ words.push(word);
+ cache.words.push(word);
+ });
+
+ states.forEach(function (d, i) {
+ d.data("word", words[i])
+ .data("map", map);
+ words[i].data("state", d)
+ .data("map", map);
+ });
+
+ } else {//cached
+ //state
+ states = mapCache[areaId].states;
+ states.forEach(function (d) {
+ d.show();
+ });
+
+ //word
+ words = mapCache[areaId].words;
+ if (conf.showWords) {
+ words.forEach(function (d) {
+ d.show();
+ });
+ }
+ }
+
+ var getStatesBox = function () {
+ var box = {};
+ var areabox = areaBoxes[areaId];
+ box.x = areabox[0];
+ box.y = areabox[1];
+ box.width = areabox[2];
+ box.height = areabox[3];
+ box.x2 = areabox[2] + areabox[0];
+ box.y2 = areabox[3] + areabox[1];
+ return box;
+ };
+
+ (function trans () {
+ var recentViewBox = map.viewBox.slice();
+ var statesBox = getStatesBox();
+ var newBox = [statesBox.x, statesBox.y, statesBox.width, statesBox.height];
+ var scale = Math.max(statesBox.width / conf.width, statesBox.height / conf.height); // the ratio that keep the font be the same size no matter what view box size is.
+
+ var viewBoxAnim = function (oldBox, newBox, time) {
+ var ti = 30;
+ var flag = true;
+ var start = +new Date();
+ var getBox = function (ratio) {
+ var box = [];
+ var i, l;
+ if (ratio >= 1) {
+ return newBox;
+ }
+ for (i = 0, l = oldBox.length; i < l; i++) {
+ box[i] = (newBox[i] - oldBox[i]) * ratio + oldBox[i];
+ };
+ return box;
+ };
+ var getRatio = function () {
+ var t = +new Date();
+ var ratio = (t - start) / time;
+ if (ratio > 1) {
+ ratio = 1;
+ }
+ var easing = function (n) {
+ return Math.pow(n, 1.7);
+ };
+ return easing(ratio);
+ };
+ var anim = function () {
+ if (flag === false) {
+ return;
+ }
+ //not permit new flame
+ flag = false;
+ //set new flame;
+ var ratio = getRatio();
+ var box = getBox(ratio);
+ //draw
+ paper.setViewBox(box[0], box[1], box[2], box[3], true);
+ //console.log(box[0] + " " + box[1] + " " + box[2] + " " + box[3]);
+
+ //clearInterval; permit new flame;
+ if (ratio >= 1) {
+ clearInterval(interval);
+ }
+ flag = true;
+ };
+ var interval = setInterval(anim, ti);
+ };
+
+ states.forEach(function (d, i) {
+ states[i].attr({
+ "stroke-width": 1 //2 * scale
+ })
+ .attr(conf.wordStyle);
+ words[i].attr({
+ "font-size": Math.round(Math.max(14 * scale, 1))//14 * scale + "px"
+ })
+ .attr(conf.borderStyle);
+ });
+
+ if (recentViewBox.length === 0) { // first render
+ paper.setViewBox(newBox[0], newBox[1], newBox[2], newBox[3], true);
+ $("#chart").css("opacity", 1);
+ } else {
+ if (Raphael.vml) {
+ paper.setViewBox(newBox[0], newBox[1], newBox[2], newBox[3], true);
+ } else {
+ viewBoxAnim(recentViewBox, newBox, 750);
+ }
+ }
+
+ map.viewBox = newBox;
+ map.viewBoxShift = (function (x, y, w, h, fit) {
+ var width = conf.width,
+ height = conf.height,
+ size = 1 / Math.max(w / width, h / height),
+ H, W;
+ if (fit) {
+ H = height / h;
+ W = width / w;
+ if (w * H < width) {
+ x -= (width - w * H) / 2 / H;
+ }
+ if (h * W < height) {
+ y -= (height - h * W) / 2 / W;
+ }
+ }
+ return {
+ dx: -x,
+ dy: -y,
+ scale: size
+ };
+ }(newBox[0], newBox[1], newBox[2], newBox[3], true));
+ }());
+
+ map.renderCallback();
+ };
+
+ d3.json(conf.geoDataPath + conf.mapId + ".json", function(json) {
+ render(conf.mapId, json);
+ });
+
+ };
+
+ /**
+ * 清除画布
+ */
+ Chinamap.prototype.clearCanvas = function () {
+ //this.canvas.innerHTML = "";
+ this.paper.clear();
+ };
+
+ /**
+ * 计算布局位置,并渲染图表
+ */
+ Chinamap.prototype.render = function (options) {
+ this.setOptions(options);
+ this.clearCanvas();
+ this.layout();
+ this.generatePaths();
+ };
+
+ /**
+ * 将点在矢量图中的位置 转化为 实际显示点相对于图片左上角的位置(像素距离)
+ */
+ Chinamap.prototype._scaleLocToPixelLoc = function (scaleLoc) {
+ var map = this;
+ var scale = map.viewBoxShift.scale;
+ var viewCenter = {
+ 'x': map.viewBox[0] + map.viewBox[2] / 2,
+ 'y': map.viewBox[1] + map.viewBox[3] / 2
+ };
+ return {
+ 'x': (scaleLoc.x - viewCenter.x) * scale + map.defaults.width / 2,
+ 'y': (scaleLoc.y - viewCenter.y) * scale + map.defaults.height / 2
+ };
+ };
+
+ /**
+ * 将实际显示点相对于图片左上角的位置(像素距离) 转化为 点在矢量图中的位置
+ */
+ Chinamap.prototype._pixelLocToScaleLoc = function (pixelLoc) {
+ var map = this;
+ var scale = map.viewBoxShift.scale;
+ var viewCenter = {
+ 'x': map.viewBox[0] + map.viewBox[2] / 2,
+ 'y': map.viewBox[1] + map.viewBox[3] / 2
+ };
+ return {
+ 'x': (pixelLoc.x - map.defaults.width / 2) / scale + viewCenter.x,
+ 'y': (pixelLoc.y - map.defaults.height / 2) / scale + viewCenter.y
+ };
+ };
+
+ /**
+ * 渲染城市点
+ */
+ Chinamap.prototype.createCityPoints = function (cities, callback) {
+ var conf = this.defaults;
+ var map = this;
+ var point;
+ var points = [];
+ var cb = callback || function (city) {
+ return this.paper.circle(city.coord[0], city.coord[1], 20)
+ .attr({
+ "fill": "steelblue",
+ "fill-opacity": 0.5
+ });
+ };
+
+ cities.forEach(function (d) {
+ var viewCenter = {
+ 'x': map.viewBox[0] + map.viewBox[2] / 2,
+ 'y': map.viewBox[1] + map.viewBox[3] / 2
+ };
+ //get format name
+ var formatName = map.getCityFormatName(d.name);
+ if (typeof formatName === 'undefined') {
+ if (typeof d.lanlon === 'undefined') {
+ return;
+ } else {
+ d.formatName = d.name;
+ }
+ } else {
+ d.formatName = formatName;
+ //get loc (lan, lon), use user provided lanlon by default;
+ d.lanlon = d.lanlon || map.getCityCenter(d.formatName);
+ }
+ //process loc (geo projection)
+ d.coord = map.projection(d.lanlon);
+ //x and y of circle center in the container;
+ d.pointLoc = map._scaleLocToPixelLoc({
+ 'x': d.coord[0],
+ 'y': d.coord[1]
+ });
+ //callback
+ point = cb.call(map, d);
+ points.push(point);
+ });
+ return points;
+ };
+
+ /**
+ * 设置自定义事件
+ */
+ Chinamap.prototype.setCustomEvent = function (eventName, callback) {
+ if ($.inArray(eventName, ["areaHoverIn", "areaHoverOut", "areaClick",
+ "wordHoverIn", "wordHoverOut", "wordClick"]) !== -1) {
+ this.defaults.customEvent[eventName] = callback;
+ }
+ };
+
+ /*!
+ * 导出Chinamap
+ */
+ return Chinamap;
+});
diff --git a/lib/charts/chord.js b/lib/charts/chord.js
index d2c9624..354e08c 100644
--- a/lib/charts/chord.js
+++ b/lib/charts/chord.js
@@ -1,481 +1,481 @@
-/*global Raphael, d3, $, define */
-/*!
- * Chord的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) {
- return this[id];
- });
- }
-})('Chord', function (require) {
- var DataV = require('DataV');
-
- /**
- * 构造函数
- * @param {Object} node 表示在html的哪个容器中绘制该组件
- * @param {Object} options 为用户自定义的组件的属性,比如画布大小
- */
- var Chord = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Chord";
- this.node = this.checkContainer(node);
- this.matrix = [];
- this.groupNames = []; //数组:记录每个group的名字
-
- //图的大小设置
- this.defaults.legend = true;
- this.defaults.width = 800;
- this.defaults.height = 800;
-
- //设置用户指定的属性
- this.setOptions(options);
-
- this.legendArea = [20, (this.defaults.height - 20 - 220), 200, 220];
- if (this.defaults.legend) {
- this.xOffset = this.legendArea[2];
- } else {
- this.xOffset = 0;
- }
-
- this.defaults.innerRadius = Math.min((this.defaults.width - this.xOffset), this.defaults.height) * 0.38;
- this.defaults.outerRadius = this.defaults.innerRadius * 1.10;
- //创建画布
- this.createCanvas();
- }
- });
-
- /**
- * 创建画布
- */
- Chord.prototype.createCanvas = function () {
- this.canvas = new Raphael(this.node, this.defaults.width, this.defaults.height);
- canvasStyle = this.node.style;
- canvasStyle.position = "relative";
- this.floatTag = DataV.FloatTag()(this.node);
- this.floatTag.css({
- "visibility": "hidden"
- });
- };
-
- /**
- * 获取颜色
- * @param {Number} i 元素类别编号
- * @return {String} 返回颜色值
- */
- Chord.prototype.getColor = function (i) {
- var color = DataV.getColor();
- return color[i % color.length][0];
- };
-
- /**
- * 绘制弦图
- */
- Chord.prototype.render = function () {
- this.layout();
- if (this.defaults.legend) {
- this.legend();
- }
- };
-
- /**
- * 绘制图例
- */
- Chord.prototype.legend = function () {
- var that = this;
- var paper = this.canvas;
- var legendArea = this.legendArea;
- var rectBn = paper.set();
- this.underBn = [];
- var underBn = this.underBn;
- for (i = 0; i <= this.groupNum; i++) {
- //底框
- underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "#ebebeb",
- "stroke": "none"
- //"r": 3
- }).hide());
- //色框
- paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
- "fill": this.getColor(i),
- "stroke": "none"
- });
- //文字
- paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groupNames[i]).attr({
- "fill": "black",
- "fill-opacity": 1,
- "font-family": "Verdana",
- "font-size": 12
- }).attr({
- "text-anchor": "start"
- });
- //选框
- rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "white",
- "fill-opacity": 0,
- "stroke": "none"
- //"r": 3
- })).data("clicked", 0);
- }
- rectBn.forEach(function (d, i) {
- d.mouseover(function () {
- if (d.data("clicked") === 0) {
- underBn[i].attr('opacity', 0.5);
- underBn[i].show();
- }
- }).mouseout(function () {
- if (d.data("clicked") === 0) {
- underBn[i].hide();
- }
- });
- d.click(function () {
- for (j = 0; j < underBn.length; j++) {
- if (j === i) {
- underBn[j].show();
- } else {
- underBn[j].hide();
- }
- }
- rectBn.forEach(function (eachBn) {
- if (eachBn !== d) {
- eachBn.data("clicked", 0);
- }
- });
- if (d.data("clicked") === 0) {
- underBn[i].attr('opacity', 1);
- underBn[i].show();
- that.chordGroups.forEach(function (d) {
- if (d.data('source') !== i && d.data('target') !== i) {
- d.attr({
- 'fill-opacity': 0.1
- });
- } else {
- d.attr({
- 'fill-opacity': 0.6
- });
- }
- });
-
- d.data("clicked", 1);
- } else if (d.data("clicked") === 1) {
- underBn[i].hide();
- d.data("clicked", 0);
- that.chordGroups.forEach(function (d) {
- d.attr({
- 'fill-opacity': 0.6
- });
- });
- }
- });
- });
- };
-
- /**
- *对原始数据进行处理
- * @param {Array} table 将要被绘制成饼图的二维表数据
- */
- Chord.prototype.setSource = function (table) {
- if (table[0][0] !== null) {
- var t;
- for (t = 0; t < table[0].length; t++) {
- this.groupNames[t] = table[0][t];
- }
- table = table.slice(1); // 从第一行开始,第0行舍去
- }
-
- var group = [];
- this.groupNum = table[0].length;
- var groupNum = this.groupNum;
- var that = this;
- table.forEach(function (d, i) {
- if (d.length !== groupNum || table.length !== groupNum) {
- throw new Error("The source data should be an n * n matrix!");
- }
-
- group[i] = [];
- var s;
- for (s = 0; s < groupNum; s++) {
- group[i][s] = Number(d[s]);
- }
- });
-
- var r;
- for (r = 0; r < groupNum; r++) {
- that.matrix[r] = group[r];
- }
- };
-
- /**
- *创建chord布局
- */
- Chord.prototype.layout = function () {
- var floatTag = this.floatTag;
- var that = this;
-
- that.canvas.clear();
- /*var see = [
- [11975, 5871, 8916, 2868],
- [1951, 10048, 2060, 6171],
- [8010, 16145, 8090, 8045],
- [1013, 990, 940, 6907]
- ];*/
-
- var chordLayout = d3.layout.chord().padding(0.05) //chord segments之间的padding间隔
- .sortSubgroups(d3.descending) //chord segments细分后的排序规则
- .matrix(that.matrix);
-
- /*var fillColor = d3.scale.ordinal()
- .domain(d3.range(4))
- .range(["#000000", "#FFDD89", "#957244", "#F26223"]);*/
-
- //groups数组:获取每个组的起始角度、数值、索引等属性
- var groups = chordLayout.groups();
-
- //由内外半径、起始角度计算路径字符串
- var pathCalc = d3.svg.arc().innerRadius(that.defaults.innerRadius).outerRadius(that.defaults.outerRadius).startAngle(function (d) {
- return d.startAngle;
- }).endAngle(function (d) {
- return d.endAngle;
- });
-
- var chords = chordLayout.chords();
-
- //计算弦的路径曲线
- var chordCalc = d3.svg.chord().radius(that.defaults.innerRadius);
-
- //Raphael: Paper.path()
- var donutEle;
- //获取每个环形的字符串表示
- var spline;
- //表示每条弦的element
- var chordEle;
- //每条弦的字符串表示
- var belt;
-
- var num; //每个group分割小格数
- var unitAngle; //每个group所占的角度
- var angle;
- var radian;
- var tickLine;
- var tickStr; //每个tick的路径
- var xTrans, yTrans;
- var aX, aY, bX, bY; //每个tick起始端点的坐标
- var anchor;
- var rotateStr;
- var wordStr;
- var word;
- var textEl;
- var wXTrans, wYTrans;
- var tips;
- var minValue = 1000;
- that.chordGroups = that.canvas.set();
- that.donutGroups = that.canvas.set();
-
- $(this.node).append(this.floatTag);
-
- //计算某条弦被赋值为target或source的颜色
- var colorCalc = function (index) {
- var i = chords[index].target.value > chords[index].source.value ? chords[index].target.index : chords[index].source.index;
- return i;
- };
-
- //添加透明效果
-
- var mouseOverDonut = function () {
- floatTag.html('
' + this.data('text') + '
');
- floatTag.css({
- "visibility": "visible"
- });
- that.underBn.forEach(function (d) {
- d.hide();
- });
- index = this.data("donutIndex");
- that.chordGroups.forEach(function (d) {
- if (d.data('source') !== index && d.data('target') !== index) {
- d.attr({
- 'fill-opacity': 0.1
- });
- } else {
- d.attr({
- 'fill-opacity': 0.6
- });
- }
- });
- //fade(this.data("donutIndex"), 0.2);
- that.underBn[index].attr('opacity', 0.5).show();
-
- };
-
- var mouseOutDonut = function () {
- floatTag.css({
- "visibility": "hidden"
- });
- index = this.data("donutIndex");
- that.chordGroups.forEach(function (d) {
- if (d.data('source') !== index && d.data('target') !== index) {
- d.attr({
- 'fill-opacity': 0.6
- });
- }
- });
- //fade(this.data("donutIndex"), 0.6);
- that.underBn[index].hide();
- };
-
- var mouseoverChord = function () {
- floatTag.html('' + this.data('text') + '
');
- floatTag.css({
- "visibility": "visible"
- });
- that.underBn.forEach(function (d) {
- d.hide();
- });
- that.chordGroups.forEach(function (d) {
- d.attr("fill-opacity", 0.1);
- });
- if (navigator.appName !== "Microsoft Internet Explorer") {
- this.toFront(); //把当前弦移到画布最上层
- }
- this.attr("fill-opacity", 0.7);
- that.underBn[this.data('source')].attr('opacity', 0.5).show();
- };
-
- var mouseoutChord = function () {
- floatTag.css({
- "visibility": "hidden"
- });
- //alert("***");
- that.chordGroups.forEach(function (d) {
- d.attr("fill-opacity", 0.6);
- });
- //this.attr("fill-opacity", 0.6);
- that.underBn[this.data('source')].hide();
- };
-
- //画弦*********************************************************
- var t;
- for (t = 0; t <= chords.length - 1; t++) {
- //alert(chords.length);
- belt = chordCalc(chords[t]);
- //hover到弦上时的效果
- tips = that.groupNames[chords[t].source.index] + " to " + that.groupNames[chords[t].target.index] + ": " + that.matrix[chords[t].source.index][chords[t].target.index] + "," + that.groupNames[chords[t].target.index] + " to " + that.groupNames[chords[t].source.index] + ": " + that.matrix[chords[t].target.index][chords[t].source.index];
-
- chordEle = that.canvas.path(belt).
- translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).attr({
- "path": belt,
- "fill": that.getColor(colorCalc(t)),
- "fill-opacity": 0.6,
- "stroke": "#d6d6d6",
- "stroke-opacity": 0.1
- }).hover(mouseoverChord, mouseoutChord).data("source", chords[t].source.index).data("target", chords[t].target.index);
- //.attr("fill", fillColor(chords[t].target.index))
- chordEle.data('text', tips);
- that.chordGroups.push(chordEle);
- }
-
-
-
- //画圆弧*********************************************************
- var i, r;
- var donutName;
- var nameStr;
- var nameX, nameY;
- var ro, a;
- var sum = 0;
- for (r = 0; r <= groups.length - 1; r++) {
- sum += groups[r].value;
- }
-
- for (i = 0; i <= groups.length - 1; i++) {
- //画外圈的pie图**************************************
- //计算每个group的path
- spline = pathCalc(groups[i]);
- tips = that.groupNames[i] + ": " + Math.round(groups[i].value) + " " + (groups[i].value * 100 / sum).toFixed(2) + "%";
-
- donutEle = that.canvas.path(spline).translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).data("donutIndex", i).attr({
- "path": spline,
- "fill": that.getColor(i),
- "stroke": that.getColor(i)
- }).mouseover(mouseOverDonut).mouseout(mouseOutDonut);
- donutEle.data('text', tips);
- that.donutGroups.push(donutEle);
-
- //每个donut上显示名称
- ro = groups[i].startAngle * 180 / Math.PI - 86 + 90;
- a = (groups[i].startAngle * 180 / Math.PI - 86) * Math.PI / 180;
- nameX = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.cos(a);
- nameY = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.sin(a);
- nameStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + ro + "T" + nameX + "," + nameY;
-
- if ((groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI > 10) {
- donutName = that.canvas.text().attr("font", "12px Verdana").attr("text", that.groupNames[i]).transform(nameStr);
- }
-
- //画刻度和刻度值**************************************
- num = groups[i].value / 5000;
- //最细分的每个小格代表的数值大小
- unitAngle = (groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI / num;
-
- var j;
- for (j = 0; j <= num; j++) {
- //计算旋转角度和水平、竖直方向所需平移的距离
- radian = ((groups[i].startAngle * 180 / Math.PI - 90) + j * unitAngle);
- angle = radian * Math.PI / 180;
- xTrans = that.defaults.outerRadius * Math.cos(angle);
- yTrans = that.defaults.outerRadius * Math.sin(angle);
-
- tickStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "T" + xTrans + "," + yTrans;
-
- //刻度线的起点终点坐标
- aX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + xTrans;
- aY = that.defaults.height / 2 + yTrans;
- bX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + (that.defaults.outerRadius + 6) * Math.cos(angle);
- bY = that.defaults.height / 2 + (that.defaults.outerRadius + 6) * Math.sin(angle);
-
- tickLine = "M" + aX + "," + aY + "L" + bX + "," + bY;
- that.canvas.path(tickLine).attr({
- 'stroke': "#929292",
- "stroke-width": '1px'
- }); //绘制刻度
-
- //每隔五个刻度,绘制一次文字
- if (j % 2 === 0) {
- //计算text-anchor
- if (radian + 90 < 180) {
- anchor = "start";
- } else {
- anchor = "end";
- }
-
- //计算文字方向是否需要旋转180度
- if (radian + 90 < 180) {
- rotateStr = null;
- } else {
- rotateStr = "R180";
- }
-
- wXTrans = (that.defaults.outerRadius + 10) * Math.cos(angle);
- wYTrans = (that.defaults.outerRadius + 10) * Math.sin(angle);
-
- word = j % 2 ? "" : Math.round(((groups[i].value / num) * j) / 1000);
-
- wordStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + radian
- /*(groups[i].startAngle * 180 / Math.PI - 90)*/ + rotateStr + "T" + wXTrans + "," + wYTrans;
-
- //绘制文字
- textEl = that.canvas.text(0, 0, word).attr("font", "12px Verdana").transform(wordStr).attr("text-anchor", anchor).attr('fill', "#929292");
- }
- }
- }
-
- /*this.canvas.text().attr("font", "12px arial").translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, this.defaults.height).attr("text", "The unit of the scale on the periphery is 1000. \n 刻度值的单位为1000。");
- */
- };
-
- return Chord;
+/*global Raphael, d3, $, define */
+/*!
+ * Chord的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) {
+ return this[id];
+ });
+ }
+})('Chord', function (require) {
+ var DataV = require('DataV');
+
+ /**
+ * 构造函数
+ * @param {Object} node 表示在html的哪个容器中绘制该组件
+ * @param {Object} options 为用户自定义的组件的属性,比如画布大小
+ */
+ var Chord = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Chord";
+ this.node = this.checkContainer(node);
+ this.matrix = [];
+ this.groupNames = []; //数组:记录每个group的名字
+
+ //图的大小设置
+ this.defaults.legend = true;
+ this.defaults.width = 800;
+ this.defaults.height = 800;
+
+ //设置用户指定的属性
+ this.setOptions(options);
+
+ this.legendArea = [20, (this.defaults.height - 20 - 220), 200, 220];
+ if (this.defaults.legend) {
+ this.xOffset = this.legendArea[2];
+ } else {
+ this.xOffset = 0;
+ }
+
+ this.defaults.innerRadius = Math.min((this.defaults.width - this.xOffset), this.defaults.height) * 0.38;
+ this.defaults.outerRadius = this.defaults.innerRadius * 1.10;
+ //创建画布
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * 创建画布
+ */
+ Chord.prototype.createCanvas = function () {
+ this.canvas = new Raphael(this.node, this.defaults.width, this.defaults.height);
+ canvasStyle = this.node.style;
+ canvasStyle.position = "relative";
+ this.floatTag = DataV.FloatTag()(this.node);
+ this.floatTag.css({
+ "visibility": "hidden"
+ });
+ };
+
+ /**
+ * 获取颜色
+ * @param {Number} i 元素类别编号
+ * @return {String} 返回颜色值
+ */
+ Chord.prototype.getColor = function (i) {
+ var color = DataV.getColor();
+ return color[i % color.length][0];
+ };
+
+ /**
+ * 绘制弦图
+ */
+ Chord.prototype.render = function () {
+ this.layout();
+ if (this.defaults.legend) {
+ this.legend();
+ }
+ };
+
+ /**
+ * 绘制图例
+ */
+ Chord.prototype.legend = function () {
+ var that = this;
+ var paper = this.canvas;
+ var legendArea = this.legendArea;
+ var rectBn = paper.set();
+ this.underBn = [];
+ var underBn = this.underBn;
+ for (i = 0; i <= this.groupNum; i++) {
+ //底框
+ underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "#ebebeb",
+ "stroke": "none"
+ //"r": 3
+ }).hide());
+ //色框
+ paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
+ "fill": this.getColor(i),
+ "stroke": "none"
+ });
+ //文字
+ paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groupNames[i]).attr({
+ "fill": "black",
+ "fill-opacity": 1,
+ "font-family": "Verdana",
+ "font-size": 12
+ }).attr({
+ "text-anchor": "start"
+ });
+ //选框
+ rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "white",
+ "fill-opacity": 0,
+ "stroke": "none"
+ //"r": 3
+ })).data("clicked", 0);
+ }
+ rectBn.forEach(function (d, i) {
+ d.mouseover(function () {
+ if (d.data("clicked") === 0) {
+ underBn[i].attr('opacity', 0.5);
+ underBn[i].show();
+ }
+ }).mouseout(function () {
+ if (d.data("clicked") === 0) {
+ underBn[i].hide();
+ }
+ });
+ d.click(function () {
+ for (j = 0; j < underBn.length; j++) {
+ if (j === i) {
+ underBn[j].show();
+ } else {
+ underBn[j].hide();
+ }
+ }
+ rectBn.forEach(function (eachBn) {
+ if (eachBn !== d) {
+ eachBn.data("clicked", 0);
+ }
+ });
+ if (d.data("clicked") === 0) {
+ underBn[i].attr('opacity', 1);
+ underBn[i].show();
+ that.chordGroups.forEach(function (d) {
+ if (d.data('source') !== i && d.data('target') !== i) {
+ d.attr({
+ 'fill-opacity': 0.1
+ });
+ } else {
+ d.attr({
+ 'fill-opacity': 0.6
+ });
+ }
+ });
+
+ d.data("clicked", 1);
+ } else if (d.data("clicked") === 1) {
+ underBn[i].hide();
+ d.data("clicked", 0);
+ that.chordGroups.forEach(function (d) {
+ d.attr({
+ 'fill-opacity': 0.6
+ });
+ });
+ }
+ });
+ });
+ };
+
+ /**
+ *对原始数据进行处理
+ * @param {Array} table 将要被绘制成饼图的二维表数据
+ */
+ Chord.prototype.setSource = function (table) {
+ if (table[0][0] !== null) {
+ var t;
+ for (t = 0; t < table[0].length; t++) {
+ this.groupNames[t] = table[0][t];
+ }
+ table = table.slice(1); // 从第一行开始,第0行舍去
+ }
+
+ var group = [];
+ this.groupNum = table[0].length;
+ var groupNum = this.groupNum;
+ var that = this;
+ table.forEach(function (d, i) {
+ if (d.length !== groupNum || table.length !== groupNum) {
+ throw new Error("The source data should be an n * n matrix!");
+ }
+
+ group[i] = [];
+ var s;
+ for (s = 0; s < groupNum; s++) {
+ group[i][s] = Number(d[s]);
+ }
+ });
+
+ var r;
+ for (r = 0; r < groupNum; r++) {
+ that.matrix[r] = group[r];
+ }
+ };
+
+ /**
+ *创建chord布局
+ */
+ Chord.prototype.layout = function () {
+ var floatTag = this.floatTag;
+ var that = this;
+
+ that.canvas.clear();
+ /*var see = [
+ [11975, 5871, 8916, 2868],
+ [1951, 10048, 2060, 6171],
+ [8010, 16145, 8090, 8045],
+ [1013, 990, 940, 6907]
+ ];*/
+
+ var chordLayout = d3.layout.chord().padding(0.05) //chord segments之间的padding间隔
+ .sortSubgroups(d3.descending) //chord segments细分后的排序规则
+ .matrix(that.matrix);
+
+ /*var fillColor = d3.scale.ordinal()
+ .domain(d3.range(4))
+ .range(["#000000", "#FFDD89", "#957244", "#F26223"]);*/
+
+ //groups数组:获取每个组的起始角度、数值、索引等属性
+ var groups = chordLayout.groups();
+
+ //由内外半径、起始角度计算路径字符串
+ var pathCalc = d3.svg.arc().innerRadius(that.defaults.innerRadius).outerRadius(that.defaults.outerRadius).startAngle(function (d) {
+ return d.startAngle;
+ }).endAngle(function (d) {
+ return d.endAngle;
+ });
+
+ var chords = chordLayout.chords();
+
+ //计算弦的路径曲线
+ var chordCalc = d3.svg.chord().radius(that.defaults.innerRadius);
+
+ //Raphael: Paper.path()
+ var donutEle;
+ //获取每个环形的字符串表示
+ var spline;
+ //表示每条弦的element
+ var chordEle;
+ //每条弦的字符串表示
+ var belt;
+
+ var num; //每个group分割小格数
+ var unitAngle; //每个group所占的角度
+ var angle;
+ var radian;
+ var tickLine;
+ var tickStr; //每个tick的路径
+ var xTrans, yTrans;
+ var aX, aY, bX, bY; //每个tick起始端点的坐标
+ var anchor;
+ var rotateStr;
+ var wordStr;
+ var word;
+ var textEl;
+ var wXTrans, wYTrans;
+ var tips;
+ var minValue = 1000;
+ that.chordGroups = that.canvas.set();
+ that.donutGroups = that.canvas.set();
+
+ $(this.node).append(this.floatTag);
+
+ //计算某条弦被赋值为target或source的颜色
+ var colorCalc = function (index) {
+ var i = chords[index].target.value > chords[index].source.value ? chords[index].target.index : chords[index].source.index;
+ return i;
+ };
+
+ //添加透明效果
+
+ var mouseOverDonut = function () {
+ floatTag.html('' + this.data('text') + '
');
+ floatTag.css({
+ "visibility": "visible"
+ });
+ that.underBn.forEach(function (d) {
+ d.hide();
+ });
+ index = this.data("donutIndex");
+ that.chordGroups.forEach(function (d) {
+ if (d.data('source') !== index && d.data('target') !== index) {
+ d.attr({
+ 'fill-opacity': 0.1
+ });
+ } else {
+ d.attr({
+ 'fill-opacity': 0.6
+ });
+ }
+ });
+ //fade(this.data("donutIndex"), 0.2);
+ that.underBn[index].attr('opacity', 0.5).show();
+
+ };
+
+ var mouseOutDonut = function () {
+ floatTag.css({
+ "visibility": "hidden"
+ });
+ index = this.data("donutIndex");
+ that.chordGroups.forEach(function (d) {
+ if (d.data('source') !== index && d.data('target') !== index) {
+ d.attr({
+ 'fill-opacity': 0.6
+ });
+ }
+ });
+ //fade(this.data("donutIndex"), 0.6);
+ that.underBn[index].hide();
+ };
+
+ var mouseoverChord = function () {
+ floatTag.html('' + this.data('text') + '
');
+ floatTag.css({
+ "visibility": "visible"
+ });
+ that.underBn.forEach(function (d) {
+ d.hide();
+ });
+ that.chordGroups.forEach(function (d) {
+ d.attr("fill-opacity", 0.1);
+ });
+ if (navigator.appName !== "Microsoft Internet Explorer") {
+ this.toFront(); //把当前弦移到画布最上层
+ }
+ this.attr("fill-opacity", 0.7);
+ that.underBn[this.data('source')].attr('opacity', 0.5).show();
+ };
+
+ var mouseoutChord = function () {
+ floatTag.css({
+ "visibility": "hidden"
+ });
+ //alert("***");
+ that.chordGroups.forEach(function (d) {
+ d.attr("fill-opacity", 0.6);
+ });
+ //this.attr("fill-opacity", 0.6);
+ that.underBn[this.data('source')].hide();
+ };
+
+ //画弦*********************************************************
+ var t;
+ for (t = 0; t <= chords.length - 1; t++) {
+ //alert(chords.length);
+ belt = chordCalc(chords[t]);
+ //hover到弦上时的效果
+ tips = that.groupNames[chords[t].source.index] + " to " + that.groupNames[chords[t].target.index] + ": " + that.matrix[chords[t].source.index][chords[t].target.index] + "," + that.groupNames[chords[t].target.index] + " to " + that.groupNames[chords[t].source.index] + ": " + that.matrix[chords[t].target.index][chords[t].source.index];
+
+ chordEle = that.canvas.path(belt).
+ translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).attr({
+ "path": belt,
+ "fill": that.getColor(colorCalc(t)),
+ "fill-opacity": 0.6,
+ "stroke": "#d6d6d6",
+ "stroke-opacity": 0.1
+ }).hover(mouseoverChord, mouseoutChord).data("source", chords[t].source.index).data("target", chords[t].target.index);
+ //.attr("fill", fillColor(chords[t].target.index))
+ chordEle.data('text', tips);
+ that.chordGroups.push(chordEle);
+ }
+
+
+
+ //画圆弧*********************************************************
+ var i, r;
+ var donutName;
+ var nameStr;
+ var nameX, nameY;
+ var ro, a;
+ var sum = 0;
+ for (r = 0; r <= groups.length - 1; r++) {
+ sum += groups[r].value;
+ }
+
+ for (i = 0; i <= groups.length - 1; i++) {
+ //画外圈的pie图**************************************
+ //计算每个group的path
+ spline = pathCalc(groups[i]);
+ tips = that.groupNames[i] + ": " + Math.round(groups[i].value) + " " + (groups[i].value * 100 / sum).toFixed(2) + "%";
+
+ donutEle = that.canvas.path(spline).translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, that.defaults.height / 2).data("donutIndex", i).attr({
+ "path": spline,
+ "fill": that.getColor(i),
+ "stroke": that.getColor(i)
+ }).mouseover(mouseOverDonut).mouseout(mouseOutDonut);
+ donutEle.data('text', tips);
+ that.donutGroups.push(donutEle);
+
+ //每个donut上显示名称
+ ro = groups[i].startAngle * 180 / Math.PI - 86 + 90;
+ a = (groups[i].startAngle * 180 / Math.PI - 86) * Math.PI / 180;
+ nameX = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.cos(a);
+ nameY = ((that.defaults.outerRadius - that.defaults.innerRadius) / 2 + that.defaults.innerRadius) * Math.sin(a);
+ nameStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + ro + "T" + nameX + "," + nameY;
+
+ if ((groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI > 10) {
+ donutName = that.canvas.text().attr("font", "12px Verdana").attr("text", that.groupNames[i]).transform(nameStr);
+ }
+
+ //画刻度和刻度值**************************************
+ num = groups[i].value / 5000;
+ //最细分的每个小格代表的数值大小
+ unitAngle = (groups[i].endAngle - groups[i].startAngle) * 180 / Math.PI / num;
+
+ var j;
+ for (j = 0; j <= num; j++) {
+ //计算旋转角度和水平、竖直方向所需平移的距离
+ radian = ((groups[i].startAngle * 180 / Math.PI - 90) + j * unitAngle);
+ angle = radian * Math.PI / 180;
+ xTrans = that.defaults.outerRadius * Math.cos(angle);
+ yTrans = that.defaults.outerRadius * Math.sin(angle);
+
+ tickStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "T" + xTrans + "," + yTrans;
+
+ //刻度线的起点终点坐标
+ aX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + xTrans;
+ aY = that.defaults.height / 2 + yTrans;
+ bX = ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + (that.defaults.outerRadius + 6) * Math.cos(angle);
+ bY = that.defaults.height / 2 + (that.defaults.outerRadius + 6) * Math.sin(angle);
+
+ tickLine = "M" + aX + "," + aY + "L" + bX + "," + bY;
+ that.canvas.path(tickLine).attr({
+ 'stroke': "#929292",
+ "stroke-width": '1px'
+ }); //绘制刻度
+
+ //每隔五个刻度,绘制一次文字
+ if (j % 2 === 0) {
+ //计算text-anchor
+ if (radian + 90 < 180) {
+ anchor = "start";
+ } else {
+ anchor = "end";
+ }
+
+ //计算文字方向是否需要旋转180度
+ if (radian + 90 < 180) {
+ rotateStr = null;
+ } else {
+ rotateStr = "R180";
+ }
+
+ wXTrans = (that.defaults.outerRadius + 10) * Math.cos(angle);
+ wYTrans = (that.defaults.outerRadius + 10) * Math.sin(angle);
+
+ word = j % 2 ? "" : Math.round(((groups[i].value / num) * j) / 1000);
+
+ wordStr = "T" + ((that.defaults.width - that.xOffset) / 2 + that.xOffset) + "," + that.defaults.height / 2 + "R" + radian
+ /*(groups[i].startAngle * 180 / Math.PI - 90)*/ + rotateStr + "T" + wXTrans + "," + wYTrans;
+
+ //绘制文字
+ textEl = that.canvas.text(0, 0, word).attr("font", "12px Verdana").transform(wordStr).attr("text-anchor", anchor).attr('fill', "#929292");
+ }
+ }
+ }
+
+ /*this.canvas.text().attr("font", "12px arial").translate((that.defaults.width - this.xOffset) / 2 + this.xOffset, this.defaults.height).attr("text", "The unit of the scale on the periphery is 1000. \n 刻度值的单位为1000。");
+ */
+ };
+
+ return Chord;
});
\ No newline at end of file
diff --git a/lib/charts/column.js b/lib/charts/column.js
index 6236cd0..6c0de8c 100644
--- a/lib/charts/column.js
+++ b/lib/charts/column.js
@@ -1,510 +1,510 @@
-/*global Raphael, d3, $, define, _ */
-/*!
- * Column图的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Column', function (require) {
- var DataV = require('DataV');
-
- /**
- * Column构造函数
- * Creates Column in a DOM node with id "chart", default width is 522; height is 522px;
- * Options:
- *
- * - `width` 宽度,默认为节点宽度
- * - `yBase` 纵坐标的基线值,有的以0为起始值,有的则以数据中的最小值为起始值
- * - `gap` 组与组之间的缝隙宽度
- *
- * Examples:
- * ```
- * var column = new Column("chart", {"width": 500, "height": 600, "typeNames": ["Y", "Z"]});
- * ```
- * @param {Mix} node The dom node or dom node Id
- * @param {Object} options options json object for determin column style.
- */
- var Column = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Column";
- this.node = this.checkContainer(node);
-
- /**
- * 柱纬度
- */
- this.dimension.column = {
- type: "string",
- required: true,
- index: 0
- };
- /**
- * 横向纬度
- */
- this.dimension.x = {
- type: "string",
- required: true,
- index: 1
- };
- /**
- * 值纬度
- */
- this.dimension.value = {
- type: "number",
- required: true,
- index: 2
- };
-
- this.defaults.typeNames = [];
- // canvas parameters
- this.defaults.width = 522;
- this.defaults.height = 522;
- this.defaults.margin = 50;
- this.defaults.gap = 15;
- this.defaults.circleR = 3;
- this.defaults.barColor = ["#308BE6","#8EEC00","#DDDF0D"];
- this.defaults.xTickNumber = 5;
- this.defaults.yTickNumber = 5;
-
- this.defaults.yBase = undefined;
-
- //图例区域的左上顶点坐标x,y,宽,高
- this.defaults.legendArea = [422, 50, 472, 220];
- //散点矩阵区域的左上顶点坐标x,y,宽,高
- this.defaults.diagramArea = [50, 50, 422, 472];
- this.columnSet = [];
-
- this.setOptions(options);
- this.createCanvas();
- this.initEvents();
- }
- });
-
- /**
- * 创建画布
- */
- Column.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.node.style.position = "relative";
- this.canvas = new Raphael(this.node, conf.width, conf.height);
- };
-
- Column.prototype.initEvents = function () {
- var that = this;
- this.on('legendOver', function (columnIndex) {
- that.columnSet.forEach(function (set, index) {
- if (index !== columnIndex) {
- set.attr({
- "fill-opacity": 0.3
- });
- }
- });
- });
-
- this.on('legendOut', function (columnIndex) {
- that.columnSet.forEach(function (set, index) {
- set.attr({
- "fill-opacity": 1
- });
- });
- });
-
- this.on('legendClick', function (clicked, columnIndex) {
- that.clicked = clicked;
- that.clickedColumnIndex = columnIndex;
- that.columnSet.forEach(function (set, index) {
- if (index !== columnIndex) {
- if (clicked) {
- set.attr({"fill-opacity": 0.1});
- } else {
- set.attr({"fill-opacity": 0.5});
- }
- } else {
- set.attr({"fill-opacity": 1});
- }
- });
- });
- };
-
- /**
- * 设置数据源
- * Examples:
- * ```
- * column.setSource(source);
- * ```
- * @param {Array} source 数据源 第一列为排布在x轴的数据,后n列为排布在y轴的数据
- */
- Column.prototype.setSource = function (source, map) {
- var conf = this.defaults;
- map = this.map(map);
- var dataTable;
- if (DataV.detect(source) === 'Table_WITH_HEAD') {
- dataTable = DataV.collectionify(source);
- } else {
- dataTable = source;
- }
- this.columns = _.groupBy(dataTable, map.column);
- this.columnCount = _.keys(this.columns).length;
-
- conf.xAxisData = _.pluck(_.first(_.values(this.columns)), map.x);
- conf.xTickNumber = Math.min(conf.xAxisData.length, conf.xTickNumber);
- // 纵坐标的范围
- conf.yExtent = d3.extent(dataTable, function (item) {
- return item[map.value];
- });
- // 纵坐标基线值
- if (conf.yBase !== undefined) {
- conf.yExtent.push(conf.yBase);
- conf.yExtent = d3.extent(conf.yExtent);
- }
- };
-
- /**
- * 设置坐标轴
- */
- Column.prototype.setAxis = function () {
- var conf = this.defaults;
- var tagWidth = conf.width / 5 > 50 ? 50 : conf.width / 5;
- conf.legendArea = [conf.width - tagWidth - conf.margin, 0, conf.width, conf.height];
- conf.diagramArea = [0, 0, conf.width - tagWidth - conf.margin, conf.height];
- var w = conf.diagramArea[2] - 2 * conf.margin;
- var h = conf.diagramArea[3] - conf.margin;
-
- //设置x轴
- this.x = d3.scale.linear().domain([0, conf.xAxisData.length]).range([conf.margin, w]);
- //设置y轴
- this.value = d3.scale.linear().domain(conf.yExtent).range([h, conf.margin]);
- var xRange = this.x.range();
- var valueRange = this.value.range();
- var axis = this.axisPosition = {
- left: xRange[0],
- right: xRange[1],
- up: valueRange[1],
- down: valueRange[0]
- };
- var columnsMaxLen = _.max(this.columns, function (column) {
- return column.length;
- }).length;
- this.barWidth = (axis.right - axis.left - columnsMaxLen * conf.gap) / columnsMaxLen / _.keys(this.columns).length;
- };
-
- /**
- * 绘制坐标
- */
- Column.prototype.drawAxis = function () {
- var that = this;
- var conf = this.defaults;
- var paper = this.canvas;
- var i, j, k, l;
- //画坐标轴
- var axisLines = paper.set();
- var tickText = paper.set();
- var axis = this.axisPosition;
- var ticks;
- // X轴
- ticks = this.x.ticks(conf.xTickNumber);
- console.log(ticks);
- var range = this.x.range();
-
- // 修复显示不从第一个x轴单位显示的bug
- for (j = 0; j < ticks.length; j++) {
- // 修改x轴单位显示在所有Column组的中间位置
- // 修复x轴单位对于柱位置的偏移
- var x = this.x(ticks[j]) + conf.gap / 2 + this.columnCount * Math.floor(this.barWidth) / 2;
- tickText.push(paper.text(x, axis.down + 14, conf.xAxisData[ticks[j]]).rotate(0, x, axis.up));
- axisLines.push(paper.path("M" + x + "," + axis.down + "L" + x + "," + (axis.down + 5)));
- }
-
- tickText.attr({
- "fill": "#878791",
- "fill-opacity": 0.7,
- "font-size": 12,
- "text-anchor": "middle"
- });
-
- axisLines.push(paper.path("M" + axis.left + "," + axis.up + "L" + axis.left + "," + axis.down));
- axisLines.attr({
- "stroke": "#D7D7D7",
- "stroke-width": 2
- });
- //Y轴
- ticks = this.value.ticks(conf.yTickNumber);
- for (j = 0; j < ticks.length; j++) {
- tickText.push(paper.text(axis.left - 8, this.value(ticks[j]), ticks[j]).attr({
- "fill": "#878791",
- "fill-opacity": 0.7,
- "font-size": 12,
- "text-anchor": "end"
- }).rotate(0, axis.right + 6, this.value(ticks[j])));
- axisLines.push(paper.path("M" + axis.left + "," + this.value(ticks[j]) + "L" + (axis.left - 5) + "," + this.value(ticks[j])));
- }
- axisLines.push(paper.path("M" + axis.left + "," + axis.down + "L" + axis.right + "," + axis.down));
- axisLines.attr({
- "stroke": "#D7D7D7",
- "stroke-width": 2
- });
-
- var numOfHLine = d3.round((axis.down - axis.up) / 30 - 1);
- var hLines = paper.set();
- for (j = 1; j <= numOfHLine; j++) {
- var hLinesPos = axis.down - j * 30;
- hLines.push(paper.path("M" + axis.left + "," + hLinesPos + "L" + axis.right + "," + hLinesPos));
- }
- hLines.attr({
- "stroke": "#ECECEC",
- "stroke-width": 1
- });
- };
-
- /**
- * 进行柱状图的绘制
- */
- Column.prototype.drawDiagram = function () {
- var that = this;
- var conf = this.defaults;
- var axis = this.axisPosition;
- var paper = this.canvas;
- var dim = that.dimension;
- //bars
- var barWidth = this.barWidth;
- var columnCount = this.columnCount;
- var columnSet = this.columnSet;
- var values = _.values(this.columns);
- var tagSet = paper.set();
-
- //bars
- var mouseOverBar = function (event) {
- var columnIndex = this.data('column');
- var xIndex = this.data('index');
- if (that.clicked && that.clickedColumnIndex !== columnIndex) {
- return;
- }
- tagSet.remove();
- var currentSet = columnSet.filter(function (set, columnIndex) {
- return that.clicked ? that.clickedColumnIndex === columnIndex : true;
- });
- currentSet.forEach(function (set, columnIndex) {
- set.animate({
- "fill-opacity": 0.3
- }, 10);
- set[xIndex].animate({
- "fill-opacity":1
- }, 10);
- });
-
- var hovered = currentSet.map(function (set) {
- return set[xIndex];
- });
- var xPos = _.max(hovered, function (item) {
- return item.attrs.x;
- }).attrs.x + barWidth + 8;
-
- var y = _.map(hovered, function (item) {
- return item.attrs.y;
- });
- // TODO: 防遮罩算法
- for (var i = 1; i < y.length; i++) {
- for (var j = i - 1; j >= 0; j--) {
- var overlapped = y.filter(function (item, index) {
- return index < i && Math.abs(item - y[i]) < 20;
- });
- if (overlapped.length > 0) {
- var extent = d3.extent(overlapped);
- if (y[i] <= extent[0]) {
- y[i] = extent[0] - 20;
- } else {
- y[i] = extent[1] + 20;
- }
- }
- }
- }
- hovered.forEach(function (item, columnIndex) {
- var yPos = y[columnIndex];
- var valueLabel = '' + values[columnIndex][xIndex][dim.value.index];
- var textWidth = 5 * valueLabel.length + 20;
-
- var rect = paper.rect(xPos, yPos - 10, textWidth, 20, 2).attr({
- "fill": conf.barColor[columnIndex],
- "fill-opacity": 1,
- "stroke": "none"
- });
- var path = paper.path("M" + xPos + "," + (yPos - 4) + "L" + (xPos - 8) + "," + yPos + "L" + xPos + "," + (yPos + 4) + "V" + yPos + "Z").attr({
- "fill" : conf.barColor[columnIndex],
- "stroke" : conf.barColor[columnIndex]
- });
- var text = paper.text(xPos + 16, yPos, valueLabel).attr({
- "fill": "#ffffff",
- "fill-opacity": 1,
- "font-weight": "bold",
- "font-size": 12,
- "text-anchor": "middle"
- });
- tagSet.push(rect, path, text);
- });
-
- xPos = hovered.reduce(function (pre, cur) {
- return pre + cur.attrs.x;
- }, 0) / hovered.length + barWidth / 2;
- var xLabel = '' + values[columnIndex][xIndex][dim.x.index];
- var textWidth = 6 * xLabel.length + 20;
- //axis x rect
- var rect = paper.rect(xPos - textWidth / 2, axis.down + 8, textWidth, 20, 2).attr({
- "fill": "#5f5f5f",
- "fill-opacity": 1,
- "stroke": "none"
- });
- // axis x text
- var text = paper.text(xPos, axis.down + 18, xLabel).attr({
- "fill": "#ffffff",
- "fill-opacity": 1,
- "font-weight": "bold",
- "font-size": 12,
- "text-anchor": "middle"
- });
- var arrow = paper.path("M" + (xPos - 4) + "," + (axis.down + 8) + "L" + xPos + "," + axis.down +
- "L" + (xPos + 4) + "," + (axis.down + 8) + "H" + xPos + "Z").attr({
- "fill": "#5F5F5F",
- "stroke": "#5F5F5F"
- });
- tagSet.push(rect, text, arrow);
- };
-
- var mouseOutBar = function (event) {
- var columnIndex = this.data('column');
- var xIndex = this.data('index');
- var currentSet = columnSet.filter(function (set, columnIndex) {
- return that.clicked ? that.clickedColumnIndex === columnIndex : true;
- });
- tagSet.animate({"opacity": 0}, 1000, function () {
- tagSet.remove();
- });
- currentSet.forEach(function (set, columnIndex) {
- set.attr({"fill-opacity": 1});
- });
- };
-
- values.forEach(function (column, index) {
- columnSet[index] = paper.set();
- column.forEach(function (row, i) {
- var value = row[dim.value.index];
- var height = that.value(value);
- var x = that.x(i);
- var rect = paper.rect(x + barWidth * index + conf.gap / 2, height, barWidth, axis.down - height).attr({
- "fill": conf.barColor[index],
- "fill-opacity": 1,
- "stroke": "none"
- });
- rect.data('column', index).data('index', i);
- rect.mouseover(mouseOverBar);
- rect.mouseout(mouseOutBar);
- columnSet[index].push(rect);
- });
- });
- };
-
- /**
- * 绘制图例
- */
- Column.prototype.drawLegend = function () {
- var that = this;
- var paper = this.canvas;
- var legendSet = paper.set();
- var bgSet = paper.set();
- var conf = this.defaults;
- var legendArea = conf.legendArea;
- var columnCount = this.columnCount;
- //legend
- var mouseOverLegend = function (event) {
- if (legendSet.clicked) {
- return;
- }
- bgSet[this.data('type')].attr({
- "fill-opacity":0.5
- });
- that.fire('legendOver', this.data('type'));
- };
-
- var mouseOutLegend = function (event) {
- if (legendSet.clicked) {
- return;
- }
- bgSet[this.data('type')].attr({"fill-opacity": 0});
- that.fire('legendOut', this.data('type'));
- };
-
- var clickLegend = function (event) {
- if (legendSet.clicked && legendSet.clickedColumn === this.data('type')) {
- legendSet.clicked = false;
- } else {
- legendSet.clicked = true;
- legendSet.clickedColumn = this.data('type');
- }
- bgSet.attr({"fill-opacity": 0});
- bgSet[this.data('type')].attr({
- "fill-opacity": legendSet.clicked ? 1 : 0
- });
- that.fire('legendClick', legendSet.clicked, this.data('type'));
- };
-
- var labels = _.keys(this.columns);
- for (var i = 0; i < labels.length; i++) {
- //底框
- bgSet.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "#ebebeb",
- "fill-opacity": 0,
- "stroke": "none"
- }));
- // 色框
- paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
- "fill": conf.barColor[i],
- "stroke": "none"
- });
- // 文字
- paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, labels[i]).attr({
- "fill": "black",
- "fill-opacity": 1,
- "font-family": "Verdana",
- "font-size": 12,
- "text-anchor": "start"
- });
- // 选框
- var rect = paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "white",
- "fill-opacity": 0,
- "stroke": "none"
- }).data("type", i);
- rect.mouseover(mouseOverLegend);
- rect.mouseout(mouseOutLegend);
- rect.click(clickLegend);
- legendSet.push(rect);
- }
- };
-
- /**
- * 绘制柱状图
- * Options:
- *
- * - `width` 宽度,默认为节点宽度
- * - `typeNames` 指定y轴上数据类目
- *
- * Examples:
- * ```
- * column.render({"width": 1024})
- * ```
- * @param {Object} options options json object for determin column style.
- */
- Column.prototype.render = function (options) {
- this.setOptions(options);
- this.canvas.clear();
- this.setAxis();
- this.drawAxis();
- this.drawDiagram();
- this.drawLegend();
- };
- /*!
- * 导出
- */
- return Column;
-});
+/*global Raphael, d3, $, define, _ */
+/*!
+ * Column图的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Column', function (require) {
+ var DataV = require('DataV');
+
+ /**
+ * Column构造函数
+ * Creates Column in a DOM node with id "chart", default width is 522; height is 522px;
+ * Options:
+ *
+ * - `width` 宽度,默认为节点宽度
+ * - `yBase` 纵坐标的基线值,有的以0为起始值,有的则以数据中的最小值为起始值
+ * - `gap` 组与组之间的缝隙宽度
+ *
+ * Examples:
+ * ```
+ * var column = new Column("chart", {"width": 500, "height": 600, "typeNames": ["Y", "Z"]});
+ * ```
+ * @param {Mix} node The dom node or dom node Id
+ * @param {Object} options options json object for determin column style.
+ */
+ var Column = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Column";
+ this.node = this.checkContainer(node);
+
+ /**
+ * 柱纬度
+ */
+ this.dimension.column = {
+ type: "string",
+ required: true,
+ index: 0
+ };
+ /**
+ * 横向纬度
+ */
+ this.dimension.x = {
+ type: "string",
+ required: true,
+ index: 1
+ };
+ /**
+ * 值纬度
+ */
+ this.dimension.value = {
+ type: "number",
+ required: true,
+ index: 2
+ };
+
+ this.defaults.typeNames = [];
+ // canvas parameters
+ this.defaults.width = 522;
+ this.defaults.height = 522;
+ this.defaults.margin = 50;
+ this.defaults.gap = 15;
+ this.defaults.circleR = 3;
+ this.defaults.barColor = ["#308BE6","#8EEC00","#DDDF0D"];
+ this.defaults.xTickNumber = 5;
+ this.defaults.yTickNumber = 5;
+
+ this.defaults.yBase = undefined;
+
+ //图例区域的左上顶点坐标x,y,宽,高
+ this.defaults.legendArea = [422, 50, 472, 220];
+ //散点矩阵区域的左上顶点坐标x,y,宽,高
+ this.defaults.diagramArea = [50, 50, 422, 472];
+ this.columnSet = [];
+
+ this.setOptions(options);
+ this.createCanvas();
+ this.initEvents();
+ }
+ });
+
+ /**
+ * 创建画布
+ */
+ Column.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.node.style.position = "relative";
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+ };
+
+ Column.prototype.initEvents = function () {
+ var that = this;
+ this.on('legendOver', function (columnIndex) {
+ that.columnSet.forEach(function (set, index) {
+ if (index !== columnIndex) {
+ set.attr({
+ "fill-opacity": 0.3
+ });
+ }
+ });
+ });
+
+ this.on('legendOut', function (columnIndex) {
+ that.columnSet.forEach(function (set, index) {
+ set.attr({
+ "fill-opacity": 1
+ });
+ });
+ });
+
+ this.on('legendClick', function (clicked, columnIndex) {
+ that.clicked = clicked;
+ that.clickedColumnIndex = columnIndex;
+ that.columnSet.forEach(function (set, index) {
+ if (index !== columnIndex) {
+ if (clicked) {
+ set.attr({"fill-opacity": 0.1});
+ } else {
+ set.attr({"fill-opacity": 0.5});
+ }
+ } else {
+ set.attr({"fill-opacity": 1});
+ }
+ });
+ });
+ };
+
+ /**
+ * 设置数据源
+ * Examples:
+ * ```
+ * column.setSource(source);
+ * ```
+ * @param {Array} source 数据源 第一列为排布在x轴的数据,后n列为排布在y轴的数据
+ */
+ Column.prototype.setSource = function (source, map) {
+ var conf = this.defaults;
+ map = this.map(map);
+ var dataTable;
+ if (DataV.detect(source) === 'Table_WITH_HEAD') {
+ dataTable = DataV.collectionify(source);
+ } else {
+ dataTable = source;
+ }
+ this.columns = _.groupBy(dataTable, map.column);
+ this.columnCount = _.keys(this.columns).length;
+
+ conf.xAxisData = _.pluck(_.first(_.values(this.columns)), map.x);
+ conf.xTickNumber = Math.min(conf.xAxisData.length, conf.xTickNumber);
+ // 纵坐标的范围
+ conf.yExtent = d3.extent(dataTable, function (item) {
+ return item[map.value];
+ });
+ // 纵坐标基线值
+ if (conf.yBase !== undefined) {
+ conf.yExtent.push(conf.yBase);
+ conf.yExtent = d3.extent(conf.yExtent);
+ }
+ };
+
+ /**
+ * 设置坐标轴
+ */
+ Column.prototype.setAxis = function () {
+ var conf = this.defaults;
+ var tagWidth = conf.width / 5 > 50 ? 50 : conf.width / 5;
+ conf.legendArea = [conf.width - tagWidth - conf.margin, 0, conf.width, conf.height];
+ conf.diagramArea = [0, 0, conf.width - tagWidth - conf.margin, conf.height];
+ var w = conf.diagramArea[2] - 2 * conf.margin;
+ var h = conf.diagramArea[3] - conf.margin;
+
+ //设置x轴
+ this.x = d3.scale.linear().domain([0, conf.xAxisData.length]).range([conf.margin, w]);
+ //设置y轴
+ this.value = d3.scale.linear().domain(conf.yExtent).range([h, conf.margin]);
+ var xRange = this.x.range();
+ var valueRange = this.value.range();
+ var axis = this.axisPosition = {
+ left: xRange[0],
+ right: xRange[1],
+ up: valueRange[1],
+ down: valueRange[0]
+ };
+ var columnsMaxLen = _.max(this.columns, function (column) {
+ return column.length;
+ }).length;
+ this.barWidth = (axis.right - axis.left - columnsMaxLen * conf.gap) / columnsMaxLen / _.keys(this.columns).length;
+ };
+
+ /**
+ * 绘制坐标
+ */
+ Column.prototype.drawAxis = function () {
+ var that = this;
+ var conf = this.defaults;
+ var paper = this.canvas;
+ var i, j, k, l;
+ //画坐标轴
+ var axisLines = paper.set();
+ var tickText = paper.set();
+ var axis = this.axisPosition;
+ var ticks;
+ // X轴
+ ticks = this.x.ticks(conf.xTickNumber);
+ console.log(ticks);
+ var range = this.x.range();
+
+ // 修复显示不从第一个x轴单位显示的bug
+ for (j = 0; j < ticks.length; j++) {
+ // 修改x轴单位显示在所有Column组的中间位置
+ // 修复x轴单位对于柱位置的偏移
+ var x = this.x(ticks[j]) + conf.gap / 2 + this.columnCount * Math.floor(this.barWidth) / 2;
+ tickText.push(paper.text(x, axis.down + 14, conf.xAxisData[ticks[j]]).rotate(0, x, axis.up));
+ axisLines.push(paper.path("M" + x + "," + axis.down + "L" + x + "," + (axis.down + 5)));
+ }
+
+ tickText.attr({
+ "fill": "#878791",
+ "fill-opacity": 0.7,
+ "font-size": 12,
+ "text-anchor": "middle"
+ });
+
+ axisLines.push(paper.path("M" + axis.left + "," + axis.up + "L" + axis.left + "," + axis.down));
+ axisLines.attr({
+ "stroke": "#D7D7D7",
+ "stroke-width": 2
+ });
+ //Y轴
+ ticks = this.value.ticks(conf.yTickNumber);
+ for (j = 0; j < ticks.length; j++) {
+ tickText.push(paper.text(axis.left - 8, this.value(ticks[j]), ticks[j]).attr({
+ "fill": "#878791",
+ "fill-opacity": 0.7,
+ "font-size": 12,
+ "text-anchor": "end"
+ }).rotate(0, axis.right + 6, this.value(ticks[j])));
+ axisLines.push(paper.path("M" + axis.left + "," + this.value(ticks[j]) + "L" + (axis.left - 5) + "," + this.value(ticks[j])));
+ }
+ axisLines.push(paper.path("M" + axis.left + "," + axis.down + "L" + axis.right + "," + axis.down));
+ axisLines.attr({
+ "stroke": "#D7D7D7",
+ "stroke-width": 2
+ });
+
+ var numOfHLine = d3.round((axis.down - axis.up) / 30 - 1);
+ var hLines = paper.set();
+ for (j = 1; j <= numOfHLine; j++) {
+ var hLinesPos = axis.down - j * 30;
+ hLines.push(paper.path("M" + axis.left + "," + hLinesPos + "L" + axis.right + "," + hLinesPos));
+ }
+ hLines.attr({
+ "stroke": "#ECECEC",
+ "stroke-width": 1
+ });
+ };
+
+ /**
+ * 进行柱状图的绘制
+ */
+ Column.prototype.drawDiagram = function () {
+ var that = this;
+ var conf = this.defaults;
+ var axis = this.axisPosition;
+ var paper = this.canvas;
+ var dim = that.dimension;
+ //bars
+ var barWidth = this.barWidth;
+ var columnCount = this.columnCount;
+ var columnSet = this.columnSet;
+ var values = _.values(this.columns);
+ var tagSet = paper.set();
+
+ //bars
+ var mouseOverBar = function (event) {
+ var columnIndex = this.data('column');
+ var xIndex = this.data('index');
+ if (that.clicked && that.clickedColumnIndex !== columnIndex) {
+ return;
+ }
+ tagSet.remove();
+ var currentSet = columnSet.filter(function (set, columnIndex) {
+ return that.clicked ? that.clickedColumnIndex === columnIndex : true;
+ });
+ currentSet.forEach(function (set, columnIndex) {
+ set.animate({
+ "fill-opacity": 0.3
+ }, 10);
+ set[xIndex].animate({
+ "fill-opacity":1
+ }, 10);
+ });
+
+ var hovered = currentSet.map(function (set) {
+ return set[xIndex];
+ });
+ var xPos = _.max(hovered, function (item) {
+ return item.attrs.x;
+ }).attrs.x + barWidth + 8;
+
+ var y = _.map(hovered, function (item) {
+ return item.attrs.y;
+ });
+ // TODO: 防遮罩算法
+ for (var i = 1; i < y.length; i++) {
+ for (var j = i - 1; j >= 0; j--) {
+ var overlapped = y.filter(function (item, index) {
+ return index < i && Math.abs(item - y[i]) < 20;
+ });
+ if (overlapped.length > 0) {
+ var extent = d3.extent(overlapped);
+ if (y[i] <= extent[0]) {
+ y[i] = extent[0] - 20;
+ } else {
+ y[i] = extent[1] + 20;
+ }
+ }
+ }
+ }
+ hovered.forEach(function (item, columnIndex) {
+ var yPos = y[columnIndex];
+ var valueLabel = '' + values[columnIndex][xIndex][dim.value.index];
+ var textWidth = 5 * valueLabel.length + 20;
+
+ var rect = paper.rect(xPos, yPos - 10, textWidth, 20, 2).attr({
+ "fill": conf.barColor[columnIndex],
+ "fill-opacity": 1,
+ "stroke": "none"
+ });
+ var path = paper.path("M" + xPos + "," + (yPos - 4) + "L" + (xPos - 8) + "," + yPos + "L" + xPos + "," + (yPos + 4) + "V" + yPos + "Z").attr({
+ "fill" : conf.barColor[columnIndex],
+ "stroke" : conf.barColor[columnIndex]
+ });
+ var text = paper.text(xPos + 16, yPos, valueLabel).attr({
+ "fill": "#ffffff",
+ "fill-opacity": 1,
+ "font-weight": "bold",
+ "font-size": 12,
+ "text-anchor": "middle"
+ });
+ tagSet.push(rect, path, text);
+ });
+
+ xPos = hovered.reduce(function (pre, cur) {
+ return pre + cur.attrs.x;
+ }, 0) / hovered.length + barWidth / 2;
+ var xLabel = '' + values[columnIndex][xIndex][dim.x.index];
+ var textWidth = 6 * xLabel.length + 20;
+ //axis x rect
+ var rect = paper.rect(xPos - textWidth / 2, axis.down + 8, textWidth, 20, 2).attr({
+ "fill": "#5f5f5f",
+ "fill-opacity": 1,
+ "stroke": "none"
+ });
+ // axis x text
+ var text = paper.text(xPos, axis.down + 18, xLabel).attr({
+ "fill": "#ffffff",
+ "fill-opacity": 1,
+ "font-weight": "bold",
+ "font-size": 12,
+ "text-anchor": "middle"
+ });
+ var arrow = paper.path("M" + (xPos - 4) + "," + (axis.down + 8) + "L" + xPos + "," + axis.down +
+ "L" + (xPos + 4) + "," + (axis.down + 8) + "H" + xPos + "Z").attr({
+ "fill": "#5F5F5F",
+ "stroke": "#5F5F5F"
+ });
+ tagSet.push(rect, text, arrow);
+ };
+
+ var mouseOutBar = function (event) {
+ var columnIndex = this.data('column');
+ var xIndex = this.data('index');
+ var currentSet = columnSet.filter(function (set, columnIndex) {
+ return that.clicked ? that.clickedColumnIndex === columnIndex : true;
+ });
+ tagSet.animate({"opacity": 0}, 1000, function () {
+ tagSet.remove();
+ });
+ currentSet.forEach(function (set, columnIndex) {
+ set.attr({"fill-opacity": 1});
+ });
+ };
+
+ values.forEach(function (column, index) {
+ columnSet[index] = paper.set();
+ column.forEach(function (row, i) {
+ var value = row[dim.value.index];
+ var height = that.value(value);
+ var x = that.x(i);
+ var rect = paper.rect(x + barWidth * index + conf.gap / 2, height, barWidth, axis.down - height).attr({
+ "fill": conf.barColor[index],
+ "fill-opacity": 1,
+ "stroke": "none"
+ });
+ rect.data('column', index).data('index', i);
+ rect.mouseover(mouseOverBar);
+ rect.mouseout(mouseOutBar);
+ columnSet[index].push(rect);
+ });
+ });
+ };
+
+ /**
+ * 绘制图例
+ */
+ Column.prototype.drawLegend = function () {
+ var that = this;
+ var paper = this.canvas;
+ var legendSet = paper.set();
+ var bgSet = paper.set();
+ var conf = this.defaults;
+ var legendArea = conf.legendArea;
+ var columnCount = this.columnCount;
+ //legend
+ var mouseOverLegend = function (event) {
+ if (legendSet.clicked) {
+ return;
+ }
+ bgSet[this.data('type')].attr({
+ "fill-opacity":0.5
+ });
+ that.fire('legendOver', this.data('type'));
+ };
+
+ var mouseOutLegend = function (event) {
+ if (legendSet.clicked) {
+ return;
+ }
+ bgSet[this.data('type')].attr({"fill-opacity": 0});
+ that.fire('legendOut', this.data('type'));
+ };
+
+ var clickLegend = function (event) {
+ if (legendSet.clicked && legendSet.clickedColumn === this.data('type')) {
+ legendSet.clicked = false;
+ } else {
+ legendSet.clicked = true;
+ legendSet.clickedColumn = this.data('type');
+ }
+ bgSet.attr({"fill-opacity": 0});
+ bgSet[this.data('type')].attr({
+ "fill-opacity": legendSet.clicked ? 1 : 0
+ });
+ that.fire('legendClick', legendSet.clicked, this.data('type'));
+ };
+
+ var labels = _.keys(this.columns);
+ for (var i = 0; i < labels.length; i++) {
+ //底框
+ bgSet.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "#ebebeb",
+ "fill-opacity": 0,
+ "stroke": "none"
+ }));
+ // 色框
+ paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
+ "fill": conf.barColor[i],
+ "stroke": "none"
+ });
+ // 文字
+ paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, labels[i]).attr({
+ "fill": "black",
+ "fill-opacity": 1,
+ "font-family": "Verdana",
+ "font-size": 12,
+ "text-anchor": "start"
+ });
+ // 选框
+ var rect = paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "white",
+ "fill-opacity": 0,
+ "stroke": "none"
+ }).data("type", i);
+ rect.mouseover(mouseOverLegend);
+ rect.mouseout(mouseOutLegend);
+ rect.click(clickLegend);
+ legendSet.push(rect);
+ }
+ };
+
+ /**
+ * 绘制柱状图
+ * Options:
+ *
+ * - `width` 宽度,默认为节点宽度
+ * - `typeNames` 指定y轴上数据类目
+ *
+ * Examples:
+ * ```
+ * column.render({"width": 1024})
+ * ```
+ * @param {Object} options options json object for determin column style.
+ */
+ Column.prototype.render = function (options) {
+ this.setOptions(options);
+ this.canvas.clear();
+ this.setAxis();
+ this.drawAxis();
+ this.drawDiagram();
+ this.drawLegend();
+ };
+ /*!
+ * 导出
+ */
+ return Column;
+});
diff --git a/lib/charts/flow.js b/lib/charts/flow.js
index 91a6e95..18e7de3 100644
--- a/lib/charts/flow.js
+++ b/lib/charts/flow.js
@@ -1,261 +1,261 @@
-/*global EventProxy, d3, Raphael, $ */
-/*!
- * Flow的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Flow', function (require) {
- var DataV = require('DataV');
- var theme = DataV.Themes;
-
- /**
- * Flow构造函数
- */
- var Flow = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Flow";
- this.node = this.checkContainer(node);
-
- // Properties
- this.font = {};
-
- // Canvas
- this.defaults.width = 500;
- this.defaults.height = 400;
- this.defaults.deep = 150;
- this.defaults.radius = 50;
- this.defaults.xStep = 300;
- this.defaults.xStart = 200;
- this.defaults.yStart = 50;
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- Flow.prototype.setSource = function (source) {
- var conf = this.defaults;
-
- this.rawData = source;
- this.source = this.remapSource(source);
- };
-
- Flow.prototype.remapSource = function (data) {
- console.log(data);
- var dataLength = data.length;
- var remapData = [];
-
- var total;
- var i;
- for (i = 0; i < dataLength; i++){
- if (!data[i][3]) {
- total = data[i][2];
- remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
- } else {
- remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
- }
- }
-
- var conf = this.defaults;
- var width = conf.width;
- var height = conf.height;
- var radius = conf.radius;
- var xStep = conf.xStep;
- var xStart = conf.xStart;
- var yStart = conf.yStart;
- var depth = 0;
-
- for (i = 0; i < dataLength; i++){
- if (remapData[i].pid) {
- remapData[i].deep = remapData[remapData[i].pid - 1].deep + 1;
- remapData[remapData[i].pid - 1].child.push(remapData[i].id - 1);
- if (remapData[i].deep > depth) {
- depth = remapData[i].deep;
- }
- }
- // remapData[remapData[i].pid].child.push(remapData[i].id);
- }
-
- this.depth = depth;
- radius = Math.min(Math.min((width - xStep * (depth - 1) - xStart * 2) / depth, height*0.55), radius);
- console.log("r:" + radius);
- for (i = 0; i < dataLength; i++){
- remapData[i].percent = remapData[i].value / total;
- remapData[i].radius = radius * remapData[i].percent;
- }
- return remapData;
- // return data;
- };
-
- Flow.prototype.layout = function () {
- var conf = this.defaults;
- var width = conf.width;
- var height = conf.height;
- var xStart = conf.xStart;
- var yStart = conf.yStart;
- var xStep = conf.xStep;
- var remapData = this.source;
-
- //console.log(this.source);
- var circleData = [];
-
- circleData.push({x: width * 0.24, y: height * 0.42, radius: Math.max(10, remapData[0].radius), deep: remapData[0].deep, name: remapData[0].name, value: remapData[0].value});
- circleData.push({x: width * 0.5, y: height * 0.245, radius: Math.max(10, remapData[1].radius), deep: remapData[1].deep, name: remapData[1].name, value: remapData[1].value});
- circleData.push({x: width * 0.5, y: height * 0.6, radius: Math.max(10, remapData[2].radius), deep: remapData[2].deep, name: remapData[2].name, value: remapData[2].value});
- circleData.push({x: width * 0.72, y: height * 0.5, radius: Math.max(10, remapData[3].radius), deep: remapData[3].deep, name: remapData[3].name, value: remapData[3].value});
- circleData.push({x: width * 0.72, y: height * 0.817, radius: Math.max(10, remapData[4].radius), deep: remapData[4].deep, name: remapData[4].name, value: remapData[4].value});
-
- for (i = 0;i < circleData.length; i++) {
- console.log(circleData[i].x);
- }
-
- this.circleData = circleData;
- };
-
- Flow.prototype.getColor = function () {
-
- var colorMatrix = DataV.getColor();
-
- return color;
- };
-
- // Tree.prototype.getFont = function () {
- // //var conf = this.defaults;
-
- // return DataV.getFont();
- // };
-
- Flow.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.width, conf.height);
-
- this.DOMNode = $(this.canvas.canvas);
- var that = this;
- this.DOMNode.click(function (event) {
- that.trigger("click", event);
- });
- this.DOMNode.dblclick(function (event) {
- that.trigger("dblclick", event);
- });
-
- var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
- this.DOMNode.bind(mousewheel, function (event) {
- that.trigger("mousewheel", event);
- });
-
- this.DOMNode.bind("contextmenu", function (event) {
- that.trigger("contextmenu", event);
- });
-
- this.DOMNode.delegate("circle", "click", function (event) {
- that.trigger("circle_click", event);
- });
-
- this.DOMNode.delegate("circle", "mouseover", function (event) {
- that.trigger("circle_mouseover", event);
- });
-
- this.DOMNode.delegate("circle", "mouseout", function (event) {
- that.trigger("circle_mouseout", event);
- });
-
- //console.log(this.canvas);
- };
-
-
- Flow.prototype.getLinkPath = function (fx, fy, tx, ty) {
- var conf = this.defaults;
-
- var c1x = fx + (tx - fx) / 1.5;
- var c1y = fy;
- var c2x = tx - (tx - fx) / 4;
- var c2y = ty - (ty - fy) / 2;
-
- var link_path = [["M", fx, fy],
- ["S", c1x, c1y, tx, ty]];
-
- return link_path;
- };
-
- Flow.prototype.generatePaths = function () {
- var canvas = this.canvas;
- var source = this.source;
- var conf = this.defaults;
- var radius = conf.radius;
- //canvas.clear();
- // var font = this.getFont();
- var font_family = '微软雅黑';
- var font_size = 8;
- var depth = this.depth;
- var crilceData = this.circleData;
- var l = crilceData.length;
- var getLinkPath = this.getLinkPath;
-
- canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[1].x, crilceData[1].y)});
- canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[2].x, crilceData[2].y)});
- canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[3].x, crilceData[3].y)});
- canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[4].x, crilceData[4].y)});
-
- var i, d;
- var thisRadius;
- var thisColor;
- var titelPath = [];
- var valuePath = [];
- for (i = 0 ; i < l ; i++) {
- d = crilceData[i];
- thisRadius = Math.max(27, d.radius);
- if (i === 1) {
- thisColor = "#b4e481";
- } else if (i === 4) {
- thisColor = "#cd3a19";
- } else {
- thisColor = "#ffe79d";
- }
- canvas.circle(d.x, d.y, thisRadius)
- .attr({"stroke": "none", fill: thisColor});
- titelPath.push(canvas.text(0, 0, d.name).attr({'font-size': 12}));
- if (i < l - 2) {
- titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5));
- } else {
- titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5)).attr({"text-anchor": "start"});
- }
-
- if (i < l - 1) {
- valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({'font-size': 12}));
- } else {
- valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({fill: "#ffffff", 'font-size': 12}));
- }
- }
-
- var n = 0;
-
- var node;
- var num = 0;
-
- var nodes = canvas.set();
- var path = [];
- var textpath = [];
-
- var tree = this;
- };
-
- Flow.prototype.render = function (options) {
- var st = new Date().getTime();
- this.canvas.clear();
- this.setOptions(options);
- this.layout();
- var st2 = new Date().getTime();
- console.log(st2 - st);
-
- this.generatePaths();
- var et = new Date().getTime();
- console.log(et - st2);
- //this.canvas.renderfix();
- };
-
- return Flow;
-});
+/*global EventProxy, d3, Raphael, $ */
+/*!
+ * Flow的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Flow', function (require) {
+ var DataV = require('DataV');
+ var theme = DataV.Themes;
+
+ /**
+ * Flow构造函数
+ */
+ var Flow = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Flow";
+ this.node = this.checkContainer(node);
+
+ // Properties
+ this.font = {};
+
+ // Canvas
+ this.defaults.width = 500;
+ this.defaults.height = 400;
+ this.defaults.deep = 150;
+ this.defaults.radius = 50;
+ this.defaults.xStep = 300;
+ this.defaults.xStart = 200;
+ this.defaults.yStart = 50;
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ Flow.prototype.setSource = function (source) {
+ var conf = this.defaults;
+
+ this.rawData = source;
+ this.source = this.remapSource(source);
+ };
+
+ Flow.prototype.remapSource = function (data) {
+ console.log(data);
+ var dataLength = data.length;
+ var remapData = [];
+
+ var total;
+ var i;
+ for (i = 0; i < dataLength; i++){
+ if (!data[i][3]) {
+ total = data[i][2];
+ remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
+ } else {
+ remapData.push({id: data[i][0], name: data[i][1], value: data[i][2], pid: data[i][3], child: [], deep: 0});
+ }
+ }
+
+ var conf = this.defaults;
+ var width = conf.width;
+ var height = conf.height;
+ var radius = conf.radius;
+ var xStep = conf.xStep;
+ var xStart = conf.xStart;
+ var yStart = conf.yStart;
+ var depth = 0;
+
+ for (i = 0; i < dataLength; i++){
+ if (remapData[i].pid) {
+ remapData[i].deep = remapData[remapData[i].pid - 1].deep + 1;
+ remapData[remapData[i].pid - 1].child.push(remapData[i].id - 1);
+ if (remapData[i].deep > depth) {
+ depth = remapData[i].deep;
+ }
+ }
+ // remapData[remapData[i].pid].child.push(remapData[i].id);
+ }
+
+ this.depth = depth;
+ radius = Math.min(Math.min((width - xStep * (depth - 1) - xStart * 2) / depth, height*0.55), radius);
+ console.log("r:" + radius);
+ for (i = 0; i < dataLength; i++){
+ remapData[i].percent = remapData[i].value / total;
+ remapData[i].radius = radius * remapData[i].percent;
+ }
+ return remapData;
+ // return data;
+ };
+
+ Flow.prototype.layout = function () {
+ var conf = this.defaults;
+ var width = conf.width;
+ var height = conf.height;
+ var xStart = conf.xStart;
+ var yStart = conf.yStart;
+ var xStep = conf.xStep;
+ var remapData = this.source;
+
+ //console.log(this.source);
+ var circleData = [];
+
+ circleData.push({x: width * 0.24, y: height * 0.42, radius: Math.max(10, remapData[0].radius), deep: remapData[0].deep, name: remapData[0].name, value: remapData[0].value});
+ circleData.push({x: width * 0.5, y: height * 0.245, radius: Math.max(10, remapData[1].radius), deep: remapData[1].deep, name: remapData[1].name, value: remapData[1].value});
+ circleData.push({x: width * 0.5, y: height * 0.6, radius: Math.max(10, remapData[2].radius), deep: remapData[2].deep, name: remapData[2].name, value: remapData[2].value});
+ circleData.push({x: width * 0.72, y: height * 0.5, radius: Math.max(10, remapData[3].radius), deep: remapData[3].deep, name: remapData[3].name, value: remapData[3].value});
+ circleData.push({x: width * 0.72, y: height * 0.817, radius: Math.max(10, remapData[4].radius), deep: remapData[4].deep, name: remapData[4].name, value: remapData[4].value});
+
+ for (i = 0;i < circleData.length; i++) {
+ console.log(circleData[i].x);
+ }
+
+ this.circleData = circleData;
+ };
+
+ Flow.prototype.getColor = function () {
+
+ var colorMatrix = DataV.getColor();
+
+ return color;
+ };
+
+ // Tree.prototype.getFont = function () {
+ // //var conf = this.defaults;
+
+ // return DataV.getFont();
+ // };
+
+ Flow.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+
+ this.DOMNode = $(this.canvas.canvas);
+ var that = this;
+ this.DOMNode.click(function (event) {
+ that.trigger("click", event);
+ });
+ this.DOMNode.dblclick(function (event) {
+ that.trigger("dblclick", event);
+ });
+
+ var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
+ this.DOMNode.bind(mousewheel, function (event) {
+ that.trigger("mousewheel", event);
+ });
+
+ this.DOMNode.bind("contextmenu", function (event) {
+ that.trigger("contextmenu", event);
+ });
+
+ this.DOMNode.delegate("circle", "click", function (event) {
+ that.trigger("circle_click", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseover", function (event) {
+ that.trigger("circle_mouseover", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseout", function (event) {
+ that.trigger("circle_mouseout", event);
+ });
+
+ //console.log(this.canvas);
+ };
+
+
+ Flow.prototype.getLinkPath = function (fx, fy, tx, ty) {
+ var conf = this.defaults;
+
+ var c1x = fx + (tx - fx) / 1.5;
+ var c1y = fy;
+ var c2x = tx - (tx - fx) / 4;
+ var c2y = ty - (ty - fy) / 2;
+
+ var link_path = [["M", fx, fy],
+ ["S", c1x, c1y, tx, ty]];
+
+ return link_path;
+ };
+
+ Flow.prototype.generatePaths = function () {
+ var canvas = this.canvas;
+ var source = this.source;
+ var conf = this.defaults;
+ var radius = conf.radius;
+ //canvas.clear();
+ // var font = this.getFont();
+ var font_family = '微软雅黑';
+ var font_size = 8;
+ var depth = this.depth;
+ var crilceData = this.circleData;
+ var l = crilceData.length;
+ var getLinkPath = this.getLinkPath;
+
+ canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[1].x, crilceData[1].y)});
+ canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[0].x, crilceData[0].y, crilceData[2].x, crilceData[2].y)});
+ canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[3].x, crilceData[3].y)});
+ canvas.path().attr({stroke: "#cdcdcd", "stroke-width": 2}).attr({path: getLinkPath(crilceData[2].x, crilceData[2].y, crilceData[4].x, crilceData[4].y)});
+
+ var i, d;
+ var thisRadius;
+ var thisColor;
+ var titelPath = [];
+ var valuePath = [];
+ for (i = 0 ; i < l ; i++) {
+ d = crilceData[i];
+ thisRadius = Math.max(27, d.radius);
+ if (i === 1) {
+ thisColor = "#b4e481";
+ } else if (i === 4) {
+ thisColor = "#cd3a19";
+ } else {
+ thisColor = "#ffe79d";
+ }
+ canvas.circle(d.x, d.y, thisRadius)
+ .attr({"stroke": "none", fill: thisColor});
+ titelPath.push(canvas.text(0, 0, d.name).attr({'font-size': 12}));
+ if (i < l - 2) {
+ titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5));
+ } else {
+ titelPath[i].transform("t" + d.x + "," + (d.y - thisRadius - titelPath[i].getBBox().height/2 - 5)).attr({"text-anchor": "start"});
+ }
+
+ if (i < l - 1) {
+ valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({'font-size': 12}));
+ } else {
+ valuePath.push(canvas.text(d.x, d.y, d.value + "人").attr({fill: "#ffffff", 'font-size': 12}));
+ }
+ }
+
+ var n = 0;
+
+ var node;
+ var num = 0;
+
+ var nodes = canvas.set();
+ var path = [];
+ var textpath = [];
+
+ var tree = this;
+ };
+
+ Flow.prototype.render = function (options) {
+ var st = new Date().getTime();
+ this.canvas.clear();
+ this.setOptions(options);
+ this.layout();
+ var st2 = new Date().getTime();
+ console.log(st2 - st);
+
+ this.generatePaths();
+ var et = new Date().getTime();
+ console.log(et - st2);
+ //this.canvas.renderfix();
+ };
+
+ return Flow;
+});
diff --git a/lib/charts/force.js b/lib/charts/force.js
index 4378057..68b0815 100644
--- a/lib/charts/force.js
+++ b/lib/charts/force.js
@@ -1,711 +1,711 @@
-/*global Raphael, d3 */
-/*!
- * Force的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) {
- return this[id];
- });
- }
-})('Force', function (require) {
- var DataV = require('DataV');
- /**
- * 构造函数
- * @param {Object} node 表示在html的哪个容器中绘制该组件
- * @param {Object} options 为用户自定义的组件的属性,比如画布大小
- */
- var Force = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Force";
- this.node = this.checkContainer(node);
- this.net = {};
- this.linkValeMin = 0;
- this.linkValeMax = 1;
- this.nodeValueMin = 0;
- this.nodeValueMax = 1;
- this.clicked = false;
- this.clickedNum = -1;
- this.legendClicked = false;
-
- // Properties
- this.font = {};
-
- // Canvas
- this.defaults.legend = true;
- this.defaults.width = 500;
- this.defaults.height = 500;
- this.defaults.linkLength = 50;
- this.defaults.linkWidth = 2;
- this.defaults.classNum = 6;
- this.defaults.forceValue = 10;
- this.defaults.iterate = 100;
- this.defaults.browserName = navigator.appName;
-
- this.setOptions(options);
- this.defaults.charge = -(this.defaults.width + this.defaults.height) / this.defaults.forceValue;
- this.legendArea = [20, (this.defaults.height - 20 - this.defaults.classNum * 20), 200, 220];
- if (this.defaults.legend) {
- this.xOffset = this.legendArea[2];
- } else {
- this.xOffset = 0;
- }
-
- this.createCanvas();
- }
- });
-
-
- /*!
- *Strings in CSV to Numbers
- * @param {Number} value the value of the elemnts in csv table
- */
- Force.prototype._toNum = function (value) {
- var type = typeof value;
- if (type === "number") {
- return value;
- } else if (type === "string" && value !== "") {
- return parseInt(value, 10);
- } else {
- return 1;
- }
- };
-
- /**
- * Set CSV content to force-directed net
- * @param {Array} table the csv table to be rendered
- */
- Force.prototype.setSource = function (table) {
- //this.net = json;
- if (table[0][0] === "Id") {
- table = table.slice(1);
- }
- var nData = [];
- var lData = [];
- var isNode = true;
- var nodeNum;
- var that = this;
- table.forEach(function (d, i) {
- var value;
- if (isNode) {
- if (d[0] === "Source") {
- isNode = false;
- nodeNum = i + 1;
- } else {
- if (d[0] === "") {
- throw new Error("ID can not be empty(line:" + (i + 1) + ").");
- }
- value = that._toNum(d[2]);
- nData[i] = {
- name: d[1],
- nodeValue: value
- };
- if (i === 0) {
- that.nodeValueMin = value;
- that.nodeValueMax = value;
- }
- that.nodeValueMin = (value < that.nodeValueMin) ? value : that.nodeValueMin;
- that.nodeValueMax = (value > that.nodeValueMax) ? value : that.nodeValueMax;
- }
- } else {
- if (d[0] === "") {
- throw new Error("Source can not be empty(line:" + (i + 1) + ").");
- }
- if (d[1] === "") {
- throw new Error("Target can not be empty(line:" + (i + 1) + ").");
- }
- value = that._toNum(d[2]);
- lData[i - nodeNum] = {
- source: that._toNum(d[0]),
- target: that._toNum(d[1]),
- value: that._toNum(d[2])
- };
- if (i === nodeNum) {
- that.linkValueMin = value;
- that.linkValueMax = value;
- }
- that.linkValueMin = (value < that.linkValueMin) ? value : that.linkValueMin;
- that.linkValueMax = (value > that.linkValueMax) ? value : that.linkValueMax;
- }
- });
- this.net.nodes = nData;
- this.net.links = lData;
- this.nodeValueMax++;
- this.linkValueMax++;
- };
-
-
- /**
- * 创建画布
- */
- Force.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.width, conf.height);
- //var c = this.canvas.circle(50, 50, 40);
- };
-
- /**
- * 获取节点颜色
- * @param {Number} i 元素类别编号
- * @return {String} 返回颜色值
- */
- Force.prototype.getColor = function (i) {
- var color = DataV.getColor(this.classNum);
- //var k = color.length * (i - this.nodeValueMin-0.1) / (this.nodeValueMax - this.nodeValueMin);
- //if (k < 0) k = 0;
- return color[i % color.length][0];
- };
-
- /**
- * 获取节点的半径
- * @param {Number} value 元素对应的数据值
- * @return {Number} 返回半径值
- */
- Force.prototype.getRadius = function (value) {
- var conf = this.defaults;
- return 16.0 * (value - this.nodeValueMin) / (this.nodeValueMax - this.nodeValueMin) + 8;
- };
-
-
- /**
- * 获取节点透明度
- * @param {Number} value 元素类别编号
- * @return {Number} 返回透明度值
- */
- Force.prototype.getOpacity = function (value) {
- return 0.083 * (value - this.linkValueMin) / (this.linkValueMax - this.linkValueMin) + 0.078;
- };
-
- /**
- * update the layout by modify the attributes of nodes and links
- */
- Force.prototype.update = function () {
- var that = this;
- var conf = this.defaults;
- var canvas = this.canvas;
-
- this.nodes = this.canvas.set();
- this.links = this.canvas.set();
- var nodes = this.nodes;
- var links = this.links;
- var i, j, temp;
- this.force.charge(conf.charge).nodes(this.net.nodes).links(this.net.links).start();
-
- var nodesData = this.net.nodes;
- var linksData = this.net.links;
- var nodesNum = nodesData.length;
- var linksNum = linksData.length;
- var connectMatrix = [];
- var linkMatrix = [];
- conf.iterate = (nodesNum + linksNum) * 2;
-
- var onMouseClick = function () {
- that.legendClicked = false;
- that.underBn.forEach(function (d) {
- d.hide();
- d.data('clicked', false);
- });
- that.clicked = true;
- if (!this.data('clicked')) {
- if (conf.browserName !== "Microsoft Internet Explorer") {
- that.force.linkDistance(conf.linkLength * 2).charge(conf.charge * 2).start();
- }
- that.nodes.forEach(function (d) {
- d.data('rect').hide();
- d.data('text').hide();
- d.attr({
- "opacity": 0.2
- });
- d.data('clicked', false);
- d.data('showText', false);
- });
- that.links.forEach(function (d) {
- d.attr({
- 'stroke-opacity': 0.0
- });
- });
- that.clickedNum = this.data('index');
- this.data('clicked', true);
- this.data("link").forEach(function (d) {
- d.attr({
- "stroke-opacity": d.data('opacity')
- });
- });
- this.data("node").forEach(function (d) {
- d.attr({
- "opacity": 0.9
- });
- d.data('showText', true);
- });
- that.underBn[this.data('colorType')].data('clicked', true).attr('opacity', 1).show();
- } else {
- that.clicked = false;
- if (conf.browserName !== "Microsoft Internet Explorer") {
- that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
- }
- nodes.forEach(function (d) {
- d.attr({
- "opacity": 0.9
- });
- if (d.data('big')) {
- d.data('showText', true);
- } else {
- d.data('rect').hide();
- d.data('text').hide();
- d.data('showText', false);
- }
- });
- links.forEach(function (d) {
- d.attr({
- 'stroke-opacity': d.data('opacity')
- });
- });
- this.data('clicked', false);
- that.underBn[this.data('colorType')].hide();
- }
- };
-
- var onCanvasClick = function () {
- that.legendClicked = false;
- that.underBn.forEach(function (d) {
- d.hide();
- d.data('clicked', false);
- });
- that.clicked = false;
- if (conf.browserName !== "Microsoft Internet Explorer") {
- that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
- } else {
- that.force.resume();
- }
- nodes.forEach(function (d) {
- d.attr({
- "opacity": 0.9
- });
- if (d.data('big')) {
- d.data('showText', true);
- } else {
- d.data('rect').hide();
- d.data('text').hide();
- d.data('showText', false);
- }
- });
- links.forEach(function (d) {
- d.attr({
- 'stroke-opacity': d.data('opacity')
- });
- });
- };
-
- var topValue = [];
- var topId = [];
- var topNum = 10;
- if (nodesNum < 10) {
- topNum = nodesNum;
- }
- for (i = 0; i < topNum; i++) {
- topValue[i] = nodesData[i].nodeValue;
- topId[i] = i;
- }
- for (i = 0; i < topNum; i++) {
- for (j = 1; j < topNum - i; j++) {
- if (topValue[j] < topValue[j - 1]) {
- temp = topValue[j];
- topValue[j] = topValue[j - 1];
- topValue[j - 1] = temp;
- temp = topId[j];
- topId[j] = topId[j - 1];
- topId[j - 1] = temp;
- }
- }
- }
- //rapheal绘制部分
- for (i = 0; i < nodesNum; i++) {
- nodesData[i].x = (conf.width + this.xOffset) / 2;
- nodesData[i].y = conf.height / 2;
- var n = nodesData[i];
- var k = Math.floor(conf.classNum * (n.nodeValue - this.nodeValueMin) / (this.nodeValueMax - this.nodeValueMin));
- if (k >= conf.classNum) k = conf.classNum - 1;
- var radius = this.getRadius(n.nodeValue);
- var cnode = canvas.circle(n.x, n.y, radius).attr({
- fill: this.getColor(k),
- 'stroke': "#ffffff",
- 'opacity': 0.9
- //title: n.name
- });
- var nodeText = canvas.text(n.x, n.y - radius, n.name).attr({
- 'opacity': 1,
- //'font-family': "微软雅黑",
- 'font': '12px Verdana'
- }).hide();
- var nodeRect = canvas.rect(n.x, n.y, nodeText.getBBox().width, nodeText.getBBox().height, 2).attr({
- 'fill': "#000000",
- 'stroke-opacity': 0,
- 'fill-opacity': 0.1
- }).hide();
- cnode.data('r', radius);
- cnode.data("name", n.name);
- cnode.data('text', nodeText);
- cnode.data('rect', nodeRect);
- cnode.data('colorType', k);
- cnode.data('clicked', false);
- cnode.data('index', i);
- cnode.data('big', false);
-
- if (i >= topNum && topValue[0] < nodesData[i].nodeValue) {
- topValue[0] = nodesData[i].nodeValue;
- topId[0] = i;
- for (j = 1; j < topNum; j++) {
- if (topValue[j] < topValue[j - 1]) {
- temp = topValue[j];
- topValue[j] = topValue[j - 1];
- topValue[j - 1] = temp;
- temp = topId[j];
- topId[j] = topId[j - 1];
- topId[j - 1] = temp;
- } else {
- break;
- }
- }
- }
-
- nodes.push(cnode);
- connectMatrix[i] = [];
- linkMatrix[i] = [];
- connectMatrix[i].push(nodes[i]);
- }
-
- for (i = 0; i < topNum; i++) {
- nodes[topId[i]].data("big", true);
- }
-
-
- for (i = 0; i < linksNum; i++) {
- var l = linksData[i];
- var clink = canvas.path("M" + l.source.x + "," + l.source.y + "L" + l.target.x + "," + l.target.y).attr({
- 'stroke-width': conf.linkWidth,
- 'stroke-opacity': this.getOpacity(l.value)
- }).toBack();
- clink.data('opacity', this.getOpacity(l.value));
- links.push(clink);
- connectMatrix[l.source.index].push(nodes[l.target.index]);
- connectMatrix[l.target.index].push(nodes[l.source.index]);
- linkMatrix[l.source.index].push(links[i]);
- linkMatrix[l.target.index].push(links[i]);
- }
-
- var background = canvas.rect(0, 0, conf.width, conf.height).attr({
- 'fill': '#ffffff',
- 'stroke-opacity': 0
- }).toBack();
- background.click(onCanvasClick);
-
- nodes.forEach(function (d, i) {
- d.data("node", connectMatrix[i]);
- d.data("link", linkMatrix[i]);
- if (d.data('big')) {
- d.data('showText', true);
- } else {
- d.data('showText', false);
- }
- d.drag(function (dx, dy) {
- d.data('x', this.ox + dx);
- d.data('y', this.oy + dy);
- }, function () {
- that.force.resume();
- this.ox = this.attr("cx");
- this.oy = this.attr("cy");
- d.data('x', this.ox);
- d.data('y', this.oy);
- d.data('drag', true);
- }, function () {
- that.force.resume();
- d.data('drag', false);
- });
- d.click(onMouseClick); //.mouseup(onmouseup);
- d.mouseover(function () {
- if (conf.browserName !== "Microsoft Internet Explorer") {
- that.force.resume();
- }
- this.attr({
- 'r': d.data('r') + 5
- });
- if (!this.data('showText')) {
- //this.attr('title', "");
- this.data('showText', true);
- this.data('hover', true);
- }
- if (!that.underBn[this.data('colorType')].data('clicked')) {
- that.underBn[this.data('colorType')].attr('opacity', 0.5).show();
- }
- }).mouseout(function () {
- this.attr({
- 'r': d.data('r')
- });
- //this.attr('title', this.data('name'));
- if (this.data('hover') && !this.data('clicked')) {
- d.data('rect').hide();
- d.data('text').hide();
- this.data('showText', false);
- this.data('hover', false);
- }
- if (!that.underBn[this.data('colorType')].data('clicked')) {
- that.underBn[this.data('colorType')].hide();
- }
- });
- });
-
- };
-
-
- /**
- * 绘制图例
- */
- Force.prototype.legend = function () {
- var that = this;
- var conf = this.defaults;
- var paper = this.canvas;
- var legendArea = this.legendArea;
- var rectBn = paper.set();
- this.underBn = [];
- var underBn = this.underBn;
- for (i = 0; i <= conf.classNum - 1; i++) {
- //底框
- underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "#ebebeb",
- "stroke": "none",
- 'opacity': 1
- }).data('clicked', false).hide());
- //色框
- paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
- "fill": that.getColor(i),
- "stroke": "none"
- });
- //文字
- var min = Math.floor(this.nodeValueMin + i * (this.nodeValueMax - this.nodeValueMin) / conf.classNum);
- var max = Math.floor(min + (this.nodeValueMax - this.nodeValueMin) / conf.classNum);
- paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, min + " ~ " + max).attr({
- "fill": "black",
- "fill-opacity": 1,
- "font-family": "Verdana",
- "font-size": 12
- }).attr({
- "text-anchor": "start"
- });
- //选框
- rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
- "fill": "white",
- "fill-opacity": 0,
- "stroke": "none"
- //"r": 3
- })).data("clicked", 0);
- }
- rectBn.forEach(function (d, i) {
- d.mouseover(function () {
- if (!underBn[i].data('clicked')) {
- underBn[i].attr('opacity', 0.5);
- underBn[i].show();
- }
- }).mouseout(function () {
- if (!underBn[i].data('clicked')) {
- underBn[i].hide();
- }
- });
- d.click(function () {
- that.clicked = false;
- if (conf.browserName !== "Microsoft Internet Explorer") {
- that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
- }
- for (j = 0; j < underBn.length; j++) {
- if (j === i) {
- underBn[j].show();
- } else {
- underBn[j].hide();
- }
- }
- rectBn.forEach(function (eachBn) {
- if (eachBn !== d) {
- eachBn.data("clicked", 0);
- }
- });
- if (d.data("clicked") === 0) {
- that.legendClicked = true;
- underBn[i].attr('opacity', 1);
- underBn[i].data('clicked', true);
- underBn[i].show();
- that.nodes.forEach(function (d) {
- if (d.data('colorType') === i) {
- d.attr({
- "opacity": 0.9
- });
- d.data('showText', true);
- } else {
- d.attr({
- "opacity": 0.2
- });
- d.data('rect').hide();
- d.data('text').hide();
- d.data('showText', false);
- }
- });
- that.links.forEach(function (d) {
- d.attr({
- "stroke-opacity": 0
- });
- });
- d.data("clicked", 1);
- } else if (d.data("clicked") === 1) {
- that.legendClicked = false;
- underBn[i].data('clicked', false);
- underBn[i].hide();
- d.data("clicked", 0);
- that.nodes.forEach(function (d) {
- d.attr({
- "opacity": 0.9
- });
- if (d.data('big')) {
- d.data('showText', true);
- } else {
- d.data('rect').hide();
- d.data('text').hide();
- d.data('showText', false);
- }
- });
- that.links.forEach(function (d) {
- d.attr({
- "stroke-opacity": d.data('opacity')
- });
- });
- }
- });
- });
- };
-
-
- /**
- * create the force-direct layout
- */
- Force.prototype.layout = function () {
- var conf = this.defaults;
- this.force = d3.layout.force().linkDistance(conf.linkLength).size([conf.width + this.xOffset, conf.height]).theta(1.5);
- };
-
- /**
- * update the force-direct layout animation
- */
- Force.prototype.animate = function () {
- var conf = this.defaults;
- var nodes = this.nodes;
- var links = this.links;
- var tick = 0;
- var that = this;
-
- var nodesData = this.net.nodes;
- var linksData = this.net.links;
-
- this.force.on("tick", function () {
- if (conf.browserName !== "Microsoft Internet Explorer" || tick > conf.iterate) {
- if (tick % 2 === 0) {
- nodes.forEach(function (d, i) {
- var margin = d.data('r');
- var nd = nodesData[i];
- if (d.data('drag')) {
- nd.x = d.data('x');
- nd.y = d.data('y');
- }
- nd.x = (nd.x < margin + that.xOffset) ? (margin + that.xOffset) : nd.x;
- nd.x = (nd.x > conf.width - margin) ? conf.width - margin : nd.x;
- nd.y = (nd.y < margin) ? margin : nd.y;
- nd.y = (nd.y > conf.height - margin) ? conf.height - margin : nd.y;
- var bx = d.data('text').getBBox().width / 2;
- var by = d.data('text').getBBox().height / 2;
-
- if (that.clicked) {
- var mx = nodesData[that.clickedNum].x;
- var my = nodesData[that.clickedNum].y;
- var tx, ty;
- if (d.data('clicked')) {
-
- if (conf.browserName !== "Microsoft Internet Explorer") {
- nd.x = (conf.width + that.xOffset) / 2;
- nd.y = conf.height / 2;
- }
- d.data('rect').attr({
- 'x': nd.x - bx,
- 'y': nd.y + d.data('r')
- });
- d.data('text').attr({
- 'x': nd.x,
- 'y': nd.y + by + d.data('r')
- });
- d.data('rect').show();
- d.data('text').show();
- } else if (d.data('showText')) {
- var lx = (nd.x - mx);
- var ly = (nd.y - my);
- var length = Math.sqrt(lx * lx + ly * ly);
- tx = nd.x + bx * lx / length;
- ty = nd.y + by * ly / length;
- tx = (nd.x < mx) ? tx - d.data('r') : tx + d.data('r');
- ty = (nd.y < my) ? ty - d.data('r') : ty + d.data('r');
- tx = (tx < margin + that.xOffset) ? (margin + that.xOffset) : tx;
- tx = (tx > conf.width - margin) ? conf.width - margin : tx;
- ty = (ty < margin) ? margin : ty;
- ty = (ty > conf.height - margin) ? conf.height - margin : ty;
- d.data('rect').attr({
- 'x': tx - bx,
- 'y': ty - by
- });
- d.data('text').attr({
- 'x': tx,
- 'y': ty
- });
- d.data('rect').show();
- d.data('text').show();
- }
- } else if (d.data('showText')) {
- d.data('rect').attr({
- 'x': nd.x - bx,
- 'y': nd.y - by + 4 + d.data('r')
- });
- d.data('text').attr({
- 'x': nd.x,
- 'y': nd.y + 4 + d.data('r')
- });
- try {
- d.data('rect').show();
- d.data('text').show();
- } catch (e) {}
-
- }
- d.attr({
- 'cx': nd.x,
- 'cy': nd.y
- });
- });
- links.forEach(function (d, i) {
- d.attr('path', "M" + linksData[i].source.x + "," + linksData[i].source.y + "L" + linksData[i].target.x + "," + linksData[i].target.y);
- });
- }
- }++tick;
- });
- };
-
- /**
- *render the force-directed net on the canvas and keep updating
- * @param {Object} options user options
- */
- Force.prototype.render = function (options) {
- this.setOptions(options);
- this.canvas.clear();
- this.layout();
- if (this.defaults.legend) {
- this.legend();
- }
- this.update();
- this.animate();
- };
-
- return Force;
+/*global Raphael, d3 */
+/*!
+ * Force的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) {
+ return this[id];
+ });
+ }
+})('Force', function (require) {
+ var DataV = require('DataV');
+ /**
+ * 构造函数
+ * @param {Object} node 表示在html的哪个容器中绘制该组件
+ * @param {Object} options 为用户自定义的组件的属性,比如画布大小
+ */
+ var Force = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Force";
+ this.node = this.checkContainer(node);
+ this.net = {};
+ this.linkValeMin = 0;
+ this.linkValeMax = 1;
+ this.nodeValueMin = 0;
+ this.nodeValueMax = 1;
+ this.clicked = false;
+ this.clickedNum = -1;
+ this.legendClicked = false;
+
+ // Properties
+ this.font = {};
+
+ // Canvas
+ this.defaults.legend = true;
+ this.defaults.width = 500;
+ this.defaults.height = 500;
+ this.defaults.linkLength = 50;
+ this.defaults.linkWidth = 2;
+ this.defaults.classNum = 6;
+ this.defaults.forceValue = 10;
+ this.defaults.iterate = 100;
+ this.defaults.browserName = navigator.appName;
+
+ this.setOptions(options);
+ this.defaults.charge = -(this.defaults.width + this.defaults.height) / this.defaults.forceValue;
+ this.legendArea = [20, (this.defaults.height - 20 - this.defaults.classNum * 20), 200, 220];
+ if (this.defaults.legend) {
+ this.xOffset = this.legendArea[2];
+ } else {
+ this.xOffset = 0;
+ }
+
+ this.createCanvas();
+ }
+ });
+
+
+ /*!
+ *Strings in CSV to Numbers
+ * @param {Number} value the value of the elemnts in csv table
+ */
+ Force.prototype._toNum = function (value) {
+ var type = typeof value;
+ if (type === "number") {
+ return value;
+ } else if (type === "string" && value !== "") {
+ return parseInt(value, 10);
+ } else {
+ return 1;
+ }
+ };
+
+ /**
+ * Set CSV content to force-directed net
+ * @param {Array} table the csv table to be rendered
+ */
+ Force.prototype.setSource = function (table) {
+ //this.net = json;
+ if (table[0][0] === "Id") {
+ table = table.slice(1);
+ }
+ var nData = [];
+ var lData = [];
+ var isNode = true;
+ var nodeNum;
+ var that = this;
+ table.forEach(function (d, i) {
+ var value;
+ if (isNode) {
+ if (d[0] === "Source") {
+ isNode = false;
+ nodeNum = i + 1;
+ } else {
+ if (d[0] === "") {
+ throw new Error("ID can not be empty(line:" + (i + 1) + ").");
+ }
+ value = that._toNum(d[2]);
+ nData[i] = {
+ name: d[1],
+ nodeValue: value
+ };
+ if (i === 0) {
+ that.nodeValueMin = value;
+ that.nodeValueMax = value;
+ }
+ that.nodeValueMin = (value < that.nodeValueMin) ? value : that.nodeValueMin;
+ that.nodeValueMax = (value > that.nodeValueMax) ? value : that.nodeValueMax;
+ }
+ } else {
+ if (d[0] === "") {
+ throw new Error("Source can not be empty(line:" + (i + 1) + ").");
+ }
+ if (d[1] === "") {
+ throw new Error("Target can not be empty(line:" + (i + 1) + ").");
+ }
+ value = that._toNum(d[2]);
+ lData[i - nodeNum] = {
+ source: that._toNum(d[0]),
+ target: that._toNum(d[1]),
+ value: that._toNum(d[2])
+ };
+ if (i === nodeNum) {
+ that.linkValueMin = value;
+ that.linkValueMax = value;
+ }
+ that.linkValueMin = (value < that.linkValueMin) ? value : that.linkValueMin;
+ that.linkValueMax = (value > that.linkValueMax) ? value : that.linkValueMax;
+ }
+ });
+ this.net.nodes = nData;
+ this.net.links = lData;
+ this.nodeValueMax++;
+ this.linkValueMax++;
+ };
+
+
+ /**
+ * 创建画布
+ */
+ Force.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+ //var c = this.canvas.circle(50, 50, 40);
+ };
+
+ /**
+ * 获取节点颜色
+ * @param {Number} i 元素类别编号
+ * @return {String} 返回颜色值
+ */
+ Force.prototype.getColor = function (i) {
+ var color = DataV.getColor(this.classNum);
+ //var k = color.length * (i - this.nodeValueMin-0.1) / (this.nodeValueMax - this.nodeValueMin);
+ //if (k < 0) k = 0;
+ return color[i % color.length][0];
+ };
+
+ /**
+ * 获取节点的半径
+ * @param {Number} value 元素对应的数据值
+ * @return {Number} 返回半径值
+ */
+ Force.prototype.getRadius = function (value) {
+ var conf = this.defaults;
+ return 16.0 * (value - this.nodeValueMin) / (this.nodeValueMax - this.nodeValueMin) + 8;
+ };
+
+
+ /**
+ * 获取节点透明度
+ * @param {Number} value 元素类别编号
+ * @return {Number} 返回透明度值
+ */
+ Force.prototype.getOpacity = function (value) {
+ return 0.083 * (value - this.linkValueMin) / (this.linkValueMax - this.linkValueMin) + 0.078;
+ };
+
+ /**
+ * update the layout by modify the attributes of nodes and links
+ */
+ Force.prototype.update = function () {
+ var that = this;
+ var conf = this.defaults;
+ var canvas = this.canvas;
+
+ this.nodes = this.canvas.set();
+ this.links = this.canvas.set();
+ var nodes = this.nodes;
+ var links = this.links;
+ var i, j, temp;
+ this.force.charge(conf.charge).nodes(this.net.nodes).links(this.net.links).start();
+
+ var nodesData = this.net.nodes;
+ var linksData = this.net.links;
+ var nodesNum = nodesData.length;
+ var linksNum = linksData.length;
+ var connectMatrix = [];
+ var linkMatrix = [];
+ conf.iterate = (nodesNum + linksNum) * 2;
+
+ var onMouseClick = function () {
+ that.legendClicked = false;
+ that.underBn.forEach(function (d) {
+ d.hide();
+ d.data('clicked', false);
+ });
+ that.clicked = true;
+ if (!this.data('clicked')) {
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ that.force.linkDistance(conf.linkLength * 2).charge(conf.charge * 2).start();
+ }
+ that.nodes.forEach(function (d) {
+ d.data('rect').hide();
+ d.data('text').hide();
+ d.attr({
+ "opacity": 0.2
+ });
+ d.data('clicked', false);
+ d.data('showText', false);
+ });
+ that.links.forEach(function (d) {
+ d.attr({
+ 'stroke-opacity': 0.0
+ });
+ });
+ that.clickedNum = this.data('index');
+ this.data('clicked', true);
+ this.data("link").forEach(function (d) {
+ d.attr({
+ "stroke-opacity": d.data('opacity')
+ });
+ });
+ this.data("node").forEach(function (d) {
+ d.attr({
+ "opacity": 0.9
+ });
+ d.data('showText', true);
+ });
+ that.underBn[this.data('colorType')].data('clicked', true).attr('opacity', 1).show();
+ } else {
+ that.clicked = false;
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
+ }
+ nodes.forEach(function (d) {
+ d.attr({
+ "opacity": 0.9
+ });
+ if (d.data('big')) {
+ d.data('showText', true);
+ } else {
+ d.data('rect').hide();
+ d.data('text').hide();
+ d.data('showText', false);
+ }
+ });
+ links.forEach(function (d) {
+ d.attr({
+ 'stroke-opacity': d.data('opacity')
+ });
+ });
+ this.data('clicked', false);
+ that.underBn[this.data('colorType')].hide();
+ }
+ };
+
+ var onCanvasClick = function () {
+ that.legendClicked = false;
+ that.underBn.forEach(function (d) {
+ d.hide();
+ d.data('clicked', false);
+ });
+ that.clicked = false;
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
+ } else {
+ that.force.resume();
+ }
+ nodes.forEach(function (d) {
+ d.attr({
+ "opacity": 0.9
+ });
+ if (d.data('big')) {
+ d.data('showText', true);
+ } else {
+ d.data('rect').hide();
+ d.data('text').hide();
+ d.data('showText', false);
+ }
+ });
+ links.forEach(function (d) {
+ d.attr({
+ 'stroke-opacity': d.data('opacity')
+ });
+ });
+ };
+
+ var topValue = [];
+ var topId = [];
+ var topNum = 10;
+ if (nodesNum < 10) {
+ topNum = nodesNum;
+ }
+ for (i = 0; i < topNum; i++) {
+ topValue[i] = nodesData[i].nodeValue;
+ topId[i] = i;
+ }
+ for (i = 0; i < topNum; i++) {
+ for (j = 1; j < topNum - i; j++) {
+ if (topValue[j] < topValue[j - 1]) {
+ temp = topValue[j];
+ topValue[j] = topValue[j - 1];
+ topValue[j - 1] = temp;
+ temp = topId[j];
+ topId[j] = topId[j - 1];
+ topId[j - 1] = temp;
+ }
+ }
+ }
+ //rapheal绘制部分
+ for (i = 0; i < nodesNum; i++) {
+ nodesData[i].x = (conf.width + this.xOffset) / 2;
+ nodesData[i].y = conf.height / 2;
+ var n = nodesData[i];
+ var k = Math.floor(conf.classNum * (n.nodeValue - this.nodeValueMin) / (this.nodeValueMax - this.nodeValueMin));
+ if (k >= conf.classNum) k = conf.classNum - 1;
+ var radius = this.getRadius(n.nodeValue);
+ var cnode = canvas.circle(n.x, n.y, radius).attr({
+ fill: this.getColor(k),
+ 'stroke': "#ffffff",
+ 'opacity': 0.9
+ //title: n.name
+ });
+ var nodeText = canvas.text(n.x, n.y - radius, n.name).attr({
+ 'opacity': 1,
+ //'font-family': "微软雅黑",
+ 'font': '12px Verdana'
+ }).hide();
+ var nodeRect = canvas.rect(n.x, n.y, nodeText.getBBox().width, nodeText.getBBox().height, 2).attr({
+ 'fill': "#000000",
+ 'stroke-opacity': 0,
+ 'fill-opacity': 0.1
+ }).hide();
+ cnode.data('r', radius);
+ cnode.data("name", n.name);
+ cnode.data('text', nodeText);
+ cnode.data('rect', nodeRect);
+ cnode.data('colorType', k);
+ cnode.data('clicked', false);
+ cnode.data('index', i);
+ cnode.data('big', false);
+
+ if (i >= topNum && topValue[0] < nodesData[i].nodeValue) {
+ topValue[0] = nodesData[i].nodeValue;
+ topId[0] = i;
+ for (j = 1; j < topNum; j++) {
+ if (topValue[j] < topValue[j - 1]) {
+ temp = topValue[j];
+ topValue[j] = topValue[j - 1];
+ topValue[j - 1] = temp;
+ temp = topId[j];
+ topId[j] = topId[j - 1];
+ topId[j - 1] = temp;
+ } else {
+ break;
+ }
+ }
+ }
+
+ nodes.push(cnode);
+ connectMatrix[i] = [];
+ linkMatrix[i] = [];
+ connectMatrix[i].push(nodes[i]);
+ }
+
+ for (i = 0; i < topNum; i++) {
+ nodes[topId[i]].data("big", true);
+ }
+
+
+ for (i = 0; i < linksNum; i++) {
+ var l = linksData[i];
+ var clink = canvas.path("M" + l.source.x + "," + l.source.y + "L" + l.target.x + "," + l.target.y).attr({
+ 'stroke-width': conf.linkWidth,
+ 'stroke-opacity': this.getOpacity(l.value)
+ }).toBack();
+ clink.data('opacity', this.getOpacity(l.value));
+ links.push(clink);
+ connectMatrix[l.source.index].push(nodes[l.target.index]);
+ connectMatrix[l.target.index].push(nodes[l.source.index]);
+ linkMatrix[l.source.index].push(links[i]);
+ linkMatrix[l.target.index].push(links[i]);
+ }
+
+ var background = canvas.rect(0, 0, conf.width, conf.height).attr({
+ 'fill': '#ffffff',
+ 'stroke-opacity': 0
+ }).toBack();
+ background.click(onCanvasClick);
+
+ nodes.forEach(function (d, i) {
+ d.data("node", connectMatrix[i]);
+ d.data("link", linkMatrix[i]);
+ if (d.data('big')) {
+ d.data('showText', true);
+ } else {
+ d.data('showText', false);
+ }
+ d.drag(function (dx, dy) {
+ d.data('x', this.ox + dx);
+ d.data('y', this.oy + dy);
+ }, function () {
+ that.force.resume();
+ this.ox = this.attr("cx");
+ this.oy = this.attr("cy");
+ d.data('x', this.ox);
+ d.data('y', this.oy);
+ d.data('drag', true);
+ }, function () {
+ that.force.resume();
+ d.data('drag', false);
+ });
+ d.click(onMouseClick); //.mouseup(onmouseup);
+ d.mouseover(function () {
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ that.force.resume();
+ }
+ this.attr({
+ 'r': d.data('r') + 5
+ });
+ if (!this.data('showText')) {
+ //this.attr('title', "");
+ this.data('showText', true);
+ this.data('hover', true);
+ }
+ if (!that.underBn[this.data('colorType')].data('clicked')) {
+ that.underBn[this.data('colorType')].attr('opacity', 0.5).show();
+ }
+ }).mouseout(function () {
+ this.attr({
+ 'r': d.data('r')
+ });
+ //this.attr('title', this.data('name'));
+ if (this.data('hover') && !this.data('clicked')) {
+ d.data('rect').hide();
+ d.data('text').hide();
+ this.data('showText', false);
+ this.data('hover', false);
+ }
+ if (!that.underBn[this.data('colorType')].data('clicked')) {
+ that.underBn[this.data('colorType')].hide();
+ }
+ });
+ });
+
+ };
+
+
+ /**
+ * 绘制图例
+ */
+ Force.prototype.legend = function () {
+ var that = this;
+ var conf = this.defaults;
+ var paper = this.canvas;
+ var legendArea = this.legendArea;
+ var rectBn = paper.set();
+ this.underBn = [];
+ var underBn = this.underBn;
+ for (i = 0; i <= conf.classNum - 1; i++) {
+ //底框
+ underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "#ebebeb",
+ "stroke": "none",
+ 'opacity': 1
+ }).data('clicked', false).hide());
+ //色框
+ paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({
+ "fill": that.getColor(i),
+ "stroke": "none"
+ });
+ //文字
+ var min = Math.floor(this.nodeValueMin + i * (this.nodeValueMax - this.nodeValueMin) / conf.classNum);
+ var max = Math.floor(min + (this.nodeValueMax - this.nodeValueMin) / conf.classNum);
+ paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, min + " ~ " + max).attr({
+ "fill": "black",
+ "fill-opacity": 1,
+ "font-family": "Verdana",
+ "font-size": 12
+ }).attr({
+ "text-anchor": "start"
+ });
+ //选框
+ rectBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({
+ "fill": "white",
+ "fill-opacity": 0,
+ "stroke": "none"
+ //"r": 3
+ })).data("clicked", 0);
+ }
+ rectBn.forEach(function (d, i) {
+ d.mouseover(function () {
+ if (!underBn[i].data('clicked')) {
+ underBn[i].attr('opacity', 0.5);
+ underBn[i].show();
+ }
+ }).mouseout(function () {
+ if (!underBn[i].data('clicked')) {
+ underBn[i].hide();
+ }
+ });
+ d.click(function () {
+ that.clicked = false;
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ that.force.linkDistance(conf.linkLength).charge(conf.charge).start();
+ }
+ for (j = 0; j < underBn.length; j++) {
+ if (j === i) {
+ underBn[j].show();
+ } else {
+ underBn[j].hide();
+ }
+ }
+ rectBn.forEach(function (eachBn) {
+ if (eachBn !== d) {
+ eachBn.data("clicked", 0);
+ }
+ });
+ if (d.data("clicked") === 0) {
+ that.legendClicked = true;
+ underBn[i].attr('opacity', 1);
+ underBn[i].data('clicked', true);
+ underBn[i].show();
+ that.nodes.forEach(function (d) {
+ if (d.data('colorType') === i) {
+ d.attr({
+ "opacity": 0.9
+ });
+ d.data('showText', true);
+ } else {
+ d.attr({
+ "opacity": 0.2
+ });
+ d.data('rect').hide();
+ d.data('text').hide();
+ d.data('showText', false);
+ }
+ });
+ that.links.forEach(function (d) {
+ d.attr({
+ "stroke-opacity": 0
+ });
+ });
+ d.data("clicked", 1);
+ } else if (d.data("clicked") === 1) {
+ that.legendClicked = false;
+ underBn[i].data('clicked', false);
+ underBn[i].hide();
+ d.data("clicked", 0);
+ that.nodes.forEach(function (d) {
+ d.attr({
+ "opacity": 0.9
+ });
+ if (d.data('big')) {
+ d.data('showText', true);
+ } else {
+ d.data('rect').hide();
+ d.data('text').hide();
+ d.data('showText', false);
+ }
+ });
+ that.links.forEach(function (d) {
+ d.attr({
+ "stroke-opacity": d.data('opacity')
+ });
+ });
+ }
+ });
+ });
+ };
+
+
+ /**
+ * create the force-direct layout
+ */
+ Force.prototype.layout = function () {
+ var conf = this.defaults;
+ this.force = d3.layout.force().linkDistance(conf.linkLength).size([conf.width + this.xOffset, conf.height]).theta(1.5);
+ };
+
+ /**
+ * update the force-direct layout animation
+ */
+ Force.prototype.animate = function () {
+ var conf = this.defaults;
+ var nodes = this.nodes;
+ var links = this.links;
+ var tick = 0;
+ var that = this;
+
+ var nodesData = this.net.nodes;
+ var linksData = this.net.links;
+
+ this.force.on("tick", function () {
+ if (conf.browserName !== "Microsoft Internet Explorer" || tick > conf.iterate) {
+ if (tick % 2 === 0) {
+ nodes.forEach(function (d, i) {
+ var margin = d.data('r');
+ var nd = nodesData[i];
+ if (d.data('drag')) {
+ nd.x = d.data('x');
+ nd.y = d.data('y');
+ }
+ nd.x = (nd.x < margin + that.xOffset) ? (margin + that.xOffset) : nd.x;
+ nd.x = (nd.x > conf.width - margin) ? conf.width - margin : nd.x;
+ nd.y = (nd.y < margin) ? margin : nd.y;
+ nd.y = (nd.y > conf.height - margin) ? conf.height - margin : nd.y;
+ var bx = d.data('text').getBBox().width / 2;
+ var by = d.data('text').getBBox().height / 2;
+
+ if (that.clicked) {
+ var mx = nodesData[that.clickedNum].x;
+ var my = nodesData[that.clickedNum].y;
+ var tx, ty;
+ if (d.data('clicked')) {
+
+ if (conf.browserName !== "Microsoft Internet Explorer") {
+ nd.x = (conf.width + that.xOffset) / 2;
+ nd.y = conf.height / 2;
+ }
+ d.data('rect').attr({
+ 'x': nd.x - bx,
+ 'y': nd.y + d.data('r')
+ });
+ d.data('text').attr({
+ 'x': nd.x,
+ 'y': nd.y + by + d.data('r')
+ });
+ d.data('rect').show();
+ d.data('text').show();
+ } else if (d.data('showText')) {
+ var lx = (nd.x - mx);
+ var ly = (nd.y - my);
+ var length = Math.sqrt(lx * lx + ly * ly);
+ tx = nd.x + bx * lx / length;
+ ty = nd.y + by * ly / length;
+ tx = (nd.x < mx) ? tx - d.data('r') : tx + d.data('r');
+ ty = (nd.y < my) ? ty - d.data('r') : ty + d.data('r');
+ tx = (tx < margin + that.xOffset) ? (margin + that.xOffset) : tx;
+ tx = (tx > conf.width - margin) ? conf.width - margin : tx;
+ ty = (ty < margin) ? margin : ty;
+ ty = (ty > conf.height - margin) ? conf.height - margin : ty;
+ d.data('rect').attr({
+ 'x': tx - bx,
+ 'y': ty - by
+ });
+ d.data('text').attr({
+ 'x': tx,
+ 'y': ty
+ });
+ d.data('rect').show();
+ d.data('text').show();
+ }
+ } else if (d.data('showText')) {
+ d.data('rect').attr({
+ 'x': nd.x - bx,
+ 'y': nd.y - by + 4 + d.data('r')
+ });
+ d.data('text').attr({
+ 'x': nd.x,
+ 'y': nd.y + 4 + d.data('r')
+ });
+ try {
+ d.data('rect').show();
+ d.data('text').show();
+ } catch (e) {}
+
+ }
+ d.attr({
+ 'cx': nd.x,
+ 'cy': nd.y
+ });
+ });
+ links.forEach(function (d, i) {
+ d.attr('path', "M" + linksData[i].source.x + "," + linksData[i].source.y + "L" + linksData[i].target.x + "," + linksData[i].target.y);
+ });
+ }
+ }++tick;
+ });
+ };
+
+ /**
+ *render the force-directed net on the canvas and keep updating
+ * @param {Object} options user options
+ */
+ Force.prototype.render = function (options) {
+ this.setOptions(options);
+ this.canvas.clear();
+ this.layout();
+ if (this.defaults.legend) {
+ this.legend();
+ }
+ this.update();
+ this.animate();
+ };
+
+ return Force;
});
\ No newline at end of file
diff --git a/lib/charts/line.js b/lib/charts/line.js
index ce35db0..22f9330 100644
--- a/lib/charts/line.js
+++ b/lib/charts/line.js
@@ -1,589 +1,589 @@
-/*global Raphael, _, $ */
-;(function (name, definition) {
- if (typeof define === 'function') {
- define(definition);
- } else {
- this[name] = definition(function (id) { return this[id];});
- }
-})('Line', function (require) {
- var DataV = require('DataV');
- var theme = DataV.Themes;
-
- /**
- * Line构造函数,继承自Chart
- * Options:
- *
- * - `width` 数字,画布宽度,默认为960,表示图片高960px
- * - `height` 数字,画布高度,默认为500
- * - `margin` 数组,这个折线图在画布中四周的留白,长度为4,分别代表[top, right, bottom, left], 默认值为[10, 40, 40, 40]
- * - `title` 字符串,一级标题, 默认值为null
- * - `subtitle` 字符串,副标题, 默认值为null
- * - `imagePath 字符串,折线图提供背景图片加载路径,默认值为null
- * - `clickMode 布尔值,是否使用默认的点击事件,默认点击事件为点击折线即选中当前折线,点击空白部分取消选中,默认值为true
- * - `hoverMode 布尔值,是否使用默认的悬停事件,默认悬停事件为当前悬停所在折线高亮,如果在节点上悬停则节点高亮,同一X维度节点也高亮, 默认值为true
- * - `nodeMode 布尔值,是否需要显示节点,默认值为true
- * - `lineSize 数字,折线粗细,默认值为2
- * - `gridSize 数字,网格线粗细,默认值为1
- * - `nodeRadius 数字,节点圆形半径,默认值为2
- * - `hasXAxis 布尔值,是否需要绘制x坐标轴,默认值为true
- * - `hasYAxis 布尔值,是否需要绘制y坐标轴,默认值为true
- * - `xAxisTick 数字,x轴刻度的跨度,默认值为10
- * - `yAxisTick 数字,y轴刻度的跨度,默认值为10
- * - `xAxisOrient 字符串,x坐标轴位置,可选值为"top"和"bottom",默认值为"bottom"
- * - `yAxisOrient 字符串,x坐标轴位置,可选值为"left"和"right",默认值为"left"
- * - `xAxisPadding 数字,x轴与x轴刻度值说明文字的距离,默认值为10
- * - `yAxisPadding 数字,y轴与y轴刻度值说明文字的距离,默认值为10
- * - `xAxisFontSize 数字,x轴说明文字字号,默认值为10
- * - `yAxisFontSize 数字,y轴说明文字字号,默认值为10
- * - `xAxisStartDx 数字,x轴刻度起始位置与坐标轴最左端水平距离,默认值为20
- * - `yAxisStartDy 数字,y轴刻度起始位置与坐标轴最下端垂直距离,默认值为10
- * - `xAxisEndDx 数字,x轴刻度起始位置与坐标轴最右端水平距离,默认值为10
- * - `yAxisEndDy 数字,y轴刻度起始位置与坐标轴最上端水平距离,默认值为10
- * - `textLean 布尔值,是否将x轴说明文字倾斜摆放,默认值为false
- * - `hasXGrid 布尔值,是否需要绘制与x坐标轴平行的网格线,默认值为true
- * - `hasYGrid 布尔值,是否需要绘制与x坐标轴平行的网格线,默认值为true
- * - `backgroundColor 字符串,折线图背景填充色,默认值为"#fff"
- * - `titleColor 字符串,标题文字颜色,默认值为"#000"
- * - `subtitleColor 字符串,副标题文字颜色,默认值为"#000"
- * - `AxisColor 字符串,坐标轴填充颜色,默认值为"#000"
- * - `gridColor 字符串,网格线填充颜色,默认值为"#000"
- * - `unchosen0pacity 数字,当有折线被选中时,其他淡出折线的透明度,默认值为0.15
- * - `grid0pacity 数字,网格线透明度,默认值为0.1
- * - `chosenGrid0pacity 数字,高亮网格线透明度,默认值为0.5
-
- * Create line in a dom node with id "chart", width is 500px; height is 600px;
- * Examples:
- * ```
- * var line = new Line("chart", {"width": 500, "height": 600});
- * ```
- * @param {Mix} node The dom node or dom node Id
- * @param {Object} options options json object for determin line style.
- */
- var Line = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Line";
- this.node = this.checkContainer(node);
-
- /**
- * 线纬度
- */
- this.dimension.line = {
- type: "string",
- required: true,
- index: 0
- };
- /**
- * 值纬度
- */
- this.dimension.x = {
- type: "string",
- required: true,
- index: 1
- };
- /**
- * 值纬度
- */
- this.dimension.value = {
- type: "number",
- required: true,
- index: 2
- };
-
- this.defaults.width = 960;
- this.defaults.height = 500;
-
- //Properties
- this.defaults.margin = [10, 40, 40, 40];
- this.defaults.title = null;
- this.defaults.subtitle = null;
- this.defaults.imagePath = null;
-
- this.defaults.clickMode = true;
- this.defaults.hoverMode = true;
-
- //绘制属性
- this.defaults.lineSize = 2;
- this.defaults.gridSize = 1;
- this.defaults.nodeMode = true;
- this.defaults.nodeRadius = 2;
- this.defaults.hasXAxis = true;
- this.defaults.hasYAxis = true;
- this.defaults.xAxisTick = 10;
- this.defaults.yAxisTick = 4;
- this.defaults.xAxisOrient = "bottom";
- this.defaults.yAxisOrient = "left";
- this.defaults.xAxisPadding = 10;
- this.defaults.yAxisPadding = 10;
- this.defaults.xAxisFontSize = 10;
- this.defaults.yAxisFontSize = 10;
- this.defaults.xAxisStartDx = 20;
- this.defaults.yAxisStartDy = 10;
- this.defaults.xAxisEndDx = 10;
- this.defaults.yAxisEndDy = 10;
-
- this.defaults.textLean = false;
-
- this.defaults.hasXGrid = true;
- this.defaults.hasYGrid = true;
-
- //Color
- this.defaults.backgroundColor = "#fff";
- this.defaults.titleColor = "#000";
- this.defaults.subtitleColor = "#000";
- this.defaults.AxisColor = "#000";
- this.defaults.gridColor = "#000";
- this.defaults.unchosen0pacity = 0.15;
- this.defaults.grid0pacity = 0.1;
- this.defaults.chosenGrid0pacity = 0.5;
-
- //设置用户指定属性
- this.setOptions(options);
-
- //创建画布
- this.createCanvas();
- }
- });
-
- /**
- * 创建画布
- */
- Line.prototype.createCanvas = function () {
- var conf = this.defaults;
-
- this.canvas = new Raphael(this.node, conf.width, conf.height);
-
- this.canvasF = this.node;
-
- var canvasStyle = this.canvasF.style;
- canvasStyle.position = "relative";
- this.floatTag = DataV.FloatTag()(this.canvasF);
- this.floatTag.css({
- "visibility": "hidden"
- });
- };
-
- /**
- * 对原始数据进行处理
- */
- Line.prototype.setSource = function (source, map) {
- map = this.map(map);
- var dataTable;
- if (DataV.detect(source) === 'Table_WITH_HEAD') {
- dataTable = DataV.collectionify(source);
- } else {
- dataTable = source;
- }
-
- var lines = _.groupBy(dataTable, map.line);
- var linesData = [];
-
- var max, min;
- var maxList = [], minList = [];
- this.maxLength = Math.max.apply(null, _.map(lines, function (line) {
- return line.length;
- }));
-
- var titles;
- _.forEach(lines, function (points, name) {
- // initialize the nodes of line
- var line = {name: name, id: name, data: [], tags: []};
- line.data = _.pluck(points, map.value);
- titles = _.pluck(points, map.x);
- linesData.push(line);
- maxList[name] = Math.max.apply(null, line.data);
- minList[name] = Math.min.apply(null, line.data);
- });
-
- var conf = this.defaults;
- var margin = conf.margin;
- this.xWidth = conf.width - margin[1] - margin[3];
- this.yHeight = conf.height - margin[0] - margin[2];
-
- this.titles = titles;
- this.linesData = linesData;
- this.max = Math.max.apply(null, _.values(maxList));
- this.min = Math.min.apply(null, _.values(minList));
- this.maxList = maxList;
- this.minList = minList;
-
- this.chosen = false;
- this.chosenNum = -1;
- };
-
- /**
- * 获取颜色函数
- * @return {function} DataV根据主题获取随机离散色函数
- */
- Line.prototype.getColor = function () {
- var colorFunction = DataV.getDiscreteColor();
- return colorFunction;
- };
-
- /**
- * 绘制X轴
- */
- Line.prototype.setXaxis = function (titles) {
- var conf = this.defaults;
- var canvas = this.canvas;
-
- if (conf.hasXAxis) {
- var lineSize = conf.lineSize;
- var gridSize = conf.gridSize;
- var axisColor = conf.AxisColor;
- var gridColor = conf.gridColor;
- var grid0pacity = conf.grid0pacity;
- var xAxisPadding = conf.xAxisPadding;
- var margin = conf.margin;
- var xAxisStartDx = conf.xAxisStartDx;
- var xAxisEndDx = conf.xAxisEndDx;
- var startX = margin[3] + xAxisStartDx;
- var startY = conf.height - margin[2];
- var xWidth = this.xWidth;
- var xDataArea = xWidth - xAxisStartDx - xAxisEndDx;
- var maxLength = this.maxLength;
- var yHeight = this.yHeight;
- var fontSize = conf.xAxisFontSize;
- var textLean = conf.textLean;
-
- var tickLength;
- if (conf.hasXGrid) {
- tickLength = yHeight;
- } else {
- tickLength = 5;
- }
-
- var tick = conf.xAxisTick;
- var tickStep;
- if (tick % maxLength === 0) {
- tickStep = xDataArea / (tick - 1);
- } else {
- tickStep = xDataArea / (this.maxLength - 1);
- tick = this.maxLength;
- }
-
- var xAxis = canvas.set();
- var xGrid = canvas.set();
- var xAxisText = canvas.set();
- xAxis.push(canvas.path("M" + (startX - xAxisStartDx) + "," + startY + "L" + (xWidth + margin[3]) + "," + startY));
-
- var l = titles.length;
- for (var i = 0; i < tick; i++) {
- xGrid.push(canvas.path("M" + (startX + i * tickStep) + "," + startY + "L" + (startX + i * tickStep) + "," + (startY - tickLength)));
- if (i < l) {
- var thisText = canvas.text((startX + i * tickStep), startY + xAxisPadding, titles[i]);
- if (textLean) {
- var d = thisText.getBBox().width / 2;
- var angle = 45 / 360 * Math.PI;
- thisText.transform("r45t" + Math.cos(angle) * d + "," + Math.sin(angle) * d);
- }
- xAxisText.push(thisText);
- }
- }
-
- xAxis.attr({fill: axisColor, "stroke-width": lineSize});
- xGrid.attr({"stroke": gridColor, "stroke-width": gridSize, "stroke-opacity": grid0pacity});
- xAxisText.attr({"font-size": fontSize});
- this.xAxis = xAxis;
- this.xGrid = xGrid;
- this.xAxisText = xAxisText;
- this.xTick = xDataArea / (maxLength - 1);
- }
- };
-
- /**
- * 绘制Y轴
- */
- Line.prototype.setYaxis = function () {
- var conf = this.defaults;
- var canvas = this.canvas;
-
- if (conf.hasYAxis) {
- var lineSize = conf.lineSize;
- var gridSize = conf.gridSize;
- var axisColor = conf.AxisColor;
- var gridColor = conf.gridColor;
- var grid0pacity = conf.grid0pacity;
- var margin = conf.margin;
- var yAxisStartDy = conf.yAxisStartDy;
- var yAxisEndDy = conf.yAxisEndDy;
- var yHeight = this.yHeight;
- var yDataArea = this.yHeight - yAxisStartDy - yAxisEndDy;
- var xWidth = this.xWidth;
- var startX = margin[3];
- var startY = margin[0] + yHeight - yAxisStartDy;
- var yAxisPadding = conf.yAxisPadding;
- var max = this.max;
- var min = this.min;
- var fontSize = conf.yAxisFontSize;
-
- var tick = conf.yAxisTick;
- var tickStep = yDataArea / (tick - 1);
-
- var d = (max - min) % (tick - 1);
- if (d !== 0) {
- max = max + (tick - 1) - d;
- }
- d = (max - min) / (tick - 1);
-
- var tickLength;
- if (conf.hasYGrid) {
- tickLength = xWidth;
- } else {
- tickLength = 5;
- }
-
- var yAxis = canvas.set();
- var yGrid = canvas.set();
- var yAxisText = canvas.set();
- yAxis.push(canvas.path("M" + startX + "," + (startY + yAxisStartDy) + "L" + startX + "," + margin[0]));
-
- for (var i = 0; i < tick; i++) {
- yGrid.push(canvas.path("M" + startX + "," + (startY - i * tickStep) + "L" + (startX + tickLength) + "," + (startY - i * tickStep)));
- yAxisText.push(canvas.text(startX - yAxisPadding, (startY - i * tickStep), (min + i * d)));
- }
-
- yAxis.attr({fill: axisColor, "stroke-width": lineSize});
- yGrid.attr({"stroke": gridColor, "stroke-width": gridSize, "stroke-opacity": grid0pacity});
- yAxisText.attr({"font-size": fontSize});
- this.yAxis = yAxis;
- this.yGrid = yGrid;
- this.yAxisText = yAxisText;
- this.yDataArea = yDataArea;
-
- this.yMatchNum = tickStep / d;
- this.y0 = startY;
- }
- };
-
- /**
- * 绘制背景
- */
- Line.prototype.setBackground = function () {
- var conf = this.defaults;
- var canvas = this.canvas;
- var backgroundColor = conf.backgroundColor;
- var imagePath = conf.imagePath;
- var xWidth = this.xWidth;
- var yHeight = this.yHeight;
- var yAxisStartDy = conf.yAxisStartDy;
- var x0 = conf.margin[3];
- var y0 = this.y0 + yAxisStartDy;
-
- var rect;
- if (imagePath !== null) {
- rect = canvas.image(imagePath, x0, (y0 - yHeight), xWidth, yHeight);
- } else {
- rect = canvas.rect(x0, (y0 - yHeight), xWidth, yHeight).attr({"fill": backgroundColor,"fill-opacity": 0, "stroke": "none"});
- }
-
- rect.toBack();
- this.background = rect;
- };
-
- /**
- * 渲染折线图
- * Examples:
- * ```
- * var line = new Line("chart");
- * line.setSource(source);
- * line.render();
- * ```
- * @param {object} options options json object for determin line style.
- */
- Line.prototype.render = function () {
- this.canvas.clear();
- this.createCanvas();
- var conf = this.defaults;
- var linesData = this.linesData;
- var nodesList = [];
-
- this.setXaxis(this.titles);
- this.setYaxis();
-
- this.setBackground();
-
- var canvas = this.canvas;
- var nodeMode = conf.nodeMode;
- var getColor = this.getColor();
- var lineSize = conf.lineSize;
- var min = this.min;
- var xAxisStartDx = conf.xAxisStartDx;
- var xTick = this.xTick;
- var yMatchNum = this.yMatchNum;
- var x0 = conf.margin[3] + xAxisStartDx;
- var y0 = this.y0;
- var radius = conf.nodeRadius;
-
- var lines = canvas.set();
- linesData.forEach(function (d, i) {
- var nodeData = d.data;
- var nodes = canvas.set();
- var color = getColor(i);
- var linePath = "M";
-
- for (var j = 0, l = nodeData.length; j < l; j++) {
- var x = x0 + xTick * (j);
- var y = y0 - ((nodeData[j] - min) * yMatchNum);
-
- linePath = linePath + x + "," + y;
-
- if (j < l - 1) {
- linePath = linePath + "L";
- }
-
- if (nodeMode) {
- var thisNode = canvas.circle(x, y, radius * 2).attr({fill: color, "stroke": "none"});
- thisNode.data('num', j);
- thisNode.data('lineNum', i);
- thisNode.data('data', nodeData[j]);
- nodes.push(thisNode);
- }
- }
-
- lines.push(canvas.path(linePath).attr({"stroke": color, "stroke-width": lineSize}).data('num', i));
- if (nodeMode) {
- nodesList.push(nodes.toFront());
- }
- });
-
- this.lines = lines;
- this.nodesList = nodesList;
-
- this.interactive();
- };
-
- /**
- * 添加交互选项
- */
- Line.prototype.interactive = function () {
- var that = this;
- var conf = this.defaults;
- var hoverMode = conf.hoverMode;
- var clickMode = conf.clickMode;
- var nodeMode = conf.nodeMode;
- var chosen = this.chosen;
- var chosenNum = this.chosenNum;
- var lines = this.lines;
- var uo = conf.unchosen0pacity;
- var nodesList = this.nodesList;
- var xGrid = this.xGrid;
- var grid0pacity = conf.grid0pacity;
- var cgo = conf.chosenGrid0pacity;
- var xAxisText = this.xAxisText;
- var xAxisFontSize = conf.xAxisFontSize;
-
- var highLight = function (num) {
- var line = lines[num];
- lines.attr({"stroke-opacity": uo});
-
- nodesList.forEach(function (d) {
- d.attr({"fill-opacity": uo});
- });
-
- line.attr({"stroke-opacity": 1}).toFront();
- nodesList[num].attr({"fill-opacity": 1}).toFront();
- };
-
- var unhighLinght = function () {
- lines.forEach(function (d) {
- d.attr({"stroke-opacity": 1});
- });
- nodesList.forEach(function (d) {
- d.attr({"fill-opacity": 1});
- });
- };
-
- var background = this.background;
-
- if (clickMode){
- background.click(function () {
- if (chosen) {
- unhighLinght();
- chosen = false;
- that.chosen = chosen;
- }
- });
- }
-
- var floatTag = this.floatTag;
- $(this.node).append(this.floatTag);
- // if (hoverMode) {
- // background.mouseover(function () {
-
- // }).mouseout(function () {
- // floatTag.css({"visibility" : "hidden"});
- // });
- // }
-
- lines.forEach(function (d) {
- if (hoverMode) {
- d.mouseover(function () {
- if (!chosen) {
- highLight(d.data('num'));
- }
- }).mouseout(function () {
- if (!chosen) {
- unhighLinght();
- }
- });
- }
-
- if (clickMode){
- d.click(function () {
- chosenNum = d.data('num');
- highLight(chosenNum);
-
- chosen = true;
- that.chosen = chosen;
- });
- }
- });
-
- if (nodeMode){
- var radius = conf.nodeRadius;
-
- nodesList.forEach(function (d) {
- d.forEach (function (d) {
- if (hoverMode) {
- var nodeNum = d.data('num');
- d.mouseover(function () {
- d.animate({r: (radius + 2) * 2}, 100);
- xGrid[nodeNum].animate({'stroke-opacity': cgo}, 100);
- xAxisText[nodeNum].animate({'font-size': xAxisFontSize * 2}, 100);
- floatTag.html('' + d.data('data') + '
');
- floatTag.css({"visibility": "visible"});
- if (!chosen) {
- highLight(d.data("lineNum"));
- nodesList.forEach(function (d) {
- if (nodeNum < d.length) {
- d[nodeNum].attr({"fill-opacity": 1});
- }
- });
- }
- }).mouseout(function () {
- d.animate({r: radius * 2}, 100);
- xGrid[nodeNum].animate({'stroke-opacity': grid0pacity}, 100);
- xAxisText[nodeNum].animate({'font-size': xAxisFontSize}, 100);
- floatTag.css({"visibility": "hidden"});
- if (!chosen) {
- unhighLinght();
- }
- });
- }
-
- if (clickMode){
- d.click(function () {
- chosenNum = d.data('lineNum');
- highLight(chosenNum);
-
- chosen = true;
- that.chosen = chosen;
- });
- }
- });
- });
- }
- };
-
- return Line;
+/*global Raphael, _, $ */
+;(function (name, definition) {
+ if (typeof define === 'function') {
+ define(definition);
+ } else {
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Line', function (require) {
+ var DataV = require('DataV');
+ var theme = DataV.Themes;
+
+ /**
+ * Line构造函数,继承自Chart
+ * Options:
+ *
+ * - `width` 数字,画布宽度,默认为960,表示图片高960px
+ * - `height` 数字,画布高度,默认为500
+ * - `margin` 数组,这个折线图在画布中四周的留白,长度为4,分别代表[top, right, bottom, left], 默认值为[10, 40, 40, 40]
+ * - `title` 字符串,一级标题, 默认值为null
+ * - `subtitle` 字符串,副标题, 默认值为null
+ * - `imagePath 字符串,折线图提供背景图片加载路径,默认值为null
+ * - `clickMode 布尔值,是否使用默认的点击事件,默认点击事件为点击折线即选中当前折线,点击空白部分取消选中,默认值为true
+ * - `hoverMode 布尔值,是否使用默认的悬停事件,默认悬停事件为当前悬停所在折线高亮,如果在节点上悬停则节点高亮,同一X维度节点也高亮, 默认值为true
+ * - `nodeMode 布尔值,是否需要显示节点,默认值为true
+ * - `lineSize 数字,折线粗细,默认值为2
+ * - `gridSize 数字,网格线粗细,默认值为1
+ * - `nodeRadius 数字,节点圆形半径,默认值为2
+ * - `hasXAxis 布尔值,是否需要绘制x坐标轴,默认值为true
+ * - `hasYAxis 布尔值,是否需要绘制y坐标轴,默认值为true
+ * - `xAxisTick 数字,x轴刻度的跨度,默认值为10
+ * - `yAxisTick 数字,y轴刻度的跨度,默认值为10
+ * - `xAxisOrient 字符串,x坐标轴位置,可选值为"top"和"bottom",默认值为"bottom"
+ * - `yAxisOrient 字符串,x坐标轴位置,可选值为"left"和"right",默认值为"left"
+ * - `xAxisPadding 数字,x轴与x轴刻度值说明文字的距离,默认值为10
+ * - `yAxisPadding 数字,y轴与y轴刻度值说明文字的距离,默认值为10
+ * - `xAxisFontSize 数字,x轴说明文字字号,默认值为10
+ * - `yAxisFontSize 数字,y轴说明文字字号,默认值为10
+ * - `xAxisStartDx 数字,x轴刻度起始位置与坐标轴最左端水平距离,默认值为20
+ * - `yAxisStartDy 数字,y轴刻度起始位置与坐标轴最下端垂直距离,默认值为10
+ * - `xAxisEndDx 数字,x轴刻度起始位置与坐标轴最右端水平距离,默认值为10
+ * - `yAxisEndDy 数字,y轴刻度起始位置与坐标轴最上端水平距离,默认值为10
+ * - `textLean 布尔值,是否将x轴说明文字倾斜摆放,默认值为false
+ * - `hasXGrid 布尔值,是否需要绘制与x坐标轴平行的网格线,默认值为true
+ * - `hasYGrid 布尔值,是否需要绘制与x坐标轴平行的网格线,默认值为true
+ * - `backgroundColor 字符串,折线图背景填充色,默认值为"#fff"
+ * - `titleColor 字符串,标题文字颜色,默认值为"#000"
+ * - `subtitleColor 字符串,副标题文字颜色,默认值为"#000"
+ * - `AxisColor 字符串,坐标轴填充颜色,默认值为"#000"
+ * - `gridColor 字符串,网格线填充颜色,默认值为"#000"
+ * - `unchosen0pacity 数字,当有折线被选中时,其他淡出折线的透明度,默认值为0.15
+ * - `grid0pacity 数字,网格线透明度,默认值为0.1
+ * - `chosenGrid0pacity 数字,高亮网格线透明度,默认值为0.5
+
+ * Create line in a dom node with id "chart", width is 500px; height is 600px;
+ * Examples:
+ * ```
+ * var line = new Line("chart", {"width": 500, "height": 600});
+ * ```
+ * @param {Mix} node The dom node or dom node Id
+ * @param {Object} options options json object for determin line style.
+ */
+ var Line = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Line";
+ this.node = this.checkContainer(node);
+
+ /**
+ * 线纬度
+ */
+ this.dimension.line = {
+ type: "string",
+ required: true,
+ index: 0
+ };
+ /**
+ * 值纬度
+ */
+ this.dimension.x = {
+ type: "string",
+ required: true,
+ index: 1
+ };
+ /**
+ * 值纬度
+ */
+ this.dimension.value = {
+ type: "number",
+ required: true,
+ index: 2
+ };
+
+ this.defaults.width = 960;
+ this.defaults.height = 500;
+
+ //Properties
+ this.defaults.margin = [10, 40, 40, 40];
+ this.defaults.title = null;
+ this.defaults.subtitle = null;
+ this.defaults.imagePath = null;
+
+ this.defaults.clickMode = true;
+ this.defaults.hoverMode = true;
+
+ //绘制属性
+ this.defaults.lineSize = 2;
+ this.defaults.gridSize = 1;
+ this.defaults.nodeMode = true;
+ this.defaults.nodeRadius = 2;
+ this.defaults.hasXAxis = true;
+ this.defaults.hasYAxis = true;
+ this.defaults.xAxisTick = 10;
+ this.defaults.yAxisTick = 4;
+ this.defaults.xAxisOrient = "bottom";
+ this.defaults.yAxisOrient = "left";
+ this.defaults.xAxisPadding = 10;
+ this.defaults.yAxisPadding = 10;
+ this.defaults.xAxisFontSize = 10;
+ this.defaults.yAxisFontSize = 10;
+ this.defaults.xAxisStartDx = 20;
+ this.defaults.yAxisStartDy = 10;
+ this.defaults.xAxisEndDx = 10;
+ this.defaults.yAxisEndDy = 10;
+
+ this.defaults.textLean = false;
+
+ this.defaults.hasXGrid = true;
+ this.defaults.hasYGrid = true;
+
+ //Color
+ this.defaults.backgroundColor = "#fff";
+ this.defaults.titleColor = "#000";
+ this.defaults.subtitleColor = "#000";
+ this.defaults.AxisColor = "#000";
+ this.defaults.gridColor = "#000";
+ this.defaults.unchosen0pacity = 0.15;
+ this.defaults.grid0pacity = 0.1;
+ this.defaults.chosenGrid0pacity = 0.5;
+
+ //设置用户指定属性
+ this.setOptions(options);
+
+ //创建画布
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * 创建画布
+ */
+ Line.prototype.createCanvas = function () {
+ var conf = this.defaults;
+
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+
+ this.canvasF = this.node;
+
+ var canvasStyle = this.canvasF.style;
+ canvasStyle.position = "relative";
+ this.floatTag = DataV.FloatTag()(this.canvasF);
+ this.floatTag.css({
+ "visibility": "hidden"
+ });
+ };
+
+ /**
+ * 对原始数据进行处理
+ */
+ Line.prototype.setSource = function (source, map) {
+ map = this.map(map);
+ var dataTable;
+ if (DataV.detect(source) === 'Table_WITH_HEAD') {
+ dataTable = DataV.collectionify(source);
+ } else {
+ dataTable = source;
+ }
+
+ var lines = _.groupBy(dataTable, map.line);
+ var linesData = [];
+
+ var max, min;
+ var maxList = [], minList = [];
+ this.maxLength = Math.max.apply(null, _.map(lines, function (line) {
+ return line.length;
+ }));
+
+ var titles;
+ _.forEach(lines, function (points, name) {
+ // initialize the nodes of line
+ var line = {name: name, id: name, data: [], tags: []};
+ line.data = _.pluck(points, map.value);
+ titles = _.pluck(points, map.x);
+ linesData.push(line);
+ maxList[name] = Math.max.apply(null, line.data);
+ minList[name] = Math.min.apply(null, line.data);
+ });
+
+ var conf = this.defaults;
+ var margin = conf.margin;
+ this.xWidth = conf.width - margin[1] - margin[3];
+ this.yHeight = conf.height - margin[0] - margin[2];
+
+ this.titles = titles;
+ this.linesData = linesData;
+ this.max = Math.max.apply(null, _.values(maxList));
+ this.min = Math.min.apply(null, _.values(minList));
+ this.maxList = maxList;
+ this.minList = minList;
+
+ this.chosen = false;
+ this.chosenNum = -1;
+ };
+
+ /**
+ * 获取颜色函数
+ * @return {function} DataV根据主题获取随机离散色函数
+ */
+ Line.prototype.getColor = function () {
+ var colorFunction = DataV.getDiscreteColor();
+ return colorFunction;
+ };
+
+ /**
+ * 绘制X轴
+ */
+ Line.prototype.setXaxis = function (titles) {
+ var conf = this.defaults;
+ var canvas = this.canvas;
+
+ if (conf.hasXAxis) {
+ var lineSize = conf.lineSize;
+ var gridSize = conf.gridSize;
+ var axisColor = conf.AxisColor;
+ var gridColor = conf.gridColor;
+ var grid0pacity = conf.grid0pacity;
+ var xAxisPadding = conf.xAxisPadding;
+ var margin = conf.margin;
+ var xAxisStartDx = conf.xAxisStartDx;
+ var xAxisEndDx = conf.xAxisEndDx;
+ var startX = margin[3] + xAxisStartDx;
+ var startY = conf.height - margin[2];
+ var xWidth = this.xWidth;
+ var xDataArea = xWidth - xAxisStartDx - xAxisEndDx;
+ var maxLength = this.maxLength;
+ var yHeight = this.yHeight;
+ var fontSize = conf.xAxisFontSize;
+ var textLean = conf.textLean;
+
+ var tickLength;
+ if (conf.hasXGrid) {
+ tickLength = yHeight;
+ } else {
+ tickLength = 5;
+ }
+
+ var tick = conf.xAxisTick;
+ var tickStep;
+ if (tick % maxLength === 0) {
+ tickStep = xDataArea / (tick - 1);
+ } else {
+ tickStep = xDataArea / (this.maxLength - 1);
+ tick = this.maxLength;
+ }
+
+ var xAxis = canvas.set();
+ var xGrid = canvas.set();
+ var xAxisText = canvas.set();
+ xAxis.push(canvas.path("M" + (startX - xAxisStartDx) + "," + startY + "L" + (xWidth + margin[3]) + "," + startY));
+
+ var l = titles.length;
+ for (var i = 0; i < tick; i++) {
+ xGrid.push(canvas.path("M" + (startX + i * tickStep) + "," + startY + "L" + (startX + i * tickStep) + "," + (startY - tickLength)));
+ if (i < l) {
+ var thisText = canvas.text((startX + i * tickStep), startY + xAxisPadding, titles[i]);
+ if (textLean) {
+ var d = thisText.getBBox().width / 2;
+ var angle = 45 / 360 * Math.PI;
+ thisText.transform("r45t" + Math.cos(angle) * d + "," + Math.sin(angle) * d);
+ }
+ xAxisText.push(thisText);
+ }
+ }
+
+ xAxis.attr({fill: axisColor, "stroke-width": lineSize});
+ xGrid.attr({"stroke": gridColor, "stroke-width": gridSize, "stroke-opacity": grid0pacity});
+ xAxisText.attr({"font-size": fontSize});
+ this.xAxis = xAxis;
+ this.xGrid = xGrid;
+ this.xAxisText = xAxisText;
+ this.xTick = xDataArea / (maxLength - 1);
+ }
+ };
+
+ /**
+ * 绘制Y轴
+ */
+ Line.prototype.setYaxis = function () {
+ var conf = this.defaults;
+ var canvas = this.canvas;
+
+ if (conf.hasYAxis) {
+ var lineSize = conf.lineSize;
+ var gridSize = conf.gridSize;
+ var axisColor = conf.AxisColor;
+ var gridColor = conf.gridColor;
+ var grid0pacity = conf.grid0pacity;
+ var margin = conf.margin;
+ var yAxisStartDy = conf.yAxisStartDy;
+ var yAxisEndDy = conf.yAxisEndDy;
+ var yHeight = this.yHeight;
+ var yDataArea = this.yHeight - yAxisStartDy - yAxisEndDy;
+ var xWidth = this.xWidth;
+ var startX = margin[3];
+ var startY = margin[0] + yHeight - yAxisStartDy;
+ var yAxisPadding = conf.yAxisPadding;
+ var max = this.max;
+ var min = this.min;
+ var fontSize = conf.yAxisFontSize;
+
+ var tick = conf.yAxisTick;
+ var tickStep = yDataArea / (tick - 1);
+
+ var d = (max - min) % (tick - 1);
+ if (d !== 0) {
+ max = max + (tick - 1) - d;
+ }
+ d = (max - min) / (tick - 1);
+
+ var tickLength;
+ if (conf.hasYGrid) {
+ tickLength = xWidth;
+ } else {
+ tickLength = 5;
+ }
+
+ var yAxis = canvas.set();
+ var yGrid = canvas.set();
+ var yAxisText = canvas.set();
+ yAxis.push(canvas.path("M" + startX + "," + (startY + yAxisStartDy) + "L" + startX + "," + margin[0]));
+
+ for (var i = 0; i < tick; i++) {
+ yGrid.push(canvas.path("M" + startX + "," + (startY - i * tickStep) + "L" + (startX + tickLength) + "," + (startY - i * tickStep)));
+ yAxisText.push(canvas.text(startX - yAxisPadding, (startY - i * tickStep), (min + i * d)));
+ }
+
+ yAxis.attr({fill: axisColor, "stroke-width": lineSize});
+ yGrid.attr({"stroke": gridColor, "stroke-width": gridSize, "stroke-opacity": grid0pacity});
+ yAxisText.attr({"font-size": fontSize});
+ this.yAxis = yAxis;
+ this.yGrid = yGrid;
+ this.yAxisText = yAxisText;
+ this.yDataArea = yDataArea;
+
+ this.yMatchNum = tickStep / d;
+ this.y0 = startY;
+ }
+ };
+
+ /**
+ * 绘制背景
+ */
+ Line.prototype.setBackground = function () {
+ var conf = this.defaults;
+ var canvas = this.canvas;
+ var backgroundColor = conf.backgroundColor;
+ var imagePath = conf.imagePath;
+ var xWidth = this.xWidth;
+ var yHeight = this.yHeight;
+ var yAxisStartDy = conf.yAxisStartDy;
+ var x0 = conf.margin[3];
+ var y0 = this.y0 + yAxisStartDy;
+
+ var rect;
+ if (imagePath !== null) {
+ rect = canvas.image(imagePath, x0, (y0 - yHeight), xWidth, yHeight);
+ } else {
+ rect = canvas.rect(x0, (y0 - yHeight), xWidth, yHeight).attr({"fill": backgroundColor,"fill-opacity": 0, "stroke": "none"});
+ }
+
+ rect.toBack();
+ this.background = rect;
+ };
+
+ /**
+ * 渲染折线图
+ * Examples:
+ * ```
+ * var line = new Line("chart");
+ * line.setSource(source);
+ * line.render();
+ * ```
+ * @param {object} options options json object for determin line style.
+ */
+ Line.prototype.render = function () {
+ this.canvas.clear();
+ this.createCanvas();
+ var conf = this.defaults;
+ var linesData = this.linesData;
+ var nodesList = [];
+
+ this.setXaxis(this.titles);
+ this.setYaxis();
+
+ this.setBackground();
+
+ var canvas = this.canvas;
+ var nodeMode = conf.nodeMode;
+ var getColor = this.getColor();
+ var lineSize = conf.lineSize;
+ var min = this.min;
+ var xAxisStartDx = conf.xAxisStartDx;
+ var xTick = this.xTick;
+ var yMatchNum = this.yMatchNum;
+ var x0 = conf.margin[3] + xAxisStartDx;
+ var y0 = this.y0;
+ var radius = conf.nodeRadius;
+
+ var lines = canvas.set();
+ linesData.forEach(function (d, i) {
+ var nodeData = d.data;
+ var nodes = canvas.set();
+ var color = getColor(i);
+ var linePath = "M";
+
+ for (var j = 0, l = nodeData.length; j < l; j++) {
+ var x = x0 + xTick * (j);
+ var y = y0 - ((nodeData[j] - min) * yMatchNum);
+
+ linePath = linePath + x + "," + y;
+
+ if (j < l - 1) {
+ linePath = linePath + "L";
+ }
+
+ if (nodeMode) {
+ var thisNode = canvas.circle(x, y, radius * 2).attr({fill: color, "stroke": "none"});
+ thisNode.data('num', j);
+ thisNode.data('lineNum', i);
+ thisNode.data('data', nodeData[j]);
+ nodes.push(thisNode);
+ }
+ }
+
+ lines.push(canvas.path(linePath).attr({"stroke": color, "stroke-width": lineSize}).data('num', i));
+ if (nodeMode) {
+ nodesList.push(nodes.toFront());
+ }
+ });
+
+ this.lines = lines;
+ this.nodesList = nodesList;
+
+ this.interactive();
+ };
+
+ /**
+ * 添加交互选项
+ */
+ Line.prototype.interactive = function () {
+ var that = this;
+ var conf = this.defaults;
+ var hoverMode = conf.hoverMode;
+ var clickMode = conf.clickMode;
+ var nodeMode = conf.nodeMode;
+ var chosen = this.chosen;
+ var chosenNum = this.chosenNum;
+ var lines = this.lines;
+ var uo = conf.unchosen0pacity;
+ var nodesList = this.nodesList;
+ var xGrid = this.xGrid;
+ var grid0pacity = conf.grid0pacity;
+ var cgo = conf.chosenGrid0pacity;
+ var xAxisText = this.xAxisText;
+ var xAxisFontSize = conf.xAxisFontSize;
+
+ var highLight = function (num) {
+ var line = lines[num];
+ lines.attr({"stroke-opacity": uo});
+
+ nodesList.forEach(function (d) {
+ d.attr({"fill-opacity": uo});
+ });
+
+ line.attr({"stroke-opacity": 1}).toFront();
+ nodesList[num].attr({"fill-opacity": 1}).toFront();
+ };
+
+ var unhighLinght = function () {
+ lines.forEach(function (d) {
+ d.attr({"stroke-opacity": 1});
+ });
+ nodesList.forEach(function (d) {
+ d.attr({"fill-opacity": 1});
+ });
+ };
+
+ var background = this.background;
+
+ if (clickMode){
+ background.click(function () {
+ if (chosen) {
+ unhighLinght();
+ chosen = false;
+ that.chosen = chosen;
+ }
+ });
+ }
+
+ var floatTag = this.floatTag;
+ $(this.node).append(this.floatTag);
+ // if (hoverMode) {
+ // background.mouseover(function () {
+
+ // }).mouseout(function () {
+ // floatTag.css({"visibility" : "hidden"});
+ // });
+ // }
+
+ lines.forEach(function (d) {
+ if (hoverMode) {
+ d.mouseover(function () {
+ if (!chosen) {
+ highLight(d.data('num'));
+ }
+ }).mouseout(function () {
+ if (!chosen) {
+ unhighLinght();
+ }
+ });
+ }
+
+ if (clickMode){
+ d.click(function () {
+ chosenNum = d.data('num');
+ highLight(chosenNum);
+
+ chosen = true;
+ that.chosen = chosen;
+ });
+ }
+ });
+
+ if (nodeMode){
+ var radius = conf.nodeRadius;
+
+ nodesList.forEach(function (d) {
+ d.forEach (function (d) {
+ if (hoverMode) {
+ var nodeNum = d.data('num');
+ d.mouseover(function () {
+ d.animate({r: (radius + 2) * 2}, 100);
+ xGrid[nodeNum].animate({'stroke-opacity': cgo}, 100);
+ xAxisText[nodeNum].animate({'font-size': xAxisFontSize * 2}, 100);
+ floatTag.html('' + d.data('data') + '
');
+ floatTag.css({"visibility": "visible"});
+ if (!chosen) {
+ highLight(d.data("lineNum"));
+ nodesList.forEach(function (d) {
+ if (nodeNum < d.length) {
+ d[nodeNum].attr({"fill-opacity": 1});
+ }
+ });
+ }
+ }).mouseout(function () {
+ d.animate({r: radius * 2}, 100);
+ xGrid[nodeNum].animate({'stroke-opacity': grid0pacity}, 100);
+ xAxisText[nodeNum].animate({'font-size': xAxisFontSize}, 100);
+ floatTag.css({"visibility": "hidden"});
+ if (!chosen) {
+ unhighLinght();
+ }
+ });
+ }
+
+ if (clickMode){
+ d.click(function () {
+ chosenNum = d.data('lineNum');
+ highLight(chosenNum);
+
+ chosen = true;
+ that.chosen = chosen;
+ });
+ }
+ });
+ });
+ }
+ };
+
+ return Line;
});
\ No newline at end of file
diff --git a/lib/charts/matrix.js b/lib/charts/matrix.js
index 3e5fe49..8438c13 100644
--- a/lib/charts/matrix.js
+++ b/lib/charts/matrix.js
@@ -1,434 +1,434 @@
-/*global EventProxy, d3, Raphael, $ */
-/*!
- * Matrix的兼容定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Matrix', function (require) {
- var DataV = require('DataV');
- var theme = DataV.Themes;
-
- /**
- * 构造函数
- */
- var Matrix = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Matrix";
- this.node = this.checkContainer(node);
-
- // Properties
- this.font = {};
-
- // Canvas
- this.defaults.width = 1200;
- this.defaults.height = 1200;
- this.defaults.axisWidth = 40;
-
- this.setOptions(options);
- this.createCanvas();
- this.move = false;
- }
- });
-
- Matrix.prototype.getDataTable = function (table) {
- var title = table[0];
- table = table.slice(1);
-
- var titleLength = title.length;
- var tableWidth = table[0].length;
- var tableHeight = table.length;
-
- this.tableWidth = tableWidth;
- this.tableHeight = tableHeight;
-
- //for symmetric matrix
- if (tableWidth !== title.length || tableHeight !== title.length) {
- throw new Error("This matrix is not symmetric matrix!!!");
- } else {
- this.tableWidth = tableWidth;
- this.tableHeight = tableHeight;
- }
-
- this.title = title;
- return table;
- };
-
- Matrix.prototype.setSource = function (source) {
- var conf = this.defaults;
-
- this.source = this.getDataTable(source);
- this.hasSort = false;
- // this.source = this.remapSource(source);
- };
-
- Matrix.prototype.layout = function () {
- var conf = this.defaults;
- var width = conf.width;
- var height = conf.height;
- var tableWidth = this.tableWidth;
- var tableHeight = this.tableHeight;
- var axisWidth = conf.axisWidth;
-
- this.cellWidth = Math.min((width - axisWidth) / tableWidth, (height - axisWidth) / tableHeight);
-
- var startX;
- var startY;
- var bRectWidth;
- var matrixWidth;
-
- if (width > height) {
- startX = (width - height)/2 + axisWidth;
- startY = axisWidth;
- bRectWidth = height - axisWidth;
- matrixWidth = bRectWidth - axisWidth;
- } else if (height > width) {
- startX = axisWidth;
- startY = (height - width) + axisWidth;
- bRectWidth = width - axisWidth;
- } else {
- startX = axisWidth;
- startY = axisWidth;
- bRectWidth = width - axisWidth;
- matrixWidth = bRectWidth - axisWidth;
- }
-
- this.startX = startX;
- this.startY = startY;
- this.bRectWidth = bRectWidth;
- this.matrixWidth = matrixWidth;
- };
-
- Matrix.prototype.getColor = function (i) {
- var colorMatrix = DataV.getColor();
- var length = colorMatrix.length;
- var num = i % length;
- //var color = '#939598';
- var color = '#FFFFFF';
-
- if (num !== 0) {
- color = colorMatrix[num][0];
- }
-
- return color;
- };
-
- Matrix.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.width, conf.height);
-
- this.DOMNode = $(this.canvas.canvas);
- var that = this;
- this.DOMNode.click(function (event) {
- that.trigger("click", event);
- that.update();
- });
- this.DOMNode.dblclick(function (event) {
- that.trigger("dblclick", event);
- });
-
- var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
- this.DOMNode.bind(mousewheel, function (event) {
- that.trigger("mousewheel", event);
- });
-
- this.DOMNode.bind("contextmenu", function (event) {
- that.trigger("contextmenu", event);
- });
-
- this.DOMNode.delegate("circle", "click", function (event) {
- that.trigger("circle_click", event);
- });
-
- this.DOMNode.delegate("circle", "mouseover", function (event) {
- that.trigger("circle_mouseover", event);
- });
-
- this.DOMNode.delegate("circle", "mouseout", function (event) {
- that.trigger("circle_mouseout", event);
- });
- };
-
- Matrix.prototype.generatePaths = function () {
- var canvas = this.canvas;
- var source = this.source;
- var conf = this.defaults;
- var width = conf.width;
- var height = conf.height;
- var startX = this.startX;
- var startY = this.startY;
- var cellWidth = this.cellWidth;
- var tableWidth = this.tableWidth;
- var tableHeight = this.tableHeight;
- var bRectWidth = this.bRectWidth;
- var matrixWidth = this.matrixWidth;
-
- //canvas.clear();
- // var color = this.getColor();
- // var font = this.getFont();
- var font_family = '微软雅黑';
- var font_size = 8;
-
- var title = this.title;
-
- var row = [];
- var columnLine = [];
- var columnText = [];
-
- var backgroundRect = canvas.rect(startX, startY, bRectWidth, bRectWidth);
- //backgroundRect.attr({fill: "#939598", stroke: "none", "fill-opacity": 0.8});
- backgroundRect.attr({fill: "#ffffff", stroke: "none", "fill-opacity": 0.8});
- backgroundRect.toBack();
-
- var sort;
- if (this.hasSort) {
- sort = this.sort;
- }
- var i, j, a, b, color, rect;
- var rects = []; //for column change move rect
- for (i = 0; i < tableHeight; i++) {
- if (!this.hasSort){
- a = i;
- } else {
- for (j = 0; j < sort.length; j++) {
- if (sort[j] === i) {
- a = j;
- }
- }
- }
- var rowRect = canvas.set();
- canvas.path("M" + startX + " " + (startY + cellWidth * i) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
- + (startY + cellWidth * i)).attr({stroke: "#D1D1D1", "stroke-width": 1});
- rowRect.push(canvas.text(-20, cellWidth / 2, title[i])
- .attr({"fill": "#000000",
- "fill-opacity": 0.7,
- "font-family": "Verdana",
- //"font-weight": "bold",
- "font-size": 12}));
-
- for (j = 0; j < tableWidth; j++) {
- if (!this.hasSort) {
- color = this.getColor(source[i][j]);
- } else {
- color = this.getColor(source[i][sort[j]]);
- }
- rect = canvas.rect(cellWidth * j, 0, cellWidth, cellWidth)
- .attr({stroke: "none", fill: color, "fill-opacity": 0.8});
- rowRect.push(rect);
- rects.push(rect);
- }
-
- rowRect.transform("t" + startX + ", " + (startY + cellWidth * a));
- row.push(rowRect);
- }
-
- canvas.path("M" + startX + " " + (startY + cellWidth * tableHeight) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
- + (startY + cellWidth * tableHeight)).attr({stroke: "#D1D1D1", "stroke-width": 1});
-
- for (i = 0; i < tableWidth; i++) {
- // var columnLine = canvas.set();
- // var columnText = canvas.set();
- if (!this.hasSort){
- a = i;
- } else {
- for (j = 0; j < sort.length; j++) {
- if (sort[j] === i) {
- a = j;
- }
- }
- }
- columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
- .attr({stroke: "#D1D1D1", "stroke-width": 1})
- .transform("t" + (startX + cellWidth * a) + ", " + startY));
- columnText.push(canvas.text(cellWidth / 2, -20, title[i])
- .attr({"fill": "#000000",
- "fill-opacity": 0.7,
- "font-family": "Verdana",
- //"font-weight": "bold",
- "font-size": 12})
- .transform("t" + (startX + cellWidth * a) + ", " + startY + "r90"));
- }
-
- columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
- .attr({stroke: "#D1D1D1", "stroke-width": 1})
- .transform("t" + (startX + cellWidth * tableWidth) + ", " + startY));
-
- this.row = row;
- this.columnText = columnText;
- this.columnLine = columnLine;
- this.rects = rects;
- };
-
- Matrix.prototype.getSort = function (source) {
- var sumQueue = [];
- var sort = [];
- var rowData;
- var rowLength;
- var sum;
- var means;
- var matrixD = [];
- var quareSum;
- var rowquareSum = [];
-
- var i, j, k;
- for (i = 0 ; i < source.length ; i++) {
- rowData = source[i];
- rowLength = rowData.length;
- sum = 0;
- quareSum = 0;
-
- for (j = 0 ; j < rowLength ; j++) {
- sum = sum + rowData[j];
- }
-
- means = sum / rowLength;
- for (j = 0 ; j < rowLength ; j++) {
- rowData[j] = rowData[j] - means;
- quareSum = quareSum + Math.pow(rowData[j], 2);
- }
-
- quareSum = Math.sqrt(quareSum);
-
- rowquareSum.push(quareSum);
- matrixD.push(rowData);
- }
-
- var rowI;
- var rowJ;
- var matrixR = [];
-
- for (i = 0 ; i < source.length ; i++) {
- matrixR[i] = [];
- for (j = 0 ; j < source.length ; j++) {
- matrixR[i][j] = 0;
- }
- }
-
- for (i = 0 ; i < source.length ; i++) {
- rowI = matrixD[i];
- matrixR[i][i] = source[i][i];
- for (j = i + 1 ; j < source.length ; j++) {
- sum = 0;
- rowJ = matrixD[j];
- for (k = 0; k < rowLength; k++) {
- sum = sum + rowI[k] * rowJ[k];
- }
-
- sum = sum / (rowquareSum[i] * rowquareSum[j]);
- matrixR[i][j] = sum;
- matrixR[j][i] = sum;
- }
- }
-
-
-
- return matrixR;
- };
-
- Matrix.prototype.update = function () {
- var i, j;
- var source = [];
- for(i = 0; i < this.source.length ; i++){
- source[i] = this.source[i].concat();
- }
-
- var sort = [];
- for (i = 0; i < source[0].length; i++) {
- sort.push(i);
- }
-
- if (this.hasSort) {
- this.sort = sort;
- this.hasSort = false;
- } else {
- var getSort = this.getSort;
- var i, j;
- var pt;
- var nowSort = [];
- var iterations = 12;
-
- for (i = 0; i < iterations; i++) {
- source = getSort(source);
- }
-
- nowSort = source[0];
-
- var a, b;
- for (i = 1; i < sort.length; i++) {
- a = sort[i];
- for (j = i + 1; j < sort.length; j++) {
- b = sort[j];
- if (nowSort[a] < nowSort[b]) {
- pt = sort[i];
- sort[i] = sort[j];
- sort[j] = pt;
- }
- }
- }
- sort = [0,7,5,2,8,3,1,9,6,14,15,4,13,10,16,11,12];
- this.sort = sort;
- this.hasSort = true;
- }
-
- if (!this.move) {
- this.move = true;
- var rects = this.rects;
- var num;
- var startX = this.startX;
- var startY = this.startY;
- var cellWidth = this.cellWidth;
-
- var rowAnim;
- var columnLineAnim;
- var columnTextAnim;
- var anim;
-
- for (i = 0; i < sort.length; i++) {
- num = sort[i];
- // if (num != i) {
- rowAnim = Raphael.animation({transform: ["t", startX, (startY + cellWidth * i)]}, 200, "<>");
- this.row[num].animate(rowAnim.delay(100 * i));
- // }
- }
-
- var that = this;
- var moveEnd = function () {
- that.move = false;
- };
-
- for (i = 0; i < sort.length; i++) {
- num = sort[i];
- // if (num != i) {
- //columnLineAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY]}, 1000, "<>");
- columnTextAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY, "r", 90]},
- 200, "<>");
- //this.columnLine[num].animate(columnLineAnim.delay(500 * (i + sort.length + 1)));
- this.columnText[num].animate(columnTextAnim.delay(100 * (i + sort.length + 1)));
-
- for (j = 0; j < sort.length; j++) {
- if (i === sort.length - 1 && j === sort.length - 1) {
- anim = Raphael.animation({'x': cellWidth * i}, 200, "<>", moveEnd);
- } else {
- anim = Raphael.animation({'x': cellWidth * i}, 200, "<>");
- }
- rects[j * sort.length + num].animate(anim.delay(100 * (i + sort.length + 1)));
- }
- // }
- }
- }
- };
-
- Matrix.prototype.render = function (options) {
- if (!this.move) {
- this.canvas.clear();
- this.setOptions(options);
- this.layout();
- this.generatePaths();
- }
- };
-
- return Matrix;
+/*global EventProxy, d3, Raphael, $ */
+/*!
+ * Matrix的兼容定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Matrix', function (require) {
+ var DataV = require('DataV');
+ var theme = DataV.Themes;
+
+ /**
+ * 构造函数
+ */
+ var Matrix = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Matrix";
+ this.node = this.checkContainer(node);
+
+ // Properties
+ this.font = {};
+
+ // Canvas
+ this.defaults.width = 1200;
+ this.defaults.height = 1200;
+ this.defaults.axisWidth = 40;
+
+ this.setOptions(options);
+ this.createCanvas();
+ this.move = false;
+ }
+ });
+
+ Matrix.prototype.getDataTable = function (table) {
+ var title = table[0];
+ table = table.slice(1);
+
+ var titleLength = title.length;
+ var tableWidth = table[0].length;
+ var tableHeight = table.length;
+
+ this.tableWidth = tableWidth;
+ this.tableHeight = tableHeight;
+
+ //for symmetric matrix
+ if (tableWidth !== title.length || tableHeight !== title.length) {
+ throw new Error("This matrix is not symmetric matrix!!!");
+ } else {
+ this.tableWidth = tableWidth;
+ this.tableHeight = tableHeight;
+ }
+
+ this.title = title;
+ return table;
+ };
+
+ Matrix.prototype.setSource = function (source) {
+ var conf = this.defaults;
+
+ this.source = this.getDataTable(source);
+ this.hasSort = false;
+ // this.source = this.remapSource(source);
+ };
+
+ Matrix.prototype.layout = function () {
+ var conf = this.defaults;
+ var width = conf.width;
+ var height = conf.height;
+ var tableWidth = this.tableWidth;
+ var tableHeight = this.tableHeight;
+ var axisWidth = conf.axisWidth;
+
+ this.cellWidth = Math.min((width - axisWidth) / tableWidth, (height - axisWidth) / tableHeight);
+
+ var startX;
+ var startY;
+ var bRectWidth;
+ var matrixWidth;
+
+ if (width > height) {
+ startX = (width - height)/2 + axisWidth;
+ startY = axisWidth;
+ bRectWidth = height - axisWidth;
+ matrixWidth = bRectWidth - axisWidth;
+ } else if (height > width) {
+ startX = axisWidth;
+ startY = (height - width) + axisWidth;
+ bRectWidth = width - axisWidth;
+ } else {
+ startX = axisWidth;
+ startY = axisWidth;
+ bRectWidth = width - axisWidth;
+ matrixWidth = bRectWidth - axisWidth;
+ }
+
+ this.startX = startX;
+ this.startY = startY;
+ this.bRectWidth = bRectWidth;
+ this.matrixWidth = matrixWidth;
+ };
+
+ Matrix.prototype.getColor = function (i) {
+ var colorMatrix = DataV.getColor();
+ var length = colorMatrix.length;
+ var num = i % length;
+ //var color = '#939598';
+ var color = '#FFFFFF';
+
+ if (num !== 0) {
+ color = colorMatrix[num][0];
+ }
+
+ return color;
+ };
+
+ Matrix.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+
+ this.DOMNode = $(this.canvas.canvas);
+ var that = this;
+ this.DOMNode.click(function (event) {
+ that.trigger("click", event);
+ that.update();
+ });
+ this.DOMNode.dblclick(function (event) {
+ that.trigger("dblclick", event);
+ });
+
+ var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
+ this.DOMNode.bind(mousewheel, function (event) {
+ that.trigger("mousewheel", event);
+ });
+
+ this.DOMNode.bind("contextmenu", function (event) {
+ that.trigger("contextmenu", event);
+ });
+
+ this.DOMNode.delegate("circle", "click", function (event) {
+ that.trigger("circle_click", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseover", function (event) {
+ that.trigger("circle_mouseover", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseout", function (event) {
+ that.trigger("circle_mouseout", event);
+ });
+ };
+
+ Matrix.prototype.generatePaths = function () {
+ var canvas = this.canvas;
+ var source = this.source;
+ var conf = this.defaults;
+ var width = conf.width;
+ var height = conf.height;
+ var startX = this.startX;
+ var startY = this.startY;
+ var cellWidth = this.cellWidth;
+ var tableWidth = this.tableWidth;
+ var tableHeight = this.tableHeight;
+ var bRectWidth = this.bRectWidth;
+ var matrixWidth = this.matrixWidth;
+
+ //canvas.clear();
+ // var color = this.getColor();
+ // var font = this.getFont();
+ var font_family = '微软雅黑';
+ var font_size = 8;
+
+ var title = this.title;
+
+ var row = [];
+ var columnLine = [];
+ var columnText = [];
+
+ var backgroundRect = canvas.rect(startX, startY, bRectWidth, bRectWidth);
+ //backgroundRect.attr({fill: "#939598", stroke: "none", "fill-opacity": 0.8});
+ backgroundRect.attr({fill: "#ffffff", stroke: "none", "fill-opacity": 0.8});
+ backgroundRect.toBack();
+
+ var sort;
+ if (this.hasSort) {
+ sort = this.sort;
+ }
+ var i, j, a, b, color, rect;
+ var rects = []; //for column change move rect
+ for (i = 0; i < tableHeight; i++) {
+ if (!this.hasSort){
+ a = i;
+ } else {
+ for (j = 0; j < sort.length; j++) {
+ if (sort[j] === i) {
+ a = j;
+ }
+ }
+ }
+ var rowRect = canvas.set();
+ canvas.path("M" + startX + " " + (startY + cellWidth * i) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
+ + (startY + cellWidth * i)).attr({stroke: "#D1D1D1", "stroke-width": 1});
+ rowRect.push(canvas.text(-20, cellWidth / 2, title[i])
+ .attr({"fill": "#000000",
+ "fill-opacity": 0.7,
+ "font-family": "Verdana",
+ //"font-weight": "bold",
+ "font-size": 12}));
+
+ for (j = 0; j < tableWidth; j++) {
+ if (!this.hasSort) {
+ color = this.getColor(source[i][j]);
+ } else {
+ color = this.getColor(source[i][sort[j]]);
+ }
+ rect = canvas.rect(cellWidth * j, 0, cellWidth, cellWidth)
+ .attr({stroke: "none", fill: color, "fill-opacity": 0.8});
+ rowRect.push(rect);
+ rects.push(rect);
+ }
+
+ rowRect.transform("t" + startX + ", " + (startY + cellWidth * a));
+ row.push(rowRect);
+ }
+
+ canvas.path("M" + startX + " " + (startY + cellWidth * tableHeight) + "L" + (startX + matrixWidth + 10 + cellWidth) + " "
+ + (startY + cellWidth * tableHeight)).attr({stroke: "#D1D1D1", "stroke-width": 1});
+
+ for (i = 0; i < tableWidth; i++) {
+ // var columnLine = canvas.set();
+ // var columnText = canvas.set();
+ if (!this.hasSort){
+ a = i;
+ } else {
+ for (j = 0; j < sort.length; j++) {
+ if (sort[j] === i) {
+ a = j;
+ }
+ }
+ }
+ columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
+ .attr({stroke: "#D1D1D1", "stroke-width": 1})
+ .transform("t" + (startX + cellWidth * a) + ", " + startY));
+ columnText.push(canvas.text(cellWidth / 2, -20, title[i])
+ .attr({"fill": "#000000",
+ "fill-opacity": 0.7,
+ "font-family": "Verdana",
+ //"font-weight": "bold",
+ "font-size": 12})
+ .transform("t" + (startX + cellWidth * a) + ", " + startY + "r90"));
+ }
+
+ columnLine.push(canvas.path("M0 0L0 " + matrixWidth + 10 + cellWidth)
+ .attr({stroke: "#D1D1D1", "stroke-width": 1})
+ .transform("t" + (startX + cellWidth * tableWidth) + ", " + startY));
+
+ this.row = row;
+ this.columnText = columnText;
+ this.columnLine = columnLine;
+ this.rects = rects;
+ };
+
+ Matrix.prototype.getSort = function (source) {
+ var sumQueue = [];
+ var sort = [];
+ var rowData;
+ var rowLength;
+ var sum;
+ var means;
+ var matrixD = [];
+ var quareSum;
+ var rowquareSum = [];
+
+ var i, j, k;
+ for (i = 0 ; i < source.length ; i++) {
+ rowData = source[i];
+ rowLength = rowData.length;
+ sum = 0;
+ quareSum = 0;
+
+ for (j = 0 ; j < rowLength ; j++) {
+ sum = sum + rowData[j];
+ }
+
+ means = sum / rowLength;
+ for (j = 0 ; j < rowLength ; j++) {
+ rowData[j] = rowData[j] - means;
+ quareSum = quareSum + Math.pow(rowData[j], 2);
+ }
+
+ quareSum = Math.sqrt(quareSum);
+
+ rowquareSum.push(quareSum);
+ matrixD.push(rowData);
+ }
+
+ var rowI;
+ var rowJ;
+ var matrixR = [];
+
+ for (i = 0 ; i < source.length ; i++) {
+ matrixR[i] = [];
+ for (j = 0 ; j < source.length ; j++) {
+ matrixR[i][j] = 0;
+ }
+ }
+
+ for (i = 0 ; i < source.length ; i++) {
+ rowI = matrixD[i];
+ matrixR[i][i] = source[i][i];
+ for (j = i + 1 ; j < source.length ; j++) {
+ sum = 0;
+ rowJ = matrixD[j];
+ for (k = 0; k < rowLength; k++) {
+ sum = sum + rowI[k] * rowJ[k];
+ }
+
+ sum = sum / (rowquareSum[i] * rowquareSum[j]);
+ matrixR[i][j] = sum;
+ matrixR[j][i] = sum;
+ }
+ }
+
+
+
+ return matrixR;
+ };
+
+ Matrix.prototype.update = function () {
+ var i, j;
+ var source = [];
+ for(i = 0; i < this.source.length ; i++){
+ source[i] = this.source[i].concat();
+ }
+
+ var sort = [];
+ for (i = 0; i < source[0].length; i++) {
+ sort.push(i);
+ }
+
+ if (this.hasSort) {
+ this.sort = sort;
+ this.hasSort = false;
+ } else {
+ var getSort = this.getSort;
+ var i, j;
+ var pt;
+ var nowSort = [];
+ var iterations = 12;
+
+ for (i = 0; i < iterations; i++) {
+ source = getSort(source);
+ }
+
+ nowSort = source[0];
+
+ var a, b;
+ for (i = 1; i < sort.length; i++) {
+ a = sort[i];
+ for (j = i + 1; j < sort.length; j++) {
+ b = sort[j];
+ if (nowSort[a] < nowSort[b]) {
+ pt = sort[i];
+ sort[i] = sort[j];
+ sort[j] = pt;
+ }
+ }
+ }
+ sort = [0,7,5,2,8,3,1,9,6,14,15,4,13,10,16,11,12];
+ this.sort = sort;
+ this.hasSort = true;
+ }
+
+ if (!this.move) {
+ this.move = true;
+ var rects = this.rects;
+ var num;
+ var startX = this.startX;
+ var startY = this.startY;
+ var cellWidth = this.cellWidth;
+
+ var rowAnim;
+ var columnLineAnim;
+ var columnTextAnim;
+ var anim;
+
+ for (i = 0; i < sort.length; i++) {
+ num = sort[i];
+ // if (num != i) {
+ rowAnim = Raphael.animation({transform: ["t", startX, (startY + cellWidth * i)]}, 200, "<>");
+ this.row[num].animate(rowAnim.delay(100 * i));
+ // }
+ }
+
+ var that = this;
+ var moveEnd = function () {
+ that.move = false;
+ };
+
+ for (i = 0; i < sort.length; i++) {
+ num = sort[i];
+ // if (num != i) {
+ //columnLineAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY]}, 1000, "<>");
+ columnTextAnim = Raphael.animation({transform: ["t", (startX + cellWidth * i), startY, "r", 90]},
+ 200, "<>");
+ //this.columnLine[num].animate(columnLineAnim.delay(500 * (i + sort.length + 1)));
+ this.columnText[num].animate(columnTextAnim.delay(100 * (i + sort.length + 1)));
+
+ for (j = 0; j < sort.length; j++) {
+ if (i === sort.length - 1 && j === sort.length - 1) {
+ anim = Raphael.animation({'x': cellWidth * i}, 200, "<>", moveEnd);
+ } else {
+ anim = Raphael.animation({'x': cellWidth * i}, 200, "<>");
+ }
+ rects[j * sort.length + num].animate(anim.delay(100 * (i + sort.length + 1)));
+ }
+ // }
+ }
+ }
+ };
+
+ Matrix.prototype.render = function (options) {
+ if (!this.move) {
+ this.canvas.clear();
+ this.setOptions(options);
+ this.layout();
+ this.generatePaths();
+ }
+ };
+
+ return Matrix;
});
\ No newline at end of file
diff --git a/lib/charts/parallel.js b/lib/charts/parallel.js
index 3b9333f..83e26da 100644
--- a/lib/charts/parallel.js
+++ b/lib/charts/parallel.js
@@ -1,591 +1,591 @@
-/*global Raphael, d3 */
-/*!
- * Parallel的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Parallel', function (require) {
- var DataV = require('DataV');
- var Axis = require('Axis');
- var Brush = require('Brush');
-
- /**
- * 构造函数
- * Options:
- *
- * - `width` 数字,图片宽度,默认为750,表示图片高750px
- * - `height` 数字,图片高度,默认为500
- * - `marginWidth` 数组,表示图片上、右、下、左的边距,默认为 [20, 20, 20, 20]
- * - `backgroundAttr` 对象,没有选中的线的样式,默认为{"fill": "none", "stroke": "#ccc", "stroke-opacity": 0.4}, 具体设置方式请参考Raphael手册:http://raphaeljs.com/reference.html#Element.attr
- * - `foregroundAttr` 对象,被选中的线的样式,默认为{"fill": "none", "stroke": "steelblue", "stroke-opacity": 0.7}, 具体设置方式请参考Raphael手册:http://raphaeljs.com/reference.html#Element.attr
- * - `axisStyle` 对象,设置坐标轴属性。3中坐标轴属性:domainAttr表示坐标轴线属性。tickAttr表示坐标轴标尺属性。tickTextAttr表示坐标轴文字属性。具体设置方式请参考Raphael手册:http://raphaeljs.com/reference.html#Element.attr
- * - `customEvent` 函数对象,其中有3个自定义函数。`brushstart` 函数,表示刚开始拖选区间的事件响应,默认为空函数; `brushend` 函数,表示拖选结束后的事件响应,默认为空函数; `brush` 函数,表示拖选时的事件响应,默认为空函数; 这些函数可以在创建对象或setOption()时一起设置,也可以通过on()函数单独设置。
- *
- * @param {Node|String|jQuery} node 容器节点,文档节点、ID或者通过jQuery查询出来的对象
- */
- var Parallel = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Parallel";
- this.node = this.checkContainer(node);
-
- // Properties
- this.allDimensions = [];
- this.dimensions = [];
- this.dimensionType = {};
- this.dimensionDomain = {};
- this.dimensionExtent = {};
-
- // Canvas
- this.defaults.width = 750;
- this.defaults.height = 500;
- this.defaults.marginWidth = [20, 20, 20, 20];
- this.defaults.backgroundAttr = {"fill": "none", "stroke": "#ccc", "stroke-opacity": 0.4};
- this.defaults.foregroundAttr = {"fill": "none", "stroke": "steelblue", "stroke-opacity": 0.7};
-
- this.defaults.axisStyle = {
- domainAttr : {"stroke": "#000"},//坐标轴线
- tickAttr : {"stroke": "#000"},//坐标轴标尺
- tickTextAttr : {}//坐标轴文字
- }
-
- this.defaults.customEvent = {
- "brushstart": function () {},
- "brushend": function () {},
- "brush": function () {}
- };
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * choose dimension
- * @param {array} dimen Array of column names
- */
- Parallel.prototype.chooseDimensions = function (dimen) {
- var conf = this.defaults;
- this.dimensions = [];
- for (var i = 0, l = dimen.length; i end) {
- temp = start;
- start = end;
- end = temp;
- }
- start = Math.max(0, start + 0.5 - 0.5);
- end = Math.min(this.dimensionDomain[dimen].length, end + 0.5 + 0.5);
- return [start, end];
- };
-
- /*!
- * create canvas
- */
- Parallel.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.node.style.position = "relative";
- this.canvas = Raphael(this.node, conf.width, conf.height);
-
- //console.log(this.canvas);
- };
-
- /*!
- * get color
- */
- Parallel.prototype.getColor = function (colorJson) {
- var colorM = DataV.getColor();
- var color;
- var colorStyle = colorJson ? colorJson : {};
- var colorMode = colorStyle.mode ? colorStyle.mode : 'default';
- switch (colorMode){
- case "gradient":
- var index = colorJson.index ? colorJson.index : 0;
- index = index <0 ? 0 : (index>colorM.length-1 ? colorM.length-1 : index);
- color = d3.interpolateRgb.apply(null, [colorM[index][0],colorM[index][1]]);
- break;
- case "random":
- case "default":
- var ratio = colorStyle.ratio ? colorStyle.ratio : 0;
- if(ratio <0 ){ratio=0;}
- if(ratio > 1) { ratio =1;}
- var colorArray =[];
- for (var i=0, l=colorM.length; i end) {
+ temp = start;
+ start = end;
+ end = temp;
+ }
+ start = Math.max(0, start + 0.5 - 0.5);
+ end = Math.min(this.dimensionDomain[dimen].length, end + 0.5 + 0.5);
+ return [start, end];
+ };
+
+ /*!
+ * create canvas
+ */
+ Parallel.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.node.style.position = "relative";
+ this.canvas = Raphael(this.node, conf.width, conf.height);
+
+ //console.log(this.canvas);
+ };
+
+ /*!
+ * get color
+ */
+ Parallel.prototype.getColor = function (colorJson) {
+ var colorM = DataV.getColor();
+ var color;
+ var colorStyle = colorJson ? colorJson : {};
+ var colorMode = colorStyle.mode ? colorStyle.mode : 'default';
+ switch (colorMode){
+ case "gradient":
+ var index = colorJson.index ? colorJson.index : 0;
+ index = index <0 ? 0 : (index>colorM.length-1 ? colorM.length-1 : index);
+ color = d3.interpolateRgb.apply(null, [colorM[index][0],colorM[index][1]]);
+ break;
+ case "random":
+ case "default":
+ var ratio = colorStyle.ratio ? colorStyle.ratio : 0;
+ if(ratio <0 ){ratio=0;}
+ if(ratio > 1) { ratio =1;}
+ var colorArray =[];
+ for (var i=0, l=colorM.length; i2: small change;
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * Stream图纬度描述
- */
- Stream.dimension = {};
- /**
- * 流向纬度,例如,时间
- */
- Stream.dimension.stream = {
- type: "string",
- required: true
- };
- /**
- * 堆叠纬度,例如,按类目
- */
- Stream.dimension.stack = {
- type: "string",
- required: true
- };
- /**
- * 值纬度,在流向和堆叠纬度上的值
- */
- Stream.dimension.value = {
- type: "number",
- required: true
- };
-
-
- Stream.prototype.initLegend = function () {
- var conf = this.defaults;
- this.legend = $(create("div")).css({
- "overflow": "hidden",
- "width": conf.legendWidth - conf.legendIndent,
- "padding": "10px 0 10px 0"
- });
- this.leftContainer = $(create("div")).css({
- "width": conf.legendWidth - 4,
- "overflow-x": "hidden"
- }).append(this.legend);
- };
-
- Stream.prototype.initNav = function () {
- var conf = this.defaults;
- this.navi = $(create("div")).css({
- "borderTop": "1px solid #ddd",
- "borderBottom": "1px solid #ddd",
- "padding": "5px 10px 10px 10px",
- "fontSize": (conf.fontSize + 1)
- });
- this.naviTrace = $(create("div")).css({
- "width": conf.totalWidth - conf.naviBackWidth - 50,
- "marginTop": "5px"
- });
-
- this.naviBack = $(create("div")).html("返回上层").css({
- "width": conf.naviBackWidth,
- "float": "right",
- "backgroundColor": "#f4f4f4",
- "paddingTop": "4px",
- "paddingBottom": "4px",
- "border": "1px solid #ddd",
- "borderRadius": "2px",
- "cursor": "pointer",
- "textAlign": "center",
- "visibility": "hidden"
- });
- var getBack = function (stream) {
- stream.cover.style.visibility = "visible";
- stream.coverMouse = undefined;
- stream.getLevelSource();
- stream.reRender();
-
- //hidden
- stream.indicatorLine.attr({"stroke": "none"});
- stream.highlightLine.attr({"stroke": "none"});
- stream.floatTag.css({"visibility" : "hidden"});
-
- stream.paths.forEach(function (d, i, array) {
- d.attr({transform: "s1,0.001,0,0"});
- d.label.hide();
- d.animate({transform: "t0,0"}, 750, "linear", function () {
- stream.cover.style.visibility = "hidden";
- if (typeof stream.coverMouse !== 'undefined') {
- stream.indicatorLine.attr({"stroke": "#000"});
- stream.highlightLine.attr({"stroke": "white"});
- stream.floatTag.css({"visibility" : "visible"});
- $(stream.canvas.canvas).trigger("mousemove", [stream.coverMouse.x, stream.coverMouse.y]);
- stream.coverMouse = undefined;
- }
- if (d.labelLoc.showLabel) {
- d.label.show();
- }
- });
- });
- };
- this.naviTrace.on("click", ".navi", {stream: this}, function (e) {
- var stream = e.data.stream;
- stream.level = e.target.data.level;
- getBack(stream);
- });
-
- this.naviBack.on("click", {stream: this}, function (e) {
- var stream = e.data.stream;
- stream.level -= 1;
- getBack(stream);
- });
- this.navi.append(this.naviBack).append(this.naviTrace);
- };
-
- Stream.prototype.initAxis = function () {
- var conf = this.defaults;
- this.axis = $(create("div")).css({
- "marginTop": "0",
- "borderTop": "1px solid #ddd",
- "height": conf.axisHeight
- });
- this.axisPaper = new Raphael(this.axis, conf.totalWidth - conf.legendWidth, conf.axisHeight);
- };
-
- Stream.prototype.initPercentage = function () {
- var conf = this.defaults;
- this.percentage = $(create("div")).css({
- "width": conf.margin[3],
- "height": conf.height,
- "float": "left",
- "marginBottom": "0",
- "borderBottom": "0",
- "paddingBottom": "0"
- });
- if (this.defaults.more) {
- this.percentagePaper = new Raphael(this.percentage, conf.margin[3], conf.height);
- }
- };
-
- /*!
- * 创建画布
- */
- Stream.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.initLegend();
- this.initNav();
- this.initAxis();
- this.initPercentage();
-
- var container = $(create("div")).css({"position": "relative"});
- this.canvas = new Raphael(container, conf.width, conf.height);console.log(conf.width, conf.height);
- this.canvasContainer = $(create("div")).css({
- "float": "left",
- "width": conf.width,
- "height": conf.height,
- "marginBottom": "0",
- "borderBottom": "0",
- "paddingVottom": "0"
- }).append(container);
-
- this.floatTag = DataV.FloatTag()(container).css({"visibility": "hidden"});
-
- // cover can block stream canvas when animating to prevent some default mouse event
- this.cover = $(create("div")).css({
- "position": "absolute",
- "width": conf.width,
- "height": conf.height,
- "zIndex": 100,
- "visibility": "hidden"
- }).bind("mousemove", {stream: this}, function (e) {
- var stream = e.data.stream;
- stream.coverMouse = {x: e.pageX, y: e.pageY};
- }).bind("mouseleave", {stream: this}, function (e) {
- var stream = e.data.stream;
- stream.coverMouse = undefined;
- });
-
- this.middleContainer = $(create("div")).css({
- "height": conf.height
- })
- .append(this.percentage)
- .append(this.canvasContainer)
- .append(this.cover);
- if (this.defaults.more) {
- this.percentagePaper = new Raphael(this.percentage, conf.margin[3], conf.height);
- }
-
- this.rightContainer = $(create("div")).css({
- "float": "right",
- "width": conf.totalWidth - conf.legendWidth
- }).append(this.navi)
- .append(this.middleContainer)
- .append(this.axis);
-
- $(this.node).css({
- "position": "relative",
- "width": conf.totalWidth
- }).append(this.rightContainer).append(this.leftContainer);
- };
-
- /**
- * 设置自定义选项
- */
- Stream.prototype.setOptions = function (options) {
- _.extend(this.defaults, options);
-
- var conf = this.defaults;
- if (options && options.width) {
- conf.totalWidth = conf.width;
- conf.width = conf.totalWidth - conf.margin[1] - conf.margin[3] - conf.legendWidth;
- if (!options.height) {
- conf.autoHeight = true;
- conf.height = conf.width * conf.heightWidthRatio;
- } else {
- conf.autoHeight = false;
- }
- }
- };
- /*!
- * 排序
- * @param {Array} source 待排序的二维数组
- */
- Stream.prototype.sort = function (source) {
- var i, j, l, ll;
- var rowSum = [];
- var columnSum = [];
- var newSource = [];
- var rowName = [];
- var that = this;
-
- for (j = 0, ll = source[0].length; j < ll; j++) {
- columnSum[j] = 0;
- }
-
- for (i = 0, l = source.length; i < l; i++) {
- rowSum[i] = 0;
- for (j = 0, ll = source[0].length; j < ll; j++) {
- rowSum[i] += source[i][j];
- columnSum[j] += source[i][j];
- }
- rowSum[i] = [rowSum[i]];
- rowSum[i].index = i;
- }
-
- rowSum.sort(function (a, b) {
- return b[0] - a[0];
- });
-
- rowSum.forEach(function (d, i) {
- newSource[i] = source[d.index];
- if (that.rowName) {
- rowName[i] = that.rowName[d.index];
- }
- });
-
- for (i = 0, l = rowSum.length; i < l; i++) {
- rowSum[i] = rowSum[i][0];
- }
-
- //this.mergeOthe
-
- this.rowName = rowName;
- this.rowSum = rowSum;
- this.columnSum = columnSum;
- this.total = d3.sum(this.rowSum);
-
- return newSource;
- };
-
- /*!
- * 合并数据
- */
- Stream.prototype.mergeOther = function (source) {
- //change digitData, rowSum, rowName;
- };
-
- /**
- * 获取数据
- * @param {Array} source 从二维数组中,获取纯数据的部分(排除掉列名后)
- */
- Stream.prototype.getDigitData = function (source) {
- //get first column name, row name and digitData;
- var conf = this.defaults,
- firstRow = source[0],
- firstColumn,
- digitData;
-
- var i, j, l, ll;
-
- firstColumn = source.map(function (d) {
- return d[0];
- });
-
- if (this.hasRowName()) {
- if (this.hasColumnName()) {
- //row names, column names
- this.rowName = firstColumn.slice(1);
- this.columnName = firstRow.slice(1);
- digitData = source.map(function (d) {
- return d.slice(1);
- }).slice(1);
- } else {
- //row names, no column names
- this.rowName = firstColumn;
- this.columnName = undefined;
- digitData = source.map(function (d) {
- return d.slice(1);
- });
- }
- } else {
- if (this.hasColumnName()) {
- //no row names, column names
- this.rowName = undefined;
- this.columnName = firstRow;
- digitData = source.slice(1);
- } else {
- //no row names, no column names
- if (conf.columnNameUsed === "auto" && conf.rowNameUsed === "auto" && !DataV.isNumeric(source[0][0])) {
- throw new Error("Please specify whether there are column names or row names");
- }
- this.rowName = undefined;
- this.columnName = undefined;
- digitData = source;
- }
- }
- for (i = 0, l = digitData.length; i < l; i++) {
- for (j = 0, ll = digitData[0].length; j < ll; j++) {
- digitData[i][j] = parseFloat(digitData[i][j]);
- }
- }
- return digitData;
- };
-
- /**
- * 获取信息数据
- */
- Stream.prototype.getInfo = function () {
- var allInfos = [];
- var i, j, l, ll;
- var infos, info;
- var column;
- var digitData = this.digitData;
- var descending = function (a, b) {
- return b.value - a.value;
- };
- for (i = 0, l = this.digitData.length; i < l; i++) {
- infos = allInfos[i] = [];
- infos.ratio = this.rowSum[i] / this.total;
- infos.value = this.rowSum[i];
- infos.name = this.rowName[i];
- infos.id = i;
- }
- for (i = 0, l = digitData.length; i < l; i++) {
- column = [];
- for (j = 0, ll = digitData[0].length; j < ll; j++) {
- allInfos[i][j] = column[j] = {
- "date": this.columnName[j],
- "id": i,
- "name": allInfos[i].name,
- "tip": "" + allInfos[i].name + "
占比:"
- + (Math.round(digitData[i][j] / this.columnSum[j] * 10000) / 100) + "%
",
- "total": allInfos[i].ratio,
- //"value": columnTotal[i]
- "value" : digitData[i][j],
- "index" : j,
- "rowInfo" : allInfos[i],
- "ratio" : digitData[i][j] / this.columnSum[j]
- };
- }
-
- column.sort(descending);
-
- for (j = 0, ll = column.length; j < ll; j++) {
- column[j].rank = j;
- }
- }
- return allInfos;
- };
-
- /**
- * 设置数据源
- * Examples:
- * 例如下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
- * ```
- * [
- * [1,2,3,9],
- * [3,4,6,3]
- * ]
- * ```
- * @param {Array} source 二维数组的数据源
- */
- Stream.prototype.setSource = function (source) {
- this.rawData = source;
- this.digitData = this.getDigitData(this.rawData);
-
- //get date, sort and allInfos;
- //date
- this.date = source[0].slice(1, source[0].length);
- this.timeRange = [0, this.date.length - 1];
- //sort
- this.digitData = this.sort(this.digitData);
- this.allInfos = this.getInfo(this.digitData);
-
- this.level = 0;
- this.getLevelSource();
- //this.source = this.remapSource(digitData);
- this.canAnimate = false;
- };
-
- /**
- * If useString is true, start and end are date string, else start and end are index number
- * @param {Number|String} start 起始范围
- * @param {Number|String} end 结束范围
- * @param {Boolean} useString 是否是字符串
- */
- Stream.prototype.setTimeRange = function (start, end, useString) {
- var idx1, idx2;
- if (useString) {
- idx1 = _.indexOf(this.date, start);
- idx2 = _.indexOf(this.date, end);
- } else {
- idx1 = start;
- idx2 = end;
- }
-
- var min = Math.min(idx1, idx2);
- var max = Math.max(idx1, idx2);
- if (min === max) {
- throw new Error("start index and end index can not be same.");
- }
- if (min < 0 || max > this.date.length - 1) {
- throw new Error("start index or end index is beyond the time range.");
- }
-
- this.timeRange = [min, max];
- this.getLevelSource();
- };
-
- /*!
- * 根据时间范围获取数据
- */
- Stream.prototype.getDataByTimeRange = function () {
- if (this.timeRange[0] === 0 && this.timeRange[1] === this.date.length - 1) {
- return this.digitData;
- } else {
- var tr = this.timeRange;
- return _.map(this.digitData, function (d, i) {
- return d.slice(tr[0], tr[1] + 1);
- });
- }
- };
-
- /*!
- * 获取等级数据
- */
- Stream.prototype.getLevelSource = function () {
- var data = this.getDataByTimeRange(),//this.digitData,
- rowStart = this.level * (this.defaults.max - 1),
- rowEnd,
- needMoreRow,
- column = data[0].length,
- remap = [],
- infos = [],
- moreRowInfo = [];
-
- if (column < 1) {
- throw new Error("Data source is empty.");
- }
- if (this.defaults.more) {
- if (rowStart + this.defaults.max >= data.length) {
- if (rowStart + this.defaults.max === data.length && this.allInfos[data.length - 1][0].id === -2) {
- //last more sum < this.defaults.other
- rowEnd = rowStart + this.defaults.max - 1;
- needMoreRow = true;
- } else {
- rowEnd = data.length;
- needMoreRow = false;
- }
- } else {
- rowEnd = rowStart + this.defaults.max - 1;
- needMoreRow = true;
- }
- } else {
- rowStart = 0;
- rowEnd = data.length;
- needMoreRow = false;
- }
- for (i = rowStart; i < rowEnd; i++) {
- k = i - rowStart;
- remap[k] = [];
- for (j = 0; j < column; j++) {
- remap[k][j] = {};
- remap[k][j].x = j;
- remap[k][j].y = parseFloat(data[i][j]);
- }
- if (this.timeRange[0] === 0 && this.timeRange[1] === this.date.length - 1) {
- infos[k] = this.allInfos[i];
- } else {
- infos[k] = this.allInfos[i].slice(this.timeRange[0], this.timeRange[1] + 1);
- }
- }
- if (needMoreRow) {
- if (rowStart + this.defaults.max === data.length && this.allInfos[data.length - 1][0].id === -2) {
- //last more sum < this.defaults.other
- var valueArray = data[data.length - 1];
- moreRow = [];
- for (j = 0; j < column; j++) {
- moreRow[j] = {};
- moreRow[j].x = j;
- moreRow[j].y = valueArray[j];
- }
- moreRowInfo = this.allInfos[data.length - 1];
- } else {
- moreRow = [];
- for (j = 0; j < column; j++) {
- moreSum = 0;
- totalSum = 0;
- for (m = data.length - 1; m >= rowEnd; m--) {
- moreSum += parseFloat(data[m][j]);
- totalSum += parseFloat(this.allInfos[m][j].total);
- }
- moreRow[j] = {};
- moreRow[j].x = j;
- moreRow[j].y = moreSum;
- moreRowInfo[j] = {
- "date": this.allInfos[0][j].date,
- "id": -1,// -1 clickable; -2 not click
- "name": "更多",
- "tip": "更多
占比:" + (Math.round(moreSum * 10000) / 100) + "%
点击查看更多信息
",
- "total": totalSum,
- "value": moreSum
- };
- }
- }
- remap = [moreRow].concat(remap);
- infos = [moreRowInfo].concat(infos);
- }
- this.infos = infos;
- this.source = remap;
- };
-
- /**
- * 生成布局
- */
- Stream.prototype.layout = function () {
- var conf = this.defaults;
- d3.layout.stack().offset(conf.offset).order(conf.order)(this.source);
- };
-
- /**
- * 根据选择方案获取颜色数据
- * @param {Object} colorJson 颜色方案
- * @return {Array} 返回颜色数据
- */
- Stream.prototype.getColor = function (colorJson) {
- var colorMatrix = DataV.getColor();
- var color;
- var colorStyle = colorJson || {};
- var colorMode = colorStyle.mode || 'default';
- var i, l;
-
- switch (colorMode) {
- case "gradient":
- l = this.source.length;
- var colorL = Math.round(l / 5);
- if (colorL > colorMatrix.length - 1) {
- colorL = colorMatrix.length - 1;
- }
- var testColor = [colorMatrix[0][0], colorMatrix[colorL][0]];
- var test1 = DataV.gradientColor(testColor, "special");
- var testColorMatrix = [];
- var testColorMatrix1 = [];
- for (i = 0; i < l; i++) {
- testColorMatrix.push([test1(i / (l - 1)), test1(i / (l - 1))]);
- }
-
- for (i = (l - 1); i >= 0; i--) {
- testColorMatrix1.push(testColorMatrix[i]);
- }
-
- colorMatrix = testColorMatrix;
-
- break;
- case "random":
- case "default":
- break;
- }
-
- var ratio = colorStyle.ratio || 0;
- if (ratio < 0) { ratio = 0; }
- if (ratio > 1) { ratio = 1; }
- var colorArray = [];
- for (i = 0, l = colorMatrix.length; i < l; i++) {
- var colorFunc = d3.interpolateRgb.apply(null, [colorMatrix[i][0], colorMatrix[i][1]]);
- colorArray.push(colorFunc(ratio));
- }
- color = d3.scale.ordinal().range(colorArray);
-
- return color;
- };
-
- /**
- * 生成路径
- */
- Stream.prototype.generatePaths = function () {
- this.createNavi();
- this.createPercentage();
- this.createAxis();
- this.createStreamPaths();
- this.createLegend();
- };
-
- /**
- * 创建图例
- */
- Stream.prototype.createLegend = function () {
- var conf = this.defaults,
- legends = [],
- m = [10, 20, 10, 20],
- left = m[3],
- top = m[0],
- lineHeight = 25,
- legendInterval = 10,
- width = conf.legendWidth - conf.legendIndent,
- r0 = 5,
- r1 = 7,
- circleW = 18,
- color = this.getColor({mode: conf.colorMode}),
- leftHeight,
- legendHeight,
- legendTopMargin,
- hoverIn = function (e) {
- var index = e.data.index;
- var stream = e.data.stream;
- var path = stream.paths[index];
- //stream.legends[stream.preIndex]
- stream.preIndex = index;
- stream.legends[index].css({"background": "#dddddd"});
- path.attr({"opacity": 0.5});
- },
- hoverOut = function (e) {
- var index = e.data.index;
- var stream = e.data.stream;
- var path = stream.paths[index];
- stream.preIndex = index;
- stream.legends[index].css({"background": "white"});
- path.attr({"opacity": 1.0});
- };
-
- var ul = $("").css({
- "margin": "0 0 0 10px",
- "paddingLeft": "0"
- });
- this.legend.append(ul);
-
- for (i = 0, l = this.infos.length; i < l; i++) {
- li = $('' + this.infos[i][0].name + "");
- li.css({
- "list-style-type": "square",
- "list-style-position": "inside",
- "color": color(i),
- "white-space": "nowrap",
- "padding-left": 5
- }).mouseenter({"index": i, "stream": this}, hoverIn)
- .mouseleave({"index": i, "stream": this}, hoverOut);
- ul.append(li);
- legends.push(li);
- }
- this.legends = legends;
-
- //height and margin
- leftHeight = this.rightContainer.height();
- legendHeight = this.legend.height();
- this.leftContainer.css({
- "height": leftHeight
- });
- this.legend.css({"marginTop": leftHeight > legendHeight ? leftHeight - legendHeight - 30 : 0});
- };
-
- /**
- * 创建导航
- */
- Stream.prototype.createNavi = function () {
- if (!this.defaults.more) {
- this.navi.css({
- "visibility": "hidden",
- "position": "absolute"
- });
- } else {
- this.navi.css({
- "visibility": "visible",
- "position": "relative"
- });
- }
- this.naviTrace.empty();
- for (var i = 0; i <= this.level; i++) {
- this.naviTrace.append($(" > "));
- var span = $(create("span")).html("第" + (i + 1) + "层");
- span.data = {level: i};
- this.naviTrace.append(span);
- if (i !== this.level) {
- span.css({"cursor": "pointer", "color": "#1E90FF"}).addClass("navi");
- }
- }
- this.naviBack.css("visibility", this.level > 0 ? "visible" : "hidden");
- };
-
- /**
- * 获取最大百分比
- */
- Stream.prototype.getMaxPercentage = function () {
- this.maxPercentage = this.allInfos.reduce(function (a, b, i, array) {
- return [{total: a[0].total + b[0].total}];
- })[0].total;
- return this.maxPercentage;
- };
-
- /**
- * 生成百分比数据
- */
- Stream.prototype.createPercentage = function () {
- if (!this.defaults.more) {
- return;
- }
- var conf = this.defaults;
- if (this.firstRender) {
- this.getMaxPercentage();
- }
- var maxY = this.getMaxY() / this.maxPercentage;
-
- var y = maxY > 0.1 ? (1 - maxY) * conf.height + conf.fontSize * 2 / 3
- : (1 - maxY) * conf.height - conf.fontSize * 2 / 3;
-
- if (this.firstRender) {
- this.percentageRect = this.percentagePaper.rect(0, (1 - maxY) * conf.height, conf.margin[3], maxY * conf.height)
- .attr({"fill": "#f4f4f4", "stroke": "#aaa", "stroke-width": 0.5});
- this.percentageText = this.percentagePaper.text(
- conf.margin[3] / 2,
- y,
- Math.round(maxY * 100) + "%"
- ).attr({"text-anchor": "middle"});
- } else {
- this.percentageRect.animate({"y": (1 - maxY) * conf.height, "height": maxY * conf.height}, 750);
- this.percentageText.attr({"text": Math.round(maxY * 100) + "%"})
- .animate({"y": y}, 750);
- }
- };
-
- /**
- * 生成Stream路径
- */
- Stream.prototype.createStreamPaths = function () {
- var canvas = this.canvas,
- paths = [],
- labels = [],
- area = this.generateArea(),
- color = this.getColor({mode: this.defaults.colorMode}),
- conf = this.defaults,
- i,
- l,
- _area,
- pathLegend,
- path,
- pathLegendMouseOver = function () {
- var path = this.path,
- anchorIndex = path.index;
- path.paths.forEach(function (d, i, array) {
- if (i !== anchorIndex) {
- array[i].attr({"fill": d3.interpolateRgb.apply(null, [array[i].color, "#fff"])(0.5)});
- }
- });
- this.style.backgroundColor = d3.interpolateRgb.apply(null, [path.color, "#fff"])(0.8);
- },
-
- pathLegendMouseOut = function () {
- var path = this.path,
- anchorIndex = path.index;
- path.paths.forEach(function (d, i, array) {
- if (i !== anchorIndex) {
- array[i].attr({"fill": array[i].color});
- }
- });
- path.legend.style.backgroundColor = path.color;
- },
-
- getLabelLocation = function (locArray, el) {
- var x = 0,
- y = 0,
- i;
- var ratioMargin = 0.15;
- var index = 0;
- var max = 0;
- var box = el.getBBox();
- var xInterval;
- var minTop, maxBottom;
- var showLabel = true;
- var loc;
- var height;
-
- xInterval = Math.ceil(box.width / (locArray[1].x - locArray[0].x) / 2);
- if (xInterval === 0) {
- xInterval = 1;
- }
-
- locArray.forEach(function (d, i, array) {
- var m = Math.max(ratioMargin * array.length, xInterval);
- if (i >= m && i <= array.length - m) {
- if (d.y > max) {
- minTop = d.y0 - d.y;
- maxBottom = d.y0;
- max = d.y;
- index = i;
- }
- }
- });
- for (i = index - xInterval; i <= index + xInterval; i++) {
- if (i < 0 || i >= locArray.length) {
- height = 0;
- showLabel = false;
- break;
- //return;
- }
- loc = locArray[i];
- //top's y is small
- if (loc.y0 - loc.y > minTop) {
- minTop = loc.y0 - loc.y;
- }
- if (loc.y0 < maxBottom) {
- maxBottom = loc.y0;
- }
- }
-
- if (showLabel && maxBottom - minTop >= box.height * 0.8) {
- x = locArray[index].x;
- y = (minTop + maxBottom) / 2;
- //y = locArray[index].y0 - locArray[index].y / 2;
- } else {
- showLabel = false;
- }
-
- return {x: x,
- y: y,
- showLabel: showLabel};
- },
-
- getLabelLocation_old2 = function (locArray, conf) {
- var x, y, height = 0, i;
- var indexMargin = Math.min(conf.indexMargin, Math.floor((locArray.length - 1) / 2));
- var ratioMargin = 0.15;
- var index = indexMargin;
- var max = 0;
- if (locArray.length >= conf.indexMargin * 2 + 1) {
- locArray.forEach(function (d, i, array) {
- var m = Math.max(indexMargin, ratioMargin * array.length);
- if (i >= m && i <= array.length - m) {
- if (d.y > max) {
- max = d.y;
- index = i;
- }
- }
- });
- x = locArray[index].x;
- y = locArray[index].y0 - locArray[index].y / 2;
- for (i = index - indexMargin; i <= index + indexMargin; i++) {
- height += locArray[i].y;
- }
- height = height / (2 * indexMargin + 1);
- } else {
- x = -100;
- y = -100;
- height = 0;
- }
-
- return {
- x: x,
- y: y,
- height: height
- };
- };
-
- canvas.rect(0, 0, conf.width, conf.height)
- .attr({"stroke": "none",
- "fill": "#e0e0e0"});
- for (i = 0, l = this.source.length; i < l; i++) {
- _area = area(this.pathSource[i]);
- path = canvas.path(_area).attr({fill: color(i),
- stroke: color(i),
- "stroke-width": 1,
- "stroke-linejoin": "round",
- "transform": "t0," + conf.topInterval
- });
- path.color = color(i);
- path.index = i;
- path.info = this.infos[i];
-
- path.paths = paths;
- path.topTrans = conf.topInterval;
- path.bottomTrans = conf.bottomInterval;
- path.stream = this;
-
- path.node.streamPath = path;
- path.node.setAttribute("class", "streamPath rvml");
-
- paths[path.index] = path;
- }
-
- //label
- for (i = 0, l = paths.length; i < l; i++) {
- path = paths[i];
- path.label = this.canvas.text(0, 0,
- conf.pathLabel ?
- path.info[0].name + " " + (Math.round(path.info[0].total * 10000) / 100) + "%" : "")
- .attr({"text-anchor": "middle",
- "fill": "white",
- "font-size": conf.fontSize,
- "font-family": "微软雅黑"});
- path.labelLoc = getLabelLocation(this.pathSource[i], path.label);
-
- if (path.labelLoc.showLabel) {
- path.label.attr({"x": path.labelLoc.x,
- "y": path.labelLoc.y});
- } else {
- path.label.attr({"opacity": 0});
- //path.labelOpacity = 1;
- }
- if (i === 0 && path.info[0].id === -1) {
- path.attr({"cursor": "pointer"});
- path.label.attr({"cursor": "pointer"});
- }
- labels.push(path.label);
- path.label.node.setAttribute("class", "streamPath rvml");
- }
-
- $(this.canvas.canvas).unbind();
-
- var mouseenter = function (e) {
- var stream = e.data.stream;
- stream.indicatorLine.attr({"stroke": "#000"});
- stream.highlightLine.attr({"stroke": "white"});
- stream.floatTag.css({"visibility" : "visible"});
- stream.axisPopText.show();
- stream.axisPopBubble.show();
- };
-
- var mouseleave = function (e) {
- var stream = e.data.stream,
- circle;
- stream.indicatorLine.attr({"stroke": "none"});
- stream.highlightLine.attr({"stroke": "none"});
- stream.floatTag.css({"visibility" : "hidden"});
- stream.axisPopText.hide();
- stream.axisPopBubble.hide();
- //recover prepath;
- if (typeof stream.prePath !== 'undefined') {
- stream.prePath.attr({"opacity": 1, "stroke-width": 1});
- // set legend
- //circle = stream.legends[stream.prePath.index].circle;
- //circle.attr({"r": circle.data("r0"), "opacity": 1});
- stream.legends[stream.prePath.index].css({"background": "white"});
- stream.prePath = undefined;
- }
- };
-
- var click = function (e) {
- var stream = e.data.stream,
- position;
- if (typeof stream.prePath !== 'undefined' && stream.prePath.info[0].id === -1) {
- //hidden
- stream.indicatorLine.attr({"stroke": "none"});
- stream.highlightLine.attr({"stroke": "none"});
- stream.floatTag.css({"visibility" : "hidden"});
-
- stream.level += 1;
-
- //set cover
- position = $(this).parent().position();
- stream.cover.css({
- left: position.left,
- top: position.top,
- visibility: "visible"
- });
- stream.coverMouse = {x: e.pageX, y: e.pageY};
-
- //redraw
- stream.getLevelSource();
- stream.reRender();
-
- //hidden
- stream.indicatorLine.attr({"stroke": "none"});
- stream.highlightLine.attr({"stroke": "none"});
- stream.floatTag.css({"visibility" : "hidden"});
-
- stream.paths.forEach(function (d, i, array) {
- d.attr({transform: "s1,0.001,0," + stream.defaults.height});
- d.label.hide();
- d.animate({transform: "t0,0"}, 750, "linear", function () {
- stream.cover.style.visibility = "hidden";
- if (typeof stream.coverMouse !== 'undefined') {
- stream.indicatorLine.attr({"stroke": "#000"});
- stream.highlightLine.attr({"stroke": "white"});
- stream.floatTag.css({"visibility" : "visible"});
- $(stream.canvas.canvas).trigger("mousemove",
- [stream.coverMouse.x, stream.coverMouse.y]);
- stream.coverMouse = undefined;
- }
- //if (d.labelOpacity === 1)
- if (d.labelLoc.showLabel) {
- d.label.show();
- }
- });
- });
- }
- };
-
- var mousemove = function (e, pageX, pageY) {
- var stream = e.data.stream;
- var offset = $(this).parent().offset();
- var position = $(this).parent().position();
- //var offset = $(this).offset();
- var x = (e.pageX || pageX) - offset.left,
- y = (e.pageY || pageY) - offset.top;
- var floatTag,
- floatTagWidth,
- floatTagHeight,
- mouseToFloatTag = {x: 20, y: 20};
- var path,
- pathSource = stream.pathSource,
- pathSourceP,
- pathIndex,
- circle;
- var i, l;
- var xIdx = Math.floor((x / (stream.defaults.width / (stream.source[0].length - 1) / 2) + 1) / 2);
- var pathsourceP,
- lineX;
-
- //get path
- path = undefined;
- pathSource = stream.pathSource;
- for (i = 0, l = pathSource.length; i < l; i++) {
- if (y >= pathSource[i][xIdx].y0 - pathSource[i][xIdx].y && y <= pathSource[i][xIdx].y0) {
- path = stream.paths[i];
- pathIndex = i;
- break;
- }
- }
- if (typeof path === 'undefined') {
- return;
- }
-
- //recover prepath;
- if (typeof stream.prePath !== 'undefined') {
- stream.prePath.attr({"opacity": 1, "stroke-width": 1});
- // set legend
- stream.legends[stream.prePath.index].css({"background": "white"});
- }
- //change new path;
- stream.prePath = path;
- path.attr({"opacity": 0.5, "stroke-width": 0});
-
- // set legend
- stream.legends[stream.prePath.index].css({"background": "#dddddd"});
-
- //set indicator and highlight line
- lineX = stream.defaults.width * xIdx / (stream.source[0].length - 1);
- pathSourceP = pathSource[pathSource.length - 1][xIdx];
- stream.indicatorLine.attr({path: "M" + lineX
- + " " + (pathSourceP.y0 - pathSourceP.y)
- + "V" + pathSource[0][xIdx].y0});
-
- pathSourceP = pathSource[pathIndex][xIdx];
- stream.highlightLine.attr({path: "M" + lineX
- + " " + (pathSourceP.y0 - pathSourceP.y)
- + "V" + pathSourceP.y0});
- if (pathIndex === 0 && path.info[0].id === -1) {
- stream.highlightLine.attr({"cursor": "pointer"});
- } else {
- stream.highlightLine.attr({"cursor": "auto"});
- }
-
- floatTag = stream.floatTag;
- floatTag.html(path.info[xIdx].tip);
-
- //axis pop bubble
- stream.axisPopText.attr({"text": stream.date[xIdx + stream.timeRange[0]]})
- .transform("t" + (lineX + stream.defaults.margin[3]) + ",0");
- stream.axisPopBubble.transform("t" + (lineX + stream.defaults.margin[3]) + ",0");
-
- //customevent;
- if (stream.defaults.customEventHandle.mousemove) {
- stream.defaults.customEventHandle.mousemove.call(stream,
- {"timeIndex": xIdx, "pathIndex": pathIndex});
- }
- };
- var $canvas = $(this.canvas.canvas);
- $canvas.bind("mouseenter", {"stream": this}, mouseenter);
- $canvas.bind("mouseleave", {"stream": this}, mouseleave);
- $canvas.bind("click", {"stream": this}, click);
- $canvas.bind("mousemove", {"stream": this}, mousemove);
-
- this.paths = paths;
- this.labels = labels;
- this.indicatorLine = canvas.path("M0 " + conf.topInterval + "V" + (conf.height - conf.bottomInterval))
- .attr({stroke: "none", "stroke-width": 1, "stroke-dasharray": "- "});
- this.highlightLine = canvas.path("M0 " + conf.topInterval + "V" + (conf.height - conf.bottomInterval))
- .attr({stroke: "none", "stroke-width": 2});
- };
-
- /**
- * 创建坐标轴
- */
- Stream.prototype.createAxis = function () {
- //all date strings' format are same, string length are same
- var conf = this.defaults,
- date = this.date.slice(this.timeRange[0], this.timeRange[1] + 1),
- left = conf.margin[3],
- //left = conf.margin[3] + conf.legendWidth,
- right = conf.totalWidth - conf.margin[1] - conf.legendWidth,
- tempWord,
- tickNumber,
- getPopPath = function (El) {
- //down pop
- var x = 0,
- y = 0,
- size = 4,
- cw = 23,
- bb = {height: 8};
- if (El) {
- bb = El.getBBox();
- bb.height *= 0.6;
- cw = bb.width / 2 - size;
- }
- return [
- 'M', x, y,
- 'l', size, size, cw, 0,
- 'a', size, size, 0, 0, 1, size, size,
- 'l', 0, bb.height,
- 'a', size, size, 0, 0, 1, -size, size,
- 'l', -(size * 2 + cw * 2), 0,
- 'a', size, size, 0, 0, 1, -size, -size,
- 'l', 0, -bb.height,
- 'a', size, size, 0, 0, 1, size, -size,
- 'l', cw, 0,
- 'z'
- ].join(',');
- };
-
- this.dateScale = d3.scale.linear()
- .domain([0, date.length - 1])
- .range([left, right]);
-
- tempWord = this.axisPaper.text(0, 0, date[0]);
- tickNumber = Math.floor((right - left) / tempWord.getBBox().width / 2) + 1;
- tempWord.remove();
- //tickNumber = 4;
-
- Axis().scale(this.dateScale)
- .ticks(tickNumber)
- //.ticks(conf.axisTickNumber)
- .tickSize(6, 3, 3)
- .tickAttr({"stroke": "none"})
- .minorTickAttr({"stroke": "none"})
- .domainAttr({"stroke": "none"})
- //.tickTextAttr({"font-size": conf.fontSize})
- .tickFormat(function (d) {
- return date[d] || "";
- })(this.axisPaper);
-
- this.axisPopText = this.axisPaper.text(0, 11, date[0])
- .attr({ "text-anchor": "middle",
- "fill": "#fff",
- //"font-size": conf.fontSize,
- "transform": "t" + left + ",0"})
- .hide();
- this.axisPopBubble = this.axisPaper.path(getPopPath(this.axisPopText))
- .attr({ "fill": "#000",
- //"opacity": 0,
- "transform": "t" + left + ",0"})
- .toBack()
- .hide();
- };
-
- /**
- * 获取纵轴最大值
- */
- Stream.prototype.getMaxY = function () {
- return d3.max(this.source, function (d) {
- return d3.max(d, function (d) {
- return d.y0 + d.y;
- });
- });
- };
-
- /**
- * 映射路径源
- */
- Stream.prototype.mapPathSource = function () {
- var conf = this.defaults,
- maxX = this.source[0].length - 1,//this.digitData[0].length - 1,
- maxY = this.getMaxY(),
- width = conf.width,
- height = conf.height - conf.topInterval - conf.bottomInterval;
- var i, j, l, l2, s, ps;
- this.pathSource = [];
- for (i = 0, l = this.source.length; i < l; i++) {
- this.pathSource[i] = [];
- for (j = 0, l2 = this.source[0].length; j < l2; j++) {
- s = this.source[i][j];
- ps = this.pathSource[i][j] = {};
- ps.x = s.x * width / maxX;
- ps.y0 = height - s.y0 * height / maxY;
- ps.y = s.y * height / maxY;
- }
- }
- };
-
- /**
- * 生成区域
- */
- Stream.prototype.generateArea = function () {
- this.mapPathSource();
- return d3.svg.area().x(function (d) {
- return d.x;
- }).y0(function (d) {
- return d.y0;
- }).y1(function (d) {
- return d.y0 - d.y;
- });
- };
-
- /*!
- * 生成区域
- */
- Stream.prototype.generateArea_old = function () {
- var conf = this.defaults,
- maxX = this.digitData[0].length - 1,
- maxY = this.getMaxY(),
- width = conf.width,
- height = conf.height - conf.topInterval - conf.bottomInterval,
- area = d3.svg.area()
- .x(function (d) {
- return d.x * width / maxX;
- })
- .y0(function (d) {
- return height - d.y0 * height / maxY;
- })
- .y1(function (d) {
- return height - (d.y + d.y0) * height / maxY;
- });
- return area;
- };
-
- /**
- * 清除画布
- */
- Stream.prototype.clearCanvas = function () {
- this.canvas.clear();
- this.legend.empty();
- this.axisPaper.clear();
- };
-
- /**
- * 重绘图表
- */
- Stream.prototype.reRender = function (options) {
- this.setOptions(options);
- this.clearCanvas();
- this.layout();
- this.generatePaths();
- this.canAnimate = true;
- };
-
- /**
- * 绘制图表
- */
- Stream.prototype.render = function (options) {
- this.firstRender = true;
- this.setOptions(options);
- this.clearCanvas();
- this.layout();
- this.generatePaths();
- this.firstRender = false;
- this.canAnimate = true;
- };
-
- /**
- * 重设图表
- */
- Stream.prototype.resize = function (options) {
- var conf = this.defaults;
-
- if (!options.width && !options.height) {
- throw new Error("no width and height input");
- } else if (options.width && !options.height) {
- if (conf.autoHeight) {
- this.setOptions({"width": options.width});
- } else {
- this.setOptions({"width": options.width, "height": conf.height});
- }
- } else if (!options.width && options.height) {
- this.setOptions({"width": conf.totalWidth, "height": options.height});
- } else {
- this.setOptions({"width": options.width, "height": options.height});
- }
-
- this.node.innerHTML = "";
- this.createCanvas();
- this.reRender();
- };
-
- /**
- * 侦听自定义事件
- * @param {String} eventName 事件名
- * @param {Function} callback 事件回调函数
- */
- Stream.prototype.on = function (eventName, callback) {
- if (typeof this.defaults.customEventHandle[eventName] !== 'undefined') {
- this.defaults.customEventHandle[eventName] = callback;
- }
- };
-
- /**
- * 设置动画
- * @param {Object} options 选项对象
- * @param {Number} timeDuration 时间
- */
- Stream.prototype.animate = function (options, timeDuration) {
- //must after render if new Source has been set;
- if (!this.canAnimate) {
- throw new Error("Function animate must be called after render if new Source has been set.");
- }
- var time = 0,
- area,
- color,
- i,
- l;
- if (arguments.length > 1) {
- time = timeDuration;
- }
-
- //this.setOptions(options);
- if (options.offset || options.order) {
- this.source = this.remapSource(this.digitData);
- this.layout();
- }
- area = this.generateArea();
- color = this.getColor();
- for (i = 0, l = this.source.length; i < l; i++) {
- var _area = area(this.source[i]);
- var anim = Raphael.animation({path: _area, fill: color(i)}, time);
- this.paths[i].animate(anim);
- }
- };
-
- /*!
- * 导出Stream
- */
- return Stream;
-});
+/*global Raphael, d3, $, define, _ */
+/*!
+ * Stream的兼容定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Stream', function (require) {
+ var DataV = require('DataV');
+ var Axis = require('Axis');
+ var create = function (tag) {
+ return document.createElement(tag);
+ };
+
+ /*
+ * Stream构造函数
+ * Create stream in a dom node with id "chart", width is 500; height is 600px;
+ * Options:
+ *
+ * - `width` 宽度,默认为节点宽度
+ *
+ * Examples:
+ * ```
+ * var stream = new Stream("chart", {"width": 500, "height": 600});
+ * ```
+ * @param {Mix} node The dom node or dom node Id
+ * @param {Object} options options json object for determin stream style.
+ */
+ var Stream = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Stream";
+ this.node = this.checkContainer(node);
+
+ this.level = 0;
+ // Properties
+ this.defaults.offset = "zero";//"expand";
+ this.defaults.order = "default";
+ this.defaults.columnNameUsed = "auto";
+ this.defaults.rowNameUsed = "auto";
+ this.defaults.topInterval = 0;
+ this.defaults.bottomInterval = 0;
+ this.defaults.legend = true;
+ this.defaults.axis = true;
+ this.defaults.pathLabel = true;
+ this.defaults.fontSize = 12;
+ this.defaults.heightWidthRatio = 0.618;
+ //this.defaults.axisTickNumber = 8; // axis ticks number
+
+ this.defaults.indexMargin = 3; // if dates.length < indexMargin * 2 + 1, do not show label
+
+ this.defaults.more = true;
+ this.defaults.max = 20;
+ this.defaults.other = 0.1;
+
+ this.timeRange = [];
+ // Canvas
+ this.defaults.width = 750;
+ this.defaults.height = 360;
+ this.defaults.totalWidth = 820;
+ this.defaults.naviBackWidth = 80;
+ this.defaults.legendHeight = 50;
+ this.defaults.legendWidth = 150;
+ this.defaults.legendIndent = 21;
+ this.defaults.axisHeight = 30;
+ this.defaults.margin = [0, 40, 0, 40];
+
+ this.defaults.customEventHandle = {"mousemove": null};
+
+ //test related
+ this.defaults.testMakeup = false;
+ this.defaults.testDays = 30;
+ this.defaults.testDataType = 0; //0: random; 1: false random; 2: same; >2: small change;
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * Stream图纬度描述
+ */
+ Stream.dimension = {};
+ /**
+ * 流向纬度,例如,时间
+ */
+ Stream.dimension.stream = {
+ type: "string",
+ required: true
+ };
+ /**
+ * 堆叠纬度,例如,按类目
+ */
+ Stream.dimension.stack = {
+ type: "string",
+ required: true
+ };
+ /**
+ * 值纬度,在流向和堆叠纬度上的值
+ */
+ Stream.dimension.value = {
+ type: "number",
+ required: true
+ };
+
+
+ Stream.prototype.initLegend = function () {
+ var conf = this.defaults;
+ this.legend = $(create("div")).css({
+ "overflow": "hidden",
+ "width": conf.legendWidth - conf.legendIndent,
+ "padding": "10px 0 10px 0"
+ });
+ this.leftContainer = $(create("div")).css({
+ "width": conf.legendWidth - 4,
+ "overflow-x": "hidden"
+ }).append(this.legend);
+ };
+
+ Stream.prototype.initNav = function () {
+ var conf = this.defaults;
+ this.navi = $(create("div")).css({
+ "borderTop": "1px solid #ddd",
+ "borderBottom": "1px solid #ddd",
+ "padding": "5px 10px 10px 10px",
+ "fontSize": (conf.fontSize + 1)
+ });
+ this.naviTrace = $(create("div")).css({
+ "width": conf.totalWidth - conf.naviBackWidth - 50,
+ "marginTop": "5px"
+ });
+
+ this.naviBack = $(create("div")).html("返回上层").css({
+ "width": conf.naviBackWidth,
+ "float": "right",
+ "backgroundColor": "#f4f4f4",
+ "paddingTop": "4px",
+ "paddingBottom": "4px",
+ "border": "1px solid #ddd",
+ "borderRadius": "2px",
+ "cursor": "pointer",
+ "textAlign": "center",
+ "visibility": "hidden"
+ });
+ var getBack = function (stream) {
+ stream.cover.style.visibility = "visible";
+ stream.coverMouse = undefined;
+ stream.getLevelSource();
+ stream.reRender();
+
+ //hidden
+ stream.indicatorLine.attr({"stroke": "none"});
+ stream.highlightLine.attr({"stroke": "none"});
+ stream.floatTag.css({"visibility" : "hidden"});
+
+ stream.paths.forEach(function (d, i, array) {
+ d.attr({transform: "s1,0.001,0,0"});
+ d.label.hide();
+ d.animate({transform: "t0,0"}, 750, "linear", function () {
+ stream.cover.style.visibility = "hidden";
+ if (typeof stream.coverMouse !== 'undefined') {
+ stream.indicatorLine.attr({"stroke": "#000"});
+ stream.highlightLine.attr({"stroke": "white"});
+ stream.floatTag.css({"visibility" : "visible"});
+ $(stream.canvas.canvas).trigger("mousemove", [stream.coverMouse.x, stream.coverMouse.y]);
+ stream.coverMouse = undefined;
+ }
+ if (d.labelLoc.showLabel) {
+ d.label.show();
+ }
+ });
+ });
+ };
+ this.naviTrace.on("click", ".navi", {stream: this}, function (e) {
+ var stream = e.data.stream;
+ stream.level = e.target.data.level;
+ getBack(stream);
+ });
+
+ this.naviBack.on("click", {stream: this}, function (e) {
+ var stream = e.data.stream;
+ stream.level -= 1;
+ getBack(stream);
+ });
+ this.navi.append(this.naviBack).append(this.naviTrace);
+ };
+
+ Stream.prototype.initAxis = function () {
+ var conf = this.defaults;
+ this.axis = $(create("div")).css({
+ "marginTop": "0",
+ "borderTop": "1px solid #ddd",
+ "height": conf.axisHeight
+ });
+ this.axisPaper = new Raphael(this.axis, conf.totalWidth - conf.legendWidth, conf.axisHeight);
+ };
+
+ Stream.prototype.initPercentage = function () {
+ var conf = this.defaults;
+ this.percentage = $(create("div")).css({
+ "width": conf.margin[3],
+ "height": conf.height,
+ "float": "left",
+ "marginBottom": "0",
+ "borderBottom": "0",
+ "paddingBottom": "0"
+ });
+ if (this.defaults.more) {
+ this.percentagePaper = new Raphael(this.percentage, conf.margin[3], conf.height);
+ }
+ };
+
+ /*!
+ * 创建画布
+ */
+ Stream.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.initLegend();
+ this.initNav();
+ this.initAxis();
+ this.initPercentage();
+
+ var container = $(create("div")).css({"position": "relative"});
+ this.canvas = new Raphael(container, conf.width, conf.height);console.log(conf.width, conf.height);
+ this.canvasContainer = $(create("div")).css({
+ "float": "left",
+ "width": conf.width,
+ "height": conf.height,
+ "marginBottom": "0",
+ "borderBottom": "0",
+ "paddingVottom": "0"
+ }).append(container);
+
+ this.floatTag = DataV.FloatTag()(container).css({"visibility": "hidden"});
+
+ // cover can block stream canvas when animating to prevent some default mouse event
+ this.cover = $(create("div")).css({
+ "position": "absolute",
+ "width": conf.width,
+ "height": conf.height,
+ "zIndex": 100,
+ "visibility": "hidden"
+ }).bind("mousemove", {stream: this}, function (e) {
+ var stream = e.data.stream;
+ stream.coverMouse = {x: e.pageX, y: e.pageY};
+ }).bind("mouseleave", {stream: this}, function (e) {
+ var stream = e.data.stream;
+ stream.coverMouse = undefined;
+ });
+
+ this.middleContainer = $(create("div")).css({
+ "height": conf.height
+ })
+ .append(this.percentage)
+ .append(this.canvasContainer)
+ .append(this.cover);
+ if (this.defaults.more) {
+ this.percentagePaper = new Raphael(this.percentage, conf.margin[3], conf.height);
+ }
+
+ this.rightContainer = $(create("div")).css({
+ "float": "right",
+ "width": conf.totalWidth - conf.legendWidth
+ }).append(this.navi)
+ .append(this.middleContainer)
+ .append(this.axis);
+
+ $(this.node).css({
+ "position": "relative",
+ "width": conf.totalWidth
+ }).append(this.rightContainer).append(this.leftContainer);
+ };
+
+ /**
+ * 设置自定义选项
+ */
+ Stream.prototype.setOptions = function (options) {
+ _.extend(this.defaults, options);
+
+ var conf = this.defaults;
+ if (options && options.width) {
+ conf.totalWidth = conf.width;
+ conf.width = conf.totalWidth - conf.margin[1] - conf.margin[3] - conf.legendWidth;
+ if (!options.height) {
+ conf.autoHeight = true;
+ conf.height = conf.width * conf.heightWidthRatio;
+ } else {
+ conf.autoHeight = false;
+ }
+ }
+ };
+ /*!
+ * 排序
+ * @param {Array} source 待排序的二维数组
+ */
+ Stream.prototype.sort = function (source) {
+ var i, j, l, ll;
+ var rowSum = [];
+ var columnSum = [];
+ var newSource = [];
+ var rowName = [];
+ var that = this;
+
+ for (j = 0, ll = source[0].length; j < ll; j++) {
+ columnSum[j] = 0;
+ }
+
+ for (i = 0, l = source.length; i < l; i++) {
+ rowSum[i] = 0;
+ for (j = 0, ll = source[0].length; j < ll; j++) {
+ rowSum[i] += source[i][j];
+ columnSum[j] += source[i][j];
+ }
+ rowSum[i] = [rowSum[i]];
+ rowSum[i].index = i;
+ }
+
+ rowSum.sort(function (a, b) {
+ return b[0] - a[0];
+ });
+
+ rowSum.forEach(function (d, i) {
+ newSource[i] = source[d.index];
+ if (that.rowName) {
+ rowName[i] = that.rowName[d.index];
+ }
+ });
+
+ for (i = 0, l = rowSum.length; i < l; i++) {
+ rowSum[i] = rowSum[i][0];
+ }
+
+ //this.mergeOthe
+
+ this.rowName = rowName;
+ this.rowSum = rowSum;
+ this.columnSum = columnSum;
+ this.total = d3.sum(this.rowSum);
+
+ return newSource;
+ };
+
+ /*!
+ * 合并数据
+ */
+ Stream.prototype.mergeOther = function (source) {
+ //change digitData, rowSum, rowName;
+ };
+
+ /**
+ * 获取数据
+ * @param {Array} source 从二维数组中,获取纯数据的部分(排除掉列名后)
+ */
+ Stream.prototype.getDigitData = function (source) {
+ //get first column name, row name and digitData;
+ var conf = this.defaults,
+ firstRow = source[0],
+ firstColumn,
+ digitData;
+
+ var i, j, l, ll;
+
+ firstColumn = source.map(function (d) {
+ return d[0];
+ });
+
+ if (this.hasRowName()) {
+ if (this.hasColumnName()) {
+ //row names, column names
+ this.rowName = firstColumn.slice(1);
+ this.columnName = firstRow.slice(1);
+ digitData = source.map(function (d) {
+ return d.slice(1);
+ }).slice(1);
+ } else {
+ //row names, no column names
+ this.rowName = firstColumn;
+ this.columnName = undefined;
+ digitData = source.map(function (d) {
+ return d.slice(1);
+ });
+ }
+ } else {
+ if (this.hasColumnName()) {
+ //no row names, column names
+ this.rowName = undefined;
+ this.columnName = firstRow;
+ digitData = source.slice(1);
+ } else {
+ //no row names, no column names
+ if (conf.columnNameUsed === "auto" && conf.rowNameUsed === "auto" && !DataV.isNumeric(source[0][0])) {
+ throw new Error("Please specify whether there are column names or row names");
+ }
+ this.rowName = undefined;
+ this.columnName = undefined;
+ digitData = source;
+ }
+ }
+ for (i = 0, l = digitData.length; i < l; i++) {
+ for (j = 0, ll = digitData[0].length; j < ll; j++) {
+ digitData[i][j] = parseFloat(digitData[i][j]);
+ }
+ }
+ return digitData;
+ };
+
+ /**
+ * 获取信息数据
+ */
+ Stream.prototype.getInfo = function () {
+ var allInfos = [];
+ var i, j, l, ll;
+ var infos, info;
+ var column;
+ var digitData = this.digitData;
+ var descending = function (a, b) {
+ return b.value - a.value;
+ };
+ for (i = 0, l = this.digitData.length; i < l; i++) {
+ infos = allInfos[i] = [];
+ infos.ratio = this.rowSum[i] / this.total;
+ infos.value = this.rowSum[i];
+ infos.name = this.rowName[i];
+ infos.id = i;
+ }
+ for (i = 0, l = digitData.length; i < l; i++) {
+ column = [];
+ for (j = 0, ll = digitData[0].length; j < ll; j++) {
+ allInfos[i][j] = column[j] = {
+ "date": this.columnName[j],
+ "id": i,
+ "name": allInfos[i].name,
+ "tip": "" + allInfos[i].name + "
占比:"
+ + (Math.round(digitData[i][j] / this.columnSum[j] * 10000) / 100) + "%
",
+ "total": allInfos[i].ratio,
+ //"value": columnTotal[i]
+ "value" : digitData[i][j],
+ "index" : j,
+ "rowInfo" : allInfos[i],
+ "ratio" : digitData[i][j] / this.columnSum[j]
+ };
+ }
+
+ column.sort(descending);
+
+ for (j = 0, ll = column.length; j < ll; j++) {
+ column[j].rank = j;
+ }
+ }
+ return allInfos;
+ };
+
+ /**
+ * 设置数据源
+ * Examples:
+ * 例如下面的数组表示2个人在一年4个季度的消费。第一个人在4个季度里消费了1、2、3、9元。第二个人消费了3、4、6、3元。
+ * ```
+ * [
+ * [1,2,3,9],
+ * [3,4,6,3]
+ * ]
+ * ```
+ * @param {Array} source 二维数组的数据源
+ */
+ Stream.prototype.setSource = function (source) {
+ this.rawData = source;
+ this.digitData = this.getDigitData(this.rawData);
+
+ //get date, sort and allInfos;
+ //date
+ this.date = source[0].slice(1, source[0].length);
+ this.timeRange = [0, this.date.length - 1];
+ //sort
+ this.digitData = this.sort(this.digitData);
+ this.allInfos = this.getInfo(this.digitData);
+
+ this.level = 0;
+ this.getLevelSource();
+ //this.source = this.remapSource(digitData);
+ this.canAnimate = false;
+ };
+
+ /**
+ * If useString is true, start and end are date string, else start and end are index number
+ * @param {Number|String} start 起始范围
+ * @param {Number|String} end 结束范围
+ * @param {Boolean} useString 是否是字符串
+ */
+ Stream.prototype.setTimeRange = function (start, end, useString) {
+ var idx1, idx2;
+ if (useString) {
+ idx1 = _.indexOf(this.date, start);
+ idx2 = _.indexOf(this.date, end);
+ } else {
+ idx1 = start;
+ idx2 = end;
+ }
+
+ var min = Math.min(idx1, idx2);
+ var max = Math.max(idx1, idx2);
+ if (min === max) {
+ throw new Error("start index and end index can not be same.");
+ }
+ if (min < 0 || max > this.date.length - 1) {
+ throw new Error("start index or end index is beyond the time range.");
+ }
+
+ this.timeRange = [min, max];
+ this.getLevelSource();
+ };
+
+ /*!
+ * 根据时间范围获取数据
+ */
+ Stream.prototype.getDataByTimeRange = function () {
+ if (this.timeRange[0] === 0 && this.timeRange[1] === this.date.length - 1) {
+ return this.digitData;
+ } else {
+ var tr = this.timeRange;
+ return _.map(this.digitData, function (d, i) {
+ return d.slice(tr[0], tr[1] + 1);
+ });
+ }
+ };
+
+ /*!
+ * 获取等级数据
+ */
+ Stream.prototype.getLevelSource = function () {
+ var data = this.getDataByTimeRange(),//this.digitData,
+ rowStart = this.level * (this.defaults.max - 1),
+ rowEnd,
+ needMoreRow,
+ column = data[0].length,
+ remap = [],
+ infos = [],
+ moreRowInfo = [];
+
+ if (column < 1) {
+ throw new Error("Data source is empty.");
+ }
+ if (this.defaults.more) {
+ if (rowStart + this.defaults.max >= data.length) {
+ if (rowStart + this.defaults.max === data.length && this.allInfos[data.length - 1][0].id === -2) {
+ //last more sum < this.defaults.other
+ rowEnd = rowStart + this.defaults.max - 1;
+ needMoreRow = true;
+ } else {
+ rowEnd = data.length;
+ needMoreRow = false;
+ }
+ } else {
+ rowEnd = rowStart + this.defaults.max - 1;
+ needMoreRow = true;
+ }
+ } else {
+ rowStart = 0;
+ rowEnd = data.length;
+ needMoreRow = false;
+ }
+ for (i = rowStart; i < rowEnd; i++) {
+ k = i - rowStart;
+ remap[k] = [];
+ for (j = 0; j < column; j++) {
+ remap[k][j] = {};
+ remap[k][j].x = j;
+ remap[k][j].y = parseFloat(data[i][j]);
+ }
+ if (this.timeRange[0] === 0 && this.timeRange[1] === this.date.length - 1) {
+ infos[k] = this.allInfos[i];
+ } else {
+ infos[k] = this.allInfos[i].slice(this.timeRange[0], this.timeRange[1] + 1);
+ }
+ }
+ if (needMoreRow) {
+ if (rowStart + this.defaults.max === data.length && this.allInfos[data.length - 1][0].id === -2) {
+ //last more sum < this.defaults.other
+ var valueArray = data[data.length - 1];
+ moreRow = [];
+ for (j = 0; j < column; j++) {
+ moreRow[j] = {};
+ moreRow[j].x = j;
+ moreRow[j].y = valueArray[j];
+ }
+ moreRowInfo = this.allInfos[data.length - 1];
+ } else {
+ moreRow = [];
+ for (j = 0; j < column; j++) {
+ moreSum = 0;
+ totalSum = 0;
+ for (m = data.length - 1; m >= rowEnd; m--) {
+ moreSum += parseFloat(data[m][j]);
+ totalSum += parseFloat(this.allInfos[m][j].total);
+ }
+ moreRow[j] = {};
+ moreRow[j].x = j;
+ moreRow[j].y = moreSum;
+ moreRowInfo[j] = {
+ "date": this.allInfos[0][j].date,
+ "id": -1,// -1 clickable; -2 not click
+ "name": "更多",
+ "tip": "更多
占比:" + (Math.round(moreSum * 10000) / 100) + "%
点击查看更多信息
",
+ "total": totalSum,
+ "value": moreSum
+ };
+ }
+ }
+ remap = [moreRow].concat(remap);
+ infos = [moreRowInfo].concat(infos);
+ }
+ this.infos = infos;
+ this.source = remap;
+ };
+
+ /**
+ * 生成布局
+ */
+ Stream.prototype.layout = function () {
+ var conf = this.defaults;
+ d3.layout.stack().offset(conf.offset).order(conf.order)(this.source);
+ };
+
+ /**
+ * 根据选择方案获取颜色数据
+ * @param {Object} colorJson 颜色方案
+ * @return {Array} 返回颜色数据
+ */
+ Stream.prototype.getColor = function (colorJson) {
+ var colorMatrix = DataV.getColor();
+ var color;
+ var colorStyle = colorJson || {};
+ var colorMode = colorStyle.mode || 'default';
+ var i, l;
+
+ switch (colorMode) {
+ case "gradient":
+ l = this.source.length;
+ var colorL = Math.round(l / 5);
+ if (colorL > colorMatrix.length - 1) {
+ colorL = colorMatrix.length - 1;
+ }
+ var testColor = [colorMatrix[0][0], colorMatrix[colorL][0]];
+ var test1 = DataV.gradientColor(testColor, "special");
+ var testColorMatrix = [];
+ var testColorMatrix1 = [];
+ for (i = 0; i < l; i++) {
+ testColorMatrix.push([test1(i / (l - 1)), test1(i / (l - 1))]);
+ }
+
+ for (i = (l - 1); i >= 0; i--) {
+ testColorMatrix1.push(testColorMatrix[i]);
+ }
+
+ colorMatrix = testColorMatrix;
+
+ break;
+ case "random":
+ case "default":
+ break;
+ }
+
+ var ratio = colorStyle.ratio || 0;
+ if (ratio < 0) { ratio = 0; }
+ if (ratio > 1) { ratio = 1; }
+ var colorArray = [];
+ for (i = 0, l = colorMatrix.length; i < l; i++) {
+ var colorFunc = d3.interpolateRgb.apply(null, [colorMatrix[i][0], colorMatrix[i][1]]);
+ colorArray.push(colorFunc(ratio));
+ }
+ color = d3.scale.ordinal().range(colorArray);
+
+ return color;
+ };
+
+ /**
+ * 生成路径
+ */
+ Stream.prototype.generatePaths = function () {
+ this.createNavi();
+ this.createPercentage();
+ this.createAxis();
+ this.createStreamPaths();
+ this.createLegend();
+ };
+
+ /**
+ * 创建图例
+ */
+ Stream.prototype.createLegend = function () {
+ var conf = this.defaults,
+ legends = [],
+ m = [10, 20, 10, 20],
+ left = m[3],
+ top = m[0],
+ lineHeight = 25,
+ legendInterval = 10,
+ width = conf.legendWidth - conf.legendIndent,
+ r0 = 5,
+ r1 = 7,
+ circleW = 18,
+ color = this.getColor({mode: conf.colorMode}),
+ leftHeight,
+ legendHeight,
+ legendTopMargin,
+ hoverIn = function (e) {
+ var index = e.data.index;
+ var stream = e.data.stream;
+ var path = stream.paths[index];
+ //stream.legends[stream.preIndex]
+ stream.preIndex = index;
+ stream.legends[index].css({"background": "#dddddd"});
+ path.attr({"opacity": 0.5});
+ },
+ hoverOut = function (e) {
+ var index = e.data.index;
+ var stream = e.data.stream;
+ var path = stream.paths[index];
+ stream.preIndex = index;
+ stream.legends[index].css({"background": "white"});
+ path.attr({"opacity": 1.0});
+ };
+
+ var ul = $("").css({
+ "margin": "0 0 0 10px",
+ "paddingLeft": "0"
+ });
+ this.legend.append(ul);
+
+ for (i = 0, l = this.infos.length; i < l; i++) {
+ li = $('' + this.infos[i][0].name + "");
+ li.css({
+ "list-style-type": "square",
+ "list-style-position": "inside",
+ "color": color(i),
+ "white-space": "nowrap",
+ "padding-left": 5
+ }).mouseenter({"index": i, "stream": this}, hoverIn)
+ .mouseleave({"index": i, "stream": this}, hoverOut);
+ ul.append(li);
+ legends.push(li);
+ }
+ this.legends = legends;
+
+ //height and margin
+ leftHeight = this.rightContainer.height();
+ legendHeight = this.legend.height();
+ this.leftContainer.css({
+ "height": leftHeight
+ });
+ this.legend.css({"marginTop": leftHeight > legendHeight ? leftHeight - legendHeight - 30 : 0});
+ };
+
+ /**
+ * 创建导航
+ */
+ Stream.prototype.createNavi = function () {
+ if (!this.defaults.more) {
+ this.navi.css({
+ "visibility": "hidden",
+ "position": "absolute"
+ });
+ } else {
+ this.navi.css({
+ "visibility": "visible",
+ "position": "relative"
+ });
+ }
+ this.naviTrace.empty();
+ for (var i = 0; i <= this.level; i++) {
+ this.naviTrace.append($(" > "));
+ var span = $(create("span")).html("第" + (i + 1) + "层");
+ span.data = {level: i};
+ this.naviTrace.append(span);
+ if (i !== this.level) {
+ span.css({"cursor": "pointer", "color": "#1E90FF"}).addClass("navi");
+ }
+ }
+ this.naviBack.css("visibility", this.level > 0 ? "visible" : "hidden");
+ };
+
+ /**
+ * 获取最大百分比
+ */
+ Stream.prototype.getMaxPercentage = function () {
+ this.maxPercentage = this.allInfos.reduce(function (a, b, i, array) {
+ return [{total: a[0].total + b[0].total}];
+ })[0].total;
+ return this.maxPercentage;
+ };
+
+ /**
+ * 生成百分比数据
+ */
+ Stream.prototype.createPercentage = function () {
+ if (!this.defaults.more) {
+ return;
+ }
+ var conf = this.defaults;
+ if (this.firstRender) {
+ this.getMaxPercentage();
+ }
+ var maxY = this.getMaxY() / this.maxPercentage;
+
+ var y = maxY > 0.1 ? (1 - maxY) * conf.height + conf.fontSize * 2 / 3
+ : (1 - maxY) * conf.height - conf.fontSize * 2 / 3;
+
+ if (this.firstRender) {
+ this.percentageRect = this.percentagePaper.rect(0, (1 - maxY) * conf.height, conf.margin[3], maxY * conf.height)
+ .attr({"fill": "#f4f4f4", "stroke": "#aaa", "stroke-width": 0.5});
+ this.percentageText = this.percentagePaper.text(
+ conf.margin[3] / 2,
+ y,
+ Math.round(maxY * 100) + "%"
+ ).attr({"text-anchor": "middle"});
+ } else {
+ this.percentageRect.animate({"y": (1 - maxY) * conf.height, "height": maxY * conf.height}, 750);
+ this.percentageText.attr({"text": Math.round(maxY * 100) + "%"})
+ .animate({"y": y}, 750);
+ }
+ };
+
+ /**
+ * 生成Stream路径
+ */
+ Stream.prototype.createStreamPaths = function () {
+ var canvas = this.canvas,
+ paths = [],
+ labels = [],
+ area = this.generateArea(),
+ color = this.getColor({mode: this.defaults.colorMode}),
+ conf = this.defaults,
+ i,
+ l,
+ _area,
+ pathLegend,
+ path,
+ pathLegendMouseOver = function () {
+ var path = this.path,
+ anchorIndex = path.index;
+ path.paths.forEach(function (d, i, array) {
+ if (i !== anchorIndex) {
+ array[i].attr({"fill": d3.interpolateRgb.apply(null, [array[i].color, "#fff"])(0.5)});
+ }
+ });
+ this.style.backgroundColor = d3.interpolateRgb.apply(null, [path.color, "#fff"])(0.8);
+ },
+
+ pathLegendMouseOut = function () {
+ var path = this.path,
+ anchorIndex = path.index;
+ path.paths.forEach(function (d, i, array) {
+ if (i !== anchorIndex) {
+ array[i].attr({"fill": array[i].color});
+ }
+ });
+ path.legend.style.backgroundColor = path.color;
+ },
+
+ getLabelLocation = function (locArray, el) {
+ var x = 0,
+ y = 0,
+ i;
+ var ratioMargin = 0.15;
+ var index = 0;
+ var max = 0;
+ var box = el.getBBox();
+ var xInterval;
+ var minTop, maxBottom;
+ var showLabel = true;
+ var loc;
+ var height;
+
+ xInterval = Math.ceil(box.width / (locArray[1].x - locArray[0].x) / 2);
+ if (xInterval === 0) {
+ xInterval = 1;
+ }
+
+ locArray.forEach(function (d, i, array) {
+ var m = Math.max(ratioMargin * array.length, xInterval);
+ if (i >= m && i <= array.length - m) {
+ if (d.y > max) {
+ minTop = d.y0 - d.y;
+ maxBottom = d.y0;
+ max = d.y;
+ index = i;
+ }
+ }
+ });
+ for (i = index - xInterval; i <= index + xInterval; i++) {
+ if (i < 0 || i >= locArray.length) {
+ height = 0;
+ showLabel = false;
+ break;
+ //return;
+ }
+ loc = locArray[i];
+ //top's y is small
+ if (loc.y0 - loc.y > minTop) {
+ minTop = loc.y0 - loc.y;
+ }
+ if (loc.y0 < maxBottom) {
+ maxBottom = loc.y0;
+ }
+ }
+
+ if (showLabel && maxBottom - minTop >= box.height * 0.8) {
+ x = locArray[index].x;
+ y = (minTop + maxBottom) / 2;
+ //y = locArray[index].y0 - locArray[index].y / 2;
+ } else {
+ showLabel = false;
+ }
+
+ return {x: x,
+ y: y,
+ showLabel: showLabel};
+ },
+
+ getLabelLocation_old2 = function (locArray, conf) {
+ var x, y, height = 0, i;
+ var indexMargin = Math.min(conf.indexMargin, Math.floor((locArray.length - 1) / 2));
+ var ratioMargin = 0.15;
+ var index = indexMargin;
+ var max = 0;
+ if (locArray.length >= conf.indexMargin * 2 + 1) {
+ locArray.forEach(function (d, i, array) {
+ var m = Math.max(indexMargin, ratioMargin * array.length);
+ if (i >= m && i <= array.length - m) {
+ if (d.y > max) {
+ max = d.y;
+ index = i;
+ }
+ }
+ });
+ x = locArray[index].x;
+ y = locArray[index].y0 - locArray[index].y / 2;
+ for (i = index - indexMargin; i <= index + indexMargin; i++) {
+ height += locArray[i].y;
+ }
+ height = height / (2 * indexMargin + 1);
+ } else {
+ x = -100;
+ y = -100;
+ height = 0;
+ }
+
+ return {
+ x: x,
+ y: y,
+ height: height
+ };
+ };
+
+ canvas.rect(0, 0, conf.width, conf.height)
+ .attr({"stroke": "none",
+ "fill": "#e0e0e0"});
+ for (i = 0, l = this.source.length; i < l; i++) {
+ _area = area(this.pathSource[i]);
+ path = canvas.path(_area).attr({fill: color(i),
+ stroke: color(i),
+ "stroke-width": 1,
+ "stroke-linejoin": "round",
+ "transform": "t0," + conf.topInterval
+ });
+ path.color = color(i);
+ path.index = i;
+ path.info = this.infos[i];
+
+ path.paths = paths;
+ path.topTrans = conf.topInterval;
+ path.bottomTrans = conf.bottomInterval;
+ path.stream = this;
+
+ path.node.streamPath = path;
+ path.node.setAttribute("class", "streamPath rvml");
+
+ paths[path.index] = path;
+ }
+
+ //label
+ for (i = 0, l = paths.length; i < l; i++) {
+ path = paths[i];
+ path.label = this.canvas.text(0, 0,
+ conf.pathLabel ?
+ path.info[0].name + " " + (Math.round(path.info[0].total * 10000) / 100) + "%" : "")
+ .attr({"text-anchor": "middle",
+ "fill": "white",
+ "font-size": conf.fontSize,
+ "font-family": "微软雅黑"});
+ path.labelLoc = getLabelLocation(this.pathSource[i], path.label);
+
+ if (path.labelLoc.showLabel) {
+ path.label.attr({"x": path.labelLoc.x,
+ "y": path.labelLoc.y});
+ } else {
+ path.label.attr({"opacity": 0});
+ //path.labelOpacity = 1;
+ }
+ if (i === 0 && path.info[0].id === -1) {
+ path.attr({"cursor": "pointer"});
+ path.label.attr({"cursor": "pointer"});
+ }
+ labels.push(path.label);
+ path.label.node.setAttribute("class", "streamPath rvml");
+ }
+
+ $(this.canvas.canvas).unbind();
+
+ var mouseenter = function (e) {
+ var stream = e.data.stream;
+ stream.indicatorLine.attr({"stroke": "#000"});
+ stream.highlightLine.attr({"stroke": "white"});
+ stream.floatTag.css({"visibility" : "visible"});
+ stream.axisPopText.show();
+ stream.axisPopBubble.show();
+ };
+
+ var mouseleave = function (e) {
+ var stream = e.data.stream,
+ circle;
+ stream.indicatorLine.attr({"stroke": "none"});
+ stream.highlightLine.attr({"stroke": "none"});
+ stream.floatTag.css({"visibility" : "hidden"});
+ stream.axisPopText.hide();
+ stream.axisPopBubble.hide();
+ //recover prepath;
+ if (typeof stream.prePath !== 'undefined') {
+ stream.prePath.attr({"opacity": 1, "stroke-width": 1});
+ // set legend
+ //circle = stream.legends[stream.prePath.index].circle;
+ //circle.attr({"r": circle.data("r0"), "opacity": 1});
+ stream.legends[stream.prePath.index].css({"background": "white"});
+ stream.prePath = undefined;
+ }
+ };
+
+ var click = function (e) {
+ var stream = e.data.stream,
+ position;
+ if (typeof stream.prePath !== 'undefined' && stream.prePath.info[0].id === -1) {
+ //hidden
+ stream.indicatorLine.attr({"stroke": "none"});
+ stream.highlightLine.attr({"stroke": "none"});
+ stream.floatTag.css({"visibility" : "hidden"});
+
+ stream.level += 1;
+
+ //set cover
+ position = $(this).parent().position();
+ stream.cover.css({
+ left: position.left,
+ top: position.top,
+ visibility: "visible"
+ });
+ stream.coverMouse = {x: e.pageX, y: e.pageY};
+
+ //redraw
+ stream.getLevelSource();
+ stream.reRender();
+
+ //hidden
+ stream.indicatorLine.attr({"stroke": "none"});
+ stream.highlightLine.attr({"stroke": "none"});
+ stream.floatTag.css({"visibility" : "hidden"});
+
+ stream.paths.forEach(function (d, i, array) {
+ d.attr({transform: "s1,0.001,0," + stream.defaults.height});
+ d.label.hide();
+ d.animate({transform: "t0,0"}, 750, "linear", function () {
+ stream.cover.style.visibility = "hidden";
+ if (typeof stream.coverMouse !== 'undefined') {
+ stream.indicatorLine.attr({"stroke": "#000"});
+ stream.highlightLine.attr({"stroke": "white"});
+ stream.floatTag.css({"visibility" : "visible"});
+ $(stream.canvas.canvas).trigger("mousemove",
+ [stream.coverMouse.x, stream.coverMouse.y]);
+ stream.coverMouse = undefined;
+ }
+ //if (d.labelOpacity === 1)
+ if (d.labelLoc.showLabel) {
+ d.label.show();
+ }
+ });
+ });
+ }
+ };
+
+ var mousemove = function (e, pageX, pageY) {
+ var stream = e.data.stream;
+ var offset = $(this).parent().offset();
+ var position = $(this).parent().position();
+ //var offset = $(this).offset();
+ var x = (e.pageX || pageX) - offset.left,
+ y = (e.pageY || pageY) - offset.top;
+ var floatTag,
+ floatTagWidth,
+ floatTagHeight,
+ mouseToFloatTag = {x: 20, y: 20};
+ var path,
+ pathSource = stream.pathSource,
+ pathSourceP,
+ pathIndex,
+ circle;
+ var i, l;
+ var xIdx = Math.floor((x / (stream.defaults.width / (stream.source[0].length - 1) / 2) + 1) / 2);
+ var pathsourceP,
+ lineX;
+
+ //get path
+ path = undefined;
+ pathSource = stream.pathSource;
+ for (i = 0, l = pathSource.length; i < l; i++) {
+ if (y >= pathSource[i][xIdx].y0 - pathSource[i][xIdx].y && y <= pathSource[i][xIdx].y0) {
+ path = stream.paths[i];
+ pathIndex = i;
+ break;
+ }
+ }
+ if (typeof path === 'undefined') {
+ return;
+ }
+
+ //recover prepath;
+ if (typeof stream.prePath !== 'undefined') {
+ stream.prePath.attr({"opacity": 1, "stroke-width": 1});
+ // set legend
+ stream.legends[stream.prePath.index].css({"background": "white"});
+ }
+ //change new path;
+ stream.prePath = path;
+ path.attr({"opacity": 0.5, "stroke-width": 0});
+
+ // set legend
+ stream.legends[stream.prePath.index].css({"background": "#dddddd"});
+
+ //set indicator and highlight line
+ lineX = stream.defaults.width * xIdx / (stream.source[0].length - 1);
+ pathSourceP = pathSource[pathSource.length - 1][xIdx];
+ stream.indicatorLine.attr({path: "M" + lineX
+ + " " + (pathSourceP.y0 - pathSourceP.y)
+ + "V" + pathSource[0][xIdx].y0});
+
+ pathSourceP = pathSource[pathIndex][xIdx];
+ stream.highlightLine.attr({path: "M" + lineX
+ + " " + (pathSourceP.y0 - pathSourceP.y)
+ + "V" + pathSourceP.y0});
+ if (pathIndex === 0 && path.info[0].id === -1) {
+ stream.highlightLine.attr({"cursor": "pointer"});
+ } else {
+ stream.highlightLine.attr({"cursor": "auto"});
+ }
+
+ floatTag = stream.floatTag;
+ floatTag.html(path.info[xIdx].tip);
+
+ //axis pop bubble
+ stream.axisPopText.attr({"text": stream.date[xIdx + stream.timeRange[0]]})
+ .transform("t" + (lineX + stream.defaults.margin[3]) + ",0");
+ stream.axisPopBubble.transform("t" + (lineX + stream.defaults.margin[3]) + ",0");
+
+ //customevent;
+ if (stream.defaults.customEventHandle.mousemove) {
+ stream.defaults.customEventHandle.mousemove.call(stream,
+ {"timeIndex": xIdx, "pathIndex": pathIndex});
+ }
+ };
+ var $canvas = $(this.canvas.canvas);
+ $canvas.bind("mouseenter", {"stream": this}, mouseenter);
+ $canvas.bind("mouseleave", {"stream": this}, mouseleave);
+ $canvas.bind("click", {"stream": this}, click);
+ $canvas.bind("mousemove", {"stream": this}, mousemove);
+
+ this.paths = paths;
+ this.labels = labels;
+ this.indicatorLine = canvas.path("M0 " + conf.topInterval + "V" + (conf.height - conf.bottomInterval))
+ .attr({stroke: "none", "stroke-width": 1, "stroke-dasharray": "- "});
+ this.highlightLine = canvas.path("M0 " + conf.topInterval + "V" + (conf.height - conf.bottomInterval))
+ .attr({stroke: "none", "stroke-width": 2});
+ };
+
+ /**
+ * 创建坐标轴
+ */
+ Stream.prototype.createAxis = function () {
+ //all date strings' format are same, string length are same
+ var conf = this.defaults,
+ date = this.date.slice(this.timeRange[0], this.timeRange[1] + 1),
+ left = conf.margin[3],
+ //left = conf.margin[3] + conf.legendWidth,
+ right = conf.totalWidth - conf.margin[1] - conf.legendWidth,
+ tempWord,
+ tickNumber,
+ getPopPath = function (El) {
+ //down pop
+ var x = 0,
+ y = 0,
+ size = 4,
+ cw = 23,
+ bb = {height: 8};
+ if (El) {
+ bb = El.getBBox();
+ bb.height *= 0.6;
+ cw = bb.width / 2 - size;
+ }
+ return [
+ 'M', x, y,
+ 'l', size, size, cw, 0,
+ 'a', size, size, 0, 0, 1, size, size,
+ 'l', 0, bb.height,
+ 'a', size, size, 0, 0, 1, -size, size,
+ 'l', -(size * 2 + cw * 2), 0,
+ 'a', size, size, 0, 0, 1, -size, -size,
+ 'l', 0, -bb.height,
+ 'a', size, size, 0, 0, 1, size, -size,
+ 'l', cw, 0,
+ 'z'
+ ].join(',');
+ };
+
+ this.dateScale = d3.scale.linear()
+ .domain([0, date.length - 1])
+ .range([left, right]);
+
+ tempWord = this.axisPaper.text(0, 0, date[0]);
+ tickNumber = Math.floor((right - left) / tempWord.getBBox().width / 2) + 1;
+ tempWord.remove();
+ //tickNumber = 4;
+
+ Axis().scale(this.dateScale)
+ .ticks(tickNumber)
+ //.ticks(conf.axisTickNumber)
+ .tickSize(6, 3, 3)
+ .tickAttr({"stroke": "none"})
+ .minorTickAttr({"stroke": "none"})
+ .domainAttr({"stroke": "none"})
+ //.tickTextAttr({"font-size": conf.fontSize})
+ .tickFormat(function (d) {
+ return date[d] || "";
+ })(this.axisPaper);
+
+ this.axisPopText = this.axisPaper.text(0, 11, date[0])
+ .attr({ "text-anchor": "middle",
+ "fill": "#fff",
+ //"font-size": conf.fontSize,
+ "transform": "t" + left + ",0"})
+ .hide();
+ this.axisPopBubble = this.axisPaper.path(getPopPath(this.axisPopText))
+ .attr({ "fill": "#000",
+ //"opacity": 0,
+ "transform": "t" + left + ",0"})
+ .toBack()
+ .hide();
+ };
+
+ /**
+ * 获取纵轴最大值
+ */
+ Stream.prototype.getMaxY = function () {
+ return d3.max(this.source, function (d) {
+ return d3.max(d, function (d) {
+ return d.y0 + d.y;
+ });
+ });
+ };
+
+ /**
+ * 映射路径源
+ */
+ Stream.prototype.mapPathSource = function () {
+ var conf = this.defaults,
+ maxX = this.source[0].length - 1,//this.digitData[0].length - 1,
+ maxY = this.getMaxY(),
+ width = conf.width,
+ height = conf.height - conf.topInterval - conf.bottomInterval;
+ var i, j, l, l2, s, ps;
+ this.pathSource = [];
+ for (i = 0, l = this.source.length; i < l; i++) {
+ this.pathSource[i] = [];
+ for (j = 0, l2 = this.source[0].length; j < l2; j++) {
+ s = this.source[i][j];
+ ps = this.pathSource[i][j] = {};
+ ps.x = s.x * width / maxX;
+ ps.y0 = height - s.y0 * height / maxY;
+ ps.y = s.y * height / maxY;
+ }
+ }
+ };
+
+ /**
+ * 生成区域
+ */
+ Stream.prototype.generateArea = function () {
+ this.mapPathSource();
+ return d3.svg.area().x(function (d) {
+ return d.x;
+ }).y0(function (d) {
+ return d.y0;
+ }).y1(function (d) {
+ return d.y0 - d.y;
+ });
+ };
+
+ /*!
+ * 生成区域
+ */
+ Stream.prototype.generateArea_old = function () {
+ var conf = this.defaults,
+ maxX = this.digitData[0].length - 1,
+ maxY = this.getMaxY(),
+ width = conf.width,
+ height = conf.height - conf.topInterval - conf.bottomInterval,
+ area = d3.svg.area()
+ .x(function (d) {
+ return d.x * width / maxX;
+ })
+ .y0(function (d) {
+ return height - d.y0 * height / maxY;
+ })
+ .y1(function (d) {
+ return height - (d.y + d.y0) * height / maxY;
+ });
+ return area;
+ };
+
+ /**
+ * 清除画布
+ */
+ Stream.prototype.clearCanvas = function () {
+ this.canvas.clear();
+ this.legend.empty();
+ this.axisPaper.clear();
+ };
+
+ /**
+ * 重绘图表
+ */
+ Stream.prototype.reRender = function (options) {
+ this.setOptions(options);
+ this.clearCanvas();
+ this.layout();
+ this.generatePaths();
+ this.canAnimate = true;
+ };
+
+ /**
+ * 绘制图表
+ */
+ Stream.prototype.render = function (options) {
+ this.firstRender = true;
+ this.setOptions(options);
+ this.clearCanvas();
+ this.layout();
+ this.generatePaths();
+ this.firstRender = false;
+ this.canAnimate = true;
+ };
+
+ /**
+ * 重设图表
+ */
+ Stream.prototype.resize = function (options) {
+ var conf = this.defaults;
+
+ if (!options.width && !options.height) {
+ throw new Error("no width and height input");
+ } else if (options.width && !options.height) {
+ if (conf.autoHeight) {
+ this.setOptions({"width": options.width});
+ } else {
+ this.setOptions({"width": options.width, "height": conf.height});
+ }
+ } else if (!options.width && options.height) {
+ this.setOptions({"width": conf.totalWidth, "height": options.height});
+ } else {
+ this.setOptions({"width": options.width, "height": options.height});
+ }
+
+ this.node.innerHTML = "";
+ this.createCanvas();
+ this.reRender();
+ };
+
+ /**
+ * 侦听自定义事件
+ * @param {String} eventName 事件名
+ * @param {Function} callback 事件回调函数
+ */
+ Stream.prototype.on = function (eventName, callback) {
+ if (typeof this.defaults.customEventHandle[eventName] !== 'undefined') {
+ this.defaults.customEventHandle[eventName] = callback;
+ }
+ };
+
+ /**
+ * 设置动画
+ * @param {Object} options 选项对象
+ * @param {Number} timeDuration 时间
+ */
+ Stream.prototype.animate = function (options, timeDuration) {
+ //must after render if new Source has been set;
+ if (!this.canAnimate) {
+ throw new Error("Function animate must be called after render if new Source has been set.");
+ }
+ var time = 0,
+ area,
+ color,
+ i,
+ l;
+ if (arguments.length > 1) {
+ time = timeDuration;
+ }
+
+ //this.setOptions(options);
+ if (options.offset || options.order) {
+ this.source = this.remapSource(this.digitData);
+ this.layout();
+ }
+ area = this.generateArea();
+ color = this.getColor();
+ for (i = 0, l = this.source.length; i < l; i++) {
+ var _area = area(this.source[i]);
+ var anim = Raphael.animation({path: _area, fill: color(i)}, time);
+ this.paths[i].animate(anim);
+ }
+ };
+
+ /*!
+ * 导出Stream
+ */
+ return Stream;
+});
diff --git a/lib/charts/tree.js b/lib/charts/tree.js
index 652a106..e2dbaf9 100644
--- a/lib/charts/tree.js
+++ b/lib/charts/tree.js
@@ -1,580 +1,580 @@
-/*global EventProxy, d3, Raphael, $ */
-/*!
- * Tree的兼容性定义
- */
-;(function (name, definition) {
- if (typeof define === 'function') {
- define(definition);
- } else {
- this[name] = definition(function (id) { return this[id];});
- }
-})('Tree', function (require) {
- var DataV = require('DataV');
- var theme = DataV.Themes;
-
- /**
- * Tree的构造函数
- * Examples:
- * ```
- * var tree = new Tree("container");
- * tree.setSource(source);
- * tree.render();
- * ```
- * Options:
- * - `width`: 画布的宽度
- * - `height`: 画布的高度
- */
- var Tree = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Tree";
- this.node = this.checkContainer(node);
-
- this.addlink = {};
-
- // Properties
- this.treeDepth = 0;
- this.font = {};
-
- // Canvas
- this.defaults.width = 750;
- this.defaults.height = 760;
- this.defaults.deep = 180;
- this.defaults.radius = 15;
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * 饼图纬度描述
- */
- Tree.dimension = {};
- /**
- * ID标签
- */
- Tree.dimension.id = {
- type: "string",
- required: true
- };
- /**
- * 父ID标签
- */
- Tree.dimension.pid = {
- type: "string",
- required: true
- };
-
- Tree.prototype.hierarchyTableToJson = function (table) {
- if (table[0][0] === "ID") {
- table = table.slice(1);
- }
-
- var rootID;
- var hierarchy = {};
- var addlink = {}; //for multi-fathernode
- // var ids = _.pluck(table, 0);
- // var pids = _.pluck(table, 3);
- // var roots = _.difference(pids, ids);
- // if (roots.length === 0) {
- // throw new Error("root node is empty");
- // } else if (roots.length > 1) {
- // throw new Error("root nodes are too many");
- // }
-
- table.forEach(function (d, i) {
- if (d[0] === "") {
- throw new Error("ID can not be empty(line:" + (i + 1) + ").");
- }
- if (!d[3]) {
- if (rootID) {
- throw new Error("2 or more lines have an empty parentID(line:" + (i + 1) + ").");
- } else {
- rootID = d[0];
- }
- }
- if (hierarchy[d[0]]) {
- throw new Error("2 or more lines have same ID: " + d[0] + "(line:" + (i + 1) + ").");
- }
-
- var value = "";
- var j, length;
- if (d.length > 4) {
- for (j = 4, length = d.length; j < length; j++) {
- if (j < length - 1) {
- value = value + d[j] + ",";
- } else {
- value = value + d[j];
- }
- }
- }
- hierarchy[d[0]] = {name: d[1], size: d[2], child: [], id: d[0], value: value};
- });
- if (!rootID) {
- throw new Error("No root node defined.");
- }
- table.forEach(function (d, i) {
- if (d[3]) {
- var record;
- var ids = d[3].split(',');
- if (ids.length === 1) {
- record = hierarchy[d[3]];
- record.child.push(d[0]);
- } else {
- record = hierarchy[ids[0]];
- record.child.push(d[0]);
- addlink[d[0]] = {child: [], path: [], pnode: []};
-
- var j, length;
- for (j = 1, length = ids.length; j < length; j++) {
- addlink[d[0]].child.push(ids[j]);
- }
- }
- if (!record) {
- throw new Error("Can not find parent with ID " + d[3] + "(line:" + (i + 1) + ").");
- }
- }
- });
-
- this.addlink = addlink;
-
- var recurse = function (rootID) {
- var record = hierarchy[rootID];
- if (record.child.length === 0) {
- if (isNaN(parseFloat(record.size))) {
- throw new Error("Leaf node's size is not a number(ID:" + (rootID + 1) + ").");
- } else {
- return {
- name: record.name,
- size: record.size,
- num: record.id,
- children: null,
- draw: false,
- value: record.value
- };
- }
- } else {
- var childNode = [];
- record.child.forEach(function (d) {
- childNode.push(recurse(d));
- });
- return {name: record.name, children: childNode, num: record.id, draw: false, value: record.value};
- }
- };
-
- return recurse(rootID);
- };
-
- Tree.prototype.setSource = function (source) {
- var conf = this.defaults;
-
- this.rawData = this.hierarchyTableToJson(source);
- this.source = this.remapSource(source);
-
- this.source.x0 = conf.width / 2;
- this.source.y0 = conf.radius * 10;
-
- this.source.children.forEach(function collapse(d) {
- if (d.children) {
- // d._children = d.children;
- // d._children.forEach(collapse);
- // d.children = null;
- d._children = null;
- d.children.forEach(collapse);
- }
- });
- };
-
- Tree.prototype.remapSource = function (data) {
- return this.hierarchyTableToJson(data);
- // return data;
- };
-
- Tree.prototype.layout = function () {
- var conf = this.defaults;
- var tree = d3.layout.tree()
- .size([conf.width, conf.height]);
-
- this.nodesData = tree.nodes(this.source);
-
- var treedepth = 0;
- var id = 0;
-
- this.nodesData.forEach(function (d) {
- if (d.depth > treedepth) {
- treedepth = d.depth;
- }
- });
-
- this.treeDepth = treedepth;
- conf.deep = conf.height / (treedepth + 1);
-
- this.nodesData.forEach(function (d) {
- d.y = conf.radius * 3 + d.depth * conf.deep;
- d.id = id;
- id++;
- });
- };
-
- Tree.prototype.getColor = function () {
- var colorMatrix = DataV.getColor();
- var color;
- if (colorMatrix.length > 1 && colorMatrix[0].length > 1) {
- color = [colorMatrix[0][0], colorMatrix[1][0]];
- } else {
- color = colorMatrix[0];
- }
-
- return DataV.gradientColor(color, "special");
- };
-
- Tree.prototype.createCanvas = function () {
- var conf = this.defaults;
- this.canvas = new Raphael(this.node, conf.width, conf.height);
- this.node.style.position = "relative";
- this.floatTag = DataV.FloatTag()(this.node);
-
- this.floatTag.css({"visibility": "hidden"});
-
- this.DOMNode = $(this.canvas.canvas);
- var that = this;
- this.DOMNode.click(function (event) {
- that.trigger("click", event);
- });
- this.DOMNode.dblclick(function (event) {
- that.trigger("dblclick", event);
- });
-
- var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
- this.DOMNode.bind(mousewheel, function (event) {
- that.trigger("mousewheel", event);
- });
-
- this.DOMNode.bind("contextmenu", function (event) {
- that.trigger("contextmenu", event);
- });
-
- this.DOMNode.delegate("circle", "click", function (event) {
- that.trigger("circle_click", event);
- });
-
- this.DOMNode.delegate("circle", "mouseover", function (event) {
- that.trigger("circle_mouseover", event);
- });
-
- this.DOMNode.delegate("circle", "mouseout", function (event) {
- that.trigger("circle_mouseout", event);
- });
- };
-
- Tree.prototype.zoom = function (d) {
- var multiple = d || 2;
- var conf = this.defaults;
- conf.width = conf.width * multiple;
-
- if (conf.height <= this.treeDepth * conf.deep) {
- conf.height = conf.height * multiple;
- }
-
- //this.createCanvas();
- this.canvas.setSize(conf.width, conf.height);
- this.canvas.setViewBox(0, 0, conf.width, 800);
- this.defaults = conf;
-
- this.render();
- };
-
-
- Tree.prototype.getLinkPath = function (fx, fy, tx, ty) {
- var conf = this.defaults;
-
- var c1x = fx;
- var c1y = fy + (ty - fy) / 2;
- var c2x = tx;
- var c2y = ty - (ty - fy) / 2;
-
- var link_path = [["M", fx, fy + conf.radius],
- ["C", c1x, c1y, c2x, c2y, tx, ty - conf.radius]];
-
- return link_path;
- };
-
- Tree.prototype.generatePaths = function () {
- var canvas = this.canvas;
- var source = this.source;
- var conf = this.defaults;
- var radius = conf.radius;
- //canvas.clear();
- var color = this.getColor();
- // var font = this.getFont();
- var font_family = '微软雅黑';
- var font_size = 8;
- var treedepth = this.treeDepth;
- var nodesData = this.nodesData;
-
- var n = 0;
-
- var addlink = this.addlink;
- var node;
- var num = 0;
-
- var nodes = canvas.set();
- var path = [];
- var textpath = [];
-
- var tree = this;
- var nodeupdate = function () {
- tree.update(this.data("num"));
- };
-
- $(this.node).append(this.floatTag);
-
- var i, nodesLength;
- for (i = 0, nodesLength = nodesData.length; i < nodesLength; i++) {
- var d = nodesData[i];
- var parent = d.parent;
-
- if (addlink[d.num]) {
- var j, k, childLength;
- for (j = 0, childLength = addlink[d.num].child.length; j < childLength; j++) {
- for (k = 0; k < nodesLength; k++) {
- if (nodesData[k].num === addlink[d.num].child[j]) {
- addlink[d.num].pnode[j] = k;
- addlink[d.num].path[j] = canvas.path()
- .attr({ stroke: "#939598", "stroke-width": 0.5});
- }
- }
- }
- }
-
- var startX;
- var startY;
-
- if (parent && d.draw) {
- startX = parent.x;
- startY = parent.y;
- } else {
- startX = d.x;
- startY = d.y;
- }
- if (parent) {
- path.push(canvas.path().attr({stroke: "#939598", "stroke-width": 0.5}));
- }
-
- nodes.push(
- canvas.circle(startX, startY, radius)
- .attr({fill: color(d.depth / treedepth),
- stroke: "#ffffff",
- "stroke-width": 1,
- "fill-opacity": 0.4,
- "data": 12})
- .data("num", i)
- .animate({cx: d.x, cy: d.y}, 500, "backOut")
- );
-
- if (d.children || d._children) {
- nodes[i].click(nodeupdate);
- }
-
- if (d._children) {
- nodes[i].attr({
- stroke: color(d.depth / treedepth),
- "stroke-width": radius,
- "stroke-opacity": 0.4,
- "fill-opacity": 1,
- "r": radius / 2
- });
- }
-
- if (d.children) {
- textpath.push(canvas.text(d.x, d.y - radius - 7, d.name).attr({'font-size': 12}));
- } else {
- textpath.push(canvas.text(d.x, d.y + radius + 7, d.name).attr({'font-size': 12}));
- }
- }
-
- // var back = function(pid, x, y){
- // s.forEach(function (d, i){
- // if (d.data('pid') == pid){
- // d.animate({cx: x, cy: y}, 200, "backOut");
- // if (nodes[i].children)
- // back(d.data('num'), d.attr('cx'), d.attr('cy'));
- // }
- // });
- // };
-
- // s.forEach(function(d, i) {
- // d.click(function(){
- // if (nodes[i].children)
- // back(d.data('num'), d.attr('cx'), d.attr('cy'));
- // tree.update(d.data("num"));
- // });
- // });
- var floatTag = this.floatTag;
- nodes.forEach(function (d, i) {
- $(d.node).attr('value', nodesData[i].value);
- var textY = textpath[i].attr('y');
- var thisradius = d.attr('r');
- var thisstrokewidth = d.attr('stroke-width');
- d.mouseover(function () {
- if (!nodesData[i]._children) {
- this.animate({r: thisradius + 2, "fill-opacity": 0.75}, 100);
- } else {
- this.animate({r: thisradius + 2, "stroke-opacity": 0.75}, 100);
- }
-
- textpath[i].attr({'font-size': 20});
-
- if (i > 0) {
- if (!nodesData[i].children) {
- textpath[i].animate({'y': textY + 12}, 100, "backOut");
- } else {
- textpath[i].animate({'y': textY - 12}, 100, "backOut");
- }
- }
-
- var getFline = function (node, num) {
- var parent = node.parent;
- if (parent) {
- path[node.id - 1].attr({"stroke-width": 4, "stroke-opacity": num});
- if ( num > 0.5) {
- num = num - 0.1;
- }
- getFline(parent, num);
- }
- };
-
- getFline(nodesData[i], 0.9);
-
- var thisparent = nodesData[i].parent;
- var j, textpathLength;
- for (j = 0, textpathLength = textpath.length; j < textpathLength; j++) {
- var parent = nodesData[j].parent;
- if (parent === thisparent && j !== i) {
- textpath[j].animate({'fill-opacity': 0.4});
- }
- }
-
- console.log(nodesData[i]);
- floatTag.html('' + nodesData[i].name + '
');
- floatTag.css({"visibility" : "visible"});
- })
- .mouseout(function () {
- floatTag.css({"visibility" : "hidden"});
- if (!nodesData[i]._children) {
- this.animate({r: thisradius, "fill-opacity": 0.4}, 100);
- } else {
- this.animate({r: thisradius, "stroke-width": thisstrokewidth, "stroke-opacity": 0.4}, 100);
- }
- textpath[i].attr({'font-size': 12});
- textpath[i].animate({'y': textY}, 100, "backOut");
-
- var getFline = function (node) {
- var parent = node.parent;
- if (parent) {
- path[node.id - 1].attr({"stroke-width": 0.5, "stroke-opacity": 1});
- getFline(parent);
- }
- };
- getFline(nodesData[i]);
-
- var thisparent = nodesData[i].parent;
- var j, textpathLength;
- for (j = 0, textpathLength = textpath.length; j < textpathLength; j++) {
- var parent = nodesData[j].parent;
- if (parent === thisparent && j !== i) {
- textpath[j].animate({'fill-opacity': 1});
- }
- }
- });
- });
-
- nodes.onAnimation(function () {
- var pathNum = 0;
- var i, nodeslength;
-
- for (i = 1, nodeslength = nodes.length; i < nodeslength; i++) {
- var d = nodes[i];
- var node = nodesData[i];
- var parent = node.parent;
-
- path[pathNum]
- .attr({path: tree.getLinkPath(parent.x, parent.y, d.attr("cx"), d.attr("cy"))});
-
- pathNum++;
-
- if (addlink[node.num]) {
- var j, k, linkchildLength, nodesLength;
- for (j = 0, linkchildLength = addlink[node.num].child.length; j < linkchildLength; j++) {
- for (k = 0, nodesLength = nodesData.length; k < nodesLength; k++) {
- var anparent = nodesData[k];
- if (anparent.num === addlink[node.num].child[j]) {
- var link_path = tree.getLinkPath(anparent.x, anparent.y, d.attr("cx"), d.attr("cy"));
- addlink[node.num].path[j].attr({path: link_path});
- }
- }
- }
- }
- }
- });
-
- this.nodes = nodes;
- this.path = path;
- this.textpath = textpath;
- };
-
- Tree.prototype.update = function (i) {
- var source = this.source;
- var conf = this.defaults;
-
- source.children.forEach(function clearDraw(d) {
- d.draw = false;
- if (d.children) {
- d.children.forEach(clearDraw);
- }
- });
-
- source.children.forEach(function find(d) {
- if (d.id === i) {
- if (d.children) {
- d._children = d.children;
- d.children = null;
- } else {
- d.children = d._children;
- if (d.children) {
- d.children.forEach(function drawn(child) {
- child.draw = true;
- if (child.children) {
- child.children.forEach(drawn);
- }
- });
- }
- d._children = null;
- }
- } else {
- if (d.children) {
- d.children.forEach(find);
- }
- }
- });
- this.source = source;
- this.source.x0 = conf.width / 2;
- this.source.y0 = conf.radius * 2;
- this.render();
- };
-
- /**
- * 渲染Tree
- */
- Tree.prototype.render = function (options) {
- this.canvas.clear();
- this.setOptions(options);
- this.layout();
- // var st2 = new Date().getTime();
- this.generatePaths();
- // var et = new Date().getTime();
- //this.canvas.renderfix();
- };
-
- return Tree;
-});
+/*global EventProxy, d3, Raphael, $ */
+/*!
+ * Tree的兼容性定义
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') {
+ define(definition);
+ } else {
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Tree', function (require) {
+ var DataV = require('DataV');
+ var theme = DataV.Themes;
+
+ /**
+ * Tree的构造函数
+ * Examples:
+ * ```
+ * var tree = new Tree("container");
+ * tree.setSource(source);
+ * tree.render();
+ * ```
+ * Options:
+ * - `width`: 画布的宽度
+ * - `height`: 画布的高度
+ */
+ var Tree = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Tree";
+ this.node = this.checkContainer(node);
+
+ this.addlink = {};
+
+ // Properties
+ this.treeDepth = 0;
+ this.font = {};
+
+ // Canvas
+ this.defaults.width = 750;
+ this.defaults.height = 760;
+ this.defaults.deep = 180;
+ this.defaults.radius = 15;
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * 饼图纬度描述
+ */
+ Tree.dimension = {};
+ /**
+ * ID标签
+ */
+ Tree.dimension.id = {
+ type: "string",
+ required: true
+ };
+ /**
+ * 父ID标签
+ */
+ Tree.dimension.pid = {
+ type: "string",
+ required: true
+ };
+
+ Tree.prototype.hierarchyTableToJson = function (table) {
+ if (table[0][0] === "ID") {
+ table = table.slice(1);
+ }
+
+ var rootID;
+ var hierarchy = {};
+ var addlink = {}; //for multi-fathernode
+ // var ids = _.pluck(table, 0);
+ // var pids = _.pluck(table, 3);
+ // var roots = _.difference(pids, ids);
+ // if (roots.length === 0) {
+ // throw new Error("root node is empty");
+ // } else if (roots.length > 1) {
+ // throw new Error("root nodes are too many");
+ // }
+
+ table.forEach(function (d, i) {
+ if (d[0] === "") {
+ throw new Error("ID can not be empty(line:" + (i + 1) + ").");
+ }
+ if (!d[3]) {
+ if (rootID) {
+ throw new Error("2 or more lines have an empty parentID(line:" + (i + 1) + ").");
+ } else {
+ rootID = d[0];
+ }
+ }
+ if (hierarchy[d[0]]) {
+ throw new Error("2 or more lines have same ID: " + d[0] + "(line:" + (i + 1) + ").");
+ }
+
+ var value = "";
+ var j, length;
+ if (d.length > 4) {
+ for (j = 4, length = d.length; j < length; j++) {
+ if (j < length - 1) {
+ value = value + d[j] + ",";
+ } else {
+ value = value + d[j];
+ }
+ }
+ }
+ hierarchy[d[0]] = {name: d[1], size: d[2], child: [], id: d[0], value: value};
+ });
+ if (!rootID) {
+ throw new Error("No root node defined.");
+ }
+ table.forEach(function (d, i) {
+ if (d[3]) {
+ var record;
+ var ids = d[3].split(',');
+ if (ids.length === 1) {
+ record = hierarchy[d[3]];
+ record.child.push(d[0]);
+ } else {
+ record = hierarchy[ids[0]];
+ record.child.push(d[0]);
+ addlink[d[0]] = {child: [], path: [], pnode: []};
+
+ var j, length;
+ for (j = 1, length = ids.length; j < length; j++) {
+ addlink[d[0]].child.push(ids[j]);
+ }
+ }
+ if (!record) {
+ throw new Error("Can not find parent with ID " + d[3] + "(line:" + (i + 1) + ").");
+ }
+ }
+ });
+
+ this.addlink = addlink;
+
+ var recurse = function (rootID) {
+ var record = hierarchy[rootID];
+ if (record.child.length === 0) {
+ if (isNaN(parseFloat(record.size))) {
+ throw new Error("Leaf node's size is not a number(ID:" + (rootID + 1) + ").");
+ } else {
+ return {
+ name: record.name,
+ size: record.size,
+ num: record.id,
+ children: null,
+ draw: false,
+ value: record.value
+ };
+ }
+ } else {
+ var childNode = [];
+ record.child.forEach(function (d) {
+ childNode.push(recurse(d));
+ });
+ return {name: record.name, children: childNode, num: record.id, draw: false, value: record.value};
+ }
+ };
+
+ return recurse(rootID);
+ };
+
+ Tree.prototype.setSource = function (source) {
+ var conf = this.defaults;
+
+ this.rawData = this.hierarchyTableToJson(source);
+ this.source = this.remapSource(source);
+
+ this.source.x0 = conf.width / 2;
+ this.source.y0 = conf.radius * 10;
+
+ this.source.children.forEach(function collapse(d) {
+ if (d.children) {
+ // d._children = d.children;
+ // d._children.forEach(collapse);
+ // d.children = null;
+ d._children = null;
+ d.children.forEach(collapse);
+ }
+ });
+ };
+
+ Tree.prototype.remapSource = function (data) {
+ return this.hierarchyTableToJson(data);
+ // return data;
+ };
+
+ Tree.prototype.layout = function () {
+ var conf = this.defaults;
+ var tree = d3.layout.tree()
+ .size([conf.width, conf.height]);
+
+ this.nodesData = tree.nodes(this.source);
+
+ var treedepth = 0;
+ var id = 0;
+
+ this.nodesData.forEach(function (d) {
+ if (d.depth > treedepth) {
+ treedepth = d.depth;
+ }
+ });
+
+ this.treeDepth = treedepth;
+ conf.deep = conf.height / (treedepth + 1);
+
+ this.nodesData.forEach(function (d) {
+ d.y = conf.radius * 3 + d.depth * conf.deep;
+ d.id = id;
+ id++;
+ });
+ };
+
+ Tree.prototype.getColor = function () {
+ var colorMatrix = DataV.getColor();
+ var color;
+ if (colorMatrix.length > 1 && colorMatrix[0].length > 1) {
+ color = [colorMatrix[0][0], colorMatrix[1][0]];
+ } else {
+ color = colorMatrix[0];
+ }
+
+ return DataV.gradientColor(color, "special");
+ };
+
+ Tree.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+ this.node.style.position = "relative";
+ this.floatTag = DataV.FloatTag()(this.node);
+
+ this.floatTag.css({"visibility": "hidden"});
+
+ this.DOMNode = $(this.canvas.canvas);
+ var that = this;
+ this.DOMNode.click(function (event) {
+ that.trigger("click", event);
+ });
+ this.DOMNode.dblclick(function (event) {
+ that.trigger("dblclick", event);
+ });
+
+ var mousewheel = document.all ? "mousewheel" : "DOMMouseScroll";
+ this.DOMNode.bind(mousewheel, function (event) {
+ that.trigger("mousewheel", event);
+ });
+
+ this.DOMNode.bind("contextmenu", function (event) {
+ that.trigger("contextmenu", event);
+ });
+
+ this.DOMNode.delegate("circle", "click", function (event) {
+ that.trigger("circle_click", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseover", function (event) {
+ that.trigger("circle_mouseover", event);
+ });
+
+ this.DOMNode.delegate("circle", "mouseout", function (event) {
+ that.trigger("circle_mouseout", event);
+ });
+ };
+
+ Tree.prototype.zoom = function (d) {
+ var multiple = d || 2;
+ var conf = this.defaults;
+ conf.width = conf.width * multiple;
+
+ if (conf.height <= this.treeDepth * conf.deep) {
+ conf.height = conf.height * multiple;
+ }
+
+ //this.createCanvas();
+ this.canvas.setSize(conf.width, conf.height);
+ this.canvas.setViewBox(0, 0, conf.width, 800);
+ this.defaults = conf;
+
+ this.render();
+ };
+
+
+ Tree.prototype.getLinkPath = function (fx, fy, tx, ty) {
+ var conf = this.defaults;
+
+ var c1x = fx;
+ var c1y = fy + (ty - fy) / 2;
+ var c2x = tx;
+ var c2y = ty - (ty - fy) / 2;
+
+ var link_path = [["M", fx, fy + conf.radius],
+ ["C", c1x, c1y, c2x, c2y, tx, ty - conf.radius]];
+
+ return link_path;
+ };
+
+ Tree.prototype.generatePaths = function () {
+ var canvas = this.canvas;
+ var source = this.source;
+ var conf = this.defaults;
+ var radius = conf.radius;
+ //canvas.clear();
+ var color = this.getColor();
+ // var font = this.getFont();
+ var font_family = '微软雅黑';
+ var font_size = 8;
+ var treedepth = this.treeDepth;
+ var nodesData = this.nodesData;
+
+ var n = 0;
+
+ var addlink = this.addlink;
+ var node;
+ var num = 0;
+
+ var nodes = canvas.set();
+ var path = [];
+ var textpath = [];
+
+ var tree = this;
+ var nodeupdate = function () {
+ tree.update(this.data("num"));
+ };
+
+ $(this.node).append(this.floatTag);
+
+ var i, nodesLength;
+ for (i = 0, nodesLength = nodesData.length; i < nodesLength; i++) {
+ var d = nodesData[i];
+ var parent = d.parent;
+
+ if (addlink[d.num]) {
+ var j, k, childLength;
+ for (j = 0, childLength = addlink[d.num].child.length; j < childLength; j++) {
+ for (k = 0; k < nodesLength; k++) {
+ if (nodesData[k].num === addlink[d.num].child[j]) {
+ addlink[d.num].pnode[j] = k;
+ addlink[d.num].path[j] = canvas.path()
+ .attr({ stroke: "#939598", "stroke-width": 0.5});
+ }
+ }
+ }
+ }
+
+ var startX;
+ var startY;
+
+ if (parent && d.draw) {
+ startX = parent.x;
+ startY = parent.y;
+ } else {
+ startX = d.x;
+ startY = d.y;
+ }
+ if (parent) {
+ path.push(canvas.path().attr({stroke: "#939598", "stroke-width": 0.5}));
+ }
+
+ nodes.push(
+ canvas.circle(startX, startY, radius)
+ .attr({fill: color(d.depth / treedepth),
+ stroke: "#ffffff",
+ "stroke-width": 1,
+ "fill-opacity": 0.4,
+ "data": 12})
+ .data("num", i)
+ .animate({cx: d.x, cy: d.y}, 500, "backOut")
+ );
+
+ if (d.children || d._children) {
+ nodes[i].click(nodeupdate);
+ }
+
+ if (d._children) {
+ nodes[i].attr({
+ stroke: color(d.depth / treedepth),
+ "stroke-width": radius,
+ "stroke-opacity": 0.4,
+ "fill-opacity": 1,
+ "r": radius / 2
+ });
+ }
+
+ if (d.children) {
+ textpath.push(canvas.text(d.x, d.y - radius - 7, d.name).attr({'font-size': 12}));
+ } else {
+ textpath.push(canvas.text(d.x, d.y + radius + 7, d.name).attr({'font-size': 12}));
+ }
+ }
+
+ // var back = function(pid, x, y){
+ // s.forEach(function (d, i){
+ // if (d.data('pid') == pid){
+ // d.animate({cx: x, cy: y}, 200, "backOut");
+ // if (nodes[i].children)
+ // back(d.data('num'), d.attr('cx'), d.attr('cy'));
+ // }
+ // });
+ // };
+
+ // s.forEach(function(d, i) {
+ // d.click(function(){
+ // if (nodes[i].children)
+ // back(d.data('num'), d.attr('cx'), d.attr('cy'));
+ // tree.update(d.data("num"));
+ // });
+ // });
+ var floatTag = this.floatTag;
+ nodes.forEach(function (d, i) {
+ $(d.node).attr('value', nodesData[i].value);
+ var textY = textpath[i].attr('y');
+ var thisradius = d.attr('r');
+ var thisstrokewidth = d.attr('stroke-width');
+ d.mouseover(function () {
+ if (!nodesData[i]._children) {
+ this.animate({r: thisradius + 2, "fill-opacity": 0.75}, 100);
+ } else {
+ this.animate({r: thisradius + 2, "stroke-opacity": 0.75}, 100);
+ }
+
+ textpath[i].attr({'font-size': 20});
+
+ if (i > 0) {
+ if (!nodesData[i].children) {
+ textpath[i].animate({'y': textY + 12}, 100, "backOut");
+ } else {
+ textpath[i].animate({'y': textY - 12}, 100, "backOut");
+ }
+ }
+
+ var getFline = function (node, num) {
+ var parent = node.parent;
+ if (parent) {
+ path[node.id - 1].attr({"stroke-width": 4, "stroke-opacity": num});
+ if ( num > 0.5) {
+ num = num - 0.1;
+ }
+ getFline(parent, num);
+ }
+ };
+
+ getFline(nodesData[i], 0.9);
+
+ var thisparent = nodesData[i].parent;
+ var j, textpathLength;
+ for (j = 0, textpathLength = textpath.length; j < textpathLength; j++) {
+ var parent = nodesData[j].parent;
+ if (parent === thisparent && j !== i) {
+ textpath[j].animate({'fill-opacity': 0.4});
+ }
+ }
+
+ console.log(nodesData[i]);
+ floatTag.html('' + nodesData[i].name + '
');
+ floatTag.css({"visibility" : "visible"});
+ })
+ .mouseout(function () {
+ floatTag.css({"visibility" : "hidden"});
+ if (!nodesData[i]._children) {
+ this.animate({r: thisradius, "fill-opacity": 0.4}, 100);
+ } else {
+ this.animate({r: thisradius, "stroke-width": thisstrokewidth, "stroke-opacity": 0.4}, 100);
+ }
+ textpath[i].attr({'font-size': 12});
+ textpath[i].animate({'y': textY}, 100, "backOut");
+
+ var getFline = function (node) {
+ var parent = node.parent;
+ if (parent) {
+ path[node.id - 1].attr({"stroke-width": 0.5, "stroke-opacity": 1});
+ getFline(parent);
+ }
+ };
+ getFline(nodesData[i]);
+
+ var thisparent = nodesData[i].parent;
+ var j, textpathLength;
+ for (j = 0, textpathLength = textpath.length; j < textpathLength; j++) {
+ var parent = nodesData[j].parent;
+ if (parent === thisparent && j !== i) {
+ textpath[j].animate({'fill-opacity': 1});
+ }
+ }
+ });
+ });
+
+ nodes.onAnimation(function () {
+ var pathNum = 0;
+ var i, nodeslength;
+
+ for (i = 1, nodeslength = nodes.length; i < nodeslength; i++) {
+ var d = nodes[i];
+ var node = nodesData[i];
+ var parent = node.parent;
+
+ path[pathNum]
+ .attr({path: tree.getLinkPath(parent.x, parent.y, d.attr("cx"), d.attr("cy"))});
+
+ pathNum++;
+
+ if (addlink[node.num]) {
+ var j, k, linkchildLength, nodesLength;
+ for (j = 0, linkchildLength = addlink[node.num].child.length; j < linkchildLength; j++) {
+ for (k = 0, nodesLength = nodesData.length; k < nodesLength; k++) {
+ var anparent = nodesData[k];
+ if (anparent.num === addlink[node.num].child[j]) {
+ var link_path = tree.getLinkPath(anparent.x, anparent.y, d.attr("cx"), d.attr("cy"));
+ addlink[node.num].path[j].attr({path: link_path});
+ }
+ }
+ }
+ }
+ }
+ });
+
+ this.nodes = nodes;
+ this.path = path;
+ this.textpath = textpath;
+ };
+
+ Tree.prototype.update = function (i) {
+ var source = this.source;
+ var conf = this.defaults;
+
+ source.children.forEach(function clearDraw(d) {
+ d.draw = false;
+ if (d.children) {
+ d.children.forEach(clearDraw);
+ }
+ });
+
+ source.children.forEach(function find(d) {
+ if (d.id === i) {
+ if (d.children) {
+ d._children = d.children;
+ d.children = null;
+ } else {
+ d.children = d._children;
+ if (d.children) {
+ d.children.forEach(function drawn(child) {
+ child.draw = true;
+ if (child.children) {
+ child.children.forEach(drawn);
+ }
+ });
+ }
+ d._children = null;
+ }
+ } else {
+ if (d.children) {
+ d.children.forEach(find);
+ }
+ }
+ });
+ this.source = source;
+ this.source.x0 = conf.width / 2;
+ this.source.y0 = conf.radius * 2;
+ this.render();
+ };
+
+ /**
+ * 渲染Tree
+ */
+ Tree.prototype.render = function (options) {
+ this.canvas.clear();
+ this.setOptions(options);
+ this.layout();
+ // var st2 = new Date().getTime();
+ this.generatePaths();
+ // var et = new Date().getTime();
+ //this.canvas.renderfix();
+ };
+
+ return Tree;
+});
diff --git a/lib/charts/treemap.js b/lib/charts/treemap.js
index 07dd001..dbccc6a 100644
--- a/lib/charts/treemap.js
+++ b/lib/charts/treemap.js
@@ -1,740 +1,740 @@
-/*global d3, $, define */
-/*!
- * Treemap兼容定义部分
- */
-;(function (name, definition) {
- if (typeof define === 'function') { // Module
- define(definition);
- } else { // Assign to common namespaces or simply the global object (window)
- this[name] = definition(function (id) { return this[id];});
- }
-})('Treemap', function (require) {
- var DataV = require('DataV');
-
- /*
- * Treemap构造函数,继承自Chart
- * Options:
- *
- * - `width` 数字,图片宽度,默认为750,表示图片高750px
- * - `height` 数字,图片高度,默认为500
- * - `showBackTag` 布尔值,回退操作导航条是否显示,默认为 true, 显示;设为false则不显示
- * - `backHeight` 数字,回退操作导航条宽度,默认为20
- * - `level1BorderWidth` 数字,一级方框的边框宽度,默认为1(1px),不建议修改
- * - `level2BorderWidth` 数字,二级方框的边框宽度,默认为1(1px),不建议修改
- * - `fontSizeRatio` 数字,表示图中文字大小。默认为1.0(1倍), 若设为2.0,字体大小会加倍;
- * - `customEvent` 函数对象,其中有4个自定义函数。`leafNodeClick` 函数,表示点击叶子节点的事件响应,默认为空函数; `hoverIn` 函数,表示鼠标移进方框的事件响应,默认为空函数; `hoverOut` 函数,表示鼠标移出方框的事件响应,默认为空函数; `mouseover` 函数,表示在方框内移动鼠标的事件响应,默认为设置浮框的内容,可以替换它修改浮框内容; 这些函数可以在创建对象或setOption()时一起设置,也可以通过on函数单独设置。
- *
- * Examples:
- * create treemap in a dom node with id "chart", width is 500; height is 600px;
- * ```
- * var treemap = new Treemap("chart", {"width": 500, "height": 600});
- * ```
- * @param {Object} node The dom node or dom node Id
- * @param {Object} options JSON object for determin treemap style
- */
- var Treemap = DataV.extend(DataV.Chart, {
- initialize: function (node, options) {
- this.type = "Treemap";
- this.node = this.checkContainer(node);
-
- // Properties
- this.selectedTreeNodes = [];//array of nodes on the path from root to recent node
- this.treeNodeJson = {};
- this.level_ = 2;
-
- this.floatTag;//浮框对象,这是个可操作的对象。
-
- // Canvas
- this.defaults.width = 750;
- this.defaults.height = 500;
-
- this.defaults.showBackTag = true;
- this.defaults.backHeight = 20;
-
- this.defaults.level1BorderWidth = 1;
- this.defaults.level2BorderWidth = 1;
- this.defaults.fontSizeRatio = 1.0;
-
- //event
- this.defaults.customEvent = {
- leafNodeClick : function () {},
- hoverIn : function () {},
- hoverOut : function () {},
- mousemove : function () {
- var jqNode = this.jqNode,
- treemap = jqNode.treemap,
- floatTag = treemap.floatTag;
-
- //set floatTag content
- floatTag.html('' + jqNode.treemapNode.name + '
' +
- '' + jqNode.treemapNode.value + '
');
- }
- };
-
- this.setOptions(options);
- this.createCanvas();
- }
- });
-
- /**
- * Create dom node relate to treemap
- */
- Treemap.prototype.createCanvas = function () {
- var conf = this.defaults,
- floatStyle,
- container = this.node,
- backStyle,
- canvasStyle;
-
- this.node.style.position = "relative";
-
- if (conf.showBackTag) {
- this.backTag = document.createElement("div");
- backStyle = this.backTag.style;
- backStyle.width = conf.width + "px";
- backStyle.height = conf.backHeight + "px";
- backStyle.paddingLeft = "5px";
- container.appendChild(this.backTag);
- }
-
- this.canvas = document.createElement("div");
- canvasStyle = this.canvas.style;
- canvasStyle.position = "relative";
- canvasStyle.width = conf.width + "px";
- canvasStyle.height = conf.height + "px";
- container.appendChild(this.canvas);
-
- this.floatTag = DataV.FloatTag()(this.canvas);
-
- this.floatTag.css({"visibility": "hidden"});
-
- //this.canvas.appendChild(this.floatTag);
- };
-
- /*!
- * Get color function according to level 1 node name
- * 根据一级节点名获取颜色函数
- */
- Treemap.prototype._changeLevel1NodeColorIndex = function (nodeName) {
- if (!this._getNodeTheme) {
- this._getNodeTheme = d3.scale.ordinal().range(d3.range(DataV.getColor().length));
- }
- return this._getNodeTheme(nodeName);
- };
-
- /**
- * 获取颜色
- * Examples:
- * ```
- * // 获取第二种颜色的渐变色。
- * {mode: "gradient", index: 1}
- * // 获取最深的离散色。
- * {mode: "random", ratio: 0}
- * // 获取最浅的离散色。
- * {mode: "random", ratio: 1}
- * // 获取适中的离散色。
- * {mode: "random", ratio: 0.5}
- * ```
- * @param {Object} colorJson Way to get color from color theme matrix
- * @return {Array} 返回颜色数组
- */
- Treemap.prototype.getColor = function (colorJson) {
- var colorMatrix = DataV.getColor();
- var color;
- var colorStyle = colorJson || {};
- var colorMode = colorStyle.mode || 'default';
- var i, l;
-
- switch (colorMode) {
- case "multiColorGradient":
- //color = d3.interpolateHsl.apply(null, ["red", "blue"]);
- //color = d3.interpolateHsl.apply(null, [colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]]);
- //color = DataV.gradientColor(["#f5f5f6", "#f6f5f5"], 'special');
- //color = DataV.gradientColor([colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]], 'special');
- //color = d3.interpolateRgb.apply(null, [colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]]);
-
- color = (function () {
- var c = [];
- colorMatrix.forEach(function (d, i) {
- c.push(d[0]);
- });
- return function (ratio) {
- var index = (c.length - 1) * ratio;
- var floor = Math.floor(index);
- var ceil = Math.ceil(index);
- if (floor === ceil) {
- return c[floor];
- } else {
- return d3.interpolateRgb.apply(null, [c[floor], c[ceil]])(index - floor);
- }
- };
- }());
- //color = d3.interpolateRgb.apply(null, ["green", "purple"]);
- break;
- case "gradient":
- var index = colorJson.index || 0;
- index = index < 0 ? 0 : Math.min(index, colorMatrix.length - 1);
- color = d3.interpolateRgb.apply(null, [colorMatrix[index][0], colorMatrix[index][1]]);
- break;
- case "random":
- case "default":
- var ratio = colorStyle.ratio || 0;
- if (ratio < 0) { ratio = 0; }
- if (ratio > 1) { ratio = 1; }
- var colorArray = [];
- for (i = 0, l = colorMatrix.length; i < l; i++) {
- var colorFunc = d3.interpolateRgb.apply(null, [colorMatrix[i][0], colorMatrix[i][1]]);
- colorArray.push(colorFunc(ratio));
- }
- color = d3.scale.ordinal().range(colorArray);
- break;
- }
- return color;
- };
-
- /*
- * 设置数据源
- * Examples:
- * treemap数据输入的格式可以是二维数组。例如下面的数组表示2000年4个季度的天数。
- * 第1季度下面还列出了1-3月的天数。数组的第一行为四个固定的字符串"ID","name","size"和"parentID"。
- * 四列数据分别表示层次数据集中各结点的ID,名称,大小和父节点ID。叶子节点必须有大小,根结点不能有父节点ID。各结点的ID、名称必须要有。
- * ```
- * [
- * ["ID", "name", "size", "parentID"],
- * [0, "2000", , ],
- * [1, "season1", , 0],
- * [2, "January", 31, 1],
- * [3, "February", 29, 1],
- * [4, "Match", 31, 1],
- * [5, "season2", 91, 0],
- * [6, "season3", 92, 0],
- * [7, "season4", 92, 0]
- * ]
- * ```
- * 数据还可以是json格式。每个结点都有`name`,如果是父节点则还有`children`,如果为叶节点则还有`size`。以上数组数据对应的json数据如下:
- * ```
- * {
- * "name": "2000",
- * "children": [
- * {
- * "name": "season1",
- * "children": [
- * {"name": "January", "size": 31},
- * {"name": "February", "size": 29},
- * {"name": "Match", "size": 31}
- * ]
- * },
- * {"name": "season2", "size": 91},
- * {"name": "season3", "size": 92},
- * {"name": "season4", "size": 92},
- * ]
- * }
- * ```
- * @param {Array|Object} source json or 2-d array
- */
- Treemap.prototype.setSource = function (source) {
- if (source instanceof Array) {
- this.rawData = this._arrayToJson(source);
- } else {
- this.rawData = source;
- }
- this.source = this._remapSource(this.rawData);
- this.selectedTreeNodes = [this.source[0]];
- };
-
- /*!
- * Change 2-d array source data to json format.
- * @param {Array} array 待转换的二维数组
- * @return {Object} 返回转换后的对象
- */
- Treemap.prototype._arrayToJson = function (array) {
- // ID name size parentID
- var getColumnIndex = function (columnName) {
- var title = array[0];
- var i, l;
- for (i = 0, l = title.length; i < l; i++) {
- if (title[i] === columnName) {
- return i;
- }
- }
- return -1;
- };
-
- var idx = {};
- var column = ["ID", "name", "size", "parentID"];
- var i, l;
- for (i = 0, l = column.length; i < l; i++) {
- if ((idx[column[i]] = getColumnIndex(column[i])) === -1) {
- throw new Error("no column \'" + column[i] + "\'");
- }
- }
-
- var table = [];
- for (i = 1, l = array.length; i < l; i++) {
- var line = array[i];
- var tableLine = table[i - 1] = [];
- var j, columnL;
- for (j = 0, columnL = column.length; j < columnL; j++) {
- tableLine.push(line[idx[column[j]]]);
- }
- }
-
- var rootID;
- var h = {};
- table.forEach(function (d, i) {
- if (d[0] === "") {
- throw new Error("ID can not be empty(line:" + (i + 1) + ").");
- }
- if (!d[3]) {
- if (rootID) {
- throw new Error("2 or more lines have an empty parentID(line:" + (i + 1) + ").");
- } else {
- rootID = d[0];
- }
- }
- if (h[d[0]]) {
- throw new Error("2 or more lines have same ID: " + d[0] + "(line:" + (i + 1) + ").");
- }
- h[d[0]] = {name: d[1], size: d[2], child: []};
- });
- if (!rootID) {
- throw new Error("No root node defined.");
- }
- table.forEach(function (d, i) {
- if (d[3]) {
- var record = h[d[3]];
- if (!record) {
- throw new Error("Can not find parent with ID " + d[3] + "(line:" + (i + 1) + ").");
- }
- record.child.push(d[0]);
- }
- });
- var recurse = function (parentID) {
- var record = h[parentID];
- if (record.child.length === 0) {
- if (isNaN(parseFloat(record.size))) {
- throw new Error("Leaf node's size is not a number(name:" + record.name + ").");
- } else {
- return {name: record.name, size: record.size};
- }
- } else {
- var childNode = [];
- record.child.forEach(function (d) {
- childNode.push(recurse(d));
- });
- return {name: record.name, children: childNode};
- }
- };
- return recurse(rootID);
- };
-
- /*!
- * map digit data to layout data format.
- * @param source The digit data source from source.
- */
- Treemap.prototype._remapSource = function (data) {
- var conf = this.defaults;
-
- var treemap = this._createTreemap();
- var source = treemap.nodes(data);
-
- var recurse = function (node) {
- if (!node.children) {
- return node.size;
- }
- var size = 0;
- node.children.forEach(function (d) {
- size += parseFloat(recurse(d), 10);
- });
- node.size = size;
- return node.size;
- };
- recurse(source[0]);
-
- return source;
- };
-
- /*!
- * input a node, return a json tree contains the node's level 1 children and level 2 children;
- */
- Treemap.prototype._create2LevelJson = function (node) {
- // return json
- var recurse = function (node, depth) {
- if (depth === 2 && node.refer) {
- node = node.refer;
- }
- if ((!node.children) || depth <= 0) {
- return {name: node.name, size: node.size, refer: node};
- }
- var childNode = [];
- node.children.forEach(function (d) {
- childNode.push(recurse(d, depth - 1));
- });
- return {name: node.name, children: childNode, refer: node};
- };
-
- this.treeNodeJson = recurse(node, this.level_);
- };
-
- /*!
- * add the clicked leaf to selected tree nodes.
- */
- Treemap.prototype._goToLeaf = function (treemapNode) {
- this.selectedTreeNodes.push(treemapNode);
- this.reRender();
- };
-
- /*!
- * remove recent leaf node, set leaf node's parent node as new leaf node;
- */
- Treemap.prototype._goToRoot = function (idx) {
- this.selectedTreeNodes = this.selectedTreeNodes.slice(0, idx + 1);
- this.reRender();
- };
-
- /*!
- * create treemap layout function
- */
- Treemap.prototype._createTreemap = function () {
- var conf = this.defaults;
- return d3.layout.treemap()
- .size([conf.width, conf.height])
- .value(function (d) { return d.size; });
- };
-
- /*!
- * d3 treemap layout
- */
- Treemap.prototype.layout = function () {
- var treemap = this._createTreemap()
- .sort(function (a, b) { return a.value - b.value; });
- this.nodes = treemap.nodes(this.treeNodeJson);
- };
-
- /*!
- * 生成绘制路径
- */
- Treemap.prototype.generatePaths = function () {
- //add interactive need
- var canvas = this.canvas,
- conf = this.defaults,
- color,
- leafIndex = 0,
- leafCount,
- colorRatio,
- level1Node,
- level1NodeArray = [],
- funcArray = {},
- i,
- l,
- level1Count = 0,
- level1ColorFun,
- goIn = function (event) {
- var treemap = event.data.treemap,
- treemapNode = event.data.treemapNode,
- jqueryNode = event.data.jqueryNode;
- jqueryNode.css({'z-index': 30,
- "backgroundColor": jqueryNode.color})
- .animate({
- left : 0,
- top : 0,
- width : treemap.defaults.width,
- height : treemap.defaults.height
- }, 1000, function () {
- treemap._goToLeaf(treemapNode);
- });
- },
-
- goOut = function (event) {
- var treemap = event.data.treemap;
- if (treemap.selectedTreeNodes.length > 1) {
- treemap._goToRoot(treemap.selectedTreeNodes.length - 2);
- }
- return false;
- },
-
- level1HoverIn = function (index, level1NodeArray) {
- return function () {
- level1NodeArray.forEach(function (d, i, Array) {
- if (i === index) {
- d.css("font-size", Array[i].fontSizeValue * 6 / 5 + "px");
- if (d.treemapNode.children) {
- Array[i].css("backgroundColor", '');
- }
- } else {
- Array[i].css("backgroundColor",
- d3.interpolateRgb.apply(null, [Array[i].color, "#fff"])(0.4));
- }
- });
- level1NodeArray[0].treemap.floatTag.css({"visibility" : "visible"});
- };
- },
-
- level1HoverOut = function (level1NodeArray) {
- return function () {
- level1NodeArray.forEach(function (d, i, Array) {
- Array[i].css("backgroundColor", Array[i].color)
- .css("font-size", Array[i].fontSizeValue + "px");
- });
- level1NodeArray[0].treemap.floatTag.css({"visibility" : "hidden"});
- };
- },
-
- level0HoverIn = function () {
- this.jqNode.treemap.floatTag.css({"visibility" : "visible"});
- },
-
- level0HoverOut = function () {
- this.jqNode.treemap.floatTag.css({"visibility" : "hidden"});
- },
-
- customEvent = function (f, o) {
- return function () {
- f.call(o);
- };
- };
-
- $(canvas).append(this.floatTag);//canvas clear before draw, add floatTag.
-
- for (i = 0, l = this.nodes.length; i < l; i++) {
- var d = this.nodes[i],//treemap node
- borderWidth,
- w,
- h,
- left,
- top,
- depthColor,
- depthColorHsl,
- jqNode = $(document.createElement("div"));
-
- if (!(d.parent && d.parent.parent)) {
- //level 0 and 1
- color = this.getColor({mode: "gradient", index: this._changeLevel1NodeColorIndex(d.name)});
- leafIndex = -1;
- leafCount = d.children ? d.children.length : 0;
- colorRatio = 0.5;
-
- if (d.parent) {
- //only level 1
- level1NodeArray.push(jqNode);
- level1Node = jqNode;
- }
- jqNode.color = color(colorRatio);
- if (d.parent) {
- //level 1
- if (this.selectedTreeNodes.length === 1) {
- //root
- if (!level1ColorFun) {
- level1ColorFun = this.getColor({mode: "multiColorGradient"});
- }
- } else {
- if (!level1ColorFun) {
- depthColor = this.selectedTreeNodes[this.selectedTreeNodes.length - 1].color;
- depthColorHsl = d3.hsl(depthColor);
- level1ColorFun = d3.interpolateHsl.apply(null, [
- d3.hsl((depthColorHsl.h + 180) % 360, depthColorHsl.s, depthColorHsl.l).toString(),
- depthColor
- ]);
- }
- }
- jqNode.color = d.parent.children.length === 1
- ? level1ColorFun(0)
- : level1ColorFun((level1Count++) / (d.parent.children.length - 1));
- color = d3.interpolateRgb.apply(null, [jqNode.color,
- d3.interpolateRgb.apply(null, [jqNode.color, "#fff"])(0.5)]);
- } else {
- //level 0
- if (this.selectedTreeNodes.length > 1) {
- jqNode.color = this.selectedTreeNodes[this.selectedTreeNodes.length - 1].color;
- }
- }
- } else {
- //level 2
- leafIndex += 1;
- colorRatio = leafCount === 1 ? 0 : leafIndex / (leafCount - 1);
- jqNode.color = color(colorRatio);
- }
- jqNode.treemapNode = d;
- d.color = jqNode.color;
- jqNode.treemap = this;
- jqNode[0].jqNode = jqNode;
- jqNode.appendTo($(canvas));
-
- jqNode.css("borderStyle", "solid")
- .css("borderColor", "#d6d6d6")
- .css("overflow", "hidden")
- .css("fontFamily", '黑体')
- //.css("fontFamily", 'sans-serif')
- .css("position", "absolute")
- .css("textIndent", "2px")
- .css("backgroundColor", jqNode.color)
- //.css("color", "#fff");
- .css("color", "white");
- //.css("textAlign", "center");
-
- if (!d.parent) {
- // level 0 node
- if (!d.children) {
- jqNode.html(d.name);
- }
- } else if (d.parent && !d.parent.parent) {
- // level 1 node
- jqNode.html(d.name)
- .css("zIndex", 20);
- if (d.children) {
- //jqNode.css("backgroundColor", '');
- jqNode.css("backgroundColor", jqNode.color);
- }
- } else {
- // level 2 node
- jqNode.css("borderColor", "rgba(255, 255, 255, 0.5)");
- }
-
- // position
- // border do not need adjust, still 1 px;
- borderWidth = 1;
- w = Math.max(0, d.dx - borderWidth);
- h = Math.max(0, d.dy - borderWidth);
- jqNode.fontSizeValue = Math.max(conf.fontSizeRatio
- * parseInt(60 * Math.sqrt(w * h / conf.width / conf.height), 10),
- 12);
- jqNode.css("borderWidth", borderWidth + "px")
- .css("left", d.x + "px")
- .css("top", d.y + "px")
- .css("width", w + "px")
- .css("height", h + "px")
- .css("fontSize", jqNode.fontSizeValue + "px");
- //.css("lineHeight", Math.max(0, d.dy - borderWidth ) + "px");
-
- if (d.parent && !d.parent.parent) {
- //level 1
- jqNode.css("cursor", "pointer");
- jqNode.clone().insertBefore(jqNode).css("opacity", 0.01);
- jqNode.click({"treemap": this, "treemapNode": d, "jqueryNode": jqNode}, goIn);
- jqNode.mouseover(level1HoverIn(level1NodeArray.length - 1, level1NodeArray));
- jqNode.mouseout(level1HoverOut(level1NodeArray));
- jqNode.mousemove(this.defaults.customEvent.mousemove);
- }
-
- if (!d.parent) {
- //level 0; leaf node
- jqNode.mouseover(level0HoverIn);
- jqNode.mouseout(level0HoverOut);
- jqNode.mousemove(this.defaults.customEvent.mousemove);
- }
-
- jqNode.bind("contextmenu", {"treemap": this}, goOut);
-
- // custom interacitves
- var o = {treemapNode : d,
- node : jqNode,
- name : d.name,
- parent : d.parent,
- value : d.value
- };
-
- if (d.parent && (!d.parent.parent) && (!d.children)) {
- jqNode.click(customEvent(this.defaults.customEvent.leafNodeClick, o));
- }
-
- jqNode.mouseover(customEvent(this.defaults.customEvent.hoverIn, o));
-
- jqNode.mouseout(customEvent(this.defaults.customEvent.hoverOut, o));
-
- //canvas.appendChild(jqNode[0]);
- }
- };
-
- /**
- * 清除画布
- */
- Treemap.prototype.clearCanvas = function () {
- var canvas = this.canvas;
- canvas.innerHTML = "";
- };
-
- /*!
- * set back navi link
- */
- Treemap.prototype._setBackTag = function () {
- if (!this.backTag) {return; }
- var p = document.createElement("p"),
- i,
- l,
- backClick = function (that, idx) {
- return function () {
- that._goToRoot(idx);
- };
- },
- lastA,
- lastGt;
- for (i = 0, l = this.selectedTreeNodes.length; i < l; i++) {
- var a = document.createElement("a");
- a.innerHTML = this.selectedTreeNodes[i].name;
- if (i < l - 1) {
- a.style.color = "blue";
- a.style.cursor = "pointer";
- a.onclick = backClick(this, i);
- } else {
- lastA = a;
- }
- if (i > 0) {
- var span = document.createElement('span');
- span.innerHTML = " > ";
- p.appendChild(span);
- if (i === l - 1) {
- lastGt = span;
- }
- }
- p.appendChild(a);
-
- }
- $(lastA).css({"opacity": 0.01, "position": "relative", "left": l === 1 ? 0 : -20})
- .animate({"opacity": 1, "left": 0}, 1000);
- this.backTag.innerHTML = "";
- this.backTag.appendChild(p);
- };
-
- /**
- * 计算布局,并重新渲染图表
- */
- Treemap.prototype.reRender = function (options) {
- this.clearCanvas();
- this.setOptions(options);
- this._create2LevelJson(this.selectedTreeNodes[this.selectedTreeNodes.length - 1]);
- this.layout();
- this._setBackTag();
- this.generatePaths();
- };
-
- /**
- * 计算布局位置,并渲染图表
- */
- Treemap.prototype.render = function (options) {
- this.clearCanvas();
- this.setOptions(options);
- this._getNodeTheme = undefined;
- this.selectedTreeNodes = this.selectedTreeNodes.slice(0, 1);
- this._create2LevelJson(this.selectedTreeNodes[0]);
- this.layout();
- this._setBackTag();
- this.generatePaths();
- };
-
- /**
- * 设置自定义事件
- */
- Treemap.prototype.on = function (eventName, callback) {
- if ($.inArray(eventName, ["leafNodeClick", "hoverIn", "hoverOut", "mousemove"]) !== -1) {
- this.defaults.customEvent[eventName] = callback;
- }
- };
-
- /*!
- * 导出Treemap
- */
- return Treemap;
-});
+/*global d3, $, define */
+/*!
+ * Treemap兼容定义部分
+ */
+;(function (name, definition) {
+ if (typeof define === 'function') { // Module
+ define(definition);
+ } else { // Assign to common namespaces or simply the global object (window)
+ this[name] = definition(function (id) { return this[id];});
+ }
+})('Treemap', function (require) {
+ var DataV = require('DataV');
+
+ /*
+ * Treemap构造函数,继承自Chart
+ * Options:
+ *
+ * - `width` 数字,图片宽度,默认为750,表示图片高750px
+ * - `height` 数字,图片高度,默认为500
+ * - `showBackTag` 布尔值,回退操作导航条是否显示,默认为 true, 显示;设为false则不显示
+ * - `backHeight` 数字,回退操作导航条宽度,默认为20
+ * - `level1BorderWidth` 数字,一级方框的边框宽度,默认为1(1px),不建议修改
+ * - `level2BorderWidth` 数字,二级方框的边框宽度,默认为1(1px),不建议修改
+ * - `fontSizeRatio` 数字,表示图中文字大小。默认为1.0(1倍), 若设为2.0,字体大小会加倍;
+ * - `customEvent` 函数对象,其中有4个自定义函数。`leafNodeClick` 函数,表示点击叶子节点的事件响应,默认为空函数; `hoverIn` 函数,表示鼠标移进方框的事件响应,默认为空函数; `hoverOut` 函数,表示鼠标移出方框的事件响应,默认为空函数; `mouseover` 函数,表示在方框内移动鼠标的事件响应,默认为设置浮框的内容,可以替换它修改浮框内容; 这些函数可以在创建对象或setOption()时一起设置,也可以通过on函数单独设置。
+ *
+ * Examples:
+ * create treemap in a dom node with id "chart", width is 500; height is 600px;
+ * ```
+ * var treemap = new Treemap("chart", {"width": 500, "height": 600});
+ * ```
+ * @param {Object} node The dom node or dom node Id
+ * @param {Object} options JSON object for determin treemap style
+ */
+ var Treemap = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = "Treemap";
+ this.node = this.checkContainer(node);
+
+ // Properties
+ this.selectedTreeNodes = [];//array of nodes on the path from root to recent node
+ this.treeNodeJson = {};
+ this.level_ = 2;
+
+ this.floatTag;//浮框对象,这是个可操作的对象。
+
+ // Canvas
+ this.defaults.width = 750;
+ this.defaults.height = 500;
+
+ this.defaults.showBackTag = true;
+ this.defaults.backHeight = 20;
+
+ this.defaults.level1BorderWidth = 1;
+ this.defaults.level2BorderWidth = 1;
+ this.defaults.fontSizeRatio = 1.0;
+
+ //event
+ this.defaults.customEvent = {
+ leafNodeClick : function () {},
+ hoverIn : function () {},
+ hoverOut : function () {},
+ mousemove : function () {
+ var jqNode = this.jqNode,
+ treemap = jqNode.treemap,
+ floatTag = treemap.floatTag;
+
+ //set floatTag content
+ floatTag.html('' + jqNode.treemapNode.name + '
' +
+ '' + jqNode.treemapNode.value + '
');
+ }
+ };
+
+ this.setOptions(options);
+ this.createCanvas();
+ }
+ });
+
+ /**
+ * Create dom node relate to treemap
+ */
+ Treemap.prototype.createCanvas = function () {
+ var conf = this.defaults,
+ floatStyle,
+ container = this.node,
+ backStyle,
+ canvasStyle;
+
+ this.node.style.position = "relative";
+
+ if (conf.showBackTag) {
+ this.backTag = document.createElement("div");
+ backStyle = this.backTag.style;
+ backStyle.width = conf.width + "px";
+ backStyle.height = conf.backHeight + "px";
+ backStyle.paddingLeft = "5px";
+ container.appendChild(this.backTag);
+ }
+
+ this.canvas = document.createElement("div");
+ canvasStyle = this.canvas.style;
+ canvasStyle.position = "relative";
+ canvasStyle.width = conf.width + "px";
+ canvasStyle.height = conf.height + "px";
+ container.appendChild(this.canvas);
+
+ this.floatTag = DataV.FloatTag()(this.canvas);
+
+ this.floatTag.css({"visibility": "hidden"});
+
+ //this.canvas.appendChild(this.floatTag);
+ };
+
+ /*!
+ * Get color function according to level 1 node name
+ * 根据一级节点名获取颜色函数
+ */
+ Treemap.prototype._changeLevel1NodeColorIndex = function (nodeName) {
+ if (!this._getNodeTheme) {
+ this._getNodeTheme = d3.scale.ordinal().range(d3.range(DataV.getColor().length));
+ }
+ return this._getNodeTheme(nodeName);
+ };
+
+ /**
+ * 获取颜色
+ * Examples:
+ * ```
+ * // 获取第二种颜色的渐变色。
+ * {mode: "gradient", index: 1}
+ * // 获取最深的离散色。
+ * {mode: "random", ratio: 0}
+ * // 获取最浅的离散色。
+ * {mode: "random", ratio: 1}
+ * // 获取适中的离散色。
+ * {mode: "random", ratio: 0.5}
+ * ```
+ * @param {Object} colorJson Way to get color from color theme matrix
+ * @return {Array} 返回颜色数组
+ */
+ Treemap.prototype.getColor = function (colorJson) {
+ var colorMatrix = DataV.getColor();
+ var color;
+ var colorStyle = colorJson || {};
+ var colorMode = colorStyle.mode || 'default';
+ var i, l;
+
+ switch (colorMode) {
+ case "multiColorGradient":
+ //color = d3.interpolateHsl.apply(null, ["red", "blue"]);
+ //color = d3.interpolateHsl.apply(null, [colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]]);
+ //color = DataV.gradientColor(["#f5f5f6", "#f6f5f5"], 'special');
+ //color = DataV.gradientColor([colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]], 'special');
+ //color = d3.interpolateRgb.apply(null, [colorMatrix[0][0], colorMatrix[colorMatrix.length - 1][0]]);
+
+ color = (function () {
+ var c = [];
+ colorMatrix.forEach(function (d, i) {
+ c.push(d[0]);
+ });
+ return function (ratio) {
+ var index = (c.length - 1) * ratio;
+ var floor = Math.floor(index);
+ var ceil = Math.ceil(index);
+ if (floor === ceil) {
+ return c[floor];
+ } else {
+ return d3.interpolateRgb.apply(null, [c[floor], c[ceil]])(index - floor);
+ }
+ };
+ }());
+ //color = d3.interpolateRgb.apply(null, ["green", "purple"]);
+ break;
+ case "gradient":
+ var index = colorJson.index || 0;
+ index = index < 0 ? 0 : Math.min(index, colorMatrix.length - 1);
+ color = d3.interpolateRgb.apply(null, [colorMatrix[index][0], colorMatrix[index][1]]);
+ break;
+ case "random":
+ case "default":
+ var ratio = colorStyle.ratio || 0;
+ if (ratio < 0) { ratio = 0; }
+ if (ratio > 1) { ratio = 1; }
+ var colorArray = [];
+ for (i = 0, l = colorMatrix.length; i < l; i++) {
+ var colorFunc = d3.interpolateRgb.apply(null, [colorMatrix[i][0], colorMatrix[i][1]]);
+ colorArray.push(colorFunc(ratio));
+ }
+ color = d3.scale.ordinal().range(colorArray);
+ break;
+ }
+ return color;
+ };
+
+ /*
+ * 设置数据源
+ * Examples:
+ * treemap数据输入的格式可以是二维数组。例如下面的数组表示2000年4个季度的天数。
+ * 第1季度下面还列出了1-3月的天数。数组的第一行为四个固定的字符串"ID","name","size"和"parentID"。
+ * 四列数据分别表示层次数据集中各结点的ID,名称,大小和父节点ID。叶子节点必须有大小,根结点不能有父节点ID。各结点的ID、名称必须要有。
+ * ```
+ * [
+ * ["ID", "name", "size", "parentID"],
+ * [0, "2000", , ],
+ * [1, "season1", , 0],
+ * [2, "January", 31, 1],
+ * [3, "February", 29, 1],
+ * [4, "Match", 31, 1],
+ * [5, "season2", 91, 0],
+ * [6, "season3", 92, 0],
+ * [7, "season4", 92, 0]
+ * ]
+ * ```
+ * 数据还可以是json格式。每个结点都有`name`,如果是父节点则还有`children`,如果为叶节点则还有`size`。以上数组数据对应的json数据如下:
+ * ```
+ * {
+ * "name": "2000",
+ * "children": [
+ * {
+ * "name": "season1",
+ * "children": [
+ * {"name": "January", "size": 31},
+ * {"name": "February", "size": 29},
+ * {"name": "Match", "size": 31}
+ * ]
+ * },
+ * {"name": "season2", "size": 91},
+ * {"name": "season3", "size": 92},
+ * {"name": "season4", "size": 92},
+ * ]
+ * }
+ * ```
+ * @param {Array|Object} source json or 2-d array
+ */
+ Treemap.prototype.setSource = function (source) {
+ if (source instanceof Array) {
+ this.rawData = this._arrayToJson(source);
+ } else {
+ this.rawData = source;
+ }
+ this.source = this._remapSource(this.rawData);
+ this.selectedTreeNodes = [this.source[0]];
+ };
+
+ /*!
+ * Change 2-d array source data to json format.
+ * @param {Array} array 待转换的二维数组
+ * @return {Object} 返回转换后的对象
+ */
+ Treemap.prototype._arrayToJson = function (array) {
+ // ID name size parentID
+ var getColumnIndex = function (columnName) {
+ var title = array[0];
+ var i, l;
+ for (i = 0, l = title.length; i < l; i++) {
+ if (title[i] === columnName) {
+ return i;
+ }
+ }
+ return -1;
+ };
+
+ var idx = {};
+ var column = ["ID", "name", "size", "parentID"];
+ var i, l;
+ for (i = 0, l = column.length; i < l; i++) {
+ if ((idx[column[i]] = getColumnIndex(column[i])) === -1) {
+ throw new Error("no column \'" + column[i] + "\'");
+ }
+ }
+
+ var table = [];
+ for (i = 1, l = array.length; i < l; i++) {
+ var line = array[i];
+ var tableLine = table[i - 1] = [];
+ var j, columnL;
+ for (j = 0, columnL = column.length; j < columnL; j++) {
+ tableLine.push(line[idx[column[j]]]);
+ }
+ }
+
+ var rootID;
+ var h = {};
+ table.forEach(function (d, i) {
+ if (d[0] === "") {
+ throw new Error("ID can not be empty(line:" + (i + 1) + ").");
+ }
+ if (!d[3]) {
+ if (rootID) {
+ throw new Error("2 or more lines have an empty parentID(line:" + (i + 1) + ").");
+ } else {
+ rootID = d[0];
+ }
+ }
+ if (h[d[0]]) {
+ throw new Error("2 or more lines have same ID: " + d[0] + "(line:" + (i + 1) + ").");
+ }
+ h[d[0]] = {name: d[1], size: d[2], child: []};
+ });
+ if (!rootID) {
+ throw new Error("No root node defined.");
+ }
+ table.forEach(function (d, i) {
+ if (d[3]) {
+ var record = h[d[3]];
+ if (!record) {
+ throw new Error("Can not find parent with ID " + d[3] + "(line:" + (i + 1) + ").");
+ }
+ record.child.push(d[0]);
+ }
+ });
+ var recurse = function (parentID) {
+ var record = h[parentID];
+ if (record.child.length === 0) {
+ if (isNaN(parseFloat(record.size))) {
+ throw new Error("Leaf node's size is not a number(name:" + record.name + ").");
+ } else {
+ return {name: record.name, size: record.size};
+ }
+ } else {
+ var childNode = [];
+ record.child.forEach(function (d) {
+ childNode.push(recurse(d));
+ });
+ return {name: record.name, children: childNode};
+ }
+ };
+ return recurse(rootID);
+ };
+
+ /*!
+ * map digit data to layout data format.
+ * @param source The digit data source from source.
+ */
+ Treemap.prototype._remapSource = function (data) {
+ var conf = this.defaults;
+
+ var treemap = this._createTreemap();
+ var source = treemap.nodes(data);
+
+ var recurse = function (node) {
+ if (!node.children) {
+ return node.size;
+ }
+ var size = 0;
+ node.children.forEach(function (d) {
+ size += parseFloat(recurse(d), 10);
+ });
+ node.size = size;
+ return node.size;
+ };
+ recurse(source[0]);
+
+ return source;
+ };
+
+ /*!
+ * input a node, return a json tree contains the node's level 1 children and level 2 children;
+ */
+ Treemap.prototype._create2LevelJson = function (node) {
+ // return json
+ var recurse = function (node, depth) {
+ if (depth === 2 && node.refer) {
+ node = node.refer;
+ }
+ if ((!node.children) || depth <= 0) {
+ return {name: node.name, size: node.size, refer: node};
+ }
+ var childNode = [];
+ node.children.forEach(function (d) {
+ childNode.push(recurse(d, depth - 1));
+ });
+ return {name: node.name, children: childNode, refer: node};
+ };
+
+ this.treeNodeJson = recurse(node, this.level_);
+ };
+
+ /*!
+ * add the clicked leaf to selected tree nodes.
+ */
+ Treemap.prototype._goToLeaf = function (treemapNode) {
+ this.selectedTreeNodes.push(treemapNode);
+ this.reRender();
+ };
+
+ /*!
+ * remove recent leaf node, set leaf node's parent node as new leaf node;
+ */
+ Treemap.prototype._goToRoot = function (idx) {
+ this.selectedTreeNodes = this.selectedTreeNodes.slice(0, idx + 1);
+ this.reRender();
+ };
+
+ /*!
+ * create treemap layout function
+ */
+ Treemap.prototype._createTreemap = function () {
+ var conf = this.defaults;
+ return d3.layout.treemap()
+ .size([conf.width, conf.height])
+ .value(function (d) { return d.size; });
+ };
+
+ /*!
+ * d3 treemap layout
+ */
+ Treemap.prototype.layout = function () {
+ var treemap = this._createTreemap()
+ .sort(function (a, b) { return a.value - b.value; });
+ this.nodes = treemap.nodes(this.treeNodeJson);
+ };
+
+ /*!
+ * 生成绘制路径
+ */
+ Treemap.prototype.generatePaths = function () {
+ //add interactive need
+ var canvas = this.canvas,
+ conf = this.defaults,
+ color,
+ leafIndex = 0,
+ leafCount,
+ colorRatio,
+ level1Node,
+ level1NodeArray = [],
+ funcArray = {},
+ i,
+ l,
+ level1Count = 0,
+ level1ColorFun,
+ goIn = function (event) {
+ var treemap = event.data.treemap,
+ treemapNode = event.data.treemapNode,
+ jqueryNode = event.data.jqueryNode;
+ jqueryNode.css({'z-index': 30,
+ "backgroundColor": jqueryNode.color})
+ .animate({
+ left : 0,
+ top : 0,
+ width : treemap.defaults.width,
+ height : treemap.defaults.height
+ }, 1000, function () {
+ treemap._goToLeaf(treemapNode);
+ });
+ },
+
+ goOut = function (event) {
+ var treemap = event.data.treemap;
+ if (treemap.selectedTreeNodes.length > 1) {
+ treemap._goToRoot(treemap.selectedTreeNodes.length - 2);
+ }
+ return false;
+ },
+
+ level1HoverIn = function (index, level1NodeArray) {
+ return function () {
+ level1NodeArray.forEach(function (d, i, Array) {
+ if (i === index) {
+ d.css("font-size", Array[i].fontSizeValue * 6 / 5 + "px");
+ if (d.treemapNode.children) {
+ Array[i].css("backgroundColor", '');
+ }
+ } else {
+ Array[i].css("backgroundColor",
+ d3.interpolateRgb.apply(null, [Array[i].color, "#fff"])(0.4));
+ }
+ });
+ level1NodeArray[0].treemap.floatTag.css({"visibility" : "visible"});
+ };
+ },
+
+ level1HoverOut = function (level1NodeArray) {
+ return function () {
+ level1NodeArray.forEach(function (d, i, Array) {
+ Array[i].css("backgroundColor", Array[i].color)
+ .css("font-size", Array[i].fontSizeValue + "px");
+ });
+ level1NodeArray[0].treemap.floatTag.css({"visibility" : "hidden"});
+ };
+ },
+
+ level0HoverIn = function () {
+ this.jqNode.treemap.floatTag.css({"visibility" : "visible"});
+ },
+
+ level0HoverOut = function () {
+ this.jqNode.treemap.floatTag.css({"visibility" : "hidden"});
+ },
+
+ customEvent = function (f, o) {
+ return function () {
+ f.call(o);
+ };
+ };
+
+ $(canvas).append(this.floatTag);//canvas clear before draw, add floatTag.
+
+ for (i = 0, l = this.nodes.length; i < l; i++) {
+ var d = this.nodes[i],//treemap node
+ borderWidth,
+ w,
+ h,
+ left,
+ top,
+ depthColor,
+ depthColorHsl,
+ jqNode = $(document.createElement("div"));
+
+ if (!(d.parent && d.parent.parent)) {
+ //level 0 and 1
+ color = this.getColor({mode: "gradient", index: this._changeLevel1NodeColorIndex(d.name)});
+ leafIndex = -1;
+ leafCount = d.children ? d.children.length : 0;
+ colorRatio = 0.5;
+
+ if (d.parent) {
+ //only level 1
+ level1NodeArray.push(jqNode);
+ level1Node = jqNode;
+ }
+ jqNode.color = color(colorRatio);
+ if (d.parent) {
+ //level 1
+ if (this.selectedTreeNodes.length === 1) {
+ //root
+ if (!level1ColorFun) {
+ level1ColorFun = this.getColor({mode: "multiColorGradient"});
+ }
+ } else {
+ if (!level1ColorFun) {
+ depthColor = this.selectedTreeNodes[this.selectedTreeNodes.length - 1].color;
+ depthColorHsl = d3.hsl(depthColor);
+ level1ColorFun = d3.interpolateHsl.apply(null, [
+ d3.hsl((depthColorHsl.h + 180) % 360, depthColorHsl.s, depthColorHsl.l).toString(),
+ depthColor
+ ]);
+ }
+ }
+ jqNode.color = d.parent.children.length === 1
+ ? level1ColorFun(0)
+ : level1ColorFun((level1Count++) / (d.parent.children.length - 1));
+ color = d3.interpolateRgb.apply(null, [jqNode.color,
+ d3.interpolateRgb.apply(null, [jqNode.color, "#fff"])(0.5)]);
+ } else {
+ //level 0
+ if (this.selectedTreeNodes.length > 1) {
+ jqNode.color = this.selectedTreeNodes[this.selectedTreeNodes.length - 1].color;
+ }
+ }
+ } else {
+ //level 2
+ leafIndex += 1;
+ colorRatio = leafCount === 1 ? 0 : leafIndex / (leafCount - 1);
+ jqNode.color = color(colorRatio);
+ }
+ jqNode.treemapNode = d;
+ d.color = jqNode.color;
+ jqNode.treemap = this;
+ jqNode[0].jqNode = jqNode;
+ jqNode.appendTo($(canvas));
+
+ jqNode.css("borderStyle", "solid")
+ .css("borderColor", "#d6d6d6")
+ .css("overflow", "hidden")
+ .css("fontFamily", '黑体')
+ //.css("fontFamily", 'sans-serif')
+ .css("position", "absolute")
+ .css("textIndent", "2px")
+ .css("backgroundColor", jqNode.color)
+ //.css("color", "#fff");
+ .css("color", "white");
+ //.css("textAlign", "center");
+
+ if (!d.parent) {
+ // level 0 node
+ if (!d.children) {
+ jqNode.html(d.name);
+ }
+ } else if (d.parent && !d.parent.parent) {
+ // level 1 node
+ jqNode.html(d.name)
+ .css("zIndex", 20);
+ if (d.children) {
+ //jqNode.css("backgroundColor", '');
+ jqNode.css("backgroundColor", jqNode.color);
+ }
+ } else {
+ // level 2 node
+ jqNode.css("borderColor", "rgba(255, 255, 255, 0.5)");
+ }
+
+ // position
+ // border do not need adjust, still 1 px;
+ borderWidth = 1;
+ w = Math.max(0, d.dx - borderWidth);
+ h = Math.max(0, d.dy - borderWidth);
+ jqNode.fontSizeValue = Math.max(conf.fontSizeRatio
+ * parseInt(60 * Math.sqrt(w * h / conf.width / conf.height), 10),
+ 12);
+ jqNode.css("borderWidth", borderWidth + "px")
+ .css("left", d.x + "px")
+ .css("top", d.y + "px")
+ .css("width", w + "px")
+ .css("height", h + "px")
+ .css("fontSize", jqNode.fontSizeValue + "px");
+ //.css("lineHeight", Math.max(0, d.dy - borderWidth ) + "px");
+
+ if (d.parent && !d.parent.parent) {
+ //level 1
+ jqNode.css("cursor", "pointer");
+ jqNode.clone().insertBefore(jqNode).css("opacity", 0.01);
+ jqNode.click({"treemap": this, "treemapNode": d, "jqueryNode": jqNode}, goIn);
+ jqNode.mouseover(level1HoverIn(level1NodeArray.length - 1, level1NodeArray));
+ jqNode.mouseout(level1HoverOut(level1NodeArray));
+ jqNode.mousemove(this.defaults.customEvent.mousemove);
+ }
+
+ if (!d.parent) {
+ //level 0; leaf node
+ jqNode.mouseover(level0HoverIn);
+ jqNode.mouseout(level0HoverOut);
+ jqNode.mousemove(this.defaults.customEvent.mousemove);
+ }
+
+ jqNode.bind("contextmenu", {"treemap": this}, goOut);
+
+ // custom interacitves
+ var o = {treemapNode : d,
+ node : jqNode,
+ name : d.name,
+ parent : d.parent,
+ value : d.value
+ };
+
+ if (d.parent && (!d.parent.parent) && (!d.children)) {
+ jqNode.click(customEvent(this.defaults.customEvent.leafNodeClick, o));
+ }
+
+ jqNode.mouseover(customEvent(this.defaults.customEvent.hoverIn, o));
+
+ jqNode.mouseout(customEvent(this.defaults.customEvent.hoverOut, o));
+
+ //canvas.appendChild(jqNode[0]);
+ }
+ };
+
+ /**
+ * 清除画布
+ */
+ Treemap.prototype.clearCanvas = function () {
+ var canvas = this.canvas;
+ canvas.innerHTML = "";
+ };
+
+ /*!
+ * set back navi link
+ */
+ Treemap.prototype._setBackTag = function () {
+ if (!this.backTag) {return; }
+ var p = document.createElement("p"),
+ i,
+ l,
+ backClick = function (that, idx) {
+ return function () {
+ that._goToRoot(idx);
+ };
+ },
+ lastA,
+ lastGt;
+ for (i = 0, l = this.selectedTreeNodes.length; i < l; i++) {
+ var a = document.createElement("a");
+ a.innerHTML = this.selectedTreeNodes[i].name;
+ if (i < l - 1) {
+ a.style.color = "blue";
+ a.style.cursor = "pointer";
+ a.onclick = backClick(this, i);
+ } else {
+ lastA = a;
+ }
+ if (i > 0) {
+ var span = document.createElement('span');
+ span.innerHTML = " > ";
+ p.appendChild(span);
+ if (i === l - 1) {
+ lastGt = span;
+ }
+ }
+ p.appendChild(a);
+
+ }
+ $(lastA).css({"opacity": 0.01, "position": "relative", "left": l === 1 ? 0 : -20})
+ .animate({"opacity": 1, "left": 0}, 1000);
+ this.backTag.innerHTML = "";
+ this.backTag.appendChild(p);
+ };
+
+ /**
+ * 计算布局,并重新渲染图表
+ */
+ Treemap.prototype.reRender = function (options) {
+ this.clearCanvas();
+ this.setOptions(options);
+ this._create2LevelJson(this.selectedTreeNodes[this.selectedTreeNodes.length - 1]);
+ this.layout();
+ this._setBackTag();
+ this.generatePaths();
+ };
+
+ /**
+ * 计算布局位置,并渲染图表
+ */
+ Treemap.prototype.render = function (options) {
+ this.clearCanvas();
+ this.setOptions(options);
+ this._getNodeTheme = undefined;
+ this.selectedTreeNodes = this.selectedTreeNodes.slice(0, 1);
+ this._create2LevelJson(this.selectedTreeNodes[0]);
+ this.layout();
+ this._setBackTag();
+ this.generatePaths();
+ };
+
+ /**
+ * 设置自定义事件
+ */
+ Treemap.prototype.on = function (eventName, callback) {
+ if ($.inArray(eventName, ["leafNodeClick", "hoverIn", "hoverOut", "mousemove"]) !== -1) {
+ this.defaults.customEvent[eventName] = callback;
+ }
+ };
+
+ /*!
+ * 导出Treemap
+ */
+ return Treemap;
+});