## Posted By

jatkins on 06/21/12

# Experimenting with a sparse matrix fill algorithm

/ Published in: JavaScript

it's got the beginnings of the gradient designer now

`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head> 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />	<title>Pie Chart Generator</title>	<script type="text/javascript">	// <![CDATA[		if(!Object.keys) Object.keys = function(o) { // from http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html		if(o!==Object(o))			throw new TypeError('Object.keys called on a non-object');		var k = [], p;		for (p in o) {			if(Object.prototype.hasOwnProperty.call(o,p))				k.push(p);		}		return k;	};	 	var misc = {		math: {			pythagoras: function(length1, length2) {				return Math.sqrt(Math.pow(length1, 2)+Math.pow(length2, 2));			},			distanceBetweenTwoPoints: function(point1, point2) {				return this.pythagoras(point2.x-point1.x, point2.y-point1.y);			},			justNumber: function(stringToParse) {				return parseFloat(stringToParse.toString().replace(/[^0-9\.\-]+/, ''));			},			degToRad: function(angleInDegrees) {				return angleInDegrees * Math.PI / 180;			}		}, 		js: {			getRandomColor: function() {				return 'rgb('+Math.round(Math.random()*254)+', '+Math.round(Math.random()*254)+', '+Math.round(Math.random()*254)+')';			},			getRealPosition: function (elmnt) { // based on http://www.quirksmode.org/js/findpos.html				elmnt = typeof elmnt == 'object' ? elmnt : document.getElementById(elmnt);				elmntLeft = elmnt.offsetLeft;				elmntTop = elmnt.offsetTop;				while(elmnt = elmnt.offsetParent) {					elmntLeft += elmnt.offsetLeft;					elmntTop += elmnt.offsetTop;				}				return {x: elmntLeft, y: elmntTop};			},			positionBelow: function(anchorElement, elementToPosition, xExtra, yExtra) {				if(typeof elementToPosition=='string')					elementToPosition = document.getElementById(elementToPosition); 				var realAnchorPosition = this.getRealPosition(anchorElement); 				var yExtra = yExtra || 0, xExtra = xExtra || 0; 				elementToPosition.style.top = realAnchorPosition.y + anchorElement.offsetHeight + yExtra + 'px';				elementToPosition.style.left = realAnchorPosition.x + xExtra + 'px';				elementToPosition.style.position = 'absolute';			}		}	} 	var pieChart = function(pieName, radius, pieData) {		var pieDiv = document.createElement('div'),		radiusSquared = Math.pow(radius, 2); 		pieDiv.id = pieName;		pieDiv.className = 'chart';		pieDiv.style.width = radius * 2 + 'px';		pieDiv.style.height = radius * 2 + 'px';		document.body.appendChild(pieDiv); 		this.renderPoint = function(x, y, pointColor) {			var newPoint = document.createElement('span');			newPoint.style.left = x + radius + 'px';			newPoint.style.top = y + radius + 'px';			newPoint.style.backgroundColor = pointColor;			pieDiv.appendChild(newPoint);			return {x: x+radius, y: y+radius};		}; 		this.renderCircle = function(sparseMatrix) {			var x = -radius;			while(x<radius) {				x++;				y = Math.sqrt(radiusSquared - Math.pow(x, 2));				this.renderPoint(x, y); // for the bottom semicircle				this.renderPoint(x, -y); // for the top semicircle 				if(!sparseMatrix['x'+x])					sparseMatrix['x'+x] = [];				sparseMatrix['x'+x]['y'+y] = '#000000';				sparseMatrix['x'+x]['y-'+y] = '#000000';			} 			return sparseMatrix;		}; 		this.rotatePoint = function(origPoint, cosTheta, sinTheta) {			return {x: origPoint.x*cosTheta - origPoint.y*sinTheta, y: origPoint.x*sinTheta + origPoint.y*cosTheta};		}; 		this.renderLine = function(startPoint, endPoint, rotateBy, lineColor, sparseMatrix) { // render a straight line (y=mx+c)			var rotateByInRadians = misc.math.degToRad(rotateBy), cosTheta, sinTheta;			cosTheta = Math.cos(rotateByInRadians);			sinTheta = Math.sin(rotateByInRadians); 			startPoint = this.rotatePoint(startPoint, cosTheta, sinTheta);			endPoint = this.rotatePoint(endPoint, cosTheta, sinTheta); 			var tempPoint, width, height, gradient, yIntercept, i = 0, pointCount, switchPoint = false; 			if(misc.math.distanceBetweenTwoPoints(startPoint, {x: 0, y: 0})>misc.math.distanceBetweenTwoPoints({x: 0, y: 0}, endPoint)) {				tempPoint = startPoint;				startPoint = endPoint;				endPoint = tempPoint;			} 			width = Math.abs(endPoint.x - startPoint.x);			height = Math.abs(endPoint.y - startPoint.y);			gradient = (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x); 			yIntercept = endPoint.y - gradient * endPoint.x;			distance = misc.math.distanceBetweenTwoPoints(startPoint, endPoint); 			var calcXCoord = function(y) {				var x = (y - yIntercept) / gradient;				return ((gradient == Infinity) || (Math.abs(x) == Infinity)) ? startPoint.x : x;			};			var calcYCoord = function(x) {				return Math.abs(gradient) == Infinity ? startPoint.y : gradient * x + yIntercept;			};			pointCount = width > height ? width : height; 			if(width>height) {				if(endPoint.x<startPoint.x)					switchPoint = true;			}			else {				if(endPoint.y<startPoint.y)					switchPoint = true;			} 			var currentPoint;			while(i<pointCount) {				if(width>height)					currentPoint = {x: parseInt((switchPoint?endPoint:startPoint).x)+i, y: calcYCoord(parseInt((switchPoint?endPoint:startPoint).x)+i)};				else					currentPoint = {x: calcXCoord(parseInt((switchPoint?endPoint:startPoint).y)+i), y: parseInt((switchPoint?endPoint:startPoint).y)+i};				this.renderPoint(currentPoint.x, currentPoint.y, lineColor); 				if(!sparseMatrix['x'+currentPoint.x])					sparseMatrix['x'+currentPoint.x] = [];				sparseMatrix['x'+currentPoint.x]['y'+currentPoint.y] = lineColor; 				i++;			} 			return sparseMatrix;		}; 		// draw sectors		var lineColor = misc.js.getRandomColor(), currentAngle = 0, sectorAngles = [], dataSum = 0, sectorColors = []; 		for(var i=0;i<pieData.length;i++)			dataSum += pieData[i][1]; 		var cumulativeAngle = 0, pieSparseMatrix = [];		for(var i=0;i<pieData.length;i++) {			cumulativeAngle += pieData[i][1] / dataSum * 360;			pieSparseMatrix = this.renderLine({x: 0, y: -radius}, {x: 0, y: 0}, cumulativeAngle, misc.js.getRandomColor(), pieSparseMatrix);		}		var pieLegend = document.createElement('ul');		pieLegend.style.left = radius*2 + 'px';		for(var i=0;i<pieData.length;i++) {			var newLegendLI = document.createElement('li'), newLegendBlock = document.createElement('span');			newLegendLI.style.color = sectorColors[i];			newLegendLI.appendChild(document.createTextNode(pieData[i][0]));			pieLegend.appendChild(newLegendLI);		}		pieDiv.appendChild(pieLegend); 		var pieSparseMatrix = [];		//pieSparseMatrix = this.renderCircle(pieSparseMatrix);		this.renderCircle([]); // if we leave the circle out of the matrix, it won't be filled in 		var pointSets = [			[[200, 300], [400, 300], [300, 200], [200, 300]], // top triangle			[[200, 250], [300, 350], [400, 250], [200, 250]], // bottom triangle 			[[50, 0], [250, 0], [250, 100], [50, 100], [50, 0]], // outer rectangle			[[0, 50], [200, 50], [200, 150], [0, 150], [0, 50]], // inner rectangle			[[50, 0], [0, 50]], // connect at top-left			[[250, 0], [200, 50]], // connect at top-right			[[50, 100], [0, 150]], // connect at bottom-left			[[250, 100], [200, 150]] // connect at bottom-right		]; 		for(var i=0;i<pointSets.length;i++) {				for(var j=0;j<pointSets[i].length-1;j++)				pieSparseMatrix = this.renderLine({x: pointSets[i][j][0], y: pointSets[i][j][1]}, {x: pointSets[i][j+1][0], y: pointSets[i][j+1][1]}, 0, '#000000', pieSparseMatrix);		} 		var numericalSort = function(a, b) {			return parseFloat(a.replace(/[^0-9\-\.]/g, '')) - parseFloat(b.replace(/[^0-9\-\.]/g, ''));		}; 		var currentColor; 		this.fillWithGradient = function(startColor, endColor) {			for(var i=0;i<3;i++) {				startColor[i] = parseInt(startColor[i]);				endColor[i] = parseInt(endColor[i]);			} 			var sMatColNames = Object.keys(pieSparseMatrix).sort(numericalSort), sMatRowNames = [], fillColor = startColor, fillIncrement = [];			for(var i=0;i<3;i++)				fillIncrement[i] = Math.round((endColor[i]-startColor[i])/sMatColNames.length*1000)/1000; 			for(var j=0;j<sMatColNames.length;j++) {				sMatRowNames = Object.keys(pieSparseMatrix[sMatColNames[j]]).sort(numericalSort);				for(var k=0;k<sMatRowNames.length-1;k++) {					if(pieSparseMatrix[sMatColNames[j]][sMatRowNames[k]]!=fillColor&&pieSparseMatrix[sMatColNames[j]][sMatRowNames[k+1]]!=fillColor) { // fill between these points						for(var l=parseFloat(sMatRowNames[k].substring(1))+1;l<parseFloat(sMatRowNames[k+1].substring(1));l++)							this.renderPoint(parseFloat(sMatColNames[j].substring(1)), l, 'rgb('+Math.round(fillColor[0])+', '+Math.round(fillColor[1])+', '+Math.round(fillColor[2])+')');						k++;					}				} 				fillColor[0] += fillIncrement[0];				fillColor[1] += fillIncrement[1];				fillColor[2] += fillIncrement[2];			}		};	}; 	var widgets = {		colorPicker: function() {			var shadeIncrement, currentColorBox, currentShadePreview, shadeAdjustmentTimer, pickerDivChildren = {};			this.setColorBox = function(newColorBox) {				currentColorBox = newColorBox;			}			this.getColorBox = function() {				return currentColorBox;			};			var pickerDiv = document.createElement('div');			this.pickerDiv = pickerDiv;			var adjustShade = function() {				var shadeColor = currentShadePreview.parentNode.childNodes[1].style.backgroundColor.replace(/[^0-9\-\,]/g, '').split(','), colorToChange = currentShadePreview.parentNode.childNodes[1].id.indexOf('Red') == -1 ? (currentShadePreview.parentNode.childNodes[1].id.indexOf('Green') == -1 ? 2 : 1) : 0; 				shadeColor[0] = 0;				shadeColor[1] = 0;				shadeColor[2] = 0;				shadeColor[colorToChange] = misc.math.justNumber(currentShadePreview.parentNode.childNodes[1].value); 				if((shadeIncrement==1&&shadeColor[colorToChange]<100)||(shadeIncrement==-1&&shadeColor[colorToChange]>0))					shadeColor[colorToChange] += shadeIncrement; 				currentShadePreview.parentNode.childNodes[1].style.backgroundColor = 'rgb('+shadeColor[0]+'%, '+shadeColor[1]+'%, '+shadeColor[2]+'%)';				currentShadePreview.parentNode.childNodes[1].value = shadeColor[colorToChange] + '%'; 				if(currentColorBox.value=='')					currentColorBox.style.backgroundColor = currentShadePreview.parentNode.childNodes[1].style.backgroundColor; 				var currentRGBColor = Math.round(255-(misc.math.justNumber(txtRed.value)/100*255)) + ',' + Math.round(255-(misc.math.justNumber(txtGreen.value)/100*255)) + ',' + Math.round(255-(misc.math.justNumber(txtBlue.value)/100*255)); 				var currentRGBColor = [Math.round(misc.math.justNumber(txtRed.value)/100*255), Math.round(misc.math.justNumber(txtGreen.value)/100*255), Math.round(misc.math.justNumber(txtBlue.value)/100*255)]; 				currentColorBox.style.backgroundColor = 'rgb(' + txtRed.value + ',' + txtGreen.value + ',' + txtBlue.value + ')';				currentColorBox.value = currentColorBox.style.backgroundColor;				currentColorBox.style.color = 'rgb(' + (255-currentRGBColor[0]) + ',' + (255-currentRGBColor[1]) + ',' + (255-currentRGBColor[2]) + ')'; // set the text color to the inverse of the background color 				updateSelectedColor(currentRGBColor.join(',')); 				if(shadeAdjustmentTimer)					clearTimeout(shadeAdjustmentTimer);				shadeAdjustmentTimer = setTimeout(adjustShade, navigator.appName=='Microsoft Internet Explorer'?50:1);			},			incShade = function() {				currentShadePreview = this;				shadeIncrement = 1;				adjustShade();			},			decShade = function() {				currentShadePreview = this;				shadeIncrement = -1;				adjustShade();			},			endShadeAdjustment = function() {				clearTimeout(shadeAdjustmentTimer);			}; 			var updateSelectedColor = function(rgbColorCode) {				if(colorNames[rgbColorCode]) {					document.getElementById('colorListDropDown').childNodes[0].childNodes[0].childNodes[2].data = colorNames[rgbColorCode];					document.getElementById('colorListDropDown').childNodes[0].childNodes[0].childNodes[1].style.background = 'rgb(' + rgbColorCode + ')';				}				else {					document.getElementById('colorListDropDown').childNodes[0].childNodes[0].childNodes[2].data = 'Custom';					document.getElementById('colorListDropDown').childNodes[0].childNodes[0].childNodes[1].style.background = 'url(customColor.png) center center no-repeat';				}			};			this.updateSelectedColor = updateSelectedColor; 			var colorListSelect = function(selectedItem) {				var selectedColor = selectedItem.childNodes[0].style.backgroundColor.replace(/[^0-9\-\,]/g, '').split(',');				txtRed.value = Math.round(selectedColor[0] / 255 * 100) + '%';				txtRed.style.backgroundColor = 'rgb('+selectedColor[0]+',0,0)';				txtGreen.value = Math.round(selectedColor[1] / 255 * 100) + '%';				txtGreen.style.backgroundColor = 'rgb(0,'+selectedColor[1]+',0)';				txtBlue.value = Math.round(selectedColor[2] / 255 * 100) + '%';				txtBlue.style.backgroundColor = 'rgb(0,0,'+selectedColor[0]+')'; 				currentColorBox.value = 'rgb('+selectedColor.join(', ')+')';				currentColorBox.style.backgroundColor = currentColorBox.value;				currentColorBox.style.color = 'rgb('+(255-selectedColor[0])+', '+(255-selectedColor[1])+', '+(255-selectedColor[2])+')';			};			var colorListDropDown = new widgets.visualDropDown('colorListDropDown', colorListSelect);			var colorNames = [];			colorNames['255,255,255'] = 'White';			colorNames['192,192,192'] = 'Silver';			colorNames['128,128,128'] = 'Gray';			colorNames['0,0,0'] = 'Black';			colorNames['255,0,0'] = 'Red';			colorNames['128,0,0'] = 'Maroon';			colorNames['255,255,0'] = 'Yellow';			colorNames['128,128,0'] = 'Olive';			colorNames['0,255,0'] = 'Lime';			colorNames['0,128,0'] = 'Green';			colorNames['0,255,255'] = 'Aqua';			colorNames['0,128,128'] = 'Teal';			colorNames['0,0,255'] = 'Blue';			colorNames['0,0,128'] = 'Navy';			colorNames['255,0,255'] = 'Fushsia';			colorNames['128,0,128'] = 'Purple';			for(colorName in colorNames)				colorListDropDown.addItem(colorNames[colorName], colorName); 			var pickerDivChildren = {				redShadeContainer: document.createElement('span'),				greenShadeContainer: document.createElement('span'),				blueShadeContainer: document.createElement('span'),				br1: document.createElement('br'),				colorList: document.getElementById('colorListDropDown'),				br2: document.createElement('br'),				collapseLink: document.createElement('a')			}; 			var lblRed = document.createElement('label'), txtRed = document.createElement('input'); 			lblRed.appendChild(document.createTextNode('Red'));			lblRed.setAttribute('for', 'txtRed'); 			txtRed.id = 'txtRed';			txtRed.type = 'text';			txtRed.className = 'colorPreview';			txtRed.value = '100%';			txtRed.style.backgroundColor = 'rgb(255, 0, 0)'; 			var btnIncRed = document.createElement('button'), btnDecRed = document.createElement('button'); 			btnIncRed.onmousedown = incShade;			btnIncRed.onmouseup = endShadeAdjustment;			btnIncRed.appendChild(document.createTextNode('+')); 			btnDecRed.onmousedown = decShade;			btnDecRed.onmouseup = endShadeAdjustment;			btnDecRed.appendChild(document.createTextNode('-')); 			pickerDivChildren.redShadeContainer.className = 'shadeContainer';			pickerDivChildren.redShadeContainer.appendChild(lblRed);			pickerDivChildren.redShadeContainer.appendChild(txtRed);			pickerDivChildren.redShadeContainer.appendChild(document.createElement('br'));			pickerDivChildren.redShadeContainer.appendChild(btnIncRed);			pickerDivChildren.redShadeContainer.appendChild(btnDecRed); 			var lblGreen = document.createElement('label'), txtGreen = document.createElement('input'); 			lblGreen.appendChild(document.createTextNode('Green'));			lblGreen.setAttribute('for', 'txtGreen'); 			txtGreen.id = 'txtGreen';			txtGreen.type = 'text';			txtGreen.className = 'colorPreview';			txtGreen.value = '100%';			txtGreen.style.backgroundColor = 'rgb(0, 255, 0)'; 			var btnIncGreen = document.createElement('button'), btnDecGreen = document.createElement('button'); 			btnIncGreen.onmousedown = incShade;			btnIncGreen.onmouseup = endShadeAdjustment;			btnIncGreen.appendChild(document.createTextNode('+')); 			btnDecGreen.onmousedown = decShade;			btnDecGreen.onmouseup = endShadeAdjustment;			btnDecGreen.appendChild(document.createTextNode('-')); 			pickerDivChildren.greenShadeContainer.className = 'shadeContainer';			pickerDivChildren.greenShadeContainer.appendChild(lblGreen);			pickerDivChildren.greenShadeContainer.appendChild(txtGreen);			pickerDivChildren.greenShadeContainer.appendChild(document.createElement('br'));			pickerDivChildren.greenShadeContainer.appendChild(btnIncGreen);			pickerDivChildren.greenShadeContainer.appendChild(btnDecGreen); 			var lblBlue = document.createElement('label'), txtBlue = document.createElement('input'); 			lblBlue.appendChild(document.createTextNode('Blue'));			lblBlue.setAttribute('for', 'txtBlue'); 			txtBlue.id = 'txtBlue';			txtBlue.type = 'text';			txtBlue.className = 'colorPreview';			txtBlue.value = '100%';			txtBlue.style.backgroundColor = 'rgb(0, 0, 255)'; 			var btnIncBlue = document.createElement('button'), btnDecBlue = document.createElement('button'); 			btnIncBlue.onmousedown = incShade;			btnIncBlue.onmouseup = endShadeAdjustment;			btnIncBlue.appendChild(document.createTextNode('+')); 			btnDecBlue.onmousedown = decShade;			btnDecBlue.onmouseup = endShadeAdjustment;			btnDecBlue.appendChild(document.createTextNode('-')); 			pickerDivChildren.blueShadeContainer.className = 'shadeContainer';			pickerDivChildren.blueShadeContainer.appendChild(lblBlue);			pickerDivChildren.blueShadeContainer.appendChild(txtBlue);			pickerDivChildren.blueShadeContainer.appendChild(document.createElement('br'));			pickerDivChildren.blueShadeContainer.appendChild(btnIncBlue);			pickerDivChildren.blueShadeContainer.appendChild(btnDecBlue); 			var collapsePickerDiv = function() {				pickerDiv.className = 'hidden';			}; 			var collapseIMG = document.createElement('img');			collapseIMG.src = 'collapse.png';			collapseIMG.alt = '[Collapse]';			collapseIMG.title = 'Collapse Color Picker';			pickerDivChildren.collapseLink.appendChild(collapseIMG);			pickerDivChildren.collapseLink.className = 'collapse';			pickerDivChildren.collapseLink.href = '#';			pickerDivChildren.collapseLink.onclick = collapsePickerDiv; 			var pickerDivChildrenKeys = Object.keys(pickerDivChildren);			for(var i=0;i<pickerDivChildrenKeys.length;i++)				this.pickerDiv.appendChild(pickerDivChildren[pickerDivChildrenKeys[i]]); 			this.pickerDiv.className = 'pickerDiv hidden';		},		gradientSelector: function() {			this.show = function() {				selectorDiv.className = 'gradientSelector';				selectorDiv.childNodes[selectorDiv.childNodes.length-2].style.width/*(the gradient designer)*/ = misc.js.getRealPosition(selectorDiv.childNodes[selectorDiv.childNodes.length-3]).x/*(the '...' button)*/ + selectorDiv.childNodes[selectorDiv.childNodes.length-3].offsetWidth - misc.js.getRealPosition(selectorDiv.childNodes[selectorDiv.childNodes.length-5]).x/*(the label)*/ + 'px';			};			this.hide = function() {				selectorDiv.className = 'gradientSelector hidden';			};			this.isVisible = function() {				return selectorDiv.className.indexOf('hidden') == -1;			}; 			var selectColorToEdit = function() {				gsColorPicker.setColorBox(this.previousSibling);				var currentColor = gsColorPicker.getColorBox().value.replace(/[^0-9\-\,]/g, '').split(',');				if(currentColor=='')					currentColor = [255, 255, 255];				txtRed.value = Math.round(currentColor[0] / 255 * 100) + '%';				txtRed.style.backgroundColor = 'rgb(' + currentColor[0] + ', 0, 0)';				txtGreen.value = Math.round(currentColor[1] / 255 * 100) + '%';				txtGreen.style.backgroundColor = 'rgb(0, ' + currentColor[1] + ', 0)';				txtBlue.value = Math.round(currentColor[2] / 255 * 100) + '%';				txtBlue.style.backgroundColor = 'rgb(0, 0, ' + currentColor[2] + ')'; 				gsColorPicker.updateSelectedColor(currentColor[0]+','+currentColor[1]+','+currentColor[2]); 				selectorDivChildren.pickerDiv.className = 'pickerDiv';			}; 			var gsColorPicker = new widgets.colorPicker(),				selectorDiv = document.createElement('div'),				selectorDivChildren = {					/*lblStartColor: document.createElement('label'),					txtStartColor: document.createElement('input'),					btnSelectStartColor: document.createElement('button'),					br1: document.createElement('br'),					lblEndColor: document.createElement('label'),					txtEndColor: document.createElement('input'),					btnSelectEndColor: document.createElement('button'),*/ 					lblCurrentColor: document.createElement('label'),					txtCurrentColor: document.createElement('input'),					btnSelectCurrentColor: document.createElement('button'),					gradientDesignDiv: document.createElement('div'), 					pickerDiv: gsColorPicker.pickerDiv				};			selectorDiv.className = 'gradientSelector hidden'; // hidden by default			this.selectorDiv = selectorDiv; 			selectorDivChildren.gradientDesignDiv.className = 'gradientDesign';			var hoverBar = document.createElement('span');			hoverBar.className = 'hidden';			selectorDivChildren.gradientDesignDiv.appendChild(hoverBar); 			selectorDivChildren.gradientDesignDiv.onmousemove = function(e) {				hoverBar.style.backgroundColor = selectorDivChildren.txtCurrentColor.value;				if(hoverBar.className=='hidden')					hoverBar.className = '';				hoverBar.style.left = (e || window.event).clientX - misc.js.getRealPosition(selectorDivChildren.gradientDesignDiv).x - 4 + 'px';			};			selectorDivChildren.gradientDesignDiv.onclick = function(e) {				var newVerticalBar = document.createElement('span');				newVerticalBar.style.left = (e || window.event).clientX - misc.js.getRealPosition(selectorDivChildren.gradientDesignDiv).x - 4 + 'px';				newVerticalBar.style.backgroundColor = selectorDivChildren.txtCurrentColor.value;				this.appendChild(newVerticalBar); 				hoverBar.className = 'hidden';			};			selectorDivChildren.gradientDesignDiv.onmouseout = function() {				hoverBar.className = 'hidden';			}; 			selectorDivChildren.lblCurrentColor.appendChild(document.createTextNode('Color:'));			selectorDivChildren.lblCurrentColor.setAttribute('for', 'txtCurrentColor'); 			selectorDivChildren.txtCurrentColor.id = 'txtCurrentColor';			selectorDivChildren.txtCurrentColor.type = 'text';			selectorDivChildren.txtCurrentColor.value = 'rgb(255, 255, 255)'; 			selectorDivChildren.btnSelectCurrentColor.appendChild(document.createTextNode('...'));			selectorDivChildren.btnSelectCurrentColor.title = 'Pick Current Color';			selectorDivChildren.btnSelectCurrentColor.onclick = selectColorToEdit; 			/*selectorDivChildren.lblStartColor.appendChild(document.createTextNode('Start Color:'));			selectorDivChildren.lblStartColor.setAttribute('for', 'txtStartColor'); 			selectorDivChildren.txtStartColor.id = 'txtStartColor';			selectorDivChildren.txtStartColor.type = 'text';			selectorDivChildren.txtStartColor.value = 'rgb(255, 255, 255)'; 			selectorDivChildren.btnSelectStartColor.appendChild(document.createTextNode('...'));			selectorDivChildren.btnSelectStartColor.title = 'Pick Start Color';			selectorDivChildren.btnSelectStartColor.onclick = selectColorToEdit; 			selectorDivChildren.lblEndColor.appendChild(document.createTextNode('End Color:'));			selectorDivChildren.lblEndColor.setAttribute('for', 'txtEndColor'); 			selectorDivChildren.txtEndColor.id = 'txtEndColor';			selectorDivChildren.txtEndColor.type = 'text';			selectorDivChildren.txtEndColor.value = 'rgb(255, 255, 255)'; 			selectorDivChildren.btnSelectEndColor.appendChild(document.createTextNode('...'));			selectorDivChildren.btnSelectEndColor.title = 'Pick End Color';			selectorDivChildren.btnSelectEndColor.onclick = selectColorToEdit;*/ 			var selectorDivChildrenKeys = Object.keys(selectorDivChildren);			for(var i=0;i<selectorDivChildrenKeys.length;i++)				selectorDiv.appendChild(selectorDivChildren[selectorDivChildrenKeys[i]]); 			document.body.appendChild(selectorDiv);		}, 		visualDropDown: function(dropDownID, callBack) {			var dropItems = [], dropDownUL = document.createElement('ul');			dropDownUL.id = dropDownID;			dropDownUL.className = 'visualDropDown';			document.body.appendChild(dropDownUL); 			var selectItem = function() {				dropDownUL.childNodes[0].childNodes[0].childNodes[2].data = this.childNodes[1].data;				dropDownUL.childNodes[0].childNodes[0].childNodes[1].style.backgroundColor = this.childNodes[0].style.backgroundColor; 				if(callBack)					callBack(this); 				toggleDropDown(); // hide the dropdown;			}; 			var toggleDropDown = function() {				if(dropDownUL.className.indexOf('expanded')==-1) {					dropDownUL.className = 'visualDropDown expanded';					dropDownUL.childNodes[0].childNodes[0].childNodes[0].src = 'arrowUp.png';				}				else {					dropDownUL.className = 'visualDropDown';					dropDownUL.childNodes[0].childNodes[0].childNodes[0].src = 'arrowDown.png';				} 				var e = e || window.event; 				if(e.preventDefault)					e.preventDefault();				else					e.returnValue = false;			}; 			var dropItem = function(itemText, iconColor) {				this.iconColor = iconColor;				this.itemText = itemText; 				var itemLI = document.createElement('li'), itemLink = document.createElement('a'), iconSpan = document.createElement('span');				itemLink.href = '#';				itemLink.title = itemText;				itemLink.onclick = selectItem;				iconSpan.style.backgroundColor = 'rgb(' + iconColor + ')'; 				itemLink.appendChild(iconSpan);				itemLink.appendChild(document.createTextNode(itemText));				itemLI.appendChild(itemLink); 				dropDownUL.appendChild(itemLI); 				if(dropItems.length==0) {					dropDownUL.appendChild(itemLI.cloneNode(true));					var dropDownExpandIcon = document.createElement('img');					dropDownExpandIcon.src = 'arrowDown.png';					dropDownExpandIcon.alt = '[Expand]';					dropDownExpandIcon.title = 'Show Options';					dropDownUL.childNodes[0].childNodes[0].onclick = toggleDropDown;					dropDownUL.childNodes[0].childNodes[0].insertBefore(dropDownExpandIcon, dropDownUL.childNodes[0].childNodes[0].childNodes[0]);					dropDownUL.childNodes[1].childNodes[0].onclick = selectItem;									}			}; 			this.addItem = function(itemText, iconColor) {				var newDropItem = new dropItem(itemText, iconColor);				dropItems.push(newDropItem);				return newDropItem;			};		}	}; 	var pieApp = {};	function initPie() {		pieApp.myPieChart = new pieChart('myPieChart', 100, [['WinXP',500],['Win7',1200],['WinVista',500],['Win8',800],['Win95',900]]);		pieApp.gsGradientSelector = new widgets.gradientSelector();	}	// ]]>	</script>	<style type="text/css">	<!--	img {		border: 0;	} 	.hidden {		display: none !important;	} 	div.gradientSelector {		position: absolute;		left: 0px;		top: 0px;		border: 1px solid #d3d3d3;		background: #f5f5f5;		font: 80% Tahoma, Arial, Helvetica, sans-serif;		padding: 0.5em;	} 	div.gradientSelector label {		width: 5.75em;		display: inline-block;	} 	span.shadeContainer {		display: inline-block;		margin-right: 0.25em;		text-align: center;	} 	div.gradientSelector button {		border: 1px solid #a1a1a1;		background: #d3d3d3;		padding: 3px;		margin-top: -2px;		cursor: pointer;		font: bold 80% Arial, Helvetica, sans-serif;	} 	span.shadeContainer button {		width: 1.25em;		height: 1.75em;		padding: 1px;		font-weight: 100;	} 	div.gradientSelector button:hover {		border-color: gray;		background: #a1a1a1;	} 	div.gradientSelector input {		border: 0;		padding: 2px;	} 	span.shadeContainer label {		display: block;		text-transform: uppercase;		font-weight: bold;	} 	div.gradientSelector span.shadeContainer input {		padding: 0px;	} 	div.pickerDiv {		text-align: center;		background: #fcfcfc;		border-top: 1px solid #d3d3d3;		margin: 0.5em -0.5em -0.5em;		padding: 0.5em;	} 	div.pickerDiv br {		clear: both;	} 	div.pickerDiv a.collapse {		display: block;		background: #f2f2f2;		border-top: 1px solid #d3d3d3;		padding: 0.25em;		margin: 0.5em -0.5em -0.5em;	} 	input.colorPreview {		width: 39px;		height: 20px;		border: 0;		padding: 0;		margin: 0;		display: inline-block;		color: #ffffff;		text-align: center;	} 	div.chart {		position: relative;		border: 1px solid #d3d3d3;	} 	div.chart span {		display: block;		width: 1px;		height: 1px;		background: #000000;		position: absolute;	} 	div.chart ul {		list-style: square;		position: absolute;		top: 0px;	} 	div.chart ul li {		list-style: square;	} 	ul.visualDropDown {		display: inline-block;		padding: 0;		margin: 0.75em 0em 0em;	} 	ul.visualDropDown li {		 list-style: none;		 display: none;	} 	ul.visualDropDown li:first-child, ul.visualDropDown.expanded li {		display: block;	} 	ul.visualDropDown li:first-child img {		float: right;		display: block;		margin: -0.25em -0.25em -0.25em 0.5em;	} 	ul.visualDropDown li span {		margin-right: 0.5em;		width: 1em;		height: 1em;		display: inline-block;		border: 1px solid black;		vertical-align: sub;	} 	ul.visualDropDown li a {		text-align: left;		display: block;		padding: 0.25em 0.5em;		color: #000000;		text-decoration: none;		font-size: 120%;		width: 7em;		background: #ffffff;		border: 1px solid #d3d3d3;		margin-top: -1px;		z-index: 1;		position: relative;	} 	ul.visualDropDown li a:hover {		background: #f8f8f8;		border-color: #c3c3c3;		z-index: 2;	} 	ul.visualDropDown li:first-child a, ul.visualDropDown li:first-child a:hover {		background: #e3e3e3;	} 	div.gradientDesign {		width: 100%;		height: 2em;		border: 1px solid #d3d3d3;		position: relative;	} 	div.gradientDesign span { /* vertical bars */		display: block;		position: absolute;		top: 0;		width: 1px;		height: 100%;		background: #000000;	}	-->	</style>	<!--[if lt IE 9]><style type="text/css">	img, span { vertical-align: baseline !important; }	div.pickerDiv a.collapse { margin-bottom: 0em !important; }	div.pickerDiv { padding-bottom: 0 !important; }	span.shadeContainer button { margin-top: 0px !important; }	</style><![endif]--></head><body onload="initPie();">	<h1><button style="float: right;" onclick="var startColor = document.getElementById('txtStartColor').value.replace(/[^0-9\-\,]/g, '').split(','), endColor = document.getElementById('txtEndColor').value.replace(/[^0-9\-\,]/g, '').split(','); pieApp.myPieChart.fillWithGradient(startColor, endColor);">Render Gradient</button><button style="float: right;" onclick="if(pieApp.gsGradientSelector.isVisible()) pieApp.gsGradientSelector.hide(); else { pieApp.gsGradientSelector.show(); misc.js.positionBelow(this, pieApp.gsGradientSelector.selectorDiv, this.offsetWidth-pieApp.gsGradientSelector.selectorDiv.offsetWidth); }">Set Gradient Colors</button> Pie Chart Generator</h1>	<h2 style="color: red;">To-Do: Make fill algorithm consider pen/brush width<br />To-Do: Make it possible to fill individual sections of a shape, so a fill button can be made<br />To-Do: Pattern fill, and make fills start and finish within each closed shape<br />Make it so you can't draw two pixels in the same place -- doing so should overwrite the existing pixel</h2></body></html>`