PageSourceSearch

https://puma.io/js/visualize.jQuery.js

js puma.io collected 2026-09-24 08:42:28 UTC 14,880 bytes, 454 lines download raw bytes

1/**
2 * --------------------------------------------------------------------
3 * jQuery-Plugin "visualize"
4 * by Scott Jehl, [email protected]
5 * http://www.filamentgroup.com
6 * Copyright (c) 2009 Filament Group
7 * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
8 *
9 * --------------------------------------------------------------------
10 */
11(function($) {
12$.fn.visualize = function(options, container){
13	return $(this).each(function(){
14		//configuration
15		var o = $.extend({
16			type: 'bar', //also available: area, pie, line
17			width: $(this).width(), //height of canvas - defaults to table height
18			height: $(this).height(), //height of canvas - defaults to table height
19			appendTitle: true, //table caption text is added to chart
20			title: null, //grabs from table caption if null
21			appendKey: true, //color key is added to chart
22			colors: ['#be1e2d','#666699','#92d5ea','#ee8310','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'],
23			textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS
24			parseDirection: 'x', //which direction to parse the table data
25			pieMargin: 20, //pie charts only - spacing around pie
26			pieLabelPos: 'inside',
27			lineWeight: 4, //for line and area - stroke weight
28			barGroupMargin: 10,
29			barMargin: 1, //space around bars in bar chart (added to both sides of bar)
30			yLabelInterval: 30, //distance between y labels
31      topValue: false,
32      bottomValue: false,
33		},options);
34
35		//reset width, height to numbers
36		o.width = parseFloat(o.width);
37		o.height = parseFloat(o.height);
38
39
40		var self = $(this);
41
42		//function to scrape data from html table
43		function scrapeTable(){
44			var colors = o.colors;
45			var textColors = o.textColors;
46			var tableData = {
47				dataGroups: function(){
48					var dataGroups = [];
49					if(o.parseDirection == 'x'){
50						self.find('tr:gt(0)').each(function(i){
51							dataGroups[i] = {};
52							dataGroups[i].points = [];
53							dataGroups[i].color = colors[i];
54							if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
55							$(this).find('td').each(function(){
56								dataGroups[i].points.push( parseFloat($(this).text()) );
57							});
58						});
59					}
60					else {
61						var cols = self.find('tr:eq(1) td').size();
62						for(var i=0; i<cols; i++){
63							dataGroups[i] = {};
64							dataGroups[i].points = [];
65							dataGroups[i].color = colors[i];
66							if(textColors[i]){ dataGroups[i].textColor = textColors[i]; }
67							self.find('tr:gt(0)').each(function(){
68								dataGroups[i].points.push( $(this).find('td').eq(i).text()*1 );
69							});
70						};
71					}
72					return dataGroups;
73				},
74				allData: function(){
75					var allData = [];
76					$(this.dataGroups()).each(function(){
77						allData.push(this.points);
78					});
79					return allData;
80				},
81				dataSum: function(){
82					var dataSum = 0;
83					var allData = this.allData().join(',').split(',');
84					$(allData).each(function(){
85						dataSum += parseFloat(this);
86					});
87					return dataSum
88				},
89				topValue: function(){
90            if(o.topValue) return o.topValue;
91
92						var topValue = 0;
93						var allData = this.allData().join(',').split(',');
94						$(allData).each(function(){
95							if(parseFloat(this,10)>topValue) topValue = parseFloat(this);
96						});
97						return topValue;
98				},
99				bottomValue: function(){
100            if(o.bottomValue) return o.bottomValue;
101
102						var bottomValue = 0;
103						var allData = this.allData().join(',').split(',');
104						$(allData).each(function(){
105							if(this<bottomValue) bottomValue = parseFloat(this);
106						});
107						return bottomValue;
108				},
109				memberTotals: function(){
110					var memberTotals = [];
111					var dataGroups = this.dataGroups();
112					$(dataGroups).each(function(l){
113						var count = 0;
114						$(dataGroups[l].points).each(function(m){
115							count +=dataGroups[l].points[m];
116						});
117						memberTotals.push(count);
118					});
119					return memberTotals;
120				},
121				yTotals: function(){
122					var yTotals = [];
123					var dataGroups = this.dataGroups();
124					var loopLength = this.xLabels().length;
125					for(var i = 0; i<loopLength; i++){
126						yTotals[i] =[];
127						var thisTotal = 0;
128						$(dataGroups).each(function(l){
129							yTotals[i].push(this.points[i]);
130						});
131						yTotals[i].join(',').split(',');
132						$(yTotals[i]).each(function(){
133							thisTotal += parseFloat(this);
134						});
135						yTotals[i] = thisTotal;
136
137					}
138					return yTotals;
139				},
140				topYtotal: function(){
141					var topYtotal = 0;
142						var yTotals = this.yTotals().join(',').split(',');
143						$(yTotals).each(function(){
144							if(parseFloat(this,10)>topYtotal) topYtotal = parseFloat(this);
145						});
146						return topYtotal;
147				},
148				totalYRange: function(){
149					return this.topValue() - this.bottomValue();
150				},
151				xLabels: function(){
152					var xLabels = [];
153					if(o.parseDirection == 'x'){
154						self.find('tr:eq(0) th').each(function(){
155							xLabels.push($(this).html());
156						});
157					}
158					else {
159						self.find('tr:gt(0) th').each(function(){
160							xLabels.push($(this).html());
161						});
162					}
163					return xLabels;
164				},
165				yLabels: function(){
166					var yLabels = [];
167					yLabels.push(bottomValue);
168					var numLabels = Math.round(o.height / o.yLabelInterval);
169					var loopInterval = Math.ceil(totalYRange / numLabels) || 1;
170					while( yLabels[yLabels.length-1] < topValue - loopInterval){
171						yLabels.push(yLabels[yLabels.length-1] + loopInterval);
172					}
173					yLabels.push(topValue);
174					return yLabels;
175				}
176			};
177
178			return tableData;
179		};
180
181
182		//function to create a chart
183		var createChart = {
184			pie: function(){
185
186				canvasContain.addClass('visualize-pie');
187
188				if(o.pieLabelPos == 'outside'){ canvasContain.addClass('visualize-pie-outside'); }
189
190				var centerx = Math.round(canvas.width()/2);
191				var centery = Math.round(canvas.height()/2);
192				var radius = centery - o.pieMargin;
193				var counter = 0.0;
194				var toRad = function(integer){ return (Math.PI/180)*integer; };
195				var labels = $('<ul class="visualize-labels"></ul>')
196					.insertAfter(canvas);
197
198				//draw the pie pieces
199				$.each(memberTotals, function(i){
200					var fraction = (this <= 0 || isNaN(this))? 0 : this / dataSum;
201					ctx.beginPath();
202					ctx.moveTo(centerx, centery);
203					ctx.arc(centerx, centery, radius,
204						counter * Math.PI * 2 - Math.PI * 0.5,
205						(counter + fraction) * Math.PI * 2 - Math.PI * 0.5,
206		                false);
207			        ctx.lineTo(centerx, centery);
208			        ctx.closePath();
209			        ctx.fillStyle = dataGroups[i].color;
210			        ctx.fill();
211			        // draw labels
212			       	var sliceMiddle = (counter + fraction/2);
213			       	var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius +  radius / 5;
214			        var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance));
215			        var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance));
216			        var leftRight = (labelx > centerx) ? 'right' : 'left';
217			        var topBottom = (labely > centery) ? 'bottom' : 'top';
218			        var labeltext = $('<span class="visualize-label">' + Math.round(fraction*100) + '%</span>')
219			        	.css(leftRight, 0)
220			        	.css(topBottom, 0);
221			        var label = $('<li class="visualize-label-pos"></li>')
222			       			.appendTo(labels)
223			        		.css({left: labelx, top: labely})
224			        		.append(labeltext);
225			        labeltext
226			        	.css('font-size', radius / 8)
227			        	.css('margin-'+leftRight, -labeltext.width()/2)
228			        	.css('margin-'+topBottom, -labeltext.outerHeight()/2);
229
230			        if(dataGroups[i].textColor){ labeltext.css('color', dataGroups[i].textColor); }
231			      	counter+=fraction;
232				});
233			},
234
235			line: function(area){
236
237				if(area){ canvasContain.addClass('visualize-area'); }
238				else{ canvasContain.addClass('visualize-line'); }
239
240				//write X labels
241				var xInterval = canvas.width() / (xLabels.length -1);
242				var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
243					.width(canvas.width())
244					.height(canvas.height())
245					.insertBefore(canvas);
246				$.each(xLabels, function(i){
247					var thisLi = $('<li><span>'+this+'</span></li>')
248						.prepend('<span class="line" />')
249						.css('left', xInterval * i)
250						.appendTo(xlabelsUL);
251					var label = thisLi.find('span:not(.line)');
252					var leftOffset = label.width()/-2;
253					if(i == 0){ leftOffset = 0; }
254					else if(i== xLabels.length-1){ leftOffset = -label.width(); }
255					label
256						.css('margin-left', leftOffset)
257						.addClass('label');
258				});
259
260				//write Y labels
261				var yScale = canvas.height() / totalYRange;
262				var liBottom = canvas.height() / (yLabels.length-1);
263				var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
264					.width(canvas.width())
265					.height(canvas.height())
266					.insertBefore(canvas);
267
268				$.each(yLabels, function(i){
269					var thisLi = $('<li><span>'+this+'</span></li>')
270						.prepend('<span class="line"  />')
271						.css('bottom',liBottom*i)
272						.prependTo(ylabelsUL);
273					var label = thisLi.find('span:not(.line)');
274					var topOffset = label.height()/-2;
275					if(i == 0){ topOffset = -label.height(); }
276					else if(i== yLabels.length-1){ topOffset = 0; }
277					label
278						.css('margin-top', topOffset)
279						.addClass('label');
280				});
281
282				//start from the bottom left
283				ctx.translate(0,zeroLoc);
284				//iterate and draw
285				$.each(dataGroups,function(h){
286					ctx.beginPath();
287					ctx.lineWidth = o.lineWeight;
288					ctx.lineJoin = 'round';
289					var points = this.points;
290					var integer = 0;
291					ctx.moveTo(0,-(points[0]*yScale));
292					$.each(points, function(){
293						ctx.lineTo(integer,-(this*yScale));
294						integer+=xInterval;
295					});
296					ctx.strokeStyle = this.color;
297					ctx.stroke();
298					if(area){
299						ctx.lineTo(integer,0);
300						ctx.lineTo(0,0);
301						ctx.closePath();
302						ctx.fillStyle = this.color;
303						ctx.globalAlpha = .3;
304						ctx.fill();
305						ctx.globalAlpha = 1.0;
306					}
307					else {ctx.closePath();}
308				});
309			},
310
311			area: function(){
312				createChart.line(true);
313			},
314
315			bar: function(){
316
317				canvasContain.addClass('visualize-bar');
318
319				//write X labels
320				var xInterval = canvas.width() / (xLabels.length);
321				var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
322					.width(canvas.width())
323					.height(canvas.height())
324					.insertBefore(canvas);
325				$.each(xLabels, function(i){
326					var thisLi = $('<li><span class="label">'+this+'</span></li>')
327						.prepend('<span class="line" />')
328						.css('left', xInterval * i)
329						.width(xInterval)
330						.appendTo(xlabelsUL);
331					var label = thisLi.find('span.label');
332					label.addClass('label');
333				});
334
335				//write Y labels
336				var yScale = canvas.height() / totalYRange;
337				var liBottom = canvas.height() / (yLabels.length-1);
338				var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
339					.width(canvas.width())
340					.height(canvas.height())
341					.insertBefore(canvas);
342				$.each(yLabels, function(i){
343					var thisLi = $('<li><span>'+this+'</span></li>')
344						.prepend('<span class="line"  />')
345						.css('bottom',liBottom*i)
346						.prependTo(ylabelsUL);
347						var label = thisLi.find('span:not(.line)');
348						var topOffset = label.height()/-2;
349						if(i == 0){ topOffset = -label.height(); }
350						else if(i== yLabels.length-1){ topOffset = 0; }
351						label
352							.css('margin-top', topOffset)
353							.addClass('label');
354				});
355
356				//start from the bottom left
357				ctx.translate(0,zeroLoc);
358				//iterate and draw
359				for(var h=0; h<dataGroups.length; h++){
360					ctx.beginPath();
361					var linewidth = (xInterval-o.barGroupMargin*2) / dataGroups.length; //removed +1
362					var strokeWidth = linewidth - (o.barMargin*2);
363					ctx.lineWidth = strokeWidth;
364					var points = dataGroups[h].points;
365					var integer = 0;
366					for(var i=0; i<points.length; i++){
367						var xVal = (integer-o.barGroupMargin)+(h*linewidth)+linewidth/2;
368						xVal += o.barGroupMargin*2;
369
370						ctx.moveTo(xVal, 0);
371						ctx.lineTo(xVal, Math.round(-points[i]*yScale));
372						integer+=xInterval;
373					}
374					ctx.strokeStyle = dataGroups[h].color;
375					ctx.stroke();
376					ctx.closePath();
377				}
378			}
379		};
380
381		//create new canvas, set w&h attrs (not inline styles)
382		var canvasNode = document.createElement("canvas");
383		canvasNode.setAttribute('height',o.height);
384		canvasNode.setAttribute('width',o.width);
385		var canvas = $(canvasNode);
386
387		//get title for chart
388		var title = o.title || self.find('caption').text();
389
390		//create canvas wrapper div, set inline w&h, append
391		var canvasContain = (container || $('<div class="visualize" role="img" aria-label="Chart representing data from the table: '+ title +'" />'))
392			.height(o.height)
393			.width(o.width)
394			.append(canvas);
395
396		//scrape table (this should be cleaned up into an obj)
397		var tableData = scrapeTable();
398		var dataGroups = tableData.dataGroups();
399		var allData = tableData.allData();
400		var dataSum = tableData.dataSum();
401		var topValue = tableData.topValue();
402		var bottomValue = tableData.bottomValue();
403		var memberTotals = tableData.memberTotals();
404		var totalYRange = tableData.totalYRange();
405		var zeroLoc = o.height * (topValue/totalYRange);
406		var xLabels = tableData.xLabels();
407		var yLabels = tableData.yLabels();
408
409		//title/key container
410		if(o.appendTitle || o.appendKey){
411			var infoContain = $('<div class="visualize-info"></div>')
412				.appendTo(canvasContain);
413		}
414
415		//append title
416		if(o.appendTitle){
417			$('<div class="visualize-title">'+ title +'</div>').appendTo(infoContain);
418		}
419
420
421		//append key
422		if(o.appendKey){
423			var newKey = $('<ul class="visualize-key"></ul>');
424			var selector = (o.parseDirection == 'x') ? 'tr:gt(0) th' : 'tr:eq(0) th' ;
425			self.find(selector).each(function(i){
426				$('<li><span class="visualize-key-color" style="background: '+dataGroups[i].color+'"></span><span class="visualize-key-label">'+ $(this).text() +'</span></li>')
427					.appendTo(newKey);
428			});
429			newKey.appendTo(infoContain);
430		};
431
432		//append new canvas to page
433
434		if(!container){canvasContain.insertAfter(this); }
435		if( typeof(G_vmlCanvasManager) != 'undefined' ){ G_vmlCanvasManager.initElement(canvas[0]); }
436
437		//set up the drawing board
438		var ctx = canvas[0].getContext('2d');
439
440		//create chart
441		createChart[o.type]();
442
443		//clean up some doubled lines that sit on top of canvas borders (done via JS due to IE)
444		$('.visualize-line li:first-child span.line, .visualize-line li:last-child span.line, .visualize-area li:first-child span.line, .visualize-area li:last-child span.line, .visualize-bar li:first-child span.line,.visualize-bar .visualize-labels-y li:last-child span.line').css('border','none');
445		if(!container){
446		//add event for updating
447		canvasContain.bind('visualizeRefresh', function(){
448			self.visualize(o, $(this).empty());
449		});
450		}
451	}).next(); //returns canvas(es)
452};
453})(jQuery);
454

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.