/*!
*	jQuery - barIndicator
*	A jQuery plugin that helps you visualize percentage or absolute amounts with bars
*	Author: Ioannis Kapantzakis
*	Released under the MIT License
*/ 
;(function($, window, document, undefined) {
	
	var pluginName = 'barIndicator';
	
	function Plugin(element, options, selector) {
		this.el = element;
		this.$el = $(element);		
		this.opt = $.extend({}, $.fn[pluginName].defaults, options );
		this.selector = selector;
		this._init();
	}
	
	Plugin.prototype = {
		
		//Private methods -------------------------------------------------------------------------------- //
		_init: function() {
			var that = this;
			var $el = that.$el;
			var opt = that.opt;
			var selector = that.selector;
			var style = opt.style;
			var data = opt.data;
			var type = opt.numType;
			var dec = opt.lbDecimals;	
			var $elID = $el.attr('id');
			
			var orText = $el.text();
			var orClass = $el.attr('class');

			if (data && !isNaN(data)) {
				var num = parseFloat(data).toFixed(dec);
			} else if (data == false) {
				var num = parseFloat(orText.replace(',','.')).toFixed(dec);
			} else {
				console.log('data are not valid');
			}
			//Get length object
			var paramsLength = {
				that: that,
				num: num
			}
			var lengthObj = Plugin.prototype._getLength.apply(this, [paramsLength]);
			var lbNum = lengthObj.lbNum;
			var barLength = lengthObj.barLength;
			
			//Add unique data-biID
			var paramsID = {
				that: that
			}
			var unID = Plugin.prototype._getUniqueID.apply(this, [paramsID]);
			$el.attr('data-biID', unID);
			
			//Add classes (bi-wrp + theme class)
			$el.addClass(opt.wrpClass + ' ' + opt.theme).attr('data-lbNum', lbNum);
			
			//Store original attributes (applied on destroy)
			$.data($el, 'storedAttr', {
				'selector': selector,
				'orClass': orClass,
				'orText': orText,
				'barLength': barLength,
				'num': num,
				'lbNum': lbNum,
				'numType': type
			});
			//console.log($.data($el, 'storedAttr'));
			
			// Build and get inner html ------------------------------------------------------------------------------------------------- //
			var numMin = opt.numMin;
			var numMax = opt.numMax;
			var numMinLabel = opt.numMinLabel;
			var numMaxLabel = opt.numMaxLabel;
			var numMinLbLeft = (opt.numMinLbLeft && !isNaN(opt.numMinLbLeft)) ? opt.numMinLbLeft : '';
			var numMaxLbRight = (opt.numMaxLbRight && !isNaN(opt.numMaxLbRight)) ? opt.numMaxLbRight : '';
			var numMinLbTop = (opt.numMinLbTop && !isNaN(opt.numMinLbTop)) ? opt.numMinLbTop : '';
			var numMaxLbTop = (opt.numMaxLbTop && !isNaN(opt.numMaxLbTop)) ? opt.numMaxLbTop : '';
			var numLabelEdge = '';
			if (numMinLabel) {
				numLabelEdge += '<span class="bi-labelEdge bi-edge-min" style="left:' + numMinLbLeft + 'px;top:' + numMinLbTop + 'px">' + numMin + '</span>';
			}
			if (numMaxLabel) {
				numLabelEdge += '<span class="bi-labelEdge bi-edge-max" style="right:' + numMaxLbRight + 'px;top:' + numMaxLbTop + 'px">' + numMax + '</span>';
			}
			if (style == 'vertical') {	
				var lb = opt.vertLabelPos;		
				var w = opt.vertBarWidth;
				var h = opt.vertBarHeight;
				var elemH = $el.css('height');
				var va = opt.vertLabelAlign;				
				$el.addClass('bi-vertical');
				
				if (h == 'line') {
					var bh = elemH;
				} else if (h.indexOf('%') != -1) {
					//Percent of line height
					var bh = parseFloat(elemH) * (parseFloat(h.replace('%','')) / 100);
				} else if (h.indexOf('px') != -1) {
					var bh = h.replace('px','');
				}
					
				var bar = '<div class="bi-bar" style="width:' + w + 'px;height:' + bh + '">' + numLabelEdge + '<div class="bi-barInner"></div></div>';
				if (lb == 'right') {
					var label = '<span class="bi-label bi-label-r" style="vertical-align:' + va + '">' + lbNum + '</span>';
					var inner = bar + label;
				} else if (lb == 'left') {
					var label = '<span class="bi-label bi-label-l" style="vertical-align:' + va + '">' + lbNum + '</span>';
					var inner = label + bar;
				}
			} else if (style == 'horizontal') {
				var bh = opt.horBarHeight;
				var lbPos = opt.horLabelPos;
				var ttl = opt.horTitle;
								
				var label = '<span class="bi-label">' + lbNum + '</span>';
				var bar = '<div class="bi-bar" style="height:' + bh + 'px">' + numLabelEdge + '<div class="bi-barInner"></div></div>';	
								
				switch (lbPos) {
					case 'topLeft':
						var horPosClass = 'bi-hor-topLeft';
						var inner = label + bar;
						break;
					case 'topRight':
						var horPosClass = 'bi-hor-topRight';
						var inner = label + bar;
						break;
					case 'left':
						var horPosClass = 'bi-hor-left';
						var inner = label + bar;
						break;
					case 'right':
						var horPosClass = 'bi-hor-right';
						var inner = bar + label;
						break;
				}
				$el.addClass('bi-horizontal ' + horPosClass);
			}
			
			if (style == 'horizontal' && ttl) {
				var t = '';
				if (ttl == 'bi-title-id') {
					t = $el.attr('id');
				} else if (ttl == 'bi-data-title') {
					var dataT = $el.attr('data-title');
					t = (dataT) ? dataT : 'noTitle';
				} else {
					t = ttl;
				}
				var limSpan = '<span class="bi-limSpan"></span>';
				var title = '<span class="bi-titleSpan bi-titleSpan-' + lbPos + '">' + t + ' ' + limSpan + '</span>';
				inner = title + inner;
			}
			
			//Append inner html
			$el.empty().append(inner);
			var bi_bar = $el.find('.bi-barInner');
			var bi_barHolder = $el.find('.bi-bar');
			var bi_label = $el.find('.bi-label');
			//Trigger event
			$(document).trigger('bi.innerContentAppended', [$el]);			
			if ($elID) {
				$(document).trigger('bi_' + $elID + '.innerContentAppended');
			}
			
			if (style == 'horizontal') {
				var lbPos = opt.horLabelPos;
				if (lbPos == 'left' || lbPos == 'right') {
					var wrpW = parseFloat($el.outerWidth());
					var lbW = parseFloat($el.find('.bi-label').outerWidth());
					var barWrpW = wrpW - lbW - 1;
					var lbH = $el.find('.bi-label').outerHeight();
					var topPos = (lbH - bh) / 2;
					//console.log(lbH + ' - ' + bh);
					bi_barHolder.css({'width':barWrpW + 'px','top':topPos + 'px'});					
				}
			}
			
			//ColorRange
			var paramsColorRange = {
				that: that,
				num: num
			}
			var lengthObj = Plugin.prototype._getColorRangeClass.apply(this, [paramsColorRange]);
			
			//Apply colours (if set in options)
			var foreColor = opt.foreColor;
			var backColor = opt.backColor;
			var labelColor = opt.labelColor;
			if (foreColor) {
				var fColor = Plugin.prototype._getColorValue.apply(this, [foreColor]);
				if (fColor) {
					bi_bar.css({'background-color':fColor});
				}
			}
			if (backColor) {
				var bColor = Plugin.prototype._getColorValue.apply(this, [backColor]);
				if (bColor) {
					bi_barHolder.css({'background-color':bColor});
				}
			}
			if (labelColor) {
				var lColor = Plugin.prototype._getColorValue.apply(this, [labelColor]);
				if (lColor) {
					bi_label.css({'color':lColor});
				}
			}
			
			//Set label visibility and position (hover)
			switch (opt.labelVisibility) {
				case 'default':
					var labelVisClass = 'bi-label-vis-default';
					break;
				case 'hover':
					var labelVisClass = 'bi-label-vis-hover';
					var posObj = opt.labelHoverPos;
					var lb = $el.find('.bi-label');
					var posString = '';
					for(n in posObj) {
						switch (n) {
							case 'top':
							case 'left':
							case 'bottom':
							case 'right':
								lb.css({n:posObj[n]});
								break;
						}
					}
					break;
				case 'hidden':
					var labelVisClass = 'bi-label-vis-hidden';
					break;
			}
			$el.addClass(labelVisClass);
			
			//If average (avgActive) is set to true, first calculate the average and then set the milestones
			var avg = opt.avgActive;
			if (avg) {
				var paramsAvg = {
					that: that
				}
				Plugin.prototype._getAverage.apply(this, [paramsAvg]);
			} else {
				//Set milestones
				var mlst = opt.milestones; 
				if (mlst && !$.isEmptyObject(mlst)) {
					var paramsMlst = {
						that: that
					}
					Plugin.prototype._getMilestones.apply(this, [paramsMlst]);
				}
			}
						
			//Load bar -------------------------------------------------------------------------------------------------------- //			
			if (opt.animation) {
				var timeOut = opt.timeout;
				var event = opt.triggerEvent;
				var forceAnim = opt.forceAnim;
				var forceDelay = opt.forceDelay;
				var paramsAnim = {
					that: that,
					bl: barLength
				}
				var paramsCount = {
					that: that,
					target: num
				}
				if (forceAnim == false) {
					if (event == 'load') {
						$(window).on('load', function() {
							Plugin.prototype._animateBar.apply(this, [paramsAnim]);
							if (opt.labelNumCount) {
								Plugin.prototype._labelNumCounter.apply(this, [paramsCount]);
							}
						});
					} else {
						$(document).on(event, function() {
							Plugin.prototype._animateBar.apply(this, [paramsAnim]);
							if (opt.labelNumCount) {
								Plugin.prototype._labelNumCounter.apply(this, [paramsCount]);
							}
						});
					}
				} else {
					setTimeout(function() {
						Plugin.prototype._animateBar.apply(this, [paramsAnim]);
						if (opt.labelNumCount) {
							Plugin.prototype._labelNumCounter.apply(this, [paramsCount]);
						}
					},forceDelay);					
				}
			} else {
				var style = opt.style;
				if (style == 'vertical') {
					bi_bar.css({'height': barLength});
				} else if (style == 'horizontal') {
					bi_bar.css({'width': barLength});
				}
			}
		},
		
		_getUniqueID: function(par) {
			if (par) {
				var that = par.that;
				var opt = that.opt;
				var wrpClass = opt.wrpClass;
				var idnum = 0;
				var nArr = [];
				$('.' + wrpClass).each(function() {
					var dn = $(this).attr('data-biID');
					if (dn) {
						var n = parseInt(dn.replace('bi_', ''));
						nArr.push(n);
					}
				});					
				if (nArr.length > 0) {
					var nmax = nArr.sort(function(a,b) { return b-a });
					idnum = parseInt(nmax) + 1;
				}
				return 'bi_' + idnum;
			}
		},
				
		_getLength: function(par) {
			if (par) {
				var that = par.that;
				var opt = that.opt;
				var type = opt.numType;
				var num = par.num;
				
				if (type == 'percent') {
					var lbNum = num + '%';
					var barLength = num + '%';
				} else if (type == 'absolute') {
					var lbNum = num;
					var min = opt.numMin;
					var max = opt.numMax;
					var barLength = (num / (max - min)) * 100 + '%';
				}
				var lengthObj = {
					lbNum: lbNum,
					barLength: barLength
				};
				
				return lengthObj;
			}
		},
				
		_getColorRangeClass: function(par) {
			if (par) {				
				var that = par.that;
				var num = par.num;
				var $el = that.$el;
				var bar = $el.find('.bi-barInner');
				var opt = that.opt;	
				var barstyle = bar.attr('style');
				if (barstyle) {
					bar.attr('style', barstyle.replace('background-color', ''));
				}
				if (opt.colorRange) {
					var limObj = opt.colorRangeLimits;					
					var allRangeClasses = '';
					for (l in limObj) {						
						var rng = limObj[l].split('-');
						var min = parseFloat(rng[0]);
						var max = parseFloat(rng[1]);
						//If range colour is passed into the options object						
						if (num >= min && num <= max) {
							var rangeClass = 'bi-cRange-' + l;
							if (rng.length == 3) {
								var rngColor = rng[2];
								var rngCl = Plugin.prototype._getColorValue.apply(this, [rngColor]);
								if (typeof rngCl !== 'undefined') {
									bar.css({'background-color':rngCl});
								};
							}
						}
						allRangeClasses += 'bi-cRange-' + l + ' ';
					}					
					//Remove any bi-cRange-* class and add the appropriate one
					$el.removeClass(allRangeClasses).addClass(rangeClass);
				}
			}
		},
				
		_animateBar: function(par) {
			if (par) {
				var that = par.that;
				var $el = that.$el;
				var $elID = $el.attr('id');
				var opt = that.opt;
				var style = opt.style;
				var at = opt.animTime;
				var eas = opt.easing;
				var tm = opt.timeOut;
				var bar = that.$el.find('.bi-barInner');
				var bl = par.bl;
				setTimeout(function() {
					if (style == 'vertical') {
						if (par.reanim) {
							bar.css({'height':0}); 
						}
						bar.animate({'height':bl},at,eas).queue(function() {
							$(document).trigger('bi.animationCompleted');
							if ($elID) {
								$(document).trigger('bi_' + $elID + '.animationCompleted');
							}
							if (par.reanim) {
								$(document).trigger('bi.reanimateBarStop'); 
								if ($elID) {
									$(document).trigger('bi_' + $elID + '.reanimateBarStop');
								}
							}
							if (par.loadData) {
								$(document).trigger('bi.loadDataStop');
								if ($elID) {
									$(document).trigger('bi_' + $elID + '.loadDataStop');
								}
							}
							$(this).dequeue();
						});
					} else if (style == 'horizontal') {
						if (par.reanim) {
							bar.css({'width':0}); 
						}
						bar.animate({'width':bl},at,eas).queue(function() {
							$(document).trigger('bi.animationCompleted');
							if ($elID) {
								$(document).trigger('bi_' + $elID + '.animationCompleted');
							}
							if (par.reanim) {
								$(document).trigger('bi.reanimateBarStop'); 
								if ($elID) {
									$(document).trigger('bi_' + $elID + '.reanimateBarStop');
								}
							}
							if (par.loadData) {
								$(document).trigger('bi.loadDataStop'); 
								if ($elID) {
									$(document).trigger('bi_' + $elID + '.loadDataStop');
								}
							}
							$(this).dequeue();
						});
					}
				},tm);
			}
		},
				
		_labelNumCounter: function(par) {
			if (par) {
				var that = par.that;
				var opt = that.opt;
				var $el = that.$el;
				var label = $el.find('.bi-label');
				var min = opt.numMin;
				var target = parseFloat(par.target);
				var countTime = opt.animTime;
				var decim = opt.lbDecimals;
				var step = opt.counterStep;
				var type = opt.numType; 
				var limLabelPos = opt.limLabelPos;
				
				if (type == 'percent') {
					var sign = '%';
				} else if (type == 'absolute') {					
					var sign = '';
				}
				var i = parseFloat(min);
				var ct = (countTime / (target - i)) * step;
				label.html(min + sign);
				function counter() {
					setTimeout(function() {
						label.html(i.toFixed(decim) + sign);					
						if (i<target) {
							i = Math.min(i + step, target);
							counter();
						} else {
							if (limLabelPos == 'num') { 
								var counterLb = label.closest('.bi-wrp').find('.bi-limSpan').prop('outerHTML');
								if (typeof counterLb != 'undefined') {
									var cntrLb = counterLb;
								} else {
									var cntrLb = '';
								}
								label.html(label.html() + cntrLb);
							}
						}
					},ct);				
				}	
				counter();		
			}
		},
				
		_getColorValue: function(par) {
			if (par) {
				var validHex = new RegExp(/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i);
				var colorNameList = ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige","Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood","CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk","Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey","DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange","DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue","DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink","DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite","ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray","Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo","Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon","LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray","LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen","LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue","LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon","MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple","MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise","MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin","NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed","Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed","PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple","Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown","SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray","SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle","Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"];
				if (par.indexOf('rgb') == -1) {
					if (par.indexOf('#') == 0) {
						if (validHex.test(par)) {
							var fColor = par;
						}
					} else {
						if ($.inArray(par, colorNameList)) {
							var fColor = par;
						}
					}
				} else {
					var fColor = par;
				}
				return fColor;
			}
		},
				
		_getMilestones: function(par) {
			if (par) {
				var that = par.that;
				var $el = that.$el;	
				var $elID = $el.attr('id');
				var opt = that.opt;
				var style = opt.style;
				var mlst = opt.milestones;
				var slf = par.self; 
				var barWrp = (slf || $el).find('.bi-bar');

				//Append milestones
				if (par.mlstObj) {
					mlst = par.mlstObj;
				}	
				for (m in mlst) {	
					var mlstObj = mlst[m];
					var pos = mlstObj.mlPos;
					var mlstId = mlstObj.mlId;
					var mlstClass = mlstObj.mlClass;
					var mlstDim = mlstObj.mlDim;
					var mlstLabel = mlstObj.mlLabel;
					var mlstVis = mlstObj.mlLabelVis;
					var mlstHoverRange = mlstObj.mlHoverRange;
					var mlstLineHeight = mlstObj.mlLineWidth;
					//Get length object
					var paramsLength = {
						that: that,
						num: pos
					}
					var mlstLb = mlstLabel;
					if (opt.avgLabelNum) {
						if (mlstObj.mlClass == 'bi-average-mlst') {							
							mlstLb = mlstLabel + ' ' + mlstObj.mlPos;
							if (opt.numType == 'percent') {
								mlstLb += '%';
							}
						}						
					}
					var lengthObj = Plugin.prototype._getLength.apply(this, [paramsLength]);
					var barLength = lengthObj.barLength;
					var ml = '<span class="bi-milestone bi-mlst_' + m + ' ' + mlstClass + '" data-id="' + mlstId + '" data-pos="' + barLength + '" data-dim="' + mlstDim + '" data-label="' + mlstLabel + '" data-visible="' + mlstVis + '" data-hoverRange="' + mlstHoverRange + '" data-mlLineWidth="' + mlstLineHeight + '">';
					ml += '<span class="bi-mlst-inner"><span class="bi-mlst-innerLine"></span>';
					ml += '<span class="bi-mlst-label"><span class="bi-mlst-labelTxt">' + mlstLb + '</span></span>';
					ml +=' </span></span>';
					barWrp.append(ml);
					var $ml = barWrp.find('.bi-mlst_' + m);
					$(document).trigger('bi.milestoneAppended', [$ml]);
					if ($elID) {
						$(document).trigger('bi_' + $elID + '_' + mlstId + '.milestoneAppended');
					}
				}
				//Give position and (if true)dimensions
				var thisEl = (slf || $el);

				thisEl.find('.bi-milestone').each(function() {
					var ml = $(this);
					var mlId = ml.attr('data-id');
					var mlPos = ml.attr('data-pos');
					var mlDim = ml.attr('data-dim');
					var mlLabel = ml.attr('data-label');
					var mlVis = ml.attr('data-visible');
					var mlHoverRange = ml.attr('data-hoverRange');
					var mlLineWidth = ml.attr('data-mlLineWidth');
					var absolutePull = parseFloat(mlHoverRange) / 2;
					var mlLineAbsPull = parseFloat(mlLineWidth) / 2;
										
					var mlInner = ml.find('.bi-mlst-inner');
					var mlLine = mlInner.find('.bi-mlst-innerLine');
										
					//Get id (if set)
					if (mlId != 'false') {
						ml.attr('id', mlId);
					}					
					//Get position
					if (style == 'vertical') {						
						ml.css({
							'bottom':mlPos,
							'height':mlHoverRange + 'px',
							'margin-bottom': -absolutePull + 'px'
						});
						mlLine.css({
							'height': mlLineWidth + 'px',
							'margin-top': -mlLineAbsPull + 'px'
						});
					} else if (style == 'horizontal') {
						ml.css({
							'left':mlPos,
							'width':mlHoverRange + 'px',
							'margin-left': -absolutePull + 'px'
						});
						mlLine.css({
							'width': mlLineWidth + 'px',
							'margin-left': -mlLineAbsPull + 'px'
						});
					}
					//Get label visibility
					if (mlVis == 'hover') {
						mlInner.addClass('bi-mlst-innerHover').removeClass('bi-mlst-innerVisible bi-mlst-innerHidden');
					} else if (mlVis == 'visible') {
						mlInner.addClass('bi-mlst-innerVisible').removeClass('bi-mlst-innerHover bi-mlst-innerHidden');
					} else if (mlVis == 'hidden') {
						mlInner.addClass('bi-mlst-innerHidden').removeClass('bi-mlst-innerVisible bi-mlst-innerHover');
					}
					//If mlDim -> apply the given dimensions
					if (mlDim) {
						if (mlDim == 'inherit') {
							var d = '100%';
						} else if (mlDim.indexOf('%') != -1 || mlDim.indexOf('px') != -1) {
							var d = mlDim;
						}						
						if (style == 'vertical') {						
							ml.css({'width':d});
							var mlW = ml.css('width');
							var mrgL = parseFloat(mlW.replace('px','')) / 2;
							ml.css({marginLeft: -mrgL + 'px'});
						} else if (style == 'horizontal') {
							ml.css({'height':d});
							var mlH = ml.css('height');
							var mrgT = parseFloat(mlH.replace('px','')) / 2;
							ml.css({marginTop: -mrgT + 'px'});
						}
					}					
				});				
			}
		},
		
		_getAverage: function(par) {
			if (par) {
				var that = par.that;
				var $el = that.$el;
				var opt = that.opt;
				var barWrp = $el.find('.bi-bar');
				
				var avgMlDim = opt.avgMlDim;
				var avgLabel = opt.avgLabel;
				var avgLabelNum = opt.avgLabelNum;
				var avgLabelVis = opt.avgLabelVis;
				var avgLabelHoverRange = opt.avgLabelHoverRange;
				var avgLineHeight = opt.avgLineWidth;
				var lim = opt.limLabel;
				var limMinLabel = opt.limMinLabel;
				var limMaxLabel = opt.limMaxLabel;
				var limMinVisible = opt.limMinVisible;
				var limMaxVisible = opt.limMaxVisible;
				var limLabelPos = opt.limLabelPos;
				
				//Detect data-avgClass attributes
				var dtAvgCl = $el.attr('data-avgClass');
				if (dtAvgCl && dtAvgCl.length > 0) {
					var avgAttr = $el.attr('data-biAvg');
					var sel = $.data($el,'storedAttr').selector;
					var elem = $(sel + '[data-avgClass="' + dtAvgCl + '"]');
					var notInitCount = 0;
					elem.each(function() {	
						var notInit = !$.data(this, 'plugin_' + pluginName);
						if (notInit) {
							notInitCount++;
						}
					});
					if (notInitCount == 1 && elem.length > 1) {
						var sum = 0;
						var i = 1;
						var numArr = [];
						elem.each(function() {		
							var that = $(this);
							if (!that.hasClass('bi-avgCalculated')) {
								var lbNum = parseFloat(that.attr('data-lbNum'));
								that.addClass('bi-avgCalculated');							
								sum += lbNum;
								i++;
								numArr.push(lbNum);
							}
						});
						var avg = sum / (i - 1);
						if (lim) {
							var numArrSorted = numArr.sort(function(a,b) { return a-b });
							var numArrMin = numArrSorted[0];
							var	numArrMax = numArrSorted[numArrSorted.length - 1];
							elem.each(function() {
								var self = $(this);
								if (self.attr('data-lbNum').indexOf(numArrMin) != -1 && limMinVisible) {
									self.addClass('bi-lbNum-min').find('.bi-limSpan').html(limMinLabel).addClass('bi-limSpan-min');
								}
								if (self.attr('data-lbNum').indexOf(numArrMax) != -1 && limMaxVisible) {
									self.addClass('bi-lbNum-max').find('.bi-limSpan').html(limMaxLabel).addClass('bi-limSpan-max');									
								}
								if (limLabelPos == 'num') {
									elem.addClass('bi-limPos-num');
								} else if (limLabelPos == 'title') {
									elem.addClass('bi-limPos-title');
								}
							});
						}
						elem.attr('data-biAvg', avg.toFixed(2));	
						var trigObj = {
							that: par.that,
							sel: sel
						}
						$(document).trigger('bi.dataAvgSet', [trigObj]);
					}	
				}
			}
		},
		
		_setAvgMilestone: function(par) {
			if (par) {
				var that = par.that;
				var $el = par.$el;
				var opt = that.opt;
				var mlst = opt.milestones;
				var avgColorIndicator = opt.avgColorIndicator;
				var avgColorBelowAvg = opt.avgColorBelowAvg;
				var avgColorAboveAvg = opt.avgColorAboveAvg;
				var avgAttr = $el.attr('data-biAvg');
				if (avgAttr && avgAttr.length > 0) {
					var avg = parseFloat(avgAttr);
					var avgObj = {
						avg: {
							mlPos: avg,
							mlId: opt.avgMlId,
							mlClass: opt.avgMlClass,
							mlDim: opt.avgMlDim,
							mlLabel: opt.avgLabel,
							mlLabelVis: opt.avgLabelVis,	 
							mlHoverRange: opt.avgLabelHoverRange,	
							mlLineWidth: opt.avgLineWidth	
						}
					};
					var mlstObj = $.extend({}, mlst, avgObj);
					if (avgColorIndicator) {
						var innerBar = $el.find('.bi-barInner');
						var lbNum = $el.attr('data-lbnum');						
						if (parseFloat(lbNum) > avg) {
							$el.addClass('bi-avgAbove');
							if (avgColorAboveAvg) {
								var colAbove = Plugin.prototype._getColorValue.apply(this, [avgColorAboveAvg]);
								innerBar.css({'background-color':colAbove});
							}
						} else {
							$el.addClass('bi-avgBelow');
							if (avgColorBelowAvg) {
								var colBelow = Plugin.prototype._getColorValue.apply(this, [avgColorBelowAvg]);
								innerBar.css({'background-color':colBelow});
							}
						}
					}
				} else {
					var mlstObj = mlst;
				}				
				if (mlstObj && !$.isEmptyObject(mlstObj)) {					
					var paramsMlst = {
						that: par.that,
						self: par.$el,
						mlstObj: mlstObj
					}					
					Plugin.prototype._getMilestones.apply(this, [paramsMlst]);
				}
			}
		},
		
		//Getters (no chainability) ---------------------------------------------------------------------- //
		getPluginData: function() {			
			var $el = this.$el;
			var pluginData = $.data($el,'storedAttr');
			return pluginData;
		},
		
		//Public methods --------------------------------------------------------------------------------- //
		reanimateBar: function() {
			var $el = this.$el;
			var $elID = $el.attr('id');
			var opt = this.opt;
			var barLength = $.data($el, 'storedAttr').barLength;
			var num = $.data($el, 'storedAttr').num;	

			//Get color range class
			var paramsColorRange = {
				that: this,
				num: num
			}
			var lengthObj = Plugin.prototype._getColorRangeClass.apply(this, [paramsColorRange]);
			
			//Reanimate bar
			var paramsAnim = {
				that: this,
				bl: barLength,
				reanim: true
			}		
			var paramsCount = {
				that: this,
				target: num
			}
			Plugin.prototype._animateBar.apply(this, [paramsAnim]);
			if (opt.labelNumCount) {
				Plugin.prototype._labelNumCounter.apply(this, [paramsCount]);
			}
			//Trigger event
			$(document).trigger('bi.reanimateBarStart');
			if ($elID) {
				$(document).trigger('bi_' + $elID + '.reanimateBarStart');
			}
		},
		
		loadNewData: function(par) {
			if (par) {
				var newNum = par;
				var $elID = this.$el.attr('id');
				//Get length object
				var paramsLength = {
					that: this,
					num: newNum
				}
				var lengthObj = Plugin.prototype._getLength.apply(this, [paramsLength]);
				var lbNum = lengthObj.lbNum;
				var barLength = lengthObj.barLength;
				
				//Get color range class
				var paramsColorRange = {
					that: this,
					num: parseFloat(lbNum)
				}
				var lengthObj = Plugin.prototype._getColorRangeClass.apply(this, [paramsColorRange]);
				
				var paramsAnim = {
					that: this,
					bl: barLength,
					loadData: true
				}
				var paramsCount = {
					that: this,
					target: lbNum
				}	
				Plugin.prototype._animateBar.apply(this, [paramsAnim]);
				if (this.opt.labelNumCount) {
					Plugin.prototype._labelNumCounter.apply(this, [paramsCount]);
				}
				//Trigger event
				$(document).trigger('bi.loadDataStart');
				if ($elID) {
					$(document).trigger('bi_' + $elID + '.loadDataStart');
				}
				//Change plugin stored data
				var storedData = $.data(this.$el, 'storedAttr');
				storedData['barLength'] = barLength;				
				storedData['num'] = parseFloat(newNum);
			}
		},
				
		destroy: function() {
			//console.log('destroy()');
			var $el = this.$el;
			var opt = this.opt;
			var storedData = $.data($el, 'storedAttr'); 
			var orText = storedData.orText;
			var orClass = storedData.orClass;
			$el.removeData()
				.empty()
				.html(orText)
				.attr('class', orClass)
				.removeAttr('data-lbNum data-biid');
		}
		
	}
		
	$.fn[pluginName] = function(options) {
		var args = arguments;	
		var selector = $(this).selector;
		if (options === undefined || typeof options === 'object') {
			return this.each(function() {			
				if (!$.data(this, 'plugin_' + pluginName)) {
					$.data(this, 'plugin_' + pluginName, new Plugin(this, options, selector));
				}
			});	
		} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
			if (Array.prototype.slice.call(args, 1).length == 0 && $.inArray(options, $.fn[pluginName].getters) != -1) {
				var instance = $.data(this[0], 'plugin_' + pluginName);
				return instance[options].apply(instance, Array.prototype.slice.call(args,1));
			} else {
				return this.each(function() {
					var instance = $.data(this, 'plugin_' + pluginName);
					if (instance instanceof Plugin && typeof instance[options] === 'function') {
						instance[options].apply(instance, Array.prototype.slice.call(args, 1));
					}
				});
			}
		}
	}
	
	$.fn[pluginName].getters = ['getPluginData'];
	
	$.fn[pluginName].defaults = {
		wrpClass: 'bi-wrp',
		data: false,
		style: 'horizontal',	
		theme: 'bi-default-theme',
		animation: true,
		animTime: 300,	
		easing: 'easeOutExpo',
		timeout: 0,	
		colorRange: false,	
		colorRangeLimits: {		
			optimal: '0-40',
			alert: '41-70',
			critical: '71-100'
		},
		foreColor: false,	
		backColor: false,
		labelColor: false,		
		labelVisibility: 'default', 
		labelHoverPos: {
			top:'0',
			left:'20px'
		},
		vertLabelPos: 'right',	
		vertLabelAlign: 'middle',	
		horLabelPos: 'topLeft', 
		horTitle: false,
		numType: 'percent',
		lbDecimals: 0,
		numMin: 0,
		numMax: 100,
		numMinLabel: false,	
		numMaxLabel: false,	
		numMinLbLeft: false, 
		numMaxLbRight: false, 
		numMinLbTop: false, 
		numMaxLbTop: false,
		vertBarWidth: 10,
		horBarHeight: 10,
		vertBarHeight: 'line',			
		triggerEvent: 'load',	
		forceAnim: false,
		forceDelay: 100,
		labelNumCount: true,	
		counterStep: 10,		
		milestones: {
			1: {
				mlPos: 50,
				mlId: 'mlst-half',
				mlClass: 'bi-middle-mlst',
				mlDim: 'inherit',
				mlLabel: 'Half',
				mlLabelVis: 'hover',	 
				mlHoverRange: 15,	
				mlLineWidth: 1	
			}
		},
		avgActive: false,
		avgColorIndicator: false,	
		avgColorBelowAvg: false,
		avgColorAboveAvg: false,
		avgMlId: false,
		avgMlClass: 'bi-average-mlst',
		avgMlDim: 'inherit',
		avgLabel: 'Average',
		avgLabelNum: true,	
		avgLabelVis: 'hover',	
		avgLabelHoverRange: 15,	
		avgLineWidth: 1,
		limLabel: true,
		limMinLabel: 'min',
		limMaxLabel: 'max',
		limMinVisible: true,
		limMaxVisible: true,
		limLabelPos: 'num'	
	}
	
	$(document).on('bi.dataAvgSet', function(e,a) {		
		var sel = a.sel;
		$(sel).each(function() {			
			var self = $(this);
			var paramsAvg = {
				that: a.that,
				$el: self
			};
			Plugin.prototype._setAvgMilestone.apply(this, [paramsAvg]);
		});
	});
		
})(jQuery, window, document);
