div.infographic-wrapper
View Compiled
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400);

.infographic-wrapper{
	width:450px;
	height:450px;
	margin:auto;
	font-family: 'Source Sans Pro', sans-serif;
	font-weight: 300;

	.outer-group, .content-group .icon-group{
		transform-origin: 50% 50%;
    	-webkit-transform-origin: 50% 50%;
    	-moz-transform-origin: 50% 50%;
	}

	.outer-group{		
    	width: 100%;
		height: 100%;
		transition: transform 2s;
    	-webkit-transition: -webkit-transform 2s;

    	[data-state~='expand'] > .content-group {
    		
    		.icon-group{
    			opacity:1
    		}
    		
		}
	}

	.content-group{
		overflow: visible;

		.icon-group{
			font-size: 22px;
			opacity: 0.4;		
			cursor: pointer;	
		}		

		.detail{
			opacity: 0;
			font-size: 15px;
			cursor: pointer;

			&.enter{
				animation: fadeIn 0.75s ease-in 0s 1 normal forwards;
				-webkit-animation: fadeIn 0.75s ease-in 0s 1 normal forwards;
			}

			&.exit{
				animation: fadeIn 0.2s ease-in 0s 1 reverse backwards;	
				-webkit-animation: fadeIn 0.2s ease-in 0s 1 reverse backwards;
			}
		}
	}

	.center-group{
		
		transition: transform 2s;
    	-webkit-transition: -webkit-transform 2s;
    	transform-origin: center center;
    	-webkit-transform-origin: center center;

		&.state-icon{

				text.head,text.subhead,text.detail,image,rect{
					display: none;
				}
		}

		&.state-image{
				text.icon,text.head{
					display: none;
				}
		}

		&.state-text{
				text.icon,image,rect{
					display: none;
				}
		}
	}
}


@keyframes fadeIn{
	0%{
		opacity: 0;
	}

	100%{
		opacity: 1;
	}
}

@-webkit-keyframes fadeIn{
	0%{
		opacity: 0;
	}

	100%{
		opacity: 1;
	}
}


View Compiled
function PieInfographic(stats) {

	this.stats = stats;
	this.render = render;

	this._angles = {};
	this._pathNodes = [];
	this._contentNodes = [];
	this._currentPie = -1;
	this._center = 0;
	this._radius = 0;
	this._centerCircleGroup = null;
	this._performAction = _performAction;
	this._drawPie = _drawPie;
	this._rotateGraphicToPie = _rotateGraphicToPie;
	this._createDOMForCenter = _createDOMForCenter;
	this._getArcPositionsForState = _getArcPositionsForState;
	this._setDataAttributes = _setDataAttributes;
	this._getNextIncrement = _getNextIncrement;	
	this._attachContent = _attachContent;
	this._detailAnimations = [];

	function render(parentEl) {

		var self = this;

		var svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
			parentGroupEl = document.createElementNS('http://www.w3.org/2000/svg', 'g'),			
			svgDimensions, center, radius,
			baseAngle, expandedAngle, shrunkAngle;

		svgEl.setAttribute('style', 'width:100%;height:100%');

		if (typeof(parentEl) !== undefined) {
			parentGroupEl.classList.add('outer-group');
			svgEl.appendChild(parentGroupEl);
			parentEl.appendChild(svgEl);
		}

		svgDimensions = {
			width: svgEl.clientWidth || svgEl.parentNode.clientWidth,
			height: svgEl.clientHeight || svgEl.parentNode.clientHeight
		};

		center = {
			x: svgDimensions.width / 2,
			y: svgDimensions.height / 2
		};

		radius = svgDimensions.width * 0.4; //Occupying 80% of the SVG size

		self._center = center;
		self._radius = radius;

		baseAngle = 2 * Math.PI / this.stats.length;
		expandedAngle = Math.min(2 * Math.PI - (0.15 * Math.PI * this.stats.length), baseAngle * 4, Math.PI); //Pie, on expansion to 3 times, should leave atleast 36 degrees for each other pie, and cannot be more than 180 degrees.
		shrunkAngle = (2 * Math.PI - expandedAngle) / (stats.length - 1); //Angle when a pie is shrunk

		self._angles.baseAngle = baseAngle;
		self._angles.expandedAngle = expandedAngle;
		self._angles.shrunkAngle = shrunkAngle;

		for(var i=0; i<stats.length;i++){
			var pathEl = document.createElementNS('http://www.w3.org/2000/svg', 'path'),
			contentEl = document.createElementNS('http://www.w3.org/2000/svg', 'g'),
			textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text'),
			groupEl = document.createElementNS('http://www.w3.org/2000/svg', 'g');
			pathEl.setAttribute('fill', stats[i].color);
			
			groupEl.addEventListener('click',function(event){

					if(this.getAttribute('data-state') === 'expand'){
						return;
					}

					var indexInParent = self._pathNodes.indexOf(this.getElementsByTagName('path')[0]),
					currentGroupEl;

					if(self._currentPie >= 0){
						currentGroupEl = self._pathNodes[self._currentPie];
						_hideContentForPie(currentGroupEl.nextSibling,self._detailAnimations[1]);
					}

					//Bring all pies to normal state
					self._performAction('normal',0,true,function(){


						_setStateForCenter(self._centerCircleGroup,'icon',{
							icon: stats[indexInParent].icon
						});

						//Rotate the graphic to center the clicked pie.
						self._rotateGraphicToPie(indexInParent,function(){
						
							//Expand/shrink pies based on the clicked pie.
							self._performAction('expand', indexInParent,true,function(){

								_renderContentForPie(self._pathNodes[indexInParent].nextSibling,self._center,self._radius,self._detailAnimations);
								self._currentPie = indexInParent;

							});
						});							
					});

					event.stopPropagation();
			});

			this._pathNodes.push(groupEl.appendChild(pathEl));
			contentEl.classList.add('content-group');
				
			this._contentNodes.push(groupEl.appendChild(contentEl));
			parentGroupEl.appendChild(groupEl);
		}
		
		self._createDOMForCenter(svgEl);

		//Create the clip-path defs.			
		var clipPath = _createSVGElement('clipPath',{
			'id':'detailClipPath'
		}),
		clipCircle = _createSVGElement('circle',{
			'r':'21.5',
			'stroke':'white'
		}),
		detailAnimationEnter = _createSVGElement('animate',{
			id:'animation-enter',
			attributeName : 'opacity',
            from: 0,
            to : 1,
            dur: '1s',
            begin: '0s',
            fill: 'freeze'
		}),
		detailAnimationExit = _createSVGElement('animate',{
			id:'animation-exit',
			attributeName : 'opacity',
            from: 1,
            to : 0,
            dur: '1s',
            begin: '0s',
            fill: 'freeze'
		});

		clipPath.appendChild(clipCircle);
		
		_addDefinition(svgEl,clipPath);

		//SVG based opacity-animation since Safari does not support CSS opacity animation on SVG group tags.
		this._detailAnimations.push(detailAnimationEnter);
		this._detailAnimations.push(detailAnimationExit);
		
		this._attachContent();

		this._performAction('normal');

		_setStateForCenter(self._centerCircleGroup,'text',{
			head: '8',
			subhead: 'leading',
			detail: 'companies'
		});
	}

	function _rotateGraphicToPie(index,callback){
		var parentGroupEl = this._pathNodes[0].parentNode.parentNode,
		angleFrom  = parseFloat(parentGroupEl.getAttribute('data-offset-angle')) || 0,
		angleTo = -1 * (index * this._angles.baseAngle * 180/ Math.PI);

		if(angleFrom === angleTo){
			if(callback)
				callback();
		}

		angleTo += Math.ceil(angleFrom/360)*360;

		if(angleTo > angleFrom)
			angleTo -= 360; //angleTo should always be less than angleFrom for a anti-clockwise rotation.

		if(callback)
			parentGroupEl.addEventListener('transitionend',function(event){
				callback();
				event.target.removeEventListener(event.type, arguments.callee);
			});

		_setTransformProp(parentGroupEl,'rotate',(angleTo) + 'deg');		

		for(var i = 0; i < this._contentNodes.length;i++){

			var icon = this._contentNodes[i].querySelector('g.icon-group');

			icon.transition = 'transform 2s';
			icon.style.webkitTransition = '-webkit-transform 2s';
					
			icon.addEventListener('transitionend',function(event){
					event.target.style.transition = '';
					event.target.style.webkitTransition = '';
					event.target.removeEventListener(event.type, arguments.callee);
			});	

			_setTransformProp(icon,'rotate',(-1 * angleTo) + 'deg');
		}

		parentGroupEl.setAttribute('data-offset-angle',angleTo);
	}

	function _createDOMForCenter(svgEl){

		//Create the circle in the center
		var circleEl = _createSVGElement('circle',{
			'fill':'#393D45',
			'cx': this._center.x,
			'cy': this._center.y,
			'r' : this._radius * 0.4			
		}),
		clipCircle = circleEl.cloneNode();

		clipCircle.setAttribute('cx',this._radius * 0.4);
		clipCircle.setAttribute('cy',this._radius * 0.4);		
		clipCircle.removeAttribute('fill');

		var clipPath = _createSVGElement('clipPath',{
			id: 'centerClipPath'
		});

		clipPath.appendChild(clipCircle);

		var textIcon = _createSVGElement('text',{
			x :  this._radius * 0.4,
			y :  this._radius * 0.4,
			class : 'icon',
			'font-family':'FontAwesome',
			'text-anchor':'middle',
			'dominant-baseline':'central',
			'font-size': '60px'
		}),textHead = _createSVGElement('text',{
			x :  this._radius * 0.4,
			y :  this._radius * 0.3,
			class : 'head',			
			'text-anchor':'middle',
			'dominant-baseline':'central',
			'font-size': '40px'
		}),
		textSubHead = _createSVGElement('text',{
			x :  this._radius * 0.4,
			y :  this._radius * 0.6,
			class : 'subhead',
			'font-size' : '10px',
			'text-anchor':'middle',
			'dominant-baseline':'central'
		}),
		textDetail = _createSVGElement('text',{
			x :  this._radius * 0.4,
			y :  this._radius * 0.7,
			class : 'detail',			
			'font-size' : '10px',
			'text-anchor':'middle',
			'dominant-baseline':'central'
		}),
		image = _createSVGElement('image',{
			width :  this._radius,
			height :  this._radius,
			x: '-' + this._radius * 0.1,
			y: '-' + this._radius * 0.1,
			fill: 'white',
			'clip-path':'url(#centerClipPath)'
		}),
		detailRect = _createSVGElement('rect',{
			width :  this._radius * 0.8,
			height :  this._radius * 0.4,
			y: this._radius * 0.55,
			class: 'detail-group',
			'fill':'#393D45',	
			'clip-path':'url(#centerClipPath)'
		}),
		contentGroup = _createSVGElement('g',{
			width :  this._radius * 0.8,
			height :  this._radius * 0.8,
			class: 'center-group',
			fill: 'white'			
		});

		_setTransformProp(contentGroup,'translate',(this._center.x - this._radius*0.4) + 'px,' + 
				(this._center.y - this._radius*0.4)+'px');
				
		contentGroup.appendChild(image);
		contentGroup.appendChild(textIcon);		
		contentGroup.appendChild(textHead);
		contentGroup.appendChild(detailRect);
		contentGroup.appendChild(textSubHead);
		contentGroup.appendChild(textDetail);
		

		svgEl.appendChild(circleEl);

		_addDefinition(svgEl,clipPath);

		this._centerCircleGroup = svgEl.appendChild(contentGroup);		
	}

	function _setStateForCenter(circleEl,state,details){		

		var textIcon,textHead,textSubHead,textDetail,image;

		textIcon = circleEl.querySelectorAll('text.icon')[0];
		textHead = circleEl.querySelectorAll('text.head')[0];
		textSubHead = circleEl.querySelectorAll('text.subhead')[0];
		textDetail = circleEl.querySelectorAll('text.detail')[0];
		image = circleEl.querySelectorAll('image')[0];		

		//@TODO: Animate out the current state and then do the below.

		//Set data for next state of the circle
		if(state === 'icon'){
			//textIcon.textContent = '\uf0e1';
			textIcon.textContent = details.icon;
		}else if(state === 'text'){
			textHead.textContent = details.head.toUpperCase();
			textSubHead.textContent = details.subhead.toUpperCase().replace('-',' ').replace('-',' ');
			textDetail.textContent = details.detail.toUpperCase();
		}else if(state === 'image'){
			image.setAttributeNS('http://www.w3.org/1999/xlink','xlink:href',details.image);
			textSubHead.textContent = details.subhead.toUpperCase();
			textDetail.textContent = details.detail.toUpperCase();
		}

		//Set classes for layout for next state of the circle
		['icon','image','text'].forEach(function(stateIterator){
			if(state !== stateIterator)
				circleEl.classList.remove('state-'+stateIterator);
			else
				circleEl.classList.add('state-'+stateIterator);
		});

		//@TODO: Animate in the new state.
	}

	function _performAction(state,index,animate,callback){

			var self = this,
			center = self._center,
			radius = self._radius,
			arcPositions = [];

			if(!animate){			

				arcPositions = self._getArcPositionsForState(state);

				for(var i=0; i< self._pathNodes.length;i++){								
					self._drawPie(i,_getPathDescForArcPositions(center,radius,arcPositions[i][0],arcPositions[i][1]));					
				}	
				
				self._setDataAttributes(state,index);

				if(callback)
					callback();

			} else{

				var targetAngles = [],
				increments = [],
				arcPositions = self._getArcPositionsForState(state,index);

				for(var i=0; i< arcPositions.length; i++){
					
					targetAngles.push([
						_cartesianToPolar(center,arcPositions[i][0]).theta,
						_cartesianToPolar(center,arcPositions[i][1]).theta
					]);

					//Calculating first set of increments
					increments.push(self._getNextIncrement(self._pathNodes[i],
							targetAngles[i],
						(i === index && state === 'expand')? radius*1.05 : null));
				}


				function drawIncrement(){
					
					for (var i = 0; i < increments.length; i++) {
						if(increments[i] !== null)
							self._drawPie(i,increments[i]);
					};

					increments = [];
					var nextFrameRequestNeeded = false;

					//Get the next set of increments
					for(i=0; i < self._pathNodes.length; i++){
						
						increments.push(self._getNextIncrement(self._pathNodes[i],
							targetAngles[i],
							(i === index && state === 'expand')? radius*1.05 : null));

						if(increments[i] !== null){
							nextFrameRequestNeeded = true;
						}
					}

					//Request next frame only if there is atleast one path that needs to be drawn.
					if(nextFrameRequestNeeded)
						_requestAnimationFrame(drawIncrement);
					else{
						self._setDataAttributes(state,index);
						if(callback)
							callback();
					}
				}

				_requestAnimationFrame(drawIncrement);
			}
		}


		//Calculate arc start-end positions for each pie based on the state.
		function _getArcPositionsForState(state,index){

			var self = this,
			center = self._center,
			radius = self._radius,
			baseAngle = self._angles.baseAngle,
			expandedAngle = self._angles.expandedAngle,
			shrunkAngle = self._angles.shrunkAngle,
			arcPositions = [],angleOffset;

			if(state === 'normal'){

					angleOffset = self._angles.baseAngle/2;
					
					for(var i=0;i<stats.length;i++){
						arcPositions.push([
							_polarToCartesian(center,radius,angleOffset),
							_polarToCartesian(center,radius,angleOffset - baseAngle)
						]);
						angleOffset-=baseAngle;
					}					

			}else if(state === 'expand'){
					index = index || 0;

					if(index === 0){
						angleOffset = self._angles.expandedAngle/2;

					}else{
						angleOffset = -1 * (index * baseAngle);
						angleOffset += expandedAngle/2 + index * shrunkAngle;						
					}

					angleOffset += shrunkAngle/2;

					for(var i=0;i<stats.length;i++){

						var arcPoints = [ _polarToCartesian(center,radius,angleOffset) ]; //Pushing the start point

						if(i === index){
							angleOffset -= expandedAngle;

							if(angleOffset < -2 * Math.PI)
								angleOffset += 2 * Math.PI;

							arcPoints.push(_polarToCartesian(center,radius,angleOffset));
							
						}else{
							angleOffset -= shrunkAngle;

							if(angleOffset < -2*Math.PI)
								angleOffset += -2*Math.PI;

							arcPoints.push(_polarToCartesian(center,radius,angleOffset));
							
						}
						arcPositions.push(arcPoints);
					}
			}

			return arcPositions;
		}

		function _getNextIncrement(el,targetAngles,targetRadius){
			
			var self = this,
			center = self._center,
			radius = targetRadius || self._radius,
			pieTargetAngles = [];
			currentArcPoints = _getArcCoordinates(el);

			Array.prototype.push.apply(pieTargetAngles,targetAngles)
			
			var sourceAngles = [ _cartesianToPolar(center,currentArcPoints[0]).theta,
				_cartesianToPolar(center,currentArcPoints[1]).theta
			];			

			if(Math.abs(pieTargetAngles[0] - sourceAngles[0]) <= Math.pow(10,-10)  && 
				Math.abs(pieTargetAngles[1] - sourceAngles[1]) <= Math.pow(10,-10)){
				//If source and target angles are equal to precision of 10 decimals, stop the increments.
				return null;
			}

			//Calculate shorter route to the targetAngles.
			if(Math.abs(pieTargetAngles[0] - sourceAngles[0]) > Math.PI){
				if(pieTargetAngles[0] < sourceAngles[0])
					pieTargetAngles[0] += 2 * Math.PI;
				else
					sourceAngles[0] += 2 * Math.PI;
			}

			if(Math.abs(pieTargetAngles[1] - sourceAngles[1]) > Math.PI){
				if(pieTargetAngles[1] < sourceAngles[1])
					pieTargetAngles[1] += 2 * Math.PI;
				else
					sourceAngles[1] += 2 * Math.PI;
			}				

			if(sourceAngles[0] < pieTargetAngles[0])
				sourceAngles[0] = Math.min(sourceAngles[0]+0.1,pieTargetAngles[0]);
			else if(sourceAngles[0] > pieTargetAngles[0])
				sourceAngles[0] = Math.max(sourceAngles[0]-0.1,pieTargetAngles[0]);

			if(sourceAngles[1] < pieTargetAngles[1])
				sourceAngles[1] = Math.min(sourceAngles[1]+0.1,pieTargetAngles[1]);
			else if(sourceAngles[1] > pieTargetAngles[1])
				sourceAngles[1] = Math.max(sourceAngles[1]-0.1,pieTargetAngles[1]);

			return _getPathDescForArcPositions(center,radius,
					_polarToCartesian(center,radius,sourceAngles[0]),
					_polarToCartesian(center,radius,sourceAngles[1]));	

		}
		
		function _getContentPositionForArc(center,radius,arcPoints){
			
			var arcStartAngle = _cartesianToPolar(center,arcPoints[0]).theta,
			arcEndAngle = _cartesianToPolar(center,arcPoints[1]).theta,
			contentAngle = arcStartAngle + (arcEndAngle - arcStartAngle)/2;

			if(Math.abs(arcEndAngle - arcStartAngle) > Math.PI)
				contentAngle -= Math.PI;

			return _polarToCartesian(center,radius*0.7,contentAngle);

		}

		function _setDataAttributes(state,index){

			var self  = this;

			for(var i=0;i<self._pathNodes.length;++i){
					if(state === 'normal')
						self._pathNodes[i].parentNode.setAttribute('data-state',state);
					else if(state === 'expand' && i === index)			
						self._pathNodes[i].parentNode.setAttribute('data-state',state);
					else
						self._pathNodes[i].parentNode.setAttribute('data-state','shrink');
			}			
		}

		function _attachContent(){
			var stats = this.stats,
			contentNodes = this._contentNodes;

			for(var i=0;i<stats.length;i++){
				_attachContentForPie(contentNodes[i],stats[i],this._center,this._radius);
			}
		}

		//Utility functions below

		function _drawPie(index,pathDesc){
			var self = this,
			pathEl = self._pathNodes[index],
			contentEl = self._contentNodes[index],
			translateTo = '';

			pathEl.setAttribute('d',pathDesc);

			contentPoints = _getContentPositionForArc(self._center,self._radius,_getArcCoordinates(pathEl));
			translateTo = (contentPoints.x) + 'px,'  + (contentPoints.y) + 'px';			

			_setTransformProp(contentEl,'translate',translateTo);			
		}

		function _attachContentForPie(el,stats,center,radius){

			var details = stats.details;			

			var centerCircleEl = el.closest('svg').querySelector('g.center-group');

			var icon = _createSVGElement('text',{
				'font-family':'FontAwesome',
				'x':'0',
				'y':'0',				
				'fill':'white',
				'text-anchor':'middle',
				'dominant-baseline':'central'
			}),
			iconGroup = _createSVGElement('g',{
				'class':'icon-group'
			});
			
			icon.textContent = stats.icon;
			iconGroup.appendChild(icon);

			el.appendChild(iconGroup);

			for(var key in details){
				
				var detailGroup = _createSVGElement('g'),
				background = _createSVGElement('circle'),				
				detailEl;

				if(typeof details[key].image !== 'undefined'){

					detailEl = _createSVGElement('image',{
							width: '43px',
							height: '43px',
							x:'-21.5',
							y: '-21.5',
							'clip-path':'url(#detailClipPath)'
					});

					detailEl.setAttributeNS('http://www.w3.org/1999/xlink','xlink:href',details[key].image);
					
					detailGroup.addEventListener('click',function(currentKey){
						
						return function(event){

							if(event.currentTarget.closest('g[data-state="shrink"]') ||
								event.currentTarget.closest('g[data-state="normal"]')){								
								return true;
							}

							_setStateForCenter(centerCircleEl,'image',{
								image: details[currentKey].image,
								subhead: details[currentKey].label,
								detail: currentKey+','+stats.name
							});
							return false;
						};
						
					}(key));

				}else{

					detailEl = _createSVGElement('text',{
						'text-anchor':'middle',
						'dominant-baseline':'central'
					});

					detailEl.textContent = details[key].toUpperCase();

					detailGroup.addEventListener('click',function(currentKey){
						
						return function(event){

							if(event.currentTarget.closest('g[data-state="shrink"]') ||
								event.currentTarget.closest('g[data-state="normal"]')){								
								return true;
							}

							_setStateForCenter(centerCircleEl,'text',{
								head:details[currentKey],
								subhead: currentKey,
								detail: stats.name
							});
							return false;
						};

					}(key));					
				}

				background.setAttribute('fill','white');
				background.setAttribute('r','22.5');				

				detailGroup.appendChild(background);
				detailGroup.appendChild(detailEl);
				detailGroup.classList.add('detail');
				
				el.appendChild(detailGroup);
			}

		}

		function _renderContentForPie(el,center,radius,animations){

			var detailsGroupEl = el.querySelectorAll('g.detail'),
			offsetAngle = parseFloat(el.closest('g.outer-group').getAttribute('data-offset-angle')),
			arcPoints = _getArcCoordinates(el.parentNode.querySelector('path')),
			arcStartAngle = _cartesianToPolar(center,arcPoints[0]).theta,
			arcEndAngle = _cartesianToPolar(center,arcPoints[1]).theta,			
			dividingAngle = Math.abs(arcEndAngle - arcStartAngle), 
			centerCoordinates = _getContentPositionForArc(center,radius,arcPoints),
			centerAngle = _cartesianToPolar(center,centerCoordinates).theta;

			if(Math.abs(arcEndAngle - arcStartAngle) > Math.PI){
				dividingAngle = 2*Math.PI - dividingAngle;
			}

			dividingAngle = dividingAngle/(detailsGroupEl.length + 1);	
			dividingAngle = dividingAngle * 1.25; //spacing out the detail icons.

			for(var i=0;i<detailsGroupEl.length;i++){

				var detailGroup = detailsGroupEl[i],
				angle = centerAngle - (i - Math.floor(detailsGroupEl.length/2)) * dividingAngle;

				if(angle < -2*Math.PI)
					angle += 2 * Math.PI;

				position = _polarToCartesian(center,radius * 0.7, angle);
				position.x += detailGroup.clientWidth/2;
				position.y += detailGroup.clientHeight/2;

				_setTransformProp(detailGroup,'translate',(position.x - centerCoordinates.x)+'px, ' + (position.y - centerCoordinates.y)+'px');
				_setTransformProp(detailGroup,'rotate',(-1 * offsetAngle) + 'deg');

				if(detailGroup.lastElementChild.tagName === 'animate')
					detailGroup.lastElementChild.remove();

				detailGroup.appendChild(animations[0].cloneNode());				
			}

			//TODO: Animate the details in after positioning.
		}

		function _hideContentForPie(el,animation){
			var detailsGroupEl = detailsGroupEl = el.querySelectorAll('g.detail');

			for(var i=0; i < detailsGroupEl.length; i++){
				if(detailsGroupEl[i].lastElementChild.tagName === 'animate')
					detailsGroupEl[i].lastElementChild.remove();

				detailsGroupEl[i].appendChild(animation.cloneNode());
			}
		}


		function _getPathDescForArcPositions(center, radius, startPoint, endPoint) {
			var points = 'M' + center.x + ',' + center.y;

			points += ' L ' + startPoint.x + ' ' + startPoint.y;

			points += ' A ' + radius + ' ' + radius +
				' 1 0 1 ' + endPoint.x + ' ' + endPoint.y;
			
			points += ' L ' + center.x + ' ' + center.y;
			points += ' Z';

			return points;
		}

		function _polarToCartesian(center,radius,angle){
			return {
				x: center.x + (Math.cos(angle) * radius),
				y: center.y - (Math.sin(angle) * radius)	
			};	
		}

		function _cartesianToPolar(center,point){

			var theta = Math.atan2(point.y - center.y,point.x - center.x);

			if(theta > 0)
				theta *= -1; 
			else if(theta < 0)
				theta = -1 * (2*Math.PI + theta);

			return {
				r: Math.sqrt(Math.pow(point.y - center.y,2) + Math.pow(point.x - center.x,2)),
				theta: theta
			};	
		}

		function _getArcCoordinates(pathEl){

			//Need to use data-attributes to get these co-ordinates instead of parsing the desc attribute.	
			var desc = pathEl.getAttribute('d'),
			startPoints = desc.split('L ')[1].split(' A')[0].split(' '),
			endPoints = desc.split('1 0 1 ')[1].split(' L')[0].split(' ');

			return [{
				x: parseFloat(startPoints[0]),
				y: parseFloat(startPoints[1])
			},{
				x: parseFloat(endPoints[0]),
				y: parseFloat(endPoints[1])
			}];
		}		

		function _createSVGElement(tag, attrs) {
			var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
			
			if(attrs){
				for (key in attrs)
					el.setAttribute(key, attrs[key]);				
			}
			
			return el;
		}

		function _addDefinition(svgEl,defEl){
			var defs = svgEl.querySelectorAll('defs')[0];

			if(!defs || defs.length === 0){
				defs = svgEl.appendChild(_createSVGElement('defs'));
			}
			defs.appendChild(defEl);
		}

		function _setTransformProp(el,prop,value){

			var currentTransform = el.style.transform,
			targetTransform;

			if(currentTransform === undefined)
				currentTransform = el.style.webkitTransform;
			 
			if(currentTransform === undefined)
				currentTransform = el.style.MozTransform;
					


			if(currentTransform.indexOf(prop) === -1)
				targetTransform = currentTransform + prop + '(' + value + ')';
			else{
				var oldValue = currentTransform.split(prop+'(')[1]
								.split(')')[0];
				targetTransform = currentTransform.replace(oldValue,value);
			}

			el.style.webkitTransform = el.style.transform = targetTransform;

		}

		function _requestAnimationFrame(callback){
			var requestAnimationFrame = window.requestAnimationFrame
			    || window.webkitRequestAnimationFrame
			    || window.mozRequestAnimationFrame
			    || window.msRequestAnimationFrame
			    || function(callback) { return setTimeout(callback, 1000 / 60); };

			    requestAnimationFrame(callback);
		}
}

window.onload = function() {

	//Create implementation of closest() for Safari.
	(function (ELEMENT) {
		ELEMENT.matches = ELEMENT.matches
			|| ELEMENT.msMatchesSelector
			|| ELEMENT.mozMatchesSelector
			|| ELEMENT.webkitMatchesSelector;

		ELEMENT.closest = ELEMENT.closest || function (selector) {
			var node = this;

			while (node) {
				if (node.matches(selector)) {
					break;
				}
				node = node.parentElement;
			}

			return node;
		};
	}(Element.prototype));


	var graphicWrapper = document.getElementsByTagName('div')[0],
		graphic = new PieInfographic([{
			color: '#5793F3',
			icon: '\uf09a',
			name: 'facebook',
			details: {
				'revenue':'$12B', 
				'ceo':{
							'label': 'Mark Zuckerberg',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/mark-zuckerberg.jpg'
					},
				'no-of-employees':'10k'			
			}
		}, {
			color: '#DD4D79',
			icon: '\uf1a0',
			name: 'google',
			details: {
				'revenue':'$66B', 
				'ceo':{
							'label': 'Larry Page',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/larry-page.jpg'
					},
				'no-of-employees':'47k'			
			}			
		}, {
			color: '#BD3B47',
			icon: '\uf0e1',
			name: 'linkedIn',
			details: {
				'revenue':'$2.2B', 
				'ceo':{
							'label': 'Jeff Weiner',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/jeff-weiner.jpg'
					},
				'no-of-employees':'7.6k'			
			}			
		}, {
			color: '#DD4444',
			icon: '\uf1ed',
			name: 'payPal',
			details: {
				'revenue':'$7.9B', 
				'ceo':{
							'label': 'Dan Schulman',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/dan-schulman.jpg'
					},
				'no-of-employees':'13k'			
			}			
		}, {
			color: '#FD9C35',
			icon: '\uf099',
			name: 'twitter',
			details: {
				'revenue':'$1.4B', 
				'ceo':{
							'label': 'Dick Costolo',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/dick-costolo.jpg'
					},
				'no-of-employees':'2k'			
			}			
		}, {
			color: '#FEC42C',
			icon: '\uf17a',
			name: 'microsoft',
			details: {
				'revenue':'$86B', 
				'ceo':{
							'label': 'Satya Nadella',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/satya-nadella.jpg'
					},
				'no-of-employees':'128k'			
			}			
		}, {
			color: '#D4Df5A',
			icon: '\uf16b',
			name: 'dropbox',
			details: {
				'revenue':'$0.2B', 
				'ceo':{
							'label': 'Drew Houston',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/drew-houston.jpg'
					},
				'no-of-employees':'600'			
			}			
		}, {
			color: '#5578C2',
			icon: '\uf179',
			name: 'apple',
			details: {
				'revenue':'$199B', 
				'ceo':{
							'label': 'Tim Cook',
							'image': 'https://raw.githubusercontent.com/shalabhvyas/codepens/master/pie-inforgraphic/images/tim-cook.jpg'
					},
				'no-of-employees':'98k'			
			}			
		}]);

	graphic.render(graphicWrapper);
};
Run Pen

External CSS

  1. https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.