/*!
 * jQuery JavaScript Plugin
 *
 * Copyright 2010, Christian Boehnke
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * Date: Fri Aug 20 18:04:52 2010 -0500
 */
(function($) {  
	
	/**
	 *	Extend jQuery function
	 */
	$.fn.barGraph = function(jsonDataFile, options) {
		try {
			var options = $.extend({
				'file':	jsonDataFile,
				'json': {}
			}, options);
			
			// enable ajax error handling
			$(document).ajaxError(function(event, request, settings, error) { 
				try {
					if (options && typeof(options['errorHandler']) == 'function') options['errorHandler'](event, request, settings, error);
				}
				catch(e) {
				}
			});
			
			// perform
			return this.each(function() {
				try {
					var obj = this;
					var $this = $(obj);
					
					// one more
					$.fn.barGraph.items ++;
					
					// get JSON data from file and make bargraph
					$.getJSON(jsonDataFile, function(json, textStatus) {
						try {
							options.json = json;
							$.fn.barGraph.make($this, obj, json, options);
							$.fn.barGraph.finishedItems ++;
						}
						catch(e) {
						}
					});
				}
				catch(e) {
				}
			});
		}
		catch(e) {
		}
	};
	
	// properties
	$.fn.barGraph.items = 0;
	$.fn.barGraph.finishedItems = 0;
	
	/**
	 *	Make the given bargraph using the JSON data string,
	 * 	passed as a parameter
	 */
	$.fn.barGraph.make = function(jQueryObj, obj, json, options) {
		try {
			var pos = jQueryObj.position();
			var initActGraph = typeof json['index'] != 'undefined' ? json['index'] : '';
			var sliderOrder = typeof json['order'] != 'undefined' ? json['order'] : [];
			var html = '';
			var i, v, t, len1, len2, lenSO;
			
			// get length of sliderOrder
			lenSO = sliderOrder.length;
			
			// get width of a single slider
			var sliderWidth = Math.floor( 538 / lenSO );
			
			// adjust position
			pos.left += parseInt(jQueryObj.css('margin-left'));
			pos.top += parseInt(jQueryObj.css('margin-top'));

			// make all charts
			html += '<div class="barGraphWrap" style="left:' + pos.left + 'px;top:' + pos.top + 'px;">';
				
			for (var s in json) {
				if ( s == 'index' || s == 'order' ) continue; 
				year = s;
				current = json[s];
				var setup = $.extend({}, current['setup']);
				var data = current['data'] ? current['data'] : [];
					
				// make header
				// we have to include all headers here and activate the current one
				html += '<div id="barGraphSingle-' + s + '" class="barGraphSingle' + ( s != json['index'] ? ' barGraphSingleDisabled' : '' ) + ' barGraphTheme-' + setup['theme'] + '">';
				html += '<div class="barGraphSliderBar">';
				for (i = 0; i < lenSO; i ++ ) {
					var curTitle = sliderOrder[ i ];
					var curTheme = setup['theme'];
					if ( typeof json[sliderOrder[ i ]] != 'undefined' && 
						 typeof json[sliderOrder[ i ]]['setup'] != 'undefined') {
						if ( typeof json[sliderOrder[ i ]]['setup']['title'] != 'undefined' ) curTitle = json[sliderOrder[ i ]]['setup']['title'];
						if ( typeof json[sliderOrder[ i ]]['setup']['theme'] != 'undefined' ) curTheme = json[sliderOrder[ i ]]['setup']['theme'];
					}
					t += sliderWidth;
					if ( lenSO > 1 && i == lenSO - 1 ) sliderWidth = 538 - sliderWidth;
					if ( s != sliderOrder[ i ] ) {
						html += '<a href="#" id="' + sliderOrder[ i ] + '" class="barGraphSlider barGraphSlider-' + curTheme + '" style="' + ( i < (lenSO - 1) ? 'width:' + (sliderWidth - 1) + 'px; margin-right:1px;' : 'width:' + sliderWidth + 'px;' ) + '">' + curTitle + '</a>';
					}
					else {
						html += '<div class="barGraphSliderActive' + (lenSO == 1 ? ' isSingle' : '' )  +' barGraphSlider-' + curTheme + '" style="' + (( i < (lenSO - 1) ? 'width:' + (sliderWidth - 1) + 'px; margin-right:1px;' : 'width:' + sliderWidth + 'px;' )) + '">' + curTitle + '</div>';
					}
				}
				html += '</div>';
				html += '<div class="' + ((lenSO > 1) ? 'barGraphSliderIndicator' : 'barGraphSliderIndicatorSingle') + ' clear">&nbsp;</div>';
				
				// make scale range and divisions
				var htmlScaleRange = '';
				var htmlDivisions = '';
				var scaleRangeLength = setup['scaleRange']['to'] - setup['scaleRange']['from'];
				var scaleRangeDivisions = setup['scaleRange']['step'] ? scaleRangeLength / setup['scaleRange']['step'] : 0;
				var scaleRangeItemWidth = Math.floor(scaleRangeDivisions ? 270 / scaleRangeDivisions : 0);
				var divisionItemWidth = scaleRangeItemWidth - 1;
				
				// begin scale range
				htmlScaleRange += '<ul class="barGraphScales" style="padding-left:' + (149 - ((scaleRangeItemWidth - 27) / 2)) + 'px;">';
				
				// begin divisions
				htmlDivisions += '<div class="barGraphDivisions">';
				
				// make items
				for (i = 0; i <= scaleRangeDivisions; i ++) {
					// add scale range item
					htmlScaleRange += '<li style="width:' + scaleRangeItemWidth + 'px">' + ((i * setup['scaleRange']['step']) + setup['scaleRange']['from']) + '</li>';
					
					// add division
					if (i < scaleRangeDivisions) htmlDivisions += '<div class="barGraphDivision" style="width:' + divisionItemWidth + 'px;">&nbsp;</div>';
				}
				
				// end scale range
				htmlScaleRange += '</ul>';
				htmlScaleRange += '<br class="clear" />';
					
				// end divisions
				htmlDivisions += '<br class="clear" />';
				htmlDivisions += '</div>';
				
				// add scale range top
				html += htmlScaleRange;
				
				// add divisions
				html += htmlDivisions;
				
				// add bars
				var lastVerticalOffset = 81;
				var divisionsHeight = 0;
				
				html += '<div class="barGraphBars">';
				for (i = 0, len1 = data.length; i < len1; i ++) {
					// parse vars
					data[i]['description'] = $.fn.barGraph._parseVars(data[i]['description'], current, {
						'data': i
					});
					
					data[i]['tip'] = $.fn.barGraph._parseVars(data[i]['tip'], current, {
						'data': i
					});
					
					for (var s2 in data[i]['partitions']) {
						data[i]['partitions'][s2]['tips'] = $.fn.barGraph._parseVars(data[i]['partitions'][s2]['tips'], current, {
							'data': i
						});
					}
					
					// insert gap
					if (data[i]['format']['insertGap']) {
						lastVerticalOffset += 8;
						divisionsHeight += 8;
					}
					
					// make normal bar
					html += $.fn.barGraph._makeSingleBarElement(data[i], setup, 8, lastVerticalOffset, false);
					
					// offset
					// mind gap and stuff
					lastVerticalOffset += 23;
					divisionsHeight += 23;
					
					// make child bars
					if (data[i]['children']) {
						for (v = 0, len2 = data[i]['children'].length; v < len2; v ++) {
							// parse vars
							data[i]['children'][v]['description'] = $.fn.barGraph._parseVars(data[i]['children'][v]['description'], current, {
								'data': i,
								'children': v
							});
							
							data[i]['children'][v]['tip'] = $.fn.barGraph._parseVars(data[i]['children'][v]['tip'], current, {
								'data': i,
								'children': v
							});
							
							for (var s in data[i]['children'][v]['partitions']) {
								data[i]['children'][v]['partitions'][s]['tips'] = $.fn.barGraph._parseVars(data[i]['children'][v]['partitions'][s]['tips'], current, {
									'data': i,
									'children': v
								});
							}
							
							// insert gap
							if (data[i]['children'][v]['format']['insertGap']) {
								lastVerticalOffset += 8;
								divisionsHeight += 8;
							}
							
							// do
							html += $.fn.barGraph._makeSingleBarElement(data[i]['children'][v], setup, 8, lastVerticalOffset, true);
							
							// offset
							lastVerticalOffset += 23;
							divisionsHeight += 23;
						}
					}
				}
				html += '</div>';
				
				// add scale range bottom
				html += htmlScaleRange;
				
				// make additional texts
				if (setup['texts']) {
					html += '<div class="barGraphFooter">' + (setup['texts']['below'] ? setup['texts']['below'] : '&nbsp;') + '</div>';
					html += '<div class="barGraphContentClose">&nbsp;</div>';
					if (setup['texts']['description']) html += '<div class="barGraphInfoText">' + setup['texts']['description'] + '</div>';
				}
				else {
					html += '<div class="barGraphContentClose">&nbsp;</div>';
				}
				
				// add print link
				html +='<div class="barGraphPrint">';
				html +='<a href="javascript:void(0);" onclick="barGraphPrintJob(\'' + options['file'] + '\',\'' + year  + '\'); return true;">Dieses Schaubild drucken</a>';
				html +='</div>';
				
				// closing div theme
				html += '</div>';
			}
			// closing div wrapper
			html += '</div>';
				
			// deal with options
			if (options && options['empty']) {
				jQueryObj.removeClass();
				jQueryObj.empty();
			}
			if (options && options['class']) jQueryObj.addClass(options['class']);
			if (options && options['html']) jQueryObj.html(options['html']);
			
			// insert html
			var barGraphObj = $(html);
			jQueryObj.append(barGraphObj);
			
			// extend divisions
			barGraphObj.find('.barGraphDivisions').css('height', (divisionsHeight + 8) + 'px');
			
			// extend original container
			jQueryObj.css('display', 'block');
			jQueryObj.css('overflow', 'hidden');
			jQueryObj.width(barGraphObj.width());
			jQueryObj.height(barGraphObj.height());
			
			// store
			barGraphObj.data('jQueryParentObject', jQueryObj);
			
			// add slider events
			barGraphObj.find('a.barGraphSlider').click(function(e) {
				e.preventDefault();
				var $t = $(e.currentTarget);
				barGraphObj.find('.barGraphSingle').css('display', 'none');
				barGraphObj.find('.barGraphSingle[id="barGraphSingle-' + $t.attr('id') + '"]').css('display', 'block');
			});
			
			// add bar events
			barGraphObj.find('.barGraphBarWrap').mouseenter(function(e) {
				try {
					var $barGraphBarWrap = $(e.currentTarget);
					
					if (!$barGraphBarWrap.find('.barGraphBarOuter').html()) {
						$barGraphBarWrap.css('cursor', 'auto');
						return;
					}
					
					$('.barGraphHighlight').each(function(i, o) {
						try {
							$(o).css({
								'display': 'none'
							});
						}
						catch(e) {
						}
					});
					
					$barGraphBarWrap.prev('.barGraphHighlight').css({
						'display':	'block',
						'opacity': .75
					});
					
					// if there are more than 1 partitions, do not show a tooltip
					$barGraphBarWrap.find('.barGraphPartition,.barGraphPartitionLast').each(function(i, o) {
						try {
							var $barGraphPartition = $(o);
							var title = '';
							
							if ((title = unescape($barGraphPartition.attr('title'))) != '') {
								$barGraphPartition.data('tiptext', title);
								$barGraphPartition.attr('title', '');
							}
							else if ($barGraphPartition.data('tiptext')) {
								title = $barGraphPartition.data('tiptext');
							}
							
							if (title) {
								// bind events for partitions
								$barGraphPartition.bind("mouseover", function(e) {
									try {
										makeToolTip($(this), $(this).data('tiptext'), e.pageX, e.pageY);
										e.stopPropagation();
									}
									catch(e) {
									}
									return false;
								});
								
								$barGraphPartition.bind("mouseout", function(e) {
									try {
										removeToolTip();
										e.stopPropagation();
									}
									catch(e) {
									}
									return false;
								});
							}
							
							// bind events for bargraph
							if (i < 1) {
								var t = '';
								if ((t = unescape($barGraphBarWrap.attr('title'))) != '') {
									$barGraphBarWrap.data('tiptext', t);
									$barGraphBarWrap.attr('title', '');
									title = t;
								}
								else if ($barGraphBarWrap.data('tiptext')) {
									title = $barGraphBarWrap.data('tiptext');
								}
								else {
									$barGraphBarWrap.data('tiptext', title);
								}
								
								if (title == 'prevent') return;
								
								$barGraphBarWrap.bind("mouseover", function(e) {
									makeToolTip($(this), $(this).data('tiptext'), e.pageX, e.pageY);
								});
								
								$barGraphBarWrap.bind("mouseout", function(e) {
									removeToolTip();
								});
							}
						}
						catch(e) {
						}
					});
				}
				catch(e) {
				}
			});
			
			// hide highlights
			barGraphObj.find('.barGraphBarWrap').mouseleave(function(e) {
				try {
					var $obj = $(e.currentTarget);
					$obj.prev('.barGraphHighlight').css({
						'display':	'none'
					});
				}
				catch(e) {
				}
			});
		}
		catch(e) {
		}
	};
	$.fn.barGraph._makeSingleBarElement = function(data, setup, left, top, sub) {
		var html = '';
		
		try {
			// make highlight
			html += '<div class="barGraphHighlight" style="left:' + (left - 10) +'px;top:' + (top - 2) + 'px;">&nbsp;</div>';
			
			// make bar
			html += '<div class="barGraphBarWrap" style="left:' + left +'px;top:' + top + 'px;"' + ((typeof(data['tip']) != 'undefined' && data['tip']) ? (' title="' + escape(data['tip']) + '"') : '') + '>';
			// mind format bold
			html += '<div class="barGraphBarName"' + (data['format']['fontWeightBold'] ? ' style="font-weight:800;"' : '') + '>'+ (sub ? '<div class="barGraphBarNameSub">' + data['abbreviation']  + '</div>' : data['abbreviation'] ) + '</div>';
			html += '<div class="barGraphBarScore">&nbsp;</div>';
			
			// make bars and partitions
			html += '<div class="barGraphBarOuter">';
			
			// if there is a bar, put it out
			if (data['range'] && 
			   (typeof(data['range']['start']) != 'undefined') && 
			   (typeof(data['range']['end']) != 'undefined')) {
				var scaleRangeLength = setup['scaleRange']['to'] - setup['scaleRange']['from'];
				var rangeStart = data['range']['start'] - setup['scaleRange']['from'];
				var rangeEnd = data['range']['end'] - setup['scaleRange']['from'];
				var dataRangeLength = rangeEnd - rangeStart;
				var barInnerOffset = Math.floor((rangeStart / scaleRangeLength) * 271);
				var barInnerLength = Math.floor((dataRangeLength / scaleRangeLength) * 271);
				
				// inner bar
				html += '<div class="barGraphBarInner" style="margin-left:' + barInnerOffset + 'px; width:' + barInnerLength + 'px;">';
				
				// make partitions
				for (var i = 0, len = data['partitions'].length; i < len; i ++) {
					var partitionLength = Math.floor(((data['partitions'][i]['amount'] - setup['scaleRange']['from']) / dataRangeLength) * barInnerLength);
					
					if (i < (len - 1)) {
						html += '<div class="barGraphPartition" style="width:' + partitionLength + 'px; background-color:' + data['partitions'][i]['color'] + ';" title="' + escape(data['partitions'][i]['tips']) + '">&nbsp;</div>';
					}
					else {
						html += '<div class="barGraphPartitionLast" style="width:' + partitionLength + 'px; background-color:' + data['partitions'][i]['color'] + ';" title="' + escape(data['partitions'][i]['tips']) + '">&nbsp;</div>';
					}
				}
				
				// close
				html += '<br class="clear" />';
				html += '</div>';
			}
			
			// close bar
			html += '</div>';
			
			// make desc
			html +='<div class="barGraphBarDesc">' + data['description'] + '</div>';
			
			// close
			html += '<br class="clear" />';
			html += '</div>';
		}
		catch(e) {
		}
		
		return html;
	};
	$.fn.barGraph._parseVars = function(str, obj, indexList) {
		var res = '';
			
		try {
			var startIndex = 0;
			var endIndex = 0;
			var t;
			
			while ((startIndex = str.indexOf('{', endIndex)) >= 0) {
				res += str.substring(endIndex, startIndex);
				startIndex ++;
				if ((t = str.indexOf('}', startIndex)) >= 0) {
					endIndex = t;
				}
				else {
					endIndex = str.length - 1;
				}
				
				// check if it's a var request or a function
				token = str.substring(startIndex, endIndex);
				
				// replace indexes
				token = token.replace(/(data.index)+/g, (indexList['data'] ? indexList['data'] : 0));
				token = token.replace(/(children.index)+/g, (indexList['children'] ? indexList['children'] : 0));
				
				// variable or function?
				if (token.substr(0, 1) == '$') {
					res += $.fn.barGraph._parseValue(token.substr(1), obj);
				}
				else if (token.substr(0, 5) == 'func:') {
					res += $.fn.barGraph._parseFunction(token.substr(5), obj);
				}
				
				// save start index
				endIndex ++;
			}
			
			if (endIndex) res += str.substr(endIndex);
		}
		catch(e) {
		}
		
		return res ? res : str;
	};
	$.fn.barGraph._parseValue = function(str, obj) {
		var res = str;
			
		try {
			var parts = str.split('.');
			var evalStr = 'obj';
			
			for (var s in parts) {
				var token = parts[s];
				var arrayIndex = '';
				var index = parts[s].indexOf('[');
				
				if (index >= 0) {
					token = parts[s].substr(0, index);
					arrayIndex = parts[s].substr(index);	
				}
				
				evalStr += '["' + token + '"]' + arrayIndex;
			}
			try {
				var evalResult = eval(evalStr);
				if (typeof(evalResult) != "undefined") res = evalResult;
			}
			catch(e) {
			}
		}
		catch(e) {
		}
		
		return res;		
	};
	$.fn.barGraph._parseFunction = function(str, obj) {
		var res = '';
			
		try {
			var parts = str.split('|');
			
			if (parts.length > 0) {
				var funcName = parts.shift();
				
				if (typeof(funcName) != 'function') {
					// parse values
					for (var s in parts) {
						parts[s] = $.fn.barGraph._parseValue(parts[s], obj);
					}
					
					// call function
					res = window[funcName].apply(document, parts);
					if (res == undefined) return '';
				}
			}
			
		}
		catch(e) {
		}
		
		return res;
	}
	
})(jQuery);
