+ * MIT Licensed
+ */
+(function (name, def) {
+ if (typeof define === 'function') {
+ define(def);
+ } else {
+ this[name] = def(function (id) {
+ return this[id];
+ });
+ }
+})('LevelChart', function (require) {
+ var DataV = require('DataV');
+
+ //init level chart,set default params
+ var LevelChart = DataV.extend(DataV.Chart, {
+ initialize: function (node, options) {
+ this.type = 'LevelChart';
+ this.node = this.checkContainer(node);
+
+ this.defaults.labels = [ '低', '偏低', '中', '偏高', '高' ];
+ this.defaults.width = 235;
+ this.defaults.height = 200;
+
+ this.setOptions(options);
+ }
+ });
+
+ LevelChart.prototype.drawGridConsume = function (r, x, y, w, h, wv, hv, color, options) {
+ color = color || "#999999";
+ var path = [];
+ var conf = this.defaults;
+ var rowHeight = h / hv,
+ columnWidth = w / wv;
+ for (var i = 0; i < hv; i++) {
+ if (i === hv - 1) {
+ r.path(["M", Math.round(x) - 0.5, Math.round(y + i * rowHeight) - 0.5, "H", Math.round(x + w) + 0.5]).attr({'stroke-width': 1, 'stroke': '#999999'});
+ continue;
+ }
+ path = path.concat(["M", Math.round(x) + 0.5, Math.round(y + i * rowHeight) + 0.5, "H", Math.round(x + w) + 0.5]);
+ }
+ for (i = 0; i < wv; i++) {
+ path = path.concat(["M", Math.round(x + i * columnWidth) + 0.5, Math.round(y) + 0.5, "V", Math.round(y + h) + 0.5]);
+ }
+ if ((!options || !options.tipImg) && (!options || !options.nobase)) {
+ r.image('http://img03.taobaocdn.com/tps/i3/T1aS6GXhFgXXcm5wzo-77-19.png', x + w - 80, 0, 77, 19);
+ } else if (!!options.tipImg) {
+ r.image(options.tipImg, x + w - 80, 0, 77, 19);
+ }
+ return r.path(path.join(",")).attr({ stroke: color, opacity: 0.7, 'stroke-width': 1});
+ }
+
+ LevelChart.prototype.createCanvas = function () {
+ var conf = this.defaults;
+ this.canvas = new Raphael(this.node, conf.width, conf.height);
+ };
+
+ LevelChart.prototype.render = function (data, options) {
+
+ if (options && options.labels) {
+ var labels = options.labels;
+ } else {
+ var labels = this.defaults.labels;
+ }
+ // var labesl = options.labels ? options.labels : this.defaults.labels;
+ var nobase = options && options.nobase;
+ var nopercent = options && options.nopercent;
+
+ var conf = this.defaults;
+ var $node = $(this.node);
+ var width = conf.width;
+ var height = conf.height;
+ var leftGutter = 10;
+ var bottomGutter = 5;
+ var topGutter = 20;
+ var padding = 10;
+
+ var r = Raphael($node[0], width, height);
+ var localMax = Math.max.apply(Math, data);
+ var globalMax = options && options.max || localMax || 100;
+ var globalBase = options && options.globalBase || [8, 17, 50, 17, 10];
+
+ if (!nobase && globalMax < 50) {
+ globalMax = 50;
+ }
+
+ this.drawGridConsume(r, 0, topGutter + 0.5, width, height - topGutter - bottomGutter, 0, 6, '#cccbbb', options);
+
+ var globalBasePlot = [];
+
+ var columnWidth = Math.floor(width / labels.length) - leftGutter - padding;
+ var columnHeight = Math.floor(height - topGutter - bottomGutter);
+
+ //判断是否需要分条
+ var is2dFlag = 0;
+ for (var x = 0; x < data.length; x++) {
+ if (Object.prototype.toString.call(data[x]) === '[object Array]') {
+ is2dFlag = 1;
+ }
+ }
+
+ if (is2dFlag === 1){ //当出现多维同柱对比时
+ var x = [];
+ var y = [];
+ var tmpMax = [];
+ // 重赋多维基数默认值
+ globalBase = Object.prototype.toString.call(globalBase) === '[object Array]' ? globalBase : [[ 10, 20, 30, 10, 15 ], [ 15, 28, 40, 20, 20 ]];
+
+ var globalMax;
+ for (var n in data) {
+ //获得多维数据中的最大值
+ for (var i = 0; i < data[n].length; i++) {
+ tmpMax.push(Math.max.apply(Math, data[n]));
+ }
+ globalMax = Math.max.apply(Math, tmpMax);
+ }
+
+ for (var n in data) {
+ var color = [];
+ for (var i = 0; i < data[n].length; i++) {
+ var k = i + n * data[n].length;
+ var dwidth = columnWidth / data.length;
+
+ x.push(Math.floor((columnWidth + leftGutter + padding) * i) + 10 + dwidth * n);
+ var dheight = Math.ceil((columnHeight - 18) * (data[n][i] / globalMax)) * 0.75;
+ y.push(columnHeight * 6 / 7 - dheight + topGutter - 5.5);
+ var attr = {'font-family': 'Arial', 'font-size': 12, 'fill': '#666666'};
+ var value = data[n][i];
+
+ r.text(x[i] + columnWidth / 2, height - topGutter, labels[i]).attr(attr);
+ var rect = r.rect(x[k], y[k], dwidth, dheight);
+
+ var unit = '';
+ if (!nopercent) {
+ unit = '%';
+ }
+
+ var text = r.text(x[k] + columnWidth / 4, y[k] - 10, value + unit).attr({'font-size': 10});
+ if (n <= 2) {
+ color = ['#3fa9f5','#ff89a3'];
+ } else {
+ var c = 'rgb(300, 200, ' + n * 100 + ')';
+ color.push(c);
+ }
+
+ rect.attr({'fill': color[n], 'stroke': color[n], 'opacity': 0.7});
+
+ rect.indexI = i;
+ rect.indexN = n;
+ if (!nobase) {
+ // hover效果
+ rect.hover(function () {
+ var li = this.indexI + this.indexN * labels.length; //标记在globalBase中当前柱状坐标的下标
+ this.attr({'opacity': 1.0});
+ var maxLen = labels.length - 1;
+ var x = globalBasePlot[this.indexI][0] + columnWidth;
+ //处理最右侧的元素
+ x = this.indexI === maxLen ? x - columnWidth * 2 - 7 : x;
+ var y = globalBasePlot[li][1] - 10;
+ this.tooltipImage = r.image('http://img01.taobaocdn.com/tps/i1/T15H_GXiJjXXc.LCDe-40-21.gif', x, y, 40, 21);
+ if (this.indexI === maxLen) {
+ this.tooltipImage.rotate(180);
+ x -= maxLen;
+ }
+ this.tooltipText = r.text(x + 23, y + 11, globalBase[this.indexN][this.indexI] + '%').attr({'fill': '#fff', 'font-size': 12});
+ }, function () {
+ this.attr({'opacity': 0.7});
+ if (this.tooltipImage) {
+ this.tooltipImage.remove();
+ }
+ if (this.tooltipText) {
+ this.tooltipText.remove();
+ }
+ });
+
+ // 计算全网基数坐标
+ globalBasePlot[k] = [x[k], columnHeight * 6 / 7
+ - (Math.ceil((columnHeight - 18) * (globalBase[n][i] / globalMax)) * 0.75) + topGutter - 6,
+ x[k] + columnWidth / 2 , columnHeight * 6 / 7
+ - (Math.ceil((columnHeight - 18) * (globalBase[n][i] / globalMax)) * 0.75) + topGutter - 6];
+ }
+ }
+ }
+ } else { //当为单条时
+ for (var i = 0; i < data.length; i++) {
+ // 计算依赖的值
+ var x = Math.floor((columnWidth + leftGutter + padding) * i) + 10;
+ var dwidth = columnWidth;
+ // 与全局最高值之间做变换
+ var dheight = Math.ceil((columnHeight - 18) * (data[i] / globalMax)) * 0.75;
+ var y = columnHeight * 6 / 7 - dheight + topGutter - 6;
+ var attr = {'font-family': 'Arial', 'font-size': 12, 'fill': '#666666'};
+ var value = data[i];
+
+ // 画坐标轴
+ r.text(x + columnWidth / 2, height - topGutter, labels[i]).attr(attr);
+ // 画柱状
+ var rect = r.rect(x, y, dwidth, dheight);
+ var unit = '';
+ if (!nopercent) {
+ unit = '%';
+ }
+ var text = r.text(x + columnWidth / 2, y - 10, value + unit);
+ rect.attr({fill: '#3fa9f5', 'stroke': '#3fa9f5', 'opacity': 0.7});
+ attr['font-size'] = 14;
+ text.attr(attr);
+ rect.indexI = i;
+ if (!nobase) {
+ // hover效果
+ rect.hover(function () {
+ this.attr({'opacity': 1.0});
+ var maxLen = labels.length - 1;
+ var x = globalBasePlot[this.indexI][0] + columnWidth;
+ //处理最右侧的元素
+ x = this.indexI === maxLen ? x - columnWidth * 2 - 7 : x;
+ var y = globalBasePlot[this.indexI][1] - 10;
+ this.tooltipImage = r.image('http://img01.taobaocdn.com/tps/i1/T15H_GXiJjXXc.LCDe-40-21.gif', x, y, 40, 21);
+ if (this.indexI === maxLen) {
+ this.tooltipImage.rotate(180);
+ x -= maxLen;
+ }
+ this.tooltipText = r.text(x + 23, y + 11, globalBase[this.indexI] + '%').attr({'fill': '#fff', 'font-size': 12});
+ }, function () {
+ this.attr({'opacity': 0.7});
+ if (this.tooltipImage) {
+ this.tooltipImage.remove();
+ }
+ if (this.tooltipText) {
+ this.tooltipText.remove();
+ }
+ });
+ // 计算全网基数坐标
+ globalBasePlot[i] = [x, columnHeight * 6 / 7
+ - (Math.ceil((columnHeight - 18) * (globalBase[i] / globalMax)) * 0.75) + topGutter - 6,
+ x + columnWidth , columnHeight * 6 / 7
+ - (Math.ceil((columnHeight - 18) * (globalBase[i] / globalMax)) * 0.75) + topGutter - 6];
+ }
+ }
+ }
+
+ if (!nobase && is2dFlag === 0) {
+ // 画全网基数线,单维时
+ var globalBasePath = [];
+ for (var i = 1; i < globalBasePlot.length; i++) {
+ globalBasePath.push('M' + globalBasePlot[i - 1].join(' ') + 'L' + globalBasePlot[i].join(' '));
+ }
+ r.path(globalBasePath.join(",")).attr({'stroke': '#999999'});
+ } else {
+ //多维时的全网基线之间不相连接,只画在柱状上
+ var globalBasePath = [];
+ for (var i = 1; i < globalBasePlot.length + 1; i++) {
+ globalBasePath.push('M' + globalBasePlot[i - 1].join(' '));
+ }
+ r.path(globalBasePath.join(",")).attr({'stroke': '#999999', 'stroke-dasharray': '-', 'stroke-width': 2});
+ }
+
+ };
+
+ return LevelChart;
+
+});
\ No newline at end of file
diff --git a/lib/charts/tifbarlevel.js b/lib/charts/tifbarlevel.js
new file mode 100644
index 00000000..87e4ea10
--- /dev/null
+++ b/lib/charts/tifbarlevel.js
@@ -0,0 +1,172 @@
+/**
+* TifBarLevel
+*/
+var TifBarLevel = function (config) {
+ // 4 params must need
+ var width = config.width || 320,
+ height = config.height || 190;
+ var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node
+ var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}];
+
+ // other optional params
+ var nullWidth = config.nullWidth || 2;
+ var nullColor = config.nullColor || 'gray';
+ var highlightColor = config.highlightColor || '#3391d4';
+ var clickHandle = config.clickHandle || function (d, i) {
+ //console.log(d.name);
+ };
+ var margin = config.margin || {
+ "top": 1,
+ "left": 1,
+ "bottom": 1,
+ "right": 1
+ };
+ var color = this.color = typeof data[0].color !== 'undefined' ?
+ data.map(function (d) { return d.color; }) :
+ d3.range(data.length).map(function () { return "#3fa9f5"; });
+ var wordSpacing = config.wordSpacing || 10; // distance between word and bar
+
+ var titleDefaultStyle = config.titleDefaultStyle || {
+ 'cursor': 'pointer',
+ 'border': 'solid 1px #fff',
+ 'display': 'inline-block',
+ 'border-radius': 3,
+ "font-family": "微软雅黑",
+ "font-size": "12px",
+ 'padding': 1
+ };
+
+ var w = width - margin.left - margin.right;
+ var h = height - margin.top - margin.bottom;
+ var barH = h / data.length;
+
+ // data process;
+ var notNullCount = 0;
+ data.forEach(function (d) {
+ d.isNull = (d.value === null);
+ d.v = (d.isNull ? 0 : d.value);
+ if (!d.isNull) {
+ notNullCount += 1;
+ }
+ });
+ var all = data.every(function (d) {
+ return !d.isNull;
+ });
+ var sum = d3.sum(data, function (d) { return d.v; });
+ var max = d3.max(data, function (d) { return d.v; });
+ var ratioArr = this.ratioArr = data.map(function (d) {
+ if (d.isNull) {
+ d.ratio = 0;
+ } else {
+ d.ratio = sum === 0 ? 0 : (d.v / sum);
+ }
+ return d.ratio;
+ });
+ var wArr = this.wArr = data.map(function (d) {
+ return max === 0 ? nullWidth : (w - nullWidth) * d.value / max + nullWidth;
+ });
+
+ //draw bar
+ $(container).css("position", "relative");
+ var paper = this.paper = new Raphael(container, width, height);
+ var rects = this.rects = paper.set();
+ data.forEach(function (d, i) {
+ var rect = paper.rect(margin.left, margin.top + barH * i, wArr[i], barH)
+ .attr({
+ "r": 3,
+ "cursor": "pointer",
+ "fill": d.isNull ? nullColor : color[i],
+ "stroke-width": 1,
+ "stroke": "#fff"
+ });
+ rects.push(rect);
+ });
+
+ // draw words
+ var ratioWord = this.ratioWord = paper.set();
+ var titles = this.titles = [];
+ //var nameWord = this.nameWord = paper.set();
+ //var nameBackground = this.nameBackground = paper.set();
+ ratioArr.forEach(function (d, i) {
+ if (data[i].isNull) {
+ return;
+ }
+ var text = paper.text(
+ margin.left + wArr[i] + wordSpacing, //x
+ margin.top + barH * (i + 0.5), //y
+ Math.round(d * 1000) / 10 + "%" //text string
+ ).attr({
+ "text-anchor": "start",
+ "fill": color[i]
+ });
+ ratioWord.push(text);
+ });
+ ratioWord.attr({
+ "font-size": "14px"
+ });
+
+ data.forEach(function (d, i) {
+ var title = $('' + d.name + '
').css(titleDefaultStyle);
+ if (!all && !d.isNull) {
+ title.css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ title.appendTo($(container));
+ var tw = title.width();
+ var th = title.outerHeight();
+ title.css({
+ 'position': 'absolute',
+ 'left': margin.left - wordSpacing - tw,
+ 'top': margin.top + barH * (i + 0.5) - th / 2,
+ 'text-align': 'right'
+ });
+ titles.push(title);
+ });
+
+ //interaction
+ //hover
+ var getHoverIn = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ };
+ };
+ var getHoverOut = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px #fff',
+ 'background-color': '#fff'
+ });
+ }
+ };
+ };
+ //click
+ var getClick = function (i) {
+ var index = i;
+ return function () {
+ clickHandle(data[index], index);
+ };
+ };
+ data.forEach(function (d, i) {
+ var hoverIn = getHoverIn(i);
+ var hoverOut = getHoverOut(i);
+ var click = getClick(i);
+ rects[i].hover(hoverIn, hoverOut);
+ rects[i].click(click);
+ titles[i].on("mouseenter", hoverIn);
+ titles[i].on("mouseleave", hoverOut);
+ titles[i].on("click", click);
+ });
+
+};
\ No newline at end of file
diff --git a/lib/charts/tifbarlevel2.js b/lib/charts/tifbarlevel2.js
new file mode 100644
index 00000000..358f2ab9
--- /dev/null
+++ b/lib/charts/tifbarlevel2.js
@@ -0,0 +1,173 @@
+/**
+* TifBarLevel
+*/
+var TifBarLevel = function (config) {
+ // 4 params must need
+ var width = config.width || 320,
+ height = config.height || 190;
+ var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node
+ var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}];
+
+ // other optional params
+ var nullWidth = config.nullWidth || 2;
+ var nullColor = config.nullColor || 'gray';
+ var highlightColor = config.highlightColor || '#3391d4';
+ var clickHandle = config.clickHandle || function (d, i) {
+ //console.log(d.name);
+ };
+ var margin = config.margin || {
+ "top": 1,
+ "left": 1,
+ "bottom": 1,
+ "right": 1
+ };
+ var color = this.color = typeof data[0].color !== 'undefined' ?
+ data.map(function (d) { return d.color; }) :
+ d3.range(data.length).map(function () { return "#3fa9f5"; });
+ var wordSpacing = config.wordSpacing || 10; // distance between word and bar
+
+ var titleDefaultStyle = config.titleDefaultStyle || {
+ 'cursor': 'pointer',
+ 'border': 'solid 1px #fff',
+ 'display': 'inline-block',
+ 'border-radius': 3,
+ "font-family": "微软雅黑",
+ "font-size": "12px",
+ 'padding': 1
+ };
+
+ var w = height - margin.left - margin.right;
+ var h = width - margin.top - margin.bottom;
+ var barH = h / data.length;
+
+ // data process;
+ var notNullCount = 0;
+ data.forEach(function (d) {
+ d.isNull = (d.value === null);
+ d.v = (d.isNull ? 0 : d.value);
+ if (!d.isNull) {
+ notNullCount += 1;
+ }
+ });
+ var all = data.every(function (d) {
+ return !d.isNull;
+ });
+ var sum = d3.sum(data, function (d) { return d.v; });
+ var max = d3.max(data, function (d) { return d.v; });
+ var ratioArr = this.ratioArr = data.map(function (d) {
+ if (d.isNull) {
+ d.ratio = 0;
+ } else {
+ d.ratio = sum === 0 ? 0 : (d.v / sum);
+ }
+ return d.ratio;
+ });
+ var wArr = this.wArr = data.map(function (d) {
+ return max === 0 ? nullWidth : (w - nullWidth) * d.value / max + nullWidth;
+ });
+
+ //draw bar
+ $(container).css("position", "relative");
+ var paper = this.paper = new Raphael(container, width, height);
+ var rects = this.rects = paper.set();
+ data.forEach(function (d, i) {
+ var rect = paper.rect(margin.top + barH * i, margin.top + w - wArr[i], barH, wArr[i] + h / 3)
+ .attr({
+ "r": 3,
+ "cursor": "pointer",
+ "fill": d.isNull ? nullColor : color[i],
+ "stroke-width": 1,
+ "stroke": "#fff"
+ });
+ console.log(margin.left);
+ rects.push(rect);
+ });
+
+ // draw words
+ var ratioWord = this.ratioWord = paper.set();
+ var titles = this.titles = [];
+ //var nameWord = this.nameWord = paper.set();
+ //var nameBackground = this.nameBackground = paper.set();
+ ratioArr.forEach(function (d, i) {
+ if (data[i].isNull) {
+ return;
+ }
+ var text = paper.text(
+ margin.top + barH * (i + 0.5) - wordSpacing * 1.5,
+ margin.top + w - wArr[i] - 15,
+ Math.round(d * 1000) / 10 + "%" //text string
+ ).attr({
+ "text-anchor": "start",
+ "fill": color[i]
+ });
+ ratioWord.push(text);
+ });
+ ratioWord.attr({
+ "font-size": "14px"
+ });
+
+ data.forEach(function (d, i) {
+ var title = $('' + d.name + '
').css(titleDefaultStyle);
+ if (!all && !d.isNull) {
+ title.css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ title.appendTo($(container));
+ var tw = title.width();
+ var th = title.outerHeight();
+ title.css({
+ 'position': 'absolute',
+ 'left': margin.top + barH * (i + 0.5) - th,
+ 'top': h - margin.top * 1.3,
+ 'text-align': 'right'
+ });
+ titles.push(title);
+ });
+
+ //interaction
+ //hover
+ var getHoverIn = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ };
+ };
+ var getHoverOut = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px #fff',
+ 'background-color': '#fff'
+ });
+ }
+ };
+ };
+ //click
+ var getClick = function (i) {
+ var index = i;
+ return function () {
+ clickHandle(data[index], index);
+ };
+ };
+ data.forEach(function (d, i) {
+ var hoverIn = getHoverIn(i);
+ var hoverOut = getHoverOut(i);
+ var click = getClick(i);
+ rects[i].hover(hoverIn, hoverOut);
+ rects[i].click(click);
+ titles[i].on("mouseenter", hoverIn);
+ titles[i].on("mouseleave", hoverOut);
+ titles[i].on("click", click);
+ });
+
+};
\ No newline at end of file
diff --git a/lib/charts/tifgender.js b/lib/charts/tifgender.js
new file mode 100644
index 00000000..aea8b5ef
--- /dev/null
+++ b/lib/charts/tifgender.js
@@ -0,0 +1,170 @@
+var TifGender = function (config) {
+ // 4 params must need
+ var width = config.width || 320,
+ height = config.height || 190;
+ var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node
+ $(container).css({'position': 'relative'});
+ var data = config.data || [{name: "男", value: 40}, {name: "女", value: 60}];
+
+ // other optional params
+ var margin = config.margin || {
+ "top": 1,
+ "left": 1,
+ "bottom": 1,
+ "right": 1
+ };
+ var highlightColor = config.highlightColor || '#3391d4';
+ var clickHandle = config.clickHandle || function (d, i) {
+ //console.log(d.name);
+ };
+ var color = this.color = typeof data[0].color !== 'undefined' ?
+ data.map(function (d) { return d.color; }) :
+ ["#3fa9f5", "#ff88a2", "#909dd0", "#909dd0", "#ff88a2", "#ff88a2", "#ff88a2"];
+
+ var rotateAngle = Math.PI / 6;
+
+ var w = width - margin.left - margin.right;
+ var h = height - margin.top - margin.bottom;
+ var r = Math.min(w, h) / 2;
+ var center = [margin.left + w / 2, margin.top + h / 2];
+
+ // data process;
+ data.forEach(function (d) {
+ d.isNull = d.value === null;
+ d.v = d.isNull ? 0 : d.value;
+ });
+ var all = data.every(function (d) {
+ return !d.isNull;
+ });
+ var sum = data[0].v + data[1].v;
+ var ratio1 = all ?
+ (sum === 0 ? 0.5 : data[0].v / sum) :
+ (data[0].isNull ? 0 : 1);
+ ratio1 = Math.max(0.000001, Math.min(0.999999, ratio1));//between 0.001 and 0.999 to ensure arc would always be drawn.
+ var angle1 = Math.PI * 2 * ratio1;
+ var angleStart = -angle1 / 2 - rotateAngle;
+ var angleEnd = angleStart + angle1;
+ var p1 = [center[0] + r * Math.cos(angleStart), center[1] + r * Math.sin(angleStart)];
+ var p2 = [center[0] + r * Math.cos(angleEnd), center[1] + r * Math.sin(angleEnd)];
+
+ //draw circle
+ var paper = this.paper = new Raphael(container, width, height);
+ var pie1 = this.pie1 = paper.path(
+ "M" + center[0] + "," + center[1] +
+ "L" + p1[0] + "," + p1[1] +
+ "A" + r + "," + r + " 0, " + (ratio1 > 0.5 ? "1" : "0") + "," + "1 " +
+ p2[0] + "," + p2[1] + "Z"
+ );
+ var pie2 = this.pie2 = paper.path(
+ "M" + center[0] + "," + center[1] +
+ "L" + p1[0] + "," + p1[1] +
+ "A" + r + "," + r + " 0, " + (ratio1 > 0.5 ? "0" : "1") + "," + "0 " +
+ p2[0] + "," + p2[1] + "Z"
+ );
+ pie1.attr({
+ 'cursor': 'pointer',
+ 'stroke-width': 2,
+ 'stroke': '#fff',
+ 'fill': color[0]
+ });
+ pie2.attr({
+ 'cursor': 'pointer',
+ 'stroke-width': 2,
+ 'stroke': '#fff',
+ 'fill': color[1]
+ });
+ var pies = this.pies = [pie1, pie2];
+
+ // draw ratios
+ var trans = [
+ [r * 1.3 * Math.cos(rotateAngle) + 0.2 * r, -r * 1.3 * Math.sin(rotateAngle) - 0.1 * r],
+ [-r * 1.3 * Math.cos(rotateAngle) - 0.8 * r, r * 1.3 * Math.sin(rotateAngle) - 0.2 * r]
+ ];
+ var ratios = this.ratios = [paper.set(), paper.set()];
+ ratios.forEach(function (d, i) {
+ if (data[i].isNull) {
+ return;
+ }
+ var v = i === 0 ? Math.round(ratio1 * 100) : 100 - Math.round(ratio1 * 100);
+ var numberCount = v < 10 ? 1 : (v === 100 ? 3 : 2);
+ d.push(
+ paper.text(center[0], center[1] + 10, v).attr({
+ "fill": color[i],
+ "font-size": "26px",
+ "text-anchor": "start"
+ }),
+ paper.text(center[0] + 15 * numberCount, center[1] + 13, "%").attr({
+ "fill": color[i],
+ "font-size": "16px",
+ "text-anchor": "start"
+ })
+ );
+ d.attr({
+ "transform": "translate(t" + trans[i][0] + "," + trans[i][1] + ")"
+ });
+ });
+
+ // draw title
+ var titles = this.titles = [];
+ data.forEach(function (d, i) {
+ var t = titles[i] = $("" + data[i].name + "
").css({
+ 'position': 'absolute',
+ 'left': center[0] + trans[i][0],
+ 'top': center[1] + trans[i][1] - 27,
+ 'border-radius': 3,
+ 'padding': 1,
+ 'cursor': 'pointer',
+ 'font-size': 14
+ }).appendTo($(container));
+ if (!all && !data[i].isNull) {
+ t.css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ });
+
+ //interaction
+ //hover
+ var getHoverIn = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ };
+ };
+ var getHoverOut = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px #fff',
+ 'background-color': '#fff'
+ });
+ }
+ };
+ };
+ //click
+ var getClick = function (i) {
+ var index = i;
+ return function () {
+ clickHandle(data[index], index);
+ };
+ };
+ data.forEach(function (d, i) {
+ var hoverIn = getHoverIn(i);
+ var hoverOut = getHoverOut(i);
+ var click = getClick(i);
+ pies[i].hover(hoverIn, hoverOut);
+ pies[i].click(click);
+ titles[i].on("mouseenter", hoverIn);
+ titles[i].on("mouseleave", hoverOut);
+ titles[i].on("click", click);
+ });
+};
\ No newline at end of file
diff --git a/lib/charts/tiflevel.js b/lib/charts/tiflevel.js
new file mode 100644
index 00000000..1f2d877f
--- /dev/null
+++ b/lib/charts/tiflevel.js
@@ -0,0 +1,263 @@
+/**
+* TifLevel for DataV.js
+*
+*/
+
+var TifBuyerLevel = function (config) {
+ this.config = config;
+ // 4 params must need
+ var width = config.width || 320,
+ height = config.height || 190;
+ var container = typeof config.container === "string" ? document.getElementById(config.container) : config.container; // id or dom node
+ var data = config.data || [{name: "18", value: 40}, {name: "25", value: 60}];
+
+ // other optional params
+ var margin = config.margin || {
+ "top": 1,
+ "left": 1,
+ "bottom": 1,
+ "right": 1
+ };
+ var highlightColor = config.highlightColor || '#3391d4';
+ var clickHandle = config.clickHandle || function (d, i) {
+ //console.log(d.name);
+ };
+ var color = this.color = typeof data[0].color !== 'undefined' ?
+ data.map(function (d) { return d.color; }) :
+ d3.range(data.length).map(function () { return "#3fa9f5"; });
+ var levelSpacing = config.levelSpacing || 20; // distance between first level words and bar, distance between first level word and second level word
+ var wordBox = config.wordBox || {w: 50, h: 30}; // bar word (includs name and ratio) box's width
+ var level2Shift = config.level2Shift || -10;
+ var showBox = config.showBox || false;
+
+ var titleDefaultStyle = config.titleDefaultStyle || {
+ 'cursor': 'pointer',
+ 'border': 'solid 1px #fff',
+ 'display': 'inline-block',
+ 'border-radius': 3,
+ 'padding': 1
+ };
+
+ var wordDefaultStyle = config.wordDefaultStyle || {
+ "font-size": "12px",
+ "font-family": "微软雅黑"
+ };
+
+ var w = width - margin.left - margin.right;
+ var h = height - margin.top - margin.bottom;
+
+ // data process;
+ var notNullCount = 0;
+ data.forEach(function (d) {
+ d.isNull = (d.value === null);
+ d.v = (d.isNull ? 0 : d.value);
+ if (!d.isNull) {
+ notNullCount += 1;
+ }
+ });
+ var all = data.every(function (d) {
+ return !d.isNull;
+ });
+ var sum = d3.sum(data, function (d) { return d.v; });
+ var ratioArr = this.ratioArr = data.map(function (d) {
+ if (d.isNull) {
+ d.ratio = 0;
+ } else {
+ d.ratio = sum === 0 ? (1 / notNullCount) : (d.v / sum);
+ }
+ return d.ratio;
+ });
+ var wArr = this.wArr = data.map(function (d) {
+ return (w - data.length + 1) * d.ratio;
+ });
+ var s = margin.left;
+ var leftArr = this.leftArr = wArr.map(function (d, i) {
+ s += wArr[i] + 1; // 1 is spacing between different bars;
+ return s - wArr[i] - 1;
+ });
+
+ //compute linkLine and word box left position
+ var shakeLevel = function (boundL, boundR, initArr, boxWidth) {
+ var wordBoxLeft = [];
+ var right = boundL;
+ var left = boundR;
+ //left to right
+ initArr.forEach(function (d, i) {
+ if (d >= right) {
+ wordBoxLeft[i] = d;
+ } else {
+ wordBoxLeft[i] = right;
+ }
+ right = wordBoxLeft[i] + boxWidth;
+ });
+ if (right < boundR) {
+ return wordBoxLeft;
+ }
+ wordBoxLeft.reverse();
+ wordBoxLeft.forEach(function (d, i) {
+ if (d + boxWidth < left) {
+ wordBoxLeft[i] = d;
+ } else {
+ wordBoxLeft[i] = left - boxWidth;
+ }
+ left = wordBoxLeft[i];
+ });
+
+ wordBoxLeft.reverse();
+ return wordBoxLeft;
+ };
+ var oddBoxLeft = leftArr.filter(function (d, i) {
+ return i % 2 === 0;
+ });
+ var evenBoxLeft = leftArr.filter(function (d, i) {
+ return i % 2 === 1;
+ });
+ var linePos = [];
+ var boxPos = [];
+ // get first level box left position
+ oddBoxLeft = shakeLevel(margin.left, margin.left + w, oddBoxLeft, wordBox.w);
+ // get second level line position
+ evenBoxLeft.forEach(function (d, i) {
+ // last item and total data number is even;
+ if (i === evenBoxLeft.length - 1 && (leftArr.length % 2 === 0)) {
+ if (oddBoxLeft[i] + wordBox.w < d) {
+ } else {
+ evenBoxLeft[i] = oddBoxLeft[i] + wordBox.w + level2Shift;
+ }
+ return;
+ }
+ if (oddBoxLeft[i] + wordBox.w < d && oddBoxLeft[i + 1] >= d + wordBox.w) {
+ } else {
+ evenBoxLeft[i] = (oddBoxLeft[i] + wordBox.w + oddBoxLeft[i + 1]) / 2 + level2Shift;
+ }
+ });
+ // get linePos
+ oddBoxLeft.forEach(function (d, i) {
+ linePos.push(d);
+ if (typeof evenBoxLeft[i] !== 'undefined') {
+ linePos.push(evenBoxLeft[i]);
+ }
+ });
+ // get second level box left position
+ evenBoxLeft = shakeLevel(margin.left, margin.left + w, evenBoxLeft, wordBox.w);
+ // get boxPos
+ oddBoxLeft.forEach(function (d, i) {
+ boxPos.push(d);
+ if (typeof evenBoxLeft[i] !== 'undefined') {
+ boxPos.push(evenBoxLeft[i]);
+ }
+ });
+
+ //draw bar
+ var paper = this.paper = new Raphael(container, width, height);
+ var rects = this.rects = paper.set();
+ data.forEach(function (d, i) {
+ var rect = paper.rect(leftArr[i], margin.top, wArr[i], h)
+ .attr({
+ "r": 3,
+ "fill": color[i],
+ 'cursor': 'pointer',
+ "stroke": "none"
+ });
+ rects.push(rect);
+ });
+
+ //draw test box and line
+ //line
+ var linkLines = this.linkLines = paper.set();
+ var words = this.words = [];
+ linePos.forEach(function (d, i) {
+ var path = "M" + leftArr[i] + "," + (margin.top + h) +
+ " C" + leftArr[i] + "," + (margin.top + h + levelSpacing / 2) +
+ " " + d + "," + (margin.top + h + levelSpacing / 2) +
+ " " + d + "," + (margin.top + h + levelSpacing);
+ if (i % 2 === 1) {
+ path += "v" + (wordBox.h + levelSpacing / 2);
+ }
+ var line = paper.path(path).attr({
+ "stroke-dasharray": ". "
+ });
+ linkLines.push(line);
+ });
+ // draw box
+ if (showBox) {
+ boxPos.forEach(function (d, i) {
+ var box = paper.rect(
+ d, //x
+ margin.top + h + levelSpacing + (i % 2 === 0 ? 0 : wordBox.h + levelSpacing / 2), //y
+ wordBox.w, //w
+ wordBox.h //h
+ );
+ });
+ }
+
+ var titles = [];
+ $(container).css('position', 'relative');
+ data.forEach(function (d, i) {
+ var wordSet = $("");
+ words.push(wordSet);
+ var title = $('' + d.name + '
').css(titleDefaultStyle);
+ if (!all && !data[i].isNull) {
+ title.css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ titles.push(title);
+ wordSet.append(title);
+ if (!d.isNull) {
+ var value = $('' + (Math.round(ratioArr[i] * 1000) / 10 + "%") + '
').css("color", color[i]);
+ wordSet.append(value);
+ }
+ wordSet.css(wordDefaultStyle).css({
+ 'position': 'absolute',
+ 'left': boxPos[i],
+ 'top': margin.top + h + levelSpacing + (i % 2 === 0 ? 0 : wordBox.h + levelSpacing / 2)
+ });
+ $(container).append(wordSet);
+ });
+
+ //interaction
+ //hover
+ var getHoverIn = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px black',
+ 'background-color': highlightColor
+ });
+ }
+ };
+ };
+ var getHoverOut = function (i) {
+ var index = i;
+ var needChange = !(!all && !data[i].isNull);
+ return function () {
+ if (needChange) {
+ titles[index].css({
+ 'border': 'solid 1px #fff',
+ 'background-color': '#fff'
+ });
+ }
+ };
+ };
+ //click
+ var getClick = function (i) {
+ var index = i;
+ return function () {
+ clickHandle(data[index], index);
+ };
+ };
+ data.forEach(function (d, i) {
+ var hoverIn = getHoverIn(i);
+ var hoverOut = getHoverOut(i);
+ var click = getClick(i);
+ rects[i].hover(hoverIn, hoverOut);
+ rects[i].click(click);
+ titles[i].on("mouseenter", hoverIn);
+ titles[i].on("mouseleave", hoverOut);
+ titles[i].on("click", click);
+ });
+};