Warning, file /office/tellico/xslt/report-templates/jquery.flot.pie.js was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 Flot plugin for rendering pie charts. The plugin assumes the data is 0003 coming is as a single data value for each series, and each of those 0004 values is a positive value or zero (negative numbers don't make 0005 any sense and will cause strange effects). The data values do 0006 NOT need to be passed in as percentage values because it 0007 internally calculates the total and percentages. 0008 0009 * Created by Brian Medendorp, June 2009 0010 * Updated by Anthony Aragues, July 2009 http://suddenDevelopment.com 0011 0012 Available options are: 0013 series: { 0014 pie: { 0015 show: true/false 0016 radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' 0017 startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result 0018 tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) 0019 offset: { 0020 top: integer value to move the pie up or down 0021 left: integer value to move the pie left or right, or 'auto' 0022 }, 0023 stroke: { 0024 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF') 0025 width: integer pixel width of the stroke 0026 }, 0027 label: { 0028 show: true/false, or 'auto' 0029 formatter: a user-defined function that modifies the text/style of the label text 0030 radius: 0-1 for percentage of fullsize, or a specified pixel length 0031 background: { 0032 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000') 0033 opacity: 0-1 0034 }, 0035 threshold: 0-1 for the percentage value at which to hide labels (if they're too small) 0036 }, 0037 combine: { 0038 threshold: 0-1 for the percentage value at which to combine slices (if they're too small) 0039 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined 0040 label: any text value of what the combined slice should be labeled 0041 } 0042 } 0043 } 0044 0045 Border Layout for labels: 0046 options.series.pie.label.show: 'border' 0047 0048 Donut hole: 0049 options.series.pie.innerRadius: > 0 0050 0051 More detail and specific examples can be found in the included HTML 0052 file. 0053 0054 */ 0055 0056 (function ($) 0057 { 0058 function init(plot) // this is the "body" of the plugin 0059 { 0060 var canvas = null, 0061 ctx = null, 0062 target = null, 0063 eventHolder = null, 0064 options = null, 0065 maxRadius = null, 0066 centerLeft = null, 0067 centerTop = null, 0068 total = 0, 0069 redraw = true, 0070 redrawAttempts = 10, 0071 shrink = 0.95, 0072 legendWidth = 0, 0073 processed = false, 0074 raw = false, 0075 0076 // interactive variables 0077 lastMousePos = { pageX: null, pageY: null }, 0078 highlights = []; 0079 0080 // add hook to determine if pie plugin in enabled, and then perform necessary operations 0081 plot.hooks.processOptions.push(checkPieEnabled); 0082 plot.hooks.bindEvents.push(bindEvents); 0083 0084 // check to see if the pie plugin is enabled 0085 function checkPieEnabled(plot, options) 0086 { 0087 if (options.series.pie.show) 0088 { 0089 //disable grid 0090 options.grid.show = false; 0091 0092 // set labels.show 0093 if (options.series.pie.label.show=='auto') 0094 if (options.legend.show) 0095 options.series.pie.label.show = false; 0096 else 0097 options.series.pie.label.show = true; 0098 0099 // set radius 0100 if (options.series.pie.radius=='auto') 0101 if (options.series.pie.label.show) 0102 options.series.pie.radius = 3/4; 0103 else 0104 options.series.pie.radius = 1; 0105 0106 // ensure sane tilt 0107 if (options.series.pie.tilt>1) 0108 options.series.pie.tilt=1; 0109 if (options.series.pie.tilt<0) 0110 options.series.pie.tilt=0; 0111 0112 // add processData hook to do transformations on the data 0113 plot.hooks.processDatapoints.push(processDatapoints); 0114 plot.hooks.drawOverlay.push(drawOverlay); 0115 0116 // add draw hook 0117 plot.hooks.draw.push(draw); 0118 } 0119 } 0120 0121 // bind hoverable events 0122 function bindEvents(plot, newEventHolder) { 0123 eventHolder = newEventHolder; 0124 var options = plot.getOptions(); 0125 0126 if (options.series.pie.show && 0127 options.grid.hoverable) 0128 eventHolder.unbind('mousemove').mousemove(onMouseMove); 0129 } 0130 0131 // debugging function that prints out an object 0132 function alertObject(obj) 0133 { 0134 var msg = ''; 0135 function traverse(obj, depth) 0136 { 0137 if (!depth) 0138 depth = 0; 0139 for(var i in obj) 0140 { 0141 for (var j=0; j<depth; j++) 0142 msg += '\t'; 0143 0144 if( typeof obj[i] == "object") 0145 { // its an object 0146 msg += ''+i+':\n'; 0147 traverse(obj[i], depth+1); 0148 } 0149 else 0150 { // its a value 0151 msg += ''+i+': '+obj[i]+'\n'; 0152 } 0153 } 0154 } 0155 traverse(obj); 0156 alert(msg); 0157 } 0158 0159 function calcTotal(data) 0160 { 0161 for (var i in data) 0162 { 0163 var item = parseFloat(data[i].data[0][1]); 0164 if (item) 0165 total += item; 0166 } 0167 } 0168 0169 function processDatapoints(plot, series, data, datapoints) 0170 { 0171 if (!processed) 0172 { 0173 processed = true; 0174 0175 canvas = plot.getCanvas(); 0176 target = $(canvas).parent(); 0177 options = plot.getOptions(); 0178 0179 plot.setData(combine(plot.getData())); 0180 } 0181 } 0182 0183 function setupPie() 0184 { 0185 legendWidth = target.children().filter('.legend').children().width(); 0186 0187 // calculate maximum radius and center point 0188 maxRadius = Math.min(canvas.width,canvas.height)/2; 0189 centerTop = (canvas.height/2)+options.series.pie.offset.top; 0190 centerLeft = (canvas.width/2); 0191 0192 if (options.series.pie.offset.left=='auto') 0193 if (options.legend.position.match('w')) 0194 centerLeft += legendWidth/2; 0195 else 0196 centerLeft -= legendWidth/2; 0197 else 0198 centerLeft += options.series.pie.offset.left; 0199 0200 if (centerLeft<maxRadius) 0201 centerLeft = maxRadius; 0202 else if (centerLeft>canvas.width-maxRadius) 0203 centerLeft = canvas.width-maxRadius; 0204 } 0205 0206 function fixData(data) 0207 { 0208 for (var i in data) 0209 { 0210 if (typeof(data[i].data)=='number') 0211 data[i].data = [[1,data[i].data]]; 0212 else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined') 0213 { 0214 if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined') 0215 data[i].label = data[i].data.label; // fix weirdness coming from flot 0216 data[i].data = [[1,0]]; 0217 0218 } 0219 } 0220 return data; 0221 } 0222 0223 function combine(data) 0224 { 0225 data = fixData(data); 0226 calcTotal(data); 0227 var combined = 0; 0228 var numCombined = 0; 0229 var color = options.series.pie.combine.color; 0230 0231 var newdata = []; 0232 for (var i in data) 0233 { 0234 // make sure its a number 0235 data[i].data[0][1] = parseFloat(data[i].data[0][1]); 0236 if (!data[i].data[0][1]) 0237 data[i].data[0][1] = 0; 0238 0239 if (data[i].data[0][1]/total<=options.series.pie.combine.threshold) 0240 { 0241 combined += data[i].data[0][1]; 0242 numCombined++; 0243 if (!color) 0244 color = data[i].color; 0245 } 0246 else 0247 { 0248 newdata.push({ 0249 data: [[1,data[i].data[0][1]]], 0250 color: data[i].color, 0251 label: data[i].label, 0252 angle: (data[i].data[0][1]*(Math.PI*2))/total, 0253 percent: (data[i].data[0][1]/total*100) 0254 }); 0255 } 0256 } 0257 if (numCombined>0) 0258 newdata.push({ 0259 data: [[1,combined]], 0260 color: color, 0261 label: options.series.pie.combine.label, 0262 angle: (combined*(Math.PI*2))/total, 0263 percent: (combined/total*100) 0264 }); 0265 return newdata; 0266 } 0267 0268 function draw(plot, newCtx) 0269 { 0270 if (!target) return; // if no series were passed 0271 0272 ctx = newCtx; 0273 0274 setupPie(); 0275 var slices = plot.getData(); 0276 0277 var attempts = 0; 0278 while (redraw && attempts<redrawAttempts) 0279 { 0280 redraw = false; 0281 if (attempts>0) 0282 maxRadius *= shrink; 0283 attempts += 1; 0284 clear(); 0285 if (options.series.pie.tilt<=0.8) 0286 drawShadow(); 0287 drawPie(); 0288 } 0289 if (attempts >= redrawAttempts) { 0290 clear(); 0291 target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>'); 0292 } 0293 0294 if ( plot.setSeries && plot.insertLegend ) 0295 { 0296 plot.setSeries(slices); 0297 plot.insertLegend(); 0298 } 0299 0300 // we're actually done at this point, just defining internal functions at this point 0301 0302 function clear() 0303 { 0304 ctx.clearRect(0,0,canvas.width,canvas.height); 0305 target.children().filter('.pieLabel, .pieLabelBackground').remove(); 0306 } 0307 0308 function drawShadow() 0309 { 0310 var shadowLeft = 5; 0311 var shadowTop = 15; 0312 var edge = 10; 0313 var alpha = 0.02; 0314 0315 // set radius 0316 if (options.series.pie.radius>1) 0317 var radius = options.series.pie.radius; 0318 else 0319 var radius = maxRadius * options.series.pie.radius; 0320 0321 if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge) 0322 return; // shadow would be outside canvas, so don't draw it 0323 0324 ctx.save(); 0325 ctx.translate(shadowLeft,shadowTop); 0326 ctx.globalAlpha = alpha; 0327 ctx.fillStyle = '#000'; 0328 0329 // center and rotate to starting position 0330 ctx.translate(centerLeft,centerTop); 0331 ctx.scale(1, options.series.pie.tilt); 0332 0333 //radius -= edge; 0334 for (var i=1; i<=edge; i++) 0335 { 0336 ctx.beginPath(); 0337 ctx.arc(0,0,radius,0,Math.PI*2,false); 0338 ctx.fill(); 0339 radius -= i; 0340 } 0341 0342 ctx.restore(); 0343 } 0344 0345 function drawPie() 0346 { 0347 startAngle = Math.PI*options.series.pie.startAngle; 0348 0349 // set radius 0350 if (options.series.pie.radius>1) 0351 var radius = options.series.pie.radius; 0352 else 0353 var radius = maxRadius * options.series.pie.radius; 0354 0355 // center and rotate to starting position 0356 ctx.save(); 0357 ctx.translate(centerLeft,centerTop); 0358 ctx.scale(1, options.series.pie.tilt); 0359 //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera 0360 0361 // draw slices 0362 ctx.save(); 0363 var currentAngle = startAngle; 0364 for (var i in slices) { 0365 slices[i].startAngle = currentAngle; 0366 drawSlice(slices[i].angle, slices[i].color, true); 0367 } 0368 ctx.restore(); 0369 0370 // draw slice outlines 0371 ctx.save(); 0372 ctx.lineWidth = options.series.pie.stroke.width; 0373 currentAngle = startAngle; 0374 for (var i in slices) 0375 drawSlice(slices[i].angle, options.series.pie.stroke.color, false); 0376 ctx.restore(); 0377 0378 // draw labels 0379 if (options.series.pie.label.show) 0380 drawLabels(); 0381 0382 // restore to original state 0383 ctx.restore(); 0384 0385 function drawSlice(angle, color, fill) 0386 { 0387 if (angle<=0) 0388 return; 0389 0390 if (fill){ ctx.fillStyle = color; } 0391 else{ ctx.strokeStyle = color; } 0392 0393 ctx.beginPath(); 0394 if (angle!=Math.PI*2) 0395 ctx.moveTo(0,0); // Center of the pie 0396 else if ($.browser.msie) 0397 angle -= 0.0001; 0398 //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera 0399 ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false); 0400 ctx.closePath(); 0401 //ctx.rotate(angle); // This doesn't work properly in Opera 0402 currentAngle += angle; 0403 0404 if (fill) 0405 ctx.fill(); 0406 else 0407 ctx.stroke(); 0408 } 0409 0410 function drawLabels() 0411 { 0412 var currentAngle = startAngle; 0413 0414 // set radius 0415 if (options.series.pie.label.radius>1) 0416 var radius = options.series.pie.label.radius; 0417 else 0418 var radius = maxRadius * options.series.pie.label.radius; 0419 0420 for(var i in slices) 0421 { 0422 if (slices[i].percent >= options.series.pie.label.threshold*100) 0423 drawLabel(slices[i], currentAngle, i); 0424 currentAngle += slices[i].angle; 0425 } 0426 0427 function drawLabel(slice, startAngle, index) 0428 { 0429 if (slice.data[0][1]==0) 0430 return; 0431 0432 // format label text 0433 var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; 0434 if (lf){ 0435 text = lf(slice.label, slice);} 0436 else{ 0437 text = slice.label;} 0438 if (plf){ 0439 text = plf(text, slice);} 0440 0441 var halfAngle = ((startAngle+slice.angle) + startAngle)/2; 0442 var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); 0443 var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; 0444 0445 var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>"; 0446 target.append(html); 0447 var label = target.children('#pieLabel'+index); 0448 var labelTop = (y - label.height()/2); 0449 var labelLeft = (x - label.width()/2); 0450 0451 /* Border layout for labels */ 0452 if(options.series.pie.label.show == 'border'){ 0453 ctx.restore(); 0454 ctx.beginPath(); 0455 ctx.strokeStyle = '#000000'; 0456 ctx.moveTo(x,y); 0457 if(x > (canvas.width/2)){ 0458 var endX = canvas.width - label.width(); 0459 var endY = y; 0460 labelLeft = endX; 0461 }else{ 0462 var endX = label.width(); 0463 var endY = y; 0464 labelLeft = 0; 0465 } 0466 ctx.lineTo(endX, endY); 0467 ctx.stroke(); 0468 ctx.save(); 0469 } 0470 0471 label.css('top', labelTop); 0472 label.css('left', labelLeft); 0473 0474 // check to make sure that the label is not outside the canvas 0475 if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0) 0476 redraw = true; 0477 0478 if (options.series.pie.label.background.opacity != 0) { 0479 // put in the transparent background separately to avoid blended labels and label boxes 0480 var c = options.series.pie.label.background.color; 0481 if (c == null) { 0482 c = slice.color; 0483 } 0484 var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;'; 0485 $('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity); 0486 } 0487 } // end individual label function 0488 } // end drawLabels function 0489 } // end drawPie function 0490 0491 if(options.series.pie.innerRadius > 0){ 0492 /* donut hole */ 0493 radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; 0494 ctx.translate(centerLeft,centerTop); 0495 ctx.scale(1, options.series.pie.tilt); 0496 ctx.save(); 0497 ctx.beginPath(); 0498 ctx.strokeStyle = options.series.pie.stroke.color; 0499 ctx.fillStyle = options.series.pie.stroke.color; 0500 ctx.arc(0,0,radius*options.series.pie.innerRadius,0,Math.PI*2,false); 0501 ctx.fill(); 0502 ctx.closePath(); 0503 } 0504 /* debug */ 0505 0506 /* end debug */ 0507 } // end draw function 0508 0509 function isPointInPoly(poly, pt){ 0510 for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) 0511 ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) 0512 && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) 0513 && (c = !c); 0514 return c; 0515 } 0516 // returns the data item the mouse is over, or null if none is found 0517 function findNearbyItem(mouseX, mouseY, seriesFilter) { 0518 var item = null, foundPoint = false, i, j, 0519 series = plot.getData(), 0520 radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; 0521 0522 for (var i = 0; i < series.length; ++i) { 0523 if (!seriesFilter(series[i])) 0524 continue; 0525 0526 var s = series[i]; 0527 0528 if (s.pie.show) { 0529 ctx.save(); 0530 ctx.beginPath(); 0531 ctx.translate(centerLeft,centerTop); 0532 ctx.scale(1, options.series.pie.tilt); 0533 ctx.moveTo(0,0); // Center of the pie 0534 ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false); 0535 ctx.closePath(); 0536 x = mouseX-centerLeft; 0537 y = mouseY-centerTop; 0538 if(ctx.isPointInPath){ 0539 if (ctx.isPointInPath(x, y)){ 0540 item = {datapoint: [series[i].percent, series[i].data], dataIndex: 0, series: series[i], seriesIndex: i}; 0541 } 0542 }else{ 0543 /* excanvas for IE doesn;t support isPointInPath, this is a workaround. */ 0544 p1X = (radius * Math.cos(s.startAngle)); 0545 p1Y = (radius * Math.sin(s.startAngle)); 0546 p2X = (radius * Math.cos(s.startAngle+(s.angle/4))); 0547 p2Y = (radius * Math.sin(s.startAngle+(s.angle/4))); 0548 p3X = (radius * Math.cos(s.startAngle+(s.angle/2))); 0549 p3Y = (radius * Math.sin(s.startAngle+(s.angle/2))); 0550 p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5))); 0551 p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5))); 0552 p5X = (radius * Math.cos(s.startAngle+s.angle)); 0553 p5Y = (radius * Math.sin(s.startAngle+s.angle)); 0554 arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]]; 0555 arrPoint = [x,y]; 0556 if(isPointInPoly(arrPoly, arrPoint)){ 0557 item = {datapoint: [series[i].percent, series[i].data], dataIndex: 0, series: series[i], seriesIndex: i}; 0558 } 0559 } 0560 ctx.restore(); 0561 } 0562 } 0563 0564 return item; 0565 } 0566 0567 function onMouseMove(e) { 0568 if (!processed) return; 0569 0570 lastMousePos.pageX = e.pageX; 0571 lastMousePos.pageY = e.pageY; 0572 0573 if (options.grid.hoverable){ 0574 triggerClickHoverEvent("plothover", lastMousePos, 0575 function (s) { return s["hoverable"] != false;}); 0576 } 0577 } 0578 0579 // trigger click or hover event (they send the same parameters 0580 // so we share their code) 0581 function triggerClickHoverEvent(eventname, event, seriesFilter) { 0582 var plotOffset = plot.getPlotOffset(), 0583 offset = eventHolder.offset(), 0584 pos = { pageX: event.pageX, pageY: event.pageY }, 0585 canvasX = event.pageX - offset.left - plotOffset.left, 0586 canvasY = event.pageY - offset.top - plotOffset.top; 0587 0588 var item = findNearbyItem(canvasX, canvasY, seriesFilter); 0589 if (item) { 0590 0591 // fill in mouse pos for any listeners out there 0592 item.pageX = event.pageX; 0593 item.pageY = event.pageY; 0594 0595 } 0596 0597 if (options.grid.autoHighlight) { 0598 // clear auto-highlights 0599 for (var i = 0; i < highlights.length; ++i) { 0600 var h = highlights[i]; 0601 if (h.auto == eventname && 0602 !(item && h.series == item.series)) 0603 unhighlight(h.series); 0604 } 0605 0606 if (item) 0607 highlight(item.series, eventname); 0608 } 0609 0610 target.trigger(eventname, [ pos, item ]); 0611 } 0612 0613 function highlight(s, auto) { 0614 if (typeof s == "number") 0615 s = series[s]; 0616 0617 var i = indexOfHighlight(s); 0618 if (i == -1) { 0619 highlights.push({ series: s, auto: auto }); 0620 0621 plot.triggerRedrawOverlay(); 0622 } 0623 else if (!auto) 0624 highlights[i].auto = false; 0625 } 0626 0627 function unhighlight(s) { 0628 if (s == null) { 0629 highlights = []; 0630 plot.triggerRedrawOverlay(); 0631 } 0632 0633 if (typeof s == "number") 0634 s = series[s]; 0635 0636 var i = indexOfHighlight(s); 0637 if (i != -1) { 0638 highlights.splice(i, 1); 0639 0640 plot.triggerRedrawOverlay(); 0641 } 0642 } 0643 0644 function indexOfHighlight(s) { 0645 for (var i = 0; i < highlights.length; ++i) { 0646 var h = highlights[i]; 0647 if (h.series == s) 0648 return i; 0649 } 0650 return -1; 0651 } 0652 0653 function drawOverlay(plot, octx) { 0654 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, 0655 i, hi; 0656 0657 octx.save(); 0658 octx.translate(centerLeft, centerTop); 0659 0660 for (i = 0; i < highlights.length; ++i) { 0661 drawHighlight(highlights[i].series); 0662 } 0663 0664 octx.restore(); 0665 0666 function drawHighlight(series) { 0667 if (series.angle < 0) return; 0668 0669 octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); 0670 0671 octx.beginPath(); 0672 if (series.angle!=Math.PI*2) 0673 octx.moveTo(0,0); // Center of the pie 0674 octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false); 0675 octx.closePath(); 0676 0677 octx.fill(); 0678 } 0679 0680 } 0681 0682 } // end init (plugin body) 0683 0684 function clamp(min, value, max) { 0685 if (value < min) 0686 return min; 0687 else if (value > max) 0688 return max; 0689 else 0690 return value; 0691 } 0692 0693 // color helpers, inspiration from the jquery color animation 0694 // plugin by John Resig 0695 function Color (r, g, b, a) { 0696 0697 var rgba = ['r','g','b','a']; 0698 var x = 4; //rgba.length 0699 0700 while (-1<--x) { 0701 this[rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0); 0702 } 0703 0704 this.toString = function() { 0705 if (this.a >= 1.0) { 0706 return "rgb("+[this.r,this.g,this.b].join(",")+")"; 0707 } else { 0708 return "rgba("+[this.r,this.g,this.b,this.a].join(",")+")"; 0709 } 0710 }; 0711 0712 this.scale = function(rf, gf, bf, af) { 0713 x = 4; //rgba.length 0714 while (-1<--x) { 0715 if (arguments[x] != null) 0716 this[rgba[x]] *= arguments[x]; 0717 } 0718 return this.normalize(); 0719 }; 0720 0721 this.adjust = function(rd, gd, bd, ad) { 0722 x = 4; //rgba.length 0723 while (-1<--x) { 0724 if (arguments[x] != null) 0725 this[rgba[x]] += arguments[x]; 0726 } 0727 return this.normalize(); 0728 }; 0729 0730 this.clone = function() { 0731 return new Color(this.r, this.b, this.g, this.a); 0732 }; 0733 0734 var limit = function(val,minVal,maxVal) { 0735 return Math.max(Math.min(val, maxVal), minVal); 0736 }; 0737 0738 this.normalize = function() { 0739 this.r = clamp(0, parseInt(this.r), 255); 0740 this.g = clamp(0, parseInt(this.g), 255); 0741 this.b = clamp(0, parseInt(this.b), 255); 0742 this.a = clamp(0, this.a, 1); 0743 return this; 0744 }; 0745 0746 this.normalize(); 0747 } 0748 0749 function parseColor(str) { 0750 var result; 0751 0752 // Look for rgb(num,num,num) 0753 if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) 0754 return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10)); 0755 0756 // Look for rgba(num,num,num,num) 0757 if (result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) 0758 return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4])); 0759 0760 // Look for rgb(num%,num%,num%) 0761 if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) 0762 return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55); 0763 0764 // Look for rgba(num%,num%,num%,num) 0765 if (result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) 0766 return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4])); 0767 0768 // Look for #a0b1c2 0769 if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) 0770 return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)); 0771 0772 // Look for #fff 0773 if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) 0774 return new Color(parseInt(result[1]+result[1], 16), parseInt(result[2]+result[2], 16), parseInt(result[3]+result[3], 16)); 0775 0776 // Otherwise, we're most likely dealing with a named color 0777 var name = $.trim(str).toLowerCase(); 0778 if (name == "transparent") 0779 return new Color(255, 255, 255, 0); 0780 else { 0781 result = lookupColors[name]; 0782 return new Color(result[0], result[1], result[2]); 0783 } 0784 } 0785 0786 // define pie specific options and their default values 0787 var options = { 0788 series: { 0789 pie: { 0790 show: false, 0791 radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) 0792 innerRadius:0, /* for donut */ 0793 startAngle: 0, 0794 tilt: 1, 0795 offset: { 0796 top: 0, 0797 left: 'auto' 0798 }, 0799 stroke: { 0800 color: '#FFF', 0801 width: 1 0802 }, 0803 label: { 0804 show: 'auto', 0805 formatter: function(label, slice){ 0806 return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>'; 0807 }, // formatter function 0808 radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) 0809 background: { 0810 color: null, 0811 opacity: 0 0812 }, 0813 threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) 0814 }, 0815 combine: { 0816 threshold: -1, // percentage at which to combine little slices into one larger slice 0817 color: null, // color to give the new slice (auto-generated if null) 0818 label: 'Other' // label to give the new slice 0819 }, 0820 highlight: { 0821 color: '#ffee77', // color to use for highlights 0822 opacity: 0.2 // opacity to use for highlights 0823 } 0824 } 0825 } 0826 }; 0827 0828 $.plot.plugins.push({ 0829 init: init, 0830 options: options, 0831 name: "pie", 0832 version: "0.4" 0833 }); 0834 })(jQuery);